Сначала немного предыстории. Когда прошивка по какой-либо причине дает сбой (например, переполнение стека, поврежденный указатель функции...), может случиться так, что она куда-то прыгнет и начнет выполнять какой-то код. Это рано или поздно приведет к сбросу сторожевого таймера. MCU перезагрузится, и мы вернемся на правильный путь. Пока не...
Что если у нас есть код, который записывает данные во флэш-память (например, загрузчик)? Теперь может случиться так, что мы случайно перескочим прямо в код записи во флэш-память — пропустив все проверки. Прежде чем сторожевой таймер начнет лаять, вы получите поврежденную прошивку. Это именно то, что происходило со мной.
Теперь кто-то может сказать — исправьте основную ошибку, из-за которой мы даже перешли к написанию кода. Ну, когда вы разрабатываете, вы постоянно меняете код. Даже если сейчас такой ошибки нет, завтра она может появиться. Кроме того, ни один код не свободен от ошибок — по крайней мере, не мой.
Так что теперь я делаю своего рода перекрестную проверку. У меня есть переменная с именем «wen», для которой я установил значение 0xa5 перед обычными проверками (например, проверкой, чтобы убедиться, что пункт назначения действителен). Затем, непосредственно перед выполнением фактического стирания или записи, я проверяю, действительно ли «wen» установлен на 0xa5. В противном случае это означает, что мы как-то случайно перескочили на написание кода. После успешной записи «wen» очищается. Я сделал это на C, и это сработало хорошо. Но все же есть небольшая теоретическая вероятность повреждения, потому что есть несколько инструкций от этой последней проверки «wen» до записи в регистр SPMCR.
Теперь я хочу улучшить это, поместив эту проверку в сборку между инструкциями записи в SPMCR и spm.
__asm__ __volatile__
(
"lds __zero_reg__, %0\n\t"
"out %1, %2\n\t"
"ldi r25, %3\n\t"
"add __zero_reg__, r25\n\t"
"brne spm_fail\n\t"
"spm\n\t"
"rjmp spm_done\n\t"
"spm_fail: clr __zero_reg__\n\t"
"call __assert\n\t"
"spm_done:"
:
: "i" ((uint16_t)(&wen)),
"I" (_SFR_IO_ADDR(__SPM_REG)),
"r" ((uint8_t)(__BOOT_PAGE_ERASE)),
"M" ((uint8_t)(-ACK)),
"z" ((uint16_t)(adr))
: "r25"
);
Код еще не пробовал, завтра сделаю. Вы видите проблемы? Как бы вы решили/решили бы такую проблему?