Search code examples
cstm32cortex-mlinker-scripts

Relocating interrupt vector table using linker script


I'm trying to move interrupt vector to DTCMRAM. The test code is simple blinking LED by timer interrupt. There I've changed load adress of .isr_vector:

MEMORY
{
  ITCMRAM (xrw)  : ORIGIN = 0x00000000, LENGTH = 64K
  FLASH (rx)     : ORIGIN = 0x08000000, LENGTH = 2048K
  DTCMRAM (xrw)  : ORIGIN = 0x20000000, LENGTH = 128K
  RAM_D1 (xrw)   : ORIGIN = 0x24000000, LENGTH = 512K
  RAM_D2 (xrw)   : ORIGIN = 0x30000000, LENGTH = 288K
  RAM_D3 (xrw)   : ORIGIN = 0x38000000, LENGTH = 64K
}

/* Define output sections */
SECTIONS
{
_sivector = LOADADDR(.isr_vector);
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    _svector = .;
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
    _evector = .;
  } >ITCMRAM AT> FLASH

After that I've added data copyer before main call (generated according to .data copier) in startup:

ldr r0, =_svector
  ldr r1, =_evector
  ldr r2, =_sivector
  movs r3, #0
  b LoopCopyVectorInit

CopyVectorInit:
  ldr r4, [r2, r3]
  str r4, [r0, r3]
  adds r3, r3, #4

LoopCopyVectorInit:
  adds r4, r0, r3
  cmp r4, r1
  bcc CopyVectorInit

Now I want to tell MCU that new vector table is availible using SCR->VTOR according to here.

Then here is main code:

extern uint32_t _sivector;
extern uint32_t _svector;
extern uint32_t _evector;
int main(void)
{
  /* USER CODE BEGIN 1 */

    __disable_irq();
      SCB->VTOR = (uint32_t)*_sivector;
    __DSB();
    __enable_irq();

But in this way debugger shows _svector and _sivector is equal to 0x24080000 and _evector=0x504f105.

Code line that reinitialize VTOR causes fault. Obviously _svector and _sivector has wrong address. Why? Even by commenting ITCMRAM AT> the _*vector variables carry wrong value.


Solution

  • It will never work this way.

    You end in the fault as when the uC boots up, it does not have the interrupt vector table at the standard place (which is required). Later you can copy and set the interrupt vector table in the place you want.

    So your startup code will never be executed as the reset vector is not set to anything meaningful.

    extern uint32_t _sivector;
    extern uint32_t _svector;
    extern uint32_t _evector;
    
    void __attribute__((constructor)) copyVect(void)
    {
      memcpy(&_sivector, &_svector, (&_evector - &_svector) * sizeof(uint32_t));
    }
    
    
    int main(void)
    {
      /* USER CODE BEGIN 1 */
    
        __disable_irq();
          SCB->VTOR = (uint32_t)&_sivector;
        __DSB();
        __enable_irq();
    

    or

    extern uint32_t _sivector[];
    extern uint32_t _svector[];
    extern uint32_t _evector[];
    
    void __attribute__((constructor)) copyVect(void)
    {
      memcpy(_sivector, _svector, (_evector - _svector) * sizeof(uint32_t));
    }
    
    
    
    
    int main(void)
    {
      /* USER CODE BEGIN 1 */
    
        __disable_irq();
          SCB->VTOR = (uint32_t)_sivector;
        __DSB();
        __enable_irq();
    
    SECTIONS
    {
      /* The startup code goes first into FLASH */
      .isr_vector :
      {
        . = ALIGN(4);
        _svector = .;
        KEEP(*(.isr_vector)) /* Startup code */
        . = ALIGN(4);
        _evector = .;
      } >FLASH
    
    /* ... */
    
      .isr_vector_itcm :
      {
        . = ALIGN(4);
        _sivector = .;
        . += _evector - _svector;
      } > ITCMRAM
    

    BTW the copy in the assembly file is not needed anymore.