3

I'm writing C code, with Rowley CrossStudio 5.3.0, for a board with an STM32F401, that "do things" after receiving commands incoming from a RS485 line. The board has a bootloader code at address 0x0800000 and when a "jump-to-boot" command comes, the main-code execute a "NVIC_SystemReset()", and the bootloader code takes control of the board. The main-code starts at 0x08008000, and the boot-code, at the end of firmware upload, jumps at main code, with a function I found online, an assembler code. Now the issue: one of the commands that main-code could receive, is a "reset" that restarts the main code, from address 0x08008000, with re-initialization of some variabiles to their default, inactivation of some external hardware, etc.

In STM32_Startup.s there's a "reset_handler" label, and I use a "goto *(&reset_handler)" instruction for this restart. I defined as "extern unsigned int reset_handler" the label, so I can use the absolute address of reset_handler label. The "goto" works, the code restarts, the compiler (gcc) doesn't show any error but the corrector on-the-text, shows an error, as in image:
IMAGE OF ERROR

"Indirect goto in function with no address-of-label expressions"

Why?

3
  • 4
    Sharing a minimal reproducible example would be helpful. Commented Oct 15 at 8:25
  • 2
    Also please post the error as text: Why should I not upload images of code/data/errors?. Commented Oct 15 at 10:30
  • At least with gcc, an address-of-label looks like &&label, where label is an actual label, not an int value. Commented Oct 15 at 11:47

4 Answers 4

4

Ok, resolved.
I already use a ".noinit" RAM zone, to obtain different behaviours of boot code.
I just added a new status code and modify the boot code for immediately jump to main code.

Sign up to request clarification or add additional context in comments.

4 Comments

There you go then :) However, it would probably make a great helpful answer for future readers if you could show how you modded the linker script to achieve this in Crossworks. And maybe a snippet of the out-of-reset decision-making too?
Oh, for sure.. ;)
Ah I meant that you could post it as an edit to this answer rather than a separate answer. Nice of you to share it anyway :)
I'm not experienced enough for this forum yet, and I probably never will be ;) Buona giornata
3

You are doing the wrong thing the wrong way.

  • reset_handler is not a C label, it is a function declared with assembler syntax.
  • goto doesn't work on addresses unless you rely on poorly documented, gcc-specific extensions. And they use a different syntax than what you are using, hence the compiler error. See the manual: https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html.

To do the wrong thing the correct way would rather be to declare a function in the C code and then call that one. A function declaration like this:

__attribute__((naked, noreturn)) void reset_handler (void);

Where the attributes strip away calling convention and return address stacking etc.

However that still won't work on Cortex M because the stack initialization is done in hardware during boot-up, based on the values programmed into the vector table along with the interrupt handler addresses. So you'll be calling the reset handler without reseting the stack, very bad idea. And in general microcontroller terms, you'll always want all hardware registers restored to their defaults anyway. In case there is some mysterious error, the cause might very well be hardware register corruption. So you want an actual hardware reset for these two reasons.


Doing the correct thing the correct way would therefore be to force a MCU reset. For example by feeding the watchdog the wrong code, WDT->CLEAR.reg = 0;. Then upon reset you can decide if you should run the bootloader or the main application, perhaps it is sufficient to pick one or the other by investigating the reason for the reset: STM32 how to get last reset status

In case that is not sufficient, then you need to invent your custom RAM area with a status variable. This area is not .data/.bss but a custom one, to be made exempt from initialization. All of this is achieved through the linker script, which in Crossworks gets generated based on flash_placement.xml. How do to this is a long and complicated story of it's own, so I won't go into that here.

But basically once you have the custom memory area up and running then:

  • You will need to edit the CRT or write your own one.
  • Check the latest reset cause from the reset vector.
  • In case of power-on reset, initialize the custom memory area.
  • In case of other reset causes, the custom memory area is not touched. Instead you check the custom memory for status codes and decide what to execute based on that.
  • In case you need to reset during program execution, write a status code to the custom memory area, then trigger a MCU reset.

1 Comment

Thank you Lundin, yes, I know that "reset_handler" is an assembly function and also know that the "goto" to reset_handler address is cheating. I already use a custom "no-init" ram, when main-code jumps to bootloader code, for different behaviours, so I just add another status for reset.
2

My solution in CrossStudio, both the main-code and the boot-code

I modified "flash_placement.xml", RAM placement section adding a no-init RAM section of 15 bytes (just because I need this space..)

<MemorySegment name="$(RAM_NAME:RAM);SRAM;RAM1">
.... (other RAM placements)
<ProgramSection alignment="1" size="0x0f" start="0x20003ff0 "load="No" name=".NOINIT" /> 

Add definition of some bytes, in main, with .noinit attribute

  volatile  COMBYTE_T __attribute__((section(".NOINIT"))) ComBytes;

