commit 2dde7c6696c589cec1f9250194be33037b81a491
parent 3a8994a06fa695725fcc6116ba59ae11c0c6732f
Author: Sylvia Ivory <git@sivory.net>
Date: Thu, 5 Feb 2026 19:36:36 -0800
TX Interrupts on UART
Diffstat:
3 files changed, 82 insertions(+), 32 deletions(-)
diff --git a/boot/root.zig b/boot/root.zig
@@ -59,7 +59,7 @@ fn panic_handler(msg: []const u8, trace_addr: ?usize) noreturn {
if (uart.is_initialized()) {
uart.write_slice("kernel panic: ");
uart.write_slice(msg);
- uart.write_byte('\n');
+ uart.write_slice("\n");
var it = std.debug.StackIterator.init(trace_addr, null);
var ix: usize = 0;
diff --git a/pi/devices/mini-uart.zig b/pi/devices/mini-uart.zig
@@ -8,7 +8,7 @@ const mem = @import("../mem.zig");
const clock = @import("./clock.zig");
const gpio = @import("./gpio.zig");
-pub const Error = error{ AlreadyInitialized, NotInitialized, InvalidReadLimit } || gpio.Error;
+pub const Error = error{ AlreadyInitialized, NotInitialized, InvalidReadLimit, Timeout } || gpio.Error;
// Page 8: Auxiliary peripherals Register Map
const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0x0021_5000;
@@ -135,8 +135,15 @@ pub fn enable_interrupts() Error!void {
interrupts_enabled = true;
}
-// var tx_buffer: [1024]u8 = undefined;
-// var tx_list: std.ArrayList(u8) = .initBuffer(&tx_buffer);
+fn enable_tx_interrupt() void {
+ mem.put_u32_barrier(@ptrFromInt(AUX_MU_IER_REG), 0b1111);
+}
+
+fn disable_tx_interrupt() void {
+ mem.put_u32_barrier(@ptrFromInt(AUX_MU_IER_REG), 0b1101);
+}
+
+var tx_list: StackRingBuffer(u8, 128) = .init();
var rx_list: StackRingBuffer(u8, 128) = .init();
var rx_writer: ?*std.Io.Writer = null;
@@ -180,29 +187,29 @@ noinline fn uart_handler(pc: usize) void {
// UART interrupt pending
if ((IIR & 0b1) == 1) return;
- if ((IIR & 0b100) != 0) {
- // can't do anything with error
- const b = read_byte_raw();
- if (rx_writer) |w| {
- rx_writer_written += 1;
- w.writeByte(b) catch {};
- } else {
- rx_list.push(b) catch {};
- }
+ switch (IIR & 0b110) {
+ // RX has byte
+ 0b100 => {
+ const b = read_byte_raw();
+ if (rx_writer) |w| {
+ rx_writer_written += 1;
+ w.writeByte(b) catch {};
+ } else {
+ rx_list.push(b) catch {};
+ }
+ },
+ // TX has space
+ 0b010 => {
+ if (tx_list.pop()) |byte| {
+ mem.put_u32_barrier(@ptrFromInt(AUX_MU_IO_REG), @as(u32, byte) & 0xFF);
+ } else {
+ disable_tx_interrupt();
+ return;
+ }
+ },
+ // Unknown
+ else => break,
}
-
- // TODO; figure out why TX interrupts aren't working
- // if ((IIR & 0b010) != 0) {
- // // TX empty
- // // while (can_write()) {
- // if (tx_list.items.len >= 1) {
- // const byte = tx_list.orderedRemove(0);
- // write_byte(byte, true);
- // } else {
- // // break;
- // }
- // // }
- // }
}
}
@@ -239,6 +246,13 @@ pub fn read_queue_length() usize {
return rx_list.length();
}
+pub fn write_queue_length() usize {
+ const cs = mem.enter_critical_section();
+ defer cs.exit();
+
+ return tx_list.length();
+}
+
pub fn can_read() bool {
// Check if FIFO has data
// Page 15: AUX_MU_LSR_REG Register
@@ -290,15 +304,51 @@ pub fn can_write() bool {
// return tx_list.items.len;
// }
-pub fn write_byte(byte: u8) void {
- while (!can_write()) {}
+pub fn write_byte(byte: u8) !void {
+ if (!interrupts_enabled) {
+ while (!can_write()) {}
+ write_byte_raw(byte);
+ return;
+ }
+
+ const cs = mem.enter_critical_section();
+ defer cs.exit();
+
+ // Let TX start draining
+ try tx_list.push(byte);
+ enable_tx_interrupt();
+}
+
+pub fn write_byte_blocking(byte: u8, timeout: ?u64) !void {
+ if (!interrupts_enabled) {
+ return write_byte(byte);
+ }
+
+ var wait_until: u64 = std.math.maxInt(u64);
+
+ if (timeout) |t| {
+ wait_until = clock.current_count() + t;
+ }
+
+ while (true) {
+ if (clock.current_count() > wait_until) {
+ return Error.Timeout;
+ }
+
+ write_byte(byte) catch continue;
+
+ return;
+ }
+}
+pub inline fn write_byte_raw(byte: u8) void {
mem.put_u32_barrier(@ptrFromInt(AUX_MU_IO_REG), @as(u32, byte) & 0xFF);
}
pub fn write_slice(bytes: []const u8) void {
for (bytes) |b| {
- write_byte(b);
+ // Error only on timeout
+ write_byte(b) catch {};
}
}
@@ -307,7 +357,7 @@ pub fn flush() void {
while (true) {
const s = status();
- if (s.tx_is_empty and s.tx_is_idle) break;
+ if (s.tx_is_empty and s.tx_is_idle and write_queue_length() == 0) break;
}
}
@@ -319,7 +369,7 @@ fn writer_drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) !u
_ = splat;
for (data[0]) |b| {
- write_byte(b);
+ write_byte(b) catch {};
}
return data[0].len;
diff --git a/programs/echo.zig b/programs/echo.zig
@@ -12,7 +12,7 @@ pub fn main() !void {
uart.read_queue_length(),
status.rx_fill_level,
uart.can_read(),
- 0,
+ uart.write_queue_length(),
status.tx_fill_level,
uart.can_write(),
}) catch {};