Поместите загрузчик после приложения во флэш-память

Я написал Bootloader для моей платы STM32F042k6, которая функционирует довольно хорошо. При перезагрузке системы запускается загрузчик, который затем может перейти к приложению. Это было замечательно:). Теперь я хочу сделать противоположное в моей Flash. Я хотел бы запустить мой загрузчик по начальному адресу, отличному от 0x08000000, скажем, по адресу 0x08007000. Когда я делаю изменения в скрипте компоновщика, программа не может быть отлажена. Проще говоря, я хочу поместить мой загрузчик в конец моей Flash. Не забывайте, что Bootloader всегда является первым кодом, запускаемым после сброса. Заранее спасибо за помощь и комментарии. Вот мой скрипт компоновщика:

/* Entry Point */
ENTRY(Boot_Reset_Handler)

/* Highest address of the user mode stack */
_estack = 0x20001800;    /* end of 6K RAM */

/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0;      /* required amount of heap  */
_Min_Stack_Size = 0x80; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{   
  BOOTLOADER (rx) : ORIGIN = 0x08007000, LENGTH = 4K    
  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 28K 
  RAM (xrw)       : ORIGIN = 0x200000C0, LENGTH = 6K - 192
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
}


/* Define output sections */
SECTIONS
{
  /* The startup code goes first into BOOTLOADER */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >BOOTLOADER



  /* The program code and other data goes into BOOTLOADER */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >BOOTLOADER

  /* Constant data goes into BOOTLOADER */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >BOOTLOADER

  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >BOOTLOADER
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >BOOTLOADER

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >BOOTLOADER
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >BOOTLOADER
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >BOOTLOADER

  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data : 
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >RAM AT> BOOTLOADER

  /* Uninitialized data section */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM

  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(4);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(4);
  } >RAM

  /* MEMORY_bank1 section, code must be located here explicitly            */
  /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */
  .memory_b1_text :
  {
    *(.mb1text)        /* .mb1text sections (code) */
    *(.mb1text*)       /* .mb1text* sections (code)  */
    *(.mb1rodata)      /* read-only data (constants) */
    *(.mb1rodata*)
  } >MEMORY_B1

  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

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

4 ответа

Решение

Боюсь, вы ищете решение для неправильной проблемы. Программы, загруженные загрузчиком, могут быть отлажены. Я делаю это все время.

Итак, что-то идет не так, прежде чем ваше приложение достигнет первой точки останова, установленной отладчиком.

Вероятно, неполный список вещей для проверки

  • Загрузчик делает то, что делает отладку невозможной
    • отключает выводы SWD
    • не переходит на обработчик сброса приложения
    • начинает сторож
    • не отключает все возможные прерывания
    • не возвращается в режим потока
    • неверное значение в указателе стека
  • загрузка приложения в отладчик повреждает загрузчик
    • например, стирает неправильные сектора вспышки
  • код запуска приложения не работает
    • проблема со скриптом компоновщика приложения
    • устанавливает неверное значение в указателе стека
    • устанавливает неправильное значение в NVIC->VTOR - сначала проверьте это, если вы используете HAL

Вам не повезло, боюсь, ваш процессор всегда начнет запускать код с адреса 0x00000000 (вроде как, он будет смотреть на 0x00000004, чтобы увидеть, где находится вектор сброса).

Существует несколько загрузочных контактов, которые изменяют псевдоним флэш-памяти или ОЗУ по адресу 0x00000000, но вы не можете выбрать, какая область флэш-памяти всегда будет 0x08000000. Если вы хотите использовать свой собственный загрузчик и запустить его первым, он должен быть в начале прошивки.

Какую проблему вы пытаетесь решить, переместив загрузчик? Возможно, есть другое возможное решение.

Проект Microsoft Jacdac помещает загрузчик в последние 4 КБ флэш-памяти STM32G0, которая изначально имеет 32 КБ. Я обнаружил это на собственном горьком опыте, когда попытался использовать последнюю страницу флэш-памяти для постоянного хранения.

Файл linker.ld для загрузчика, созданный во время сборки, имеет содержимое:

      MEMORY {
RAM (rwx)   : ORIGIN = 0x20000000, LENGTH = 8K
FLASH (rx)  : ORIGIN = 0x8000000 + 32K - 4K, LENGTH = 4K
}
INCLUDE jacdac-stm32x0/ld/gcc_arm_bl_at_end.ld

Файл jacdac-jacdac-stm32x0/ld/gcc_arm_bl_at_end.ld можно найти на GitHub по адресу: https://github.com/microsoft/jacdac-stm32x0/blob/05f7b6913a0f6cfcd7a15252daff773ead2834da/ld/gcc_arm_bl_at_end.ld .

Обычно загрузчик — это первый фрагмент кода, который выполняется первым. И таким образом невозможно переместить загрузчик в другой раздел памяти. Однако есть еще одна концепция, способная переместить загрузчик на любой адрес памяти. То есть загрузчик нужен только для обновления ПО позже. При таком подходе вашим основным программным обеспечением будет загрузочный код. И когда есть запрос на обновление, вы переходите к загрузчику и выполняете обновление программного обеспечения, а затем перезагружаетесь для нормальной работы. Недостатком этого подхода является то, что в случае сбоя обновленного программного обеспечения не существует никакого механизма восстановления, и вам необходимо перепрошить с помощью инструментов для прошивки.

Другие вопросы по тегам