commit 5ba71952a6169b411f1f32f66dd3ed1920bb0fe1
parent 69f6f54a28a74691db0c34c7e1cdabf9b78a0563
Author: Sylvia Ivory <git@sivory.net>
Date: Wed, 14 Jan 2026 22:10:09 -0800
Add fake-pi
Diffstat:
9 files changed, 212 insertions(+), 19 deletions(-)
diff --git a/build.zig b/build.zig
@@ -1,6 +1,35 @@
const std = @import("std");
const config = @import("build.zig.zon");
+fn add_exe_fake_pi(exe_name: []const u8, path: std.Build.LazyPath, b: *std.Build) !*std.Build.Step.Compile {
+ const pi = b.createModule(.{
+ .root_source_file = path,
+ .target = b.graph.host,
+ .optimize = .ReleaseSafe,
+ });
+
+ const fake_pi = b.createModule(.{
+ .root_source_file = b.path("fake-pi/main.zig"),
+ .target = b.graph.host,
+ .optimize = .ReleaseSafe,
+ });
+
+ fake_pi.addImport("pi", pi);
+
+ const exe_name_fake = try std.fmt.allocPrint(b.allocator, "{s}-fake-pi", .{exe_name});
+ defer b.allocator.free(exe_name_fake);
+
+ const exe = b.addExecutable(.{
+ .name = exe_name_fake,
+ .linkage = .static,
+ .root_module = fake_pi,
+ });
+
+ b.installArtifact(exe);
+
+ return exe;
+}
+
fn add_exe(exe_name: []const u8, path: std.Build.LazyPath, b: *std.Build) !*std.Build.Step.Compile {
var cpu_features = std.Target.arm.cpu.arm1176jzf_s.features;
cpu_features.addFeatureSet(std.Target.arm.featureSet(&[_]std.Target.arm.Feature{.strict_align}));
@@ -54,7 +83,7 @@ fn lab(name: []const u8, units: []const []const u8, b: *std.Build) !void {
const template =
\\ const lab = @import("{s}");
\\ export fn abort() void {{ while(true) {{}} }}
- \\ export fn main() void {{ _ = lab.{s}() catch {{}}; }}
+ \\ pub export fn kmain() void {{ _ = lab.{s}() catch {{}}; }}
;
const root_src = try std.fmt.allocPrint(b.allocator, template, .{ lab_path, unit });
@@ -96,10 +125,16 @@ fn handle_lab(name: []const u8, b: *std.Build) !void {
}
pub fn build(b: *std.Build) !void {
+ const fake_pi = b.option(bool, "fake-pi", "build with fake-pi");
const lab_opt = b.option([]const u8, "lab", "compile specific lab");
+
if (lab_opt) |lab_name| {
try handle_lab(lab_name, b);
}
- _ = try add_exe(@tagName(config.name), b.path("src/root.zig"), b);
+ if (fake_pi orelse false) {
+ _ = try add_exe_fake_pi(@tagName(config.name), b.path("src/root.zig"), b);
+ } else {
+ _ = try add_exe(@tagName(config.name), b.path("src/root.zig"), b);
+ }
}
diff --git a/fake-pi/device.zig b/fake-pi/device.zig
@@ -0,0 +1,5 @@
+pub const Error = error{
+ InvalidAddress,
+ IllegalWrite,
+ IllegalRead,
+};
diff --git a/fake-pi/devices/gpio.zig b/fake-pi/devices/gpio.zig
@@ -0,0 +1,78 @@
+const std = @import("std");
+
+const device = @import("../device.zig");
+
+// TODO; these constants should come from src/devices/gpio.zig
+pub const START_OFFSET: usize = 0x0020_0000;
+pub const END_OFFSET: usize = 0x0020_00B0;
+
+const GPFSEL0_OFFSET: usize = START_OFFSET + 0x0000;
+const GPSET0_OFFSET: usize = START_OFFSET + 0x001C;
+const GPCLR0_OFFSET: usize = START_OFFSET + 0x0028;
+const GPLEV0_OFFSET: usize = START_OFFSET + 0x0034;
+var base_address: u32 = 0;
+
+var gpfsel0 = ~@as(u32, 0);
+var gpfsel1 = ~@as(u32, 0);
+var gpfsel2 = ~@as(u32, 0);
+var gpfsel3 = ~@as(u32, 0);
+var gpfsel4 = ~@as(u32, 0);
+var gpfsel5 = ~@as(u32, 0);
+
+var gpset0: u32 = 0;
+var gpset1: u32 = 0;
+
+var gpclr0: u32 = 0;
+var gpclr1: u32 = 0;
+
+var gplev0: u32 = 0;
+var gplev1: u32 = 0;
+
+pub const Error = error{
+ ReadGPFSEL,
+ ReadGPCLR,
+ WriteGPLEV,
+} || device.Error;
+
+pub fn initialize(base_addr: u32) void {
+ base_address = base_addr;
+}
+
+pub fn read_u32(address: *u32) Error!u32 {
+ std.log.info("GPFSEL_OFFSET = 0x{X} to 0x{X}, address = 0x{X}", .{ GPFSEL0_OFFSET, GPFSEL0_OFFSET + @sizeOf(u32) * 5, @intFromPtr(address) - base_address });
+
+ switch (@intFromPtr(address) - base_address) {
+ GPFSEL0_OFFSET + @sizeOf(u32) * 0 => return gpfsel0,
+ GPFSEL0_OFFSET + @sizeOf(u32) * 1 => return gpfsel1,
+ GPFSEL0_OFFSET + @sizeOf(u32) * 2 => return gpfsel2,
+ GPFSEL0_OFFSET + @sizeOf(u32) * 3 => return gpfsel3,
+ GPFSEL0_OFFSET + @sizeOf(u32) * 4 => return gpfsel4,
+ GPFSEL0_OFFSET + @sizeOf(u32) * 5 => return gpfsel5,
+
+ GPSET0_OFFSET...(GPSET0_OFFSET + @sizeOf(u32)) => return Error.ReadGPFSEL,
+ GPCLR0_OFFSET...(GPCLR0_OFFSET + @sizeOf(u32)) => return Error.ReadGPCLR,
+
+ GPLEV0_OFFSET...(GPLEV0_OFFSET + @sizeOf(u32)) => return std.crypto.random.int(u32),
+ else => return Error.IllegalRead,
+ }
+}
+
+pub fn put_u32(address: *u32, value: u32) Error!void {
+ switch (@intFromPtr(address) - base_address) {
+ GPFSEL0_OFFSET + @sizeOf(u32) * 0 => gpfsel0 = value,
+ GPFSEL0_OFFSET + @sizeOf(u32) * 1 => gpfsel1 = value,
+ GPFSEL0_OFFSET + @sizeOf(u32) * 2 => gpfsel2 = value,
+ GPFSEL0_OFFSET + @sizeOf(u32) * 3 => gpfsel3 = value,
+ GPFSEL0_OFFSET + @sizeOf(u32) * 4 => gpfsel4 = value,
+ GPFSEL0_OFFSET + @sizeOf(u32) * 5 => gpfsel5 = value,
+
+ GPSET0_OFFSET => gpset0 = value,
+ GPSET0_OFFSET + @sizeOf(u32) => gpset0 = value,
+
+ GPCLR0_OFFSET => gpclr0 = value,
+ GPCLR0_OFFSET + @sizeOf(u32) => gpclr1 = value,
+
+ GPLEV0_OFFSET...(GPLEV0_OFFSET + @sizeOf(u32)) => return Error.WriteGPLEV,
+ else => return Error.IllegalWrite,
+ }
+}
diff --git a/fake-pi/main.zig b/fake-pi/main.zig
@@ -0,0 +1,9 @@
+const std = @import("std");
+const pi = @import("pi");
+
+const root = @import("root.zig");
+
+pub fn main() void {
+ root.initialize();
+ pi.kmain();
+}
diff --git a/fake-pi/root.zig b/fake-pi/root.zig
@@ -0,0 +1,42 @@
+const std = @import("std");
+
+const gpio = @import("devices/gpio.zig");
+
+const BASE_ADDRESS: u32 = 0x2000_0000;
+
+pub fn initialize() void {
+ std.log.info("fake-pi initialized", .{});
+ gpio.initialize(BASE_ADDRESS);
+}
+
+export fn get_u32(address: *u32) u32 {
+ switch (@intFromPtr(address)) {
+ (BASE_ADDRESS + gpio.START_OFFSET)...(BASE_ADDRESS + gpio.END_OFFSET) => {
+ std.log.info("read_u32(0x{X}): gpio subsystem", .{@intFromPtr(address)});
+
+ return gpio.read_u32(address) catch |e| {
+ std.log.err("read_u32 gpio error: {t}", .{e});
+ return std.crypto.random.int(u32);
+ };
+ },
+ else => {
+ std.log.err("read_u32: illegal read", .{});
+ return std.crypto.random.int(u32);
+ },
+ }
+}
+
+export fn put_u32(address: *u32, value: u32) void {
+ switch (@intFromPtr(address)) {
+ (BASE_ADDRESS + gpio.START_OFFSET)...(BASE_ADDRESS + gpio.END_OFFSET) => {
+ std.log.info("put_u32(0x{X}, 0x{X}): gpio subsystem", .{ @intFromPtr(address), value });
+
+ gpio.put_u32(address, value) catch |e| {
+ std.log.err("put_u32 gpio error: {t}", .{e});
+ };
+ },
+ else => {
+ std.log.err("put_u32: illegal write", .{});
+ },
+ }
+}
diff --git a/src/main.zig b/src/main.zig
@@ -1 +1,9 @@
-pub fn main() void {}
+const gpio = @import("devices/gpio.zig");
+
+const BASE_ADDRESS: u32 = 0x2000_0000;
+
+pub fn main() !void {
+ gpio.initialize(BASE_ADDRESS);
+
+ try gpio.set_output(21);
+}
diff --git a/src/root.zig b/src/root.zig
@@ -6,8 +6,8 @@ export fn abort() noreturn {
while (true) {}
}
-export fn main() void {
- _ = fake_main();
+pub export fn kmain() void {
+ _ = fake_main() catch {};
}
fn panic_handler(msg: []const u8, trace_addr: ?usize) noreturn {
diff --git a/src/util.zig b/src/util.zig
@@ -1,19 +1,21 @@
pub extern fn nop() void;
+pub extern fn get_u32(address: *u32) u32;
+pub extern fn put_u32(address: *u32, value: u32) void;
-pub inline fn get_u32(address: *u32) u32 {
- return asm volatile ("ldr %[result], [%[address]]"
- : [result] "=r" (-> u32),
- : [address] "r" (address),
- );
-}
+// 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 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;
diff --git a/start.s b/start.s
@@ -3,7 +3,7 @@
.align 8
_start:
mov sp, 0x800000
- bl main
+ bl kmain
bl abort
.global nop
@@ -11,3 +11,17 @@ _start:
.align 8
nop:
bx lr
+
+.global put_32
+.type _start, function
+.align 8
+put_32:
+ str r1,[r0]
+ bx lr
+
+.global get_32
+.type _start, function
+.align 8
+get_32:
+ ldr r0,[r0]
+ bx lr