diff --git a/thoughts.md b/thoughts.md new file mode 100644 index 0000000..3df4111 --- /dev/null +++ b/thoughts.md @@ -0,0 +1,262 @@ +# microzig Design Meeting + +## Package structure + +`microzig` package exports all functions and types available in the HAL as well as `microzig.mcu` which will provide access to the MCU. All functions in `microzig` which are not generic will be inline-forwarded to the `board` or `mcu` package. + +``` +root + |- std + |- microzig + | |- mcu (should be specified if "board" does not exit) + | |- build_options (defines if "mcu" and/or "board" exists) + | \- board (must export ".mcu") + | \- mcu + \- mcu (user decision) +``` + +## Minimal Root file + +Declaring `panic` in the root file will cause `builtin` to reference the proper package which will then instantiate microzig which will reference `mcu` package which will instantiate `reset` (or similar) which will invoke `microzig.main()` which will invoke `root.main()`. Oh boy :laughing: + +```zig +const micro = @import("micro"); + +pub const panic = micro.panic; // this will instantiate microzig + +comptime { _ = micro }; // this should not be necessary + +pub fn main() void { + +} +``` + +## `microzig.mcu` + +`microzig` exports a symbol `mcu` which will provide access to the MCU, CPU and board features + +```zig +// mcu.zig in microzig.zig +const config = @import("build_options"); + +usingnamespace if(config.has_board) + @import("board").mcu +else + @import("mcu"); + +pub const has_board = config.has_board; +pub const board = @import("board"); +``` + +## Interrupt API + +All interrupt handlers are static and will be collected from `root.interrupt_handlers`. Each symbol will be verified if it's a valid interrupt vector. +A symbol can be a function `fn() void`, or a anonymous enum literal `.hang` or `.reset`. + +`microzig.interrupts.enable` and `microzig.interrupts.disable` will allow masking/unmasking those interrupts, `.cli()` and `.sei()` will globally disable/enable interrupts + +`microzig.interrupts` also provides some handling of critical sections + +```zig +pub fn main() void { + micro.interrupts.enable(.WDT); // enables the WDT interrupt + micro.interrupts.disable(.WDT); // enables the WDT interrupt + micro.interrupts.enable(.WTF); // yields compile error "WTF is not a valid interrupt" + micro.interrupts.enable(.PCINT0); // yields compile error "PCINT0 has no defined interrupt handler" + micro.interrupts.enable(.NMI); // yields compile error "NMI cannot be masked" + micro.interrupts.enableAll(); + micro.interrupts.batchEnable(.{ .NMI, .WDT }); + + micro.interrupts.cli(); // set interrupt enabled (global enable) + + { // critical section + var crit = micro.interrupts.enterCriticalSection(); + defer crit.leave(); + } +} + +var TIMER2_OVF_RUNTIME: fn()void = foo; + +pub const interrupt_handlers = struct { + + // AVR + pub fn TIMER2_OVF() void { TIMER2_OVF_RUNTIME(); } + pub fn WDT() void { } + + pub const PCINT1 = .reset; + pub const PCINT2 = .hang; + + // cortex-mX exceptions + pub fn NMI() void { } + pub fn HardFault() void {} + + // LPC 1768 interrupt/irq + pub fn SSP1() void { } +}; +``` + +## Timer API + +microzig should allow having a general purpose timer mechanism + +```zig +pub var cpu_frequency = 16.0 * micro.clock.mega_hertz; + +pub const sleep_mode = .timer; // timer, busyloop, whatever + +pub fn main() !void { + led.init(); + + while(true) { + led.toggle(); + micro.sleep(100_000); // sleep 100ms + } +} +``` + +## GPIO API + +`microzig.Pin` parses a pin definition and returns a type that encodes all relevant info and functions to route that pin. +`microzig.Gpio` is a GPIO port/pin configuration that allows modifying pin levels. + +```zig + +// micro.Pin returns a type containing all relevant pin information +const status_led_pin = micro.Pin("PA3"); + +// generate a runtime possible pin that cannot be used in all APIs +var generic_pic: micro.RuntimePin.init(status_led_pin); + +// 4 Bit IEEE-488 bit banging register +const serial_out = micro.GpioOutputRegister(.{ + micro.Pin("PA0"), + micro.Pin("PA1"), + micro.Pin("PA3"), // whoopsies, i miswired, let the software fix that + micro.Pin("PA2"), +}); + +pub fn bitBang(nibble: u4) void { + serial_out.write(nibble); +} + +pub fn main() !void { + + // Route all gpio pins from the bit bang register + inline for(serial_out.pins) |pin| { + pin.route(".gpio"); + } + serial_out.init(); + + // route that pin to UART.RXD + status_led_pin.route(.uart0_rxd); + + //var uart_read_dma_channel = micro.Dma.init(.{.channel = 1}); + + const status_led = micro.Gpio(status_led_pin, .{ + .mode = .output, // { input, output, input_output, open_drain, generic } + .initial_state = .unspecificed, // { unspecified, low, high, floating, driven } + }); + status_led.init(); + + switch(status_led.mode) { + // only reading API is available + .input => { + _ = status_led.read(); + }, + + // reading and writing is available + .output => { + _ = status_led.read(); + status_led.write(.high); + + // "subvariant" of the write + status_led.toggle(); + status_led.setToHigh(); + status_led.setToLow(); + }, + + // reading, writing and changing direction is available + .input_output => { + status_led.setDirection(.input, undefined); + _ = status_led.read(); + status_led.setDirection(.output, .high); // reqires a defined state after setting the direction + status_led.write(.high); + }, + + // reading and setDive is available + .open_drain => { + status_led.setDrive(.disabled); + _ = status_led.read(); + status_led.setDrive(.enabled); + }, + + // will have all available APIs enabled + .generic => {}, + } + + // AVR: PORTA[3] => "PA3" + // NXP: PORT[1][3] => "P1.3" + // PICO: PORT[1] => "P3" + // STM: PORT[A][3] => "A3" + // ESP32: PORT[1] => "P3" +``` + + +## UART example + +```zig +const std = @import("std"); +const micro = @import("µzig"); + +// if const it can be comptime-optimized +pub var cpu_frequency = 100.0 * micro.clock.mega_hertz; + +// if this is enabled, a event loop will run +// in microzig.main() that allows using `async`/`await` "just like that" *grin* +pub const io_mode = .evented; + +pub fn main() !void { + var debug_port = micro.Uart.init(0, .{ + .baud_rate = 9600, + .stop_bits = .@"2", + .parity = .none, // { none, even, odd, mark, space } + .data_bits = .@"8", // 5, 6, 7, 8, or 9 data bits + //.in_dma_channel = 0, + //.out_dma_channel = 0, + }); + + debug_port.configureDMA(???); + + try debug_port.writer().writeAll("Hello, World!"); + + var line_buffer: [64]u8 = undefined; + const len = try debug_port.reader().readUntilDelimiter(&line_buffer, '\n'); +} +``` + + + + +## Initialization + +somewhere inside micro.zig +```zig +extern fn reset() noreturn { + if(@hasDecl(mcu, "mcu_reset")) { + mcu.mcu_reset(); + @unreachable(); + } + // zeroing bss, copying .data, setting stack address + + if(@hasDecl(root, "early_main")) // idk about the name + root.early_main(); + else { + mcu.init(); + + if(@hasDecl(root, "main")) + root.main(); + else + @compileError("main or entry_main missing") + } +} +```