From edf25671b9fd64d4fd6f2351ef1a428d5994513c Mon Sep 17 00:00:00 2001 From: Matt Knight Date: Thu, 19 May 2022 00:07:18 -0700 Subject: [PATCH] add parsing the access type for svd (#22) --- tools/regz/.gitignore | 1 + tools/regz/src/Database.zig | 66 ++++++-- tools/regz/src/Field.zig | 1 + tools/regz/src/Register.zig | 1 + tools/regz/src/access.zig | 7 + tools/regz/src/atdf.zig | 4 + tools/regz/src/svd.zig | 12 +- tools/regz/tests/svd.zig | 327 +++++++++++++++++++++++++++++++++--- 8 files changed, 383 insertions(+), 36 deletions(-) create mode 100644 tools/regz/src/access.zig diff --git a/tools/regz/.gitignore b/tools/regz/.gitignore index 3f6d516..e599560 100644 --- a/tools/regz/.gitignore +++ b/tools/regz/.gitignore @@ -1,3 +1,4 @@ zig-cache zig-out .gyro/redirects +data diff --git a/tools/regz/src/Database.zig b/tools/regz/src/Database.zig index 8e44dcf..0a71f95 100644 --- a/tools/regz/src/Database.zig +++ b/tools/regz/src/Database.zig @@ -7,6 +7,7 @@ const Peripheral = @import("Peripheral.zig"); const Register = @import("Register.zig"); const Field = @import("Field.zig"); const Enumeration = @import("Enumeration.zig"); +pub const Access = svd.Access; const assert = std.debug.assert; const ArenaAllocator = std.heap.ArenaAllocator; @@ -60,9 +61,11 @@ fn RegisterProperties(comptime IndexType: type) type { return struct { /// register size in bits size: std.AutoHashMapUnmanaged(IndexType, usize) = .{}, + access: std.AutoHashMapUnmanaged(IndexType, Access) = .{}, fn deinit(self: *@This(), allocator: Allocator) void { 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)), dimensions: Dimensions, register_properties: RegisterPropertyTables = .{}, +field_access: std.AutoArrayHashMapUnmanaged(FieldIndex, Access) = .{}, /// takes ownership of arena allocator fn init(allocator: Allocator) Self { @@ -139,6 +143,7 @@ pub fn deinit(self: *Self) void { self.enumerations_in_fields.deinit(); self.dimensions.deinit(); self.register_properties.deinit(self.allocator); + self.field_access.deinit(self.allocator); self.arena.deinit(); } @@ -177,6 +182,9 @@ pub fn initFromSvd(allocator: Allocator, doc: *xml.Doc) !Self { if (register_properties.size) |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"); while (interrupt_it != null) : (interrupt_it = xml.findNode(interrupt_it.?.next, "interrupt")) { 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| 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(.{ .cluster_idx = cluster_idx, .peripheral_idx = peripheral_idx, @@ -340,6 +352,9 @@ fn loadRegisters( if (register_properties.size) |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); if (xml.findNode(register_nodes, "fields")) |fields_node| { 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| 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 //if (xml.findNode(field_nodes, "enumeratedValues")) |enum_values_node| { // // TODO: usage @@ -441,6 +459,9 @@ fn loadNestedClusters( if (register_properties.size) |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(.{ .parent_idx = parent_idx, .child_idx = cluster_idx, @@ -1380,15 +1401,30 @@ fn findPeripheralContainingCluster(self: Self, cluster_idx: ClusterIndex) ?Perip } 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( self: Self, reg_idx: RegisterIndex, ) !Register { const register = self.registers.items[reg_idx]; - // build full "path" to register - // first check if var clusters = std.ArrayListUnmanaged(ClusterIndex){}; defer clusters.deinit(self.allocator); @@ -1408,20 +1444,28 @@ pub fn getRegister( self.findPeripheralContainingCluster(clusters.items[clusters.items.len - 1]) orelse return error.PeripheralNotFound; - // TODO do upwards search of register, cluster(s), peripheral, and device - const size = self.register_properties.register.size.get(reg_idx) orelse - for (clusters.items) |cluster_idx| - { - if (self.register_properties.cluster.size.get(cluster_idx)) |size| - break size; - } else self.register_properties.peripheral.size.get(peripheral_idx) orelse - self.device.?.register_properties.size orelse return error.SizeNotFound; + const size = self.registerPropertyUpwardsSearch( + usize, + "size", + reg_idx, + clusters.items, + peripheral_idx, + ) orelse if (self.device) |device| device.width else return error.SizeNotFound; + + const access: Access = self.registerPropertyUpwardsSearch( + Access, + "access", + reg_idx, + clusters.items, + peripheral_idx, + ) orelse .read_write; return Register{ .name = register.name, .description = register.description, .addr_offset = register.addr_offset, .size = size, + .access = access, }; } diff --git a/tools/regz/src/Field.zig b/tools/regz/src/Field.zig index b298ba2..a411fa4 100644 --- a/tools/regz/src/Field.zig +++ b/tools/regz/src/Field.zig @@ -2,6 +2,7 @@ name: []const u8, description: ?[]const u8, offset: u8, width: u8, +access: ?@import("svd.zig").Access, pub fn lessThan(_: void, lhs: @This(), rhs: @This()) bool { return if (lhs.offset == rhs.offset) diff --git a/tools/regz/src/Register.zig b/tools/regz/src/Register.zig index df62eaa..1de935e 100644 --- a/tools/regz/src/Register.zig +++ b/tools/regz/src/Register.zig @@ -2,3 +2,4 @@ name: []const u8, description: ?[]const u8, addr_offset: usize, size: ?usize, +access: ?@import("svd.zig").Access, diff --git a/tools/regz/src/access.zig b/tools/regz/src/access.zig new file mode 100644 index 0000000..df5d233 --- /dev/null +++ b/tools/regz/src/access.zig @@ -0,0 +1,7 @@ +pub const Access = enum { + read_only, + write_only, + read_write, + write_once, + read_write_once, +}; diff --git a/tools/regz/src/atdf.zig b/tools/regz/src/atdf.zig index 3ea1e9a..53b8e37 100644 --- a/tools/regz/src/atdf.zig +++ b/tools/regz/src/atdf.zig @@ -59,6 +59,8 @@ pub fn parseRegister(arena: *ArenaAllocator, node: *xml.Node, regs_start_addr: u break :blk full_size; } 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, .offset = offset, .width = width, + // TODO: does atdf have a field level access? + .access = null, }; } diff --git a/tools/regz/src/svd.zig b/tools/regz/src/svd.zig index 0b4e2a1..12c9243 100644 --- a/tools/regz/src/svd.zig +++ b/tools/regz/src/svd.zig @@ -240,6 +240,7 @@ pub fn parseRegister(arena: *ArenaAllocator, nodes: *xml.Node) !Register { .description = try xml.parseDescription(allocator, nodes, "description"), .addr_offset = try std.fmt.parseInt(usize, xml.findValueForKey(nodes, "addressOffset") orelse return error.NoAddrOffset, 0), .size = null, + .access = .read_write, }; } @@ -317,6 +318,10 @@ pub fn parseField(arena: *ArenaAllocator, nodes: *xml.Node) !Field { .offset = bit_range.offset, .width = bit_range.width, .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 { size: ?usize, + access: ?Access, pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !RegisterProperties { _ = arena; 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, }; } }; diff --git a/tools/regz/tests/svd.zig b/tools/regz/tests/svd.zig index 3ea7e5e..83809d6 100644 --- a/tools/regz/tests/svd.zig +++ b/tools/regz/tests/svd.zig @@ -4,6 +4,7 @@ const Database = @import("Database"); const allocator = std.testing.allocator; const expectEqual = std.testing.expectEqual; +const Access = Database.Access; pub fn initDbFromSvd(text: []const u8) !Database { const doc: *xml.Doc = try xml.readFromMemory(text); @@ -20,6 +21,36 @@ test "cmsis example" { 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( + \\ + \\ ARMCM3xxx + \\ 8 + \\ 42 + \\ + \\ + \\ TIMER0 + \\ 0x40010000 + \\ + \\ + \\ test + \\ 0x0 + \\ + \\ + \\ + \\ + \\ + \\ + ); + 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" { var db = try initDbFromSvd( \\ @@ -116,7 +147,44 @@ test "register.size from cluster" { try expectEqual(@as(usize, 42), register.size.?); } -// TODO: nested cluster +test "register.size from nested cluster" { + var db = try initDbFromSvd( + \\ + \\ ARMCM3xxx + \\ 8 + \\ 32 + \\ 32 + \\ + \\ + \\ TIMER0 + \\ 0x40010000 + \\ + \\ + \\ bruh + \\ 0x0 + \\ + \\ bruh2 + \\ 0x0 + \\ 42 + \\ + \\ test + \\ 0x0 + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ); + 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" { var db = try initDbFromSvd( @@ -149,29 +217,240 @@ test "register.size from register" { 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.size missing" { -// try std.testing.expectError(error.SizeNotFound, initDbFromSvd( -// \\ -// \\ ARMCM3xxx -// \\ 8 -// \\ 32 -// \\ -// \\ -// \\ TIMER0 -// \\ 0x40010000 -// \\ -// \\ -// \\ test -// \\ 0x0 -// \\ -// \\ -// \\ -// \\ -// \\ -// \\ -// )); -//} +test "register.access from device" { + var db = try initDbFromSvd( + \\ + \\ ARMCM3xxx + \\ 8 + \\ 32 + \\ 32 + \\ read-write + \\ + \\ + \\ TIMER0 + \\ 0x40010000 + \\ + \\ + \\ test + \\ 0x0 + \\ + \\ + \\ + \\ + \\ + \\ + ); + 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( + \\ + \\ ARMCM3xxx + \\ 8 + \\ 32 + \\ 32 + \\ read-only + \\ + \\ + \\ TIMER0 + \\ 0x40010000 + \\ read-write + \\ + \\ + \\ test + \\ 0x0 + \\ + \\ + \\ + \\ + \\ + \\ + ); + 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( + \\ + \\ ARMCM3xxx + \\ 8 + \\ 32 + \\ 32 + \\ read-only + \\ + \\ + \\ TIMER0 + \\ 0x40010000 + \\ + \\ + \\ bruh + \\ 0x0 + \\ read-write + \\ + \\ test + \\ 0x0 + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ); + 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( + \\ + \\ ARMCM3xxx + \\ 8 + \\ 32 + \\ 32 + \\ read-only + \\ + \\ + \\ TIMER0 + \\ 0x40010000 + \\ + \\ + \\ bruh + \\ 0x0 + \\ + \\ bruh2 + \\ 0x0 + \\ read-write + \\ + \\ test + \\ 0x0 + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ); + 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( + \\ + \\ ARMCM3xxx + \\ 8 + \\ 32 + \\ 32 + \\ read-only + \\ + \\ + \\ TIMER0 + \\ 0x40010000 + \\ + \\ + \\ test + \\ 0x0 + \\ 42 + \\ read-write + \\ + \\ + \\ + \\ + \\ + \\ + ); + 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( + \\ + \\ ARMCM3xxx + \\ 8 + \\ 32 + \\ + \\ + \\ TIMER0 + \\ 0x40010000 + \\ + \\ + \\ test + \\ 0x0 + \\ + \\ + \\ + \\ + \\ + \\ + ); + 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( + \\ + \\ ARMCM3xxx + \\ 8 + \\ 32 + \\ + \\ + \\ TIMER0 + \\ 0x40010000 + \\ + \\ + \\ test + \\ 0x0 + \\ + \\ + \\ field + \\ read-only + \\ [0:0] + \\ + \\ + \\ + \\ + \\ + \\ + \\ + \\ + ); + defer db.deinit(); + + try expectEqual(Access.read_only, db.field_access.get(0).?); +} //test "register.access" {} // TODO: figure out if `protection` is interesting/important for us