Electronics > Microcontrollers

Understanding ARM startup code (__scatterload stuff)

(1/4) > >>

simonlasnier:
Hi :)

I recently moved from an STM8 to an ARM Cortex M-23 (GD32E230).

The nice thing about the STM8 is the simplicity, in particular the startup code, which only consists of one assembly file that I could easily read and change.
So to save space on my 32kB ROM I ended up removing pretty much all the startup code apart from setting up the stack pointer. For the variable initialization I just initialized the whole RAM to zero, then initialized in my main the (very few) variables that needed a different value. It worked very well, and I saved loads of ROM space.

I am trying to do the same for the ARM Cortex-M, but I just want to make sure I do not miss anything "important". Like I did for the STM8, I am checking the final ASM code in the project AXF file.

Reading on the net, it seems the __scatterloadXXX ASM functions do the following:
* initialize to zero all global variables which are uninitialized in C code (I guess as a safety?)
* initialize the other global variables (this part just gets removed if I have no initialized variables in my C code)

So far so good. But then comes the stuff I do not get:
* __user_setup_stackheap sets up the stack and the heap... The stack is already set up by the MSP register... What is there left to do? :o
And for the heap I guess it is only a "software" thing since it is not mentioned anywhere in the ARM Cortex Device Generic User Guide?
* After the call to my main() there is the whole exit and __rt_exit, which should just be a dumb forever loop? (or not needed at all since the main() does not ever end)

Unless any of these is used for debugging?

Many questions I know - sorry about that  ;D

Thank you!
Simon

PS: not 100% related, but the STM8 compiler used to create very readable Assembly listings with the associated C code in comments above each small group of Assembly commands.
When it comes to ARM, the only ASM listing I can get from the compiled code is the output of the "fromelf.exe", which shows a lot but not the original C code - is that possible?

Siwastaja:
ARM startup is really simple because as you say, SP is set by hardware from the vector table.

Initializing globals to zero is not for safety and has nothing to do with ARM startup; it's C runtime feature and every C program on every architecture has to do it. It's simply because standard guarantees that globals (and statics within functions) without any explicit initializers are initialized to zero. So of course programmer expects this guarantee holds, but it needs to be actually done somewhere because real RAM contains random values, startup code is the place to do that.

Nothing else is really needed, and you can even skip those global initialization things but then it ain't standard-compliant C.

Usually you do want initialized globals because it makes your life easier, so then you need that, plus call to main().

It's matter of taste if you want to add something extra to the startup code, or do it in the beginning of main().

simonlasnier:
Brilliant  ;D ;D ;D

That is exactly what I had hoped   :phew:

Also I had no idea it was a C standard that non-initialized variables should be set to zero... Gosh I could have saved so many hours writing these =0; everywhere for the past couple of years :palm:

newbrain:

--- Quote from: simonlasnier on October 20, 2021, 11:13:15 am ---* initialize to zero all global variables which are uninitialized in C code (I guess as a safety?)
* initialize the other global variables (this part just gets removed if I have no initialized variables in my C code)
[--8<--]
PS: not 100% related, but the STM8 compiler used to create very readable Assembly listings with the associated C code in comments above each small group of Assembly commands.
When it comes to ARM, the only ASM listing I can get from the compiled code is the output of the "fromelf.exe", which shows a lot but not the original C code - is that possible?

--- End quote ---
Both initialization to zero and to an assigned value for variables defined at file scope (with external or internal linkage) or statically defined in a block scope are standard C semantics, C programmers rely on that behaviour.
All ARM startup code that I have encountered - NXP, ST, TI, Cypress etc. - takes care of this by default.

I'm not familiar with Gigadevice toolchain and startup code, but often a number of basic HW initialisations are also performed (e.g. clocks, watchdog, memory protection unit etc.).
The GD32E230 is a Cortex-m23 device, which also has some security features, some of the initialization you see might be related to that.

If you want to generate the assembler listing and you are using gcc, check the -S option (will generate the assembler and stop) or the --save-temps option, which will keep the .i (preprocessed source) and the .S (assembler) intermediate products, more easily integrated in an existing makefile or other compilation script.

Siwastaja:
The whole idea in "implicit" zero-initialization is that it's such commonly used initializer value, you can save program memory because all those zero-initialized values can be clumped into their own group and initialized to zero using simple loop. These zeroes do not need to be stored anywhere.

But all the other values need to be stored, the initialization loop then reads out those stored values from ROM and write them to RAM.

At least with some C compilers, explicitly writing "= 0" causes those variables to end up in .data instead of .bss, so that they reserve space in ROM to store those zeroes. This is why I prefer not to explicitly initialize to zero. Also avoiding excess writing.

You could make a counterargument that being explicit is better and "more readable" than being implicit, but to this I respond this is such basic and well-known feature of C that every competent C programmer should know about it  :).

Navigation

[0] Message Index

[#] Next page

There was an error while thanking
Thanking...
Go to full version