Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Buttonless DFU works on dev kits but not on custom board

Hello,

I’m using the secure bootloader for serial DFU (UART, not USB). To enter bootloader mode from the application, I write 0xB1 to GPREGRET, then call NVIC_SystemReset(). On the dev boards I have (PCA10056 and Particle Boron), this works to reboot into DFU mode. However, on my custom board, it doesn’t work. The device appears to boot immediately into the application code.

I have made a custom board file that looks like this

#define LEDS_NUMBER    2

#define LED_1          NRF_GPIO_PIN_MAP(0,15)
#define LED_2          NRF_GPIO_PIN_MAP(0,18)
#define LED_START      LED_1
#define LED_STOP       LED_2

#define LEDS_ACTIVE_STATE 1

#define LEDS_LIST { LED_1, LED_2 }

#define LEDS_INV_MASK  LEDS_MASK

#define BSP_LED_0      NRF_GPIO_PIN_MAP(0,15)
#define BSP_LED_1      NRF_GPIO_PIN_MAP(0,18)

#define BUTTONS_NUMBER 0

#define RX_PIN_NUMBER  NRF_GPIO_PIN_MAP(0,23)
#define TX_PIN_NUMBER  NRF_GPIO_PIN_MAP(0,24)
#define CTS_PIN_NUMBER NRF_GPIO_PIN_MAP(0,30)
#define RTS_PIN_NUMBER NRF_GPIO_PIN_MAP(0,31) 
#define HWFC           false

I’ve already read some advice including checking the LF crystal, and checking the UART pins. These don’t appear to be causing my problem, so I’m not really sure what to try next. I am open to debugging the bootloader, but I’m not sure how that will help since the device just enters the application code. Any guidance would be greatly appreciated

Thank you,

Carlos

Parents
  • Hi Carlos

    It could be that the bootloader start address is not added correctly to the UICR, which the bootloader will not run without. 

    In order to check this, could you run the following command when you have a debugger connected to your board, and let me know what you get in return?

    nrfjprog --memrd 0x10001014

    Best regards
    Torbjørn

  • Unfortunately I can't use a JLink device with this board. I would have to make a special breakout adapter for that. I flash/debug through an FT2232H with OpenOCD and GDB.

    When I read the contents of that register with GDB the result is:

    (gdb) p/x *0x10001014
    $3 = 0xf8000

    That matches the start address from secure_bootloader_gcc_nrf52.ld:

    MEMORY
    {
      FLASH (rx) : ORIGIN = 0xf8000, LENGTH = 0x6000
      RAM (rwx) :  ORIGIN = 0x20000008, LENGTH = 0x3fff8
      uicr_bootloader_start_address (r) : ORIGIN = 0x10001014, LENGTH = 0x4
      bootloader_settings_page (r) : ORIGIN = 0x000FF000, LENGTH = 0x1000
      uicr_mbr_params_page (r) : ORIGIN = 0x10001018, LENGTH = 0x4
      mbr_params_page (r) : ORIGIN = 0x000FE000, LENGTH = 0x1000
    }

    Here is the memory layout from the linker script I use for the application code:

    MEMORY
    {
      FLASH (rx) : ORIGIN = 0x1000, LENGTH = 0xff000
      RAM (rwx) :  ORIGIN = 0x20000008, LENGTH = 0x3fff8
    }

  • Hello,

    Torbjørn asked me to follow up on this.

    Thanks for confirming the that the bootloader start address was set correctly. The MBR knows a bootloader is present when this register is set, see Master boot record and SoftDevice reset procedure for details. So this means the bootloader should executing  before the application on startup. The problem might be that it fails to enter DFU mode for some reason.

    Could you try to start a GDB session with the bootloader project and see if it reaches main()?

    Edit: you may also want to place breakpoints in the fault handlers in main.c to catch any code assertions.

  • Sure, I have a question about this though. I read on a different thread that I would have to turn off optimization for the bootloader, and this would make it too big to fit in flash at the address designated by the linker script. What address should I place the bootloader at for this to work with optimization set to 0?

    Also, for the breakpoints do you recommend using NRF_BREAKPOINT?

Reply
  • Sure, I have a question about this though. I read on a different thread that I would have to turn off optimization for the bootloader, and this would make it too big to fit in flash at the address designated by the linker script. What address should I place the bootloader at for this to work with optimization set to 0?

    Also, for the breakpoints do you recommend using NRF_BREAKPOINT?

Children
  • It’s not a requirement to turn off optimization for more simple debugging like this, so I’d suggest to leave it as is for now. 

    For breakpoints I would recommend to use the breakpoint command in GDB. E.g. breakpoint main.c:<insert line number> Or breakpoint main to break on main(). Using the NRF_BREAKPOINT macro is also an option, but note that it will halt the CPU even when you are not debugging.

  • I put a 10 second delay in the beginning of main() so that I would have time to attach the debugger after sending the command to boot into DFU mode. In this way I have discovered that the bootloader does make it into the main() function. I will see tomorrow if I can find out more

  • Well, I have discovered something interesting.

    After I send the reset into DFU command to my device, I can verify that GPREGRET has the correct contents in the following way

    (gdb) p/x *0x4000051c
    $1 = 0xb1

    However, when my device then resets into the bootloader, I run the same command to check the contents of GPREGRET, right at the beginning of main(), and it appears the value has been erased or overwritten

    (gdb) p/x *0x4000051c
    $1 = 0x0

    The documentation states that retained registers will only be overwritten on power cycle or brownout. I've also discovered that the RESETREAS register is set to 0 after the reset, which suggests that the device might be browning out or power cycling?

    (gdb) p/x *0x40000400
    $3 = 0x0

    However, I'm seeing that NVIC_SystemReset() doesn't actually write anything to RESETREAS, so it makes sense that value would be 0 as far as I can tell. So I'm not really sure why GPREGRET is being overwritten, especially since it doesn't seem to happen on the other boards

  • The dfu flag in GPREGRET gets cleared by the bootloader when it reaches the dfu_enter_flags_clear() function in nrf_bootloader.c after entering DFU mode, so this is probably to be expected.

    Based on your findings thus far, I suspect the problem must be an error occurring in the bootloader after entering DFU mode that leads to the device being reset again. Maybe something related to the UART pinout considering the problem only happens with your board. Could you try to place a breakpoint in nrf_dfu_serial_uart.c::uart_event_handler()::NRF_DRV_UART_EVT_ERROR and see if it gets hit?

  • I tried what you've suggested, and the breakpoint was never hit. Also, in my application code I use UART to send the command to switch to DFU mode. I have that set up as the same pins as what I designate in the custom.h board file I wrote for my board.

    Also, I don't believe that the bootloader is code is running, then resetting, then running again. I have inserted in the first line of the bootloader's main function: nrf_delay_ms(5000);

    When I power cycle my device, the application code loads after about 5 seconds (indicated by an LED). If it was resetting from the bootloader (so bootloader code is essentially running twice), I would expect that when I reset the device using the ENTER_DFU command I send to my application code, I would have to wait 10 seconds for the application code to start running. But the application code will load after 5 seconds, even when I am using the serial command.

    Is there some minimum hardware requirements in terms of how many LEDs must be designated in the board file for the bootloader to work? My board only has 2 LEDs and implementations on the dev kits I have seen use more LEDs than that (I have done this with PCA10056 and the particle boron which has more than 4 LEDs

Related