Implement various `adtf` features needed on ARM (#115)

* Fix test step not running tests

* Fix `error.Todo` causing a crash while outputting json

* Cleanup `@as` usage

* Implement count attribute for registers

* Add ad-hoc alignment to support an initial reserved region.

* Implement register group instances

* Add `build.zig.zon` paths
wch-ch32v003
Jacob Young 10 months ago committed by Matt Knight
parent d897125bf6
commit 77968f04d2

@ -71,6 +71,7 @@ pub fn build(b: *std.build.Builder) !void {
.optimize = optimize, .optimize = optimize,
}); });
tests.linkLibrary(libxml2_dep.artifact("xml2")); tests.linkLibrary(libxml2_dep.artifact("xml2"));
const run_tests = b.addRunArtifact(tests);
const test_step = b.step("test", "Run unit tests"); const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&tests.step); test_step.dependOn(&run_tests.step);
} }

@ -1,6 +1,15 @@
.{ .{
.name = "regz", .name = "regz",
.version = "0.0.0", .version = "0.0.0",
.paths = .{
"LICENSE",
"README.md",
"build.zig",
"build.zig.zon",
"characterized",
"src",
"tests",
},
.dependencies = .{ .dependencies = .{
.libxml2 = .{ .libxml2 = .{
.url = "https://github.com/mitchellh/zig-build-libxml2/archive/4e80a167d6e13e09e0da77457310401084e758cc.tar.gz", .url = "https://github.com/mitchellh/zig-build-libxml2/archive/4e80a167d6e13e09e0da77457310401084e758cc.tar.gz",

@ -9,6 +9,7 @@ attrs: struct {
description: HashMap(EntityId, []const u8) = .{}, description: HashMap(EntityId, []const u8) = .{},
offset: HashMap(EntityId, u64) = .{}, offset: HashMap(EntityId, u64) = .{},
access: HashMap(EntityId, Access) = .{}, access: HashMap(EntityId, Access) = .{},
group: HashMap(EntityId, void) = .{},
count: HashMap(EntityId, u64) = .{}, count: HashMap(EntityId, u64) = .{},
size: HashMap(EntityId, u64) = .{}, size: HashMap(EntityId, u64) = .{},
reset_value: HashMap(EntityId, u64) = .{}, reset_value: HashMap(EntityId, u64) = .{},
@ -75,6 +76,7 @@ const log = std.log.scoped(.database);
pub const EntityId = u32; pub const EntityId = u32;
pub const EntitySet = ArrayHashMap(EntityId, void); pub const EntitySet = ArrayHashMap(EntityId, void);
pub const RegisterKind = enum { register, register_group };
// concrete arch's that we support in codegen, for stuff like interrupt // concrete arch's that we support in codegen, for stuff like interrupt
// table generation // table generation
@ -217,6 +219,7 @@ pub fn deinit(db: *Database) void {
db.attrs.description.deinit(db.gpa); db.attrs.description.deinit(db.gpa);
db.attrs.offset.deinit(db.gpa); db.attrs.offset.deinit(db.gpa);
db.attrs.access.deinit(db.gpa); db.attrs.access.deinit(db.gpa);
db.attrs.group.deinit(db.gpa);
db.attrs.count.deinit(db.gpa); db.attrs.count.deinit(db.gpa);
db.attrs.size.deinit(db.gpa); db.attrs.size.deinit(db.gpa);
db.attrs.reset_value.deinit(db.gpa); db.attrs.reset_value.deinit(db.gpa);
@ -324,6 +327,7 @@ pub fn destroy_entity(db: *Database, id: EntityId) void {
log.debug("{}: destroying peripheral", .{id}); log.debug("{}: destroying peripheral", .{id});
// TODO: remove children // TODO: remove children
_ = db.types.peripherals.swapRemove(id); _ = db.types.peripherals.swapRemove(id);
_ = db.instances.peripherals.swapRemove(id);
}, },
else => {}, else => {},
} }
@ -476,6 +480,7 @@ pub fn create_register(
offset: u64, offset: u64,
/// size is in bits /// size is in bits
size: u64, size: u64,
kind: RegisterKind = .register,
/// count if there is an array /// count if there is an array
count: ?u64 = null, count: ?u64 = null,
access: ?Access = null, access: ?Access = null,
@ -499,6 +504,11 @@ pub fn create_register(
try db.add_offset(id, opts.offset); try db.add_offset(id, opts.offset);
try db.add_size(id, opts.size); try db.add_size(id, opts.size);
switch (opts.kind) {
.register => {},
.register_group => try db.add_group(id),
}
if (opts.count) |c| if (opts.count) |c|
try db.add_count(id, c); try db.add_count(id, c);
@ -730,6 +740,11 @@ pub fn add_access(db: *Database, id: EntityId, access: Access) !void {
try db.attrs.access.putNoClobber(db.gpa, id, access); try db.attrs.access.putNoClobber(db.gpa, id, access);
} }
pub fn add_group(db: *Database, id: EntityId) !void {
log.debug("{}: adding register group", .{id});
try db.attrs.group.putNoClobber(db.gpa, id, {});
}
pub fn add_count(db: *Database, id: EntityId, count: u64) !void { pub fn add_count(db: *Database, id: EntityId, count: u64) !void {
log.debug("{}: adding count: {}", .{ id, count }); log.debug("{}: adding count: {}", .{ id, count });
try db.attrs.count.putNoClobber(db.gpa, id, count); try db.attrs.count.putNoClobber(db.gpa, id, count);

@ -39,11 +39,11 @@ pub fn load_into_db(db: *Database, doc: xml.Doc) !void {
defer ctx.deinit(); defer ctx.deinit();
const root = try doc.get_root_element(); const root = try doc.get_root_element();
var module_it = root.iterate(&.{"modules"}, "module"); var module_it = root.iterate(&.{"modules"}, &.{"module"});
while (module_it.next()) |entry| while (module_it.next()) |entry|
try load_module_type(&ctx, entry); try load_module_type(&ctx, entry);
var device_it = root.iterate(&.{"devices"}, "device"); var device_it = root.iterate(&.{"devices"}, &.{"device"});
while (device_it.next()) |entry| while (device_it.next()) |entry|
try load_device(&ctx, entry); try load_device(&ctx, entry);
@ -75,13 +75,13 @@ fn load_device(ctx: *Context, node: xml.Node) !void {
if (node.get_attribute("series")) |series| if (node.get_attribute("series")) |series|
try db.add_device_property(id, "series", series); try db.add_device_property(id, "series", series);
var module_it = node.iterate(&.{"peripherals"}, "module"); var module_it = node.iterate(&.{"peripherals"}, &.{"module"});
while (module_it.next()) |module_node| while (module_it.next()) |module_node|
load_module_instances(ctx, module_node, id) catch |err| { load_module_instances(ctx, module_node, id) catch |err| {
log.warn("failed to instantiate module: {}", .{err}); log.warn("failed to instantiate module: {}", .{err});
}; };
if (node.find_child("interrupts")) |interrupts_node| if (node.find_child(&.{"interrupts"})) |interrupts_node|
try load_interrupts(ctx, interrupts_node, id); try load_interrupts(ctx, interrupts_node, id);
try infer_peripheral_offsets(ctx); try infer_peripheral_offsets(ctx);
@ -104,11 +104,11 @@ fn load_device(ctx: *Context, node: xml.Node) !void {
} }
fn load_interrupts(ctx: *Context, node: xml.Node, device_id: EntityId) !void { fn load_interrupts(ctx: *Context, node: xml.Node, device_id: EntityId) !void {
var interrupt_it = node.iterate(&.{}, "interrupt"); var interrupt_it = node.iterate(&.{}, &.{"interrupt"});
while (interrupt_it.next()) |interrupt_node| while (interrupt_it.next()) |interrupt_node|
try load_interrupt(ctx, interrupt_node, device_id); try load_interrupt(ctx, interrupt_node, device_id);
var interrupt_group_it = node.iterate(&.{}, "interrupt-group"); var interrupt_group_it = node.iterate(&.{}, &.{"interrupt-group"});
while (interrupt_group_it.next()) |interrupt_group_node| while (interrupt_group_it.next()) |interrupt_group_node|
try load_interrupt_group(ctx, interrupt_group_node, device_id); try load_interrupt_group(ctx, interrupt_group_node, device_id);
} }
@ -198,26 +198,33 @@ fn infer_peripheral_offset(ctx: *Context, type_id: EntityId, instance_id: Entity
// TODO: assert that there's only one instance using this type // TODO: assert that there's only one instance using this type
var min_offset: ?u64 = null; var min_offset: ?u64 = null;
var max_size: u64 = 8;
// first find the min offset of all the registers for this peripheral // first find the min offset of all the registers for this peripheral
const register_set = db.children.registers.get(type_id) orelse return; const register_set = db.children.registers.get(type_id) orelse return;
for (register_set.keys()) |register_id| { for (register_set.keys()) |register_id| {
const offset = db.attrs.offset.get(register_id) orelse continue; const offset = db.attrs.offset.get(register_id) orelse continue;
if (min_offset == null) if (min_offset == null)
min_offset = offset min_offset = offset
else else
min_offset = @min(min_offset.?, offset); min_offset = @min(min_offset.?, offset);
if (db.attrs.size.get(register_id)) |size|
max_size = @max(max_size, size);
} }
if (min_offset == null) if (min_offset == null)
return error.NoRegisters; return error.NoRegisters;
const instance_offset: u64 = db.attrs.offset.get(instance_id) orelse 0; const assumed_align = @min(@divExact(std.math.ceilPowerOfTwoAssert(u64, max_size), 8), 8);
try db.attrs.offset.put(db.gpa, instance_id, instance_offset + min_offset.?); const old_instance_offset = db.attrs.offset.get(instance_id) orelse 0;
const new_instance_offset = std.mem.alignBackward(u64, old_instance_offset + min_offset.?, assumed_align);
const offset_delta = @as(i65, new_instance_offset) - @as(i65, old_instance_offset);
try db.attrs.offset.put(db.gpa, instance_id, new_instance_offset);
for (register_set.keys()) |register_id| { for (register_set.keys()) |register_id| {
if (db.attrs.offset.getEntry(register_id)) |offset_entry| if (db.attrs.offset.getEntry(register_id)) |offset_entry|
offset_entry.value_ptr.* -= min_offset.?; offset_entry.value_ptr.* = @intCast(offset_entry.value_ptr.* - offset_delta);
} }
} }
@ -287,7 +294,7 @@ fn infer_enum_size(db: *Database, enum_id: EntityId) !void {
// TODO: instances use name in module // TODO: instances use name in module
fn get_inlined_register_group(parent_node: xml.Node, parent_name: []const u8) ?xml.Node { fn get_inlined_register_group(parent_node: xml.Node, parent_name: []const u8) ?xml.Node {
var register_group_it = parent_node.iterate(&.{}, "register-group"); var register_group_it = parent_node.iterate(&.{}, &.{"register-group"});
const rg_node = register_group_it.next() orelse return null; const rg_node = register_group_it.next() orelse return null;
const rg_name = rg_node.get_attribute("name") orelse return null; const rg_name = rg_node.get_attribute("name") orelse return null;
log.debug("rg name is {s}, parent is {s}", .{ rg_name, parent_name }); log.debug("rg name is {s}, parent is {s}", .{ rg_name, parent_name });
@ -325,11 +332,11 @@ fn load_module_type(ctx: *Context, node: xml.Node) !void {
if (node.get_attribute("caption")) |caption| if (node.get_attribute("caption")) |caption|
try db.add_description(id, caption); try db.add_description(id, caption);
var value_group_it = node.iterate(&.{}, "value-group"); var value_group_it = node.iterate(&.{}, &.{"value-group"});
while (value_group_it.next()) |value_group_node| while (value_group_it.next()) |value_group_node|
try load_enum(ctx, value_group_node, id); try load_enum(ctx, value_group_node, id);
var interrupt_group_it = node.iterate(&.{}, "interrupt-group"); var interrupt_group_it = node.iterate(&.{}, &.{"interrupt-group"});
while (interrupt_group_it.next()) |interrupt_group_node| while (interrupt_group_it.next()) |interrupt_group_node|
try load_module_interrupt_group(ctx, interrupt_group_node); try load_module_interrupt_group(ctx, interrupt_group_node);
@ -340,7 +347,7 @@ fn load_module_type(ctx: *Context, node: xml.Node) !void {
if (get_inlined_register_group(node, name)) |register_group_node| { if (get_inlined_register_group(node, name)) |register_group_node| {
try load_register_group_children(ctx, register_group_node, id); try load_register_group_children(ctx, register_group_node, id);
} else { } else {
var register_group_it = node.iterate(&.{}, "register-group"); var register_group_it = node.iterate(&.{}, &.{"register-group"});
while (register_group_it.next()) |register_group_node| while (register_group_it.next()) |register_group_node|
try load_register_group(ctx, register_group_node, id); try load_register_group(ctx, register_group_node, id);
} }
@ -350,7 +357,7 @@ fn load_module_interrupt_group(ctx: *Context, node: xml.Node) !void {
const name = node.get_attribute("name") orelse return error.MissingInterruptGroupName; const name = node.get_attribute("name") orelse return error.MissingInterruptGroupName;
try ctx.interrupt_groups.put(ctx.db.gpa, name, .{}); try ctx.interrupt_groups.put(ctx.db.gpa, name, .{});
var interrupt_it = node.iterate(&.{}, "interrupt"); var interrupt_it = node.iterate(&.{}, &.{"interrupt"});
while (interrupt_it.next()) |interrupt_node| while (interrupt_it.next()) |interrupt_node|
try load_module_interrupt_group_entry(ctx, interrupt_node, name); try load_module_interrupt_group_entry(ctx, interrupt_node, name);
} }
@ -382,13 +389,13 @@ fn load_register_group_children(
assert(db.entity_is("type.peripheral", dest_id) or assert(db.entity_is("type.peripheral", dest_id) or
db.entity_is("type.register_group", dest_id)); db.entity_is("type.register_group", dest_id));
var mode_it = node.iterate(&.{}, "mode"); var mode_it = node.iterate(&.{}, &.{"mode"});
while (mode_it.next()) |mode_node| while (mode_it.next()) |mode_node|
load_mode(ctx, mode_node, dest_id) catch |err| { load_mode(ctx, mode_node, dest_id) catch |err| {
log.err("{}: failed to load mode: {}", .{ dest_id, err }); log.err("{}: failed to load mode: {}", .{ dest_id, err });
}; };
var register_it = node.iterate(&.{}, "register"); var register_it = node.iterate(&.{}, &.{ "register", "register-group" });
while (register_it.next()) |register_node| while (register_it.next()) |register_node|
try load_register(ctx, register_node, dest_id); try load_register(ctx, register_node, dest_id);
} }
@ -565,6 +572,16 @@ fn load_register(
try std.fmt.parseInt(u64, offset_str, 0) try std.fmt.parseInt(u64, offset_str, 0)
else else
return error.MissingRegisterOffset, return error.MissingRegisterOffset,
.kind = if (std.mem.eql(u8, node.get_name(), "register"))
.register
else if (std.mem.eql(u8, node.get_name(), "register-group"))
.register_group
else
unreachable,
.count = if (node.get_attribute("count")) |count_str|
try std.fmt.parseInt(u64, count_str, 0)
else
null,
}); });
errdefer db.destroy_entity(id); errdefer db.destroy_entity(id);
@ -594,13 +611,13 @@ fn load_register(
} }
// assumes that modes are parsed before registers in the register group // assumes that modes are parsed before registers in the register group
var mode_it = node.iterate(&.{}, "mode"); var mode_it = node.iterate(&.{}, &.{"mode"});
while (mode_it.next()) |mode_node| while (mode_it.next()) |mode_node|
load_mode(ctx, mode_node, id) catch |err| { load_mode(ctx, mode_node, id) catch |err| {
log.err("{}: failed to load mode: {}", .{ id, err }); log.err("{}: failed to load mode: {}", .{ id, err });
}; };
var field_it = node.iterate(&.{}, "bitfield"); var field_it = node.iterate(&.{}, &.{"bitfield"});
while (field_it.next()) |field_node| while (field_it.next()) |field_node|
load_field(ctx, field_node, id) catch {}; load_field(ctx, field_node, id) catch {};
} }
@ -758,7 +775,7 @@ fn load_enum(
if (node.get_attribute("caption")) |caption| if (node.get_attribute("caption")) |caption|
try db.add_description(id, caption); try db.add_description(id, caption);
var value_it = node.iterate(&.{}, "value"); var value_it = node.iterate(&.{}, &.{"value"});
while (value_it.next()) |value_node| while (value_it.next()) |value_node|
load_enum_field(ctx, value_node, id) catch {}; load_enum_field(ctx, value_node, id) catch {};
@ -826,7 +843,7 @@ fn load_module_instances(
} }
}; };
var instance_it = node.iterate(&.{}, "instance"); var instance_it = node.iterate(&.{}, &.{"instance"});
while (instance_it.next()) |instance_node| while (instance_it.next()) |instance_node|
try load_module_instance(ctx, instance_node, device_id, type_id); try load_module_instance(ctx, instance_node, device_id, type_id);
} }
@ -885,14 +902,14 @@ fn load_module_instance_from_peripheral(
} else { } else {
return error.Todo; return error.Todo;
//unreachable; //unreachable;
//var register_group_it = node.iterate(&.{}, "register-group"); //var register_group_it = node.iterate(&.{}, &.{"register-group"});
//while (register_group_it.next()) |register_group_node| //while (register_group_it.next()) |register_group_node|
// loadRegisterGroupInstance(db, register_group_node, id, peripheral_type_id) catch { // loadRegisterGroupInstance(db, register_group_node, id, peripheral_type_id) catch {
// log.warn("skipping register group instance in {s}", .{name}); // log.warn("skipping register group instance in {s}", .{name});
// }; // };
} }
var signal_it = node.iterate(&.{"signals"}, "signal"); var signal_it = node.iterate(&.{"signals"}, &.{"signal"});
while (signal_it.next()) |signal_node| while (signal_it.next()) |signal_node|
try load_signal(ctx, signal_node, id); try load_signal(ctx, signal_node, id);
@ -907,7 +924,7 @@ fn load_module_instance_from_register_group(
) !void { ) !void {
const db = ctx.db; const db = ctx.db;
const register_group_node = blk: { const register_group_node = blk: {
var it = node.iterate(&.{}, "register-group"); var it = node.iterate(&.{}, &.{"register-group"});
const ret = it.next() orelse return error.MissingInstanceRegisterGroup; const ret = it.next() orelse return error.MissingInstanceRegisterGroup;
if (it.next() != null) { if (it.next() != null) {
return error.TodoInstanceWithMultipleRegisterGroups; return error.TodoInstanceWithMultipleRegisterGroups;

@ -42,7 +42,7 @@ pub fn to_zig(db: Database, out_writer: anytype) !void {
try writer.writeByte(0); try writer.writeByte(0);
// format the generated code // format the generated code
var ast = try std.zig.Ast.parse(db.gpa, @as([:0]const u8, @ptrCast(buffer.items[0 .. buffer.items.len - 1])), .zig); var ast = try std.zig.Ast.parse(db.gpa, buffer.items[0 .. buffer.items.len - 1 :0], .zig);
defer ast.deinit(db.gpa); defer ast.deinit(db.gpa);
// TODO: ast check? // TODO: ast check?
@ -262,7 +262,7 @@ fn write_peripheral_instance(db: Database, instance_id: EntityId, offset: u64, o
else else
""; "";
try writer.print("pub const {s} = @as(*volatile {s}{s}, @ptrFromInt(0x{x}));\n", .{ try writer.print("pub const {s}: *volatile {s}{s} = @ptrFromInt(0x{x});\n", .{
std.zig.fmtId(name), std.zig.fmtId(name),
array_prefix, array_prefix,
type_ref, type_ref,
@ -687,7 +687,13 @@ fn write_register(
else else
""; "";
if (db.children.fields.get(register_id)) |field_set| { if (db.attrs.group.contains(register_id)) {
try writer.print("{s}: {s}{s},\n", .{
std.zig.fmtId(name),
array_prefix,
std.zig.fmtId(name),
});
} else if (db.children.fields.get(register_id)) |field_set| {
var fields = std.ArrayList(EntityWithOffset).init(db.gpa); var fields = std.ArrayList(EntityWithOffset).init(db.gpa);
defer fields.deinit(); defer fields.deinit();
@ -906,7 +912,7 @@ test "gen.peripheral instantiation" {
\\pub const devices = struct { \\pub const devices = struct {
\\ pub const TEST_DEVICE = struct { \\ pub const TEST_DEVICE = struct {
\\ pub const peripherals = struct { \\ pub const peripherals = struct {
\\ pub const TEST0 = @as(*volatile types.peripherals.TEST_PERIPHERAL, @ptrFromInt(0x1000)); \\ pub const TEST0: *volatile types.peripherals.TEST_PERIPHERAL = @ptrFromInt(0x1000);
\\ }; \\ };
\\ }; \\ };
\\}; \\};
@ -940,8 +946,8 @@ test "gen.peripherals with a shared type" {
\\pub const devices = struct { \\pub const devices = struct {
\\ pub const TEST_DEVICE = struct { \\ pub const TEST_DEVICE = struct {
\\ pub const peripherals = struct { \\ pub const peripherals = struct {
\\ pub const TEST0 = @as(*volatile types.peripherals.TEST_PERIPHERAL, @ptrFromInt(0x1000)); \\ pub const TEST0: *volatile types.peripherals.TEST_PERIPHERAL = @ptrFromInt(0x1000);
\\ pub const TEST1 = @as(*volatile types.peripherals.TEST_PERIPHERAL, @ptrFromInt(0x2000)); \\ pub const TEST1: *volatile types.peripherals.TEST_PERIPHERAL = @ptrFromInt(0x2000);
\\ }; \\ };
\\ }; \\ };
\\}; \\};
@ -1160,8 +1166,8 @@ test "gen.namespaced register groups" {
\\pub const devices = struct { \\pub const devices = struct {
\\ pub const ATmega328P = struct { \\ pub const ATmega328P = struct {
\\ pub const peripherals = struct { \\ pub const peripherals = struct {
\\ pub const PORTB = @as(*volatile types.peripherals.PORT.PORTB, @ptrFromInt(0x23)); \\ pub const PORTB: *volatile types.peripherals.PORT.PORTB = @ptrFromInt(0x23);
\\ pub const PORTC = @as(*volatile types.peripherals.PORT.PORTC, @ptrFromInt(0x26)); \\ pub const PORTC: *volatile types.peripherals.PORT.PORTC = @ptrFromInt(0x26);
\\ }; \\ };
\\ }; \\ };
\\}; \\};
@ -1202,7 +1208,7 @@ test "gen.peripheral with reserved register" {
\\pub const devices = struct { \\pub const devices = struct {
\\ pub const ATmega328P = struct { \\ pub const ATmega328P = struct {
\\ pub const peripherals = struct { \\ pub const peripherals = struct {
\\ pub const PORTB = @as(*volatile types.peripherals.PORTB, @ptrFromInt(0x23)); \\ pub const PORTB: *volatile types.peripherals.PORTB = @ptrFromInt(0x23);
\\ }; \\ };
\\ }; \\ };
\\}; \\};
@ -1235,7 +1241,7 @@ test "gen.peripheral with count" {
\\pub const devices = struct { \\pub const devices = struct {
\\ pub const ATmega328P = struct { \\ pub const ATmega328P = struct {
\\ pub const peripherals = struct { \\ pub const peripherals = struct {
\\ pub const PORTB = @as(*volatile [4]types.peripherals.PORTB, @ptrFromInt(0x23)); \\ pub const PORTB: *volatile [4]types.peripherals.PORTB = @ptrFromInt(0x23);
\\ }; \\ };
\\ }; \\ };
\\}; \\};
@ -1268,7 +1274,7 @@ test "gen.peripheral with count, padding required" {
\\pub const devices = struct { \\pub const devices = struct {
\\ pub const ATmega328P = struct { \\ pub const ATmega328P = struct {
\\ pub const peripherals = struct { \\ pub const peripherals = struct {
\\ pub const PORTB = @as(*volatile [4]types.peripherals.PORTB, @ptrFromInt(0x23)); \\ pub const PORTB: *volatile [4]types.peripherals.PORTB = @ptrFromInt(0x23);
\\ }; \\ };
\\ }; \\ };
\\}; \\};
@ -1302,7 +1308,7 @@ test "gen.register with count" {
\\pub const devices = struct { \\pub const devices = struct {
\\ pub const ATmega328P = struct { \\ pub const ATmega328P = struct {
\\ pub const peripherals = struct { \\ pub const peripherals = struct {
\\ pub const PORTB = @as(*volatile types.peripherals.PORTB, @ptrFromInt(0x23)); \\ pub const PORTB: *volatile types.peripherals.PORTB = @ptrFromInt(0x23);
\\ }; \\ };
\\ }; \\ };
\\}; \\};
@ -1335,7 +1341,7 @@ test "gen.register with count and fields" {
\\pub const devices = struct { \\pub const devices = struct {
\\ pub const ATmega328P = struct { \\ pub const ATmega328P = struct {
\\ pub const peripherals = struct { \\ pub const peripherals = struct {
\\ pub const PORTB = @as(*volatile types.peripherals.PORTB, @ptrFromInt(0x23)); \\ pub const PORTB: *volatile types.peripherals.PORTB = @ptrFromInt(0x23);
\\ }; \\ };
\\ }; \\ };
\\}; \\};

@ -803,6 +803,9 @@ fn populate_peripheral(
if (db.attrs.version.get(id)) |version| if (db.attrs.version.get(id)) |version|
try peripheral.put("version", .{ .string = version }); try peripheral.put("version", .{ .string = version });
if (db.attrs.group.contains(id))
try peripheral.put("group", .{ .bool = true });
if (db.attrs.count.get(id)) |count| if (db.attrs.count.get(id)) |count|
try peripheral.put("count", .{ .integer = @as(i64, @intCast(count)) }); try peripheral.put("count", .{ .integer = @as(i64, @intCast(count)) });

@ -84,7 +84,7 @@ pub fn load_into_db(db: *Database, doc: xml.Doc) !void {
// peripherals // peripherals
// vendorExtensions // vendorExtensions
var cpu_it = root.iterate(&.{}, "cpu"); var cpu_it = root.iterate(&.{}, &.{"cpu"});
if (cpu_it.next()) |cpu| { if (cpu_it.next()) |cpu| {
const required_properties: []const []const u8 = &.{ const required_properties: []const []const u8 = &.{
"name", "name",
@ -138,7 +138,7 @@ pub fn load_into_db(db: *Database, doc: xml.Doc) !void {
const register_props = try RegisterProperties.parse(root); const register_props = try RegisterProperties.parse(root);
try ctx.register_props.put(db.gpa, device_id, register_props); try ctx.register_props.put(db.gpa, device_id, register_props);
var peripheral_it = root.iterate(&.{"peripherals"}, "peripheral"); var peripheral_it = root.iterate(&.{"peripherals"}, &.{"peripheral"});
while (peripheral_it.next()) |peripheral_node| while (peripheral_it.next()) |peripheral_node|
load_peripheral(&ctx, peripheral_node, device_id) catch |err| load_peripheral(&ctx, peripheral_node, device_id) catch |err|
log.warn("failed to load peripheral: {}", .{err}); log.warn("failed to load peripheral: {}", .{err});
@ -286,7 +286,7 @@ pub fn load_peripheral(ctx: *Context, node: xml.Node, device_id: EntityId) !void
try db.add_size(type_id, elements.dim_increment); try db.add_size(type_id, elements.dim_increment);
} }
var interrupt_it = node.iterate(&.{}, "interrupt"); var interrupt_it = node.iterate(&.{}, &.{"interrupt"});
while (interrupt_it.next()) |interrupt_node| while (interrupt_it.next()) |interrupt_node|
try load_interrupt(db, interrupt_node, device_id); try load_interrupt(db, interrupt_node, device_id);
@ -302,13 +302,13 @@ pub fn load_peripheral(ctx: *Context, node: xml.Node, device_id: EntityId) !void
const register_props = try ctx.derive_register_properties_from(node, device_id); const register_props = try ctx.derive_register_properties_from(node, device_id);
try ctx.register_props.put(db.gpa, type_id, register_props); try ctx.register_props.put(db.gpa, type_id, register_props);
var register_it = node.iterate(&.{"registers"}, "register"); var register_it = node.iterate(&.{"registers"}, &.{"register"});
while (register_it.next()) |register_node| while (register_it.next()) |register_node|
load_register(ctx, register_node, type_id) catch |err| load_register(ctx, register_node, type_id) catch |err|
log.warn("failed to load register: {}", .{err}); log.warn("failed to load register: {}", .{err});
// TODO: handle errors when implemented // TODO: handle errors when implemented
var cluster_it = node.iterate(&.{"registers"}, "cluster"); var cluster_it = node.iterate(&.{"registers"}, &.{"cluster"});
while (cluster_it.next()) |cluster_node| while (cluster_it.next()) |cluster_node|
load_cluster(ctx, cluster_node, type_id) catch |err| load_cluster(ctx, cluster_node, type_id) catch |err|
log.warn("failed to load cluster: {}", .{err}); log.warn("failed to load cluster: {}", .{err});
@ -425,7 +425,7 @@ fn load_register(
}); });
errdefer db.destroy_entity(id); errdefer db.destroy_entity(id);
var field_it = node.iterate(&.{"fields"}, "field"); var field_it = node.iterate(&.{"fields"}, &.{"field"});
while (field_it.next()) |field_node| while (field_it.next()) |field_node|
load_field(ctx, field_node, id) catch |err| load_field(ctx, field_node, id) catch |err|
log.warn("failed to load register: {}", .{err}); log.warn("failed to load register: {}", .{err});
@ -470,7 +470,7 @@ fn load_field(ctx: *Context, node: xml.Node, register_id: EntityId) !void {
if (node.get_value("access")) |access_str| if (node.get_value("access")) |access_str|
try db.add_access(id, try parse_access(access_str)); try db.add_access(id, try parse_access(access_str));
if (node.find_child("enumeratedValues")) |enum_values_node| if (node.find_child(&.{"enumeratedValues"})) |enum_values_node|
try load_enumerated_values(ctx, enum_values_node, id); try load_enumerated_values(ctx, enum_values_node, id);
if (node.get_attribute("derivedFrom")) |derived_from| if (node.get_attribute("derivedFrom")) |derived_from|
@ -503,7 +503,7 @@ fn load_enumerated_values(ctx: *Context, node: xml.Node, field_id: EntityId) !vo
try db.attrs.@"enum".putNoClobber(db.gpa, field_id, id); try db.attrs.@"enum".putNoClobber(db.gpa, field_id, id);
var value_it = node.iterate(&.{}, "enumeratedValue"); var value_it = node.iterate(&.{}, &.{"enumeratedValue"});
while (value_it.next()) |value_node| while (value_it.next()) |value_node|
try load_enumerated_value(ctx, value_node, id); try load_enumerated_value(ctx, value_node, id);
} }

@ -17,21 +17,23 @@ pub const Node = struct {
pub const Iterator = struct { pub const Iterator = struct {
node: ?Node, node: ?Node,
filter: []const u8, filter: []const []const u8,
pub fn next(it: *Iterator) ?Node { pub fn next(it: *Iterator) ?Node {
// TODO: what if current node doesn't fit the bill? // TODO: what if current node doesn't fit the bill?
return while (it.node != null) : (it.node = if (it.node.?.impl.next) |impl| Node{ .impl = impl } else null) { while (it.node != null) : (it.node = if (it.node.?.impl.next) |impl| Node{ .impl = impl } else null) {
if (it.node.?.impl.type != 1) if (it.node.?.impl.type != 1)
continue; continue;
if (it.node.?.impl.name) |name| if (it.node.?.impl.name) |name|
if (std.mem.eql(u8, it.filter, std.mem.span(name))) { for (it.filter) |elem|
if (std.mem.eql(u8, elem, std.mem.span(name))) {
const ret = it.node; const ret = it.node;
it.node = if (it.node.?.impl.next) |impl| Node{ .impl = impl } else null; it.node = if (it.node.?.impl.next) |impl| Node{ .impl = impl } else null;
break ret; return ret;
}; };
} else return null; }
return null;
} }
}; };
@ -57,6 +59,10 @@ pub const Node = struct {
} }
}; };
pub fn get_name(node: Node) []const u8 {
return std.mem.span(node.impl.name);
}
pub fn get_attribute(node: Node, key: [:0]const u8) ?[]const u8 { pub fn get_attribute(node: Node, key: [:0]const u8) ?[]const u8 {
if (c.xmlHasProp(node.impl, key.ptr)) |prop| { if (c.xmlHasProp(node.impl, key.ptr)) |prop| {
if (@as(*c.xmlAttr, @ptrCast(prop)).children) |value_node| { if (@as(*c.xmlAttr, @ptrCast(prop)).children) |value_node| {
@ -69,24 +75,26 @@ pub const Node = struct {
return null; return null;
} }
pub fn find_child(node: Node, key: []const u8) ?Node { pub fn find_child(node: Node, keys: []const []const u8) ?Node {
var it = @as(?*c.xmlNode, @ptrCast(node.impl.children)); var it = @as(?*c.xmlNode, @ptrCast(node.impl.children));
return while (it != null) : (it = it.?.next) { while (it != null) : (it = it.?.next) {
if (it.?.type != 1) if (it.?.type != 1)
continue; continue;
const name = std.mem.span(it.?.name orelse continue); const name = std.mem.span(it.?.name orelse continue);
for (keys) |key|
if (std.mem.eql(u8, key, name)) if (std.mem.eql(u8, key, name))
break Node{ .impl = it.? }; return Node{ .impl = it.? };
} else null; }
return null;
} }
// `skip` will only delve into a specific path of elements // `skip` will only delve into a specific path of elements
// `name` will iterate the child elements with that name // `name` will iterate the child elements with that name
pub fn iterate(node: Node, skip: []const []const u8, filter: []const u8) Iterator { pub fn iterate(node: Node, skip: []const []const u8, filter: []const []const u8) Iterator {
var current: Node = node; var current: Node = node;
for (skip) |elem| for (skip) |elem|
current = current.find_child(elem) orelse return Iterator{ current = current.find_child(&.{elem}) orelse return Iterator{
.node = null, .node = null,
.filter = filter, .filter = filter,
}; };
@ -105,7 +113,7 @@ pub const Node = struct {
/// up to you to copy /// up to you to copy
pub fn get_value(node: Node, key: []const u8) ?[:0]const u8 { pub fn get_value(node: Node, key: []const u8) ?[:0]const u8 {
return if (node.find_child(key)) |child| return if (node.find_child(&.{key})) |child|
if (child.impl.children) |value_node| if (child.impl.children) |value_node|
if (@as(*c.xmlNode, @ptrCast(value_node)).content) |content| if (@as(*c.xmlNode, @ptrCast(value_node)).content) |content|
std.mem.span(content) std.mem.span(content)

Loading…
Cancel
Save