diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c69a4ee9..981ff4d37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: workflow_dispatch: env: - ZIG_VERSION: 0.15.1 + ZIG_VERSION: master jobs: formatting-check: @@ -188,7 +188,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - example_dir: [ + example_dir: + [ espressif/esp, gigadevice/gd32, microchip/atmega, @@ -245,7 +246,7 @@ jobs: - name: Setup Zig uses: mlugg/setup-zig@v2 with: - version: 0.15.1 + version: master - name: Build Website run: zig build working-directory: website diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 196df1259..d36c80ccb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Zig uses: mlugg/setup-zig@v2 with: - version: 0.15.1 + version: master - name: Extract version run: echo "MICROZIG_VERSION=$(zig build package -- get-version)" >> $GITHUB_ENV diff --git a/.github/workflows/drivers.yaml b/.github/workflows/drivers.yaml index 7f510ffd8..d60e59dd6 100644 --- a/.github/workflows/drivers.yaml +++ b/.github/workflows/drivers.yaml @@ -20,7 +20,7 @@ jobs: - name: Setup Zig uses: mlugg/setup-zig@v2 with: - version: 0.15.1 + version: master - name: Run Test Suite working-directory: drivers diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a157f89c3..781df4bfe 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Zig uses: mlugg/setup-zig@v2 with: - version: 0.15.1 + version: master # Build linter from trusted base branch code - name: Build linter diff --git a/.github/workflows/publish-github-pages.yml b/.github/workflows/publish-github-pages.yml index 4becbe241..8e2ccca88 100644 --- a/.github/workflows/publish-github-pages.yml +++ b/.github/workflows/publish-github-pages.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Zig uses: mlugg/setup-zig@v2 with: - version: 0.15.1 + version: master - name: Download and install GitHub CLI run: | @@ -65,8 +65,6 @@ jobs: echo "Generating metadata.json" echo "{ \"releases\": [$(IFS=,; echo "${releases_with_artifact[*]}")] }" > website/zig-out/downloads/microzig/metadata.json - - - name: List Contents run: tree zig-out working-directory: website diff --git a/.github/workflows/sim-aviron.yml b/.github/workflows/sim-aviron.yml index ed71a1fa2..a824a359b 100644 --- a/.github/workflows/sim-aviron.yml +++ b/.github/workflows/sim-aviron.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Zig uses: mlugg/setup-zig@v2 with: - version: 0.15.1 + version: master - name: Build working-directory: sim/aviron diff --git a/.gitignore b/.gitignore index 64d47eee7..f020959dc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ __pycache__/ .zig-cache/ boxzer-out microzig-deploy/ -zig-cache/ zig-out/ zig-pkg/ diff --git a/README.md b/README.md index 3bb05577c..09e1898a9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## What version of Zig to use -Zig 0.15.1 +Zig master ## Getting Started With MicroZig diff --git a/build.zig b/build.zig index 06c634afd..9f231925a 100644 --- a/build.zig +++ b/build.zig @@ -112,25 +112,27 @@ pub const PortSelect = struct { // Don't know if this is required but it doesn't hurt either. // Helps in case there are multiple microzig instances including the same ports (eg: examples). pub const PortCache = blk: { - var fields: []const std.builtin.Type.StructField = &.{}; - for (port_list) |port| { - const typ = ?(custom_lazy_import(port.dep_name) orelse struct {}); - fields = fields ++ [_]std.builtin.Type.StructField{.{ - .name = port.name, - .type = typ, - .default_value_ptr = @as(*const anyopaque, @ptrCast(&@as(typ, null))), - .is_comptime = false, - .alignment = @alignOf(typ), - }}; + var field_names: [port_list.len][]const u8 = undefined; + var field_types: [port_list.len]type = undefined; + var field_attrs: [port_list.len]std.builtin.Type.StructField.Attributes = undefined; + + for (port_list, 0..) |port, i| { + const typ = custom_lazy_import(port.dep_name) orelse struct {}; + const default: ?typ = null; + field_names[i] = port.name; + field_types[i] = ?typ; + field_attrs[i] = .{ + .default_value_ptr = &default, + }; } - break :blk @Type(.{ - .@"struct" = .{ - .layout = .auto, - .fields = fields, - .decls = &.{}, - .is_tuple = false, - }, - }); + + break :blk @Struct( + .auto, + null, + &field_names, + &field_types, + &field_attrs, + ); }; var port_cache: PortCache = .{}; @@ -174,29 +176,31 @@ pub fn MicroBuild(port_select: PortSelect) type { const Self = @This(); const SelectedPorts = blk: { - var fields: []const std.builtin.Type.StructField = &.{}; + var field_names: [port_list.len][]const u8 = undefined; + var field_types: [port_list.len]type = undefined; + var field_attrs: [port_list.len]std.builtin.Type.StructField.Attributes = undefined; + var count: usize = 0; for (port_list) |port| { if (@field(port_select, port.name)) { + const i = count; const typ = custom_lazy_import(port.dep_name) orelse struct {}; - fields = fields ++ [_]std.builtin.Type.StructField{.{ - .name = port.name, - .type = typ, - .default_value_ptr = null, - .is_comptime = false, - .alignment = @alignOf(typ), - }}; + + field_names[i] = port.name; + field_types[i] = typ; + field_attrs[i] = .{}; + + count += 1; } } - break :blk @Type(.{ - .@"struct" = .{ - .layout = .auto, - .fields = fields, - .decls = &.{}, - .is_tuple = false, - }, - }); + break :blk @Struct( + .auto, + null, + field_names[0..count], + field_types[0..count], + field_attrs[0..count], + ); }; const InitReturnType = blk: { @@ -315,6 +319,9 @@ pub fn MicroBuild(port_select: PortSelect) type { /// Dwarf format option for the firmware executable. dwarf_format: ?std.dwarf.Format = null, + + /// Whether to omit the stack frame pointer. + omit_frame_pointer: ?bool = null, }; /// Creates a new firmware for a given target. @@ -508,6 +515,7 @@ pub fn MicroBuild(port_select: PortSelect) type { .unwind_tables = options.unwind_tables, .error_tracing = options.error_tracing, .dwarf_format = options.dwarf_format, + .omit_frame_pointer = options.omit_frame_pointer, }), .linkage = .static, }), diff --git a/core/src/core/usb.zig b/core/src/core/usb.zig index 4d1446b48..5935d2c61 100644 --- a/core/src/core/usb.zig +++ b/core/src/core/usb.zig @@ -1,5 +1,6 @@ const std = @import("std"); const assert = std.debug.assert; +const StructFieldAttributes = std.builtin.Type.StructField.Attributes; const log = std.log.scoped(.usb_ctrl); pub const descriptor = @import("usb/descriptor.zig"); @@ -13,41 +14,6 @@ pub const types = @import("usb/types.zig"); pub const ack: []const u8 = ""; pub const nak: ?[]const u8 = null; -/// Meant to make transition to zig 0.16 easier -pub const StructFieldAttributes = struct { - @"comptime": bool = false, - @"align": ?usize = null, - default_value_ptr: ?*const anyopaque = null, -}; - -/// Helper to create a struct, wrapping around @Type, meant to make transition to zig 0.16 easier -pub fn Struct( - layout: std.builtin.Type.ContainerLayout, - BackingInt: ?type, - field_names: []const [:0]const u8, - field_types: *const [field_names.len]type, - field_attrs: *const [field_names.len]StructFieldAttributes, -) type { - var fields: []const std.builtin.Type.StructField = &.{}; - // Iterate over the names, field types, and attributes, creating a new struct field entry - for (field_names, field_types, field_attrs) |n, T, a| { - fields = fields ++ &[1]std.builtin.Type.StructField{.{ - .name = n, - .type = T, - .alignment = a.@"align" orelse @alignOf(T), - .default_value_ptr = a.default_value_ptr, - .is_comptime = a.@"comptime", - }}; - } - return @Type(.{ .@"struct" = .{ - .layout = layout, - .backing_integer = BackingInt, - .decls = &.{}, - .fields = fields, - .is_tuple = false, - } }); -} - // What does this do? It lets you iterate through interfaces and endpoints? pub const DescriptorAllocator = struct { next_ep_num: [2]u8, @@ -173,7 +139,7 @@ pub fn DriverHandlers(Driver: type) type { else => {}, }; - return Struct( + return @Struct( .auto, null, field_names, @@ -214,7 +180,7 @@ pub const Config = struct { // And save the type of the third field_types[i] = params[2].type.?; } - return Struct(.auto, null, &field_names, &field_types, &@splat(.{})); + return @Struct(.auto, null, &field_names, &field_types, &@splat(.{})); } }; @@ -265,7 +231,7 @@ pub fn validate_controller(T: type) void { /// Responds to host requests and dispatches to the appropriate drivers. /// When this type is build (at comptime), it builds descriptor and handler tables based on the /// provided config. -pub fn DeviceController(config: Config, driver_args: config.DriverArgs()) type { +pub fn DeviceController(comptime config: Config, comptime driver_args: config.DriverArgs()) type { std.debug.assert(config.configurations.len == 1); return struct { @@ -381,11 +347,9 @@ pub fn DeviceController(config: Config, driver_args: config.DriverArgs()) type { field_attrs[drv_id] = .{ .default_value_ptr = &descriptors }; } - // Finally, bind the handler functions based on the data collected above. - const Tuple = std.meta.Tuple; // Create a tuple with the appropriate types - const ep_handlers_types: [2]type = .{ Tuple(&ep_handler_types[0]), Tuple(&ep_handler_types[1]) }; - var ep_handlers: Tuple(&ep_handlers_types) = undefined; + const ep_handlers_types: [2]type = .{ @Tuple(&ep_handler_types[0]), @Tuple(&ep_handler_types[1]) }; + var ep_handlers: @Tuple(&ep_handlers_types) = undefined; // Iterate over all IN and OUT endpoints and bind the handler for any that are set. for (&ep_handler_types, &ep_handler_names, &ep_handler_drivers, 0..) |htypes, hnames, hdrivers, dir| { for (&htypes, &hnames, &hdrivers, 0..) |T, name, drv_id, ep| { @@ -394,13 +358,22 @@ pub fn DeviceController(config: Config, driver_args: config.DriverArgs()) type { } } - const DriverConfig = Struct(.@"extern", null, &field_names, &field_types, &field_attrs); + const DriverConfig = @Struct(.@"extern", null, &field_names, &field_types, &field_attrs); const idx_in = @intFromEnum(types.Dir.In); const idx_out = @intFromEnum(types.Dir.Out); + + const ConfigDescriptor = extern struct { + first: descriptor.Configuration, + drv: DriverConfig, + }; + const Handlers_EP = struct { + In: ep_handlers_types[idx_in], + Out: ep_handlers_types[idx_out], + }; break :blk .{ .device_descriptor = desc_device, - .config_descriptor = extern struct { - first: descriptor.Configuration = .{ + .config_descriptor = ConfigDescriptor{ + .first = .{ .total_length = .from(size), .num_interfaces = alloc.next_itf_num, .configuration_value = 1, @@ -408,16 +381,16 @@ pub fn DeviceController(config: Config, driver_args: config.DriverArgs()) type { .attributes = config0.attributes, .max_current = .from_ma(config0.max_current_ma), }, - drv: DriverConfig = .{}, - }{}, + .drv = .{}, + }, .string_descriptors = alloc.string_descriptors(config.language), .handlers_itf = itf_handlers, - .handlers_ep = struct { - In: ep_handlers_types[idx_in] = ep_handlers[idx_in], - Out: ep_handlers_types[idx_out] = ep_handlers[idx_out], - }{}, + .handlers_ep = Handlers_EP{ + .In = ep_handlers[idx_in], + .Out = ep_handlers[idx_out], + }, .drivers_ep = ep_handler_drivers, - .DriverAlloc = Struct( + .DriverAlloc = @Struct( .auto, null, driver_alloc_names, diff --git a/core/src/cpus/cortex_m.zig b/core/src/cpus/cortex_m.zig index 2a8e2e425..8d17d5644 100644 --- a/core/src/cpus/cortex_m.zig +++ b/core/src/cpus/cortex_m.zig @@ -16,7 +16,9 @@ const Core = enum { cortex_m7, }; -const cortex_m = std.meta.stringToEnum(Core, microzig.config.cpu_name) orelse +const cortex_m = if (@hasField(Core, microzig.config.cpu_name)) + @field(Core, microzig.config.cpu_name) +else @compileError(std.fmt.comptimePrint("Unrecognized Cortex-M core name: {s}", .{microzig.config.cpu_name})); /// Segger's RTT support @@ -69,25 +71,19 @@ pub const ExternalInterrupt = blk: { if (result_len == 0) break :blk enum {}; - var fields: [result_len]std.builtin.Type.EnumField = undefined; + var field_names: [result_len][]const u8 = undefined; + var field_values: [result_len]u8 = undefined; var field_index: usize = 0; for (microzig.chip.interrupts) |intr| { if (intr.index >= 0) { - fields[field_index] = .{ - .name = intr.name, - .value = intr.index, - }; + field_names[field_index] = intr.name; + field_values[field_index] = intr.index; field_index += 1; } } - break :blk @Type(.{ .@"enum" = .{ - .tag_type = u8, - .fields = &fields, - .decls = &.{}, - .is_exhaustive = true, - } }); + break :blk @Enum(u8, .exhaustive, &field_names, &field_values); }; /// Machine exceptions. @@ -105,25 +101,19 @@ pub const Exception = blk: { if (result_len == 0) break :blk enum {}; - var fields: [result_len]std.builtin.Type.EnumField = undefined; + var field_names: [result_len][]const u8 = undefined; + var field_values: [result_len]u4 = undefined; var field_index: usize = 0; for (microzig.chip.interrupts) |intr| { if (intr.index < 0) { - fields[field_index] = .{ - .name = intr.name, - .value = 16 + intr.index, // Cortex-M exceptions are mapped to vector table slots 0 - 15 - }; + field_names[field_index] = intr.name; + field_values[field_index] = 16 + intr.index; // Cortex-M exceptions are mapped to vector table slots 0 - 15 field_index += 1; } } - break :blk @Type(.{ .@"enum" = .{ - .tag_type = u4, - .fields = &fields, - .decls = &.{}, - .is_exhaustive = true, - } }); + break :blk @Enum(u4, .exhaustive, field_names, field_values); }; pub const interrupt = struct { diff --git a/core/src/microzig.zig b/core/src/microzig.zig index b761913df..f76917e7d 100644 --- a/core/src/microzig.zig +++ b/core/src/microzig.zig @@ -42,12 +42,17 @@ pub const panic = std.debug.FullPanic(struct { pub fn panic_fn(message: []const u8, first_trace_address: ?usize) noreturn { std.log.err("panic: {s}", .{message}); - var frame_index: usize = 0; - if (@errorReturnTrace()) |trace| frame_index = utilities.dump_stack_trace(trace); - - var iter = std.debug.StackIterator.init(first_trace_address orelse @returnAddress(), null); - while (iter.next()) |address| : (frame_index += 1) { - std.log.err("{d: >3}: 0x{X:0>8}", .{ frame_index, address }); + var trace_index: usize = 0; + if (@errorReturnTrace()) |trace| trace_index = utilities.dump_error_trace(trace); + + // Skip if we can't use fp based stack tracing. + if (options.panic_stack_trace and utilities.StackIterator.can_use_fp_based_stack_trace) { + var it: utilities.StackIterator = .{ + .fp = first_trace_address orelse @frameAddress(), + }; + while (it.next()) |address| : (trace_index += 1) { + utilities.dump_trace_line(trace_index, address); + } } // Attach a breakpoint. this might trigger another panic internally, so @@ -77,7 +82,7 @@ pub const Options = struct { ) void = struct { fn log( comptime message_level: std.log.Level, - comptime scope: @Type(.enum_literal), + comptime scope: @EnumLiteral(), comptime format: []const u8, args: anytype, ) void { @@ -101,6 +106,9 @@ pub const Options = struct { /// reduce code size as the string literals for error names no longer have to /// be included in the executable. simple_panic_if_main_errors: bool = false, + + /// Enable panic stack traces. + panic_stack_trace: bool = true, }; pub const options: Options = if (@hasDecl(app, "microzig_options")) app.microzig_options else .{}; diff --git a/core/src/mmio.zig b/core/src/mmio.zig index 98c08579a..689f32b3d 100644 --- a/core/src/mmio.zig +++ b/core/src/mmio.zig @@ -68,7 +68,7 @@ pub fn Mmio(comptime PackedT: type) type { const U = enum_info.tag_type; @field(val, field_name) = @as(FieldType, @enumFromInt(@as(U, @intFromEnum(@field(val, field_name))) ^ - @as(U, @intFromEnum(@as(FieldType, value))))); + @as(U, @intFromEnum(@as(FieldType, value))))); }, else => |T| { @compileError("unsupported register field type '" ++ @typeName(T) ++ "'"); diff --git a/core/src/start.zig b/core/src/start.zig index 004e20a5f..d9794c416 100644 --- a/core/src/start.zig +++ b/core/src/start.zig @@ -16,6 +16,11 @@ pub const std_options: std.Options = .{ .logFn = microzig.options.logFn, }; +pub const std_options_debug_io: std.Io = if (@hasDecl(microzig.app, "microzig_options_debug_io")) + microzig.app.microzig_options_debug_io +else + .failing; + // Startup logic: comptime { // Instantiate the startup logic for the given CPU type. diff --git a/core/src/utilities.zig b/core/src/utilities.zig index 9bcad56bf..39782c0e3 100644 --- a/core/src/utilities.zig +++ b/core/src/utilities.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; @@ -60,24 +61,19 @@ pub fn SliceVector(comptime Slice: type) type { if (type_info.pointer.size != .slice) @compileError("Slice must have a slice type!"); - const item_ptr_info: std.builtin.Type = .{ - .pointer = .{ - .alignment = @min(type_info.pointer.alignment, @alignOf(type_info.pointer.child)), - .size = .one, - .child = type_info.pointer.child, - .address_space = type_info.pointer.address_space, - .is_const = type_info.pointer.is_const, - .is_volatile = type_info.pointer.is_volatile, - .is_allowzero = type_info.pointer.is_allowzero, - .sentinel_ptr = null, - }, + const item_ptr_attrs: std.builtin.Type.Pointer.Attributes = .{ + .@"align" = type_info.pointer.alignment, + .@"addrspace" = type_info.pointer.address_space, + .@"const" = type_info.pointer.is_const, + .@"volatile" = type_info.pointer.is_volatile, + .@"allowzero" = type_info.pointer.is_allowzero, }; return struct { const Vector = @This(); pub const Item = type_info.pointer.child; - pub const ItemPtr = @Type(item_ptr_info); + pub const ItemPtr = @Pointer(.one, item_ptr_attrs, type_info.pointer.child, null); /// The slice of slices. The first and the last slice of this slice must /// be non-empty or the slice-of-slices must be empty. @@ -257,21 +253,15 @@ pub fn GenerateInterruptEnum(TagType: type) type { if (microzig.chip.interrupts.len == 0) return enum {}; - var fields: [microzig.chip.interrupts.len]std.builtin.Type.EnumField = undefined; + var field_names: [microzig.chip.interrupts.len][]const u8 = undefined; + var field_values: [microzig.chip.interrupts.len]TagType = undefined; - for (&fields, microzig.chip.interrupts) |*field, interrupt| { - field.* = .{ - .name = interrupt.name, - .value = interrupt.index, - }; + for (microzig.chip.interrupts, &field_names, &field_values) |interrupt, *name, *value| { + name.* = interrupt.name; + value.* = interrupt.index; } - return @Type(.{ .@"enum" = .{ - .tag_type = TagType, - .fields = &fields, - .decls = &.{}, - .is_exhaustive = true, - } }); + return @Enum(TagType, .exhaustive, &field_names, &field_values); } pub const Source = struct { @@ -280,30 +270,38 @@ pub const Source = struct { }; pub fn GenerateInterruptOptions(sources: []const Source) type { - var ret_fields: []const std.builtin.Type.StructField = &.{}; + const len = blk: { + var len: usize = 0; + for (sources) |source| { + if (@typeInfo(source.InterruptEnum) != .@"enum") @compileError("expected an enum type"); + + for (@typeInfo(source.InterruptEnum).@"enum".fields) |_| { + len += 1; + } + } + + break :blk len; + }; + var field_names: [len][]const u8 = undefined; + var field_types: [len]type = undefined; + var field_attrs: [len]std.builtin.Type.StructField.Attributes = undefined; + var idx: usize = 0; for (sources) |source| { if (@typeInfo(source.InterruptEnum) != .@"enum") @compileError("expected an enum type"); for (@typeInfo(source.InterruptEnum).@"enum".fields) |enum_field| { - ret_fields = ret_fields ++ .{std.builtin.Type.StructField{ - .name = enum_field.name, - .type = ?source.HandlerFn, + field_names[idx] = enum_field.name; + field_types[idx] = ?source.HandlerFn; + field_attrs[idx] = .{ .default_value_ptr = @as(*const anyopaque, @ptrCast(&@as(?source.HandlerFn, null))), - .is_comptime = false, - .alignment = @alignOf(?source.HandlerFn), - }}; + }; + + idx += 1; } } - return @Type(.{ - .@"struct" = .{ - .layout = .auto, - .fields = ret_fields, - .decls = &.{}, - .is_tuple = false, - }, - }); + return @Struct(.auto, null, &field_names, &field_types, &field_attrs); } test SliceVector { @@ -482,7 +480,7 @@ test "SliceVector.Iterator.next_chunk" { } } -pub fn dump_stack_trace(trace: *std.builtin.StackTrace) usize { +pub fn dump_error_trace(trace: *std.builtin.StackTrace) usize { const frame_count = @min(trace.index, trace.instruction_addresses.len); var frame_index: usize = 0; @@ -492,12 +490,173 @@ pub fn dump_stack_trace(trace: *std.builtin.StackTrace) usize { frame_index = (frame_index + 1) % trace.instruction_addresses.len; }) { const address = trace.instruction_addresses[frame_index]; - std.log.err("{d: >3}: 0x{X:0>8}", .{ frame_index, address }); + dump_trace_line(frame_index, address); } return frame_count; } +pub fn dump_trace_line(index: usize, address: usize) void { + std.log.err("{d: >3}: 0x{X:0>8}", .{ index, address }); +} + +pub const StackIterator = struct { + pub const can_use_fp_based_stack_trace = switch (fp_usability) { + .useless => false, + .unsafe, .safe => !builtin.omit_frame_pointer, + .ideal => true, + }; + + fp: usize, + + pub fn next(it: *StackIterator) ?usize { + if (!can_use_fp_based_stack_trace) return null; + + const fp = it.fp; + + if (fp == 0) return null; // we reached the "sentinel" base pointer + + const bp_addr = apply_offset(fp, fp_to_bp_offset) orelse return null; + const ra_addr = apply_offset(fp, fp_to_ra_offset) orelse return null; + + if (bp_addr == 0 or !std.mem.isAligned(bp_addr, @alignOf(usize)) or + ra_addr == 0 or !std.mem.isAligned(ra_addr, @alignOf(usize))) + { + // This isn't valid, but it most likely indicates end of stack. + return null; + } + + const bp_ptr: *const usize = @ptrFromInt(bp_addr); + const ra_ptr: *const usize = @ptrFromInt(ra_addr); + const bp = apply_offset(bp_ptr.*, stack_bias) orelse return null; + + // If the stack grows downwards, `bp > fp` should always hold; conversely, if it + // grows upwards, `bp < fp` should always hold. If that is not the case, this + // frame is invalid, so we'll treat it as though we reached end of stack. The + // exception is address 0, which is a graceful end-of-stack signal, in which case + // *this* return address is valid and the *next* iteration will be the last. + if (bp != 0 and switch (comptime builtin.target.stackGrowth()) { + .down => bp <= fp, + .up => bp >= fp, + }) return null; + + it.fp = bp; + const ra = std.debug.stripInstructionPtrAuthCode(ra_ptr.*); + if (ra <= 1) return null; + return ra; + } + + const native_arch = builtin.cpu.arch; + + const FpUsability = enum { + /// FP unwinding is impractical on this target. For example, due to its very silly ABI + /// design decisions, it's not possible to do generic FP unwinding on MIPS without a + /// complicated code scanning algorithm. + useless, + /// FP unwinding is unsafe on this target; we may crash when doing so. We will only perform + /// FP unwinding in the case of crashes/panics, or if the user opts in. + unsafe, + /// FP unwinding is guaranteed to be safe on this target. We will do so if unwinding with + /// debug info does not work, and if this compilation has frame pointers enabled. + safe, + /// FP unwinding is the best option on this target. This is usually because the ABI requires + /// a backchain pointer, thus making it always available, safe, and fast. + ideal, + }; + + const fp_usability: FpUsability = switch (native_arch) { + .alpha, + .avr, + .csky, + .microblaze, + .microblazeel, + .mips, + .mipsel, + .mips64, + .mips64el, + .msp430, + .sh, + .sheb, + .xcore, + => .useless, + .hexagon, + // The PowerPC ABIs don't actually strictly require a backchain pointer; they allow omitting + // it when full unwind info is present. Despite this, both GCC and Clang always enforce the + // presence of the backchain pointer no matter what options they are given. This seems to be + // a case of "the spec is only a polite suggestion", except it works in our favor this time! + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + .sparc, + .sparc64, + => .ideal, + // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-purpose-of-specific-CPU-registers + .aarch64 => if (builtin.target.os.tag.isDarwin()) .safe else .unsafe, + else => .unsafe, + }; + + /// Offset of the saved base pointer (previous frame pointer) wrt the frame pointer. + const fp_to_bp_offset = off: { + // On 32-bit PA-RISC, the base pointer is the final word of the frame marker. + if (native_arch == .hppa) break :off -1 * @sizeOf(usize); + // On 64-bit PA-RISC, the frame marker was shrunk significantly; now there's just the return + // address followed by the base pointer. + if (native_arch == .hppa64) break :off -1 * @sizeOf(usize); + // On LoongArch and RISC-V, the frame pointer points to the top of the saved register area, + // in which the base pointer is the first word. + if (native_arch.isLoongArch() or native_arch.isRISCV()) break :off -2 * @sizeOf(usize); + // On OpenRISC, the frame pointer is stored below the return address. + if (native_arch == .or1k) break :off -2 * @sizeOf(usize); + // On SPARC, the frame pointer points to the save area which holds 16 slots for the local + // and incoming registers. The base pointer (i6) is stored in its customary save slot. + if (native_arch.isSPARC()) break :off 14 * @sizeOf(usize); + // Everywhere else, the frame pointer points directly to the location of the base pointer. + break :off 0; + }; + + /// Offset of the saved return address wrt the frame pointer. + const fp_to_ra_offset = off: { + // On 32-bit PA-RISC, the return address sits in the middle-ish of the frame marker. + if (native_arch == .hppa) break :off -5 * @sizeOf(usize); + // On 64-bit PA-RISC, the frame marker was shrunk significantly; now there's just the return + // address followed by the base pointer. + if (native_arch == .hppa64) break :off -2 * @sizeOf(usize); + // On LoongArch and RISC-V, the frame pointer points to the top of the saved register area, + // in which the return address is the second word. + if (native_arch.isLoongArch() or native_arch.isRISCV()) break :off -1 * @sizeOf(usize); + // On OpenRISC, the return address is stored below the stack parameter area. + if (native_arch == .or1k) break :off -1 * @sizeOf(usize); + if (native_arch.isPowerPC64()) break :off 2 * @sizeOf(usize); + // On s390x, r14 is the link register and we need to grab it from its customary slot in the + // register save area (ELF ABI s390x Supplement ยง1.2.2.2). + if (native_arch == .s390x) break :off 14 * @sizeOf(usize); + // On SPARC, the frame pointer points to the save area which holds 16 slots for the local + // and incoming registers. The return address (i7) is stored in its customary save slot. + if (native_arch.isSPARC()) break :off 15 * @sizeOf(usize); + break :off @sizeOf(usize); + }; + + /// Value to add to the stack pointer and frame/base pointers to get the real location being + /// pointed to. Yes, SPARC really does this. + const stack_bias = bias: { + if (native_arch == .sparc64) break :bias 2047; + break :bias 0; + }; + + /// On some oddball architectures, a return address points to the call instruction rather than + /// the instruction following it. + const ra_call_offset = off: { + if (native_arch.isSPARC()) break :off 0; + break :off 1; + }; + + fn apply_offset(addr: usize, comptime off: comptime_int) ?usize { + if (off >= 0) return std.math.add(usize, addr, off) catch return null; + return std.math.sub(usize, addr, -off) catch return null; + } +}; + pub fn get_end_of_stack() *const anyopaque { if (microzig.config.end_of_stack.address) |address| { return @ptrFromInt(address); diff --git a/drivers/base/DateTime.zig b/drivers/base/DateTime.zig index e2005769b..818ed79cd 100644 --- a/drivers/base/DateTime.zig +++ b/drivers/base/DateTime.zig @@ -343,7 +343,7 @@ pub const Timezone = enum(i16) { /// * `separator` - An optional separator to add between the hours and minutes /// defaults to ":" if null pub fn to_string(self: Timezone, out_string: []u8, separator: ?[]const u8) Error!usize { - if (out_string.len < 5 + (separator orelse ":").len) return error.NotEnoughSpace; + if (out_string.len < 5 + if (separator) |sep| sep.len else 1) return error.NotEnoughSpace; const minus = @intFromEnum(self) < 0; diff --git a/examples/espressif/esp/build.zig.zon b/examples/espressif/esp/build.zig.zon index 3b85b142c..4a823d6f3 100644 --- a/examples/espressif/esp/build.zig.zon +++ b/examples/espressif/esp/build.zig.zon @@ -1,6 +1,7 @@ .{ .name = .examples_espressif_esp, .fingerprint = 0xa568458fa3375cb1, + .minimum_zig_version = "0.16.0", .version = "0.0.0", .dependencies = .{ .microzig = .{ .path = "../../.." }, diff --git a/examples/espressif/esp/src/ledc_pwm_servo.zig b/examples/espressif/esp/src/ledc_pwm_servo.zig index bae40572a..94ab34c53 100644 --- a/examples/espressif/esp/src/ledc_pwm_servo.zig +++ b/examples/espressif/esp/src/ledc_pwm_servo.zig @@ -11,8 +11,8 @@ const PWM_PERIOD_MS = 1000 / PWM_FREQ_HZ; const PWM_PRECISION_BITS = 14; const PWM_MAX_DUTY = std.math.pow(u32, 2, PWM_PRECISION_BITS) - 1; -const PWM_MIN_LEVEL: u16 = @trunc(@as(f32, 1.0) / PWM_PERIOD_MS * PWM_MAX_DUTY); -const PWM_MAX_LEVEL: u16 = @trunc(@as(f32, 2.0) / PWM_PERIOD_MS * PWM_MAX_DUTY); +const PWM_MIN_LEVEL: u16 = @intFromFloat(@trunc(@as(f32, 1.0) / PWM_PERIOD_MS * PWM_MAX_DUTY)); +const PWM_MAX_LEVEL: u16 = @intFromFloat(@trunc(@as(f32, 2.0) / PWM_PERIOD_MS * PWM_MAX_DUTY)); pub fn main() !void { // pwm servo driver diff --git a/examples/raspberrypi/rp2xxx/build.zig b/examples/raspberrypi/rp2xxx/build.zig index a99aec1ef..dd20aa973 100644 --- a/examples/raspberrypi/rp2xxx/build.zig +++ b/examples/raspberrypi/rp2xxx/build.zig @@ -72,6 +72,7 @@ pub fn build(b: *std.Build) void { .{ .name = "pwm", .file = "src/pwm.zig" }, .{ .name = "uart-echo", .file = "src/uart_echo.zig" }, .{ .name = "uart-log", .file = "src/uart_log.zig" }, + .{ .name = "uart-reader-writer", .file = "src/uart_reader_writer.zig" }, .{ .name = "rtt-log", .file = "src/rtt_log.zig", .works_with_riscv = false }, .{ .name = "spi-master", .file = "src/spi_master.zig" }, .{ .name = "spi-slave", .file = "src/spi_slave.zig" }, diff --git a/examples/raspberrypi/rp2xxx/build.zig.zon b/examples/raspberrypi/rp2xxx/build.zig.zon index 2f0e8fe4d..043c24b77 100644 --- a/examples/raspberrypi/rp2xxx/build.zig.zon +++ b/examples/raspberrypi/rp2xxx/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .examples_raspberrypi_rp2xxx, .fingerprint = 0xffa4bfa151162a57, - .minimum_zig_version = "0.15.1", + .minimum_zig_version = "0.16.0", .version = "0.0.0", .dependencies = .{ .microzig = .{ .path = "../../.." }, diff --git a/examples/raspberrypi/rp2xxx/src/adc.zig b/examples/raspberrypi/rp2xxx/src/adc.zig index 7fa78fc88..06d4cacdd 100644 --- a/examples/raspberrypi/rp2xxx/src/adc.zig +++ b/examples/raspberrypi/rp2xxx/src/adc.zig @@ -20,7 +20,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); adc.apply(.{ .temp_sensor_enabled = true, diff --git a/examples/raspberrypi/rp2xxx/src/allocator.zig b/examples/raspberrypi/rp2xxx/src/allocator.zig index 3f8fcdc65..fceed056c 100644 --- a/examples/raspberrypi/rp2xxx/src/allocator.zig +++ b/examples/raspberrypi/rp2xxx/src/allocator.zig @@ -55,7 +55,7 @@ pub fn main() !void { // --- Set up Logger ----------------------------- - hal.uart.init_logger(uart); + hal.uart.init_logger(uart, &.{}); time.sleep_ms(1000); diff --git a/examples/raspberrypi/rp2xxx/src/board_id.zig b/examples/raspberrypi/rp2xxx/src/board_id.zig index 6581fcd70..ad0d0da51 100644 --- a/examples/raspberrypi/rp2xxx/src/board_id.zig +++ b/examples/raspberrypi/rp2xxx/src/board_id.zig @@ -26,7 +26,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); while (true) { log.info("unique board id: {x}", .{rp2xxx.get_board_id()}); diff --git a/examples/raspberrypi/rp2xxx/src/cyw43.zig b/examples/raspberrypi/rp2xxx/src/cyw43.zig index 8fe38ed4b..d8d04a557 100644 --- a/examples/raspberrypi/rp2xxx/src/cyw43.zig +++ b/examples/raspberrypi/rp2xxx/src/cyw43.zig @@ -27,7 +27,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // init cyw43 var wifi = try wifi_driver.init(.{}); diff --git a/examples/raspberrypi/rp2xxx/src/cyw43/wifi_connect.zig b/examples/raspberrypi/rp2xxx/src/cyw43/wifi_connect.zig index 0e5db7e2c..f940a7b04 100644 --- a/examples/raspberrypi/rp2xxx/src/cyw43/wifi_connect.zig +++ b/examples/raspberrypi/rp2xxx/src/cyw43/wifi_connect.zig @@ -26,7 +26,7 @@ const WIFI_AUTH = Security.wpa2_psk; pub fn main() !void { uart_tx_pin.set_function(.uart); uart.apply(.{ .clock_config = rp2xxx.clock_config }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); std.log.info("Initializing CYW43...", .{}); diff --git a/examples/raspberrypi/rp2xxx/src/cyw43/wifi_scan.zig b/examples/raspberrypi/rp2xxx/src/cyw43/wifi_scan.zig index 61cdd5c27..4cb5aa07a 100644 --- a/examples/raspberrypi/rp2xxx/src/cyw43/wifi_scan.zig +++ b/examples/raspberrypi/rp2xxx/src/cyw43/wifi_scan.zig @@ -18,7 +18,7 @@ pub const microzig_options = microzig.Options{ pub fn main() !void { uart_tx_pin.set_function(.uart); uart.apply(.{ .clock_config = rp2xxx.clock_config }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); std.log.info("Initializing CYW43...", .{}); diff --git a/examples/raspberrypi/rp2xxx/src/dma.zig b/examples/raspberrypi/rp2xxx/src/dma.zig index f0ad6f907..759b81437 100644 --- a/examples/raspberrypi/rp2xxx/src/dma.zig +++ b/examples/raspberrypi/rp2xxx/src/dma.zig @@ -37,7 +37,7 @@ pub fn main() !void { .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); const channel = dma.claim_unused_channel().?; diff --git a/examples/raspberrypi/rp2xxx/src/freertos/hello_task.zig b/examples/raspberrypi/rp2xxx/src/freertos/hello_task.zig index 614eb0dcd..347fd2344 100644 --- a/examples/raspberrypi/rp2xxx/src/freertos/hello_task.zig +++ b/examples/raspberrypi/rp2xxx/src/freertos/hello_task.zig @@ -40,7 +40,7 @@ pub fn main() !void { .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // Create a task using the new idiomatic API _ = try freertos.task.create( diff --git a/examples/raspberrypi/rp2xxx/src/freertos/multitask_demo.zig b/examples/raspberrypi/rp2xxx/src/freertos/multitask_demo.zig index 99130411b..5b8519448 100644 --- a/examples/raspberrypi/rp2xxx/src/freertos/multitask_demo.zig +++ b/examples/raspberrypi/rp2xxx/src/freertos/multitask_demo.zig @@ -59,7 +59,7 @@ pub fn main() !void { // Hardware setup uart_tx_pin.set_function(.uart); uart.apply(.{ .clock_config = rp2xxx.clock_config }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); std.log.info("[main] Starting FreeRTOS multitask demo...", .{}); diff --git a/examples/raspberrypi/rp2xxx/src/freertos/queue_demo.zig b/examples/raspberrypi/rp2xxx/src/freertos/queue_demo.zig index 1b5dae39d..e805983b7 100644 --- a/examples/raspberrypi/rp2xxx/src/freertos/queue_demo.zig +++ b/examples/raspberrypi/rp2xxx/src/freertos/queue_demo.zig @@ -33,7 +33,7 @@ pub fn main() !void { .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // Create a queue that holds up to 5 u32 values message_queue = try freertos.queue.create(u32, 5); diff --git a/examples/raspberrypi/rp2xxx/src/gpio_irq.zig b/examples/raspberrypi/rp2xxx/src/gpio_irq.zig index af1c8ea92..d431ac5ed 100644 --- a/examples/raspberrypi/rp2xxx/src/gpio_irq.zig +++ b/examples/raspberrypi/rp2xxx/src/gpio_irq.zig @@ -54,7 +54,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // Initialize the loop var i: u32 = 0; diff --git a/examples/raspberrypi/rp2xxx/src/i2c_accel.zig b/examples/raspberrypi/rp2xxx/src/i2c_accel.zig index e7526654d..02291d1ff 100644 --- a/examples/raspberrypi/rp2xxx/src/i2c_accel.zig +++ b/examples/raspberrypi/rp2xxx/src/i2c_accel.zig @@ -26,7 +26,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); defer i2c0.reset(); diff --git a/examples/raspberrypi/rp2xxx/src/i2c_bus_scan.zig b/examples/raspberrypi/rp2xxx/src/i2c_bus_scan.zig index 541375aa8..708a4fdac 100644 --- a/examples/raspberrypi/rp2xxx/src/i2c_bus_scan.zig +++ b/examples/raspberrypi/rp2xxx/src/i2c_bus_scan.zig @@ -22,7 +22,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); const sda_pin = gpio.num(4); const scl_pin = gpio.num(5); diff --git a/examples/raspberrypi/rp2xxx/src/i2c_hall_effect.zig b/examples/raspberrypi/rp2xxx/src/i2c_hall_effect.zig index 76086144c..ce44f5c71 100644 --- a/examples/raspberrypi/rp2xxx/src/i2c_hall_effect.zig +++ b/examples/raspberrypi/rp2xxx/src/i2c_hall_effect.zig @@ -29,7 +29,7 @@ pub fn main() !void { .baud_rate = baud_rate, .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // Configure i2c peripheral const sda_pin = gpio.num(4); diff --git a/examples/raspberrypi/rp2xxx/src/mlx90640.zig b/examples/raspberrypi/rp2xxx/src/mlx90640.zig index 5740759e2..7f2b70dec 100644 --- a/examples/raspberrypi/rp2xxx/src/mlx90640.zig +++ b/examples/raspberrypi/rp2xxx/src/mlx90640.zig @@ -75,7 +75,7 @@ fn init() !void { i2c0.apply(i2c.Config{ .clock_config = rp2xxx.clock_config }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); _ = pin_config.apply(); std.log.info("Hello from mlx90640", .{}); diff --git a/examples/raspberrypi/rp2xxx/src/net/irq.zig b/examples/raspberrypi/rp2xxx/src/net/irq.zig index ecc135764..d2ad0cb77 100644 --- a/examples/raspberrypi/rp2xxx/src/net/irq.zig +++ b/examples/raspberrypi/rp2xxx/src/net/irq.zig @@ -55,7 +55,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // Enable gpio interrupt callback microzig.interrupt.enable(.IO_IRQ_BANK0); diff --git a/examples/raspberrypi/rp2xxx/src/net/pong.zig b/examples/raspberrypi/rp2xxx/src/net/pong.zig index b360bd2df..a734073e6 100644 --- a/examples/raspberrypi/rp2xxx/src/net/pong.zig +++ b/examples/raspberrypi/rp2xxx/src/net/pong.zig @@ -26,7 +26,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // init cyw43 var wifi_driver: drivers.WiFi = .{}; diff --git a/examples/raspberrypi/rp2xxx/src/net/scan.zig b/examples/raspberrypi/rp2xxx/src/net/scan.zig index 9a6433310..c4eb52582 100644 --- a/examples/raspberrypi/rp2xxx/src/net/scan.zig +++ b/examples/raspberrypi/rp2xxx/src/net/scan.zig @@ -25,7 +25,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // init cyw43 var wifi_driver: drivers.WiFi = .{}; diff --git a/examples/raspberrypi/rp2xxx/src/net/tcp_client.zig b/examples/raspberrypi/rp2xxx/src/net/tcp_client.zig index eebc006e4..971400b47 100644 --- a/examples/raspberrypi/rp2xxx/src/net/tcp_client.zig +++ b/examples/raspberrypi/rp2xxx/src/net/tcp_client.zig @@ -26,7 +26,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // init cyw43 var wifi_driver: drivers.WiFi = .{}; diff --git a/examples/raspberrypi/rp2xxx/src/net/tcp_server.zig b/examples/raspberrypi/rp2xxx/src/net/tcp_server.zig index 8fcfa396b..0fab25655 100644 --- a/examples/raspberrypi/rp2xxx/src/net/tcp_server.zig +++ b/examples/raspberrypi/rp2xxx/src/net/tcp_server.zig @@ -26,7 +26,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // init cyw43 var wifi_driver: drivers.WiFi = .{}; diff --git a/examples/raspberrypi/rp2xxx/src/net/udp.zig b/examples/raspberrypi/rp2xxx/src/net/udp.zig index 25ff47736..8b061e8fc 100644 --- a/examples/raspberrypi/rp2xxx/src/net/udp.zig +++ b/examples/raspberrypi/rp2xxx/src/net/udp.zig @@ -26,7 +26,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // init cyw43 var wifi_driver: drivers.WiFi = .{}; diff --git a/examples/raspberrypi/rp2xxx/src/rp2040_only/flash_program.zig b/examples/raspberrypi/rp2xxx/src/rp2040_only/flash_program.zig index 50ddbb611..b84cdb006 100644 --- a/examples/raspberrypi/rp2xxx/src/rp2040_only/flash_program.zig +++ b/examples/raspberrypi/rp2xxx/src/rp2040_only/flash_program.zig @@ -31,7 +31,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); led.set_function(.sio); led.set_direction(.out); diff --git a/examples/raspberrypi/rp2xxx/src/rp2040_only/i2c_slave.zig b/examples/raspberrypi/rp2xxx/src/rp2040_only/i2c_slave.zig index 4f5f998b0..17c2dcf0c 100644 --- a/examples/raspberrypi/rp2xxx/src/rp2040_only/i2c_slave.zig +++ b/examples/raspberrypi/rp2xxx/src/rp2040_only/i2c_slave.zig @@ -36,7 +36,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); _ = pin_config.apply(); diff --git a/examples/raspberrypi/rp2xxx/src/rp2040_only/random.zig b/examples/raspberrypi/rp2xxx/src/rp2040_only/random.zig index 32bf16a68..d2c43baf6 100644 --- a/examples/raspberrypi/rp2xxx/src/rp2040_only/random.zig +++ b/examples/raspberrypi/rp2xxx/src/rp2040_only/random.zig @@ -30,7 +30,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); led.set_function(.sio); led.set_direction(.out); diff --git a/examples/raspberrypi/rp2xxx/src/rp2350_only/always_on_timer.zig b/examples/raspberrypi/rp2xxx/src/rp2350_only/always_on_timer.zig index 2980b3582..65d920f16 100644 --- a/examples/raspberrypi/rp2xxx/src/rp2350_only/always_on_timer.zig +++ b/examples/raspberrypi/rp2xxx/src/rp2350_only/always_on_timer.zig @@ -31,7 +31,7 @@ pub fn main() !void { .clock_config = hal.clock_config, }); - uart.init_logger(uart0); + uart.init_logger(uart0, &.{}); std.log.info("Hello, World!", .{}); diff --git a/examples/raspberrypi/rp2xxx/src/rp2350_only/random_data.zig b/examples/raspberrypi/rp2xxx/src/rp2350_only/random_data.zig index 80142241d..a8f528313 100644 --- a/examples/raspberrypi/rp2xxx/src/rp2350_only/random_data.zig +++ b/examples/raspberrypi/rp2xxx/src/rp2350_only/random_data.zig @@ -27,7 +27,7 @@ pub fn main() !void { .clock_config = hal.clock_config, }); - uart.init_logger(uart0); + uart.init_logger(uart0, &.{}); std.log.info("Hello, World!", .{}); diff --git a/examples/raspberrypi/rp2xxx/src/spi_loopback_dma.zig b/examples/raspberrypi/rp2xxx/src/spi_loopback_dma.zig index 9c5de5a6a..a94e83147 100644 --- a/examples/raspberrypi/rp2xxx/src/spi_loopback_dma.zig +++ b/examples/raspberrypi/rp2xxx/src/spi_loopback_dma.zig @@ -24,7 +24,7 @@ pub fn main() !void { .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); var in_buf: [BUF_LEN]u8 = .{ 'h', 'e', 'y', ' ', 'y', 'o', 'u', '!' } ** (BUF_LEN / 8); var out_buf = std.mem.zeroes([BUF_LEN]u8); diff --git a/examples/raspberrypi/rp2xxx/src/spi_slave.zig b/examples/raspberrypi/rp2xxx/src/spi_slave.zig index 3f8a79458..be066a457 100644 --- a/examples/raspberrypi/rp2xxx/src/spi_slave.zig +++ b/examples/raspberrypi/rp2xxx/src/spi_slave.zig @@ -39,7 +39,7 @@ pub fn main() !void { .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); std.log.info("Setting SPI as slave device", .{}); spi.set_slave(true); diff --git a/examples/raspberrypi/rp2xxx/src/st7789_lcd.zig b/examples/raspberrypi/rp2xxx/src/st7789_lcd.zig index 82ad22711..c06fcad7f 100644 --- a/examples/raspberrypi/rp2xxx/src/st7789_lcd.zig +++ b/examples/raspberrypi/rp2xxx/src/st7789_lcd.zig @@ -31,7 +31,6 @@ const DISPLAY_BACKLIGHT_PIN = 13; const uart = rp2xxx.uart.instance.num(0); const uart_tx_pin = gpio.num(0); -const uart_rx_pin = gpio.num(1); pub const microzig_options = microzig.Options{ .log_level = .debug, @@ -81,13 +80,11 @@ fn flush_display_buffer(display: *DisplayDriver, buffer: []DisplayDriver.Color) } pub fn main() !void { - inline for (&.{ uart_tx_pin, uart_rx_pin }) |pin| { - pin.set_function(.uart); - } - + uart_tx_pin.set_function(.uart); uart.apply(.{ .clock_config = rp2xxx.clock_config, }); + rp2xxx.uart.init_logger(uart, &.{}); led.set_function(.pwm); led.set_direction(.out); diff --git a/examples/raspberrypi/rp2xxx/src/stepper_driver.zig b/examples/raspberrypi/rp2xxx/src/stepper_driver.zig index c47fc6376..a0790cc34 100644 --- a/examples/raspberrypi/rp2xxx/src/stepper_driver.zig +++ b/examples/raspberrypi/rp2xxx/src/stepper_driver.zig @@ -26,7 +26,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // Setup all pins for the stepper driver var pins: struct { diff --git a/examples/raspberrypi/rp2xxx/src/stepper_driver_dumb.zig b/examples/raspberrypi/rp2xxx/src/stepper_driver_dumb.zig index 761b8e941..4a72c8225 100644 --- a/examples/raspberrypi/rp2xxx/src/stepper_driver_dumb.zig +++ b/examples/raspberrypi/rp2xxx/src/stepper_driver_dumb.zig @@ -26,7 +26,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); // Setup all pins for the stepper driver var pins: struct { diff --git a/examples/raspberrypi/rp2xxx/src/system_timer.zig b/examples/raspberrypi/rp2xxx/src/system_timer.zig index edea29444..197ad0bf4 100644 --- a/examples/raspberrypi/rp2xxx/src/system_timer.zig +++ b/examples/raspberrypi/rp2xxx/src/system_timer.zig @@ -44,7 +44,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); led.set_function(.sio); led.set_direction(.out); diff --git a/examples/raspberrypi/rp2xxx/src/uart_log.zig b/examples/raspberrypi/rp2xxx/src/uart_log.zig index 48f1fa85d..b1c6b7324 100644 --- a/examples/raspberrypi/rp2xxx/src/uart_log.zig +++ b/examples/raspberrypi/rp2xxx/src/uart_log.zig @@ -14,7 +14,7 @@ pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noretu while (true) {} } -pub const microzig_options = microzig.Options{ +pub const microzig_options: microzig.Options = .{ .log_level = .debug, .logFn = rp2xxx.uart.log, }; @@ -30,7 +30,7 @@ pub fn main() !void { .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); var i: u32 = 0; while (true) : (i += 1) { diff --git a/examples/raspberrypi/rp2xxx/src/uart_reader_writer.zig b/examples/raspberrypi/rp2xxx/src/uart_reader_writer.zig new file mode 100644 index 000000000..0ddf33c2e --- /dev/null +++ b/examples/raspberrypi/rp2xxx/src/uart_reader_writer.zig @@ -0,0 +1,55 @@ +const std = @import("std"); +const microzig = @import("microzig"); +const rp2xxx = microzig.hal; +const time = rp2xxx.time; +const gpio = rp2xxx.gpio; + +const led = gpio.num(25); +const uart = rp2xxx.uart.instance.num(0); +const uart_tx_pin = gpio.num(0); +const uart_rx_pin = gpio.num(1); + +pub const microzig_options: microzig.Options = .{ + .logFn = rp2xxx.uart.log, +}; + +pub fn main() !void { + led.set_function(.sio); + led.set_direction(.out); + led.put(1); + + uart_tx_pin.set_function(.uart); + uart_rx_pin.set_function(.uart); + + uart.apply(.{ + .clock_config = rp2xxx.clock_config, + .baud_rate = 115_200, + }); + rp2xxx.uart.init_logger(uart, &.{}); + + var read_buf: [32]u8 = undefined; + var reader = uart.reader(&read_buf, .no_deadline); + + var write_buf: [32]u8 = undefined; + var writer = uart.writer(&write_buf, .no_deadline); + + var i: u32 = 0; + while (true) : (i += 1) { + // Read one line + const secret_text = (try reader.interface.takeDelimiter('\r')) orelse continue; + + // Split the line into words + var words = std.mem.splitScalar(u8, std.mem.trim(u8, secret_text, &.{ '\r', '\n' }), ' '); + + // Output each word + while (words.next()) |word| { + try writer.interface.print("Word: {s}\n\r", .{word}); + } + + // Don't forget to flush + try writer.interface.flush(); + + // Toggle the led after each written line + led.toggle(); + } +} diff --git a/examples/raspberrypi/rp2xxx/src/usb_cdc.zig b/examples/raspberrypi/rp2xxx/src/usb_cdc.zig index eca1a1c65..fc212c3a0 100644 --- a/examples/raspberrypi/rp2xxx/src/usb_cdc.zig +++ b/examples/raspberrypi/rp2xxx/src/usb_cdc.zig @@ -60,7 +60,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); pins.led.put(1); diff --git a/examples/raspberrypi/rp2xxx/src/usb_hid.zig b/examples/raspberrypi/rp2xxx/src/usb_hid.zig index 39eda917d..4f97adb26 100644 --- a/examples/raspberrypi/rp2xxx/src/usb_hid.zig +++ b/examples/raspberrypi/rp2xxx/src/usb_hid.zig @@ -152,7 +152,7 @@ pub fn main() !void { uart.apply(.{ .clock_config = rp2xxx.clock_config, }); - rp2xxx.uart.init_logger(uart); + rp2xxx.uart.init_logger(uart, &.{}); pins.led.put(1); diff --git a/modules/foundation-libc/build.zig b/modules/foundation-libc/build.zig index 47d6410ea..1647b1475 100644 --- a/modules/foundation-libc/build.zig +++ b/modules/foundation-libc/build.zig @@ -13,17 +13,19 @@ pub fn build(b: *std.Build) void { b.getInstallStep().dependOn(validation_step); } + const mod = b.createModule(.{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/libc.zig"), + .single_threaded = single_threaded, + }); + const libc = b.addLibrary(.{ .name = "foundation", - .root_module = b.createModule(.{ - .target = target, - .optimize = optimize, - .root_source_file = b.path("src/libc.zig"), - .single_threaded = single_threaded, - }), + .root_module = mod, }); - libc.addIncludePath(b.path("include")); + mod.addIncludePath(b.path("include")); for (header_files) |header_name| libc.installHeader( b.path(b.fmt("include/{s}", .{header_name})), diff --git a/modules/freertos/build.zig b/modules/freertos/build.zig index acf03f589..35ccd2662 100644 --- a/modules/freertos/build.zig +++ b/modules/freertos/build.zig @@ -145,7 +145,7 @@ fn addPicoSDKIncludeDirs( .PICO_SDK_VERSION_REVISION = "0", }); - _ = wf.addCopyFile(cmake_version_file.getOutput(), "picosdk_generated/pico/version.h"); + _ = wf.addCopyFile(cmake_version_file.getOutputFile(), "picosdk_generated/pico/version.h"); // Generate required config_autogen.h (this support custom #include directives) _ = wf.addCopyFile(pico_root.path(b, "bazel/include/pico/config_autogen.h"), "picosdk_generated/pico/config_autogen.h"); @@ -186,16 +186,16 @@ fn addAllIncludeDirs( subdir: []const u8, ) void { const allocator = b.allocator; - const full = std.fs.path.join(allocator, &.{ base.getPath(b), subdir }) catch @panic("join"); + const full = std.Io.Dir.path.join(allocator, &.{ base.getPath(b), subdir }) catch @panic("join"); defer allocator.free(full); - var dir = std.fs.openDirAbsolute(full, .{ .iterate = true }) catch return; - defer dir.close(); + var dir = std.Io.Dir.openDirAbsolute(b.graph.io, full, .{ .iterate = true }) catch return; + defer dir.close(b.graph.io); var walker = dir.walk(allocator) catch @panic("walk"); defer walker.deinit(); - while (walker.next() catch @panic("next")) |e| { + while (walker.next(b.graph.io) catch @panic("next")) |e| { if (e.kind != .directory) continue; if (!std.mem.eql(u8, std.fs.path.basename(e.path), "include")) continue; diff --git a/modules/lwip/build.zig b/modules/lwip/build.zig index 54ad40161..425b0c0c2 100644 --- a/modules/lwip/build.zig +++ b/modules/lwip/build.zig @@ -22,15 +22,15 @@ pub fn build(b: *std.Build) void { .linkage = .static, }); - lwip.addCSourceFiles(.{ + lwip.root_module.addCSourceFiles(.{ .root = upstream.path("src"), .files = &files, .flags = &flags, }); - lwip.addIncludePath(upstream.path("src/include")); + lwip.root_module.addIncludePath(upstream.path("src/include")); if (maybe_include_dir) |include_dir| { - lwip.addIncludePath(include_dir); + lwip.root_module.addIncludePath(include_dir); lwip.installHeadersDirectory(include_dir, "", .{}); } diff --git a/modules/rtt/src/rtt.zig b/modules/rtt/src/rtt.zig index 9956f6141..653399177 100644 --- a/modules/rtt/src/rtt.zig +++ b/modules/rtt/src/rtt.zig @@ -91,7 +91,7 @@ pub const channel = struct { } fn mode(self: *Self) Mode { - return std.meta.intToEnum(Mode, self.flags & 3) catch unreachable; + return std.enums.fromInt(Mode, self.flags & 3) orelse unreachable; } fn set_mode(self: *Self, mode_: Mode) void { @@ -422,39 +422,26 @@ pub const channel = struct { /// /// Fields follow the naming convention "up_buffer_N" for up channels, and "down_buffer_N" for down channels. fn BuildBufferStorageType(comptime up_channels: []const channel.Config, comptime down_channels: []const channel.Config) type { - const fields: []const std.builtin.Type.StructField = comptime v: { - var fields_temp: [up_channels.len + down_channels.len]std.builtin.Type.StructField = undefined; - for (up_channels, 0..) |up_cfg, idx| { - const buffer_type = [up_cfg.buffer_size]u8; - fields_temp[idx] = .{ - .name = std.fmt.comptimePrint("up_buffer_{d}", .{idx}), - .type = buffer_type, - .is_comptime = false, - .alignment = @alignOf(buffer_type), - .default_value_ptr = null, - }; - } - for (down_channels, 0..) |down_cfg, idx| { - const buffer_type = [down_cfg.buffer_size]u8; - fields_temp[up_channels.len + idx] = .{ - .name = std.fmt.comptimePrint("down_buffer_{d}", .{idx}), - .type = buffer_type, - .is_comptime = false, - .alignment = @alignOf(buffer_type), - .default_value_ptr = null, - }; - } - break :v &fields_temp; - }; + const len = up_channels.len + down_channels.len; + var field_names: [len][]const u8 = undefined; + var field_types: [len]type = undefined; + var field_attrs: [len]std.builtin.Type.StructField.Attributes = undefined; + + for (up_channels, 0..) |up_cfg, idx| { + const buffer_type = [up_cfg.buffer_size]u8; + field_names[idx] = std.fmt.comptimePrint("up_buffer_{d}", .{idx}); + field_types[idx] = buffer_type; + field_attrs[idx] = .{}; + } + + for (down_channels, 0..) |down_cfg, idx| { + const buffer_type = [down_cfg.buffer_size]u8; + field_names[idx + up_channels.len] = std.fmt.comptimePrint("down_buffer_{d}", .{idx}); + field_types[idx + up_channels.len] = buffer_type; + field_attrs[idx + up_channels.len] = .{}; + } - return @Type(.{ - .@"struct" = .{ - .layout = .@"extern", - .fields = fields, - .decls = &[_]std.builtin.Type.Declaration{}, - .is_tuple = false, - }, - }); + return @Struct(.@"extern", null, &field_names, &field_types, &field_attrs); } /// Creates a control block struct for the given channel configs. Buffer storage is also contained within this struct, although diff --git a/port/espressif/esp/ld/esp32_c3/direct_boot_sections.ld b/port/espressif/esp/ld/esp32_c3/direct_boot_sections.ld index 770c6f2ba..db5299afd 100644 --- a/port/espressif/esp/ld/esp32_c3/direct_boot_sections.ld +++ b/port/espressif/esp/ld/esp32_c3/direct_boot_sections.ld @@ -83,4 +83,14 @@ SECTIONS microzig_data_load_start = ORIGIN(DROM) + _irom_size + _drom_size; PROVIDE(__global_pointer$ = microzig_data_start + 0x800); + + .eh_frame_hdr 0 (INFO) : + { + KEEP(*(.eh_frame_hdr)) + } + + .eh_frame 0 (INFO) : + { + KEEP(*(.eh_frame)) + } } diff --git a/port/espressif/esp/ld/esp32_c3/flashless_sections.ld b/port/espressif/esp/ld/esp32_c3/flashless_sections.ld index 833eaf141..e31d637c3 100644 --- a/port/espressif/esp/ld/esp32_c3/flashless_sections.ld +++ b/port/espressif/esp/ld/esp32_c3/flashless_sections.ld @@ -58,4 +58,14 @@ SECTIONS } > DRAM PROVIDE(__global_pointer$ = microzig_data_start + 0x800); + + .eh_frame_hdr 0 (INFO) : + { + KEEP(*(.eh_frame_hdr)) + } + + .eh_frame 0 (INFO) : + { + KEEP(*(.eh_frame)) + } } diff --git a/port/espressif/esp/ld/esp32_c3/image_boot_sections.ld b/port/espressif/esp/ld/esp32_c3/image_boot_sections.ld index bcc305a03..7c2de5816 100644 --- a/port/espressif/esp/ld/esp32_c3/image_boot_sections.ld +++ b/port/espressif/esp/ld/esp32_c3/image_boot_sections.ld @@ -115,4 +115,14 @@ SECTIONS } > DRAM PROVIDE(__global_pointer$ = microzig_data_start + 0x800); + + .eh_frame_hdr 0 (INFO) : + { + KEEP(*(.eh_frame_hdr)) + } + + .eh_frame 0 (INFO) : + { + KEEP(*(.eh_frame)) + } } diff --git a/port/espressif/esp/src/hal/efuse.zig b/port/espressif/esp/src/hal/efuse.zig index bf302fe62..1f48812e2 100644 --- a/port/espressif/esp/src/hal/efuse.zig +++ b/port/espressif/esp/src/hal/efuse.zig @@ -11,8 +11,8 @@ pub fn read_mac() [6]u8 { return mac; } -pub fn read(comptime what: Item) @Type(.{ .int = .{ .bits = what.bit_length, .signedness = .unsigned } }) { - const OutputType = @Type(.{ .int = .{ .bits = what.bit_length, .signedness = .unsigned } }); +pub fn read(comptime what: Item) @Int(.unsigned, what.bit_length) { + const OutputType = @Int(.unsigned, what.bit_length); const block_byte_offsets = [_]u8{ 0x2C, 0x44, 0x5C }; if (what.block >= block_byte_offsets.len) @compileError("invalid block"); diff --git a/port/espressif/esp/src/hal/radio/wifi.zig b/port/espressif/esp/src/hal/radio/wifi.zig index 3a1f1eeef..476d3604a 100644 --- a/port/espressif/esp/src/hal/radio/wifi.zig +++ b/port/espressif/esp/src/hal/radio/wifi.zig @@ -1248,7 +1248,7 @@ pub const c_patched = struct { sae_h2e_identifier: [32]u8 = std.mem.zeroes([32]u8), // NOTE: maybe a little more imagination - pub const Packed1 = packed struct { + pub const Packed1 = packed struct(u32) { rm_enabled: bool, btm_enabled: bool, mbo_enabled: bool, @@ -1258,7 +1258,7 @@ pub const c_patched = struct { reserved: u26, }; - pub const Packed2 = packed struct { + pub const Packed2 = packed struct(u32) { he_dcm_set: u1, he_dcm_max_constellation_tx: u2, he_dcm_max_constellation_rx: u2, diff --git a/port/espressif/esp/src/hal/rtos.zig b/port/espressif/esp/src/hal/rtos.zig index e21f9e08a..1d58708c0 100644 --- a/port/espressif/esp/src/hal/rtos.zig +++ b/port/espressif/esp/src/hal/rtos.zig @@ -76,10 +76,7 @@ pub const Options = struct { ready_queue_force_no_buckets: bool = false, }; -pub const Priority = enum(@Type(.{ .int = .{ - .bits = rtos_options.priority_bits, - .signedness = .unsigned, -} })) { +pub const Priority = enum(@Int(.unsigned, rtos_options.priority_bits)) { idle = 0, lowest = 1, _, diff --git a/port/espressif/esp/src/hal/usb_serial_jtag.zig b/port/espressif/esp/src/hal/usb_serial_jtag.zig index 8cce3a36f..0c51f50db 100644 --- a/port/espressif/esp/src/hal/usb_serial_jtag.zig +++ b/port/espressif/esp/src/hal/usb_serial_jtag.zig @@ -20,56 +20,84 @@ pub fn tx_fifo_write(byte: u8) void { }); } +pub fn writer(buffer: []u8) std.Io.Writer { + return .{ + .buffer = buffer, + .vtable = &.{ + .drain = struct { + fn drain(w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize { + const buffered = w.buffered(); + if (buffered.len > 0) { + write_buf(buffered) catch return error.WriteFailed; + _ = w.consumeAll(); + } + + var n: usize = 0; + for (data[0 .. data.len - 1]) |buf| { + if (buf.len == 0) continue; + write_buf(buffered) catch return error.WriteFailed; + n += buf.len; + } + + const splat_buf = data[data.len - 1]; + if (splat > 0 and splat_buf.len > 0) { + for (0..splat) |_| { + write_buf(splat_buf) catch return error.WriteFailed; + n += splat_buf.len; + } + } + + return n; + } + + var timed_out: bool = false; + + fn write_buf(buf: []const u8) error{Timeout}!void { + if (tx_fifo_full()) { + if (timed_out) { + return error.Timeout; + } + + try wait_for_flush(); + } else { + timed_out = false; + } + + for (buf) |byte| { + if (tx_fifo_full()) { + tx_fifo_flush(); + try wait_for_flush(); + } + + tx_fifo_write(byte); + } + + tx_fifo_flush(); + } + + fn wait_for_flush() error{Timeout}!void { + var timeout: usize = 50_000; + while (tx_fifo_full()) { + if (timeout == 0) { + timed_out = true; + return error.Timeout; + } + timeout -= 1; + } + } + }.drain, + }, + }; +} + /// USB Serial JTAG logger. To use this, add `.logFn = logger.log` to your /// `microzig_options`. pub const logger = struct { pub const Error = error{ Timeout, }; - pub const Writer = std.io.GenericWriter(void, Error, generic_writer_fn); - - const writer: Writer = .{ .context = {} }; - - var timed_out: bool = false; - - fn generic_writer_fn(_: void, buffer: []const u8) Error!usize { - const cs = microzig.interrupt.enter_critical_section(); - defer cs.leave(); - - if (tx_fifo_full()) { - if (timed_out) { - return error.Timeout; - } - - try wait_for_flush(); - } else { - timed_out = false; - } - for (buffer) |byte| { - if (tx_fifo_full()) { - tx_fifo_flush(); - try wait_for_flush(); - } - - tx_fifo_write(byte); - } - - tx_fifo_flush(); - - return buffer.len; - } - - fn wait_for_flush() error{Timeout}!void { - var timeout: usize = 50_000; - while (tx_fifo_full()) { - if (timeout == 0) { - timed_out = true; - return error.Timeout; - } - timeout -= 1; - } - } + var log_writer = writer(&.{}); pub fn log( comptime level: std.log.Level, @@ -85,6 +113,9 @@ pub const logger = struct { // TODO: add timestamp to log message - writer.print(prefix ++ format ++ "\r\n", args) catch {}; + const cs = microzig.interrupt.enter_critical_section(); + defer cs.leave(); + + log_writer.print(prefix ++ format ++ "\r\n", args) catch {}; } }; diff --git a/port/espressif/esp/src/tools/cat.zig b/port/espressif/esp/src/tools/cat.zig index 18dc55df7..24f853446 100644 --- a/port/espressif/esp/src/tools/cat.zig +++ b/port/espressif/esp/src/tools/cat.zig @@ -1,29 +1,23 @@ const std = @import("std"); -pub fn main() !void { - var debug_allocator: std.heap.DebugAllocator(.{}) = .init; - defer _ = debug_allocator.deinit(); - - const allocator = debug_allocator.allocator(); - - const args = try std.process.argsAlloc(allocator); - defer std.process.argsFree(allocator, args); - +pub fn main(init: std.process.Init) !void { + const io = init.io; + const allocator = init.arena.allocator(); + const args = try init.minimal.args.toSlice(allocator); if (args.len < 2) { std.log.err("usage: ./cat [src_file ...] dst_file", .{}); return error.InvalidArguments; } - const output_file = try std.fs.createFileAbsolute(args[args.len - 1], .{}); - defer output_file.close(); + const output_file = try std.Io.Dir.createFileAbsolute(io, args[args.len - 1], .{}); + defer output_file.close(io); + var output_file_buf: [4096]u8 = undefined; + var output_file_writer = output_file.writer(io, &output_file_buf); for (args[1 .. args.len - 1]) |arg| { - const file = try std.fs.openFileAbsolute(arg, .{ .mode = .read_only }); - defer file.close(); - - const data = try file.readToEndAlloc(allocator, 1_000_000); - defer allocator.free(data); - - try output_file.writeAll(data); + const file = try std.Io.Dir.openFileAbsolute(io, arg, .{}); + defer file.close(io); + var file_reader = file.reader(io, &.{}); + _ = try output_file_writer.interface.sendFileAll(&file_reader, .unlimited); } } diff --git a/port/raspberrypi/rp2xxx/build.zig b/port/raspberrypi/rp2xxx/build.zig index c966e7a0f..07e25f47f 100644 --- a/port/raspberrypi/rp2xxx/build.zig +++ b/port/raspberrypi/rp2xxx/build.zig @@ -299,7 +299,7 @@ pub fn build(b: *std.Build) !void { }}, }), }); - unit_tests.addIncludePath(b.path("src/hal/pio/assembler")); + unit_tests.root_module.addIncludePath(b.path("src/hal/pio/assembler")); const unit_tests_run = b.addRunArtifact(unit_tests); const test_step = b.step("test", "Run platform agnostic unit tests"); @@ -332,7 +332,7 @@ fn get_bootrom(b: *std.Build, target: *const microzig.Target, rom: BootROM) std. //rom_exe.linkage = .static; rom_exe.build_id = .none; rom_exe.setLinkerScript(b.path(b.fmt("src/bootroms/{s}/shared/stage2.ld", .{target.chip.name}))); - rom_exe.addAssemblyFile(b.path(b.fmt("src/bootroms/{s}/{s}.S", .{ target.chip.name, @tagName(rom) }))); + rom_exe.root_module.addAssemblyFile(b.path(b.fmt("src/bootroms/{s}/{s}.S", .{ target.chip.name, @tagName(rom) }))); rom_exe.entry = .{ .symbol_name = "_stage2_boot" }; const rom_objcopy = b.addObjCopy(rom_exe.getEmittedBin(), .{ diff --git a/port/raspberrypi/rp2xxx/src/hal/bootmeta.zig b/port/raspberrypi/rp2xxx/src/hal/bootmeta.zig index 4c8f2cafc..24e67eaa9 100644 --- a/port/raspberrypi/rp2xxx/src/hal/bootmeta.zig +++ b/port/raspberrypi/rp2xxx/src/hal/bootmeta.zig @@ -60,12 +60,12 @@ pub fn Block(Items: type) type { }; } -pub const ImageDef = packed struct { +pub const ImageDef = packed struct(u32) { item_type: u8 = 0x42, block_size: u8 = 0x01, image_type_flags: ImageTypeFlags, - pub const ImageTypeFlags = packed struct { + pub const ImageTypeFlags = packed struct(u16) { image_type: ImageType, exe_security: ExeSecurity, reserved0: u2 = 0, @@ -101,7 +101,7 @@ pub const ImageDef = packed struct { pub fn EntryPoint(with_stack_limit: bool) type { if (with_stack_limit) { return extern struct { - header: packed struct { + header: packed struct(u32) { item_type: u8 = 0x44, block_size: u8 = 0x04, padding: u16 = 0, @@ -112,7 +112,7 @@ pub fn EntryPoint(with_stack_limit: bool) type { }; } else { return extern struct { - header: packed struct { + header: packed struct(u32) { item_type: u8 = 0x44, block_size: u8 = 0x03, padding: u16 = 0, diff --git a/port/raspberrypi/rp2xxx/src/hal/pins.zig b/port/raspberrypi/rp2xxx/src/hal/pins.zig index e1a82c0e8..d71736466 100644 --- a/port/raspberrypi/rp2xxx/src/hal/pins.zig +++ b/port/raspberrypi/rp2xxx/src/hal/pins.zig @@ -767,44 +767,39 @@ pub const GlobalConfiguration = struct { } pub fn PinsType(self: GlobalConfiguration) type { - var fields: []const StructField = &.{}; + const len = blk: { + var len: usize = 0; + for (@typeInfo(GlobalConfiguration).@"struct".fields) |field| { + if (@field(self, field.name)) |_| { + len += 1; + } + } + + break :blk len; + }; + + var field_names: [len][]const u8 = undefined; + var field_types: [len]type = undefined; + var field_attrs: [len]std.builtin.Type.StructField.Attributes = undefined; + var idx: usize = 0; for (@typeInfo(GlobalConfiguration).@"struct".fields) |field| { if (@field(self, field.name)) |pin_config| { - var pin_field = StructField{ - .is_comptime = false, - .default_value_ptr = null, - - // initialized below: - .name = undefined, - .type = undefined, - .alignment = undefined, - }; - - pin_field.name = pin_config.name orelse field.name; - if (pin_config.function == .SIO) { - pin_field.type = gpio.Pin; - } else if (pin_config.function.is_pwm()) { - pin_field.type = pwm.Pwm; - } else if (pin_config.function.is_adc()) { - pin_field.type = adc.Input; - } else { + field_types[idx] = if (pin_config.function == .SIO) + gpio.Pin + else if (pin_config.function.is_pwm()) + pwm.Pwm + else if (pin_config.function.is_adc()) + adc.Input + else continue; - } - - pin_field.alignment = @alignOf(field.type); + field_names[idx] = pin_config.name orelse field.name; + field_attrs[idx] = .{}; - fields = fields ++ &[_]StructField{pin_field}; + idx += 1; } } - return @Type(.{ - .@"struct" = .{ - .layout = .auto, - .is_tuple = false, - .fields = fields, - .decls = &.{}, - }, - }); + return @Struct(.auto, null, field_names[0..idx], field_types[0..idx], field_attrs[0..idx]); } /// Populate and return the PinsType struct diff --git a/port/raspberrypi/rp2xxx/src/hal/uart.zig b/port/raspberrypi/rp2xxx/src/hal/uart.zig index fcb96054e..747f2e61a 100644 --- a/port/raspberrypi/rp2xxx/src/hal/uart.zig +++ b/port/raspberrypi/rp2xxx/src/hal/uart.zig @@ -144,20 +144,12 @@ pub const instance = struct { pub const UART = enum(u1) { _, - pub const UART_With_Timeout = struct { - instance: UART, - deadline: mdf.time.Deadline, - }; - - pub const Writer = std.io.GenericWriter(UART_With_Timeout, TransmitError, generic_writer_fn); - pub const Reader = std.io.GenericReader(UART_With_Timeout, ReceiveError, generic_reader_fn); - - pub fn writer(uart: UART, deadline: mdf.time.Deadline) Writer { - return .{ .context = .{ .instance = uart, .deadline = deadline } }; + pub fn writer(uart: UART, buffer: []u8, deadline: mdf.time.Deadline) Writer { + return .init(uart, buffer, deadline); } - pub fn reader(uart: UART, deadline: mdf.time.Deadline) Reader { - return .{ .context = .{ .instance = uart, .deadline = deadline } }; + pub fn reader(uart: UART, buffer: []u8, deadline: mdf.time.Deadline) Reader { + return .init(uart, buffer, deadline); } pub inline fn get_regs(uart: UART) *volatile UartRegs { @@ -316,12 +308,6 @@ pub const UART = enum(u1) { } } - /// Wraps write_blocking() for use as a GenericWriter - fn generic_writer_fn(uart: UART_With_Timeout, buffer: []const u8) TransmitError!usize { - try uart.instance.write_blocking(buffer, uart.deadline); - return buffer.len; - } - // TODO: Will potentially be modified in a future DMA overhaul pub fn dreq_tx(uart: UART) dma.Dreq { return switch (@intFromEnum(uart)) { @@ -414,12 +400,6 @@ pub const UART = enum(u1) { return try uart.read_rx_fifo_with_error_check(); } - /// Wraps read_blocking() for use as a GenericReader - fn generic_reader_fn(uart: UART_With_Timeout, buffer: []u8) ReceiveBlockingError!usize { - try uart.instance.read_blocking(buffer, uart.deadline); - return buffer.len; - } - pub fn set_format( uart: UART, word_bits: WordBits, @@ -490,17 +470,115 @@ pub const UART = enum(u1) { } }; -var uart_logger: ?UART.Writer = null; +pub const Writer = struct { + uart: UART, + deadline: mdf.time.Deadline, + interface: std.Io.Writer, + + pub fn init(uart: UART, buffer: []u8, deadline: mdf.time.Deadline) Writer { + return .{ + .uart = uart, + .deadline = deadline, + .interface = .{ + .vtable = comptime &.{ + .drain = drain, + }, + .buffer = buffer, + }, + }; + } + + fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize { + const writer: *Writer = @alignCast(@fieldParentPtr("interface", io_w)); + + const buffered = io_w.buffered(); + if (buffered.len > 0) { + writer.uart.write_blocking(buffered, writer.deadline) catch unreachable; + _ = io_w.consumeAll(); + } + + var n: usize = 0; + for (data[0 .. data.len - 1]) |buf| { + if (buf.len == 0) continue; + writer.uart.write_blocking(buffered, writer.deadline) catch unreachable; + n += buf.len; + } + + const splat_buf = data[data.len - 1]; + if (splat > 0 and splat_buf.len > 0) { + for (0..splat) |_| { + writer.uart.write_blocking(splat_buf, writer.deadline) catch unreachable; + n += splat_buf.len; + } + } + + return n; + } +}; + +pub const Reader = struct { + uart: UART, + deadline: mdf.time.Deadline, + err: ?ReceiveError = null, + error_states: ErrorStates = .{}, + interface: std.Io.Reader, + + pub fn init(uart: UART, buffer: []u8, deadline: mdf.time.Deadline) Reader { + return .{ + .uart = uart, + .deadline = deadline, + .interface = .{ + .vtable = comptime &.{ + .stream = stream, + }, + .buffer = buffer, + .seek = 0, + .end = 0, + }, + }; + } + + fn stream(io_r: *std.Io.Reader, io_w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize { + const reader: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); + + // Clear errors from previous call (if any). + reader.error_states = .{}; + reader.err = null; + + // If the deadline expired, return error.EndOfStream. + reader.deadline.check(time.get_time_since_boot()) catch { + reader.err = error.Timeout; + return error.ReadFailed; + }; + + const dest = limit.slice(try io_w.writableSliceGreedy(1)); + + var i: usize = 0; + while (i < dest.len and reader.uart.is_readable()) : (i += 1) { + dest[i] = reader.uart.read_rx_fifo_with_error_check() catch |err| { + reader.err = err; + reader.error_states = reader.uart.get_errors(); + reader.uart.clear_errors(); + return error.ReadFailed; + }; + io_w.advance(1); + } + + return i; + } +}; + +var uart_logger: ?Writer = null; /// Set a specific uart instance to be used for logging. /// /// Allows system logging over uart via: -/// pub const microzig_options = .{ +/// pub const microzig_options: microzig.Options = .{ /// .logFn = hal.uart.log, /// }; -pub fn init_logger(uart: UART) void { - uart_logger = uart.writer(.no_deadline); - uart_logger.?.writeAll("\r\n================ STARTING NEW LOGGER ================\r\n") catch {}; +pub fn init_logger(uart: UART, buffer: []u8) void { + uart_logger = .init(uart, buffer, .no_deadline); + uart_logger.?.interface.writeAll("\r\n================ STARTING NEW LOGGER ================\r\n") catch {}; } /// Disables logging via the uart instance. @@ -520,12 +598,13 @@ pub fn log( else => " (" ++ @tagName(scope) ++ "): ", }; - if (uart_logger) |uart| { + if (uart_logger) |*uart| { const current_time = time.get_time_since_boot(); const seconds = current_time.to_us() / std.time.us_per_s; const microseconds = current_time.to_us() % std.time.us_per_s; - uart.print(prefix ++ format ++ "\r\n", .{ seconds, microseconds } ++ args) catch {}; + uart.interface.print(prefix ++ format ++ "\r\n", .{ seconds, microseconds } ++ args) catch {}; + uart.interface.flush() catch {}; } } diff --git a/port/stmicro/stm32/build.zig b/port/stmicro/stm32/build.zig index c3c8cece0..00f23892a 100644 --- a/port/stmicro/stm32/build.zig +++ b/port/stmicro/stm32/build.zig @@ -101,7 +101,7 @@ pub fn build(b: *std.Build) !void { generate_exe.root_module.addImport("regz", regz); const generate_run = b.addRunArtifact(generate_exe); - generate_run.max_stdio_size = std.math.maxInt(usize); + generate_run.stdio_limit = .unlimited; generate_run.addFileArg(stm32_data_generated.path(".")); const generate_step = b.step("generate", "Generate chips file 'src/Chips.zig'"); diff --git a/port/stmicro/stm32/src/hals/common/i2c_v2.zig b/port/stmicro/stm32/src/hals/common/i2c_v2.zig index 79951c469..8ea27cb0d 100644 --- a/port/stmicro/stm32/src/hals/common/i2c_v2.zig +++ b/port/stmicro/stm32/src/hals/common/i2c_v2.zig @@ -30,8 +30,7 @@ const TimingSpec_Standard = .{ .t_min_af = 0.05, }; -const TIMINGR = blk: for (@typeInfo(I2C_Peripherals).@"struct".fields) |field| -{ +const TIMINGR = blk: for (@typeInfo(I2C_Peripherals).@"struct".fields) |field| { if (std.mem.eql(u8, "TIMINGR", field.name)) { break :blk field.type; } diff --git a/port/texasinstruments/msp430/build.zig b/port/texasinstruments/msp430/build.zig index 1d2dad19c..dea3c9a31 100644 --- a/port/texasinstruments/msp430/build.zig +++ b/port/texasinstruments/msp430/build.zig @@ -58,7 +58,6 @@ pub fn build(b: *std.Build) void { generate_exe.root_module.addImport("regz", regz); const generate_run = b.addRunArtifact(generate_exe); - generate_run.max_stdio_size = std.math.maxInt(usize); generate_run.addFileArg(targetdb); const generate_step = b.step("generate", "Generate chips file 'src/Chips.zig'"); diff --git a/port/texasinstruments/tm4c/build.zig b/port/texasinstruments/tm4c/build.zig index ff43d89bb..006b3d538 100644 --- a/port/texasinstruments/tm4c/build.zig +++ b/port/texasinstruments/tm4c/build.zig @@ -50,7 +50,7 @@ pub fn build(b: *std.Build) void { generate_exe.root_module.addImport("regz", regz); const generate_run = b.addRunArtifact(generate_exe); - generate_run.max_stdio_size = std.math.maxInt(usize); + generate_run.stdio_limit = .unlimited; generate_run.addFileArg(targetdb); const generate_step = b.step("generate", "Generate chips file 'src/Chips.zig'"); diff --git a/sim/aviron/build.zig b/sim/aviron/build.zig index 388b4bfa5..c9caed0b7 100644 --- a/sim/aviron/build.zig +++ b/sim/aviron/build.zig @@ -123,6 +123,7 @@ fn add_test_suite( args_module: *Build.Module, aviron_module: *Build.Module, ) !void { + const io = b.graph.io; const unit_tests = b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), @@ -161,13 +162,13 @@ fn add_test_suite( // Scan the testsuite directory for files. Based on the extension, either load or compile them. // Files in testsuite.avr-gcc will be compiled with avr-gcc and have the output copied to // this directory. - var walkdir = try b.build_root.handle.openDir("testsuite", .{ .iterate = true }); - defer walkdir.close(); + var walkdir = try b.build_root.handle.openDir(io, "testsuite", .{ .iterate = true }); + defer walkdir.close(io); var walker = try walkdir.walk(b.allocator); defer walker.deinit(); - while (try walker.next()) |entry| { + while (try walker.next(io)) |entry| { if (entry.kind != .file) continue; @@ -217,8 +218,8 @@ fn add_test_suite( .ignore => continue, .compile => blk: { - var file = try entry.dir.openFile(entry.basename, .{}); - defer file.close(); + var file = try entry.dir.openFile(io, entry.basename, .{}); + defer file.close(io); const config = try parse_test_suite_config(b, file); @@ -254,7 +255,7 @@ fn add_test_suite( }), .use_llvm = true, }); - test_payload.want_lto = false; // AVR has no LTO support! + test_payload.lto = .none; // AVR has no LTO support! test_payload.verbose_link = true; test_payload.verbose_cc = true; test_payload.bundle_compiler_rt = false; @@ -262,16 +263,16 @@ fn add_test_suite( test_payload.setLinkerScript(b.path("linker.ld")); if (is_c_test or is_asm_test) { - test_payload.addIncludePath(b.path("testsuite")); + test_payload.root_module.addIncludePath(b.path("testsuite")); } if (is_c_test) { - test_payload.addCSourceFile(.{ + test_payload.root_module.addCSourceFile(.{ .file = source_file, .flags = &.{}, }); } if (is_asm_test) { - test_payload.addAssemblyFile(source_file); + test_payload.root_module.addAssemblyFile(source_file); } if (is_zig_test) { test_payload.root_module.addAnonymousImport("testsuite", .{ @@ -294,9 +295,9 @@ fn add_test_suite( }, .load => blk: { const config_path = b.fmt("{s}.json", .{entry.basename}); - const config = if (entry.dir.openFile(config_path, .{})) |file| cfg: { - defer file.close(); - break :cfg try TestSuiteConfig.load(b.allocator, file); + const config = if (entry.dir.openFile(io, config_path, .{})) |file| cfg: { + defer file.close(io); + break :cfg try TestSuiteConfig.load(io, b.allocator, file); } else |_| { // If JSON file doesn't exist, skip this test (likely during testsuite update) std.log.warn("Skipping test {s} - JSON config file {s} not found (run 'zig build update-testsuite' first)", .{ entry.path, config_path }); @@ -336,6 +337,7 @@ fn add_test_suite_update( b: *Build, invoke_step: *Build.Step, ) !void { + const io = b.graph.io; const avr_gcc = if (b.findProgram(&.{"avr-gcc"}, &.{})) |path| LazyPath{ .cwd_relative = path, } else |_| b.addExecutable(.{ @@ -347,13 +349,13 @@ fn add_test_suite_update( .use_llvm = true, }).getEmittedBin(); - var walkdir = try b.build_root.handle.openDir("testsuite.avr-gcc", .{ .iterate = true }); - defer walkdir.close(); + var walkdir = try b.build_root.handle.openDir(io, "testsuite.avr-gcc", .{ .iterate = true }); + defer walkdir.close(io); var walker = try walkdir.walk(b.allocator); defer walker.deinit(); - while (try walker.next()) |entry| { + while (try walker.next(io)) |entry| { if (entry.kind != .file) continue; @@ -389,8 +391,8 @@ fn add_test_suite_update( .ignore => continue, .compile => { - var file = try entry.dir.openFile(entry.basename, .{}); - defer file.close(); + var file = try entry.dir.openFile(io, entry.basename, .{}); + defer file.close(io); const config = try parse_test_suite_config(b, file); @@ -447,12 +449,13 @@ fn add_test_suite_update( } } -fn parse_test_suite_config(b: *Build, file: std.fs.File) !TestSuiteConfig { +fn parse_test_suite_config(b: *Build, file: std.Io.File) !TestSuiteConfig { + const io = b.graph.io; var code = std.array_list.Managed(u8).init(b.allocator); defer code.deinit(); var read_buf: [4096]u8 = undefined; - var file_reader = file.reader(&read_buf); + var file_reader = file.reader(io, &read_buf); const reader = &file_reader.interface; while (true) { diff --git a/sim/aviron/build.zig.zon b/sim/aviron/build.zig.zon index c7075f7cc..c4b83c6ce 100644 --- a/sim/aviron/build.zig.zon +++ b/sim/aviron/build.zig.zon @@ -4,8 +4,8 @@ .fingerprint = 0xfc536f957aeaeb54, .dependencies = .{ .args = .{ - .url = "git+https://github.com/MasterQ32/zig-args#8ae26b44a884ff20dca98ee84c098e8f8e94902f", - .hash = "args-0.0.0-CiLiqojRAACGzDRO7A9dw7kWSchNk29caJZkXuMCb0Cn", + .url = "git+https://github.com/ikskuh/zig-args#8ae26b44a884ff20dca98ee84c098e8f8e94902f", + .hash = "N-V-__8AAIjRAACGzDRO7A9dw7kWSchNk29caJZkXuMCb0Cn", }, .@"bounded-array" = .{ .path = "../../modules/bounded-array" }, .ihex = .{ diff --git a/sim/aviron/src/testconfig.zig b/sim/aviron/src/testconfig.zig index 14d3b4375..c99e61fcd 100644 --- a/sim/aviron/src/testconfig.zig +++ b/sim/aviron/src/testconfig.zig @@ -89,9 +89,9 @@ pub const TestSuiteConfig = struct { }) catch @panic("oom"); } - pub fn load(allocator: std.mem.Allocator, file: std.fs.File) !TestSuiteConfig { + pub fn load(io: std.Io, allocator: std.mem.Allocator, file: std.Io.File) !TestSuiteConfig { var buf: [4096]u8 = undefined; - var file_reader = file.reader(&buf); + var file_reader = file.reader(io, &buf); var json_reader = std.json.Reader.init(allocator, &file_reader.interface); return try std.json.parseFromTokenSourceLeaky(TestSuiteConfig, allocator, &json_reader, .{ diff --git a/tools/esp-image/build.zig b/tools/esp-image/build.zig index 13900418b..1cc15b563 100644 --- a/tools/esp-image/build.zig +++ b/tools/esp-image/build.zig @@ -101,9 +101,6 @@ pub fn build(b: *std.Build) void { }), }); - const clap = b.dependency("clap", .{}); - elf2image_exe.root_module.addImport("clap", clap.module("clap")); - const elf2image_test = b.addTest(.{ .name = "elf2image", .root_module = b.createModule(.{ diff --git a/tools/esp-image/build.zig.zon b/tools/esp-image/build.zig.zon index 8fe07d7b4..96205ff5a 100644 --- a/tools/esp-image/build.zig.zon +++ b/tools/esp-image/build.zig.zon @@ -2,12 +2,7 @@ .name = .mz_tools_espimage, .fingerprint = 0x7e1bc55e5a45fa73, .version = "0.1.0", - .dependencies = .{ - .clap = .{ - .url = "git+https://github.com/Hejsil/zig-clap.git#5289e0753cd274d65344bef1c114284c633536ea", - .hash = "clap-0.11.0-oBajB-HnAQDPCKYzwF7rO3qDFwRcD39Q0DALlTSz5H7e", - }, - }, + .dependencies = .{}, .paths = .{ "README.md", "build.zig", diff --git a/tools/esp-image/src/elf2image.zig b/tools/esp-image/src/elf2image.zig index b013ad042..147971396 100644 --- a/tools/esp-image/src/elf2image.zig +++ b/tools/esp-image/src/elf2image.zig @@ -1,6 +1,5 @@ const std = @import("std"); const Sha256 = std.crypto.hash.sha2.Sha256; -const clap = @import("clap"); const esp_image = @import("esp_image"); pub const std_options: std.Options = .{ @@ -11,88 +10,172 @@ var elf_file_reader_buf: [1024]u8 = undefined; var elf_file_hashing_buf: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined; var output_writer_buf: [1024]u8 = undefined; -pub fn main() !void { - var debug_allocator: std.heap.DebugAllocator(.{}) = .init; - defer _ = debug_allocator.deinit(); - const allocator = debug_allocator.allocator(); - - const args = comptime clap.parseParamsComptime( - \\--help Show this message - \\--output Path to save the generated file - \\--chip-id Chip id - \\--min-rev-full Minimal chip revision (in format: major * 100 + minor) - \\--max-rev-full Maximal chip revision (in format: major * 100 + minor) - \\--dont-append-digest Don't append a SHA256 digest of the entire image after the checksum - \\--flash-freq SPI Flash frequency - \\--flash-mode SPI Flash mode - \\--flash-size SPI Flash size in megabytes - \\--flash-mmu-page-size Flash MMU page size - \\--use-segments Use program headers instead of section headers - \\ - \\ - ); - - const stderr = std.fs.File.stderr(); - var stderr_writer = stderr.writer(&.{}); - - var diag: clap.Diagnostic = .{}; - var res = clap.parse(clap.Help, &args, .{ - .u16 = clap.parsers.int(u16, 10), - .str = clap.parsers.string, - .ChipId = clap.parsers.enumeration(esp_image.ChipId), - .FlashFreq = clap.parsers.enumeration(esp_image.FlashFreq), - .FlashMode = clap.parsers.enumeration(esp_image.FlashMode), - .FlashSize = clap.parsers.enumeration(esp_image.FlashSize), - .FlashMMU_PageSize = clap.parsers.enumeration(esp_image.FlashMMU_PageSize), - }, .{ - .diagnostic = &diag, - .allocator = allocator, - }) catch |err| { - diag.report(&stderr_writer.interface, err) catch {}; +const Args = struct { + help: bool = false, + input_elf: ?[]const u8 = null, + output: ?[]const u8 = null, + chip_id: ?esp_image.ChipId = null, + min_rev_full: u16 = 0x0000, + max_rev_full: u16 = 0xffff, + dont_append_digest: bool = false, + flash_freq: esp_image.FlashFreq = .@"40m", + flash_mode: esp_image.FlashMode = .dio, + flash_size: esp_image.FlashSize = .@"4mb", + flash_mmu_page_size: esp_image.FlashMMU_PageSize = .default, + use_segments: bool = false, + + pub fn parse(args: []const [:0]const u8) !Args { + var ret: Args = .{}; + + var index: usize = 1; + while (index < args.len) : (index += 1) { + if (std.mem.eql(u8, args[index], "--help")) { + ret.help = true; + } else if (std.mem.eql(u8, args[index], "--output")) { + index += 1; + if (index >= args.len) { + return error.FormatRequiresArgument; + } + ret.output = args[index]; + } else if (std.mem.eql(u8, args[index], "--chip-id")) { + index += 1; + if (index >= args.len) { + return error.FormatRequiresArgument; + } + ret.chip_id = std.meta.stringToEnum(esp_image.ChipId, args[index]) orelse { + return error.InvalidChipId; + }; + } else if (std.mem.eql(u8, args[index], "--min-rev-full")) { + index += 1; + if (index >= args.len) { + return error.FormatRequiresArgument; + } + ret.min_rev_full = std.fmt.parseInt(u16, args[index], 10) catch { + return error.InvalidNumber; + }; + } else if (std.mem.eql(u8, args[index], "--max-rev-full")) { + index += 1; + if (index >= args.len) { + return error.FormatRequiresArgument; + } + ret.max_rev_full = std.fmt.parseInt(u16, args[index], 10) catch { + return error.InvalidNumber; + }; + } else if (std.mem.eql(u8, args[index], "--dont-append-digest")) { + ret.dont_append_digest = true; + } else if (std.mem.eql(u8, args[index], "--flash-freq")) { + index += 1; + if (index >= args.len) { + return error.FormatRequiresArgument; + } + ret.flash_freq = std.meta.stringToEnum(esp_image.FlashFreq, args[index]) orelse { + return error.InvalidFlashFreq; + }; + } else if (std.mem.eql(u8, args[index], "--flash-mode")) { + index += 1; + if (index >= args.len) { + return error.FormatRequiresArgument; + } + ret.flash_mode = std.meta.stringToEnum(esp_image.FlashMode, args[index]) orelse { + return error.InvalidFlashMode; + }; + } else if (std.mem.eql(u8, args[index], "--flash-size")) { + index += 1; + if (index >= args.len) { + return error.FormatRequiresArgument; + } + ret.flash_size = std.meta.stringToEnum(esp_image.FlashSize, args[index]) orelse { + return error.InvalidFlashSize; + }; + } else if (std.mem.eql(u8, args[index], "--flash-mmu-page-size")) { + index += 1; + if (index >= args.len) { + return error.FormatRequiresArgument; + } + ret.flash_mmu_page_size = std.meta.stringToEnum(esp_image.FlashMMU_PageSize, args[index]) orelse { + return error.InvalidFlashMMU_PageSize; + }; + } else if (std.mem.eql(u8, args[index], "--use-segments")) { + ret.use_segments = true; + } else { + if (ret.input_elf != null) { + std.log.err("extra arg: {s}", .{args[index]}); + return error.ExtraArguments; + } + ret.input_elf = args[index]; + } + } + + return ret; + } +}; + +const help_message = + \\--help Show this message + \\--output Path to save the generated file + \\--chip-id Chip id + \\--min-rev-full Minimal chip revision (in format: major * 100 + minor) + \\--max-rev-full Maximal chip revision (in format: major * 100 + minor) + \\--dont-append-digest Don't append a SHA256 digest of the entire image after the checksum + \\--flash-freq SPI Flash frequency + \\--flash-mode SPI Flash mode + \\--flash-size SPI Flash size in megabytes + \\--flash-mmu-page-size Flash MMU page size + \\--use-segments Use program headers instead of section headers + \\ + \\ +; + +pub fn main(init: std.process.Init) !void { + const io = init.io; + const arena = init.arena.allocator(); + const gpa = init.gpa; + + const stderr = std.Io.File.stderr(); + var stderr_writer = stderr.writer(io, &.{}); + + const args_slice = try init.minimal.args.toSlice(arena); + const args = Args.parse(args_slice) catch |err| { + try stderr_writer.interface.writeAll(help_message); + try stderr_writer.interface.flush(); return err; }; - defer res.deinit(); - if (res.args.help != 0) { - return clap.help(&stderr_writer.interface, clap.Help, &args, .{}); + if (args.help) { + try stderr_writer.interface.writeAll(help_message); + try stderr_writer.interface.flush(); + return; } - const elf_path = res.positionals[0] orelse return error.MissingInputFile; - const output_path = res.args.output orelse return error.MissingOutputFile; + const elf_path = args.input_elf orelse return error.MissingInputFile; + const output_path = args.output orelse return error.MissingOutputFile; - const chip_id = res.args.@"chip-id" orelse return error.MissingChipId; + const chip_id = args.chip_id orelse return error.MissingChipId; const chip = chips.get(chip_id) orelse { std.log.err("support for chip `{s}` is not implemented yet", .{@tagName(chip_id)}); return error.UnimplementedChip; }; - const min_rev: u16 = res.args.@"min-rev-full" orelse 0x0000; - const max_rev: u16 = res.args.@"max-rev-full" orelse 0xffff; - const no_digest = res.args.@"dont-append-digest" != 0; - const flash_freq: esp_image.FlashFreq = res.args.@"flash-freq" orelse .@"40m"; - const flash_mode: esp_image.FlashMode = res.args.@"flash-mode" orelse .dio; - const flash_size: esp_image.FlashSize = res.args.@"flash-size" orelse .@"4mb"; - - var flash_mmu_page_size: esp_image.FlashMMU_PageSize = esp_image.DEFAULT_FLASH_MMU_PAGE_SIZE; - if (res.args.@"flash-mmu-page-size") |page_size_override| { + var flash_mmu_page_size: esp_image.FlashMMU_PageSize = .default; + if (args.flash_mmu_page_size != flash_mmu_page_size) { if (chip.supported_flash_mmu_page_sizes) |supported_flash_mmu_page_sizes| { - if (std.mem.indexOfScalar(esp_image.FlashMMU_PageSize, supported_flash_mmu_page_sizes, page_size_override) != null) { - flash_mmu_page_size = page_size_override; + if (std.mem.indexOfScalar(esp_image.FlashMMU_PageSize, supported_flash_mmu_page_sizes, args.flash_mmu_page_size) != null) { + flash_mmu_page_size = args.flash_mmu_page_size; } else { std.log.err("flash mmu page size `{t}` is not supported by chip `{t}`... using default `{t}`", .{ - flash_mmu_page_size, + args.flash_mmu_page_size, chip_id, - esp_image.DEFAULT_FLASH_MMU_PAGE_SIZE, + flash_mmu_page_size, }); } } } - const use_segments = res.args.@"use-segments" != 0; + const cwd = std.Io.Dir.cwd(); - const elf_file = try std.fs.cwd().openFile(elf_path, .{}); - defer elf_file.close(); - var elf_file_reader = elf_file.reader(&elf_file_reader_buf); + const elf_file = try cwd.openFile(io, elf_path, .{}); + defer elf_file.close(io); + var elf_file_reader = elf_file.reader(io, &elf_file_reader_buf); var elf_file_hash: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined; { @@ -107,26 +190,26 @@ pub fn main() !void { const entry_point: u32 = @intCast(elf_header.entry); var flash_segments: std.ArrayList(Segment) = .empty; - defer flash_segments.deinit(allocator); + defer flash_segments.deinit(gpa); defer for (flash_segments.items) |segment| { - segment.deinit(allocator); + segment.deinit(gpa); }; var ram_segments: std.ArrayList(Segment) = .empty; - defer ram_segments.deinit(allocator); + defer ram_segments.deinit(gpa); defer for (ram_segments.items) |segment| { - segment.deinit(allocator); + segment.deinit(gpa); }; { var info_list: std.ArrayList(SegmentInfo) = .empty; - defer info_list.deinit(allocator); + defer info_list.deinit(gpa); - if (use_segments) { + if (args.use_segments) { var it = elf_header.iterateProgramHeaders(&elf_file_reader); while (try it.next()) |hdr| { if (hdr.p_type == std.elf.PT_LOAD and hdr.p_memsz > 0 and hdr.p_filesz > 0) { - try info_list.append(allocator, .{ + try info_list.append(gpa, .{ .addr = @as(u32, @intCast(hdr.p_paddr)), .file_offset = @as(u32, @intCast(hdr.p_offset)), .size = @as(u32, @intCast(hdr.p_filesz)), @@ -137,7 +220,7 @@ pub fn main() !void { var it = elf_header.iterateSectionHeaders(&elf_file_reader); while (try it.next()) |hdr| { if (hdr.sh_type == std.elf.SHT_PROGBITS and hdr.sh_flags & std.elf.SHF_ALLOC != 0 and hdr.sh_size > 0) { - try info_list.append(allocator, .{ + try info_list.append(gpa, .{ .addr = @as(u32, @intCast(hdr.sh_addr)), .file_offset = @as(u32, @intCast(hdr.sh_offset)), .size = @as(u32, @intCast(hdr.sh_size)), @@ -156,9 +239,9 @@ pub fn main() !void { if ((chip.irom_map_start <= segment_info.addr and segment_info.addr < chip.irom_map_end) or (chip.drom_map_start <= segment_info.addr and segment_info.addr < chip.drom_map_end)) { - try flash_segments.append(allocator, try .init(allocator, segment_info, &elf_file_reader)); + try flash_segments.append(gpa, try .init(gpa, segment_info, &elf_file_reader)); } else { - try ram_segments.append(allocator, try .init(allocator, segment_info, &elf_file_reader)); + try ram_segments.append(gpa, try .init(gpa, segment_info, &elf_file_reader)); } } } @@ -170,8 +253,8 @@ pub fn main() !void { // TODO: maybe also check if sections overlap // merge segments - try do_segment_merge(allocator, &flash_segments); - try do_segment_merge(allocator, &ram_segments); + try do_segment_merge(gpa, &flash_segments); + try do_segment_merge(gpa, &ram_segments); for (flash_segments.items) |segment| { std.log.debug("flash segment at addr 0x{x} of size 0x{x}", .{ segment.addr, segment.size }); @@ -190,15 +273,15 @@ pub fn main() !void { var writer: std.Io.Writer = .fixed(flash_segments.items[0].data); // TODO: override time and date - app_desc.min_efuse_blk_rev_full = min_rev; - app_desc.max_efuse_blk_rev_full = max_rev; - app_desc.mmu_page_size = flash_mmu_page_size; + app_desc.min_efuse_blk_rev_full = args.min_rev_full; + app_desc.max_efuse_blk_rev_full = args.max_rev_full; + app_desc.mmu_page_size = args.flash_mmu_page_size; app_desc.app_elf_sha256 = elf_file_hash; try writer.writeStruct(app_desc, .little); } } - var segment_data: std.Io.Writer.Allocating = .init(allocator); + var segment_data: std.Io.Writer.Allocating = .init(gpa); defer segment_data.deinit(); var segment_count: u8 = 0; @@ -208,39 +291,39 @@ pub fn main() !void { while (segment.get_flash_align_padding_len(segment_data.writer.end + esp_image.IMAGE_HEADER_LEN, flash_mmu_page_size)) |pad_len| { if (ram_segments.items.len > 0 and pad_len > esp_image.SEGMENT_HEADER_LEN) { const ram_seg = &ram_segments.items[ram_segments.items.len - 1]; - try ram_seg.write_to(allocator, &segment_data.writer, pad_len, &checksum); + try ram_seg.write_to(gpa, &segment_data.writer, pad_len, &checksum); if (ram_seg.size == 0) { ram_segments.items.len -= 1; } } else { - var padding_seg: Segment = try .init_padding(allocator, pad_len); - defer padding_seg.deinit(allocator); - try padding_seg.write_to(allocator, &segment_data.writer, null, &checksum); + var padding_seg: Segment = try .init_padding(gpa, pad_len); + defer padding_seg.deinit(gpa); + try padding_seg.write_to(gpa, &segment_data.writer, null, &checksum); } segment_count += 1; } - try segment.write_to(allocator, &segment_data.writer, null, &checksum); + try segment.write_to(gpa, &segment_data.writer, null, &checksum); segment_count += 1; } for (ram_segments.items) |*segment| { - try segment.write_to(allocator, &segment_data.writer, null, &checksum); + try segment.write_to(gpa, &segment_data.writer, null, &checksum); segment_count += 1; } } - const output_file = try std.fs.cwd().createFile(output_path, .{}); - defer output_file.close(); - var output_file_writer = output_file.writer(&.{}); + const output_file = try cwd.createFile(io, output_path, .{}); + defer output_file.close(io); + var output_file_writer = output_file.writer(io, &.{}); var sha256_hasher = output_file_writer.interface.hashed(Sha256.init(.{}), &output_writer_buf); const file_header: FileHeader = .{ .number_of_segments = segment_count, - .flash_mode = flash_mode, - .flash_size = flash_size, - .flash_freq = flash_freq, + .flash_mode = args.flash_mode, + .flash_size = args.flash_size, + .flash_freq = args.flash_freq, .entry_point = entry_point, }; @@ -248,9 +331,9 @@ pub fn main() !void { .wp = .disabled, .flash_pins_drive_settings = 0, // TODO: figure out what to set this to .chip_id = chip_id, - .min_rev = min_rev, - .max_rev = max_rev, - .hash = !no_digest, + .min_rev = args.min_rev_full, + .max_rev = args.max_rev_full, + .hash = !args.dont_append_digest, }; try file_header.write_to(&sha256_hasher.writer); @@ -266,7 +349,7 @@ pub fn main() !void { try sha256_hasher.writer.flush(); - if (!no_digest) { + if (!args.dont_append_digest) { try output_file_writer.interface.writeAll(&sha256_hasher.hasher.finalResult()); try output_file_writer.interface.flush(); } @@ -361,7 +444,7 @@ const Segment = struct { size: usize, data: []u8, - pub fn init(allocator: std.mem.Allocator, segment_info: SegmentInfo, elf_file_reader: *std.fs.File.Reader) !Segment { + pub fn init(allocator: std.mem.Allocator, segment_info: SegmentInfo, elf_file_reader: *std.Io.File.Reader) !Segment { try elf_file_reader.seekTo(segment_info.file_offset); const data = try allocator.alloc(u8, segment_info.size + segment_info.size % 4); diff --git a/tools/esp-image/src/esp_image.zig b/tools/esp-image/src/esp_image.zig index 56f60768b..cd93e5d12 100644 --- a/tools/esp-image/src/esp_image.zig +++ b/tools/esp-image/src/esp_image.zig @@ -6,7 +6,6 @@ const std = @import("std"); pub const SEGMENT_HEADER_LEN = 0x8; pub const IMAGE_HEADER_LEN = 0x18; pub const CHECKSUM_XOR_BYTE = 0xEF; -pub const DEFAULT_FLASH_MMU_PAGE_SIZE: FlashMMU_PageSize = .@"64k"; pub const FlashMode = enum(u8) { qio = 0, @@ -36,6 +35,8 @@ pub const FlashMMU_PageSize = enum(u8) { @"32k" = 15, @"64k" = 16, + pub const default: FlashMMU_PageSize = .@"64k"; + pub fn in_bytes(self: FlashMMU_PageSize) usize { return @as(usize, 1) << @intCast(@intFromEnum(self)); } @@ -66,7 +67,7 @@ pub const AppDesc = extern struct { app_elf_sha256: [32]u8 = std.mem.zeroes([32]u8), min_efuse_blk_rev_full: u16 = 0, max_efuse_blk_rev_full: u16 = 0xffff, - mmu_page_size: FlashMMU_PageSize = DEFAULT_FLASH_MMU_PAGE_SIZE, + mmu_page_size: FlashMMU_PageSize = .default, reserved1: [3]u8 = std.mem.zeroes([3]u8), reserved2: [18]u32 = std.mem.zeroes([18]u32), }; diff --git a/tools/generate_linker_script.zig b/tools/generate_linker_script.zig index ef1f4079b..5fe014a0f 100644 --- a/tools/generate_linker_script.zig +++ b/tools/generate_linker_script.zig @@ -14,15 +14,11 @@ pub const Args = struct { var writer_buf: [1024]u8 = undefined; -pub fn main() !void { - var debug_allocator: std.heap.DebugAllocator(.{}) = .init; - defer _ = debug_allocator.deinit(); +pub fn main(init: std.process.Init) !void { + const io = init.io; + const arena = init.arena.allocator(); - var arena: std.heap.ArenaAllocator = .init(debug_allocator.allocator()); - defer arena.deinit(); - - const allocator = arena.allocator(); - const args = try std.process.argsAlloc(allocator); + const args = try init.minimal.args.toSlice(arena); if (args.len < 3 or args.len > 4) { return error.UsageError; } @@ -30,17 +26,17 @@ pub fn main() !void { const json_args = args[1]; const output_path = args[2]; - const parsed_args = try std.json.parseFromSliceLeaky(Args, allocator, json_args, .{}); + const parsed_args = try std.json.parseFromSliceLeaky(Args, arena, json_args, .{}); const maybe_user_linker_script = if (args.len == 4) - try std.fs.cwd().readFileAlloc(allocator, args[3], 100 * 1024 * 1024) + try std.Io.Dir.cwd().readFileAlloc(io, args[3], arena, .limited(100 * 1024 * 1024)) else null; - const file = try std.fs.cwd().createFile(output_path, .{}); - defer file.close(); + const file = try std.Io.Dir.cwd().createFile(io, output_path, .{}); + defer file.close(io); - var writer = file.writer(&writer_buf); + var writer = file.writer(io, &writer_buf); try writer.interface.print( \\/* \\ * Target CPU: {[cpu]s} @@ -54,14 +50,14 @@ pub fn main() !void { }); // name all unnamed regions - const region_names: [][]const u8 = try allocator.alloc([]const u8, parsed_args.memory_regions.len); + const region_names: [][]const u8 = try arena.alloc([]const u8, parsed_args.memory_regions.len); { var counters: [5]usize = @splat(0); for (region_names, parsed_args.memory_regions) |*region_name, region| { if (region.name) |name| { - region_name.* = try allocator.dupe(u8, name); + region_name.* = try arena.dupe(u8, name); } else { - region_name.* = try std.fmt.allocPrint(allocator, "{s}{}", .{ + region_name.* = try std.fmt.allocPrint(arena, "{s}{}", .{ @tagName(region.tag), counters[@intFromEnum(region.tag)], }); @@ -252,7 +248,7 @@ pub fn main() !void { \\ , .{ if (!parsed_args.ram_image) - try std.fmt.allocPrint(allocator, "{s} AT> {s}", .{ ram_region_name, flash_region_name }) + try std.fmt.allocPrint(arena, "{s} AT> {s}", .{ ram_region_name, flash_region_name }) else ram_region_name, ram_region_name, @@ -273,10 +269,21 @@ pub fn main() !void { } try writer.interface.writeAll( + \\ + \\ .eh_frame_hdr 0 (INFO) : + \\ { + \\ KEEP(*(.eh_frame_hdr)) + \\ } + \\ + \\ .eh_frame 0 (INFO) : + \\ { + \\ KEEP(*(.eh_frame)) + \\ } \\ \\ microzig_data_load_start = LOADADDR(.data); \\ ); + switch (parsed_args.cpu_arch) { .riscv32, .riscv64 => try writer.interface.writeAll( \\ PROVIDE(__global_pointer$ = microzig_data_start + 0x800); diff --git a/tools/regz/build.zig b/tools/regz/build.zig index 1570f018b..a54b2e729 100644 --- a/tools/regz/build.zig +++ b/tools/regz/build.zig @@ -39,7 +39,7 @@ pub fn build(b: *Build) !void { }), .use_llvm = true, }); - regz.linkLibrary(libxml2_dep.artifact("xml2")); + regz.root_module.linkLibrary(libxml2_dep.artifact("xml2")); regz.root_module.addImport("zqlite", zqlite); b.installArtifact(regz); @@ -66,7 +66,7 @@ pub fn build(b: *Build) !void { }), .use_llvm = true, }); - tests.linkLibrary(libxml2_dep.artifact("xml2")); + tests.root_module.linkLibrary(libxml2_dep.artifact("xml2")); tests.root_module.addImport("zqlite", zqlite); tests.step.dependOn(®z.step); diff --git a/tools/regz/build.zig.zon b/tools/regz/build.zig.zon index 154dc6eaa..56251da42 100644 --- a/tools/regz/build.zig.zon +++ b/tools/regz/build.zig.zon @@ -10,12 +10,13 @@ }, .dependencies = .{ .libxml2 = .{ - .url = "git+https://github.com/mattnite/zig-build-libxml2.git#5474281ad4d173ed298ee789c7dce4f5edb78e10", - .hash = "libxml2-0.0.0-Kr0Y1Ac4ngAiRuFTS2qMO9j-KZsD0PKFKKXoZjynTHLq", + // .path = "../../../zig-build-libxml2", + .url = "git+https://github.com/mattnite/zig-build-libxml2.git#0a410ac11a842e7f366dac13d0ca59adbc7b4c5e", + .hash = "libxml2-0.0.0-Kr0Y1C44ngAn0qlZ5eZXiDDAnjbAk1TWMqRiuKyIMmUZ", }, .sqlite3 = .{ - .url = "git+https://github.com/allyourcodebase/sqlite3#8f840560eae88ab66668c6827c64ffbd0d74ef37", - .hash = "sqlite3-3.51.0-DMxLWssOAABZ8cAvU_LfBIbp0kZjm824PU8sSLXpEDdr", + .url = "git+https://github.com/allyourcodebase/sqlite3#42c703d0c800dcc39d3e0a2ca40380691274380f", + .hash = "sqlite3-3.51.0-DMxLWtcOAADDoP7M630_Ws36wFHX5WHwcs-zjlswwakL", }, .zqlite = .{ .url = "git+https://github.com/karlseguin/zqlite.zig#b44ed5cdc64859b08446c246f307e65ebe2b2d9c", diff --git a/tools/regz/src/Database.zig b/tools/regz/src/Database.zig index 00f606371..f586821a9 100644 --- a/tools/regz/src/Database.zig +++ b/tools/regz/src/Database.zig @@ -615,7 +615,7 @@ pub fn create_from_doc(allocator: Allocator, format: Format, doc: xml.Doc) !*Dat return db; } -pub fn create_from_path(allocator: Allocator, format: Format, path: []const u8, device: ?[]const u8) !*Database { +pub fn create_from_path(io: std.Io, allocator: Allocator, format: Format, path: []const u8, device: ?[]const u8) !*Database { return switch (format) { .embassy => blk: { var db = try Database.create(allocator); @@ -624,7 +624,7 @@ pub fn create_from_path(allocator: Allocator, format: Format, path: []const u8, db.destroy(); } - try embassy.load_into_db(db, path, device); + try embassy.load_into_db(io, db, path, device); break :blk db; }, .targetdb => blk: { @@ -634,11 +634,11 @@ pub fn create_from_path(allocator: Allocator, format: Format, path: []const u8, db.destroy(); } - try targetdb.load_into_db(db, path, device); + try targetdb.load_into_db(io, db, path, device); break :blk db; }, .svd, .atdf => blk: { - const text = try std.fs.cwd().readFileAlloc(allocator, path, file_size_max); + const text = try std.Io.Dir.cwd().readFileAlloc(io, path, allocator, .limited(file_size_max)); defer allocator.free(text); break :blk create_from_xml(allocator, format, text); @@ -811,7 +811,7 @@ fn all(db: *Database, comptime T: type, comptime query: []const u8, allocator: A var rows = try db.conn.rows(query, args); defer rows.deinit(); - var list: std.ArrayList(T) = .{}; + var list: std.ArrayList(T) = .empty; while (rows.next()) |row| { try list.append(allocator, try scan_row(T, allocator, row)); } @@ -919,7 +919,7 @@ fn get_nested_struct_fields( fn recursively_calculate_struct_size( db: *Database, depth: *u8, - cache: *std.AutoArrayHashMap(StructID, u64), + cache: *std.AutoArrayHashMapUnmanaged(StructID, u64), allocator: Allocator, struct_id: StructID, ) !u64 { @@ -974,8 +974,8 @@ pub fn get_nested_struct_fields_with_calculated_size( log.debug("nested_struct_fields.len={} struct_id={f}", .{ nested_struct_fields.len, struct_id }); - var size_cache: std.AutoArrayHashMap(StructID, u64) = .init(gpa); - defer size_cache.deinit(); + var size_cache: std.AutoArrayHashMapUnmanaged(StructID, u64) = .empty; + defer size_cache.deinit(gpa); for (nested_struct_fields) |*nsf| { if (nsf.size_bytes != null) { @@ -2060,7 +2060,7 @@ fn cleanup_unused_enums(db: *Database) !void { } pub fn apply_patch(db: *Database, zon_text: [:0]const u8, diags: *std.zon.parse.Diagnostics) !void { - const patches = try std.zon.parse.fromSlice([]const Patch, db.gpa, zon_text, diags, .{}); + const patches = try std.zon.parse.fromSliceAlloc([]const Patch, db.gpa, zon_text, diags, .{}); defer std.zon.parse.free(db.gpa, patches); for (patches) |patch| { diff --git a/tools/regz/src/FS_Directory.zig b/tools/regz/src/FS_Directory.zig index 69e0bd8e5..32e8e3d18 100644 --- a/tools/regz/src/FS_Directory.zig +++ b/tools/regz/src/FS_Directory.zig @@ -1,4 +1,5 @@ -dir: std.fs.Dir, +io: std.Io, +dir: std.Io.Dir, const vtable = Directory.VTable{ .create_file = create_file, @@ -6,19 +7,20 @@ const vtable = Directory.VTable{ pub fn create_file(ctx: *anyopaque, filename: []const u8, contents: []const u8) Directory.CreateFileError!void { const fs: *FS_Directory = @ptrCast(@alignCast(ctx)); - const file: std.fs.File = if (std.fs.path.dirname(filename)) |dirname| blk: { - var dir = fs.dir.makeOpenPath(dirname, .{}) catch return error.System; - defer dir.close(); + const file: std.Io.File = if (std.fs.path.dirname(filename)) |dirname| blk: { + var dir = fs.dir.createDirPathOpen(fs.io, dirname, .{}) catch return error.System; + defer dir.close(fs.io); - break :blk dir.createFile(std.fs.path.basename(filename), .{}) catch return error.System; - } else fs.dir.createFile(filename, .{}) catch return error.System; - defer file.close(); + break :blk dir.createFile(fs.io, std.fs.path.basename(filename), .{}) catch return error.System; + } else fs.dir.createFile(fs.io, filename, .{}) catch return error.System; + defer file.close(fs.io); - file.writeAll(contents) catch return error.System; + file.writeStreamingAll(fs.io, contents) catch return error.System; } -pub fn init(dir: std.fs.Dir) FS_Directory { +pub fn init(io: std.Io, dir: std.Io.Dir) FS_Directory { return FS_Directory{ + .io = io, .dir = dir, }; } diff --git a/tools/regz/src/atdf.zig b/tools/regz/src/atdf.zig index 70c140570..31bf0998b 100644 --- a/tools/regz/src/atdf.zig +++ b/tools/regz/src/atdf.zig @@ -368,7 +368,7 @@ fn load_module_type(ctx: *Context, node: xml.Node) !void { 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, .{}); + try ctx.interrupt_groups.put(ctx.db.gpa, name, .empty); var interrupt_it = node.iterate(&.{}, &.{"interrupt"}); while (interrupt_it.next()) |interrupt_node| diff --git a/tools/regz/src/embassy.zig b/tools/regz/src/embassy.zig index 1aa577195..e1bd5fc87 100644 --- a/tools/regz/src/embassy.zig +++ b/tools/regz/src/embassy.zig @@ -144,12 +144,12 @@ pub const ChipFile = struct { } }; -pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void { - var package_dir = try std.fs.cwd().openDir(path, .{}); - defer package_dir.close(); +pub fn load_into_db(io: std.Io, db: *Database, path: []const u8, device: ?[]const u8) !void { + var package_dir = try std.Io.Dir.cwd().openDir(io, path, .{}); + defer package_dir.close(io); - var data_dir = try package_dir.openDir("data", .{}); - defer data_dir.close(); + var data_dir = try package_dir.openDir(io, "data", .{}); + defer data_dir.close(io); var arena = std.heap.ArenaAllocator.init(db.gpa); defer arena.deinit(); @@ -163,19 +163,19 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void chip_files.deinit(allocator); } - var register_files = std.StringArrayHashMap(std.json.Parsed(std.json.Value)).init(allocator); + var register_files: std.StringArrayHashMapUnmanaged(std.json.Parsed(std.json.Value)) = .empty; defer { for (register_files.values()) |*value| value.deinit(); - register_files.deinit(); + register_files.deinit(allocator); } - var chips_dir = try data_dir.openDir("chips", .{ .iterate = true }); - defer chips_dir.close(); + var chips_dir = try data_dir.openDir(io, "chips", .{ .iterate = true }); + defer chips_dir.close(io); var it = chips_dir.iterate(); - while (try it.next()) |entry| { + while (try it.next(io)) |entry| { if (entry.kind != .file) continue; @@ -189,7 +189,7 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void std.log.info("file: {s}", .{entry.name}); - const chip_file_text = try chips_dir.readFileAlloc(allocator, entry.name, std.math.maxInt(usize)); + const chip_file_text = try chips_dir.readFileAlloc(io, entry.name, allocator, .unlimited); defer allocator.free(chip_file_text); var scanner = std.json.Scanner.initCompleteInput(allocator, chip_file_text); @@ -214,8 +214,8 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void return error.DeviceMissing; } - var registers_dir = try data_dir.openDir("registers", .{ .iterate = true }); - defer registers_dir.close(); + var registers_dir = try data_dir.openDir(io, "registers", .{ .iterate = true }); + defer registers_dir.close(io); //This holds extra data for extended registers var extends_list_arena = std.heap.ArenaAllocator.init(allocator); @@ -223,13 +223,13 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void const extends_list_allocator = extends_list_arena.allocator(); it = registers_dir.iterate(); - while (try it.next()) |entry| { + while (try it.next(io)) |entry| { if (entry.kind != .file) continue; std.log.info("file: {s}", .{entry.name}); - const register_file_text = try registers_dir.readFileAlloc(allocator, entry.name, std.math.maxInt(usize)); + const register_file_text = try registers_dir.readFileAlloc(io, entry.name, allocator, .unlimited); defer allocator.free(register_file_text); var scanner = std.json.Scanner.initCompleteInput(allocator, register_file_text); @@ -249,7 +249,7 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void try handle_extends(allocator, extends_list_allocator, ®ister_file.value); const register_name = try allocator.dupe(u8, entry.name[0 .. entry.name.len - std.fs.path.extension(entry.name).len]); - try register_files.put(register_name, register_file); + try register_files.put(allocator, register_name, register_file); } // sort to try to keep things somewhat in order @@ -260,8 +260,8 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void .name = name, }); - var enums = std.StringArrayHashMap(Database.EnumID).init(allocator); - defer enums.deinit(); + var enums: std.StringArrayHashMapUnmanaged(Database.EnumID) = .empty; + defer enums.deinit(allocator); for (register_file.value.object.keys(), register_file.value.object.values()) |key, obj| { if (!std.mem.startsWith(u8, key, "enum/")) @@ -277,7 +277,7 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void .size_bits = @intCast(size), }); - try enums.put(key["enum/".len..], enum_id); + try enums.put(allocator, key["enum/".len..], enum_id); for (obj.object.get("variants").?.array.items) |item| { const enum_field_name = item.object.get("name").?.string; @@ -600,8 +600,8 @@ fn handle_extends(allocator: std.mem.Allocator, extends_allocator: std.mem.Alloc if (item_value.*.object.contains("extends")) { // This Collects unique items from the ancestors. - var arr: std.json.ObjectMap = std.json.ObjectMap.init(allocator); - defer arr.deinit(); + var arr: std.json.ObjectMap = .empty; + defer arr.deinit(allocator); // Get child value and kind holder of inherting items var child = root_json.object.get(item_name).?; @@ -610,7 +610,7 @@ fn handle_extends(allocator: std.mem.Allocator, extends_allocator: std.mem.Alloc // Add child items to dictionary so they are not overwritten. for (child.object.get(list_name).?.array.items) |child_item| { const child_item_name = child_item.object.get("name").?.string; - try arr.put(child_item_name, child_item); + try arr.put(allocator, child_item_name, child_item); } // Handle all parents and grandparents of the current child. @@ -621,7 +621,7 @@ fn handle_extends(allocator: std.mem.Allocator, extends_allocator: std.mem.Alloc for (arr.values()) |value| { try new_list.append(value); } - try child.object.put(list_name, std.json.Value{ .array = new_list }); + try child.object.put(allocator, list_name, std.json.Value{ .array = new_list }); } } } @@ -643,7 +643,7 @@ fn resolve_inheritance_recursively(allocator: std.mem.Allocator, json_data: *std for (parent_section_array.items) |parent_element| { const parent_element_name = if (parent_element.object.get("name")) |name| name.string else @panic("No Name exist in array properties"); if (!accumulator.contains(parent_element_name)) { - try accumulator.put(parent_element_name, parent_element); + try accumulator.put(allocator, parent_element_name, parent_element); } } diff --git a/tools/regz/src/main.zig b/tools/regz/src/main.zig index 4a1cd79ad..7cdc56ea0 100644 --- a/tools/regz/src/main.zig +++ b/tools/regz/src/main.zig @@ -8,14 +8,16 @@ const ArenaAllocator = std.heap.ArenaAllocator; const Allocator = std.mem.Allocator; const assert = std.debug.assert; -pub const std_options = std.Options{ - .log_level = .warn, -}; +// pub const std_options = std.Options{ +// .log_level = .debug, +// }; -pub fn main() !void { - main_impl() catch |err| switch (err) { +pub fn main(init: std.process.Init) !void { + main_impl(init) catch |err| switch (err) { error.Explained => std.process.exit(1), - else => return err, + else => { + return err; + }, }; } @@ -25,7 +27,7 @@ const Arguments = struct { input_path: ?[]const u8 = null, output_path: ?[:0]const u8 = null, device: ?[]const u8 = null, - patch_paths: std.ArrayList([]const u8) = .{}, + patch_paths: std.ArrayList([]const u8) = .empty, dump_path: ?[:0]const u8 = null, help: bool = false, @@ -62,9 +64,10 @@ fn print_usage(writer: *std.Io.Writer) !void { try writer.flush(); } -fn parse_args(allocator: Allocator) !Arguments { - const args = try std.process.argsAlloc(allocator); - defer std.process.argsFree(allocator, args); +fn parse_args(init: std.process.Init) !Arguments { + const io = init.io; + const allocator = init.arena.allocator(); + const args = try init.minimal.args.toSlice(allocator); var ret = Arguments{ .allocator = allocator, @@ -104,7 +107,7 @@ fn parse_args(allocator: Allocator) !Arguments { std.log.err("Unknown argument '{s}'", .{args[i]}); var buf: [80]u8 = undefined; - var writer = std.fs.File.stderr().writer(&buf); + var writer = std.Io.File.stderr().writer(io, &buf); try print_usage(&writer.interface); return error.Explained; } else if (ret.input_path != null) { @@ -120,20 +123,20 @@ fn parse_args(allocator: Allocator) !Arguments { return ret; } -fn main_impl() anyerror!void { +fn main_impl(init: std.process.Init) anyerror!void { defer xml.cleanupParser(); - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); + const io = init.io; + const arena = init.arena.allocator(); - const allocator = gpa.allocator(); + const allocator = init.gpa; - var args = try parse_args(allocator); + var args = try parse_args(init); defer args.deinit(); if (args.help) { var buf: [80]u8 = undefined; - var writer = std.fs.File.stdout().writer(&buf); + var writer = std.Io.File.stdout().writer(io, &buf); try print_usage(&writer.interface); return; } @@ -146,11 +149,11 @@ fn main_impl() anyerror!void { return error.Explained; }; - var db = try Database.create_from_path(allocator, format, input_path, args.device); + var db = try Database.create_from_path(io, allocator, format, input_path, args.device); defer db.destroy(); for (args.patch_paths.items) |patch_path| { - const patch = try std.fs.cwd().readFileAllocOptions(allocator, patch_path, std.math.maxInt(u64), null, .@"1", 0); + const patch = try std.Io.Dir.cwd().readFileAllocOptions(io, patch_path, allocator, .limited(1024 * 1024), .@"1", 0); defer allocator.free(patch); var diags: std.zon.parse.Diagnostics = .{}; @@ -167,10 +170,7 @@ fn main_impl() anyerror!void { // arch dependent stuff { - var arena = ArenaAllocator.init(allocator); - defer arena.deinit(); - - for (try db.get_devices(arena.allocator())) |device| { + for (try db.get_devices(arena)) |device| { if (device.arch.is_arm()) { const arm = @import("arch/arm.zig"); try arm.load_system_interrupts(db, device.id, device.arch); @@ -182,9 +182,9 @@ fn main_impl() anyerror!void { try db.backup(dump_path); } // output_path is the directory to write files - var output_dir = try std.fs.cwd().makeOpenPath(output_path, .{}); - defer output_dir.close(); + var output_dir = try std.Io.Dir.cwd().createDirPathOpen(io, output_path, .{}); + defer output_dir.close(io); - var fs = FS_Directory.init(output_dir); + var fs = FS_Directory.init(io, output_dir); try db.to_zig(fs.directory(), .{}); } diff --git a/tools/regz/src/svd.zig b/tools/regz/src/svd.zig index 050b691f0..105e6a835 100644 --- a/tools/regz/src/svd.zig +++ b/tools/regz/src/svd.zig @@ -746,7 +746,7 @@ const DimElements = struct { pattern: []const u8, fn expand(list: *const List, gpa: Allocator) ![]const []const u8 { - var ret: std.ArrayList([]const u8) = .{}; + var ret: std.ArrayList([]const u8) = .empty; defer ret.deinit(gpa); if (std.mem.indexOf(u8, list.pattern, "-")) |dash_idx| { diff --git a/tools/regz/src/targetdb.zig b/tools/regz/src/targetdb.zig index ade6d1796..8fc58fcf9 100644 --- a/tools/regz/src/targetdb.zig +++ b/tools/regz/src/targetdb.zig @@ -34,24 +34,24 @@ fn parse_isa_to_arch(isa: []const u8) Arch { return .unknown; } -pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void { - var targetdb_dir = try std.fs.cwd().openDir(path, .{}); - defer targetdb_dir.close(); +pub fn load_into_db(io: std.Io, db: *Database, path: []const u8, device: ?[]const u8) !void { + var targetdb_dir = try std.Io.Dir.cwd().openDir(io, path, .{}); + defer targetdb_dir.close(io); - var devices_dir = try targetdb_dir.openDir("devices", .{ .iterate = true }); - defer devices_dir.close(); + var devices_dir = try targetdb_dir.openDir(io, "devices", .{ .iterate = true }); + defer devices_dir.close(io); - var modules = std.StringHashMap(ModuleEntry).init(db.gpa); + var modules: std.StringHashMapUnmanaged(ModuleEntry) = .empty; defer { var key_it = modules.keyIterator(); while (key_it.next()) |key| { db.gpa.free(key.*); } - modules.deinit(); + modules.deinit(db.gpa); } var it = devices_dir.iterate(); - while (try it.next()) |entry| { + while (try it.next(io)) |entry| { if (entry.kind != .file) continue; @@ -60,22 +60,25 @@ pub fn load_into_db(db: *Database, path: []const u8, device: ?[]const u8) !void if (device) |d| { if (std.mem.eql(u8, d, entry.name[0 .. entry.name.len - ".xml".len])) { - try load_device(db, devices_dir, entry.name, &modules); + try load_device(io, db, devices_dir, entry.name, &modules); return; } } else { - try load_device(db, devices_dir, entry.name, &modules); + try load_device(io, db, devices_dir, entry.name, &modules); } } else if (device != null) { return error.DeviceMissing; } } -fn load_device(db: *Database, devices_dir: std.fs.Dir, filename: []const u8, modules: *std.StringHashMap(ModuleEntry)) !void { - const device_file = try devices_dir.openFile(filename, .{}); - defer device_file.close(); - - const device_text = try device_file.readToEndAlloc(db.gpa, 1024 * 1024); +fn load_device( + io: std.Io, + db: *Database, + devices_dir: std.Io.Dir, + filename: []const u8, + modules: *std.StringHashMapUnmanaged(ModuleEntry), +) !void { + const device_text = try devices_dir.readFileAlloc(io, filename, db.gpa, .unlimited); defer db.gpa.free(device_text); var doc = try xml.Doc.from_memory(device_text); @@ -125,11 +128,18 @@ fn load_device(db: *Database, devices_dir: std.fs.Dir, filename: []const u8, mod var instance_it = cpu_node.iterate(&.{}, &.{"instance"}); while (instance_it.next()) |instance_node| { - try load_instance(db, device_id, devices_dir, instance_node, modules); + try load_instance(io, db, device_id, devices_dir, instance_node, modules); } } -fn load_instance(db: *Database, device_id: DeviceID, devices_dir: std.fs.Dir, node: xml.Node, modules: *std.StringHashMap(ModuleEntry)) !void { +fn load_instance( + io: std.Io, + db: *Database, + device_id: DeviceID, + devices_dir: std.Io.Dir, + node: xml.Node, + modules: *std.StringHashMapUnmanaged(ModuleEntry), +) !void { const name = node.get_attribute("id") orelse return error.MissingField; const href = node.get_attribute("href") orelse return error.MissingField; @@ -145,10 +155,7 @@ fn load_instance(db: *Database, device_id: DeviceID, devices_dir: std.fs.Dir, no // Load the module file for the first time log.debug("Loading new peripheral type from module file: {s}", .{href}); - const module_file = try devices_dir.openFile(href, .{}); - defer module_file.close(); - - const module_text = try module_file.readToEndAlloc(db.gpa, 1024 * 1024); + const module_text = try devices_dir.readFileAlloc(io, href, db.gpa, .unlimited); defer db.gpa.free(module_text); var doc = try xml.Doc.from_memory(module_text); @@ -209,7 +216,7 @@ fn load_instance(db: *Database, device_id: DeviceID, devices_dir: std.fs.Dir, no .base_offset = base_offset, }; const href_copy = try db.gpa.dupe(u8, href); - try modules.put(href_copy, new_module_entry); + try modules.put(db.gpa, href_copy, new_module_entry); break :blk new_module_entry; }; diff --git a/tools/uf2/src/elf2uf2.zig b/tools/uf2/src/elf2uf2.zig index c4fb66e38..ce1107eb2 100644 --- a/tools/uf2/src/elf2uf2.zig +++ b/tools/uf2/src/elf2uf2.zig @@ -37,15 +37,12 @@ fn find_arg(args: []const []const u8, key: []const u8) !?[]const u8 { var elf_reader_buf: [1024]u8 = undefined; var output_writer_buf: [1024]u8 = undefined; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - - const args = try std.process.argsAlloc(gpa.allocator()); - defer std.process.argsFree(gpa.allocator(), args); - +pub fn main(init: std.process.Init) !void { + const io = init.io; + const gpa = init.gpa; + const args = try init.minimal.args.toSlice(init.arena.allocator()); for (args) |arg| if (std.mem.eql(u8, "--help", arg)) { - var writer = std.fs.File.stdout().writer(&.{}); + var writer = std.Io.File.stdout().writer(io, &.{}); try writer.interface.writeAll(usage); return; }; @@ -74,21 +71,21 @@ pub fn main() !void { else null; - var archive = uf2.Archive.init(gpa.allocator()); + var archive = uf2.Archive.init(gpa); defer archive.deinit(); - const elf_file = try std.fs.cwd().openFile(elf_path, .{}); - defer elf_file.close(); - var elf_reader = elf_file.reader(&elf_reader_buf); + const elf_file = try std.Io.Dir.cwd().openFile(io, elf_path, .{}); + defer elf_file.close(io); + var elf_reader = elf_file.reader(io, &elf_reader_buf); try archive.add_elf(&elf_reader, .{ .family_id = family_id, }); - const dest_file = try std.fs.cwd().createFile(output_path, .{}); - defer dest_file.close(); + const dest_file = try std.Io.Dir.cwd().createFile(io, output_path, .{}); + defer dest_file.close(io); - var writer = dest_file.writer(&output_writer_buf); + var writer = dest_file.writer(io, &output_writer_buf); try archive.write_to(&writer.interface); try writer.interface.flush(); } diff --git a/tools/uf2/src/uf2.zig b/tools/uf2/src/uf2.zig index a7f069636..214403139 100644 --- a/tools/uf2/src/uf2.zig +++ b/tools/uf2/src/uf2.zig @@ -77,7 +77,7 @@ pub const Archive = struct { /// Adds an elf to the archive. Returns error if the family id (if /// specified) is already present in the archive. - pub fn add_elf(self: *Archive, reader: *std.fs.File.Reader, opts: ELF_Options) !void { + pub fn add_elf(self: *Archive, reader: *std.Io.File.Reader, opts: ELF_Options) !void { if (opts.family_id) |family_id| if (try self.families.fetchPut(self.allocator, family_id, {}) != null) return error.FamilyIdCollision; diff --git a/website/content/docs/getting-started.smd b/website/content/docs/getting-started.smd index 044ca40e8..5ab73b304 100644 --- a/website/content/docs/getting-started.smd +++ b/website/content/docs/getting-started.smd @@ -22,7 +22,7 @@ used for the [2024 SYCL](https://sycl.it) ## Let's Begin -Quickly check that you have Zig `0.15.1` installed by running `zig version`. +Quickly check that you have Zig `master` installed by running `zig version`. Assuming you've initialized a zig project with `zig init`, or have set one up yourself, you can add microzig as a dependency with the following command: @@ -114,4 +114,4 @@ Copy the uf2 file into the mounted drive. Execution should take place immediatel ## Further Resources -- [Zig Language Reference](https://ziglang.org/documentation/0.15.1/) +- [Zig Language Reference](https://ziglang.org/documentation/master/) diff --git a/website/content/index.smd b/website/content/index.smd index 1ec15f261..3ea4eb4fd 100644 --- a/website/content/index.smd +++ b/website/content/index.smd @@ -28,7 +28,7 @@ Zig is an excellent programming language for embedded systems. explicit and even verbose at times. Nullable pointers, pointer casting, integer conversions, etc. -To learn more: [The Zig Language Reference](https://ziglang.org/documentation/0.15.1/) +To learn more: [The Zig Language Reference](https://ziglang.org/documentation/master/) ## Why MicroZig?