You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

263 lines
6.8 KiB
Markdown

# 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")
}
}
```