Skip to main content
Explorer
November 19, 2024
Solved

Undocumented behaviour STM32C011F4U6TR

  • November 19, 2024
  • 10 replies
  • 6759 views

Hi,

We notice the startup time from power up can be affected by setting one GPIOB pin to input. The startup time here refers to the time from SystemInit() until main(). Basically it contains scatterload and __rt_entry to my understanding.

Specifically, the sample code below, built with ARM Compiler 6.16 Tool, would demonstrate that

#define MEASURE_PIN 5

int main(void)
{
 IO_SET_PIN(GPIOA, MEASURE_PIN, 1); 

 // Enable IWDG
 IWDG->KR = 0xCCCC;
 // Enable write access
 IWDG->KR = 0x5555;
 // Set prescaler
 IWDG->PR = 0;
 // Set reload
 IWDG->RLR = 1;

 // Wait for the registers reload
 while (IWDG->SR)
 {
 }
 
 // refresh the watchdog.
 IWDG->KR = 0xAAAA;

 while(1);
 return 0;
}
void SystemInit(void)
{
 // enable GPIOA clock
 RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN;

 // set GPIOA pin5 to output
 IO_SET_PIN_DIR(GPIOA, MEASURE_PIN, 1);

 // set GPIOA pin5 to low
 IO_SET_PIN(GPIOA, MEASURE_PIN, 0);
 
 // Set GPIOB pin7 to input
 // GPIOB->MODER &= 0xffff3fff; 
} 

Basically GPIOA pin 5 is toggled to low and high for the time between SystemInit() and main() to be measured.

main() also has IWDG setup and ends with a while loop so that IWDG reset happens periodically since power up.

On the oscilloscope it looks like the screenshots below.

powerup_time_without_gpiob_input.png2nd_iwdg_reset_without_gpio.png

Respectively from the screenshots, it can be observed that the 1st startup(power up) takes 254 μs while the 2nd startup (IWDG reset) takes 132 μs.

Now if uncommenting this line GPIOB->MODER &= 0xffff3fff; and redo the measurement, we get the results below.

powerup_time_with_gpiob_input.png

We can see that having the GPIOB pin 7 set to input reduces the startup time from 254 μs to 132 μs and the rest startup remains taking the same time.

Could you please confirm the following:

  1. What causes the differences between 1st and 2nd startup?
  2. Why setting GPIOB ping 7 to input would affect the startup timing?

Just in case, this is how Reset_Handler looks like in our startup file. It is basically without any customization.

