stm32duino (f1) bootstrap

Post your cool example code here.
Post Reply
ag123
Posts: 740
Joined: Thu Jul 21, 2016 4:24 pm

stm32duino (f1) bootstrap

Post by ag123 » Sun May 28, 2017 9:40 am

this post is some of my findings examining the current bootstrap sequences in stm32f1 duino codes
---------------
there are things that run before main() executes, this is how it works
in the variant's folder e.g. STM32F1/variants/maple_mini/wirish there is start.S and start_c.c, the call graph starts as
start.s -> start_c() -> __libc_init_array();
__libc_init_array() is part of newlib which looks like this
https://github.com/eblot/newlib/blob/ma ... isc/init.c

Code: Select all

/* Iterate over all the init routines.  */
void
__libc_init_array (void)
{
  size_t count;
  size_t i;

  count = __preinit_array_end - __preinit_array_start;
  for (i = 0; i < count; i++)
    __preinit_array_start[i] (); //*********

  _init ();

  count = __init_array_end - __init_array_start;
  for (i = 0; i < count; i++)
    __init_array_start[i] ();
}
__preinit_array_start (); calls > that *special* premain() hook in Arduino_STM32/STM32F1/cores/maple/main.cpp
note the use of __attribute__(( constructor (101))) this statement inserts a function call into __preinit_array_start so that it calls premain()
https://github.com/rogerclarkmelbourne/ ... e/main.cpp

Code: Select all

extern void setup(void);
extern void loop(void);
extern void init(void);

// Force init to be called *first*, i.e. before static object allocation.
// Otherwise, statically allocated objects that need libmaple may fail.
 __attribute__(( constructor (101))) void premain() {
    init();
}

int main(void) {
    setup();

    while (1) {
        loop();
    }
    return 0;
}
the call graph then continues this way
start.s -> start_c() -> __libc_init_array(); -> __preinit_array_start ();

__preinit_array_start (); -> premain() -> init()
and init() is part of the board/variant setup code
https://github.com/rogerclarkmelbourne/ ... boards.cpp

Code: Select all

void init(void) {
    setup_flash();
    setup_clocks();
    setup_nvic();
    systick_init(SYSTICK_RELOAD_VAL);
    wirish::priv::board_setup_gpio();
    setup_adcs();
    setup_timers();
    wirish::priv::board_setup_usb();
    wirish::priv::series_init();
    boardInit();
}
after init() returns it goes back to start_c() returning from __libc_init_array();

Code: Select all

