Skip to content

Commit c4a988b

Browse files
committed
redo mmio operations
1 parent d16c3d3 commit c4a988b

File tree

1 file changed

+95
-117
lines changed

1 file changed

+95
-117
lines changed

core/src/mmio.zig

Lines changed: 95 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,12 @@ pub const Access = struct {
5555
pub const reserved: @This() = .{ .read = .garbage, .write = .ignored };
5656
};
5757

58-
/// If a field is not null, it contains the name of one of the registers
59-
/// that prevent this capability, so that a nice error message can be displayed.
60-
const Capabilities = struct {
61-
/// If null, register can be read from.
62-
read: ?[:0]const u8 = null,
63-
/// If null, register can be written to.
64-
write: ?[:0]const u8 = null,
65-
/// If null, register can have bits set by a write operation.
66-
set_mask: ?[:0]const u8 = null,
67-
/// If null, register can have bits cleared by a write operation.
68-
clear_mask: ?[:0]const u8 = null,
69-
/// If null, register can have bits toggled by a write operation.
70-
toggle_mask: ?[:0]const u8 = null,
71-
};
58+
fn check_type_has_all_fields(T: type, fields: anytype) void {
59+
inline for (@typeInfo(@TypeOf(fields)).@"struct".fields) |field| {
60+
if (!@hasField(T, field.name))
61+
@compileError("Field " ++ field.name ++ " not present in " ++ @typeName(T));
62+
}
63+
}
7264

