commit af078277eb1cd892c681d040f3a5fa8892a405d9
parent 7ebb5bafd8a4a433d75c6d380efdeed9108e3d14
Author: Sylvia Ivory <git@sivory.net>
Date: Fri, 6 Feb 2026 22:15:29 -0800
Software UART
Diffstat:
4 files changed, 100 insertions(+), 3 deletions(-)
diff --git a/boot/root.zig b/boot/root.zig
@@ -30,6 +30,9 @@ export fn kmain() void {
uart.write_slice("Setting up stack\n");
pi.setup_stacks();
+ uart.write_slice("Setting Cycle Counter\n");
+ pi.cycle_counter_init();
+
uart.write_slice("Exception Vector:\n");
for (0..16) |i| {
const dst: *allowzero u32 = @ptrFromInt(i * @sizeOf(*u32));
@@ -39,7 +42,6 @@ export fn kmain() void {
uart.write_slice("Enabling Interrupts\n");
uart.enable_interrupts() catch {};
pi.interrupts.enable_interrupts();
- pi.cycle_counter_init();
uart.write_slice("Entering program\n");
uart.flush();
diff --git a/pi/devices/sw-uart.zig b/pi/devices/sw-uart.zig
@@ -0,0 +1,70 @@
+const std = @import("std");
+
+const interrupts = @import("../interrupts.zig");
+const mem = @import("../mem.zig");
+
+const clock = @import("./clock.zig");
+const gpio = @import("./gpio.zig");
+const pi = @import("../root.zig");
+
+pub const Error = error{ AlreadyInitialized, NotInitialized, InvalidBaud } || gpio.Error;
+
+var initialized = false;
+var interrupts_enabled = false;
+
+pub var delay: u32 = undefined;
+var tx: u8 = undefined;
+var rx: u8 = undefined;
+
+pub fn initialize(baud: u32, tx_pin: u8, rx_pin: u8) Error!void {
+ if (initialized) return Error.AlreadyInitialized;
+
+ try gpio.fn_sel(tx_pin, .output);
+ // try gpio.set_pull(tx_pin, .Up);
+ tx = tx_pin;
+
+ try gpio.fn_sel(rx_pin, .input);
+ // try gpio.set_pull(rx_pin, .Up);
+ rx = rx_pin;
+
+ try gpio.set_on(tx_pin);
+
+ mem.barrier(.Write);
+
+ // Cycles to delay
+ delay = (@divFloor(700_000_000, baud));
+ if (delay == 0) return Error.InvalidBaud;
+
+ initialized = true;
+}
+
+pub fn write_byte(byte: u8) !void {
+ if (!initialized) return Error.NotInitialized;
+
+ gpio.set_off(tx) catch {};
+ mem.barrier(.Write);
+
+ var deadline = pi.cycle_counter_read() + delay;
+ pi.cycle_wait_until(deadline);
+
+ for (0..8) |n| {
+ const state = (byte & (@as(u8, 1) << @truncate(n))) != 0;
+ gpio.write(tx, state) catch {};
+ mem.barrier(.Write);
+
+ deadline += delay;
+ pi.cycle_wait_until(deadline);
+ }
+
+ gpio.set_on(tx) catch {};
+ mem.barrier(.Write);
+
+ deadline += delay;
+ pi.cycle_wait_until(deadline);
+}
+
+pub fn write_slice(bytes: []const u8) !void {
+ for (bytes) |b| {
+ try write_byte(b);
+ }
+}
diff --git a/pi/root.zig b/pi/root.zig
@@ -1,3 +1,5 @@
+const std = @import("std");
+
pub const Scheduler = @import("scheduler.zig");
pub const PSR = @import("./psr.zig").PSR;
@@ -10,15 +12,15 @@ pub const devices = struct {
pub const clock = @import("./devices/clock.zig");
pub const gpio = @import("./devices/gpio.zig");
pub const mini_uart = @import("./devices/mini-uart.zig");
+ pub const sw_uart = @import("./devices/sw-uart.zig");
pub const timer = @import("./devices/timer.zig");
pub const mailbox = @import("./devices/mailbox.zig");
};
pub inline fn cycle_counter_init() void {
- const in: u32 = 1;
asm volatile ("MCR p15, 0, %[in], c15, c12, 0"
:
- : [in] "r" (in),
+ : [in] "r" (1),
);
}
@@ -28,6 +30,12 @@ pub inline fn cycle_counter_read() u32 {
);
}
+pub inline fn cycle_wait_until(deadline: u32) void {
+ while (cycle_counter_read() < deadline) {
+ std.mem.doNotOptimizeAway(deadline);
+ }
+}
+
pub inline fn set_sp(sp: *usize) void {
asm volatile ("mov sp, %[value]"
:
diff --git a/programs/sw-uart.zig b/programs/sw-uart.zig
@@ -0,0 +1,17 @@
+const std = @import("std");
+const pi = @import("pi");
+
+const mini_uart = pi.devices.mini_uart;
+const sw_uart = pi.devices.sw_uart;
+const clock = pi.devices.clock;
+
+pub fn main() !void {
+ try sw_uart.initialize(115200, 8, 9);
+
+ while (true) {
+ mini_uart.write_slice("Hello World!\n");
+ try sw_uart.write_slice("Hello World!\n");
+
+ clock.delay_s(1);
+ }
+}