Home > Software design >  Relocating interrupt vector table using linker script
Relocating interrupt vector table using linker script

Time:01-05

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.

CodePudding user response:

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.

CodePudding user response:

So, the obvious question is, why are those values incorrect.

I think you need to take address of those variables. SCB->VTOR = (uint32_t)&_sivector, not with asterisk. You want the address of that variable, not its content (which is undefined btw) dereferenced as a pointer and casted to uint32_t.

It's exactly the same logic with putting vars into data sections. Variables from linker scripts don't have a usable value. They're needed as address pointers, so just like you take &_stackstart or whatever you call it, similar thing should apply here.

EDIT: I've used the word "variable" a little too liberally. Linker script defines a symbol. There is no defined value associated with it.

  • Related