sylveos

Toy Operating System
Log | Files | Refs

commit 7ebb5bafd8a4a433d75c6d380efdeed9108e3d14
parent 061f53af5e373ac8abe45148cd43c7760b27ae66
Author: Sylvia Ivory <git@sivory.net>
Date:   Fri,  6 Feb 2026 17:51:49 -0800

Add new register API

Diffstat:
Api/register.zig | 353+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpi/root.zig | 1+
2 files changed, 354 insertions(+), 0 deletions(-)

diff --git a/pi/register.zig b/pi/register.zig @@ -0,0 +1,353 @@ +// Referenced based off of Swift MMIO +const std = @import("std"); + +const Error = error{OutOfRange}; + +const Kind = enum { + // Only read methods + ReadOnly, + // Only write methods + WriteOnly, + // Read, write, mutate methods + ReadModifyWrite, +}; + +// e.g. clock COUNTER_LOWER +pub fn Register(comptime T: type, comptime kind: Kind) type { + comptime if (@sizeOf(T) != @sizeOf(usize)) @compileError("expected type as large as usize"); + + const RegisterBase = struct { + const Self = @This(); + + inline fn get(addr_ptr: *volatile usize) T { + return @bitCast(addr_ptr.*); + } + + inline fn set(addr_ptr: *volatile usize, value: T) void { + addr_ptr.* = @bitCast(value); + } + }; + + switch (kind) { + .ReadOnly => { + return struct { + address: *volatile usize, + + const Self = @This(); + + pub inline fn init(address: usize) Self { + return .{ .address = @ptrFromInt(address) }; + } + + pub inline fn get(self: *const Self) T { + return RegisterBase.get(self.address); + } + }; + }, + .WriteOnly => { + return struct { + address: *volatile usize, + + const Self = @This(); + + pub inline fn init(address: usize) Self { + return .{ .address = @ptrFromInt(address) }; + } + + pub inline fn set(self: *const Self, value: T) void { + RegisterBase.set(self.address, value); + } + }; + }, + .ReadModifyWrite => { + return struct { + address: *volatile usize, + + const Self = @This(); + + pub inline fn init(address: usize) Self { + return .{ .address = @ptrFromInt(address) }; + } + + pub inline fn get(self: *const Self) T { + return RegisterBase.get(self.address); + } + + pub inline fn set(self: *const Self, value: T) void { + return RegisterBase.set(self.address, value); + } + + pub inline fn bor(self: *const Self, value: usize) void { + const original = self.address.*; + + self.address.* = original | value; + } + + pub inline fn band(self: *const Self, value: usize) void { + const original = self.address.*; + + self.address.* = original & value; + } + }; + }, + } +} + +// These tests should be non-issues since Register is simple +test "Register read" { + var value: usize = 617; + const value_register: Register(usize, .ReadOnly) = .init(@intFromPtr(&value)); + + try std.testing.expectEqual(value_register.get(), value); +} + +test "Register write" { + var value: usize = 0; + const value_register: Register(usize, .WriteOnly) = .init(@intFromPtr(&value)); + + value_register.set(617); + + try std.testing.expectEqual(value, 617); +} + +test "Register bor" { + var value: usize = 0b1111_0000; + const value_register: Register(usize, .ReadModifyWrite) = .init(@intFromPtr(&value)); + + value_register.bor(0b1111); + + try std.testing.expectEqual(value, 0b1111_1111); +} + +test "Register band" { + var value: usize = 0b1111_0000; + const value_register: Register(usize, .ReadModifyWrite) = .init(@intFromPtr(&value)); + + value_register.band(0b1001_1111); + + try std.testing.expectEqual(value, 0b1001_0000); +} + +// e.g. gpio GPFSEL +pub fn Array(comptime T: type, length: comptime_int, comptime kind: Kind) type { + comptime if (@sizeOf(T) > @sizeOf(usize)) @compileError("cannot store elements larger than usize"); + const elem_mask: usize = comptime (~@as(usize, 0)) >> (@bitSizeOf(usize) - @bitSizeOf(T)); + const ValueT = switch (@sizeOf(T)) { + 0...8 => u8, + 9...16 => u16, + 17...32 => u32, + 33...64 => u64, + // maybe you're on a 128 bit platform + else => usize, + }; + + const ArrayBase = struct { + const Self = @This(); + + inline fn calculate_shift(index: usize) usize { + return @bitSizeOf(T) * (index % @bitSizeOf(T)); + } + + inline fn calculate_address(start: usize, index: usize) *volatile usize { + const ptr_offset = @divFloor(index, @bitSizeOf(T)) * @sizeOf(usize); + const address: *volatile usize = @ptrFromInt(start + ptr_offset); + return address; + } + + inline fn calculate_mask(index: usize) usize { + return elem_mask << @truncate(Self.calculate_shift(index)); + } + + inline fn calculate_put_mask(index: usize, value: T) usize { + const value_t: ValueT = @bitCast(value); + const masked = value_t & elem_mask; + + return masked << @truncate(Self.calculate_shift(index)); + } + + inline fn get(start: usize, index: usize) !T { + if (index > length) return Error.OutOfRange; + + const address = Self.calculate_address(start, index); + const mask = Self.calculate_mask(index); + + const masked = (address.*) & mask; + const value: ValueT = @truncate(masked >> Self.calculate_shift(index)); + + return @bitCast(value); + } + + inline fn put(start: usize, index: usize, value: T) !void { + if (index > length) return Error.OutOfRange; + + const address = Self.calculate_address(start, index); + const masked = Self.calculate_put_mask(index, value); + + address.* = masked; + } + }; + + switch (kind) { + .ReadOnly => { + return struct { + start: usize, + + const Self = @This(); + + pub inline fn init(address: usize) Self { + return .{ .start = address }; + } + + pub inline fn get(self: *const Self, index: usize) !T { + return ArrayBase.get(self.start, index); + } + }; + }, + .WriteOnly => { + return struct { + start: usize, + + const Self = @This(); + + pub inline fn init(address: usize) Self { + return .{ .start = address }; + } + + pub inline fn put(self: *const Self, index: usize, value: T) !void { + return ArrayBase.put(self.start, index, value); + } + }; + }, + .ReadModifyWrite => { + return struct { + start: usize, + + const Self = @This(); + + pub inline fn init(address: usize) Self { + return .{ .start = address }; + } + + pub inline fn get(self: *const Self, index: usize) !T { + return ArrayBase.get(self.start, index); + } + + pub inline fn put(self: *const Self, index: usize, value: T) !void { + return ArrayBase.put(self.start, index, value); + } + + pub inline fn insert(self: *const Self, index: usize, value: T) !void { + const address = ArrayBase.calculate_address(self.start, index); + const original = address.*; + const masked = ArrayBase.calculate_put_mask(index, value); + const mask = ArrayBase.calculate_mask(index); + + // Remove then insert + address.* = (original & ~mask) | masked; + } + + pub inline fn remove(self: *const Self, index: usize) !void { + self.insert(index, 0); + } + }; + }, + } +} + +test "Array read" { + // Note; this will not work on non-64 bit + const array: [2]usize = .{ + 0xFFEEDDCCBBAA9988, + 0x7766554433221100, + }; + + const arr: Array(u8, 2 * 8, .ReadOnly) = .init(@intFromPtr(&array)); + + try std.testing.expectEqual(0x88, try arr.get(0)); + try std.testing.expectEqual(0x99, try arr.get(1)); + try std.testing.expectEqual(0xAA, try arr.get(2)); + try std.testing.expectEqual(0xBB, try arr.get(3)); + try std.testing.expectEqual(0xCC, try arr.get(4)); + try std.testing.expectEqual(0xDD, try arr.get(5)); + try std.testing.expectEqual(0xEE, try arr.get(6)); + try std.testing.expectEqual(0xFF, try arr.get(7)); + try std.testing.expectEqual(0x00, try arr.get(8)); + try std.testing.expectEqual(0x11, try arr.get(9)); + try std.testing.expectEqual(0x22, try arr.get(10)); + try std.testing.expectEqual(0x33, try arr.get(11)); + try std.testing.expectEqual(0x44, try arr.get(12)); + try std.testing.expectEqual(0x55, try arr.get(13)); + try std.testing.expectEqual(0x66, try arr.get(14)); + try std.testing.expectEqual(0x77, try arr.get(15)); +} + +test "Array write" { + var array: [2]usize = .{ 0, 0 }; + + const arr: Array(u8, 2 * 8, .WriteOnly) = .init(@intFromPtr(&array)); + + try arr.put(0, 0x88); + try std.testing.expectEqual(0x88, array[0]); + try std.testing.expectEqual(0, array[1]); + + try arr.put(1, 0x99); + try std.testing.expectEqual(0x9900, array[0]); + try std.testing.expectEqual(0, array[1]); + + try arr.put(2, 0xAA); + try std.testing.expectEqual(0xAA0000, array[0]); + try std.testing.expectEqual(0, array[1]); + + try arr.put(3, 0xBB); + try std.testing.expectEqual(0xBB000000, array[0]); + try std.testing.expectEqual(0, array[1]); + + try arr.put(4, 0xCC); + try std.testing.expectEqual(0xCC00000000, array[0]); + try std.testing.expectEqual(0, array[1]); + + try arr.put(9, 0x11); + try std.testing.expectEqual(0xCC00000000, array[0]); + try std.testing.expectEqual(0x0000001100, array[1]); + + try arr.put(10, 0x22); + try std.testing.expectEqual(0xCC00000000, array[0]); + try std.testing.expectEqual(0x0000220000, array[1]); +} + +test "Array insert" { + var array: [2]usize = .{ 0, 0 }; + + const arr: Array(u8, 2 * 8, .ReadModifyWrite) = .init(@intFromPtr(&array)); + + try arr.insert(0, 0x88); + try std.testing.expectEqual(0x88, array[0]); + try std.testing.expectEqual(0, array[1]); + + try arr.insert(1, 0x99); + try std.testing.expectEqual(0x9988, array[0]); + try std.testing.expectEqual(0, array[1]); + + try arr.insert(0, 0x99); + try std.testing.expectEqual(0x9999, array[0]); + try std.testing.expectEqual(0, array[1]); + + try arr.insert(0, 0x88); + try std.testing.expectEqual(0x9988, array[0]); + try std.testing.expectEqual(0, array[1]); + + try arr.insert(3, 0xBB); + try std.testing.expectEqual(0xBB009988, array[0]); + try std.testing.expectEqual(0, array[1]); + + try arr.insert(10, 0x22); + try std.testing.expectEqual(0x00BB009988, array[0]); + try std.testing.expectEqual(0x0000220000, array[1]); +} + +// e.g. gpio GPCLR +pub fn BitField(start: usize, length: comptime_int, comptime kind: Kind) type { + return Array(start, u1, length, kind); +} + +// If Array is correct, then BitField is also correct diff --git a/pi/root.zig b/pi/root.zig @@ -2,6 +2,7 @@ pub const Scheduler = @import("scheduler.zig"); pub const PSR = @import("./psr.zig").PSR; pub const interrupts = @import("./interrupts.zig"); +pub const register = @import("./register.zig"); pub const journal = @import("./journal.zig"); pub const mem = @import("./mem.zig");