Перейти к содержанию
    

Разместил таблицу векторов и код обработчика в ОЗУ, при прерывании вылетает в 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;

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

Изменено пользователем _Desh_

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

#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);

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

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

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

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

 

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 minutes ago, Forger said:

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

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

systick_flag = 1;

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

lr = 0xfffffff9

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

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

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

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

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

Изменено пользователем _Desh_

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

13 minutes ago, _Desh_ said:

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

 

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

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

13 minutes ago, _Desh_ said:

 

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

 

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 minutes ago, Forger said:

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, _Desh_ said:

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Объявил новую секцию 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)
{
  ...
}

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

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

Изменено пользователем _Desh_

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 hours ago, _Desh_ said:

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 hours ago, haker_fox said:

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

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

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

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

Изменено пользователем _Desh_

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...