commit 37c46355d2fc8d0b229301f7b8bce7d12f56b44f
parent d22a34ed425d445c9e903ecd1129283279bb3435
Author: Sylvia Ivory <git@sivory.net>
Date: Wed, 14 Jan 2026 20:40:39 -0800
Rework devices
Diffstat:
6 files changed, 177 insertions(+), 144 deletions(-)
diff --git a/src/devices/gpio.zig b/src/devices/gpio.zig
@@ -0,0 +1,117 @@
+const std = @import("std");
+const util = @import("../util.zig");
+
+// Page 90, Table 6-1: GPIO Register Assignment
+const BASE_OFFSET: usize = 0x0020_0000;
+const GPFSEL0_OFFSET: usize = BASE_OFFSET + 0x0000;
+const GPSET0_OFFSET: usize = BASE_OFFSET + 0x001C;
+const GPCLR0_OFFSET: usize = BASE_OFFSET + 0x0028;
+const GPLEV0_OFFSET: usize = BASE_OFFSET + 0x0034;
+
+pub const MAX_GPIO: u8 = 53;
+const GPIO_PER_FSEL: u8 = 10;
+
+// Page 92, Table 6-2: GPIO Alternate function select register 0
+// All function select registers reflect this bit layout
+const FunctionSelect = enum(u3) {
+ input = 0b000,
+ output = 0b001,
+
+ alt_fn_0 = 0b100,
+ alt_fn_1 = 0b101,
+ alt_fn_2 = 0b110,
+ alt_fn_3 = 0b111,
+ alt_fn_4 = 0b011,
+ alt_fn_5 = 0b010,
+};
+
+pub const Error = error{
+ PinOutOfRange,
+ NotInitialized,
+};
+
+var base_address: usize = 0;
+
+fn gpio_check(pin: u8) Error!void {
+ if (base_address == 0) return Error.NotInitialized;
+ if (pin > MAX_GPIO) return Error.PinOutOfRange;
+}
+
+fn get_address(pin: u8, offset: usize, chunk: ?u32) Error!*u32 {
+ try gpio_check(pin);
+
+ // One might say "@bitSizeOf(u32)" is redundant but I do not care
+ const index = @divFloor(pin, chunk orelse @bitSizeOf(u32)) * @sizeOf(u32);
+ const address: *u32 = @ptrFromInt(base_address + offset + index);
+
+ return address;
+}
+
+fn fn_sel(pin: u8, sel: FunctionSelect) Error!void {
+ const address = try get_address(pin, GPSET0_OFFSET, GPIO_PER_FSEL);
+
+ const offset: u5 = @truncate((pin % GPIO_PER_FSEL) * 3);
+ const mask = ~(@as(u32, 0b111) << offset);
+
+ var state = util.get_u32(address);
+ state &= mask;
+ state |= (@as(u32, @intCast(@intFromEnum(sel))) << offset);
+ util.put_u32(address, state);
+}
+
+fn addr_bitset(pin: u8, address: *u32) void {
+ const offset: u5 = @truncate(pin % 32);
+ const mask = (@as(u32, 1) << offset);
+
+ // We don't want to preserve the old value
+ // SET and CLR are separated for this purpose
+ util.put_u32(address, mask);
+}
+
+// Turn on
+fn output_set(pin: u8) Error!void {
+ addr_bitset(pin, try get_address(pin, GPSET0_OFFSET, null));
+}
+
+// Turn off
+fn output_clear(pin: u8) Error!void {
+ addr_bitset(pin, try get_address(pin, GPCLR0_OFFSET, null));
+}
+
+// Public API
+pub fn initialize(base_addr: u32) Error!void {
+ base_address = base_addr;
+}
+
+pub fn set_output(pin: u8) Error!void {
+ try fn_sel(pin, .output);
+}
+
+pub fn set_input(pin: u8) Error!void {
+ try fn_sel(pin, .input);
+}
+
+pub fn set_on(pin: u8) Error!void {
+ try output_set(pin);
+}
+
+pub fn set_off(pin: u8) Error!void {
+ try output_clear(pin);
+}
+
+pub fn read(pin: u8) Error!bool {
+ const address = try get_address(pin, GPLEV0_OFFSET, GPIO_PER_FSEL);
+
+ const offset: u5 = @truncate(pin % 32);
+ const mask = (@as(u32, 1) << offset);
+
+ return (util.get_u32(address) & mask) == mask;
+}
+
+pub fn write(pin: u8, state: bool) Error!void {
+ if (state) {
+ try set_on(pin);
+ } else {
+ try set_off(pin);
+ }
+}
diff --git a/src/devices/timer.zig b/src/devices/timer.zig
@@ -0,0 +1,44 @@
+const std = @import("std");
+
+const barrier = @import("barrier.zig");
+const util = @import("../util.zig");
+
+var base_address: usize = 0;
+
+const BASE_OFFSET: usize = 0x3000;
+const COUNTER_LOWER_OFFSET: usize = 0x04;
+const COUNTER_UPPER_OFFSET: usize = 0x08;
+
+pub fn initialize(base_addr: u32) void {
+ base_address = base_addr;
+}
+
+pub fn current_count() u64 {
+ var b = barrier.obtain();
+ defer b.release();
+
+ const upper = util.get_u32(base_address + COUNTER_UPPER_OFFSET);
+ const lower = util.get_u32(base_address + COUNTER_LOWER_OFFSET);
+ const extended: u64 = @intCast(upper);
+
+ return (extended << 32) | lower;
+}
+
+// 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 fn delay_ms(ms: u64) void {
+ delay(ms * 1000);
+}
+
+pub fn delay_s(s: u64) void {
+ delay_ms(s * 1000);
+}
diff --git a/src/gpio.zig b/src/gpio.zig
@@ -1,101 +0,0 @@
-const std = @import("std");
-const util = @import("util.zig");
-
-// Page 90, Table 6-1: GPIO Register Assignment
-const BASE_ADDR: u32 = 0x2020_0000;
-
-const GPFSEL0: [*]volatile u32 = @ptrFromInt(BASE_ADDR + 0x0000);
-const GPSET0: [*]volatile u32 = @ptrFromInt(BASE_ADDR + 0x001C);
-const GPCLR0: [*]volatile u32 = @ptrFromInt(BASE_ADDR + 0x0028);
-const GPLEV0: [*]volatile u32 = @ptrFromInt(BASE_ADDR + 0x0034);
-pub const MAX_GPIO: u8 = 53;
-
-// Page 92, Table 6-2: GPIO Alternate function select register 0
-// All function select registers reflect this bit layout
-const FunctionSelect = enum(u3) {
- input = 0b000,
- output = 0b001,
-
- alt_fn_0 = 0b100,
- alt_fn_1 = 0b101,
- alt_fn_2 = 0b110,
- alt_fn_3 = 0b111,
- alt_fn_4 = 0b011,
- alt_fn_5 = 0b010,
-};
-
-pub const Error = error{
- PinOutOfRange,
-};
-
-fn fn_sel(pin: u8, sel: FunctionSelect) Error!void {
- if (pin > MAX_GPIO) return Error.PinOutOfRange;
-
- const index = @divFloor(pin, 10);
- const address: *volatile u32 = &GPFSEL0[index];
- const offset: u5 = @truncate((pin % 10) * 3);
- const mask = ~(@as(u32, 0b111) << offset);
-
- var state = address.*;
- state &= mask;
- state |= (@as(u32, @intCast(@intFromEnum(sel))) << offset);
- address.* = state;
-}
-
-fn addr_get(pin: u8, address: [*]volatile u32) Error!*volatile u32 {
- if (pin > MAX_GPIO) return Error.PinOutOfRange;
-
- return &address[@divFloor(pin, 32)];
-}
-
-fn addr_bitset(pin: u8, address: *volatile u32) void {
- const offset: u5 = @truncate(pin % 32);
- const mask = (@as(u32, 1) << offset);
-
- // We don't want to preserve the old value
- // SET and CLR are separated for this purpose
- address.* = mask;
-}
-
-// Turn on
-fn output_set(pin: u8) Error!void {
- addr_bitset(pin, try addr_get(pin, GPSET0));
-}
-
-// Turn off
-fn output_clear(pin: u8) Error!void {
- addr_bitset(pin, try addr_get(pin, GPCLR0));
-}
-
-pub fn set_output(pin: u8) Error!void {
- try fn_sel(pin, .output);
-}
-
-pub fn set_input(pin: u8) Error!void {
- try fn_sel(pin, .input);
-}
-
-pub fn set_on(pin: u8) Error!void {
- try output_set(pin);
-}
-
-pub fn set_off(pin: u8) Error!void {
- try output_clear(pin);
-}
-
-pub fn read(pin: u8) Error!bool {
- const address = try addr_get(pin, GPLEV0);
-
- const offset: u5 = @truncate(pin % 32);
- const mask = (@as(u32, 1) << offset);
-
- return (address.* & mask) == mask;
-}
-
-pub fn write(pin: u8, state: bool) Error!void {
- if (state) {
- try set_on(pin);
- } else {
- try set_off(pin);
- }
-}
diff --git a/src/labs/2-gpio.zig b/src/labs/2-gpio.zig
@@ -1,4 +1,4 @@
-const gpio = @import("../gpio.zig");
+const gpio = @import("../devices/gpio.zig");
const util = @import("../util.zig");
const led0 = 20;
diff --git a/src/sys-timer.zig b/src/sys-timer.zig
@@ -1,42 +0,0 @@
-const std = @import("std");
-const barrier = @import("barrier.zig");
-
-const BASE_ADDR: u32 = 0x2000_3000;
-
-// const STATUS: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x00);
-const COUNTER_LOWER: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x04);
-const COUNTER_UPPER: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x08);
-// const COMPARE_0: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x0C);
-// const COMPARE_1: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x10);
-// const COMPARE_2: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x14);
-// const COMPARE_3: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x18);
-
-pub fn current_count() u64 {
- var b = barrier.obtain();
- defer b.release();
-
- const upper = COUNTER_UPPER.*;
- const lower = COUNTER_LOWER.*;
- const extended: u64 = @intCast(upper);
-
- return (extended << 32) | lower;
-}
-
-// 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 fn delay_ms(ms: u64) void {
- delay(ms * 1000);
-}
-
-pub fn delay_s(s: u64) void {
- delay_ms(s * 1000);
-}
diff --git a/src/util.zig b/src/util.zig
@@ -1,5 +1,20 @@
pub extern fn nop() void;
+pub inline fn get_u32(address: *u32) u32 {
+ return asm volatile ("ldr %[result], [%[address]]"
+ : [result] "=r" (-> u32),
+ : [address] "r" (address),
+ );
+}
+
+pub inline fn put_u32(address: *u32, value: u32) void {
+ asm volatile ("str %[value], [%[address]]"
+ :
+ : [address] "r" (address),
+ [value] "r" (value),
+ );
+}
+
pub noinline fn delay_cycles(ticks: usize) void {
var counter = ticks;
while (counter > 0) {