====== Отладка программ на языке Оберон-07 ======
Отладка программ на Обероне при использовании компилятора [[ob:o7|O7]] осуществляется главным образом через обработку аварийных остановок (TRAP) с помощью модуля **MicroARMv{6,7}MTraps**. При срабатывании аварийной остановки, микроконтроллер перезагружается, переменная этого модуля **trapFlag** становится **TRUE**, а информация о причине перезагрузки доступна в переменной **trap**.
Так во время разработки и отладки программы удобным является следующий прием:
MODULE MyProgram;
IMPORT Traps := MicroARMv7MTraps;
PROCEDURE Init;
BEGIN
...
IF Traps.trapFlag THEN Traps.ClearTrapFlag; ... handle Traps.trap ... END
...
END Init;
BEGIN
Init;
MainLoop
END MyProgram.
Аварийные остановки происходят по ряду причин.
Программные причины (Oberon):
* Код 1: index check
* Код 2: type cast
* Код 3: array size
* Код 4: nil check
* Код 5: nil check (procedure call)
* Код 6: bad divisor (must be > 0)
* Код 7: assert
Аппаратные причины (ARMv{6,7}-M):
* Код 10: hard fault
* Код 11: memory manage
* Код 12: bus fault
* Код 13: usage fault
Аварийные остановки сообщают program counter **pc** и положение в исходном тексте модуля **pos**, поэтому, чтобы узнать место в программе, в котором произошло исключение, надо:
- Используя значение **pc** узнать модуль, в котором оно произошло.
- По номеру положения **pos** (номер символа) найти место в исходном тексте модуля (**O7->Position**)
Для микроконтроллеров NXP достаточно **pc** поделить на 2 (т. к. команды Thumb 2-байтные). Потом сравнить полученное число со списком вывода команды O7ARMv{6,7}MLinker.Link. Ошибка произошла в том модуле, где самое близкое из чисел меньших **pc**.
Для микроконтроллеров STM32 надо предварительно из **pc** вычесть 8000000H, так как программа в ROM начинается с этого положения.
===== ASSERT =====
ASSERT в готовых модулях служит для проверки предусловия или постусловия в рамках контрактного программирования.
SYSTEM.GET(SYSTEM.REG(SP), stkPos); (* SP = 13 *)
ASSERT(stkPos > MicroKernel0.heapLim);
Этот код проверит, не начал ли стек «заползать» на кучу. И если такая ошибка происходит, произойдет аварийная остановка.
Чтобы узнать больше про особенности работы с памятью читайте [[ob:o7:memory|эту заметку]].
===== Пример отладчика =====
Чтобы осуществлять отладку микроконтроллера необходимо обмениваться сообщениями. Более подробно организация обмена описана в заметке [[ob:o7:trimble|Обмен сообщениями с контроллером]].
MODULE MobxAssert;
IMPORT SYSTEM, ARMv7M := MicroARMv7M,
Traps := MicroARMv7MTraps,
Sys := MicroSTM32F103x8System,
Pins := MicroSTM32F10xPinCfg,
TPs := MicroSTM32F1TPorts;
CONST
idInit = 0CBX; idTrap = 0DEX;
VAR
p0: TPs.Port;
PROCEDURE Receive (id: CHAR; a: ARRAY OF CHAR; len: INTEGER);
BEGIN
ASSERT(FALSE)
END Receive;
PROCEDURE MainLoop;
BEGIN
REPEAT
TPs.Receive(p0);
ARMv7M.WFI
UNTIL FALSE
END MainLoop;
PROCEDURE Init;
VAR ok: BOOLEAN; par: TPs.InitPar;
BEGIN
par.n := TPs.USART1;
par.RXPinPort := Pins.A; par.RXPinN := 10;
par.TXPinPort := Pins.A; par.TXPinN := 9;
par.UCLK := Sys.PCLK2;
par.baud := 19200;
par.parity := TPs.parityNone;
par.receive := Receive;
par.version2 := 2;
TPs.Init(p0, par);
TPs.Send(p0, idInit, 0, 0, ok); (* required for "heating" UART *)
IF Traps.trapFlag THEN
Traps.ClearTrapFlag;
TPs.Send(p0, idTrap, SYSTEM.ADR(Traps.trap), 9, ok)
END
END Init;
BEGIN
Init;
MainLoop
END MobxAssert.
Собирать командой:
^Q O7ARMv7MLinker.Link STM32F103C8 MobxAssert
В журнал будет выведена информация о модулях.
linking MobxAssert Ok
MicroARMv7M 256
MicroKernel0 301
MicroARMv7MTraps 987
MicroSTM32F10xxD 1283
MicroSTM32F103x8System 1286
MicroSTM32F10xPinCfg 1535
MicroSTM32F1TPorts 1662
MobxAssert 3146
ROM: 6816 B; RAM: 748 B
Программа для Блэкбокса, которая покажет отладочную информацию
MODULE MobxDebug;
IMPORT SYSTEM, TP := MicroTPorts, Log := StdLog;
TYPE
MessagesHandler = POINTER TO RECORD (TP.MessagesHandler) END;
CONST
id = 20X;
idTrap = 0DEX;
VAR
p: TP.Port;
mh: MessagesHandler;
PROCEDURE Send*(t: INTEGER);
VAR a: ARRAY 4 OF SHORTCHAR;
BEGIN
SYSTEM.PUT(SYSTEM.ADR(a), t);
TP.SendMessage(p, id, a, 4)
END Send;
PROCEDURE (h: MessagesHandler) MessageReceived (id: SHORTCHAR; IN a: ARRAY OF SHORTCHAR; len: INTEGER);
VAR pc, pos, code: INTEGER;
BEGIN
IF id = idTrap THEN
SYSTEM.GET(SYSTEM.ADR (a), pc);
SYSTEM.GET(SYSTEM.ADR (a)+4, pos);
code := ORD(a[8]);
IF pc >= 8000000H THEN pc := pc - 8000000H END;
Log.String("pc: "); Log.Int(pc DIV 2); Log.Ln;
Log.String("pos: "); Log.Int(pos); Log.Ln;
Log.String("code: "); Log.Int(code); Log.Ln;
END
END MessageReceived;
PROCEDURE Open*(port: ARRAY OF CHAR);
BEGIN
IF p = NIL THEN
p := TP.NewPort(port, 19200, {}, mh);
Log.Bool(p # NIL); Log.Ln;
END
END Open;
PROCEDURE Close*;
BEGIN
TP.Close(p)
END Close;
BEGIN
NEW(mh)
CLOSE
Close
END MobxDebug.
В Windows запускается командой:
^Q "MobxDebug.Open('COM1')"
Для Linux будет следующая команда:
^Q "MobxDebug.Open('/dev/ttyUSB0')"
Любая отправка МК команды, вызовет аварийную остановку из-за ASSERT(FALSE) в модуле прошивки.
^Q "MobxDebug.Send(0)"
В журнал Блэкбокса будет выведена информация:
pc: 3155
pos: 337
code: 7
Program Counter (pc) равен 3155, что больше 3146 (см. информацию при сборке), а значит аварийная остановка произошла в модуле MobxAssert в положении 337 с кодом 7.
Чтобы перейти к положению ошибки в модуле, удобно воспользоваться инструментом **O7->Position**.
===== Сторожевой таймер =====
Чтобы определить, когда микроконтроллер «завис» в бесконечном цикле, полезным инструментом является сторожевой таймер **MicroARMv7MSTM32F4WWDG** ([[https://github.com/aixp/O7/blob/master/BlackBox/Micro/Files/STM32F4IWDG.odc|github]]).
Чтобы его использовать, сначала необходимо выполнить процедуру **Init**, и затем периодически вызывать процедуру **Update**. Тогда, при «зависании» микроконтроллера сработает **аварийная остановка с кодом 20**, и возможно определить место в программе, где это произошло (**Traps.trap.pc**)
Более универсальный сторожевой таймер не имеет возможности определения места остановки: **MicroSTM32FxIWDG** ([[https://github.com/aixp/O7/blob/master/BlackBox/Micro/Files/STM32FxIWDG.odc|github]]). Но зато он работает на любых типах микроконтроллеров моделей STM32F.
----
Авторы заметки: [[http://iadenisov.ru|И. А. Денисов]], А. В. Ширяев