From 6f3f8b162048ca27a00d2749ee9eda0f48959913 Mon Sep 17 00:00:00 2001 From: Mathieu Suen Date: Thu, 12 Feb 2026 10:16:44 +0100 Subject: [PATCH 1/2] Regz: Fix embassy stride > 4 --- .../stmicro/stm32/src/hals/STM32F103/exti.zig | 28 ++-- port/stmicro/stm32/src/hals/STM32L47X/lcd.zig | 4 +- tools/regz/src/embassy.zig | 128 ++++++++++-------- 3 files changed, 92 insertions(+), 68 deletions(-) diff --git a/port/stmicro/stm32/src/hals/STM32F103/exti.zig b/port/stmicro/stm32/src/hals/STM32F103/exti.zig index 062eca9ad..4d5980615 100644 --- a/port/stmicro/stm32/src/hals/STM32F103/exti.zig +++ b/port/stmicro/stm32/src/hals/STM32F103/exti.zig @@ -69,37 +69,37 @@ pub fn set_line(line: u4, port: gpio.Port) void { pub fn set_line_edge(line: u5, edge: TriggerEdge) void { switch (edge) { .None => { - EXTI.RTSR.raw &= ~(@as(u32, 1) << line); - EXTI.FTSR.raw &= ~(@as(u32, 1) << line); + EXTI.@"RTSR[0]".raw &= ~(@as(u32, 1) << line); + EXTI.@"FTSR[0]".raw &= ~(@as(u32, 1) << line); }, .rising => { - EXTI.RTSR.raw |= (@as(u32, 1) << line); - EXTI.FTSR.raw &= ~(@as(u32, 1) << line); + EXTI.@"RTSR[0]".raw |= (@as(u32, 1) << line); + EXTI.@"FTSR[0]".raw &= ~(@as(u32, 1) << line); }, .falling => { - EXTI.RTSR.raw &= ~(@as(u32, 1) << line); - EXTI.FTSR.raw |= (@as(u32, 1) << line); + EXTI.@"RTSR[0]".raw &= ~(@as(u32, 1) << line); + EXTI.@"FTSR[0]".raw |= (@as(u32, 1) << line); }, .both => { - EXTI.RTSR.raw |= (@as(u32, 1) << line); - EXTI.FTSR.raw |= (@as(u32, 1) << line); + EXTI.@"RTSR[0]".raw |= (@as(u32, 1) << line); + EXTI.@"FTSR[0]".raw |= (@as(u32, 1) << line); }, } } pub fn set_event(line: u5, enable: bool) void { if (enable) { - EXTI.EMR.raw |= (@as(u32, 1) << line); + EXTI.@"EMR[0]".raw |= (@as(u32, 1) << line); } else { - EXTI.EMR.raw &= ~(@as(u32, 1) << line); + EXTI.@"EMR[0]".raw &= ~(@as(u32, 1) << line); } } pub fn set_interrupt(line: u5, enable: bool) void { if (enable) { - EXTI.IMR.raw |= (@as(u32, 1) << line); + EXTI.@"IMR[0]".raw |= (@as(u32, 1) << line); } else { - EXTI.IMR.raw &= ~(@as(u32, 1) << line); + EXTI.@"IMR[0]".raw &= ~(@as(u32, 1) << line); } } @@ -111,9 +111,9 @@ pub inline fn software_trigger(line: u5) void { ///check for pending lines (for both events and interrupts) pub inline fn pending() pendingLine { - return @bitCast(@as(u20, @intCast(EXTI.PR.raw))); + return @bitCast(@as(u20, @intCast(EXTI.@"PR[0]".raw))); } ///clears all pending lines returned by: `pending()`. pub inline fn clear_pending(pendings: pendingLine) void { - EXTI.PR.raw = @as(u20, @bitCast(pendings)); + EXTI.@"PR[0]".raw = @as(u20, @bitCast(pendings)); } diff --git a/port/stmicro/stm32/src/hals/STM32L47X/lcd.zig b/port/stmicro/stm32/src/hals/STM32L47X/lcd.zig index eef142bbc..4341332fd 100644 --- a/port/stmicro/stm32/src/hals/STM32L47X/lcd.zig +++ b/port/stmicro/stm32/src/hals/STM32L47X/lcd.zig @@ -22,7 +22,9 @@ const LCD_RAM = extern struct { COM3H: LCD_COM_H, }; -const ram: *volatile LCD_RAM = @ptrCast(&LCD.RAM_COM); +// LCD RAM is not correctly defined in the stm32-data from embassy +// The high segments are inside a reserved space. +const ram: *volatile LCD_RAM = @ptrCast(&LCD.@"RAM_COM[0]"); pub fn init_lcd() void { LCD.CR.modify(.{ diff --git a/tools/regz/src/embassy.zig b/tools/regz/src/embassy.zig index 7b8234816..9d2866415 100644 --- a/tools/regz/src/embassy.zig +++ b/tools/regz/src/embassy.zig @@ -306,32 +306,52 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void }); for (obj.object.get("items").?.array.items) |item| { + var register_ids: std.ArrayList(Database.RegisterID) = .empty; + defer register_ids.deinit(allocator); const register_name = item.object.get("name").?.string; const description: ?[]const u8 = if (item.object.get("description")) |desc| desc.string else null; - const byte_offset = item.object.get("byte_offset").?.integer; + const byte_offset: u64 = @intCast(item.object.get("byte_offset").?.integer); const item_bit_size = if (item.object.get("bit_size")) |v| v.integer else 32; - - const register_id = try db.create_register(group_id, .{ - .name = register_name, - .description = description, - .offset_bytes = @intCast(byte_offset), - .size_bits = @intCast(item_bit_size), - .count = if (item.object.get("array")) |array| blk: { - if (array.object.get("len")) |count| { - // ensure stride is always 4 for now, assuming that - // it's in bytes - const stride = array.object.get("stride").?.integer; - if (stride != 4) { - std.log.warn("ignoring register array with unsupported stride: {} != 4 for register {s} in {s} in {s}", .{ stride, register_name, key["block/".len..], name }); - break :blk null; + const maybe_count: ?u64, const maybe_stride: ?u64 = if (item.object.get("array")) |array| blk: { + if (array.object.get("len")) |count| { + if (array.object.get("stride")) |stride| { + if (stride.integer > 4) { + break :blk .{ @intCast(count.integer), @intCast(stride.integer) }; } - - break :blk @intCast(count.integer); } + break :blk .{ @intCast(count.integer), null }; + } - break :blk null; - } else null, - }); + break :blk .{ null, null }; + } else .{ null, null }; + + if (maybe_stride) |stride| { + if (maybe_count) |count| { + for (0..count) |id| { + const register_part_name = try std.fmt.allocPrint(allocator, "{s}[{d}]", .{ + register_name, + id, + }); + const register_id = try db.create_register(group_id, .{ + .name = register_part_name, + .description = description, + .offset_bytes = byte_offset + (id * stride), + .size_bits = @intCast(item_bit_size), + .count = null, + }); + try register_ids.append(allocator, register_id); + } + } + } else { + const register_id = try db.create_register(group_id, .{ + .name = register_name, + .description = description, + .offset_bytes = @intCast(byte_offset), + .size_bits = @intCast(item_bit_size), + .count = maybe_count, + }); + try register_ids.append(allocator, register_id); + } if (item.object.get("fieldset")) |fieldset| blk: { const fieldset_key = try std.fmt.allocPrint(allocator, "fieldset/{s}", .{fieldset.string}); @@ -356,7 +376,7 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void // these are evenly spaced and much nicer to work with. array_count = if (object_map.get("len")) |len| @intCast(len.integer) else null; - array_stride = if (object_map.get("stride")) |stride| @intCast(stride.integer) else null; + array_stride = if (object_map.get("stride")) |field_stride| @intCast(field_stride.integer) else null; // This category where there is an array of items, but it is given by // individual offsets as opposed to a count + stride. This is used when strides are @@ -365,30 +385,32 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void if (object_map.get("offsets")) |positions| { for (positions.array.items, 0..) |position, idx| { const field_name_irregular_stride = try std.fmt.allocPrint(allocator, "{s}[{}]", .{ field_name, idx }); - - try db.add_register_field(register_id, .{ - .name = field_name_irregular_stride, - .description = field_description, - .offset_bits = @intCast(position.integer + bit_offset), - .size_bits = @intCast(bit_size), - .enum_id = enum_id, - .count = null, - .stride = null, - }); + for (register_ids.items) |register_id| { + try db.add_register_field(register_id, .{ + .name = field_name_irregular_stride, + .description = field_description, + .offset_bits = @intCast(position.integer + bit_offset), + .size_bits = @intCast(bit_size), + .enum_id = enum_id, + .count = null, + .stride = null, + }); + } } continue :next_field; } } - - try db.add_register_field(register_id, .{ - .name = field_name, - .description = field_description, - .offset_bits = @intCast(bit_offset), - .size_bits = @intCast(bit_size), - .enum_id = enum_id, - .count = array_count, - .stride = array_stride, - }); + for (register_ids.items) |register_id| { + try db.add_register_field(register_id, .{ + .name = field_name, + .description = field_description, + .offset_bits = @intCast(bit_offset), + .size_bits = @intCast(bit_size), + .enum_id = enum_id, + .count = array_count, + .stride = array_stride, + }); + } }, .array => |arr| { // This case is for discontinuous fields where the first few bits are @@ -411,18 +433,19 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void var array_stride: ?u8 = null; if (field.object.get("array")) |array| { array_count = if (array.object.get("len")) |len| @intCast(len.integer) else null; - array_stride = if (array.object.get("stride")) |stride| @intCast(stride.integer) else null; + array_stride = if (array.object.get("stride")) |field_stride| @intCast(field_stride.integer) else null; + } + for (register_ids.items) |register_id| { + try db.add_register_field(register_id, .{ + .name = non_contiguous_field_name, + .description = field_description, + .offset_bits = @intCast(bit_offset), + .size_bits = @intCast(bit_size), + .enum_id = enum_id, + .count = array_count, + .stride = array_stride, + }); } - - try db.add_register_field(register_id, .{ - .name = non_contiguous_field_name, - .description = field_description, - .offset_bits = @intCast(bit_offset), - .size_bits = @intCast(bit_size), - .enum_id = enum_id, - .count = array_count, - .stride = array_stride, - }); } }, else => |val| { @@ -603,4 +626,3 @@ const std = @import("std"); const Database = @import("Database.zig"); const Arch = @import("arch.zig").Arch; const arm = @import("arch/arm.zig"); -const FS_Directory = @import("FS_Directory.zig"); From a58d0fbf0b9e413769ea39f843924f7e252fd311 Mon Sep 17 00:00:00 2001 From: Mathieu Suen Date: Fri, 13 Feb 2026 18:41:12 +0100 Subject: [PATCH 2/2] Regz: Embassy fix, register cluster now reference the type --- port/stmicro/stm32/src/hals/STM32L47X/lcd.zig | 4 +- tools/regz/src/Database.zig | 21 +++--- tools/regz/src/embassy.zig | 70 +++++++++++++++++++ tools/regz/src/gen.zig | 14 ++++ 4 files changed, 99 insertions(+), 10 deletions(-) diff --git a/port/stmicro/stm32/src/hals/STM32L47X/lcd.zig b/port/stmicro/stm32/src/hals/STM32L47X/lcd.zig index 4341332fd..1e146af8a 100644 --- a/port/stmicro/stm32/src/hals/STM32L47X/lcd.zig +++ b/port/stmicro/stm32/src/hals/STM32L47X/lcd.zig @@ -23,8 +23,8 @@ const LCD_RAM = extern struct { }; // LCD RAM is not correctly defined in the stm32-data from embassy -// The high segments are inside a reserved space. -const ram: *volatile LCD_RAM = @ptrCast(&LCD.@"RAM_COM[0]"); +// The high segments occupy the full 32 bit but is limited a few lsb. +const ram: *volatile LCD_RAM = @ptrCast(&LCD.RAM_COM); pub fn init_lcd() void { LCD.CR.modify(.{ diff --git a/tools/regz/src/Database.zig b/tools/regz/src/Database.zig index d9d751d27..00f606371 100644 --- a/tools/regz/src/Database.zig +++ b/tools/regz/src/Database.zig @@ -130,6 +130,7 @@ pub const Register = struct { struct_id: ?StructID, name: []const u8, description: ?[]const u8, + ref_type: ?[]const u8, size_bits: u64, offset_bytes: u64, count: ?u64, @@ -147,17 +148,19 @@ pub const Register = struct { pub fn from_row(allocator: Allocator, row: zqlite.Row) !Register { const name = try allocator.dupe(u8, row.text(2)); const description: ?[]const u8 = if (row.nullableText(3)) |text| try allocator.dupe(u8, text) else null; + const ref_type: ?[]const u8 = if (row.nullableText(4)) |text| try allocator.dupe(u8, text) else null; return Register{ .id = @enumFromInt(row.int(0)), .struct_id = if (row.nullableInt(1)) |value| @enumFromInt(value) else null, .name = name, .description = description, - .size_bits = @intCast(row.int(4)), - .offset_bytes = @intCast(row.int(5)), - .count = if (row.nullableInt(6)) |value| @intCast(value) else null, - .access = std.meta.stringToEnum(Access, row.text(7)) orelse return error.InvalidAccess, - .reset_mask = if (row.nullableInt(8)) |value| @intCast(value) else null, - .reset_value = if (row.nullableInt(9)) |value| @intCast(value) else null, + .ref_type = ref_type, + .size_bits = @intCast(row.int(5)), + .offset_bytes = @intCast(row.int(6)), + .count = if (row.nullableInt(7)) |value| @intCast(value) else null, + .access = std.meta.stringToEnum(Access, row.text(8)) orelse return error.InvalidAccess, + .reset_mask = if (row.nullableInt(9)) |value| @intCast(value) else null, + .reset_value = if (row.nullableInt(10)) |value| @intCast(value) else null, }; } @@ -1705,6 +1708,7 @@ pub const CreateRegisterOptions = struct { // make name required for now name: []const u8, description: ?[]const u8 = null, + ref_type: ?[]const u8 = null, /// offset is in bytes offset_bytes: u64, /// size is in bits @@ -1736,13 +1740,14 @@ pub fn create_register(db: *Database, parent: StructID, opts: CreateRegisterOpti const register_id: RegisterID = blk: { const row = try db.conn.row( \\INSERT INTO registers - \\ (name, description, offset_bytes, size_bits, count, access, reset_mask, reset_value) + \\ (name, description, ref_type, offset_bytes, size_bits, count, access, reset_mask, reset_value) \\VALUES - \\ (?, ?, ?, ?, ?, ?, ?, ?) + \\ (?, ?, ?, ?, ?, ?, ?, ?, ?) \\RETURNING id , .{ opts.name, opts.description, + opts.ref_type, opts.offset_bytes, opts.size_bits, opts.count, diff --git a/tools/regz/src/embassy.zig b/tools/regz/src/embassy.zig index 9d2866415..1aa577195 100644 --- a/tools/regz/src/embassy.zig +++ b/tools/regz/src/embassy.zig @@ -314,6 +314,7 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void const item_bit_size = if (item.object.get("bit_size")) |v| v.integer else 32; const maybe_count: ?u64, const maybe_stride: ?u64 = if (item.object.get("array")) |array| blk: { if (array.object.get("len")) |count| { + if (count.integer == 1) break :blk .{ null, null }; if (array.object.get("stride")) |stride| { if (stride.integer > 4) { break :blk .{ @intCast(count.integer), @intCast(stride.integer) }; @@ -325,8 +326,77 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void break :blk .{ null, null }; } else .{ null, null }; + // This register can be a cluster. In this case we can reference its type only. + // We still need to handle the stride. This is the reason we need to compute size + // of cluster. + if (item.object.get("block")) |block| { + const ref_type = block.string; + const block_key = try std.fmt.allocPrint(allocator, "block/{s}", .{ref_type}); + const block_value = (register_file.value.object.get(block_key) orelse continue).object; + var max_bytes: u64 = 0; + for (block_value.get("items").?.array.items) |nested_item| { + // Let's assume we do not recurse on block + const offset: u64 = @intCast(nested_item.object.get("byte_offset").?.integer); + if (nested_item.object.get("array")) |array| { + if (array.object.get("len")) |count| { + if (array.object.get("stride")) |stride| { + max_bytes = @max(offset + 4 + @as(u64, @intCast(stride.integer * (count.integer - 1))), max_bytes); + continue; + } + } + } + max_bytes = @max(offset + 4, max_bytes); + } + + if (maybe_stride == max_bytes) { + // We have a contiguous cluster array + _ = try db.create_register(group_id, .{ + .name = register_name, + .description = description, + .ref_type = ref_type, + .offset_bytes = @intCast(byte_offset), + .size_bits = max_bytes * 8, + .count = maybe_count, + }); + continue; + } else if (maybe_stride) |stride| { + // We have spread cluster + for (0..maybe_count.?) |id| { + const register_part_name = try std.fmt.allocPrint(allocator, "{s}[{d}]", .{ + register_name, + id, + }); + _ = try db.create_register(group_id, .{ + .name = register_part_name, + .description = description, + .ref_type = ref_type, + .offset_bytes = byte_offset + (id * stride), + .size_bits = max_bytes * 8, + .count = null, + }); + } + continue; + } else if (maybe_stride == null) { + // We have a single cluster + _ = try db.create_register(group_id, .{ + .name = register_name, + .description = description, + .ref_type = ref_type, + .offset_bytes = @intCast(byte_offset), + .size_bits = max_bytes * 8, + .count = null, + }); + continue; + } + // No match default to u32 + // At this point it should be unreachable. + // I leave this comment as a placeholder for other special cases. + } + + // Not a cluster but a fieldset if (maybe_stride) |stride| { if (maybe_count) |count| { + // We spread the register as it jumps over other registers for (0..count) |id| { const register_part_name = try std.fmt.allocPrint(allocator, "{s}[{d}]", .{ register_name, diff --git a/tools/regz/src/gen.zig b/tools/regz/src/gen.zig index c5bff78d1..b0173c971 100644 --- a/tools/regz/src/gen.zig +++ b/tools/regz/src/gen.zig @@ -1149,6 +1149,12 @@ fn write_register( try write_fields(db, arena, fields, register.size_bits, register_reset, writer); try writer.writeAll("}),\n"); + } else if (register.ref_type) |ref_type| { + try writer.print("{f}: {s}{s},\n", .{ + std.zig.fmtId(register.name), + array_prefix, + ref_type, + }); } else if (array_prefix.len != 0) { try writer.print("{f}: {s}u{},\n", .{ std.zig.fmtId(register.name), @@ -1473,6 +1479,7 @@ test "gen.StructFieldIterator.single register" { .struct_id = @enumFromInt(1), .description = "This is a description", .name = "TEST_REGISTER", + .ref_type = null, .size_bits = 32, .offset_bytes = 0, .access = .read_write, @@ -1500,6 +1507,7 @@ test "gen.StructFieldIterator.two registers perfect overlap" { .struct_id = @enumFromInt(1), .description = "This is a description", .name = "TEST_REGISTER1", + .ref_type = null, .size_bits = 32, .offset_bytes = 0, .access = .read_write, @@ -1512,6 +1520,7 @@ test "gen.StructFieldIterator.two registers perfect overlap" { .struct_id = @enumFromInt(2), .description = "This is a description", .name = "TEST_REGISTER2", + .ref_type = null, .size_bits = 32, .offset_bytes = 0, .access = .read_write, @@ -1541,6 +1550,7 @@ test "gen.StructFieldIterator.two registers overlap but one is smaller" { .struct_id = @enumFromInt(1), .description = "This is a description", .name = "TEST_REGISTER1", + .ref_type = null, .size_bits = 32, .offset_bytes = 0, .access = .read_write, @@ -1553,6 +1563,7 @@ test "gen.StructFieldIterator.two registers overlap but one is smaller" { .struct_id = @enumFromInt(2), .description = "This is a description", .name = "TEST_REGISTER2", + .ref_type = null, .size_bits = 16, .offset_bytes = 0, .access = .read_write, @@ -1582,6 +1593,7 @@ test "gen.StructFieldIterator.two registers overlap with different offsets" { .struct_id = @enumFromInt(1), .description = "This is a description", .name = "TEST_REGISTER1", + .ref_type = null, .size_bits = 32, .offset_bytes = 0, .access = .read_write, @@ -1594,6 +1606,7 @@ test "gen.StructFieldIterator.two registers overlap with different offsets" { .struct_id = @enumFromInt(2), .description = "This is a description", .name = "TEST_REGISTER2", + .ref_type = null, .size_bits = 16, .offset_bytes = 2, .access = .read_write, @@ -1645,6 +1658,7 @@ test "gen.StructFieldIterator.one nested struct field and a register" { .struct_id = @enumFromInt(3), .description = "This is a description", .name = "TEST_REGISTER", + .ref_type = null, .size_bits = 32, .offset_bytes = 0, .access = .read_write,