@@ -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
7365pub 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