regzon is the name for the json output for reg'z data model. This PR includes a number of tests for serialization and deserialization. The intention of this format is to provide an accessible format for tooling, and to provide a medium for schema patching. Rather than errata, patching can be utilized to improve codegen such as type deduplication.

This format is subject to change.
wch-ch32v003
Matt Knight 2 years ago committed by Matt Knight
parent ec84f7eebf
commit eb24365786

@ -26,6 +26,7 @@ attrs: struct {
} = .{}, } = .{},
children: struct { children: struct {
modes: ArrayHashMap(EntityId, EntitySet) = .{},
interrupts: ArrayHashMap(EntityId, EntitySet) = .{}, interrupts: ArrayHashMap(EntityId, EntitySet) = .{},
peripherals: ArrayHashMap(EntityId, EntitySet) = .{}, peripherals: ArrayHashMap(EntityId, EntitySet) = .{},
register_groups: ArrayHashMap(EntityId, EntitySet) = .{}, register_groups: ArrayHashMap(EntityId, EntitySet) = .{},
@ -33,19 +34,18 @@ children: struct {
fields: ArrayHashMap(EntityId, EntitySet) = .{}, fields: ArrayHashMap(EntityId, EntitySet) = .{},
enums: ArrayHashMap(EntityId, EntitySet) = .{}, enums: ArrayHashMap(EntityId, EntitySet) = .{},
enum_fields: ArrayHashMap(EntityId, EntitySet) = .{}, enum_fields: ArrayHashMap(EntityId, EntitySet) = .{},
modes: ArrayHashMap(EntityId, EntitySet) = .{},
} = .{}, } = .{},
types: struct { types: struct {
// atdf has modes which make registers into unions
modes: ArrayHashMap(EntityId, Mode) = .{},
peripherals: ArrayHashMap(EntityId, void) = .{}, peripherals: ArrayHashMap(EntityId, void) = .{},
register_groups: ArrayHashMap(EntityId, void) = .{}, register_groups: ArrayHashMap(EntityId, void) = .{},
registers: ArrayHashMap(EntityId, void) = .{}, registers: ArrayHashMap(EntityId, void) = .{},
fields: ArrayHashMap(EntityId, void) = .{}, fields: ArrayHashMap(EntityId, void) = .{},
enums: ArrayHashMap(EntityId, void) = .{}, enums: ArrayHashMap(EntityId, void) = .{},
enum_fields: ArrayHashMap(EntityId, u32) = .{}, enum_fields: ArrayHashMap(EntityId, u32) = .{},
// atdf has modes which make registers into unions
modes: ArrayHashMap(EntityId, Mode) = .{},
} = .{}, } = .{},
instances: struct { instances: struct {
@ -282,11 +282,11 @@ pub fn initFromDslite(allocator: Allocator, doc: xml.Doc) !Database {
return db; return db;
} }
pub fn initFromJson(allocator: Allocator, reader: anytype) !Database { pub fn initFromJson(allocator: Allocator, text: []const u8) !Database {
var db = try Database.init(allocator); var db = try Database.init(allocator);
errdefer db.deinit(); errdefer db.deinit();
try regzon.loadIntoDb(&db, reader); try regzon.loadIntoDb(&db, text);
return db; return db;
} }
@ -333,6 +333,7 @@ pub fn createDevice(
opts: struct { opts: struct {
// required for now // required for now
name: []const u8, name: []const u8,
description: ?[]const u8 = null,
arch: Arch = .unknown, arch: Arch = .unknown,
}, },
) !EntityId { ) !EntityId {
@ -345,6 +346,8 @@ pub fn createDevice(
}); });
try db.addName(id, opts.name); try db.addName(id, opts.name);
if (opts.description) |d|
try db.addDescription(id, d);
return id; return id;
} }
@ -356,6 +359,7 @@ pub fn createPeripheralInstance(
opts: struct { opts: struct {
// required for now // required for now
name: []const u8, name: []const u8,
description: ?[]const u8 = null,
// required for now // required for now
offset: u64, offset: u64,
// count for an array // count for an array
@ -374,6 +378,9 @@ pub fn createPeripheralInstance(
try db.addName(id, opts.name); try db.addName(id, opts.name);
try db.addOffset(id, opts.offset); try db.addOffset(id, opts.offset);
if (opts.description) |d|
try db.addDescription(id, d);
if (opts.count) |c| if (opts.count) |c|
try db.addCount(id, c); try db.addCount(id, c);
@ -385,6 +392,7 @@ pub fn createPeripheral(
db: *Database, db: *Database,
opts: struct { opts: struct {
name: []const u8, name: []const u8,
description: ?[]const u8 = null,
size: ?u64 = null, size: ?u64 = null,
}, },
) !EntityId { ) !EntityId {
@ -396,6 +404,9 @@ pub fn createPeripheral(
try db.types.peripherals.put(db.gpa, id, {}); try db.types.peripherals.put(db.gpa, id, {});
try db.addName(id, opts.name); try db.addName(id, opts.name);
if (opts.description) |d|
try db.addDescription(id, d);
if (opts.size) |s| if (opts.size) |s|
try db.addSize(id, s); try db.addSize(id, s);
@ -407,6 +418,7 @@ pub fn createRegisterGroup(
parent_id: EntityId, parent_id: EntityId,
opts: struct { opts: struct {
name: []const u8, name: []const u8,
description: ?[]const u8 = null,
}, },
) !EntityId { ) !EntityId {
assert(db.entityIs("type.peripheral", parent_id)); assert(db.entityIs("type.peripheral", parent_id));
@ -418,6 +430,9 @@ pub fn createRegisterGroup(
try db.types.register_groups.put(db.gpa, id, {}); try db.types.register_groups.put(db.gpa, id, {});
try db.addName(id, opts.name); try db.addName(id, opts.name);
if (opts.description) |d|
try db.addDescription(id, d);
try db.addChild("type.register_group", parent_id, id); try db.addChild("type.register_group", parent_id, id);
return id; return id;
} }
@ -527,6 +542,7 @@ pub fn createEnum(
parent_id: EntityId, parent_id: EntityId,
opts: struct { opts: struct {
name: ?[]const u8 = null, name: ?[]const u8 = null,
description: ?[]const u8 = null,
size: ?u64 = null, size: ?u64 = null,
}, },
) !EntityId { ) !EntityId {
@ -541,6 +557,9 @@ pub fn createEnum(
if (opts.name) |n| if (opts.name) |n|
try db.addName(id, n); try db.addName(id, n);
if (opts.description) |d|
try db.addDescription(id, d);
if (opts.size) |s| if (opts.size) |s|
try db.addSize(id, s); try db.addSize(id, s);
@ -573,6 +592,30 @@ pub fn createEnumField(
return id; return id;
} }
pub fn createMode(db: *Database, parent_id: EntityId, opts: struct {
name: []const u8,
description: ?[]const u8 = null,
value: []const u8,
qualifier: []const u8,
}) !EntityId {
// TODO: what types of parents can it have?
const id = db.createEntity();
errdefer db.destroyEntity(id);
log.debug("{}: creating mode", .{id});
try db.types.modes.put(db.gpa, id, .{
.value = try db.arena.allocator().dupe(u8, opts.value),
.qualifier = try db.arena.allocator().dupe(u8, opts.qualifier),
});
try db.addName(id, opts.name);
if (opts.description) |d|
try db.addDescription(id, d);
try db.addChild("type.mode", parent_id, id);
return id;
}
pub fn createInterrupt(db: *Database, device_id: EntityId, opts: struct { pub fn createInterrupt(db: *Database, device_id: EntityId, opts: struct {
name: []const u8, name: []const u8,
index: i32, index: i32,
@ -701,7 +744,7 @@ pub fn addDeviceProperty(
if (db.instances.devices.getEntry(id)) |entry| if (db.instances.devices.getEntry(id)) |entry|
try entry.value_ptr.properties.put( try entry.value_ptr.properties.put(
db.gpa, db.gpa,
key, try db.arena.allocator().dupe(u8, key),
try db.arena.allocator().dupe(u8, value), try db.arena.allocator().dupe(u8, value),
) )
else else
@ -754,6 +797,17 @@ pub const EntityType = enum {
device, device,
interrupt, interrupt,
peripheral_instance, peripheral_instance,
pub fn isInstance(entity_type: EntityType) bool {
return switch (entity_type) {
.device, .interrupt, .peripheral_instance => true,
else => false,
};
}
pub fn isType(entity_type: EntityType) bool {
return !entity_type.isType();
}
}; };
pub fn getEntityType( pub fn getEntityType(

@ -468,23 +468,14 @@ fn loadMode(ctx: *Context, node: xml.Node, parent_id: EntityId) !void {
"caption", "caption",
}); });
const id = db.createEntity(); const id = try db.createMode(parent_id, .{
errdefer db.destroyEntity(id); .name = node.getAttribute("name") orelse return error.MissingModeName,
.description = node.getAttribute("caption"),
const value_str = node.getAttribute("value") orelse return error.MissingModeValue; .value = node.getAttribute("value") orelse return error.MissingModeValue,
const qualifier = node.getAttribute("qualifier") orelse return error.MissingModeQualifier; .qualifier = node.getAttribute("qualifier") orelse return error.MissingModeQualifier,
log.debug("{}: creating mode, value={s}, qualifier={s}", .{ id, value_str, qualifier });
try db.types.modes.put(db.gpa, id, .{
.value = try db.arena.allocator().dupe(u8, value_str),
.qualifier = try db.arena.allocator().dupe(u8, qualifier),
}); });
errdefer db.destroyEntity(id);
const name = node.getAttribute("name") orelse return error.MissingModeName;
try db.addName(id, name);
if (node.getAttribute("caption")) |caption|
try db.addDescription(id, caption);
try db.addChild("type.mode", parent_id, id);
// TODO: "mask": "optional", // TODO: "mask": "optional",
} }

@ -877,26 +877,12 @@ fn getOrderedRegisterList(
return registers; return registers;
} }
const tests = @import("output_tests.zig");
test "gen.peripheral type with register and field" { test "gen.peripheral type with register and field" {
var db = try Database.init(std.testing.allocator); var db = try tests.peripheralTypeWithRegisterAndField(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const register_id = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 32,
.offset = 0,
});
_ = try db.createField(register_id, .{
.name = "TEST_FIELD",
.size = 1,
.offset = 0,
});
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -918,34 +904,9 @@ test "gen.peripheral type with register and field" {
} }
test "gen.peripheral instantiation" { test "gen.peripheral instantiation" {
var db = try Database.init(std.testing.allocator); var db = try tests.peripheralInstantiation(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const register_id = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 32,
.offset = 0,
});
_ = try db.createField(register_id, .{
.name = "TEST_FIELD",
.size = 1,
.offset = 0,
});
const device_id = try db.createDevice(.{
.name = "TEST_DEVICE",
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "TEST0",
.offset = 0x1000,
});
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -975,32 +936,9 @@ test "gen.peripheral instantiation" {
} }
test "gen.peripherals with a shared type" { test "gen.peripherals with a shared type" {
var db = try Database.init(std.testing.allocator); var db = try tests.peripheralsWithSharedType(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const register_id = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 32,
.offset = 0,
});
_ = try db.createField(register_id, .{
.name = "TEST_FIELD",
.size = 1,
.offset = 0,
});
const device_id = try db.createDevice(.{
.name = "TEST_DEVICE",
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{ .name = "TEST0", .offset = 0x1000 });
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{ .name = "TEST1", .offset = 0x2000 });
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1031,54 +969,9 @@ test "gen.peripherals with a shared type" {
} }
test "gen.peripheral with modes" { test "gen.peripheral with modes" {
var db = try Database.init(std.testing.allocator); var db = try tests.peripheralWithModes(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const mode1_id = db.createEntity();
try db.addName(mode1_id, "TEST_MODE1");
try db.types.modes.put(db.gpa, mode1_id, .{
.value = "0x00",
.qualifier = "TEST_PERIPHERAL.TEST_MODE1.COMMON_REGISTER.TEST_FIELD",
});
const mode2_id = db.createEntity();
try db.addName(mode2_id, "TEST_MODE2");
try db.types.modes.put(db.gpa, mode2_id, .{
.value = "0x01",
.qualifier = "TEST_PERIPHERAL.TEST_MODE2.COMMON_REGISTER.TEST_FIELD",
});
var register1_modeset = EntitySet{};
try register1_modeset.put(db.gpa, mode1_id, {});
var register2_modeset = EntitySet{};
try register2_modeset.put(db.gpa, mode2_id, {});
const peripheral_id = try db.createPeripheral(.{ .name = "TEST_PERIPHERAL" });
try db.addChild("type.mode", peripheral_id, mode1_id);
try db.addChild("type.mode", peripheral_id, mode2_id);
const register1_id = try db.createRegister(peripheral_id, .{ .name = "TEST_REGISTER1", .size = 32, .offset = 0 });
const register2_id = try db.createRegister(peripheral_id, .{ .name = "TEST_REGISTER2", .size = 32, .offset = 0 });
const common_reg_id = try db.createRegister(peripheral_id, .{ .name = "COMMON_REGISTER", .size = 32, .offset = 4 });
try db.attrs.modes.put(db.gpa, register1_id, register1_modeset);
try db.attrs.modes.put(db.gpa, register2_id, register2_modeset);
_ = try db.createField(common_reg_id, .{
.name = "TEST_FIELD",
.size = 1,
.offset = 0,
});
// TODO: study the types of qualifiers that come up. it's possible that
// we'll have to read different registers or read registers without fields.
//
// might also have registers with enum values
// naive implementation goes through each mode and follows the qualifier,
// next level will determine if they're reading the same address even if
// different modes will use different union members
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1134,27 +1027,9 @@ test "gen.peripheral with modes" {
} }
test "gen.peripheral with enum" { test "gen.peripheral with enum" {
var db = try Database.init(std.testing.allocator); var db = try tests.peripheralWithEnum(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const enum_id = try db.createEnum(peripheral_id, .{
.name = "TEST_ENUM",
.size = 4,
});
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD1", .value = 0 });
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD2", .value = 1 });
_ = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 8,
.offset = 0,
});
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1179,27 +1054,9 @@ test "gen.peripheral with enum" {
} }
test "gen.peripheral with enum, enum is exhausted of values" { test "gen.peripheral with enum, enum is exhausted of values" {
var db = try Database.init(std.testing.allocator); var db = try tests.peripheralWithEnumEnumIsExhaustedOfValues(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const enum_id = try db.createEnum(peripheral_id, .{
.name = "TEST_ENUM",
.size = 1,
});
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD1", .value = 0 });
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD2", .value = 1 });
_ = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 8,
.offset = 0,
});
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1223,34 +1080,9 @@ test "gen.peripheral with enum, enum is exhausted of values" {
} }
test "gen.field with named enum" { test "gen.field with named enum" {
var db = try Database.init(std.testing.allocator); var db = try tests.fieldWithNamedEnum(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const enum_id = try db.createEnum(peripheral_id, .{
.name = "TEST_ENUM",
.size = 4,
});
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD1", .value = 0 });
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD2", .value = 1 });
const register_id = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 8,
.offset = 0,
});
_ = try db.createField(register_id, .{
.name = "TEST_FIELD",
.size = 4,
.offset = 0,
.enum_id = enum_id,
});
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1281,33 +1113,9 @@ test "gen.field with named enum" {
} }
test "gen.field with anonymous enum" { test "gen.field with anonymous enum" {
var db = try Database.init(std.testing.allocator); var db = try tests.fieldWithAnonymousEnum(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const enum_id = try db.createEnum(peripheral_id, .{
.size = 4,
});
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD1", .value = 0 });
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD2", .value = 1 });
const register_id = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 8,
.offset = 0,
});
_ = try db.createField(register_id, .{
.name = "TEST_FIELD",
.size = 4,
.offset = 0,
.enum_id = enum_id,
});
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1336,33 +1144,9 @@ test "gen.field with anonymous enum" {
} }
test "gen.namespaced register groups" { test "gen.namespaced register groups" {
var db = try Database.init(std.testing.allocator); var db = try tests.namespacedRegisterGroups(std.testing.allocator);
defer db.deinit(); defer db.deinit();
// peripheral
const peripheral_id = try db.createPeripheral(.{
.name = "PORT",
});
// register_groups
const portb_group_id = try db.createRegisterGroup(peripheral_id, .{ .name = "PORTB" });
const portc_group_id = try db.createRegisterGroup(peripheral_id, .{ .name = "PORTC" });
// registers
_ = try db.createRegister(portb_group_id, .{ .name = "PORTB", .size = 8, .offset = 0 });
_ = try db.createRegister(portb_group_id, .{ .name = "DDRB", .size = 8, .offset = 1 });
_ = try db.createRegister(portb_group_id, .{ .name = "PINB", .size = 8, .offset = 2 });
_ = try db.createRegister(portc_group_id, .{ .name = "PORTC", .size = 8, .offset = 0 });
_ = try db.createRegister(portc_group_id, .{ .name = "DDRC", .size = 8, .offset = 1 });
_ = try db.createRegister(portc_group_id, .{ .name = "PINC", .size = 8, .offset = 2 });
// device
const device_id = try db.createDevice(.{ .name = "ATmega328P" });
// instances
_ = try db.createPeripheralInstance(device_id, portb_group_id, .{ .name = "PORTB", .offset = 0x23 });
_ = try db.createPeripheralInstance(device_id, portc_group_id, .{ .name = "PORTC", .offset = 0x26 });
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1400,25 +1184,9 @@ test "gen.namespaced register groups" {
} }
test "gen.peripheral with reserved register" { test "gen.peripheral with reserved register" {
var db = try Database.init(std.testing.allocator); var db = try tests.peripheralWithReservedRegister(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
});
_ = try db.createRegister(peripheral_id, .{ .name = "PORTB", .size = 32, .offset = 0 });
_ = try db.createRegister(peripheral_id, .{ .name = "PINB", .size = 32, .offset = 8 });
const device_id = try db.createDevice(.{
.name = "ATmega328P",
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "PORTB",
.offset = 0x23,
});
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1447,26 +1215,9 @@ test "gen.peripheral with reserved register" {
} }
test "gen.peripheral with count" { test "gen.peripheral with count" {
var db = try Database.init(std.testing.allocator); var db = try tests.peripheralWithCount(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const device_id = try db.createDevice(.{ .name = "ATmega328P" });
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
.size = 3,
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "PORTB",
.offset = 0x23,
.count = 4,
});
_ = try db.createRegister(peripheral_id, .{ .name = "PORTB", .size = 8, .offset = 0 });
_ = try db.createRegister(peripheral_id, .{ .name = "DDRB", .size = 8, .offset = 1 });
_ = try db.createRegister(peripheral_id, .{ .name = "PINB", .size = 8, .offset = 2 });
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1495,26 +1246,9 @@ test "gen.peripheral with count" {
} }
test "gen.peripheral with count, padding required" { test "gen.peripheral with count, padding required" {
var db = try Database.init(std.testing.allocator); var db = try tests.peripheralWithCountPaddingRequired(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const device_id = try db.createDevice(.{ .name = "ATmega328P" });
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
.size = 4,
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "PORTB",
.offset = 0x23,
.count = 4,
});
_ = try db.createRegister(peripheral_id, .{ .name = "PORTB", .size = 8, .offset = 0 });
_ = try db.createRegister(peripheral_id, .{ .name = "DDRB", .size = 8, .offset = 1 });
_ = try db.createRegister(peripheral_id, .{ .name = "PINB", .size = 8, .offset = 2 });
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1544,24 +1278,9 @@ test "gen.peripheral with count, padding required" {
} }
test "gen.register with count" { test "gen.register with count" {
var db = try Database.init(std.testing.allocator); var db = try tests.registerWithCount(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const device_id = try db.createDevice(.{ .name = "ATmega328P" });
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "PORTB",
.offset = 0x23,
});
_ = try db.createRegister(peripheral_id, .{ .name = "PORTB", .size = 8, .offset = 0, .count = 4 });
_ = try db.createRegister(peripheral_id, .{ .name = "DDRB", .size = 8, .offset = 4 });
_ = try db.createRegister(peripheral_id, .{ .name = "PINB", .size = 8, .offset = 5 });
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1590,36 +1309,9 @@ test "gen.register with count" {
} }
test "gen.register with count and fields" { test "gen.register with count and fields" {
var db = try Database.init(std.testing.allocator); var db = try tests.registerWithCountAndFields(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const device_id = try db.createDevice(.{ .name = "ATmega328P" });
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "PORTB",
.offset = 0x23,
});
const portb_id = try db.createRegister(peripheral_id, .{
.name = "PORTB",
.size = 8,
.offset = 0,
.count = 4,
});
_ = try db.createRegister(peripheral_id, .{ .name = "DDRB", .size = 8, .offset = 4 });
_ = try db.createRegister(peripheral_id, .{ .name = "PINB", .size = 8, .offset = 5 });
_ = try db.createField(portb_id, .{
.name = "TEST_FIELD",
.size = 4,
.offset = 0,
});
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1651,26 +1343,9 @@ test "gen.register with count and fields" {
} }
test "gen.field with count, width of one, offset, and padding" { test "gen.field with count, width of one, offset, and padding" {
var db = try Database.init(std.testing.allocator); var db = try tests.fieldWithCountWidthOfOneOffsetAndPadding(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
});
const portb_id = try db.createRegister(peripheral_id, .{
.name = "PORTB",
.size = 8,
.offset = 0,
});
_ = try db.createField(portb_id, .{
.name = "TEST_FIELD",
.size = 1,
.offset = 2,
.count = 5,
});
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1693,26 +1368,9 @@ test "gen.field with count, width of one, offset, and padding" {
} }
test "gen.field with count, multi-bit width, offset, and padding" { test "gen.field with count, multi-bit width, offset, and padding" {
var db = try Database.init(std.testing.allocator); var db = try tests.fieldWithCountMultiBitWidthOffsetAndPadding(std.testing.allocator);
defer db.deinit(); defer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
});
const portb_id = try db.createRegister(peripheral_id, .{
.name = "PORTB",
.size = 8,
.offset = 0,
});
_ = try db.createField(portb_id, .{
.name = "TEST_FIELD",
.size = 2,
.offset = 2,
.count = 2,
});
var buffer = std.ArrayList(u8).init(std.testing.allocator); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();
@ -1735,24 +1393,9 @@ test "gen.field with count, multi-bit width, offset, and padding" {
} }
test "gen.interrupts.avr" { test "gen.interrupts.avr" {
var db = try Database.init(std.testing.allocator); var db = try tests.interruptsAvr(std.testing.allocator);
defer db.deinit(); 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); var buffer = std.ArrayList(u8).init(std.testing.allocator);
defer buffer.deinit(); defer buffer.deinit();

@ -61,12 +61,9 @@ fn mainImpl() anyerror!void {
var schema: ?Schema = if (res.args.schema) |schema_str| var schema: ?Schema = if (res.args.schema) |schema_str|
if (std.meta.stringToEnum(Schema, schema_str)) |s| s else { if (std.meta.stringToEnum(Schema, schema_str)) |s| s else {
std.log.err( std.log.err("Unknown schema type: {s}, must be one of: svd, atdf, json", .{
"Unknown schema type: {s}, must be one of: svd, atdf, json",
.{
schema_str, schema_str,
}, });
);
return error.Explained; return error.Explained;
} }
else else
@ -79,10 +76,6 @@ fn mainImpl() anyerror!void {
return error.Explained; return error.Explained;
} }
if (schema.? == .json) {
return error.Todo;
}
var stdin = std.io.getStdIn().reader(); var stdin = std.io.getStdIn().reader();
var doc = try xml.Doc.fromIo(readFn, &stdin); var doc = try xml.Doc.fromIo(readFn, &stdin);
defer doc.deinit(); defer doc.deinit();
@ -101,13 +94,18 @@ fn mainImpl() anyerror!void {
} }
} }
// schema is guaranteed to be non-null from this point on const path = try arena.allocator().dupeZ(u8, res.positionals[0]);
if (schema.? == .json) { if (schema.? == .json) {
return error.Todo; const file = try std.fs.cwd().openFile(path, .{});
defer file.close();
const text = try file.reader().readAllAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(text);
break :blk try Database.initFromJson(allocator, text);
} }
// all other schema types are xml based // all other schema types are xml based
const path = try arena.allocator().dupeZ(u8, res.positionals[0]);
var doc = try xml.Doc.fromFile(path); var doc = try xml.Doc.fromFile(path);
defer doc.deinit(); defer doc.deinit();

@ -0,0 +1,487 @@
//! Common test conditions for code generation and regzon
const std = @import("std");
const Allocator = std.mem.Allocator;
const Database = @import("Database.zig");
const EntitySet = Database.EntitySet;
pub fn peripheralTypeWithRegisterAndField(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
//.description = "test peripheral",
});
const register_id = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
//.description = "test register",
.size = 32,
.offset = 0,
});
_ = try db.createField(register_id, .{
.name = "TEST_FIELD",
//.description = "test field",
.size = 1,
.offset = 0,
});
return db;
}
pub fn peripheralInstantiation(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const register_id = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 32,
.offset = 0,
});
_ = try db.createField(register_id, .{
.name = "TEST_FIELD",
.size = 1,
.offset = 0,
});
const device_id = try db.createDevice(.{
.name = "TEST_DEVICE",
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "TEST0",
.offset = 0x1000,
});
return db;
}
pub fn peripheralsWithSharedType(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const register_id = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 32,
.offset = 0,
});
_ = try db.createField(register_id, .{
.name = "TEST_FIELD",
.size = 1,
.offset = 0,
});
const device_id = try db.createDevice(.{
.name = "TEST_DEVICE",
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{ .name = "TEST0", .offset = 0x1000 });
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{ .name = "TEST1", .offset = 0x2000 });
return db;
}
pub fn peripheralWithModes(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const mode1_id = db.createEntity();
try db.addName(mode1_id, "TEST_MODE1");
try db.types.modes.put(db.gpa, mode1_id, .{
.value = "0x00",
.qualifier = "TEST_PERIPHERAL.TEST_MODE1.COMMON_REGISTER.TEST_FIELD",
});
const mode2_id = db.createEntity();
try db.addName(mode2_id, "TEST_MODE2");
try db.types.modes.put(db.gpa, mode2_id, .{
.value = "0x01",
.qualifier = "TEST_PERIPHERAL.TEST_MODE2.COMMON_REGISTER.TEST_FIELD",
});
var register1_modeset = EntitySet{};
try register1_modeset.put(db.gpa, mode1_id, {});
var register2_modeset = EntitySet{};
try register2_modeset.put(db.gpa, mode2_id, {});
const peripheral_id = try db.createPeripheral(.{ .name = "TEST_PERIPHERAL" });
try db.addChild("type.mode", peripheral_id, mode1_id);
try db.addChild("type.mode", peripheral_id, mode2_id);
const register1_id = try db.createRegister(peripheral_id, .{ .name = "TEST_REGISTER1", .size = 32, .offset = 0 });
const register2_id = try db.createRegister(peripheral_id, .{ .name = "TEST_REGISTER2", .size = 32, .offset = 0 });
const common_reg_id = try db.createRegister(peripheral_id, .{ .name = "COMMON_REGISTER", .size = 32, .offset = 4 });
try db.attrs.modes.put(db.gpa, register1_id, register1_modeset);
try db.attrs.modes.put(db.gpa, register2_id, register2_modeset);
_ = try db.createField(common_reg_id, .{
.name = "TEST_FIELD",
.size = 1,
.offset = 0,
});
// TODO: study the types of qualifiers that come up. it's possible that
// we'll have to read different registers or read registers without fields.
//
// might also have registers with enum values
// naive implementation goes through each mode and follows the qualifier,
// next level will determine if they're reading the same address even if
// different modes will use different union members
return db;
}
pub fn peripheralWithEnum(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const enum_id = try db.createEnum(peripheral_id, .{
.name = "TEST_ENUM",
.size = 4,
});
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD1", .value = 0 });
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD2", .value = 1 });
_ = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 8,
.offset = 0,
});
return db;
}
pub fn peripheralWithEnumEnumIsExhaustedOfValues(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const enum_id = try db.createEnum(peripheral_id, .{
.name = "TEST_ENUM",
.size = 1,
});
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD1", .value = 0 });
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD2", .value = 1 });
_ = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 8,
.offset = 0,
});
return db;
}
pub fn fieldWithNamedEnum(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const enum_id = try db.createEnum(peripheral_id, .{
.name = "TEST_ENUM",
.size = 4,
});
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD1", .value = 0 });
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD2", .value = 1 });
const register_id = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 8,
.offset = 0,
});
_ = try db.createField(register_id, .{
.name = "TEST_FIELD",
.size = 4,
.offset = 0,
.enum_id = enum_id,
});
return db;
}
pub fn fieldWithAnonymousEnum(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "TEST_PERIPHERAL",
});
const enum_id = try db.createEnum(peripheral_id, .{
.size = 4,
});
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD1", .value = 0 });
_ = try db.createEnumField(enum_id, .{ .name = "TEST_ENUM_FIELD2", .value = 1 });
const register_id = try db.createRegister(peripheral_id, .{
.name = "TEST_REGISTER",
.size = 8,
.offset = 0,
});
_ = try db.createField(register_id, .{
.name = "TEST_FIELD",
.size = 4,
.offset = 0,
.enum_id = enum_id,
});
return db;
}
pub fn namespacedRegisterGroups(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
// peripheral
const peripheral_id = try db.createPeripheral(.{
.name = "PORT",
});
// register_groups
const portb_group_id = try db.createRegisterGroup(peripheral_id, .{ .name = "PORTB" });
const portc_group_id = try db.createRegisterGroup(peripheral_id, .{ .name = "PORTC" });
// registers
_ = try db.createRegister(portb_group_id, .{ .name = "PORTB", .size = 8, .offset = 0 });
_ = try db.createRegister(portb_group_id, .{ .name = "DDRB", .size = 8, .offset = 1 });
_ = try db.createRegister(portb_group_id, .{ .name = "PINB", .size = 8, .offset = 2 });
_ = try db.createRegister(portc_group_id, .{ .name = "PORTC", .size = 8, .offset = 0 });
_ = try db.createRegister(portc_group_id, .{ .name = "DDRC", .size = 8, .offset = 1 });
_ = try db.createRegister(portc_group_id, .{ .name = "PINC", .size = 8, .offset = 2 });
// device
const device_id = try db.createDevice(.{ .name = "ATmega328P" });
// instances
_ = try db.createPeripheralInstance(device_id, portb_group_id, .{ .name = "PORTB", .offset = 0x23 });
_ = try db.createPeripheralInstance(device_id, portc_group_id, .{ .name = "PORTC", .offset = 0x26 });
return db;
}
pub fn peripheralWithReservedRegister(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
});
_ = try db.createRegister(peripheral_id, .{ .name = "PORTB", .size = 32, .offset = 0 });
_ = try db.createRegister(peripheral_id, .{ .name = "PINB", .size = 32, .offset = 8 });
const device_id = try db.createDevice(.{
.name = "ATmega328P",
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "PORTB",
.offset = 0x23,
});
return db;
}
pub fn peripheralWithCount(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const device_id = try db.createDevice(.{ .name = "ATmega328P" });
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
.size = 3,
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "PORTB",
.offset = 0x23,
.count = 4,
});
_ = try db.createRegister(peripheral_id, .{ .name = "PORTB", .size = 8, .offset = 0 });
_ = try db.createRegister(peripheral_id, .{ .name = "DDRB", .size = 8, .offset = 1 });
_ = try db.createRegister(peripheral_id, .{ .name = "PINB", .size = 8, .offset = 2 });
return db;
}
pub fn peripheralWithCountPaddingRequired(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const device_id = try db.createDevice(.{ .name = "ATmega328P" });
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
.size = 4,
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "PORTB",
.offset = 0x23,
.count = 4,
});
_ = try db.createRegister(peripheral_id, .{ .name = "PORTB", .size = 8, .offset = 0 });
_ = try db.createRegister(peripheral_id, .{ .name = "DDRB", .size = 8, .offset = 1 });
_ = try db.createRegister(peripheral_id, .{ .name = "PINB", .size = 8, .offset = 2 });
return db;
}
pub fn registerWithCount(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const device_id = try db.createDevice(.{ .name = "ATmega328P" });
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "PORTB",
.offset = 0x23,
});
_ = try db.createRegister(peripheral_id, .{ .name = "PORTB", .size = 8, .offset = 0, .count = 4 });
_ = try db.createRegister(peripheral_id, .{ .name = "DDRB", .size = 8, .offset = 4 });
_ = try db.createRegister(peripheral_id, .{ .name = "PINB", .size = 8, .offset = 5 });
return db;
}
pub fn registerWithCountAndFields(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const device_id = try db.createDevice(.{ .name = "ATmega328P" });
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
});
_ = try db.createPeripheralInstance(device_id, peripheral_id, .{
.name = "PORTB",
.offset = 0x23,
});
const portb_id = try db.createRegister(peripheral_id, .{
.name = "PORTB",
.size = 8,
.offset = 0,
.count = 4,
});
_ = try db.createRegister(peripheral_id, .{ .name = "DDRB", .size = 8, .offset = 4 });
_ = try db.createRegister(peripheral_id, .{ .name = "PINB", .size = 8, .offset = 5 });
_ = try db.createField(portb_id, .{
.name = "TEST_FIELD",
.size = 4,
.offset = 0,
});
return db;
}
pub fn fieldWithCountWidthOfOneOffsetAndPadding(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
});
const portb_id = try db.createRegister(peripheral_id, .{
.name = "PORTB",
.size = 8,
.offset = 0,
});
_ = try db.createField(portb_id, .{
.name = "TEST_FIELD",
.size = 1,
.offset = 2,
.count = 5,
});
return db;
}
pub fn fieldWithCountMultiBitWidthOffsetAndPadding(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();
const peripheral_id = try db.createPeripheral(.{
.name = "PORTB",
});
const portb_id = try db.createRegister(peripheral_id, .{
.name = "PORTB",
.size = 8,
.offset = 0,
});
_ = try db.createField(portb_id, .{
.name = "TEST_FIELD",
.size = 2,
.offset = 2,
.count = 2,
});
return db;
}
pub fn interruptsAvr(allocator: Allocator) !Database {
var db = try Database.init(allocator);
errdefer 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,
});
return db;
}

