diff --git a/tools/regz/src/Database.zig b/tools/regz/src/Database.zig index 1839fac..f959c1f 100644 --- a/tools/regz/src/Database.zig +++ b/tools/regz/src/Database.zig @@ -54,9 +54,6 @@ instances: struct { peripherals: ArrayHashMap(EntityId, EntityId) = .{}, } = .{}, -// to speed up lookups -indexes: struct {} = .{}, - const std = @import("std"); const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; @@ -71,16 +68,109 @@ const dslite = @import("dslite.zig"); const gen = @import("gen.zig"); const regzon = @import("regzon.zig"); +const TypeOfField = @import("testing.zig").TypeOfField; + const Database = @This(); const log = std.log.scoped(.database); -const TypeOfField = @import("testing.zig").TypeOfField; - pub const EntityId = u32; pub const EntitySet = ArrayHashMap(EntityId, void); -// not sure how to communicate the *_once values in generated code besides -// adding it to documentation comments +// concrete arch's that we support in codegen, for stuff like interrupt +// table generation +pub const Arch = enum { + unknown, + + // arm + arm_v81_mml, + arm_v8_mbl, + arm_v8_mml, + cortex_a15, + cortex_a17, + cortex_a5, + cortex_a53, + cortex_a57, + cortex_a7, + cortex_a72, + cortex_a8, + cortex_a9, + cortex_m0, + cortex_m0plus, + cortex_m1, + cortex_m23, + cortex_m3, + cortex_m33, + cortex_m35p, + cortex_m4, + cortex_m55, + cortex_m7, + sc000, // kindof like an m3 + sc300, + // old + arm926ej_s, + + // avr + avr8, + avr8l, + avr8x, + avr8xmega, + + // mips + mips, + + pub fn toString(arch: Arch) []const u8 { + return inline for (@typeInfo(Arch).Enum.fields) |field| { + if (@field(Arch, field.name) == arch) + break field.name; + } else unreachable; + } + + pub fn isArm(arch: Arch) bool { + return switch (arch) { + .cortex_m0, + .cortex_m0plus, + .cortex_m1, + .sc000, // kindof like an m3 + .cortex_m23, + .cortex_m3, + .cortex_m33, + .cortex_m35p, + .cortex_m55, + .sc300, + .cortex_m4, + .cortex_m7, + .arm_v8_mml, + .arm_v8_mbl, + .arm_v81_mml, + .cortex_a5, + .cortex_a7, + .cortex_a8, + .cortex_a9, + .cortex_a15, + .cortex_a17, + .cortex_a53, + .cortex_a57, + .cortex_a72, + .arm926ej_s, + => true, + else => false, + }; + } + + pub fn isAvr(arch: Arch) bool { + return switch (arch) { + .avr8, + .avr8l, + .avr8x, + .avr8xmega, + => true, + else => false, + }; + } +}; + +// not sure how to communicate the *_once values in generated code +// besides adding it to documentation comments pub const Access = enum { read_write, read_only, @@ -90,6 +180,7 @@ pub const Access = enum { }; pub const Device = struct { + arch: Arch, properties: std.StringHashMapUnmanaged([]const u8) = .{}, pub fn deinit(self: *Device, gpa: Allocator) void { @@ -151,10 +242,6 @@ pub fn deinit(db: *Database) void { deinitMapAndValues(db.gpa, &db.instances.devices); db.instances.interrupts.deinit(db.gpa); db.instances.peripherals.deinit(db.gpa); - //db.instances.register_groups.deinit(db.gpa); - //db.instances.registers.deinit(db.gpa); - - // indexes db.arena.deinit(); db.gpa.destroy(db.arena); @@ -246,13 +333,17 @@ pub fn createDevice( opts: struct { // required for now name: []const u8, + arch: Arch = .unknown, }, ) !EntityId { const id = db.createEntity(); errdefer db.destroyEntity(id); log.debug("{}: creating device", .{id}); - try db.instances.devices.put(db.gpa, id, .{}); + try db.instances.devices.put(db.gpa, id, .{ + .arch = opts.arch, + }); + try db.addName(id, opts.name); return id; @@ -478,6 +569,27 @@ pub fn createEnumField( return id; } +pub fn createInterrupt(db: *Database, device_id: EntityId, opts: struct { + name: []const u8, + index: i32, + description: ?[]const u8 = null, +}) !EntityId { + assert(db.entityIs("instance.device", device_id)); + + const id = db.createEntity(); + errdefer db.destroyEntity(id); + + log.debug("{}: creating interrupt", .{id}); + try db.instances.interrupts.put(db.gpa, id, opts.index); + try db.addName(id, opts.name); + + if (opts.description) |d| + try db.addDescription(id, d); + + try db.addChild("instance.interrupt", device_id, id); + return id; +} + pub fn addName(db: *Database, id: EntityId, name: []const u8) !void { if (name.len == 0) return; @@ -620,7 +732,6 @@ pub fn getEntityIdByName( return while (it.next()) |entry| { const entry_id = entry.key_ptr.*; const entry_name = db.attrs.name.get(entry_id) orelse continue; - log.debug("looking at name: {s}", .{entry_name}); if (std.mem.eql(u8, name, entry_name)) { assert(db.entityIs(entity_location, entry_id)); return entry_id; @@ -712,8 +823,9 @@ pub fn toZig(db: Database, out_writer: anytype) !void { test "all" { @setEvalBranchQuota(2000); - std.testing.refAllDeclsRecursive(svd); std.testing.refAllDeclsRecursive(atdf); std.testing.refAllDeclsRecursive(dslite); std.testing.refAllDeclsRecursive(gen); + std.testing.refAllDeclsRecursive(regzon); + std.testing.refAllDeclsRecursive(svd); } diff --git a/tools/regz/src/atdf.zig b/tools/regz/src/atdf.zig index 4a8601d..c9b296e 100644 --- a/tools/regz/src/atdf.zig +++ b/tools/regz/src/atdf.zig @@ -10,22 +10,46 @@ const xml = @import("xml.zig"); const log = std.log.scoped(.atdf); +const InterruptGroupEntry = struct { + name: []const u8, + index: i32, + description: ?[]const u8, +}; + +const Context = struct { + db: *Database, + interrupt_groups: std.StringHashMapUnmanaged(std.ArrayListUnmanaged(InterruptGroupEntry)) = .{}, + + fn deinit(ctx: *Context) void { + { + var it = ctx.interrupt_groups.iterator(); + while (it.next()) |entry| + entry.value_ptr.deinit(ctx.db.gpa); + } + + ctx.interrupt_groups.deinit(ctx.db.gpa); + } +}; + // TODO: scratchpad datastructure for temporary string based relationships, // then stitch it all together in the end pub fn loadIntoDb(db: *Database, doc: xml.Doc) !void { + var ctx = Context{ .db = db }; + defer ctx.deinit(); + const root = try doc.getRootElement(); var module_it = root.iterate(&.{"modules"}, "module"); while (module_it.next()) |entry| - try loadModuleType(db, entry); + try loadModuleType(&ctx, entry); var device_it = root.iterate(&.{"devices"}, "device"); while (device_it.next()) |entry| - try loadDevice(db, entry); + try loadDevice(&ctx, entry); db.assertValid(); } -fn loadDevice(db: *Database, node: xml.Node) !void { +fn loadDevice(ctx: *Context, node: xml.Node) !void { validateAttrs(node, &.{ "architecture", "name", @@ -33,15 +57,17 @@ fn loadDevice(db: *Database, node: xml.Node) !void { "series", }); - const id = db.createEntity(); - errdefer db.destroyEntity(id); - - log.debug("{}: creating device", .{id}); const name = node.getAttribute("name") orelse return error.NoDeviceName; const arch = node.getAttribute("architecture") orelse return error.NoDeviceArch; const family = node.getAttribute("family") orelse return error.NoDeviceFamily; - try db.instances.devices.put(db.gpa, id, .{}); - try db.addName(id, name); + + const db = ctx.db; + const id = try db.createDevice(.{ + .name = name, + .arch = archFromStr(arch), + }); + errdefer db.destroyEntity(id); + try db.addDeviceProperty(id, "arch", arch); try db.addDeviceProperty(id, "family", family); if (node.getAttribute("series")) |series| @@ -49,30 +75,84 @@ fn loadDevice(db: *Database, node: xml.Node) !void { var module_it = node.iterate(&.{"peripherals"}, "module"); while (module_it.next()) |module_node| - loadModuleInstances(db, module_node, id) catch |err| { + loadModuleInstances(ctx, module_node, id) catch |err| { log.warn("failed to instantiate module: {}", .{err}); }; - var interrupt_it = node.iterate(&.{"interrupts"}, "interrupt"); - while (interrupt_it.next()) |interrupt_node| - try loadInterrupt(db, interrupt_node, id); + if (node.findChild("interrupts")) |interrupts_node| + try loadInterrupts(ctx, interrupts_node, id); - try inferPeripheralOffsets(db); - try inferEnumSizes(db); + try inferPeripheralOffsets(ctx); + try inferEnumSizes(ctx); // TODO: // address-space.memory-segment // events.generators.generator // events.users.user // interfaces.interface.parameters.param - // TODO: This is capitalized for some reason :facepalm: - // interrupts.Interrupt - // interrupts.interrupt-group // parameters.param // property-groups.property-group.property } +fn loadInterrupts(ctx: *Context, node: xml.Node, device_id: EntityId) !void { + var interrupt_it = node.iterate(&.{}, "interrupt"); + while (interrupt_it.next()) |interrupt_node| + try loadInterrupt(ctx, interrupt_node, device_id); + + var interrupt_group_it = node.iterate(&.{}, "interrupt-group"); + while (interrupt_group_it.next()) |interrupt_group_node| + try loadInterruptGroup(ctx, interrupt_group_node, device_id); +} + +fn loadInterruptGroup(ctx: *Context, node: xml.Node, device_id: EntityId) !void { + const db = ctx.db; + const module_instance = node.getAttribute("module-instance") orelse return error.MissingModuleInstance; + const name_in_module = node.getAttribute("name-in-module") orelse return error.MissingNameInModule; + const index_str = node.getAttribute("index") orelse return error.MissingInterruptGroupIndex; + const index = try std.fmt.parseInt(i32, index_str, 0); + + if (ctx.interrupt_groups.get(name_in_module)) |group_list| { + for (group_list.items) |entry| { + const full_name = try std.mem.join(db.arena.allocator(), "_", &.{ module_instance, entry.name }); + _ = try db.createInterrupt(device_id, .{ + .name = full_name, + .index = entry.index + index, + .description = entry.description, + }); + } + } +} + +fn archFromStr(str: []const u8) Database.Arch { + return if (std.mem.eql(u8, "ARM926EJ-S", str)) + .arm926ej_s + else if (std.mem.eql(u8, "AVR8", str)) + .avr8 + else if (std.mem.eql(u8, "AVR8L", str)) + .avr8l + else if (std.mem.eql(u8, "AVR8X", str)) + .avr8x + else if (std.mem.eql(u8, "AVR8_XMEGA", str)) + .avr8xmega + else if (std.mem.eql(u8, "CORTEX-A5", str)) + .cortex_a5 + else if (std.mem.eql(u8, "CORTEX-A7", str)) + .cortex_a7 + else if (std.mem.eql(u8, "CORTEX-M0PLUS", str)) + .cortex_m0plus + else if (std.mem.eql(u8, "CORTEX-M23", str)) + .cortex_m23 + else if (std.mem.eql(u8, "CORTEX-M4", str)) + .cortex_m4 + else if (std.mem.eql(u8, "CORTEX-M7", str)) + .cortex_m7 + else if (std.mem.eql(u8, "MIPS", str)) + .mips + else + .unknown; +} + // This function is intended to normalize the struct layout of some peripheral // instances. Like in ATmega328P there will be register groups with offset of 0 // and then the registers themselves have their absolute offset. We'd like to @@ -80,7 +160,8 @@ fn loadDevice(db: *Database, node: xml.Node) !void { // the register group to the beginning of its registers (and adjust registers // accordingly). This should make it easier to determine what register groups // might be of the same "type". -fn inferPeripheralOffsets(db: *Database) !void { +fn inferPeripheralOffsets(ctx: *Context) !void { + const db = ctx.db; // only infer the peripheral offset if there is only one instance for a given type. var type_counts = std.AutoArrayHashMap(EntityId, struct { count: usize, instance_id: EntityId }).init(db.gpa); defer type_counts.deinit(); @@ -102,12 +183,13 @@ fn inferPeripheralOffsets(db: *Database) !void { while (type_it.next()) |type_entry| if (type_entry.value_ptr.count == 1) { const type_id = type_entry.key_ptr.*; const instance_id = type_entry.value_ptr.instance_id; - inferPeripheralOffset(db, type_id, instance_id) catch |err| + inferPeripheralOffset(ctx, type_id, instance_id) catch |err| log.warn("failed to infer peripheral instance offset: {}", .{err}); }; } -fn inferPeripheralOffset(db: *Database, type_id: EntityId, instance_id: EntityId) !void { +fn inferPeripheralOffset(ctx: *Context, type_id: EntityId, instance_id: EntityId) !void { + const db = ctx.db; // TODO: assert that there's only one instance using this type var min_offset: ?u64 = null; @@ -140,7 +222,8 @@ fn inferPeripheralOffset(db: *Database, type_id: EntityId, instance_id: EntityId // for each enum in the database get its max value, each field that references // it, and determine the size of the enum -fn inferEnumSizes(db: *Database) !void { +fn inferEnumSizes(ctx: *Context) !void { + const db = ctx.db; var enum_it = db.types.enums.iterator(); while (enum_it.next()) |entry| { const enum_id = entry.key_ptr.*; @@ -226,7 +309,7 @@ fn getInlinedRegisterGroup(parent_node: xml.Node, parent_name: []const u8) ?xml. } // module instances are listed under atdf-tools-device-file.modules. -fn loadModuleType(db: *Database, node: xml.Node) !void { +fn loadModuleType(ctx: *Context, node: xml.Node) !void { validateAttrs(node, &.{ "oldname", "name", @@ -236,6 +319,7 @@ fn loadModuleType(db: *Database, node: xml.Node) !void { "name2", }); + const db = ctx.db; const id = db.createEntity(); errdefer db.destroyEntity(id); @@ -249,49 +333,80 @@ fn loadModuleType(db: *Database, node: xml.Node) !void { var value_group_it = node.iterate(&.{}, "value-group"); while (value_group_it.next()) |value_group_node| - try loadEnum(db, value_group_node, id); + try loadEnum(ctx, value_group_node, id); + + var interrupt_group_it = node.iterate(&.{}, "interrupt-group"); + while (interrupt_group_it.next()) |interrupt_group_node| + try loadModuleInterruptGroup(ctx, interrupt_group_node); // special case but the most common, if there is only one register // group and it's name matches the peripheral, then inline the // registers. This operation needs to be done in // `loadModuleInstance()` as well if (getInlinedRegisterGroup(node, name)) |register_group_node| { - try loadRegisterGroupChildren(db, register_group_node, id); + try loadRegisterGroupChildren(ctx, register_group_node, id); } else { var register_group_it = node.iterate(&.{}, "register-group"); while (register_group_it.next()) |register_group_node| - try loadRegisterGroup(db, register_group_node, id); + try loadRegisterGroup(ctx, register_group_node, id); } +} + +fn loadModuleInterruptGroup(ctx: *Context, node: xml.Node) !void { + const name = node.getAttribute("name") orelse return error.MissingInterruptGroupName; + try ctx.interrupt_groups.put(ctx.db.gpa, name, .{}); + + var interrupt_it = node.iterate(&.{}, "interrupt"); + while (interrupt_it.next()) |interrupt_node| + try loadModuleInterruptGroupEntry(ctx, interrupt_node, name); +} - // TODO: interrupt-group +fn loadModuleInterruptGroupEntry( + ctx: *Context, + node: xml.Node, + group_name: []const u8, +) !void { + assert(ctx.interrupt_groups.contains(group_name)); + const list = ctx.interrupt_groups.getEntry(group_name).?.value_ptr; + + try list.append(ctx.db.gpa, .{ + .name = node.getAttribute("name") orelse return error.MissingInterruptName, + .index = if (node.getAttribute("index")) |index_str| + try std.fmt.parseInt(i32, index_str, 0) + else + return error.MissingInterruptIndex, + .description = node.getAttribute("caption"), + }); } fn loadRegisterGroupChildren( - db: *Database, + ctx: *Context, node: xml.Node, dest_id: EntityId, ) !void { + const db = ctx.db; assert(db.entityIs("type.peripheral", dest_id) or db.entityIs("type.register_group", dest_id)); var mode_it = node.iterate(&.{}, "mode"); while (mode_it.next()) |mode_node| - loadMode(db, mode_node, dest_id) catch |err| { + loadMode(ctx, mode_node, dest_id) catch |err| { log.err("{}: failed to load mode: {}", .{ dest_id, err }); }; var register_it = node.iterate(&.{}, "register"); while (register_it.next()) |register_node| - try loadRegister(db, register_node, dest_id); + try loadRegister(ctx, register_node, dest_id); } // loads a register group which is under a peripheral or under another // register-group fn loadRegisterGroup( - db: *Database, + ctx: *Context, node: xml.Node, parent_id: EntityId, ) !void { + const db = ctx.db; assert(db.entityIs("type.peripheral", parent_id) or db.entityIs("type.register_group", parent_id)); @@ -332,14 +447,15 @@ fn loadRegisterGroup( if (node.getAttribute("size")) |size| try db.addSize(id, try std.fmt.parseInt(u64, size, 0)); - try loadRegisterGroupChildren(db, node, id); + try loadRegisterGroupChildren(ctx, node, id); // TODO: register-group // connect with parent try db.addChild("type.register_group", parent_id, id); } -fn loadMode(db: *Database, node: xml.Node, parent_id: EntityId) !void { +fn loadMode(ctx: *Context, node: xml.Node, parent_id: EntityId) !void { + const db = ctx.db; assert(db.entityIs("type.peripheral", parent_id) or db.entityIs("type.register_group", parent_id) or db.entityIs("type.register", parent_id)); @@ -376,11 +492,12 @@ fn loadMode(db: *Database, node: xml.Node, parent_id: EntityId) !void { // then we have our entry. If not found then the input is malformed. // TODO: assert unique mode name fn assignModesToEntity( - db: *Database, + ctx: *Context, id: EntityId, parent_id: EntityId, mode_names: []const u8, ) !void { + const db = ctx.db; var modes = Database.Modes{}; errdefer modes.deinit(db.gpa); @@ -426,10 +543,11 @@ fn assignModesToEntity( } fn loadRegister( - db: *Database, + ctx: *Context, node: xml.Node, parent_id: EntityId, ) !void { + const db = ctx.db; assert(db.entityIs("type.register_group", parent_id) or db.entityIs("type.peripheral", parent_id)); @@ -468,7 +586,7 @@ fn loadRegister( errdefer db.destroyEntity(id); if (node.getAttribute("modes")) |modes| - assignModesToEntity(db, id, parent_id, modes) catch { + assignModesToEntity(ctx, id, parent_id, modes) catch { log.warn("failed to find mode '{s}' for register '{s}'", .{ modes, name, @@ -495,16 +613,17 @@ fn loadRegister( // assumes that modes are parsed before registers in the register group var mode_it = node.iterate(&.{}, "mode"); while (mode_it.next()) |mode_node| - loadMode(db, mode_node, id) catch |err| { + loadMode(ctx, mode_node, id) catch |err| { log.err("{}: failed to load mode: {}", .{ id, err }); }; var field_it = node.iterate(&.{}, "bitfield"); while (field_it.next()) |field_node| - loadField(db, field_node, id) catch {}; + loadField(ctx, field_node, id) catch {}; } -fn loadField(db: *Database, node: xml.Node, register_id: EntityId) !void { +fn loadField(ctx: *Context, node: xml.Node, register_id: EntityId) !void { + const db = ctx.db; assert(db.entityIs("type.register", register_id)); validateAttrs(node, &.{ "caption", @@ -553,7 +672,7 @@ fn loadField(db: *Database, node: xml.Node, register_id: EntityId) !void { errdefer db.destroyEntity(id); if (node.getAttribute("modes")) |modes| - assignModesToEntity(db, id, register_id, modes) catch { + assignModesToEntity(ctx, id, register_id, modes) catch { log.warn("failed to find mode '{s}' for field '{s}'", .{ modes, name, @@ -588,7 +707,7 @@ fn loadField(db: *Database, node: xml.Node, register_id: EntityId) !void { // TODO: modes are space delimited, and multiple can apply to a single bitfield or register if (node.getAttribute("modes")) |modes| - assignModesToEntity(db, id, register_id, modes) catch { + assignModesToEntity(ctx, id, register_id, modes) catch { log.warn("failed to find mode '{s}' for field '{s}'", .{ modes, name, @@ -636,10 +755,11 @@ fn accessFromString(str: []const u8) !Database.Access { } fn loadEnum( - db: *Database, + ctx: *Context, node: xml.Node, peripheral_id: EntityId, ) !void { + const db = ctx.db; assert(db.entityIs("type.peripheral", peripheral_id)); validateAttrs(node, &.{ @@ -659,16 +779,17 @@ fn loadEnum( var value_it = node.iterate(&.{}, "value"); while (value_it.next()) |value_node| - loadEnumField(db, value_node, id) catch {}; + loadEnumField(ctx, value_node, id) catch {}; try db.addChild("type.enum", peripheral_id, id); } fn loadEnumField( - db: *Database, + ctx: *Context, node: xml.Node, enum_id: EntityId, ) !void { + const db = ctx.db; assert(db.entityIs("type.enum", enum_id)); validateAttrs(node, &.{ @@ -705,10 +826,11 @@ fn loadEnumField( // module instances are listed under atdf-tools-device-file.devices.device.peripherals fn loadModuleInstances( - db: *Database, + ctx: *Context, node: xml.Node, device_id: EntityId, ) !void { + const db = ctx.db; const module_name = node.getAttribute("name") orelse return error.MissingModuleName; const type_id = blk: { var periph_it = db.types.peripherals.iterator(); @@ -726,7 +848,7 @@ fn loadModuleInstances( var instance_it = node.iterate(&.{}, "instance"); while (instance_it.next()) |instance_node| - try loadModuleInstance(db, instance_node, device_id, type_id); + try loadModuleInstance(ctx, instance_node, device_id, type_id); } fn peripheralIsInlined(db: Database, id: EntityId) bool { @@ -735,11 +857,12 @@ fn peripheralIsInlined(db: Database, id: EntityId) bool { } fn loadModuleInstance( - db: *Database, + ctx: *Context, node: xml.Node, device_id: EntityId, peripheral_type_id: EntityId, ) !void { + const db = ctx.db; assert(db.entityIs("type.peripheral", peripheral_type_id)); validateAttrs(node, &.{ @@ -752,17 +875,18 @@ fn loadModuleInstance( // that they're used as variants of a peripheral, and never used like // clusters in SVD. return if (peripheralIsInlined(db.*, peripheral_type_id)) - loadModuleInstanceFromPeripheral(db, node, device_id, peripheral_type_id) + loadModuleInstanceFromPeripheral(ctx, node, device_id, peripheral_type_id) else - loadModuleInstanceFromRegisterGroup(db, node, device_id, peripheral_type_id); + loadModuleInstanceFromRegisterGroup(ctx, node, device_id, peripheral_type_id); } fn loadModuleInstanceFromPeripheral( - db: *Database, + ctx: *Context, node: xml.Node, device_id: EntityId, peripheral_type_id: EntityId, ) !void { + const db = ctx.db; const id = db.createEntity(); errdefer db.destroyEntity(id); @@ -790,17 +914,18 @@ fn loadModuleInstanceFromPeripheral( var signal_it = node.iterate(&.{"signals"}, "signal"); while (signal_it.next()) |signal_node| - try loadSignal(db, signal_node, id); + try loadSignal(ctx, signal_node, id); try db.addChild("instance.peripheral", device_id, id); } fn loadModuleInstanceFromRegisterGroup( - db: *Database, + ctx: *Context, node: xml.Node, device_id: EntityId, peripheral_type_id: EntityId, ) !void { + const db = ctx.db; const register_group_node = blk: { var it = node.iterate(&.{}, "register-group"); const ret = it.next() orelse return error.MissingInstanceRegisterGroup; @@ -841,11 +966,12 @@ fn loadModuleInstanceFromRegisterGroup( } fn loadRegisterGroupInstance( - db: *Database, + ctx: *Context, node: xml.Node, peripheral_id: EntityId, peripheral_type_id: EntityId, ) !void { + const db = ctx.db; assert(db.entityIs("instance.peripheral", peripheral_id)); assert(db.entityIs("type.peripheral", peripheral_type_id)); validateAttrs(node, &.{ @@ -908,7 +1034,8 @@ fn loadRegisterGroupInstance( // "id": "optional", } -fn loadSignal(db: *Database, node: xml.Node, peripheral_id: EntityId) !void { +fn loadSignal(ctx: *Context, node: xml.Node, peripheral_id: EntityId) !void { + const db = ctx.db; assert(db.entityIs("instance.peripheral", peripheral_id)); validateAttrs(node, &.{ "group", @@ -923,7 +1050,8 @@ fn loadSignal(db: *Database, node: xml.Node, peripheral_id: EntityId) !void { } // TODO: there are fields like irq-index -fn loadInterrupt(db: *Database, node: xml.Node, device_id: EntityId) !void { +fn loadInterrupt(ctx: *Context, node: xml.Node, device_id: EntityId) !void { + const db = ctx.db; assert(db.entityIs("instance.device", device_id)); validateAttrs(node, &.{ "index", @@ -938,9 +1066,6 @@ fn loadInterrupt(db: *Database, node: xml.Node, device_id: EntityId) !void { "alternate-caption", }); - const id = db.createEntity(); - errdefer db.destroyEntity(id); - const name = node.getAttribute("name") orelse return error.MissingInterruptName; const index_str = node.getAttribute("index") orelse return error.MissingInterruptIndex; const index = std.fmt.parseInt(i32, index_str, 0) catch |err| { @@ -951,13 +1076,16 @@ fn loadInterrupt(db: *Database, node: xml.Node, device_id: EntityId) !void { return err; }; - log.debug("{}: creating interrupt {}", .{ id, index }); - try db.instances.interrupts.put(db.gpa, id, index); - try db.addName(id, name); - if (node.getAttribute("caption")) |caption| - try db.addDescription(id, caption); + const full_name = if (node.getAttribute("module-instance")) |module_instance| + try std.mem.join(db.arena.allocator(), "_", &.{ module_instance, name }) + else + name; - try db.addChild("instance.interrupt", device_id, id); + _ = try db.createInterrupt(device_id, .{ + .name = full_name, + .index = index, + .description = node.getAttribute("caption"), + }); } // for now just emit warning logs when the input has attributes that it shouldn't have @@ -1295,8 +1423,87 @@ test "atdf.instance of register group" { try expectAttr(db, "offset", 0x0, pinb_id); } -test "log2_int_ceil" { - try expectEqual(@as(u64, 6), std.math.log2_int(u64, 90)); - try expectEqual(@as(u64, 7), std.math.log2_int(u64, 255)); - try expectEqual(@as(u64, 8), std.math.log2_int(u64, 256)); +test "atdf.interrupts" { + const text = + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ; + + var doc = try xml.Doc.fromMemory(text); + var db = try Database.initFromAtdf(std.testing.allocator, doc); + defer db.deinit(); + + const vector1_id = try db.getEntityIdByName("instance.interrupt", "TEST_VECTOR1"); + try expectEqual(@as(i32, 1), db.instances.interrupts.get(vector1_id).?); + + const vector2_id = try db.getEntityIdByName("instance.interrupt", "TEST_VECTOR2"); + try expectEqual(@as(i32, 5), db.instances.interrupts.get(vector2_id).?); +} + +test "atdf.interrupts with module-instance" { + const text = + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ; + + var doc = try xml.Doc.fromMemory(text); + var db = try Database.initFromAtdf(std.testing.allocator, doc); + defer db.deinit(); + + const crcscan_nmi_id = try db.getEntityIdByName("instance.interrupt", "CRCSCAN_NMI"); + try expectEqual(@as(i32, 1), db.instances.interrupts.get(crcscan_nmi_id).?); + + const bod_vlm_id = try db.getEntityIdByName("instance.interrupt", "BOD_VLM"); + try expectEqual(@as(i32, 2), db.instances.interrupts.get(bod_vlm_id).?); +} + +test "atdf.interrupts with interrupt-groups" { + const text = + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ; + + var doc = try xml.Doc.fromMemory(text); + var db = try Database.initFromAtdf(std.testing.allocator, doc); + defer db.deinit(); + + const portb_int0_id = try db.getEntityIdByName("instance.interrupt", "PORTB_INT0"); + try expectEqual(@as(i32, 1), db.instances.interrupts.get(portb_int0_id).?); + + const portb_int1_id = try db.getEntityIdByName("instance.interrupt", "PORTB_INT1"); + try expectEqual(@as(i32, 2), db.instances.interrupts.get(portb_int1_id).?); } diff --git a/tools/regz/src/gen.zig b/tools/regz/src/gen.zig index b80cbe7..aac902e 100644 --- a/tools/regz/src/gen.zig +++ b/tools/regz/src/gen.zig @@ -7,6 +7,9 @@ const Database = @import("Database.zig"); const EntityId = Database.EntityId; const EntitySet = Database.EntitySet; +const arm = @import("gen/arm.zig"); +const avr = @import("gen/avr.zig"); + const log = std.log.scoped(.gen); const EntityWithOffsetAndSize = struct { @@ -29,7 +32,11 @@ pub fn toZig(db: Database, out_writer: anytype) !void { defer buffer.deinit(); const writer = buffer.writer(); - try writer.writeAll("const mmio = @import(\"mmio\");\n"); + try writer.writeAll( + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; + \\ + ); try writeDevices(db, writer); try writeTypes(db, writer); try writer.writeByte(0); @@ -67,7 +74,7 @@ fn writeDevices(db: Database, writer: anytype) !void { try writer.writeAll("};\n"); } -fn writeComment(allocator: Allocator, comment: []const u8, writer: anytype) !void { +pub fn writeComment(allocator: Allocator, comment: []const u8, writer: anytype) !void { var tokenized = std.ArrayList(u8).init(allocator); defer tokenized.deinit(); @@ -120,7 +127,8 @@ fn writeDevice(db: Database, device_id: EntityId, out_writer: anytype) !void { // TODO: alphabetic order const properties = db.instances.devices.get(device_id).?.properties; - { + if (properties.count() > 0) { + try writer.writeAll("pub const properties = struct {\n"); var it = properties.iterator(); while (it.next()) |entry| { try writer.print("pub const {s} = ", .{ @@ -131,10 +139,11 @@ fn writeDevice(db: Database, device_id: EntityId, out_writer: anytype) !void { try writer.writeAll(";\n"); } - try writer.writeByte('\n'); + try writer.writeAll("};\n\n"); } - // TODO: interrupts + writeVectorTable(db, device_id, writer) catch |err| + log.warn("failed to write vector table: {}", .{err}); if (db.children.peripherals.get(device_id)) |peripheral_set| { var list = std.ArrayList(EntityWithOffset).init(db.gpa); @@ -148,10 +157,14 @@ fn writeDevice(db: Database, device_id: EntityId, out_writer: anytype) !void { } std.sort.sort(EntityWithOffset, list.items, {}, EntityWithOffset.lessThan); + + try writer.writeAll("pub const peripherals = struct {\n"); for (list.items) |periph| writePeripheralInstance(db, periph.id, periph.offset, writer) catch |err| { log.warn("failed to serialize peripheral instance: {}", .{err}); }; + + try writer.writeAll("};\n"); } try writer.writeAll("};\n"); @@ -193,6 +206,30 @@ fn typesReference(db: Database, type_id: EntityId) ![]const u8 { return full_name.toOwnedSlice(); } +fn writeVectorTable( + db: Database, + device_id: EntityId, + out_writer: anytype, +) !void { + assert(db.entityIs("instance.device", device_id)); + + var buffer = std.ArrayList(u8).init(db.arena.allocator()); + defer buffer.deinit(); + + const writer = buffer.writer(); + const arch = db.instances.devices.get(device_id).?.arch; + if (arch.isArm()) + try arm.writeInterruptVector(db, device_id, writer) + else if (arch.isAvr()) + try avr.writeInterruptVector(db, device_id, writer) + else if (arch == .unknown) + return + else + unreachable; + + try out_writer.writeAll(buffer.items); +} + fn writePeripheralInstance(db: Database, instance_id: EntityId, offset: u64, out_writer: anytype) !void { assert(db.entityIs("instance.peripheral", instance_id)); var buffer = std.ArrayList(u8).init(db.arena.allocator()); @@ -832,7 +869,8 @@ test "gen.peripheral type with register and field" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const types = struct { \\ pub const TEST_PERIPHERAL = extern struct { @@ -880,11 +918,14 @@ test "gen.peripheral instantiation" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const devices = struct { \\ pub const TEST_DEVICE = struct { - \\ pub const TEST0 = @ptrCast(*volatile types.TEST_PERIPHERAL, 0x1000); + \\ pub const peripherals = struct { + \\ pub const TEST0 = @ptrCast(*volatile types.TEST_PERIPHERAL, 0x1000); + \\ }; \\ }; \\}; \\ @@ -932,12 +973,15 @@ test "gen.peripherals with a shared type" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const devices = struct { \\ pub const TEST_DEVICE = struct { - \\ pub const TEST0 = @ptrCast(*volatile types.TEST_PERIPHERAL, 0x1000); - \\ pub const TEST1 = @ptrCast(*volatile types.TEST_PERIPHERAL, 0x2000); + \\ pub const peripherals = struct { + \\ pub const TEST0 = @ptrCast(*volatile types.TEST_PERIPHERAL, 0x1000); + \\ pub const TEST1 = @ptrCast(*volatile types.TEST_PERIPHERAL, 0x2000); + \\ }; \\ }; \\}; \\ @@ -1007,7 +1051,8 @@ test "gen.peripheral with modes" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const types = struct { \\ pub const TEST_PERIPHERAL = extern union { @@ -1082,7 +1127,8 @@ test "gen.peripheral with enum" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const types = struct { \\ pub const TEST_PERIPHERAL = extern struct { @@ -1126,7 +1172,8 @@ test "gen.peripheral with enum, enum is exhausted of values" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const types = struct { \\ pub const TEST_PERIPHERAL = extern struct { @@ -1176,7 +1223,8 @@ test "gen.field with named enum" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const types = struct { \\ pub const TEST_PERIPHERAL = extern struct { @@ -1232,7 +1280,8 @@ test "gen.field with anonymous enum" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const types = struct { \\ pub const TEST_PERIPHERAL = extern struct { @@ -1286,12 +1335,15 @@ test "gen.namespaced register groups" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const devices = struct { \\ pub const ATmega328P = struct { - \\ pub const PORTB = @ptrCast(*volatile types.PORT.PORTB, 0x23); - \\ pub const PORTC = @ptrCast(*volatile types.PORT.PORTC, 0x26); + \\ pub const peripherals = struct { + \\ pub const PORTB = @ptrCast(*volatile types.PORT.PORTB, 0x23); + \\ pub const PORTC = @ptrCast(*volatile types.PORT.PORTC, 0x26); + \\ }; \\ }; \\}; \\ @@ -1339,11 +1391,14 @@ test "gen.peripheral with reserved register" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const devices = struct { \\ pub const ATmega328P = struct { - \\ pub const PORTB = @ptrCast(*volatile types.PORTB, 0x23); + \\ pub const peripherals = struct { + \\ pub const PORTB = @ptrCast(*volatile types.PORTB, 0x23); + \\ }; \\ }; \\}; \\ @@ -1384,11 +1439,14 @@ test "gen.peripheral with count" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const devices = struct { \\ pub const ATmega328P = struct { - \\ pub const PORTB = @ptrCast(*volatile [4]types.PORTB, 0x23); + \\ pub const peripherals = struct { + \\ pub const PORTB = @ptrCast(*volatile [4]types.PORTB, 0x23); + \\ }; \\ }; \\}; \\ @@ -1429,11 +1487,14 @@ test "gen.peripheral with count, padding required" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const devices = struct { \\ pub const ATmega328P = struct { - \\ pub const PORTB = @ptrCast(*volatile [4]types.PORTB, 0x23); + \\ pub const peripherals = struct { + \\ pub const PORTB = @ptrCast(*volatile [4]types.PORTB, 0x23); + \\ }; \\ }; \\}; \\ @@ -1473,11 +1534,14 @@ test "gen.register with count" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const devices = struct { \\ pub const ATmega328P = struct { - \\ pub const PORTB = @ptrCast(*volatile types.PORTB, 0x23); + \\ pub const peripherals = struct { + \\ pub const PORTB = @ptrCast(*volatile types.PORTB, 0x23); + \\ }; \\ }; \\}; \\ @@ -1528,11 +1592,14 @@ test "gen.register with count and fields" { try db.toZig(buffer.writer()); try std.testing.expectEqualStrings( - \\const mmio = @import("mmio"); + \\const micro = @import("microzig"); + \\const mmio = micro.mmio; \\ \\pub const devices = struct { \\ pub const ATmega328P = struct { - \\ pub const PORTB = @ptrCast(*volatile types.PORTB, 0x23); + \\ pub const peripherals = struct { + \\ pub const PORTB = @ptrCast(*volatile types.PORTB, 0x23); + \\ }; \\ }; \\}; \\ @@ -1549,3 +1616,47 @@ test "gen.register with count and fields" { \\ , buffer.items); } + +test "gen.interrupts.avr" { + var db = try Database.init(std.testing.allocator); + defer db.deinit(); + + const device_id = try db.createDevice(.{ + .name = "ATmega328P", + .arch = .avr8, + }); + + _ = try db.createInterrupt(device_id, .{ + .name = "TEST_VECTOR1", + .index = 1, + }); + + _ = try db.createInterrupt(device_id, .{ + .name = "TEST_VECTOR2", + .index = 3, + }); + + 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 devices = struct { + \\ pub const ATmega328P = struct { + \\ pub const VectorTable = extern struct { + \\ const Handler = micro.interrupt.Handler; + \\ const unhandled = micro.interrupt.unhandled; + \\ + \\ RESET: Handler = unhandled, + \\ TEST_VECTOR1: Handler = unhandled, + \\ reserved2: [1]u16 = undefined, + \\ TEST_VECTOR2: Handler = unhandled, + \\ }; + \\ }; + \\}; + \\ + , buffer.items); +} diff --git a/tools/regz/src/gen/InterruptWithIndexAndName.zig b/tools/regz/src/gen/InterruptWithIndexAndName.zig new file mode 100644 index 0000000..9720f94 --- /dev/null +++ b/tools/regz/src/gen/InterruptWithIndexAndName.zig @@ -0,0 +1,14 @@ +id: EntityId, +name: []const u8, +index: i32, + +const InterruptWithIndexAndName = @This(); +const EntityId = @import("../Database.zig").EntityId; + +pub fn lessThan( + _: void, + lhs: InterruptWithIndexAndName, + rhs: InterruptWithIndexAndName, +) bool { + return lhs.index < rhs.index; +} diff --git a/tools/regz/src/gen/arm.zig b/tools/regz/src/gen/arm.zig new file mode 100644 index 0000000..c7611da --- /dev/null +++ b/tools/regz/src/gen/arm.zig @@ -0,0 +1,29 @@ +//! codegen specific to arm +const std = @import("std"); +const assert = std.debug.assert; + +const Database = @import("../Database.zig"); +const Arch = Database.Arch; +const EntityId = Database.EntityId; + +const InterruptWithIndexAndName = @import("InterruptWithIndexAndName.zig"); + +const log = std.log.scoped(.@"gen.arm"); + +pub fn writeInterruptVector( + db: Database, + device_id: EntityId, + writer: anytype, +) !void { + assert(db.entityIs("instance.device", device_id)); + const arch = db.instances.devices.get(device_id).?.arch; + assert(arch.isArm()); + + log.warn("TODO: implement interrupt table for arch: {}", .{arch}); + + _ = writer; + //try writer.writeAll("pub const VectorTable = extern struct {\n"); + // TODO: fill + //try writer.writeAll("};\n"); + return error.Todo; +} diff --git a/tools/regz/src/gen/avr.zig b/tools/regz/src/gen/avr.zig new file mode 100644 index 0000000..ba76c3b --- /dev/null +++ b/tools/regz/src/gen/avr.zig @@ -0,0 +1,84 @@ +//! codegen specific to AVR +const std = @import("std"); +const assert = std.debug.assert; + +const Database = @import("../Database.zig"); +const Arch = Database.Arch; +const EntityId = Database.EntityId; + +const gen = @import("../gen.zig"); +const InterruptWithIndexAndName = @import("InterruptWithIndexAndName.zig"); + +const log = std.log.scoped(.@"gen.avr"); + +pub fn writeInterruptVector( + db: Database, + device_id: EntityId, + writer: anytype, +) !void { + assert(db.entityIs("instance.device", device_id)); + const arch = db.instances.devices.get(device_id).?.arch; + assert(arch.isAvr()); + + try writer.writeAll( + \\pub const VectorTable = extern struct { + \\ const Handler = micro.interrupt.Handler; + \\ const unhandled = micro.interrupt.unhandled; + \\ + \\ RESET: Handler = unhandled, + \\ + ); + + if (db.children.interrupts.get(device_id)) |interrupt_set| { + var interrupts = std.ArrayList(InterruptWithIndexAndName).init(db.gpa); + defer interrupts.deinit(); + + var it = interrupt_set.iterator(); + while (it.next()) |entry| { + const interrupt_id = entry.key_ptr.*; + const index = db.instances.interrupts.get(interrupt_id).?; + const name = db.attrs.name.get(interrupt_id) orelse continue; + + try interrupts.append(.{ + .id = interrupt_id, + .name = name, + .index = index, + }); + } + + std.sort.sort( + InterruptWithIndexAndName, + interrupts.items, + {}, + InterruptWithIndexAndName.lessThan, + ); + + var index: i32 = 1; + var i: u32 = 0; + + while (i < interrupts.items.len) : (i += 1) { + const interrupt = interrupts.items[i]; + if (index < interrupt.index) { + try writer.print("reserved{}: [{}]u16 = undefined,\n", .{ + index, + interrupt.index - index, + }); + index = interrupt.index; + } else if (index > interrupt.index) { + log.warn("skipping interrupt: {s}", .{interrupt.name}); + continue; + } + + if (db.attrs.description.get(interrupt.id)) |description| + try gen.writeComment(db.gpa, description, writer); + + try writer.print("{s}: Handler = unhandled,\n", .{ + std.zig.fmtId(interrupt.name), + }); + + index += 1; + } + } + + try writer.writeAll("};\n\n"); +} diff --git a/tools/regz/src/regzon.zig b/tools/regz/src/regzon.zig index cbf3350..c90b7cb 100644 --- a/tools/regz/src/regzon.zig +++ b/tools/regz/src/regzon.zig @@ -182,6 +182,9 @@ fn populateDevice( entry.value_ptr.*, ); + const arch = db.instances.devices.get(id).?.arch; + try device.put("arch", .{ .String = arch.toString() }); + if (properties.count() > 0) try device.put("properties", .{ .Object = properties }); diff --git a/tools/regz/src/svd.zig b/tools/regz/src/svd.zig index 94c2aa9..fff455f 100644 --- a/tools/regz/src/svd.zig +++ b/tools/regz/src/svd.zig @@ -47,7 +47,9 @@ pub fn loadIntoDb(db: *Database, doc: xml.Doc) !void { const root = try doc.getRootElement(); const device_id = db.createEntity(); - try db.instances.devices.put(db.gpa, device_id, .{}); + try db.instances.devices.put(db.gpa, device_id, .{ + .arch = .unknown, + }); const name = root.getValue("name") orelse return error.MissingDeviceName; try db.addName(device_id, name); @@ -77,8 +79,10 @@ pub fn loadIntoDb(db: *Database, doc: xml.Doc) !void { const nvic_prio_bits = cpu.getValue("nvicPrioBits") orelse return error.MissingNvicPrioBits; const vendor_systick_config = cpu.getValue("vendorSystickConfig") orelse return error.MissingVendorSystickConfig; + db.instances.devices.getEntry(device_id).?.value_ptr.arch = archFromStr(cpu_name); + // cpu name => arch - try db.addDeviceProperty(device_id, "arch", cpu_name); + try db.addDeviceProperty(device_id, "cpu.name", cpu_name); try db.addDeviceProperty(device_id, "cpu.revision", cpu_revision); try db.addDeviceProperty(device_id, "cpu.nvic_prio_bits", nvic_prio_bits); try db.addDeviceProperty(device_id, "cpu.vendor_systick_config", vendor_systick_config); @@ -145,6 +149,61 @@ pub fn loadIntoDb(db: *Database, doc: xml.Doc) !void { db.assertValid(); } +fn archFromStr(str: []const u8) Database.Arch { + return if (std.mem.eql(u8, "CM0", str)) + .cortex_m0 + else if (std.mem.eql(u8, "CM0PLUS", str)) + .cortex_m0plus + else if (std.mem.eql(u8, "CM0+", str)) + .cortex_m0plus + else if (std.mem.eql(u8, "CM1", str)) + .cortex_m1 + else if (std.mem.eql(u8, "SC000", str)) + .sc000 + else if (std.mem.eql(u8, "CM23", str)) + .cortex_m23 + else if (std.mem.eql(u8, "CM3", str)) + .cortex_m3 + else if (std.mem.eql(u8, "CM33", str)) + .cortex_m33 + else if (std.mem.eql(u8, "CM35P", str)) + .cortex_m35p + else if (std.mem.eql(u8, "CM55", str)) + .cortex_m55 + else if (std.mem.eql(u8, "SC300", str)) + .sc300 + else if (std.mem.eql(u8, "CM4", str)) + .cortex_m4 + else if (std.mem.eql(u8, "CM7", str)) + .cortex_m7 + else if (std.mem.eql(u8, "ARMV8MML", str)) + .arm_v81_mml + else if (std.mem.eql(u8, "ARMV8MBL", str)) + .arm_v8_mbl + else if (std.mem.eql(u8, "ARMV81MML", str)) + .arm_v8_mml + else if (std.mem.eql(u8, "CA5", str)) + .cortex_a5 + else if (std.mem.eql(u8, "CA7", str)) + .cortex_a7 + else if (std.mem.eql(u8, "CA8", str)) + .cortex_a8 + else if (std.mem.eql(u8, "CA9", str)) + .cortex_a9 + else if (std.mem.eql(u8, "CA15", str)) + .cortex_a15 + else if (std.mem.eql(u8, "CA17", str)) + .cortex_a17 + else if (std.mem.eql(u8, "CA53", str)) + .cortex_a53 + else if (std.mem.eql(u8, "CA57", str)) + .cortex_a57 + else if (std.mem.eql(u8, "CA72", str)) + .cortex_a72 + else + .unknown; +} + pub fn deriveEntity(db: Database, id: EntityId, derived_name: []const u8) !void { log.debug("{}: derived from {s}", .{ id, derived_name }); const entity_type = db.getEntityType(id); @@ -220,6 +279,7 @@ pub fn loadPeripheral(ctx: *Context, node: xml.Node, device_id: EntityId) !void fn loadPeripheralType(ctx: *Context, node: xml.Node) !EntityId { const db = ctx.db; + // TODO: get version const id = try db.createPeripheral(.{ .name = node.getValue("name") orelse return error.PeripheralMissingName, }); @@ -390,7 +450,13 @@ fn loadEnumeratedValue(ctx: *Context, node: xml.Node, enum_id: EntityId) !void { assert(db.entityIs("type.enum", enum_id)); const id = try db.createEnumField(enum_id, .{ - .name = node.getValue("name") orelse return error.EnumFieldMissingName, + .name = if (node.getValue("name")) |name| + if (std.mem.eql(u8, "_", name)) + return error.InvalidEnumFieldName + else + name + else + return error.EnumFieldMissingName, .description = node.getValue("description"), .value = if (node.getValue("value")) |value_str| try std.fmt.parseInt(u32, value_str, 0) @@ -416,7 +482,12 @@ pub const Revision = struct { } }; -pub const Endian = enum { little, big, selectable, other }; +pub const Endian = enum { + little, + big, + selectable, + other, +}; pub const DataType = enum { uint8_t, @@ -474,149 +545,6 @@ test "svd.Revision.parse" { try expectError(error.InvalidCharacter, Revision.parse("rp2")); } -// -//pub const CpuName = enum { -// cortex_m0, -// cortex_m0plus, -// cortex_m1, -// sc000, // kindof like an m3 -// cortex_m23, -// cortex_m3, -// cortex_m33, -// cortex_m35p, -// cortex_m55, -// sc300, -// cortex_m4, -// cortex_m7, -// arm_v8_mml, -// arm_v8_mbl, -// arm_v81_mml, -// cortex_a5, -// cortex_a7, -// cortex_a8, -// cortex_a9, -// cortex_a15, -// cortex_a17, -// cortex_a53, -// cortex_a57, -// cortex_a72, -// -// // avr -// avr, -// other, -// -// // TODO: finish -// pub fn parse(str: []const u8) ?CpuName { -// return if (std.mem.eql(u8, "CM0", str)) -// CpuName.cortex_m0 -// else if (std.mem.eql(u8, "CM0PLUS", str)) -// CpuName.cortex_m0plus -// else if (std.mem.eql(u8, "CM0+", str)) -// CpuName.cortex_m0plus -// else if (std.mem.eql(u8, "CM1", str)) -// CpuName.cortex_m1 -// else if (std.mem.eql(u8, "SC000", str)) -// CpuName.sc000 -// else if (std.mem.eql(u8, "CM23", str)) -// CpuName.cortex_m23 -// else if (std.mem.eql(u8, "CM3", str)) -// CpuName.cortex_m3 -// else if (std.mem.eql(u8, "CM33", str)) -// CpuName.cortex_m33 -// else if (std.mem.eql(u8, "CM35P", str)) -// CpuName.cortex_m35p -// else if (std.mem.eql(u8, "CM55", str)) -// CpuName.cortex_m55 -// else if (std.mem.eql(u8, "SC300", str)) -// CpuName.sc300 -// else if (std.mem.eql(u8, "CM4", str)) -// CpuName.cortex_m4 -// else if (std.mem.eql(u8, "CM7", str)) -// CpuName.cortex_m7 -// else if (std.mem.eql(u8, "AVR8", str)) -// CpuName.avr -// else -// null; -// } -//}; -// -//pub const Endian = enum { -// little, -// big, -// selectable, -// other, -// -// pub fn parse(str: []const u8) !Endian { -// return if (std.meta.stringToEnum(Endian, str)) |val| -// val -// else -// error.UnknownEndianType; -// } -//}; -// -//pub const Cpu = struct { -// //name: ?CpuName, -// name: ?[]const u8, -// revision: []const u8, -// endian: Endian, -// mpu_present: bool, -// //fpu_present: bool, -// //fpu_dp: bool, -// //dsp_present: bool, -// //icache_present: bool, -// //dcache_present: bool, -// //itcm_present: bool, -// //dtcm_present: bool, -// vtor_present: bool, -// nvic_prio_bits: u8, -// vendor_systick_config: bool, -// device_num_interrupts: ?usize, -// //sau_num_regions: usize, -// -// pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Cpu { -// return Cpu{ -// .name = if (xml.findValueForKey(nodes, "name")) |name| try arena.allocator().dupe(u8, name) else null, -// .revision = xml.findValueForKey(nodes, "revision") orelse unreachable, -// .endian = try Endian.parse(xml.findValueForKey(nodes, "endian") orelse unreachable), -// .nvic_prio_bits = if (xml.findValueForKey(nodes, "nvicPrioBits")) |nvic_prio_bits| -// try std.fmt.parseInt(u8, nvic_prio_bits, 0) -// else -// 0, -// // TODO: booleans -// .vendor_systick_config = (try xml.parseBoolean(arena.child_allocator, nodes, "vendorSystickConfig")) orelse false, -// .device_num_interrupts = if (xml.findValueForKey(nodes, "deviceNumInterrupts")) |size_str| -// try std.fmt.parseInt(usize, size_str, 0) -// else -// null, -// .vtor_present = (try xml.parseBoolean(arena.child_allocator, nodes, "vtorPresent")) orelse false, -// .mpu_present = (try xml.parseBoolean(arena.child_allocator, nodes, "mpuPresent")) orelse false, -// }; -// } -//}; -// -//pub const Access = enum { -// read_only, -// write_only, -// read_write, -// writeonce, -// read_writeonce, -// -// pub fn parse(str: []const u8) !Access { -// return if (std.mem.eql(u8, "read-only", str)) -// Access.read_only -// else if (std.mem.eql(u8, "write-only", str)) -// Access.write_only -// else if (std.mem.eql(u8, "read-write", str)) -// Access.read_write -// else if (std.mem.eql(u8, "writeOnce", str)) -// Access.writeonce -// else if (std.mem.eql(u8, "read-writeOnce", str)) -// Access.read_writeonce -// else -// error.UnknownAccessType; -// } -//}; -// //pub fn parsePeripheral(arena: *ArenaAllocator, nodes: *xml.Node) !Peripheral { // const allocator = arena.allocator(); // return Peripheral{ @@ -744,64 +672,6 @@ const DimElements = struct { } }; -//pub const Dimension = struct { -// dim: usize, -// increment: usize, -// /// a range of 0-index, only index is recorded -// index: ?Index, -// name: ?[]const u8, -// //array_index: , -// -// const Index = union(enum) { -// num: usize, -// list: std.ArrayList([]const u8), -// }; -// -// pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !?Dimension { -// const allocator = arena.allocator(); -// return Dimension{ -// .dim = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "dim")) orelse return null, -// .increment = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "dimIncrement")) orelse return null, -// .index = if (xml.findValueForKey(nodes, "dimIndex")) |index_str| -// if (std.mem.indexOf(u8, index_str, ",") != null) blk: { -// var list = std.ArrayList([]const u8).init(allocator); -// var it = std.mem.tokenize(u8, index_str, ","); -// var expected: usize = 0; -// while (it.next()) |token| : (expected += 1) -// try list.append(try allocator.dupe(u8, token)); -// -// break :blk Index{ -// .list = list, -// }; -// } else blk: { -// var it = std.mem.tokenize(u8, index_str, "-"); -// const begin = try std.fmt.parseInt(usize, it.next() orelse return error.InvalidDimIndex, 10); -// const end = try std.fmt.parseInt(usize, it.next() orelse return error.InvalidDimIndex, 10); -// -// if (begin == 0) -// break :blk Index{ -// .num = end + 1, -// }; -// -// var list = std.ArrayList([]const u8).init(allocator); -// var i = begin; -// while (i <= end) : (i += 1) -// try list.append(try std.fmt.allocPrint(allocator, "{}", .{i})); -// -// break :blk Index{ -// .list = list, -// }; -// } -// else -// null, -// .name = if (xml.findValueForKey(nodes, "dimName")) |name_str| -// try allocator.dupe(u8, name_str) -// else -// null, -// }; -// } -//}; - const BitRange = struct { offset: u64, width: u64,