initial vector table support for arm (#80)

When parsing ATDF or SVD files, seed the interrupts with known exception handlers for different architectures.
wch-ch32v003
Matt Knight 2 years ago committed by Matt Knight
parent eb24365786
commit 3d6cf928b3

@ -167,6 +167,13 @@ pub const Arch = enum {
else => false, 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 // not sure how to communicate the *_once values in generated code

@ -0,0 +1,3 @@
name: []const u8,
index: i32,
description: ?[]const u8 = null,

@ -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");
}

@ -7,6 +7,7 @@ const Database = @import("Database.zig");
const EntityId = Database.EntityId; const EntityId = Database.EntityId;
const xml = @import("xml.zig"); const xml = @import("xml.zig");
const arm = @import("arch/arm.zig");
const log = std.log.scoped(.atdf); 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 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 family = node.getAttribute("family") orelse return error.NoDeviceFamily;
const db = ctx.db; const db = ctx.db;
const id = try db.createDevice(.{ const id = try db.createDevice(.{
.name = name, .name = name,
.arch = archFromStr(arch), .arch = arch,
}); });
errdefer db.destroyEntity(id); errdefer db.destroyEntity(id);
try db.addDeviceProperty(id, "arch", arch); try db.addDeviceProperty(id, "arch", arch_str);
try db.addDeviceProperty(id, "family", family); try db.addDeviceProperty(id, "family", family);
if (node.getAttribute("series")) |series| if (node.getAttribute("series")) |series|
try db.addDeviceProperty(id, "series", series); try db.addDeviceProperty(id, "series", series);
@ -85,6 +87,12 @@ fn loadDevice(ctx: *Context, node: xml.Node) !void {
try inferPeripheralOffsets(ctx); try inferPeripheralOffsets(ctx);
try inferEnumSizes(ctx); try inferEnumSizes(ctx);
// system interrupts
if (arch.isArm())
try arm.loadSystemInterrupts(db, id);
// TODO: maybe others?
// TODO: // TODO:
// address-space.memory-segment // address-space.memory-segment
// events.generators.generator // events.generators.generator

@ -7,8 +7,8 @@ const Database = @import("Database.zig");
const EntityId = Database.EntityId; const EntityId = Database.EntityId;
const EntitySet = Database.EntitySet; const EntitySet = Database.EntitySet;
const arm = @import("gen/arm.zig"); const arm = @import("arch/arm.zig");
const avr = @import("gen/avr.zig"); const avr = @import("arch/avr.zig");
const log = std.log.scoped(.gen); const log = std.log.scoped(.gen);

@ -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;
}

@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator;
const assert = std.debug.assert; const assert = std.debug.assert;
const xml = @import("xml.zig"); const xml = @import("xml.zig");
const arm = @import("arch/arm.zig");
const Database = @import("Database.zig"); const Database = @import("Database.zig");
const EntityId = Database.EntityId; 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 { pub fn loadIntoDb(db: *Database, doc: xml.Doc) !void {
const root = try doc.getRootElement(); 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 nvic_prio_bits = cpu.getValue("nvicPrioBits") orelse return error.MissingNvicPrioBits;
const vendor_systick_config = cpu.getValue("vendorSystickConfig") orelse return error.MissingVendorSystickConfig; 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 // cpu name => arch
try db.addDeviceProperty(device_id, "cpu.name", cpu_name); try db.addDeviceProperty(device_id, "cpu.name", cpu_name);

Loading…
Cancel
Save