initial support for dimElementsGroup for SVD (#70)

wch-ch32v003
Matt Knight 2 years ago committed by Matt Knight
parent 293f25e6c3
commit a34e4ef648

@ -9,7 +9,7 @@ attrs: struct {
description: HashMap(EntityId, []const u8) = .{},
offset: HashMap(EntityId, u64) = .{},
access: HashMap(EntityId, Access) = .{},
repeated: HashMap(EntityId, u64) = .{},
count: HashMap(EntityId, u64) = .{},
size: HashMap(EntityId, u64) = .{},
reset_value: HashMap(EntityId, u64) = .{},
reset_mask: HashMap(EntityId, u64) = .{},
@ -119,7 +119,7 @@ pub fn deinit(db: *Database) void {
db.attrs.description.deinit(db.gpa);
db.attrs.offset.deinit(db.gpa);
db.attrs.access.deinit(db.gpa);
db.attrs.repeated.deinit(db.gpa);
db.attrs.count.deinit(db.gpa);
db.attrs.size.deinit(db.gpa);
db.attrs.reset_value.deinit(db.gpa);
db.attrs.reset_mask.deinit(db.gpa);
@ -209,8 +209,36 @@ pub fn createEntity(db: *Database) EntityId {
}
pub fn destroyEntity(db: *Database, id: EntityId) void {
_ = db;
_ = id;
switch (db.getEntityType(id) orelse return) {
.register => {
log.debug("{}: destroying register", .{id});
if (db.attrs.parent.get(id)) |parent_id| {
if (db.children.registers.getEntry(parent_id)) |entry| {
_ = entry.value_ptr.swapRemove(id);
}
}
// if has a parent, remove it from the set
// remove all attributes
// TODO: remove fields
_ = db.types.registers.swapRemove(id);
},
else => {},
}
db.removeAttrs(id);
}
fn removeAttrs(db: *Database, id: EntityId) void {
inline for (@typeInfo(TypeOfField(Database, "attrs")).Struct.fields) |field| {
if (@hasDecl(field.type, "swapRemove"))
_ = @field(db.attrs, field.name).swapRemove(id)
else if (@hasDecl(field.type, "remove"))
_ = @field(db.attrs, field.name).remove(id)
else
unreachable;
}
}
pub fn createDevice(
@ -239,6 +267,8 @@ pub fn createPeripheralInstance(
name: []const u8,
// required for now
offset: u64,
// count for an array
count: ?u64 = null,
},
) !EntityId {
assert(db.entityIs("instance.device", device_id));
@ -253,6 +283,9 @@ pub fn createPeripheralInstance(
try db.addName(id, opts.name);
try db.addOffset(id, opts.offset);
if (opts.count) |c|
try db.addCount(id, c);
try db.addChild("instance.peripheral", device_id, id);
return id;
}
@ -260,7 +293,8 @@ pub fn createPeripheralInstance(
pub fn createPeripheral(
db: *Database,
opts: struct {
name: ?[]const u8 = null,
name: []const u8,
size: ?u64 = null,
},
) !EntityId {
const id = db.createEntity();
@ -269,8 +303,10 @@ pub fn createPeripheral(
log.debug("{}: creating peripheral", .{id});
try db.types.peripherals.put(db.gpa, id, {});
if (opts.name) |n|
try db.addName(id, n);
try db.addName(id, opts.name);
if (opts.size) |s|
try db.addSize(id, s);
return id;
}
@ -303,9 +339,14 @@ pub fn createRegister(
name: []const u8,
description: ?[]const u8 = null,
/// offset is in bytes
offset: ?u64 = null,
offset: u64,
/// size is in bits
size: ?u64 = null,
size: u64,
/// count if there is an array
count: ?u64 = null,
access: ?Access = null,
reset_mask: ?u64 = null,
reset_value: ?u64 = null,
},
) !EntityId {
assert(db.entityIs("type.peripheral", parent_id) or
@ -321,11 +362,20 @@ pub fn createRegister(
if (opts.description) |d|
try db.addDescription(id, d);
if (opts.offset) |o|
try db.addOffset(id, o);
try db.addOffset(id, opts.offset);
try db.addSize(id, opts.size);
if (opts.size) |s|
try db.addSize(id, s);
if (opts.count) |c|
try db.addCount(id, c);
if (opts.access) |a|
try db.addAccess(id, a);
if (opts.reset_mask) |rm|
try db.addResetMask(id, rm);
if (opts.reset_value) |rv|
try db.addResetValue(id, rv);
try db.addChild("type.register", parent_id, id);
@ -385,7 +435,6 @@ pub fn createEnum(
size: ?u64 = null,
},
) !EntityId {
// TODO: other parent types
assert(db.entityIs("type.peripheral", parent_id) or
db.entityIs("type.field", parent_id));
@ -476,7 +525,7 @@ pub fn addSize(db: *Database, id: EntityId, size: u64) !void {
}
pub fn addOffset(db: *Database, id: EntityId, offset: u64) !void {
log.debug("{}: adding offset: {}", .{ id, offset });
log.debug("{}: adding offset: 0x{x}", .{ id, offset });
try db.attrs.offset.putNoClobber(db.gpa, id, offset);
}
@ -486,7 +535,7 @@ pub fn addResetValue(db: *Database, id: EntityId, reset_value: u64) !void {
}
pub fn addResetMask(db: *Database, id: EntityId, reset_mask: u64) !void {
log.debug("{}: adding register mask: 0x{}", .{ id, reset_mask });
log.debug("{}: adding register mask: 0x{x}", .{ id, reset_mask });
try db.attrs.reset_mask.putNoClobber(db.gpa, id, reset_mask);
}
@ -495,6 +544,11 @@ pub fn addAccess(db: *Database, id: EntityId, access: Access) !void {
try db.attrs.access.putNoClobber(db.gpa, id, access);
}
pub fn addCount(db: *Database, id: EntityId, count: u64) !void {
log.debug("{}: adding count: {}", .{ id, count });
try db.attrs.count.putNoClobber(db.gpa, id, count);
}
pub fn addChild(
db: *Database,
comptime entity_location: []const u8,
@ -562,10 +616,12 @@ pub fn getEntityIdByName(
comptime var group = (tok_it.next() orelse unreachable) ++ "s";
comptime var table = (tok_it.next() orelse unreachable) ++ "s";
log.debug("group: {s}, table: {s}", .{ group, table });
var it = @field(@field(db, group), table).iterator();
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;
@ -589,7 +645,7 @@ pub const EntityType = enum {
pub fn getEntityType(
db: Database,
id: EntityId,
) EntityType {
) ?EntityType {
inline for (@typeInfo(TypeOfField(Database, "types")).Struct.fields) |field| {
if (@field(db.types, field.name).contains(id))
return @field(EntityType, field.name[0 .. field.name.len - 1]);
@ -603,7 +659,7 @@ pub fn getEntityType(
@field(EntityType, field.name[0 .. field.name.len - 1]);
}
@panic("unhandled entity type");
return null;
}
// assert that the database is in valid state

@ -459,11 +459,11 @@ fn loadRegister(
.size = if (node.getAttribute("size")) |size_str|
@as(u64, 8) * try std.fmt.parseInt(u64, size_str, 0)
else
null,
return error.MissingRegisterSize,
.offset = if (node.getAttribute("offset")) |offset_str|
try std.fmt.parseInt(u64, offset_str, 0)
else
null,
return error.MissingRegisterOffset,
});
errdefer db.destroyEntity(id);

@ -209,8 +209,15 @@ fn writePeripheralInstance(db: Database, instance_id: EntityId, offset: u64, out
else if (db.attrs.description.get(type_id)) |description|
try writeComment(db.arena.allocator(), description, writer);
try writer.print("pub const {s} = @ptrCast(*volatile {s}, 0x{x});\n", .{
var array_prefix_buf: [80]u8 = undefined;
const array_prefix = if (db.attrs.count.get(instance_id)) |count|
try std.fmt.bufPrint(&array_prefix_buf, "[{}]", .{count})
else
"";
try writer.print("pub const {s} = @ptrCast(*volatile {s}{s}, 0x{x});\n", .{
std.zig.fmtId(name),
array_prefix,
type_ref,
offset,
});
@ -282,27 +289,11 @@ fn isPeripheralZeroSized(db: Database, peripheral_id: EntityId) bool {
} else true;
}
// have to fill this in manually when new errors are introduced. This is because writePeripheral is recursive via writePeripheralBase
const ErrorWritePeripheralBase = error{
OutOfMemory,
InvalidCharacter,
Overflow,
NameNotFound,
MissingEnumSize,
MissingEnumFields,
MissingEnumFieldName,
MissingEnumFieldValue,
};
fn WritePeripheralError(comptime Writer: type) type {
return ErrorWritePeripheralBase || Writer.Error;
}
fn writePeripheral(
db: Database,
peripheral_id: EntityId,
out_writer: anytype,
) WritePeripheralError(@TypeOf(out_writer))!void {
) !void {
assert(db.entityIs("type.peripheral", peripheral_id) or
db.entityIs("type.register_group", peripheral_id));
@ -339,7 +330,7 @@ fn writePeripheral(
\\
, .{
std.zig.fmtId(name),
if (zero_sized) "" else "packed",
if (zero_sized) "" else "extern",
if (has_modes) "union" else "struct",
});
@ -561,7 +552,7 @@ fn writeRegistersWithModes(
} else try moded_registers.append(register);
}
try writer.print("{s}: packed struct {{\n", .{
try writer.print("{s}: extern struct {{\n", .{
std.zig.fmtId(mode_name),
});
@ -578,8 +569,6 @@ fn writeRegistersBase(
registers: []const EntityWithOffset,
out_writer: anytype,
) !void {
_ = parent_id;
// registers _should_ be sorted when then make their way here
assert(std.sort.isSorted(EntityWithOffset, registers, {}, EntityWithOffset.lessThan));
var buffer = std.ArrayList(u8).init(db.arena.allocator());
@ -589,7 +578,6 @@ fn writeRegistersBase(
// don't have to care about modes
// prioritize smaller fields that come earlier
{
var offset: u64 = 0;
var i: u32 = 0;
@ -610,7 +598,14 @@ fn writeRegistersBase(
const next = blk: {
var ret: ?EntityWithOffsetAndSize = null;
for (registers[i..end]) |register| {
const size = db.attrs.size.get(register.id) orelse unreachable;
const size = if (db.attrs.size.get(register.id)) |size|
if (db.attrs.count.get(register.id)) |count|
size * count
else
size
else
unreachable;
if (ret == null or (size < ret.?.size))
ret = .{
.id = register.id,
@ -628,6 +623,16 @@ fn writeRegistersBase(
offset += next.size / 8;
i = end;
}
// TODO: name collision
if (db.attrs.size.get(parent_id)) |size| {
if (offset > size)
@panic("peripheral size too small, parsing should have caught this");
if (offset != size)
try writer.print("padding: [{}]u8,\n", .{
size - offset,
});
}
try out_writer.writeAll(buffer.items);
@ -648,6 +653,12 @@ fn writeRegister(
if (db.attrs.description.get(register_id)) |description|
try writeComment(db.arena.allocator(), description, writer);
var array_prefix_buf: [80]u8 = undefined;
const array_prefix = if (db.attrs.count.get(register_id)) |count|
try std.fmt.bufPrint(&array_prefix_buf, "[{}]", .{count})
else
"";
if (db.children.fields.get(register_id)) |field_set| {
var fields = std.ArrayList(EntityWithOffset).init(db.gpa);
defer fields.deinit();
@ -662,14 +673,19 @@ fn writeRegister(
}
std.sort.sort(EntityWithOffset, fields.items, {}, EntityWithOffset.lessThan);
try writer.print("{s}: mmio.Mmio({}, packed struct{{\n", .{
try writer.print("{s}: mmio.Mmio({}, {s}packed struct{{\n", .{
std.zig.fmtId(name),
size,
array_prefix,
});
try writeFields(db, fields.items, size, writer);
try writer.writeAll("}),\n");
} else try writer.print("{s}: u{},\n", .{ std.zig.fmtId(name), size });
} else try writer.print("{s}: {s}u{},\n", .{
std.zig.fmtId(name),
array_prefix,
size,
});
try out_writer.writeAll(buffer.items);
}
@ -819,7 +835,7 @@ test "gen.peripheral type with register and field" {
\\const mmio = @import("mmio");
\\
\\pub const types = struct {
\\ pub const TEST_PERIPHERAL = packed struct {
\\ pub const TEST_PERIPHERAL = extern struct {
\\ TEST_REGISTER: mmio.Mmio(32, packed struct {
\\ TEST_FIELD: u1,
\\ padding: u31 = 0,
@ -873,7 +889,7 @@ test "gen.peripheral instantiation" {
\\};
\\
\\pub const types = struct {
\\ pub const TEST_PERIPHERAL = packed struct {
\\ pub const TEST_PERIPHERAL = extern struct {
\\ TEST_REGISTER: mmio.Mmio(32, packed struct {
\\ TEST_FIELD: u1,
\\ padding: u31 = 0,
@ -926,7 +942,7 @@ test "gen.peripherals with a shared type" {
\\};
\\
\\pub const types = struct {
\\ pub const TEST_PERIPHERAL = packed struct {
\\ pub const TEST_PERIPHERAL = extern struct {
\\ TEST_REGISTER: mmio.Mmio(32, packed struct {
\\ TEST_FIELD: u1,
\\ padding: u31 = 0,
@ -994,7 +1010,7 @@ test "gen.peripheral with modes" {
\\const mmio = @import("mmio");
\\
\\pub const types = struct {
\\ pub const TEST_PERIPHERAL = packed union {
\\ pub const TEST_PERIPHERAL = extern union {
\\ pub const Mode = enum {
\\ TEST_MODE1,
\\ TEST_MODE2,
@ -1019,14 +1035,14 @@ test "gen.peripheral with modes" {
\\ unreachable;
\\ }
\\
\\ TEST_MODE1: packed struct {
\\ TEST_MODE1: extern struct {
\\ TEST_REGISTER1: u32,
\\ COMMON_REGISTER: mmio.Mmio(32, packed struct {
\\ TEST_FIELD: u1,
\\ padding: u31 = 0,
\\ }),
\\ },
\\ TEST_MODE2: packed struct {
\\ TEST_MODE2: extern struct {
\\ TEST_REGISTER2: u32,
\\ COMMON_REGISTER: mmio.Mmio(32, packed struct {
\\ TEST_FIELD: u1,
@ -1069,7 +1085,7 @@ test "gen.peripheral with enum" {
\\const mmio = @import("mmio");
\\
\\pub const types = struct {
\\ pub const TEST_PERIPHERAL = packed struct {
\\ pub const TEST_PERIPHERAL = extern struct {
\\ pub const TEST_ENUM = enum(u4) {
\\ TEST_ENUM_FIELD1 = 0x0,
\\ TEST_ENUM_FIELD2 = 0x1,
@ -1113,7 +1129,7 @@ test "gen.peripheral with enum, enum is exhausted of values" {
\\const mmio = @import("mmio");
\\
\\pub const types = struct {
\\ pub const TEST_PERIPHERAL = packed struct {
\\ pub const TEST_PERIPHERAL = extern struct {
\\ pub const TEST_ENUM = enum(u1) {
\\ TEST_ENUM_FIELD1 = 0x0,
\\ TEST_ENUM_FIELD2 = 0x1,
@ -1163,7 +1179,7 @@ test "gen.field with named enum" {
\\const mmio = @import("mmio");
\\
\\pub const types = struct {
\\ pub const TEST_PERIPHERAL = packed struct {
\\ pub const TEST_PERIPHERAL = extern struct {
\\ pub const TEST_ENUM = enum(u4) {
\\ TEST_ENUM_FIELD1 = 0x0,
\\ TEST_ENUM_FIELD2 = 0x1,
@ -1219,7 +1235,7 @@ test "gen.field with anonymous enum" {
\\const mmio = @import("mmio");
\\
\\pub const types = struct {
\\ pub const TEST_PERIPHERAL = packed struct {
\\ pub const TEST_PERIPHERAL = extern struct {
\\ TEST_REGISTER: mmio.Mmio(8, packed struct {
\\ TEST_FIELD: packed union {
\\ raw: u4,
@ -1281,13 +1297,13 @@ test "gen.namespaced register groups" {
\\
\\pub const types = struct {
\\ pub const PORT = struct {
\\ pub const PORTB = packed struct {
\\ pub const PORTB = extern struct {
\\ PORTB: u8,
\\ DDRB: u8,
\\ PINB: u8,
\\ };
\\
\\ pub const PORTC = packed struct {
\\ pub const PORTC = extern struct {
\\ PORTC: u8,
\\ DDRC: u8,
\\ PINC: u8,
@ -1332,7 +1348,7 @@ test "gen.peripheral with reserved register" {
\\};
\\
\\pub const types = struct {
\\ pub const PORTB = packed struct {
\\ pub const PORTB = extern struct {
\\ PORTB: u32,
\\ reserved8: [4]u8,
\\ PINB: u32,
@ -1342,19 +1358,136 @@ test "gen.peripheral with reserved register" {
, buffer.items);
}
// TODO:
// - write some tests regarding register scoped modes, and what fields look like
// when they have a peripheral/register_group scoped mode
// - default values should be reset value, otherwise 0
//
test "gen.peripheral with count" {
var db = try Database.init(std.testing.allocator);
defer db.deinit();
// more test ideas:
// - interrupts
// - anonymous peripherals
// - multiple register groups
// - access
// - modes
// - repeated
// - ordered address printing
// - modes with discontiguous bits
//
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);
defer buffer.deinit();
try db.toZig(buffer.writer());
try std.testing.expectEqualStrings(
\\const mmio = @import("mmio");
\\
\\pub const devices = struct {
\\ pub const ATmega328P = struct {
\\ pub const PORTB = @ptrCast(*volatile [4]types.PORTB, 0x23);
\\ };
\\};
\\
\\pub const types = struct {
\\ pub const PORTB = extern struct {
\\ PORTB: u8,
\\ DDRB: u8,
\\ PINB: u8,
\\ };
\\};
\\
, buffer.items);
}
test "gen.peripheral with count, padding required" {
var db = try Database.init(std.testing.allocator);
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);
defer buffer.deinit();
try db.toZig(buffer.writer());
try std.testing.expectEqualStrings(
\\const mmio = @import("mmio");
\\
\\pub const devices = struct {
\\ pub const ATmega328P = struct {
\\ pub const PORTB = @ptrCast(*volatile [4]types.PORTB, 0x23);
\\ };
\\};
\\
\\pub const types = struct {
\\ pub const PORTB = extern struct {
\\ PORTB: u8,
\\ DDRB: u8,
\\ PINB: u8,
\\ padding: [1]u8,
\\ };
\\};
\\
, buffer.items);
}
test "gen.register with count" {
var db = try Database.init(std.testing.allocator);
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);
defer buffer.deinit();
try db.toZig(buffer.writer());
try std.testing.expectEqualStrings(
\\const mmio = @import("mmio");
\\
\\pub const devices = struct {
\\ pub const ATmega328P = struct {
\\ pub const PORTB = @ptrCast(*volatile types.PORTB, 0x23);
\\ };
\\};
\\
\\pub const types = struct {
\\ pub const PORTB = extern struct {
\\ PORTB: [4]u8,
\\ DDRB: u8,
\\ PINB: u8,
\\ };
\\};
\\
, buffer.items);
}

@ -27,46 +27,19 @@ const Context = struct {
log.debug("{}: derived from '{s}'", .{ id, derived_from });
}
fn getRegisterPropsDerivedFromParent(
fn deriveRegisterPropertiesFrom(
ctx: *Context,
id: EntityId,
node: xml.Node,
from: EntityId,
) !RegisterProperties {
const register_props = try RegisterProperties.parse(node);
try ctx.addRegisterPropertiesDerivedFromParent(id, register_props);
return ctx.register_props.get(id).?;
}
fn addRegisterPropertiesDerivedFrom(
ctx: *Context,
id: EntityId,
from: EntityId,
register_props: RegisterProperties,
) !void {
const db = ctx.db;
var base_register_props = ctx.register_props.get(from) orelse unreachable;
inline for (@typeInfo(RegisterProperties).Struct.fields) |field| {
if (@field(register_props, field.name)) |value|
@field(base_register_props, field.name) = value;
}
log.debug("deriving register props: {}", .{base_register_props});
try ctx.register_props.put(db.gpa, id, base_register_props);
}
fn addRegisterPropertiesDerivedFromParent(
ctx: *Context,
id: EntityId,
register_props: RegisterProperties,
) !void {
const db = ctx.db;
if (db.attrs.parent.get(id)) |parent_id|
try ctx.addRegisterPropertiesDerivedFrom(id, parent_id, register_props)
else
try ctx.register_props.put(db.gpa, id, register_props);
return base_register_props;
}
};
@ -154,7 +127,6 @@ pub fn loadIntoDb(db: *Database, doc: xml.Doc) !void {
defer ctx.deinit();
const register_props = try RegisterProperties.parse(root);
log.debug("parsed register props: {}", .{register_props});
try ctx.register_props.put(db.gpa, device_id, register_props);
var peripheral_it = root.iterate(&.{"peripherals"}, "peripheral");
@ -176,53 +148,54 @@ pub fn loadIntoDb(db: *Database, doc: xml.Doc) !void {
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);
log.warn("TODO: implement derivation for {}", .{entity_type});
log.warn("TODO: implement derivation for {?}", .{entity_type});
}
pub fn loadPeripheral(ctx: *Context, node: xml.Node, device_id: EntityId) !void {
const db = ctx.db;
const name = node.getValue("name") orelse return error.PeripheralMissingName;
const base_address = node.getValue("baseAddress") orelse return error.PeripheralMissingBaseAddress;
const offset = try std.fmt.parseInt(u64, base_address, 0);
// dim elements before creation as it might require creating multiple instances
const dim_elements = try DimElements.parse(node);
if (dim_elements != null)
return error.TodoDimElements;
const type_id = try db.createPeripheral(.{
.name = name,
});
const type_id = try loadPeripheralType(ctx, node);
errdefer db.destroyEntity(type_id);
const instance_id = try db.createPeripheralInstance(device_id, type_id, .{
.name = name,
.offset = offset,
.name = node.getValue("name") orelse return error.PeripheralMissingName,
.offset = if (node.getValue("baseAddress")) |base_address|
try std.fmt.parseInt(u64, base_address, 0)
else
return error.PeripheralMissingBaseAddress,
});
errdefer db.destroyEntity(instance_id);
if (node.findChild("interrupt")) |interrupt_node|
try loadInterrupt(db, interrupt_node, device_id);
const dim_elements = try DimElements.parse(node);
if (dim_elements) |elements| {
// peripherals can't have dimIndex set according to the spec
if (elements.dim_index != null)
return error.Malformed;
// TODO: skip if:
// - any dimElementGroup values are set
if (elements.dim_name != null)
return error.TodoDimElementsExtended;
log.debug("{}: created peripheral instance", .{instance_id});
// count is applied to the specific instance
try db.addCount(instance_id, elements.dim);
if (node.getValue("description")) |description| {
try db.addDescription(type_id, description);
try db.addDescription(instance_id, description);
// size is applied to the type
try db.addSize(type_id, elements.dim_increment);
}
if (node.findChild("interrupt")) |interrupt_node|
try loadInterrupt(db, interrupt_node, device_id);
if (node.getValue("description")) |description|
try db.addDescription(instance_id, description);
if (node.getValue("version")) |version|
try db.addVersion(instance_id, version);
if (node.getAttribute("derivedFrom")) |derived_from|
try ctx.addDerivedEntity(instance_id, derived_from);
const register_props = try RegisterProperties.parse(node);
log.debug("parsed register props: {}", .{register_props});
try ctx.addRegisterPropertiesDerivedFrom(type_id, device_id, register_props);
const register_props = try ctx.deriveRegisterPropertiesFrom(node, device_id);
try ctx.register_props.put(db.gpa, type_id, register_props);
var register_it = node.iterate(&.{"registers"}, "register");
while (register_it.next()) |register_node|
@ -235,7 +208,6 @@ pub fn loadPeripheral(ctx: *Context, node: xml.Node, device_id: EntityId) !void
loadCluster(ctx, cluster_node, type_id) catch |err|
log.warn("failed to load cluster: {}", .{err});
// dimElementGroup
// alternatePeripheral
// groupName
// prependToName
@ -245,6 +217,20 @@ pub fn loadPeripheral(ctx: *Context, node: xml.Node, device_id: EntityId) !void
// addressBlock
}
fn loadPeripheralType(ctx: *Context, node: xml.Node) !EntityId {
const db = ctx.db;
const id = try db.createPeripheral(.{
.name = node.getValue("name") orelse return error.PeripheralMissingName,
});
errdefer db.destroyEntity(id);
if (node.getValue("description")) |description|
try db.addDescription(id, description);
return id;
}
fn loadInterrupt(db: *Database, node: xml.Node, device_id: EntityId) !void {
assert(db.entityIs("instance.device", device_id));
@ -292,34 +278,34 @@ fn loadRegister(
parent_id: EntityId,
) !void {
const db = ctx.db;
const register_props = try ctx.deriveRegisterPropertiesFrom(node, parent_id);
const size = register_props.size orelse return error.MissingRegisterSize;
const count: ?u64 = if (try DimElements.parse(node)) |elements| count: {
if (elements.dim_index != null or elements.dim_name != null)
return error.TodoDimElementsExtended;
if ((elements.dim_increment * 8) != size)
return error.DimIncrementSizeMismatch;
break :count elements.dim;
} else null;
const id = try db.createRegister(parent_id, .{
.name = node.getValue("name") orelse return error.MissingRegisterName,
.description = node.getValue("description"),
.offset = if (node.getValue("addressOffset")) |offset_str|
try std.fmt.parseInt(u64, offset_str, 0)
else
null,
return error.MissingRegisterOffset,
.size = size,
.count = count,
.access = register_props.access,
.reset_mask = register_props.reset_mask,
.reset_value = register_props.reset_value,
});
errdefer db.destroyEntity(id);
const dim_elements = try DimElements.parse(node);
if (dim_elements != null)
return error.TodoDimElements;
const register_props = try ctx.getRegisterPropsDerivedFromParent(id, node);
if (register_props.size) |size|
try db.addSize(id, size);
// TODO: protection
if (register_props.access) |access|
try db.addAccess(id, access);
if (register_props.reset_mask) |reset_mask|
try db.addResetMask(id, reset_mask);
if (register_props.reset_value) |reset_value|
try db.addResetValue(id, reset_value);
var field_it = node.iterate(&.{"fields"}, "field");
while (field_it.next()) |field_node|
loadField(ctx, field_node, id) catch |err|
@ -381,7 +367,7 @@ fn loadEnumeratedValues(ctx: *Context, node: xml.Node, field_id: EntityId) !void
.name = node.getValue("name"),
.size = db.attrs.size.get(field_id),
});
defer db.destroyEntity(id);
errdefer db.destroyEntity(id);
try db.attrs.@"enum".putNoClobber(db.gpa, field_id, id);
@ -402,7 +388,7 @@ fn loadEnumeratedValue(ctx: *Context, node: xml.Node, enum_id: EntityId) !void {
else
return error.EnumFieldMissingValue,
});
defer db.destroyEntity(id);
errdefer db.destroyEntity(id);
}
pub const Revision = struct {
@ -910,7 +896,7 @@ fn parseAccess(str: []const u8) !Access {
error.UnknownAccessType;
}
test "device register properties" {
test "svd.device register properties" {
const text =
\\<device>
\\ <name>TEST_DEVICE</name>
@ -925,6 +911,7 @@ test "device register properties" {
\\ <registers>
\\ <register>
\\ <name>TEST_REGISTER</name>
\\ <addressOffset>0</addressOffset>
\\ </register>
\\ </registers>
\\ </peripheral>
@ -948,7 +935,7 @@ test "device register properties" {
try expectAttr(db, "reset_mask", 0xffffffff, register_id);
}
test "peripheral register properties" {
test "svd.peripheral register properties" {
const text =
\\<device>
\\ <name>TEST_DEVICE</name>
@ -967,6 +954,7 @@ test "peripheral register properties" {
\\ <registers>
\\ <register>
\\ <name>TEST_REGISTER</name>
\\ <addressOffset>0</addressOffset>
\\ </register>
\\ </registers>
\\ </peripheral>
@ -989,7 +977,7 @@ test "peripheral register properties" {
try expectAttr(db, "reset_mask", 0xffff, register_id);
}
test "register register properties" {
test "svd.register register properties" {
const text =
\\<device>
\\ <name>TEST_DEVICE</name>
@ -1008,6 +996,7 @@ test "register register properties" {
\\ <registers>
\\ <register>
\\ <name>TEST_REGISTER</name>
\\ <addressOffset>0</addressOffset>
\\ <size>8</size>
\\ <access>read-write</access>
\\ <resetValue>0x0002</resetValue>
@ -1034,7 +1023,7 @@ test "register register properties" {
try expectAttr(db, "reset_mask", 0xff, register_id);
}
test "register with fields" {
test "svd.register with fields" {
const text =
\\<device>
\\ <name>TEST_DEVICE</name>
@ -1049,6 +1038,7 @@ test "register with fields" {
\\ <registers>
\\ <register>
\\ <name>TEST_REGISTER</name>
\\ <addressOffset>0</addressOffset>
\\ <fields>
\\ <field>
\\ <name>TEST_FIELD</name>
@ -1073,7 +1063,7 @@ test "register with fields" {
try expectAttr(db, "access", .read_write, field_id);
}
test "field with enum value" {
test "svd.field with enum value" {
const text =
\\<device>
\\ <name>TEST_DEVICE</name>
@ -1088,6 +1078,7 @@ test "field with enum value" {
\\ <registers>
\\ <register>
\\ <name>TEST_REGISTER</name>
\\ <addressOffset>0</addressOffset>
\\ <fields>
\\ <field>
\\ <name>TEST_FIELD</name>
@ -1139,3 +1130,153 @@ test "field with enum value" {
try expectAttr(db, "parent", enum_id, enum_field2_id);
try expectAttr(db, "description", "test enum field 2", enum_field2_id);
}
test "svd.peripheral with dimElementGroup" {
const text =
\\<device>
\\ <name>TEST_DEVICE</name>
\\ <size>32</size>
\\ <access>read-only</access>
\\ <resetValue>0x00000000</resetValue>
\\ <resetMask>0xffffffff</resetMask>
\\ <peripherals>
\\ <peripheral>
\\ <name>TEST_PERIPHERAL</name>
\\ <baseAddress>0x1000</baseAddress>
\\ <dim>4</dim>
\\ <dimIncrement>4</dimIncrement>
\\ <registers>
\\ <register>
\\ <name>TEST_REGISTER</name>
\\ <addressOffset>0</addressOffset>
\\ </register>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
;
var doc = try xml.Doc.fromMemory(text);
var db = try Database.initFromSvd(std.testing.allocator, doc);
defer db.deinit();
const peripheral_id = try db.getEntityIdByName("type.peripheral", "TEST_PERIPHERAL");
try expectAttr(db, "size", 4, peripheral_id);
const instance_id = try db.getEntityIdByName("instance.peripheral", "TEST_PERIPHERAL");
try expectAttr(db, "count", 4, instance_id);
}
test "svd.peripheral with dimElementgroup, dimIndex set" {
const text =
\\<device>
\\ <name>TEST_DEVICE</name>
\\ <size>32</size>
\\ <access>read-only</access>
\\ <resetValue>0x00000000</resetValue>
\\ <resetMask>0xffffffff</resetMask>
\\ <peripherals>
\\ <peripheral>
\\ <name>TEST_PERIPHERAL</name>
\\ <baseAddress>0x1000</baseAddress>
\\ <dim>4</dim>
\\ <dimIncrement>4</dimIncrement>
\\ <dimIndex>foo</dimIndex>
\\ <registers>
\\ <register>
\\ <name>TEST_REGISTER</name>
\\ <addressOffset>0</addressOffset>
\\ </register>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
;
var doc = try xml.Doc.fromMemory(text);
var db = try Database.initFromSvd(std.testing.allocator, doc);
defer db.deinit();
_ = try db.getEntityIdByName("instance.device", "TEST_DEVICE");
// should not exist since dimIndex is not allowed to be defined for peripherals
try expectError(error.NameNotFound, db.getEntityIdByName("type.peripheral", "TEST_PERIPHERAL"));
try expectError(error.NameNotFound, db.getEntityIdByName("instance.peripheral", "TEST_PERIPHERAL"));
}
test "svd.register with dimElementGroup" {
const text =
\\<device>
\\ <name>TEST_DEVICE</name>
\\ <size>32</size>
\\ <access>read-only</access>
\\ <resetValue>0x00000000</resetValue>
\\ <resetMask>0xffffffff</resetMask>
\\ <peripherals>
\\ <peripheral>
\\ <name>TEST_PERIPHERAL</name>
\\ <baseAddress>0x1000</baseAddress>
\\ <registers>
\\ <register>
\\ <name>TEST_REGISTER</name>
\\ <addressOffset>0</addressOffset>
\\ <dim>4</dim>
\\ <dimIncrement>4</dimIncrement>
\\ </register>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
;
var doc = try xml.Doc.fromMemory(text);
var db = try Database.initFromSvd(std.testing.allocator, doc);
defer db.deinit();
const register_id = try db.getEntityIdByName("type.register", "TEST_REGISTER");
try expectAttr(db, "count", 4, register_id);
}
test "svd.register with dimElementGroup, dimIncrement != size" {
const text =
\\<device>
\\ <name>TEST_DEVICE</name>
\\ <size>32</size>
\\ <access>read-only</access>
\\ <resetValue>0x00000000</resetValue>
\\ <resetMask>0xffffffff</resetMask>
\\ <peripherals>
\\ <peripheral>
\\ <name>TEST_PERIPHERAL</name>
\\ <baseAddress>0x1000</baseAddress>
\\ <registers>
\\ <register>
\\ <name>TEST_REGISTER</name>
\\ <addressOffset>0</addressOffset>
\\ <dim>4</dim>
\\ <dimIncrement>8</dimIncrement>
\\ <fields>
\\ <field>
\\ <name>TEST_FIELD</name>
\\ <access>read-write</access>
\\ <bitRange>[7:0]</bitRange>
\\ </field>
\\ </fields>
\\ </register>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
;
var doc = try xml.Doc.fromMemory(text);
var db = try Database.initFromSvd(std.testing.allocator, doc);
defer db.deinit();
_ = try db.getEntityIdByName("instance.device", "TEST_DEVICE");
_ = try db.getEntityIdByName("instance.peripheral", "TEST_PERIPHERAL");
_ = try db.getEntityIdByName("type.peripheral", "TEST_PERIPHERAL");
// dimIncrement is different than the size of the register, so it should never be made
try expectError(error.NameNotFound, db.getEntityIdByName("type.register", "TEST_REGISTER"));
}

Loading…
Cancel
Save