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

Исключение Hard Fault на Cortex-M3

17 минут назад, EdgeAligned сказал:

Напишите обработчик прерывания  void HardFault_Handler(void){ while(1) ; } и поставьте на нем брейкпоинт, запустите отладку.

void HardFault_Handler(void)
{
    volatile int i = 0;
    while(!i)
        ;
}

Попав в него отладчиком ставим i = 1 и шагая по ассемблерным командам выходим из этого обработчика. Попадаем на следующую инструкцию за вызвавшей исключение. Смотрим на нее (вызвавшую исключение), на содержимое задействованных в ней регистров, медитируем. 

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


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

ну так смотрите по регистрам кто туда полез по этому адресу и разматывайте в обратную сторону ход программы 

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


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

1 hour ago, koluna said:

там два устройства, второе устройство обменивается по USART с глючащим устройством...

В функции приема по usart есть ограничение диапазона памяти для записи принимаемых данных? Встречался с такой проблемой - шум на линии, либо второе устройство глючит и выдает посылку длиннее чем надо - и контроллер пишет принимаемые байты по инкрементируемому указателю, попадая в итоге в несуществующую память и уходя в hardfault.

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


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

Это не относится именно к UART-у. Это - программная ошибка в построении кода на приеме байтов.

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


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

On 8/13/2024 at 11:41 AM, Priest_89 said:

В функции приема по usart есть ограничение диапазона памяти для записи принимаемых данных? Встречался с такой проблемой - шум на линии, либо второе устройство глючит и выдает посылку длиннее чем надо - и контроллер пишет принимаемые байты по инкрементируемому указателю, попадая в итоге в несуществующую память и уходя в hardfault.

Данные пишутся в массив 2 кБ, но на самом деле данных меньше гораздо. Устройства соединены кабелем 0.5 м, на столе все лежит...
В принимаемом пакете есть информация о длине пакета. Если значение больше размера буфера - пакет игнорируется, сообщается об ошибке.
Иначе - обработчик считает принятые байты (одновременно их записывая в массив) и как только примет необходимое количество байт - прием пакета заканчивается. Дальше проверка контрольной суммы и разбор пакета.

Обработчик прерывания:

void USART2_IRQHandler(void)
{
    uint16_t status = USART2->SR;                                               // Получаем флаги.

    if(status & USART_SR_RXNE)
    {                                                                           // Прерывание: регистр данных приемника не пуст.
                                                                                // RXNE сбрасывается автоматически после чтения USART_DR или записью нуля.
        exchRXNEHandler();
    }
    else if(status & USART_SR_TXE)
    {                                                                           // Прерывание: регистр данных передатчика пуст.
                                                                                // TXE сбрасывается автоматически при записи в USART_DR.
        exchTXEHandler();
    }
}

Функция, вызываемая из него для обработки прерывания:

