summaryrefslogtreecommitdiff
path: root/examples/iocheck.zig
blob: 959e7f4337ec80c6b63ee80b116b1ef1789322be (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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
    );
}