Skip to content

Commit

Permalink
Regz patching (#290)
Browse files Browse the repository at this point in the history
* Adding and setting enums
* Patch USBCTRL_DPRAM as an example
* add patching to `add_firmware()`
* Set default log level for regz to error
  • Loading branch information
mattnite authored Nov 22, 2024
1 parent 6ec3c8d commit eb8e5c7
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 27 deletions.
5 changes: 5 additions & 0 deletions build-internals/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ const Build = std.Build;
const LazyPath = Build.LazyPath;
const Module = Build.Module;

const regz = @import("regz");
const Patch = regz.patch.Patch;

pub fn build(b: *Build) void {
_ = b.addModule("build-internals", .{
.root_source_file = b.path("build.zig"),
Expand Down Expand Up @@ -106,6 +109,8 @@ pub const Chip = struct {

/// The memory regions that are present in this chip.
memory_regions: []const MemoryRegion,

patches: []const Patch = &.{},
};

/// Defines a hardware abstraction layer.
Expand Down
3 changes: 3 additions & 0 deletions build-internals/build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
.{
.name = "build-internals",
.version = "0.0.0",
.dependencies = .{
.regz = .{ .path = "../tools/regz" },
},
.paths = .{
"LICENSE",
"build.zig",
Expand Down
37 changes: 36 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ const std = @import("std");
const Build = std.Build;
const LazyPath = Build.LazyPath;

const internals = @import("build-internals");
const internals = @import("microzig/build-internals");
pub const Target = internals.Target;
pub const Chip = internals.Chip;
pub const HardwareAbstractionLayer = internals.HardwareAbstractionLayer;
pub const Board = internals.Board;
pub const BinaryFormat = internals.BinaryFormat;
pub const MemoryRegion = internals.MemoryRegion;

const regz = @import("microzig/tools/regz");

const port_list: []const struct {
name: [:0]const u8,
dep_name: [:0]const u8,
Expand Down Expand Up @@ -229,8 +231,23 @@ pub fn MicroBuild(port_select: PortSelect) type {
/// exe.link_data_sections = true;
/// exe.link_function_sections = true;
strip_unused_symbols: bool = true,

/// Additional patches the user may apply to the generated register
/// code. This does not override the chip's existing patches.
patches: []const regz.Patch = .{},
};

fn serialize_patches(b: *Build, patches: []const regz.patch.Patch) []const u8 {
var buf = std.ArrayList(u8).init(b.allocator);

for (patches) |patch| {
std.json.stringify(patch, .{}, buf.writer()) catch @panic("OOM");
buf.writer().writeByte('\n') catch @panic("OOM");
}

return buf.toOwnedSlice() catch @panic("OOM");
}

/// Creates a new firmware for a given target.
pub fn add_firmware(mb: *Self, options: CreateFirmwareOptions) *Firmware {
const b = mb.dep.builder;
Expand Down Expand Up @@ -285,6 +302,24 @@ pub fn MicroBuild(port_select: PortSelect) type {
regz_run.addArg("--output_path"); // Write to a file
const zig_file = regz_run.addOutputFileArg("chip.zig");

var patches = std.ArrayList(regz.Patch).init(b.allocator);

// From chip definition
patches.appendSlice(target.chip.patches) catch @panic("OOM");

// From user invoking `add_firmware`
patches.appendSlice(options.patches) catch @panic("OOM");

if (patches.len > 0) {
// write patches to file
const patch_ndjson = serialize_patches(b, patches);
const write_file_step = b.addWriteFiles();
const patch_file = write_file_step.add("patch.ndjson", patch_ndjson);

regz_run.addArg("--patch_path");
regz_run.addFileArg(patch_file);
}

regz_run.addFileArg(file);

break :blk zig_file;
Expand Down
1 change: 1 addition & 0 deletions port/raspberrypi/rp2xxx/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub fn init(dep: *std.Build.Dependency) Self {
.{ .kind = .flash, .offset = 0x10000000, .length = 256 },
.{ .kind = .ram, .offset = 0x20000000, .length = 256 * 1024 },
},
.patches = @import("patches/rp2040.zig").patches,
},
.hal = hal,
.linker_script = b.path("rp2040.ld"),
Expand Down
1 change: 1 addition & 0 deletions port/raspberrypi/rp2xxx/build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"build.zig.zon",
"build.zig",
"src",
"patches",
"tools",
"rp2040.ld",
"rp2350.ld",
Expand Down
47 changes: 47 additions & 0 deletions port/raspberrypi/rp2xxx/patches/rp2040.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pub const patches = &.{
.{
.add_enum = .{
.parent = "types.peripherals.USBCTRL_DPRAM",
.@"enum" = .{
.name = "EndpointType",
.bitsize = 2,
.fields = &.{
.{ .value = 0x0, .name = "control" },
.{ .value = 0x1, .name = "isochronous" },
.{ .value = 0x2, .name = "bulk" },
.{ .value = 0x3, .name = "interrupt" },
},
},
},
},
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP1_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP1_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP2_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP2_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP3_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP3_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP4_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP4_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP5_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP5_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP6_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP6_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP7_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP7_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP8_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP8_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP9_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP9_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP10_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP10_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP11_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP11_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP12_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP12_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP13_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP13_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP14_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP14_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP15_IN_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
.{ .set_enum_type = .{ .of = "types.peripherals.USBCTRL_DPRAM.EP15_OUT_CONTROL.ENDPOINT_TYPE", .to = "types.peripherals.USBCTRL_DPRAM.EndpointType" } },
};
2 changes: 2 additions & 0 deletions tools/regz/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const Compile = Build.Compile;
const Step = Build.Step;
const GeneratedFile = Build.GeneratedFile;

pub const patch = @import("src/patch.zig");

pub fn build(b: *Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
Expand Down
147 changes: 139 additions & 8 deletions tools/regz/src/Database.zig
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ const atdf = @import("atdf.zig");
const dslite = @import("dslite.zig");
const gen = @import("gen.zig");
const regzon = @import("regzon.zig");
const patch = @import("patch.zig");
const Patch = patch.Patch;

const TypeOfField = @import("testing.zig").TypeOfField;

Expand Down Expand Up @@ -267,53 +269,52 @@ pub fn init(allocator: std.mem.Allocator) !Database {
};
}

// TODO: figure out how to do completions: bash, zsh, fish, powershell, cmd
fn init_from_atdf_xml(allocator: Allocator, doc: xml.Doc) !Database {
pub fn init_from_atdf_xml(allocator: Allocator, doc: xml.Doc) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();

try atdf.load_into_db(&db, doc);
return db;
}

fn init_from_atdf(allocator: Allocator, text: []const u8) !Database {
pub fn init_from_atdf(allocator: Allocator, text: []const u8) !Database {
var doc = try xml.Doc.from_memory(text);
defer doc.deinit();

return init_from_atdf_xml(allocator, doc);
}

fn init_from_svd_xml(allocator: Allocator, doc: xml.Doc) !Database {
pub fn init_from_svd_xml(allocator: Allocator, doc: xml.Doc) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();

try svd.load_into_db(&db, doc);
return db;
}

fn init_from_svd(allocator: Allocator, text: []const u8) !Database {
pub fn init_from_svd(allocator: Allocator, text: []const u8) !Database {
var doc = try xml.Doc.from_memory(text);
defer doc.deinit();

return init_from_svd_xml(allocator, doc);
}

fn init_from_dslite_xml(allocator: Allocator, doc: xml.Doc) !Database {
pub fn init_from_dslite_xml(allocator: Allocator, doc: xml.Doc) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();

try dslite.load_into_db(&db, doc);
return db;
}

fn init_from_dslite(allocator: Allocator, text: []const u8) !Database {
pub fn init_from_dslite(allocator: Allocator, text: []const u8) !Database {
var doc = try xml.Doc.from_memory(text);
defer doc.deinit();

return init_from_dslite_xml(allocator, doc);
}

fn init_from_json(allocator: Allocator, text: []const u8) !Database {
pub fn init_from_json(allocator: Allocator, text: []const u8) !Database {
var db = try Database.init(allocator);
errdefer db.deinit();

Expand Down Expand Up @@ -989,6 +990,136 @@ pub fn to_zig(db: Database, out_writer: anytype) !void {
try gen.to_zig(db, out_writer);
}

pub fn apply_patch(db: *Database, ndjson: []const u8) !void {
var list = std.ArrayList(std.json.Parsed(Patch)).init(db.gpa);
defer {
for (list.items) |*entry| entry.deinit();
list.deinit();
}

var line_it = std.mem.tokenizeScalar(u8, ndjson, '\n');
while (line_it.next()) |line| {
const p = try Patch.from_json_str(db.gpa, line);
errdefer p.deinit();
try list.append(p);
}

// TODO: handle patches
// TODO: keep track of changes so they can be done, maybe Patch => Change,
// and also have a ChangeResult so you could undo deletion of a thing

for (list.items) |entry| {
switch (entry.value) {
.add_enum => |added_enum| {
const parent_id = try db.get_entity_id_by_ref(added_enum.parent);
// TODO: parent constraints
const enum_id = try db.create_enum(parent_id, .{
.name = added_enum.@"enum".name,
.description = added_enum.@"enum".description,
.size = added_enum.@"enum".bitsize,
});

for (added_enum.@"enum".fields) |field| {
_ = try db.create_enum_field(enum_id, .{
.name = field.name,
.description = field.description,
.value = field.value,
});
}
},
.set_enum_type => |set_enum_type| {
const of_id = try db.get_entity_id_by_ref(set_enum_type.of);
const to_id = try db.get_entity_id_by_ref(set_enum_type.to);
// TODO: type checks
// TODO: size checks
// of_id should be a field, it's parent is a register
// to_id is a type, an enum

const old_enum = db.attrs.@"enum".get(of_id);
if (old_enum) |old_id| {
const old_size = db.attrs.size.get(old_id).?;
const new_size = db.attrs.size.get(to_id).?;

if (old_size != new_size)
return error.SizeMismatch;
}

try db.attrs.@"enum".put(db.gpa, of_id, to_id);

if (old_enum) |old_id| {
// TODO: make a general "is referenced" function
const is_used = for (db.attrs.@"enum".values()) |used_id| {
if (used_id == old_id)
break true;
} else false;

if (is_used)
db.destroy_entity(old_id);
}
},
}
}
}

fn get_keys_from_group_and_table(db: *const Database, comptime group: []const u8, table: []const u8) ![]const EntityId {
return inline for (@typeInfo(@TypeOf(@field(db, group))).Struct.fields) |field| {
if (std.mem.eql(u8, field.name, table)) {
break @field(@field(db, group), field.name).keys();
}
} else error.InvalidRef;
}

fn get_all_children(db: *const Database, allocator: Allocator, entity_id: EntityId) ![]const EntityId {
var ids = std.AutoArrayHashMap(EntityId, void).init(allocator);
defer ids.deinit();

inline for (@typeInfo(@TypeOf(db.children)).Struct.fields) |field| {
if (@field(db.children, field.name).get(entity_id)) |keyset| {
for (keyset.keys()) |id|
try ids.putNoClobber(id, {});
}
}

return try allocator.dupe(EntityId, ids.keys());
}

pub fn get_entity_id_by_ref(db: *const Database, ref: []const u8) !EntityId {
var tok_it = std.mem.tokenizeScalar(u8, ref, '.');

const group = tok_it.next() orelse return error.InvalidRef;
const table = tok_it.next() orelse return error.InvalidRef;

const keys: []const EntityId = if (std.mem.eql(u8, "types", group))
try db.get_keys_from_group_and_table("types", table)
else if (std.mem.eql(u8, "instances", group))
try db.get_keys_from_group_and_table("instances", table)
else
return error.InvalidRef;

const base_name = tok_it.next() orelse return error.InvalidRef;
const base_id: EntityId = for (keys) |id| {
const name = db.attrs.name.get(id) orelse continue;
if (std.mem.eql(u8, base_name, name)) {
break id;
}
} else return error.InvalidRef;

var current_id = base_id;
while (tok_it.next()) |name| {
const children_ids = try db.get_all_children(db.gpa, current_id);
defer db.gpa.free(children_ids);

current_id = for (children_ids) |child_id| {
const child_name = db.attrs.name.get(child_id) orelse continue;
if (std.mem.eql(u8, name, child_name)) {
break child_id;
}
} else return error.InvlidRef;
}

return current_id;
}

test "all" {
@setEvalBranchQuota(2000);
std.testing.refAllDeclsRecursive(atdf);
Expand Down
Loading

0 comments on commit eb8e5c7

Please sign in to comment.