diff --git a/doc/MemoryAnalysis.md b/doc/MemoryAnalysis.md index 9563895e..3251c98d 100644 --- a/doc/MemoryAnalysis.md +++ b/doc/MemoryAnalysis.md @@ -4,12 +4,12 @@ The PineTime is equipped with the following memories: - The internal Flash : **512KB** - The external (SPI) Flash : **4MB** -Note that the NRF52832 cannot execute code stored in the external flash : we need to store the whole firmware in the internal flash memory, and use the external one to store graphicals assets, fonts,... +Note that the NRF52832 cannot execute code stored in the external flash : we need to store the whole firmware in the internal flash memory, and use the external one to store graphicals assets, fonts... -This document describes how the RAM and Flash memories and used in InfiniTime and how to analyze and monitor their usage. It was written in the context of [this memory analysis effort](https://github.com/JF002/InfiniTime/issues/313). +This document describes how the RAM and Flash memories are used in InfiniTime and how to analyze and monitor their usage. It was written in the context of [this memory analysis effort](https://github.com/JF002/InfiniTime/issues/313). ## Code sections -A binary is composed of multiple sections. Most of the time, these sections are .text, .rodata, .data and .bss but more sections can be defined in the linker script. +A binary is composed of multiple sections. Most of the time, these sections are : .text, .rodata, .data and .bss but more sections can be defined in the linker script. Here is a small description of these sections and where they end up in memory: @@ -20,7 +20,7 @@ Here is a small description of these sections and where they end up in memory: ## Internal FLASH -The internal flash memory stores the whole firmware : code, variable that are not default-initialized, constants,... +The internal flash memory stores the whole firmware: code, variable that are not default-initialized, constants... The content of the flash memory can be easily analyzed thanks to the MAP file generated by the compiler. This file lists all the symbols from the program along with their size and location (section and addresses) in RAM and FLASH. @@ -36,12 +36,12 @@ In this analysis, I used [Linkermapviz](https://github.com/PromyLOPh/linkermapvi ![linkermapviz](./memoryAnalysis/linkermapviz.png) -Using this tool, you can easily see the size of each symbol relative to the other one, check what is using most of the space,... +Using this tool, you can easily see the size of each symbol relative to the other one, and check what is using most of the space,... -Also, as Linkermapviz is written in Python, you can easily modify it to adapt it to your firmware, export data in another format,... For example, [I modified it to parse the content of the MAP file and export it in a CSV file](https://github.com/JF002/InfiniTime/issues/313#issuecomment-842338620). I could later on open this file in LibreOffice Calc and use sort/filter functionality to search for specific symbols in specific files,... +Also, as Linkermapviz is written in Python, you can easily modify it to adapt it to your firmware, export data in another format,... For example, [I modified it to parse the contents of the MAP file and export it in a CSV file](https://github.com/JF002/InfiniTime/issues/313#issuecomment-842338620). I could later on open this file in LibreOffice Calc and use sort/filter functionality to search for specific symbols in specific files... ### Puncover -[Puncover](https://github.com/HBehrens/puncover) is another useful tools that analyses the binary file generated by the compiler (the .out file that contains all debug information). It provides valuable information about the symbols (data and code) : name, position, size, max stack of each functions, callers, callees,... +[Puncover](https://github.com/HBehrens/puncover) is another useful tools that analyses the binary file generated by the compiler (the .out file that contains all debug information). It provides valuable information about the symbols (data and code): name, position, size, max stack of each functions, callers, callees... ![Puncover](./memoryAnalysis/puncover.png) Puncover is really easy to install: @@ -51,7 +51,12 @@ Puncover is really easy to install: - `python -m virtualenv venv` - `source venv/bin/activate` - Install : `pip install .` - - Run : `puncover --gcc_tools_base=/home/jf/nrf52/gcc-arm-none-eabi-9-2020-q2-update/bin/a-none-eabi- --elf_file /home/jf/git/PineTime/cmake-build-release/src/pinetime-app-1.1.0.out --src_root /home/jf/git/PineTime --build_dir /home/jf/git/PineTime/cmake-build-release/ ` + - Run : `puncover --gcc_tools_base=/path/to/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi- --elf_file /path/to/build/directory/src/pinetime-app-1.1.0.out --src_root /path/to/sources --build_dir /path/to/build/directory` + - Replace + * `/path/to/gcc-arm-none-eabi-9-2020-q2-update/bin` with the path to your gcc-arm-none-eabi toolchain + * `/path/to/build/directory/src/pinetime-app-1.1.0.out` with the path to the binary generated by GCC (.out file) + * `/path/to/sources` with the path to the root folder of the sources (checkout directory) + * `/path/to/build/directory` with the path to the build directory - Launch a browser at http://localhost:5000/ ### Analysis @@ -68,14 +73,14 @@ It's always a good idea to check the flash memory space when working on the proj - Analysis with Puncover : https://github.com/JF002/InfiniTime/issues/313#issuecomment-847311392 ## RAM -RAM memory contains all the data that can be modified at run-time: variables, stack, heap,... +RAM memory contains all the data that can be modified at run-time: variables, stack, heap... ### Data RAM memory can be *statically* allocated, meaning that the size and position of the data are known at compile-time: -You can easily analyze the memory used by variables declared in the global scope using the MAP. You'll find them int the sections .BSS or .DATA. Linkermapviz and Puncover can be used to analyze their memory usage. +You can easily analyze the memory used by variables declared in the global scope using the MAP. You'll find them in the .BSS or .DATA sections. Linkermapviz and Puncover can be used to analyze their memory usage. -Variables declared in the scope of a function will be allocated on the stack. It means that the stack usage will vary according to the state of the program, and cannot be easily analyze at compile time. +Variables declared in the scope of a function will be allocated on the stack. It means that the stack usage will vary according to the state of the program, and cannot be easily analyzed at compile time. ``` uint8_t buffer[1024] @@ -142,7 +147,7 @@ nrfjprog --readram ram.bin You can then display the file using objdump: ``` -hexdump ram.bin -v |less +hexdump ram.bin -v | less ``` The stack is positionned at the end of the RAM -> 0xFFFF. Its size is 8192 bytes, so the end of the stack is at 0xE000. @@ -195,7 +200,7 @@ According to my experimentations, we don't use the stack that much, and 8192 byt - https://github.com/JF002/InfiniTime/issues/313#issuecomment-851035070 ### Heap -The heap is declared in the [linker script](https://github.com/JF002/InfiniTime/blob/develop/nrf_common.ld#L136) and its current size is 8192 bytes. The heap is used for dynamic memory allocation(`malloc()`, `new`,...). +The heap is declared in the [linker script](https://github.com/JF002/InfiniTime/blob/develop/nrf_common.ld#L136) and its current size is 8192 bytes. The heap is used for dynamic memory allocation(`malloc()`, `new`...). Heap monitoring is not easy, but it seems that we can use the following code to know the current usage of the heap: @@ -263,7 +268,7 @@ I tried to monitor this max value while going through all the apps of InfiniTime ## FreeRTOS heap and task stack FreeRTOS statically allocate its own heap buffer in a global variable named `ucHeap`. This is an array of *uint8_t*. Its size is specified by the definition `configTOTAL_HEAP_SIZE` in *FreeRTOSConfig.h* -FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes,...). +FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes...). The function `xPortGetFreeHeapSize()` returns the amount of memory available in this *ucHeap* buffer. If this value reaches 0, FreeRTOS runs out of memory.