A quick fix for ATDF codegen and register groups (#59)

* A quick fix for ATDF codegen and register groups

The current code was expecting register groups and peripherals to have a
1:1 ratio, however the register groups under `modules` are more like
templates which are used to instantiate peripherals. This does the
minimal amount of refactoring in order for this to work and unblock some
users.

* i386 -> x86 target name update
wch-ch32v003
Matt Knight 2 years ago committed by Matt Knight
parent 7a0d75f105
commit f204d7f42c

@ -14,10 +14,10 @@ steps:
- "aarch64-linux-gnu"
- "aarch64-linux-musl"
- "aarch64-macos"
- "i386-linux-gnu"
- "i386-linux-musl"
- "x86-linux-gnu"
- "x86-linux-musl"
# TODO: when _tls_index is fixed
#- "i386-windows"
#- "x86-windows"
- "x86_64-linux-gnu"
- "x86_64-linux-musl"
- "x86_64-macos"

@ -108,6 +108,19 @@ pub fn build(b: *std.build.Builder) !void {
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const ndjson = b.addExecutable("ndjson", "src/ndjson.zig");
ndjson.addPackagePath("xml", "src/xml.zig");
regz.xml.link(ndjson);
const ndjson_run = ndjson.run();
ndjson_run.step.dependOn(b.getInstallStep());
if (b.args) |args| {
ndjson_run.addArgs(args);
}
const ndjson_step = b.step("ndjson", "Run ndjson program");
ndjson_step.dependOn(&ndjson_run.step);
const test_chip_file = regz.addGeneratedChipFile("tests/svd/cmsis-example.svd");
const tests = b.addTest("tests/main.zig");

@ -528,8 +528,11 @@ pub fn initFromAtdf(allocator: Allocator, doc: *xml.Doc) !Database {
const root_element: *xml.Node = xml.docGetRootElement(doc) orelse return error.NoRoot;
const tools_node = xml.findNode(root_element, "avr-tools-device-file") orelse return error.NoToolsNode;
var peripheral_instances = std.StringHashMap(void).init(allocator);
defer peripheral_instances.deinit();
var register_groups = std.StringHashMap(struct {
reg_range: IndexRange(RegisterIndex),
description: ?[]const u8,
}).init(allocator);
defer register_groups.deinit();
var db = Database{
.gpa = allocator,
@ -539,107 +542,13 @@ pub fn initFromAtdf(allocator: Allocator, doc: *xml.Doc) !Database {
};
errdefer db.deinit();
if (xml.findNode(tools_node.children orelse return error.NoChildren, "devices")) |devices_node| {
var device_it: ?*xml.Node = xml.findNode(devices_node.children, "device");
while (device_it != null) : (device_it = xml.findNode(device_it.?.next, "device")) {
if (db.device != null) {
std.log.err("multiple devices defined in this file. TODO: give user list of devices to choose from", .{});
return error.Explained;
}
// this name lowercased should line up with a zig target
const name: ?[]const u8 = if (xml.getAttribute(device_it, "name")) |n|
try db.arena.allocator().dupe(u8, n)
else
null;
const arch: ?[]const u8 = if (xml.getAttribute(device_it, "architecture")) |a|
try db.arena.allocator().dupe(u8, a)
else
null;
const family: ?[]const u8 = if (xml.getAttribute(device_it, "family")) |f|
try db.arena.allocator().dupe(u8, f)
else
null;
db.device = .{
.vendor = "Atmel",
.series = family,
.name = name,
.address_unit_bits = 16,
.width = 8,
.register_properties = .{},
};
db.cpu = svd.Cpu{
.name = arch,
.revision = "0.0.1",
.endian = .little,
.nvic_prio_bits = 0,
.vendor_systick_config = false,
.device_num_interrupts = null,
.vtor_present = false,
.mpu_present = false,
};
const device_nodes: *xml.Node = device_it.?.children orelse continue;
if (xml.findNode(device_nodes, "peripherals")) |peripherals_node| {
var module_it: ?*xml.Node = xml.findNode(peripherals_node.children.?, "module");
while (module_it != null) : (module_it = xml.findNode(module_it.?.next, "module")) {
const module_nodes = module_it.?.children orelse continue;
var instance_it: ?*xml.Node = xml.findNode(module_nodes, "instance");
while (instance_it != null) : (instance_it = xml.findNode(instance_it.?.next, "instance")) {
const instance_nodes = instance_it.?.children orelse continue;
const instance_name = xml.getAttribute(instance_it, "name") orelse return error.NoInstanceName;
if (xml.findNode(instance_nodes, "register-group")) |register_group| {
const group_name = xml.getAttribute(register_group, "name") orelse return error.NoRegisterGroupName;
const name_in_module = xml.getAttribute(register_group, "name-in-module") orelse return error.NoNameInModule;
if (!std.mem.eql(u8, instance_name, group_name) or !std.mem.eql(u8, group_name, name_in_module)) {
std.log.warn("mismatching names for name-in-module: {s}, ignoring, if you see this please cut a ticket: https://github.com/ZigEmbeddedGroup/regz", .{
name_in_module,
});
continue;
}
try peripheral_instances.put(try db.arena.allocator().dupe(u8, name_in_module), {});
}
}
}
}
if (xml.findNode(device_nodes, "interrupts")) |interrupts_node| {
var interrupt_it: ?*xml.Node = xml.findNode(interrupts_node.children.?, "interrupt");
while (interrupt_it != null) : (interrupt_it = xml.findNode(interrupt_it.?.next, "interrupt")) {
const interrupt = svd.Interrupt{
.name = if (xml.getAttribute(interrupt_it, "name")) |interrupt_name|
try db.arena.allocator().dupe(u8, interrupt_name)
else
return error.NoName,
.description = if (xml.getAttribute(interrupt_it, "caption")) |caption|
try db.arena.allocator().dupe(u8, caption)
else
return error.NoCaption,
.value = if (xml.getAttribute(interrupt_it, "index")) |index_str|
try std.fmt.parseInt(usize, index_str, 0)
else
return error.NoIndex,
};
// if the interrupt doesn't exist then do a sorted insert
if (std.sort.binarySearch(svd.Interrupt, interrupt, db.interrupts.items, {}, svd.Interrupt.compare) == null) {
try db.interrupts.append(db.gpa, interrupt);
std.sort.sort(svd.Interrupt, db.interrupts.items, {}, svd.Interrupt.lessThan);
}
}
}
}
}
if (xml.findNode(tools_node.children orelse return error.NoChildren, "modules")) |modules_node| {
std.log.debug("looking at modules", .{});
var module_it: ?*xml.Node = xml.findNode(modules_node.children.?, "module");
while (module_it != null) : (module_it = xml.findNode(module_it.?.next, "module")) {
const module_nodes: *xml.Node = module_it.?.children orelse continue;
// value groups are enums
var value_groups = std.StringHashMap(IndexRange(u32)).init(allocator);
defer value_groups.deinit();
@ -673,22 +582,25 @@ pub fn initFromAtdf(allocator: Allocator, doc: *xml.Doc) !Database {
});
}
// register groups in this part of the ATDF are templates for
// peripherals. In `devices` peripherals are instantiated using a
// register group.
//
// TODO: determine if peripheral instantiation can contain multiple
// register groups
var register_group_it: ?*xml.Node = xml.findNode(module_nodes, "register-group");
while (register_group_it != null) : (register_group_it = xml.findNode(register_group_it.?.next, "register-group")) {
const register_group_nodes: *xml.Node = register_group_it.?.children orelse continue;
const group_name = xml.getAttribute(register_group_it, "name") orelse continue;
if (!peripheral_instances.contains(group_name))
if (register_groups.contains(group_name)) {
std.log.warn("register name collision: {s}", .{group_name});
continue;
const register_group_offset = try xml.parseIntForKey(usize, db.gpa, register_group_nodes, "offset");
const peripheral_idx = @intCast(PeripheralIndex, db.peripherals.items.len);
try db.peripherals.append(db.gpa, try atdf.parsePeripheral(&db.arena, register_group_it.?));
}
const reg_begin_idx = @intCast(RegisterIndex, db.registers.items.len);
var register_it: ?*xml.Node = xml.findNode(register_group_nodes, "register");
while (register_it != null) : (register_it = xml.findNode(register_it.?.next, "register")) {
const register = try atdf.parseRegister(&db.arena, register_it.?, register_group_offset, register_it.?.children != null);
const register = try atdf.parseRegister(&db.arena, register_it.?, null, register_it.?.children != null);
const register_idx = @intCast(RegisterIndex, db.registers.items.len);
try db.registers.append(db.gpa, register);
@ -730,14 +642,129 @@ pub fn initFromAtdf(allocator: Allocator, doc: *xml.Doc) !Database {
});
}
try db.registers_in_peripherals.put(db.gpa, peripheral_idx, .{
.begin = reg_begin_idx,
.end = @intCast(RegisterIndex, db.registers.items.len),
std.log.debug("found register group: {s}", .{group_name});
try register_groups.put(try db.arena.allocator().dupe(u8, group_name), .{
.description = if (xml.getAttribute(register_group_it, "caption")) |caption|
try db.arena.allocator().dupe(u8, caption)
else
null,
.reg_range = .{
.begin = reg_begin_idx,
.end = @intCast(RegisterIndex, db.registers.items.len),
},
});
}
}
}
if (xml.findNode(tools_node.children orelse return error.NoChildren, "devices")) |devices_node| {
var device_it: ?*xml.Node = xml.findNode(devices_node.children, "device");
while (device_it != null) : (device_it = xml.findNode(device_it.?.next, "device")) {
if (db.device != null) {
std.log.err("multiple devices defined in this file. TODO: give user list of devices to choose from", .{});
return error.Explained;
}
// this name lowercased should line up with a zig target
const name: ?[]const u8 = if (xml.getAttribute(device_it, "name")) |n|
try db.arena.allocator().dupe(u8, n)
else
null;
const arch: ?[]const u8 = if (xml.getAttribute(device_it, "architecture")) |a|
try db.arena.allocator().dupe(u8, a)
else
null;
const family: ?[]const u8 = if (xml.getAttribute(device_it, "family")) |f|
try db.arena.allocator().dupe(u8, f)
else
null;
db.device = .{
.vendor = "Atmel",
.series = family,
.name = name,
.address_unit_bits = 16,
.width = 8,
.register_properties = .{},
};
db.cpu = svd.Cpu{
.name = arch,
.revision = "0.0.1",
.endian = .little,
.nvic_prio_bits = 0,
.vendor_systick_config = false,
.device_num_interrupts = null,
.vtor_present = false,
.mpu_present = false,
};
const device_nodes: *xml.Node = device_it.?.children orelse continue;
if (xml.findNode(device_nodes, "peripherals")) |peripherals_node| {
var module_it: ?*xml.Node = xml.findNode(peripherals_node.children.?, "module");
while (module_it != null) : (module_it = xml.findNode(module_it.?.next, "module")) {
const module_nodes = module_it.?.children orelse continue;
var instance_it: ?*xml.Node = xml.findNode(module_nodes, "instance");
while (instance_it != null) : (instance_it = xml.findNode(instance_it.?.next, "instance")) {
const instance_nodes = instance_it.?.children orelse continue;
const instance_name = xml.getAttribute(instance_it, "name") orelse return error.NoInstanceName;
if (xml.findNode(instance_nodes, "register-group")) |register_group| {
const name_in_module = xml.getAttribute(register_group, "name-in-module") orelse return error.NoNameInModule;
const template_group = register_groups.get(name_in_module) orelse {
std.log.warn("failed to find register group '{s}' in for peripheral '{s}'", .{
name_in_module,
instance_name,
});
continue;
};
std.log.debug("creating peripheral instance: {s}", .{instance_name});
const peripheral_idx = @intCast(PeripheralIndex, db.peripherals.items.len);
try db.peripherals.append(db.gpa, .{
.name = try db.arena.allocator().dupe(u8, instance_name),
.version = null,
.description = template_group.description,
.base_addr = if (xml.getAttribute(register_group, "offset")) |reg_offset_str|
try std.fmt.parseInt(usize, reg_offset_str, 0)
else
return error.NoOffset,
});
try db.registers_in_peripherals.put(db.gpa, peripheral_idx, template_group.reg_range);
}
}
}
}
// TODO: `module-instance` is used to relate an interrupt to a peripheral
if (xml.findNode(device_nodes, "interrupts")) |interrupts_node| {
var interrupt_it: ?*xml.Node = xml.findNode(interrupts_node.children.?, "interrupt");
while (interrupt_it != null) : (interrupt_it = xml.findNode(interrupt_it.?.next, "interrupt")) {
const interrupt = svd.Interrupt{
.name = if (xml.getAttribute(interrupt_it, "name")) |interrupt_name|
try db.arena.allocator().dupe(u8, interrupt_name)
else
return error.NoName,
.description = if (xml.getAttribute(interrupt_it, "caption")) |caption|
try db.arena.allocator().dupe(u8, caption)
else
null,
.value = if (xml.getAttribute(interrupt_it, "index")) |index_str|
try std.fmt.parseInt(usize, index_str, 0)
else
return error.NoIndex,
};
// if the interrupt doesn't exist then do a sorted insert
if (std.sort.binarySearch(svd.Interrupt, interrupt, db.interrupts.items, {}, svd.Interrupt.compare) == null) {
try db.interrupts.append(db.gpa, interrupt);
std.sort.sort(svd.Interrupt, db.interrupts.items, {}, svd.Interrupt.lessThan);
}
}
}
}
}
// there is also pinouts, however that's linked to IC package information,
// not exactly sure if we're going to do anything with that
@ -1224,14 +1251,6 @@ fn genZigFields(
if (std.mem.indexOf(u8, field.name, "%s") != null)
return error.MissingDimension;
if (expected_bit > field.offset) {
std.log.err("found overlapping fields in register:", .{});
for (fields) |f| {
std.log.err(" {s}: {}+{}", .{ f.name, f.offset, f.width });
}
return error.Explained;
}
while (expected_bit < field.offset) : ({
expected_bit += 1;
reserved_num += 1;

@ -8,8 +8,6 @@ const ArenaAllocator = std.heap.ArenaAllocator;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
pub const log_level: std.log.Level = .info;
const svd_schema = @embedFile("cmsis-svd.xsd");
const params = clap.parseParamsComptime(

@ -0,0 +1,142 @@
//! This program takes nested xml objects and turns them into newline delimited JSON
const std = @import("std");
const json = std.json;
const assert = std.debug.assert;
const xml = @import("xml");
const ContextMap = std.StringHashMap(std.StringHashMapUnmanaged([]const u8));
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{ .stack_trace_frames = 10 }){};
defer _ = gpa.deinit();
var args = std.process.args();
_ = args.next();
const query = args.next() orelse return error.NoQuery;
const path = args.next() orelse return error.NoXmlPath;
if (args.next() != null)
return error.TooManyArgs;
var components = std.ArrayList([]const u8).init(gpa.allocator());
defer components.deinit();
{
var it = std.mem.split(u8, query, ".");
while (it.next()) |component|
try components.append(component);
}
if (components.items.len == 0)
return error.NoComponents;
const doc = xml.readFile(path.ptr, null, 0) orelse return error.ReadXmlFile;
const root_element: *xml.Node = xml.docGetRootElement(doc) orelse return error.NoRoot;
if (!std.mem.eql(u8, components.items[0], std.mem.span(root_element.name)))
return;
var context = std.StringHashMap(std.StringHashMapUnmanaged([]const u8)).init(gpa.allocator());
defer context.deinit();
const stdout = std.io.getStdOut().writer();
try recursiveSearchAndPrint(
gpa.allocator(),
components.items,
context,
root_element,
stdout,
);
var children: ?*xml.Node = root_element.children;
while (children != null) : (children = children.?.next) {
if (1 != children.?.type)
continue;
}
}
fn RecursiveSearchAndPrintError(comptime Writer: type) type {
return Writer.Error || error{OutOfMemory};
}
fn recursiveSearchAndPrint(
allocator: std.mem.Allocator,
components: []const []const u8,
context: ContextMap,
node: *xml.Node,
writer: anytype,
) RecursiveSearchAndPrintError(@TypeOf(writer))!void {
assert(components.len != 0);
var attr_map = std.StringHashMapUnmanaged([]const u8){};
defer attr_map.deinit(allocator);
{
var attr_it: ?*xml.Attr = node.properties;
while (attr_it != null) : (attr_it = attr_it.?.next)
if (attr_it.?.name) |name|
if (@ptrCast(*xml.Node, attr_it.?.children).content) |content|
try attr_map.put(allocator, std.mem.span(name), std.mem.span(content));
}
var current_context = ContextMap.init(allocator);
defer current_context.deinit();
{
var it = context.iterator();
while (it.next()) |entry|
try current_context.put(entry.key_ptr.*, entry.value_ptr.*);
}
if (attr_map.count() > 0)
try current_context.put(components[0], attr_map);
if (components.len == 1) {
// we're done, convert into json tree and write to writer.
var tree = json.ValueTree{
.arena = std.heap.ArenaAllocator.init(allocator),
.root = json.Value{ .Object = json.ObjectMap.init(allocator) },
};
defer {
var it = tree.root.Object.iterator();
while (it.next()) |entry|
entry.value_ptr.Object.deinit();
tree.root.Object.deinit();
tree.deinit();
}
var it = current_context.iterator();
while (it.next()) |entry| {
var obj = json.Value{ .Object = json.ObjectMap.init(allocator) };
var attr_it = entry.value_ptr.iterator();
while (attr_it.next()) |attr_entry| {
try obj.Object.put(attr_entry.key_ptr.*, json.Value{
.String = attr_entry.value_ptr.*,
});
}
try tree.root.Object.put(entry.key_ptr.*, obj);
}
try tree.root.jsonStringify(.{}, writer);
try writer.writeByte('\n');
} else {
// pass it down to the children
var child_it: ?*xml.Node = node.children;
while (child_it != null) : (child_it = child_it.?.next) {
if (1 != child_it.?.type)
continue;
if (std.mem.eql(u8, components[1], std.mem.span(child_it.?.name)))
try recursiveSearchAndPrint(
allocator,
components[1..],
current_context,
child_it.?,
writer,
);
}
}
}

@ -10,6 +10,7 @@ const Allocator = std.mem.Allocator;
pub const Node = c.xmlNode;
pub const Doc = c.xmlDoc;
pub const Attr = c.xmlAttr;
pub const readFile = c.xmlReadFile;
pub const readIo = c.xmlReadIO;
pub const cleanupParser = c.xmlCleanupParser;

Loading…
Cancel
Save