What are the Steps after activating Position Independ Code (PIC) for A/B Swaping?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

What are the Steps after activating Position Independ Code (PIC) for A/B Swaping?

Jump to solution
417 Views
Ojav
Contributor II

Using: S32K146, GCC Compiler

I'm trying to perform the A/B Firmware Update procedure described in Application Note AN12323.

I successfully managed to perform the flashing from Firmware A to Firmware B using Hardcoded .ld Files (see below) and now I want to perform the same task using Position Independent Code.

What I did:

HARDCODED Linker File for Firmware A

/* Entry Point */
ENTRY(Reset_Handler)

HEAP_SIZE  = DEFINED(__heap_size__)  ? __heap_size__  : 0x00000400;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x00000400;

/* If symbol __flash_vector_table__=1 is defined at link time
 * the interrupt vector will not be copied to RAM.
 * Warning: Using the interrupt vector from Flash will not allow
 * INT_SYS_InstallHandler because the section is Read Only.
 */
M_VECTOR_RAM_SIZE = DEFINED(__flash_vector_table__) ? 0x0 : 0x0400;



/* Specify the memory areas */
MEMORY
{
  m_interrupts          (RX)  : ORIGIN = 0x00001000, LENGTH = 0x00000400   
  m_flash_config        (RX)  : ORIGIN = 0x00001400, LENGTH = 0x00000010   /*16 Bytes*/
  m_got                 (RX)  : ORIGIN = 0x00001410, LENGTH = 0x00001000 /*Global Offset Table*/


  /*All Code -> example: Reset_Handler is the first code located at 0x2400 */
  m_text                (RX)  : ORIGIN = 0x00002410, LENGTH = 0x0007DC00 - 0x1410    /*503 kB Firmware 1 partition A TODO die length von m_flash_config subtrahieren*/

  /* SRAM_L Adresse_Start 0x1FFF_0000 and Adress_End 0x1FFF_FFFF*/
  m_data                (RW)  : ORIGIN = 0x1FFF0000, LENGTH = 0x1FFFFFFF/*uses all 64kB or 65536 bytes of SRAM_L since it will be cleared when jumping*/
  /* SRAM_U Adress_Start 0x2000_0000 and Adress_End 0x2000_EFFF*/
  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x0000F000 /*Uses all 60kB or */
}



