From 3d6cf928b38d80a83cbe40d1250301335e4cc101 Mon Sep 17 00:00:00 2001 From: Matt Knight Date: Wed, 11 Jan 2023 23:17:32 -0800 Subject: [PATCH] initial vector table support for arm (#80) When parsing ATDF or SVD files, seed the interrupts with known exception handlers for different architectures. --- tools/regz/src/Database.zig | 7 + tools/regz/src/arch/Interrupt.zig | 3 + .../InterruptWithIndexAndName.zig | 0 tools/regz/src/arch/arm.zig | 160 ++++++++++++++++++ tools/regz/src/{gen => arch}/avr.zig | 0 tools/regz/src/atdf.zig | 14 +- tools/regz/src/gen.zig | 4 +- tools/regz/src/gen/arm.zig | 29 ---- tools/regz/src/svd.zig | 23 ++- 9 files changed, 205 insertions(+), 35 deletions(-) create mode 100644 tools/regz/src/arch/Interrupt.zig rename tools/regz/src/{gen => arch}/InterruptWithIndexAndName.zig (100%) create mode 100644 tools/regz/src/arch/arm.zig rename tools/regz/src/{gen => arch}/avr.zig (100%) delete mode 100644 tools/regz/src/gen/arm.zig diff --git a/tools/regz/src/Database.zig b/tools/regz/src/Database.zig index 017a23d..7586d06 100644 --- a/tools/regz/src/Database.zig +++ b/tools/regz/src/Database.zig @@ -167,6 +167,13 @@ pub const Arch = enum { else => false, }; } + + pub fn isMips(arch: Arch) bool { + return switch (arch) { + .mips => true, + else => false, + }; + } }; // not sure how to communicate the *_once values in generated code diff --git a/tools/regz/src/arch/Interrupt.zig b/tools/regz/src/arch/Interrupt.zig new file mode 100644 index 0000000..4282720 --- /dev/null +++ b/tools/regz/src/arch/Interrupt.zig @@ -0,0 +1,3 @@ +name: []const u8, +index: i32, +description: ?[]const u8 = null, diff --git a/tools/regz/src/gen/InterruptWithIndexAndName.zig b/tools/regz/src/arch/InterruptWithIndexAndName.zig similarity index 100% rename from tools/regz/src/gen/InterruptWithIndexAndName.zig rename to tools/regz/src/arch/InterruptWithIndexAndName.zig diff --git a/tools/regz/src/arch/arm.zig b/tools/regz/src/arch/arm.zig new file mode 100644 index 0000000..f7046cc --- /dev/null +++ b/tools/regz/src/arch/arm.zig @@ -0,0 +1,160 @@ +//! codegen specific to arm +const std = @import("std"); +const assert = std.debug.assert; + +const Database = @import("../Database.zig"); +const Arch = Database.Arch; +const EntityId = Database.EntityId; + +const gen = @import("../gen.zig"); +const InterruptWithIndexAndName = @import("InterruptWithIndexAndName.zig"); +const Interrupt = @import("Interrupt.zig"); + +const log = std.log.scoped(.@"gen.arm"); + +// it's intended that these interrupts are added to the database in the +// different front-ends. This way this information is serialized for +// tooling +pub const system_interrupts = struct { + pub const cortex_m0 = [_]Interrupt{ + Interrupt{ .name = "NMI", .index = -14 }, + Interrupt{ .name = "HardFault", .index = -13 }, + Interrupt{ .name = "SVCall", .index = -5 }, + Interrupt{ .name = "PendSV", .index = -2 }, + }; + pub const cortex_m0plus = cortex_m0; + pub const cortex_m3 = [_]Interrupt{ + Interrupt{ .name = "MemManageFault", .index = -12 }, + Interrupt{ .name = "BusFault", .index = -11 }, + Interrupt{ .name = "UsageFault", .index = -10 }, + Interrupt{ .name = "DebugMonitor", .index = -4 }, + } ++ cortex_m0; + pub const cortex_m4 = cortex_m3; +}; + +pub fn loadSysTickInterrupt(db: *Database, device_id: EntityId) !void { + _ = try db.createInterrupt(device_id, .{ + .name = "SysTick", + .index = -1, + // TODO: description + }); +} + +pub fn loadSystemInterrupts(db: *Database, device_id: EntityId) !void { + const arch = db.instances.devices.get(device_id).?.arch; + assert(arch.isArm()); + + inline for (@typeInfo(Database.Arch).Enum.fields) |field| { + if (arch == @field(Database.Arch, field.name)) { + if (@hasDecl(system_interrupts, field.name)) { + for (@field(system_interrupts, field.name)) |interrupt| { + _ = try db.createInterrupt(device_id, .{ + .name = interrupt.name, + .index = interrupt.index, + .description = interrupt.description, + }); + } + } + + break; + } + } else { + log.warn("TODO: system interrupts handlers for {}", .{arch}); + } +} + +pub fn writeInterruptVector( + db: Database, + device_id: EntityId, + writer: anytype, +) !void { + assert(db.entityIs("instance.device", device_id)); + const arch = db.instances.devices.get(device_id).?.arch; + assert(arch.isArm()); + + switch (arch) { + // the basic vector table below should be fine for cortex-m + .cortex_m0, + .cortex_m0plus, + .cortex_m1, + .cortex_m23, + .cortex_m3, + .cortex_m33, + .cortex_m35p, + .cortex_m4, + .cortex_m55, + .cortex_m7, + => {}, + else => { + log.warn("TODO: exception handlers for {}", .{arch}); + return; + }, + } + + try writer.writeAll( + \\pub const VectorTable = extern struct { + \\ const Handler = micro.interrupt.Handler; + \\ const unhandled = micro.interrupt.unhandled; + \\ + \\ initial_stack_pointer: u32, + \\ Reset: Handler = unhandled, + \\ + ); + + if (db.children.interrupts.get(device_id)) |interrupt_set| { + var interrupts = std.ArrayList(InterruptWithIndexAndName).init(db.gpa); + defer interrupts.deinit(); + + var it = interrupt_set.iterator(); + while (it.next()) |entry| { + const interrupt_id = entry.key_ptr.*; + const index = db.instances.interrupts.get(interrupt_id).?; + const name = db.attrs.name.get(interrupt_id) orelse continue; + + try interrupts.append(.{ + .id = interrupt_id, + .name = name, + .index = index, + }); + } + + switch (arch) { + else => log.warn("TODO: exception handlers for {}", .{arch}), + } + + std.sort.sort( + InterruptWithIndexAndName, + interrupts.items, + {}, + InterruptWithIndexAndName.lessThan, + ); + + var index: i32 = -14; + var i: u32 = 0; + + while (i < interrupts.items.len) : (i += 1) { + const interrupt = interrupts.items[i]; + if (index < interrupt.index) { + try writer.print("reserved{}: [{}]u32 = undefined,\n", .{ + index + 14, + interrupt.index - index, + }); + index = interrupt.index; + } else if (index > interrupt.index) { + log.warn("skipping interrupt: {s}", .{interrupt.name}); + continue; + } + + if (db.attrs.description.get(interrupt.id)) |description| + try gen.writeComment(db.gpa, description, writer); + + try writer.print("{s}: Handler = unhandled,\n", .{ + std.zig.fmtId(interrupt.name), + }); + + index += 1; + } + } + + try writer.writeAll("};\n\n"); +} diff --git a/tools/regz/src/gen/avr.zig b/tools/regz/src/arch/avr.zig similarity index 100% rename from tools/regz/src/gen/avr.zig rename to tools/regz/src/arch/avr.zig diff --git a/tools/regz/src/atdf.zig b/tools/regz/src/atdf.zig index a2a4dc8..bb4d58d 100644 --- a/tools/regz/src/atdf.zig +++ b/tools/regz/src/atdf.zig @@ -7,6 +7,7 @@ const Database = @import("Database.zig"); const EntityId = Database.EntityId; const xml = @import("xml.zig"); +const arm = @import("arch/arm.zig"); const log = std.log.scoped(.atdf); @@ -58,17 +59,18 @@ fn loadDevice(ctx: *Context, node: xml.Node) !void { }); const name = node.getAttribute("name") orelse return error.NoDeviceName; - const arch = node.getAttribute("architecture") orelse return error.NoDeviceArch; + const arch_str = node.getAttribute("architecture") orelse return error.NoDeviceArch; + const arch = archFromStr(arch_str); const family = node.getAttribute("family") orelse return error.NoDeviceFamily; const db = ctx.db; const id = try db.createDevice(.{ .name = name, - .arch = archFromStr(arch), + .arch = arch, }); errdefer db.destroyEntity(id); - try db.addDeviceProperty(id, "arch", arch); + try db.addDeviceProperty(id, "arch", arch_str); try db.addDeviceProperty(id, "family", family); if (node.getAttribute("series")) |series| try db.addDeviceProperty(id, "series", series); @@ -85,6 +87,12 @@ fn loadDevice(ctx: *Context, node: xml.Node) !void { try inferPeripheralOffsets(ctx); try inferEnumSizes(ctx); + // system interrupts + if (arch.isArm()) + try arm.loadSystemInterrupts(db, id); + + // TODO: maybe others? + // TODO: // address-space.memory-segment // events.generators.generator diff --git a/tools/regz/src/gen.zig b/tools/regz/src/gen.zig index 981b55e..0772b72 100644 --- a/tools/regz/src/gen.zig +++ b/tools/regz/src/gen.zig @@ -7,8 +7,8 @@ const Database = @import("Database.zig"); const EntityId = Database.EntityId; const EntitySet = Database.EntitySet; -const arm = @import("gen/arm.zig"); -const avr = @import("gen/avr.zig"); +const arm = @import("arch/arm.zig"); +const avr = @import("arch/avr.zig"); const log = std.log.scoped(.gen); diff --git a/tools/regz/src/gen/arm.zig b/tools/regz/src/gen/arm.zig deleted file mode 100644 index c7611da..0000000 --- a/tools/regz/src/gen/arm.zig +++ /dev/null @@ -1,29 +0,0 @@ -//! codegen specific to arm -const std = @import("std"); -const assert = std.debug.assert; - -const Database = @import("../Database.zig"); -const Arch = Database.Arch; -const EntityId = Database.EntityId; - -const InterruptWithIndexAndName = @import("InterruptWithIndexAndName.zig"); - -const log = std.log.scoped(.@"gen.arm"); - -pub fn writeInterruptVector( - db: Database, - device_id: EntityId, - writer: anytype, -) !void { - assert(db.entityIs("instance.device", device_id)); - const arch = db.instances.devices.get(device_id).?.arch; - assert(arch.isArm()); - - log.warn("TODO: implement interrupt table for arch: {}", .{arch}); - - _ = writer; - //try writer.writeAll("pub const VectorTable = extern struct {\n"); - // TODO: fill - //try writer.writeAll("};\n"); - return error.Todo; -} diff --git a/tools/regz/src/svd.zig b/tools/regz/src/svd.zig index 664adcb..b951ed6 100644 --- a/tools/regz/src/svd.zig +++ b/tools/regz/src/svd.zig @@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator; const assert = std.debug.assert; const xml = @import("xml.zig"); +const arm = @import("arch/arm.zig"); const Database = @import("Database.zig"); const EntityId = Database.EntityId; @@ -43,6 +44,13 @@ const Context = struct { } }; +const svd_boolean = std.ComptimeStringMap(bool, .{ + .{ "true", true }, + .{ "1", true }, + .{ "false", false }, + .{ "0", false }, +}); + pub fn loadIntoDb(db: *Database, doc: xml.Doc) !void { const root = try doc.getRootElement(); @@ -79,7 +87,20 @@ pub fn loadIntoDb(db: *Database, doc: xml.Doc) !void { const nvic_prio_bits = cpu.getValue("nvicPrioBits") orelse return error.MissingNvicPrioBits; const vendor_systick_config = cpu.getValue("vendorSystickConfig") orelse return error.MissingVendorSystickConfig; - db.instances.devices.getEntry(device_id).?.value_ptr.arch = archFromStr(cpu_name); + const arch = archFromStr(cpu_name); + db.instances.devices.getEntry(device_id).?.value_ptr.arch = arch; + if (arch.isArm()) + try arm.loadSystemInterrupts(db, device_id); + + // TODO: is this the right logic? + if (svd_boolean.get(vendor_systick_config)) |systick| { + if (!systick) + try arm.loadSysTickInterrupt(db, device_id); + } else { + try arm.loadSysTickInterrupt(db, device_id); + } + + // TODO: // cpu name => arch try db.addDeviceProperty(device_id, "cpu.name", cpu_name);