Lab 2: Renode for MCU Emulation and Validation

Duration

2 hours:

  • 0h30 guided introduction to Renode and the digital-twin workflow;
  • 1h30 practical firmware exercise using a virtual sensor stream.

Learning Objectives

After this laboratory, students should be able to:

  • explain the role of Renode in embedded firmware development and testing;
  • distinguish between .resc Renode scripts and .repl platform descriptions;
  • run Raspberry Pi Pico firmware in Renode without physical hardware;
  • attach a custom virtual I2C peripheral to an emulated board;
  • validate firmware behavior through UART output and generated reports;
  • implement and compare a naive threshold filter with a tiny fixed-point model.

Laboratory Scenario

The lab uses a Raspberry Pi Pico firmware project running in a Renode digital twin. Renode provides the RP2040 board model, UART capture, and a deterministic virtual I2C sensor. No physical Pico board or sensor is required.

[Virtual I2C sensor @ 0x52] -> sample stream -> [RP2040 firmware]

Each virtual sensor sample contains:

  • seq: sample number;
  • value: signed sensor reading.

The firmware must classify every sample with two methods:

  • Threshold filter: simple hand-written if/else logic using minimum value, maximum value, and maximum jump thresholds.
  • TinyML-style filter: a small fixed-point linear model using weights from firmware/model_weights.h.

The goal is not to train a model during the lab. The model weights are already provided. Students focus on running firmware in a digital twin, reading the virtual sensor, implementing the inference formula, and comparing the two filtering methods.

Project Layout

The important files are:

firmware/main.c
firmware/model_weights.h
renode/run_test.resc
renode/run_metrics.resc
renode/sensor_i2c.repl
renode/peripherals/VirtualSensorStream.cs
docker/scripts/build_firmware.sh
docker/scripts/run_test.sh
docker/scripts/generate_report.py
docs/renode_primer.md
docs/exercise_guide.md
reference/firmware/main.c
output/report.html

Short description:

  • firmware/main.c contains the student TODOs.
  • firmware/model_weights.h contains the fixed-point model constants.
  • renode/run_test.resc creates the Renode scenario, loads firmware, and captures UART output.
  • renode/sensor_i2c.repl connects the virtual sensor to the Pico I2C bus.
  • renode/peripherals/VirtualSensorStream.cs implements the deterministic I2C sensor stream.
  • docker/scripts/run_test.sh validates the UART output and final summary.
  • docker/scripts/generate_report.py creates output/report.html.
  • reference/firmware/main.c contains the completed instructor reference implementation.

Part 1: Renode Theory

What Renode Is

Renode is an emulator for embedded systems. It can run firmware built for a microcontroller or SoC inside a virtual machine that includes CPUs, buses, memories, UARTs, timers, and peripherals.

For this laboratory, Renode gives us a repeatable digital twin:

  • the same Pico firmware is loaded every run;
  • the same deterministic virtual sensor samples are produced every run;
  • the same validation script checks the UART output every run.

This makes the exercise independent of physical boards, USB cables, sensor availability, and lab hardware differences.

Digital Twin Mental Model

A Renode lab scenario has three layers:

  • firmware: the compiled application, here firmware.elf;
  • virtual hardware: the Raspberry Pi Pico plus a custom I2C sensor;
  • automation: the .resc script that creates, wires, runs, and observes the machine.

In this lab:

firmware/main.c                      -> compiled to firmware.elf
renode/peripherals/VirtualSensorStream.cs -> custom I2C peripheral
renode/sensor_i2c.repl               -> virtual wiring overlay
renode/run_test.resc                 -> Renode automation script

RESC Files

.resc files are Renode monitor scripts. They automate what an instructor could otherwise type manually in the Renode monitor.

The lab script is renode/run_test.resc. It performs these jobs:

  • makes the RP2040 Renode support visible;
  • loads the custom C# sensor peripheral;
  • creates a Raspberry Pi Pico machine;
  • applies the I2C wiring overlay;
  • loads the compiled firmware;
  • captures UART output;
  • runs the emulation for a fixed time.

Typical commands in this scenario:

