SparkFun Forums 

Where electronics enthusiasts find answers.

Everything ARM and LPC
User avatar
By bertrik
#23091
I'm no expert in this, but as far as I know sbrk is a low-level function to claim memory and is used by malloc. To be able to do this, you need to have some memory space that acts as the C "heap". I can imagine that 'end' indicates the end of the heap.
By mlu
#23100
The end is close.
The end in this case is a symbol that points to the first free word in RAM after all global variables has been placed there. Some codelibraries use the symbol _end and some use the symbol end (without the underscore). It is usually defined in the linker script, in you case

demo_at91sam7_blink_flash.cmd

At the end, after the the definition of the symbol _end, add the line PROVIDE(end = .);
This means: let the symbol 'end' point to the current position unless it has been given a differnt value somewhere else.
[Jim] Perhaps this should standard in the linker scripts.
Code: Select all
	    _end = .;
	    PROVIDE(end = .);
So actually end is the beginning of the heap.

Regards,
Magnus
By rain
#23126
Many Thanks - now i got newlib working!

I have one last question:

things like malloc and sprintf are working fine, But how can i redefine the puts or putchar function from the newlib so that it uses my own functions for this job and all my printf stuff is printed directly via the ARM's debug port?


Once again, thanks a lot

rain
By mifi
#23131
Hello rain,

I think you can write your own puts and putchar
function. This will override the function from the
library. Have you try this?

What do you mean with "printed directly via the ARM's debug port"?
Do you want to use the DCC of the ARM?

I think in this case you must have a debugger which support this
feature.

Regards,

Michael
By mlu
#23132
There is a debug unit uart on the ATSAM7S that can be used, any other UART is also OK.

Most file writing in newlib goes through the _write function. As far as I know, also puts and putchar uses this for the actual output. A very simple implementation, taken from the newlib sources is:
Code: Select all
int _write(int file, char *ptr, int len){
    int todo;
  
    for (todo = 0; todo < len; todo++) {
        writechar(*ptr++);   /* PUT YOUR OWN CODE TO SEND ONE CHAR HERE */
    }
    return len;
}
This code will replace the _write function in libnosys and send the output to your chosen dstination. But I have not tested this specific code.

Magnus
By rain
#23265
thanks for your kind help.

There is also a _write function defined in the libc.a(syscalls.o). So if i write my own _write function there is an error.

Please correct me if i'm wrong. Does defining personal _write and _read funtions mean that i have to recompile the whole newlib. Or is there any other possibility?


Thanks a lot,

rain
By mlu
#23282
no.

But if you add libraries using
lib\libc.a lib\libm.a libgcc.a
that can be the problem,

The prefered way is
-lc -lm -lgcc
Sometimes a path must be specified with -L $(PATH TO LIBRARIES) -lname

The linker wil subsitute -lname with libname.a or libname.so
and it also knows that this is a library so only load the functions used from other parts of the code.
The exact behaviour of linkers is deep magic.

If you define your own verison of library files with the same names and calling parametrs , then they are uesd insted of the library versions. So the library must not be rebuilt.

But your versions must be built with the same, or at least compatible compiler, flags as the library since they will simoetimes be called by other functions in the library.

Simply : your vesrion replaces the library version and thy must work together.

How can we now it i works. Test, try, debug and send in different data. :)

Debugging and testing is when we really learn about both the underlying system, our code and the problem we try to solve.

regards,
Magnus
By rain
#23317
Dear mlu,


Thanks for your hints.

now i tried the following:
Code: Select all
int _write(int file, char *ptr, int len){
    int todo;
      	AT91PS_DBGU pDBGU = AT91C_BASE_DBGU;
 
    for (todo = 0; todo < len; todo++) {
     	while(!(pDBGU->DBGU_CSR & AT91C_US_TXRDY)) ; 
   		pDBGU->DBGU_THR= *ptr++;
    }
    return len;
} 
And this is the output - similar to the error message i got before:
Code: Select all
make -k all 
...compiling debug.c
arm-elf-gcc -I./ -c -fno-common -O0 -g debug.c
...linking
arm-elf-ld -v -Map main.map -Tdemo_at91sam7_blink_flash.cmd -o main.out crt.o	main.o lcd.o lowlevelInit.o debug.o -L C:\Programme\ARM_TOOLCHAIN\yagarto\arm-elf\lib  -lc -lm -lgcc
GNU ld version 2.17
C:\Programme\ARM_TOOLCHAIN\yagarto\arm-elf\lib\libc.a(syscalls.o): In function `_write':
../../../../../../newlib-1.14.0/newlib/libc/sys/arm/syscalls.c:333: multiple definition of `_write'
debug.o:I:\SOFTWARE\5.Semester\ARM_Projects\demo_at91sam7_blink_flash/debug.c:34: first defined here
arm-elf-ld: Warning: size of symbol `_write' changed from 148 in debug.o to 112 in C:\Programme\ARM_TOOLCHAIN\yagarto\arm-elf\lib\libc.a(syscalls.o)
make: *** [main.out] Error 1
make: Target `all' not remade because of errors.
Do i have to add any other compiler / or linker flags so that my version of _write is used instead of newlib one's?

