diff --git a/build.zig b/build.zig index 08fe12a..20372a0 100644 --- a/build.zig +++ b/build.zig @@ -36,6 +36,8 @@ pub fn build(b: *std.Build) void { b.installArtifact(elf_file); } + // TODO: Fully automate development flow + // Integrate OpenOCD and GDB const picotool_convert_cmd = b.addSystemCommand(&.{ "picotool", "uf2", @@ -52,4 +54,9 @@ pub fn build(b: *std.Build) void { install.step.dependOn(&picotool_convert_cmd.step); // Copying uf2 depends on picotool b.getInstallStep().dependOn(&install.step); // Install depends on picotool + + // NOTE: Hardcoding file doesn't feel right. Probably should be an option + const picocom = b.step("monitor", "Monitor the /dev/ttyACM0 for debug probe"); + const command = b.addSystemCommand(&.{ "picocom", "-b", "115200", "/dev/ttyACM0" }); + picocom.dependOn(&command.step); } diff --git a/src/hal/clocks.zig b/src/hal/clocks.zig index c9e88e6..85792c7 100644 --- a/src/hal/clocks.zig +++ b/src/hal/clocks.zig @@ -145,9 +145,13 @@ pub const PLL = enum(u1) { // ceil((12 * 10^6 x 1 * 10^-3) % 256) = 47 pub const XOSC_STARTUP_DEFAULT = 47; -pub const XOSC_STABLE_BIT = 31; +const XOSC_STABLE_BIT = 31; +const CLK_ENABLE_BIT = 11; +const CLK_ENABLED_BIT = 28; -pub const XOSC_STABLE_MASK = 1 << XOSC_STABLE_BIT; +const XOSC_STABLE_MASK = 1 << XOSC_STABLE_BIT; +const CLK_ENABLE_MASK = 1 << CLK_ENABLE_BIT; +const CLK_ENABLED_MASK = 1 << CLK_ENABLED_BIT; pub const XOSCConfig = struct { pub const pico2_default = XOSCConfig{ .startup_delay = XOSC_STARTUP_DEFAULT, .freq_range = .FREQ_1_15 }; @@ -171,7 +175,7 @@ pub fn xoscInit(config: XOSCConfig) void { // TODO: Implement clock switches in a better way. // NOTE: Possibly using comptime to limit the enum values for certain clock generators? -pub fn switchCLKRef() void { +pub fn switchCLKref() void { // Use XOSC for CLK_REF hal.mmio.write(hal.registers.CLOCKS_CLK_REF_CTRL, 0x2); @@ -187,3 +191,9 @@ pub fn switchCLKsys() void { hal.mmio.write_set(hal.registers.CLOCKS_CLK_SYS_CTRL, 1); hal.mmio_wait(hal.registers.CLOCKS_CLK_SYS_SELECTED, 0x3, true, 0b10); } + +pub fn enableCLKperi() void { + hal.mmio.write_set(hal.registers.CLOCKS_CLK_PERI_CTRL, CLK_ENABLE_MASK); + + hal.mmio_wait_status(hal.registers.CLOCKS_CLK_PERI_CTRL, CLK_ENABLED_MASK, false); +} diff --git a/src/hal/hal.zig b/src/hal/hal.zig index 5d2d754..2fbc8b1 100644 --- a/src/hal/hal.zig +++ b/src/hal/hal.zig @@ -40,6 +40,7 @@ pub fn dummy_wait(max_cycles: u32) void { } } +// TODO: mmio_wait* must be replaced with a better alternative! pub fn mmio_wait(address: u32, mask: u32, non_equal: bool, value: u32) void { while (switch (non_equal) { true => mmio.read(address) & mask != value, diff --git a/src/hal/peripherals/uart.zig b/src/hal/peripherals/uart.zig index 1eecbf4..c13c11d 100644 --- a/src/hal/peripherals/uart.zig +++ b/src/hal/peripherals/uart.zig @@ -1,5 +1,20 @@ const hal = @import("../hal.zig"); +const UART_LCR_WLEN_BIT = 5; +const UART_LCR_FEN_BIT = 4; +const UART_LCR_WLEN_MASK = 0x3 << UART_LCR_WLEN_BIT; +const UART_LCR_FEN_MASK = 0x1 << UART_LCR_FEN_BIT; + +const UART_CR_TEX_BIT = 8; +const UART_CR_EN_BIT = 0; +const UART_CR_TEX_MASK = 1 << UART_CR_TEX_BIT; +const UART_CR_EN_MASK = 1 << UART_CR_EN_BIT; + +const UART_FR_TXFF_BIT = 5; +const UART_FR_BUSY_BIT = 3; +const UART_FR_TXFF_MASK = 1 << UART_FR_TXFF_BIT; +const UART_FR_BUSY_MASK = 1 << UART_FR_BUSY_BIT; + pub const Uart = enum(u1) { UART0, UART1, @@ -11,6 +26,10 @@ pub const Uart = enum(u1) { }; } + pub fn reg(self: Uart, offset: u32) u32 { + return self.addr() + offset; + } + pub fn subsystem(self: Uart) hal.resets.Subsystems { return switch (self) { .UART0 => .UART0, @@ -20,30 +39,31 @@ pub const Uart = enum(u1) { }; pub fn init(uart: Uart, clk_peri_freq: u32, baudrate: u32) void { - hal.resets.unreset(uart.subsystem()); + hal.resets.restart(uart.subsystem()); + + const baudrate_div: u32 = 4 * clk_peri_freq / baudrate; + const baudrate_divfrac = baudrate_div & 0x3F; + const baudrate_divint = (baudrate_div & (0xFFFFF << 6)) >> 6; - const divisor: f32 = @as(f32, @floatFromInt(clk_peri_freq)) / (16 * @as(f32, @floatFromInt(baudrate))); - const divisor_int: u32 = @intFromFloat(@floor(divisor)); - const divisor_frac: u32 = @intFromFloat(@round((divisor - @as(f32, @floatFromInt(divisor_int))) * 64 + 0.5)); + hal.mmio.write(uart.reg(hal.registers.UART_UARTIBRD), baudrate_divint); + hal.mmio.write(uart.reg(hal.registers.UART_UARTFBRD), baudrate_divfrac); - hal.mmio.write(uart.addr() + hal.registers.UART_UARTIBRD, divisor_int); - hal.mmio.write(uart.addr() + hal.registers.UART_UARTFBRD, divisor_frac); - hal.mmio.write(uart.addr() + hal.registers.UART_UARTLCR_H, 0b111 << 4); - hal.mmio.write_set(uart.addr() + hal.registers.UART_UARTCR, 0b110000001); + // WLEN = 8 bits, FIFO enabled + hal.mmio.write_set(uart.reg(hal.registers.UART_UARTLCR_H), UART_LCR_WLEN_MASK | UART_LCR_FEN_MASK); - // TODO: Proper pins for UART - hal.mmio.write(hal.registers.GPIO_CTRL + (0 * 8), 0x2); // TX - hal.mmio.write(hal.registers.GPIO_CTRL + (1 * 8), 0x2); // RX + // Enable UART and Transmit Enable + hal.mmio.write_set(uart.reg(hal.registers.UART_UARTCR), UART_CR_TEX_MASK | UART_CR_EN_MASK); +} - hal.mmio.write_clr(hal.registers.PADS_BANK0_GPIO + (0 * 4), 1 << 8); - hal.mmio.write_xor(hal.registers.PADS_BANK0_GPIO + (1 * 4), 0b101 << 6); +// TODO: Ditch this naive polling implementation in favor of interrupts and DMA in future +fn wait_fifo(uart: Uart) void { + // Wait until TX Fifo Full is de-asserted + hal.mmio_wait_status(uart.reg(hal.registers.UART_UARTFR), UART_FR_TXFF_MASK, true); } pub fn write(uart: Uart, text: []const u8) void { - for (text) |c| { - while (hal.mmio.read(uart.addr() + hal.registers.UART_UARTFR) & (1 << 5) == 1) { - asm volatile ("add zero, zero, zero"); - } - hal.mmio.write(uart.addr() + hal.registers.UART_UARTDR, c); + for (text) |character| { + wait_fifo(uart); + hal.mmio.write(uart.reg(hal.registers.UART_UARTDR), character); } } diff --git a/src/startup.zig b/src/startup.zig index 858d7a9..3c25566 100644 --- a/src/startup.zig +++ b/src/startup.zig @@ -17,13 +17,18 @@ export fn _main() void { flash_led(1_000_000, 10); clocks.xoscInit(.pico2_default); - clocks.switchCLKRef(); - clocks.PLL.PLL_SYS.applyConfiguration(.pico2_pll_sys_default); + clocks.switchCLKref(); + clocks.PLL.applyConfiguration(.PLL_SYS, .pico2_pll_sys_default); clocks.switchCLKsys(); + clocks.enableCLKperi(); - // FIXME: Seems to not work? - //hal.uart.init(.UART0, 12_000_000, 115200); - //hal.uart.write(.UART0, "Hello!\n"); + // TODO: Refactor the API to feel better + hal.uart.init(.UART0, 150_000_000, 115200); + + hal.mmio.write(hal.registers.GPIO_CTRL + (0 * 8), 0x2); + hal.mmio.write_clr(hal.registers.PADS_BANK0_GPIO + (0 * 4), 1 << 8); + + hal.uart.write(.UART0, "Hello World!\r\n"); flash_led(1_000_000, -1); }