path add @/opt/renode-rp2040
include @/workspace/renode/peripherals/VirtualSensorStream.cs
EnsureTypeIsLoaded "Antmicro.Renode.Peripherals.I2C.VirtualSensorStream"
include @boards/initialize_raspberry_pico.resc
machine LoadPlatformDescription @/workspace/renode/sensor_i2c.repl
sysbus LoadELF @/workspace/build/firmware.elf
sysbus.uart0 CreateFileBackend @/workspace/output/uart_output.txt true
emulation RunFor "00:00:05"

Key idea: .resc files describe the experiment procedure.

REPL Files

.repl files describe platform topology: which peripherals exist and where they are connected.

This lab uses a small overlay instead of redefining the whole Pico:

sensor: I2C.VirtualSensorStream @ i2c0 0x52

Read it as:

  • create a peripheral instance named sensor;
  • instantiate the Renode type I2C.VirtualSensorStream;
  • attach it to bus i2c0;
  • expose it at I2C address 0x52.

Key idea: .repl files describe the virtual wires.

Custom Virtual Peripheral

VirtualSensorStream.cs is a minimal Renode I2C peripheral. It models only the behavior needed by this lab:

  • firmware writes register 0x00;
  • firmware reads four bytes;
  • the sensor returns a frame: seq:uint16_be and value:int16_be;
  • each complete frame advances to the next sample.

The component implements the Renode I2C peripheral interface. It is intentionally not a complete real sensor. For this exercise, deterministic behavior is more important than detailed physical realism.

The virtual wiring path is:

run_test.resc
  loads VirtualSensorStream.cs
  initializes Raspberry Pi Pico
  applies sensor_i2c.repl

sensor_i2c.repl
  connects VirtualSensorStream to i2c0 address 0x52

firmware/main.c
  initializes i2c0 on the Pico pins
  writes register 0x00
  reads 4-byte sample frames

Validation Surface

The validation harness does not inspect C variables directly. It checks the observable firmware behavior through UART output.

Important UART lines:

SAMPLE seq=... value=...
THRESHOLD seq=... decision=...
MODEL seq=... decision=... score=...
DISAGREE seq=...
SUMMARY threshold_keep=... threshold_drop=... model_keep=... model_drop=... disagreements=...

This is close to a real board workflow: the observable interface is serial output plus emulator logs and generated reports.

Part 2: Running The Starter

From the browser IDE, press Run in the side panel.

From a shell:

./run.sh up --build digital-twin

The starter code should build and read the sensor stream. The filter checks fail until students implement the TODO functions.

Expected final passing summary after implementation:

SUMMARY threshold_keep=8 threshold_drop=2 model_keep=6 model_drop=4 disagreements=2
>>> ALL CHECKS PASSED - sensor filters behave as expected <<<

The HTML report is written to:

output/report.html

Part 3: Exercise Data

The virtual sensor emits this deterministic stream:

Seq Value Intended role
0 500 normal baseline
1 506 small normal movement
2 612 borderline jump
3 520 return to normal
4 785 high outlier
5 532 normal
6 340 low outlier
7 650 borderline jump
8 545 normal
9 498 normal

The threshold filter should reject only the gross high and low outliers:

seq=4
seq=6

The tiny model should reject the two gross outliers and two borderline jumps:

seq=2
seq=4
seq=6
seq=7

Therefore, the two methods disagree at:

seq=2
seq=7

Part 4: Exercises

Exercise 1: Identify The Renode Scenario

Open renode/run_test.resc.

Tasks:

  1. Find the command that loads VirtualSensorStream.cs.
  2. Find the command that initializes the Raspberry Pi Pico machine.
  3. Find the command that applies sensor_i2c.repl.
  4. Find the command that loads firmware.elf.
  5. Find where UART output is written.

Questions:

  1. Which file describes the virtual sensor wiring?
  2. Which bus is the sensor attached to?
  3. What I2C address does the sensor use?
  4. Which output file contains firmware UART logs?

hints:

  • wiring file: renode/sensor_i2c.repl;
  • bus: i2c0;
  • I2C address: 0x52;
  • UART output: /workspace/output/uart_output.txt.

Exercise 2: Follow One Sensor Sample End To End

Files to inspect:

renode/peripherals/VirtualSensorStream.cs
renode/sensor_i2c.repl
firmware/main.c

