diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0cb064a --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.zig text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3cef7be --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +zig-cache/ diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..2d315ba --- /dev/null +++ b/build.zig @@ -0,0 +1,178 @@ +//! Some words on the build script here: +//! We cannot use a test runner here as we're building for freestanding. +//! This means we need to use addExecutable() instead of using + +const std = @import("std"); + +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, +}; + +fn addEmbeddedExecutable(builder: *std.build.Builder, name: []const u8, source: []const u8, backing: Backing) *std.build.LibExeObjStep { + const Pkg = std.build.Pkg; + + const exe = builder.addExecutable(name, source); + + // TODO: + // - 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. + // - src/tools/linkerscript-gen.zig is the source file for this + + exe.bundle_compiler_rt = false; + + switch (backing) { + .chip => |chip| { + exe.addBuildOption(bool, "microzig_has_board", false); + exe.addBuildOption([]const u8, "microzig_chip_name", chip.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{ + .name = "microzig", + .path = "src/core/microzig.zig", + .dependencies = &[_]Pkg{ + Pkg{ + .name = "chip", + .path = chip.path, + .dependencies = &[_]Pkg{ + Pkg{ + .name = "cpu", + .path = chip.cpu.path, + }, + }, + }, + }, + }); + }, + .board => |board| { + exe.addBuildOption(bool, "microzig_has_board", true); + exe.addBuildOption([]const u8, "microzig_board_name", board.name); + exe.addBuildOption([]const u8, "microzig_chip_name", board.chip.name); + exe.addBuildOption([]const u8, "microzig_cpu_name", board.chip.cpu.name); + exe.setTarget(board.chip.cpu.target); + exe.setLinkerScriptPath(board.chip.cpu.linker_script); + exe.addPackage(Pkg{ + .name = "microzig", + .path = "src/core/microzig.zig", + .dependencies = &[_]Pkg{ + Pkg{ + .name = "board", + .path = board.path, + .dependencies = &[_]Pkg{ + Pkg{ + .name = "chip", + .path = board.chip.path, + .dependencies = &[_]Pkg{ + Pkg{ + .name = "cpu", + .path = board.chip.cpu.path, + }, + }, + }, + }, + }, + }, + }); + }, + } + return exe; +} + +const pkgs = struct { + const cpus = struct { + const avr5 = Cpu{ + .name = "avr", + .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 = "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 = .eabi, + }, + }; + }; + + const chips = struct { + const atmega328p = Chip{ + .name = "AtMega328p", + .path = "src/modules/chips/atmega328p/atmega328p.zig", + .cpu = cpus.avr5, + }; + const lpc1768 = Chip{ + .name = "mcu", + .path = "src/modules/chips/lpc1768/lpc1768.zig", + .cpu = cpus.cortex_m3, + }; + }; + + const boards = struct { + const arduino_nano = Board{ + .name = "board", + .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, + }; + }; +}; + +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); + } +} diff --git a/src/core/gpio.zig b/src/core/gpio.zig new file mode 100644 index 0000000..718e6fb --- /dev/null +++ b/src/core/gpio.zig @@ -0,0 +1,2 @@ +const std = @import("std"); +const micro = @import("microzig.zig"); diff --git a/src/core/interrupts.zig b/src/core/interrupts.zig new file mode 100644 index 0000000..27d158a --- /dev/null +++ b/src/core/interrupts.zig @@ -0,0 +1,58 @@ +const std = @import("std"); +const micro = @import("microzig.zig"); + +/// Unmasks the given interrupt and enables its execution. +/// Note that interrupts must be globally enabled with `sei()` as well. +pub fn enable(comptime interrupt: anytype) void { + @panic("not implemented yet!"); +} + +/// Masks the given interrupt and disables its execution. +pub fn disable(comptime interrupt: anytype) void { + @panic("not implemented yet!"); +} + +/// Returns true when the given interrupt is unmasked. +pub fn isEnabled(comptime interrupt: anytype) bool { + @panic("not implemented yet!"); +} + +/// *Set Enable Interrupt*, will enable IRQs globally, but keep the masking done via +/// `enable` and `disable` intact. +pub fn sei() void { + @panic("not implemented yet!"); +} + +/// *Clear Enable Interrupt*, will disable IRQs globally, but keep the masking done via +/// `enable` and `disable` intact. +pub fn cli() void { + @panic("not implemented yet!"); +} + +/// Returns true, when interrupts are globally enabled via `sei()`. +pub fn areGloballyEnabled() bool { + @panic("not implemented yet!"); +} + +/// Enters a critical section and disables interrupts globally. +/// Call `.leave()` on the return value to restore the previous state. +pub fn enterCriticalSection() CriticalSection { + var section = CriticalSection{ + .enable_on_leave = areGloballyEnabled(), + }; + cli(); + return section; +} + +/// A critical section structure that allows restoring the interrupt +/// status that was set before entering. +const CriticalSection = struct { + enable_on_leave: bool, + + /// Leaves the critical section and restores the interrupt state. + pub fn leave(self: @This()) void { + if (self.enable_on_leave) { + sei(); + } + } +}; diff --git a/src/core/microzig.zig b/src/core/microzig.zig new file mode 100644 index 0000000..463db09 --- /dev/null +++ b/src/core/microzig.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +/// Module that helps with interrupt handling. +pub const interrupts = @import("interrupts.zig"); + +/// The microzig panic handler. Will disable interrupts and loop endlessly. +/// Export this symbol from your main file to enable microzig: +/// ``` +/// const micro = @import("microzig"); +/// pub const panic = micro.panic; +/// ``` +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + while (true) { + interrupts.cli(); + + // "this loop has side effects, don't optimize the endless loop away please. thanks!" + asm volatile ("" ::: "memory"); + } +} diff --git a/src/core/pin.zig b/src/core/pin.zig new file mode 100644 index 0000000..718e6fb --- /dev/null +++ b/src/core/pin.zig @@ -0,0 +1,2 @@ +const std = @import("std"); +const micro = @import("microzig.zig"); diff --git a/src/modules/boards/arduino-nano/arduino-nano.zig b/src/modules/boards/arduino-nano/arduino-nano.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig b/src/modules/boards/mbed-lpc1768/mbed-lpc1768.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/chips/atmega328p/atmega328p.zig b/src/modules/chips/atmega328p/atmega328p.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/chips/lpc1768/lpc1768.zig b/src/modules/chips/lpc1768/lpc1768.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/cpus/avr/avr5.zig b/src/modules/cpus/avr/avr5.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/cpus/avr/linker.ld b/src/modules/cpus/avr/linker.ld new file mode 100644 index 0000000..f17cabd --- /dev/null +++ b/src/modules/cpus/avr/linker.ld @@ -0,0 +1,31 @@ +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); +} \ No newline at end of file diff --git a/src/modules/cpus/cortex-m3/cortex-m3.zig b/src/modules/cpus/cortex-m3/cortex-m3.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/cpus/cortex-m3/linker.ld b/src/modules/cpus/cortex-m3/linker.ld new file mode 100644 index 0000000..17b070a --- /dev/null +++ b/src/modules/cpus/cortex-m3/linker.ld @@ -0,0 +1,79 @@ +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 = .); +} \ No newline at end of file diff --git a/src/tools/linkerscript-gen.zig b/src/tools/linkerscript-gen.zig new file mode 100644 index 0000000..c3266df --- /dev/null +++ b/src/tools/linkerscript-gen.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +const chip = @import("chip"); + +pub fn main() !u8 { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + + const allocator = &arena.deinit(); + + const args = try std.process.argsAlloc(allocator); + if (args.len < 2) { + std.log.err("Missing CLI argument. Give the output file name!"); + return 1; + } + + var dest_file = try std.fs.cwd().createFile(args[1], .{}); + defer dest_file.close(); + + try dest_file.writeAll("THIS FILE IS NOT USABLE YET!"); +} diff --git a/tests/minimal.zig b/tests/minimal.zig new file mode 100644 index 0000000..9ad7f13 --- /dev/null +++ b/tests/minimal.zig @@ -0,0 +1,8 @@ +const micro = @import("microzig"); + +// this will instantiate microzig and pull in all dependencies +pub const panic = micro.panic; + +pub fn main() void { + return; +}