const std = @import("std"); const defs = @import("microzig/build/definitions"); const MemoryRegion = defs.MemoryRegion; pub const Args = struct { cpu_name: []const u8, cpu_arch: std.Target.Cpu.Arch, chip_name: []const u8, memory_regions: []const MemoryRegion, }; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); var arena = std.heap.ArenaAllocator.init(gpa.allocator()); defer arena.deinit(); const allocator = arena.allocator(); const args = try std.process.argsAlloc(allocator); const json_args = args[1]; const output_path = args[2]; const parsed_args = try std.json.parseFromSlice(Args, allocator, json_args, .{}); const program_args = parsed_args.value; const file = try std.fs.cwd().createFile(output_path, .{}); defer file.close(); const writer = file.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 = program_args.cpu_name, .chip = program_args.chip_name, }); try writer.writeAll("MEMORY\n{\n"); { var counters = [4]usize{ 0, 0, 0, 0 }; for (program_args.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 (program_args.cpu_arch) { .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!" ); // \\ // ); }