* snapshotting so that I can work on this as I travel

* add atdf parsing

* update readme

* fix register generation edge case and skip fields named RESERVED
wch-ch32v003
Matt Knight 3 years ago committed by Matt Knight
parent b6821bf8c1
commit 4b7c5c531b

@ -2,8 +2,8 @@
regz is a Zig code generator for microcontrollers. Vendors often publish files regz is a Zig code generator for microcontrollers. Vendors often publish files
that have the details of special function registers, for ARM this is called a that have the details of special function registers, for ARM this is called a
"System View Description" (SVD), and this tool outputs a single file for you to "System View Description" (SVD), for AVR the format is called ATDF. This tool
start interacting with the hardware: outputs a single file for you to start interacting with the hardware:
```zig ```zig
const regs = @import("nrf52.zig").registers; const regs = @import("nrf52.zig").registers;
@ -22,12 +22,15 @@ pub fn main() void {
NOTE: just including that file is not enough to run code on a microcontroller, NOTE: just including that file is not enough to run code on a microcontroller,
this is a fairly low-level tool and it is intended that the generated code be this is a fairly low-level tool and it is intended that the generated code be
used with [microzig](https://github.com/ZigEmbeddedGroup/microzig) used with something like [microzig](https://github.com/ZigEmbeddedGroup/microzig).
One can get the required SVD file from your vendor, or another good place is One can get SVD files from your vendor, or another good place is
[posborne/cmsis-svd](https://github.com/posborne/cmsis-svd/tree/master/data), [posborne/cmsis-svd](https://github.com/posborne/cmsis-svd/tree/master/data),
it's a python based SVD parser and they have a large number of files available. it's a python based SVD parser and they have a large number of files available.
For ATDF you need to unzip the appropriate atpack from the
[registry](https://packs.download.microchip.com).
## Building ## Building
regz targets zig master. regz targets zig master.
@ -39,6 +42,8 @@ zig build
## Using regz to generate code ## Using regz to generate code
Files provided may be either SVD or ATDF.
Provide path on command line: Provide path on command line:
``` ```
regz <path-to-svd> > my-chip.zig regz <path-to-svd> > my-chip.zig
@ -56,13 +61,6 @@ RISC-V based products despite it being an ARM standard. At best regz will
generate the register definitions without an interrupt table (for now), if you generate the register definitions without an interrupt table (for now), if you
run into problems issues will be warmly welcomed! run into problems issues will be warmly welcomed!
### How about AVR?
Atmel/Microchip publishes their register definitions for AVRs in ATDF, it is
not implemented, but we do plan on supporting it. There are tools like
[Rahix/atdf2svd](https://github.com/Rahix/atdf2svd) if you really can't wait to
get your hands dirty.
### What about MSP430? ### What about MSP430?
TI does have another type of XML-based register schema, it is also TI does have another type of XML-based register schema, it is also
@ -82,5 +80,5 @@ future. If you know of any others we should look into, please make an issue!
- [ ] finalize derivation of different components - [ ] finalize derivation of different components
- [ ] comprehensive suite of tests - [ ] comprehensive suite of tests
- [ ] RISC-V interrupt table generation - [ ] RISC-V interrupt table generation
- [ ] ATDF: Atmel's register schema format - [x] ATDF: AVR's register schema format
- [ ] insert name of Texus Insturment's register schema format for MSP430 - [ ] insert name of Texus Insturment's register schema format for MSP430

@ -12,9 +12,19 @@ pub fn build(b: *std.build.Builder) !void {
}); });
xml.step.install(); xml.step.install();
const commit_result = try std.ChildProcess.exec(.{
.allocator = b.allocator,
.argv = &.{ "git", "rev-parse", "HEAD" },
.cwd = std.fs.path.dirname(@src().file) orelse unreachable,
});
const build_options = b.addOptions();
build_options.addOption([]const u8, "commit", commit_result.stdout);
const exe = b.addExecutable("regz", "src/main.zig"); const exe = b.addExecutable("regz", "src/main.zig");
exe.setTarget(target); exe.setTarget(target);
exe.setBuildMode(mode); exe.setBuildMode(mode);
exe.addOptions("build_options", build_options);
exe.addPackagePath("clap", "libs/zig-clap/clap.zig"); exe.addPackagePath("clap", "libs/zig-clap/clap.zig");
xml.link(exe); xml.link(exe);
exe.install(); exe.install();

@ -1 +1 @@
Subproject commit cf8a34d11f0520bdf2afc08eda88862597a88b23 Subproject commit 5f7b75d5523d9581eca5a72a362868ff517992e8

@ -1,6 +1,12 @@
const std = @import("std"); const std = @import("std");
const build_options = @import("build_options");
const svd = @import("svd.zig"); const svd = @import("svd.zig");
const xml = @import("xml.zig"); const xml = @import("xml.zig");
const atdf = @import("atdf.zig");
const Peripheral = @import("Peripheral.zig");
const Register = @import("Register.zig");
const Field = @import("Field.zig");
const Enumeration = @import("Enumeration.zig");
const assert = std.debug.assert; const assert = std.debug.assert;
const ArenaAllocator = std.heap.ArenaAllocator; const ArenaAllocator = std.heap.ArenaAllocator;
@ -38,58 +44,76 @@ const Nesting = enum {
contained, contained,
}; };
arena: std.heap.ArenaAllocator, allocator: Allocator,
device: svd.Device, arena: ArenaAllocator,
device: ?svd.Device,
cpu: ?svd.Cpu, cpu: ?svd.Cpu,
interrupts: std.ArrayList(svd.Interrupt), interrupts: std.ArrayList(svd.Interrupt),
peripherals: std.ArrayList(svd.Peripheral), peripherals: std.ArrayList(Peripheral),
clusters: std.ArrayList(svd.Cluster), clusters: std.ArrayList(svd.Cluster),
registers: std.ArrayList(svd.Register), registers: std.ArrayList(Register),
fields: std.ArrayList(svd.Field), fields: std.ArrayList(Field),
enumerations: std.ArrayList(Enumeration),
peripherals_use_interrupts: std.ArrayList(PeripheralUsesInterrupt), peripherals_use_interrupts: std.ArrayList(PeripheralUsesInterrupt),
clusters_in_peripherals: std.ArrayList(ClusterInPeripheral), clusters_in_peripherals: std.ArrayList(ClusterInPeripheral),
clusters_in_clusters: std.ArrayList(ClusterInCluster), clusters_in_clusters: std.ArrayList(ClusterInCluster),
registers_in_peripherals: std.AutoHashMap(u32, Range), registers_in_peripherals: std.AutoHashMap(u32, Range),
registers_in_clusters: std.AutoHashMap(u32, Range), registers_in_clusters: std.AutoHashMap(u32, Range),
fields_in_registers: std.MultiArrayList(FieldsInRegister), fields_in_registers: std.AutoHashMap(u32, Range),
enumerations_in_fields: std.AutoHashMap(u32, Range),
dimensions: Dimensions, dimensions: Dimensions,
/// takes ownership of arena allocator /// takes ownership of arena allocator
fn init(arena: std.heap.ArenaAllocator, device: svd.Device) Self { fn init(allocator: Allocator) Self {
const allocator = arena.child_allocator;
return Self{ return Self{
.arena = arena, .allocator = allocator,
.device = device, .arena = ArenaAllocator.init(allocator),
.device = null,
.cpu = null, .cpu = null,
.interrupts = std.ArrayList(svd.Interrupt).init(allocator), .interrupts = std.ArrayList(svd.Interrupt).init(allocator),
.peripherals = std.ArrayList(svd.Peripheral).init(allocator), .peripherals = std.ArrayList(Peripheral).init(allocator),
.clusters = std.ArrayList(svd.Cluster).init(allocator), .clusters = std.ArrayList(svd.Cluster).init(allocator),
.registers = std.ArrayList(svd.Register).init(allocator), .registers = std.ArrayList(Register).init(allocator),
.fields = std.ArrayList(svd.Field).init(allocator), .fields = std.ArrayList(Field).init(allocator),
.enumerations = std.ArrayList(Enumeration).init(allocator),
.peripherals_use_interrupts = std.ArrayList(PeripheralUsesInterrupt).init(allocator), .peripherals_use_interrupts = std.ArrayList(PeripheralUsesInterrupt).init(allocator),
.clusters_in_peripherals = std.ArrayList(ClusterInPeripheral).init(allocator), .clusters_in_peripherals = std.ArrayList(ClusterInPeripheral).init(allocator),
.clusters_in_clusters = std.ArrayList(ClusterInCluster).init(allocator), .clusters_in_clusters = std.ArrayList(ClusterInCluster).init(allocator),
.registers_in_peripherals = std.AutoHashMap(u32, Range).init(allocator), .registers_in_peripherals = std.AutoHashMap(u32, Range).init(allocator),
.registers_in_clusters = std.AutoHashMap(u32, Range).init(allocator), .registers_in_clusters = std.AutoHashMap(u32, Range).init(allocator),
.fields_in_registers = std.MultiArrayList(FieldsInRegister){}, .fields_in_registers = std.AutoHashMap(u32, Range).init(allocator),
.enumerations_in_fields = std.AutoHashMap(u32, Range).init(allocator),
.dimensions = Dimensions.init(allocator), .dimensions = Dimensions.init(allocator),
}; };
} }
pub fn initFromSvd(allocator: std.mem.Allocator, doc: *xml.Doc) !Self { pub fn deinit(self: *Self) void {
self.interrupts.deinit();
self.peripherals.deinit();
self.clusters.deinit();
self.registers.deinit();
self.fields.deinit();
self.enumerations.deinit();
self.peripherals_use_interrupts.deinit();
self.registers_in_peripherals.deinit();
self.fields_in_registers.deinit();
self.clusters_in_peripherals.deinit();
self.clusters_in_clusters.deinit();
self.registers_in_clusters.deinit();
self.enumerations_in_fields.deinit();
self.dimensions.deinit();
self.arena.deinit();
}
pub fn initFromSvd(allocator: Allocator, doc: *xml.Doc) !Self {
const root_element: *xml.Node = xml.docGetRootElement(doc) orelse return error.NoRoot; const root_element: *xml.Node = xml.docGetRootElement(doc) orelse return error.NoRoot;
const device_node = xml.findNode(root_element, "device") orelse return error.NoDevice; const device_node = xml.findNode(root_element, "device") orelse return error.NoDevice;
const device_nodes: *xml.Node = device_node.children orelse return error.NoDeviceNodes; const device_nodes: *xml.Node = device_node.children orelse return error.NoDeviceNodes;
var arena = std.heap.ArenaAllocator.init(allocator); var db = Self.init(allocator);
const device = blk: {
errdefer arena.deinit();
break :blk try svd.Device.parse(&arena, device_nodes);
};
var db = Self.init(arena, device);
errdefer db.deinit(); errdefer db.deinit();
db.device = try svd.Device.parse(&db.arena, device_nodes);
db.cpu = if (xml.findNode(device_nodes, "cpu")) |cpu_node| db.cpu = if (xml.findNode(device_nodes, "cpu")) |cpu_node|
try svd.Cpu.parse(&db.arena, @ptrCast(*xml.Node, cpu_node.children orelse return error.NoCpu)) try svd.Cpu.parse(&db.arena, @ptrCast(*xml.Node, cpu_node.children orelse return error.NoCpu))
else else
@ -102,7 +126,7 @@ pub fn initFromSvd(allocator: std.mem.Allocator, doc: *xml.Doc) !Self {
var peripheral_it: ?*xml.Node = xml.findNode(peripherals_node.children, "peripheral"); //peripherals_node.children; var peripheral_it: ?*xml.Node = xml.findNode(peripherals_node.children, "peripheral"); //peripherals_node.children;
while (peripheral_it != null) : (peripheral_it = xml.findNode(peripheral_it.?.next, "peripheral")) { while (peripheral_it != null) : (peripheral_it = xml.findNode(peripheral_it.?.next, "peripheral")) {
const peripheral_nodes: *xml.Node = peripheral_it.?.children orelse continue; const peripheral_nodes: *xml.Node = peripheral_it.?.children orelse continue;
const peripheral = try svd.Peripheral.parse(&db.arena, peripheral_nodes); const peripheral = try svd.parsePeripheral(&db.arena, peripheral_nodes);
try db.peripherals.append(peripheral); try db.peripherals.append(peripheral);
const peripheral_idx = @intCast(u32, db.peripherals.items.len - 1); const peripheral_idx = @intCast(u32, db.peripherals.items.len - 1);
@ -231,11 +255,17 @@ pub fn initFromSvd(allocator: std.mem.Allocator, doc: *xml.Doc) !Self {
const parent_idx = entry.value_ptr.*; const parent_idx = entry.value_ptr.*;
const child_idx = entry.key_ptr.*; const child_idx = entry.key_ptr.*;
if (db.fields_in_registers.items(.field_range)[child_idx].begin != // TODO: determine how deriving from a register while having fields
db.fields_in_registers.items(.field_range)[child_idx].end) // works, does it just merge the fields in?
if (db.fields_in_registers.contains(child_idx))
return error.Todo; return error.Todo;
db.fields_in_registers.items(.field_range)[child_idx] = db.fields_in_registers.items(.field_range)[parent_idx]; if (db.fields_in_registers.get(parent_idx)) |field_range|
try db.fields_in_registers.put(child_idx, field_range)
else
std.log.warn("register '{s}' derived from '{s}', but the latter does not have fields", .{
db.registers.items[child_idx].name, db.registers.items[parent_idx].name,
});
} }
} }
@ -250,22 +280,26 @@ fn loadRegisters(
var register_it: ?*xml.Node = xml.findNode(nodes, "register"); var register_it: ?*xml.Node = xml.findNode(nodes, "register");
while (register_it != null) : (register_it = xml.findNode(register_it.?.next, "register")) { while (register_it != null) : (register_it = xml.findNode(register_it.?.next, "register")) {
const register_nodes: *xml.Node = register_it.?.children orelse continue; const register_nodes: *xml.Node = register_it.?.children orelse continue;
const register = try svd.Register.parse(&db.arena, register_nodes, db.device.register_properties.size orelse db.device.width); const register = try svd.parseRegister(&db.arena, register_nodes, db.device.?.register_properties.size orelse db.device.?.width);
const register_idx = @intCast(u32, db.registers.items.len);
try db.registers.append(register); try db.registers.append(register);
const register_idx = @intCast(u32, db.registers.items.len - 1);
if (xml.getAttribute(register_it, "derivedFrom")) |derived_from| if (xml.getAttribute(register_it, "derivedFrom")) |derived_from|
try named_derivations.registers.put(register_idx, try db.arena.allocator().dupe(u8, derived_from)); try named_derivations.registers.put(register_idx, try db.arena.allocator().dupe(u8, derived_from));
if (try svd.Dimension.parse(&db.arena, register_nodes)) |dimension| if (try svd.Dimension.parse(&db.arena, register_nodes)) |dimension|
try db.dimensions.registers.put(register_idx, dimension); try db.dimensions.registers.put(register_idx, dimension);
const field_begin_idx = db.fields.items.len; const field_begin_idx = @intCast(u32, 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");
while (field_it != null) : (field_it = xml.findNode(field_it.?.next, "field")) { while (field_it != null) : (field_it = xml.findNode(field_it.?.next, "field")) {
const field_nodes: *xml.Node = field_it.?.children orelse continue; const field_nodes: *xml.Node = field_it.?.children orelse continue;
const field = try svd.Field.parse(&db.arena, field_nodes); const field_name = xml.findValueForKey(field_nodes, "name") orelse continue;
if (useless_field_names.has(field_name))
continue;
const field = try svd.parseField(&db.arena, field_nodes);
try db.fields.append(field); try db.fields.append(field);
const field_idx = @intCast(u32, db.fields.items.len - 1); const field_idx = @intCast(u32, db.fields.items.len - 1);
@ -292,7 +326,7 @@ fn loadRegisters(
} }
// sort fields by offset // sort fields by offset
std.sort.sort(svd.Field, db.fields.items[field_begin_idx..], {}, svd.Field.lessThan); std.sort.sort(Field, db.fields.items[field_begin_idx..], {}, Field.lessThan);
// TODO: can we use unions for overlapping fields? // TODO: can we use unions for overlapping fields?
// remove overlapping fields // remove overlapping fields
@ -310,14 +344,14 @@ fn loadRegisters(
db.fields.items[i - 1].offset + db.fields.items[i - 1].width, db.fields.items[i - 1].offset + db.fields.items[i - 1].width,
register.name, register.name,
}); });
} else if (db.fields.items[i].offset + db.fields.items[i].width > db.device.width) { } else if (db.fields.items[i].offset + db.fields.items[i].width > db.device.?.width) {
const ignored = db.fields.orderedRemove(i); const ignored = db.fields.orderedRemove(i);
std.log.warn("ignoring field '{s}' ({}-{}) in register '{s}' because it's outside it's size: {}", .{ std.log.warn("ignoring field '{s}' ({}-{}) in register '{s}' because it's outside it's size: {}", .{
ignored.name, ignored.name,
ignored.offset, ignored.offset,
ignored.offset + ignored.width, ignored.offset + ignored.width,
register.name, register.name,
db.device.width, db.device.?.width,
}); });
} else { } else {
current_bit = db.fields.items[i].offset + db.fields.items[i].width; current_bit = db.fields.items[i].offset + db.fields.items[i].width;
@ -325,12 +359,10 @@ fn loadRegisters(
} }
} }
try db.fields_in_registers.append(db.arena.child_allocator, .{ if (field_begin_idx != db.fields.items.len)
.register_idx = @intCast(u32, db.registers.items.len - 1), try db.fields_in_registers.put(register_idx, .{
.field_range = .{ .begin = field_begin_idx,
.begin = @intCast(u32, field_begin_idx),
.end = @intCast(u32, db.fields.items.len), .end = @intCast(u32, db.fields.items.len),
},
}); });
} }
} }
@ -372,27 +404,238 @@ fn loadNestedClusters(
} }
} }
pub fn initFromAtdf(allocator: std.mem.Allocator, doc: *xml.Doc) !Self { pub fn initFromAtdf(allocator: Allocator, doc: *xml.Doc) !Self {
_ = doc; const root_element: *xml.Node = xml.docGetRootElement(doc) orelse return error.NoRoot;
_ = allocator; const tools_node = xml.findNode(root_element, "avr-tools-device-file") orelse return error.NoToolsNode;
return error.Todo; var regs_start_addr: usize = 0;
var peripheral_instances = std.StringHashMap(void).init(allocator);
defer peripheral_instances.deinit();
var db = Self.init(allocator);
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;
} }
pub fn deinit(self: *Self) void { // this name lowercased should line up with a zig target
const allocator = self.arena.child_allocator; const name: ?[]const u8 = if (xml.getAttribute(device_it, "name")) |n|
self.peripherals.deinit(); try db.arena.allocator().dupe(u8, n)
self.interrupts.deinit(); else
self.registers.deinit(); null;
self.fields.deinit(); const arch: ?[]const u8 = if (xml.getAttribute(device_it, "architecture")) |a|
self.clusters.deinit(); try db.arena.allocator().dupe(u8, a)
self.peripherals_use_interrupts.deinit(); else
self.registers_in_peripherals.deinit(); null;
self.fields_in_registers.deinit(allocator); const family: ?[]const u8 = if (xml.getAttribute(device_it, "family")) |f|
self.clusters_in_peripherals.deinit(); try db.arena.allocator().dupe(u8, f)
self.clusters_in_clusters.deinit(); else
self.registers_in_clusters.deinit(); null;
self.dimensions.deinit();
self.arena.deinit(); 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,
};
const device_nodes: *xml.Node = device_it.?.children orelse continue;
regs_start_addr = if (xml.findNode(device_nodes, "address-spaces")) |address_spaces_node| blk: {
var ret: ?usize = null;
var address_space_it: ?*xml.Node = xml.findNode(address_spaces_node.children.?, "address-space");
while (address_space_it != null) : (address_space_it = xml.findNode(address_space_it.?.next, "address-space")) {
const address_space_nodes: *xml.Node = address_space_it.?.children orelse continue;
var memory_segment_it: ?*xml.Node = xml.findNode(address_space_nodes, "memory-segment");
while (memory_segment_it != null) : (memory_segment_it = xml.findNode(memory_segment_it.?.next, "memory-segment")) {
const memory_type = xml.getAttribute(memory_segment_it, "type") orelse continue;
if (std.mem.eql(u8, "regs", memory_type)) {
if (ret != null) {
std.log.err("multiple register memory segments found, no idea what to do, please cut a ticket: https://github.com/ZigEmbeddedGroup/regz", .{});
return error.Explained;
} else if (xml.getAttribute(memory_segment_it, "start")) |addr_str| {
ret = try std.fmt.parseInt(usize, addr_str, 0);
}
}
}
}
if (ret) |start_address| {
break :blk start_address;
} else {
std.log.err("failed to determine the start address for registers", .{});
return error.Explained;
}
} else unreachable;
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(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| {
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;
var value_groups = std.StringHashMap(Range).init(allocator);
defer value_groups.deinit();
var value_group_it: ?*xml.Node = xml.findNode(module_nodes, "value-group");
while (value_group_it != null) : (value_group_it = xml.findNode(value_group_it.?.next, "value-group")) {
const value_group_nodes: *xml.Node = value_group_it.?.children orelse continue;
const value_group_name = if (xml.getAttribute(value_group_it, "name")) |name|
try db.arena.allocator().dupe(u8, name)
else
continue;
const first_enum_idx = @intCast(u32, db.enumerations.items.len);
var value_it: ?*xml.Node = xml.findNode(value_group_nodes, "value");
while (value_it != null) : (value_it = xml.findNode(value_it.?.next, "value")) {
try db.enumerations.append(.{
.value = if (xml.getAttribute(value_it, "value")) |value_str|
try std.fmt.parseInt(usize, value_str, 0)
else
continue,
.description = if (xml.getAttribute(value_it, "caption")) |caption|
try db.arena.allocator().dupe(u8, caption)
else
null,
});
}
std.sort.sort(Enumeration, db.enumerations.items[first_enum_idx..], {}, Enumeration.lessThan);
try value_groups.put(value_group_name, .{
.begin = first_enum_idx,
.end = @intCast(u32, db.enumerations.items.len),
});
}
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))
continue;
const peripheral_idx = @intCast(u32, db.peripherals.items.len);
try db.peripherals.append(try atdf.parsePeripheral(&db.arena, register_group_it.?));
const reg_begin_idx = @intCast(u32, 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_idx = @intCast(u32, db.registers.items.len);
try db.registers.append(try atdf.parseRegister(&db.arena, register_it.?, regs_start_addr, register_it.?.children != null));
const register_nodes: *xml.Node = register_it.?.children orelse continue;
const field_begin_idx = @intCast(u32, db.fields.items.len);
var bitfield_it: ?*xml.Node = xml.findNode(register_nodes, "bitfield");
while (bitfield_it != null) : (bitfield_it = xml.findNode(bitfield_it.?.next, "bitfield")) {
try db.fields.append(atdf.parseField(&db.arena, bitfield_it.?) catch |err| switch (err) {
error.InvalidMask => continue,
else => return err,
});
}
// we expect fields to be sorted by offset
std.sort.sort(Field, db.fields.items[field_begin_idx..], {}, Field.lessThan);
// go back through bitfields and get the enumerations
bitfield_it = xml.findNode(register_nodes, "bitfield");
while (bitfield_it != null) : (bitfield_it = xml.findNode(bitfield_it.?.next, "bitfield")) {
if (xml.getAttribute(bitfield_it, "values")) |value_group_name| {
const field_name = xml.getAttribute(bitfield_it, "name") orelse continue;
const field_idx = for (db.fields.items[field_begin_idx..]) |field, offset| {
if (std.mem.eql(u8, field_name, field.name))
break field_begin_idx + @intCast(u32, offset);
} else continue;
if (value_groups.get(value_group_name)) |enum_range|
try db.enumerations_in_fields.put(field_idx, enum_range);
}
}
try db.fields_in_registers.put(register_idx, .{
.begin = field_begin_idx,
.end = @intCast(u32, db.fields.items.len),
});
}
try db.registers_in_peripherals.put(peripheral_idx, .{
.begin = reg_begin_idx,
.end = @intCast(u32, db.registers.items.len),
});
}
}
}
// there is also pinouts, however that's linked to IC package information,
// not exactly sure if we're going to do anything with that
return db;
} }
fn writeDescription( fn writeDescription(
@ -436,11 +679,20 @@ fn writeDescription(
} }
pub fn toZig(self: *Self, writer: anytype) !void { pub fn toZig(self: *Self, writer: anytype) !void {
try writer.writeAll("// this file is generated by regz\n//\n"); if (self.device == null) {
if (self.device.vendor) |vendor_name| std.log.err("failed to find device info", .{});
return error.Explained;
}
try writer.print(
\\// this file was generated by regz: https://github.com/ZigEmbeddedGroup/regz
\\// commit: {s}
, .{build_options.commit});
try writer.writeAll("//\n");
if (self.device.?.vendor) |vendor_name|
try writer.print("// vendor: {s}\n", .{vendor_name}); try writer.print("// vendor: {s}\n", .{vendor_name});
if (self.device.name) |device_name| if (self.device.?.name) |device_name|
try writer.print("// device: {s}\n", .{device_name}); try writer.print("// device: {s}\n", .{device_name});
if (self.cpu) |cpu| if (cpu.name) |cpu_name| if (self.cpu) |cpu| if (cpu.name) |cpu_name|
@ -450,6 +702,8 @@ pub fn toZig(self: *Self, writer: anytype) !void {
if (svd.CpuName.parse(self.cpu.?.name.?)) |cpu_type| { if (svd.CpuName.parse(self.cpu.?.name.?)) |cpu_type| {
try writer.writeAll("\npub const VectorTable = struct {\n"); try writer.writeAll("\npub const VectorTable = struct {\n");
// TODO: isCortexM()
if (cpu_type != .avr) {
// this is an arm machine // this is an arm machine
try writer.writeAll( try writer.writeAll(
\\ initial_stack_pointer: u32, \\ initial_stack_pointer: u32,
@ -486,6 +740,7 @@ pub fn toZig(self: *Self, writer: anytype) !void {
\\ SysTick: InterruptVector = unhandled, \\ SysTick: InterruptVector = unhandled,
\\ \\
); );
}
var reserved_count: usize = 2; var reserved_count: usize = 2;
var expected: usize = 0; var expected: usize = 0;
@ -502,7 +757,7 @@ pub fn toZig(self: *Self, writer: anytype) !void {
try writer.print(" reserved{}: u32 = undefined,\n", .{reserved_count}); try writer.print(" reserved{}: u32 = undefined,\n", .{reserved_count});
} }
if (interrupt.description) |description| if (!isUselessDescription(description)) if (interrupt.description) |description| if (!useless_descriptions.has(description))
try writeDescription(self.arena.child_allocator, writer, description, 1); try writeDescription(self.arena.child_allocator, writer, description, 1);
try writer.print(" {s}: InterruptVector = unhandled,\n", .{std.zig.fmtId(interrupt.name)}); try writer.print(" {s}: InterruptVector = unhandled,\n", .{std.zig.fmtId(interrupt.name)});
@ -534,13 +789,15 @@ pub fn toZig(self: *Self, writer: anytype) !void {
const reg_range = self.registers_in_peripherals.get(peripheral_idx).?; const reg_range = self.registers_in_peripherals.get(peripheral_idx).?;
const registers = self.registers.items[reg_range.begin..reg_range.end]; const registers = self.registers.items[reg_range.begin..reg_range.end];
if (registers.len != 0 or has_clusters) { if (registers.len != 0 or has_clusters) {
if (peripheral.description) |description| if (!isUselessDescription(description)) if (peripheral.description) |description| if (!useless_descriptions.has(description)) {
try writer.writeByte('\n');
try writeDescription(self.arena.child_allocator, writer, description, 1); try writeDescription(self.arena.child_allocator, writer, description, 1);
try writer.print( };
\\ pub const {s} = struct {{
\\ pub const base_address = 0x{x}; try writer.print(" pub const {s} = struct {{\n", .{std.zig.fmtId(peripheral.name)});
\\ if (peripheral.base_addr) |base_addr|
, .{ std.zig.fmtId(peripheral.name), peripheral.base_addr }); try writer.print(" pub const base_address = 0x{x};\n", .{base_addr});
if (peripheral.version) |version| if (peripheral.version) |version|
try writer.print(" pub const version = \"{s}\";\n", .{version}); try writer.print(" pub const version = \"{s}\";\n", .{version});
@ -575,7 +832,7 @@ pub fn toZig(self: *Self, writer: anytype) !void {
fn genZigCluster( fn genZigCluster(
db: *Self, db: *Self,
writer: anytype, writer: anytype,
base_addr: usize, base_addr: ?usize,
cluster_idx: u32, cluster_idx: u32,
indent: usize, indent: usize,
nesting: Nesting, nesting: Nesting,
@ -595,7 +852,7 @@ fn genZigCluster(
const registers = db.registers.items[range.begin..range.end]; const registers = db.registers.items[range.begin..range.end];
try writer.writeByte('\n'); try writer.writeByte('\n');
if (cluster.description) |description| if (cluster.description) |description|
if (!isUselessDescription(description)) if (!useless_descriptions.has(description))
try writeDescription(db.arena.child_allocator, writer, description, indent); try writeDescription(db.arena.child_allocator, writer, description, indent);
if (dimension_opt) |dimension| { if (dimension_opt) |dimension| {
@ -612,11 +869,11 @@ fn genZigCluster(
bits += register.size; bits += register.size;
} }
if (bits % 8 != 0 or db.device.width % 8 != 0) if (bits % 8 != 0 or db.device.?.width % 8 != 0)
return error.InvalidWordSize; return error.InvalidWordSize;
const bytes = bits / 8; const bytes = bits / 8;
const bytes_per_word = db.device.width / 8; const bytes_per_word = db.device.?.width / 8;
if (bytes > dimension.increment) if (bytes > dimension.increment)
return error.InvalidClusterSize; return error.InvalidClusterSize;
@ -624,7 +881,7 @@ fn genZigCluster(
var i: usize = 0; var i: usize = 0;
while (i < num_padding_words) : (i += 1) { while (i < num_padding_words) : (i += 1) {
try writer.writeByteNTimes(' ', (indent + 1) * 4); try writer.writeByteNTimes(' ', (indent + 1) * 4);
try writer.print("padding{}: u{},\n", .{ i, db.device.width }); try writer.print("padding{}: u{},\n", .{ i, db.device.?.width });
} }
try writer.writeByteNTimes(' ', indent * 4); try writer.writeByteNTimes(' ', indent * 4);
@ -650,50 +907,69 @@ fn genZigSingleRegister(
writer: anytype, writer: anytype,
name: []const u8, name: []const u8,
width: usize, width: usize,
has_base_addr: bool,
addr_offset: usize, addr_offset: usize,
fields: []svd.Field, field_range_opt: ?Range,
first_field_idx: u32,
array_prefix: []const u8, array_prefix: []const u8,
indent: usize, indent: usize,
nesting: Nesting, nesting: Nesting,
) !void { ) !void {
const single_line_declaration = fields.len == 0 or (fields.len == 1 and std.mem.eql(u8, fields[0].name, name)); if (field_range_opt) |field_range| {
if (single_line_declaration) { const fields = self.fields.items[field_range.begin..field_range.end];
if (fields.len == 1 and fields[0].width < width) { assert(fields.len != 0);
if (fields.len == 1 and std.mem.eql(u8, fields[0].name, name)) {
if (fields[0].width > width)
return error.BadWidth;
try writer.writeByteNTimes(' ', indent * 4); try writer.writeByteNTimes(' ', indent * 4);
if (fields[0].width == width)
// TODO: oof please refactor this
switch (nesting) { switch (nesting) {
.namespaced => try writer.print("pub const {s} = @intToPtr(*volatile {s}MmioInt({}, u{}), base_address + 0x{x});\n", .{ .namespaced => if (has_base_addr)
try writer.print("pub const {s} = @intToPtr(*volatile {s}u{}, base_address + 0x{x});\n", .{
std.zig.fmtId(name),
array_prefix,
width,
addr_offset,
})
else
try writer.print("pub const {s} = @intToPtr(*volatile {s}u{}, 0x{x});\n", .{
std.zig.fmtId(name), std.zig.fmtId(name),
array_prefix, array_prefix,
width, width,
fields[0].width,
addr_offset, addr_offset,
}), }),
.contained => try writer.print("{s}: {s}MmioInt({}, u{}),\n", .{ .contained => try writer.print("{s}: {s}u{},\n", .{
std.zig.fmtId(name), std.zig.fmtId(name),
array_prefix, array_prefix,
width, width,
fields[0].width,
}), }),
} }
} else if (fields.len == 1 and fields[0].width > width) { else switch (nesting) {
return error.BadWidth; .namespaced => if (has_base_addr)
} else { try writer.print("pub const {s} = @intToPtr(*volatile {s}MmioInt({}, u{}), base_address + 0x{x});\n", .{
try writer.writeByteNTimes(' ', indent * 4);
switch (nesting) {
.namespaced => try writer.print("pub const {s} = @intToPtr(*volatile {s}u{}, base_address + 0x{x});\n", .{
std.zig.fmtId(name), std.zig.fmtId(name),
array_prefix, array_prefix,
width, width,
fields[0].width,
addr_offset,
})
else
try writer.print("pub const {s} = @intToPtr(*volatile {s}MmioInt({}, u{}), 0x{x});\n", .{
std.zig.fmtId(name),
array_prefix,
width,
fields[0].width,
addr_offset, addr_offset,
}), }),
.contained => try writer.print("{s}: {s}u{},\n", .{ .contained => try writer.print("{s}: {s}MmioInt({}, u{}),\n", .{
std.zig.fmtId(name), std.zig.fmtId(name),
array_prefix, array_prefix,
width, width,
fields[0].width,
}), }),
} }
}
} else { } else {
try writer.writeByteNTimes(' ', indent * 4); try writer.writeByteNTimes(' ', indent * 4);
switch (nesting) { switch (nesting) {
@ -713,23 +989,50 @@ fn genZigSingleRegister(
writer, writer,
width, width,
fields, fields,
first_field_idx, field_range.begin,
indent + 1, indent + 1,
); );
try writer.writeByteNTimes(' ', indent * 4); try writer.writeByteNTimes(' ', indent * 4);
switch (nesting) { switch (nesting) {
.namespaced => try writer.print("}}), base_address + 0x{x});\n", .{addr_offset}), .namespaced => if (has_base_addr)
try writer.print("}}), base_address + 0x{x});\n", .{addr_offset})
else
try writer.print("}}), 0x{x});\n", .{addr_offset}),
.contained => try writer.writeAll("}),\n"), .contained => try writer.writeAll("}),\n"),
} }
} }
} else {
try writer.writeByteNTimes(' ', indent * 4);
switch (nesting) {
.namespaced => if (has_base_addr)
try writer.print("pub const {s} = @intToPtr(*volatile {s}u{}, base_address + 0x{x});\n", .{
std.zig.fmtId(name),
array_prefix,
width,
addr_offset,
})
else
try writer.print("pub const {s} = @intToPtr(*volatile {s}u{}, 0x{x});\n", .{
std.zig.fmtId(name),
array_prefix,
width,
addr_offset,
}),
.contained => try writer.print("{s}: {s}u{},\n", .{
std.zig.fmtId(name),
array_prefix,
width,
}),
}
}
} }
fn genZigFields( fn genZigFields(
self: *Self, self: *Self,
writer: anytype, writer: anytype,
reg_width: usize, reg_width: usize,
fields: []svd.Field, fields: []Field,
first_field_idx: u32, first_field_idx: u32,
indent: usize, indent: usize,
) !void { ) !void {
@ -798,10 +1101,29 @@ fn genZigFields(
break; break;
} }
const enumerations_opt = if (self.enumerations_in_fields.get(field_idx)) |enum_range|
self.enumerations.items[enum_range.begin..enum_range.end]
else
null;
// TODO: default values? // TODO: default values?
if (field.description) |description| if (field.description) |description|
if (!isUselessDescription(description)) if (!useless_descriptions.has(description)) {
try writeDescription(self.arena.child_allocator, writer, description, indent); try writeDescription(self.arena.child_allocator, writer, description, indent);
if (enumerations_opt != null) {
try writer.writeByteNTimes(' ', indent * 4);
try writer.writeAll("///\n");
}
};
if (enumerations_opt) |enumerations| for (enumerations) |enumeration| {
try writer.writeByteNTimes(' ', indent * 4);
try writer.print("/// 0x{x}: ", .{enumeration.value});
if (enumeration.description) |description|
try writer.print("{s}\n", .{description})
else
try writer.writeAll("undocumented\n");
};
try writer.writeByteNTimes(' ', indent * 4); try writer.writeByteNTimes(' ', indent * 4);
try writer.print("{s}: u{},\n", .{ std.zig.fmtId(field.name), field.width }); try writer.print("{s}: u{},\n", .{ std.zig.fmtId(field.name), field.width });
@ -823,16 +1145,13 @@ fn genZigFields(
fn genZigRegister( fn genZigRegister(
self: *Self, self: *Self,
writer: anytype, writer: anytype,
base_addr: usize, base_addr: ?usize,
reg_idx: u32, reg_idx: u32,
indent: usize, indent: usize,
nesting: Nesting, nesting: Nesting,
) !void { ) !void {
const register = self.registers.items[reg_idx]; const register = self.registers.items[reg_idx];
const fields = blk: { const field_range = if (self.fields_in_registers.get(reg_idx)) |range| range else null;
const range = self.fields_in_registers.items(.field_range)[reg_idx];
break :blk self.fields.items[range.begin..range.end];
};
const dimension_opt = self.dimensions.registers.get(reg_idx); const dimension_opt = self.dimensions.registers.get(reg_idx);
if (dimension_opt == null and std.mem.indexOf(u8, register.name, "%s") != null) if (dimension_opt == null and std.mem.indexOf(u8, register.name, "%s") != null)
@ -858,8 +1177,9 @@ fn genZigRegister(
try writer.writeByte('\n'); try writer.writeByte('\n');
if (nesting == .namespaced) { if (nesting == .namespaced) {
const addr = if (base_addr) |base| base + addr_offset else addr_offset;
try writer.writeByteNTimes(' ', indent * 4); try writer.writeByteNTimes(' ', indent * 4);
try writer.print("/// address: 0x{x}\n", .{base_addr + addr_offset}); try writer.print("/// address: 0x{x}\n", .{addr});
} }
if (register.description) |description| if (register.description) |description|
@ -869,9 +1189,9 @@ fn genZigRegister(
writer, writer,
name, name,
register.size, register.size,
base_addr != null,
addr_offset, addr_offset,
fields, field_range,
self.fields_in_registers.items(.field_range)[reg_idx].begin,
"", "",
indent, indent,
nesting, nesting,
@ -893,8 +1213,9 @@ fn genZigRegister(
try writer.writeByte('\n'); try writer.writeByte('\n');
if (nesting == .namespaced) { if (nesting == .namespaced) {
const addr = if (base_addr) |base| base + addr_offset else addr_offset;
try writer.writeByteNTimes(' ', indent * 4); try writer.writeByteNTimes(' ', indent * 4);
try writer.print("/// address: 0x{x}\n", .{base_addr + addr_offset}); try writer.print("/// address: 0x{x}\n", .{addr});
} }
if (register.description) |description| if (register.description) |description|
@ -904,9 +1225,9 @@ fn genZigRegister(
writer, writer,
name, name,
register.size, register.size,
base_addr != null,
addr_offset, addr_offset,
fields, field_range,
self.fields_in_registers.items(.field_range)[reg_idx].begin,
"", "",
indent, indent,
nesting, nesting,
@ -949,8 +1270,9 @@ fn genZigRegister(
const name = try std.mem.replaceOwned(u8, self.arena.allocator(), register.name, "[%s]", ""); const name = try std.mem.replaceOwned(u8, self.arena.allocator(), register.name, "[%s]", "");
try writer.writeByte('\n'); try writer.writeByte('\n');
if (nesting == .namespaced) { if (nesting == .namespaced) {
const addr = if (base_addr) |base| base + register.addr_offset else register.addr_offset;
try writer.writeByteNTimes(' ', indent * 4); try writer.writeByteNTimes(' ', indent * 4);
try writer.print("/// address: 0x{x}\n", .{base_addr + register.addr_offset}); try writer.print("/// address: 0x{x}\n", .{addr});
} }
if (register.description) |description| if (register.description) |description|
@ -960,9 +1282,9 @@ fn genZigRegister(
writer, writer,
name, name,
register.size, register.size,
base_addr != null,
register.addr_offset, register.addr_offset,
fields, field_range,
self.fields_in_registers.items(.field_range)[reg_idx].begin,
array_prefix, array_prefix,
indent, indent,
nesting, nesting,
@ -978,7 +1300,6 @@ fn genZigRegister(
std.log.info(" dim_index: {}", .{dimension.index}); std.log.info(" dim_index: {}", .{dimension.index});
} }
std.log.info(" fields: {}", .{fields.len});
assert(false); // haven't figured out this configuration yet assert(false); // haven't figured out this configuration yet
} }
@ -1038,13 +1359,10 @@ const Dimensions = struct {
} }
}; };
const useless_descriptions: []const []const u8 = &.{ const useless_descriptions = std.ComptimeStringMap(void, .{
"Unspecified", .{"Unspecified"},
}; });
fn isUselessDescription(description: []const u8) bool { const useless_field_names = std.ComptimeStringMap(void, .{
return for (useless_descriptions) |useless_description| { .{"RESERVED"},
if (std.mem.eql(u8, description, useless_description)) });
break true;
} else false;
}

@ -0,0 +1,6 @@
value: usize,
description: ?[]const u8,
pub fn lessThan(_: void, lhs: @This(), rhs: @This()) bool {
return lhs.value < rhs.value;
}

@ -0,0 +1,11 @@
name: []const u8,
description: ?[]const u8,
offset: u8,
width: u8,
pub fn lessThan(_: void, lhs: @This(), rhs: @This()) bool {
return if (lhs.offset == rhs.offset)
lhs.width < rhs.width
else
lhs.offset < rhs.offset;
}

@ -0,0 +1,4 @@
name: []const u8,
version: ?[]const u8,
description: ?[]const u8,
base_addr: ?usize,

@ -0,0 +1,4 @@
name: []const u8,
description: ?[]const u8,
addr_offset: usize,
size: usize,

@ -0,0 +1,92 @@
const std = @import("std");
const xml = @import("xml.zig");
const Peripheral = @import("Peripheral.zig");
const Register = @import("Register.zig");
const Field = @import("Field.zig");
const ArenaAllocator = std.heap.ArenaAllocator;
const Allocator = std.mem.Allocator;
pub fn parsePeripheral(arena: *ArenaAllocator, node: *xml.Node) !Peripheral {
const allocator = arena.allocator();
return Peripheral{
.name = try allocator.dupe(u8, xml.getAttribute(node, "name") orelse return error.NoName),
.version = if (xml.getAttribute(node, "version")) |version|
try allocator.dupe(u8, version)
else
null,
.description = if (xml.getAttribute(node, "caption")) |caption|
try allocator.dupe(u8, caption)
else
null,
// TODO: make sure this is always null
.base_addr = null,
};
}
pub fn parseRegister(arena: *ArenaAllocator, node: *xml.Node, regs_start_addr: usize, has_fields: bool) !Register {
const allocator = arena.allocator();
return Register{
.name = try allocator.dupe(u8, xml.getAttribute(node, "name") orelse return error.NoName),
.description = if (xml.getAttribute(node, "caption")) |caption|
try allocator.dupe(u8, caption)
else
null,
.addr_offset = if (xml.getAttribute(node, "offset")) |reg_offset_str|
regs_start_addr + try std.fmt.parseInt(usize, reg_offset_str, 0)
else
return error.NoAddrOffset,
.size = if (xml.getAttribute(node, "size")) |size_str| blk: {
const full_size = 8 * try std.fmt.parseInt(usize, size_str, 0);
if (!has_fields) {
const mask = try std.fmt.parseInt(u64, xml.getAttribute(node, "mask") orelse break :blk full_size, 0);
// ensure it starts at bit 0
if (mask & 0x1 == 0)
break :blk full_size;
var cursor: u7 = 0;
while ((mask & (@as(u64, 1) << @intCast(u6, cursor))) != 0 and cursor < @bitSizeOf(u64)) : (cursor += 1) {}
// we now have width, make sure that the mask is contiguous
const width = cursor;
while (cursor < @bitSizeOf(u64)) : (cursor += 1) {
if ((mask & (@as(u64, 1) << @intCast(u6, cursor))) != 0) {
break :blk full_size;
}
} else break :blk width;
}
break :blk full_size;
} else return error.NoSize, // if this shows up then we need to determine the default size of a register
};
}
pub fn parseField(arena: *ArenaAllocator, node: *xml.Node) !Field {
const allocator = arena.allocator();
const name = try allocator.dupe(u8, xml.getAttribute(node, "name") orelse return error.NoName);
const mask = try std.fmt.parseInt(u64, xml.getAttribute(node, "mask") orelse return error.NoMask, 0);
var offset: u7 = 0;
while ((mask & (@as(u64, 1) << @intCast(u6, offset))) == 0 and offset < @bitSizeOf(usize)) : (offset += 1) {}
var cursor: u7 = offset;
while ((mask & (@as(u64, 1) << @intCast(u6, cursor))) != 0 and cursor < @bitSizeOf(u64)) : (cursor += 1) {}
const width = cursor - offset;
while (cursor < @bitSizeOf(u64)) : (cursor += 1)
if ((mask & (@as(u64, 1) << @intCast(u6, cursor))) != 0) {
std.log.warn("found mask with discontinuous bits: {s}, ignoring", .{name});
return error.InvalidMask;
};
return Field{
.name = name,
.description = if (xml.getAttribute(node, "caption")) |caption|
try allocator.dupe(u8, caption)
else
null,
.offset = offset,
.width = width,
};
}

@ -14,13 +14,8 @@ const svd_schema = @embedFile("cmsis-svd.xsd");
const params = [_]clap.Param(clap.Help){ const params = [_]clap.Param(clap.Help){
clap.parseParam("-h, --help Display this help and exit") catch unreachable, clap.parseParam("-h, --help Display this help and exit") catch unreachable,
clap.parseParam("-s, --schema <TYPE> Explicitly set schema type, one of: svd, atdf, json") catch unreachable, clap.parseParam("-s, --schema <str> Explicitly set schema type, one of: svd, atdf, json") catch unreachable,
clap.parseParam("<POS>...") catch unreachable, clap.parseParam("<str>...") catch unreachable,
};
const parsers = .{
.TYPE = clap.parsers.string,
.POS = clap.parsers.string,
}; };
pub fn main() !void { pub fn main() !void {
@ -41,12 +36,17 @@ const Schema = enum {
fn mainImpl() anyerror!void { fn mainImpl() anyerror!void {
defer xml.cleanupParser(); defer xml.cleanupParser();
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{
.stack_trace_frames = 20,
}){};
const allocator = gpa.allocator(); const allocator = gpa.allocator();
defer _ = gpa.deinit(); defer _ = gpa.deinit();
var arena = ArenaAllocator.init(allocator);
defer arena.deinit();
var diag = clap.Diagnostic{}; var diag = clap.Diagnostic{};
var res = clap.parse(clap.Help, &params, parsers, .{ .diagnostic = &diag }) catch |err| { var res = clap.parse(clap.Help, &params, clap.parsers.default, .{ .diagnostic = &diag }) catch |err| {
// Report useful error and exit // Report useful error and exit
diag.report(std.io.getStdErr().writer(), err) catch {}; diag.report(std.io.getStdErr().writer(), err) catch {};
return error.Explained; return error.Explained;
@ -127,7 +127,7 @@ fn readFn(ctx: ?*anyopaque, buffer: ?[*]u8, len: c_int) callconv(.C) c_int {
} else -1; } else -1;
} }
fn parseXmlDatabase(allocator: std.mem.Allocator, doc: *xml.Doc, schema: Schema) !Database { fn parseXmlDatabase(allocator: Allocator, doc: *xml.Doc, schema: Schema) !Database {
return switch (schema) { return switch (schema) {
.json => unreachable, .json => unreachable,
.atdf => try Database.initFromAtdf(allocator, doc), .atdf => try Database.initFromAtdf(allocator, doc),

@ -1,27 +1,28 @@
const std = @import("std"); const std = @import("std");
const xml = @import("xml.zig"); const xml = @import("xml.zig");
const Peripheral = @import("Peripheral.zig");
const Register = @import("Register.zig");
const Field = @import("Field.zig");
const ArenaAllocator = std.heap.ArenaAllocator; const ArenaAllocator = std.heap.ArenaAllocator;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
// TODO: normalize descriptions, watch out for explicit '\n's tho, we want to replicate those newlines in generated text
pub const Device = struct { pub const Device = struct {
vendor: ?[]const u8, vendor: ?[]const u8 = null,
vendor_id: ?[]const u8, vendor_id: ?[]const u8 = null,
name: ?[]const u8, name: ?[]const u8 = null,
series: ?[]const u8, series: ?[]const u8 = null,
version: ?[]const u8, version: ?[]const u8 = null,
description: ?[]const u8, description: ?[]const u8 = null,
license_text: ?[]const u8, license_text: ?[]const u8 = null,
address_unit_bits: usize, address_unit_bits: usize,
width: usize, width: usize,
register_properties: struct { register_properties: struct {
size: ?usize, size: ?usize = null,
access: ?Access, access: ?Access = null,
protection: ?[]const u8, protection: ?[]const u8 = null,
reset_value: ?[]const u8, reset_value: ?[]const u8 = null,
reset_mask: ?[]const u8, reset_mask: ?[]const u8 = null,
}, },
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Device { pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Device {
@ -79,6 +80,9 @@ pub const CpuName = enum {
cortex_a53, cortex_a53,
cortex_a57, cortex_a57,
cortex_a72, cortex_a72,
// avr
avr,
other, other,
// TODO: finish // TODO: finish
@ -109,6 +113,8 @@ pub const CpuName = enum {
CpuName.cortex_m4 CpuName.cortex_m4
else if (std.mem.eql(u8, "CM7", str)) else if (std.mem.eql(u8, "CM7", str))
CpuName.cortex_m7 CpuName.cortex_m7
else if (std.mem.eql(u8, "AVR8", str))
CpuName.avr
else else
null; null;
} }
@ -186,13 +192,7 @@ pub const Access = enum {
} }
}; };
pub const Peripheral = struct { pub fn parsePeripheral(arena: *ArenaAllocator, nodes: *xml.Node) !Peripheral {
name: []const u8,
version: ?[]const u8,
description: ?[]const u8,
base_addr: usize,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Peripheral {
const allocator = arena.allocator(); const allocator = arena.allocator();
return Peripheral{ return Peripheral{
.name = try allocator.dupe(u8, xml.findValueForKey(nodes, "name") orelse return error.NoName), .name = try allocator.dupe(u8, xml.findValueForKey(nodes, "name") orelse return error.NoName),
@ -204,7 +204,6 @@ pub const Peripheral = struct {
.base_addr = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "baseAddress")) orelse return error.NoBaseAddr, // isDefault? .base_addr = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "baseAddress")) orelse return error.NoBaseAddr, // isDefault?
}; };
} }
};
pub const Interrupt = struct { pub const Interrupt = struct {
name: []const u8, name: []const u8,
@ -234,13 +233,7 @@ pub const Interrupt = struct {
} }
}; };
pub const Register = struct { pub fn parseRegister(arena: *ArenaAllocator, nodes: *xml.Node, device_width: usize) !Register {
name: []const u8,
description: ?[]const u8,
addr_offset: usize,
size: usize,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node, device_width: usize) !Register {
const allocator = arena.allocator(); const allocator = arena.allocator();
return Register{ return Register{
.name = try allocator.dupe(u8, xml.findValueForKey(nodes, "name") orelse return error.NoName), .name = try allocator.dupe(u8, xml.findValueForKey(nodes, "name") orelse return error.NoName),
@ -249,7 +242,6 @@ pub const Register = struct {
.size = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "size")) orelse device_width, .size = (try xml.parseIntForKey(usize, arena.child_allocator, nodes, "size")) orelse device_width,
}; };
} }
};
pub const Cluster = struct { pub const Cluster = struct {
name: []const u8, name: []const u8,
@ -271,13 +263,7 @@ const BitRange = struct {
width: u8, width: u8,
}; };
pub const Field = struct { pub fn parseField(arena: *ArenaAllocator, nodes: *xml.Node) !Field {
name: []const u8,
description: ?[]const u8,
offset: u8,
width: u8,
pub fn parse(arena: *ArenaAllocator, nodes: *xml.Node) !Field {
const allocator = arena.allocator(); const allocator = arena.allocator();
// TODO: // TODO:
const bit_range = blk: { const bit_range = blk: {
@ -334,14 +320,6 @@ pub const Field = struct {
}; };
} }
pub fn lessThan(_: void, lhs: Field, rhs: Field) bool {
return if (lhs.offset == rhs.offset)
lhs.width < rhs.width
else
lhs.offset < rhs.offset;
}
};
pub const EnumeratedValue = struct { pub const EnumeratedValue = struct {
name: []const u8, name: []const u8,
description: ?[]const u8, description: ?[]const u8,

Loading…
Cancel
Save