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!
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.
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
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
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
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.
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?
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.
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
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.
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.
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.
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