COMBYTE_T is a three-byte structure: MainStatus (bit managed), PrimoByte, Secondo Byte, I already use to change boot-code behaviour, but even a single byte would have been enough.

So, when a reset command is received, I write values into .noinit RAM section, and after execute a system reset.

case cmd_Reset:
        if(MexBroadcastBUS == false) {
      SendBYTE2BUS(cst_ACK);
      AspettaN100uSec(30);
    }
    LED_GPIO_PORT->ODR &= ~(LED_2_ROSSO | LED_3_VERDE | LED_1_GIALLO);//LED FLASH
    ComBytes.MainStatus  = FROM_SWRESET + FOR_RESTART;//BIT MANAGED BYTE
    ComBytes.PrimoByte   = RESET_CODE_1;              // 1st BYTE 
    ComBytes.SecondoByte = RESET_CODE_2;              // 2nd BYTE
    NVIC_SystemReset();                               //(jump to 0x0800000)

    break;

And this is main cycle, in boot-code:

Same no-init RAM definition...
  volatile  COMBYTE_T __attribute__((section(".NOINIT"))) ComBytes;
..and same flash_placement.xml mod

<MemorySegment name="$(RAM_NAME:RAM);SRAM;RAM1">
.... (other RAM placements)
<ProgramSection alignment="1" size="0x0f" start="0x20003ff0 "load="No" name=".NOINIT" />

//-------------------------------------------------------------------------------------
int Main() {

...main code.... 
if((ComBytes.PrimoByte...etc..
    --do things--
// COMMON RAM SET TO EXECUTE A MAIN CODE RESTART
}else if((ComBytes.PrimoByte   == RESET_CODE_1) && //1st BYTE STATEMENT
         (ComBytes.SecondoByte == RESET_CODE_2) && //2nd BYTE STATEMENT
         (ComBytes.MainStatus  == FROM_SWRESET + FOR_RESTART)) {//BIT MANAGED 
    ComBytes.MainStatus = 0;  //RESET COMMON BYTES RAM
    ComBytes.PrimoByte = 0;
    ComBytes.SecondoByte = 0;
    TimeoutBoot_1ms = 1;   //THIS 1ms TIMEOUT STARTS FW_LAUNCH FUNCTION      
//------------------------------------------------------------------------------------

...
if(TimeoutBoot_1m == 0) Fw_Launch(); //THE JUMP-TO-MAIN-CODE FUNCTION 

I hope that's all clear...

Comments

0

The simplest method to call the reset handler without any CRT woodoo is:

    void (*rst)(void) = (void (*)(void))*((uint32_t *)0x4);
    rst();

Or simply

    ((void (*)(void))*((uint32_t *)0x4))();

It will work on any Cortex-M MCU.

10 Comments

Calling the reset handler manually will however not reset the stack pointer nor all hardware registers, so it is unlikely that you'll ever want to do that instead of simply forcing a MCU reset. Some MCUs (non-ARM) have different reset handlers based on the reset cause even, so you can have different code running depending on if it was power-on, COP, brown-out, clock fail etc etc.
1. How calling the function directly is different from calling it via pointer? How does it set registers. What is this comment about? 2. "Some MCUs (non-ARM)" - how is it related to to the question? What about CRAY computers ? PDP-11
The point is that you shouldn't be calling it at all, it is a callback function to be called from hardware upon reset. And ARM or not matters because Cortex M automatically sets the stack pointer upon hardware reset, unlike most other MCUs where it is set through inline assembler from the reset vector - meaning it is extra wrong to call the reset vector from software on ARM parts.
The discussion is not about resetting, only calling the reset handler - which is something completely different. If you want to reset, you need to use the appropriate register to soft reset the chip.
Yeah as mentioned in my post, this is about doing the wrong thing, which the OP is doing in the wrong way. Rather than showing them how to do the wrong thing the correct way, we ought to show how to do the correct thing the correct way. As also mentioned in my post, if you just plain call the reset vector with void f(void) you'll leave behind crap on the stack which will never get popped. You have to strip calling convention stacking. Which is admittedly a minor concern compared to the stack pointer becoming haywire since calling reset doesn't reset it...
Lundin - it is not only the stack - but also for example run mode ans many more. Software reset does not reset the memory (startup will - .bss .data and functions in RAM segments) so you vcan pass any parameters you want
I have no clue what "software reset" even is - that's some misconception coming from the PC world. You can reset the hardware from software though, traditionally by writing a bad value to the watchdog registers on purpose.
If you do not know, then why do you comment? Most modern (like Cortex, ESP , MSP340 ,....) MCU-s have special register bits for that without any tricks and watchdog hacks. And they call it - "software reset" - maybe more correct would be "software-initiated hardware reset:
Ah that - but that's something else and will not occur just because you call the reset vector from software. Specifically, STM32_Startup.s (the CRT) does not use it.
Why should CRT use it if it is called after the reset?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.