/* Define output sections */
SECTIONS
{  

  /* The startup code goes first into internal flash */
  .interrupts :
  {
    __VECTOR_TABLE = .;
    __interrupts_start__ = .;
    . = ALIGN(4);
    KEEP(*(.isr_vector))     /* Startup code */
    __interrupts_end__ = .;
    . = ALIGN(4);
  } > m_interrupts

  .flash_config :
  {
    . = ALIGN(4);
    KEEP(*(.FlashConfig))    /* Flash Configuration Field (FCF) */
    . = ALIGN(4);
  } > m_flash_config

  /* Define the .got sections */
    .got : 
    {
    . = ALIGN(4);
    KEEP*(.got)
    . = ALIGN(4);
    } > m_got

    .got.plt : 
    {
    . = ALIGN(4);
    KEEP*(.got.plt)
    . = ALIGN(4);
    } > m_got
    /* Version is always saved at 0x4000 and can occupy 24 Bytes*/
  .version 0x5000 : 
  {
      . = ALIGN(4);
      KEEP(*(.version))
      . = ALIGN(4);
  }    > m_text

  /* Model is always saved at 0x4018 directly after the version*/
  .model 0x5018 : {
      . = ALIGN(4);
      KEEP(*(.model))
      . = ALIGN(4);
  } > m_text


  /* The program code and other data goes into internal flash */
  .text :
  {
    . = ALIGN(4);
    *(.text)                 /* .text sections (code) */
    *(.text*)                /* .text* sections (code) */
    *(.rodata)               /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)              /* .rodata* sections (constants, strings, etc.) */
    *(.init)                 /* section used in crti.o files */
    *(.fini)                 /* section used in crti.o files */
    *(.eh_frame)             /* section used in crtbegin.o files */
    . = ALIGN(4);
  } > m_text

  /* Section used by the libgcc.a library for fvp4 */
  .ARM :
  {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } > m_text

  __etext = .;    /* Define a global symbol at end of code. */
  __DATA_ROM = .; /* Symbol is used by startup for data initialization. */



  .interrupts_ram :
  {
    . = ALIGN(4);
    __VECTOR_RAM__ = .;
    __RAM_START = .;
    __interrupts_ram_start__ = .; /* Create a global symbol at data start. */
    *(.m_interrupts_ram)          /* This is a user defined section. */
    . += M_VECTOR_RAM_SIZE;
    . = ALIGN(4);
    __interrupts_ram_end__ = .;   /* Define a global symbol at data end. */
  } > m_data

  __VECTOR_RAM = DEFINED(__flash_vector_table__) ? ORIGIN(m_interrupts) : __VECTOR_RAM__ ;
  __RAM_VECTOR_TABLE_SIZE = DEFINED(__flash_vector_table__) ? 0x0 : (__interrupts_ram_end__ - __interrupts_ram_start__) ;

  .data : AT(__DATA_ROM)
  {
    . = ALIGN(4);
    __DATA_RAM = .;
    __data_start__ = .;      /* Create a global symbol at data start. */
    *(.data)                 /* .data sections */
    *(.data*)                /* .data* sections */
    . = ALIGN(4);
    __data_end__ = .;        /* Define a global symbol at data end. */
  } > m_data

  __DATA_END = __DATA_ROM + (__data_end__ - __data_start__);
  __CODE_ROM = __DATA_END; /* Symbol is used by code initialization. */

  .code : AT(__CODE_ROM)
  {
    . = ALIGN(4);
    __CODE_RAM = .;
    __code_start__ = .;      /* Create a global symbol at code start. */
    __code_ram_start__ = .;
    *(.code_ram)             /* Custom section for storing code in RAM */
    . = ALIGN(4);
    __code_end__ = .;        /* Define a global symbol at code end. */
    __code_ram_end__ = .;
  } > m_data

  __CODE_END = __CODE_ROM + (__code_end__ - __code_start__);
  __CUSTOM_ROM = __CODE_END;

  /* Custom Section Block that can be used to place data at absolute address. */
  /* Use __attribute__((section (".customSection"))) to place data here. */
  .customSectionBlock  ORIGIN(m_data_2) : AT(__CUSTOM_ROM)
  {
    __customSection_start__ = .;
    KEEP(*(.customSection))  /* Keep section even if not referenced. */
    __customSection_end__ = .;
  } > m_data_2
  __CUSTOM_END = __CUSTOM_ROM + (__customSection_end__ - __customSection_start__);
  __rom_end    = __CUSTOM_END;

  /* Uninitialized data section. */
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section. */
    . = ALIGN(4);
    __BSS_START = .;
    __bss_start__ = .;
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
    __BSS_END = .;
  } > m_data_2

   /* Put heap section after the program data */
  .heap :
  {
    . = ALIGN(8);
    __end__ = .;
    __heap_start__ = .;
    PROVIDE(end = .);
    PROVIDE(_end = .);
    PROVIDE(__end = .);
    __HeapBase = .;
    . += HEAP_SIZE;
    __HeapLimit = .;
    __heap_limit = .;
    __heap_end__ = .;
  } > m_data_2

  /* Initializes stack on the end of block */
  __StackTop   = ORIGIN(m_data_2) + LENGTH(m_data_2);
  __StackLimit = __StackTop - STACK_SIZE;
  PROVIDE(__stack = __StackTop);
  __RAM_END = __StackTop;

    .stack :
  {
    . = ALIGN(8);
    . += STACK_SIZE;
  } > m_data_2


  /* Initializes stack on the end of block */
  __StackTop   = ORIGIN(m_data_2) + LENGTH(m_data_2);
  __StackLimit = __StackTop - STACK_SIZE;

  PROVIDE(__stack = __StackTop);
  __RAM_START = ORIGIN(m_data);
  __RAM_END = __StackTop + LENGTH(m_shared_ram);

  .ARM.attributes 0 : { *(.ARM.attributes) }

  /* Memory validation */
  ASSERT(__rom_end <= (ORIGIN(m_text) + LENGTH(m_text)), "Region m_text overflowed!")

  ASSERT(__StackLimit >= __HeapLimit, "region m_data_2 overflowed with stack and heap")
}

HARDCODED Linker File for Firmware B

/* Entry Point */
ENTRY(Reset_Handler)

HEAP_SIZE  = DEFINED(__heap_size__)  ? __heap_size__  : 0x00000400;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x00000400;

/* If symbol __flash_vector_table__=1 is defined at link time
 * the interrupt vector will not be copied to RAM.
 * Warning: Using the interrupt vector from Flash will not allow
 * INT_SYS_InstallHandler because the section is Read Only.
 */
M_VECTOR_RAM_SIZE = DEFINED(__flash_vector_table__) ? 0x0 : 0x0400;



/* Specify the memory areas */
MEMORY
{
  m_interrupts          (RX)  : ORIGIN = 0x00081000, LENGTH = 0x00000400   /* 1024 bytes 1kB Firmware 1 Vector Table Partition A / -10 for the m_flash_config that is part of this*/
  m_flash_config        (RX)  : ORIGIN = 0x00081400, LENGTH = 0x00000010   /*16 Bytes*/
  m_got                 (RX)  : ORIGIN = 0x00081410, LENGTH = 0x00001000 /*Global Offset Table*/


  /*All Code -> example: Reset_Handler is the first code located at 0x2400 */
  m_text                (RX)  : ORIGIN = 0x00082410, LENGTH = 0x0007DC00 - 0x1410    /*503 kB Firmware 1 partition A TODO die length von m_flash_config subtrahieren*/

  /* SRAM_L Adresse_Start 0x1FFF_0000 and Adress_End 0x1FFF_FFFF*/
  m_data                (RW)  : ORIGIN = 0x1FFF0000, LENGTH = 0x1FFFFFFF/*uses all 64kB or 65536 bytes of SRAM_L since it will be cleared when jumping*/
  /* SRAM_U Adress_Start 0x2000_0000 and Adress_End 0x2000_EFFF*/
  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x0000F000 /*Uses all 60kB or */
}



