Embedded and Freestanding Programming

Target bare metal: -target thumb-freestanding-eabi -mcpu cortex_m0plus, no OS, no libc.

Provide your own entry point, vector table, linker script, and panic handler:

MicroZig (Zig Embedded Group): HAL, board support (RP2040/RP2350, STM32, nRF, ESP32-C3, ATSAMD), register generation from SVD files (regz), USB, flashing.

Use ReleaseSmall, FixedBufferAllocator, packed structs for registers, volatile MMIO, and interrupt handlers with callconv(.{ .arm_interrupt = .{} }) or similar.

export fn _start() callconv(.naked) noreturn {
    asm volatile ("bl main");
    while (true) {}
}

export fn main() noreturn {
    const GPIO_OUT: *volatile u32 = @ptrFromInt(0x5000_0504);
    while (true) {
        GPIO_OUT.* ^= (1 << 13);            // toggle an LED
        var i: u32 = 0;
        while (i < 100_000) : (i += 1) asm volatile ("nop");
    }
}

pub fn panic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
    _ = msg;
    while (true) {}
}
// build.zig snippet
exe.setLinkerScript(b.path("linker.ld"));
exe.entry = .{ .symbol_name = "_start" };
const bin = b.addObjCopy(exe.getEmittedBin(), .{ .format = .bin });
b.getInstallStep().dependOn(&b.addInstallBinFile(bin.getOutput(), "firmware.bin").step);