Программирование ARM IAR: создание загрузчика для Cortex-M Tue, October 20 2020  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

IAR: создание загрузчика для Cortex-M Печать
Добавил(а) microsin   

В этом документе (перевод TN160822 [1]) приведено руководство по созданию загрузчика в среде IAR Embedded Workbench for ARM. Многие рекомендации здесь общие, хотя проект примера основан на микропроцессоре Cortex-M (STM32L152VB компании STMicroelectronics).

При создании загрузчика следует учитывать некоторые аспекты, связанные с настройкой проекта и передачей управления из загрузчика в приложение. См. проект примера для STM32L152VB и выполните следующие действия. Имейте в виду, что это руководство не описывает, как выполняется действительное обновление кода (IAP – in application programming, самопрограммирование) приложения, или как конфигурировать коммуникационные интерфейсы, хотя это общая функция в загрузчиках.

Основные шаги по созданию загрузчика следующие:

• Создайте 2 отдельных проекта – один для загрузчика, и еще один для приложения. Эти 2 проекта могут быть размещены в одном рабочем пространстве. С отдельными проектами Вы гарантируете, что код и библиотечные функции не будут совместно использоваться загрузчиком и приложением.

bootloader tn160822 workspace fig01

• Проверьте размер блока flash-памяти (самый маленький стираемый элемент flash). Деление между загрузчиком и приложением должно происходить на границе блока flash, иначе есть риск стирания частей приложения или загрузчика, когда вы перепрограммируете кого-то из них.

• Любая инициализация железа обычно должна быть выполнена только один раз, чаще всего в коде загрузчика. Убедитесь, что приложение не делает повторную инициализацию железа, если это не требуется.

• Перед тем, как загрузчик передаст управление в приложение, убедитесь в следующем:

1. Прерывания запрещены – чтобы избежать неожиданных прерываний во время старта приложения.
2. Сконфигурируйте/сбросьте указатель стека – чтобы память стека приложения не использовалась загрузчиком.
3. Сконфигурируйте регистр VTOR – чтобы прерывания обрабатывались в приложении, не в загрузчике.

__disable_interrupt();              // 1. Запрет прерываний.
__set_SP(vector_p->stack_addr);     // 2. Настройка указателя стека.
SCB->VTOR = (uint32_t)&app_vector;  // 3. Конфигурирование VTOR.
vector_p->func_p();                 // 4. Переход в приложение.

[Особенности отладки]

Отладка загрузчика и приложения одновременно возможна, однако процедура может быть сложной, например потому что у них обоих есть функция main. Чтобы отладчик знал о наличии двух приложений, выберите в свойствах проекта Debugger -> Images, чтобы загрузить информацию из выходного ELF-файла другого проекта.

bootloader tn160822 images load fig02

Убедитесь, что опция Run to main запрещена на закладке Debugger -> Setup, чтобы избежать двусмысленности, какая из функций main должна запускаться (функция main загрузчика или функция main в приложении).

После загрузки кода отладчик по умолчанию начинает выполнение по адресу, заданному символом __vector_table. Когда Вы отлаживаете загрузчик и приложение одновременно, может появиться следующее сообщение:

bootloader tn160822 vector table fig03

Чтобы направить отладчик на корректный символ __vector_table, Вы должны явно указать, где находится таблица векторов загрузчика. Выберите на закладке Debugger -> Extra Options:

--drv_vector_table_base=0x08000000

Когда отлаживаете код, используйте окно View -> Images, чтобы выбрать приложение в области действия и избежать проблем с конфликтами имен в двух приложениях.

bootloader tn160822 images debug fig04

При пошаговом переходе в режиме отладки от загрузчика в приложение помните, что нужно поменять текущую загруженную отладочную информацию (в окне Images).

[Запись Flash]

Чтобы загрузить приложение и загрузчик в память Flash, есть несколько вариантов. Из-за того, что загрузчик и приложение находятся в разных проектах, они могут быть загружены индивидуально из каждого проекта. Альтернативно Вы можете загрузить их оба в память Flash одновременно, например используя линкер для подключения двоичного кода приложения в проекте загрузчика (или наоборот). Для этого выберите в свойствах проекта закладку Linker -> Input.

bootloader tn160822 linker fig05

В файле конфигурации линкера двоичный образ приложения помещается в память Flash следующей строкой:

place at address mem: app_vector { readonly section .APP };

[Пример загрузчика]

Готовый пример загрузчика, который загружает образ программы пользователя через сеть Ethernet по протоколу TFTP или HTTP, можно найти в проекте LwIP_IAP из пакета библиотек STM32Cube (см. папку Projects\STM324x9I_EVAL\Applications\LwIP).

Идея загрузчика состоит в том, что загрузчик находится в младших адресах Flash (с адреса 0x08000000), и он первым получает управление при включении питания/сбросе. Программа пользователя находится дальше в памяти, в других секторах Flash (например с адреса 0x08020000).

Итак, при включении питания всегда запускается загрузчик. В этот момент код принимает решение - должен ли оставаться активным загрузчик (чтобы иметь возможность переписать основную программу), или же нужно передать управление в основную программу. Обычно это делается по так называемому условию загрузки - например, нажата ли при включении питания какая-нибудь кнопка.

[Как изменить адрес старта загружаемого приложения]

