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

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


ob:o7:memory

Различия

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

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

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
ob:o7:memory [2016/08/24 19:06]
иван_денисов
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.
  
 Доступная память RAM используется под следующие ресурсы: Доступная память RAM используется под следующие ресурсы:
-  * heap (heapLim - heapOrg) +  * куча; 
-  * stack +  * стек; 
-  * global variables +  * глобальные переменные; 
-  * pointers tablestringsmodules table+  * таблица указателейстрокитаблица модулей.
  
-Heap (кучаиспользуется только при динамическом выделении памяти через команду NEW, и она заполняется снизу вверх. (Перед вызовом команды NEW для указателя, ему необходимо присвоить значение NIL). Для сборки мусора в куче надо вызывать команду **MicroGC0.Collect**. Количество выделенной динамической памяти доступно в переменной **MicroKernel0.allocated**, начало кучи --- **MicroKernel0.heapOrg**, конец --- **MicroKernel0.heapLim**. Следовательно общий размер легко вычислить как их разность.+Куча используется только при динамическом выделении памяти через команду **NEW**, и она заполняется снизу вверх. Для сборки мусора в куче надо вызывать команду **MicroGC0.Collect**. Количество выделенной динамической памяти доступно в переменной **MicroKernel0.allocated**, начало кучи --- **MicroKernel0.heapOrg**, конец --- **MicroKernel0.heapLim**. Следовательно общий размер легко вычислить как их разность.
  
-Стек заполняет выделенную под него память сверху вниз. И когда этот объем полностью заполняется, он начинает расти поверх кучи. Поэтому большие переменные рекомендуется размещать либо глобально, либо в куче, выход за границы которой контролируется автоматически.+Стек заполняет выделенную под него память сверху вниз. И когда этот объем полностью заполняется, он начинает расти поверх кучи. Поэтому большие переменные рекомендуется размещать либо глобально, либо в куче, выход за границы которой контролируется автоматически. При переполнении кучи указатель будет равен NIL после вызова NEW.
  
-Размер памяти, выделяемой под стек, определяется переменной **StkSize** в модуле **System** для конкретного контроллера. Например, для STM32F4 это 8192 байта, для STM32F103x{8,B} --- 2048 байт. Это значение вы можете легко изменить исходя из требований вашего проекта. Если вы используете рекурсивные алгоритмы, то скорее всего вам потребуется зарезервировать больше места под стек.+Размер памяти, выделяемой под стек, определяется константой **StkSize** в модуле **System** для конкретного контроллера. Например, для STM32F4 это 8192 байта, для STM32F103x{8,B} --- 2048 байт. Это значение вы можете легко изменить исходя из требований вашего проекта. Если вы используете рекурсивные алгоритмы, то скорее всего вам потребуется зарезервировать больше места под стек.
  
 Степень заполнение стека возможно вычислить, исходя из положения начала и его текущего положения: Степень заполнение стека возможно вычислить, исходя из положения начала и его текущего положения:
  
- SYSTEM.GET(SYSTEM.REG(6), stkOrg); + SYSTEM.GET(SYSTEM.REG(MT), stkOrg); (* MT = 6 *) 
- SYSTEM.GET(SYSTEM.REG(13), stkPos);+ SYSTEM.GET(SYSTEM.REG(SP), stkPos); (* SP = 13 *)
  stkLen := stkOrg - stkPos;  stkLen := stkOrg - stkPos;
  
 Так возможно контролировать при отладке, не превышает ли **stkLen** значение **StkSize** Так возможно контролировать при отладке, не превышает ли **stkLen** значение **StkSize**
  
-Переполнение стека возможно обнаружить и более простым путем, сравнив **stkPos** с **MicroKernel0.heapLim**. Если он меньше, значит стек уже начал писаться в область памяти, выделенной для кучи.+Переполнение стека возможно обнаружить и более простым путем, сравнив **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|И.А. Денисов]]
ob/o7/memory.1472054785.txt.gz · Последнее изменение: 2020/10/29 07:08 (внешнее изменение)