diff --git a/port/microchip/atmega/src/hals/atmega328p/interrupts.zig b/port/microchip/atmega/src/hals/atmega328p/interrupts.zig
index b43806373..a78d74c32 100644
--- a/port/microchip/atmega/src/hals/atmega328p/interrupts.zig
+++ b/port/microchip/atmega/src/hals/atmega328p/interrupts.zig
@@ -2,10 +2,7 @@
const microzig = @import("microzig");
-// The generated `peripherals.CPU` is a pointer to address 0, so SREG gets its
-// own pointer at its offset (see `spi.zig`).
-const CPU_Regs = microzig.chip.types.peripherals.CPU;
-const SREG: *volatile u8 = @ptrFromInt(@offsetOf(CPU_Regs, "SREG"));
+const SREG = µzig.chip.peripherals.CPU.SREG;
/// Interrupt state saved by `enter_critical_section`.
pub const CriticalSection = struct {
@@ -14,14 +11,14 @@ pub const CriticalSection = struct {
/// Restores the interrupt state from before `enter_critical_section`.
pub inline fn leave(section: CriticalSection) void {
asm volatile ("" ::: .{ .memory = true });
- SREG.* = section.sreg;
+ SREG.write_raw(section.sreg);
}
};
/// Disables interrupts until `leave` is called on the result. Memory accesses
/// aren't moved out of the section.
pub inline fn enter_critical_section() CriticalSection {
- const section: CriticalSection = .{ .sreg = SREG.* };
+ const section: CriticalSection = .{ .sreg = SREG.raw };
asm volatile ("cli" ::: .{ .memory = true });
return section;
}
diff --git a/port/microchip/atmega/src/hals/atmega328p/spi.zig b/port/microchip/atmega/src/hals/atmega328p/spi.zig
index 48e4cc6c9..2e649eb91 100644
--- a/port/microchip/atmega/src/hals/atmega328p/spi.zig
+++ b/port/microchip/atmega/src/hals/atmega328p/spi.zig
@@ -9,13 +9,7 @@ const std = @import("std");
const microzig = @import("microzig");
const gpio = microzig.hal.gpio;
-// The generated `peripherals.SPI` is a pointer to address 0 (not `allowzero`)
-// with absolute register offsets, so it can't be used. Each register gets its
-// own pointer instead, at its offset (which is its data-space address).
-const SPI_Regs = microzig.chip.types.peripherals.SPI;
-const SPCR: *volatile @FieldType(SPI_Regs, "SPCR") = @ptrFromInt(@offsetOf(SPI_Regs, "SPCR"));
-const SPSR: *volatile @FieldType(SPI_Regs, "SPSR") = @ptrFromInt(@offsetOf(SPI_Regs, "SPSR"));
-const SPDR: *volatile u8 = @ptrFromInt(@offsetOf(SPI_Regs, "SPDR"));
+const SPI0 = microzig.chip.peripherals.SPI;
const ss = gpio.pin(.b, 2);
const mosi = gpio.pin(.b, 3);
@@ -64,8 +58,8 @@ pub const SPI = enum {
mosi.set_direction(.output);
sck.set_direction(.output);
- SPSR.write_raw(@intFromBool(clock.double_speed)); // SPI2X
- SPCR.write(.{
+ SPI0.SPSR.write_raw(@intFromBool(clock.double_speed)); // SPI2X
+ SPI0.SPCR.write(.{
.SPR = @fromBackingInt(@intCast(clock.spr)),
.CPHA = @backingInt(config.phase),
.CPOL = @backingInt(config.polarity),
@@ -169,7 +163,7 @@ test compute_clock {
/// Sends `byte` and returns the byte received at the same time.
fn transfer_byte(byte: u8) u8 {
- SPDR.* = byte;
- while (SPSR.read().SPIF == 0) {}
- return SPDR.*;
+ SPI0.SPDR = byte;
+ while (SPI0.SPSR.read().SPIF == 0) {}
+ return SPI0.SPDR;
}
diff --git a/tools/regz/src/atdf.zig b/tools/regz/src/atdf.zig
index a4379da5a..f8aa65e6d 100644
--- a/tools/regz/src/atdf.zig
+++ b/tools/regz/src/atdf.zig
@@ -52,7 +52,7 @@ pub fn load_into_db(db: *Database, doc: xml.Doc) !void {
const root = try doc.get_root_element();
var module_it = root.iterate(&.{"modules"}, &.{"module"});
while (module_it.next()) |entry|
- try load_module_type(&ctx, entry);
+ try load_module_type(&ctx, root, entry);
var device_it = root.iterate(&.{"devices"}, &.{"device"});
while (device_it.next()) |entry|
@@ -283,7 +283,7 @@ fn get_inlined_register_group(parent_node: xml.Node, parent_name: []const u8) ?x
}
// module instances are listed under atdf-tools-device-file.modules.
-fn load_module_type(ctx: *Context, node: xml.Node) !void {
+fn load_module_type(ctx: *Context, root: xml.Node, node: xml.Node) !void {
validate_attrs(node, &.{
"oldname",
"name",
@@ -322,6 +322,8 @@ fn load_module_type(ctx: *Context, node: xml.Node) !void {
// registers. This operation needs to be done in
// `loadModuleInstance()` as well
if (get_inlined_register_group(node, name)) |register_group_node| {
+ if (instances_at_offset_zero(root, name))
+ try infer_register_group_offset(ctx, register_group_node, struct_id);
try load_register_group_children(ctx, register_group_node, peripheral, struct_id);
} else {
var register_group_it = node.iterate(&.{}, &.{"register-group"});
@@ -330,6 +332,30 @@ fn load_module_type(ctx: *Context, node: xml.Node) !void {
}
}
+/// True if every instance of the module is at offset 0, so the lowest
+/// register offset can be the instance base (SPI on the ATmega328P).
+fn instances_at_offset_zero(root: xml.Node, module_name: []const u8) bool {
+ var device_it = root.iterate(&.{"devices"}, &.{"device"});
+ while (device_it.next()) |device_node| {
+ var module_it = device_node.iterate(&.{"peripherals"}, &.{"module"});
+ while (module_it.next()) |module_node| {
+ const name = module_node.get_attribute("name") orelse continue;
+ if (!std.mem.eql(u8, name, module_name)) continue;
+
+ var instance_it = module_node.iterate(&.{}, &.{"instance"});
+ while (instance_it.next()) |instance_node| {
+ var register_group_it = instance_node.iterate(&.{}, &.{"register-group"});
+ while (register_group_it.next()) |register_group_node| {
+ const offset = register_group_node.get_attribute_int(u64, "offset") catch return false;
+ if (offset != 0) return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
fn load_module_interrupt_group(ctx: *Context, node: xml.Node) !void {
const name = node.get_attribute("name") orelse return error.MissingInterruptGroupName;
try ctx.interrupt_groups.put(ctx.db.gpa, name, .empty);
@@ -822,10 +848,14 @@ fn load_module_instance_from_peripheral(
const name = node.get_attribute("name") orelse return error.MissingInstanceName;
const desc = node.get_attribute("caption");
+ const struct_id = try db.get_peripheral_struct(peripheral_id);
const offset_bytes: u64 = if (get_inlined_register_group(node, name)) |register_group_node| blk: {
log.debug("inlining {s}", .{name});
const offset = try register_group_node.get_attribute_int(u64, "offset") orelse return error.MissingPeripheralOffset;
- break :blk offset;
+ break :blk if (offset == 0)
+ ctx.inferred_register_group_offsets.get(struct_id) orelse offset
+ else
+ offset;
} else {
return error.Todo;
//unreachable;
@@ -836,7 +866,6 @@ fn load_module_instance_from_peripheral(
// };
};
- const struct_id = try db.get_peripheral_struct(peripheral_id);
const instance_id = try db.create_device_peripheral(device_id, .{
.struct_id = struct_id,
.name = name,
@@ -1310,6 +1339,100 @@ test "atdf.instance of register group" {
try expectEqual(@as(u64, 0x0), pinb.offset_bytes);
}
+test "atdf.inlined register group with absolute offsets" {
+ const text =
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ ;
+
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
+ defer arena.deinit();
+
+ const doc = try xml.Doc.from_memory(text);
+ var db = try Database.create_from_doc(std.testing.allocator, .atdf, doc);
+ defer db.destroy();
+
+ const device_id = try db.get_device_id_by_name("ATmega328P") orelse return error.NoDevice;
+
+ const spi_instance = try db.get_device_peripheral_by_name(arena.allocator(), device_id, "SPI");
+ try expectEqual(@as(u64, 0x4C), spi_instance.offset_bytes);
+
+ const spcr = try db.get_register_by_name(arena.allocator(), spi_instance.struct_id, "SPCR");
+ try expectEqual(@as(u64, 0x0), spcr.offset_bytes);
+
+ const spdr = try db.get_register_by_name(arena.allocator(), spi_instance.struct_id, "SPDR");
+ try expectEqual(@as(u64, 0x2), spdr.offset_bytes);
+}
+
+test "atdf.inlined register group with relative offsets" {
+ const text =
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ ;
+
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
+ defer arena.deinit();
+
+ const doc = try xml.Doc.from_memory(text);
+ var db = try Database.create_from_doc(std.testing.allocator, .atdf, doc);
+ defer db.destroy();
+
+ const device_id = try db.get_device_id_by_name("ATtiny1616") orelse return error.NoDevice;
+
+ const syscfg_instance = try db.get_device_peripheral_by_name(arena.allocator(), device_id, "SYSCFG");
+ try expectEqual(@as(u64, 0xF00), syscfg_instance.offset_bytes);
+
+ const revid = try db.get_register_by_name(arena.allocator(), syscfg_instance.struct_id, "REVID");
+ try expectEqual(@as(u64, 0x1), revid.offset_bytes);
+}
+
test "atdf.interrupts" {
const text =
\\