|
|
  |
Nested interrupts and Hard Fault, Происходит hard fault если есть вложенные прерывания |
|
|
|
Jun 21 2016, 13:08
|

Местный
  
Группа: Свой
Сообщений: 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 и тела высокоприоритетного прерывания).
Кто-нибудь может дать теоретически обоснованную модель такому поведению, чтобы понять куда еще стоит посмотреть?
|
|
|
|
|
Jun 21 2016, 14:02
|

Местный
  
Группа: Свой
Сообщений: 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] - для короткого!!!
|
|
|
|
|
Jun 21 2016, 14:05
|
Гуру
     
Группа: Свой
Сообщений: 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 фоновый процесс работает?
|
|
|
|
|
Jun 21 2016, 14:22
|

Местный
  
Группа: Свой
Сообщений: 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. Ось не используем.
|
|
|
|
|
Jun 21 2016, 16:37
|
Участник

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

|
Цитата(xelax @ Jun 21 2016, 16:08)  Исходные данные. Код для cortex-m4 (nrf52), язык С, компилятор Keil. ....... Второе предположение что поведение ломают вложенные прерывания подтвердилось. Сделали все прерывания одинакового приоритета hard fault исчез. ..... Кто-нибудь может дать теоретически обоснованную модель такому поведению, чтобы понять куда еще стоит посмотреть? Судя по приведенному ниже дизассемблеру, код кейл скомпилировал с опцией --use_frame_pointer. C этой опцией кейл генерит некорректный выход из функции..Попробуйте скомпилировать без нее...и еще нюанс-отключите оптимизацию,то есть --O0
|
|
|
|
|
Jun 22 2016, 13:11
|

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

|
Цитата(romas2010 @ Jun 21 2016, 19:37)  Судя по приведенному ниже дизассемблеру, код кейл скомпилировал с опцией --use_frame_pointer. C этой опцией кейл генерит некорректный выход из функции..Попробуйте скомпилировать без нее...и еще нюанс-отключите оптимизацию,то есть --O0 Да мы действительно используем --use_frame_pointer. r11 как раз компилятором отводится под frame pointer. Можно поподробней о некорректном выходе из функции? есть какая-нибудь errata или ссылка на описание проблемы? И оптимизацию зачем выключать? Мы без нее на финальном этапе проекта не влезем в размер памяти, отведенный нам по ТЗ.
|
|
|
|
|
Jun 22 2016, 15:36
|
Участник

Группа: Участник
Сообщений: 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
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|