void __attribute__((noreturn)) start_c(void) {

...
    /* Run initializers. */
    __libc_init_array();

    /* Jump to main. */
    exit_code = main(0, 0, 0);
    if (exit) {
        exit(exit_code);
    }

    /* If exit is NULL, make sure we don't return. */
    for (;;)
continue;
and the next statement is to call main();

ag123
Posts: 740
Joined: Thu Jul 21, 2016 4:24 pm

Re: stm32duino (f1) bootstrap

Post by ag123 » Sun May 28, 2017 9:45 am

i'm not really against the special 'constructor' hook

Code: Select all

 __attribute__(( constructor (101))) void premain() {
    init();
}
despite that this feature is rather obscure. the statement

Code: Select all

 __attribute__(( constructor (101)))
causes premain() to be inserted into __preinit_array_start this is a set of codes called by start_c() -> __libc_init_array() before main() runs.
the benefit here is that you can have many 'constructors' and i think that 101 indicates the priority or sequence in which they are called

User avatar
Rick Kimball
Posts: 1014
Joined: Tue Apr 28, 2015 1:26 am
Location: Eastern NC, US
Contact:

Re: stm32duino (f1) bootstrap

Post by Rick Kimball » Mon May 29, 2017 2:35 pm

ag123 wrote:... the statement

Code: Select all

 __attribute__(( constructor (101)))
causes premain() to be inserted into __preinit_array_start

I don't think you will ever see anything in the __preinit_array while generating code for stm32 chips. From what I've been able to find, it is meant to handle static constructors that are in dynamic libraries. Everything we link here is static, there are no libraries being loaded on demand.

If we look at the .o files with command arm-none-eabi-nm, you can see the section names used in each object file, you won't find any in the .preinit_array section. Look at the output from the main.cpp.o:

Code: Select all

$ arm-none-eabi-nm -C -a ./core/main.cpp.o
00000000 n .ARM.attributes
00000000 b .bss
00000000 n .comment
00000000 d .data
00000000 N .debug_abbrev
00000000 N .debug_aranges
00000000 N .debug_frame
00000000 N .debug_info
00000000 N .debug_line
00000000 N .debug_ranges
00000000 N .debug_str
00000000 t .init_array.00101
00000000 T main
00000000 a main.cpp
00000000 t .text
00000000 t .text.startup.main
00000000 t .text.startup._Z7premainv
         U init()
         U loop()
         U setup()
That output shows that using the constructor(101) attribute with the premain() function places the code into a special section named ".init_array.00101" That section naming comes into play when you look at how the arrays are placed in flash using the linker script.

Look at the common.inc file in the variants/.../ld directory. When the linker is combining all the .o files and placing them into flash, it orders them based on the section directives in the common.inc You can see that the address of the premain() function is going to be put into the __init_array_start before everything because its section name is .init_array.00101

here is a snippet from the common.ini file

Code: Select all

       . = ALIGN(4);
        __init_array_start = .;
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array))
        __init_array_end = .;
The linker finds all the sections in all the .o files with a .init_array.* name, It also sorts them and adds them to the array. Then all the other constructors are added to the list just based random order. Well it isn't actually random, it all depends on the the how you order the files to link on the command line.

Looking at this closely now, it seems if we want to insure that the premain() function is always the first function called we should be using:

Code: Select all

 __attribute__(( constructor (1))) void premain() {
    init();
}
I verified this, it ends up in a section called ".init_array.00001"

I mentioned in another thread, it would make more sense to just call our init function _init() and then it would be called from start_c just before the constructors are iterated.
-rick

ag123
Posts: 740
Joined: Thu Jul 21, 2016 4:24 pm

Re: stm32duino (f1) bootstrap

Post by ag123 » Mon May 29, 2017 4:02 pm

thanks rick

i think there's once i ran into a missing _init() issue after setting the compile option -nostdlibs,
http://www.stm32duino.com/viewtopic.php ... =80#p25978

back then while in debug, it seemed premain() gets called from __preinit_array_start ();
hence i sort of concluded that way.

but i do agree _init() is possibly useful for this purpose and it makes codes quite a bit more readable instead of depending on the

Code: Select all

 __attribute__(( constructor (101))) void premain() {
    init();
}
the other way of course, is that we can after all simply call init() in main()

Code: Select all

main() {
  init();
  setup();
  while(1) 
    loop();
}
i think both options has an effect of making the codes more readable compared to using __attribute__(( constructor (101)))

User avatar
Rick Kimball
Posts: 1014
Joined: Tue Apr 28, 2015 1:26 am
Location: Eastern NC, US
Contact:

Re: stm32duino (f1) bootstrap

Post by Rick Kimball » Mon May 29, 2017 4:11 pm

ag123 wrote:back then while in debug, it seemed premain() gets called from __preinit_array_start ();
hence i sort of concluded that way.

debug symbols naming in gdb can be deceiving:

Code: Select all

(gdb) p/x &__preinit_array_start 
$1 = 0x80028ac
(gdb) p/x &__preinit_array_end   
$2 = 0x80028ac
(gdb) p/x &__
$3 = 0x80028ac
(gdb) p/x &__init_array_end   
$4 = 0x80028c0
(gdb)
You can see that the __preinit_array_end == __preinit_array_start.
You can also see that __init_array_start is the same address as __preinit_array_start.

