add parsing the access type for svd (#22)

wch-ch32v003
Matt Knight 2 years ago committed by Matt Knight
parent 2eb508c318
commit edf25671b9

@ -1,3 +1,4 @@
zig-cache zig-cache
zig-out zig-out
.gyro/redirects .gyro/redirects
data

@ -7,6 +7,7 @@ const Peripheral = @import("Peripheral.zig");
const Register = @import("Register.zig"); const Register = @import("Register.zig");
const Field = @import("Field.zig"); const Field = @import("Field.zig");
const Enumeration = @import("Enumeration.zig"); const Enumeration = @import("Enumeration.zig");
pub const Access = svd.Access;
const assert = std.debug.assert; const assert = std.debug.assert;
const ArenaAllocator = std.heap.ArenaAllocator; const ArenaAllocator = std.heap.ArenaAllocator;
@ -60,9 +61,11 @@ fn RegisterProperties(comptime IndexType: type) type {
return struct { return struct {
/// register size in bits /// register size in bits
size: std.AutoHashMapUnmanaged(IndexType, usize) = .{}, size: std.AutoHashMapUnmanaged(IndexType, usize) = .{},
access: std.AutoHashMapUnmanaged(IndexType, Access) = .{},
fn deinit(self: *@This(), allocator: Allocator) void { fn deinit(self: *@This(), allocator: Allocator) void {
self.size.deinit(allocator); self.size.deinit(allocator);
self.access.deinit(allocator);
} }
}; };
} }
@ -98,6 +101,7 @@ fields_in_registers: std.AutoHashMap(RegisterIndex, IndexRange(FieldIndex)),
enumerations_in_fields: std.AutoHashMap(FieldIndex, IndexRange(EnumIndex)), enumerations_in_fields: std.AutoHashMap(FieldIndex, IndexRange(EnumIndex)),
dimensions: Dimensions, dimensions: Dimensions,
register_properties: RegisterPropertyTables = .{}, register_properties: RegisterPropertyTables = .{},
field_access: std.AutoArrayHashMapUnmanaged(FieldIndex, Access) = .{},
/// takes ownership of arena allocator /// takes ownership of arena allocator
fn init(allocator: Allocator) Self { fn init(allocator: Allocator) Self {
@ -139,6 +143,7 @@ pub fn deinit(self: *Self) void {
self.enumerations_in_fields.deinit(); self.enumerations_in_fields.deinit();
self.dimensions.deinit(); self.dimensions.deinit();
self.register_properties.deinit(self.allocator); self.register_properties.deinit(self.allocator);
self.field_access.deinit(self.allocator);
self.arena.deinit(); self.arena.deinit();
} }
@ -177,6 +182,9 @@ pub fn initFromSvd(allocator: Allocator, doc: *xml.Doc) !Self {
if (register_properties.size) |size| if (register_properties.size) |size|
try db.register_properties.peripheral.size.put(db.allocator, peripheral_idx, size); try db.register_properties.peripheral.size.put(db.allocator, peripheral_idx, size);
if (register_properties.access) |access|
try db.register_properties.peripheral.access.put(db.allocator, peripheral_idx, access);
var interrupt_it: ?*xml.Node = xml.findNode(peripheral_nodes, "interrupt"); var interrupt_it: ?*xml.Node = xml.findNode(peripheral_nodes, "interrupt");
while (interrupt_it != null) : (interrupt_it = xml.findNode(interrupt_it.?.next, "interrupt")) { while (interrupt_it != null) : (interrupt_it = xml.findNode(interrupt_it.?.next, "interrupt")) {
const interrupt_nodes: *xml.Node = interrupt_it.?.children orelse continue; const interrupt_nodes: *xml.Node = interrupt_it.?.children orelse continue;
@ -221,6 +229,10 @@ pub fn initFromSvd(allocator: Allocator, doc: *xml.Doc) !Self {
if (cluster_register_props.size) |size| if (cluster_register_props.size) |size|
try db.register_properties.cluster.size.put(db.allocator, cluster_idx, size); try db.register_properties.cluster.size.put(db.allocator, cluster_idx, size);
if (cluster_register_props.access) |access| {
try db.register_properties.cluster.access.put(db.allocator, cluster_idx, access);
}
try db.clusters_in_peripherals.append(.{ try db.clusters_in_peripherals.append(.{
.cluster_idx = cluster_idx, .cluster_idx = cluster_idx,
.peripheral_idx = peripheral_idx, .peripheral_idx = peripheral_idx,
@ -340,6 +352,9 @@ fn loadRegisters(
if (register_properties.size) |size| if (register_properties.size) |size|
try db.register_properties.register.size.put(db.allocator, register_idx, size); try db.register_properties.register.size.put(db.allocator, register_idx, size);
if (register_properties.access) |access|
try db.register_properties.register.access.put(db.allocator, register_idx, access);
const field_begin_idx = @intCast(FieldIndex, db.fields.items.len); const field_begin_idx = @intCast(FieldIndex, db.fields.items.len);
if (xml.findNode(register_nodes, "fields")) |fields_node| { if (xml.findNode(register_nodes, "fields")) |fields_node| {
var field_it: ?*xml.Node = xml.findNode(fields_node.children, "field"); var field_it: ?*xml.Node = xml.findNode(fields_node.children, "field");
@ -359,6 +374,9 @@ fn loadRegisters(
if (try svd.Dimension.parse(&db.arena, field_nodes)) |dimension| if (try svd.Dimension.parse(&db.arena, field_nodes)) |dimension|
try db.dimensions.fields.put(field_idx, dimension); try db.dimensions.fields.put(field_idx, dimension);
if (field.access) |access|
try db.field_access.put(db.allocator, field_idx, access);
// TODO: enumerations at some point when there's a solid plan // TODO: enumerations at some point when there's a solid plan
//if (xml.findNode(field_nodes, "enumeratedValues")) |enum_values_node| { //if (xml.findNode(field_nodes, "enumeratedValues")) |enum_values_node| {
// // TODO: usage // // TODO: usage
@ -441,6 +459,9 @@ fn loadNestedClusters(
if (register_properties.size) |size| if (register_properties.size) |size|
try db.register_properties.cluster.size.put(db.allocator, cluster_idx, size); try db.register_properties.cluster.size.put(db.allocator, cluster_idx, size);
if (register_properties.access) |access|
try db.register_properties.cluster.access.put(db.allocator, cluster_idx, access);
try db.clusters_in_clusters.append(.{ try db.clusters_in_clusters.append(.{
.parent_idx = parent_idx, .parent_idx = parent_idx,
.child_idx = cluster_idx, .child_idx = cluster_idx,
@ -1380,15 +1401,30 @@ fn findPeripheralContainingCluster(self: Self, cluster_idx: ClusterIndex) ?Perip
} else null; } else null;
} }
// TODO: get register properties from cluster fn registerPropertyUpwardsSearch(
self: Self,
comptime T: type,
comptime field: []const u8,
reg_idx: RegisterIndex,
clusters: []const ClusterIndex,
peripheral_idx: PeripheralIndex,
) ?T {
return @field(self.register_properties.register, field).get(reg_idx) orelse
for (clusters) |cluster_idx|
{
if (@field(self.register_properties.cluster, field).get(cluster_idx)) |value| {
break value;
}
} else @field(self.register_properties.peripheral, field).get(peripheral_idx) orelse
@field(self.device.?.register_properties, field);
}
pub fn getRegister( pub fn getRegister(
self: Self, self: Self,
reg_idx: RegisterIndex, reg_idx: RegisterIndex,
) !Register { ) !Register {
const register = self.registers.items[reg_idx]; const register = self.registers.items[reg_idx];
// build full "path" to register
// first check if
var clusters = std.ArrayListUnmanaged(ClusterIndex){}; var clusters = std.ArrayListUnmanaged(ClusterIndex){};
defer clusters.deinit(self.allocator); defer clusters.deinit(self.allocator);
@ -1408,20 +1444,28 @@ pub fn getRegister(
self.findPeripheralContainingCluster(clusters.items[clusters.items.len - 1]) orelse self.findPeripheralContainingCluster(clusters.items[clusters.items.len - 1]) orelse
return error.PeripheralNotFound; return error.PeripheralNotFound;
// TODO do upwards search of register, cluster(s), peripheral, and device const size = self.registerPropertyUpwardsSearch(
const size = self.register_properties.register.size.get(reg_idx) orelse usize,
for (clusters.items) |cluster_idx| "size",
{ reg_idx,
if (self.register_properties.cluster.size.get(cluster_idx)) |size| clusters.items,
break size; peripheral_idx,
} else self.register_properties.peripheral.size.get(peripheral_idx) orelse ) orelse if (self.device) |device| device.width else return error.SizeNotFound;
self.device.?.register_properties.size orelse return error.SizeNotFound;
const access: Access = self.registerPropertyUpwardsSearch(
Access,
"access",
reg_idx,
clusters.items,
peripheral_idx,
) orelse .read_write;
return Register{ return Register{
.name = register.name, .name = register.name,
.description = register.description, .description = register.description,
.addr_offset = register.addr_offset, .addr_offset = register.addr_offset,
.size = size, .size = size,
.access = access,
}; };
} }

@ -2,6 +2,7 @@ name: []const u8,
description: ?[]const u8, description: ?[]const u8,
offset: u8, offset: u8,
width: u8, width: u8,
access: ?@import("svd.zig").Access,
pub fn lessThan(_: void, lhs: @This(), rhs: @This()) bool { pub fn lessThan(_: void, lhs: @This(), rhs: @This()) bool {
return if (lhs.offset == rhs.offset) return if (lhs.offset == rhs.offset)

@ -2,3 +2,4 @@ name: []const u8,
description: ?[]const u8, description: ?[]const u8,
addr_offset: usize, addr_offset: usize,
size: ?usize, size: ?usize,
access: ?@import("svd.zig").Access,

@ -0,0 +1,7 @@
pub const Access = enum {
read_only,
write_only,
read_write,
write_once,
read_write_once,
};

@ -59,6 +59,8 @@ pub fn parseRegister(arena: *ArenaAllocator, node: *xml.Node, regs_start_addr: u
break :blk full_size; break :blk full_size;
} else return error.NoSize, // if this shows up then we need to determine the default size of a register } else return error.NoSize, // if this shows up then we need to determine the default size of a register
// TODO: ATDF register access
.access = .read_write,
}; };
} }
@ -88,5 +90,7 @@ pub fn parseField(arena: *ArenaAllocator, node: *xml.Node) !Field {
null, null,
.offset = offset, .offset = offset,
.width = width, .width = width,
// TODO: does atdf have a field level access?
.access = null,
}; };
} }

@ -240,6 +240,7 @@ pub fn parseRegister(arena: *ArenaAllocator, nodes: *xml.Node) !Register {
.description = try xml.parseDescription(allocator, nodes, "description"), .description = try xml.parseDescription(allocator, nodes, "description"),
.addr_offset = try std.fmt.parseInt(usize, xml.findValueForKey(nodes, "addressOffset") orelse return error.NoAddrOffset, 0), .addr_offset = try std.fmt.parseInt(usize, xml.findValueForKey(nodes, "addressOffset") orelse return error.NoAddrOffset, 0),
.size = null, .size = null,
.access = .read_write,
}; };
} }
@ -317,6 +318,10 @@ pub fn parseField(arena: *ArenaAllocator, nodes: *xml.Node) !Field {
.offset = bit_range.offset, .offset = bit_range.offset,
.width = bit_range.width, .width = bit_range.width,
.description = try xml.parseDescription(allocator, nodes, "description"), .description = try xml.parseDescription(allocator, nodes, "description"),
.access = if (xml.findValueForKey(nodes, "access")) |access_str|
try Access.parse(access_str)
else
null,
}; };
} }
@ -395,11 +400,16 @@ pub const Dimension = struct {
pub const RegisterProperties = struct { pub const RegisterProperties = struct {
size: ?usize, size: ?usize,
access: ?Access,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !RegisterProperties { pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !RegisterProperties {
_ = arena; _ = arena;
return RegisterProperties{ return RegisterProperties{
.size = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "size")), .size = try xml.parseIntForKey(usize, arena.child_allocator, nodes, "size"),
.access = if (xml.findValueForKey(nodes, "access")) |access_str|
try Access.parse(access_str)
else
null,
}; };
} }
}; };

@ -4,6 +4,7 @@ const Database = @import("Database");
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
const expectEqual = std.testing.expectEqual; const expectEqual = std.testing.expectEqual;
const Access = Database.Access;
pub fn initDbFromSvd(text: []const u8) !Database { pub fn initDbFromSvd(text: []const u8) !Database {
const doc: *xml.Doc = try xml.readFromMemory(text); const doc: *xml.Doc = try xml.readFromMemory(text);
@ -20,6 +21,36 @@ test "cmsis example" {
try expectEqual(@as(@TypeOf(db.cpu), null), db.cpu); try expectEqual(@as(@TypeOf(db.cpu), null), db.cpu);
} }
// TODO: is getting the size from the width as a last resort correct?
test "register.size from device.size" {
var db = try initDbFromSvd(
\\<device>
\\ <name>ARMCM3xxx</name>
\\ <addressUnitBits>8</addressUnitBits>
\\ <width>42</width>
\\ <peripherals>
\\ <peripheral>
\\ <name>TIMER0</name>
\\ <baseAddress>0x40010000</baseAddress>
\\ <registers>
\\ <register>
\\ <name>test</name>
\\ <addressOffset>0x0</addressOffset>
\\ </register>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
\\
);
defer db.deinit();
const register_idx = 0;
const register = try db.getRegister(register_idx);
try expectEqual(@as(usize, 42), register.size.?);
}
test "register.size from device" { test "register.size from device" {
var db = try initDbFromSvd( var db = try initDbFromSvd(
\\<device> \\<device>
@ -116,7 +147,44 @@ test "register.size from cluster" {
try expectEqual(@as(usize, 42), register.size.?); try expectEqual(@as(usize, 42), register.size.?);
} }
// TODO: nested cluster test "register.size from nested cluster" {
var db = try initDbFromSvd(
\\<device>
\\ <name>ARMCM3xxx</name>
\\ <addressUnitBits>8</addressUnitBits>
\\ <width>32</width>
\\ <size>32</size>
\\ <peripherals>
\\ <peripheral>
\\ <name>TIMER0</name>
\\ <baseAddress>0x40010000</baseAddress>
\\ <registers>
\\ <cluster>
\\ <name>bruh</name>
\\ <addressOffset>0x0</addressOffset>
\\ <cluster>
\\ <name>bruh2</name>
\\ <addressOffset>0x0</addressOffset>
\\ <size>42</size>
\\ <register>
\\ <name>test</name>
\\ <addressOffset>0x0</addressOffset>
\\ </register>
\\ </cluster>
\\ </cluster>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
\\
);
defer db.deinit();
const register_idx = 0;
const register = try db.getRegister(register_idx);
try expectEqual(@as(usize, 42), register.size.?);
}
test "register.size from register" { test "register.size from register" {
var db = try initDbFromSvd( var db = try initDbFromSvd(
@ -149,29 +217,240 @@ test "register.size from register" {
try expectEqual(@as(usize, 42), register.size.?); try expectEqual(@as(usize, 42), register.size.?);
} }
// TODO: the way this is architected we'd see the error in the code generation side of things test "register.access from device" {
//test "register.size missing" { var db = try initDbFromSvd(
// try std.testing.expectError(error.SizeNotFound, initDbFromSvd( \\<device>
// \\<device> \\ <name>ARMCM3xxx</name>
// \\ <name>ARMCM3xxx</name> \\ <addressUnitBits>8</addressUnitBits>
// \\ <addressUnitBits>8</addressUnitBits> \\ <width>32</width>
// \\ <width>32</width> \\ <size>32</size>
// \\ <peripherals> \\ <access>read-write</access>
// \\ <peripheral> \\ <peripherals>
// \\ <name>TIMER0</name> \\ <peripheral>
// \\ <baseAddress>0x40010000</baseAddress> \\ <name>TIMER0</name>
// \\ <registers> \\ <baseAddress>0x40010000</baseAddress>
// \\ <register> \\ <registers>
// \\ <name>test</name> \\ <register>
// \\ <addressOffset>0x0</addressOffset> \\ <name>test</name>
// \\ </register> \\ <addressOffset>0x0</addressOffset>
// \\ </registers> \\ </register>
// \\ </peripheral> \\ </registers>
// \\ </peripherals> \\ </peripheral>
// \\</device> \\ </peripherals>
// \\ \\</device>
// )); \\
//} );
defer db.deinit();
const register_idx = 0;
const register = try db.getRegister(register_idx);
try expectEqual(Access.read_write, register.access.?);
}
test "register.access from peripheral" {
var db = try initDbFromSvd(
\\<device>
\\ <name>ARMCM3xxx</name>
\\ <addressUnitBits>8</addressUnitBits>
\\ <width>32</width>
\\ <size>32</size>
\\ <access>read-only</access>
\\ <peripherals>
\\ <peripheral>
\\ <name>TIMER0</name>
\\ <baseAddress>0x40010000</baseAddress>
\\ <access>read-write</access>
\\ <registers>
\\ <register>
\\ <name>test</name>
\\ <addressOffset>0x0</addressOffset>
\\ </register>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
\\
);
defer db.deinit();
const register_idx = 0;
const register = try db.getRegister(register_idx);
try expectEqual(Access.read_write, register.access.?);
}
test "register.access from cluster" {
var db = try initDbFromSvd(
\\<device>
\\ <name>ARMCM3xxx</name>
\\ <addressUnitBits>8</addressUnitBits>
\\ <width>32</width>
\\ <size>32</size>
\\ <access>read-only</access>
\\ <peripherals>
\\ <peripheral>
\\ <name>TIMER0</name>
\\ <baseAddress>0x40010000</baseAddress>
\\ <registers>
\\ <cluster>
\\ <name>bruh</name>
\\ <addressOffset>0x0</addressOffset>
\\ <access>read-write</access>
\\ <register>
\\ <name>test</name>
\\ <addressOffset>0x0</addressOffset>
\\ </register>
\\ </cluster>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
\\
);
defer db.deinit();
const register_idx = 0;
const register = try db.getRegister(register_idx);
try expectEqual(Access.read_write, register.access.?);
}
test "register.access from nested cluster" {
var db = try initDbFromSvd(
\\<device>
\\ <name>ARMCM3xxx</name>
\\ <addressUnitBits>8</addressUnitBits>
\\ <width>32</width>
\\ <size>32</size>
\\ <access>read-only</access>
\\ <peripherals>
\\ <peripheral>
\\ <name>TIMER0</name>
\\ <baseAddress>0x40010000</baseAddress>
\\ <registers>
\\ <cluster>
\\ <name>bruh</name>
\\ <addressOffset>0x0</addressOffset>
\\ <cluster>
\\ <name>bruh2</name>
\\ <addressOffset>0x0</addressOffset>
\\ <access>read-write</access>
\\ <register>
\\ <name>test</name>
\\ <addressOffset>0x0</addressOffset>
\\ </register>
\\ </cluster>
\\ </cluster>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
\\
);
defer db.deinit();
const register_idx = 0;
const register = try db.getRegister(register_idx);
try expectEqual(Access.read_write, register.access.?);
}
test "register.access from register" {
var db = try initDbFromSvd(
\\<device>
\\ <name>ARMCM3xxx</name>
\\ <addressUnitBits>8</addressUnitBits>
\\ <width>32</width>
\\ <size>32</size>
\\ <access>read-only</access>
\\ <peripherals>
\\ <peripheral>
\\ <name>TIMER0</name>
\\ <baseAddress>0x40010000</baseAddress>
\\ <registers>
\\ <register>
\\ <name>test</name>
\\ <addressOffset>0x0</addressOffset>
\\ <size>42</size>
\\ <access>read-write</access>
\\ </register>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
\\
);
defer db.deinit();
const register_idx = 0;
const register = try db.getRegister(register_idx);
try expectEqual(Access.read_write, register.access.?);
}
// TODO: determine if assuming read-write by default is okay
test "register.access missing" {
var db = try initDbFromSvd(
\\<device>
\\ <name>ARMCM3xxx</name>
\\ <addressUnitBits>8</addressUnitBits>
\\ <width>32</width>
\\ <peripherals>
\\ <peripheral>
\\ <name>TIMER0</name>
\\ <baseAddress>0x40010000</baseAddress>
\\ <registers>
\\ <register>
\\ <name>test</name>
\\ <addressOffset>0x0</addressOffset>
\\ </register>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
\\
);
defer db.deinit();
const register_idx = 0;
const register = try db.getRegister(register_idx);
try expectEqual(Access.read_write, register.access.?);
}
test "field.access" {
var db = try initDbFromSvd(
\\<device>
\\ <name>ARMCM3xxx</name>
\\ <addressUnitBits>8</addressUnitBits>
\\ <width>32</width>
\\ <peripherals>
\\ <peripheral>
\\ <name>TIMER0</name>
\\ <baseAddress>0x40010000</baseAddress>
\\ <registers>
\\ <register>
\\ <name>test</name>
\\ <addressOffset>0x0</addressOffset>
\\ <fields>
\\ <field>
\\ <name>field</name>
\\ <access>read-only</access>
\\ <bitRange>[0:0]</bitRange>
\\ </field>
\\ </fields>
\\ </register>
\\ </registers>
\\ </peripheral>
\\ </peripherals>
\\</device>
\\
);
defer db.deinit();
try expectEqual(Access.read_only, db.field_access.get(0).?);
}
//test "register.access" {} //test "register.access" {}
// TODO: figure out if `protection` is interesting/important for us // TODO: figure out if `protection` is interesting/important for us

Loading…
Cancel
Save