/* Define output sections */
SECTIONS
{  

  /* The startup code goes first into internal flash */
  .interrupts :
  {
    __VECTOR_TABLE = .;
    __interrupts_start__ = .;
    . = ALIGN(4);
    KEEP(*(.isr_vector))     /* Startup code */
    __interrupts_end__ = .;
    . = ALIGN(4);
  } > m_interrupts

  .flash_config :
  {
    . = ALIGN(4);
    KEEP(*(.FlashConfig))    /* Flash Configuration Field (FCF) */
    . = ALIGN(4);
  } > m_flash_config

  /* Define the .got sections */
    .got : 
    {
    . = ALIGN(4);
    KEEP*(.got)
    . = ALIGN(4);
    } > m_got

    .got.plt : 
    {
    . = ALIGN(4);
    KEEP*(.got.plt)
    . = ALIGN(4);
    } > m_got
    /* Version is always saved at 0x4000 and can occupy 24 Bytes*/
  .version 0x85000 : 
  {
      . = ALIGN(4);
      KEEP(*(.version))
      . = ALIGN(4);
  }    > m_text

  /* Model is always saved at 0x4018 directly after the version*/
  .model 0x85018 : {
      . = ALIGN(4);
      KEEP(*(.model))
      . = ALIGN(4);
  } > m_text


  /* The program code and other data goes into internal flash */
  .text :
  {
    . = ALIGN(4);
    *(.text)                 /* .text sections (code) */
    *(.text*)                /* .text* sections (code) */
    *(.rodata)               /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)              /* .rodata* sections (constants, strings, etc.) */
    *(.init)                 /* section used in crti.o files */
    *(.fini)                 /* section used in crti.o files */
    *(.eh_frame)             /* section used in crtbegin.o files */
    . = ALIGN(4);
  } > m_text

  /* Section used by the libgcc.a library for fvp4 */
  .ARM :
  {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } > m_text

  __etext = .;    /* Define a global symbol at end of code. */
  __DATA_ROM = .; /* Symbol is used by startup for data initialization. */



  .interrupts_ram :
  {
    . = ALIGN(4);
    __VECTOR_RAM__ = .;
    __RAM_START = .;
    __interrupts_ram_start__ = .; /* Create a global symbol at data start. */
    *(.m_interrupts_ram)          /* This is a user defined section. */
    . += M_VECTOR_RAM_SIZE;
    . = ALIGN(4);
    __interrupts_ram_end__ = .;   /* Define a global symbol at data end. */
  } > m_data

  __VECTOR_RAM = DEFINED(__flash_vector_table__) ? ORIGIN(m_interrupts) : __VECTOR_RAM__ ;
  __RAM_VECTOR_TABLE_SIZE = DEFINED(__flash_vector_table__) ? 0x0 : (__interrupts_ram_end__ - __interrupts_ram_start__) ;

  .data : AT(__DATA_ROM)
  {
    . = ALIGN(4);
    __DATA_RAM = .;
    __data_start__ = .;      /* Create a global symbol at data start. */
    *(.data)                 /* .data sections */
    *(.data*)                /* .data* sections */
    . = ALIGN(4);
    __data_end__ = .;        /* Define a global symbol at data end. */
  } > m_data

  __DATA_END = __DATA_ROM + (__data_end__ - __data_start__);
  __CODE_ROM = __DATA_END; /* Symbol is used by code initialization. */

  .code : AT(__CODE_ROM)
  {
    . = ALIGN(4);
    __CODE_RAM = .;
    __code_start__ = .;      /* Create a global symbol at code start. */
    __code_ram_start__ = .;
    *(.code_ram)             /* Custom section for storing code in RAM */
    . = ALIGN(4);
    __code_end__ = .;        /* Define a global symbol at code end. */
    __code_ram_end__ = .;
  } > m_data

  __CODE_END = __CODE_ROM + (__code_end__ - __code_start__);
  __CUSTOM_ROM = __CODE_END;

  /* Custom Section Block that can be used to place data at absolute address. */
  /* Use __attribute__((section (".customSection"))) to place data here. */
  .customSectionBlock  ORIGIN(m_data_2) : AT(__CUSTOM_ROM)
  {
    __customSection_start__ = .;
    KEEP(*(.customSection))  /* Keep section even if not referenced. */
    __customSection_end__ = .;
  } > m_data_2
  __CUSTOM_END = __CUSTOM_ROM + (__customSection_end__ - __customSection_start__);
  __rom_end    = __CUSTOM_END;

  /* Uninitialized data section. */
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section. */
    . = ALIGN(4);
    __BSS_START = .;
    __bss_start__ = .;
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
    __BSS_END = .;
  } > m_data_2

   /* Put heap section after the program data */
  .heap :
  {
    . = ALIGN(8);
    __end__ = .;
    __heap_start__ = .;
    PROVIDE(end = .);
    PROVIDE(_end = .);
    PROVIDE(__end = .);
    __HeapBase = .;
    . += HEAP_SIZE;
    __HeapLimit = .;
    __heap_limit = .;
    __heap_end__ = .;
  } > m_data_2

  /* Initializes stack on the end of block */
  __StackTop   = ORIGIN(m_data_2) + LENGTH(m_data_2);
  __StackLimit = __StackTop - STACK_SIZE;
  PROVIDE(__stack = __StackTop);
  __RAM_END = __StackTop;

    .stack :
  {
    . = ALIGN(8);
    . += STACK_SIZE;
  } > m_data_2


  /* Initializes stack on the end of block */
  __StackTop   = ORIGIN(m_data_2) + LENGTH(m_data_2);
  __StackLimit = __StackTop - STACK_SIZE;

  PROVIDE(__stack = __StackTop);
  __RAM_START = ORIGIN(m_data);
  __RAM_END = __StackTop + LENGTH(m_shared_ram);

  .ARM.attributes 0 : { *(.ARM.attributes) }

  /* Memory validation */
  ASSERT(__rom_end <= (ORIGIN(m_text) + LENGTH(m_text)), "Region m_text overflowed!")

  ASSERT(__StackLimit >= __HeapLimit, "region m_data_2 overflowed with stack and heap")
}

