commit 8d9fc0af14640de4e4aeed81401ae7904b154874
parent e2ea44793d4e394e5fcee53b5692b4cee3296fea
Author: Sylvia Ivory <git@sivory.net>
Date: Thu, 22 Jan 2026 19:52:08 -0800
Add interrupts
Diffstat:
11 files changed, 455 insertions(+), 50 deletions(-)
diff --git a/build.zig b/build.zig
@@ -60,7 +60,7 @@ fn add_exe_real(exe_name: []const u8, path: std.Build.LazyPath, b: *std.Build) !
const pi = b.createModule(.{
.root_source_file = b.path("pi/root.zig"),
.target = b.graph.host,
- .optimize = .ReleaseSafe,
+ .optimize = .ReleaseFast,
});
// BCM2835 is **very specifically** the ARM1176JZF-S
@@ -68,7 +68,7 @@ fn add_exe_real(exe_name: []const u8, path: std.Build.LazyPath, b: *std.Build) !
const boot = b.createModule(.{
.root_source_file = path,
.target = b.resolveTargetQuery(target),
- .optimize = .ReleaseSafe,
+ .optimize = .ReleaseFast,
.unwind_tables = .none,
.single_threaded = true,
.error_tracing = false,
@@ -76,6 +76,7 @@ fn add_exe_real(exe_name: []const u8, path: std.Build.LazyPath, b: *std.Build) !
.no_builtin = true,
});
boot.addImport("pi", pi);
+ pi.addImport("boot", boot);
const exe = b.addExecutable(.{
.name = exe_name,
diff --git a/pi/cpsr.zig b/pi/cpsr.zig
@@ -0,0 +1,53 @@
+pub const Mode = enum(u5) {
+ User = 0b10000,
+ FIQ = 0b10001,
+ IRQ = 0b10010,
+ Supervisor = 0b10011,
+ Abort = 0b10111,
+ Undefined = 0b11011,
+ System = 0b11111,
+};
+
+pub const CPSR = packed struct(u32) {
+ mode: Mode,
+ t: bool,
+ f: bool,
+ i: bool,
+ a: bool,
+ e: bool,
+ _reserved_10_15: u6,
+ ge: u4,
+ _reserved_20_23: u4,
+ j: bool,
+ res: u2,
+ q: bool,
+ v: bool,
+ c: bool,
+ z: bool,
+ n: bool,
+};
+
+pub inline fn get() CPSR {
+ return asm volatile ("MRS %[result], cpsr"
+ : [result] "=r" (-> CPSR),
+ );
+}
+
+pub inline fn set(cpsr: CPSR) void {
+ return asm volatile ("MSR cpsr_c, %[value]"
+ :
+ : [value] "r" (@as(u32, @bitCast(cpsr))),
+ : .{ .cpsr = true });
+}
+
+pub inline fn enable_interrupts() void {
+ var cpsr = get();
+ cpsr.i = true;
+ set(cpsr);
+}
+
+pub inline fn disable_interrupts() void {
+ var cpsr = get();
+ cpsr.i = false;
+ set(cpsr);
+}
diff --git a/pi/interrupts.zig b/pi/interrupts.zig
@@ -0,0 +1,151 @@
+const cpsr = @import("./cpsr.zig");
+const mem = @import("./mem.zig");
+
+const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0xB200;
+
+const IRQ_BASIC_PENDING: usize = BASE_ADDRESS + 0x00;
+const IRQ_PENDING_1: usize = BASE_ADDRESS + 0x04;
+const IRQ_PENDING_2: usize = BASE_ADDRESS + 0x08;
+
+const IRQ_FIQ_CONTROL: usize = BASE_ADDRESS + 0x0C;
+
+const IRQ_ENABLE_1: usize = BASE_ADDRESS + 0x10;
+const IRQ_ENABLE_2: usize = BASE_ADDRESS + 0x14;
+const IRQ_ENABLE_BASIC: usize = BASE_ADDRESS + 0x18;
+
+const IRQ_DISABLE_1: usize = BASE_ADDRESS + 0x1C;
+const IRQ_DISABLE_2: usize = BASE_ADDRESS + 0x20;
+const IRQ_DISABLE_BASIC: usize = BASE_ADDRESS + 0x24;
+
+pub inline fn enable_interrupts() void {
+ asm volatile ("cpsie i\n");
+}
+
+pub inline fn disable_interrupts() void {
+ asm volatile ("cpsid i\n");
+}
+
+pub inline fn clear_interrupt_flags() void {
+ mem.put_u32(@ptrFromInt(IRQ_DISABLE_1), ~@as(u32, 0));
+ mem.put_u32(@ptrFromInt(IRQ_DISABLE_2), ~@as(u32, 0));
+ mem.barrier(.Write);
+}
+
+const BasicInterrupt = enum(u8) {
+ Timer = 1 << 0,
+ Mailbox = 1 << 1,
+ Doorbell0 = 1 << 2,
+ Doorbell1 = 1 << 3,
+ GPU0Halted = 1 << 4,
+ GPU1Halted = 1 << 5,
+ AccessError1 = 1 << 6,
+ AccessError0 = 1 << 7,
+};
+
+pub inline fn enable_basic_interrupt(i: BasicInterrupt) void {
+ mem.put_u32_barrier(@ptrFromInt(IRQ_ENABLE_BASIC), @intFromEnum(i));
+}
+pub inline fn disable_basic_interrupt(i: BasicInterrupt) void {
+ mem.put_u32_barrier(@ptrFromInt(IRQ_DISABLE_BASIC), @intFromEnum(i));
+}
+pub inline fn pending_basic(i: BasicInterrupt) bool {
+ return (mem.get_u32_barrier(@ptrFromInt(IRQ_BASIC_PENDING)) & @intFromEnum(i)) != 0;
+}
+
+fn empty(lr: u32) void {
+ @import("boot").uart.write_slice("default handler called\n");
+ _ = lr;
+}
+
+var reset_handler: *const fn (u32) void = empty;
+var undefined_instruction_handler: *const fn (u32) void = empty;
+var software_interrupt_handler: *const fn (u32) void = empty;
+var prefetch_abort_handler: *const fn (u32) void = empty;
+var data_abort_handler: *const fn (u32) void = empty;
+var interrupt_handler: *const fn (u32) void = empty;
+var fast_interrupt_handler: *const fn (u32) void = empty;
+
+fn get_lr() u32 {
+ return asm volatile ("mov %[result], lr"
+ : [result] "=r" (-> u32),
+ );
+}
+
+export fn reset() callconv(.{ .arm_interrupt = .{ .type = .generic } }) void {
+ reset_handler(0);
+}
+export fn undefined_instruction() callconv(.{ .arm_interrupt = .{ .type = .undef } }) void {
+ undefined_instruction_handler(get_lr() - 4);
+}
+export fn software_interrupt() callconv(.{ .arm_interrupt = .{ .type = .swi } }) void {
+ software_interrupt_handler(get_lr() - 4);
+}
+export fn prefetch_abort() callconv(.{ .arm_interrupt = .{ .type = .abort } }) void {
+ prefetch_abort_handler(get_lr() - 8);
+}
+export fn data_abort() callconv(.{ .arm_interrupt = .{ .type = .abort } }) void {
+ data_abort_handler(get_lr() - 4);
+}
+export fn interrupt() callconv(.{ .arm_interrupt = .{ .type = .irq } }) void {
+ interrupt_handler(get_lr() - 4);
+}
+export fn fast_interrupt() callconv(.{ .arm_interrupt = .{ .type = .fiq } }) void {
+ fast_interrupt_handler(get_lr() - 4);
+}
+
+// https://leiradel.github.io/2019/02/09/Initialization.html
+pub fn setup_exception_vector() void {
+ var exception_vector: usize = 0;
+
+ for (0..8) |_| {
+ mem.put_u32(@ptrFromInt(exception_vector), 0xE59FF018); // ldr pc, [pc, #24]
+ exception_vector += @sizeOf(u32);
+ }
+
+ mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&reset));
+ exception_vector += @sizeOf(u32);
+
+ mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&undefined_instruction));
+ exception_vector += @sizeOf(u32);
+
+ mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&software_interrupt));
+ exception_vector += @sizeOf(u32);
+
+ mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&prefetch_abort));
+ exception_vector += @sizeOf(u32);
+
+ mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&data_abort));
+ exception_vector += @sizeOf(u32);
+
+ mem.put_u32(@ptrFromInt(exception_vector), 0);
+ exception_vector += @sizeOf(u32);
+
+ mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&interrupt));
+ exception_vector += @sizeOf(u32);
+
+ mem.put_u32(@ptrFromInt(exception_vector), @intFromPtr(&fast_interrupt));
+
+ mem.barrier(.Write);
+}
+
+const ExceptionVector = enum {
+ Reset,
+ UndefinedInstruction,
+ SoftwareInterrupt,
+ PrefetchAbort,
+ DataAbort,
+ IRQ,
+ FIQ,
+};
+
+pub fn set_exception_handler(vector: ExceptionVector, handler: *const fn (u32) void) void {
+ switch (vector) {
+ .Reset => reset_handler = handler,
+ .UndefinedInstruction => undefined_instruction_handler = handler,
+ .SoftwareInterrupt => software_interrupt_handler = handler,
+ .PrefetchAbort => prefetch_abort_handler = handler,
+ .DataAbort => data_abort_handler = handler,
+ .IRQ => interrupt_handler = handler,
+ .FIQ => fast_interrupt_handler = handler,
+ }
+}
diff --git a/pi/mem.zig b/pi/mem.zig
@@ -34,7 +34,7 @@ pub inline fn barrier(op: Operation) void {
}
}
-pub inline fn put_u32(address: *u32, value: u32) void {
+pub inline fn put_u32(address: *allowzero u32, value: u32) void {
asm volatile ("str %[value], [%[address]]"
:
: [value] "r" (value),
@@ -43,21 +43,21 @@ pub inline fn put_u32(address: *u32, value: u32) void {
}
pub inline fn put_u32_barrier(
- address: *u32,
+ address: *allowzero u32,
value: u32,
) void {
put_u32(address, value);
barrier(.Write);
}
-pub inline fn get_u32(address: *u32) u32 {
+pub inline fn get_u32(address: *allowzero u32) u32 {
return asm volatile ("ldr %[result], [%[address]]"
: [result] "=r" (-> u32),
: [address] "r" (address),
);
}
-pub inline fn get_u32_barrier(address: *u32) u32 {
+pub inline fn get_u32_barrier(address: *allowzero u32) u32 {
barrier(.Read);
return get_u32(address);
}
diff --git a/pi/root.zig b/pi/root.zig
@@ -1,3 +1,5 @@
+pub const interrupts = @import("./interrupts.zig");
+pub const cpsr = @import("./cpsr.zig");
pub const mem = @import("./mem.zig");
pub inline fn cycle_counter_init() void {
@@ -13,3 +15,40 @@ pub inline fn cycle_counter_read() u32 {
: [result] "=r" (-> u32),
);
}
+
+pub inline fn set_sp(sp: *usize) void {
+ asm volatile ("mov sp, %[value]"
+ :
+ : [value] "r" (sp),
+ : .{ .r13 = true }); // sp
+}
+
+// TODO; should do something better
+var svc_stack: [2024]usize align(8) = .{0} ** 2024;
+var abort_stack: [32]usize align(8) = .{0} ** 32;
+var irq_stack: [32]usize align(8) = .{0} ** 32;
+var fiq_stack: [32]usize align(8) = .{0} ** 32;
+
+pub export fn setup_stacks() void {
+ const original_cpsr = cpsr.get();
+ var new_cpsr = original_cpsr;
+
+ new_cpsr.mode = .Undefined;
+ cpsr.set(new_cpsr);
+ set_sp(@ptrFromInt(@intFromPtr(&abort_stack) + (abort_stack.len * @sizeOf(usize))));
+
+ new_cpsr.mode = .Abort;
+ cpsr.set(new_cpsr);
+ set_sp(@ptrFromInt(@intFromPtr(&abort_stack) + (abort_stack.len * @sizeOf(usize))));
+
+ new_cpsr.mode = .IRQ;
+ cpsr.set(new_cpsr);
+ set_sp(@ptrFromInt(@intFromPtr(&irq_stack) + (irq_stack.len * @sizeOf(usize))));
+
+ new_cpsr.mode = .FIQ;
+ cpsr.set(new_cpsr);
+ set_sp(@ptrFromInt(@intFromPtr(&fiq_stack) + (fiq_stack.len * @sizeOf(usize))));
+
+ // Restore
+ cpsr.set(original_cpsr);
+}
diff --git a/src/devices/timer.zig b/src/devices/clock.zig
diff --git a/src/devices/mini-uart.zig b/src/devices/mini-uart.zig
@@ -2,7 +2,6 @@ const std = @import("std");
const mem = @import("pi").mem;
const gpio = @import("./gpio.zig");
-const timer = @import("./timer.zig");
pub const Error = error{ AlreadyInitialized, NotInitialized, InvalidReadLimit } || gpio.Error;
@@ -110,21 +109,21 @@ pub fn is_initialized() bool {
return initialized;
}
-fn can_write() bool {
+pub fn can_write() bool {
// Check if FIFO can accept data
// Page 15: AUX_MU_LSR_REG Register
// Bit 5 is set when FIFO can accept at least 1 byte
return (mem.get_u32(@ptrFromInt(AUX_MU_LSR_REG)) & 0x20) != 0;
}
-fn can_read() bool {
+pub fn can_read() bool {
// Check if FIFO has data
// Page 15: AUX_MU_LSR_REG Register
// Bit 1 is set when FIFO holds at least 1 byte
return (mem.get_u32(@ptrFromInt(AUX_MU_LSR_REG)) & 1) == 1;
}
-fn write_byte(byte: u8) void {
+pub fn write_byte(byte: u8) void {
// TODO; support timeout
while (!can_write()) {}
@@ -134,7 +133,13 @@ fn write_byte(byte: u8) void {
mem.put_u32(@ptrFromInt(AUX_MU_IO_REG), @as(u32, byte) & 0xFF);
}
-fn read_byte() u8 {
+pub fn write_slice(bytes: []const u8) void {
+ for (bytes) |b| {
+ write_byte(b);
+ }
+}
+
+pub fn read_byte() u8 {
// TODO; support timeout
while (!can_read()) {}
diff --git a/src/devices/timer.zig b/src/devices/timer.zig
@@ -1,33 +1,113 @@
const std = @import("std");
-const mem = @import("pi").mem;
+const pi = @import("pi");
-const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0x0000_3000;
-const COUNTER_LOWER: usize = BASE_ADDRESS + 0x0004;
-const COUNTER_UPPER: usize = BASE_ADDRESS + 0x0008;
+const clock = @import("./clock.zig");
-pub fn current_count() u64 {
- const upper = mem.get_u32_barrier(@ptrFromInt(COUNTER_UPPER));
- const lower = mem.get_u32_barrier(@ptrFromInt(COUNTER_LOWER));
- const extended: u64 = @intCast(upper);
+const interrupts = pi.interrupts;
+const mem = pi.mem;
- return (extended << 32) | lower;
+const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0xB400;
+
+const TIMER_LOAD: usize = BASE_ADDRESS + 0x00;
+const TIMER_VALUE: usize = BASE_ADDRESS + 0x04;
+const TIMER_CONTROL: usize = BASE_ADDRESS + 0x08;
+
+const TIMER_IRQ_CLEAR: usize = BASE_ADDRESS + 0x0C;
+const TIMER_IRQ_RAW: usize = BASE_ADDRESS + 0x10;
+const TIMER_IRQ_MASKED: usize = BASE_ADDRESS + 0x14;
+
+const TIMER_RELOAD: usize = BASE_ADDRESS + 0x18;
+const TIMER_PREDIV: usize = BASE_ADDRESS + 0x1C;
+const TIMER_COUNTER: usize = BASE_ADDRESS + 0x20;
+
+const PreScale = enum(u2) {
+ None = 0b00,
+ Sixteen = 0b01,
+ TwoFiftySix = 0b10,
+ One = 0b11,
+};
+
+const Counter = enum(u1) {
+ Bit16 = 0,
+ Bit32 = 1,
+};
+
+const Control = packed struct(u32) {
+ _unused_1: u1, // 0
+ counter: Counter, // 1
+ pre_scale: PreScale, // 2-3
+ _unused_4: u1, // 4
+ interrupt_enabled: bool, // 5
+ _unused_6: u1, // 6
+ timer_enabled: bool, // 7
+ run_in_debug_halted: bool, // 8
+ free_running_counter_enable: bool, // 9
+ _unused_15_10: u5, // 10-15
+ free_running_counter_pre_scaler: u7, // 16-23
+ _unused_31_24: u10,
+};
+
+fn default_control() Control {
+ return @bitCast(@as(u32, 0));
+}
+
+fn empty(pc: usize) void {
+ _ = pc;
+}
+
+var tick_fn: *const fn (usize) void = empty;
+
+pub fn initialize(pre_scale: PreScale, cycles: u32) void {
+ mem.barrier(.Write);
+
+ interrupts.set_exception_handler(.IRQ, timer_handler);
+ interrupts.enable_basic_interrupt(.Timer);
+
+ mem.put_u32(@ptrFromInt(TIMER_LOAD), cycles);
+
+ var control = default_control();
+ control.counter = .Bit32;
+ control.timer_enabled = true;
+ control.interrupt_enabled = true;
+ control.pre_scale = pre_scale;
+
+ mem.put_u32_barrier(@ptrFromInt(TIMER_CONTROL), @bitCast(control));
+}
+
+var count: u32 = 0;
+var previous_clock: u64 = 0;
+var period: u64 = 0;
+var period_sum: u64 = 0;
+
+noinline fn timer_handler(pc: usize) void {
+ mem.barrier(.Write); // Sync
+
+ if (!interrupts.pending_basic(.Timer)) return;
+
+ // Clear interrupt
+ mem.put_u32_barrier(@ptrFromInt(TIMER_IRQ_CLEAR), 1);
+
+ // TODO; check if we need volatile
+ count += 1;
+
+ const current_clock = clock.current_count();
+ period = if (previous_clock == 0) 0 else current_clock - previous_clock;
+ period_sum += period;
+ previous_clock = current_clock;
+
+ tick_fn(pc);
+
+ mem.barrier(.Write); // Sync before restoring
}
-// Busy delay
-// Not the best way to delay *but* a proper delay
-// requires *a lot* more code
-pub fn delay(us: u64) void {
- const start = current_count();
- while (true) {
- const current = current_count();
- if ((current - start) >= us) return;
- }
+pub inline fn get_count() u32 {
+ return count;
}
-pub fn delay_ms(ms: u64) void {
- delay(ms * 1000);
+pub inline fn get_period() u64 {
+ return period;
}
-pub fn delay_s(s: u64) void {
- delay_ms(s * 1000);
+pub inline fn get_period_sum() u64 {
+ return period_sum;
}
diff --git a/src/labs/4-measure.zig b/src/labs/4-measure.zig
@@ -2,7 +2,7 @@ const std = @import("std");
const pi = @import("pi");
const uart = @import("../devices/mini-uart.zig");
-const timer = @import("../devices/timer.zig");
+const clock = @import("../devices/clock.zig");
pub fn main() !void {
try uart.initialize(115200, .Gpio14, .Gpio15);
@@ -10,8 +10,8 @@ pub fn main() !void {
const start = pi.cycle_counter_read();
- const s = timer.current_count();
- while ((timer.current_count() - s) < 1000 * 1000) {}
+ const s = clock.current_count();
+ while ((clock.current_count() - s) < 1000 * 1000) {}
const end = pi.cycle_counter_read();
const total = end - start;
diff --git a/src/main.zig b/src/main.zig
@@ -1,5 +1,44 @@
+const interrupts = @import("pi").interrupts;
+
const uart = @import("devices/mini-uart.zig");
+const clock = @import("devices/clock.zig");
+const timer = @import("devices/timer.zig");
pub fn main() !void {
- try uart.writer.print("Hello {s}", .{"World!"});
+ try uart.writer.print("entered main safely...\n", .{});
+
+ timer.initialize(.Sixteen, 0x100);
+
+ try uart.writer.print("initialized timer...\n", .{});
+
+ interrupts.enable_interrupts();
+
+ try uart.writer.print("enabled interrupts...\n", .{});
+
+ const start = clock.current_count();
+ var iter: u32 = 0;
+ const N = 20;
+
+ while (timer.get_count() < N) {
+ try uart.writer.print("iter={d}: count={d}, time between interrupts = {d} usec (0x{x})\n", .{
+ iter,
+ timer.get_count(),
+ timer.get_period(),
+ timer.get_period(),
+ });
+ iter += 1;
+ }
+
+ const total = clock.current_count() - start;
+ const total_sec = total / (1000 * 1000);
+ const total_ms = (total / 1000) % 10000;
+ const total_us = (total % 1000);
+
+ try uart.writer.print(
+ \\ total iterations: {}
+ \\ total interrupts: {}
+ \\ iterations / interrupt: {}
+ \\ average period: {}
+ \\ total execution time: {}s.{}ms.{}us
+ , .{ iter, N, iter / N, timer.get_period_sum() / (N - 1), total_sec, total_ms, total_us });
}
diff --git a/src/root.zig b/src/root.zig
@@ -1,24 +1,61 @@
const std = @import("std");
+const pi = @import("pi");
-const uart = @import("devices/mini-uart.zig");
+pub const uart = @import("devices/mini-uart.zig");
const user_main = @import("main.zig").main;
extern const __bss_start__: usize;
extern const __bss_end__: usize;
fn zero_bss() void {
- for (__bss_start__..__bss_end__) |b| {
+ // No bss data allocated
+ if (__bss_start__ >= __bss_end__) return;
+
+ const n: usize = @divFloor(__bss_end__ - __bss_start__, @sizeOf(u32));
+
+ for (0..n) |b| {
// Force a write
- const ptr: *volatile u8 = @ptrFromInt(b);
- ptr.* = 0;
+ const ptr: *allowzero u32 = @ptrFromInt(__bss_start__ + b * @sizeOf(u32));
+ pi.mem.put_u32(ptr, 0);
}
}
-noinline fn kmain() void {
- zero_bss();
+fn initialize_interrupts() void {
+ uart.write_slice(" Disabling interrupts\n");
+ pi.interrupts.disable_interrupts();
+ pi.mem.barrier(.Write);
+
+ uart.write_slice(" Clearing interrupt flags\n");
+ pi.interrupts.clear_interrupt_flags();
+ pi.mem.barrier(.Write);
+ uart.write_slice(" Setting exception vector\n");
+ pi.interrupts.setup_exception_vector();
+ pi.mem.barrier(.Write);
+}
+
+export fn kmain() void {
uart.initialize(115200, .Gpio14, .Gpio15) catch {};
+ uart.write_slice("Clearing bss\n");
+ uart.writer.print(" __bss_start__ = 0x{X}\n", .{__bss_start__}) catch {};
+ uart.writer.print(" __bss_end__ = 0x{X}\n", .{__bss_end__}) catch {};
+ zero_bss();
+
+ uart.write_slice("Initializing interrupts\n");
+ initialize_interrupts();
+
+ uart.write_slice("Setting up stack\n");
+ pi.setup_stacks();
+
+ uart.write_slice("Exception Vector:\n");
+ for (0..16) |i| {
+ const dst: *allowzero u32 = @ptrFromInt(i * @sizeOf(*u32));
+ uart.writer.print(" 0x{X} = 0x{X}\n", .{ @intFromPtr(dst), dst.* }) catch {};
+ }
+
+ uart.write_slice("Calling user main\n");
+
user_main() catch |e| {
uart.writer.print("main returned error: {t}\n", .{e}) catch {};
};
@@ -27,15 +64,12 @@ noinline fn kmain() void {
export fn _start() linksection(".kmain") callconv(.naked) noreturn {
asm volatile (
\\ mov sp, 0x800000
- \\ bl %[kmain:P]
- \\ bl %[abort:P]
- :
- : [kmain] "X" (&kmain),
- [abort] "X" (&abort),
+ \\ bl kmain
+ \\ bl abort
);
}
-noinline fn abort() noreturn {
+export fn abort() noreturn {
@branchHint(.cold);
while (true) {}
}
@@ -44,12 +78,15 @@ fn panic_handler(msg: []const u8, trace_addr: ?usize) noreturn {
@branchHint(.cold);
if (uart.is_initialized()) {
- uart.writer.print("kernel panic: {s}\n", .{msg}) catch {};
+ uart.write_slice("kernel panic: ");
+ uart.write_slice(msg);
+ uart.write_byte('\n');
var it = std.debug.StackIterator.init(trace_addr, null);
var ix: usize = 0;
while (it.next()) |frame| : (ix += 1) {
+ // TODO; remove
uart.writer.print("| #{d:0>2}: 0x{X:0>16}\n", .{ ix, frame }) catch {};
}
}