Function block architecture in a C program - c

I would like a create a software where some functions (or block) can be programmed later on my micro controller without having to flash the entire software again (flash will be done by a communication interface e.g. SPI).
The new blocks will all have the same API (e.g. 5 bytes as arguments, 1 byte returned).
Memory architecture will be organized as shown on this picture: Block architecuture.
Currently, I see no issues if I only use local variables in my new functions, because the variables will be pushed in the stack and will not be initialized in the .bss segment. But if I want to add a static variable in my function, I will have some troubles because the startup code will not initialize this variable in the .bss (or .data) segment.
My question is, is that event possible to implement an architecture like this one in a C code ? If yes, how can I adapt my startup code to initialize my new variables ? Do you think C++ is more appropriate to do such kind of things ?
Thanks a lot !

Include one function in the API which initializes the block. Host must call this function before any other functions of the block.
Also, keep in mind that if you do this, you must also reserve part of the RAM (in addition to flash) for the block, which the host shall not touch. These variables are normally marked as __no_init or something similar to prevent linker even trying to generate initialization data for .data placement.
Language you use (C or C++) most likely won't matter. Memory placement is not feature of either language, but instead is done with compiler extensions and linker features, which are toolchain specific.

Related

C startup code is only written in assembly confusion

I understand that the C startup code is for initializing the C runtime environment, initializes static variables, sets up the stack pointer etc. and finally branches to main().
They say that this can only be written in assembly language as it's platform-specific. However, can't this still be written in C and compiled for the specific platform?
Function calls of course would be not possible because we "more than likely" don't have the stack pointer set up at that stage. I still can't see other main reasons. Thanks in advance.
Startup code can be written in C language only if:
Implementation provides all necessary intrinsic functions to set hardware features that cannot be set using standard C
Provides mechanism of placing fragments of code and data in the specific place and in specific order (gcc support for ld linker scripts for example).
If both conditions are met you can write the startup code in C language.
I use my own startup code written in C (instead of one provided by the chip vendors) for Cortex-M microcontrollers as ARM provides CMSIS header files with all needed inline assembly functions and gcc based toolchain gives me full memory layout control.
Most of the problem with writing early startup code in C is, in fact, the absence of a properly structured stack. It's worse than just not being able to make function calls. All of a C compiler's generated machine code assumes the existence of a stack, pointed to by the ABI-specified register, that can be used for scratch storage at any time. Changing this assumption would be so much work as to amount to a complete second "back end" for the compiler—way more work than continuing to write early startup code by hand in assembly.
Early bootstrap code, bringing up the machine from power-on, also has to do a bunch of special operations that can't usually be accessed from C, like configuring interrupts and virtual memory. And it may have to deal with the code not having been loaded at the address it was linked for, or the relocation table not having been processed, or other similar problems; these also break pervasive assumptions made by the C compiler (e.g. that it can inject a call to memcpy whenever it wants).
Despite all that, most of a user mode C library's startup code will, in fact, be written in C, for exactly the reason you are thinking. Nobody wants to write more code in assembly, over and over for each supported ISA, than absolutely necessary.
A minimal C runtime environment requires a stack, and a jump to a start address. Setting the stack pointer on most architectures requires assembly code. Once a stack is available it is possible to run code generated from C source.
ARM Cortex-M devices load the stack pointer and start address from the vector table on reset, so can in fact boot directly into code generated from C source.
On other architectures, the minimal assembly requires is to set a stack pointer, and jump to the start address. Thereafter it is possible to write other start-up tasks in C ( or C++ even). Such startup code is responsible for establishing the full C runtime, so must not assume static initialisation or library initialisation (no heap or filesystem for example), which are things that must be done by the startup code.
In that sense you can run code generated from C source, but the environment is not strictly conforming until main() has been called, so there are some constraints.
Even where assembly code is used, it need not be the whole start-up code that is in assembly.

