реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> Nested interrupts and Hard Fault, Происходит hard fault если есть вложенные прерывания
xelax
сообщение Jun 21 2016, 13:08
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 370
Регистрация: 7-11-06
Пользователь №: 22 035



Исходные данные.
Код для cortex-m4 (nrf52), язык С, компилятор Keil.

Ситуация следующая. В принципе все ок и работает. Работаем в основном с GCC, но по условиям ТЗ код должен собираться и работать также на IAR и Keil, так что периодически билдим и запускаем код на них.
GCC и IAR работают без проблем. Keil падает в hard fault в рандомном месте. Первые подозрения были на проезды по памяти. Детальное исследование одного из падений показало, что hard fault случается так как портится значение в регистре общего назначения r5, который используется компилятором внутри функции как базовый адрес для работы с периферией. Внутри функции r5 не обновляется (записывается константа при входе в функцию один раз). То есть предположение проезда по памяти отпадает.
Второе предположение что поведение ломают вложенные прерывания подтвердилось. Сделали все прерывания одинакового приоритета hard fault исчез.

Вроде как баг исчез и стоит порадоваться, но очень похоже на черную магию, так как логического объяснения такому поведению не можем дать. Сохранение регистров в стек и восстановление везде присутствует (как минимум тщательно проверил сохранение\восстановление контекста в функции где упал в hard fault и тела высокоприоритетного прерывания).

Кто-нибудь может дать теоретически обоснованную модель такому поведению, чтобы понять куда еще стоит посмотреть?
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jun 21 2016, 13:19
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(xelax @ Jun 21 2016, 19:08) *
Кто-нибудь может дать теоретически обоснованную модель такому поведению, чтобы понять куда еще стоит посмотреть?

Посмотрите размер стека прерываний.
Ассемблерные вставки есть?
Go to the top of the page
 
+Quote Post
xelax
сообщение Jun 21 2016, 14:02
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 370
Регистрация: 7-11-06
Пользователь №: 22 035



Сразу хочу извиниться дизасм сделал гнусным objdump, так как кейловским fromelf пользоваться не очень умею. В принципе разницы быть не должно, так как исходный файл в elf формате.
hard fault был после вызова по адресу 1324c, так как в r0 была ересь, которая попала туда из r5.
Самое примечательное, что до той строчки внутри функции все было ок, то есть в r5 ереси не было. Сама функция uart_irq_handler является прерывание, но с низким приоритетом, то есть ее вытеснить и испоганить r5 могло только прерывание с высоким приоритетом. r5 нигде не пишется кроме адреса 13174.

Сам код собственно даже не наш, а взят из SDK Nordic.

CODE
#if defined(UART_IN_USE)
__STATIC_INLINE void uart_irq_handler()
{
13170: e92d 4bff stmdb sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, fp, lr}
if (nrf_uart_int_enable_check(NRF_UART0, NRF_UART_INT_MASK_ERROR) &&
13174: 4d43 ldr r5, [pc, #268]; (13284 <uart_irq_handler+0x114>)
}


#if defined(UART_IN_USE)
__STATIC_INLINE void uart_irq_handler()
{
13176: f10d 0b2c add.w fp, sp, #44; 0x2c
if (nrf_uart_int_enable_check(NRF_UART0, NRF_UART_INT_MASK_ERROR) &&
1317a: f44f 7100 mov.w r1, #512; 0x200
1317e: 4628 mov r0, r5
13180: f7fc f920 bl f3c4 <nrf_uart_int_enable_check>
13184: 2701 movs r7, #1
nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_ERROR))
{
nrf_drv_uart_event_t event;
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_ERROR);
nrf_uart_int_disable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_ERROR);
if (!m_cb.rx_enabled)
13186: 4c40 ldr r4, [pc, #256]; (13288 <uart_irq_handler+0x118>)
if (nrf_uart_int_enable_check(NRF_UART0, NRF_UART_INT_MASK_ERROR) &&
nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_ERROR))
{
nrf_drv_uart_event_t event;
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_ERROR);
nrf_uart_int_disable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_ERROR);
13188: f44f 7801 mov.w r8, #516; 0x204
1318c: 2600 movs r6, #0


