Lecture 5
Nikolaus Huber
Memory Management
Memory Architecture
µCs often use a Harvard architecture
Separate memory + buses for code (ROM) and data (RAM)
Code is usually executed in place (XIP), not copied to RAM before execution
Some amount of RAM/ROM is integrated in the µC
Usually more ROM than RAM
Can be extended through external memory if needed
RP2040 actually relies on external ROM
Read-only Memory (ROM)
Used to store code + constant data (e.g. lookup tables)
Unrestricted reading, usually difficult to write
Most common (today): Flash memory
Other kinds do exist: PROM, UV-EPROM, EEPROM
Pico Board has 2MB of Flash (external)
Random-Access Memory (RAM)
Used for data (e.g. stack, heap)
Can also be use for code (more uncommon)
Two main kinds in use today: SRAM, DRAM
Can be accessed through caches
Not so common for internal RAM in µC (why?)
RP2040 has 265kB on-chip SRAM
RP2040 Datasheet
RP2040 Datasheet
ARM Cortex-M0 layout
Processor has a fixed memory map providing up to 4GB of addressable memory
RAM, ROM, peripheral registers, ... are mapped into different memory regions
Details depend on the implemenentation of the chip
For example: Section 2.2. Address Map (RP2040 datasheet)
Read - Modify - Write
We often want to set a single bit in a register
Typically, a CPU cannot write individual bits, only entire bytes or even words
If we want to set a single bit, we need to
Read the whole register
Modify one bit (i.e., do some logic operation)
Write the modified value back into the register
This is not atomic!
Solution 1 - Bit-Banding
Optional feature of ARM Cortex-M3 and M4 µCs
1MB of bit-band region is mapped to 32MB alias region
Each bit in bit-band region corresponds to a full word in alias region
We can set a single bit by writing a full word in the alias region
We can read a single bit by reading from the alias region
Used for example by STM32 chips
More info:
ARM Developer Manual
Solution 2 - Bit Set and Clear Registers
Used by RP2040
A register with multiple bit fields has dedicated SET and CLR registers
Writing a word to SET will set all bits in the main register that are set in the word
Writing a word to CLR will clear all bits in the main register that are set in the word
Main register might not even be visible/accessible!
Ways to manage memory
At compile time (statically)
At startup time
At runtime (dynamically)
Management at compile time
Compiler/Linker creates segments for different kinds of data/code
Code (.text)
Read-only data (.rodata)
Read-write data (.data)
Zero-initialized read-write data (.bss)
More segments possible, depends on implementation and compiler/linker
Code/data is flashed to µC, RAM is initialized during start-up
Management at compile time
Mapping of data to segments can be automatic (static, const) or explicit (compiler specific, gcc: #pragma)
Layout of segments in memory can be chosen in linker file
Examples (assume global variables)
char rwData[100]; => .bss, RAM
char raData[3] = {1, 2, 3}; => .data, RAM
const char roData[3] = {1, 2, 3}; => .rodata/.constdata, ROM
Example - Linker file
Memory in embedded systems
Most common way of memory management
Allocate all required memory at compile time
Sometimes enforced by coding standards
Little risk of runtime errors due to memory management problems
Dynamic memory management
Manual management: malloc( ), free( )
More flexible, might be necessary in some cases
Dynamic memory management
Can cause various problems
Possibly insufficient memory during runtime
Fragmentation
Implementation of malloc and free can be of substantial size
Are malloc and free reentrant functions?
Sometimes compromise: no free, only use malloc during start-up
Easier to test and verify
Malloc and free
Often implemented by the (RT)OS
FreeRTOS
Heap_1: only malloc, no free
Heap_2: malloc + free
Heap_3: malloc + free, both thread-safe
Heap_4: same as Heap_3, but tries to avoid fragmentation
Zephyr
Heaps can be created with K_HEAP_DEFINE
Can use k_heap_alloc and k_heap_free
Use of libc malloc and free is discouraged
For more info look into the Zephyr documentation!
Task stack overflow detection
Canaries
Initially write some known data/pattern to the end of the stack
Periodically check wether that data is still there
In Zephyr => CONFIG_STACK_SENTINEL, gets checked at
Context switch
Hardware interrupts
Thread returns from entry point
k_yield
In Zephyr only used when there is no MPU
Processor modes
ARM processor has multiple processor modes
Thread mode
For executing applications
SW can be executed as privileged or unpriviledged
Handler mode
Entered as a result of an exception, always in privileged
Processor returns to Thread mode when it has finished exception handling
Software execution modes
Unpriviledged mode
Limited access to instructions (cannot change processor state for example)
Cannot access the sytem timer, NVIC, or system control registers
Might have restricted access to memory or peripherals
Privileged mode
Software can use all instructions and has access to all resources
Unpriviledged SW usually requests OS services by issuing a software interrupt => change to handler mode
ARM Operation Modes
Adapted from: The Definite Guide to ARM Cortex-M3 and Cortex-M4 Processors, Joseph Yiu, 3rd Edition
Memory protection unit
Allows access rules to be set up for privileged access and user program access
When an access rule is violated => fault exception
Example usages:
MPU can protect data that belongs to the OS from user programs
MPU can set certain memory regions read-only to prevent accidentally erasing configuration data
MPU can shield different tasks in a multitasking system
More info:
ARM Developer Manual
Thanks for today!