Portenta software reset makes the board crash

Hi,

I am currently working on a project where I send command to a portenta H7 with vision shield over ethernet.
One of those commands asks the portenta to reset itself, but by doing so the board crashes and it then needs to be manually reset (button press) to restart.

[code]
//reset function -- Call it to reset the arduino
void (*resetFunc)(void) = 0;

//reset function -- Call it to reset the arduino
void resetFunc2(void) {
  unsigned long *registerAddr;
  registerAddr = (unsigned long *)0xE000ED0C; //Writes to the AIRCR register of the stm32h747 to software restet the arduino
  //It is a 32 bit register set bit 2 to request a reset and write 0x05FA on the first bytes to perform 
   // the write
  //See ArmĀ® v7-M Architecture Reference Manual for more information
  Serial.println(*registerAddr,HEX); //For debug
  //*registerAddr = *registerAddr | (unsigned long) 0x05FA0304;
  *registerAddr = (unsigned long) 0x05FA0304;
  Serial.println(*registerAddr,HEX); //For debug
}
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  while(!Serial);
  Serial.println("Hello there");
  delay(500);
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(LEDB,HIGH);
  delay(100);
  digitalWrite(LEDB,LOW);
  delay(100);
  //resetFunc();
  resetFunc2();
}
[/code]

I found a workaround by writting directly to the register (resetFunc2) and this seems to work, but I'm still wondering why the first function doesn't work.

Probably I am completely wrong here, but it could be that the reset vector is not located at address 0 for this processor.

It looks like the Portenta uses the STM32H747 as its main processor. In this document it is mentioned that the address of the reset vector is located at address 0x00000004. If this is all correct, then maybe you could try the following.

void (*reset)(void) = (void (*)(void))*(size_t*)4;

Again, this is a stab in the dark, I have not done any proper research on this.

Yes, you define a function pointer and let it point to address 0x00000000. But this address is ITCM memory (uninitialized, not used) - FLASH ROM vector table would be on 0x08000000 (default).
But the first entry there is the StackPointer SP value (top address), not an instruction.

BTW: we have THUMB instructions: so, any address for a function call is odd (bit 0 of new PC value must be set!). An address 0x00000000 (or even 0x20000000) will result in a HardFault_Handler because the address is not odd. This bit 0 set means: call as THUMB code. And CM3, CM4, CM7 do not have ARM instructions (where bit 0 would be cleared).

BTW: mbed moves the vector table to DTCM RAM, 0x20000000. When your FW runs, the vector table is relocated to 0x20000000 (DTCM).

Even, if you would call the ResetHandler vector properly (via taking address from offset +4, the second entry in vector table), it cannot work!: the entries in Vector Table are just addresses, not instructions. You had to read the second entry, take the address from there, assign this address read to a function and than call it. But never jump to a Vector Table location directly.

Even I would not do so: you could execute (again) the ResetHandler but it does not reset really MCU: anything in MCU, including to change the mode (MSP vs. PSP) is not done. And it would NOT load the SP from first entry in vector table. The SP register would remain at it is. Calling a ResetHandler again without really to reset MCU will potentially have issues (e.g. HW devices are not reset, in active running mode).

The better way is what you do: call the MCU SystemCtrl register to force a MCU reset.
Just instead to go to this register - the easier way is to use the CMSIS function provided:

NVIC_SystemReset();

In the document I linked to, I found the following line: "On reset, the processor loads the PC with the value of the reset vector, which is at address 0x00000004." I think at least this is reflected in the code snippet (the value at address 4 is assigned to a function pointer).

But if I understand you correctly, the actual location of the vector table is not at address 0?

Correct: default vector table is at 0x08000000 (start of FLASH ROM), never on 0x00000000 (ITCM which is empty).
But vector table can be moved to somewhere else and the bootloader DOES!
So, the vector table is at 0x20000000 (DTCM) when your sketch code is running.

What you have found is correct: but it means: there was really a hardware RESET signal asserted so that the ResetHandler vector is activated. via this real RESET signal generated - all this happens as you have mentioned, esp. to read the first entry for the SP value to set SP to "top of stack".

