sylveos

Toy Operating System
Log | Files | Refs

commit 2dde7c6696c589cec1f9250194be33037b81a491
parent 3a8994a06fa695725fcc6116ba59ae11c0c6732f
Author: Sylvia Ivory <git@sivory.net>
Date:   Thu,  5 Feb 2026 19:36:36 -0800

TX Interrupts on UART

Diffstat:
Mboot/root.zig | 2+-
Mpi/devices/mini-uart.zig | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mprograms/echo.zig | 2+-
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 {};