Using pointer functions - 2 separate applications on 1 device

I asked some time ago this question How can I use one function from main application and bootloader? (embedded) and started to implement proposed solution but ran into a few problems.
On my cortex M4 I, have 2 separate applications - bootloader and user application. Now I had some (many) functions which were the same for both apps. So I compiled them only for bootloader, then created an array of function pointers at specified address, which is known for user application. So in application, I didn't compile the files with those functions again, but I use those pointers whenever needed.
This is example of code I tried to make common for both applications:
static uint8_t m_var_1;
// Sends events to the application.
static void send_event(fs_op_t const * const p_op, fs_ret_t result)
{
uint8_t var_2;
[...]
}
My application ends in Hardfault, which happens e.g. when dividing by zero or using pointer to function with NULL value. I am not sure why yet, but I started wondering what happens with those variables. var_2 will most surely be located on stack so this is no problem. But what about m_var_1? In the map file, it has a specified place in RAM. But I don't have seperate RAM sections for app and bootloader. I am not sure, but I have a feeling that this variable may use the same RAM location as when created for bootloader. Is this possible? Maybe some other issues?
Yes you are right, the code will attempt to access the global variable at the same location as it is linked for loader. This is because linking involves replacing all occurrences of identifiers (including function names and variable names) by the addresses determined after compiling.
In your application, the variable, even if it does exist there too, is likely to be at a different address.
The calling of the functions happens to work, because they are located in ROM and cannot be different for application and loader. Calling them via const pointers, which are also stored in ROM, bypasses the problem.
The solution is using a file system simulator, if you can find one for your hardware.
Otherwise you will hate having to do the following.
Part 1, setup:
introduce a special linker section with all the variables accessed by both system paprts (application and loader)
let one linker fill it
set it up for the other linker as don't-tocuh
be careful with the initialisation
preferrably do not assume any intialisation value
if you need initialisation, e.g. "bss" (init to 0) or "data" (init to specified value),
do so explicitly at the start of the system part which is not associated to the linker you let setup the variables
for safety, it is recommended to do the init the same way in both system parts
"data" init uses a special non-volatile linker section with a copy of the to-be-initialised variables, accessing that is possible
Part 2, access:
option 1)
store const pointers to those variables, like you did for the functions
option 2)
get the second linker (the other one, which did not do the actual setup of the common variable section) to create an identically structured and identically located section as the one from first linker; more studying of your linker needed here
Part 3, resuing values stored by other system part
(e.g. you want to leave some kind of message from loader, to be read my application)
design which system part initisalises which variable, the other one only reads them
separate the common variables in four sections,
written and read by both system parts, initialised by both
written and read by x, only read by y, initialised by x
written and read by y, only read by x, initialised by y
written by both system parts, not initialised, uses checksums and plausibility cehcks,
if a variable has not been initialised, init to default
init each section only in the corresponding writer system part
setup as "no init" in the other linker
setup as "no init" in both linkers for the fourth case
use getters and setters with checksum update and plausibility for the fourth case
To do all that, intense study of your linker features and syntax is needed.
So I recommend not to try, if you can get around it. Consider using an existing file system simulator; because that is basically what above means.

Why globals shouldn't be initialized at 0 / NULL / false in the linux kernel?

I'm using the checkpatch.pl script from the linux kernel for my own firmware since I use the same coding style (which I like).
There is just an error that I don't quite understand about global variables:
do not initialise globals to 0
For sure I want to avoid using globals as much as possible, but don't know why this is a style error?
Is it because some compilers don't put such globals in .BSS?
(Usually they are smart enough)
First, it is redundant, and increases the size of the kernel (not what is finally loaded, but by having explicit instructions to the linker which are unnecessary).
It is part of a larger problem:
Supposing that you had two different object files to link together, with different ideas of how to initialize them. Then the linker has to detect that and produce a symbol conflict error. The script is concerned with that as well.
Further reading:
.bss vs COMMON: what goes where?
Uninitialised global data in C – .bss section vs common symbols
Shared Libraries Redux (Ian Lance Taylor)

