Jump to content
    

Разместил таблицу векторов и код обработчика в ОЗУ, при прерывании вылетает в HardFault

Разрабатываю в STM32CubeIDE. Контроллер STM32F407V.

Так я копирую таблицу векторов в ОЗУ:

#define VECTOR_EXCEPTION_NUM  (16)

/* Ссылка на таблицу векторов, размещенную в памяти Flash. Взята из файла
 * startup.s */
extern uint32_t g_pfnVectors[];

/* Массив для хранения адресов обработчиков прерываний, размещенных в RAM. */
static uint32_t vector_table_ram[94]
  __attribute__((section(".RAM"))) __attribute__((aligned(512)));

void SystemInit(void)
{
  uint32_t i = 0;

  /* Копируем таблицу векторов. */
  for (i = 0; i < 94; i++) {
    vector_table_ram[i] = g_pfnVectors[i];
  }
  
  /* Регистрация нового обработчика. */
  vector_table_ram[VECTOR_EXCEPTION_NUM + SysTick_IRQn] = (uint32_t)&systick_handler_ram;
  
  /* VTOR. */
  SCB->VTOR = (uint32_t)vector_table_ram;
}

Правильность копирования проверял в отладчике. Сама таблица разместилась по адресу 0x20000600, содержимое регистра VTOR соответствует.

Обработчик прерывания, который я хочу разместить в ОЗУ:

void systick_handler_ram(void) __attribute__((section(".RAM")));

void systick_handler_ram(void)
{
  systick_inc_tick_1ms();
  systick_flag = 1;
}

Обработчик успешно размещается по адресу 0х20000200, в новую таблицу векторов это значение вносится. Программа один раз заходит в обработчик, прошагивает инструкции до конца, а при выходе из него вылетает в HardFault.

Если убрать из объявления systick_handler_ram атрибут размещения в секции RAM, обработчик размещается во FLASH, и прерывание работает - по прерыванию я завел счетчик и моргалку.

Пробовал добавлять __attribute__((interrupt)) или __attribute__((interrupt("IRQ"))) - программа перестала даже заходить в обработчик - сразу в HardFault.

 

Подскажите, пожалуйста, что я делаю не так?

 

P.S. Пробовал делать так:

vector_table_ram[VECTOR_EXCEPTION_NUM + SysTick_IRQn] = ((uint32_t)&systick_handler_ram) | 1UL;

Ничего не меняется, компилятор добавляет единицу в младший разряд самостоятельно.

Edited by _Desh_

Share this post


Link to post
Share on other sites

А зачем таблицу векторов копировать? Если векторов немного,  то проще их устанавливать.

#define VTABLE_ADR 0x2000'0000

#define InstallIRQ(name)  void name##Handler(); \
*(volatile uint32_t *)(VTABLE_ADR + 0x40 + name##n * 4) = (uint32_t)name##Handler

SCB->VTOR = VTABLE_ADR;
          
InstallIRQ(USB_LP_CAN1_RX0_IRQ);

 

 

Share this post


Link to post
Share on other sites

Вероятно, что возникает прерывания до того, как все обработчики скопированы в ОЗУ.

Делать важно в ТАКОМ ПОРЯДКЕ:

1.  запретить все прерывания

2. скопировать обработчики и таблицу (необязательно)

3. перенастроить vtor

4. разрешить прерывания

Делать это лучше в main !!!

 

Скорее всего в вашем случае сначала вызывается system_init, а уже потом копируются сами обработчики прерываний из flash в озу (см как работает ram function). Не путать с копированием таблицы!

 

Share this post


Link to post
Share on other sites

14 minutes ago, Forger said:

Скорее всего в вашем случае сначала вызывается system_init, а уже потом копируются сами обработчики прерываний из flash в озу

Попробовал перенести весь код в main с запретом прерываний на время инициализации - не помогло.

Спасибо, но дело похоже не в этом.

Первый-то раз в обработчик программа заходит как положено. Сбой при выходе из него.

Порядок вызова в startup-файле -  System_Init->__libc_init_array->main. Копирование всего, кроме таблицы, происходит до вызова System_Init.

Для успокоения совести убирал вызов __libc_init_array - результат прежний.

Share this post


Link to post
Share on other sites

Стека прерываний хватает? Если дело не в этом, то проверяйте содержимое регистров непосредственно перед выходом из обработчика (по шагам в disasm).

Share this post


Link to post
Share on other sites

Раз обработчик успешно вызывается -- значит, дело точно не в таблице векторов и не в настройке VTORа. Надо смотреть, что на момент завершения обработчика находится в LR и стеках и куда, собственно, происходит возврат. Кроме того, я проверил бы, а правильно ли присваивается значение переменной 

systick_flag = 1;

Тут дело в том, что процессор не ожидает фактического завершения записи и продолжает выполнять программу; соответственно, если запись обломилась, прерывание произойдёт не на команде записи, а уже позже -- и, если нет правильного обработчика подобных прерываний (а его обычно нет), вывалится HardFault.

Share this post


Link to post
Share on other sites

Для начала стоит добиться нормальной работы с пустыми обработчиками (кроме сброса соотв. флага перерывания).

Share this post


Link to post
Share on other sites

Поведение остается прежним и при пустом обработчике.

sp = 0x1000ffd0 (я разместил стек в области CCMRAM)

lr = 0xfffffff9

Также наблюдал такую картину: содержимое окна дизассемблера кардинально различается в двух вариантах программы - с обработчиком во FLASH и с обработчиком в RAM.

Для пустого обработчика во FLASH всего лишь:

080202ec:   push    {r7}
080202ee:   add     r7, sp, #0

А вот для обработчика в RAM полная белиберда, ничего похожего, просто непонятный набор инструкций.

Как будто никакого копирования кода функции в ОЗУ не произошло. Разве для этого недостаточно просто указать рядом с объявлением/определением функции соответствующий атрибут?

Edited by _Desh_

Share this post


Link to post
Share on other sites

13 minutes ago, _Desh_ said:

Как будто никакого копирования кода функции в ОЗУ не произошло. Разве для этого недостаточно просто указать рядом с объявлением/определением функции соответствующий атрибут?

 

Значит входа в обработчик все же нет. А говорили, что входит в обработчик ))

