For deeply embedded applications a minimal C runtime system is needed which takes up as little memory as possible, is easily portable to the target hardware, and only supports those functions required for such an application.
The ARM Software Development Toolkit comes with a minimal runtime system in source form. The 'behind the scenes' jobs which it performs are:
Note that no support is provided for outputting data down the debugging channel. This can be done, but is specific to the target application. The example C programs described below use the ARM Debug Monitor available under armsd to output messages using in-line SWIs.
Before attempting any of the demonstrations below create a working directory, and set this up as your current directory. Copy the contents of the clstand directory into your working directory, and also copy the files fpe*.o from the fpe340 directory of the cl directory into your working directory. You are now ready to experiment with the C standalone runtime system.
In the examples below, the following options are passed to armcc, armasm, and in the first case armsd:
-------------------------------------------------------- Option |Description -------------------------------------------------------- -li |Specifies that the the target is a |little endian ARM. -------------------------------------------------------- -apcs 3/32bit |This specifies that the 32 bit variant |of APCS 3 should be used. For armasm |this is used to set the built in |variable {CONFIG} to 32. --------------------------------------------------------
These arguments can be changed if the target hardware differs from this configuration. If the ARM Software Tools have been configured as desired then these options may be omitted, as the tools will default to the configuration time values.
These demonstrations are likely to be most useful if the sources rtstand.s, errtest.c and memtest.c are studied in conjunction with this recipe.
We can then execute this image under the armsd as follows:
armcc -c errtest.c -li -apcs 3/32bit
armasm rtstand.s -o rtstand.o -li -apcs 3/32bit
armlink -o errtest errtest.o rtstand.o
The '>' prompt is the Operating System prompt, and the 'armsd:' prompt
is output by armsd to indicate that user input is required.
> armsd -li errtest
A.R.M. Source-level Debugger, version 4.10 (A.R.M.) [Aug 26 1992]
ARMulator V1.20, 512 Kb RAM, MMU present, Demon 1.01, FPE, Little
endian.
Object program file errtest
armsd: go
(the floating point instruction-set is not available)
Using integer arithmetic ...
10000 / 0X0000000A = 0X000003E8
10000 / 0X00000009 = 0X00000457
10000 / 0X00000008 = 0X000004E2
10000 / 0X00000007 = 0X00000594
10000 / 0X00000006 = 0X00000682
10000 / 0X00000005 = 0X000007D0
10000 / 0X00000004 = 0X000009C4
10000 / 0X00000003 = 0X00000D05
10000 / 0X00000002 = 0X00001388
10000 / 0X00000001 = 0X00002710
Program terminated normally at PC = 0x00008550
0x00008550: 0xef000011 .... : > swi 0x11
armsd: quit
Quitting
>
Already several of the standalone runtime system's facilities have been demonstrated:
Again, we can now execute this image under the armsd as follows:
armcc -c errtest.c -li -apcs 3/32bit -DDIVIDE_ERROR
armlink -o errtest errtest.o rtstand.o
This time an integer division by zero has been detected by the standalone
runtime system, which called __err_handler. __err_hander
output the first set of error messages in the above output. Control was
then returned to the runtime system which output the second set of error
messages and terminated execution.
> armsd -li errtest
A.R.M. Source-level Debugger, version 4.10 (A.R.M.) [Aug 26 1992]
ARMulator V1.20, 512 Kb RAM, MMU present, Demon 1.01, FPE, Little
endian.
Object program file errtest
armsd: go
(the floating point instruction-set is not available)
Using integer arithmetic ...
10000 / 0X0000000A = 0X000003E8
10000 / 0X00000009 = 0X00000457
10000 / 0X00000008 = 0X000004E2
10000 / 0X00000007 = 0X00000594
10000 / 0X00000006 = 0X00000682
10000 / 0X00000005 = 0X000007D0
10000 / 0X00000004 = 0X000009C4
10000 / 0X00000003 = 0X00000D05
10000 / 0X00000002 = 0X00001388
10000 / 0X00000001 = 0X00002710
10000 / 0X00000000 = errhandler called: code = 0X00000001: divide by 0
caller's pc = 0X00008304
returning...
run time error: divide by 0
program terminated
Program terminated normally at PC = 0x0000854c
0x0000854c: 0xef000011 .... : > swi 0x11
armsd: quit
Quitting
>
longjmp and setjmp
A further demonstration can be made using errtest by predefining
the macro LONGJMP to perform a longjmp out of __err_handler
back into the user program, thus catching and dealing with the error.
First recompile and link errtest:
Then rerun errtest under armsd. We expect the integer
divide by zero to occur once again:
armcc -c errtest.c -li -apcs 3/32bit -DDIVIDE_ERROR -DLONGJMP
armlink -o errtest errtest.o rtstand.o
The runtime system detected the integer divide by zero, and as before
__err_handler was called, which produced the error messages. However,
this time __err_handler used longjmp to return control to the program,
rather than the runtime system.
> armsd -li errtest
A.R.M. Source-level Debugger, version 4.10 (A.R.M.) [Aug 26 1992]
ARMulator V1.20, 512 Kb RAM, MMU present, Demon 1.01, FPE, Little
endian.
Object program file errtest
armsd: go
(the floating point instruction-set is not available)
Using integer arithmetic ...
10000 / 0X0000000A = 0X000003E8
10000 / 0X00000009 = 0X00000457
10000 / 0X00000008 = 0X000004E2
10000 / 0X00000007 = 0X00000594
10000 / 0X00000006 = 0X00000682
10000 / 0X00000005 = 0X000007D0
10000 / 0X00000004 = 0X000009C4
10000 / 0X00000003 = 0X00000D05
10000 / 0X00000002 = 0X00001388
10000 / 0X00000001 = 0X00002710
10000 / 0X00000000 = errhandler called: code = 0X00000001: divide by 0
caller's pc = 0X00008310
returning...
Returning from __err_handler() with errnum = 0X00000001
Program terminated normally at PC = 0x00008558
0x00008558: 0xef000011 .... : > swi 0x11
armsd: quit
Quitting
>
However, in addition to this it is also necessary to link with an fpe stub, which we must compile from the source given (fpestub.s).
The resulting executable, errtest, can be run under armsd as
before:
armasm fpestub.s -o fpestub.o -li -apcs 3/32bit
armlink -o errtest errtest.o rtstand.o fpestub.o fpe_32l.o -d
This time the floating point instruction set is found to be available, and
when a floating point division by zero is attempted, __err_handler
is called with the details of the floating point divide by zero
exception.
> armsd -li errtest
A.R.M. Source-level Debugger, version 4.10 (A.R.M.) [Aug 26 1992]
ARMulator V1.20, 512 Kb RAM, MMU present, Demon 1.01, FPE, Little
endian.
Object program file errtest
armsd: go
(the floating point instruction-set is available)
Using Floating point, but casting to int ...
10000 / 0X0000000A = 0X000003E8
10000 / 0X00000009 = 0X00000457
10000 / 0X00000008 = 0X000004E2
10000 / 0X00000007 = 0X00000594
10000 / 0X00000006 = 0X00000682
10000 / 0X00000005 = 0X000007D0
10000 / 0X00000004 = 0X000009C4
10000 / 0X00000003 = 0X00000D05
10000 / 0X00000002 = 0X00001388
10000 / 0X00000001 = 0X00002710
10000 / 0X00000000 = errhandler called: code = 0X80000202: Floating
Point
Exception : Divide By Zero
caller's pc = 0XE92DE000
returning...
Returning from __err_handler() with errnum = 0X80000202
Program terminated normally at PC = 0x00008558 (__rt_exit + 0x10)
+0010 0x00008558: 0xef000011 .... : > swi 0x11
armsd: quit
Quitting
>
Note that if you have compiled errtest.c other than as in longjmp and setjmp, you will not see precisely this dialogue with armsd.
This can be run under armsd in the usual way:
armcc -li -apcs 3/32bit memtest.c -c
armlink -o memtest memtest.o rtstand.o
This demonstrates that allocating space on the stack is working correctly,
and also that the __rt_alloc routine is working as expected. The
program terminated because in the end __rt_alloc could not allocate
the requested amount of memory.
> armsd -li memtest
A.R.M. Source-level Debugger, version 4.10 (A.R.M.) [Aug 26 1992]
ARMulator V1.20, 512 Kb RAM, MMU present, Demon 1.01, FPE, Little
endian.
Object program file memtest
armsd: go
kernel memory management test
force stack to 4KB
request 0 words of heap - allocate 256 words at 0X000085A0
force stack to 8KB
..
force stack to 60KB
request 33211 words of heap - allocate 33211 words at 0X00049388
force stack to 64KB
request 49816 words of heap - allocate 5739 words at 0X00069A74
memory exhausted, 105376 words of heap, 64KB of stack
Program terminated normally at PC = 0x0000847c
0x0000847c: 0xef000011 .... : > swi 0x11
armsd: quit
Quitting
>
Stack overflow checking
memtest can also be used to demonstrate stack overflow checking by
recompiling with the macro STACK_OVERFLOW defined. In this case the
amount of stack required is increased until there is not enough stack
available, and stack overflow detection causes the program to be
aborted.
To recompile and link memtest.c issue the following commands:
Running this program under armsd produces the following output:
armcc -li -apcs 3/32bit memtest.c -c -DSTACK_OVERFLOW
armlink -o memtest memtest.o rtstand.o
Clearly stack overlfow checking did indeed catch the case where too much
stack was required, and caused the runtime system to terminate the program
after giving an appropriate diagnostic.
> armsd -li memtest
A.R.M. Source-level Debugger, version 4.10 (A.R.M.) [Aug 26 1992]
ARMulator V1.20, 512 Kb RAM, MMU present, Demon 1.01, FPE, Little
endian.
Object program file memtest
armsd: go
kernel memory management test
force stack to 4KB
...
force stack to 256KB
request 1296 words of heap - allocate 1296 words at 0X0000AE20
force stack to 512KB
run time error: stack overflow
program terminated
Program terminated normally at PC = 0x0000847c
0x0000847c: 0xef000011 .... : > swi 0x11
armsd: quit
Quitting
>
The function which we will add to rtstand is memmove. Although this is small, and easily extracted from the C library source, the same methodology can be applied to larger sections of the C library, eg. the dynamic memory allocation system (malloc, free, etc).
The source of the C library can be found in the cl directory. The source for the memmove function is in string.c. The extracted source for memmove has been put into memmove.c, and the compile time option _copywords has been removed. The function declaration for memmove and a typedef for size_t (extracted from include/stddef.h) have been put into memmove.h.
Our memmove module can be compiled as follows.
The output, memmove.o can be linkedwith the user's other object
modules together with rtstand.o in the normal way (see previous examples
in this section).
armcc -c memmove.c -li -apcs 3/32bit
The table below shows the typical size of the Areas in rtstand.o:
--------------------------------------------------------- Area |Size |Functions |(bytes)| --------------------------------------------------------- C$$data |4 | --------------------------------------------------------- C$$code$$__main |96 |__main, __rt_exit --------------------------------------------------------- C$$code$$__rt_fpavail|8 |__rt_fpavailable able | | --------------------------------------------------------- C$$code$$__rt_trap |128 |__rt_trap --------------------------------------------------------- C$$code$$__rt_alloc |68 |__rt_alloc --------------------------------------------------------- C$$code$$__rt_stkovf |76 |__rt_stkovf_split_* --------------------------------------------------------- C$$code$$__jmp |100 |longjmp, setjmp --------------------------------------------------------- C$$code$$__divide |256 |__rt_sdiv, __rt_udiv, | |__rt_udiv10, --------------------------------------------------------- All Areas |736 |__rt_sdiv10, __rt_divtest ---------------------------------------------------------
If floating point support is definitely not required, then the EnsureNoFPSupport variable can be set to {TRUE}, and some extra space will be saved. After making any modifications to rtstand.s, the size of the various areas can be found by using the command:
From the above table it is clear that for many applications the standalone
runtime library will be roughly 0.5Kb.
decaof -b rtstand.o