void exchRXNEHandler(void)
{
    static uint16_t packetLength;
    static uint32_t startTime;
    static enum
    {
        RX_ISR_START_MARKER1,
        RX_ISR_START_MARKER2,
        RX_ISR_START_MARKER3,
        RX_ISR_START_LENGTH1,
        RX_ISR_START_LENGTH2,
        RX_ISR_RECEIVE_DATA
    } rxIsrState = RX_ISR_START_MARKER1;

    uint8_t data = exchReadByte();                                              // Считываем принятый байт.

    switch(rxIsrState)
    {
        case RX_ISR_START_MARKER1:                                              // Ожидаем первый символ МНП.
            if(EXCH_MARKER1 == data)
            {
                rxIsrState = RX_ISR_START_MARKER2;
                exchBuff[rxCnt++] = data;
            }
            break;

        case RX_ISR_START_MARKER2:                                              // Ожидаем второй символ МНП.
            if(EXCH_MARKER2 == data)
            {
                rxIsrState = RX_ISR_START_MARKER3;
                exchBuff[rxCnt++] = data;
            }
            else
            {                                                                   // Принят другой символ - начинаем снова.
                rxIsrState = RX_ISR_START_MARKER1;
                rxCnt = 0;
            }
            break;

        case RX_ISR_START_MARKER3:                                              // Ожидаем третий символ МНП.
            if(EXCH_MARKER3 == data)
            {
                rxIsrState = RX_ISR_START_LENGTH1;
                exchBuff[rxCnt++] = data;
            }
            else
            {                                                                   // Принят другой символ - начинаем снова.
                rxIsrState = RX_ISR_START_MARKER1;
                rxCnt = 0;
            }
            break;

        case RX_ISR_START_LENGTH1:                                              // Ожидаем младший байт длины пакета.
            rxIsrState = RX_ISR_START_LENGTH2;
            exchBuff[rxCnt++] = data;
            packetLength = data;
            break;

        case RX_ISR_START_LENGTH2:                                              // Ожидаем старший байт длины пакета.
            exchBuff[rxCnt++] = data;
            packetLength |= (uint16_t) data << 8;

            if(packetLength > EXCH_BUFF_SIZE)
            {                                                                   // Длина больше допустимой, прием завершаем!
                rxComplete = true;
                rxOverflow = true;
                exchRxneDisable();
                rxCnt = 0;
                rxIsrState = RX_ISR_START_MARKER1;
            }
            else
            {
                rxIsrState = RX_ISR_RECEIVE_DATA;
                startTime = getTickCount();                                     // Инициализируем таймер для обнаружения таймаута приема оставшихся данных.
            }
            break;

        case RX_ISR_RECEIVE_DATA:
            if(calcTicks(startTime) > EXCH_RECEIVE_TIMEOUT)
            {                                                                   // Таймаут при приеме, прием завершаем!
                rxComplete = true;
                rxTimeout = true;
                exchRxneDisable();
                rxCnt = 0;
                rxIsrState = RX_ISR_START_MARKER1;
            }
            else
            {                                                                   // Укладываемся во временные рамки при приеме.
                exchBuff[rxCnt++] = data;
                if(rxCnt == packetLength)
                {                                                               // Пакет полностью принят.
                    rxComplete = true;
                    exchRxneDisable();
                    rxCnt = 0;
                    rxIsrState = RX_ISR_START_MARKER1;
                }
            }
            break;

        default:
            break;
    }
}

А вот так запускается прием:

static PT_THREAD(waitForRequest(PT* pt, EXCH_Result* exchRes))
{
    PT_BEGIN(pt);

    memset(exchBuff, 0, sizeof(exchBuff));
    *exchRes = EXCH_UNEXPECTED_RES;
    rxCnt = 0;
    rxComplete = false;
    rxOverflow = false;
    rxTimeout = false;
    exchReadByte();
    exchRxneEnable();

    dbgout(LOG_DEBUG, "[%s()] Wait for packet... ", __func__);
    static uint32_t dbgTm;
    dbgStartTime(&dbgTm);
    PT_WAIT_UNTIL(pt, rxComplete);
    rxComplete = false;
    dbgout(LOG_DEBUG, "%u\n", dbgStopTime(&dbgTm));

    if(rxOverflow)
    {                                                                           // Переполнение приемного буфера!
        rxOverflow = false;
        *exchRes = EXCH_OVERFLOW;
    }
    else if(rxTimeout)
    {                                                                           // Таймаут при приеме пакета!
        rxTimeout = false;
        *exchRes = EXCH_TIMEOUT;
    }
    else if(!checkCrc())
    {
        *exchRes = EXCH_CRC_ERROR;
    }
    else
    {                                                                           // Пакет принят нормально.
        *exchRes = EXCH_OK;
    }

    uint16_t packetLength = getPacketLength();
    dbgout(LOG_DEBUG, "\n[%s()] Result: %u, Received packet length: %u\n", __func__, *exchRes, packetLength);

    PT_END(pt);
}

 

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


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

Чтож, повторю еще раз анекдот:

--- Ты чего тут по полу ползаешь?
--- Да я вон там ключи потерял, ищу...
--- Так и ищи там, где потерял
--- Так здесь светлее же!

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


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

rxCnt нигде не проверяется. используйте assert.

вы приняли неизвестно что

         rxIsrState = RX_ISR_START_LENGTH2;
            exchBuff[rxCnt++] = data;
            packetLength = data;
                exchBuff[rxCnt++] = data;
                if(rxCnt == packetLength)
                {                                                               // Пакет полностью принят.
                    rxComplete = true;
                    exchRxneDisable();
                    rxCnt = 0;
                    rxIsrState = RX_ISR_START_MARKER1;
                }

и пытаетесь неизвестно куда это записать.

Все такие вещи надо проверять на выход за пределы массива

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


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

14 часов назад, Arlleex сказал:

Считайте BFAR и в нем будет адрес инструкции, которая вызвала ошибку.

