Embedded Mastery: C, Ada, Rust & Zig
A Project-Based Tutorial for Embedded Development
2026
A comprehensive, project-based tutorial for mastering embedded development in C, Ada, Rust, and Zig. Build 15 projects from LED blinker to safety-critical systems, all verified in QEMU and Renode emulators.
Prerequisites & Toolchain Setup
This guide walks you through installing every tool needed for this course. Follow each section in order and verify installations before proceeding.
Note: All commands assume a Debian/Ubuntu-based Linux system. Adapt package managers for your distribution (e.g.,
brewon macOS,pacmanon Arch).
ARM Cross-Compiler (GCC)
The ARM GCC toolchain is the foundation for C development and is also used by other toolchains for linking.
Installation
sudo apt update
sudo apt install gcc-arm-none-eabi gdb-multiarch binutils-arm-none-eabiVerify Installation
arm-none-eabi-gcc --version
# Expected: arm-none-eabi-gcc (15:12.3.rel1-1) 12.3.0 or similar
arm-none-eabi-gdb --version
# Expected: GNU gdb (GDB) 13.x or similar
arm-none-eabi-objdump --version
# Expected: GNU objdump (GNU Binutils) 2.xVerify GDB Multiarch
gdb-multiarch --version
# Expected: GNU gdb (GDB) 13.x or similarTip: If
gdb-multiarchis not available, usearm-none-eabi-gdbinstead. Both work for Cortex-M debugging.
Rust Toolchain
Rust’s embedded ecosystem is mature and well-supported. We use
rustup for management and cargo-embed
for flashing/debugging.
Installation
# Install rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
# Verify
rustc --version
# Expected: rustc 1.75.0 or newer
cargo --version
# Expected: cargo 1.75.0 or newerInstall Embedded Targets
This course targets a single ARM Cortex-M4F variant:
# Cortex-M4F with FPU (STM32F4 — primary target for all projects)
# Used on both QEMU (netduinoplus2 / STM32F405) and real hardware (NUCLEO-F446RE / STM32F446)
rustup target add thumbv7em-none-eabihfVerify Targets
rustup target list --installed
# Should include:
# thumbv7em-none-eabihfInstall Cargo Tools
# cargo-binutils: LLVM tools (objdump, nm, size) via cargo
cargo install cargo-binutils
# cargo-generate: project scaffolding from templates
cargo install cargo-generate
# cargo-embed: flash and debug embedded Rust (alternative to probe-rs)
cargo install cargo-embed
# probe-rs: modern embedded debugging tool (recommended)
cargo install probe-rs-toolsVerify Cargo Tools
cargo size --version
cargo generate --version
cargo embed --version
probe-rs --versionTip:
cargo-binutilsrequiresrustup component add llvm-tools-preview. Install it ifcargo sizefails.
rustup component add llvm-tools-previewAda Toolchain
Ada for embedded uses GNAT ARM/ELF and the Alire package manager.
GNAT ARM/ELF Installation
# Download GNAT Community Edition for ARM
# Visit: https://www.adacore.com/download
# Select: GNAT Studio / GNAT ARM-ELF
# Or install via package manager (community builds):
sudo apt install gnat gprbuildGNAT ARM-ELF (Recommended for Cross-Compilation)
# Download from AdaCore
wget https://github.com/AdaCore/gnat-community/releases/download/2023/2023/gnat-arm-elf-2023-x86_64-linux-bin
chmod +x gnat-arm-elf-2023-x86_64-linux-bin
sudo ./gnat-arm-elf-2023-x86_64-linux-bin
# Add to PATH (add to ~/.bashrc or ~/.zshrc for persistence)
export PATH="/opt/GNAT/2023-arm-elf/bin:$PATH"Verify GNAT
arm-eabi-gcc --version
# Expected: gcc (GCC) 13.x or similar (GNAT ARM-ELF)
gprbuild --version
# Expected: GPRBUILD Pro x.x.x or GPRBUILD CommunityAlire Package Manager
Alire is Ada’s equivalent of Cargo — it manages projects, dependencies, and toolchains.
# Install Alire via bootstrap script
wget https://github.com/alire-project/alire/releases/download/v2.0.0/alr-2.0.0-bin-x86_64-linux.tar.gz
tar -xzf alr-2.0.0-bin-x86_64-linux.tar.gz
sudo mv alr /usr/local/bin/
alr version
# Expected: 2.0.0 or newer
# Configure Alire for ARM cross-compilation
alr toolchain --select
# Select: gnat_arm_elf when promptedVerify Alire
alr version
alr toolchain
# Should show gnat_arm_elf as selected toolchainNote: If
alr toolchain --selectdoes not offergnat_arm_elf, install it manually:alr get gnat_arm_elf alr toolchain --select
Zig Toolchain
Zig ships as a single self-contained binary with no external dependencies.
Installation
# Download latest Zig release
wget https://ziglang.org/download/0.13.0/zig-linux-x86_64-0.13.0.tar.xz
# Extract
tar -xf zig-linux-x86_64-0.13.0.tar.xz
# Move to /opt and create symlink
sudo mv zig-linux-x86_64-0.13.0 /opt/zig
sudo ln -s /opt/zig/zig /usr/local/bin/zig
# Verify
zig version
# Expected: 0.13.0 or newerVerify Cross-Compilation Targets
# Zig includes cross-compilation out of the box — no target installation needed
zig build-exe --help | grep "target"
# Test cross-compilation for ARM Cortex-M4F
zig build-exe -lc -target arm-freestanding-eabihf -mcpu cortex_m4 --verboseTip: Zig’s built-in cross-compilation is one of its strongest features. You never need to install separate cross-compilers — Zig bundles everything.
Common Utilities
These tools are used across all languages for emulation, debugging, and communication.
Installation
sudo apt update
sudo apt install \
qemu-system-arm \
openocd \
picocom \
make \
cmake \
python3 \
python3-pip \
git \
wget \
curl \
tree \
tmuxVerify Utilities
qemu-system-arm --version
# Expected: QEMU emulator version 8.x or newer
openocd --version
# Expected: Open On-Chip Debugger 0.12.0 or newer
picocom --version
# Expected: picocom v3.1 or newer
make --version
# Expected: GNU Make 4.x
cmake --version
# Expected: cmake version 3.22 or newer
python3 --version
# Expected: Python 3.10 or newerInstall Python Dependencies (for build scripts)
pip3 install pyyaml intelhexComplete Verification Script
Save this as verify-toolchains.sh and run it to
check everything:
#!/bin/bash
set -e
echo "=== ARM GCC ==="
arm-none-eabi-gcc --version | head -1
echo ""
echo "=== GDB ==="
gdb-multiarch --version | head -1
echo ""
echo "=== Rust ==="
rustc --version
cargo --version
echo ""
echo "=== Rust Targets ==="
rustup target list --installed | grep -E "thumbv"
echo ""
echo "=== Cargo Tools ==="
cargo size --version 2>/dev/null || echo "cargo-binutils not installed"
cargo generate --version 2>/dev/null || echo "cargo-generate not installed"
cargo embed --version 2>/dev/null || echo "cargo-embed not installed"
echo ""
echo "=== Ada/GNAT ==="
arm-eabi-gcc --version 2>/dev/null | head -1 || echo "GNAT ARM-ELF not found"
gprbuild --version 2>/dev/null | head -1 || echo "gprbuild not found"
alr version 2>/dev/null || echo "Alire not installed"
echo ""
echo "=== Zig ==="
zig version
echo ""
echo "=== QEMU ==="
qemu-system-arm --version | head -1
echo ""
echo "=== OpenOCD ==="
openocd --version | head -1
echo ""
echo "=== All checks complete ==="chmod +x verify-toolchains.sh
./verify-toolchains.shTarget Hardware
This course is designed to work in emulation (QEMU) and on real hardware with identical code.
| Property | QEMU (Primary) | Real Hardware |
|---|---|---|
| Board | Netduino Plus 2 | NUCLEO-F446RE |
| MCU | STM32F405RGT6 | STM32F446RET6 |
| Core | Cortex-M4F | Cortex-M4F |
| Flash | 1 MiB | 512 KiB |
| SRAM | 128 KiB | 128 KiB |
| QEMU Machine | netduinoplus2 |
N/A (flashed via ST-Link) |
Both MCUs are in the STM32F4 family and share the same peripheral architecture (GPIO, USART, SPI, I2C, timers, etc.). Code written for one runs on the other with only a pin configuration header swap.
Note: You do not need physical hardware to complete this course. All projects run in QEMU. The NUCLEO-F446RE (~$20-25) is recommended if you want to test on real silicon — it has an on-board ST-Link debugger, Arduino headers, and is widely available.
Troubleshooting
ARM GCC: “command not found”
# Check if installed
dpkg -l | grep gcc-arm
# If missing, reinstall
sudo apt install --reinstall gcc-arm-none-eabi
# Verify PATH
echo $PATH | tr ':' '\n' | grep armRust:
“error[E0463]: can’t find crate for core”
This means the embedded target is not installed:
rustup target add thumbv7em-none-eabihfRust: “cargo size” fails
rustup component add llvm-tools-previewGNAT: “gprbuild: command not found”
sudo apt install gprbuild
# Or if using Alire:
alr get gprbuildAlire: “no toolchain available for ARM”
alr update
alr toolchain --select
# Choose gnat_arm_elf from the listZig: “unable to find ziglang directory”
Ensure the Zig binary is in your PATH:
export PATH="/opt/zig:$PATH"
# Add to ~/.bashrc or ~/.zshrc for persistenceQEMU: “could not load PC BIOS”
sudo apt install --reinstall qemu-system-arm
# Or install BIOS files separately:
sudo apt install qemu-efi-armOpenOCD: “no device found”
This is expected when using QEMU (no physical hardware). OpenOCD is only needed for real hardware debugging. For emulation, use QEMU’s built-in GDB server.
Permission Denied on USB Devices (Real Hardware)
# Add user to dialout and plugdev groups
sudo usermod -aG dialout $USER
sudo usermod -aG plugdev $USER
# Log out and back in for changes to take effectWhat’s Next?
With all toolchains installed and verified, proceed to:
- Emulator Setup & Usage Guide — Configure QEMU and Renode for hardware-free development
- Project 1: LED Blinker — Your first embedded project in all 4 languages
Tip: Before starting Project 1, run the verification script above and ensure every tool reports a valid version. Future projects assume a working toolchain.
References
STMicroelectronics Documentation
- STM32F4 Reference Manual (RM0090) — Complete peripheral reference for STM32F4 family
- STM32F405/407 Datasheet — Pin assignments, memory sizes, electrical characteristics
- NUCLEO-F446RE Documentation — Board schematics, user manual, ST-Link/V2-1 details
ARM Documentation
- Cortex-M4 Technical Reference Manual — Processor architecture, FPU, NVIC, SysTick
- ARMv7-M Architecture Reference Manual — Exception model, memory ordering, instruction set
Tools & Emulation
- QEMU ARM Documentation — qemu-system-arm usage, GDB stub, semihosting
- QEMU STM32 Documentation — netduinoplus2 machine, supported peripherals
- Renode Documentation — Multi-node simulation, bus analyzers, peripheral models
- ARM EABI Specification (IHI 0045) — Procedure call standard, stack alignment, calling conventions