1
\$\begingroup\$

I'm going through my company's code for a Bluetooth remote that uses a bootloader to download the main application over Bluetooth. The remote is pre-programmed with the bootloader application. Once the main application is loaded, the remote boots from the main application, unless there is a specific command to jump back to bootloader.

I'm new to bootloaders, so I've been trying to understand how it works. So far I'm quite confused. My main points of confusion are:

  1. Why is there a need to remap the vector table in the main application? I've seen a post on this, but I'm still not satisfied with the answer. How does the remap enable differentiation of the ISRs between that of the bootloader and main app? I include here a snippet of the remapping portion of the code:

    void remapMemToSRAM(void)
    {
        uint32_t vecIndex = 0;
        __disable_irq();
    
        for(vecIndex = 0; vecIndex < 48; vecIndex++)
        {
            VectorTable[vecIndex] = *(volatile uint32_t*)(APPLICATION_START_ADDRESS + (vecIndex << 2));
        }
    
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    
        SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM);
    
        __enable_irq();
    }
    

    APPLICATION_START_ADDRESS is defined as 0x08004000. What does that for loop do? What kind of data is in those memory locations from 0x08004000, and how does it get put there?

    In the startup file, I see that reset handler branches to main after setting up the data in RAM etc. How does remapping the vector table enable the branching to go to different main()? I still don't see that.

  2. Do the bootloader and main application need separate linker files?

  3. Why must we remap the whole vector table? I understand the need for it for the reset vector, it needs an address to jump to upon power on or a reset. But for the other vectors shouldn't the stack be enough to tell if the caller is from the bootloader code or the main application?

I apologize if the questions are not clear, if they are let me know and I'll try my best to be clearer. I'm just very confused at this point.

\$\endgroup\$
2
\$\begingroup\$

1) The vector table is at the start of code normally and in your case there is the bootloader. The application code and it's vectors are later in code, so the interrupt controller needs to be told that when interrupt happens the vector is fetched from where the vectors for the main application are, otherwise it would jump to bootloader code. Bootloader uses bootloader vectors, application uses it's own vectors. But in your code it seems the application vectors are moved to SRAM, and they are used from there. Maybe they are used from there to enable changing the vectors during runtime.

2) If bootloader and application are two separate projects, then yes. Sometimes this is the easiest solution.

3) The hardware just has one register to tell at which address the vectors start, and all vectors are there.

\$\endgroup\$
2
\$\begingroup\$

STM32F051 is based on Cortex-M0. This CPU core always loads interrupt vectors from address 0. Note ARM Cortex design documents strongly suggests that flash is at address 0x00000000, RAM at 0x20000000, peripherals at 0x40000000.

For a bootloader/application interworking based on ARM Cortexes, main problem is:

  • CPU boots by loading initial stack pointer and PC from 0x00000000 and 0x00000004, this defaults to base of flash, and is not modifiable, it should be bootloader there.
  • Once application takes control of CPU, application code should be able to override any interrupt vector to suit its needs without bootloader to be specifically coded for it.
  • One should be able to update application without ever changing the bootloader.

The usual response to this constraints is to allow remapping of interrupt vector base address. For Cortex M0+ and after, there is a register in CPU to actually remap base address of interrupt vector (See DDI0403E, B3.2.5). This does not exist in M0.

As ST wanted interrupt vector relocation to be possible even for a M0, they did it at the platform-level. Their solution is to have flash at 0x08000000, and mirror it to 0x00000000 by default. Then there is a platform-level register that allows to remap RAM at 0x00000000 instead of flash (See RM0091 9.1.1, "SYSCFG_CFGR1", bits 1:0).

This way, interrupt vectors are loaded from Flash by default, but application may override them using the start of RAM as a storage. CPU by itself is not aware of remapping, it still uses 0 as base address for interrupt vector.

Note that most of the time, using beginning of RAM to override interrupt vectors do not imply any sort of dynamic modification of interrupt vectors. This is simply the only proper way to not load vectors from start of flash on STM32F051.

You should not share linker scripts between bootloader and application. Typically, they will both declare disjoint subsets of flash for code memory. Bootloader typically takes first page of flash, and application takes the rest of it.

This remapping feature is a ST-specific platform-level feature, other manufacturers do equivalent things, but another way (see KL03P24M48SF0RM, 6.3.1 for NXP MKL03 line similar hack with their internal bootloader).

\$\endgroup\$
  • \$\begingroup\$ Can you help clear my point of confusion on interrupt vectors? Specifically, what do you mean by overriding? Say for example if the bootloader and main application both use timer 1 overflow interrupt, does this mean that remapping the vectors causes the timer 1 overflow interrupt to have two separate addresses? \$\endgroup\$ – Lim LS Aug 2 at 19:50
  • \$\begingroup\$ To add to the above comment, the addresses should be different else I wonder how would the program be able to tell if a particular version of the interrupt belongs to the bootloader or main application \$\endgroup\$ – Lim LS Aug 2 at 19:59
  • \$\begingroup\$ @LimLS If boot loader uses an interrupt, then in the bootloader vector table this must point to interrupt code that is in the boot loader area. If main application uses an interrupt, then in the application vector table it must point to interrupt code that is in the main application area. Therefore, the CPU must be told from which area to fetch the vectors to know where to jump for execution. \$\endgroup\$ – Justme Aug 2 at 20:00
  • \$\begingroup\$ @LimLS, you seem to forget Bootloader and Application never run at the same time. As soon as Bootloader passes control to the Applicaiton code, interrupt vectors belong to Application exclusively. \$\endgroup\$ – Nipo Aug 2 at 20:04
  • \$\begingroup\$ @Justme Does that mean for a certain interrupt handler, the address for it isn't predefined and can vary from one application to another? With the PIC24, the starting address of each ISR is programmed into the vector table. That's why I'm confused. \$\endgroup\$ – Lim LS Aug 2 at 20:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.