However when you examine the __init_array_start in the debugger, it incorrectly tags it with the name __preinit_array_start

Code: Select all

[(gdb) x/4xw __init_array_start 
0x80028ac <__preinit_array_start>:      0x080026b9      0x08000fd5      0x08000111      0x
080001f9
-rick

ag123
Posts: 740
Joined: Thu Jul 21, 2016 4:24 pm

Re: stm32duino (f1) bootstrap

Post by ag123 » Mon May 29, 2017 4:15 pm

the other thing which seemed to be a 'catch' as well is i wonder if __libc_init_array (void) is actually calling the c++ class constructors.
if it is then we may have a problem as __libc_init_array calls _init()

Code: Select all

void __libc_init_array (void)
{
  size_t count;
  size_t i;

  count = __preinit_array_end - __preinit_array_start;
  for (i = 0; i < count; i++)
    __preinit_array_start[i] (); //*********

  _init();

  count = __init_array_end - __init_array_start;
  for (i = 0; i < count; i++)
    __init_array_start[i] ();
}
 
** i found a useful link
https://docs.oracle.com/cd/E19683-01/80 ... index.html

Code: Select all

Example 5–2 _init()

static void *xxstatep;
int
_init(void)
{
    int error;
    const int max_instance = 20;    /* estimated max device instances */

    ddi_soft_state_init(&xxstatep, sizeof (struct xxstate), max_instance);
    error = mod_install(&xxmodlinkage);
    if (error != 0) {
            /*
             * Cleanup after a failure
             */
            ddi_soft_state_fini(&xxstatep);
    }
    return (error);
}
The driver should perform any one-time resource allocation or data initialization during driver loading in _init(). For example, it should initialize any mutexes global to the driver in this routine. The driver should not, however, use _init(9E) to allocate or initialize anything that has to do with a particular instance of the device. Per-instance initialization must be done in attach(9E). For example, if a driver for a printer can handle more than one printer at the same time, it should allocate resources specific to each printer instance in attach(9E).
this seem to suggest that g++ won't use __libc_init_array() to call the class constructors. if this is true, then it is likely

Code: Select all

 __attribute__(( constructor (101))) void premain() {
    init();
}
won't mix up with c++ class/object constructors as they are really independent of each other. i.e. __attribute__(( constructor (101))) void premain() works more like a regular c function call

** found yet another useful resource
https://arobenko.gitbooks.io/bare_metal ... piled.html
Every application must have a startup code usually written in Assembler. This startup code must perform the following steps:

Write the interrupt vector table at appropriate location (usually at address 0x0000).
Set the stack pointers for every runtime mode.
Zero the .bss section
Call constructors of global (static) objects (applicable only to C++)
Call the main function.

...
Call the __libc_init_array function provided by standard library which will initialise all the global objects. It will treat the area between __init_array_start and __init_array_end as list of pointers to initialisation functions and call them one by one.
what this would likely mean is that for global *static* objects, the constructors should literally just initialise its own instance / global variables.
as touching hardware has an unpredictable effect since _init() would only be called after the __preinit_array_start (); are called
Last edited by ag123 on Mon May 29, 2017 5:06 pm, edited 1 time in total.

User avatar
Rick Kimball
Posts: 1014
Joined: Tue Apr 28, 2015 1:26 am
Location: Eastern NC, US
Contact:

Re: stm32duino (f1) bootstrap

Post by Rick Kimball » Mon May 29, 2017 5:02 pm

ag123 wrote:the other thing which seemed to be a 'catch' as well is i wonder if __libc_init_array (void) is actually calling the c++ class constructors.
Yes it will call any "static" global class constructors. i.e, those declarations of a class that aren't local. So if you declare a thing like say, SerialUSB USBSerial in a global context, if it has a constructor it will be called. That is what is in the __init_array_start. The addresses of each global static constructor address.
ag123 wrote:if it is then we may have a problem as __libc_init_array calls _init()
Yes I agree.

It doesn't matter if we rename our function to be _init() or leave it with as premain() both will run the init() function before any of the global static constructors have been run. If the init() function is assuming all static global constructors have been called before it is run then it is going to do what we would expect.

The right thing for the _init() function to do is to setup the system clock, turn on all the peripheral RCC clocks so that constructors will work properly if they attempt to configure peripherals. I haven't looked at exactly what the init() function does, however I think that is its intended purpose.

However, looking at init() seems to call the wirish::board_setup_usb() function. Then further looking at the wirish/boards_setup.cpp this code seems to violate that intent.

Code: Select all

        __weak void board_setup_usb(void) {
#ifdef SERIAL_USB
           
#ifdef GENERIC_BOOTLOADER    
            //Reset the USB interface on generic boards - developed by Victor PV
            gpio_set_mode(PIN_MAP[PA12].gpio_device, PIN_MAP[PA12].gpio_bit, GPIO_OUTPUT_PP);
            gpio_write_bit(PIN_MAP[PA12].gpio_device, PIN_MAP[PA12].gpio_bit,0);
            ^M
            for(volatile unsigned int i=0;i<512;i++);// Only small delay seems to be needed, and USB pins will get configured in Serial.begin
            gpio_set_mode(PIN_MAP[PA12].gpio_device, PIN_MAP[PA12].gpio_bit, GPIO_INPUT_FLOATING);
#endif         
            Serial.begin();// Roger Clark. Changed SerialUSB to Serial for Arduino sketch compatibility
#endif
        }
That is going to result in the Serial.begin() call to be made before the constructor has run, it will use whatever is in memory. This goes back to a comment I saw in another thread about the static class variable working in the SerialUSB class. He is right. The only reason that works is because its value is initialized early in the start_c function when the .data section is copied.

Quite a web we weave :)
-rick