File diff suppressed because it is too large Load Diff

@ -38,3 +38,200 @@ pub fn expectAttr(
else => try expectEqual(expected, @field(db.attrs, attr_name).get(id).?), else => try expectEqual(expected, @field(db.attrs, attr_name).get(id).?),
} }
} }
const DatabaseAndId = struct {
db: Database,
id: EntityId,
};
fn expectEqualAttr(
comptime attr_name: []const u8,
expected: DatabaseAndId,
actual: DatabaseAndId,
) !void {
const found = @field(expected.db.attrs, attr_name).contains(expected.id);
try expectEqual(
found,
@field(actual.db.attrs, attr_name).contains(actual.id),
);
if (!found)
return;
const expected_value = @field(expected.db.attrs, attr_name).get(expected.id).?;
const actual_value = @field(actual.db.attrs, attr_name).get(actual.id).?;
switch (Attr(attr_name)) {
[]const u8 => try expectEqualStrings(expected_value, actual_value),
else => try expectEqual(expected_value, actual_value),
}
}
fn expectEqualAttrs(
expected: DatabaseAndId,
actual: DatabaseAndId,
) !void {
// skip name since that's usually been compared
try expectEqualAttr("description", expected, actual);
try expectEqualAttr("offset", expected, actual);
try expectEqualAttr("access", expected, actual);
try expectEqualAttr("count", expected, actual);
try expectEqualAttr("size", expected, actual);
try expectEqualAttr("reset_value", expected, actual);
try expectEqualAttr("reset_mask", expected, actual);
try expectEqualAttr("version", expected, actual);
// TODO:
// - modes
// - enum
}
pub fn expectEqualDatabases(
expected: Database,
actual: Database,
) !void {
var it = expected.types.peripherals.iterator();
while (it.next()) |entry| {
const peripheral_id = entry.key_ptr.*;
const name = expected.attrs.name.get(peripheral_id) orelse unreachable;
std.log.debug("peripheral: {s}", .{name});
const expected_id = try expected.getEntityIdByName("type.peripheral", name);
const actual_id = try actual.getEntityIdByName("type.peripheral", name);
try expectEqualEntities(
.{ .db = expected, .id = expected_id },
.{ .db = actual, .id = actual_id },
);
}
// difficult to debug, but broad checks
inline for (@typeInfo(TypeOfField(Database, "attrs")).Struct.fields) |field| {
std.log.debug("attr: {s}", .{field.name});
try expectEqual(
@field(expected.attrs, field.name).count(),
@field(actual.attrs, field.name).count(),
);
}
inline for (@typeInfo(TypeOfField(Database, "children")).Struct.fields) |field| {
std.log.debug("child: {s}", .{field.name});
try expectEqual(
@field(expected.children, field.name).count(),
@field(actual.children, field.name).count(),
);
}
inline for (@typeInfo(TypeOfField(Database, "types")).Struct.fields) |field| {
std.log.debug("type: {s}", .{field.name});
try expectEqual(
@field(expected.types, field.name).count(),
@field(actual.types, field.name).count(),
);
}
inline for (@typeInfo(TypeOfField(Database, "instances")).Struct.fields) |field| {
std.log.debug("instance: {s}", .{field.name});
try expectEqual(
@field(expected.instances, field.name).count(),
@field(actual.instances, field.name).count(),
);
}
}
const ErrorEqualEntities = error{
TestExpectedEqual,
NameNotFound,
TestUnexpectedResult,
};
fn expectEqualEntities(
expected: DatabaseAndId,
actual: DatabaseAndId,
) ErrorEqualEntities!void {
const expected_type = expected.db.getEntityType(expected.id).?;
const actual_type = actual.db.getEntityType(actual.id).?;
try expectEqual(expected_type, actual_type);
switch (expected_type) {
.enum_field => {
const expected_value = expected.db.types.enum_fields.get(expected.id).?;
const actual_value = actual.db.types.enum_fields.get(actual.id).?;
try expectEqual(expected_value, actual_value);
},
.mode => {
const expected_mode = expected.db.types.modes.get(expected.id).?;
const actual_mode = actual.db.types.modes.get(actual.id).?;
try expectEqualStrings(expected_mode.qualifier, actual_mode.qualifier);
try expectEqualStrings(expected_mode.value, actual_mode.value);
},
.interrupt => {
const expected_value = expected.db.instances.interrupts.get(expected.id).?;
const actual_value = actual.db.instances.interrupts.get(actual.id).?;
try expectEqual(expected_value, actual_value);
},
.peripheral_instance => {
const expected_id = expected.db.instances.peripherals.get(expected.id).?;
const actual_id = actual.db.instances.peripherals.get(actual.id).?;
try expectEqualEntities(
.{ .db = expected.db, .id = expected_id },
.{ .db = actual.db, .id = actual_id },
);
},
.device => {
const expected_device = expected.db.instances.devices.get(expected.id).?;
const actual_device = actual.db.instances.devices.get(actual.id).?;
try expectEqual(expected_device.arch, actual_device.arch);
// properties
try expectEqual(
expected_device.properties.count(),
actual_device.properties.count(),
);
var it = expected_device.properties.iterator();
while (it.next()) |entry| {
const key = entry.key_ptr.*;
const expected_value = entry.value_ptr.*;
try expect(actual_device.properties.contains(key));
try expectEqualStrings(
expected_value,
actual_device.properties.get(key).?,
);
}
},
else => {},
}
try expectEqualAttrs(expected, actual);
try expectEqualChildren(expected, actual);
}
fn expectEqualChildren(
expected: DatabaseAndId,
actual: DatabaseAndId,
) ErrorEqualEntities!void {
inline for (@typeInfo(TypeOfField(Database, "children")).Struct.fields) |field| {
if (@field(expected.db.children, field.name).get(expected.id)) |entity_set| {
var it = entity_set.iterator();
while (it.next()) |entry| {
const expected_child_id = entry.key_ptr.*;
const expected_child_name = expected.db.attrs.name.get(expected_child_id) orelse continue;
try expect(@field(actual.db.children, field.name).contains(actual.id));
var actual_it = @field(actual.db.children, field.name).get(actual.id).?.iterator();
const actual_child_id = while (actual_it.next()) |child_entry| {
const child_id = child_entry.key_ptr.*;
const actual_child_name = actual.db.attrs.name.get(child_id) orelse continue;
if (std.mem.eql(u8, expected_child_name, actual_child_name))
break child_id;
} else return error.NameNotFound;
try expectEqualEntities(
.{ .db = expected.db, .id = expected_child_id },
.{ .db = actual.db, .id = actual_child_id },
);
}
}
}
}

Loading…
Cancel
Save