Skip to main content
Explorer
October 23, 2023
Solved

STM32H735G-DK, cannot get response from CANFD ROM bootloader

  • October 23, 2023
  • 6 replies
  • 3870 views

I have an STM32H735G-DK and a sample CAN application that is working fine.  I am now trying to use the CANFD ROM bootloader in system memory but cannot get any response from it. All the jumpers and solder bridges on the DK are at their default configuration.

I have a Waveshare dual CANFD hat for a raspberry pi connected to the FDCAN1 pins (CN18). My test application works great with this setup, I can send and receive both CAN and CANFD messages all day long without issue. I am absolutely confident that there is no hardware issue at play here because the same hardware with my CAN application running in Flash works. Tested with bitrates from 125k to 1M, and CANFD data switching to 4M.

I have set the boot switch (SW1) from the normal "0 - Flash" to "1 - SYS MEM", and looking at both AN2606 as well as AN5405, it looks like I should be able to set the CAN bitrate to 500kbps and issue a standard CAN message with ID=0x0 to get a response (Section 3.1 in AN5405).

I do note that Section 2 states that Filter ID1 is 0x111 and also see Note 2 at the bottom of page 3 says that the MessageID and FilterID1 must match exactly, but even then I get no response from the ROM bootloader. I have tried different bit rates, standard CAN vs FDCAN frames, standard vs extended CAN IDs, different DLCs... I cannot get this to work. The raspberry pi is not seeing the message acknowledged, leading me to think that there is a missing step somewhere in the hardware initialization of the FDCAN1 peripheral, but since this is all in the system ROM from ST and my Flash is blank... that would be odd.

I have also noticed that AN5405 seems to be missing the "synchronization" section that the older (standard CAN) app note AN3154 has, where it says I should be sending CAN ID 0x079 with any DLC until I get an ACK response, but that also does not work.

I do not have any other hardware connected to the DK, so I am quite sure that the ROM bootloader is not accidentally triggering on the UART or SPI or USB bootloaders.

