Инструменты пользователя

Инструменты сайта


ob:o7:debug

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

ob:o7:debug [2018/11/30 01:13]
ob:o7:debug [2020/10/29 07:08] (текущий)
Строка 1: Строка 1:
 +====== Отладка программ на языке Оберон-07 ======
 +
 +Отладка программ на Обероне при использовании компилятора [[ob:o7|O7]] осуществляется главным образом через обработку аварийных остановок (TRAP) с помощью модуля **MicroARMv{6,7}MTraps**. При срабатывании аварийной остановки, микроконтроллер перезагружается, переменная этого модуля **trapFlag** становится **TRUE**, а информация о причине перезагрузки доступна в переменной **trap**.
 +
 +Так во время разработки и отладки программы удобным является следующий прием:
 +
 +<code>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.</code>
 +
 +
 +Аварийные остановки происходят по ряду причин.
 +
 +Программные причины (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|Обмен сообщениями с контроллером]].
 +
 +<code>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.</code>
 +
 +Собирать командой:
 +<code>^Q O7ARMv7MLinker.Link STM32F103C8 MobxAssert</code>
 +
 +В журнал будет выведена информация о модулях.
 +<code>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
 +</code>
 +
 +
 +Программа для Блэкбокса, которая покажет отладочную информацию
 +
 +<code>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.</code>
 +
 +В Windows запускается командой:
 +<code>^Q "MobxDebug.Open('COM1')"</code>
 +
 +Для Linux будет следующая команда:
 +<code>^Q "MobxDebug.Open('/dev/ttyUSB0')"</code>
 +
 +Любая отправка МК команды, вызовет аварийную остановку из-за ASSERT(FALSE) в модуле прошивки.
 +<code>^Q "MobxDebug.Send(0)"</code>
 +
 +В журнал Блэкбокса будет выведена информация:
 +<code>pc:  3155
 +pos:  337
 +code:  7</code>
 +
 +Program Counter (pc) равен 3155, что больше 3146 (см. информацию при сборке), а значит аварийная остановка произошла в модуле MobxAssert в положении 337 с кодом 7.
 +
 +Чтобы перейти к положению ошибки в модуле, удобно воспользоваться инструментом **O7->Position**.
 +
 +
 +===== Сторожевой таймер =====
 +
 +Чтобы определить, когда микроконтроллер «завис» в бесконечном цикле, полезным инструментом является сторожевой таймер **MicroARMv7MSTM32F4WWDG** ([[https://github.com/aixp/O7/blob/master/Micro/Files/STM32F4IWDG.odc|github]]).
 +
 +Чтобы его использовать, сначала необходимо выполнить процедуру **Init**, и затем периодически вызывать процедуру **Update**. Тогда, при «зависании» микроконтроллера сработает **аварийная остановка с кодом 20**, и возможно определить место в программе, где это произошло (**Traps.trap.pc**)
 +
 +Более универсальный сторожевой таймер не имеет возможности определения места остановки: **MicroSTM32FxIWDG** ([[https://github.com/aixp/O7/blob/master/Micro/Files/STM32FxIWDG.odc|github]]). Но зато он работает на любых типах микроконтроллеров моделей STM32F.
 +
 +
 +----
 +
 +Авторы заметки: [[http://iadenisov.ru|И. А. Денисов]], А. В. Ширяев