Skip to main content
rclar.6
Associate III
July 24, 2025
Question

Auto STM32IDE generated code leaving VTOR at 0x00000000 causes unhandled interrupts

  • July 24, 2025
  • 5 replies
  • 1047 views

Trouble Report: STM32G474 FD CAN RX Interrupt Causes HardFault Due to VTOR Left at 0x00000000

Platform Details
----------------
- Microcontroller: STM32G474VETx
- IDE: STM32CubeIDE
- Version: 1.16.0
- Build: 21983_20240628_1741
- Startup project: Standard CubeMX-generated application with FDCAN peripheral using interrupt-based RX
- RTOS: None (bare metal)
- Debugger: ST-LINK / OpenOCD

Problem Summary
---------------
When enabling FDCAN RX FIFO0 interrupts via HAL_FDCAN_ActivateNotification() on STM32G474VETx,
the system crashes immediately with a hard fault (0xFFFFFFF9) on the first received CAN message.

After the fault, resetting via the debugger fails to return to main(). On inspection, SCB->VTOR is
found to be 0x00000000, which causes all vector table lookups to point to incorrect (and un-programmed)
memory, resulting in undefined interrupt dispatch.

Technical Findings
------------------
- FDCAN1_IT0_IRQHandler() is correctly defined and linked (nm confirms symbol is present and in vector table).
- HAL_FDCAN_ActivateNotification(...) returns HAL_OK.
- HAL_FDCAN_ConfigFilter(...) is used correctly with valid filter indexes (≤ ExtFiltersNbr - 1).
- Only extended ID messages are used.
- StdFiltersNbr = 0, ExtFiltersNbr = 4, as required.
- Interrupt is not handled; system faults without entering handler.
- Debugger shows return address 0xFFFFFFF9 — invalid exception return due to unhandled interrupt.
- After crash, SCB->VTOR is 0x00000000. This is incorrect — the vector table lives at 0x08000000.

Workaround
----------
Manually setting SCB->VTOR = 0x08000000; at the top of main() resolves the issue and restores correct interrupt behavior.
After applying this fix:
- The CAN interrupt is handled correctly
- Callback is invoked as expected
- System remains stable
- Debugger reset behaves correctly

Suggested Root Cause
---------------------
CubeMX-generated startup or system init code for STM32G474 may not explicitly set SCB->VTOR to the
correct Flash base (0x08000000). If the CPU starts with VTOR = 0x00000000 (e.g., after soft-reset or
via debugger), the vector table lookup fails, leading to:
- IRQs jumping to invalid addresses
- Crashes on first interrupt
- Unrecoverable core state even after reset

Recommended Actions by ST
-------------------------
1. Ensure that CubeMX and HAL startup code always initializes SCB->VTOR explicitly, even in single-image,
no-bootloader projects.
2. Consider adding a warning or assertion if SCB->VTOR is zero at main().
3. Update documentation for FDCAN usage to mention VTOR importance if not using a bootloader.
4. Provide CubeMX configuration option for VTOR setup under system settings.

Supporting Files Available on Request
-------------------------------------
- main.c, stm32g4xx_it.c, FDCAN_Config() source
- ELF map and nm dump confirming ISR linkage
- Screenshot or logs from debugger showing VTOR = 0x00000000

 

 

 

This resulted in a lot of re-coding the 'CANFD' and trying to fix this. With no interrupts enabled the code ran as expected and I could POLL and RX can . But as soon as the interrupts were enabled, on the first CAN message rxd it crashed. Nasty as well as the trashed vectors meant reset in debug did not work either

5 replies

InsignificantBit
ST Employee
July 24, 2025

Hello.

Thank you for this detailed report.

 

In older versions of CMSIS that ST used to provide, the function SystemInit() (called by the startup code prior to main() entry) would always initialize the VTOR to point to FLASH_BASE or SRAM_BASE (depending on project configuration). This was causing some other issues issues and was later removed, so the VTOR currently indeed is not initialized during startup and left at reset value of 0 - this was intentional change.

 

This, in most cases, doesn't constitute a problem, since, depending on the Boot mode, either Flash, system memory (bootloader), or SRAM is cloned/remapped to the base of the address space (0x0), so that the NVIC can see a copy of the program, including the vector table, at 0x0.

 

