====== Отладка программ на языке Оберон-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/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|И. А. Денисов]], А. В. Ширяев