Arlleex 158 29 мая Опубликовано 29 мая · Жалоба 6 минут назад, aaarrr сказал: где прекрасно сохранится дамп. В таком случае разве что самым значимым будет содержимое статусных регистров fault CPU. Содержимое регистров РОН - на втором месте. А на указатель стека уже ориентироваться, в общем-то, нельзя. А ведь по нему можно было бы развернуть какой-никакой стек вызовов. А это было бы куда интереснее, чем первый пункт. 4 минуты назад, makc сказал: Есть разные варианты, типа сначала прерывание, а потом ресет, если не отпустило. Зависит от модели МК, но в целом это по-моему есть приблизительно везде. Ну вот, например, STM32🙂 Вроде только WWDG имеет прерывание. Но программно настраиваемый вачдог - такое себе)) Около МК всегда (у меня) имеется аппаратный. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 211 30 мая Опубликовано 30 мая · Жалоба 5 часов назад, Arlleex сказал: или вовсе улетели по адресу, который допустим для исполнения прошивки (MPU не увидит в этом проблем), но в конечном счете все равно улетит в какой-нибудь хэндлер или lockup. Или банально улетит по адресу куска памяти, где разблокируется Flash и что-то перепрограммирует (такой кусок кода имеется практически в любом проекте). Или сразу улетит по адресу NVIC_SysytemReset(). Что-то странное рассказываете... При неконтролируемой передаче управления (например - вследствие разрушения стека) вероятность попасть в точку старта прошивки (это всего от 0 до нескольких байт после точки) - близка к вероятности выигрыша в лотерею суперприза. Просто достаточно посчитать - какова вероятность случайного выпадения заданного адреса в случайном 32-битном значении? "Разблокируется флешь" - вообще равно нулю. Так как в используемых мной сейчас XMC4xxx модифицировать внутреннюю флешь может только код, находящийся в ОЗУ. И копирует его туда бутлоадер после множества проверок и программирований MPU. Вероятность попасть в какой-то удачный адрес при неконтролируемой передаче управления - крайне мала. В 99% будет попадание в такое место, при котором будет сгенерирован HF (а соответственно - и последующий trap) или по недопустимой команде или по недопустимому обращению к памяти. Пишу это не от фонаря, а потому что использую это давно и много раз помогало. В том числе и при улётах неизвестно куда. Да, fault сработает не сразу, но по содержимому регистров CPU или дампов стеков как правило - можно быстро найти место начальной проблемы. 5 часов назад, Arlleex сказал: Аппаратный вачдог заводится напрямую на RESET МК. Да и внутренние вачдоги дергают ресет. Последний раз видел прерывание от вачдога в AVR, что еще 10 лет назад показалось мне не очень надежным 😃 Срабатывание сторожевика - это ещё один источник возбуждения trap(). И такой случай должен быть расследован программистом при отладке прошивки в обязательном порядке. 4 часа назад, Arlleex сказал: Но программно настраиваемый вачдог - такое себе)) Около МК всегда (у меня) имеется аппаратный. Наличие внешнего WDT нисколько не мешает использовать и внутренний. Почти во всех проектах имею внешний WDT. И везде он у меня работает совместно с внутренним WDT. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 69 30 мая Опубликовано 30 мая · Жалоба 8 hours ago, Arlleex said: В таком случае разве что самым значимым будет содержимое статусных регистров fault CPU. Наиболее значим адрес сбоя, затем содержимое РОН, затем всё остальное. 8 hours ago, Arlleex said: А на указатель стека уже ориентироваться, в общем-то, нельзя. Почему нельзя? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 158 30 мая Опубликовано 30 мая · Жалоба 2 часа назад, aaarrr сказал: Почему нельзя? Не прав, указатель будет в порядке. А вот сам стек уже может быть вполне затерт - и что там смотреть? 5 часов назад, jcxz сказал: Просто достаточно посчитать - какова вероятность случайного выпадения заданного адреса в случайном 32-битном значении? В прошивке не обязательно попадать на вызов самой функции рестарта или какой-то "опасной" функции. Ведь еще много линейного кода, который тоже в конце концов может вызвать эти функции. Цитата "Разблокируется флешь" - вообще равно нулю. Так как в используемых мной сейчас XMC4xxx модифицировать внутреннюю флешь может только код, находящийся в ОЗУ. И копирует его туда бутлоадер после множества проверок и программирований MPU. Хорошо, что Ваш МК имеет такую возможность. А в том же STM32 работать с флешкой можно прямо из того же кода во флеш. Цитата Да, fault сработает не сразу, но по содержимому регистров CPU или дампов стеков как правило - можно быстро найти место начальной проблемы Быстро? Не верю. Если мы говорим об очень частых проявлениях бага, то не составит труда под отладчиком с вероятностью 99.9% выловить этот баг на столе, без дампов и т.д. Если же девайс сбоит при непонятных обстоятельствах за тридевять земель от дома, то взгляд на дамп регистров ничего не скажет с очень большой вероятностью. Мне штатный перехватчик переполнения стека в FreeRTOS гораздо чаще помогал, чем дампы. И еще: чтобы иметь хоть какой-то адекватный шанс что-то понять из дампа регистров, нужно изначально обладать вполне объемным контекстом и предысторией: что в процессоре задействовано, что не задействовано, и т.д., чтобы в голове уже представлять потенциальный сценарий развития и проявления бага. Я однажды неделю отлавливал всяческими дампами и перехватчиками баг в Cortex-A9 на цинке, который возникал от силы пару раз в день - и это еще очень повезло, можно сказать. И да - я улетал в DataAbortHandler. Ну отматывал я указатель стека, смотрел что вызывалось - и каждый раз - разное. И так можно было бы сидеть искать до бесконечности. Не обладая минимальными сведениями проблему не сыскать. А я помнил, что тогдашний порт FreeRTOS-а был довольно древним, и что возможно индусы как-то криво запилили переключатель контекста. Полез смотреть - бинго - FPU-контекст вообще не сохранялся, а он вообще-то задействуется библиотечными функциями memcpy() и т.д. Это невозможно понять взглянув на регистры. Из них максимум будет понятно - что что-то где-то нагадило в память и не более. Еще один пример. В МК поднимался SPI и его задача была пару раз в секунду запрашивать со слейва данные. В функцию записи передавался массив куда писать и длина. Смотрю - раз в минуту стабильно срывает крышу и на CLK сплошной непрерывный поток данных - а девайс "подвис" (не заморачивался я с "правильным" SPI на прерываниях или DMA - мне надо было провериться на макетке). Сети fault-ов МК - пустые. Ставлю оптимизацию повыше - все работает. -О0 - не работает. "WTF? Обычно же наоборот!" - думал я. Ну полез я отладчиком смотреть регистры - все в порядке - на входе в функцию все ок. А дальше происходит интересное: кейловский 5 компилятор (ныне устаревший) иногда (от незначительных изменений в коде) в спонтанных местах асм-листинга этой функции SPI_Read() вставлял инструкцию "отката" указателя стека. Т.е. на входе в функцию резервировал себе память под локальные переменные, а далее где-то в середине функции - откатывался обратно! И поэтому перед циклом SPI-транзакций len в for(u32 i = 0; i < len; ++i) брался из памяти "сверху" стека, а там периодически лежало что-то большое, из-за чего вместо цикла на 10 транзакций он получал миллион. Позже эту проблему описали к следующей ревизии компилятора. Случаев у меня было много разных и инетресных, когда я пялился в монитор (поняв уже проблему) и говоря "да какой нафиг дамп такое обнаружит?". Для себя решил, что регистры процессора нужно смотреть когда уже под отладчиком запускаешь девайс и ловишь баг, а не писать дампы на флешки. Они лишь подогревают надежду, но их результативность околонулевая. Поэтому критические баги считаю потенциально неотлавливаемыми "за хвост", только аналитически, играя в следователя. Первое с чего начинаю - разблокирую девайн DEBUG/PRINT, который принтует все потенциальные куски (ветки) кода, куда мы не должны были зайти, но зашли. Т.е. какая-то функция может вернуть -1 в качестве ошибки - я ее не просто обработаю, но еще и принт выведу, мол - "смотри, человек, шо у нас тут за бардак творится...". Все это мое субъективное мнение на этот счет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 211 30 мая Опубликовано 30 мая · Жалоба 3 часа назад, Arlleex сказал: Не прав, указатель будет в порядке. А вот сам стек уже может быть вполне затерт - и что там смотреть? В прошивке не обязательно попадать на вызов самой функции рестарта или какой-то "опасной" функции. Ведь еще много линейного кода, который тоже в конце концов может вызвать эти функции. Хорошо - предположим перед ней 100 байт линейного кода. Достаточно? Причём такого, что даже нигде к памяти не обращается (иначе скорей всего случился бы вылет по MPU или HF из-за обращения по недопустимым адресам) - что само по себе очень редкий случай. Получаем: 100/2^32 = ~2.3e-8 - вероятность попадания в такой регион при переходе по случайному адресу. С такой вероятностью можно считать это событие вообще невероятным. 3 часа назад, Arlleex сказал: Быстро? Не верю. Если мы говорим об очень частых проявлениях бага, то не составит труда под отладчиком с вероятностью 99.9% выловить этот баг на столе, без дампов и т.д. Да ладно? Предположим - работаете вы не один, а есть другие люди, тестирующие ваш девайс или работающие над проектом. В девайсе несколько десятков настроек + довольно богатый функционал. Далее к вам обращается тестировщик: вот тестировал вчера вечером девайс, вдруг он завис (или перегрузился; а скорее даже скажет: "Контроллер крутил мотор и вдруг мотор остановился (девайс - инвертор для PMSM-моторов" (так как тестировщик даже не заметил, что девайс просто молча перезагрузился)). Спрашиваете его: "Какую последовательность действий выполнял перед этим событием?" В ответ - "Не помню, много что делал, параллельно ещё меня отвлекали". Т.е. - воспроизвести баг вы не можете. А если ещё девайс - мощный мотор на стенде, в несколько сотен кВт, который на своём столе просто не запустите. Как собираетесь искать баг? А в моём случае я просто попрошу его показать содержимое ловушки. И с большой вероятностью сразу найду причину. 3 часа назад, Arlleex сказал: Если же девайс сбоит при непонятных обстоятельствах за тридевять земель от дома, то взгляд на дамп регистров ничего не скажет с очень большой вероятностью. Опять не соглашусь. Как раз в последнее время много приходится отлаживать девайсов "за тридевять земель". В 9-и случаев из 10-и благодяря системе ловушек, баг нахожу сразу, за несколько секунд. Большинство из таких багов: 1. Впаяли не тот чип (например - не ту SPI-флешку). Драйвер SPI-флешь при старте читает ID флешки и падает в ловушку. Или читает регистры защиты флешь - и также падает в ловушку из-за установленной защиты, которой не должно быть. Причина определяется сразу по дампу ловушки. 2. Непропай - опять ошибка инициализации какого-либо драйвера и падение в ловушку. Опять определяется сразу по дампу ловушки. 3. Подали не то питание или что-то не подключили или подключили неправильно - опять ловушка. И опять причина находится сразу. 4. Сильный перегрев платы - уходит частота генератора на плате - падаем в ловушку. Причина находится быстро. ... и таких багов - большинство. И реально ловушки помогают. Так как проверками у меня обложены все потенциально опасные места. 3 часа назад, Arlleex сказал: Еще один пример. В МК поднимался SPI и его задача была пару раз в секунду запрашивать со слейва данные. Я не понимаю вашу логику. Вы пытаетесь доказать, что моя система ловушек не пригодна по отдельным, частным случаям, когда она бы не сработала. Это примерно то же самое, что говорить: "Вот я вчера шёл не заметил кочку и споткнулся, упал. Получается - глаза мне не помогли её увидеть. Значит глаза не нужны". Это если следовать вашей логике. Странная логика.... 3 часа назад, Arlleex сказал: Поэтому критические баги считаю потенциально неотлавливаемыми "за хвост", только аналитически, играя в следователя. Первое с чего начинаю - разблокирую девайн DEBUG/PRINT, который принтует все потенциальные куски (ветки) кода, куда мы не должны были зайти, но зашли. Т.е. какая-то функция может вернуть -1 в качестве ошибки - я ее не просто обработаю, но еще и принт выведу, мол - "смотри, человек, шо у нас тут за бардак творится...". Ок - раскомментировали вы отладочный вывод. Время выполнения кода изменилось, а также изменилось распредление памяти - проявление бага пропало. И будете искать баг до морковкина заговения. Так что такой способ - так себе способ. 3 часа назад, Arlleex сказал: Все это мое субъективное мнение на этот счет. Хорошо, я понял - вы почему-то против моего способа. Тогда предложите ваше решение. Вот случай (исходя из вопроса ТС): Вызываете сервис ОС, он возвращает вам ошибку (например какой-то ресурс недоступен). Ваши дествия? Повиснуть? Перегрузиться? Что будете делать. Я просто вызову trap(TRAP_OS, ...) (и по выводу сразу найду место бага). Но что будете делать вы? Или другой случай: У меня есть множество функций с assert-ами (проверками входных аргументов на входе). Если проверка не проходит - trap(TRAP_ASSERT, регион_assert-а, ...). И сразу вижу причину и место. Каково ваше решение в этом случае (без механизма ловушек)? Повиснуть? Перегрузиться? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 158 30 мая Опубликовано 30 мая · Жалоба 1 час назад, jcxz сказал: А в моём случае я просто попрошу его показать содержимое ловушки. И с большой вероятностью сразу найду причину. Цитата В 9-и случаев из 10-и благодяря системе ловушек, баг нахожу сразу, за несколько секунд. Большинство из таких багов: Цитата Хорошо, я понял - вы почему-то против моего способа. Тогда предложите ваше решение. Цитата trap(TRAP_OS, ...) Цитата trap(TRAP_ASSERT, регион_assert-а, ...) Вообще-то я не против функций ловушек. Только ловушка в моем понимании должна защелкивать не содержимое регистров, а некое логическое состояние программы. Потому что при Цитата 1. Впаяли не тот чип (например - не ту SPI-флешку). содержимое процессорных регистров мне ни о чем не скажет совершенно. А вот выведенный в консоль или записанный в лог флешки текст расшифрованной ловушки в виде ... 00:10:22:765 [ ERROR ] Error SPI-Flash ID code: 0x7545 ... еще как скажет. Цитата Или другой случай: У меня есть множество функций с assert-ами (проверками входных аргументов на входе). Если проверка не проходит - trap(TRAP_ASSERT, регион_assert-а, ...). И сразу вижу причину и место. Каково ваше решение в этом случае (без механизма ловушек)? Повиснуть? Перегрузиться? А Вы, по-Вашему, не повиснете? Не перезагрузитесь? Вы не отвечаете на вопрос, что будете делать дальше с точки зрения выполнения ПО. Цитата Ваши дествия? Повиснуть? Перегрузиться? Что будете делать. Я просто вызову trap(TRAP_OS, ...) (и по выводу сразу найду место бага). Но что будете делать вы? Мои действия: выдать лог или записать его на флешку если девайс работал автономно, а потом да - либо перезагрузиться, либо повиснуть, ожидая действий - все ситуативно. Вы тоже повиснете или перезагрузитесь - потому что Цитата Я просто вызову trap(TRAP_OS, ...) (и по выводу сразу найду место бага) это не ответ на вопрос Цитата Ваши дествия? Повиснуть? Перегрузиться? Что будете делать. То, что Вы дальше будете разбираться, что произошло с девайсом - не отвечает на вопрос - что будет делать программа. А программа, как Вы ранее сами писали, войдет в режим циклического вывода дампа регистров в консоль. Т.е. с точки зрения "боевого" режима изделия - оно повиснет. Вот и все. Я не говорю что вообще ничего ловить не надо - я говорю, что представленный пример дампа содержимого регистров - в реальной моей практике ни о чем не говорил. Ну - половина регистров хранит мусор, другая - тоже какой-то мусор. И что дальше с этим делать? Те примеры ошибок (не правильная флешка припаяна, например) - не есть критические ошибки, которые невозможно предусмотреть обработкой в ПО. Считали кривой ID? Ок - рапортуем лампочкой TOTAL ERROR встроенной системой контроля и отдаем на завод. Драйвер должен поджечь эту лампочку или выдать лог - как угодно на вкус и цвет. Это все можно предусмотреть в коде драйвера. С такими ситуациями вообще проблем нет - за них даже не интересно. Интересно как раз - когда проц падает по каким-то совсем не внятным причинам - я выше приводил примеры. Вот словил я в цинке исключение "отказ данных", вот вывел содержимое всех процессорных регистров - даже стек размотал, чтобы вызовы посмотреть - а толку нет - каждый раз баг в новом месте. Как ловить такие ошибки ловушками? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Mty 0 30 мая Опубликовано 30 мая · Жалоба Спасибо за информацию, оба подхода очень интересны! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 211 30 мая Опубликовано 30 мая · Жалоба 38 минут назад, Arlleex сказал: содержимое процессорных регистров мне ни о чем не скажет совершенно. А вот выведенный в консоль или записанный в лог флешки текст расшифрованной ловушки в виде ... 00:10:22:765 [ ERROR ] Error SPI-Flash ID code: 0x7545 ... еще как скажет. Я вроде сразу в исходном посте ещё писал: В 29.05.2024 в 13:34, jcxz сказал: По логу виден номер trap (3) + набор аргументов данного номера (в квадратных скобках) + регистры + вершина стека. Номер ловушки + набор аргументов (определяемый номером ловушки) как раз это и есть. 38 минут назад, Arlleex сказал: А Вы, по-Вашему, не повиснете? Не перезагрузитесь? Вы не отвечаете на вопрос, что будете делать дальше с точки зрения выполнения ПО. Войду в режим ловушки. Аналог синего окна смерти винды. И функционал тот же - защёлкнуться в данном состоянии чтобы дать возможность пользователю найти причину. 38 минут назад, Arlleex сказал: Мои действия: выдать лог или записать его на флешку если девайс работал автономно, а потом да - либо перезагрузиться, либо повиснуть, ожидая действий - все ситуативно. Ну т.е. - сделаете примерно то же самое, но только хуже (так как без предварительного реинита программы)? А почему тогда критикуете мой способ? 38 минут назад, Arlleex сказал: То, что Вы дальше будете разбираться, что произошло с девайсом - не отвечает на вопрос - что будет делать программа. А программа, как Вы ранее сами писали, войдет в режим циклического вывода дампа регистров в консоль. Т.е. с точки зрения "боевого" режима изделия - оно повиснет. Вот и все. Это не повисание. Это режим ловушки. Повиснуть - это значит молча и без какой-либо индикации войти в непонятное состояние устройства. Которое невозможно определить по внешним признакам. Вот это и есть повисание. А режим ловушки штатный и известный пользователям (на время отладки). Даёт возможность надёжно обнаружить ошибку (а не замести её под ковёр, перегрузившись) и дать возможность программисту устранить её. 38 минут назад, Arlleex сказал: Я не говорю что вообще ничего ловить не надо - я говорю, что представленный пример дампа содержимого регистров - в реальной моей практике ни о чем не говорил. Ну - половина регистров хранит мусор, другая - тоже какой-то мусор. И что дальше с этим делать? В вашем может ничего не говорил, а я по этому дампу неоднократно находил причину. Так как можно увидеть адрес, увидеть содержимое регистров. Этого чаще всего уже достаточно для нахождения причины. Не всегда конечно, но в большинстве случаев. Если следовать вашей логике, то непонятно зачем в отладчиках выводят содержимое регистров? или памяти, стека? Ведь там тоже - половина - мусор. Чтобы понять где мусор, а где полезные данные - на то у программиста есть голова. 38 минут назад, Arlleex сказал: Интересно как раз - когда проц падает по каким-то совсем не внятным причинам - я выше приводил примеры. Вот словил я в цинке исключение "отказ данных", вот вывел содержимое всех процессорных регистров - даже стек размотал, чтобы вызовы посмотреть - а толку нет - каждый раз баг в новом месте. Как ловить такие ошибки ловушками? Вы бы исходный вопрос темы прочитали, что-ли. Где там про ваш цинк что-то написано?? Я предложил свой способ для решения вопроса ТС. Если вы с ним не согласны - предложите свой. Но к чему тут проблемы цинка (причём только определённые) - совсем не ясно. И всегда, для любого способа борьбы с багами, можно придумать такой сценарий, который не будет работать. Предложите свой способ, а мы придумаем кучу ситуаций, когда он не сработает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Mty 0 30 мая Опубликовано 30 мая · Жалоба Спасибо за интересную информацию! У меня вопрос к jcxz - как я понял у Вас вызывается макрос по нештатной ситуации, скажем if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) { MY_TRAP( 3 ) } Этот макрос (или функция) сливает все регистры и стек с указателем в лог в fram или в UART и рестатует МК? Затем Вы "на базе" анализируете этот лог. Верно? Только я не понял, как это технически делается дебаггером, если баг не воспроизводится каждый раз? Доходите до этой функции дебаггером на работающем девайсе, и сравниваете содержимое стека и регистров с тем что в дампе? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 211 30 мая Опубликовано 30 мая · Жалоба 14 минут назад, Mty сказал: Спасибо за информацию, оба подхода очень интересны, прямо супер! У меня вопрос к jcxz - как я понял вызывается макрос по нештатной ситуации, скажем if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS ) { MY_TRAP( 3 ) } Этот макрос (или функция) сливает все регистры и стек с указателем в лог в fram или в UART и рестатует МК? Не совсем. Если trap генерируется программный (например: в результате проверки каких-то переменных в программе обнаружено их недопустимое состояние), то делается вызов trap() (это не макрос и не функция в обычном виде, это возбуждение прерывания SVC, оформленное в виде функции (IAR умеет такое)). Внутри ISR SVC защёлкивается в ОЗУ дамп = id_trap+аргументы+регистры+стек, потом делается реинит всей периферии и CPU с активацией только нужной для trap-режима. МК можно рестартовать или не рестартовать. Главное - отключить все работающие DMA и перевести все интерфейсы работающие с внешней периферией в дефолтное выключенное состояние (выключить питание моторов, отключить Ethernet, USB, CAN и т.п.). Если trap не программный, а вход в ловушку идёт из ISR HF или ISR MPU или ISR шинных ошибок или ошибок чётности памяти (и т.п.), которые сами являются по факту событиями ошибки, то симулируется как будто была получена SVC (повышение приоритета прерывания до максимума и т.д.), и содержимое регистров и стека берётся таким, каким оно было до входа в этот ISR (HF, MPU, ...) Чтобы причина trap указывала на причину, вызвавшую исходный ISR, а не на код внутри ISR. Также для HF декодируется причина fault-а (по соответствующим регистрам), и эта инфа передаётся как номер и аргументы trap(). Для таких ловушек у меня есть целый класс номеров: #define TRAP_EXC_HARD 0x100 //исключение Hard Fault (неуточнённое) #define TRAP_EXC_HARD_VECTTBL 0x101 //ошибка при попытке считать содержимое таблицы векторов прерываний #define TRAP_EXC_HARD_FORCED 0x102 //произошла эскалациЯ прерываниЯ до HardFault (исходный fault неизвестен) #define TRAP_EXC_MPU 0x110 //MEMORY MANAGER FAULT (неуточнённое) #define TRAP_EXC_MPU_IERR 0x111 //MM FAULT: нарушение прав доступа к коду #define TRAP_EXC_MPU_DERR 0x112 //MM FAULT: нарушение прав доступа к данным #define TRAP_EXC_MPU_MUSTKE 0x113 //MM FAULT: при выходе из обработчика прерываниЯ возникло порождённое прерывание MemFault #define TRAP_EXC_MPU_MSTKE 0x114 //MM FAULT: при входе в обработчик прерываниЯ возникло порождённое прерывание MemFault #define TRAP_EXC_MPU_LSPE 0x115 //MM FAULT: при отложенном сохранении состоЯниЯ FPU возникла ошибка #define TRAP_EXC_BUS 0x120 //BUS FAULT (неуточнённое) #define TRAP_EXC_BUS_IBUS 0x121 //BUS FAULT: ошибка при попытке выборки инструкции #define TRAP_EXC_BUS_PRECISE 0x122 //BUS FAULT: точнаЯ ошибка при обращении к данным #define TRAP_EXC_BUS_IMPRECISE 0x123 //BUS FAULT: неточнаЯ ошибка при обращении к данным #define TRAP_EXC_BUS_USTK 0x124 //BUS FAULT: при попытке выхода из обработчика прерываниЯ #define TRAP_EXC_BUS_STK 0x125 //BUS FAULT: при попытке входа в обработчик прерываниЯ #define TRAP_EXC_BUS_LSPE 0x126 //BUS FAULT: при отложенном сохранении состоЯниЯ FPU #define TRAP_EXC_USAGE 0x130 //USAGE FAULT (неуточнённое) #define TRAP_EXC_USAGE_DIV0 0x131 //USAGE FAULT: попытка делениЯ на 0 #define TRAP_EXC_USAGE_UNALIGN 0x132 //USAGE FAULT: попытка невыровненного доступа к памЯти #define TRAP_EXC_USAGE_NOCP 0x133 //USAGE FAULT: попытка доступа к отсутствующему или отключенному сопроцессору #define TRAP_EXC_USAGE_INVPC 0x134 //USAGE FAULT: ошибочное значение в EXC_RETURN #define TRAP_EXC_USAGE_INVSTAT 0x135 //USAGE FAULT: попытка выполнениЯ инструкции при неверном значении бита T или полЯ IT регистра EPSR #define TRAP_EXC_USAGE_UNDEF 0x136 //USAGE FAULT: попытка выполнить неизвестную или привилегированную инструкцию у некоторых ещё есть аргументы. Вообще типичное содержимое trap.h из текущего проекта: Спойлер #define TRAP_NONE 0x00 #define TRAP_MISC 0x01 #define TRAP_INTERNAL 0x02 #define TRAP_ASSERT 0x03 #define TRAP_OVERBUF 0x04 #define TRAP_OS 0x05 #define TRAP_UART 0x09 #define TRAP_PRG_IMAGE 0x0A //размер образа программы больше размера, контролируемого CRC в IAR. Либо невернаЯ CRC образа #define TRAP_SERV 0x0B #define TRAP_ISR_NESTED 0x0C //нарушение уровнЯ вложенности ISR-ов #define TRAP_TOUT_ACT 0x0D //таймаут обмена с периферией #define TRAP_SETRATE 0x0E //ошибка установки скорости #define TRAP_OS_HOOK 0x0F //ошибка в хуках ОС #define TRAP_FCE 0x10 //ошибка Flexible CRC Engine #define TRAP_DMA 0x11 //DMA bus error is pending #define TRAP_DMA_NESTED 0x12 //повторное разрешение/запрет уже разрешённого/запрещённого DMA-канала #define TRAP_ERU_NESTED 0x13 //превышение кол-ва включений или кол-ва выключений ERU #define TRAP_TERMINAL 0x14 #define TRAP_SIMCOM 0x15 #define TRAP_GYRO 0x16 //ошибка службы гироскопа #define TRAP_RF_ISR_OVER 0x17 //переполнение FIFO ISR RF-приёмника #define TRAP_ADC_OVER 0x18 //не успели обработаться предыдущие данные slow АЦП когда уже поступили новые #define TRAP_I2C_BUSY_SCL 0x20 //линия SCL залипла в состоянии "0" #define TRAP_I2C_BUSY_SDA 0x21 //линия SDA залипла в состоянии "0" #define TRAP_I2C_ERROR 0x22 #define TRAP_I2C_RXOVER 0x23 #define TRAP_I2C_USIC 0x24 #define TRAP_HIB 0x25 //ошибка в Hibernation-драйвере #define TRAP_PBA0 0x26 //ошибка Peripheral Bridge0 #define TRAP_PBA1 0x27 //ошибка Peripheral Bridge1 #define TRAP_PARITY 0x28 //ошибка чётности памяти (аргумент - биткарта регионов ошибки) #define TRAP_NMI 0x29 //NMI, причина неизвестна #define TRAP_CCU_ON_NESTED 0x2B //повторное разрешение уже разрешённого CCU-слайса #define TRAP_CCU_OFF_NESTED 0x2C //повторный запрет уже запрещённого CCU-слайса #define TRAP_SPI 0x30 #define TRAP_DFLASH 0x31 #define TRAP_USB 0x32 #define TRAP_OSC 0x33 #define TRAP_CEDIT 0x34 #define TRAP_EXC_HARD 0x100 //исключение Hard Fault (неуточнённое) #define TRAP_EXC_HARD_VECTTBL 0x101 //ошибка при попытке считать содержимое таблицы векторов прерываний #define TRAP_EXC_HARD_FORCED 0x102 //произошла эскалациЯ прерываниЯ до HardFault (исходный fault неизвестен) #define TRAP_EXC_MPU 0x110 //MEMORY MANAGER FAULT (неуточнённое) #define TRAP_EXC_MPU_IERR 0x111 //MM FAULT: нарушение прав доступа к коду #define TRAP_EXC_MPU_DERR 0x112 //MM FAULT: нарушение прав доступа к данным #define TRAP_EXC_MPU_MUSTKE 0x113 //MM FAULT: при выходе из обработчика прерываниЯ возникло порождённое прерывание MemFault #define TRAP_EXC_MPU_MSTKE 0x114 //MM FAULT: при входе в обработчик прерываниЯ возникло порождённое прерывание MemFault #define TRAP_EXC_MPU_LSPE 0x115 //MM FAULT: при отложенном сохранении состоЯниЯ FPU возникла ошибка #define TRAP_EXC_BUS 0x120 //BUS FAULT (неуточнённое) #define TRAP_EXC_BUS_IBUS 0x121 //BUS FAULT: ошибка при попытке выборки инструкции #define TRAP_EXC_BUS_PRECISE 0x122 //BUS FAULT: точнаЯ ошибка при обращении к данным #define TRAP_EXC_BUS_IMPRECISE 0x123 //BUS FAULT: неточнаЯ ошибка при обращении к данным #define TRAP_EXC_BUS_USTK 0x124 //BUS FAULT: при попытке выхода из обработчика прерываниЯ #define TRAP_EXC_BUS_STK 0x125 //BUS FAULT: при попытке входа в обработчик прерываниЯ #define TRAP_EXC_BUS_LSPE 0x126 //BUS FAULT: при отложенном сохранении состоЯниЯ FPU #define TRAP_EXC_USAGE 0x130 //USAGE FAULT (неуточнённое) #define TRAP_EXC_USAGE_DIV0 0x131 //USAGE FAULT: попытка делениЯ на 0 #define TRAP_EXC_USAGE_UNALIGN 0x132 //USAGE FAULT: попытка невыровненного доступа к памЯти #define TRAP_EXC_USAGE_NOCP 0x133 //USAGE FAULT: попытка доступа к отсутствующему или отключенному сопроцессору #define TRAP_EXC_USAGE_INVPC 0x134 //USAGE FAULT: ошибочное значение в EXC_RETURN #define TRAP_EXC_USAGE_INVSTAT 0x135 //USAGE FAULT: попытка выполнениЯ инструкции при неверном значении бита T или полЯ IT регистра EPSR #define TRAP_EXC_USAGE_UNDEF 0x136 //USAGE FAULT: попытка выполнить неизвестную или привилегированную инструкцию #define TRAP_UNEXPECTED_INT 0x140 //неожиданное прерывание #define TRAP_UNEXPECTED_DMA 0x141 //неожиданное DMA-событие #define TRAP_INTDIS_OVER 0x142 //слишком длинный запрет прерываниЯ #define TRAP_SVC_UNKNOWN 0x143 //неизвестная функция SVC #define TRAP_VIC 0x144 //недопустимаЯ таблица приоритетов VIC //регионы ловушек #define TRAPR_ANY 0 #define TRAPR_DFLASH 1 #define TRAPR_LOG 2 #define TRAPR_TERM 3 #define TRAPR_PWM 4 #define TRAPR_SIMCOM 5 #define TRAPR_I2C 6 #define TRAPR_GYRO 7 #define TRAPR_DTE 8 //отладочная консоль #define TRAPR_HIB 9 //Hibernate-драйвер #define TRAPR_TIME 10 //Служба Времени #define TRAPR_MOTOR 11 //Служба управления мотором #define TRAPR_CONFIG 12 #define TRAPR_USB 13 //USB #define TRAPR_SENSOR 14 // #define TRAPR_OSC 15 14 минут назад, Mty сказал: Только я не понял, как это технически делается дебаггером если баг не воспроизводится каждый раз? Доходите до этой функции дебаггером на работающем девайсе, и сравниваете содержимое стека и регистров с тем что в дампе? А зачем его воспроизводить? Адрес есть, значит можно встать на этот адрес и проанализировать, что там не так. А дальше действовать по обстоятельствам. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
artemkad 58 30 мая Опубликовано 30 мая · Жалоба 35 минут назад, jcxz сказал: Не совсем. Если trap генерируется программный (например: в результате проверки каких-то переменных в программе обнаружено их недопустимое состояние), то делается вызов trap() (это не макрос и не функция в обычном виде, это возбуждение прерывания SVC, оформленное в виде функции (IAR умеет такое)). Пока что это выглядит как обычный LOG() только вместо вменяемого сообщения сохраняется константа, а сообщение надо глазами читать в комментариях к этой константе в trap.h - файле. Удовольствие по части объяснения произошедшего в программе - то еще. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 211 30 мая Опубликовано 30 мая · Жалоба 46 минут назад, artemkad сказал: Пока что это выглядит как обычный LOG() только вместо вменяемого сообщения сохраняется константа, а сообщение надо глазами читать в комментариях к этой константе в trap.h - файле. Ну если вам нравится текстом - кто ж вам мешает? А я буду делать как удобнее мне и пользователям моих устройств. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 23 30 мая Опубликовано 30 мая · Жалоба В лог вывожу сообщения, обычно простой короткий текст с минимумом параметров, по которому однозначно ясно где и что произошло. На каждую строку лога пишется время от старта и номер включения (количество сбросов без снятия питания). Дальше по обстоятельствам, можно продолжить работу (переключив тактирование с кварца на RC, например), сделать сброс, или перейти в безопасный режим (остановить основные функции устройства). Лог хранится в RAM в области которая не обнуляется на старте. В любой момент его можно прочитать или очистить. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Mty 0 30 мая Опубликовано 30 мая · Жалоба 6 hours ago, jcxz said: А зачем его воспроизводить? Адрес есть, значит можно встать на этот адрес и проанализировать, что там не так. А дальше действовать по обстоятельствам. А адрес функции вызвавшей прерывание (PC) лежит последний или предпоследний в стеке? 3 hours ago, amaora said: В лог вывожу сообщения, обычно простой короткий текст с минимумом параметров, по которому однозначно ясно где и что произошло. На каждую строку лога пишется время от старта и номер включения (количество сбросов без снятия питания). Дальше по обстоятельствам, можно продолжить работу (переключив тактирование с кварца на RC, например), сделать сброс, или перейти в безопасный режим (остановить основные функции устройства). Лог хранится в RAM в области которая не обнуляется на старте. В любой момент его можно прочитать или очистить. Благодарю, интересный вариант. А эту область объявляете что то типа static char массив в C? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 23 30 мая Опубликовано 30 мая · Жалоба 3 minutes ago, Mty said: А эту область объявляете что то типа static char массив в C? В отдельную .noinit секцию, которая никак не обрабатывается в startup коде. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться