Программирование ARM: решение проблем, FAQ IAR EW ARM: программа вылетает в Abort_Handler Thu, October 19 2017  

Поделиться

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

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


IAR EW ARM: программа вылетает в Abort_Handler Печать
Добавил(а) microsin   

Обычно Abort Handler срабатывает, когда произошло некорректное обращение к данным (чтение или запись) - программа попыталась что-то сделать с данными по адресу, который не существует.

При этом срабатывает исключение Data Abort, которое заставляет процессор ARM перейти по фиксированному адресу 0x00000010, где, в свою очередь, стоит безусловный переход в обработчик Abort_Handler. Например, по адресу 0x00000010 может быть команда LDR PC, [PC, #+20], загружающая в счетчик команд адрес из таблицы векторов переходов по исключению (адрес находится по смещению +20 относительно текущего счетчика команд. Назовем этот адрес меткой Abort Handler:). По этому месту компилятор ставит тупое зацикливание:
Abort Handler:
    B Abort_Handler

При срабатывании Abort Handler можно посмотреть, откуда это произошло (какой код вызвал ошибку). Для этого нужно в отладчике остановить выполнение программы (курсор текущего местоположения окна Disassembler должен показывать на Abort_Handler), и посмотреть содержимое регистра LR (Link Register - в нем сохраняется содержимое программного счетчика при вызове подпрограмм) - в нем будет находится адрес, где сработало исключение Data Abort.

Несмотря на то, что есть такое удобное средство, чтобы выявить источник ошибки, иногда разобраться в ошибке бывает нелегко, особенно если она возникает случайно или в том месте, где работает чужой код. У меня такое произошло в обработчике прерывания, обслуживающее прерывание по изменению уровня на ножках ввода/вывода. Этот обработчик я взял из примеров IAR, так как там было очень удобно сделана обработка изменения уровней на отдельных ножках - для каждой ножки можно назначить свой обработчик. Вот код, который у меня вызывал Data Abort (pio_it.c):
//------------------------------------------------------------------------------
/// Handles all interrupts on the given PIO controller.
/// \param id  PIO controller ID.
/// \param pBase  PIO controller base address.
//------------------------------------------------------------------------------
void PioInterruptHandler (unsigned int id, AT91S_PIO *pBase)
{
    unsigned int status;
    unsigned int i;

    // Check PIO controller status
    status = pBase->PIO_ISR;
    status &= pBase->PIO_IMR;
    if (status != 0)
    {
        trace_LOG(trace_DEBUG, "-D- PIO interrupt on PIO controller #%d\n\r", id);

        // Check all sources
        i = 0;
        while (status != 0)
        {
            // There cannot be an unconfigured source enabled.
            SANITY_CHECK(i < numSources);

            // Source if configured on PIOA
            if (pSources[i].pPin->id == id)     //в этом месте у меня срабатывал Data Abort
            {
                // Source has PIOs which have changed
                if ((status & pSources[i].pPin->mask) != 0)
                {
                    trace_LOG(trace_DEBUG, "-D- Interrupt source #%d triggered\n\r", i);

                    pSources[i].handler(pSources[i].pPin);
                    status &= ~(pSources[i].pPin->mask);
                }
            }
            i++;
        }
    }
}

Ошибка происходила случайно, и локализовать её обычными средствами не удавалось. Причина была в переполнении массива static InterruptSource pSources[MAX_INTERRUPT_SOURCES] (pio_it.c). У меня при задании нового источника прерывания количество прерываний numSources превышало MAX_INTERRUPT_SOURCES (стало 8, а MAX_INTERRUPT_SOURCES было равно 7), и происходило обращение к переменной вне массива (if (pSources[i].pPin->id == id)), что и вызывало Data Abort. Макрос ASSERT, который проверял переполнение массива при инициализации, я отключил, поэтому процессор не выдавал ошибки переполнения массива. Увеличил константу MAX_INTERRUPT_SOURCES до 8, и все встало на свои места.

[Общая схема - как ловить ошибки при зависании Abort_Handler]

1. В отладчике нужно посмотреть содержимое LR (адрес) - оттуда произошло зависание.
2. В подпрограмме (которую находим по адресу в LR), где произошло зависание, нужно поставить условную точку останова. Условие останова нужно подобрать такое, при котором должно произойти зависание (чтобы оно сработало до зависания). После попадания в точку останова нужно изучить неверные параметры вызова функции, являющиеся причиной зависания. Потом нужно изучить код, который стал причиной появления таких неверных параметров.
3. Если создать условие останова трудно (условие сложное), то подготавливают глобальные переменные, куда сохраняют все параметры исследуемой (вызывающей зависание) функции. После того, как произошло исключение Abort_Handler, изучают содержимое сохраненных параметров функции, и по их содержимому вычисляют причину ошибки. 

[Ссылки]

1. Как повесить ARM AT91SAM7X256
2. IAR EW ARM: устранение ошибок компиляции
3. IAR EW ARM: устранение ошибок с отображением регистров
4. IAR EW ARM: применение #ifndef и #define для разрешения конфликтов включаемых файлов (#include)
5. IAR EW ARM: Error[Pe147]: declaration is incompatible...
6. IAR EW ARM: пути с $PROJ_DIR$ не работают, почему?

 

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


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

Top of Page