...
To assess Flash and static RAM usage at compile time, by analysing memory map files.
To measure Heap and Stack usage at runtime using the technique of memory painting.
To measure execution time using internal and external tools (hardware timers and logic analysers).
To measure energy consumption using osciloscopes and power profilers.
To correlate execution time and energy consumption data in a granular and step-by-step way, allowing the identification of performance bottlenecks.
To measure message sizes of IoT protocols deployed in embedded systems, using techniques such as logging and packet sniffing.
Overview of the setup and workflow:
...
Measuring Memory Usage
Flash and RAM (Compile Time)
,
analyse compiled binary
, nm, analysing linker
with GNU size
and objdump
inspect memory map files
To measure memory at compile time, one can analyse the resulting binary and associated metadata, such as memory map files. Analysis at compile time allows assessing full Flash usage and partial RAM usage. It can be performed with more or less granularity, depending on the tool used. Available tools include GNU objdump
, size
, and nm
; the memory map file generated by the linker can also be used for manual inspection or automated parsing.
...
Look at the target datasheet. For example, in the nRF52840, the RAM goes from 0x2000000 to 0x4000000.
Look at or configure the GNU linker script. For example, our application sets
RAM : ORIGIN = 0x20000000, LENGTH = 64K
, meaning that the RAM begins at 0x2000000 (as per the datasheet) and has a total size of 64 kiB.Look at the generated memory map file, and find where are the symbols
_stack_start
and__sheap
. Since the stack grows from top to bottom (e.g. from 0x2000000 to 0x2000000 + 64 kiB), and the heap grows from the bottom, that_stack_start - __sheap
is the total size of our allocatable RAM (i.e. discarding . Note that this discards sections such as .bss, .data, .uninit). , which we do not want to paint!
We now know that we want to paint the memory from __sheap
up to _stack_start
.
Before continuing, remember that we want to write in the RAM before our code starts executing, otherwise we risk overwriting the stack that is already in use. One way of doing that is writing the loop in assembly (using only registers), and another way is doing it in the reset handler or in some pre-initialisation code in your platform. The cortex-m-rt
crate provides a pre_init
hook that runs before main, which is ideal to put the stack painting code. In the code below, we first obtain the address where the heap starts using the symbol defined by the linker. Next, since our code is already executing, we do not want to overwrite already allocated stack memory, so we get the current value of the stack pointer , (offset by a constant, since we are using the stack while painting it). Finally, we run the loop that writes the pattern to the memory.
...
Code Block |
---|
$ painted_words=$(( (0x20001000 - 0x20000440) / 4 )) $ remaining_bytes=$(probe-rs read b32 0x20000440 $painted_words --chip nRF52840_xxAA | tr ' ' '\n' | grep deadbeef | wc -l | awk '{print $1*4}') $ echo $(( (painted_words * 4) - remaining_bytes)) 908 |
Measuring Execution Time
timers, gpio's connected to logic
...
Measuring Energy Consumption
multimeters, oscilloscopes, power profilers
Correlating Execution Time and Energy Consumption
Syncing execution time and energy consumption: power profiler with gpio
Step-by-step Granular Time and Energy Consumption
...
analyzers
There are two main approaches to measuring execution time. In one of them, let’s call it internal, we rely on some timer already available in our board, and use it to keep track of certain interesting events that we want to measure. While this approach is simple, it has the downsides of consuming some energy and an additional peripheral, as well as some extra added code (e.g. a RTC driver). The second approach is the external, where an instrument such as a logical analyser is connected to one or more GPIOs in the board. Then, we want to log an event, we toggle those GPIOs on or off, and have them logged over time by the external instrument software.
Measuring Energy Consumption
multimeters, oscilloscopes, power profilers
To measure energy consumption we connect the device to an external instrument, such as a multimeter, an oscilloscope or a power profiler, that can measure current draw.
Multimeters can be used to read the current consumption at a given point in time, but do not allow automating measurements or taking them over time.
TODO: how to set up the hardware part of measuring with an osciloscope?
A more sophisticated and accurate way of measuring energy consumption consists in using a power profiler, such as the nRF Power Profiler Kit or the Otii Arc Pro. For this tutorial we selected the latter, since it is designed to be board-agnostic and also allow interacting with GPIO pins.
Correlating Execution Time and Energy Consumption
Syncing execution time and energy consumption: power profiler (otii arc) with gpio
Advanced Measurements for Energy and Time
Step-by-step Granular Time and Energy Consumption
Obtaining step-by-step time and energy consumption: merging results from power profiler and logic analyser
PPI (Programmable Peripheral Interconnect) / EXTI (External Interrupt) Mapping
Often it is necessary to link an input GPIO directly to an output GPIO at the hardware level, bypassing software delays. This type of operation is available on certain microcontrollers and systems to reduce latency. In some scenarios peripheral devices are attached to a MCU and we want to measure the energy consumption during the peripheral interaction. Instead of raising a GPIO in SW that is connected to the measurement kit (e.g. PPK), one can use the PPI/ EXTI functionality of the MCU.
Examples:
A button press connected to an input GPIO immediately toggles an LED on an output GPIO.
A sensor’s input signal on GPIO triggers another peripheral or an output pin change via an interrupt.
HW-Platforms:
Nordic Semiconductor (nRF Series) – PPI (Programmable Peripheral Interconnect): PPI allows one hardware event (e.g., a GPIO input change) to directly trigger another (e.g., GPIO output) without CPU intervention.
STM32 Microcontrollers – EXTI (External Interrupt) with Direct GPIO Mapping: GPIOs can be mapped to trigger interrupts or interact with timers, enabling fast response without CPU control.
Texas Instruments (TI) – PRU (Programmable Real-Time Unit): On TI microcontrollers, real-time units handle direct GPIO linking and control, ensuring minimal latency between input and output operations.
Measuring Message Sizes
logging, hardware packet sniffing, packet analysis tools (wireshark)
...
If the constrained devices talks to a computer or gateway, just run Wireshark on the computer.
If two devices talk between each other, you need a third device that understand the protocol to sniff the conversation. Some IoT platforms offer facilities to save the conversation as a .cap file, which can be later analyzed on Wireshark.
drafts
nm (actually not recommended)
...