EmbDev.net

Forum: ARM programming with GCC/GNU tools newlib not calling _write_r


von Michael O. (m1ke0)


Rate this post
useful
not useful
Hi,

I'm trying to hook newlib's printf into my STR912's UART.  I've replaced 
the syscall.c functions (_write_r, _open_r, etc) just fine, but I can't 
figure out what function calls them.  I'm using newlib-1.16.0.

printf( "test\r\n") calls _putc_r, which I haven't overridden, and 
expects a valid rentrancy structure.  Calling fprintf( "test\r\n") calls 
_fwrite_r, which I also haven't overriden.

What printf-like function calls _write_r?

Alternatively, how can I provide and initialize a reentrancy struct (I 
think it's called __REENT or _false_ptr, so that the rentrant functions 
stop causing data abort exceptions due to a bad rentrancy pointer?

I suppose I could go in and really hack up newlib, but that seems like a 
really bad way to solve the problem.  What do other people do?

I'd really like to use fprintf and fscanf, etc, so I can redirect I/O to 
the UART, SD card, LCD, etc, depending on the file pointer.

Thanks!

von Clifford S. (clifford)


Rate this post
useful
not useful
I would expect printf to call _write_r, if it is not doing that you have 
something wrong - the abort exception is also a give-away! ;-)

Bill Gatliff is probably the greatest expert of such things; here are a 
couple of articles by him on porting Newlib:

http://www.embedded.com/story/OEG20020103S0073
http://neptune.billgatliff.com/newlib.html

Clifford

von Martin T. (mthomas) (Moderator)


Rate this post
useful
not useful
Michael Okincha wrote:
> Hi,
>
> I'm trying to hook newlib's printf into my STR912's UART.  I've replaced
> the syscall.c functions (_write_r, _open_r, etc) just fine, but I can't
> figure out what function calls them.  I'm using newlib-1.16.0.
>
> printf( "test\r\n") calls _putc_r, which I haven't overridden, and
> expects a valid rentrancy structure.  Calling fprintf( "test\r\n") calls
> _fwrite_r, which I also haven't overriden.

I'm not an expert in this but AFAIK the mentioned functions are 
implemented in the newlib and there is not need to override them. They 
all should finally call the write-syscall.

> What printf-like function calls _write_r?
All that use a explicit or implicit (i.e. stdout) file-handle should. 
Did you continue stepping though the source? How did you notice that the 
write-syscall never gets called?