Не обязательно. Только если соответствующий бит валидности установлен.

МК ТС-а имеет буфер записи? Такие простейшие - имеют его? Не работал, не в курсе... Но если имеет, то запросто может быть "неточная ошибка при обращении к данным" (imprecisible bus error) в случае записи куда не следует. А при ней нет валидного адреса fault-а.

 

ТС-у: Открыть мануал на ядро Cortex-M и прочитать про обработку исключений. Там всё разжёвано предельно ясно. 

13 часов назад, koluna сказал:
[HardFault_Handler()] BFAR=x20002000

Да, это Bus Fault, я уже понял. И адрес RAM. В просессоре 8 кБ, как раз на конец указывает, только вот что это значит...

Вроде как очевидная высокая вероятность, что какой-то цикл у вас начал писать с автоинкрементом в память без берегов. Пока не дошёл до границы ОЗУ. Где и вылетел в fault.

даже не заглядывая в код...

1 час назад, EdgeAligned сказал:

Напишите обработчик прерывания  void HardFault_Handler(void){ while(1) ; } и поставьте на нем брейкпоинт, запустите отладку. При остановке посмотрите отладочную инфу с указанием адреса инструкции, вызвавшей "тяжелый сбой". Откройте листинг кода и по адресу найдите, какая инструкция и к чему (какому куску кода) она принадлежит. Далее будет видно.

Гораздо проще.

Раз выяснено, что имеется bus fault по обращению к конкретному адресу, то просто ставим data-бряк на обращение к 0x20002000 по записи или чтению. И получаем останов в точке бага.

2-й совет ТС-у: Изучить возможности собственных средств отладки.

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


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

57 минут назад, koluna сказал:

Данные пишутся в массив 2 кБ

"Массив 2 кБ" - это 2 килобита или 2 килобайта?

59 минут назад, koluna сказал:

В принимаемом пакете есть информация о длине пакета. Если значение больше размера буфера - пакет игнорируется, сообщается об ошибке.

Что есть неправильное действие.

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


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

1 hour ago, koluna said:

Иначе - обработчик считает принятые байты (одновременно их записывая в массив) и как только примет необходимое количество байт - прием пакета заканчивается. Дальше проверка контрольной суммы и разбор пакета.

А что будет, если "необходимое количество байт" в переменной packetLenght будет меньше пяти?

 

И вообще, было б крайне полезно отладчиком посмотреть на содержимое приемного буфера (и следующих за ним байт) после того, как всё грохнулось.

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


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

On 8/13/2024 at 12:34 PM, jcxz said:

Не обязательно. Только если соответствующий бит валидности установлен.

Установлен.

On 8/13/2024 at 12:34 PM, jcxz said:

МК ТС-а имеет буфер записи?

exchBuf в коде, в него данные записываются при приеме, а потом ответ в него записывается при передаче.

On 8/13/2024 at 12:34 PM, jcxz said:

ТС-у: Открыть мануал на ядро Cortex-M и прочитать про обработку исключений. Там всё разжёвано предельно ясно. 

Читал Джозефа Ю, смотрел даташит... 

On 8/13/2024 at 12:34 PM, jcxz said:

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

Да вот не найду я этот цикл никак...

On 8/13/2024 at 12:34 PM, jcxz said:

то просто ставим data-бряк на обращение к 0x20002000 по записи или чтению. И получаем останов в точке бага.

Что Вы имеете в виду? Настройка отладчика или что-то еще?

Так-то по стеку вызовов в отладчике видно где оно падает, но вот почему оно там падает - пока не понял...

On 8/13/2024 at 12:51 PM, jcxz said:

"Массив 2 кБ" - это 2 килобита или 2 килобайта?

2 килобайта.

 

On 8/13/2024 at 12:51 PM, jcxz said:

Что есть неправильное действие.

Как было бы правильнее? 

assert не помогает с rxCnt. Да я и до этого его мониторил...

On 8/13/2024 at 11:06 AM, Сергей Борщ said:
void HardFault_Handler(void)
{
    volatile int i = 0;
    while(!i)
        ;
}

Попав в него отладчиком ставим i = 1 и шагая по ассемблерным командам выходим из этого обработчика. Попадаем на следующую инструкцию за вызвавшей исключение. Смотрим на нее (вызвавшую исключение), на содержимое задействованных в ней регистров, медитируем. 

