Module 0 Exercises — Toolchains, Targets, and the Availability Matrix

Back to the Course 2 syllabus. Read first: Module 0 lessons (Seacord 1, Rust Book 1 and 14, and the Embedded Rust Book’s Getting Started remain available as optional deep-dives).

Work in the labs repo’s course2/ folder — c/host, c/mcu, rust/host, rust/qemu, rust/mcu, rust/linux as each exercise names — and record everything in m0/notes.md. Everything in this module runs on the Mac: host builds with clang and cargo, Cortex-M cross-compiles with clang --target=thumbv7em-none-eabihf and cargo --target thumbv7em-none-eabihf, bare-metal execution in QEMU. Flashing the NUCLEO with probe-rs and building on the Jetson are the optional last steps of Exercises 0.1 and 0.4 only. Predicted cells are filled in before building; observed cells at the machine.

Exercises

Exercise 0.1 — Bring-up on every tier. Install the toolchain from lessons §3 and prove each reach exists with the smallest possible artifact. C: build c/host (one trivial executable under ASan/UBSan), then cross-compile a freestanding kernel in c/mcu and disassemble it. Rust: cargo build the workspace for the host, cargo check -p mcu --target thumbv7em-none-eabihf, cargo check -p linux --target aarch64-unknown-linux-gnu, and cargo build -p qemu --target thumbv7m-none-eabi. Fill the reach table:

Reach Command that proves it Toolchain component it needs Works?
Host C with sanitizers
Cortex-M C, compile + disassemble
Cortex-M C, link a real ELF arm-none-eabi-gcc (optional)
Host Rust
thumbv7em-none-eabihf check
thumbv7m-none-eabi build
aarch64-unknown-linux-gnu check
NUCLEO flash (optional) probe-rs info / cargo run -p mcu --bin …
Jetson build (optional) ssh … cargo build --release

Deliverable: the table plus the version line of every tool involved (clang --version, rustc -Vv, qemu-system-arm --version, probe-rs --version), recorded in notes.md so that a future toolchain drift has a baseline to diff against.

Exercise 0.2 — Hello, no_std, in QEMU. Write rust/qemu/src/bin/ex-0-2.rs: a #![no_std] #![no_main] program with cortex-m-rt’s #[entry], panic-halt, one hprintln!, and debug::exit. Run it with cargo run. Then, one change at a time, and predicting the outcome before each: (a) remove the use panic_halt as _; line; (b) change fn main() -> ! to fn main(); (c) replace the final loop {} with a return-free fall-off; (d) call hprintln! before #[entry] is reached by moving it into a static initializer expression. Record for each whether the failure is a compile error, a link error, a QEMU hang, or a runtime trap, and which tool reported it.

Change Predicted failure (stage, message) Observed
(a) no panic handler
(b) returning main
(c) fall off the end
(d) I/O before entry

Deliverable: the working program, the table, and one paragraph in notes.md on what cortex-m-rt did between reset and your first line.

Exercise 0.3 — Startup, side by side. Read cortex-m-rt’s Reset handler (the crate source is in ~/.cargo/registry) next to the Course 3 labs repo’s startup_stm32l476xx.s and its linker script. Map every responsibility of lessons §5.2 to the exact lines in each, then confirm two of the claims with tools: use cargo objdump -- -d on the QEMU binary to find the vector table and the .data-copy loop, and cargo size -- -A to read the sizes of .vector_table, .text, .rodata, .data, and .bss. Predict, before running cargo size, the ordering of those five sections by size for the hello program and whether .data is empty.

Responsibility cortex-m-rt (file:line) startup_stm32l476xx.s / .ld (file:line) Verified how
Initial stack pointer
Vector table placement
.bss zeroing
.data copy from flash
Default exception handler
Jump to user entry

Deliverable: the table and the annotated objdump excerpt showing the vector table’s first two words.

