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

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


ob:o7:debug

Различия

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

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

ob:o7:debug [2018/05/26 07:00]
иван_денисов [Сторожевой таймер]
ob:o7:debug [2018/11/30 01:13]
Строка 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**) 
- 
-Более универсальный сторожевой таймер для любых типов микроконтроллеров STM32F* не имеет возможности определения места остановки: **MicroSTM32FxIWDG** ([[https://github.com/aixp/O7/blob/master/Micro/Files/STM32FxIWDG.odc|github]]). 
- 
- 
----- 
- 
-Авторы заметки: [[http://iadenisov.ru|И. А. Денисов]], А. В. Ширяев 
  
ob/o7/debug.txt · Последнее изменение: 2020/10/29 07:08 (внешнее изменение)