world leader in high performance signal processing
Trace: » newlib

newlib

The bfin-elf target toolchain uses newlib for standard C library to develop standalone applications. As given in Wikipedia: “Newlib is a C standard library implementation intended for use on embedded systems. It is a conglomeration of several library parts, all under free software licenses that make them easily usable on embedded products.”

The library is maintained here: newlib

Available APIs: newlib_libc, newlib_libm

Other good articles for reference:

Embedding-with-GNU-Newlib

Porting and Using Newlib

embecosm newlib

Since it is meant for standalone applications, users need to create their own stubs, or low level drivers.

STDIO

Currently the bfin-elf- port does not include native host support - blindly including a statement such as printf(“\nHello World\n”); will generate warnings & errors that actually indicate that there is no support for STDIO at all.

/usr/local/src/blackfin/git/toolchain/gcc-4.3/newlib/libc/reent/closer.c:53: warning: _close is not implemented and will always fail
c:/program files/analog devices/gnu toolchain/svn-20101128/elf/bin/../lib/gcc/bfin-elf/4.3.5/../../../../bfin-elf/lib\libc.a(fstatr.o): In function `fstat_r':
/usr/local/src/blackfin/git/toolchain/gcc-4.3/newlib/libc/reent/fstatr.c:62: warning: _fstat is not implemented and will always fail
c:/program files/analog devices/gnu toolchain/svn-20101128/elf/bin/../lib/gcc/bfin-elf/4.3.5/../../../../bfin-elf/lib\libc.a(makebuf.o): In function `_smakebuf':
/usr/local/src/blackfin/git/toolchain/gcc-4.3/newlib/libc/stdio/makebuf.c:96: warning: isatty is not implemented and will always fail
c:/program files/analog devices/gnu toolchain/svn-20101128/elf/bin/../lib/gcc/bfin-elf/4.3.5/../../../../bfin-elf/lib\libc.a(lseekr.o): In function `lseek_r':
/usr/local/src/blackfin/git/toolchain/gcc-4.3/newlib/libc/reent/lseekr.c:58: warning: _lseek is not implemented and will always fail
c:/program files/analog devices/gnu toolchain/svn-20101128/elf/bin/../lib/gcc/bfin-elf/4.3.5/../../../../bfin-elf/lib\libc.a(readr.o): In function `read_r':
'Finished building target: hello-world'
/usr/local/src/blackfin/git/toolchain/gcc-4.3/newlib/libc/reent/readr.c:58: warning: _read is not implemented and will always fail
c:/program files/analog devices/gnu toolchain/svn-20101128/elf/bin/../lib/gcc/bfin-elf/4.3.5/../../../../bfin-elf/lib\libc.a(writer.o): In function `write_r':
/usr/local/src/blackfin/git/toolchain/gcc-4.3/newlib/libc/reent/writer.c:58: warning: _write is not implemented and will always fail

The most common practical implementation is that of serial stdio, where the stdio functions printf/scanf are re-directed to a UART driver. This UART driver talks to a Host computer through an RS-232 cable, on requests from the newlib APIs. The Host usually runs a console software (such as Kermit / Minicom on Linux or Putty / Hyperterminal on Windows).

Serial STDIO

A sample code for STDIO re-direct through serial bus is provided. The code re-directs all printfs and scanfs over UART port on BF533 EZ-KIT Lite through its RS-232 interface. The code should work out of box on all Blackfin Processors, except BF54x which has some design changes on the same. Refer to Application Note BF54x UART changes for changes on the BF54x UART port:

Note that the divisor value needs to be calculated according to the SCLK rate. The formula for this calculation (as per the Hardware Reference Manual):

BAUD RATE = SCLK/(16 x Divisor)

Code tested with Baud Rate = 57600.

The API stubs are taken from embecosm newlib. For more details, look at newlib stubs

Application Program

app_console.c
/********************************************************************
Main application program.
Use textcolor(foreground color, background color) to define colors.
Initialize the baud rate with UART_inititialize(divisor);
**********************************************************************/
#include <stdio.h>
 
extern void textcolor(int fg, int bg);
extern int init_serial_console(void);
int UART_inititialize(int);
 
///////////////////////////////////////
#define BLACK   	 0
#define RED     	 1
#define GREEN   	 2
#define YELLOW    	 3
#define BLUE   		 4
#define MAGENTA 	 5
#define CYAN    	 6
#define WHITE   	 7
//////////////////////////////////////
 
char console_buf[1024] = "hello world";
 
int main(void)
{
	init_serial_console();
 
	textcolor(RED, WHITE);
 
	printf("\r\nHello World!\n\r");
        printf("\nType in something, press <Enter>, and I'll just repeat it!\n\r\r");
        scanf("%31[0-9a-zA-Z \t]s", console_buf);
 
	textcolor(GREEN, WHITE);
	printf("\nYou wrote me - %s !\n\r",console_buf);
 
	textcolor(BLUE, CYAN);
	printf("\nGood Bye Cruel World!\n\r");
 
	return 1;
}
 
int init_serial_console()
{
	int divisor=129;
 
	UART_inititialize(divisor);
 
	return 0;
 
}

newlib STDIO stubs for UART

stdio_serial.c
/*************************************************************
Supports echo back for scanfs
limited support for back space / erase operation - on Putty console currently
**************************************************************/
///////////////////////////////////////////////////////////////
#include <stdio.h>
#include <errno.h>
#include <cdefBF533.h>
#include <sys/stat.h>
#include <gcc.h>
///////////////////////////////////////////////////////////////
void textcolor(int fg, int bg);
int UART_inititialize(int);
////////////////////////////////////////////////////////////////
 
#define _SERIAL_THROWBACK //for echo
 
//#define _PERFORM__BACKSPACE   //turns on back space support - seems to be working on Putty well.
//#define _PUTTY_BACKSPACE 	//enable for either putty or kermit. kermit not completely tested.
//#define _KERMIT_BACKSPACE
 
//////////////////////////////////////////////////////////////////
//change the console properties - attribute, foreground & background
//////////////////////////////////////////////////////////////////
void textcolor(int fg, int bg)
{
	  char command[13];
 
	  /* Command is the control command to the terminal */
	  sprintf(command, "%c[%d;%dm", 0x1B, fg + 30, bg + 40);
	  printf("%s", command);
}
///////////////////////////////////////////////////////////////
//Configures UART in 8 data bits, no parity, 1 stop bit mode.
///////////////////////////////////////////////////////////////
int UART_inititialize(int divisor)
{
	// enable UART clock.
	*pUART_GCTL = UCEN;
 
	// Write result to the two 8-bit DL registers (DLH:DLL).
	*pUART_LCR = DLAB;
	*pUART_DLL = divisor;
	*pUART_DLH = divisor>>8;
 
	// Clear DLAB again and set UART frame to 8 bits, no parity, 1 stop bit.
	*pUART_LCR = WLS(8);
 
	return 0;
}
 
///////////////////////////////////////////////////////////////
//poll the TEMT bit in LSR reg and wait until all data shifted out.
///////////////////////////////////////////////////////////////
void UART_waitForTransferCompletion(void)
{
	while (!(*pUART_LSR & TEMT)) { }; // wait
	ssync();
}
///////////////////////////////////////////////////////////////
//transmit character by polling the THRE bit in the LSR register.
///////////////////////////////////////////////////////////////
void UART_putc(char c)
{
	while (!(*pUART_LSR & THRE)) { }; //wait
	*pUART_THR = c;
}
///////////////////////////////////////////////////////////////
//receive character by polling the DR bit in the LSR register.
///////////////////////////////////////////////////////////////
char UART_getc(void)
{
	char c;
	while (!(*pUART_LSR & DR)) { }; //wait
	c = *pUART_RBR;
 
	#ifdef _SERIAL_THROWBACK
	UART_putc(c);
	#endif
 
	return c;
}
///////////////////////////////////////////////////////////////
//receive an LF ('ENTER') by polling the DR bit in the LSR register.
///////////////////////////////////////////////////////////////
int UART_gets(char *str, int max)
{
	int i;
 
	#ifndef _PERFORM__BACKSPACE
	for(i = 0; i < max; i++)
	{
		str[i] = UART_getc();
 
		if (str[i] == 13)
		{
			return i+1;
		}
	}
	#endif
 
	#ifdef _PERFORM__BACKSPACE
	#ifdef _KERMIT_BACKSPACE
	for(i = 0; i < max; i++)
	{
		str[i] = UART_getc();
 
		if (str[i] == 13)
		{
			return i+1;
		}
 
		if (str[i] == 0x7f)
		{
			i = i - 1;
 
			str[i] = NULL;
 
			UART_putc(0x08);
			UART_putc(0x08);
			UART_putc(0x7f);
			UART_putc(0x08);
 
			i = i - 1;
 
		}
	}
	#endif
	#ifdef _PUTTY_BACKSPACE
	for(i = 0; i < max; i++)
	{
		str[i] = UART_getc();
 
		if (str[i] == 13)
		{
			return i+1;
		}
 
		if (str[i] == 0x7f)
		{
			i = i - 1;
 
			str[i] = NULL;
 
			i = i - 1;
 
		}
	}
	#endif
	#endif
 
	return max;
}
 
///////////////////////////////////////////////////////////////
//	LOW LEVEL STUBS                                          //
///////////////////////////////////////////////////////////////
 
int
_open (const char *name,
       int         flags,
       int         mode)
{
  errno = ENOSYS;
  return -1;                    /* Always fails */
 
}       /* _open () */
 
int
_close (int   file)
{
  errno = EBADF;
  return -1;                    /* Always fails */
 
}       /* _close () */
 
int
_write (int   file,
        char *buf,
        int   nbytes)
{
  int i;
 
  /* Output character at at time */
  for (i = 0; i < nbytes; i++)
    {
	  UART_putc( *buf++ );
    }
 
  UART_waitForTransferCompletion();
 
  return nbytes;
 
}       /* _write () */
 
int
_read (int   file,
	   char *buf,
	   int   nbytes)
{
 
  int result = UART_gets( (char *) buf, nbytes );
 
  return result;                          /* EOF */
 
}       /* _read () */
 
int
_fstat (int          file,
        struct stat *st)
{
  st->st_mode = S_IFCHR;
  return  0;
 
}       /* _fstat () */
 
int
_lseek (int   file,
        int   offset,
        int   whence)
{
  return  0;
 
}       /* _lseek () */
 
int
isatty (int   file)
{
  return  1;
 
}       /* _isatty () */
 
///////////////////////////////////////////////////////////////

There is a nice console plugin for Eclipse - Target Management Terminal. To install this:

Install RXTX:

   Name = RXTX
   URL  = http://rxtx.qbang.org/eclipse/ 
   

Install RSE TMT:

   Name = TMT
   URL  = http://download.eclipse.org/tm/updates/3.3/
   

Screenshot of TMT on Helios:

Screenshot of TMT running alongside serial console program:

malloc on newlib

Malloc API() on newlib depends on _sbrk() function. This is implemented as given in below code, and is based on the “start” and “end” of a memory block defined for heap in the Linker Script.

In the Linker Script:

  __lds_hp_start = ORIGIN(MEM_L1_DATA_B); 
    PROVIDE (_lds_hp_start = ORIGIN(MEM_L1_DATA_B));
  
  __lds_hp_end   = ORIGIN(MEM_L1_DATA_B) + LENGTH(MEM_L1_DATA_B); 
    PROVIDE (_lds_hp_end = ORIGIN(MEM_L1_DATA_B) + LENGTH(MEM_L1_DATA_B));

Example code for malloc() testing:

malloc_test.c
#include <stdlib.h>
#include <errno.h>
int main();
 
char *malloc_ret1,*malloc_ret2,*malloc_ret3;
int malloc_ret_err1,malloc_ret_err2,malloc_ret_err3;
 
/*expected:
If __lds_hp_start = 0xFF900000, __lds_hp_end = 0xFF908000, heap size is 32768
Then
malloc_ret_err1 = 0
malloc_ret_err2 = 1
malloc_ret_err3 = 1
*/
 
int main(void)
{
	malloc_ret1 = malloc (32000);
    if (malloc_ret1==NULL) malloc_ret_err1++;
 
	malloc_ret2 = malloc (10000);
    if (malloc_ret2==NULL) malloc_ret_err2++;
 
    free(malloc_ret1);
 
	malloc_ret2 = malloc (10000);
    if (malloc_ret2==NULL) malloc_ret_err2++;
 
	malloc_ret3 = malloc (32000);
    if (malloc_ret3==NULL) malloc_ret_err3++;
 
    free(malloc_ret2);
 
	malloc_ret3 = malloc (32000);
    if (malloc_ret2==NULL) malloc_ret_err3++;
 
    return 0;
}
 
void *
_sbrk (int nbytes)
{
	  extern char lds_hp_end[];		/* Defined by the linker.  */
	  extern char lds_hp_start[];		/* Defined by the linker.  */
 
	  static char *heap_end;
	  char *prev_heap_end;
 
	  if (heap_end == NULL)
	    heap_end = &lds_hp_start;
 
	  prev_heap_end = heap_end;
 
	  if (heap_end + nbytes > &lds_hp_end)
	    {
	      errno = ENOMEM;
	      return (void *) -1;
	    }
 
	  heap_end += nbytes;
 
	  return prev_heap_end;
 
} // _sbrk () //