Exercise 0.4 — The availability matrix, by experiment. Lessons §8 makes claims; this exercise tests them. For each row, write the smallest probe that would fail to compile or link if the facility were absent, in both languages, and build it for the bare-metal tier (c/mcu freestanding; rust/mcu with cargo check --target thumbv7em-none-eabihf) and the host. Suggested probes: #include <threads.h> / use std::thread; _Atomic uint64_t with atomic_is_lock_free / core::sync::atomic::AtomicU64; a double multiply compiled at -O2 with the disassembly inspected for a library call vs. an FPU instruction / the same in Rust with f64; fopen / std::fs::File; clock_gettime / std::time::Instant; signal / nix::sys::signal. Record the exact diagnostic for every absent facility.

Facility Bare metal C — predicted / observed Bare metal Rust — predicted / observed Host (either)
Threads … / … … / …
8-byte atomic, lock-free … / … … / …
double in hardware … / … … / …
Files … / … … / …
Monotonic clock (without hardware setup) … / … … / …
Signals … / … … / …

Optional rung: repeat the Rust column on the Jetson (cargo build on the board) to fill the Linux tier from a real aarch64 glibc rather than from the Mac.

Deliverable: the completed matrix and, in notes.md, which cells surprised you and which the lessons page got wrong, if any.

Exercise 0.5 — Deliberate build failures, read closely. Produce and explain six failures, three per language, choosing constructs that fail at different stages: C — a hosted header in a freestanding build, a long-width assumption pinned by _Static_assert that holds on the Mac and breaks on thumbv7em, and a -Wconversion diagnostic promoted to an error with -Werror; Rust — a std item in a #![no_std] crate, a #[global_allocator]-less Vec, and a Clippy unwrap_used denial in the mcu crate. For each, record the stage (preprocess, compile, link, lint), the first line of the diagnostic, and the one-line fix that keeps the intent.

Failure Stage First diagnostic line Fix

Deliverable: the table. The point is fluency in the messages: by Module 7, “the compiler said” should be a precise quotation.

Exercise 0.6 — The warning set, applied. Take one real C file from the Course 3 labs repo firmware (a HAL-generated main.c is fine) and one Rust file you wrote in this module. Build the C file under the lessons §7.1 warning set on clang and, if installed, arm-none-eabi-gcc; run cargo clippy --all-targets -- -D warnings on the Rust crate. Triage every diagnostic into fix, justify in a comment, or the warning is wrong here, and why. Count them by flag.

Flag / lint Count Fixed Justified Disputed

Deliverable: the table and the resulting pinned warning-set lines committed to c/host, c/mcu, and rust/ — the contract every later module builds under.

Exercise 0.7 — The Python environment and the device probe. From the labs-repo root: uv sync, then uv sync --group ml, then uv run python course2/python/src/smoke.py and uv run pytest course2/python/tests. Record the resolved versions of numpy, scipy, matplotlib, pandas, scikit-learn, torch, onnxruntime from uv pip list (or uv tree). Then write python/src/ex-0-7.py: print numpy.__config__.show()’s BLAS/LAPACK backend, numpy.finfo(numpy.float32) and finfo(float64), the result of torch.backends.mps.is_available() / torch.cuda.is_available(), and — the first cross-language probe — the strides, flags['C_CONTIGUOUS'], and nbytes of a (4, 3) int16 array before and after .T, alongside the byte count the equivalent C int16_t[4][3] occupies. Predict every value before running.

Probe Predicted Observed
BLAS backend NumPy links against on the Mac
float32 machine epsilon / float64 machine epsilon
Accelerator visible to PyTorch on the Mac
(4, 3) int16 strides, C order
Same array after .T: strides, contiguous?
nbytes vs. sizeof(int16_t[4][3])

Optional rung: repeat the accelerator row on the Jetson (torch.cuda.is_available(), and CuPy’s cupy.cuda.runtime.getDeviceProperties(0)) and on the RTX 4090 desktop over SSH.

Deliverable: the table, the version list in m0/notes.md, and the pytest run green — the environment Module 1 starts from.