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

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


ob:o7:memory

Различия

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

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

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
Следующая версия Следующая версия справа и слева
ob:o7:memory [2017/04/06 23:34]
иван_денисов
ob:o7:memory [2019/10/27 09:44]
иван_денисов
Строка 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.
Строка 24: Строка 24:
  
 Переполнение стека возможно обнаружить и более простым путем, сравнив **stkPos** с **MicroKernel0.heapLim**. Если он меньше, значит стек уже начал писаться в область памяти, выделенной для кучи. Так возможно добавить ASSERT для аварийной остановки в случае переполнения стека. Про обработку аварийных остановок читайте [[http://obertone.ru/ob/o7/debug|заметку про отладку]]. Переполнение стека возможно обнаружить и более простым путем, сравнив **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|И.А. Денисов]] Автор заметки: [[http://iadenisov.ru|И.А. Денисов]]
ob/o7/memory.txt · Последнее изменение: 2020/10/29 07:08 (внешнее изменение)