STM32G4: Jump to bootloader mode Bus Fault (Makefile) - Page 1
Forum section: electronics / general

author: betocool
author_rank: Regular Contributor
timestamp: May 29, 2024, 12:26:38 am
content: Hi all,I'm working on an STM32G491 processor, and one of our requirements is to have a UART bootloader. No biggie, the controller has a UART (and several other) bootloaders in the system memory. In the past I've used it with USB and DFU and it works alright.The development environment is the usual STM32CubeIDE with initialising over CubeMX. I'm using FreeRTOS as well, but I was able to jump into bootloader mode by using this code (reference only):Code:#include "bootload.h"const uint8_t bootload_cmd = BOOTLOAD_COMMAND;void (*JumpToApplication)(void);void bootload_enable(void){/* Disable LPUART1 */HAL_UART_DeInit(&hlpuart1);HAL_UART_DeInit(&huart1);HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_3;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* Send system to sleep */vTaskSuspendAll();vPortRaiseBASEPRI();/* Disable all interrupts */__disable_irq();/* Disable Systick timer */SysTick->CTRL = 0;/* Clear Interrupt Enable Register & Interrupt Pending Register */for (uint32_t i = 0; i < 8; i++){NVIC->ICER=0xFFFFFFFF;NVIC->ICPR=0xFFFFFFFF;}/* Re-enable all interrupts */__enable_irq();/* Set the main stack pointer to the boot loader stack */__set_MSP(*(uint32_t *)BOOTLOADER_ADDRESS);/* Jump to system memory bootloader */JumpToApplication = (void (*)(void)) (*((uint32_t *) ((BOOTLOADER_ADDRESS + 4))));JumpToApplication();while(1);}Anyway, the code works.A few days later I decided I want to switch to VSCode for coding, compiling and flashing. I followed a few instructions online and it's not as hard as it seems. CubeMX generates a Makefile of the project, what could possibly go wrong?Well, I had to adapt most of the flags between what the IDE was doing and what the makefile was telling the compiler. I "think" I managed most of it, but to be fair I might have missed the odd flag here or there. So or so, after a few trial and error I was able to run my application OK. Everything worked as expected, and compilation, flashing, etc worked well.At some point I tested again the "enter bootloader" mode, but that failed miserably. After a lot of testing and some debugging later I found out that the micro went into HardFault mode, and it happened just by trying to enter the "JumpToApplication()" address. The complaint was a BusFault error (I think, I don't have the code open anymore).I went back to compile with the IDE, and lo and behold, it all works fine again. So for the time being I'm coding in VS code and using a 1.7GB IDE to click the compile and flash buttonsI'm guessing that I'm missing some flag, option or something that tells the micro to allow jumping to that address, but from what I've looked (not extensively) I cannot find the difference between the Makefile flags and the IDE flags. At some point I even copied them verbatim. I might have missed the assembler flags. As additional information, I'm using the arm-none-eabi-gcc binaries that are part of the IDE (plugin.blablabla.stmarm....) and I am flashing using the ST-Link V2. For flashing with the makefile I used:Code:flash:STM32_Programmer_CLI -c port=SWD freq=4000 -w $(BUILD_DIR)\$(TARGET).elfHave you guys had issues with that before? Any information where I should be looking at is appreciated. It's not a dealbreaker fortunately, more like an annoyance...Cheers,Alberto

author: eutectique
author_rank: Frequent Contributor
timestamp: May 29, 2024, 12:51:18 am
content: First, you should not re-enable the interrupts. You came to the point of no return, setting the new stack, and the last thing you would want is an interrupt.Then, is there any difference in generated assembly code?Also, step through assembly instructions in the debugger. Is there anything unexpected?A better option -- use AIRCR.An even better and cleaner hardware reset, as opposed to just jumping to reset vector, is watchdog reset. Your peripherals would be in a known state.

author: ataradov
author_rank: Super Contributor
timestamp: May 29, 2024, 01:18:53 am
content: Yes, use AIRCR.Separate __set_MSP() and JumpToApplication() is not the best idea and the results may vary depending on compiler optimization settings. You should really do that in an atomic assembly section, since you can't use the old stack after __set_MSP().

author: betocool
author_rank: Regular Contributor
timestamp: May 29, 2024, 11:55:28 pm
content: Thanks, I will look into AIRCR, I'm not aware of what it is to be honest.I will also try out your other suggestions. I did try going the Reset route reading backup registers, but for some reason that didn't quite work for me. Might try it again sometimeCheers,Alberto

author: ataradov
author_rank: Super Contributor
timestamp: May 30, 2024, 12:20:42 am
content: Assuming you are using standard CMSIS harder files for the core peripherals, then you have a convenient function defined - NVIC_SystemReset(). Just call that, it will write the AIRCR.

author: betocool
author_rank: Regular Contributor
timestamp: May 30, 2024, 02:22:20 pm
content: I realised that after I answered, I've been using NVIC SystemReset for a while.I haven't had more time to play around with it, so I'll put this in the back burner for a few days and have a look at the Makefile configuration later on.Fortunately things work this way still, so I'm not under pressure.Cheers,Alberto