diff --git a/tools/regz/src/Database.zig b/tools/regz/src/Database.zig index f959c1f..dfa9ef3 100644 --- a/tools/regz/src/Database.zig +++ b/tools/regz/src/Database.zig @@ -485,6 +485,7 @@ pub fn createField( /// size is in bits size: ?u64 = null, enum_id: ?EntityId = null, + count: ?u64 = null, }, ) !EntityId { assert(db.entityIs("type.register", parent_id)); @@ -504,6 +505,9 @@ pub fn createField( if (opts.size) |s| try db.addSize(id, s); + if (opts.count) |c| + try db.addCount(id, c); + if (opts.enum_id) |enum_id| { assert(db.entityIs("type.enum", enum_id)); if (db.attrs.size.get(enum_id)) |enum_size| diff --git a/tools/regz/src/gen.zig b/tools/regz/src/gen.zig index ed609be..43c7011 100644 --- a/tools/regz/src/gen.zig +++ b/tools/regz/src/gen.zig @@ -762,12 +762,19 @@ fn writeFields( while (end < fields.len and fields[end].offset == offset) : (end += 1) {} const next = blk: { var ret: ?EntityWithOffsetAndSize = null; - for (fields[i..end]) |register| { - const size = db.attrs.size.get(register.id) orelse unreachable; + for (fields[i..end]) |field| { + const size = if (db.attrs.size.get(field.id)) |size| + if (db.attrs.count.get(field.id)) |count| + size * count + else + size + else + unreachable; + if (ret == null or (size < ret.?.size)) ret = .{ - .id = register.id, - .offset = register.offset, + .id = field.id, + .offset = field.offset, .size = size, }; } @@ -789,7 +796,25 @@ fn writeFields( if (db.attrs.description.get(next.id)) |description| try writeComment(db.arena.allocator(), description, writer); - if (db.attrs.@"enum".get(fields[i].id)) |enum_id| { + if (db.attrs.count.get(fields[i].id)) |count| { + if (db.attrs.@"enum".contains(fields[i].id)) + log.warn("TODO: field array with enums", .{}); + + try writer.print("{s}: packed struct(u{}) {{ ", .{ + std.zig.fmtId(name), + next.size, + }); + + var j: u32 = 0; + while (j < count) : (j += 1) { + if (j > 0) + try writer.writeAll(", "); + + try writer.print("u{}", .{next.size / count}); + } + + try writer.writeAll(" },\n"); + } else if (db.attrs.@"enum".get(fields[i].id)) |enum_id| { if (db.attrs.name.get(enum_id)) |enum_name| { try writer.print( \\{s}: packed union {{ @@ -797,14 +822,22 @@ fn writeFields( \\ value: {s}, \\}}, \\ - , .{ name, next.size, std.zig.fmtId(enum_name) }); + , .{ + std.zig.fmtId(name), + next.size, + std.zig.fmtId(enum_name), + }); } else { try writer.print( \\{s}: packed union {{ \\ raw: u{}, \\ value: enum(u{}) {{ \\ - , .{ name, next.size, next.size }); + , .{ + std.zig.fmtId(name), + next.size, + next.size, + }); try writeEnumFields(db, enum_id, writer); try writer.writeAll("},\n},\n"); } @@ -1617,6 +1650,90 @@ test "gen.register with count and fields" { , buffer.items); } +test "gen.field with count, width of one, offset, and padding" { + var db = try Database.init(std.testing.allocator); + defer db.deinit(); + + const peripheral_id = try db.createPeripheral(.{ + .name = "PORTB", + }); + + const portb_id = try db.createRegister(peripheral_id, .{ + .name = "PORTB", + .size = 8, + .offset = 0, + }); + + _ = try db.createField(portb_id, .{ + .name = "TEST_FIELD", + .size = 1, + .offset = 2, + .count = 5, + }); + + var buffer = std.ArrayList(u8).init(std.testing.allocator); + defer buffer.deinit(); + + try db.toZig(buffer.writer()); + try std.testing.expectEqualStrings( + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; + \\ + \\pub const types = struct { + \\ pub const PORTB = extern struct { + \\ PORTB: mmio.Mmio(packed struct(u8) { + \\ reserved2: u2, + \\ TEST_FIELD: packed struct(u5) { u1, u1, u1, u1, u1 }, + \\ padding: u1, + \\ }), + \\ }; + \\}; + \\ + , buffer.items); +} + +test "gen.field with count, multi-bit width, offset, and padding" { + var db = try Database.init(std.testing.allocator); + defer db.deinit(); + + const peripheral_id = try db.createPeripheral(.{ + .name = "PORTB", + }); + + const portb_id = try db.createRegister(peripheral_id, .{ + .name = "PORTB", + .size = 8, + .offset = 0, + }); + + _ = try db.createField(portb_id, .{ + .name = "TEST_FIELD", + .size = 2, + .offset = 2, + .count = 2, + }); + + var buffer = std.ArrayList(u8).init(std.testing.allocator); + defer buffer.deinit(); + + try db.toZig(buffer.writer()); + try std.testing.expectEqualStrings( + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; + \\ + \\pub const types = struct { + \\ pub const PORTB = extern struct { + \\ PORTB: mmio.Mmio(packed struct(u8) { + \\ reserved2: u2, + \\ TEST_FIELD: packed struct(u4) { u2, u2 }, + \\ padding: u2, + \\ }), + \\ }; + \\}; + \\ + , buffer.items); +} + test "gen.interrupts.avr" { var db = try Database.init(std.testing.allocator); defer db.deinit(); diff --git a/tools/regz/src/svd.zig b/tools/regz/src/svd.zig index 7040aa8..664adcb 100644 --- a/tools/regz/src/svd.zig +++ b/tools/regz/src/svd.zig @@ -332,14 +332,14 @@ fn loadCluster( return error.TodoDimElements; } -fn getNameWithoutArraySuffix(node: xml.Node) ![]const u8 { +fn getNameWithoutSuffix(node: xml.Node, suffix: []const u8) ![]const u8 { return if (node.getValue("name")) |name| - if (std.mem.endsWith(u8, name, "[%s]")) - name[0 .. name.len - 4] + if (std.mem.endsWith(u8, name, suffix)) + name[0 .. name.len - suffix.len] else name else - error.MissingRegisterName; + error.MissingName; } fn loadRegister( @@ -348,7 +348,6 @@ fn loadRegister( parent_id: EntityId, ) !void { const db = ctx.db; - const register_props = try ctx.deriveRegisterPropertiesFrom(node, parent_id); const size = register_props.size orelse return error.MissingRegisterSize; const count: ?u64 = if (try DimElements.parse(node)) |elements| count: { @@ -362,7 +361,7 @@ fn loadRegister( } else null; const id = try db.createRegister(parent_id, .{ - .name = try getNameWithoutArraySuffix(node), + .name = try getNameWithoutSuffix(node, "[%s]"), .description = node.getValue("description"), .offset = if (node.getValue("addressOffset")) |offset_str| try std.fmt.parseInt(u64, offset_str, 0) @@ -399,18 +398,25 @@ fn loadField(ctx: *Context, node: xml.Node, register_id: EntityId) !void { const db = ctx.db; const bit_range = try BitRange.parse(node); + const count: ?u64 = if (try DimElements.parse(node)) |elements| count: { + if (elements.dim_index != null or elements.dim_name != null) + return error.TodoDimElementsExtended; + + if (elements.dim_increment != bit_range.width) + return error.DimIncrementSizeMismatch; + + break :count elements.dim; + } else null; + const id = try db.createField(register_id, .{ - .name = node.getValue("name") orelse return error.MissingFieldName, + .name = try getNameWithoutSuffix(node, "%s"), .description = node.getValue("description"), .size = bit_range.width, .offset = bit_range.offset, + .count = count, }); errdefer db.destroyEntity(id); - const dim_elements = try DimElements.parse(node); - if (dim_elements != null) - return error.TodoDimElements; - if (node.getValue("access")) |access_str| try db.addAccess(id, try parseAccess(access_str)); @@ -421,12 +427,9 @@ fn loadField(ctx: *Context, node: xml.Node, register_id: EntityId) !void { try ctx.addDerivedEntity(id, derived_from); // TODO: - // dimElementGroup // modifiedWriteValues // writeConstraint // readAction - // enumeratedValues - } fn loadEnumeratedValues(ctx: *Context, node: xml.Node, field_id: EntityId) !void { @@ -1211,3 +1214,44 @@ test "svd.register with dimElementGroup, suffixed with [%s]" { const register_id = try db.getEntityIdByName("type.register", "TEST_REGISTER"); try expectAttr(db, "count", 4, register_id); } + +test "svd.field with dimElementGroup, suffixed with %s" { + const text = + \\ + \\ TEST_DEVICE + \\ 32 + \\ read-only + \\ 0x00000000 + \\ 0xffffffff + \\ + \\ + \\ TEST_PERIPHERAL + \\ 0x1000 + \\ + \\ + \\ TEST_REGISTER + \\ 0 + \\ + \\ + \\ TEST_FIELD%s + \\ read-write + \\ [0:0] + \\ 2 + \\ 1 + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ; + + var doc = try xml.Doc.fromMemory(text); + var db = try Database.initFromSvd(std.testing.allocator, doc); + defer db.deinit(); + + // %s is dropped from name, it is redundant + const register_id = try db.getEntityIdByName("type.field", "TEST_FIELD"); + try expectAttr(db, "count", 2, register_id); +}