sylveos

Toy Operating System
Log | Files | Refs

commit 0c7e16ca24bc194c3902be9b967bbcd2a1d62adc
parent 5047cb829dd4d38eab3c4c454d73772af562f594
Author: Sylvia Ivory <git@sivory.net>
Date:   Sun, 11 Jan 2026 23:37:13 -0800

Idea of GPIO

Diffstat:
AJustfile | 5+++++
Mbuild.zig | 13+++++++++++--
Asrc/gpio.zig | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/main.zig | 13++++++++++++-
Msrc/util.zig | 8++++++++
5 files changed, 160 insertions(+), 3 deletions(-)

diff --git a/Justfile b/Justfile @@ -0,0 +1,5 @@ +build: + zig build + +list: build + arm-none-eabi-objdump -D zig-out/bin/SylveOS diff --git a/build.zig b/build.zig @@ -1,14 +1,20 @@ const std = @import("std"); +const config = @import("build.zig.zon"); + const fs = std.fs; +// BCM2835 is **very specifically** the ARM1176JZF-S +// https://www.raspberrypi.com/documentation/computers/processors.html const target: std.Target.Query = .{ .cpu_arch = .arm, + .cpu_model = .{ .explicit = &std.Target.arm.cpu.arm1176jzf_s }, + .cpu_features_add = std.Target.arm.cpu.arm1176jzf_s.features, .os_tag = .freestanding, }; pub fn build(b: *std.Build) !void { const exe = b.addExecutable(.{ - .name = "SylveOS", + .name = @tagName(config.name), .root_module = b.createModule(.{ .root_source_file = b.path("src/root.zig"), .target = b.resolveTargetQuery(target), @@ -18,7 +24,10 @@ pub fn build(b: *std.Build) !void { }), .linkage = .static, }); - exe.setLinkerScript(b.path("linker.ld")); + + const install_asm = b.addInstallBinFile(exe.getEmittedAsm(), @tagName(config.name) ++ ".s"); + b.getInstallStep().dependOn(&install_asm.step); + b.installArtifact(exe); } diff --git a/src/gpio.zig b/src/gpio.zig @@ -0,0 +1,124 @@ +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 GPFSEL1: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x0004); +const GPFSEL2: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x0008); +const GPFSEL3: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x000C); +const GPFSEL4: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x0010); +const GPFSEL5: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x0014); + +const GPSET0: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x001C); +const GPSET1: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x0020); + +const GPCLR0: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x0028); +const GPCLR1: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x002C); + +const GPLEV0: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x0034); +const GPLEV1: *volatile u32 = @ptrFromInt(BASE_ADDR + 0x0038); + +// 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, +}; + +fn fn_sel(pin: u8, sel: FunctionSelect) void { + // TODO; this can be array assignment + const address: *volatile u32 = switch (pin) { + 0...9 => GPFSEL0, + 10...19 => GPFSEL1, + 20...29 => GPFSEL2, + 30...39 => GPFSEL3, + 40...49 => GPFSEL4, + 50...53 => GPFSEL5, + else => @panic("pin out of range (0...53)"), + }; + const offset: u5 = @truncate((pin % 10) * 3); + const mask = ~(@as(u32, 0b111) << offset); + + var state = address.*; // util.get_32(@ptrFromInt(address)); + state &= mask; + state |= (@as(u32, @intCast(@intFromEnum(sel))) << offset); + // util.put_32(@ptrFromInt(address), state); + address.* = state; +} + +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) void { + const address: *volatile u32 = switch (pin) { + 0...31 => GPSET0, + 32...53 => GPSET1, + else => @panic("pin out of range (0...53)"), + }; + + addr_bitset(pin, address); +} + +// Turn off +fn output_clear(pin: u8) void { + const address: *volatile u32 = switch (pin) { + 0...31 => GPCLR0, + 32...53 => GPCLR1, + else => @panic("pin out of range (0...53)"), + }; + + addr_bitset(pin, address); +} + +pub fn set_output(pin: u8) void { + fn_sel(pin, .output); +} + +pub fn set_input(pin: u8) void { + fn_sel(pin, .input); +} + +pub fn set_on(pin: u8) void { + output_set(pin); +} + +pub fn set_off(pin: u8) void { + output_clear(pin); +} + +pub fn read(pin: u8) bool { + const address: *volatile u32 = switch (pin) { + 0...31 => GPLEV0, + 32...53 => GPLEV1, + else => @panic("pin out of range (0...53)"), + }; + + const offset: u5 = @truncate(pin % 32); + const mask = (@as(u32, 1) << offset); + + return (address.* & mask) == mask; +} + +pub fn write(pin: u8, state: bool) void { + if (state) { + set_on(pin); + } else { + set_off(pin); + } +} diff --git a/src/main.zig b/src/main.zig @@ -1,3 +1,14 @@ const util = @import("util.zig"); +const gpio = @import("gpio.zig"); -pub fn main() void {} +pub fn main() void { + const led = 20; + gpio.set_output(led); + for (0..10) |_| { + gpio.set_on(led); + util.delay_cycles(1000000); + gpio.set_off(led); + util.delay_cycles(1000000); + } + gpio.set_on(led); +} diff --git a/src/util.zig b/src/util.zig @@ -16,3 +16,11 @@ pub fn put_32(addr: *u32, value: u32) void { pub fn nop() void { asm volatile ("bx lr"); } + +pub fn delay_cycles(ticks: usize) void { + var counter = ticks; + while (counter > 0) { + counter -= 1; + nop(); // Prevent optimization + } +}