Чтобы и загрузчик, и программа пользователя находились в памяти Flash одновременно, их необходимо разнести в памяти по отдельным секторам Flash. В примере, приведенном здесь, загрузчик находится начиная с сектора 0 и может занимать секторы 0, 1, 2, 3 и 4 (адресное пространство 0x08000000 - 0x0801FFFF, размер 128 кбайт). Вся остальная память Flash, начиная с сектора 5 (сектора 5 .. 23, адресное пространство 0x08020000 - 0x081FFFFF, размер 1966080 байт), может быть использована под код пользователя.

Таблица 6. Модуль Flash - организация 2 мегабайта dual bank organization (STM32F42xxx и STM32F43xxx).

Блок Банк Сектор Адреса в памяти Размер, кбайт
Основная память Bank 1 0 0x08000000 - 0x08003FFF 16
1 0x08004000 - 0x08007FFF 16
2 0x08008000 - 0x0800BFFF 16
3 0x0800C000 - 0x0800FFFF 16
4 0x08010000 - 0x0801FFFF 64
5 0x08020000 - 0x0803FFFF 128
6 0x08040000 - 0x0805FFFF 128
.. .. ..
11 0x080E0000 - 0x080FFFFF 128
Bank 2 12 0x08100000 - 0x08103FFF 16
13 0x08104000 - 0x08107FFF 16
14 0x08108000 - 0x0810BFFF 16
15 0x0810C000 - 0x0810FFFF 16
16 0x08110000 - 0x0811FFFF 64
17 0x08120000 - 0x0813FFFF 128
18 0x08140000 - 0x0815FFFF 128
.. .. ..
23 0x081E0000 - 0x081FFFFF 128
Системная память 0x1FFF0000 - 0x1FFF77FF 30
OTP [3] 0x1FFF7800 - 0x1FFF7A0F 528 байт
Байты опций [3] Bank 1   0x1FFFC000 - 0x1FFFC00F 16 байт
Bank 2   0x1FFEC000 - 0x1FFEC00F 16 байт

Чтобы приложение пользователя могло находиться начиная с адреса 0x08020000, необходимо внести некоторые изменение в проект приложения. Процесс по шагам:

1. Откройте в текстовом редакторе файл конфигурации линкера *.icf, и внесите в него следующие изменения (помечено красным цветом):

/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = 0x08020000;
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__    = 0x08020000;
define symbol __ICFEDIT_region_ROM_end__      = 0x081FFFFF;
define symbol __ICFEDIT_region_RAM_start__    = 0x20000000;
define symbol __ICFEDIT_region_RAM_end__      = 0x2002FFFF;
define symbol __ICFEDIT_region_CCMRAM_start__ = 0x10000000;
define symbol __ICFEDIT_region_CCMRAM_end__   = 0x1000FFFF;
define symbol __ICFEDIT_region_SDRAM_start__  = 0xC0000000;
define symbol __ICFEDIT_region_SDRAM_end__    = 0xC1FFFFFF;
/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = 0x400;
define symbol __ICFEDIT_size_heap__   = 0x200;
/**** End of ICF editor section. ###ICF###*/
 
 
define memory mem with size = 4G;
define region ROM_region      = mem:[from __ICFEDIT_region_ROM_start__   to __ICFEDIT_region_ROM_end__];
define region RAM_region      = mem:[from __ICFEDIT_region_RAM_start__   to __ICFEDIT_region_RAM_end__];
define region SDRAM_region    = mem:[from __ICFEDIT_region_SDRAM_start__   to __ICFEDIT_region_SDRAM_end__];
define region CCMRAM_region   = mem:[from __ICFEDIT_region_CCMRAM_start__   to __ICFEDIT_region_CCMRAM_end__];
 
define block CSTACK    with alignment = 8, size = __ICFEDIT_size_cstack__   { };
define block HEAP      with alignment = 8, size = __ICFEDIT_size_heap__     { };
 
initialize by copy { readwrite };
do not initialize  { section .noinit };
 
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
 
place in ROM_region   { readonly };
place in RAM_region   { readwrite,
                        block CSTACK, block HEAP };
place in SDRAM_region   { section SDRAM_DATA };

2. Откройте в редакторе файл system_stm32f4xx.c, и внесите в функцию SystemInit следующие изменения (помечено красным цветом):

/**  * @brief  Setup the microcontroller system
  *         Initialize the FPU setting, vector table location and External memory
   *         configuration.
  * @param  None
  * @retval None  */
void SystemInit(void)
{
  /* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
#endif
  /* Reset the RCC clock configuration to the default reset state ------------*/
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;
  /* Reset CFGR register */
  RCC->CFGR = 0x00000000;
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;
  /* Reset PLLCFGR register */
  RCC->PLLCFGR = 0x24003010;
  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;
  /* Disable all interrupts */
  RCC->CIR = 0x00000000;
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
  SystemInit_ExtMemCtl();
 #endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
  /* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  //SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
  SCB->VTOR = FLASH_BASE
            | 0x00020000
            | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}

[Ссылки]

1. Technical Note 160822 Creating a bootloader for Cortex-M site:iar.com.
2STM32F407: внустрисхемное программирование через Ethernet.
3STM32F4xx: байты опций.

 

Добавить комментарий


Защитный код
Обновить

Top of Page