; Reset handler routine
Reset_Handler PROC
 EXPORT Reset_Handler [WEAK]
 IMPORT __main
 IMPORT SystemInit 
 LDR R0, =SystemInit
 BLX R0
 LDR R0, =__main
 BX R0
 ENDP

  Anta

    This topic has been closed for replies.
    Best answer by waclawek.jan

    I'm just describing what I see in the disasm. I am not ARM/Keil so can't explain their decisions... ;)

    I'm not sure the described phenomenon can be explained without having deep access into the 'C0's innards. I was asking for the chip revision because of the "first SRAM access may fail" erratum, although the erratum is not clear about what constitutes the circumstances of failure and what exactly are its consequences. But regardless of that erratum, one of the mechanisms I can envisage is, that after poweron reset a hardware process runs, which performs some operation across the whole SRAM array, and thus the first SRAM access is delayed by some time - well say it precalculates the parity, one word per cycle, 6kBytes at 12MHz would take around 130us...

    The difference between the "with gpio" and "without gpio" is, that in the former the first access to RAM is the push which happens *before* the observed pin is pulled low, i.e. the lengthy RAM operation happens before the pin is pulled low and thus the pulse has the expected length; whereas in the latter (without gpio) the first access to RAM is the first zero write for the initialization, which is *after* the pin is pulled low, thus prolongs the observed pulse.

    This is just a theory and could be proven or disproven by a carefully crafted test (possibly in asm), where one pin edge would be generated before the first access to SRAM, and another edge (either on the same or other pin) after that access. I don't have a 'C0 to experiment.

    In any case, the total time between the moment when POR releases the processor and the moment when the observed pin is pulled high at beginning of main() should be roughly the same in both cases. This could be proven or disproven by powering the circuit from a source which can go from 0V to 3V quickly enough (in a few us).

    JW

     

    10 replies

    Visitor II
    November 19, 2024

    Confirm if GPIOB is linked to other peripherals (e.g., alternate functions or shared clocks) that might be influencing startup time.

    ah2Author
    Explorer
    November 19, 2024

    According to datasheet of STM32C011x4/x6, GPIOB pin 7 could setup with various things by setting up alternate functions but that's not the case for the demo code above.

    Graduate II
    November 19, 2024

    For me this looks like:

    - During Voltage rise CPU starts to work

    and then loops

    - Watchdog triggers reset

    - New Startup sequence

     

    What behaviour do you expect?

    ah2Author
    Explorer
    November 19, 2024

    @Uwe Bonnes I expect the time takes to startup from power up would not be affected by setting GPIOB pin direction. Or if there is an explanation, as I asked in the original post, I'm all ears.

    Graduate II
    November 19, 2024

    Sorry, I did not read carefully enough. As I read the scope picture, the long first period happens well below 2.0 Volt.  HSI48 is only characterize from 2.0 to 3.6 Volt. I expect HSI48 to be much slower below 2.0 Volt.

     

    Perhaps route HSI tp MCO and measure HSI frequency at voltages that low.

    Super User
    November 19, 2024

    Which chip revision?

    Show disasm of the program for both cases.

    JW

    ah2Author
    Explorer
    November 20, 2024

    @waclawek.jan 

    Chip revision is STM32C011F4U6TR, not sure if there is more I should share. Please let me know.

    I generated the disasm with the following command and attached for both cases.

    fromelf.exe --text -a -c --interleave=source --output=without_gpio_asm.txt .\test.axf
    ST Employee
    November 20, 2024

    Hello @ah2 ,

     

    Is it possible to also probe the NRESET pin (If there is a capacitor on the NRESET pin please remove it) to see when it is released by the MCU after the Power On Reset ? 

     

     

    Best regards,

     

    Simon

    ah2Author
    Explorer
    November 20, 2024

    @Simon.T 

    On the NRST pin we have a capacitor 10kΩ pull up and a resistor 100 nF to ground, and we program the options bytes NRST_MODE to have Reset Input only.

    The scope pictures are as follows:

    NRST_measurement_with_gpio.png

     

    NRST_measurement_without_gpio.png

     

    Graduate II
    November 21, 2024

    Have you tried enabling the brown out? The MCU now boots before voltage reaches its max. Shouldn't affect it, but doesn't hurt to try. Perhaps it affects the internal clock. If it doesn't get enough voltage then doing anything might get it to do strange things.
    Can you show disassembly of IO_SET_PIN? Is it a function or a macro? What clock frequency is the MCU running? Because a difference of 122μs seems like a lot.

    ah2Author
    Explorer
    November 22, 2024

    I tried setting the brownout level to various settings but the timing behaves the same. The timing seems quite consistent.

    From the attached asm file, IO_SET_PIN is as follows:

    ;;;46 // set GPIOA pin5 to low
    ;;;47 IO_SET_PIN(GPIOA, MEASURE_PIN, 0);
     0x08000136: 6008 .` STR r0,[r1,#0]
     0x08000138: 4903 .I LDR r1,[pc,#12] ; [0x8000148] = 0x50000400

    and IO_SET_PIN is a macro

    The MCU is running on HSI48 with the default DIV so at 12 MHz. One observation worth mentioning is that if I set the frequency to 48MHz in SystemInit(), both reset time (power up and IWDG reset) takes the same at around 40 μs.

     

    Graduate II
    November 22, 2024

    @ah2 wrote:

    I tried setting the brownout level to various settings but the timing behaves the same. The timing seems quite consistent.


    Good. We can exclude voltage level as a cause. I do see a lot of voltage ripple in your scope images. Have you tried stronger decoupling?

    @ah2 wrote:

     

     

    ;;;46 // set GPIOA pin5 to low
    ;;;47 IO_SET_PIN(GPIOA, MEASURE_PIN, 0);
     0x08000136: 6008 .` STR r0,[r1,#0]
     0x08000138: 4903 .I LDR r1,[pc,#12] ; [0x8000148] = 0x50000400

     

     


    This probably takes just 2 CPU cycles. And initalizing the ram-variables (in __rt_entry) shouldn't take up any time as I don't see any global or static variable in your code. I suspect the internal clock is not fully stabilized yet.

     


    @ah2 wrote:

    One observation worth mentioning is that if I set the frequency to 48MHz in SystemInit(), both reset time (power up and IWDG reset) takes the same at around 40 μs.


    Perhaps this code waits for the clock to stabilize. Does this use the PLL? Can you share this code?

    Super User
    November 22, 2024

    > And initalizing the ram-variables (in __rt_entry) shouldn't take up any time as I don't see any global or static variable in your code. I suspect the internal clock is not fully stabilized yet.

    0x400 bytes starting at 0x20000020 are set to zero - there are 256 word writes.

    If the loop takes 6 cycles to execute, which IMO it can, that's around 130us at 12MHz clock.

    JW

    Graduate II
    November 22, 2024

    @waclawek.jan wrote:

    > And initalizing the ram-variables (in __rt_entry) shouldn't take up any time as I don't see any global or static variable in your code. I suspect the internal clock is not fully stabilized yet.

    0x400 bytes starting at 0x20000020 are set to zero - there are 256 word writes.

    If the loop takes 6 cycles to execute, which IMO it can, that's around 130us at 12MHz clock.

    JW


    That's quite a lot of zero-initialized variables. Or does it always clear the entire block reserved in the linker file?

    Super User
    November 22, 2024

    I'm just describing what I see in the disasm. I am not ARM/Keil so can't explain their decisions... ;)

    I'm not sure the described phenomenon can be explained without having deep access into the 'C0's innards. I was asking for the chip revision because of the "first SRAM access may fail" erratum, although the erratum is not clear about what constitutes the circumstances of failure and what exactly are its consequences. But regardless of that erratum, one of the mechanisms I can envisage is, that after poweron reset a hardware process runs, which performs some operation across the whole SRAM array, and thus the first SRAM access is delayed by some time - well say it precalculates the parity, one word per cycle, 6kBytes at 12MHz would take around 130us...

    The difference between the "with gpio" and "without gpio" is, that in the former the first access to RAM is the push which happens *before* the observed pin is pulled low, i.e. the lengthy RAM operation happens before the pin is pulled low and thus the pulse has the expected length; whereas in the latter (without gpio) the first access to RAM is the first zero write for the initialization, which is *after* the pin is pulled low, thus prolongs the observed pulse.

    This is just a theory and could be proven or disproven by a carefully crafted test (possibly in asm), where one pin edge would be generated before the first access to SRAM, and another edge (either on the same or other pin) after that access. I don't have a 'C0 to experiment.

    In any case, the total time between the moment when POR releases the processor and the moment when the observed pin is pulled high at beginning of main() should be roughly the same in both cases. This could be proven or disproven by powering the circuit from a source which can go from 0V to 3V quickly enough (in a few us).

    JW

     

    ah2Author
    Explorer
    November 28, 2024

    Hi @waclawek.jan I was trying to follow your conjecture and wanted to conduct the test afterwards if possible but failed to verify on a few points, such as:

    • Where do you see the PUSH after the pin is pulled low from the "without gpio" disasm? I can find it from the "with gpio" but not the other file.
    • "0x400 bytes starting at 0x20000020 are set to zero", where is this number 0x400 coming from?

    Could you please share more pointers on these 2 items?

    Anta

    Super User
    November 28, 2024

    > I can find it [PUSH] from the "with gpio" but not the other file.

    Exactly: there is no PUSH (which is before the observed pin pulled low) in the "without gpio" firmware; that's why I wrote above:

    >> whereas in the latter (without gpio) the first access to RAM is the first zero write for the initialization

    ---

    > "0x400 bytes starting at 0x20000020 are set to zero", where is this number 0x400 coming from?

    In __scatterload, 0x80001b4 is loaded into r4, and then LDM r4!,{r0-r2} loads 3 words from table at 0x80001b4 into r0, r1, r2 and calls address which is the 4th word of table at 0x80001b4 (loaded previosly into r3). That 4th word is 080001a4, which is address of __scatterload_zeroinit, and that transfers r2/4 zero words (i.e. r2 zero bytes) from r0 into memory starting at r1. As r2 was loaded as 3rd word from the table 0x80001b4, that's the 0x400.

    JW

     

    ah2Author
    Explorer
    December 4, 2024

    @waclawek.jan wrote:

    > I can find it [PUSH] from the "with gpio" but not the other file.

    Exactly: there is no PUSH (which is before the observed pin pulled low) in the "without gpio" firmware; that's why I wrote above:

    >> whereas in the latter (without gpio) the first access to RAM is the first zero write for the initialization

    ---


    Thanks for the explanation. I expect the 2nd reset would have been through the same zero initialization though; however, the measured time on the 2nd reset between "with gpio" and "without gpio" is the same. Am I overlooking something here? 

    Anta

    Super User
    December 4, 2024

    My hypothesis was, that after power-on reset (i.e. not watchdog reset), the first access to RAM lasts longer.

    JW

    Super User
    December 10, 2024

    > I'm still missing the connection/explanation of why having one extra GPIO direction configuration would lead to earlier SRAM access

    Because with the extra GPIO direction configuration the compiler felt a register pressure and decided to push registers in the prologue of SystemInit() function.

    JW

    Graduate II
    December 10, 2024

    @waclawek.jan wrote:

    > I'm still missing the connection/explanation of why having one extra GPIO direction configuration would lead to earlier SRAM access

    Because with the extra GPIO direction configuration the compiler felt a register pressure and decided to push registers in the prologue of SystemInit() function.

    JW


    Push on the stack? Does SystemInit even need stack? Would marking the function as _Noreturn or __attribute__((noreturn)) help?

    I think we need to ask the engineers who designed the chip. They should know what conditions trigger this behavior and perhaps how to avoid it.

     

     

     

    Super User
    December 10, 2024

    > Push on the stack? Does SystemInit even need stack?

    It's a normal C function.

    While in this particular case the registers wouldn't need to be preserved, there's no guarantee that that is the case generally.

    > Would marking the function as _Noreturn or __attribute__((noreturn)) help?

    It's a normal C function which does return.

    > I think we need to ask the engineers who designed the chip

    Developers with the required knowledge are highly unlikely to turn up here. The OP may ask ST directly, but I wouldn't hold my breath.

    > They should know what conditions trigger this behavior and perhaps how to avoid it.

    Most probably power-on reset triggers it. Maybe RAM_PARITY_CHECK option bit is involved. I don't have a C0 to try and also I am not interested in this that much; OP may try to check this.

    You can also try to write your program so that it won't touch RAM for quite some time (e.g. initialize clocks, peripherals meantime), if you really need every microsecond of powerup startup latency.  Note, that t RSTTEMPO = typ.270us max. 500us.

    JW