Once again,

Thanks a lot
rain
By mifi
#23320
Hello rain,

this should be your solution, I have test it here with an lpc2294 board.

How to solve the problem with newlib from YAGARTO?
Therefore you must know that the libc.a for YAGARTO is created
with the syscalls function. Syscalls provide some functionality for
malloc printf and more.

To overwrite we need a libc.a without the syscalls functionality.
The reason why you get the error with the _write is that this
function is in syscalls.

Now we remove syscalls from the libc.a I have installed YAGARTO at
d:\Compiler, therefore I will find libc.a at the following location:

d:\Compiler\yagarto\arm-elf\lib\libc.a

Go inside the d:\Compiler\yagarto\arm-elf\lib directory
and make a copy of the libc.a for example: libc_with_syscalls.a.

Use now the following command to remove the syscalls from libc.a:

arm-elf-ar -d libc.a syscalls.o

Now you have a libc.a library without the syscalls functionality.
The disavantage is here, you must write this functionality by yourself.

If you compile your program now, you will get a lot of warnings.

Try to use this syscalls.c file and add it to your project.

=============================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

extern int DebugWrite (const void *buffer, int len);

#define STDIN_FILENO 0 /* standard input file descriptor */
#define STDOUT_FILENO 1 /* standard output file descriptor */
#define STDERR_FILENO 2 /* standard error file descriptor */


void _exit (int n)
{
while(1)
{
n = n;
}
}

int _stat(char *file, struct stat *st) {
return (0);
}

int _fstat (int fd, struct stat * st)
{
if (fd == STDOUT_FILENO)
{
memset(st, 0, sizeof (* st));
st->st_mode = S_IFCHR;
st->st_blksize = 1024;
return 0;
}
else
{
DebugWrite("Error: _fstat\r\n", 15);
return(-1);
}
}

register char *stack_ptr asm ("sp");
caddr_t _sbrk_r(void *reent, size_t incr)
{
extern char end asm ("end"); // Defined by the linker
static char *heap_end;
char *prev_heap_end;

if( heap_end == NULL )
heap_end = &end;
prev_heap_end = heap_end;

if(( heap_end + incr ) > stack_ptr )
{
/* Some of the libstdc++-v3 tests rely upon detecting */
/* out of memory errors, so do not abort here. */
exit(1);
return (caddr_t) -1;
}

heap_end += incr;
return (caddr_t) prev_heap_end;
}

int isatty (int fd)
{
return(1);
}

int _lseek (int fd, int ptr,int dir)
{
return (-1);
}

int _open(const char *name, int mode)
{
return(-1);
}

int _close(int fd)
{
return(0);
}

int _write(int fd, const void *data, unsigned int count)
{
if (fd == STDOUT_FILENO)
{
count = DebugWrite(data, count);
}
else
{
DebugWrite("Error: _write\r\n", 15);
count = -1;
}

return(count);
}

int _read(int fd, void *buffer, unsigned int count)
{
return(-1);
}

=====================================================================

Inside the _write function I use DebugWrite, therefore
your application must now provide a DebugWrite function like this:

int DebugWrite (const void *buffer, int len);

Here you get the buffer of the string to write and the len.
You must return the len you have wrote.

int DebugWrite (const void *buffer, int len)
{
int c = len;
char *cp = (char*)buffer;

while(c--)
{
output(*cp++); /* You must write this function */
}

return len;
}


Best regards,

Michael
By mlu
#23321
Try adding -lnosys after -lc

/Magnus
By rain
#23344
Dear Michael,

wow - thanks a lot not i got it working.

I want to thank all of those who contributed to this topic.

Greetings from vienna,
rain
By hellospencer
#31660
Hello!

This thread was a great help for me understanding the role of the newlib and the syscalls.c file. Thank you very much for sharing knowledge.

Nevertheless, I still got a problem: The end of a string is always printed twice. See e.g.:
Hello Hello!
o!
1111111111111
111
Hello Hello!
o!
dec=123123 hex=1e0f3
hex=1e0f3

Hello, can you see me?
you see me?
e?
This text I produced with my at91sam7x-ek board following this thread and this piece of code:
Code: Select all
printf("Hello Hello!\r\n");
printf("1111111111111\r\n");
printf("Hello Hello!\r\n");
printf("dec=%d hex=%x\r\n", num, um);
I tracked down the error and found out, that after each call to DebugWrite, the fflush function is called and it makes another call to DebugWrite outputting some old data from the buffer.

Thanks for help and comments.
Armin
By hellospencer
#31889
Okay, I was able to solve my problem:

I didn't return the number of printed characters correctly; I missed line

return len;

in function DebugWrite.

I don't know why the compiler didn't complain..