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

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


ob:o7:memory

Различия

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

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

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
ob:o7:memory [2018/11/30 01:13]
127.0.0.1 внешнее изменение
ob:o7:memory [2020/10/29 07:08] (текущий)
Строка 1: Строка 1:
-====== Особенности работы с памятью ======+====== Работа с динамической памятью ======
  
 При выполнении команды **O7ARMv{6,7}Linker.Link** в рабочий журнал будет выведено, сколько полученная программа потребляет ROM и RAM. Например, для программы из [[ob:o7:stm32f103|первого урока]] требуется 880 байт ROM и 16 байт RAM. При выполнении команды **O7ARMv{6,7}Linker.Link** в рабочий журнал будет выведено, сколько полученная программа потребляет ROM и RAM. Например, для программы из [[ob:o7:stm32f103|первого урока]] требуется 880 байт ROM и 16 байт RAM.
Строка 9: Строка 9:
   * таблица указателей, строки, таблица модулей.   * таблица указателей, строки, таблица модулей.
  
-Куча используется только при динамическом выделении памяти через команду NEW, и она заполняется снизу вверх. Для сборки мусора в куче надо вызывать команду **MicroGC0.Collect**. Количество выделенной динамической памяти доступно в переменной **MicroKernel0.allocated**, начало кучи --- **MicroKernel0.heapOrg**, конец --- **MicroKernel0.heapLim**. Следовательно общий размер легко вычислить как их разность.+Куча используется только при динамическом выделении памяти через команду **NEW**, и она заполняется снизу вверх. Для сборки мусора в куче надо вызывать команду **MicroGC0.Collect**. Количество выделенной динамической памяти доступно в переменной **MicroKernel0.allocated**, начало кучи --- **MicroKernel0.heapOrg**, конец --- **MicroKernel0.heapLim**. Следовательно общий размер легко вычислить как их разность.
  
 Стек заполняет выделенную под него память сверху вниз. И когда этот объем полностью заполняется, он начинает расти поверх кучи. Поэтому большие переменные рекомендуется размещать либо глобально, либо в куче, выход за границы которой контролируется автоматически. При переполнении кучи указатель будет равен NIL после вызова NEW. Стек заполняет выделенную под него память сверху вниз. И когда этот объем полностью заполняется, он начинает расти поверх кучи. Поэтому большие переменные рекомендуется размещать либо глобально, либо в куче, выход за границы которой контролируется автоматически. При переполнении кучи указатель будет равен NIL после вызова NEW.
Строка 23: Строка 23:
 Так возможно контролировать при отладке, не превышает ли **stkLen** значение **StkSize** Так возможно контролировать при отладке, не превышает ли **stkLen** значение **StkSize**
  
-Переполнение стека возможно обнаружить и более простым путем, сравнив **stkPos** с **MicroKernel0.heapLim**. Если он меньше, значит стек уже начал писаться в область памяти, выделенной для кучи. Так возможно добавить ASSERT для аварийной остановки в случае переполнения стека. Про обработку аварийных остановок читайте [[http://obertone.ru/ob/o7/debug|заметку про отладку]].+Переполнение стека возможно обнаружить и более простым путем, сравнив **stkPos** с **MicroKernel0.heapLim**. Если он меньше, значит стек уже начал писаться в область памяти, выделенной для кучи. Так возможно добавить ASSERT для аварийной остановки в случае переполнения стека. Про обработку аварийных остановок читайте [[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|И.А. Денисов]] Автор заметки: [[http://iadenisov.ru|И.А. Денисов]]
ob/o7/memory.1543529596.txt.gz · Последнее изменение: 2020/10/29 07:08 (внешнее изменение)