Делал так, не выходит из исключения и ведет себя как-то странно... как бы подвисает чтоли...

On 8/13/2024 at 12:59 PM, esaulenka said:

А что будет, если "необходимое количество байт" в переменной packetLenght будет меньше пяти?

Да, будет какая-то такая ошибка 🙂 Спасибо, учту. Но у меня логи пишутся со второго устройства, я вижу, что уходит то, что нужно, менее 8 не бывает 🙂
И логи с глючного устройства тоже вижу - там видно, что принимается оно нормально...

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


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

А вот и баг:

1 час назад, koluna сказал:
            if(packetLength > EXCH_BUFF_SIZE)
            {                                                                   // Длина больше допустимой, прием завершаем!
                rxComplete = true;
                rxOverflow = true;
                exchRxneDisable();
                rxCnt = 0;
                rxIsrState = RX_ISR_START_MARKER1;
            }
            else
            {
                rxIsrState = RX_ISR_RECEIVE_DATA;
                startTime = getTickCount();                                     // Инициализируем таймер для обнаружения таймаута приема оставшихся данных.
            }
            break;

        case RX_ISR_RECEIVE_DATA:
            if(calcTicks(startTime) > EXCH_RECEIVE_TIMEOUT)
            {                                                                   // Таймаут при приеме, прием завершаем!
                rxComplete = true;
                rxTimeout = true;
                exchRxneDisable();
                rxCnt = 0;
                rxIsrState = RX_ISR_START_MARKER1;
            }
            else
            {                                                                   // Укладываемся во временные рамки при приеме.
                exchBuff[rxCnt++] = data;
                if(rxCnt == packetLength)

При приёме нулевой длины пакета получите как раз переполнение ОЗУ. Что и наблюдается.

 

И здесь:

1 час назад, koluna сказал:
case RX_ISR_RECEIVE_DATA:
            if(calcTicks(startTime) > EXCH_RECEIVE_TIMEOUT)
            {             

Что за дикий способ контроля таймаута???  :punish:

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

 

PS: Совет: До того как лепить собственные деревянные лисапеды-"протоколы" с квадратными колёсами, следует изучить существующие стандартные. SLIP, COBS, ... Чего и вам советую. Они на голову выше вашего поделия. А всё что выше - выкинуть в мусорку.

29 минут назад, koluna сказал:

exchBuf в коде, в него данные записываются при приеме, а потом ответ в него записывается при передаче.

Речь шла о буфере записи в ядре (CPU). См. мануал на ядро. При наличии такого буфера, записи в недопустимые адреса часто возбуждают "imprecisible bus error" без адреса.

29 минут назад, koluna сказал:

Да вот не найду я этот цикл никак...

Я его уже нашёл. См. выше.

29 минут назад, koluna сказал:

Что Вы имеете в виду? Настройка отладчика или что-то еще?

Нормальные отладчики (типа J-Link) имеют возможность установки не только code-breakpoints, но и data-breakpoints. Речь шла о них. Если ваш их умеет, то это иногда позволяет быстро находить баги с записью по неверному известному адресу.

29 минут назад, koluna сказал:

Как было бы правильнее? 

При обнаружении превышения пакетом максимально допустимого размера, правильное действие: Осуществить полный приём всего пакета, без его сохранения в буфер. Т.е. - нормальный пропуск пакета. А в вашем случае при переполнении вы просто раскладываете себе грабли: Если в теле данных пакета случится наличие маркеров, то будет ложная сработка обнаружения пакета. Со всеми вытекающими. Ведь "протокол" у вас не кодонезависимый. В отличие от нормальных протоколов (COBS, SLIP).

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


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

24 minutes ago, koluna said:

я вижу, что уходит то

RXNE это не только правильный приём, но и overrun error туда может проскочить, если разрешено.

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


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

34 minutes ago, koluna said:

Что Вы имеете в виду? Настройка отладчика или что-то еще?

https://community.silabs.com/s/article/how-do-i-trigger-a-breakpoint-when-a-value-in-memory-is-read-and-or-written-x

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


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

On 8/13/2024 at 1:21 PM, jcxz said:

Нормальные отладчики (типа J-Link) имеют возможность установки не только code-breakpoints, но и data-breakpoints. Речь шла о них. Если ваш их умеет, то это иногда позволяет быстро находить баги с записью по неверному известному адресу.

У меня TE-ARM-Link + OpenOCD. С data-breakpoints не знаком пока...

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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