> Alternatively, how can I provide and initialize a reentrancy struct (I
> think it's called __REENT or _false_ptr, so that the rentrant functions
> stop causing data abort exceptions due to a bad rentrancy pointer?

I don't know enough about reentrancy in the newlib. Clifford already 
suggested some sources for information. Also look into the newlib-lpc 
source-code and into the libsysbase-code from devkitpro. There is also a 
newlib mailinglist where the developers read, they most probably can 
answer any question.

> I suppose I could go in and really hack up newlib, but that seems like a
> really bad way to solve the problem.  What do other people do?

"Hack" might not be needed. Do you know how the used newlib has been 
configured. Do you use an older version of Yagarto or a self-compiled 
toolchain?

> I'd really like to use fprintf and fscanf, etc, so I can redirect I/O to
> the UART, SD card, LCD, etc, depending on the file pointer.
newlib-lpc from Aeolus and libsysbase/libfat from DivkitPro/DevkitARM 
show how this can be implemented.

von Michael F. (mifi)


Rate this post
useful
not useful
Hello Michael,

>"Hack" might not be needed. Do you know how the used newlib has been
>configured. Do you use an older version of Yagarto or a self-compiled
>toolchain?
The new version of YAGARTO (29.03.2009) was build to support
newlib with reentrant stubs.

If you use here the printf function, you must implement
some stubs by yourself, e.g:

_sbrk_r, _fstat_r, _isatty_r, _close_r, _lseek_r, _write_r, _read_r

An example implementation can be found in my syscalls.c too.

Best regards,

Michael

von Michael O. (m1ke0)


Rate this post
useful
not useful
Hi All,

Thanks for the input.

I did finally track this down.  It turns out I wasn't initializing the 
pre-initialized RAM structures, like _impure_ptr or impure_data.

I gutted my startup.s to the bare minimum when debugging and interrupt 
issue, inlcuding the code that copied the .data code from ROM to RAM. 
That explains why it didn't work even when I declared my own impure_data 
structure.  Reinstating the .data copy code fixed everything.

One thing I might suggest is either including with the Yagarto tools or 
building for yourself is the debug version of the libraries.  It was 
very hard to debug this until I compiled my own newlib, which included 
all the C source in the debug files.

I do see the problem with scanf & printf.  The binary file became huge 
as soon as I added printf.  Now to explore things like iprintf and 
other, smaller libs.

Thanks all!

Mike

von Clifford S. (clifford)


Rate this post
useful
not useful
Michael Okincha wrote:

> I gutted my startup.s to the bare minimum when debugging and interrupt
> issue, inlcuding the code that copied the .data code from ROM to RAM.
> That explains why it didn't work even when I declared my own impure_data
> structure.  Reinstating the .data copy code fixed everything.

I would suggest that you gutted it to less than the bare minimum. Static 
initialisation is an essential part of the C runtime environment set-up. 
Without it, no static variable with an initialisation expression will 
get initialised, which will probably cause a great many bugs.

The zero initialisation is also essential for correct initialisation of 
statics without initialisers (just in case you removed that too).

The 'bare minimum' would be:

* Set initial stack pointer
* Zero un-initialised statics
* Initialise statics
* Call static constructors (C++)
* Jump to main.

>
> I do see the problem with scanf & printf.  The binary file became huge
> as soon as I added printf.  Now to explore things like iprintf and
> other, smaller libs.
>
Note also that formatted I/O requires a significant amount of stack 
space (about 4K in my experience) just to get out of bed. Removing 
floating point support may help lean it down. You have to rebuild Newlib 
to do that. Using alternatives may be preferable.

Clifford

von Tim C. (hardwing)


Rate this post
useful
not useful
Hello,

My system cannot call _write_r too, although I did the 'bare minimum'. 
The routine return back after called _sfvwrite_r, but never touch 
_write_r, any idea?

I modified the _write_r only in syscalls.c from yagarto and reserved 8k 
for user stack + heap.  It should be enough, I think.

von Clifford S. (clifford)


Rate this post
useful
not useful
Tim Choi wrote:
>  any idea?
>
From the information provided... no. If it did not get as far as your 
stub, then I would suspect your application code. But without seeing it 
who can tell?

von Richard S. (Company: Home Use) (shadders)


Rate this post
useful
not useful
Hi,

Can someone provide a basic explanation for me - and possibly other 
newbies.

The Yagarto web site provides the syscalls.c code and this works when i 
link it with the program that uses sprintf.

The code also runs on the embedded evaluation kit.

What i do not understand is that the syscalls.c seems to be a workaround 
since the following was written by Michael Fischer ::

>The new version of YAGARTO (29.03.2009) was build to support
>newlib with reentrant stubs.

>If you use here the printf function, you must implement
>some stubs by yourself, e.g:

>_sbrk_r, _fstat_r, _isatty_r, _close_r, _lseek_r, _write_r, _read_r

>An example implementation can be found in my syscalls.c too.

In my program sprintf did not work until i linked syscalls.c, and from 
what Michael has written you need to implement your own stubs.

So why does one have to implement ones own stubs ?. if the syscalls.c 
stubs work ?

Does this mean that sprintf works how i have used it at the moment, and 
yet may not work later if i added extra code to the program ?

What should the stubs look like to ensure that the sprintf works in all 
cases ?.

Is this the requirements of embedded programming, every part of the C 
language and the way the compiler implements it must be understood in 
detail ?.

Thanks.

Regards,

Richard.

von Clifford S. (clifford)


Rate this post
useful
not useful
Richard Shadbolt wrote:
>
> So why does one have to implement ones own stubs ?. if the syscalls.c
> stubs work ?
>
Because the compiler does not know what your hardware looks like, or 
what physical devices you will choose to be stdout, stdin and stderr, or 
whether or not you have a file-system or other devices that will be 
supported by the standard library I/O.


> Does this mean that sprintf works how i have used it at the moment, and
> yet may not work later if i added extra code to the program ?
>
sprintf() does not need any syscall stubs because its output device is 
RAM. Parts of stdio access a physical device need stubs to be 
implemented. printf() is an example of a call that needs device support 
(stdout). fprintf() may access output streams other than stdout and 
stderr, and you may wish to support that. For example you might have a 
file system, or multiple serial ports that you want to access with 
stdio. In addition to I/O devices, dynamic memory allocation also 
requires sbrk to be implemented because the compiler does not know know 
where your free memory is or how much you have.

> Is this the requirements of embedded programming, every part of the C
> language and the way the compiler implements it must be understood in
> detail ?.
>
If you are using a hardware platform for which the board support work 
has already been done by someone else, and that meets your requirement 
(and its usage license allows it) you may use it as a black-box to a 
large extent. Commercial vendors such as IAR and Kiel provide 
out-of-the-box support for a large range of micro-controllers and COTS 
boards. If you are using your own hardware a little more work may be 
required to support it. If you are using a microprocessor or 
microcontroller not already directly supported, you do need to know what 
your are doing with both the toolchain and the hardware. In the case of 
GCC for ARM, the compiler only knows about the ARM core. You need to 
provide run-time start up code to initialise at least the CPU clock and 
memory devices (which are not defined by the ARM core itself), as well 
as the C library support stubs.

Check out the "ARM-GCC development resources" sticky thread at the top 
of this forum for more information. The "Building bare-metal ARM with 
GNU" link is especially relevant.


Clifford

von Richard S. (Company: Home Use) (shadders)


Rate this post
useful
not useful
Hi Clifford,

Thanks for the explanations, has helped a great deal in understanding 
this new area.

I appreciate the time you have spent in answering the questions. Thanks.

Regards,

Richard.

von Tim C. (hardwing)


Rate this post
useful
not useful
Hi,

After traced the code, I found my "printf("Hello World....") just move 
the string to the memory somewhere (becase the buffer size is bigger 
than len of string?) and then finish the printf job and return back. 
Did I miss to do something to zero the buffer size?  From the source, if 
the buffer size become zero, it will call _write_r immediately......

BRs.

von Tim C. (hardwing)


Rate this post
useful
not useful
Hello,

I found the root cause.  My string was bufferred.  I need to send a "\n" 
(e.g. printf("Hello World....\n")) or even flush the buffer driectly 
(i.e.: fflush(stdout)) to force the library function to call _write_r.

Tim.

von Clifford S. (clifford)


Rate this post
useful
not useful
Tim Choi wrote:
> I found the root cause.  My string was bufferred.  I need to send a "\n"

As I suggested, posting your code would have helped.

von setoy (Guest)


Rate this post
useful
not useful
Tim Choi wrote:
> I found the root cause.  My string was bufferred.  I need to send a "\n"


That is old, but anyway...
Solution is call of

  setbuf(stdin, NULL);
  setbuf(stdout, NULL);

before first call of i/o functions

Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
No account? Register here.