If you call the ResetHandler just by yourself (find below code how you "could" do), nothing is reset! And the SP is not reset! It is NOT a hardware trigger interrupt for Reset (you just execute the code of ResetHandler like a regular function but MCU does NOT do any other Reset Actions like resetting the mode, reload the SP ... (via hardware logic triggered, you would not trigger HW by calling just the code).

Here, how you could figure out where your VectorTable is (and how to call the ResetHandler there). But do NOT do it: use the CMSIS NVIC_SystemReset() or what you did with the SCB register (the same, the correct way). This generates a real (internal) HW signal for Reset (for the MCU and acts like a real hardware reset).

		typedef void (*ResetHandler)(void);
		ResetHandler pResetHandler;
		char s[80];
		unsigned long *vectTable;

		vectTable = (unsigned long *)SCB->VTOR;				/* find out where the current Vector Table is */
		sprintf(s, "Vector Table at: %0lx", vectTable);
		Serial.println(s);

		pResetHandler = (ResetHandler) *(vectTable + 4);	/* read and assign the address for ResetHandler entry */
		/* remark: this address should be odd, bit[0] = 1, for THUMB code
		*  print the entry address for ResetHandler()
		*/
		sprintf(s, "ResetHandler: %0lx", (unsigned long)pResetHandler);
		Serial.println(s);

		/* call the ResetHandler() - but DON'T do it without a real MCU core reset!
		*  all configured devices remain active, cannot be reconfigured,
		*  and the SP is not reset back to Top Of Stack!
		* instead: use NVIC_SystemReset() - it will do all the MCU reset
		* bear in mind: it will just reset the MCU and internal stuff, not any external devices, e.g.
		* when RESETB signal on board is used, e.g. to reset USB, ETH controllers - they are NOT reset!
		*/
		//pResetHandler();

Bear in mind:
this (internally) generated RESETB signal (for MCU) is similar like the external RESETB signal on board. But this external RESETB signal does not toggle (on the outside of MCU)!
So, if your board uses on other external components, e.g. USB-C controller, ETH controller, PMIC ... the real BOARD Reset signal - they are NOT reset. The MCU can only hard-reset itself, but not external chips needing a real "reset button pressed" and external RESETB signal would toggle (it does not).

Thanks for the clarification, it has been quite educational.

So, in principle the following would be the Portenta equivalent of void (*reset)(void) = 0; assuming that the vector table is always relocated to the same address?

void (*reset)(void) = (void (*)(void))*(size_t*)0x20000004;

You explained that calling the reset vector is not a good idea. I guess that this also applies for other boards, so should the use of void (*reset)(void) = 0; be discouraged altogether?

Just calling ResetHandler does not do anything with RESET. It just executes the code like any other function.
But the correct way to "activate" the ResetHandler is to generate a HARDWARE Reset signal. And this SCB register bit does: it does not call anything, it just generates the internal RESET hardware signal. And this results in activating the ResetHandler.

And this real internal hardware Reset signal does several actions:

  • reset internal MCU core stuff (e.g. clear all INTs, disable INTs, disable all internal devices, clear all FIFOs ...

  • read 32bit address from first entry in VectorTable and assign it to SP register (PSP, I guess, there is also a MSP, two stack pointers), change mode to privileged (not handler, the other SP)

  • set the PC register to the address read from second entry in VectorTable (+4).

The VectorTable has just addresses (32bit) - not executable code! All the handlers are referenced from VectorTable via EntryAddress itself, not via a branch instrcution.

And: if you check the entries for all the Handler Addresses in VectorTable: all are there with bit[0] set to 1: all addresses are odd (even the code starts on the even address). This is indication for THUMB instructions (not ARM instructions).

So, if you print the address of any function, even ResetHandler - the bit[0] of this function entry address is always 1 (odd), even the code starts on the even address (code fetch is aligned to even addresses and potentially also to 4-byte aligned entry address).

Sorry, this C-code example looks so complicated: it it ends up still in "jumping/calling address 0x20000004" - it will NOT work!
As mentioned: there is an address which points to the entry, but it not the start/entry of the function.

And as mentioned: when resetting MCU - it means to generate really a hardware signal, not just calling any code.
The only way to generate this signal is via this SCB register. Calling code does not generate any hardware signal.

It will jump to the address stored at 0x20000004, it does not jump to 0x20000004 itself.

But your message is clear, this is not the right way to do a reset.

BTW: yes - the same for any other board, even any other architecture: the ResetHandler is an INT handler for an external interrupt signal. And the Reset signal is an external HW INT signal, like any other external interrupt, e.g. EXT_IO, NMI, ...
You have to "generate" this HW signal and only such SCB register will do (for internal HW signal generation).

You could also create an external HW reset logic, e.g. writing a GPIO to 0 and this is connected to external RESETB signal. So, you set the GPIO which will reset via real external RESETB signal.
It works exactly in the same way, except:
now you could reset also all other stuff on board (BOARD Reset signal toggles),
just to make sure this external signal never get stuck with value 0 still driven: you would not come out from RESET anymore).
But assuming the GPIO, which you drive and connect with external RESETB signal, is reset as well and becomes floating - a pull-up to high would "release" the external RESETB signal.

You can try to experiment with using a GPIO as output, connect it with the RESET signal on a header, the board - but have a pull-up there - and set this GPIO to 0: it will do the same hardware reset.

Then perhaps the commonly advocated software reset should be discouraged? Maybe we should discuss this in a separate thread.

Take it this way: "Reset is an external INTERRUPT signal toggling to 0 (and released to 1 after reset pulse generation)". And the ResetHandler is only triggered (and should only be triggered) by toggling this external signal.
The MCU provides to you a SCB register which will do exactly this HW signal generation (but just internally for all stuff in MCU itself, but doing the same as the external Reset signal).

Trigger ResetHandler just via toggling a real Reset Signal (which activates the ResetHandler Vector Table entry like any other external interrupt. Reset is just like any other "regular" interrupt triggered by an external signal, just doing a more internally when this specific hardware signal toggles.

Good point: "soft" and "hard" reset:
Often, and I would also, consider: writing SCB register is more like a "soft" core interrupt (even it behaves inside MCU like a real HW interrupt).
But it is not "hard" because: it does not reset the external stuff (just the MCU itself). External components, chips on board ... are not reset.

Reset logic is pretty complex in such chips. For instance: you can also force a "soft" reset on devices, peripherals, e.g. reset the SPI device, the CAN controller, w/o to reset other devices or the software.

But this SCB "Core Reset" combines all internal reset signals. It resets all inside the MCU. Let's call it "Hard Core Reset".
But a real " Hard System Reset" can only be done via toggling the board RESET signal.
And a "Soft Core Reset" can be there as well (depends on MCU), e.g. reinitialize just the software stuff, but do not reset devices, peripherals, like a SW-startover.
A watchdog could be setup to do such a "soft reset", e.g. restart just the FW, but also trigger a "hard core reset".

A real "hard reset" is using GPIO and external RESET signal. Anything else is a bit more "soft"., or call it just "core reset" (does not include "system reset").
And just calling the ResetHandler code as a regular sub-function is "very soft" (and will potentially fail, e.g. devices must be reset first before they can be changed again).

Sorry for being "very educational"
(why not? No need to read if "you" do not like and do not have time).

The Arduino sketch Arduino hard reset you have mentioned - my comments on it:

  • Yes: you could use a GPIO, connected from MCU to reset signal on board: it will reset all, entire board, when SW sets this GPIO to 0 (Reset is usually low active).

  • But make sure, there is a need for a pull-up: when GPIO out is 0 and it forces a reset: the GPIO itself becomes floating (due to reset). But if no active pull-up - this external reset signal may stay at low level. And you never come out of reset again (or very flaky behavior).

  • The length of this reset low pulse depends on the (internal) reset timing of the MCU generating this GPIO reset signal. Problem is: some external chips on board might need a minimal reset pulse (where signals stays 0 for period of time). Chips differ a lot on this required Reset Pulse generation: some need a longer period, some a shorter.
    Worst case is: the MCU setting this signal to 0 (for a System Reset) is so fast that the GPIO pin out of MCU becomes floating after the reset seen so fast. So, the pulse might be very short (the MCU resets itself very fast, also this GPIO output). Even possible that reset becomes floating and pull-up-ed to high so fast that even this period violates the MCU spec.
    Especially other chips want to see much longer reset 0 period. Potentially, external chips are not properly reset (period too short, the "MCU reset timing" violates other chip requirements).

Also to think about how everybody would write the code for a ResetHandler:
Usually they assume: this ResetHandler code is called after and only a real HW reset, on power up (which is the same HW reset condition). So, all is reset, all internal stuff, e.g. devices, are in "reset default state", e.g. disabled, all cleared.

Some registers in devices (peripherals) are only possible to write when the device is in disabled (or reset default) state. So, you would not code to disable a device - you would assume after a reset the device is disabled: no need to "soft" reset or disable something.

So, your ResetHandler tries to initialize the device (again) and deviice has to be disabled (like after reset). But you do not care: you assume after a real reset it is disabled and all fine.
Your code tries to write to a register which becomes write-able just in disabled mode (after a real reset).

But when you call the same code, the ResetHandler, as a sub-function: this device was not reset. It is still enabled and active, even acting on data, interrupts coming in etc. So, the same code will fail: it cannot change a register because the device is still in use. Calling again ResetHandler as a function will will fail when there was not a real reset done.

You could assume the opposite: assume there was not a reset but I want to reconfigure all stuff again. Therefore, before I can do (in my code) - I had to "soft" reset, at least to disable a device I am touching. And you had to do on all "resources" you want to re-enable: you had to assume it was not reset, therefore disable, "soft reset" before re-configuring it again.

But imagine what an effort!: reset all manually before to configure again. A big difference because after a real reset it is not needed: you save a lot of steps and code. And just for the case that somebody calls the ResetHandler again? (who would do? would you test your code for this very rare condition?) I had to add so much code, to disable, to do a "soft" reset on the devices before I try to reconfigure those (again). Nobody would write such "reentrant" ResetHandler code when we assume: this code is just and always running after a real (core) reset, and only once.

So, potentially, I would state: "a ResetHandler is not reentrant. A ResetHandler cannot be called again just as a regular function. A ResetHandler has to be activated just and after a real RESET happened. Only a real Reset signal should activate the ResetHandler."

So, calling the ResetHandler again from FW, never mind where it is - does not make sense. The condition for what the code in reset handler was written - esp. assuming all is properly reset and disabled - makes it impossible to execute the same code again, from a running system. It would "violate" some "assumptions", e.g. that the devices are in "reset state" and "disabled". Potentially, on all systems, it will fail to call again ResetHandler (as a sub-function).

Doing a "core reset" means really to generate a HW reset signal (and this triggers to call the ResetHandler again, nothing else).
Just calling code does not do anything with HW signals, neither generating a reset signal "needed".

I would never try to call ResetHandler again: just look for the hooks if you can force a real HW reset. Most of the MCUs should have a mean to do so. If not: you need an external reset logic, driven by the MCU.

And even you would do just a "soft" FW reset (restart it) - bear in mind some actions needed, e.g.: "when and how is my SP register set?" If it is coded, an instruction there to set SP - fine. But if you rely on reset logic and you "assume" the SP is set by/after doing a reset - it is not "thread-safe", not "reentrant" when calling such code (not setting again the SP via instructions and you "eat up" on every "soft" reset the stack until it crashes).

It was not meant as criticism, I really appreciate how you explained the inner workings of the bootloader and how to execute a proper reset procedure.

Thank you for the very informative answers @jfjlaros and @tjaekel .
So to summarize what was said on this thread, calling the reset handler is a very bad way of resetting a MCU. It won't reset any internal register and will only restart the code, it can produce unpredictable results.
Writting directly to the register like I did or using the CMSIS NVIC_SystemReset() function will produce a MCU reset but will probably not reset other components on the board.
Using a GPIO pin that is connected to the reset pin of the board while also being pulled-up will produce a real HW reset (just like pressing the reset button). But depending on the MCU specs the generated signal might be too short to properly reset the board.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.