Is it possible to instruct C to not zero-initialize global arrays?

I'm writing an embedded application and almost all of my RAM is used by global byte-arrays. When my firmware boots it starts by overwriting the whole BSS section in RAM with zeroes, which is completely unnecessary in my case.
Is there some way I can instruct the compiler that it doesn't need to zero-initialize certain arrays? I know this can also be solved by declaring them as pointers, and using malloc(), but there are several reasons I want to avoid that.
The problem is that standard C enforces zero initialization of static objects. If the compiler skips it, it wouldn't conform to the C standard.
On embedded systems compilers there is usually a non-standard option "compact startup" or similar. When enabled, no initialization of static/global objects will occur at all, anywhere in the program. How to do this depends on your compiler, or in this case, on your gcc port.
If you mention which system you are using, someone might be able to provide a solution for that particular compiler port.
This means that any static/global (static storage duration) variable that you initialize explicitly will no longer be initialized. You will have to initialize it in runtime, that is, instead of static int x=1; you will have to write static int x; x=1;. It is rather common to write embedded C programs in this manner, to make them compatible with compilers where the static initialization is disabled.
It turned out that the linker-script included in my toolchain has a special "noinit" section.
__attribute__ ((section (".noinit")))
/** Forces the compiler to not automatically zero the given global
variable on startup, so that the current RAM contents is retained.
Under most conditions this value will be random due to the
behaviour of volatile memory once power is removed, but may be used in some specific
circumstances, like the passing of values back after a system watchdog reset.
So all global variabeles marked with that attribute will not be zero-initialised during boot.
The C standard REQUIRES global data to be initialized to zero.
It is possible that SOME embedded system manufacturers provide a way to bypass this option, but there are certainly many typical applications that would simply fail if the "initialize to zero" wasn't done.
Some compilers also allow you to have further sections, which may have other characteristics than the 'bss' section.
The other alternative is of course to "make your own allocation". Since it's an embedded system, I suppose you have control over how the application and data is loaded into RAM, in particular, what addresses are used for that.
So, you could use a pointer, and simply use your own mechanism for assigning the pointer to a memory region that is reserved for whatever you need large arrays for. This avoids the rather complex usage of malloc - and it gives you a more or less permanent address, so you don't have to worry about trying to find where your data is later on. This will of course have a small effect on performance, since it adds another level of indirection, but in most cases, that disappears as soon as the array is used as an argument to a function, as it decays to a pointer at that point anyways.
There are a few workarounds like:
Deleting the BSS section from the binary or setting its size to 0 or 1. This will not work if the loader must explicitly allocate memory for all sections. This will work if the loader simply copies data to the RAM.
Declaring your arrays as extern in C code and defining the symbols (along with their addresses) either in assembly code in separate assembly files or in the linker script. Again, if memory must be explicitly allocated, this won't work.
Patching or removing the relevant BSS-zeroing code either in the loader or in the startup code that is executed in your program before main().
All embedded compilers should allow a noinit segment. With the IAR AVR compiler the variables you don't want to be initialised are simply declared as follows:
__no_init uint16_t foo;
The most useful reason for this is to allow variables to maintain their values over a watchdog or brown-out reset, which of course doesn't happen in computer-based C programs, hence its omission from standard C.
Just search you compiler manual for "noinit" or something similar.
Are you sure the binary format actually includes a BSS section in the binary? In the binary formats I've worked with BSS is simply a integer that tells the kernel/loader how much memory to allocate and zero out.
There definitely is no general way in C to get uninitialized global variables. This would be a function of your compiler/linker/runtime system and highly specific to that.
with gcc, -fno-zero-initialized-in-bss

How to declare a C array that takes up all free space in a memory section?

Assume I have a 128KB memory region. In my linker directives I split this region into three sections:
.section_text
.section_data
.section_bss
The size of each section is unknown pre-compilation, but I have constrained .section_bss to use all remaining space within the memory region after .section_text and .section_data are allocated.
Is there any way I can declare a C array that uses up all available space in .region_bss? Assume it is the only thing using .region_bss so it can safely use the entire region. For example purposes but obviously wrong:
char entire_bss[sizeof(.region_bss)];
Here are my pre-answers to some anticipated responses. First, I know sizeof() doesn't work like this. I'm just using it to get an idea across. Second, assume this must be done with an array and not with pointers (solving with pointers is possible and fairly simple). Third, I'm aware I can get the start and end addresses of .region_bss, but I'm not aware of any way to use them to size my array. At least not any way that works in C.
There very well may be no way to do this, but I'm hoping some genius out there has figured it out. Extra credit if you can make it work with the Green Hills toolset.
Is there any way I can declare a C array that uses up all available space in .region_bss?
Short answer is "no."
If GreenHills is using GNU toolchain with binutils and allows customizing the linker script, then you can add into an application namespace a variable using PROVIDE to mark the end of the 128K block. (See an example how to access such variables here). You will not have a C array of fixed size that way, but program would be able to find the end of the array thus the size of the array what is generally sufficient for C programs.
If you want to accomplish that using pure C99, then you might be out of luck as it is highly unreliable as it is not covered by the standard.
Usually the way you do this is with something like:
extern char section_bss[];
With some extra stuff thrown in so that the compiler, assembler, and linker know that section_bss is an alias for .section_bss (or similar name).
To get the size you would probably be want to do the same thing for a symbol that is at the end of bss and find the difference.
No you can't. It's also easy to understand why. At the compilation phase there is no concept of .bss and .data these are pure linker conventions. The compiler knows some abstract concepts (const data, static data, code), but the mapping of them to the linker section is only a convention, and depending on linker, OS and processor or even memory model options. When building position independend code, often the constants are put in the code segment for example. On embedded platforms you sometime have to struggle with these things and even have to adapt your coding style because of that (we had once a model based in 80186 processors that were built with MS-C 5.1 compiler, were we couldn't use the .data section (global, initialized variables and statics) because the toolchain put them in the ROM.
We solved once a similar problem of yours by patching the generated binary at the end of the whole build process. We extracted the used memory of the different section from the map file generated by the linker (it's an option look it up), and subtracted it from the memory size of the given model (we had 256K, 512K and 896K models) and set one of the global constants with the right value. It was thus important that all references to this size work via this global variable. This memory pool was then used by our implementation of malloc/free.
What you are seeking to do will be very platform dependent. Your best bet is probably to declare an array as extern char the_array[],end_of_array[]; within your C file, and then, perhaps in an assembly-language source file, declare a couple of data sections for the_array and end_of_array. Then arrange the linker spec so that the_array will be at the end of stuff that's allocated bottom-up, and end_of_array will be at the start of stuff that's allocated top-down, and nothing else will be between them. It's possible--I've done it on some platforms--but it's important to read the linker documentation carefully to ensure that the linker won't rearrange things when you don't expect it.
What's wrong with
extern char __ghsbegin_region_bss[];
extern char __ghssize_region_bss[];
#define entire_bss __ghsbegin_region_bss
#define sizeof_entire_bss ((size_t)__ghssize_region_bss)
? I can think of at least one reason you might not be satisfied with this answer, but it involves C++ template metaprogramming magic (namely, the type of entire_bss wouldn't have the right size, so templates that depend on its size would resolve differently). As long as you don't use the size of the array for anything (and this is true, in C), this approach should be fine.
Note that __ghssize_region_bss is not a compile-time constant — it's not resolved until link-time — so there's 100% no way to get it inside the [...] of an array type at compile time.
See the chapter on "Beginning, End, and Size of Section Symbols" in the Green Hills manual.

Resources