Bootloader Memory Layout

MEMORY
{
  /* Flash */
  /*1kB Default Vector Table for Bootloader minus the flash config that is part of the Table plus the Bootloader Check and MPU stuff that has to be performed for the Chain of Trust*/
  m_interrupts          (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000400 
  m_flash_config        (RX)  : ORIGIN = 0x00000400, LENGTH = 0x00000010 

  m_text                (RX)  : ORIGIN = 0x10000000, LENGTH = 0x00004000 - 4 /*16 kB Bootloader Application located at D-Flash*/
  m_shared_flash        (R)      : ORIGIN = 0x10003FFC, LENGTH = 0x00000004     /* 4 bytes for loader entry function pointer READ ONLY*/

  /* SRAM_L */
  m_data                (RW)  : ORIGIN = 0x1FFF0000, LENGTH = 0x1FFFFFFF - 0x1FFF0000      /*uses all 64kB or 0x10000 = 65536 bytes of SRAM_L since it will be cleared anyways when jumping to firmware*/

  /* SRAM_U */
  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x0000F000 -4         /*Uses whole 60kB or 0xF000 = 61440 bytes of SRAM_U since it will be cleared anyways when jumping to firmware*/
  m_shared_ram            (RW)  : ORIGIN = 0x2000EFFC, LENGTH = 0x00000004    /* 4 bytes for status variable shared with application using the last adressspace */

  /*Data sheet sagt Adresse ende ist 0x2000EFFF und adresse start ist 0x20000000  aber das sind nicht 61440 bytes sondern 61399 wenn man adresse_ende - adresse_start macht*/
}

Minimalistic Bootloader with basic JMP Function

#define FIRMWARE_A_VECTOR_TABLE_ADRESSE        0x00001000
#define FIRMWARE_B_VECTOR_TABLE_ADRESSE        0x00081000

void JumpToAdresse(uint32_t StackPointerAdresse,  uint32_t ProgramCounterAdresse)
{
    __asm volatile("msr msp, r0");
    __asm volatile ("ISB");
    __asm volatile("msr psp, r0");
    DISABLE_INTERRUPTS();
    S32_SCB->VTOR = (uint32_t)FIRMWARE_A_VECTOR_TABLE_ADRESSE;
    __asm volatile("mov pc, r1");

}

int main(void) {
    uint32_t StackPointerAdress = *((volatile uint32_t*)FIRMWARE_A_VECTOR_TABLE_ADRESSE);
    uint32_t ProgramCounterAdress = *((volatile uint32_t*)(FIRMWARE_A_VECTOR_TABLE_ADRESSE + 4));

    JumpToAdresse(StackPointerAdress, ProgramCounterAdress);
    __NO_RETURN
    return 0;
}

Currently working:

    1. First flash the 0x1000 Hardcoded Firmware
    2. Then flash the bootloader.
    3. Start my Application which first goes to the Bootloader and from there to Firmware_A 0x1000, and from there I start the Update Procedure Flashing my hardcoded .ld 0x81000 .bin using:
#define FIRMWARE_A 0x1000
#define FIRMWARE_B 0x81000


#define FLASH_OFFSET FIRMWARE_B  // Starting offset for the ISR vector table
#define NUM_OF_INT_VECTORS  255

uint32_t address = FLASH_OFFSET; //adress to flash the next bytes starting at flash_offset
uint16_t last_package_number = 0;
/*

* Erases whole Firmware Area, either A or B
  */
  void EraseFirmwareB() { 
   /* Define the start and end addresses */
   uint32_t start_address = flashSSDConfig.PFlashBase + FLASH_OFFSET;
   uint32_t end_address;
   if(FLASH_OFFSET == 0x1000){

       end_address = 0x00080000;

   }
   else{

       end_address = 0x00100000;

   }

   uint32_t ret;
   /* Ensure the start address is at the beginning of a sector */
   bool is_address_at_beginning_of_sector = (FLASH_OFFSET % FEATURE_FLS_PF_BLOCK_SECTOR_SIZE) == 0;
   DEV_ASSERT(is_address_at_beginning_of_sector);
   uint32_t flash_temp_address = start_address;
   uint32_t lenght= (end_address-FLASH_OFFSET) / FEATURE_FLS_PF_BLOCK_SECTOR_SIZE;
   //The Remainder of (end_address-FIRMWARE_B_VECTOR_TABLE_ADDRESS) % FEATURE_FLS_PF_BLOCK_SECTOR_SIZE should be 0
   for(uint32_t i=0;i<lenght;i++)
   {

       ret = FLASH_DRV_EraseSector(&flashSSDConfig, flash_temp_address, FEATURE_FLS_PF_BLOCK_SECTOR_SIZE);
       DEV_ASSERT(STATUS_SUCCESS == ret);
       /* Verify the erase operation at margin level value of 1, user read */
       ret = FLASH_DRV_VerifySection(&flashSSDConfig, flash_temp_address, FTFx_DPHRASE_SIZE, 1u);
       DEV_ASSERT(STATUS_SUCCESS == ret);
       flash_temp_address=flash_temp_address +FEATURE_FLS_PF_BLOCK_SECTOR_SIZE;

   }
  }

uint32_t WriteFirmwareB(MCU_UpdatingFileData *file_information) {

    status_t ret;
    uint32_t failAddr;


    // Assuming file_information->current_package_data_length is of type size_t or similar
    size_t alignment = 8;
    size_t remainder = file_information->current_package_data_length % alignment;

    if (remainder != 0) {
        // Calculate the additional bytes needed for alignment
        size_t padding = alignment - remainder;
        file_information->current_package_data_length += padding;
    }

    /* Write some data to the erased DFlash sector */
    ret = FLASH_DRV_Program(&flashSSDConfig, address, file_information->current_package_data_length, file_information->current_package_content_data);

    DEV_ASSERT(STATUS_SUCCESS == ret);

    //TODO use test data to check
    uint8_t indddex = (file_information->upgrade_package_number -1);

    /* Verify the program operation at margin level value of 1, user margin */
    ret = FLASH_DRV_ProgramCheck(&flashSSDConfig, address, file_information->current_package_data_length,file_information->current_package_content_data , &failAddr, 1u);//array_of_arrays[indddex]
    DEV_ASSERT(STATUS_SUCCESS == ret);

    // Update the address for the next package
    address += file_information->current_package_data_length;
    // Update last package number
    last_package_number = file_information->upgrade_package_number;
    return STATUS_SUCCESS == ret;

}

Everything works The flashing is sucessfull and I can jump correctly to Firmware B located at 0x81000!

My Goal is to now activate Position Independent Code

This is what I did.

    1. Activate -fPIC in the compiler settings.
    2. Change the Interrupt Table starting address in the .ld file since PIC is now active.
    3. Add the Global Offset Section (got, plt) manually because the compiler required it.
 /* Entry Point */
ENTRY(Reset_Handler)

HEAP_SIZE  = DEFINED(__heap_size__)  ? __heap_size__  : 0x00000400;
STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x00000400;

/* If symbol __flash_vector_table__=1 is defined at link time
 * the interrupt vector will not be copied to RAM.
 * Warning: Using the interrupt vector from Flash will not allow
 * INT_SYS_InstallHandler because the section is Read Only.
 */
M_VECTOR_RAM_SIZE = DEFINED(__flash_vector_table__) ? 0x0 : 0x0400;



/* Specify the memory areas */
MEMORY
{
  m_interrupts          (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000400   /* 1024 bytes 1kB Firmware 1 Vector Table Partition A / -10 for the m_flash_config that is part of this*/
  m_flash_config        (RX)  : ORIGIN = 0x00000400, LENGTH = 0x00000010   /*16 Bytes*/
  m_got                 (RX)  : ORIGIN = 0x00000410, LENGTH = 0x00001000 /*Global Offset Table*/


  /*All Code -> example: Reset_Handler is the first code located at 0x2400 */
  m_text                (RX)  : ORIGIN = 0x00001410, LENGTH = 0x0007DC00 - 0x1410    /*503 kB Firmware 1 partition A TODO die length von m_flash_config subtrahieren*/

  /* SRAM_L Adresse_Start 0x1FFF_0000 and Adress_End 0x1FFF_FFFF*/
  m_data                (RW)  : ORIGIN = 0x1FFF0000, LENGTH = 0x1FFFFFFF/*uses all 64kB or 65536 bytes of SRAM_L since it will be cleared when jumping*/
  /* SRAM_U Adress_Start 0x2000_0000 and Adress_End 0x2000_EFFF*/
  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x0000F000 /*Uses all 60kB or */
}



/* Define output sections */
SECTIONS
{  

  /* The startup code goes first into internal flash */
  .interrupts :
  {
    __VECTOR_TABLE = .;
    __interrupts_start__ = .;
    . = ALIGN(4);
    KEEP(*(.isr_vector))     /* Startup code */
    __interrupts_end__ = .;
    . = ALIGN(4);
  } > m_interrupts

  .flash_config :
  {
    . = ALIGN(4);
    KEEP(*(.FlashConfig))    /* Flash Configuration Field (FCF) */
    . = ALIGN(4);
  } > m_flash_config

  /* Define the .got sections */
    .got : 
    {
    . = ALIGN(4);
    KEEP*(.got)
    . = ALIGN(4);
    } > m_got

    .got.plt : 
    {
    . = ALIGN(4);
    KEEP*(.got.plt)
    . = ALIGN(4);
    } > m_got
    /* Version is always saved at 0x4000 and can occupy 24 Bytes*/
  .version 0x54000 : 
  {
      . = ALIGN(4);
      KEEP(*(.version))
      . = ALIGN(4);
  }    > m_text

  /* Model is always saved at 0x4018 directly after the version*/
  .model 0x4018 : {
      . = ALIGN(4);
      KEEP(*(.model))
      . = ALIGN(4);
  } > m_text


  /* The program code and other data goes into internal flash */
  .text :
  {
    . = ALIGN(4);
    *(.text)                 /* .text sections (code) */
    *(.text*)                /* .text* sections (code) */
    *(.rodata)               /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)              /* .rodata* sections (constants, strings, etc.) */
    *(.init)                 /* section used in crti.o files */
    *(.fini)                 /* section used in crti.o files */
    *(.eh_frame)             /* section used in crtbegin.o files */
    . = ALIGN(4);
  } > m_text

  /* Section used by the libgcc.a library for fvp4 */
  .ARM :
  {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } > m_text

  __etext = .;    /* Define a global symbol at end of code. */
  __DATA_ROM = .; /* Symbol is used by startup for data initialization. */



  .interrupts_ram :
  {
    . = ALIGN(4);
    __VECTOR_RAM__ = .;
    __RAM_START = .;
    __interrupts_ram_start__ = .; /* Create a global symbol at data start. */
    *(.m_interrupts_ram)          /* This is a user defined section. */
    . += M_VECTOR_RAM_SIZE;
    . = ALIGN(4);
    __interrupts_ram_end__ = .;   /* Define a global symbol at data end. */
  } > m_data

  __VECTOR_RAM = DEFINED(__flash_vector_table__) ? ORIGIN(m_interrupts) : __VECTOR_RAM__ ;
  __RAM_VECTOR_TABLE_SIZE = DEFINED(__flash_vector_table__) ? 0x0 : (__interrupts_ram_end__ - __interrupts_ram_start__) ;

  .data : AT(__DATA_ROM)
  {
    . = ALIGN(4);
    __DATA_RAM = .;
    __data_start__ = .;      /* Create a global symbol at data start. */
    *(.data)                 /* .data sections */
    *(.data*)                /* .data* sections */
    . = ALIGN(4);
    __data_end__ = .;        /* Define a global symbol at data end. */
  } > m_data

  __DATA_END = __DATA_ROM + (__data_end__ - __data_start__);
  __CODE_ROM = __DATA_END; /* Symbol is used by code initialization. */

  .code : AT(__CODE_ROM)
  {
    . = ALIGN(4);
    __CODE_RAM = .;
    __code_start__ = .;      /* Create a global symbol at code start. */
    __code_ram_start__ = .;
    *(.code_ram)             /* Custom section for storing code in RAM */
    . = ALIGN(4);
    __code_end__ = .;        /* Define a global symbol at code end. */
    __code_ram_end__ = .;
  } > m_data

  __CODE_END = __CODE_ROM + (__code_end__ - __code_start__);
  __CUSTOM_ROM = __CODE_END;

  /* Custom Section Block that can be used to place data at absolute address. */
  /* Use __attribute__((section (".customSection"))) to place data here. */
  .customSectionBlock  ORIGIN(m_data_2) : AT(__CUSTOM_ROM)
  {
    __customSection_start__ = .;
    KEEP(*(.customSection))  /* Keep section even if not referenced. */
    __customSection_end__ = .;
  } > m_data_2
  __CUSTOM_END = __CUSTOM_ROM + (__customSection_end__ - __customSection_start__);
  __rom_end    = __CUSTOM_END;

  /* Uninitialized data section. */
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section. */
    . = ALIGN(4);
    __BSS_START = .;
    __bss_start__ = .;
    *(.bss)
    *(.bss*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
    __BSS_END = .;
  } > m_data_2

   /* Put heap section after the program data */
  .heap :
  {
    . = ALIGN(8);
    __end__ = .;
    __heap_start__ = .;
    PROVIDE(end = .);
    PROVIDE(_end = .);
    PROVIDE(__end = .);
    __HeapBase = .;
    . += HEAP_SIZE;
    __HeapLimit = .;
    __heap_limit = .;
    __heap_end__ = .;
  } > m_data_2

  /* Initializes stack on the end of block */
  __StackTop   = ORIGIN(m_data_2) + LENGTH(m_data_2);
  __StackLimit = __StackTop - STACK_SIZE;
  PROVIDE(__stack = __StackTop);
  __RAM_END = __StackTop;

    .stack :
  {
    . = ALIGN(8);
    . += STACK_SIZE;
  } > m_data_2


  /* Initializes stack on the end of block */
  __StackTop   = ORIGIN(m_data_2) + LENGTH(m_data_2);
  __StackLimit = __StackTop - STACK_SIZE;

  PROVIDE(__stack = __StackTop);
  __RAM_START = ORIGIN(m_data);
  __RAM_END = __StackTop + LENGTH(m_shared_ram);

  .ARM.attributes 0 : { *(.ARM.attributes) }

  /* Memory validation */
  ASSERT(__rom_end <= (ORIGIN(m_text) + LENGTH(m_text)), "Region m_text overflowed!")

  ASSERT(__StackLimit >= __HeapLimit, "region m_data_2 overflowed with stack and heap")
}

After flashing, I perform a relocation of the Vector Table (VTOR) where the Firmware B offset is added for each entry of the interrupt table, except for the first entry that is the stack pointer.

 void EraseInterruptTableArea() {
    uint32_t ret;
   
    // Ensure the start address is at the beginning of a sector
    bool is_offset_at_beginning_of_sector = (FLASH_OFFSET % FEATURE_FLS_PF_BLOCK_SECTOR_SIZE) == 0;
    DEV_ASSERT(is_offset_at_beginning_of_sector);
   
    // Erase the sector(s) in PFlash where the ISR vector table is located
    ret = FLASH_DRV_EraseSector(&flashSSDConfig, FLASH_OFFSET, FEATURE_FLS_PF_BLOCK_SECTOR_SIZE);
    DEV_ASSERT(STATUS_SUCCESS == ret);
    /* Verify the erase operation at margin level value of 1, user read */
    ret = FLASH_DRV_VerifySection(&flashSSDConfig, FLASH_OFFSET, FTFx_DPHRASE_SIZE, 1u);
    DEV_ASSERT(STATUS_SUCCESS == ret);
   

}
#define NUM_OF_INT_VECTORS 255
uint32_t UpdateInterruptTableOffset() {
    status_t ret;
    uint32_t failAddr;

    uint32_t ONE_P_FLASH_BLOCK_SIZE = FEATURE_FLS_PF_BLOCK_SECTOR_SIZE / 4; 
    uint32_t updated_block_sector_with_relocated_VTOR[ONE_P_FLASH_BLOCK_SIZE]; //This is 4096 Bytes Big one Block because of the erase
    
    // Read the current ISR addresses
    uint32_t *isr_vector_table = (uint32_t *)FLASH_OFFSET;
    
    // Add offset to ISR addresses and store them
    for (uint32_t i = 0; i < ONE_P_FLASH_BLOCK_SIZE; i++) {
        if(i == 0){
            updated_block_sector_with_relocated_VTOR[i] = isr_vector_table[i]; //SKIP StackPointer
        }
        else if(i >= NUM_OF_INT_VECTORS){ //if its beyond the interrupt table just copy the old value
            updated_block_sector_with_relocated_VTOR[i] = isr_vector_table[i];
        }

//        else if (isr_vector_table[i] == 0) {  // Skip empty entries
//            updated_block_sector_with_relocated_VTOR[i] = 0;
//        }
        else { //add_offset
            updated_block_sector_with_relocated_VTOR[i] = isr_vector_table[i] + FLASH_OFFSET;
        }
    }

    uint8_t* bytePtr = (uint8_t*)updated_block_sector_with_relocated_VTOR;
    uint32_t length = sizeof(updated_block_sector_with_relocated_VTOR);
    
    size_t alignment = 8;
    size_t remainder = length % alignment;
    DEV_ASSERT(remainder == 0);
    
    remainder = FLASH_OFFSET % alignment;
    DEV_ASSERT(remainder == 0);
    
    
    EraseInterruptTableArea();
    /* Write updated ISR addresses to the erased DFlash sector */
    ret = FLASH_DRV_Program(&flashSSDConfig, FLASH_OFFSET, length, bytePtr);
    DEV_ASSERT(STATUS_SUCCESS == ret);
    
    /* Verify the program operation at margin level value of 1, user margin */
    ret = FLASH_DRV_ProgramCheck(&flashSSDConfig, FLASH_OFFSET, length, bytePtr, &failAddr, 1u);
    DEV_ASSERT(STATUS_SUCCESS == ret);
    
    
    return STATUS_SUCCESS == ret;

}

According to the Application Note, this should be enough to flash at any memory address of my choosing. I perform the flashing the same way I did when the .ld was hardcoded, and my bootloader is able to receive the correct Stack Pointer (SP) and Program Counter (PC). The jump is successful, but then it crashes at Address 0x85050.

cxvcx.PNG

00085020:   cpsid   i
00085022:   mov.w   r1, #0
00085026:   mov.w   r2, #0
0008502a:   mov.w   r3, #0
0008502e:   mov.w   r4, #0
00085032:   mov.w   r5, #0
00085036:   mov.w   r6, #0
0008503a:   mov.w   r7, #0
0008503e:   mov     r8, r7
00085040:   mov     r9, r7
00085042:   mov     r10, r7
00085044:   mov     r11, r7
00085046:   mov     r12, r7
00085048:   ldr     r0, [pc, #16]   ; (0x8505c)
0008504a:   mov     sp, r0
0008504c:   ldr     r0, [pc, #16]   ; (0x85060)
0008504e:   blx     r0
00085050:   ldr     r0, [pc, #16]   ; (0x85064)
00085052:   blx     r0
00085054:   cpsie   i
00085056:   bl      0x93964
0008505a:   b.n     0x8505a
0008505c:   vext.8  d18, d12, d0, #0
00085060:   ldr     r4, [pc, #692]  ; (0x85318)
00085062:   movs    r0, r0
00085064:   ldr     r4, [pc, #980]  ; (0x8543c)
00085066:   movs    r0, r0
00085068:   b.w     0x85068
0008506c:   eor.w   r1, r1, #2147483648     ; 0x80000000
00085070:   b.n     0x85078

This represents the first lines of the 'Reset_Handler' inside 'Startup.S' and it looks like it's the line 'ldr r0,=init_data_bss': or 'System_Init'

Reset_Handler:
    cpsid   i               /* Mask interrupts */

    /* Init the rest of the registers */
    ldr     r1,=0
    ldr     r2,=0
    ldr     r3,=0
    ldr     r4,=0
    ldr     r5,=0
    ldr     r6,=0
    ldr     r7,=0
    mov     r8,r7
    mov     r9,r7
    mov     r10,r7
    mov     r11,r7
    mov     r12,r7

#ifdef START_FROM_FLASH

    /* Init ECC RAM */

    ldr r1, =__RAM_START
    ldr r2, =__RAM_END

    subs    r2, r1
    subs    r2, #1
    ble .LC5

    movs    r0, 0
    movs    r3, #4
.LC4:
    str r0, [r1]
    add    r1, r1, r3
    subs r2, 4
    bge .LC4
.LC5:
#endif

    /* Initialize the stack pointer */
    ldr     r0,=__StackTop
    mov     r13,r0

#ifndef __NO_SYSTEM_INIT
    /* Call the system init routine */
    ldr     r0,=SystemInit
    blx     r0
#endif

    /* Init .data and .bss sections */
    ldr     r0,=init_data_bss
    blx     r0
    cpsie   i               /* Unmask interrupts */
    bl      main

My assumption is that either my VTOR Relocation Function is wrong or I have to manually add some more configurations after specifying -fPIC in the compiler and adding the .got sections.

Would appreciate any suggestions on what I might be missing or why it is crashing using PIC but not with Hardcoded .ld files. The Application Note Demo Project doesnt have PIC activated so I cant get an understanding from there.

0 Kudos
1 Solution
311 Views
johnrusso
NXP Employee
NXP Employee

Hello, 

This part does not look right:

00085048:   ldr     r0, [pc, #16]   ; (0x8505c)
0008504a:   mov     sp, r0
0008504c:   ldr     r0, [pc, #16]   ; (0x85060)
0008504e:   blx     r0
00085050:   ldr     r0, [pc, #16]   ; (0x85064)
00085052:   blx     r0
00085054:   cpsie   i
00085056:   bl      0x93964
0008505a:   b.n     0x8505a
0008505c:   vext.8  d18, d12, d0, #0
00085060:   ldr     r4, [pc, #692]  ; (0x85318)
00085062:   movs    r0, r0
00085064:   ldr     r4, [pc, #980]  ; (0x8543c)

 

Specifically, as an example, the code at 0x85050 seems to be explicitly loading an address from 0x85064, to be loaded into r0 and then call that routine via the blx at 0x85052. But at 0x85064, it looks like another indirection, perhaps the base of the GOT.  (0x85064 - ldr r4, [pc, #980]). So you are essentially loading an instruction bit pattern at 0x85064 instead of a static address (init_data_bss). 

Here are some links I found that might be helpful:

Position-Independent Code with GCC for ARM Cortex-M | MCU on Eclipse

https://www.eevblog.com/forum/microcontrollers/bare-metal-arm-gcc-position-independent-code-possible

Cannot build position independent code that works - Compilers and Libraries forum - Support forums -...

c - Compiling and linking position independent code (PIC) for ARM M4 - Stack Overflow 

Portable Position-Independent Code (PIC) bootloader and firmware for ARM Cortex-M0 and Cortex-M4 – J...

 

In particular, the compiler flags:

-msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative -fPIC

seem to be the magic flags to get things to work.

Hope it helps.

View solution in original post

1 Reply
312 Views
johnrusso
NXP Employee
NXP Employee

Hello, 

This part does not look right:

00085048:   ldr     r0, [pc, #16]   ; (0x8505c)
0008504a:   mov     sp, r0
0008504c:   ldr     r0, [pc, #16]   ; (0x85060)
0008504e:   blx     r0
00085050:   ldr     r0, [pc, #16]   ; (0x85064)
00085052:   blx     r0
00085054:   cpsie   i
00085056:   bl      0x93964
0008505a:   b.n     0x8505a
0008505c:   vext.8  d18, d12, d0, #0
00085060:   ldr     r4, [pc, #692]  ; (0x85318)
00085062:   movs    r0, r0
00085064:   ldr     r4, [pc, #980]  ; (0x8543c)

 

Specifically, as an example, the code at 0x85050 seems to be explicitly loading an address from 0x85064, to be loaded into r0 and then call that routine via the blx at 0x85052. But at 0x85064, it looks like another indirection, perhaps the base of the GOT.  (0x85064 - ldr r4, [pc, #980]). So you are essentially loading an instruction bit pattern at 0x85064 instead of a static address (init_data_bss). 

Here are some links I found that might be helpful:

Position-Independent Code with GCC for ARM Cortex-M | MCU on Eclipse

https://www.eevblog.com/forum/microcontrollers/bare-metal-arm-gcc-position-independent-code-possible

Cannot build position independent code that works - Compilers and Libraries forum - Support forums -...

c - Compiling and linking position independent code (PIC) for ARM M4 - Stack Overflow 

Portable Position-Independent Code (PIC) bootloader and firmware for ARM Cortex-M0 and Cortex-M4 – J...

 

In particular, the compiler flags:

-msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative -fPIC

seem to be the magic flags to get things to work.

Hope it helps.