I have a strong suspicion that your underlying problem is that for whichever reason, different address gets remapped to 0x0 than your application. It is possible that e.g. SysTick handler is being handled correctly, just not by the handler in your application, hence the application doesn't crash at first SysTick ISR. Most typical suspect is the "Boot from bank 2" BFB2 option bit. Please verify that your option bytes differ from factory values only in ways you intend.

 

If the change to the boot address is intentional, you can either do this by uncommenting the definition of #USER_VECT_TAB_ADDRESS in system_stm32g4xx.c (and potentially change its address too), or just rewrite the VTOR at the beginning of main().

 

If you still suspect that this is caused by the way the project is generated, please let us know more.

rclar.6
rclar.6Author
Associate III
July 24, 2025

I still think this is wrong. Bare metal designs used to work correctly
with code automatically generated. I think that many people will spend time
scratching their heads because they will (naturally assume) that the
interrupt code they have just implemented has some kind of bug in it, not
that ANY new interrupt will be unhandled. So sorry I disagree. This has the potential to cause a lot of user frustration and wasted time. I think this
is a BUG.

InsignificantBit
ST Employee
July 24, 2025

I understand your frustration. It is not a problem that users should find themselves having unless they started doing nonstandard modifications to the hardware and project settings. It most certainly isn't anything that should happen with a generated project with hardware in factory state.

First of all, I would like to verify, whether this is the former or the latter. Can you please check the hardware configuration (mainly option bytes and boot pins) as I've written previously? I would like to identify the root cause here, only then we can tell whether it's plain bug in the toolchains, or a misconfiguration (potentially due to some problem in documentation).

As I've explained previously as well, having the VTOR left at 0x0 is absolutely intended and should not by itself cause any of the behavior you have experienced.

Please do let me know whether anything I've written helps you narrow down the root cause.

 

(Personally I also think that startup code should initialize the VTOR to point to an address taken from the linker script - this is possible, but requires three different #IFDEF'd codes for the three supported toolchains. For various reasons, this is not what's done. That's not the underlying cause of this issue, though.)

 

 

rclar.6
rclar.6Author
Associate III
July 24, 2025

Screenshot from 2025-07-24 14-43-01.png

 

This is not booting from BANK 2. But the VTOR is zero to start with

InsignificantBit
ST Employee
July 24, 2025

Can you please check the MEM_MODE and FB_MODE values in the SYSCFG_MEMRMP to see which part memory is being mapped to 0x0?

 

rclar.6
rclar.6Author
Associate III
July 24, 2025

Screenshot from 2025-07-24 15-39-46.png

 

Hope this is what you wanted. The function was called as first function in main (i.e. no HAL config calls yet)

InsignificantBit
ST Employee
July 24, 2025

That's pretty strange, MEM_MODE == 0 and FB_MODE == 0 should mean that main Flash memory at 0x08000000 should be mapped onto 0x00000000 and changing VTOR from 0x0 to 0x08000000 should make no difference.

Did you enable the clock for SYSCFG before reading it? That would be RCC_APB2ENR_SYSCFGEN. Without the clock, it would always read as zero.

If it actually contains zeroes (you can make sure by checking whether you're able to toggle the bits using the debugger), can you re-check that the problem exists when VTOR=0 and gets fixed when VTOR=0x08000000, and if yes, check whether you see the same or different memory content at memory 0x0 and 0x08000000?

rclar.6
rclar.6Author
Associate III
July 24, 2025

Screenshot from 2025-07-24 16-20-01.png

Ok this does not appear to be configured. Where would I find this in cubeMX part of stm32ide?

 

InsignificantBit
ST Employee
July 24, 2025

SYSCFG clock gets enabled by HAL_Init() by default, but if you want to access the registers prior to that, you just write 1 to that bit. There's also macro __HAL_RCC_SYSCFG_CLK_ENABLE() to do that.

rclar.6
rclar.6Author
Associate III
July 25, 2025

OK I had BOOT0 floating (I thought this might have been the problem) but I have now 10k to ground on this. I still need to set SCB->VTOR to 0x08000000 to get the interrupts working.