Nice. I recently played around with an esp32c6 and ended up with a similar api.
My implementation is slightly different.
const UpdateStruct = blk: {
const fields = @typeInfo(T).@"struct".fields;
var types: [fields.len]type = undefined;
var names: [fields.len][]const u8 = undefined;
var attrs: [fields.len]std.builtin.Type.StructField.Attributes = undefined;
var i = 0;
for (fields) |field| {
if (field.type == Reserved(@bitSizeOf(field.type))) continue;
types[i] = ?field.type;
names[i] = field.name;
attrs[i] = .{ .default_value_ptr = &@as(?field.type, null) };
i += 1;
}
break :blk @Struct(.auto, null, names[0..i], types[0..i], attrs[0..i]);
};
pub inline fn update(reg: @This(), fields: UpdateStruct) void {
var a = reg.read();
inline for (comptime std.meta.fieldNames(UpdateStruct)) |name| {
if (@field(fields, name)) |value| @field(a, name) = value;
}
reg.write(a);
}
I was curious whether explicitly constructing the mask as you did would lead to better codegen, so I changed my implementation.
As I was surprised to find out, for limited examples, this led to slightly more instructions for my limited examples.
After some tweaking, I ended up with the following implementation, which produced exactly the same assembly as my original implementation.
pub inline fn update(reg: @This(), fields: UpdateStruct) void {
const orig_mask: Raw, const new_data: Raw = D: {
var mask: Mask = @bitCast(~@as(Raw, 0));
var t: T = @bitCast(@as(Raw, 0));
inline for (comptime std.meta.fieldNames(@TypeOf(fields))) |name| {
if (@field(fields, name)) |v| {
@field(t, name) = v;
@field(mask, name) = 0;
}
}
break :D .{ @bitCast(mask), @bitCast(t) };
};
var raw: Raw = @bitCast(reg.read());
raw &= orig_mask;
raw |= new_data;
reg.write(@bitCast(raw));
}
Good luck in convincing your colleagues
. I also found zig to be a joy to use for embedded.