Does anyone have a *verified* setup, synchronization and communication example with the FDCAN bootloader in the system ROM of this device?

    This topic has been closed for replies.
    Best answer by torwier

    Same problem on NUCLEO-H563ZI.

    ROM Bootloader is accessable via UART and is able to download images.

    Setup host according to AN5405 FDCAN settings:

     

    ip link set can0 type can bitrate 250000 sample-point 0.75 sjw 16 dbitrate 1000000 dsample-point 0.75 dsjw 4 fd on berr-reporting on

     

     On sending request:

     

    cansend can0 000##0

     

     the following error shows up:

     

    ip -details link show can0
    9: can0: <NOARP,UP,LOWER_UP> mtu 72 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
     link/can promiscuity 0 
     can <BERR-REPORTING,FD> state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
    	 bitrate 250000 sample-point 0.750 
    	 tq 12 prop-seg 119 phase-seg1 120 phase-seg2 80 sjw 16
    	 pcan: tseg1 1..256 tseg2 1..128 sjw 1..128 brp 1..1024 brp-inc 1
    	 dbitrate 1000000 dsample-point 0.750 
    	 dtq 25 dprop-seg 14 dphase-seg1 15 dphase-seg2 10 dsjw 4
    	 pcan: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..1024 dbrp-inc 1
    	 clock 80000000numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 

     

     Thank you very much for hints and support.

    6 replies

    torwierAnswer
    Graduate
    October 30, 2023

    Same problem on NUCLEO-H563ZI.

    ROM Bootloader is accessable via UART and is able to download images.

    Setup host according to AN5405 FDCAN settings:

     

    ip link set can0 type can bitrate 250000 sample-point 0.75 sjw 16 dbitrate 1000000 dsample-point 0.75 dsjw 4 fd on berr-reporting on

     

     On sending request:

     

    cansend can0 000##0

     

     the following error shows up:

     

    ip -details link show can0
    9: can0: <NOARP,UP,LOWER_UP> mtu 72 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
     link/can promiscuity 0 
     can <BERR-REPORTING,FD> state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
    	 bitrate 250000 sample-point 0.750 
    	 tq 12 prop-seg 119 phase-seg1 120 phase-seg2 80 sjw 16
    	 pcan: tseg1 1..256 tseg2 1..128 sjw 1..128 brp 1..1024 brp-inc 1
    	 dbitrate 1000000 dsample-point 0.750 
    	 dtq 25 dprop-seg 14 dphase-seg1 15 dphase-seg2 10 dsjw 4
    	 pcan: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..1024 dbrp-inc 1
    	 clock 80000000numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 

     

     Thank you very much for hints and support.

    Graduate
    October 30, 2023

    Hi,

    Check the following:
    In order to have the FDCan2 available on the Nucleo board STM32H563 on the corresponding connector strips, the bridges SB29 and SB12 must be removed.
    This is described in document UM3115.

     

    jherrmann1_0-1698680857112.png

    My anwser of get Command.

    jherrmann1_1-1698681117095.png

    I hope this helps.

    Graduate
    November 2, 2023

    Thank you so much. This worked for me, although it might not solve the top post here.

    Graduate
    January 20, 2024

    @anotherandrew I have the same problem than you with STM32h735G-DK board. I tried to check if some hardware is interfering, to access the bootloader. I try to use another bootloader source like I2C or USART but only STlink/virtualCOM and  USB OTG FS are available without share PINes with another hardware (audio, lcd, ethernet).

    To use USB OTG and USART with the bootloader first I changed optional bits BOOT_ADD0(optionbyte) = 0x1FF0 and BOOT_ADD1(optionbyte) = 0x1FF0. I am still working in a solution for this issue. I hope that headache don't breaks me before!!! 

    Regards, If you found a solution, share it with us.

     

    Explorer
    February 7, 2024

    I have finally achieved success.

    # configure the CANFD driver on Linux:
    sudo ip link set can0 up type can bitrate 250000 sample-point 0.75 sjw 16 dbitrate 1000000 dsample-point 0.75 dsjw 4 fd on

    # monitor the CAN bus (in a different window/tab):
    candump -tdex can0

    # send a standard (11-bit) CANFD frame with BRS set, ID=000 and a 64-byte payload:
    cansend can0 000##100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

    # it is important to note in the command above the double-hash (##) which tells cansend that this
    # will be a CANFD packet, and then the single '1' before the 64-byte payload which sets the Bit
    # Rate Switch (BRS) switch in the outgoing packet.

    # observe the CAN traffic in the candump window:
    (179.707038) can0 000 [64] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    (000.001531) can0 000 [01] 79
    (000.000025) can0 000 [01] 0B
    (000.000009) can0 000 [01] 11
    (000.000009) can0 000 [01] 00
    (000.000007) can0 000 [01] 01
    (000.000008) can0 000 [01] 02
    (000.000008) can0 000 [01] 11
    (000.000008) can0 000 [01] 21
    (000.000008) can0 000 [01] 31
    (000.000999) can0 000 [01] 44
    (000.000012) can0 000 [01] 63
    (000.000006) can0 000 [01] 73
    (000.000005) can0 000 [01] 82
    (000.000006) can0 000 [01] 92
    (000.000006) can0 000 [01] 79

    I can issue various commands, including the ID=0 but with no payload at all and get the expected response.

    My CAN network is a little more complicated; I have the STM32H7 of course, a Waveshare "dual CANFD hat" connected to a raspberry pi 4 (where I am executing the commands above), and two CAN (not CAN-FD) monitors for other equipment. The network is terminated with 120R on the ends of the network.

    Visitor II
    February 3, 2025

    @anotherandrew 
    I am writing a can bootloader with a custom board same as the the STM32h735-disco with clocks and power etc. I have my application running and CAN working (nom =250khz,data=1Mhz canfd with brs). I have also tested and validated the CAN works as it echoes back any data it receives. The CAN CLK is HSE muxed out at 25Mhz. However, when I jump to the RoM bootloader from the application, I do not get any response from CAN that was working in the application. I have spent time trying to figure out why.

    I then physically set the Boot pin to "1" and when I send the same CAN data the Rom bootloader responds. This leads me to think that I am not doing something right within my "jumpto bootloader" function and the device may not be properly configured prior to jumping to the boot RoM. Will you be kind enough to show what housekeeping you did in your jumpto bootloader function? See my current implementation. 
    Regards

    static void JumpToSystemBootloader(void) {
     vTaskEndScheduler();
     __disable_irq();
     SysTick->CTRL = 0;
     for(int i=0; i<96; i++) HAL_NVIC_DisableIRQ(i);
    
     // Switch FDCAN clock to HSI first
     // No need to reconfigure FDCAN clock source. Bootloader will handle clocks.
     HAL_FDCAN_DeInit(&hfdcan1);
     HAL_GPIO_DeInit(GPIOH, FDCAN1_TX_Pin | FDCAN1_RX_Pin);
    
     // Reset all GPIO ports
     __HAL_RCC_GPIOA_FORCE_RESET(); __HAL_RCC_GPIOA_RELEASE_RESET();
     __HAL_RCC_GPIOB_FORCE_RESET(); __HAL_RCC_GPIOB_RELEASE_RESET();
     // Repeat for other ports...
    
     // __HAL_RCC_FDCAN_FORCE_RESET();
     // __HAL_RCC_FDCAN_RELEASE_RESET();
    
     // // 4. (Optional) Deinit USB if interfering
     HAL_GPIO_DeInit(GPIOA, USB_FS_DM_Pin | USB_FS_DP_Pin | USB_FS_ID_Pin | USB_FS_VBUS_Pin);
     HAL_GPIO_DeInit(GPIOH, USB_FS_PWR_EN_Pin | USB_FS_OVCR_Pin);
     __HAL_RCC_USB_OTG_HS_FORCE_RESET();
     __HAL_RCC_USB_OTG_HS_RELEASE_RESET();
    
    
     // Force HSI as system clock
     HAL_RCC_DeInit();
     __HAL_RCC_HSI_ENABLE();
     while(!__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY)) {}
    
     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
     RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
     HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);
    
     HAL_Delay(200); // Extra stabilization delay
    
     SCB_DisableICache();
     SCB_DisableDCache();
    
     SCB->VTOR = 0x1FF00000U;
     __set_MSP(*(__IO uint32_t*)0x1FF00000U);
     void (*bootloader)(void) = (void (*)(void))(*(__IO uint32_t*)(0x1FF00000U + 4));
     bootloader();
    }

     

    Explorer
    February 3, 2025

    I won't debug your code but I do know that the ROM bootloader makes a number of assumptions about the state of the peripherals. It's important to either make sure all the peripherals you used are set back to reset state or do something much simpler: use the watchdog timer to reset the chip and then early in your code detect this and jump to the ROM bootloader. Your code looks like you're doing the right things but it would take a lot of effort to verify that you've got everything, which is why I advocate the reset method instead.

    I typically shave off 16-64 bytes from the very top of RAM in the linker script and then declare a volatile structure there that I check on boot. If it has magic values, I jump to ROM, otherwise I initialize the area and boot my application normally. When it's time to enter the bootloader, I set up the magic values in the structure, disable interrupts and loop waiting for the watchdog to reset me.

    This code fragment doesn't declare a struct, it just writes two magic values and checks for them, but the idea is the same:

    #define REBOOT_SENTRY_ADDR ((volatile uint32_t *)0x38003ff8);
    #define REBOOT_SENTRY_VAL1 (0xaa55aa55)
    #define REBOOT_SENTRY_VAL2 (0x55aa55aa)
    #define ROMBL_ENTRY_POINT ((uint32_t *)0x1ff09800);
    
    static void _jump_to_rombl_maybe(void)
    {
     volatile uint32_t *mem;
     uint32_t val1, val2;
    
     /* read the magic words and invalidate them no matter what */
     mem = REBOOT_SENTRY_ADDR;
     val1 = mem[0];
     val2 = mem[1];
     mem[0] = 0x00000000;
     mem[1] = 0x00000000;
    
     if (val1 == REBOOT_SENTRY_VAL1 && val2 == REBOOT_SENTRY_VAL2) {
     void (*rombl)(void);
    
     mem = ROMBL_ENTRY_POINT;
     rombl = (void (*)(void))(mem[1]);
     __set_MSP(mem[0]);
     rombl();
     }
    }
    
    __attribute__((noreturn)) void main(void)
    {
     _jump_to_rombl_maybe();
    
     HAL_Init();
     SystemClock_Config();
    
     /* ... */
    }