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

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


ob:o7:memory

Различия

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

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

ob:o7:memory [2019/10/27 09:44]
иван_денисов
ob:o7:memory [2020/10/29 07:08]
Строка 1: Строка 1:
-====== Работа с динамической памятью ====== 
  
-При выполнении команды **O7ARMv{6,7}Linker.Link** в рабочий журнал будет выведено, сколько полученная программа потребляет ROM и RAM. Например, для программы из [[ob:o7:stm32f103|первого урока]] требуется 880 байт ROM и 16 байт RAM. 
- 
-Доступная память RAM используется под следующие ресурсы: 
-  * куча; 
-  * стек; 
-  * глобальные переменные; 
-  * таблица указателей, строки, таблица модулей. 
- 
-Куча используется только при динамическом выделении памяти через команду **NEW**, и она заполняется снизу вверх. Для сборки мусора в куче надо вызывать команду **MicroGC0.Collect**. Количество выделенной динамической памяти доступно в переменной **MicroKernel0.allocated**, начало кучи --- **MicroKernel0.heapOrg**, конец --- **MicroKernel0.heapLim**. Следовательно общий размер легко вычислить как их разность. 
- 
-Стек заполняет выделенную под него память сверху вниз. И когда этот объем полностью заполняется, он начинает расти поверх кучи. Поэтому большие переменные рекомендуется размещать либо глобально, либо в куче, выход за границы которой контролируется автоматически. При переполнении кучи указатель будет равен NIL после вызова NEW. 
- 
-Размер памяти, выделяемой под стек, определяется константой **StkSize** в модуле **System** для конкретного контроллера. Например, для STM32F4 это 8192 байта, для STM32F103x{8,B} --- 2048 байт. Это значение вы можете легко изменить исходя из требований вашего проекта. Если вы используете рекурсивные алгоритмы, то скорее всего вам потребуется зарезервировать больше места под стек. 
- 
-Степень заполнение стека возможно вычислить, исходя из положения начала и его текущего положения: 
- 
- SYSTEM.GET(SYSTEM.REG(MT), stkOrg); (* MT = 6 *) 
- SYSTEM.GET(SYSTEM.REG(SP), stkPos); (* SP = 13 *) 
- stkLen := stkOrg - stkPos; 
- 
-Так возможно контролировать при отладке, не превышает ли **stkLen** значение **StkSize** 
- 
-Переполнение стека возможно обнаружить и более простым путем, сравнив **stkPos** с **MicroKernel0.heapLim**. Если он меньше, значит стек уже начал писаться в область памяти, выделенной для кучи. Так возможно добавить ASSERT для аварийной остановки в случае переполнения стека. Про обработку аварийных остановок читайте [[http://obertone.ru/ob/o7/debug|заметку про отладку]]. 
- 
-===== Тестирование сборщика мусора ===== 
- 
-Александр Ширяев разработал модуль для тестирования сборщика мусора: 
- 
-{{ :ob:o7:testgc0.odc |}} 
- 
- MODULE TmpTestGC0; 
-  
- (* 
- Alexander Shiryaev, 2019.10 
-  
- Dynamic memory (and garbage collection) test 
-  
- Board: Nucleo-F446RE 
- PA2: UART TX 
- PA3: UART RX 
- PA5: LED 
- *) 
-  
- IMPORT 
- SYSTEM, 
- ARMv7M := MicroARMv7M, 
- Traps := MicroARMv7MTraps, 
- Kernel := MicroKernel0, 
- GC := MicroGC0, 
- TPorts := MicroSTM32F4TPorts, 
- Pins := MicroSTM32F4Pins, 
- Sys := MicroSTM32F405System, 
- SysTick0 := DplaARMv7MSTM32SysTick0, 
- WWDG := MicroARMv7MSTM32F4WWDG; 
-  
- CONST 
- enableLED = TRUE; 
-  
- led0PinPort = Pins.A; 
- led0PinN = 5; 
- led0BSRR = Pins.GPIOABSRR; 
-  
- freq0 = 128; (* Hz *) 
-  
- TYPE 
- Operation = POINTER TO OperationDesc; 
- OperationDesc = RECORD 
- END; 
- Repeat = POINTER TO RepeatDesc; 
- RepeatDesc = RECORD (OperationDesc) 
- value: INTEGER 
- END; 
- End = POINTER TO EndDesc; 
- EndDesc = RECORD (OperationDesc) 
- END; 
-  
- VAR 
- cnt0: INTEGER; 
- operations: ARRAY 100 OF Operation; 
- nOp: INTEGER; 
- port: TPorts.Port; 
-  
- PROCEDURE LEDOn; 
- BEGIN 
- SYSTEM.PUT(led0BSRR, {led0PinN}) 
- END LEDOn; 
-  
- PROCEDURE LEDOff; 
- BEGIN 
- SYSTEM.PUT(led0BSRR, {led0PinN+16}) 
- END LEDOff; 
-  
- PROCEDURE InitOperations; 
- VAR i: INTEGER; 
- BEGIN 
- i := 0; 
- WHILE i < LEN(operations) DO 
- operations[i] := NIL; 
- INC(i) 
- END; 
- GC.Collect; 
- nOp := 0 
- END InitOperations; 
-  
- PROCEDURE AddRepeat (val: INTEGER); 
- VAR repeat: Repeat; 
- BEGIN 
- repeat := NIL; 
- NEW(repeat); 
- repeat.value := val; 
- operations[nOp] := repeat; 
- INC(nOp) 
- END AddRepeat; 
-  
- PROCEDURE AddEnd; 
- VAR end: Repeat; 
- BEGIN 
- end := NIL; 
- NEW(end); 
- INC(nOp) 
- END AddEnd; 
-  
- PROCEDURE PopulateOperations; 
- VAR i: INTEGER; 
- BEGIN 
- i := 0; 
- WHILE i < 10 DO 
- AddRepeat(i); 
- INC(i) 
- END; 
- i := 0; 
- WHILE i < 5 DO 
- AddEnd; 
- INC(i) 
- END 
- END PopulateOperations; 
-  
- PROCEDURE Receive (id: CHAR; a: ARRAY OF CHAR; len: INTEGER); 
- VAR ok: BOOLEAN; 
- i: INTEGER; 
- BEGIN 
- IF id = 20X (* communication test *) THEN 
- TPorts.Send(port, 21X, SYSTEM.ADR(a), len, ok) 
- ELSIF id = 22X (* clear dynamic memory *) THEN 
- IF len = 0 THEN 
- InitOperations; 
- TPorts.Send(port, 23X, 0, 0, ok) 
- END 
- ELSIF id = 24X (* populate dynamic memory *) THEN 
- IF len = 0 THEN 
- PopulateOperations; 
- TPorts.Send(port, 25X, 0, 0, ok) 
- END 
- ELSIF id = 26X (* Kernel.allocated query *) THEN 
- IF len = 0 THEN 
- TPorts.Send(port, 27X, SYSTEM.ADR(Kernel.allocated), 4, ok) 
- END 
- ELSIF id = 28X (* TRAP test *) THEN 
- IF len = 0 THEN 
- ASSERT(FALSE) 
- END 
- ELSIF id = 2AX (* hangup test *) THEN 
- IF len = 0 THEN 
- REPEAT UNTIL FALSE 
- END 
- ELSIF id = 2CX (* multiple clear/populate calls *) THEN 
- IF len = 0 THEN 
- i := 1000; 
- REPEAT DEC(i); 
- InitOperations; 
- PopulateOperations; 
- WWDG.Update 
- UNTIL i = 0; 
- TPorts.Send(port, 2DX, 0, 0, ok) 
- END 
- END 
- END Receive; 
-  
- PROCEDURE OnFreq0; 
- BEGIN 
- IF enableLED THEN 
- (* LED blink *) 
- INC(cnt0); 
- IF cnt0 MOD (freq0 DIV 4) = 0 THEN 
- LEDOn 
- ELSIF cnt0 MOD (freq0 DIV 4) = freq0 DIV 8 THEN 
- LEDOff 
- END 
- END; 
-  
- WWDG.Update (* reset watchdog timer *) 
- END OnFreq0; 
-  
- PROCEDURE MainLoop; 
- BEGIN 
- REPEAT 
- TPorts.Receive(port); 
- IF SysTick0.OnTimer() THEN 
- OnFreq0 
- END; 
- ARMv7M.WFI 
- UNTIL FALSE 
- END MainLoop; 
-  
- PROCEDURE Init; 
- VAR ok: BOOLEAN; 
- i: INTEGER; 
-  
- PROCEDURE InitPort; 
- VAR p: TPorts.InitPar; 
- BEGIN 
- p.n := TPorts.USART2; 
- p.RXPinPort := Pins.A; 
- p.RXPinN := 3; 
- p.RXPinAF := Pins.AF7; 
- p.TXPinPort := Pins.A; 
- p.TXPinN := 2; 
- p.TXPinAF := Pins.AF7; 
- p.UCLK := Sys.PCLK1; 
- p.baud := 115200; 
- p.parity := TPorts.parityNone; 
- p.receive := Receive; 
- p.version1 := 1; 
- TPorts.Init(port, p) 
- END InitPort; 
-  
- PROCEDURE InitLED; 
- BEGIN 
- Pins.Configure(led0PinPort, led0PinN, 
- Pins.output, Pins.pushPull, Pins.low, Pins.noPull, 0) 
- END InitLED; 
-  
- BEGIN 
- InitPort; (* initialize communication port *) 
- IF enableLED THEN 
- InitLED; LEDOff; cnt0 := 0 (* initialize LED and related logic *) 
- END; 
- WWDG.Init(WWDG.WDGTBMax, WWDG.WMax, WWDG.TMax); (* initialize watchdog timer *) 
- SysTick0.Init(Sys.HCLK, freq0); (* initialize periodic timer *) 
-  
- IF Traps.trapFlag THEN Traps.ClearTrapFlag; 
- i := 3; 
- REPEAT DEC(i); 
- TPorts.Send(port, 0DEX, SYSTEM.ADR(Traps.trap), 9, ok) 
- UNTIL i = 0 
- ELSE 
- i := 3; 
- REPEAT DEC(i); 
- TPorts.Send(port, 0CBX, 0, 0, ok) 
- UNTIL i = 0 
- END 
- END Init; 
-  
- BEGIN 
- Init; 
- MainLoop 
- END TmpTestGC0. 
- 
- O7ARMv7MLinker.Link STM32F446RE TmpTestGC0 
- 
----- 
- 
-Автор заметки: [[http://iadenisov.ru|И.А. Денисов]] 
ob/o7/memory.txt · Последнее изменение: 2020/10/29 07:08 (внешнее изменение)