diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-25 12:40:53 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-25 12:46:51 -0300 |
| commit | f5f8068fac59b4f16046c2022c2fc7c7e447ef4c (patch) | |
| tree | 2731a3ed4e51cae09e184e25778eded5fc37d1f5 /examples/iocheck.zig | |
| download | esp32p4-f5f8068fac59b4f16046c2022c2fc7c7e447ef4c.tar.gz esp32p4-f5f8068fac59b4f16046c2022c2fc7c7e447ef4c.zip | |
zig-p4: pure-Zig ESP32-P4 toolchain
build.zig generates the linker script and drives Zig's own LLD; tools/image.zig
turns the ELF into a flashable image and tools/{rom,serial}.zig speak the mask
ROM loader over the UART. No CMake, ninja, idf.py, esptool, or external linker.
src/soc.zig is a comptime register model over ESP-IDF's own *_reg.h headers;
src/hal/ adds peripheral sequences; src/io/ implements std.Io for the chip;
src/oracle/ diffs this HAL against ESP-IDF's on the die.
Diffstat (limited to 'examples/iocheck.zig')
| -rw-r--r-- | examples/iocheck.zig | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/examples/iocheck.zig b/examples/iocheck.zig new file mode 100644 index 0000000..959e7f4 --- /dev/null +++ b/examples/iocheck.zig @@ -0,0 +1,110 @@ +//! Does `std.Io.sleep` wake up on this chip? +//! +//! Written to answer one question during radio bring-up. ESP-Hosted's transport stops immediately +//! after a successful SDIO card init, at a `_h_msleep(100)` - and a cooperative scheduler whose +//! sleep never returns is indistinguishable, from the outside, from a driver that hung. The 21 host +//! tests in src/io/p4.zig cover the scheduler against a virtual clock; what they cannot cover is +//! whether a real deadline against the real systimer ever comes due. +//! +//! Four claims, each answered by the die: +//! +//! MARK IO_NOW the timebase advances at all, and at 16 MHz +//! MARK IO_SLEEP a sleep from the main task returns, and after roughly the right time +//! MARK IO_TASK a spawned task runs, sleeps, and finishes +//! MARK IO_ORDER three tasks with different deadlines wake in deadline order +//! +//! Run: zig build -Dapp=examples/iocheck.zig run -Dseconds=15 + +const std = @import("std"); +const soc = @import("soc"); +const hal = @import("hal"); +const p4 = @import("io"); + +pub const std_options: std.Options = .{ .page_size_min = 4096 }; + +pub const panic = std.debug.FullPanic(struct { + fn call(msg: []const u8, _: ?usize) noreturn { + soc.rom.print("MARK IO_PANIC %s\r\n", .{msg.ptr}); + while (true) {} + } +}.call); + +var pool: p4.Static(4, 4096) = .{}; + +/// Wake order, appended by the three tasks in `IO_ORDER`. +var order: [3]u8 = .{ 0, 0, 0 }; +var order_len: usize = 0; + +fn sleeper(ctx: struct { io: std.Io, ms: i64, tag: u8 }) void { + ctx.io.sleep(.fromMilliseconds(ctx.ms), .awake) catch {}; + order[order_len] = ctx.tag; + order_len += 1; +} + +export fn zig_main() noreturn { + // The bootloader leaves this armed and nothing here feeds it. + _ = hal.rwdt.disable(); + + const runtime = pool.init(.{}); + const io = runtime.io(); + + // 1. Does the timebase move? Everything below is meaningless if not. + const t0 = hal.systimer.read(.unit0) orelse 0; + soc.rom.ets_delay_us(10_000); + const t1 = hal.systimer.read(.unit0) orelse 0; + soc.rom.print("MARK IO_NOW advanced=%u ticks expect~160000\r\n", .{@as(u32, @truncate(t1 - t0))}); + + // 2. The question that matters: does a sleep from the calling task return? Measured against the + // systimer rather than trusted, because a sleep that returns instantly would also look like + // success from a print alone. + const before = hal.systimer.read(.unit0) orelse 0; + io.sleep(.fromMilliseconds(100), .awake) catch |err| { + soc.rom.print("MARK IO_SLEEP failed=%s\r\n", .{@errorName(err).ptr}); + park(); + }; + const after = hal.systimer.read(.unit0) orelse 0; + const slept_ms = (after - before) / 16_000; + soc.rom.print("MARK IO_SLEEP returned after %u ms, asked 100\r\n", .{@as(u32, @truncate(slept_ms))}); + + // 3. A spawned task that sleeps: this is the shape ESP-Hosted's threads have. + var fut = io.async(sleeper, .{.{ .io = io, .ms = 20, .tag = 'A' }}); + fut.await(io); + soc.rom.print("MARK IO_TASK ran=%u\r\n", .{@as(u32, @intCast(order_len))}); + + // 4. Three deadlines, out of submission order, to prove the scheduler picks the earliest. + order_len = 0; + var f1 = io.async(sleeper, .{.{ .io = io, .ms = 60, .tag = '3' }}); + var f2 = io.async(sleeper, .{.{ .io = io, .ms = 20, .tag = '1' }}); + var f3 = io.async(sleeper, .{.{ .io = io, .ms = 40, .tag = '2' }}); + f1.await(io); + f2.await(io); + f3.await(io); + soc.rom.print("MARK IO_ORDER %c%c%c expect 123\r\n", .{ + @as(u32, order[0]), @as(u32, order[1]), @as(u32, order[2]), + }); + + soc.rom.print("MARK IO_DONE\r\n", .{}); + park(); +} + +fn park() noreturn { + while (true) {} +} + +export fn _start() linksection(".text.entry") callconv(.naked) noreturn { + asm volatile ( + \\ li t0, 1 << 13 + \\ csrs mstatus, t0 + \\ la sp, __stack_top + \\ mv fp, sp + \\ la t0, __bss_start + \\ la t1, __bss_end + \\ bgeu t0, t1, 2f + \\1: + \\ sw zero, 0(t0) + \\ addi t0, t0, 4 + \\ bltu t0, t1, 1b + \\2: + \\ j zig_main + ); +} |
