sylveos

Toy Operating System
Log | Files | Refs

commit 37c46355d2fc8d0b229301f7b8bce7d12f56b44f
parent d22a34ed425d445c9e903ecd1129283279bb3435
Author: Sylvia Ivory <git@sivory.net>
Date:   Wed, 14 Jan 2026 20:40:39 -0800

Rework devices

Diffstat:
Asrc/devices/gpio.zig | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/devices/timer.zig | 44++++++++++++++++++++++++++++++++++++++++++++
Dsrc/gpio.zig | 101-------------------------------------------------------------------------------
Msrc/labs/2-gpio.zig | 2+-
Dsrc/sys-timer.zig | 42------------------------------------------
Msrc/util.zig | 15+++++++++++++++
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) {