commit ca03d15828ed04f363d82bd4169d4927cf81f7cb
parent b9bb1a3d004151f13cba3a17489e76cf4ab05712
Author: Sylvia Ivory <git@sivory.net>
Date: Thu, 15 Jan 2026 18:46:15 -0800
Add fake-random
Diffstat:
10 files changed, 562 insertions(+), 16 deletions(-)
diff --git a/build.zig b/build.zig
@@ -13,18 +13,27 @@ fn add_exe_fake_pi(exe_name: []const u8, path: std.Build.LazyPath, b: *std.Build
const fake_pi = b.createModule(.{
.root_source_file = b.path("fake-pi/main.zig"),
.target = b.graph.host,
- .optimize = .Debug,
+ .optimize = .ReleaseFast,
+ .link_libc = true,
});
fake_pi.addImport("boot", boot);
+ // TODO; C should get its own module so we can have fake-pi be in debug for faster comp
+ const CFlags = &.{"-O2"};
+ fake_pi.addCSourceFile(.{ .file = b.path("include/fake-random.c"), .flags = CFlags });
+ fake_pi.addCSourceFile(.{ .file = b.path("include/pi-random.c"), .flags = CFlags });
+ fake_pi.addIncludePath(b.path("include/"));
+
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,
+ .linkage = .dynamic,
.root_module = fake_pi,
+ .use_lld = true,
+ .use_llvm = true,
});
b.installArtifact(exe);
diff --git a/fake-pi/devices/gpio.zig b/fake-pi/devices/gpio.zig
@@ -1,6 +1,9 @@
const std = @import("std");
const device = @import("../device.zig");
+const c = @cImport({
+ @cInclude("fake-random.h");
+});
// TODO; these constants should come from src/devices/gpio.zig
pub const START_OFFSET: usize = 0x0020_0000;
@@ -36,6 +39,19 @@ pub const Error = error{
pub fn initialize(base_addr: u32) void {
base_address = base_addr;
+
+ gpfsel0 = c.fake_random();
+ gpfsel1 = c.fake_random();
+ gpfsel2 = c.fake_random();
+ gpfsel3 = c.fake_random();
+ gpfsel4 = gpfsel3; // Extend bits
+ gpfsel5 = gpfsel3;
+
+ gpset0 = c.fake_random();
+ gpset1 = gpset0;
+
+ gpclr0 = c.fake_random();
+ gpclr1 = gpclr0;
}
pub fn read_u32(address: *u32) Error!u32 {
@@ -50,7 +66,7 @@ pub fn read_u32(address: *u32) Error!u32 {
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),
+ GPLEV0_OFFSET...(GPLEV0_OFFSET + @sizeOf(u32)) => return c.fake_random(),
else => return Error.IllegalRead,
}
}
diff --git a/fake-pi/main.zig b/fake-pi/main.zig
@@ -6,4 +6,5 @@ const root = @import("root.zig");
pub fn main() void {
root.initialize();
boot.kmain();
+ root.finalize();
}
diff --git a/fake-pi/root.zig b/fake-pi/root.zig
@@ -1,36 +1,77 @@
const std = @import("std");
const gpio = @import("devices/gpio.zig");
+const c = @cImport({
+ @cInclude("fake-random.h");
+});
const BASE_ADDRESS: u32 = 0x2000_0000;
+var trace_count: usize = 0;
+
+fn trace_no_count(comptime format: []const u8, args: anytype) void {
+ var stdout_buffer: [4096]u8 = undefined;
+ var stdout = std.fs.File.stdout().writer(&stdout_buffer);
+ const writer = &stdout.interface;
+
+ writer.print("TRACE: " ++ format, args) catch |e| {
+ std.log.err("trace() -> {t}", .{e});
+ };
+ writer.flush() catch |e| {
+ std.log.err("trace() -> {t}", .{e});
+ };
+}
+
+fn trace(comptime format: []const u8, args: anytype) void {
+ var stdout_buffer: [4096]u8 = undefined;
+ var stdout = std.fs.File.stdout().writer(&stdout_buffer);
+ const writer = &stdout.interface;
+
+ writer.print("TRACE:{}: ", .{trace_count}) catch |e| {
+ std.log.err("trace() -> {t}", .{e});
+ };
+ writer.print(format, args) catch |e| {
+ std.log.err("trace() -> {t}", .{e});
+ };
+ writer.flush() catch |e| {
+ std.log.err("trace() -> {t}", .{e});
+ };
+
+ trace_count += 1;
+}
pub fn initialize() void {
- std.log.info("fake-pi initialized", .{});
+ c.fake_random_init();
gpio.initialize(BASE_ADDRESS);
+
+ trace("calling pi code\n", .{});
+}
+
+pub fn finalize() void {
+ trace_no_count("pi exited cleanly: {} calls to random\n", .{c.fake_random_calls()});
}
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| {
+ const value = gpio.read_u32(address) catch |e| {
std.log.err("read_u32 gpio error: {t}", .{e});
- return std.crypto.random.int(u32);
+ return c.fake_random();
};
+
+ trace("GET32(0x{x}) = 0x{x}\n", .{ @intFromPtr(address), value });
+ return value;
},
else => {
std.log.err("read_u32: illegal read", .{});
- return std.crypto.random.int(u32);
+ return c.fake_random();
},
}
}
export fn put_u32(address: *u32, value: u32) void {
+ trace("PUT32(0x{x}) = 0x{x}\n", .{ @intFromPtr(address), value });
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});
};
diff --git a/include/fake-random.c b/include/fake-random.c
@@ -0,0 +1,46 @@
+// wrapper for the our local random() implementation (in pi-random.c).
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "fake-random.h"
+#include "pi-random.h"
+
+
+
+#define STATESIZE 128
+static int seed = 0;
+static char statebuf[STATESIZE];
+static struct pi_random_data r;
+static int init_p = 0;
+
+void fake_random_seed(unsigned x) {
+ init_p = 1;
+ memset(&r, 0, sizeof r);
+ if(pi_initstate_r(seed, statebuf, STATESIZE, &r))
+ assert(0);
+ if(pi_srandom_r(seed, &r))
+ assert(0);
+}
+
+// make sure that everyone has the same random.
+void fake_random_init(void) {
+ fake_random_seed(0x140e);
+ unsigned u = fake_random();
+ assert(0x6b8b4567 == u);
+}
+
+static unsigned nrandom_calls;
+unsigned fake_random_calls(void) {
+ return nrandom_calls;
+}
+
+unsigned (fake_random)(void) {
+ assert(init_p);
+
+ nrandom_calls++;
+ int x;
+ if(pi_random_r(&r, &x))
+ assert(0);
+ return x;
+}
diff --git a/include/fake-random.h b/include/fake-random.h
@@ -0,0 +1,22 @@
+#ifndef __FAKE_PI_H__
+#define __FAKE_PI_H__
+
+#define random() "do not call random() directly"
+
+// prototypes for our fake_random implementation
+
+// must call this first: used to check that everyone
+// has the same seed.
+void fake_random_init(void);
+
+// call to change seed.
+void fake_random_seed(unsigned x);
+
+// total number of random calls (can be used to debug
+// your fake implementation).
+unsigned fake_random_calls(void);
+
+// call this to get a 32-bit pseudo-random number.
+unsigned fake_random(void);
+
+#endif
diff --git a/include/pi-random.c b/include/pi-random.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * This is derived from the Berkeley source:
+ * @(#)random.c 5.5 (Berkeley) 7/6/88
+ * It was reworked for the GNU C Library by Roland McGrath.
+ * Rewritten to be reentrant by Ulrich Drepper, 1995
+ */
+
+#include <stddef.h>
+#include "pi-random.h"
+
+
+/* An improved random number generation package. In addition to the standard
+ rand()/srand() like interface, this package also has a special state info
+ interface. The initstate() routine is called with a seed, an array of
+ bytes, and a count of how many bytes are being passed in; this array is
+ then initialized to contain information for random number generation with
+ that much state information. Good sizes for the amount of state
+ information are 32, 64, 128, and 256 bytes. The state can be switched by
+ calling the setstate() function with the same array as was initialized
+ with initstate(). By default, the package runs with 128 bytes of state
+ information and generates far better random numbers than a linear
+ congruential generator. If the amount of state information is less than
+ 32 bytes, a simple linear congruential R.N.G. is used. Internally, the
+ state information is treated as an array of longs; the zeroth element of
+ the array is the type of R.N.G. being used (small integer); the remainder
+ of the array is the state information for the R.N.G. Thus, 32 bytes of
+ state information will give 7 longs worth of state information, which will
+ allow a degree seven polynomial. (Note: The zeroth word of state
+ information also has some other information stored in it; see setstate
+ for details). The random number generation technique is a linear feedback
+ shift register approach, employing trinomials (since there are fewer terms
+ to sum up that way). In this approach, the least significant bit of all
+ the numbers in the state table will act as a linear feedback shift register,
+ and will have period 2^deg - 1 (where deg is the degree of the polynomial
+ being used, assuming that the polynomial is irreducible and primitive).
+ The higher order bits will have longer periods, since their values are
+ also influenced by pseudo-random carries out of the lower bits. The
+ total period of the generator is approximately deg*(2**deg - 1); thus
+ doubling the amount of state information has a vast influence on the
+ period of the generator. Note: The deg*(2**deg - 1) is an approximation
+ only good for large deg, when the period of the shift register is the
+ dominant factor. With deg equal to seven, the period is actually much
+ longer than the 7*(2**7 - 1) predicted by this formula. */
+
+
+
+/* For each of the currently supported random number generators, we have a
+ break value on the amount of state information (you need at least this many
+ bytes of state info to support this random number generator), a degree for
+ the polynomial (actually a trinomial) that the R.N.G. is based on, and
+ separation between the two lower order coefficients of the trinomial. */
+
+/* Linear congruential. */
+#define TYPE_0 0
+#define BREAK_0 8
+#define DEG_0 0
+#define SEP_0 0
+
+/* x**7 + x**3 + 1. */
+#define TYPE_1 1
+#define BREAK_1 32
+#define DEG_1 7
+#define SEP_1 3
+
+/* x**15 + x + 1. */
+#define TYPE_2 2
+#define BREAK_2 64
+#define DEG_2 15
+#define SEP_2 1
+
+/* x**31 + x**3 + 1. */
+#define TYPE_3 3
+#define BREAK_3 128
+#define DEG_3 31
+#define SEP_3 3
+
+/* x**63 + x + 1. */
+#define TYPE_4 4
+#define BREAK_4 256
+#define DEG_4 63
+#define SEP_4 1
+
+
+/* Array versions of the above information to make code run faster.
+ Relies on fact that TYPE_i == i. */
+
+#define MAX_TYPES 5 /* Max number of types above. */
+
+struct random_poly_info
+{
+ int seps[MAX_TYPES];
+ int degrees[MAX_TYPES];
+};
+
+static const struct random_poly_info random_poly_info =
+{
+ { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 },
+ { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 }
+};
+
+
+
+
+/* If we are using the trivial TYPE_0 R.N.G., just do the old linear
+ congruential bit. Otherwise, we do our fancy trinomial stuff, which is the
+ same in all the other cases due to all the global variables that have been
+ set up. The basic operation is to add the number at the rear pointer into
+ the one at the front pointer. Then both pointers are advanced to the next
+ location cyclically in the table. The value returned is the sum generated,
+ reduced to 31 bits by throwing away the "least random" low bit.
+ Note: The code takes advantage of the fact that both the front and
+ rear pointers can't wrap on the same call by not testing the rear
+ pointer if the front one has wrapped. Returns a 31-bit random number. */
+int pi_random_r(struct pi_random_data *buf, int32_t *result)
+{
+ int32_t *state;
+
+ if (buf == NULL || result == NULL)
+ goto fail;
+
+ state = buf->state;
+
+ if (buf->rand_type == TYPE_0)
+ {
+ int32_t val = state[0];
+ val = ((state[0] * 1103515245) + 12345) & 0x7fffffff;
+ state[0] = val;
+ *result = val;
+ }
+ else
+ {
+ int32_t *fptr = buf->fptr;
+ int32_t *rptr = buf->rptr;
+ int32_t *end_ptr = buf->end_ptr;
+ int32_t val;
+
+ val = *fptr += *rptr;
+ /* Chucking least random bit. */
+ *result = (val >> 1) & 0x7fffffff;
+ ++fptr;
+ if (fptr >= end_ptr)
+ {
+ fptr = state;
+ ++rptr;
+ }
+ else
+ {
+ ++rptr;
+ if (rptr >= end_ptr)
+ rptr = state;
+ }
+ buf->fptr = fptr;
+ buf->rptr = rptr;
+ }
+ return 0;
+
+fail:
+ return -1;
+}
+
+/* Initialize the random number generator based on the given seed. If the
+ type is the trivial no-state-information type, just remember the seed.
+ Otherwise, initializes state[] based on the given "seed" via a linear
+ congruential generator. Then, the pointers are set to known locations
+ that are exactly rand_sep places apart. Lastly, it cycles the state
+ information a given number of times to get rid of any initial dependencies
+ introduced by the L.C.R.N.G. Note that the initialization of randtbl[]
+ for default usage relies on values produced by this routine. */
+int pi_srandom_r (unsigned int seed, struct pi_random_data *buf) {
+ int type;
+ int32_t *state;
+ long int i;
+ long int word;
+ int32_t *dst;
+ int kc;
+
+ if (buf == NULL)
+ goto fail;
+ type = buf->rand_type;
+ if ((unsigned int) type >= MAX_TYPES)
+ goto fail;
+
+ state = buf->state;
+ /* We must make sure the seed is not 0. Take arbitrarily 1 in this case. */
+ if (seed == 0)
+ seed = 1;
+ state[0] = seed;
+ if (type == TYPE_0)
+ goto done;
+
+ dst = state;
+ word = seed;
+ kc = buf->rand_deg;
+ for (i = 1; i < kc; ++i)
+ {
+ /* This does:
+ state[i] = (16807 * state[i - 1]) % 2147483647;
+ but avoids overflowing 31 bits. */
+ long int hi = word / 127773;
+ long int lo = word % 127773;
+ word = 16807 * lo - 2836 * hi;
+ if (word < 0)
+ word += 2147483647;
+ *++dst = word;
+ }
+
+ buf->fptr = &state[buf->rand_sep];
+ buf->rptr = &state[0];
+ kc *= 10;
+ while (--kc >= 0)
+ {
+ int32_t discard;
+ (void) pi_random_r (buf, &discard);
+ }
+
+done:
+ return 0;
+
+fail:
+ return -1;
+}
+
+/* Initialize the state information in the given array of N bytes for
+ future random number generation. Based on the number of bytes we
+ are given, and the break values for the different R.N.G.'s, we choose
+ the best (largest) one we can and set things up for it. srandom is
+ then called to initialize the state information. Note that on return
+ from srandom, we set state[-1] to be the type multiplexed with the current
+ value of the rear pointer; this is so successive calls to initstate won't
+ lose this information and will be able to restart with setstate.
+ Note: The first thing we do is save the current state, if any, just like
+ setstate so that it doesn't matter when initstate is called.
+ Returns a pointer to the old state. */
+int pi_initstate_r (unsigned int seed, char *arg_state, size_t n, struct pi_random_data *buf)
+{
+ int type;
+ int degree;
+ int separation;
+ int32_t *state;
+
+ if (buf == NULL)
+ goto fail;
+
+ if (n >= BREAK_3)
+ type = n < BREAK_4 ? TYPE_3 : TYPE_4;
+ else if (n < BREAK_1)
+ {
+ if (n < BREAK_0)
+ {
+ goto fail;
+ }
+ type = TYPE_0;
+ }
+ else
+ type = n < BREAK_2 ? TYPE_1 : TYPE_2;
+
+ degree = random_poly_info.degrees[type];
+ separation = random_poly_info.seps[type];
+
+ buf->rand_type = type;
+ buf->rand_sep = separation;
+ buf->rand_deg = degree;
+ state = &((int32_t *) arg_state)[1]; /* First location. */
+ /* Must set END_PTR before srandom. */
+ buf->end_ptr = &state[degree];
+
+ buf->state = state;
+
+ pi_srandom_r (seed, buf);
+
+ state[-1] = TYPE_0;
+ if (type != TYPE_0)
+ state[-1] = (buf->rptr - state) * MAX_TYPES + type;
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+/* Restore the state from the given state array.
+ Note: It is important that we also remember the locations of the pointers
+ in the current state information, and restore the locations of the pointers
+ from the old state information. This is done by multiplexing the pointer
+ location into the zeroth word of the state information. Note that due
+ to the order in which things are done, it is OK to call setstate with the
+ same state as the current state
+ Returns a pointer to the old state information. */
+int pi_setstate_r (char *arg_state, struct pi_random_data *buf)
+{
+ int32_t *new_state = 1 + (int32_t *) arg_state;
+ int type;
+ int old_type;
+ int32_t *old_state;
+ int degree;
+ int separation;
+
+ if (arg_state == NULL || buf == NULL)
+ goto fail;
+
+ old_type = buf->rand_type;
+ old_state = buf->state;
+ if (old_type == TYPE_0)
+ old_state[-1] = TYPE_0;
+ else
+ old_state[-1] = (MAX_TYPES * (buf->rptr - old_state)) + old_type;
+
+ type = new_state[-1] % MAX_TYPES;
+ if (type < TYPE_0 || type > TYPE_4)
+ goto fail;
+
+ buf->rand_deg = degree = random_poly_info.degrees[type];
+ buf->rand_sep = separation = random_poly_info.seps[type];
+ buf->rand_type = type;
+
+ if (type != TYPE_0)
+ {
+ int rear = new_state[-1] / MAX_TYPES;
+ buf->rptr = &new_state[rear];
+ buf->fptr = &new_state[(rear + separation) % degree];
+ }
+ buf->state = new_state;
+ /* Set end_ptr too. */
+ buf->end_ptr = &new_state[degree];
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+
+#ifdef TEST
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+
+#define STATESIZE 128
+
+// we are not guaranteed that the libc implement of random is the same.
+// we provide our own.
+
+static void print_random(struct pi_random_data *r, unsigned n) {
+ for(int i = 0; i < n; i++) {
+ int x;
+ if(pi_random_r(r, &x))
+ assert(0);
+ printf("x=%x, %d\n", x,x%1024);
+ }
+}
+
+int main() {
+ int seed = 0;
+ char statebuf[STATESIZE];
+ struct pi_random_data r;
+
+ memset(&r, 0, sizeof r);
+ if(pi_initstate_r(seed, statebuf, STATESIZE, &r))
+ assert(0);
+ if(pi_srandom_r(seed, &r))
+ assert(0);
+
+ // should be same.
+ print_random(&r, 8);
+ pi_srandom_r(seed, &r);
+ print_random(&r, 8);
+
+ return 0;
+}
+#endif
diff --git a/include/pi-random.h b/include/pi-random.h
@@ -0,0 +1,23 @@
+#ifndef __PI_RANDOM_H__
+#define __PI_RANDOM_H__
+
+#include <stdint.h>
+#include <stddef.h>
+
+struct pi_random_data
+ {
+ int32_t *fptr; /* Front pointer. */
+ int32_t *rptr; /* Rear pointer. */
+ int32_t *state; /* Array of state values. */
+ int rand_type; /* Type of random number generator. */
+ int rand_deg; /* Degree of random number generator. */
+ int rand_sep; /* Distance between front and rear. */
+ int32_t *end_ptr; /* Pointer behind state table. */
+ };
+
+int pi_random_r(struct pi_random_data *buf, int32_t *result);
+int pi_srandom_r (unsigned int seed, struct pi_random_data *buf);
+int pi_initstate_r (unsigned int seed, char *arg_state, size_t n, struct pi_random_data *buf);
+int pi_setstate_r (char *arg_state, struct pi_random_data *buf);
+
+#endif
diff --git a/src/devices/gpio.zig b/src/devices/gpio.zig
@@ -100,7 +100,7 @@ pub fn set_off(pin: u8) Error!void {
}
pub fn read(pin: u8) Error!bool {
- const address = try get_address(pin, GPLEV0_OFFSET, GPIO_PER_FSEL);
+ const address = try get_address(pin, GPLEV0_OFFSET, null);
const offset: u5 = @truncate(pin % 32);
const mask = (@as(u32, 1) << offset);
diff --git a/src/main.zig b/src/main.zig
@@ -1,9 +1,7 @@
const gpio = @import("devices/gpio.zig");
-const BASE_ADDRESS: u32 = 0x2000_0000;
-
pub fn main() !void {
- gpio.initialize(BASE_ADDRESS);
+ gpio.initialize(0x2000_0000);
- try gpio.set_output(21);
+ _ = try gpio.read(17);
}