#if defined(UART_IN_USE)
__STATIC_INLINE void uart_irq_handler()
{
if (nrf_uart_int_enable_check(NRF_UART0, NRF_UART_INT_MASK_ERROR) &&
1318e: b318 cbz r0, 131d8 <uart_irq_handler+0x68>
nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_ERROR))
13190: f44f 7192 mov.w r1, #292; 0x124
13194: 4628 mov r0, r5
13196: f7fc f907 bl f3a8 <nrf_uart_event_check>
1319a: b1e8 cbz r0, 131d8 <uart_irq_handler+0x68>
1319c: f8c5 6124 str.w r6, [r5, #292]; 0x124
131a0: f8c5 8308 str.w r8, [r5, #776]; 0x308
{
nrf_drv_uart_event_t event;
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_ERROR);
nrf_uart_int_disable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_ERROR);
if (!m_cb.rx_enabled)
131a4: 7ea0 ldrb r0, [r4, #26]
131a6: b900 cbnz r0, 131aa <uart_irq_handler+0x3a>
131a8: 606f str r7, [r5, #4]
{
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STOPRX);
}
event.type = NRF_DRV_UART_EVT_ERROR;
131aa: 2002 movs r0, #2
131ac: f80b 0c2c strb.w r0, [fp, #-44]
131b0: f8d5 0480 ldr.w r0, [r5, #1152]; 0x480
131b4: f8c5 0480 str.w r0, [r5, #1152]; 0x480
event.data.error.error_mask = nrf_uart_errorsrc_get_and_clear(NRF_UART0);
131b8: f84b 0c20 str.w r0, [fp, #-32]
event.data.error.rxtx.bytes = m_cb.rx_buffer_length;
131bc: 7de0 ldrb r0, [r4, #23]
131be: f80b 0c24 strb.w r0, [fp, #-36]
event.data.error.rxtx.p_data = m_cb.p_rx_buffer;
131c2: 68e0 ldr r0, [r4, #12]
131c4: f84b 0c28 str.w r0, [fp, #-40]

//abort transfer
m_cb.rx_buffer_length = 0;
131c8: 75e6 strb r6, [r4, #23]
m_cb.rx_secondary_buffer_length = 0;
131ca: 7626 strb r6, [r4, #24]

m_cb.handler(&event,m_cb.p_context);
131cc: e9d4 1200 ldrd r1, r2, [r4]
131d0: f1ab 002c sub.w r0, fp, #44; 0x2c
131d4: 4790 blx r2
}
131d6: e024 b.n 13222 <uart_irq_handler+0xb2>
else if (nrf_uart_int_enable_check(NRF_UART0, NRF_UART_INT_MASK_RXDRDY) &&
131d8: 2104 movs r1, #4
131da: 4628 mov r0, r5
131dc: f7fc f8f2 bl f3c4 <nrf_uart_int_enable_check>
131e0: b1f8 cbz r0, 13222 <uart_irq_handler+0xb2>
nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_RXDRDY))
131e2: f44f 7184 mov.w r1, #264; 0x108
131e6: 4628 mov r0, r5
131e8: f7fc f8de bl f3a8 <nrf_uart_event_check>
131ec: b1c8 cbz r0, 13222 <uart_irq_handler+0xb2>
{
rx_byte();
131ee: f7fe f89b bl 11328 <rx_byte>
if (m_cb.rx_buffer_length == m_cb.rx_counter)
131f2: 7de0 ldrb r0, [r4, #23]
131f4: 7e61 ldrb r1, [r4, #25]
131f6: 4288 cmp r0, r1
131f8: d113 bne.n 13222 <uart_irq_handler+0xb2>
{
if (m_cb.rx_secondary_buffer_length)
131fa: 7e22 ldrb r2, [r4, #24]
131fc: b13a cbz r2, 1320e <uart_irq_handler+0x9e>
{
uint8_t * p_data = m_cb.p_rx_buffer;
uint8_t rx_counter = m_cb.rx_counter;
131fe: 7e60 ldrb r0, [r4, #25]
13200: 68e1 ldr r1, [r4, #12]

//Switch to secondary buffer.
m_cb.rx_buffer_length = m_cb.rx_secondary_buffer_length;
13202: 75e2 strb r2, [r4, #23]
m_cb.p_rx_buffer = m_cb.p_rx_secondary_buffer;
13204: 6922 ldr r2, [r4, #16]
m_cb.rx_secondary_buffer_length = 0;
13206: 60e2 str r2, [r4, #12]
13208: 7626 strb r6, [r4, #24]
m_cb.rx_counter = 0;
1320a: 7666 strb r6, [r4, #25]
rx_done_event(rx_counter, p_data);
}
1320c: e007 b.n 1321e <uart_irq_handler+0xae>
else
{
if (!m_cb.rx_enabled)
1320e: 7ea0 ldrb r0, [r4, #26]
13210: b900 cbnz r0, 13214 <uart_irq_handler+0xa4>
13212: 606f str r7, [r5, #4]
13214: f8c5 8308 str.w r8, [r5, #776]; 0x308
{
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STOPRX);
}
nrf_uart_int_disable(NRF_UART0, NRF_UART_INT_MASK_RXDRDY | NRF_UART_INT_MASK_ERROR);
m_cb.rx_buffer_length = 0;
13218: 75e6 strb r6, [r4, #23]
rx_done_event(m_cb.rx_counter, m_cb.p_rx_buffer);
1321a: 7e60 ldrb r0, [r4, #25]
1321c: 68e1 ldr r1, [r4, #12]
//Switch to secondary buffer.
m_cb.rx_buffer_length = m_cb.rx_secondary_buffer_length;
m_cb.p_rx_buffer = m_cb.p_rx_secondary_buffer;
m_cb.rx_secondary_buffer_length = 0;
m_cb.rx_counter = 0;
rx_done_event(rx_counter, p_data);
1321e: f7fe f8a3 bl 11368 <rx_done_event>
rx_done_event(m_cb.rx_counter, m_cb.p_rx_buffer);
}
}
}

if (nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_TXDRDY))
13222: f44f 718e mov.w r1, #284; 0x11c
13226: 4628 mov r0, r5
13228: f7fc f8be bl f3a8 <nrf_uart_event_check>
1322c: b158 cbz r0, 13246 <uart_irq_handler+0xd6>
{
if (m_cb.tx_counter < (uint16_t) m_cb.tx_buffer_length)
1322e: 8aa1 ldrh r1, [r4, #20]
13230: 7da0 ldrb r0, [r4, #22]
13232: 4281 cmp r1, r0
13234: d202 bcs.n 1323c <uart_irq_handler+0xcc>
{
tx_byte();
13236: f7ff fea1 bl 12f7c <tx_byte>
1323a: e004 b.n 13246 <uart_irq_handler+0xd6>
1323c: f8c5 611c str.w r6, [r5, #284]; 0x11c
}
else
{
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_TXDRDY);
if (m_cb.tx_buffer_length)
13240: b108 cbz r0, 13246 <uart_irq_handler+0xd6>
{
tx_done_event(m_cb.tx_buffer_length);
13242: f7ff feb5 bl 12fb0 <tx_done_event>
}
}
}

if (nrf_uart_event_check(NRF_UART0, NRF_UART_EVENT_RXTO))
13246: f44f 71a2 mov.w r1, #324; 0x144
1324a: 4628 mov r0, r5
1324c: f7fc f8ac bl f3a8 <nrf_uart_event_check>
13250: 46dd mov sp, fp
13252: 2800 cmp r0, #0
13254: b08b sub sp, #44; 0x2c
13256: d013 beq.n 13280 <uart_irq_handler+0x110>
13258: f8c5 6144 str.w r6, [r5, #324]; 0x144
{
nrf_uart_event_clear(NRF_UART0, NRF_UART_EVENT_RXTO);

// RXTO event may be triggered as a result of abort call. In th
if (m_cb.rx_enabled)
1325c: 7ea0 ldrb r0, [r4, #26]
1325e: b100 cbz r0, 13262 <uart_irq_handler+0xf2>
13260: 602f str r7, [r5, #0]
{
nrf_uart_task_trigger(NRF_UART0, NRF_UART_TASK_STARTRX);
}
if (m_cb.rx_buffer_length)
13262: 7de0 ldrb r0, [r4, #23]
13264: 46dd mov sp, fp
13266: 2800 cmp r0, #0
13268: b08b sub sp, #44; 0x2c
1326a: d009 beq.n 13280 <uart_irq_handler+0x110>
{
m_cb.rx_buffer_length = 0;
1326c: 75e6 strb r6, [r4, #23]
rx_done_event(m_cb.rx_counter, m_cb.p_rx_buffer);
1326e: 7e60 ldrb r0, [r4, #25]
13270: 68e1 ldr r1, [r4, #12]
13272: b004 add sp, #16
13274: e8bd 4bf0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9, fp, lr}
13278: f7fe b876 b.w 11368 <rx_done_event>
1327c: 46dd mov sp, fp
1327e: b08b sub sp, #44; 0x2c
}
}
}
13280: e8bd 8bff ldmia.w sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, fp, pc}
13284: 40002000 .word 0x40002000
13288: 200011d8 .word 0x200011d8


Цитата(jcxz @ Jun 21 2016, 16:19) *
Посмотрите размер стека прерываний.
Ассемблерные вставки есть?

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

Ассемблерные вставки не используем.

Сообщение отредактировал IgorKossak - Jun 21 2016, 18:24
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jun 21 2016, 14:05
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(xelax @ Jun 21 2016, 19:50) *
Сразу хочу извиниться дизасм сделал гнусным objdump, так как кейловским fromelf пользоваться не очень умею. В принципе разницы быть не должно, так как исходный файл в elf формате.
13170: e92d 4bff stmdb sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, fp, lr}
if (nrf_uart_int_enable_check(NRF_UART0, NRF_UART_INT_MASK_ERROR) &&
13174: 4d43 ldr r5, [pc, #268]; (13284 <uart_irq_handler+0x114>)
}


#if defined(UART_IN_USE)
__STATIC_INLINE void uart_irq_handler()
{
13176: f10d 0b2c add.w fp, sp, #44; 0x2c
...

Мусор это какой-то..... Что за регистр fp? Зачем на входе в ISR сохранять так много регистров?
И зачем Вы это привели? У Вас ISR-ы на асме написаны?

Цитата(xelax @ Jun 21 2016, 20:02) *
На стек 1024 байта выделили, так что по идее должно хватить, тем более модель памяти держим сходящуюся. Глобальные переменные с одного края памяти, стек с другого, так что даже при превышении размера стека, проедем по неиспользуемой памяти.

Я спрашивал не про "стек фонового процесса", а про "стек прерываний". У Cortex-M есть MSP и PSP. У Вас в каком режиме CPU фоновый процесс работает?
Go to the top of the page
 
+Quote Post
xelax
сообщение Jun 21 2016, 14:22
Сообщение #5


Местный
***

Группа: Свой
Сообщений: 370
Регистрация: 7-11-06
Пользователь №: 22 035



Цитата(jcxz @ Jun 21 2016, 17:05) *
Мусор это какой-то..... Что за регистр fp?

К сожалению objdump трактует r11 как fp.

Цитата(jcxz @ Jun 21 2016, 17:05) *
Зачем на входе в ISR сохранять так много регистров?

Не знаю, это Keil делает.

Цитата(jcxz @ Jun 21 2016, 17:05) *
И зачем Вы это привели?

Показать в каком коде происходит hard fault. Возможно разговор пойдет предметный, а не абстрактный.

Цитата(jcxz @ Jun 21 2016, 17:05) *
У Вас ISR-ы на асме написаны?

нет

Цитата(jcxz @ Jun 21 2016, 17:05) *
Я спрашивал не про "стек фонового процесса", а про "стек прерываний". У Cortex-M есть MSP и PSP. У Вас в каком режиме CPU фоновый процесс работает?

мы не переключаем в PSP, работаем в MSP. Ось не используем.
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jun 21 2016, 15:54
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(xelax @ Jun 21 2016, 20:22) *
мы не переключаем в PSP, работаем в MSP. Ось не используем.

Если нет асм-кода и нет переполнения стека, то возможно где-то есть разрушение переменных в стеке (и R5 восстанавливается из стека).
Если Вы нашли точку, где портится R5, то там и надо проверить.
Go to the top of the page
 
+Quote Post
romas2010
сообщение Jun 21 2016, 16:37
Сообщение #7


Участник
*

Группа: Участник
Сообщений: 63
Регистрация: 25-11-11
Пользователь №: 68 515



Цитата(xelax @ Jun 21 2016, 16:08) *
Исходные данные.
Код для cortex-m4 (nrf52), язык С, компилятор Keil.
.......
Второе предположение что поведение ломают вложенные прерывания подтвердилось. Сделали все прерывания одинакового приоритета hard fault исчез.
.....
Кто-нибудь может дать теоретически обоснованную модель такому поведению, чтобы понять куда еще стоит посмотреть?


Судя по приведенному ниже дизассемблеру, код кейл скомпилировал с опцией --use_frame_pointer. C этой опцией кейл генерит некорректный выход из функции..Попробуйте скомпилировать без нее...и еще нюанс-отключите оптимизацию,то есть --O0
Go to the top of the page
 
+Quote Post
xelax
сообщение Jun 22 2016, 13:11
Сообщение #8


Местный
***

Группа: Свой
Сообщений: 370
Регистрация: 7-11-06
Пользователь №: 22 035



Цитата(romas2010 @ Jun 21 2016, 19:37) *
Судя по приведенному ниже дизассемблеру, код кейл скомпилировал с опцией --use_frame_pointer. C этой опцией кейл генерит некорректный выход из функции..Попробуйте скомпилировать без нее...и еще нюанс-отключите оптимизацию,то есть --O0

Да мы действительно используем --use_frame_pointer. r11 как раз компилятором отводится под frame pointer. Можно поподробней о некорректном выходе из функции? есть какая-нибудь errata или ссылка на описание проблемы?
И оптимизацию зачем выключать? Мы без нее на финальном этапе проекта не влезем в размер памяти, отведенный нам по ТЗ.
Go to the top of the page
 
+Quote Post
xelax
сообщение Jun 22 2016, 14:12
Сообщение #9


Местный
***

Группа: Свой
Сообщений: 370
Регистрация: 7-11-06
Пользователь №: 22 035



Убрал опцию --use_frame_pointer, вернул вложенные прерывания, оставил максимальную оптимизацию --O3. Hard fault исчезли.
Вернул опцию, буквально несколько секунд и софт падает в Hard fault.

Спасибо, судя по всему, действительно идет некорректная работа со стеком с включенной опцией.
Go to the top of the page
 
+Quote Post
romas2010
сообщение Jun 22 2016, 15:36
Сообщение #10


Участник
*

Группа: Участник
Сообщений: 63
Регистрация: 25-11-11
Пользователь №: 68 515



Цитата(xelax @ Jun 22 2016, 17:12) *
Убрал опцию --use_frame_pointer, вернул вложенные прерывания, оставил максимальную оптимизацию --O3. Hard fault исчезли.
Вернул опцию, буквально несколько секунд и софт падает в Hard fault.

Спасибо, судя по всему, действительно идет некорректная работа со стеком с включенной опцией.


Фишка вот в чем
непонятно зачем кейл двигает указатель стека SP на FP в командах
mov sp,fp
sub sp,#const

Если между этими командами возникнет прерывание,стек будет уже смещен,и контекст возникшего прерывания затрет контекст,сохраненный ранее при входе..я понимаю,мои объяснения звучат сумбурно,но возьмите лист бумаги,разрисуйте стек,идите по коду,там,где есть в командах использование sp и fp, то "разрисовывайте" эти регистры,и вы увидите....учитывайте,что стек растет ото дна

stmdb sp!,{r0...lr} -> sp=sp-4;RAM[sp]=r0 и т.д.
ldmia sp!,{r0..lr}-> r0=RAM[sp]; sp=sp+4

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

Сообщение отредактировал romas2010 - Jun 22 2016, 15:53
Go to the top of the page
 
+Quote Post
GetSmart
сообщение Jun 22 2016, 18:13
Сообщение #11


.
******

Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753



Цитата(xelax @ Jun 22 2016, 17:11) *
Да мы действительно используем --use_frame_pointer. r11 как раз компилятором отводится под frame pointer.

Зачем, если исходник не заточен строго под Кейл?

И в чём смысл STATIC_INLINE для обработчика прерывания? Как у кортексов, так и у других ядер ARM, обработчик всегда есть независимая от остального кода функция. Причём тут инлайн?


--------------------
Заблуждаться - Ваше законное право :-)
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 18th July 2025 - 14:21
Рейтинг@Mail.ru


Страница сгенерированна за 0.01484 секунд с 7
ELECTRONIX ©2004-2016