ag123
Posts: 740
Joined: Thu Jul 21, 2016 4:24 pm

Re: stm32duino (f1) bootstrap

Post by ag123 » Mon May 29, 2017 5:12 pm

i'm thinking if in this case we should after all call init() in main() itself before setup().

this would allow the static class constructors to complete initialization in __libc_init_array() or perhaps we should after all have 2 sets of init()

imho it isn't a good practice to initialise hardware in class/object constructors as the constructors would not handle exceptions and that it create dependencies that may well be unpredictable. for instance if a class/object constructor setup an interrupt vector/handler. if it is in flash a hardfault would probably result. But say if it is in ram it would probably succeed, but then when the hardware initialization runs and setup the interrupt vectors from a template the interrupt vector/handler set by the constructor would literally be simply overwritten/lost. this could also happen with registers or any 'shared variables' etc as the dependencies is resolved by virtue of its *sequence*.

actually it do sound like 2 different init() is necessary. this scenario seem unlikely to affect stm32f1 but it would likely affect stm32f4.
on stm32 f4 fpu need to be enabled for floating point usage. if we run init() in main() and some static class has some floating point initialization a hard fault would result. however, if we call init() from _init() and usbSerial() initialization would likely run into errors/exception since usbSerial static class would not have initialized yet by the time init() is run. this would possibly mean that we need to separate init() into 2 parts.

the initializations that the static class constructors depend on would be called from _init(), then those that depend on the static objects to be constructed would need to run in main() before setup(). but to separate main() from the boards, it would be more appropriate that main() calls a second init() function that's in board/mcu specific codes. e.g. usb initialization on f1 would be different from that in f4 as the usb hardware is different. then on the same mcu different boards place their pins, leds, buttons and jumpers differently. hence the 2nd init would need to be a function call from main() into the board specific files.

if we prefer to have only 1 set of init() then i'd think it would be better to simply call init() in main() before setup(). but that's until the static constructors has a *dependency* on _init() in which we'd need to split the init() into 2 separate functions. one to be called before the static constructors and one to be called in main() before setup()

just 2 cents

Post Reply