Implements (untested) startup for AVR and LPC1768.

wch-ch32v003
Felix (xq) Queißner 3 years ago
parent bbfdb421d8
commit e68a282981

@ -4,35 +4,106 @@
const std = @import("std"); const std = @import("std");
const Board = struct { pub fn build(b: *std.build.Builder) void {
name: []const u8, const mode = b.standardReleaseOptions();
path: []const u8,
chip: Chip,
};
const Chip = struct { const test_step = b.step("test", "Builds and runs the library test suite");
name: []const u8,
path: []const u8,
cpu: Cpu,
};
const Cpu = struct { const BuildConfig = struct { name: []const u8, backing: Backing };
name: []const u8, const all_backings = [_]BuildConfig{
path: []const u8, BuildConfig{ .name = "boards.arduino_nano", .backing = Backing{ .board = pkgs.boards.arduino_nano } },
target: std.zig.CrossTarget, BuildConfig{ .name = "boards.mbed_lpc1768", .backing = Backing{ .board = pkgs.boards.mbed_lpc1768 } },
linker_script: []const u8, BuildConfig{ .name = "chips.atmega328p", .backing = Backing{ .chip = pkgs.chips.atmega328p } },
}; BuildConfig{ .name = "chips.lpc1768", .backing = Backing{ .chip = pkgs.chips.lpc1768 } },
};
pub const Backing = union(enum) { inline for (all_backings) |cfg| {
board: Board, const exe = addEmbeddedExecutable(
chip: Chip, b,
}; "test-minimal-" ++ cfg.name,
"tests/minimal.zig",
cfg.backing,
);
exe.setBuildMode(mode);
exe.install();
test_step.dependOn(&exe.step);
}
}
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 Pkg = std.build.Pkg;
const chip = switch (backing) {
.chip => |c| c,
.board => |b| b.chip,
};
const chip_package = Pkg{
.name = "chip",
.path = chip.path,
.dependencies = &[_]Pkg{
Pkg{
.name = "cpu",
.path = chip.cpu.path,
},
Pkg{
.name = "microzig-linker",
.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 = std.fmt.bufPrint(&ld_file_name, "{s}{}{s}", .{
file_prefix,
std.fmt.fmtSliceHexLower(&hash),
file_suffix,
}) catch unreachable;
break :blk builder.dupe(filename);
};
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 = "src/modules/linker/linker.zig",
});
linkerscript_gen.addBuildOption([]const u8, "microzig_chip_name", chip.name);
linkerscript_gen.addBuildOption([]const u8, "microzig_cpu_name", chip.cpu.name);
linkerscript_gen.addBuildOption([]const u8, "microzig_target_triple", chip.cpu.target.zigTriple(builder.allocator) catch unreachable);
const linkerscript_invocation = linkerscript_gen.run();
linkerscript_invocation.addArg(linker_script_name);
const exe = builder.addExecutable(name, source); const exe = builder.addExecutable(name, source);
// 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.single_threaded = true;
exe.setTarget(chip.cpu.target);
exe.setLinkerScriptPath(linker_script_name);
exe.step.dependOn(&linkerscript_invocation.step);
// TODO: // TODO:
// - Generate the linker scripts from the "chip" or "board" package instead of using hardcoded ones. // - Generate the linker scripts from the "chip" or "board" package instead of using hardcoded ones.
// - This requires building another tool that runs on the host that compiles those files and emits the linker script. // - This requires building another tool that runs on the host that compiles those files and emits the linker script.
@ -41,55 +112,30 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source:
exe.bundle_compiler_rt = false; exe.bundle_compiler_rt = false;
switch (backing) { switch (backing) {
.chip => |chip| { .chip => {
exe.addBuildOption(bool, "microzig_has_board", false); exe.addBuildOption(bool, "microzig_has_board", false);
exe.addBuildOption([]const u8, "microzig_chip_name", chip.name); exe.addBuildOption([]const u8, "microzig_chip_name", chip.name);
exe.addBuildOption([]const u8, "microzig_cpu_name", chip.cpu.name); exe.addBuildOption([]const u8, "microzig_cpu_name", chip.cpu.name);
exe.setTarget(chip.cpu.target);
exe.setLinkerScriptPath(chip.cpu.linker_script);
exe.addPackage(Pkg{ exe.addPackage(Pkg{
.name = "microzig", .name = "microzig",
.path = "src/core/microzig.zig", .path = "src/core/microzig.zig",
.dependencies = &[_]Pkg{ .dependencies = &[_]Pkg{chip_package},
Pkg{
.name = "chip",
.path = chip.path,
.dependencies = &[_]Pkg{
Pkg{
.name = "cpu",
.path = chip.cpu.path,
},
},
},
},
}); });
}, },
.board => |board| { .board => |board| {
exe.addBuildOption(bool, "microzig_has_board", true); exe.addBuildOption(bool, "microzig_has_board", true);
exe.addBuildOption([]const u8, "microzig_board_name", board.name); exe.addBuildOption([]const u8, "microzig_board_name", board.name);
exe.addBuildOption([]const u8, "microzig_chip_name", board.chip.name); exe.addBuildOption([]const u8, "microzig_chip_name", chip.name);
exe.addBuildOption([]const u8, "microzig_cpu_name", board.chip.cpu.name); exe.addBuildOption([]const u8, "microzig_cpu_name", chip.cpu.name);
exe.setTarget(board.chip.cpu.target);
exe.setLinkerScriptPath(board.chip.cpu.linker_script);
exe.addPackage(Pkg{ exe.addPackage(Pkg{
.name = "microzig", .name = "microzig",
.path = "src/core/microzig.zig", .path = "src/core/microzig.zig",
.dependencies = &[_]Pkg{ .dependencies = &[_]Pkg{
chip_package,
Pkg{ Pkg{
.name = "board", .name = "board",
.path = board.path, .path = board.path,
.dependencies = &[_]Pkg{ .dependencies = &[_]Pkg{chip_package},
Pkg{
.name = "chip",
.path = board.chip.path,
.dependencies = &[_]Pkg{
Pkg{
.name = "cpu",
.path = board.chip.cpu.path,
},
},
},
},
}, },
}, },
}); });
@ -98,10 +144,34 @@ fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source:
return exe; 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,
};
const pkgs = struct { const pkgs = struct {
const cpus = struct { const cpus = struct {
const avr5 = Cpu{ const avr5 = Cpu{
.name = "avr", .name = "AVR5",
.path = "src/modules/cpus/avr/avr5.zig", .path = "src/modules/cpus/avr/avr5.zig",
.linker_script = "src/modules/cpus/avr/linker.ld", .linker_script = "src/modules/cpus/avr/linker.ld",
.target = std.zig.CrossTarget{ .target = std.zig.CrossTarget{
@ -112,7 +182,7 @@ const pkgs = struct {
}, },
}; };
const cortex_m3 = Cpu{ const cortex_m3 = Cpu{
.name = "cortex-m3", .name = "ARM Cortex-M3",
.path = "src/modules/cpus/cortex-m3/cortex-m3.zig", .path = "src/modules/cpus/cortex-m3/cortex-m3.zig",
.linker_script = "src/modules/cpus/cortex-m3/linker.ld", .linker_script = "src/modules/cpus/cortex-m3/linker.ld",
.target = std.zig.CrossTarget{ .target = std.zig.CrossTarget{
@ -126,12 +196,12 @@ const pkgs = struct {
const chips = struct { const chips = struct {
const atmega328p = Chip{ const atmega328p = Chip{
.name = "AtMega328p", .name = "ATmega328p",
.path = "src/modules/chips/atmega328p/atmega328p.zig", .path = "src/modules/chips/atmega328p/atmega328p.zig",
.cpu = cpus.avr5, .cpu = cpus.avr5,
}; };
const lpc1768 = Chip{ const lpc1768 = Chip{
.name = "mcu", .name = "NXP LPC1768",
.path = "src/modules/chips/lpc1768/lpc1768.zig", .path = "src/modules/chips/lpc1768/lpc1768.zig",
.cpu = cpus.cortex_m3, .cpu = cpus.cortex_m3,
}; };
@ -139,7 +209,7 @@ const pkgs = struct {
const boards = struct { const boards = struct {
const arduino_nano = Board{ const arduino_nano = Board{
.name = "board", .name = "Arduino Nano",
.path = "src/modules/boards/arduino-nano/arduino-nano.zig", .path = "src/modules/boards/arduino-nano/arduino-nano.zig",
.chip = chips.atmega328p, .chip = chips.atmega328p,
}; };
@ -150,29 +220,3 @@ const pkgs = struct {
}; };
}; };
}; };
pub fn build(b: *std.build.Builder) void {
const mode = b.standardReleaseOptions();
const test_step = b.step("test", "Builds and runs the library test suite");
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 = "chips.atmega328p", .backing = Backing{ .chip = pkgs.chips.atmega328p } },
BuildConfig{ .name = "chips.lpc1768", .backing = Backing{ .chip = pkgs.chips.lpc1768 } },
};
inline for (all_backings) |cfg| {
const exe = addEmbeddedExecutable(
b,
"test-minimal-" ++ cfg.name,
"tests/minimal.zig",
cfg.backing,
);
exe.setBuildMode(mode);
test_step.dependOn(&exe.step);
}
}

@ -20,13 +20,13 @@ pub fn isEnabled(comptime interrupt: anytype) bool {
/// *Set Enable Interrupt*, will enable IRQs globally, but keep the masking done via /// *Set Enable Interrupt*, will enable IRQs globally, but keep the masking done via
/// `enable` and `disable` intact. /// `enable` and `disable` intact.
pub fn sei() void { pub fn sei() void {
@panic("not implemented yet!"); micro.chip.cpu.sei();
} }
/// *Clear Enable Interrupt*, will disable IRQs globally, but keep the masking done via /// *Clear Enable Interrupt*, will disable IRQs globally, but keep the masking done via
/// `enable` and `disable` intact. /// `enable` and `disable` intact.
pub fn cli() void { pub fn cli() void {
@panic("not implemented yet!"); micro.chip.cpu.cli();
} }
/// Returns true, when interrupts are globally enabled via `sei()`. /// Returns true, when interrupts are globally enabled via `sei()`.

@ -1,4 +1,11 @@
const std = @import("std"); const std = @import("std");
const root = @import("root");
/// Provides access to the low level features of the current microchip.
pub const chip = @import("chip");
/// Provides access to the low level features of the CPU.
pub const cpu = chip.cpu;
/// Module that helps with interrupt handling. /// Module that helps with interrupt handling.
pub const interrupts = @import("interrupts.zig"); pub const interrupts = @import("interrupts.zig");
@ -10,6 +17,11 @@ pub const interrupts = @import("interrupts.zig");
/// pub const panic = micro.panic; /// pub const panic = micro.panic;
/// ``` /// ```
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
hang();
}
/// Hangs the processor and will stop doing anything useful. Use with caution!
pub fn hang() noreturn {
while (true) { while (true) {
interrupts.cli(); interrupts.cli();
@ -17,3 +29,48 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur
asm volatile ("" ::: "memory"); asm volatile ("" ::: "memory");
} }
} }
/// This is the logical entry point for microzig.
/// It will invoke the main function from the root source file
/// and provides error return handling as well as a event loop if requested.
///
/// Why is this function exported?
/// This is due to the modular design of microzig to allow the "chip" dependency of microzig
/// to call into our main function here. If we would use a normal function call, we'd have a
/// circular dependency between the `microzig` and `chip` package. This function is also likely
/// to be invoked from assembly, so it's also convenient in that regard.
export fn microzig_main() noreturn {
if (!@hasDecl(root, "main"))
@compileError("The root source file must provide a public function main!");
const main = @field(root, "main");
const info: std.builtin.TypeInfo = @typeInfo(@TypeOf(main));
const invalid_main_msg = "main must be either 'pub fn main() void' or 'pub fn main() !void'.";
if (info != .Fn or info.Fn.args.len > 0)
@compileError(invalid_main_msg);
const return_type = info.Fn.return_type orelse @compileError(invalid_main_msg);
if (info.Fn.calling_convention == .Async)
@compileError("TODO: Embedded event loop not supported yet. Please try again later.");
if (@typeInfo(return_type) == .ErrorUnion) {
main() catch |err| {
// TODO:
// - Compute maximum size on the type of "err"
// - Do not emit error names when std.builtin.strip is set.
var msg: [64]u8 = undefined;
@panic(std.fmt.bufPrint(&msg, "main() returned error {s}", .{@tagName(err)}) catch @panic("main() returned error."));
};
} else {
main();
}
// main returned, just hang around here a bit
hang();
}
comptime {
_ = cpu.startup_logic;
}

@ -0,0 +1,29 @@
pub const chip = @import("chip");
pub const pin_map = .{
// Port A
.D0 = "PD0",
.D1 = "PD1",
.D2 = "PD2",
.D3 = "PD3",
.D4 = "PD4",
.D5 = "PD5",
.D6 = "PD6",
.D7 = "PD7",
// Port B
.D8 = "PB0",
.D9 = "PB1",
.D10 = "PB2",
.D11 = "PB3",
.D12 = "PB4",
.D13 = "PB5",
// Port C (Analog)
.A0 = "PC0",
.A1 = "PC1",
.A2 = "PC2",
.A3 = "PC3",
.A4 = "PC4",
.A5 = "PC5",
.A6 = "ADC6",
.A7 = "ADC7",
};

@ -0,0 +1,6 @@
pub const chip = @import("chip");
pub const chip = @import("chip");
pub const pin_map = .{
// TODO: Fill this
};

@ -0,0 +1,8 @@
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 },
};

@ -0,0 +1,9 @@
const micro_linker = @import("microzig-linker");
pub const cpu = @import("cpu");
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 },
};

@ -0,0 +1,106 @@
const std = @import("std");
pub fn sei() void {
asm volatile ("sei");
}
pub fn cli() void {
asm volatile ("cli");
}
pub const startup_logic = struct {
comptime {
asm (
\\.section microzig_flash_start
\\ jmp _start
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
\\ jmp _unhandled_vector
);
}
export fn _unhandled_vector() callconv(.Naked) noreturn {
@panic("Unhandled interrupt");
}
extern fn microzig_main() noreturn;
export fn _start() callconv(.Naked) noreturn {
// At startup the stack pointer is at the end of RAM
// so, no need to set it manually!
copy_data_to_ram();
clear_bss();
microzig_main();
}
fn copy_data_to_ram() void {
asm volatile (
\\ ; load Z register with the address of the data in flash
\\ ldi r30, lo8(microzig_data_load_start)
\\ ldi r31, hi8(microzig_data_load_start)
\\ ; load X register with address of the data in ram
\\ ldi r26, lo8(microzig_data_start)
\\ ldi r27, hi8(microzig_data_start)
\\ ; load address of end of the data in ram
\\ ldi r24, lo8(microzig_data_end)
\\ ldi r25, hi8(microzig_data_end)
\\ rjmp .L2
\\
\\.L1:
\\ lpm r18, Z+ ; copy from Z into r18 and increment Z
\\ st X+, r18 ; store r18 at location X and increment X
\\
\\.L2:
\\ cp r26, r24
\\ cpc r27, r25 ; check and branch if we are at the end of data
\\ brne .L1
);
// Probably a good idea to add clobbers here, but compiler doesn't seem to care
}
fn clear_bss() void {
asm volatile (
\\ ; load X register with the beginning of bss section
\\ ldi r26, lo8(microzig_bss_start)
\\ ldi r27, hi8(microzig_bss_start)
\\ ; load end of the bss in registers
\\ ldi r24, lo8(microzig_bss_end)
\\ ldi r25, hi8(microzig_bss_end)
\\ ldi r18, 0x00
\\ rjmp .L4
\\
\\.L3:
\\ st X+, r18
\\
\\.L4:
\\ cp r26, r24
\\ cpc r27, r25 ; check and branch if we are at the end of bss
\\ brne .L3
);
// Probably a good idea to add clobbers here, but compiler doesn't seem to care
}
};

@ -1,31 +0,0 @@
MEMORY
{
flash (rx) : ORIGIN = 0, LENGTH = 32K
ram (rw!x) : ORIGIN = 0x800100, LENGTH = 2K
}
SECTIONS
{
.text :
{
KEEP(*(.vectors))
*(.text*)
} > flash
.data :
{
__data_start = .;
*(.rodata*)
*(.data*)
__data_end = .;
} > ram AT> flash
.bss (NOLOAD) :
{
__bss_start = .;
*(.bss*)
__bss_end = .;
} > ram
__data_load_start = LOADADDR(.data);
}

@ -0,0 +1,85 @@
const std = @import("std");
pub fn sei() void {
__enable_irq();
}
pub fn cli() void {
__disable_irq();
}
pub fn __enable_irq() void {
asm volatile ("cpsie i");
}
pub fn __disable_irq() void {
asm volatile ("cpsid i");
}
pub fn __enable_fault_irq() void {
asm volatile ("cpsie f");
}
pub fn __disable_fault_irq() void {
asm volatile ("cpsid f");
}
pub fn __NOP() void {
asm volatile ("nop");
}
pub fn __WFI() void {
asm volatile ("wfi");
}
pub fn __WFE() void {
asm volatile ("wfe");
}
pub fn __SEV() void {
asm volatile ("sev");
}
pub fn __ISB() void {
asm volatile ("isb");
}
pub fn __DSB() void {
asm volatile ("dsb");
}
pub fn __DMB() void {
asm volatile ("dmb");
}
pub fn __CLREX() void {
asm volatile ("clrex");
}
pub const startup_logic = struct {
const InterruptVector = fn () callconv(.C) void;
const VectorTable = extern struct {
initial_stack_pointer: u32,
reset: InterruptVector,
nmi: InterruptVector = unhandledInterrupt,
hard_fault: InterruptVector = unhandledInterrupt,
mpu_fault: InterruptVector = unhandledInterrupt,
bus_fault: InterruptVector = unhandledInterrupt,
usage_fault: InterruptVector = unhandledInterrupt,
reserved: u32 = 0,
};
export const vectors linksection("microzig_flash_start") = VectorTable{
// TODO: How to compute/get the initial stack pointer?
.initial_stack_pointer = 0x1000_7FFC, // HACK: hardcoded, do not keep!
.reset = _start,
};
fn unhandledInterrupt() callconv(.C) noreturn {
@panic("unhandled interrupt");
}
extern fn microzig_main() noreturn;
fn _start() callconv(.C) noreturn {
// TODO:
// - Load .data
// - Clear .bss
microzig_main();
}
};

@ -1,79 +0,0 @@
MEMORY
{
flash (rx!w) : ORIGIN = 0x00000000, LENGTH = 512k
ram0 (rw!x) : ORIGIN = 0x10000000, LENGTH = 32k
ram1 (rw!x) : ORIGIN = 0x2007C000, LENGTH = 32k
}
SECTIONS
{
. = 0;
/* Code-Speicher im Flash ***********/
.text :
{
__code_start__ = .;
LONG( ORIGIN(ram1) + LENGTH(ram1) )
KEEP(*( .isr_vector ));
*(.text)
*(.text.*)
. = ALIGN(4);
__code_end__ = .;
*(.gnu.linkonce.t.*)
*(.glue_7)
*(.glue_7t)
*(.gcc_except_table)
*(.gnu.linkonce.r.*)
} >flash
. = ALIGN(4);
/* contains unwinding information */
.ARM.exidx : {
. = ALIGN(4);
__exidx_start = .;
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
__exidx_end = .;
. = ALIGN(4);
} >flash
. = ALIGN(4);
/* Konstanten im Flash ****************/
.rodata . :
{
. = ALIGN(4);
*(.rodata)
. = ALIGN(4);
*(.rodata.*)
} >flash
. = ALIGN(4);
PROVIDE (__text__end = .);
.data : AT (__text__end)
{
PROVIDE (__data__start = .);
*(.data)
*(.data.*)
*(.gnu.linkonce.d*)
PROVIDE (__data__end = .);
} >ram0
.bss :
{
PROVIDE (__bss__start = .);
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b*)
. = ALIGN(4);
PROVIDE (__bss__end = .);
} >ram0
_end = .;
PROVIDE (end = .);
}

@ -0,0 +1,20 @@
//! 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,
};
};

@ -1,20 +1,138 @@
const std = @import("std"); const std = @import("std");
const build_options = @import("build_options");
const chip = @import("chip"); 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 { pub fn main() !u8 {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = &arena.deinit(); const allocator = &arena.allocator;
const args = try std.process.argsAlloc(allocator); const args = try std.process.argsAlloc(allocator);
if (args.len < 2) { if (args.len < 2) {
std.log.err("Missing CLI argument. Give the output file name!"); std.log.err("Missing CLI argument. Give the output file name!", .{});
return 1; return 1;
} }
if (std.fs.path.dirname(args[1])) |dir| {
try std.fs.cwd().makePath(dir);
}
var dest_file = try std.fs.cwd().createFile(args[1], .{}); var dest_file = try std.fs.cwd().createFile(args[1], .{});
defer dest_file.close(); defer dest_file.close();
try dest_file.writeAll("THIS FILE IS NOT USABLE YET!"); 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(
\\
// 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);
\\
\\
);
try writer.writeAll("MEMORY\n{\n");
{
var counters = [2]usize{ 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;
},
.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
\\
\\
);
if (target.cpu_arch.? == .arm or target.cpu_arch.? == .thumb) {
try writer.writeAll(
\\ .ARM.exidx : {
\\ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
\\ } >flash0
\\
\\
);
}
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!" );
// \\
// );
return 0;
} }

@ -4,5 +4,5 @@ const micro = @import("microzig");
pub const panic = micro.panic; pub const panic = micro.panic;
pub fn main() void { pub fn main() void {
return; // This function will contain the application logic.
} }

Loading…
Cancel
Save