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