Выходит, что валится не туда, куда надо, а вероятнее всего туда, где ничего нужного нет :buba:

13 minutes ago, _Desh_ said:

 

 Разве для этого недостаточно просто указать рядом с объявлением/определением функции соответствующий атрибут?

 

Конечно нет )))

Читайте в мануале про ram function. Там расписано в картинках что надо делать

Share this post


Link to post
Share on other sites

10 minutes ago, Forger said:

Читайте в мануале про ram function.

Подскажите пожалуйста хотя бы название. Что за мануал - на МК, на среду разработки, на сам GCC?

Share this post


Link to post
Share on other sites

9 minutes ago, _Desh_ said:

Подскажите пожалуйста хотя бы название. Что за мануал - на МК, на среду разработки, на сам GCC?

Блин, я же не знаю в какой среде разработке работаете и как там это организовано.

В двух словах: для файла, где размещены ram функции, нужно выделять соотв. секцию в скрипте линкера. К тому же среда должна поддерживать такие вещщи как ram функции. 

Ну и как капитан очевидность: гугль в помощь :)

Share this post


Link to post
Share on other sites

4 часа назад, _Desh_ сказал:

void systick_handler_ram(void) __attribute__((section(".RAM")));

А покажите как у вас в скрипте линкера объявлена секция .RAM?

Или попробуйте поместить функцию в .data.

Share this post


Link to post
Share on other sites

Нагуглил и сделал следующее (пусть это наверное всем кроме меня известный баян, напишу хотя бы для упорядочивания в своей голове).

Объявил новую секцию RAMFUNC для размещения кода нужных мне функций в скрипте линкера. Пока выбрал 8 Кб, отрезав столько же от остальной RAM.

MEMORY
{
  CCMRAM  (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
  RAMFUNC (rx)  : ORIGIN = 0x20000000, LENGTH = 8K
  RAM     (xrw) : ORIGIN = 0x20002000, LENGTH = 120K
  FLASH   (rx)  : ORIGIN = 0x08020000, LENGTH = 128K
}

Описание секции между секциями .data и .bss:

_siramfunc = LOADADDR(.ramfunc);

.ramfunc :
{
  . = ALIGN(4);
  _sramfunc = .;        /* для копирования кода в startup-файле */
  *(.RAMFUNC.ISR)
  *(.RAMFUNC)
  *(.RAMFUNC*)
  . = ALIGN(4);
  _eramfunc = .;        /* для копирования кода в startup-файле */
} >RAMFUNC AT> FLASH

В startup-файле вписал объявленные ранее переменные и добавил копирование кода по аналогии с уже имеющимся копированием данных .data и .bss. Как уже указывал уважаемый Forger, копировать код функций надо до вызова основной программы:

.word _siramfunc
.word _sramfunc
.word _eramfunc

...

CopyCodeRamFunc:
  ldr   r3, =_siramfunc
  ldr   r3, [r3, r1]
  str   r3, [r0, r1]
  adds  r1, r1, #4

LoopCopyCodeRamFunc:
  ldr   r0, =_sramfunc
  ldr   r3, =_eramfunc
  adds  r2, r0, r1
  cmp   r2, r3
  bcc   CopyCodeRamFunc

  bl    SystemInit

  bl    main

...

После этого осталось только объявить соответствующий атрибут для нужных мне функций. Для целей тестирования я сделал для одной обычной функции (инициализация Systick) и одного обработчика прерывания (того же Systick):

__attribute__((section(".RAMFUNC")))
void
systick_config(void)
{
  ...
}

__attribute__((interrupt))
__attribute__((section(".RAMFUNC.ISR")))
void
systick_handler_ram(void)
{
  ...
}

Теперь обработчик работает корректно. Обычная функция тоже.

Извините за простыню. Всем спасибо за подсказки.

Edited by _Desh_

Share this post


Link to post
Share on other sites

10 hours ago, _Desh_ said:

Обработчик прерывания, который я хочу разместить в ОЗУ:

А для чего вы размещаете сам обработчик в ОЗУ? Пока не могу придумать, зачем это нужно) Мне хватает разместить в ОЗУ только таблицу.

Share this post


Link to post
Share on other sites

2 hours ago, haker_fox said:

А для чего вы размещаете сам обработчик в ОЗУ? Пока не могу придумать, зачем это нужно) Мне хватает разместить в ОЗУ только таблицу.

Предполагаю стирать/писать флэш-память без запрета прерываний. Раньше я загрузку прошивки на лету ни разу не делал, опыта у меня мало.

Сейчас я увидел неудобства, связанные с этим (начинал с Кейла, там гораздо проще делается размещение функций в ОЗУ), возможно в конце концов обойдусь простым запретом прерываний на время стирания/записи.

Ну и сами-то функции стирания/записи по-любому придется в ОЗУ размещать. Так что осваивать все равно пришлось бы, пусть и не на примере обработчика.

Edited by _Desh_

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...