7365
pub fn MmioAccess(comptime PackedT: type) type {
7466
@setEvalBranchQuota(20_000);
@@ -119,59 +111,24 @@ pub fn Mmio(comptime PackedT: type, access_type: MmioAccess(PackedT)) type {
119111
raw: IntT,
120112

121113
pub const underlying_type = PackedT;
122-
const capabilities: Capabilities = blk: {
123-
var ret: Capabilities = .{};
124-
for (reg_fields) |field| {
125-
const a: Access = @field(access_type, field.name);
126-
switch (a.read) {
127-
.normal, .garbage => {},
128-
.special, .illegal => ret.read = field.name,
129-
}
130-
switch (a.write) {
131-
.normal => {
132-
ret.set_mask = field.name;
133-
ret.clear_mask = field.name;
134-
ret.toggle_mask = field.name;
135-
},
136-
.ignored => {},
137-
.set_mask => {
138-
ret.write = field.name;
139-
ret.clear_mask = field.name;
140-
ret.toggle_mask = field.name;
141-
},
142-
.clear_mask => {
143-
ret.write = field.name;
144-
ret.set_mask = field.name;
145-
ret.toggle_mask = field.name;
146-
},
147-
.toggle_mask => {
148-
ret.write = field.name;
149-
ret.set_mask = field.name;
150-
ret.clear_mask = field.name;
151-
},
152-
.special, .illegal => {
153-
ret.write = field.name;
154-
ret.set_mask = field.name;
155-
ret.clear_mask = field.name;
156-
ret.toggle_mask = field.name;
157-
},
158-
}
159-
break :blk ret;
160-
}
161-
};
114+
const all_zeros: PackedT = @bitCast(@as(IntT, 0));
162115

163116
pub inline fn read(self: *volatile @This()) PackedT {
164-
if (capabilities.read) |name|
165-
reg_type_op_error(name, "reading from the register");
166-
117+
comptime for (reg_fields) |field|
118+
switch (@field(access_type, field.name).read) {
119+
.normal, .garbage, .special => {},
120+
.illegal => reg_type_op_error(field.name, "reading from any fields of the register"),
121+
};
167122
return @bitCast(self.raw);
168123
}
169124

170-
pub inline fn write(self: *volatile @This(), val: PackedT) void {
171-
if (capabilities.write) |name|
172-
reg_type_op_error(name, "writing to the register");
173-
174-
self.raw = @bitCast(val);
125+
pub inline fn write(self: *volatile @This(), w: PackedT) void {
126+
comptime for (reg_fields) |field|
127+
switch (@field(access_type, field.name).write) {
128+
.normal, .ignored, .set_mask, .clear_mask, .toggle_mask, .special => {},
129+
.illegal => reg_type_op_error(field.name, "writing to any fields of the register"),
130+
};
131+
self.raw = @bitCast(w);
175132
}
176133

177134
/// Set field `field_name` of this register to `value`.
@@ -181,74 +138,95 @@ pub fn Mmio(comptime PackedT: type, access_type: MmioAccess(PackedT)) type {
181138
comptime field_name: []const u8,
182139
value: @FieldType(underlying_type, field_name),
183140
) void {
184-
if (capabilities.read) |name|
185-
reg_type_op_error(name, "modifying this register by read-modify-write");
186-
if (capabilities.write) |name|
187-
reg_type_op_error(name, "modifying this register by read-modify-write");
188-
189-
if (@field(access_type, field_name).write != .normal)
190-
reg_type_op_error(field_name, "modifying this field by read-modify-write");
191-
192-
var val: PackedT = @bitCast(self.raw);
193-
@field(val, field_name) = value;
194-
self.raw = @bitCast(val);
141+
// Replace with @Struct when migrating to zig 0.16
142+
var fields: @import("core/usb.zig").Struct(
143+
.auto,
144+
null,
145+
&.{field_name},
146+
&.{@TypeOf(value)},
147+
&.{.{}},
148+
) = undefined;
149+
@field(fields, field_name) = value;
150+
self.modify(fields);
195151
}
196152

197153
/// For each `.Field = value` entry of `fields`:
198154
/// Set field `Field` of this register to `value`.
199155
/// This is implemented using read-modify-write.
200156
pub inline fn modify(self: *volatile @This(), fields: anytype) void {
201-
if (capabilities.read) |name|
202-
reg_type_op_error(name, "modifying this register by read-modify-write");
203-
if (capabilities.write) |name|
204-
reg_type_op_error(name, "modifying this register by read-modify-write");
205-
206-
self.modify_passed_value_and_write(
207-
@bitCast(@as(IntT, 0)),
208-
fields,
209-
.normal,
210-
"modifying this field by read-modify-write",
211-
);
157+
check_type_has_all_fields(PackedT, fields);
158+
const Fields = @TypeOf(fields);
159+
160+
const r = self.read();
161+
var w: PackedT = undefined;
162+
inline for (reg_fields) |field| {
163+
const access = @field(access_type, field.name);
164+
@field(w, field.name) = if (@hasField(Fields, field.name))
165+
// Overwrite this field
166+
if (access.write == .normal and (access.read == .normal or access.read == .garbage))
167+
@field(fields, field.name)
168+
else
169+
reg_type_op_error(field.name, "modifying this field by read-modify-write")
170+
else switch (access.write) {
171+
// Leave field unchanged
172+
.normal => if (access.read == .normal)
173+
@field(r, field.name)
174+
else
175+
// This should actually be:
176+
// reg_type_op_error(field.name, "modifying any field in this register by read-modify-write")
177+
@field(all_zeros, field.name),
178+
// Preserve old functionality
179+
.ignored => @field(r, field.name),
180+
// Write zeros so that nothing happens
181+
.set_mask, .clear_mask, .toggle_mask => @field(all_zeros, field.name),
182+
else => reg_type_op_error(field.name, "modifying any field in this register by read-modify-write"),
183+
};
184+
}
185+
self.write(w);
212186
}
213187

214188
pub inline fn set_mask(self: *volatile @This(), fields: anytype) void {
215-
if (capabilities.set_mask) |name|
216-
reg_type_op_error(name, "setting bits in this register by masking");
217-
218-
self.modify_passed_value_and_write(
219-
@bitCast(@as(IntT, 0)),
220-
fields,
221-
.set_mask,
222-
"setting bits in this field by masking",
223-
);
189+
check_type_has_all_fields(PackedT, fields);
190+
const Fields = @TypeOf(fields);
191+
192+
var w: PackedT = undefined;
193+
inline for (reg_fields) |field| {
194+
const access = @field(access_type, field.name);
195+
@field(w, field.name) = if (@hasField(Fields, field.name))
196+
// Set bits in this field
197+
if (access.write == .set_mask)
198+
@field(fields, field.name)
199+
else
200+
reg_type_op_error(field.name, "setting bits of this field by masking")
201+
else switch (access.write) {
202+
// Write zeros so that nothing happens
203+
.ignored, .set_mask, .clear_mask, .toggle_mask => @field(all_zeros, field.name),
204+
else => reg_type_op_error(field.name, "setting bits of any field in this register by masking"),
205+
};
206+
}
207+
self.write(w);
224208
}
225209

226210
pub inline fn clear_mask(self: *volatile @This(), fields: anytype) void {
227-
if (capabilities.clear_mask) |name|
228-
reg_type_op_error(name, "clearing bits in this register by masking");
229-
230-
self.modify_passed_value_and_write(
231-
@bitCast(@as(IntT, 0)),
232-
fields,
233-
.clear_mask,
234-
"clearing bits in this field by masking",
235-
);
236-
}
237-
238-
inline fn modify_passed_value_and_write(
239-
self: *volatile @This(),
240-
initial: PackedT,
241-
fields: anytype,
242-
allowed_write_access: Access.Write,
243-
comptime action: [:0]const u8,
244-
) void {
245-
var val = initial;
246-
inline for (@typeInfo(@TypeOf(fields)).@"struct".fields) |field| {
247-
if (@field(access_type, field.name).write != allowed_write_access)
248-
reg_type_op_error(field.name, action);
249-
@field(val, field.name) = @field(fields, field.name);
211+
check_type_has_all_fields(PackedT, fields);
212+
const Fields = @TypeOf(fields);
213+
214+
var w: PackedT = undefined;
215+
inline for (reg_fields) |field| {
216+
const access = @field(access_type, field.name);
217+
@field(w, field.name) = if (@hasField(Fields, field.name))
218+
// Clear bits in this field
219+
if (access.write == .clear_mask)
220+
@field(fields, field.name)
221+
else
222+
reg_type_op_error(field.name, "clearing bits of this field by masking")
223+
else switch (access.write) {
224+
// Write zeros so that nothing happens
225+
.ignored, .set_mask, .clear_mask, .toggle_mask => @field(all_zeros, field.name),
226+
else => reg_type_op_error(field.name, "clearing bits of any field in this register by masking"),
227+
};
250228
}
251-
self.raw = @bitCast(val);
229+
self.write(w);
252230
}
253231

254232
fn reg_type_op_error(comptime reg_name: [:0]const u8, comptime action: []const u8) noreturn {

0 commit comments

Comments
 (0)