From 3bdb7381be3f066aa2ea0ed1c2c7ea796141ac55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Quei=C3=9Fner?= Date: Fri, 22 Sep 2023 09:01:19 +0200 Subject: [PATCH] Microzig Generation 2 Build Interface (#144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Starts to rework build framework. * Fully reworks build API, builds with RP2040 again. * Adds docs and doc generation. * Renames source to register_definitions, makes Target.configure optional. * Adds issue tracking links. * Drops CI for now --------- Co-authored-by: Felix "xq" Queißner --- .buildkite/pipeline.yml | 7 - .github/workflows/build.yml | 24 - build.zig | 1063 ++++++++++++++++++++++----- build.zig.zon | 14 + src/{modules => }/cpus/avr5.zig | 0 src/{modules => }/cpus/cortex-m.zig | 0 src/{modules => }/cpus/riscv32.zig | 0 src/modules/Board.zig | 6 - src/modules/Chip.zig | 44 -- src/modules/Cpu.zig | 5 - src/modules/LinkerScriptStep.zig | 172 ----- src/modules/MemoryRegion.zig | 18 - src/modules/cpus.zig | 84 --- 13 files changed, 911 insertions(+), 526 deletions(-) delete mode 100644 .buildkite/pipeline.yml delete mode 100644 .github/workflows/build.yml create mode 100644 build.zig.zon rename src/{modules => }/cpus/avr5.zig (100%) rename src/{modules => }/cpus/cortex-m.zig (100%) rename src/{modules => }/cpus/riscv32.zig (100%) delete mode 100644 src/modules/Board.zig delete mode 100644 src/modules/Chip.zig delete mode 100644 src/modules/Cpu.zig delete mode 100644 src/modules/LinkerScriptStep.zig delete mode 100644 src/modules/MemoryRegion.zig delete mode 100644 src/modules/cpus.zig diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml deleted file mode 100644 index 723f5a2..0000000 --- a/.buildkite/pipeline.yml +++ /dev/null @@ -1,7 +0,0 @@ -steps: - - group: Build and Test - steps: - - label: Debug - command: zig build test - - label: ReleaseSmall - command: zig build test -Doptimize=ReleaseSmall diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index e387a38..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Build -on: - push: - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ - ubuntu-latest, - windows-latest, - macos-latest, - ] - steps: - - uses: actions/checkout@v2 - - - name: Setup Zig - uses: goto-bus-stop/setup-zig@v1.3.0 - with: - version: 0.11.0 - - - name: Build tests - run: zig build test -Doptimize=ReleaseSmall diff --git a/build.zig b/build.zig index f66b2b5..9b92d6b 100644 --- a/build.zig +++ b/build.zig @@ -3,97 +3,419 @@ //! This means we need to use addExecutable() instead of using const std = @import("std"); -const LibExeObjStep = std.Build.LibExeObjStep; -const Module = std.Build.Module; -const LazyPath = std.Build.LazyPath; -const OptionsStep = std.Build.OptionsStep; -const Build = std.Build; - -// alias for packages -pub const LinkerScriptStep = @import("src/modules/LinkerScriptStep.zig"); -pub const cpus = @import("src/modules/cpus.zig"); -pub const Board = @import("src/modules/Board.zig"); -pub const Chip = @import("src/modules/Chip.zig"); -pub const Cpu = @import("src/modules/Cpu.zig"); -pub const MemoryRegion = @import("src/modules/MemoryRegion.zig"); - -pub const Backing = union(enum) { - board: Board, - chip: Chip, +const uf2 = @import("uf2"); + +//////////////////////////////////////// +// MicroZig Gen 2 Interface // +//////////////////////////////////////// + +fn root() []const u8 { + return comptime (std.fs.path.dirname(@src().file) orelse "."); +} +const build_root = root(); + +const MicroZig = @This(); + +b: *std.Build, +self: *std.Build.Dependency, + +/// Creates a new instance of the MicroZig build support. +/// +/// This is necessary as we need to keep track of some internal state to prevent +/// duplicated work per firmware built. +pub fn init(b: *std.Build, dependency_name: []const u8) *MicroZig { + const mz = b.allocator.create(MicroZig) catch @panic("out of memory"); + mz.* = MicroZig{ + .b = b, + .self = b.dependency(dependency_name, .{}), + }; + return mz; +} + +/// This build script validates usage patterns we expect from MicroZig +pub fn build(b: *std.Build) !void { + const uf2_dep = b.dependency("uf2", .{}); - pub fn get_target(self: @This()) std.zig.CrossTarget { - return switch (self) { - .board => |brd| brd.chip.cpu.target, - .chip => |chip| chip.cpu.target, + const build_test = b.addTest(.{ + .root_source_file = .{ .path = "build.zig" }, + }); + + build_test.addAnonymousModule("uf2", .{ + .source_file = .{ .cwd_relative = uf2_dep.builder.pathFromRoot("build.zig") }, + }); + + const install_docs = b.addInstallDirectory(.{ + .source_dir = build_test.getEmittedDocs(), + .install_dir = .prefix, + .install_subdir = "docs", + }); + + b.getInstallStep().dependOn(&install_docs.step); + + // const backings = @import("test/backings.zig"); + // const optimize = b.standardOptimizeOption(.{}); + + // const minimal = addEmbeddedExecutable(b, .{ + // .name = "minimal", + // .source_file = .{ + // .path = comptime root_dir() ++ "/test/programs/minimal.zig", + // }, + // .backing = backings.minimal, + // .optimize = optimize, + // }); + + // const has_hal = addEmbeddedExecutable(b, .{ + // .name = "has_hal", + // .source_file = .{ + // .path = comptime root_dir() ++ "/test/programs/has_hal.zig", + // }, + // .backing = backings.has_hal, + // .optimize = optimize, + // }); + + // const has_board = addEmbeddedExecutable(b, .{ + // .name = "has_board", + // .source_file = .{ + // .path = comptime root_dir() ++ "/test/programs/has_board.zig", + // }, + // .backing = backings.has_board, + // .optimize = optimize, + // }); + + // const core_tests = b.addTest(.{ + // .root_source_file = .{ + // .path = comptime root_dir() ++ "/src/core.zig", + // }, + // .optimize = optimize, + // }); + + // const test_step = b.step("test", "build test programs"); + // test_step.dependOn(&minimal.inner.step); + // test_step.dependOn(&has_hal.inner.step); + // test_step.dependOn(&has_board.inner.step); + // test_step.dependOn(&b.addRunArtifact(core_tests).step); +} + +/// The resulting binary format for the firmware file. +/// A lot of embedded systems don't use plain ELF files, thus we provide means +/// to convert the resulting ELF into other common formats. +pub const BinaryFormat = union(enum) { + /// [Executable and Linkable Format](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format), the standard output from the compiler. + elf, + + /// A flat binary, contains only the loaded portions of the firmware with an unspecified base offset. + bin, + + /// The [Intel HEX](https://en.wikipedia.org/wiki/Intel_HEX) format, contains + /// an ASCII description of what memory to load where. + hex, + + /// A [Device Firmware Upgrade](https://www.usb.org/sites/default/files/DFU_1.1.pdf) file. + dfu, + + /// The [USB Flashing Format (UF2)](https://github.com/microsoft/uf2) designed by Microsoft. + uf2: uf2.FamilyId, + + /// The [firmware format](https://docs.espressif.com/projects/esptool/en/latest/esp32/advanced-topics/firmware-image-format.html) used by the [esptool](https://github.com/espressif/esptool) bootloader. + esp, + + /// Custom option for non-standard formats. + custom: *Custom, + + /// Returns the standard extension for the resulting binary file. + pub fn getExtension(format: BinaryFormat) []const u8 { + return switch (format) { + .elf => ".elf", + .bin => ".bin", + .hex => ".hex", + .dfu => ".dfu", + .uf2 => ".uf2", + .esp => ".bin", + + .custom => |c| c.extension, }; } -}; -pub const EmbeddedExecutable = struct { - inner: *LibExeObjStep, + pub const Custom = struct { + /// The standard extension of the format. + extension: []const u8, - pub const AppDependencyOptions = struct { - depend_on_microzig: bool = false, + /// A function that will convert a given `elf` file into the custom output format. + /// + /// The `*Custom` format is passed so contextual information can be obtained by using + /// `@fieldParentPtr` to provide access to tooling. + convert: *const fn (*Custom, elf: std.Build.LazyPath) std.Build.LazyPath, }; - pub fn addAppDependency(exe: *EmbeddedExecutable, name: []const u8, module: *Module, options: AppDependencyOptions) void { - if (options.depend_on_microzig) { - const microzig_module = exe.inner.modules.get("microzig").?; - module.dependencies.put("microzig", microzig_module) catch @panic("OOM"); + const Enum = std.meta.Tag(BinaryFormat); + + const Context = struct { + pub fn hash(self: @This(), fmt: BinaryFormat) u32 { + _ = self; + + var hasher = std.hash.XxHash32.init(0x1337_42_21); + + hasher.update(@tagName(fmt)); + + switch (fmt) { + .elf, .bin, .hex, .dfu, .esp => |val| { + if (@TypeOf(val) != void) @compileError("Missing update: Context.hash now requires special care!"); + }, + + .uf2 => |family_id| hasher.update(@tagName(family_id)), + .custom => |custom| hasher.update(std.mem.asBytes(custom)), + } + + return hasher.final(); } - const app_module = exe.inner.modules.get("app").?; - app_module.dependencies.put(name, module) catch @panic("OOM"); - } + pub fn eql(self: @This(), fmt_a: BinaryFormat, fmt_b: BinaryFormat, index: usize) bool { + _ = self; + _ = index; + if (@as(BinaryFormat.Enum, fmt_a) != @as(BinaryFormat.Enum, fmt_b)) + return false; - pub fn installArtifact(exe: *EmbeddedExecutable, b: *Build) void { - b.installArtifact(exe.inner); - } + return switch (fmt_a) { + .elf, .bin, .hex, .dfu, .esp => |val| { + if (@TypeOf(val) != void) @compileError("Missing update: Context.eql now requires special care!"); + return true; + }, - pub fn addIncludePath(exe: *EmbeddedExecutable, path: LazyPath) void { - exe.inner.addIncludePath(path); - } + .uf2 => |a| (a == fmt_b.uf2), + .custom => |a| (a == fmt_b.custom), + }; + } + }; +}; - pub fn addSystemIncludePath(exe: *EmbeddedExecutable, path: LazyPath) void { - return exe.inner.addSystemIncludePath(path); +/// The CPU model a target uses. +/// +/// The CPUs usually require special care on how to do interrupts, and getting an entry point. +/// +/// MicroZig officially only supports the CPUs listed here, but other CPUs might be provided +/// via the `custom` field. +pub const CpuModel = union(enum) { + avr5, + cortex_m0, + cortex_m0plus, + cortex_m3, + cortex_m4, + riscv32_imac, + + custom: *const Cpu, + + pub fn getDescriptor(model: CpuModel) *const Cpu { + return switch (@as(std.meta.Tag(CpuModel), model)) { + inline else => |tag| &@field(cpus, @tagName(tag)), + .custom => model.custom, + }; } +}; - pub fn addCSourceFile(exe: *EmbeddedExecutable, source: Build.Step.Compile.CSourceFile) void { - exe.inner.addCSourceFile(source); - } +/// A cpu descriptor. +pub const Cpu = struct { + /// Display name of the CPU. + name: []const u8, - pub fn addOptions(exe: *EmbeddedExecutable, module_name: []const u8, options: *OptionsStep) void { - exe.inner.addOptions(module_name, options); - const app_module = exe.inner.modules.get("app").?; - const opt_module = exe.inner.modules.get(module_name).?; - app_module.dependencies.put(module_name, opt_module) catch @panic("OOM"); - } + /// Source file providing startup code and memory initialization routines. + source_file: std.build.LazyPath, - pub fn addObjectFile(exe: *EmbeddedExecutable, source: LazyPath) void { - exe.inner.addObjectFile(source); - } + /// The compiler target we use to compile all the code. + target: std.zig.CrossTarget, }; -fn root_dir() []const u8 { - return std.fs.path.dirname(@src().file) orelse unreachable; -} +/// A descriptor for memory regions in a microcontroller. +pub const MemoryRegion = struct { + /// The type of the memory region for generating a proper linker script. + kind: Kind, + offset: u64, + length: u64, + + pub const Kind = union(enum) { + /// This is a (normally) immutable memory region where the code is stored. + flash, + + /// This is a mutable memory region for data storage. + ram, + + /// This is a memory region that maps MMIO devices. + io, + + /// This is a memory region that exists, but is reserved and must not be used. + reserved, + + /// This is a memory region used for internal linking tasks required by the board support package. + private: PrivateRegion, + }; + + pub const PrivateRegion = struct { + /// The name of the memory region. Will not have an automatic numeric counter and must be unique. + name: []const u8, + + /// Is the memory region executable? + executable: bool, + + /// Is the memory region readable? + readable: bool, -pub const EmbeddedExecutableOptions = struct { + /// Is the memory region writable? + writeable: bool, + }; +}; + +/// Defines a custom microcontroller. +pub const Chip = struct { + /// The display name of the controller. name: []const u8, - source_file: LazyPath, - backing: Backing, - optimize: std.builtin.OptimizeMode = .Debug, - linkerscript_source_file: ?LazyPath = null, + + /// (optional) link to the documentation/vendor page of the controller. + url: ?[]const u8 = null, + + /// The cpu model this controller uses. + cpu: CpuModel, + + /// The provider for register definitions. + register_definition: union(enum) { + /// Use `regz` to create a zig file from a JSON schema. + json: std.Build.LazyPath, + + /// Use `regz` to create a json file from a SVD schema. + svd: std.Build.LazyPath, + + /// Use `regz` to create a zig file from an ATDF schema. + atdf: std.Build.LazyPath, + + /// Use the provided file directly as the chip file. + zig: std.Build.LazyPath, + }, + + /// The memory regions that are present in this chip. + memory_regions: []const MemoryRegion, }; -pub fn addEmbeddedExecutable(b: *Build, opts: EmbeddedExecutableOptions) *EmbeddedExecutable { - const has_board = (opts.backing == .board); - const chip = switch (opts.backing) { - .chip => |chip| chip, - .board => |board| board.chip, - }; +/// Defines a hardware abstraction layer. +pub const HardwareAbstractionLayer = struct { + /// Root source file for this HAL. + source_file: std.Build.LazyPath, +}; + +/// Provides a description of a board. +/// +/// Boards provide additional information to a chip and HAL package. +/// For example, they can list attached peripherials, external crystal frequencies, +/// flash sizes, ... +pub const BoardDefinition = struct { + /// Display name of the board + name: []const u8, + + /// (optional) link to the documentation/vendor page of the board. + url: ?[]const u8 = null, + + /// Provides the root file for the board definition. + source_file: std.Build.LazyPath, +}; + +/// The linker script used to link the firmware. +pub const LinkerScript = union(enum) { + /// Auto-generated linker script derived from the memory regions of the chip. + generated, + + /// Externally defined linker script. + source_file: std.build.LazyPath, +}; + +/// A compilation target for MicroZig. Provides information about the chip, +/// hal, board and so on. +/// +/// This is used instead of `std.zig.CrossTarget` to define a MicroZig Firmware. +pub const Target = struct { + /// The preferred binary format of this MicroZig target. If `null`, the user must + /// explicitly give the `.format` field during a call to `getEmittedBin()` or installation steps. + preferred_format: ?BinaryFormat, + + /// The chip this target uses, + chip: Chip, + + /// Usually, embedded projects are single-threaded and single-core applications. Platforms that + /// support multiple CPUs should set this to `false`. + single_threaded: bool = true, + + /// Determines whether the compiler_rt package is bundled with the application or not. + /// This should always be true except for platforms where compiler_rt cannot be built right now. + bundle_compiler_rt: bool = true, + + /// (optional) Provides a default hardware abstraction layer that is used. + /// If `null`, no `microzig.hal` will be available. + hal: ?HardwareAbstractionLayer = null, + + /// (optional) Provides description of external hardware and connected devices + /// like oscillators and such. + /// + /// This structure isn't used by MicroZig itself, but can be utilized from the HAL + /// if present. + board: ?BoardDefinition = null, + + /// (optional) Provide a custom linker script for the hardware or define a custom generation. + linker_script: LinkerScript = .generated, + + /// (optional) Further configures the created firmware depending on the chip and/or board settings. + /// This can be used to set/change additional properties on the created `*Firmware` object. + configure: ?*const fn (host_build: *std.Build, *Firmware) void = null, - const has_hal = chip.hal != null; + /// (optional) Post processing step that will patch up and modify the elf file if necessary. + binary_post_process: ?*const fn (host_build: *std.Build, std.Build.LazyPath) std.Build.LazyPath = null, +}; + +/// Options to the `addFirmware` function. +pub const FirmwareOptions = struct { + /// The name of the firmware file. + name: []const u8, + + /// The MicroZig target that the firmware is built for. Either a board or a chip. + target: Target, + + /// The optimization level that should be used. Usually `ReleaseSmall` or `Debug` is a good choice. + /// Also using `std.Build.standardOptimizeOption` is a good idea. + optimize: std.builtin.OptimizeMode, + + /// The root source file for the application. This is your `src/main.zig` file. + source_file: std.Build.LazyPath, + + // Overrides: + + /// If set, overrides the `single_threaded` property of the target. + single_threaded: ?bool = null, + + /// If set, overrides the `bundle_compiler_rt` property of the target. + bundle_compiler_rt: ?bool = null, + + /// If set, overrides the `hal` property of the target. + hal: ?HardwareAbstractionLayer = null, + + /// If set, overrides the `board` property of the target. + board: ?BoardDefinition = null, + + /// If set, overrides the `linker_script` property of the target. + linker_script: ?LinkerScript = null, +}; + +/// Declares a new MicroZig firmware file. +pub fn addFirmware( + /// The MicroZig instance that should be used to create the firmware. + mz: *MicroZig, + /// The instance of the `build.zig` that is calling this function. + host_build: *std.Build, + /// Options that define how the firmware is built. + options: FirmwareOptions, +) *Firmware { + const micro_build = mz.self.builder; + + const chip = &options.target.chip; + const cpu = chip.cpu.getDescriptor(); + const maybe_hal = options.hal orelse options.target.hal; + const maybe_board = options.board orelse options.target.board; + + const linker_script = options.linker_script orelse options.target.linker_script; // TODO: let the user override which ram section to use the stack on, // for now just using the first ram section in the memory region list @@ -104,142 +426,551 @@ pub fn addEmbeddedExecutable(b: *Build, opts: EmbeddedExecutableOptions) *Embedd } else @panic("no ram memory region found for setting the end-of-stack address"); }; - const config = b.addOptions(); - config.addOption(bool, "has_hal", has_hal); - config.addOption(bool, "has_board", has_board); - if (has_board) - config.addOption([]const u8, "board_name", opts.backing.board.name); + // On demand, generate chip definitions via regz: + const chip_source = switch (chip.register_definition) { + .json, .atdf, .svd => |file| blk: { + const regz_exe = mz.dependency("regz", .{ .optimize = .ReleaseSafe }).artifact("regz"); + + const regz_gen = host_build.addRunArtifact(regz_exe); + + regz_gen.addArg("--schema"); // Explicitly set schema type, one of: svd, atdf, json + regz_gen.addArg(@tagName(chip.register_definition)); + + regz_gen.addArg("--output_path"); // Write to a file + const zig_file = regz_gen.addOutputFileArg("chip.zig"); + + regz_gen.addFileArg(file); + + break :blk zig_file; + }, + + .zig => |src| src, + }; + + const config = host_build.addOptions(); + config.addOption(bool, "has_hal", (maybe_hal != null)); + config.addOption(bool, "has_board", (maybe_board != null)); + + config.addOption(?[]const u8, "board_name", if (maybe_board) |brd| brd.name else null); config.addOption([]const u8, "chip_name", chip.name); config.addOption([]const u8, "cpu_name", chip.name); config.addOption(usize, "end_of_stack", first_ram.offset + first_ram.length); - const microzig_module = b.createModule(.{ - .source_file = .{ .path = comptime std.fmt.comptimePrint("{s}/src/microzig.zig", .{root_dir()}) }, - }); + const fw: *Firmware = host_build.allocator.create(Firmware) catch @panic("out of memory"); + fw.* = Firmware{ + .mz = mz, + .host_build = host_build, + .artifact = host_build.addExecutable(.{ + .name = options.name, + .optimize = options.optimize, + .target = cpu.target, + .linkage = .static, + .root_source_file = .{ .cwd_relative = mz.self.builder.pathFromRoot("src/start.zig") }, + }), + .target = options.target, + .output_files = Firmware.OutputFileMap.init(host_build.allocator), + + .config = config, - microzig_module.dependencies.put("config", b.createModule(.{ - .source_file = config.getSource(), - })) catch unreachable; + .modules = .{ + .microzig = micro_build.createModule(.{ + .source_file = .{ .cwd_relative = micro_build.pathFromRoot("src/microzig.zig") }, + .dependencies = &.{ + .{ + .name = "config", + .module = micro_build.createModule(.{ .source_file = config.getSource() }), + }, + }, + }), + + .cpu = undefined, + .chip = undefined, + + .board = null, + .hal = null, - microzig_module.dependencies.put("chip", b.createModule(.{ - .source_file = chip.source, + .app = undefined, + }, + }; + errdefer fw.output_files.deinit(); + + fw.modules.chip = micro_build.createModule(.{ + .source_file = chip_source, .dependencies = &.{ - .{ .name = "microzig", .module = microzig_module }, + .{ .name = "microzig", .module = fw.modules.microzig }, }, - })) catch unreachable; + }); + fw.modules.microzig.dependencies.put("chip", fw.modules.chip) catch @panic("out of memory"); - microzig_module.dependencies.put("cpu", b.createModule(.{ - .source_file = chip.cpu.source, + fw.modules.cpu = micro_build.createModule(.{ + .source_file = cpu.source_file, .dependencies = &.{ - .{ .name = "microzig", .module = microzig_module }, + .{ .name = "microzig", .module = fw.modules.microzig }, }, - })) catch unreachable; + }); + fw.modules.microzig.dependencies.put("cpu", fw.modules.cpu) catch @panic("out of memory"); - if (chip.hal) |hal_module_source| { - microzig_module.dependencies.put("hal", b.createModule(.{ - .source_file = hal_module_source, + if (maybe_hal) |hal| { + fw.modules.hal = micro_build.createModule(.{ + .source_file = hal.source_file, .dependencies = &.{ - .{ .name = "microzig", .module = microzig_module }, + .{ .name = "microzig", .module = fw.modules.microzig }, }, - })) catch unreachable; + }); + fw.modules.microzig.dependencies.put("hal", fw.modules.hal.?) catch @panic("out of memory"); } - switch (opts.backing) { - .board => |board| { - microzig_module.dependencies.put("board", b.createModule(.{ - .source_file = board.source, - .dependencies = &.{ - .{ .name = "microzig", .module = microzig_module }, - }, - })) catch unreachable; - }, - else => {}, + if (maybe_board) |brd| { + fw.modules.board = micro_build.createModule(.{ + .source_file = brd.source_file, + .dependencies = &.{ + .{ .name = "microzig", .module = fw.modules.microzig }, + }, + }); + fw.modules.microzig.dependencies.put("board", fw.modules.board.?) catch @panic("out of memory"); } - const app_module = b.createModule(.{ - .source_file = opts.source_file, + fw.modules.app = host_build.createModule(.{ + .source_file = options.source_file, .dependencies = &.{ - .{ .name = "microzig", .module = microzig_module }, + .{ .name = "microzig", .module = fw.modules.microzig }, }, }); - const exe = b.allocator.create(EmbeddedExecutable) catch unreachable; - exe.* = EmbeddedExecutable{ - .inner = b.addExecutable(.{ - .name = opts.name, - .root_source_file = .{ .path = comptime std.fmt.comptimePrint("{s}/src/start.zig", .{root_dir()}) }, - .target = chip.cpu.target, - .optimize = opts.optimize, - }), - }; - exe.inner.addModule("app", app_module); - exe.inner.addModule("microzig", microzig_module); + fw.artifact.addModule("app", fw.modules.app); + fw.artifact.addModule("microzig", fw.modules.microzig); - exe.inner.strip = false; // we always want debug symbols, stripping brings us no benefit on embedded + fw.artifact.strip = false; // we always want debug symbols, stripping brings us no benefit on embedded + fw.artifact.single_threaded = options.single_threaded orelse fw.target.single_threaded; + fw.artifact.bundle_compiler_rt = options.bundle_compiler_rt orelse fw.target.bundle_compiler_rt; - // might not be true for all machines (Pi Pico), but - // for the HAL it's true (it doesn't know the concept of threading) - exe.inner.single_threaded = true; + switch (linker_script) { + .generated => { + fw.artifact.setLinkerScript( + generateLinkerScript(host_build, chip.*) catch @panic("out of memory"), + ); + }, - if (opts.linkerscript_source_file) |linkerscript_source_file| { - exe.inner.setLinkerScriptPath(linkerscript_source_file); - } else { - const linkerscript = LinkerScriptStep.create(b, chip) catch unreachable; - exe.inner.setLinkerScriptPath(.{ .generated = &linkerscript.generated_file }); + .source_file => |source| { + fw.artifact.setLinkerScriptPath(source); + }, } - // TODO: - // - Generate the linker scripts from the "chip" or "board" module instead of using hardcoded ones. - // - This requires building another tool that runs on the host that compiles those files and emits the linker script. - // - src/tools/linkerscript-gen.zig is the source file for this - exe.inner.bundle_compiler_rt = (exe.inner.target.getCpuArch() != .avr); // don't bundle compiler_rt for AVR as it doesn't compile right now + if (options.target.configure) |configure| { + configure(host_build, fw); + } - return exe; + return fw; } -/// This build script validates usage patterns we expect from MicroZig -pub fn build(b: *Build) !void { - const backings = @import("test/backings.zig"); - const optimize = b.standardOptimizeOption(.{}); - - const minimal = addEmbeddedExecutable(b, .{ - .name = "minimal", - .source_file = .{ - .path = comptime root_dir() ++ "/test/programs/minimal.zig", - }, - .backing = backings.minimal, - .optimize = optimize, +/// Configuration options for firmware installation. +pub const InstallFirmwareOptions = struct { + /// Overrides the output format for the binary. If not set, the standard preferred file format for the firmware target is used. + format: ?BinaryFormat = null, +}; + +/// Adds a new dependency to the `install` step that will install the `firmware` into the folder `$prefix/firmware`. +pub fn installFirmware( + /// The MicroZig instance that was used to create the firmware. + mz: *MicroZig, + /// The instance of the `build.zig` that should perform installation. + b: *std.Build, + /// The firmware that should be installed. Please make sure that this was created with the same `MicroZig` instance as `mz`. + firmware: *Firmware, + /// Optional configuration of the installation process. Pass `.{}` if you're not sure what to do here. + options: InstallFirmwareOptions, +) void { + std.debug.assert(mz == firmware.mz); + const install_step = addInstallFirmware(mz, b, firmware, options); + b.getInstallStep().dependOn(&install_step.step); +} + +/// Creates a new `std.Build.Step.InstallFile` instance that will install the given firmware to `$prefix/firmware`. +/// +/// **NOTE:** This does not actually install the firmware yet. You have to add the returned step as a dependency to another step. +/// If you want to just install the firmware, use `installFirmware` instead! +pub fn addInstallFirmware( + /// The MicroZig instance that was used to create the firmware. + mz: *MicroZig, + /// The instance of the `build.zig` that should perform installation. + b: *std.Build, + /// The firmware that should be installed. Please make sure that this was created with the same `MicroZig` instance as `mz`. + firmware: *Firmware, + /// Optional configuration of the installation process. Pass `.{}` if you're not sure what to do here. + options: InstallFirmwareOptions, +) *std.Build.Step.InstallFile { + const format = firmware.resolveFormat(options.format); + + const basename = b.fmt("{s}{s}", .{ + firmware.artifact.name, + format.getExtension(), }); - const has_hal = addEmbeddedExecutable(b, .{ - .name = "has_hal", - .source_file = .{ - .path = comptime root_dir() ++ "/test/programs/has_hal.zig", + _ = mz; + + return b.addInstallFileWithDir(firmware.getEmittedBin(format), .{ .custom = "firmware" }, basename); +} + +/// Declaration of a firmware build. +pub const Firmware = struct { + const OutputFileMap = std.ArrayHashMap(BinaryFormat, std.Build.LazyPath, BinaryFormat.Context, false); + + const Modules = struct { + app: *std.Build.Module, + cpu: *std.Build.Module, + chip: *std.Build.Module, + board: ?*std.Build.Module, + hal: ?*std.Build.Module, + microzig: *std.Build.Module, + }; + + // privates: + mz: *MicroZig, + host_build: *std.Build, + target: Target, + output_files: OutputFileMap, + + // publics: + + /// The artifact that is built by Zig. + artifact: *std.Build.Step.Compile, + + /// The options step that provides `microzig.config`. If you need custom configuration, you can add this here. + config: *std.Build.Step.Options, + + /// Declaration of the MicroZig modules used by this firmware. + modules: Modules, + + /// Path to the emitted elf file, if any. + emitted_elf: ?std.Build.LazyPath = null, + + /// Returns the emitted ELF file for this firmware. This is useful if you need debug information + /// or want to use a debugger like Segger, ST-Link or similar. + /// + /// **NOTE:** This is similar, but not equivalent to `std.Build.Step.Compile.getEmittedBin`. The call on the compile step does + /// not include post processing of the ELF files necessary by certain targets. + pub fn getEmittedElf(firmware: *Firmware) std.Build.LazyPath { + if (firmware.emitted_elf == null) { + const raw_elf = firmware.artifact.getEmittedBin(); + firmware.emitted_elf = if (firmware.target.binary_post_process) |binary_post_process| + binary_post_process(firmware.host_build, raw_elf) + else + raw_elf; + } + return firmware.emitted_elf.?; + } + + /// Returns the emitted binary for this firmware. The file is either in the preferred file format for + /// the target or in `format` if not null. + /// + /// **NOTE:** The file returned here is the same file that will be installed. + pub fn getEmittedBin(firmware: *Firmware, format: ?BinaryFormat) std.Build.LazyPath { + const actual_format = firmware.resolveFormat(format); + + const gop = firmware.output_files.getOrPut(actual_format) catch @panic("out of memory"); + if (!gop.found_existing) { + const elf_file = firmware.getEmittedElf(); + + const basename = firmware.host_build.fmt("{s}{s}", .{ + firmware.artifact.name, + actual_format.getExtension(), + }); + + gop.value_ptr.* = switch (actual_format) { + .elf => elf_file, + + .bin => blk: { + const objcopy = firmware.host_build.addObjCopy(elf_file, .{ + .basename = basename, + .format = .bin, + }); + + break :blk objcopy.getOutput(); + }, + + .hex => blk: { + const objcopy = firmware.host_build.addObjCopy(elf_file, .{ + .basename = basename, + .format = .hex, + }); + + break :blk objcopy.getOutput(); + }, + + .uf2 => |family_id| blk: { + const uf2_exe = firmware.mz.dependency("uf2", .{ .optimize = .ReleaseSafe }).artifact("elf2uf2"); + + const convert = firmware.host_build.addRunArtifact(uf2_exe); + + convert.addArg("--family-id"); + convert.addArg(firmware.host_build.fmt("0x{X:0>4}", .{@intFromEnum(family_id)})); + + convert.addArg("--elf-path"); + convert.addFileArg(elf_file); + + convert.addArg("--output-path"); + break :blk convert.addOutputFileArg(basename); + }, + + .dfu => buildConfigError(firmware.host_build, "DFU is not implemented yet. See https://github.com/ZigEmbeddedGroup/microzig/issues/145 for more details!", .{}), + .esp => buildConfigError(firmware.host_build, "ESP firmware image is not implemented yet. See https://github.com/ZigEmbeddedGroup/microzig/issues/146 for more details!", .{}), + + .custom => |generator| generator.convert(generator, elf_file), + }; + } + return gop.value_ptr.*; + } + + pub const AppDependencyOptions = struct { + depend_on_microzig: bool = false, + }; + + /// Adds a regular dependency to your application. + pub fn addAppDependency(fw: *Firmware, name: []const u8, module: *std.Build.Module, options: AppDependencyOptions) void { + if (options.depend_on_microzig) { + module.dependencies.put("microzig", fw.modules.microzig) catch @panic("OOM"); + } + fw.modules.app.dependencies.put(name, module) catch @panic("OOM"); + } + + pub fn addIncludePath(fw: *Firmware, path: std.Build.LazyPath) void { + fw.artifact.addIncludePath(path); + } + + pub fn addSystemIncludePath(fw: *Firmware, path: std.Build.LazyPath) void { + fw.artifact.addSystemIncludePath(path); + } + + pub fn addCSourceFile(fw: *Firmware, source: std.Build.Step.Compile.CSourceFile) void { + fw.artifact.addCSourceFile(source); + } + + pub fn addOptions(fw: *Firmware, module_name: []const u8, options: *std.Build.OptionsStep) void { + fw.artifact.addOptions(module_name, options); + fw.modules.app.dependencies.put( + module_name, + fw.host_build.createModule(.{ + .source_file = options.getOutput(), + }), + ) catch @panic("OOM"); + } + + pub fn addObjectFile(fw: *Firmware, source: std.Build.LazyPath) void { + fw.artifact.addObjectFile(source); + } + + fn resolveFormat(firmware: *Firmware, format: ?BinaryFormat) BinaryFormat { + if (format) |fmt| return fmt; + + if (firmware.target.preferred_format) |fmt| return fmt; + + buildConfigError(firmware.host_build, "{s} has no preferred output format, please provide one in the `format` option.", .{ + firmware.target.chip.name, + }); + } +}; + +pub const cpus = struct { + pub const avr5 = Cpu{ + .name = "AVR5", + .source_file = .{ .path = build_root ++ "/src/cpus/avr5.zig" }, + .target = std.zig.CrossTarget{ + .cpu_arch = .avr, + .cpu_model = .{ .explicit = &std.Target.avr.cpu.avr5 }, + .os_tag = .freestanding, + .abi = .eabi, }, - .backing = backings.has_hal, - .optimize = optimize, - }); + }; - const has_board = addEmbeddedExecutable(b, .{ - .name = "has_board", - .source_file = .{ - .path = comptime root_dir() ++ "/test/programs/has_board.zig", + pub const cortex_m0 = Cpu{ + .name = "ARM Cortex-M0", + .source_file = .{ .path = build_root ++ "/src/cpus/cortex-m.zig" }, + .target = std.zig.CrossTarget{ + .cpu_arch = .thumb, + .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m0 }, + .os_tag = .freestanding, + .abi = .none, }, - .backing = backings.has_board, - .optimize = optimize, - }); + }; + + pub const cortex_m0plus = Cpu{ + .name = "ARM Cortex-M0+", + .source_file = .{ .path = build_root ++ "/src/cpus/cortex-m.zig" }, + .target = std.zig.CrossTarget{ + .cpu_arch = .thumb, + .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m0plus }, + .os_tag = .freestanding, + .abi = .none, + }, + }; + + pub const cortex_m3 = Cpu{ + .name = "ARM Cortex-M3", + .source_file = .{ .path = build_root ++ "/src/cpus/cortex-m.zig" }, + .target = std.zig.CrossTarget{ + .cpu_arch = .thumb, + .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m3 }, + .os_tag = .freestanding, + .abi = .none, + }, + }; + + pub const cortex_m4 = Cpu{ + .name = "ARM Cortex-M4", + .source_file = .{ .path = build_root ++ "/src/cpus/cortex-m.zig" }, + .target = std.zig.CrossTarget{ + .cpu_arch = .thumb, + .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 }, + .os_tag = .freestanding, + .abi = .none, + }, + }; - const core_tests = b.addTest(.{ - .root_source_file = .{ - .path = comptime root_dir() ++ "/src/core.zig", + pub const riscv32_imac = Cpu{ + .name = "RISC-V 32-bit", + .source_file = .{ .path = build_root ++ "/src/cpus/riscv32.zig" }, + .target = std.zig.CrossTarget{ + .cpu_arch = .riscv32, + .cpu_model = .{ .explicit = &std.Target.riscv.cpu.sifive_e21 }, + .os_tag = .freestanding, + .abi = .none, }, - .optimize = optimize, + }; +}; + +fn buildConfigError(b: *std.Build, comptime fmt: []const u8, args: anytype) noreturn { + const msg = b.fmt(fmt, args); + @panic(msg); +} + +fn dependency(mz: *MicroZig, name: []const u8, args: anytype) *std.Build.Dependency { + return mz.self.builder.dependency(name, args); +} + +fn generateLinkerScript(b: *std.Build, chip: Chip) !std.Build.LazyPath { + const cpu = chip.cpu.getDescriptor(); + + var contents = std.ArrayList(u8).init(b.allocator); + const writer = contents.writer(); + try writer.print( + \\/* + \\ * This file was auto-generated by microzig + \\ * + \\ * Target CPU: {[cpu]s} + \\ * Target Chip: {[chip]s} + \\ */ + \\ + // This is not the "true" entry point, but there's no such thing on embedded platforms + // anyways. This is the logical entrypoint that should be invoked when + // stack, .data and .bss are set up and the CPU is ready to be used. + \\ENTRY(microzig_main); + \\ + \\ + , .{ + .cpu = cpu.name, + .chip = chip.name, }); - const test_step = b.step("test", "build test programs"); - test_step.dependOn(&minimal.inner.step); - test_step.dependOn(&has_hal.inner.step); - test_step.dependOn(&has_board.inner.step); - test_step.dependOn(&b.addRunArtifact(core_tests).step); + try writer.writeAll("MEMORY\n{\n"); + { + var counters = [4]usize{ 0, 0, 0, 0 }; + for (chip.memory_regions) |region| { + // flash (rx!w) : ORIGIN = 0x00000000, LENGTH = 512k + + switch (region.kind) { + .flash => { + try writer.print(" flash{d} (rx!w)", .{counters[0]}); + counters[0] += 1; + }, + + .ram => { + try writer.print(" ram{d} (rw!x)", .{counters[1]}); + counters[1] += 1; + }, + + .io => { + try writer.print(" io{d} (rw!x)", .{counters[2]}); + counters[2] += 1; + }, + + .reserved => { + try writer.print(" reserved{d} (rw!x)", .{counters[3]}); + counters[3] += 1; + }, + + .private => |custom| { + try writer.print(" {s} (", .{custom.name}); + if (custom.readable) try writer.writeAll("r"); + if (custom.writeable) try writer.writeAll("w"); + if (custom.executable) try writer.writeAll("x"); + + if (!custom.readable or !custom.writeable or !custom.executable) { + try writer.writeAll("!"); + if (!custom.readable) try writer.writeAll("r"); + if (!custom.writeable) try writer.writeAll("w"); + if (!custom.executable) try writer.writeAll("x"); + } + try writer.writeAll(")"); + }, + } + try writer.print(" : ORIGIN = 0x{X:0>8}, LENGTH = 0x{X:0>8}\n", .{ region.offset, region.length }); + } + } + + try writer.writeAll("}\n\nSECTIONS\n{\n"); + { + try writer.writeAll( + \\ .text : + \\ { + \\ KEEP(*(microzig_flash_start)) + \\ *(.text*) + \\ } > flash0 + \\ + \\ + ); + + switch (cpu.target.getCpuArch()) { + .arm, .thumb => try writer.writeAll( + \\ .ARM.exidx : { + \\ *(.ARM.exidx* .gnu.linkonce.armexidx.*) + \\ } >flash0 + \\ + \\ + ), + else => {}, + } + + try writer.writeAll( + \\ .data : + \\ { + \\ microzig_data_start = .; + \\ *(.rodata*) + \\ *(.data*) + \\ microzig_data_end = .; + \\ } > ram0 AT> flash0 + \\ + \\ .bss (NOLOAD) : + \\ { + \\ microzig_bss_start = .; + \\ *(.bss*) + \\ microzig_bss_end = .; + \\ } > ram0 + \\ + \\ microzig_data_load_start = LOADADDR(.data); + \\ + ); + } + try writer.writeAll("}\n"); + + // TODO: Assert that the flash can actually hold all data! + // try writer.writeAll( + // \\ + // \\ ASSERT( (SIZEOF(.text) + SIZEOF(.data) > LENGTH(flash0)), "Error: .text + .data is too large for flash!" ); + // \\ + // ); + + const write = b.addWriteFiles(); + + return write.add("linker.ld", contents.items); } diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..5f0ac76 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,14 @@ +.{ + .name = "microzig", + .version = "0.1.0", + .dependencies = .{ + .uf2 = .{ + .url = "https://github.com/ZigEmbeddedGroup/uf2/archive/c523a4d23469282f95658a879f5ba925757dc9d9.tar.gz", + .hash = "12208530bdc194e8c1f3405ad681a409c7fabfe82735cd3972ec07c221a7786db03a", + }, + .regz = .{ + .url = "https://github.com/ZigEmbeddedGroup/regz/archive/b0ded63fc284da0ed9f4776eb7d1c4ad7175622d.tar.gz", + .hash = "1220e9299f949d3566a1dc4dd62caf82a06bb6c8ad5a693e62117b0941da9dc55ea2", + }, + }, +} diff --git a/src/modules/cpus/avr5.zig b/src/cpus/avr5.zig similarity index 100% rename from src/modules/cpus/avr5.zig rename to src/cpus/avr5.zig diff --git a/src/modules/cpus/cortex-m.zig b/src/cpus/cortex-m.zig similarity index 100% rename from src/modules/cpus/cortex-m.zig rename to src/cpus/cortex-m.zig diff --git a/src/modules/cpus/riscv32.zig b/src/cpus/riscv32.zig similarity index 100% rename from src/modules/cpus/riscv32.zig rename to src/cpus/riscv32.zig diff --git a/src/modules/Board.zig b/src/modules/Board.zig deleted file mode 100644 index 8e09b4a..0000000 --- a/src/modules/Board.zig +++ /dev/null @@ -1,6 +0,0 @@ -const std = @import("std"); -const Chip = @import("Chip.zig"); - -name: []const u8, -source: std.build.LazyPath, -chip: Chip, diff --git a/src/modules/Chip.zig b/src/modules/Chip.zig deleted file mode 100644 index 3676fc9..0000000 --- a/src/modules/Chip.zig +++ /dev/null @@ -1,44 +0,0 @@ -const std = @import("std"); -const LazyPath = std.build.LazyPath; - -const MemoryRegion = @import("MemoryRegion.zig"); -const Cpu = @import("Cpu.zig"); - -const Chip = @This(); - -name: []const u8, -source: LazyPath, -cpu: Cpu, -hal: ?LazyPath = null, -json_register_schema: ?LazyPath = null, -memory_regions: []const MemoryRegion, - -pub fn from_standard_paths(comptime root_dir: []const u8, args: struct { - name: []const u8, - cpu: Cpu, - memory_regions: []const MemoryRegion, -}) Chip { - return Chip{ - .name = args.name, - .cpu = args.cpu, - .memory_regions = args.memory_regions, - .source = .{ - .path = std.fmt.comptimePrint("{s}/chips/{s}.zig", .{ - root_dir, - args.name, - }), - }, - .hal = .{ - .path = std.fmt.comptimePrint("{s}/hals/{s}.zig", .{ - root_dir, - args.name, - }), - }, - .json_register_schema = .{ - .path = std.fmt.comptimePrint("{s}/chips/{s}.json", .{ - root_dir, - args.name, - }), - }, - }; -} diff --git a/src/modules/Cpu.zig b/src/modules/Cpu.zig deleted file mode 100644 index e4cd307..0000000 --- a/src/modules/Cpu.zig +++ /dev/null @@ -1,5 +0,0 @@ -const std = @import("std"); - -name: []const u8, -source: std.build.LazyPath, -target: std.zig.CrossTarget, diff --git a/src/modules/LinkerScriptStep.zig b/src/modules/LinkerScriptStep.zig deleted file mode 100644 index fb92277..0000000 --- a/src/modules/LinkerScriptStep.zig +++ /dev/null @@ -1,172 +0,0 @@ -const std = @import("std"); -const MemoryRegion = @import("MemoryRegion.zig"); -const Chip = @import("Chip.zig"); -const Step = std.build.Step; -const Build = std.Build; -const GeneratedFile = std.build.GeneratedFile; - -const LinkerscriptStep = @This(); - -step: Step, -generated_file: std.build.GeneratedFile, -chip: Chip, - -pub fn create(owner: *Build, chip: Chip) !*LinkerscriptStep { - var linkerscript = try owner.allocator.create(LinkerscriptStep); - linkerscript.* = LinkerscriptStep{ - .step = Step.init(.{ - .id = .custom, - .name = "linkerscript", - .owner = owner, - .makeFn = make, - }), - .generated_file = .{ - .step = &linkerscript.step, - }, - .chip = chip, - }; - - return linkerscript; -} - -fn make(step: *Step, _: *std.Progress.Node) anyerror!void { - const linkerscript = @fieldParentPtr(LinkerscriptStep, "step", step); - - const owner = linkerscript.step.owner; - const target = linkerscript.chip.cpu.target; - - var contents = std.ArrayList(u8).init(owner.allocator); - const writer = contents.writer(); - try writer.print( - \\/* - \\ * This file was auto-generated by microzig - \\ * - \\ * Target CPU: {s} - \\ * Target Chip: {s} - \\ */ - \\ - // This is not the "true" entry point, but there's no such thing on embedded platforms - // anyways. This is the logical entrypoint that should be invoked when - // stack, .data and .bss are set up and the CPU is ready to be used. - \\ENTRY(microzig_main); - \\ - \\ - , .{ linkerscript.chip.cpu.name, linkerscript.chip.name }); - - try writer.writeAll("MEMORY\n{\n"); - { - var counters = [2]usize{ 0, 0 }; - for (linkerscript.chip.memory_regions) |region| { - // flash (rx!w) : ORIGIN = 0x00000000, LENGTH = 512k - - switch (region.kind) { - .flash => { - try writer.print(" flash{d} (rx!w)", .{counters[0]}); - counters[0] += 1; - }, - .ram => { - try writer.print(" ram{d} (rw!x)", .{counters[1]}); - counters[1] += 1; - }, - .custom => |custom| { - try writer.print(" {s} (", .{custom.name}); - if (custom.readable) try writer.writeAll("r"); - if (custom.writeable) try writer.writeAll("w"); - if (custom.executable) try writer.writeAll("x"); - - if (!custom.readable or !custom.writeable or !custom.executable) { - try writer.writeAll("!"); - if (!custom.readable) try writer.writeAll("r"); - if (!custom.writeable) try writer.writeAll("w"); - if (!custom.executable) try writer.writeAll("x"); - } - try writer.writeAll(")"); - }, - } - try writer.print(" : ORIGIN = 0x{X:0>8}, LENGTH = 0x{X:0>8}\n", .{ region.offset, region.length }); - } - } - - try writer.writeAll("}\n\nSECTIONS\n{\n"); - { - try writer.writeAll( - \\ .text : - \\ { - \\ KEEP(*(microzig_flash_start)) - \\ *(.text*) - \\ } > flash0 - \\ - \\ - ); - - switch (target.getCpuArch()) { - .arm, .thumb => try writer.writeAll( - \\ .ARM.exidx : { - \\ *(.ARM.exidx* .gnu.linkonce.armexidx.*) - \\ } >flash0 - \\ - \\ - ), - else => {}, - } - - try writer.writeAll( - \\ .data : - \\ { - \\ microzig_data_start = .; - \\ *(.rodata*) - \\ *(.data*) - \\ microzig_data_end = .; - \\ } > ram0 AT> flash0 - \\ - \\ .bss (NOLOAD) : - \\ { - \\ microzig_bss_start = .; - \\ *(.bss*) - \\ microzig_bss_end = .; - \\ } > ram0 - \\ - \\ microzig_data_load_start = LOADADDR(.data); - \\ - ); - } - try writer.writeAll("}\n"); - - // TODO: Assert that the flash can actually hold all data! - // try writer.writeAll( - // \\ - // \\ ASSERT( (SIZEOF(.text) + SIZEOF(.data) > LENGTH(flash0)), "Error: .text + .data is too large for flash!" ); - // \\ - // ); - - const filename = try std.fmt.allocPrint(owner.allocator, "{s}_{s}.ld", .{ - linkerscript.chip.name, - linkerscript.chip.cpu.name, - }); - - var hash = owner.cache.hash; - hash.addBytes(linkerscript.chip.name); - hash.addBytes(linkerscript.chip.cpu.name); - - // TODO: hash more information to reduce chance of collision - for (linkerscript.chip.memory_regions) |memory_region| { - hash.add(memory_region.offset); - hash.add(memory_region.length); - } - - const digest = hash.final(); - const dir_path = try owner.cache_root.join(owner.allocator, &.{ - "microzig", - &digest, - }); - - var dir = try owner.cache_root.handle.makeOpenPath(dir_path, .{}); - defer dir.close(); - - const file = try dir.createFile(filename, .{}); - defer file.close(); - - try file.writeAll(contents.items); - const full_path = owner.pathJoin(&.{ dir_path, filename }); - linkerscript.generated_file.path = full_path; -} diff --git a/src/modules/MemoryRegion.zig b/src/modules/MemoryRegion.zig deleted file mode 100644 index 048b2b5..0000000 --- a/src/modules/MemoryRegion.zig +++ /dev/null @@ -1,18 +0,0 @@ -//! This module is meant to be used to define linking apis - -kind: Kind, -offset: u64, -length: u64, - -pub const Kind = union(enum) { - flash, - ram, - custom: RegionSpec, -}; - -pub const RegionSpec = struct { - name: []const u8, - executable: bool, - readable: bool, - writeable: bool, -}; diff --git a/src/modules/cpus.zig b/src/modules/cpus.zig deleted file mode 100644 index a2cf396..0000000 --- a/src/modules/cpus.zig +++ /dev/null @@ -1,84 +0,0 @@ -const std = @import("std"); -const Cpu = @import("Cpu.zig"); - -fn root_dir() []const u8 { - return std.fs.path.dirname(@src().file) orelse "."; -} - -pub const avr5 = Cpu{ - .name = "AVR5", - .source = .{ - .path = std.fmt.comptimePrint("{s}/cpus/avr5.zig", .{root_dir()}), - }, - .target = std.zig.CrossTarget{ - .cpu_arch = .avr, - .cpu_model = .{ .explicit = &std.Target.avr.cpu.avr5 }, - .os_tag = .freestanding, - .abi = .eabi, - }, -}; - -pub const cortex_m0 = Cpu{ - .name = "ARM Cortex-M0", - .source = .{ - .path = std.fmt.comptimePrint("{s}/cpus/cortex-m.zig", .{root_dir()}), - }, - .target = std.zig.CrossTarget{ - .cpu_arch = .thumb, - .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m0 }, - .os_tag = .freestanding, - .abi = .none, - }, -}; - -pub const cortex_m0plus = Cpu{ - .name = "ARM Cortex-M0+", - .source = .{ - .path = std.fmt.comptimePrint("{s}/cpus/cortex-m.zig", .{root_dir()}), - }, - .target = std.zig.CrossTarget{ - .cpu_arch = .thumb, - .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m0plus }, - .os_tag = .freestanding, - .abi = .none, - }, -}; - -pub const cortex_m3 = Cpu{ - .name = "ARM Cortex-M3", - .source = .{ - .path = std.fmt.comptimePrint("{s}/cpus/cortex-m.zig", .{root_dir()}), - }, - .target = std.zig.CrossTarget{ - .cpu_arch = .thumb, - .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m3 }, - .os_tag = .freestanding, - .abi = .none, - }, -}; - -pub const cortex_m4 = Cpu{ - .name = "ARM Cortex-M4", - .source = .{ - .path = std.fmt.comptimePrint("{s}/cpus/cortex-m.zig", .{root_dir()}), - }, - .target = std.zig.CrossTarget{ - .cpu_arch = .thumb, - .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 }, - .os_tag = .freestanding, - .abi = .none, - }, -}; - -pub const riscv32_imac = Cpu{ - .name = "RISC-V 32-bit", - .source = .{ - .path = std.fmt.comptimePrint("{s}/cpus/riscv32.zig", .{root_dir()}), - }, - .target = std.zig.CrossTarget{ - .cpu_arch = .riscv32, - .cpu_model = .{ .explicit = &std.Target.riscv.cpu.sifive_e21 }, - .os_tag = .freestanding, - .abi = .none, - }, -};