diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35a13f4..9135476 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,8 +9,14 @@ on: jobs: build: - runs-on: ubuntu-latest - + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ + ubuntu-latest, + windows-latest, + macos-latest, + ] steps: - name: Checkout uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index ac2eddc..eacd52b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ zig-cache/ -dev-scripts/ \ No newline at end of file +dev-scripts/ +zig-out diff --git a/build.zig b/build.zig index 6543c41..eb478e8 100644 --- a/build.zig +++ b/build.zig @@ -3,6 +3,13 @@ //! This means we need to use addExecutable() instead of using const std = @import("std"); +const boards = @import("src/modules/boards.zig"); +const chips = @import("src/modules/chips.zig"); +const LinkerscriptStep = @import("src/modules/LinkerScriptStep.zig"); + +const Board = boards.Board; +const Chip = chips.Chip; +const Cpu = chips.Cpu; pub fn build(b: *std.build.Builder) !void { const mode = b.standardReleaseOptions(); @@ -12,9 +19,9 @@ pub fn build(b: *std.build.Builder) !void { const BuildConfig = struct { name: []const u8, backing: Backing }; const all_backings = [_]BuildConfig{ //BuildConfig{ .name = "boards.arduino_nano", .backing = Backing{ .board = pkgs.boards.arduino_nano } }, - BuildConfig{ .name = "boards.mbed_lpc1768", .backing = Backing{ .board = pkgs.boards.mbed_lpc1768 } }, + BuildConfig{ .name = "boards.mbed_lpc1768", .backing = Backing{ .board = boards.mbed_lpc1768 } }, //BuildConfig{ .name = "chips.atmega328p", .backing = Backing{ .chip = pkgs.chips.atmega328p } }, - //BuildConfig{ .name = "chips.lpc1768", .backing = Backing{ .chip = pkgs.chips.lpc1768 } }, + BuildConfig{ .name = "chips.lpc1768", .backing = Backing{ .chip = chips.lpc1768 } }, }; const Test = struct { name: []const u8, source: []const u8 }; @@ -45,7 +52,12 @@ pub fn build(b: *std.build.Builder) !void { } } -fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source: []const u8, backing: Backing) !*std.build.LibExeObjStep { +fn addEmbeddedExecutable( + builder: *std.build.Builder, + name: []const u8, + source: []const u8, + backing: Backing, +) !*std.build.LibExeObjStep { const Pkg = std.build.Pkg; const microzig_base = Pkg{ @@ -71,40 +83,9 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source: .path = .{ .path = chip.cpu.path }, .dependencies = &[_]Pkg{ microzig_base, pkgs.mmio }, }, - Pkg{ - .name = "microzig-linker", - .path = .{ .path = "src/modules/linker/linker.zig" }, - }, }, }; - const linker_script_name = blk: { - const hash = hash_blk: { - var hasher = std.hash.SipHash128(1, 2).init("abcdefhijklmnopq"); - - hasher.update(chip.name); - hasher.update(chip.path); - hasher.update(chip.cpu.name); - hasher.update(chip.cpu.path); - - var mac: [16]u8 = undefined; - hasher.final(&mac); - break :hash_blk mac; - }; - - const file_prefix = "zig-cache/microzig/"; - const file_suffix = ".ld"; - - var ld_file_name: [file_prefix.len + 2 * hash.len + file_suffix.len]u8 = undefined; - const filename = try std.fmt.bufPrint(&ld_file_name, "{s}{}{s}", .{ - file_prefix, - std.fmt.fmtSliceHexLower(&hash), - file_suffix, - }); - - break :blk builder.dupe(filename); - }; - const config_file_name = blk: { const hash = hash_blk: { var hasher = std.hash.SipHash128(1, 2).init("abcdefhijklmnopq"); @@ -156,22 +137,7 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source: .path = .{ .path = config_file_name }, }; - const build_options = builder.addOptions(); - build_options.addOption([]const u8, "microzig_chip_name", chip.name); - build_options.addOption([]const u8, "microzig_cpu_name", chip.cpu.name); - build_options.addOption([]const u8, "microzig_target_triple", try chip.cpu.target.zigTriple(builder.allocator)); - - const linkerscript_gen = builder.addExecutable("linkerscript-gen", "src/tools/linkerscript-gen.zig"); - linkerscript_gen.addPackage(chip_package); - linkerscript_gen.addPackage(Pkg{ - .name = "microzig-linker", - .path = .{ .path = "src/modules/linker/linker.zig" }, - }); - linkerscript_gen.addOptions("build_options", build_options); - - const linkerscript_invocation = linkerscript_gen.run(); - linkerscript_invocation.addArg(linker_script_name); - + const linkerscript = try LinkerscriptStep.create(builder, chip); const exe = builder.addExecutable(name, source); // might not be true for all machines (Pi Pico), but @@ -179,8 +145,7 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source: exe.single_threaded = true; exe.setTarget(chip.cpu.target); - exe.setLinkerScriptPath(.{ .path = linker_script_name }); - exe.step.dependOn(&linkerscript_invocation.step); + exe.setLinkerScriptPath(.{ .generated = &linkerscript.generated_file }); // TODO: // - Generate the linker scripts from the "chip" or "board" package instead of using hardcoded ones. @@ -216,25 +181,6 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source: return exe; } -const Board = struct { - name: []const u8, - path: []const u8, - chip: Chip, -}; - -const Chip = struct { - name: []const u8, - path: []const u8, - cpu: Cpu, -}; - -const Cpu = struct { - name: []const u8, - path: []const u8, - target: std.zig.CrossTarget, - linker_script: []const u8, -}; - pub const Backing = union(enum) { board: Board, chip: Chip, @@ -245,55 +191,4 @@ const pkgs = struct { .name = "microzig-mmio", .path = .{ .path = "src/core/mmio.zig" }, }; - - const cpus = struct { - const avr5 = Cpu{ - .name = "AVR5", - .path = "src/modules/cpus/avr/avr5.zig", - .linker_script = "src/modules/cpus/avr/linker.ld", - .target = std.zig.CrossTarget{ - .cpu_arch = .avr, - .cpu_model = .{ .explicit = &std.Target.avr.cpu.avr5 }, - .os_tag = .freestanding, - .abi = .eabi, - }, - }; - const cortex_m3 = Cpu{ - .name = "ARM Cortex-M3", - .path = "src/modules/cpus/cortex-m3/cortex-m3.zig", - .linker_script = "src/modules/cpus/cortex-m3/linker.ld", - .target = std.zig.CrossTarget{ - .cpu_arch = .arm, - .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m3 }, - .os_tag = .freestanding, - .abi = .none, - }, - }; - }; - - const chips = struct { - const atmega328p = Chip{ - .name = "ATmega328p", - .path = "src/modules/chips/atmega328p/atmega328p.zig", - .cpu = cpus.avr5, - }; - const lpc1768 = Chip{ - .name = "NXP LPC1768", - .path = "src/modules/chips/lpc1768/lpc1768.zig", - .cpu = cpus.cortex_m3, - }; - }; - - const boards = struct { - const arduino_nano = Board{ - .name = "Arduino Nano", - .path = "src/modules/boards/arduino-nano/arduino-nano.zig", - .chip = chips.atmega328p, - }; - const mbed_lpc1768 = Board{ - .name = "mbed LPC1768", - .path = "src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig", - .chip = chips.lpc1768, - }; - }; }; diff --git a/src/core/debug.zig b/src/core/debug.zig index 88d0f7a..8f27bb3 100644 --- a/src/core/debug.zig +++ b/src/core/debug.zig @@ -27,7 +27,9 @@ fn writerWrite(ctx: void, string: []const u8) DebugErr!usize { const DebugWriter = std.io.Writer(void, DebugErr, writerWrite); pub fn write(string: []const u8) void { - if (!micro.config.has_board and !@hasDecl(micro.board, "debugWrite")) + if (!micro.config.has_board) + return; + if (!@hasDecl(micro.board, "debugWrite")) return; micro.board.debugWrite(string); diff --git a/src/tools/linkerscript-gen.zig b/src/modules/LinkerScriptStep.zig similarity index 61% rename from src/tools/linkerscript-gen.zig rename to src/modules/LinkerScriptStep.zig index a23c272..92f42f7 100644 --- a/src/tools/linkerscript-gen.zig +++ b/src/modules/LinkerScriptStep.zig @@ -1,46 +1,72 @@ const std = @import("std"); -const build_options = @import("build_options"); -const chip = @import("chip"); -const micro_linker = @import("microzig-linker"); - -const target = blk: { - @setEvalBranchQuota(20_000); - - const t = std.zig.CrossTarget.parse(std.zig.CrossTarget.ParseOptions{ - .arch_os_abi = build_options.microzig_target_triple, - }) catch unreachable; - std.debug.assert(t.cpu_arch != null); - break :blk t; -}; - -pub fn main() !u8 { - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - - const allocator = &arena.allocator; - - const args = try std.process.argsAlloc(allocator); +const MemoryRegion = @import("MemoryRegion.zig"); +const Chip = @import("chips.zig").Chip; +const Step = std.build.Step; +const Builder = std.build.Builder; +const GeneratedFile = std.build.GeneratedFile; + +const LinkerscriptStep = @This(); + +step: Step, +generated_file: std.build.GeneratedFile, +builder: *Builder, +chip: Chip, + +pub fn create(builder: *Builder, chip: Chip) !*LinkerscriptStep { + var hasher = std.hash.SipHash128(1, 2).init("abcdefhijklmnopq"); + + hasher.update(chip.name); + hasher.update(chip.path); + hasher.update(chip.cpu.name); + hasher.update(chip.cpu.path); + + var mac: [16]u8 = undefined; + hasher.final(&mac); + + const filename = try std.fmt.allocPrint(builder.allocator, "{}{s}", .{ + std.fmt.fmtSliceHexLower(&mac), + ".ld", + }); + + const path = try std.fs.path.join(builder.allocator, &.{ + "zig-cache", + "microzig", + filename, + }); + try std.fs.cwd().makePath(std.fs.path.dirname(path).?); + + var ret = try builder.allocator.create(LinkerscriptStep); + ret.* = LinkerscriptStep{ + .step = Step.init(.custom, "linkerscript", builder.allocator, make), + .generated_file = .{ + .step = &ret.step, + .path = path, + }, + .builder = builder, + .chip = chip, + }; + return ret; +} - if (args.len < 2) { - std.log.err("Missing CLI argument. Give the output file name!", .{}); - return 1; - } +fn make(step: *Step) !void { + const linkerscript = @fieldParentPtr(LinkerscriptStep, "step", step); + const file = try std.fs.cwd().createFile(linkerscript.generated_file.path.?, .{}); + defer file.close(); - if (std.fs.path.dirname(args[1])) |dir| { - try std.fs.cwd().makePath(dir); + const target = linkerscript.chip.cpu.target; + if (target.cpu_arch == null) { + std.log.err("target does not have 'cpu_arch'", .{}); + return error.NoCpuArch; } - var dest_file = try std.fs.cwd().createFile(args[1], .{}); - defer dest_file.close(); - - var writer = dest_file.writer(); - - try writer.writeAll("/*\n * This file was auto-generated by microzig\n *\n"); - try writer.print(" * Target CPU: {s}\n", .{build_options.microzig_cpu_name}); - try writer.print(" * Target Chip: {s}\n", .{build_options.microzig_chip_name}); - try writer.writeAll(" */\n\n"); - - try writer.writeAll( + const writer = file.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 @@ -48,12 +74,12 @@ pub fn main() !u8 { \\ENTRY(microzig_main); \\ \\ - ); + , .{ linkerscript.chip.cpu.name, linkerscript.chip.name }); try writer.writeAll("MEMORY\n{\n"); { var counters = [2]usize{ 0, 0 }; - for (chip.memory_regions) |region| { + for (linkerscript.chip.memory_regions) |region| { // flash (rx!w) : ORIGIN = 0x00000000, LENGTH = 512k switch (region.kind) { @@ -134,5 +160,4 @@ pub fn main() !u8 { // \\ ASSERT( (SIZEOF(.text) + SIZEOF(.data) > LENGTH(flash0)), "Error: .text + .data is too large for flash!" ); // \\ // ); - return 0; } diff --git a/src/modules/MemoryRegion.zig b/src/modules/MemoryRegion.zig new file mode 100644 index 0000000..048b2b5 --- /dev/null +++ b/src/modules/MemoryRegion.zig @@ -0,0 +1,18 @@ +//! 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/boards.zig b/src/modules/boards.zig new file mode 100644 index 0000000..c435044 --- /dev/null +++ b/src/modules/boards.zig @@ -0,0 +1,20 @@ +const chips = @import("chips.zig"); + +const Chip = chips.Chip; +pub const Board = struct { + name: []const u8, + path: []const u8, + chip: Chip, +}; + +pub const arduino_nano = Board{ + .name = "Arduino Nano", + .path = "src/modules/boards/arduino-nano/arduino-nano.zig", + .chip = chips.atmega328p, +}; + +pub const mbed_lpc1768 = Board{ + .name = "mbed LPC1768", + .path = "src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig", + .chip = chips.lpc1768, +}; diff --git a/src/modules/chips.zig b/src/modules/chips.zig new file mode 100644 index 0000000..c736d38 --- /dev/null +++ b/src/modules/chips.zig @@ -0,0 +1,37 @@ +const std = @import("std"); +const cpus = @import("cpus.zig"); +const MemoryRegion = @import("MemoryRegion.zig"); + +fn root() []const u8 { + return std.fs.path.dirname(@src().file) orelse unreachable; +} + +const root_path = root() ++ "/"; + +pub const Chip = struct { + name: []const u8, + path: []const u8, + cpu: cpus.Cpu, + memory_regions: []const MemoryRegion, +}; + +pub const atmega328p = Chip{ + .name = "ATmega328p", + .path = root_path ++ "chips/atmega328p/atmega328p.zig", + .cpu = cpus.avr5, + .memory_regions = &[_]MemoryRegion{ + MemoryRegion{ .offset = 0x000000, .length = 32 * 1024, .kind = .flash }, + MemoryRegion{ .offset = 0x800100, .length = 2048, .kind = .ram }, + }, +}; + +pub const lpc1768 = Chip{ + .name = "NXP LPC1768", + .path = root_path ++ "chips/lpc1768/lpc1768.zig", + .cpu = cpus.cortex_m3, + .memory_regions = &[_]MemoryRegion{ + MemoryRegion{ .offset = 0x00000000, .length = 512 * 1024, .kind = .flash }, + MemoryRegion{ .offset = 0x10000000, .length = 32 * 1024, .kind = .ram }, + MemoryRegion{ .offset = 0x2007C000, .length = 32 * 1024, .kind = .ram }, + }, +}; diff --git a/src/modules/chips/atmega328p/atmega328p.zig b/src/modules/chips/atmega328p/atmega328p.zig index da30901..1d6f83d 100644 --- a/src/modules/chips/atmega328p/atmega328p.zig +++ b/src/modules/chips/atmega328p/atmega328p.zig @@ -1,13 +1,6 @@ const std = @import("std"); -const micro_linker = @import("microzig-linker"); pub const cpu = @import("cpu"); - -pub const memory_regions = [_]micro_linker.MemoryRegion{ - micro_linker.MemoryRegion{ .offset = 0x000000, .length = 32 * 1024, .kind = .flash }, - micro_linker.MemoryRegion{ .offset = 0x800100, .length = 2048, .kind = .ram }, -}; - const Port = enum(u8) { B = 1, C = 2, diff --git a/src/modules/chips/lpc1768/lpc1768.zig b/src/modules/chips/lpc1768/lpc1768.zig index b4a8e05..9341851 100644 --- a/src/modules/chips/lpc1768/lpc1768.zig +++ b/src/modules/chips/lpc1768/lpc1768.zig @@ -1,16 +1,9 @@ const std = @import("std"); const micro = @import("microzig"); -const micro_linker = @import("microzig-linker"); pub const cpu = @import("cpu"); pub const registers = @import("registers.zig"); -pub const memory_regions = [_]micro_linker.MemoryRegion{ - micro_linker.MemoryRegion{ .offset = 0x00000000, .length = 512 * 1024, .kind = .flash }, - micro_linker.MemoryRegion{ .offset = 0x10000000, .length = 32 * 1024, .kind = .ram }, - micro_linker.MemoryRegion{ .offset = 0x2007C000, .length = 32 * 1024, .kind = .ram }, -}; - pub const PinTarget = enum(u2) { func00 = 0b00, func01 = 0b01, diff --git a/src/modules/cpus.zig b/src/modules/cpus.zig new file mode 100644 index 0000000..a58f472 --- /dev/null +++ b/src/modules/cpus.zig @@ -0,0 +1,35 @@ +const std = @import("std"); + +fn root() []const u8 { + return std.fs.path.dirname(@src().file) orelse unreachable; +} + +const root_path = root() ++ "/"; + +pub const Cpu = struct { + name: []const u8, + path: []const u8, + target: std.zig.CrossTarget, +}; + +pub const avr5 = Cpu{ + .name = "AVR5", + .path = root_path ++ "cpus/avr/avr5.zig", + .target = std.zig.CrossTarget{ + .cpu_arch = .avr, + .cpu_model = .{ .explicit = &std.Target.avr.cpu.avr5 }, + .os_tag = .freestanding, + .abi = .eabi, + }, +}; + +pub const cortex_m3 = Cpu{ + .name = "ARM Cortex-M3", + .path = root_path ++ "cpus/cortex-m3/cortex-m3.zig", + .target = std.zig.CrossTarget{ + .cpu_arch = .arm, + .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m3 }, + .os_tag = .freestanding, + .abi = .none, + }, +}; diff --git a/src/modules/cpus/cortex-m3/cortex-m3.zig b/src/modules/cpus/cortex-m3/cortex-m3.zig index 29c1414..4f24e7d 100644 --- a/src/modules/cpus/cortex-m3/cortex-m3.zig +++ b/src/modules/cpus/cortex-m3/cortex-m3.zig @@ -71,11 +71,11 @@ pub const startup_logic = struct { extern fn microzig_main() noreturn; - extern var microzig_data_start: c_void; - extern var microzig_data_end: c_void; - extern var microzig_bss_start: c_void; - extern var microzig_bss_end: c_void; - extern const microzig_data_load_start: c_void; + extern var microzig_data_start: anyopaque; + extern var microzig_data_end: anyopaque; + extern var microzig_bss_start: anyopaque; + extern var microzig_bss_end: anyopaque; + extern const microzig_data_load_start: anyopaque; fn _start() callconv(.C) noreturn { diff --git a/src/modules/linker/linker.zig b/src/modules/linker/linker.zig deleted file mode 100644 index f6508b2..0000000 --- a/src/modules/linker/linker.zig +++ /dev/null @@ -1,20 +0,0 @@ -//! This module is meant to be used to define linking apis - -pub const MemoryRegion = struct { - 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, - }; -};