Цитата
Ребята стал попадать в обработчик хард-фолта с таким вот состоянием.
В LR у Вас явно не то, что должно быть, обработчик видимо у Вас кривой.
Адрес возврата и другую информацию отлавливаю так. верный вариант по задумке разработчика armv7m
Код
eHardFault
mov r0,#0
mov r1,lr
mov r2,sp; always MSP in exception handler
mrs r3,PSP
bl printfault
b .
void printfault(U32 flt,U32 lr,U32 *msp,U32 *psp){
U32 retaddr;
// analyze EXC_RETURN
if((lr&0x0F)==1||(lr&0x0F)==9){
retaddr=msp[6];
}else if((lr&0x0F)==0x0D){
retaddr=psp[6];
}else ra=0xFFFFFFFF;
// print
switch(flt){
case ...:
}
}
В месте с адресом возврата нужно печатать CFSR, BFAR и HFSR.
Что касается отлова места, от куда растут ноги... Во первых надо внимательно прочитать описание на вышеприведенные регистры CFSR, BFAR, HFSR - информации про fault там не мало.
Обычно, если занесло не туда(на пример при переполнении буффера в стеке), то исключениия могут быть следующие
1. UsageFault - попали на существующую память, но дело дошло до некорректной инструкции.
2. BusFault - попали на недоступную для выполнения кода память
В обеих случаях адрес возврата может быть не тот,что нам интересно. Для начала нужно это проверить, посмотрев в дизассемблер вашего реального КОДА - если там этого адреса нету - значит он не тот. В таком случаи дальше уже стандартного метода нету.
Если баг вылазит сразу, то и поймать место легко - стевим бряк на выходы с функций и смотрим куда попадает.
Если не очень сразу - в дебаггерах есть трассировка вызовов и переходов. Натрейсить многомегабайтный лог и потом искать в обратном порядке последнюю запланированную инструкцию(так, которая есть в вашем КОДЕ).
Если баг вылазит очень редко (на пример, раз в неделю) и весь код вы уже перечесали на предмет переполнений и indirect-вызовов - тут надо хорошенько напрячь мозги и прокурить документацию.
У меня была ситуация с PendSV, SVC обработчиками.
Код
ePendSV
; push LR(EXC_RETURN),R11-R4 to PSP, save PSP
mrs r0,PSP
stmdb r0!,{r4-r11,lr}
...save context....
... get new context....
; restore LR(EXC_RETURN),R11-R4 from new PSP, set new PSP, return
ldmia r0!,{r4-r11,lr}
msr PSP,r0
bx lr
void eSVCall(void){
U32 a0,a1,a2,a3,svc,*psp;
psp=(U32*)__get_PSP();
a0=psp[0];a1=psp[1];a2=psp[2];a3=psp[3]; // get arguments from user stack
svc=psp[4]; // get service call ID from user stack
a=service(svc,a0,a1,a2,a3);
psp[0]=a0; // save return value (r0) to user stack
}
используем это дело так:
Код
mov r0,...; arg1
mov r1,...; arg2
mov r12,#1; SVC ID
svc 0
; now r0 has return value
При вызове обработчика инструкцией svc регистры сохранятся в стек, обработчиком перезапишется в стеке значение регистра(ов), при выходе из исключения регистры восстановятся со стека с уже перезаписанными значениями.
Вроде все гуд, НО!
Прочитав фразу "Because SVCall and PendSV have the same execution priority they cannot preempt each other" из refмануала на armv7m так и сделал - назначил обеим обработчикам одинаковый приоритет и был уверен, что все сделал правильно. Хотя в моем случаи это было неверно.
Дело в том, что в armv7m есть такая вероятность, что PendSV и SVCall одновременно могут висеть в ожидании, а если они имеют одинаковый приоритет, то порядок их выполнения неопределен, тк там есть механизмы Late arrival preemption, Tail-chaining и конвеер. Подробно можно почитать в ARM DDI0403D
Теперь, если исключение PendSV наступит в момент,
когда иснтрукция svc уже в конвеере, но пока само исключение SVCall еще не сгенерировано, мы попадаем в обработчик PendSV, которий в итоге перезапишет значение PSP и выйдет. Далее по tail-chain выполнится уже SVCall, который будет уже обращатся к чужому стеку... Баг вылазит довольно редко, с интервалом от нескольких часов, до недели а то и больше. И вылазит он не в виде fault-a, а в виде вызова исключения(зачастую успешным) с неправильными аргументами или значением возврата.программа вроде работает, но не так, как надо. я очень долго не мог этот баг выудить... Недели копания в дебагере, чтения документации, анализа кода... И все практически напрасно. Помогло только чтение документации + симуляция поведения проца в голове..
Зато решение оказалось просто смешное - поднять приоритет SVCall

Это даст гарантию, что SVCall всегда будет выполнено до PendSV. и глюка уже не будет.
Заставить PendSV и SVCall одновременно висеть в Pending state легко: Запускаем таймер, частоту задть по максимуму, чтобы вероятность поднять. в таймере делаем PENDSVSET. Теперь если прерывание таймера вытеснит SVCall до старта самого обработчика SVCall(тоесть SVCall все еще в pending-state), выполнится обработчик таймера, установит PendSV тоже в pending-state. Все, при выходе с обработчика таймера при равном приоритете PendSV и SVCall может выполнится любая из них,порядок неопределен.