sylveos

Toy Operating System
Log | Files | Refs

register.zig (11227B)


      1 // Referenced based off of Swift MMIO
      2 const std = @import("std");
      3 
      4 const mem = @import("./mem.zig");
      5 const Error = error{OutOfRange};
      6 
      7 const Kind = enum {
      8     // Only read methods
      9     ReadOnly,
     10     // Only write methods
     11     WriteOnly,
     12     // Read, write, mutate methods
     13     ReadModifyWrite,
     14 };
     15 
     16 // e.g. clock COUNTER_LOWER
     17 pub fn Register(comptime T: type, comptime kind: Kind) type {
     18     comptime if (@sizeOf(T) != @sizeOf(usize)) @compileError("expected type as large as usize");
     19 
     20     const RegisterBase = struct {
     21         const Self = @This();
     22 
     23         inline fn get(addr_ptr: *volatile usize) T {
     24             return @bitCast(mem.get_u32(@volatileCast(addr_ptr)));
     25         }
     26 
     27         inline fn set(addr_ptr: *volatile usize, value: T) void {
     28             return mem.put_u32(@volatileCast(addr_ptr), @bitCast(value));
     29         }
     30     };
     31 
     32     switch (kind) {
     33         .ReadOnly => {
     34             return struct {
     35                 address: *volatile usize,
     36 
     37                 const Self = @This();
     38 
     39                 pub inline fn init(address: usize) Self {
     40                     return .{ .address = @ptrFromInt(address) };
     41                 }
     42 
     43                 pub inline fn get(self: *const Self) T {
     44                     return RegisterBase.get(self.address);
     45                 }
     46             };
     47         },
     48         .WriteOnly => {
     49             return struct {
     50                 address: *volatile usize,
     51 
     52                 const Self = @This();
     53 
     54                 pub inline fn init(address: usize) Self {
     55                     return .{ .address = @ptrFromInt(address) };
     56                 }
     57 
     58                 pub inline fn set(self: *const Self, value: T) void {
     59                     RegisterBase.set(self.address, value);
     60                 }
     61             };
     62         },
     63         .ReadModifyWrite => {
     64             return struct {
     65                 address: *volatile usize,
     66 
     67                 const Self = @This();
     68 
     69                 pub inline fn init(address: usize) Self {
     70                     return .{ .address = @ptrFromInt(address) };
     71                 }
     72 
     73                 pub inline fn get(self: *const Self) T {
     74                     return RegisterBase.get(self.address);
     75                 }
     76 
     77                 pub inline fn set(self: *const Self, value: T) void {
     78                     return RegisterBase.set(self.address, value);
     79                 }
     80 
     81                 pub inline fn bor(self: *const Self, value: usize) void {
     82                     const original = self.address.*;
     83 
     84                     self.address.* = original | value;
     85                 }
     86 
     87                 pub inline fn band(self: *const Self, value: usize) void {
     88                     const original = self.address.*;
     89 
     90                     self.address.* = original & value;
     91                 }
     92             };
     93         },
     94     }
     95 }
     96 
     97 // These tests should be non-issues since Register is simple
     98 test "Register read" {
     99     var value: usize = 617;
    100     const value_register: Register(usize, .ReadOnly) = .init(@intFromPtr(&value));
    101 
    102     try std.testing.expectEqual(value_register.get(), value);
    103 }
    104 
    105 test "Register write" {
    106     var value: usize = 0;
    107     const value_register: Register(usize, .WriteOnly) = .init(@intFromPtr(&value));
    108 
    109     value_register.set(617);
    110 
    111     try std.testing.expectEqual(value, 617);
    112 }
    113 
    114 test "Register bor" {
    115     var value: usize = 0b1111_0000;
    116     const value_register: Register(usize, .ReadModifyWrite) = .init(@intFromPtr(&value));
    117 
    118     value_register.bor(0b1111);
    119 
    120     try std.testing.expectEqual(value, 0b1111_1111);
    121 }
    122 
    123 test "Register band" {
    124     var value: usize = 0b1111_0000;
    125     const value_register: Register(usize, .ReadModifyWrite) = .init(@intFromPtr(&value));
    126 
    127     value_register.band(0b1001_1111);
    128 
    129     try std.testing.expectEqual(value, 0b1001_0000);
    130 }
    131 
    132 // e.g. gpio GPFSEL
    133 pub fn Array(comptime T: type, length: comptime_int, comptime kind: Kind) type {
    134     comptime if (@sizeOf(T) > @sizeOf(usize)) @compileError("cannot store elements larger than usize");
    135     const elem_mask: usize = comptime (~@as(usize, 0)) >> (@bitSizeOf(usize) - @bitSizeOf(T));
    136     const ValueT = switch (@sizeOf(T)) {
    137         0...8 => u8,
    138         9...16 => u16,
    139         17...32 => u32,
    140         33...64 => u64,
    141         // maybe you're on a 128 bit platform
    142         else => usize,
    143     };
    144 
    145     const ArrayBase = struct {
    146         const Self = @This();
    147 
    148         inline fn calculate_shift(index: usize) usize {
    149             return @bitSizeOf(T) * (index % @bitSizeOf(T));
    150         }
    151 
    152         inline fn calculate_address(start: usize, index: usize) *volatile usize {
    153             const ptr_offset = @divFloor(index, @bitSizeOf(T)) * @sizeOf(usize);
    154             const address: *volatile usize = @ptrFromInt(start + ptr_offset);
    155             return address;
    156         }
    157 
    158         inline fn calculate_mask(index: usize) usize {
    159             return elem_mask << @truncate(Self.calculate_shift(index));
    160         }
    161 
    162         inline fn calculate_put_mask(index: usize, value: T) usize {
    163             const value_t: ValueT = @bitCast(value);
    164             const masked = value_t & elem_mask;
    165 
    166             return masked << @truncate(Self.calculate_shift(index));
    167         }
    168 
    169         inline fn get(start: usize, index: usize) !T {
    170             if (index > length) return Error.OutOfRange;
    171 
    172             const address = Self.calculate_address(start, index);
    173             const mask = Self.calculate_mask(index);
    174 
    175             const masked = (address.*) & mask;
    176             const value: ValueT = @truncate(masked >> Self.calculate_shift(index));
    177 
    178             return @bitCast(value);
    179         }
    180 
    181         inline fn put(start: usize, index: usize, value: T) !void {
    182             if (index > length) return Error.OutOfRange;
    183 
    184             const address = Self.calculate_address(start, index);
    185             const masked = Self.calculate_put_mask(index, value);
    186 
    187             address.* = masked;
    188         }
    189     };
    190 
    191     switch (kind) {
    192         .ReadOnly => {
    193             return struct {
    194                 start: usize,
    195 
    196                 const Self = @This();
    197 
    198                 pub inline fn init(address: usize) Self {
    199                     return .{ .start = address };
    200                 }
    201 
    202                 pub inline fn get(self: *const Self, index: usize) !T {
    203                     return ArrayBase.get(self.start, index);
    204                 }
    205             };
    206         },
    207         .WriteOnly => {
    208             return struct {
    209                 start: usize,
    210 
    211                 const Self = @This();
    212 
    213                 pub inline fn init(address: usize) Self {
    214                     return .{ .start = address };
    215                 }
    216 
    217                 pub inline fn put(self: *const Self, index: usize, value: T) !void {
    218                     return ArrayBase.put(self.start, index, value);
    219                 }
    220             };
    221         },
    222         .ReadModifyWrite => {
    223             return struct {
    224                 start: usize,
    225 
    226                 const Self = @This();
    227 
    228                 pub inline fn init(address: usize) Self {
    229                     return .{ .start = address };
    230                 }
    231 
    232                 pub inline fn get(self: *const Self, index: usize) !T {
    233                     return ArrayBase.get(self.start, index);
    234                 }
    235 
    236                 pub inline fn put(self: *const Self, index: usize, value: T) !void {
    237                     return ArrayBase.put(self.start, index, value);
    238                 }
    239 
    240                 pub inline fn insert(self: *const Self, index: usize, value: T) !void {
    241                     const address = ArrayBase.calculate_address(self.start, index);
    242                     const original = address.*;
    243                     const masked = ArrayBase.calculate_put_mask(index, value);
    244                     const mask = ArrayBase.calculate_mask(index);
    245 
    246                     // Remove then insert
    247                     address.* = (original & ~mask) | masked;
    248                 }
    249 
    250                 pub inline fn remove(self: *const Self, index: usize) !void {
    251                     self.insert(index, 0);
    252                 }
    253             };
    254         },
    255     }
    256 }
    257 
    258 test "Array read" {
    259     // Note; this will not work on non-64 bit
    260     const array: [2]usize = .{
    261         0xFFEEDDCCBBAA9988,
    262         0x7766554433221100,
    263     };
    264 
    265     const arr: Array(u8, 2 * 8, .ReadOnly) = .init(@intFromPtr(&array));
    266 
    267     try std.testing.expectEqual(0x88, try arr.get(0));
    268     try std.testing.expectEqual(0x99, try arr.get(1));
    269     try std.testing.expectEqual(0xAA, try arr.get(2));
    270     try std.testing.expectEqual(0xBB, try arr.get(3));
    271     try std.testing.expectEqual(0xCC, try arr.get(4));
    272     try std.testing.expectEqual(0xDD, try arr.get(5));
    273     try std.testing.expectEqual(0xEE, try arr.get(6));
    274     try std.testing.expectEqual(0xFF, try arr.get(7));
    275     try std.testing.expectEqual(0x00, try arr.get(8));
    276     try std.testing.expectEqual(0x11, try arr.get(9));
    277     try std.testing.expectEqual(0x22, try arr.get(10));
    278     try std.testing.expectEqual(0x33, try arr.get(11));
    279     try std.testing.expectEqual(0x44, try arr.get(12));
    280     try std.testing.expectEqual(0x55, try arr.get(13));
    281     try std.testing.expectEqual(0x66, try arr.get(14));
    282     try std.testing.expectEqual(0x77, try arr.get(15));
    283 }
    284 
    285 test "Array write" {
    286     var array: [2]usize = .{ 0, 0 };
    287 
    288     const arr: Array(u8, 2 * 8, .WriteOnly) = .init(@intFromPtr(&array));
    289 
    290     try arr.put(0, 0x88);
    291     try std.testing.expectEqual(0x88, array[0]);
    292     try std.testing.expectEqual(0, array[1]);
    293 
    294     try arr.put(1, 0x99);
    295     try std.testing.expectEqual(0x9900, array[0]);
    296     try std.testing.expectEqual(0, array[1]);
    297 
    298     try arr.put(2, 0xAA);
    299     try std.testing.expectEqual(0xAA0000, array[0]);
    300     try std.testing.expectEqual(0, array[1]);
    301 
    302     try arr.put(3, 0xBB);
    303     try std.testing.expectEqual(0xBB000000, array[0]);
    304     try std.testing.expectEqual(0, array[1]);
    305 
    306     try arr.put(4, 0xCC);
    307     try std.testing.expectEqual(0xCC00000000, array[0]);
    308     try std.testing.expectEqual(0, array[1]);
    309 
    310     try arr.put(9, 0x11);
    311     try std.testing.expectEqual(0xCC00000000, array[0]);
    312     try std.testing.expectEqual(0x0000001100, array[1]);
    313 
    314     try arr.put(10, 0x22);
    315     try std.testing.expectEqual(0xCC00000000, array[0]);
    316     try std.testing.expectEqual(0x0000220000, array[1]);
    317 }
    318 
    319 test "Array insert" {
    320     var array: [2]usize = .{ 0, 0 };
    321 
    322     const arr: Array(u8, 2 * 8, .ReadModifyWrite) = .init(@intFromPtr(&array));
    323 
    324     try arr.insert(0, 0x88);
    325     try std.testing.expectEqual(0x88, array[0]);
    326     try std.testing.expectEqual(0, array[1]);
    327 
    328     try arr.insert(1, 0x99);
    329     try std.testing.expectEqual(0x9988, array[0]);
    330     try std.testing.expectEqual(0, array[1]);
    331 
    332     try arr.insert(0, 0x99);
    333     try std.testing.expectEqual(0x9999, array[0]);
    334     try std.testing.expectEqual(0, array[1]);
    335 
    336     try arr.insert(0, 0x88);
    337     try std.testing.expectEqual(0x9988, array[0]);
    338     try std.testing.expectEqual(0, array[1]);
    339 
    340     try arr.insert(3, 0xBB);
    341     try std.testing.expectEqual(0xBB009988, array[0]);
    342     try std.testing.expectEqual(0, array[1]);
    343 
    344     try arr.insert(10, 0x22);
    345     try std.testing.expectEqual(0x00BB009988, array[0]);
    346     try std.testing.expectEqual(0x0000220000, array[1]);
    347 }
    348 
    349 // e.g. gpio GPCLR
    350 pub fn BitField(start: usize, length: comptime_int, comptime kind: Kind) type {
    351     return Array(start, u1, length, kind);
    352 }
    353 
    354 // If Array is correct, then BitField is also correct