vector table generation for ATDF (#74)

wch-ch32v003
Matt Knight 2 years ago committed by Matt Knight
parent efeba02119
commit 43ee9c8af0

@ -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);
}

@ -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 =
\\<avr-tools-device-file>
\\ <devices>
\\ <device name="ATmega328P" architecture="AVR8" family="megaAVR">
\\ <interrupts>
\\ <interrupt name="TEST_VECTOR1" index="1"/>
\\ <interrupt name="TEST_VECTOR2" index="5"/>
\\ </interrupts>
\\ </device>
\\ </devices>
\\</avr-tools-device-file>
\\
;
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 =
\\<avr-tools-device-file>
\\ <devices>
\\ <device name="ATmega328P" architecture="AVR8" family="megaAVR">
\\ <interrupts>
\\ <interrupt index="1" module-instance="CRCSCAN" name="NMI"/>
\\ <interrupt index="2" module-instance="BOD" name="VLM"/>
\\ </interrupts>
\\ </device>
\\ </devices>
\\</avr-tools-device-file>
\\
;
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 =
\\<avr-tools-device-file>
\\ <devices>
\\ <device name="ATmega328P" architecture="AVR8" family="megaAVR">
\\ <interrupts>
\\ <interrupt-group index="1" module-instance="PORTB" name-in-module="PORT"/>
\\ </interrupts>
\\ </device>
\\ </devices>
\\ <modules>
\\ <module name="PORT">
\\ <interrupt-group name="PORT">
\\ <interrupt index="0" name="INT0"/>
\\ <interrupt index="1" name="INT1"/>
\\ </interrupt-group>
\\ </module>
\\ </modules>
\\</avr-tools-device-file>
\\
;
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).?);
}

@ -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);
}

@ -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;
}

@ -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;
}

@ -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");
}

@ -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 });

@ -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,

Loading…
Cancel
Save