Tasks:

  1. In VirtualSensorStream.cs, find the deterministic sample table.
  2. Find the code that returns a 4-byte sample frame.
  3. In firmware/main.c, find the code that reads one sample from I2C.
  4. Run the validation and locate the UART line for seq=0.

Fill the table:

Step File Evidence
Sensor provides sample table
REPL connects sensor to I2C
Firmware reads sample frame
Firmware prints sample
Harness validates sample count

Exercise 3: Implement The Threshold Filter

Open firmware/main.c and implement:

threshold_filter_should_keep

Rules:

  • drop values below THRESHOLD_MIN_VALUE;
  • drop values above THRESHOLD_MAX_VALUE;
  • drop values whose absolute jump from previous_kept is greater than THRESHOLD_MAX_JUMP;
  • keep all other values.

Expected threshold drops:

seq=4
seq=6

After this exercise, some checks may still fail because the model filter is not implemented yet.

Exercise 4: Implement The Tiny Fixed-Point Model Score

Open:

firmware/main.c
firmware/model_weights.h

Implement:

model_filter_score

The model uses two features:

abs_center = abs(value - MODEL_CENTER_VALUE)
abs_jump   = abs(value - previous_kept)

The score formula is:

score = MODEL_BIAS_Q0
      + MODEL_WEIGHT_ABS_CENTER_Q0 * abs_center
      + MODEL_WEIGHT_ABS_JUMP_Q0 * abs_jump;

This model is intentionally tiny:

  • it uses integer arithmetic;
  • there is no runtime training;
  • all weights are already provided;
  • the goal is to understand how a compact model can replace hand-written thresholds.

Exercise 5: Implement The TinyML Keep/Drop Decision

Implement:

model_filter_should_keep

Rule:

  • keep the sample if score >= MODEL_KEEP_THRESHOLD_Q0;
  • drop it otherwise.

Expected model drops:

seq=2
seq=4
seq=6
seq=7

Run the validation again. The final summary should be:

SUMMARY threshold_keep=8 threshold_drop=2 model_keep=6 model_drop=4 disagreements=2

Exercise 6: Compare The Two Methods

Use output/report.html and UART output to answer:

  1. Which samples are rejected by both methods?
  2. Which samples are accepted by the threshold filter but rejected by the model?
  3. Why do the two methods disagree at seq=2 and seq=7?
  4. Which method is easier to explain?
  5. Which method is easier to tune if the data distribution changes?

hint:

  • the threshold filter catches large absolute outliers and large immediate jumps;
  • the model uses both distance from a learned center and distance from the previous accepted model value;
  • the model is stricter on borderline jump samples because both features contribute to the score.

Exercise 7: Optional Extension

Choose one:

  • change one threshold constant and explain how the final summary changes;
  • add one more sample to VirtualSensorStream.cs and update the expected summary;
  • change one model weight and explain which samples become more or less likely to be kept;
  • update docker/scripts/generate_report.py to make disagreements easier to inspect in the HTML report.

Common Problems

Docker Socket Errors

If Docker cannot connect to the daemon, try:

DOCKER_HOST=unix:///var/run/docker.sock ./run.sh up --build digital-twin

If group membership was just changed and the current shell has not picked it up, use:

sg docker -c 'DOCKER_HOST=unix:///var/run/docker.sock ./run.sh up --build digital-twin'

Firmware Builds But Samples Are Wrong

Check:

  • renode/sensor_i2c.repl connects the sensor to i2c0 at 0x52;
  • firmware uses the same I2C address;
  • the virtual sensor preserves a whole sample frame across byte-wise reads.

Validation Fails After Editing

Use evidence in this order:

  1. output/report.html for the high-level failing check;
  2. output/uart_output.txt for firmware-side behavior;
  3. renode/run_test.resc and renode/sensor_i2c.repl for wiring issues;
  4. VirtualSensorStream.cs for sample stream issues.

Starter Code Fails Filter Checks

This is expected. The starter code reads the sensor but does not yet implement the threshold and model decisions correctly.

rasb/lab/02.txt ยท Last modified: 2026/06/24 18:34 by cezar.zlatea
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0