diff --git a/docs/ATPCS.pdf b/docs/ATPCS.pdf new file mode 100644 index 0000000..970a5bb Binary files /dev/null and b/docs/ATPCS.pdf differ diff --git a/docs/DDI0403E_B_armv7m_arm.pdf b/docs/DDI0403E_B_armv7m_arm.pdf new file mode 100644 index 0000000..149f78d Binary files /dev/null and b/docs/DDI0403E_B_armv7m_arm.pdf differ diff --git a/docs/DDI0439B_cortex_m4_r0p0_trm.pdf b/docs/DDI0439B_cortex_m4_r0p0_trm.pdf new file mode 100644 index 0000000..4a36cfe Binary files /dev/null and b/docs/DDI0439B_cortex_m4_r0p0_trm.pdf differ diff --git a/docs/SAM-D5x-E5x-Family-Silicon-Errata-and-Data-Sheet-Clarification-DS80000748.pdf b/docs/SAM-D5x-E5x-Family-Silicon-Errata-and-Data-Sheet-Clarification-DS80000748.pdf new file mode 100644 index 0000000..860d9af Binary files /dev/null and b/docs/SAM-D5x-E5x-Family-Silicon-Errata-and-Data-Sheet-Clarification-DS80000748.pdf differ diff --git a/src/Port.zig b/src/Port.zig new file mode 100644 index 0000000..dfa59b2 --- /dev/null +++ b/src/Port.zig @@ -0,0 +1,110 @@ +pub const TFT_RST: Port = .{ .group = .A, .pin = 0 }; +pub const TFT_LITE: Port = .{ .group = .A, .pin = 1 }; +pub const A0: Port = .{ .group = .A, .pin = 2 }; +pub const AVCC: Port = .{ .group = .A, .pin = 3 }; +pub const A4: Port = .{ .group = .A, .pin = 4 }; +pub const A1: Port = .{ .group = .A, .pin = 5 }; +pub const A5: Port = .{ .group = .A, .pin = 6 }; +pub const @"+3V3": Port = .{ .Group = .A, .pin = 7 }; +pub const QSPI_DATA: [4]Port = .{ + .{ .group = .A, .pin = 8 }, + .{ .group = .A, .pin = 9 }, + .{ .group = .A, .pin = 10 }, + .{ .group = .A, .pin = 11 }, +}; +pub const SDA_3V: Port = .{ .group = .A, .pin = 12 }; +pub const SCL_3V: Port = .{ .group = .A, .pin = 13 }; +pub const D4: Port = .{ .group = .A, .pin = 14 }; +pub const D8_NEOPIX: Port = .{ .group = .A, .pin = 15 }; +pub const D5: Port = .{ .group = .A, .pin = 16 }; +pub const SCK: Port = .{ .group = .A, .pin = 17 }; +pub const D6: Port = .{ .group = .A, .pin = 18 }; +pub const D9: Port = .{ .group = .A, .pin = 19 }; +pub const D10: Port = .{ .group = .A, .pin = 20 }; +pub const D11: Port = .{ .group = .A, .pin = 21 }; +pub const D12: Port = .{ .group = .A, .pin = 22 }; +pub const D13: Port = .{ .group = .A, .pin = 23 }; +pub const @"D-": Port = .{ .group = .A, .pin = 24 }; +pub const @"D+": Port = .{ .group = .A, .pin = 25 }; +// PA26 not present +pub const SPKR_EN: Port = .{ .group = .A, .pin = 27 }; +// PA28-PA29 not present +pub const SWCLK: Port = .{ .group = .A, .pin = 30 }; +pub const SWDIO: Port = .{ .group = .A, .pin = 31 }; + +pub const BUTTON_LATCH: Port = .{ .group = .B, .pin = 0 }; +pub const A6_VMEAS: Port = .{ .group = .B, .pin = 1 }; +pub const D3_A9: Port = .{ .group = .B, .pin = 2 }; +pub const D2_A8: Port = .{ .group = .B, .pin = 3 }; +pub const A7_LIGHT: Port = .{ .group = .B, .pin = 4 }; +pub const TFT_DC: Port = .{ .group = .B, .pin = 5 }; +// PB06 not connected +pub const TFT_CS: Port = .{ .group = .B, .pin = 7 }; +pub const A2: Port = .{ .group = .B, .pin = 8 }; +pub const A3: Port = .{ .group = .B, .pin = 9 }; +pub const QSPI_SCK: Port = .{ .group = .B, .pin = 10 }; +pub const QSPI_CS: Port = .{ .group = .B, .pin = 11 }; +// PB12 not connected +pub const TFT_SCK: Port = .{ .group = .B, .pin = 13 }; +pub const D7_LISIRQ: Port = .{ .group = .B, .pin = 14 }; +pub const TFT_MOSI: Port = .{ .group = .B, .pin = 15 }; +pub const TX: Port = .{ .group = .B, .pin = 16 }; +pub const RX: Port = .{ .group = .B, .pin = 17 }; +// PB18-PB21 not present +pub const MISO: Port = .{ .group = .B, .pin = 22 }; +pub const MOSI: Port = .{ .group = .B, .pin = 23 }; +// PB24-PB29 not present +pub const BUTTON_OUT: Port = .{ .group = .B, .pin = 30 }; +pub const BUTTON_CLK: Port = .{ .group = .B, .pin = 31 }; + +group: Group, +pin: u5, + +pub const Group = enum { A, B }; + +pub inline fn setDir(port: Port, dir: enum { in, out }) void { + switch (dir) { + .in => port.groupPtr().DIRCLR.write(.{ .DIRCLR = @as(u32, 1) << port.pin }), + .out => port.groupPtr().DIRSET.write(.{ .DIRSET = @as(u32, 1) << port.pin }), + } +} + +pub inline fn write(port: Port, value: bool) void { + switch (value) { + false => port.groupPtr().OUTCLR.write(.{ .OUTCLR = @as(u32, 1) << port.pin }), + true => port.groupPtr().OUTSET.write(.{ .OUTSET = @as(u32, 1) << port.pin }), + } +} + +pub inline fn read(port: Port) bool { + return port.groupPtr().IN.read() & @as(u32, 1) << port.pin != 0; +} + +pub const Mux = enum(u4) { A, B, C, D, E, F, G, H, I, J, K, L, M, N }; +pub inline fn setMux(port: Port, mux: Mux) void { + const pmux = &port.groupPtr().PMUX[port.pin / 2]; + switch (@as(u1, @truncate(port.pin))) { + 0 => pmux.modify(.{ .PMUXE = .{ + .value = @as(io_types.PORT.PORT_PMUX__PMUXE, @enumFromInt(@intFromEnum(mux))), + } }), + 1 => pmux.modify(.{ .PMUXO = .{ + .value = @as(io_types.PORT.PORT_PMUX__PMUXO, @enumFromInt(@intFromEnum(mux))), + } }), + } + port.configPtr().modify(.{ .PMUXEN = 1 }); +} + +const PinCfg = @typeInfo(std.meta.FieldType(io_types.PORT.GROUP, .PINCFG)).Array.child; +pub inline fn configPtr(port: Port) *volatile PinCfg { + return &port.groupPtr().PINCFG[port.pin]; +} + +fn groupPtr(port: Port) *volatile io_types.PORT.GROUP { + return &io.PORT.GROUP[@intFromEnum(port.group)]; +} + +const io = microzig.chip.peripherals; +const io_types = microzig.chip.types.peripherals; +const microzig = @import("microzig"); +const Port = @This(); +const std = @import("std"); diff --git a/src/main.zig b/src/main.zig index c27f4d5..b712e10 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,10 +1,11 @@ const builtin = @import("builtin"); const io = microzig.chip.peripherals; const io_types = microzig.chip.types.peripherals; -const led_pad = 1 << 23; const microzig = @import("microzig"); +const Port = @import("Port.zig"); const sleep = microzig.core.experimental.debug.busy_sleep; const std = @import("std"); +const utils = @import("utils.zig"); pub const std_options = struct { pub const log_level = switch (builtin.mode) { @@ -14,6 +15,103 @@ pub const std_options = struct { pub const logFn = log; }; +pub const microzig_options = struct { + const interrupts = .{ + .EIC_EIC_EXTINT_0 = .{ .C = &buttonInterruptTest }, + }; +}; + +const GCLK = struct { + const GEN = struct { + fn Gen(comptime id: u4) type { + const tag = std.fmt.comptimePrint("GCLK{d}", .{id}); + return struct { + const ID = id; + const SYNCBUSY_GENCTRL = @intFromEnum(@field(io_types.GCLK.GCLK_SYNCBUSY__GENCTRL, tag)); + const PCHCTRL_GEN = @field(io_types.GCLK.GCLK_PCHCTRL__GEN, tag); + }; + } + const @"120MHz" = Gen(0); + const @"48MHz" = Gen(2); + const @"1MHz" = Gen(3); + const @"64KHz" = Gen(11); + }; + const PCH = struct { + const OSCCTRL_DFLL48 = 0; + const OSCCTRL_FDPLL0 = 1; + const OSCCTRL_FDPLL1 = 2; + const OSCCTRL_FDPLL0_32K = 3; + const OSCCTRL_FDPLL1_32K = 3; + const SDHC0_SLOW = 3; + const SDHC1_SLOW = 3; + const SERCOM0_SLOW = 3; + const SERCOM1_SLOW = 3; + const SERCOM2_SLOW = 3; + const SERCOM3_SLOW = 3; + const SERCOM4_SLOW = 3; + const SERCOM5_SLOW = 3; + const SERCOM6_SLOW = 3; + const SERCOM7_SLOW = 3; + const EIC = 4; + const FREQM_MSR = 5; + const FREQM_REF = 6; + const SERCOM0_CORE = 7; + const SERCOM1_CORE = 8; + const TC0 = 9; + const TC1 = 9; + const USB = 10; + const EVSYS0 = 11; + const EVSYS1 = 12; + const EVSYS2 = 13; + const EVSYS3 = 14; + const EVSYS4 = 15; + const EVSYS5 = 16; + const EVSYS6 = 17; + const EVSYS7 = 18; + const EVSYS8 = 19; + const EVSYS9 = 20; + const EVSYS10 = 21; + const EVSYS11 = 22; + const SERCOM2_CORE = 23; + const SERCOM3_CORE = 24; + const TCC0_CORE = 25; + const TCC1_CORE = 25; + const TC2 = 26; + const TC3 = 26; + const CAN0 = 27; + const CAN1 = 28; + const TCC2 = 29; + const TCC3 = 29; + const TC4 = 30; + const TC5 = 30; + const PDEC = 31; + const AC = 32; + const CCL = 33; + const SERCOM4_CORE = 34; + const SERCOM5_CORE = 35; + const SERCOM6_CORE = 36; + const SERCOM7_CORE = 37; + const TCC4 = 38; + const TC6 = 39; + const TC7 = 39; + const ADC0 = 40; + const ADC1 = 41; + const DAC = 42; + const I2C = .{ 43, 44 }; + const SDHC0 = 45; + const SDHC1 = 46; + const CM4_TRACE = 47; + }; +}; + +const NVMCTRL = struct { + const SW0: *volatile io_types.FUSES.SW0_FUSES = @ptrFromInt(0x00800080); +}; + +fn buttonInterruptTest() callconv(.C) void { + std.log.scoped(.interrupt).info("buttonInterruptTest();", .{}); +} + const InOutError = error{NoConnection}; pub const in: std.io.Reader(void, InOutError, struct { fn read(_: void, buffer: []u8) InOutError!usize { @@ -46,171 +144,50 @@ pub fn log( out.print("[" ++ level.asText() ++ "] (" ++ @tagName(scope) ++ "): " ++ format ++ "\n", args) catch return; } -fn blink(count: usize, comptime on_time: comptime_int, comptime off_time: comptime_int) void { - io.PORT.GROUP[0].DIR.write(.{ .DIR = 1 << 23 }); - for (0..count) |_| { - io.PORT.GROUP[0].OUT.write(.{ .OUT = 1 << 23 }); - sleep(on_time); - io.PORT.GROUP[0].OUT.write(.{ .OUT = 0 }); - sleep(off_time); - } -} - -//:lib/samd51/include/samd51j19a.h -const HSRAM = struct { - const ADDR: *align(4) [SIZE]u8 = @ptrFromInt(0x20000000); - const SIZE = 0x00030000; -}; - -const NVMCTRL = struct { - const SW0: *volatile [4]u32 = @ptrFromInt(0x00800080); // (NVMCTRL) SW0 Base Address - const SW1: *volatile [4]u32 = @ptrFromInt(0x00800090); // (NVMCTRL) SW1 Base Address - const SW2: *volatile [4]u32 = @ptrFromInt(0x008000A0); // (NVMCTRL) SW2 Base Address - const SW3: *volatile [4]u32 = @ptrFromInt(0x008000B0); // (NVMCTRL) SW3 Base Address - const SW4: *volatile [4]u32 = @ptrFromInt(0x008000C0); // (NVMCTRL) SW4 Base Address - const SW5: *volatile [4]u32 = @ptrFromInt(0x008000D0); // (NVMCTRL) SW5 Base Address - const SW6: *volatile [4]u32 = @ptrFromInt(0x008000E0); // (NVMCTRL) SW6 Base Address - const SW7: *volatile [4]u32 = @ptrFromInt(0x008000F0); // (NVMCTRL) SW7 Base Address -}; - -//:lib/samd51/include/pio/samd51j19a.h -const PIN = struct { - const PA24H_USB_DM = 24; - const PA25H_USB_DP = 25; -}; -const MUX = struct { - const PA24H_USB_DM = 7; - const PA25H_USB_DP = 7; -}; - -const USB = struct { - //:lib/samd51/include/component/nvmctrl.h - const FUSES: *volatile microzig.mmio.Mmio(packed struct(u32) { - TRANSN: u5, - TRANSP: u5, - TRIM: u3, - reserved: u19, - }) = @ptrCast(&NVMCTRL.SW0[1]); - - //:lib/samd51/include/instance/usb.h - const GCLK_ID = 10; -}; +pub fn main() !void { + io.MCLK.AHBMASK.modify(.{ .CMCC_ = 1 }); + io.CMCC.CTRL.write(.{ + .CEN = 1, + .padding = 0, + }); -//:src/init_samd51.c -fn system_init() void { - // Automatic wait states. io.NVMCTRL.CTRLA.modify(.{ .AUTOWS = 1 }); - // Software reset the module to ensure it is re-initialized correctly io.GCLK.CTRLA.write(.{ .SWRST = 1, .padding = 0 }); - // wait for reset to complete while (io.GCLK.SYNCBUSY.read().SWRST != 0) {} - // Temporarily switch the CPU to the internal 32k oscillator while we - // reconfigure the DFLL. - io.GCLK.GENCTRL[0].write(.{ - .SRC = .{ .value = .OSCULP32K }, - .reserved8 = 0, - .GENEN = 1, - .IDC = 0, - .OOV = 0, - .OE = 1, - .DIVSEL = .{ .value = .DIV1 }, - .RUNSTDBY = 0, - .reserved16 = 0, - .DIV = 0, - }); - // Wait for synchronization - while ((io.GCLK.SYNCBUSY.read().GENCTRL.raw & @intFromEnum(io_types.GCLK.GCLK_SYNCBUSY__GENCTRL.GCLK0)) != 0) {} + microzig.cpu.dmb(); - // Configure the DFLL for USB clock recovery. - io.OSCCTRL.DFLLCTRLA.write(.{ - .reserved1 = 0, - .ENABLE = 0, + usb.init(); + // ID detection + const id_port = Port.D13; + id_port.setDir(.out); + id_port.write(true); + id_port.configPtr().write(.{ + .PMUXEN = 0, + .INEN = 1, + .PULLEN = 1, .reserved6 = 0, - .RUNSTDBY = 0, - .ONDEMAND = 0, + .DRVSTR = 0, + .padding = 0, }); - io.OSCCTRL.DFLLMUL.write(.{ - .MUL = 0xBB80, - .FSTEP = 1, - .reserved26 = 0, - .CSTEP = 1, - }); - // Wait for synchronization - while (io.OSCCTRL.DFLLSYNC.read().DFLLMUL != 0) {} - - io.OSCCTRL.DFLLCTRLB.write(.{ - .MODE = 0, - .STABLE = 0, - .LLAW = 0, - .USBCRM = 0, - .CCDIS = 0, - .QLDIS = 0, - .BPLCKC = 0, - .WAITLOCK = 0, - }); - // Wait for synchronization - while (io.OSCCTRL.DFLLSYNC.read().DFLLCTRLB != 0) {} - - io.OSCCTRL.DFLLCTRLA.write(.{ - .reserved1 = 0, - .ENABLE = 1, + usb.reinitMode(.DEVICE); + + // button testing + Port.BUTTON_OUT.setDir(.in); + Port.BUTTON_OUT.configPtr().write(.{ + .PMUXEN = 0, + .INEN = 1, + .PULLEN = 0, .reserved6 = 0, - .RUNSTDBY = 0, - .ONDEMAND = 0, - }); - // Wait for synchronization - while (io.OSCCTRL.DFLLSYNC.read().ENABLE != 0) {} - - io.OSCCTRL.DFLLVAL.modify(.{}); - // Wait for synchronization - while (io.OSCCTRL.DFLLSYNC.read().DFLLVAL != 0) {} - - io.OSCCTRL.DFLLCTRLB.write(.{ - .MODE = 0, - .STABLE = 0, - .LLAW = 0, - .USBCRM = 1, - .CCDIS = 1, - .QLDIS = 0, - .BPLCKC = 0, - .WAITLOCK = 1, + .DRVSTR = 0, + .padding = 0, }); - // Wait for synchronization - while (io.OSCCTRL.STATUS.read().DFLLRDY == 0) {} - - // 5) Switch Generic Clock Generator 0 to DFLL48M. CPU will run at 48MHz. - io.GCLK.GENCTRL[0].write(.{ - .SRC = .{ .value = .DFLL }, - .reserved8 = 0, - .GENEN = 1, - .IDC = 1, - .OOV = 0, - .OE = 1, - .DIVSEL = .{ .value = .DIV1 }, - .RUNSTDBY = 0, - .reserved16 = 0, - .DIV = 0, - }); - // Wait for synchronization - while ((io.GCLK.SYNCBUSY.read().GENCTRL.raw & @intFromEnum(io_types.GCLK.GCLK_SYNCBUSY__GENCTRL.GCLK0)) != 0) {} - - // Now that all system clocks are configured, we can set CLKDIV. - // These values are normally the ones present after Reset. - io.MCLK.CPUDIV.write(.{ .DIV = .{ .value = .DIV1 } }); - - //SysTick_Config(1000); -} - -//:src/main.c -pub fn main() !void { - system_init(); - microzig.cpu.dmb(); - usb.init(); - - io.PORT.GROUP[0].DIR.write(.{ .DIR = led_pad }); - io.PORT.GROUP[0].OUT.write(.{ .OUT = 0 }); + Port.BUTTON_CLK.setDir(.out); + Port.BUTTON_CLK.write(true); + Port.BUTTON_LATCH.setDir(.out); + Port.BUTTON_LATCH.write(false); + io.MCLK.APBAMASK.modify(.{ .EIC_ = 1 }); var was_ready = false; while (true) { @@ -225,15 +202,140 @@ pub fn main() !void { break :input; }]; if (!was_ready) { - std.log.info("Hello, {s}!", .{"world"}); + std.log.info("Ready!", .{}); was_ready = true; } if (data.len > 0) for (data) |c| switch (c) { else => out.writeByte(c) catch break :input, 'B' - '@' => utils.resetIntoBootloader(), + 'C' - '@' => { // Debug clock frequencies + const clock_log = std.log.scoped(.clock); + + io.MCLK.APBAMASK.modify(.{ .FREQM_ = 1 }); + + // Use OSCULP32K / 512 as reference + io.GCLK.GENCTRL[GCLK.GEN.@"64KHz".ID].write(.{ + .SRC = .{ .value = .OSCULP32K }, + .reserved8 = 0, + .GENEN = 1, + .IDC = 0, + .OOV = 0, + .OE = 0, + .DIVSEL = .{ .value = .DIV2 }, + .RUNSTDBY = 0, + .reserved16 = 0, + .DIV = 8, + }); + io.GCLK.PCHCTRL[GCLK.PCH.FREQM_REF].write(.{ + .GEN = .{ .value = GCLK.GEN.@"64KHz".PCHCTRL_GEN }, + .reserved6 = 0, + .CHEN = 1, + .WRTLOCK = 0, + .padding = 0, + }); + + for (0.., &io.GCLK.GENCTRL) |gen_id, *gen_ctrl| { + if (gen_id == GCLK.GEN.@"64KHz".ID) continue; + const config = gen_ctrl.read(); + if (config.GENEN == 0) continue; + + io.GCLK.PCHCTRL[GCLK.PCH.FREQM_MSR].write(.{ + .GEN = .{ .raw = @intCast(gen_id) }, + .reserved6 = 0, + .CHEN = 1, + .WRTLOCK = 0, + .padding = 0, + }); + + // Reset Frequency Meter + io.FREQM.CTRLA.write(.{ + .SWRST = 1, + .ENABLE = 0, + .padding = 0, + }); + while (io.FREQM.SYNCBUSY.read().SWRST != 0) {} + + // Run Frequency Meter + io.FREQM.CFGA.write(.{ + .REFNUM = 8, + .padding = 0, + }); + io.FREQM.CTRLA.write(.{ + .SWRST = 0, + .ENABLE = 1, + .padding = 0, + }); + while (io.FREQM.SYNCBUSY.read().ENABLE != 0) {} + io.FREQM.CTRLB.write(.{ + .START = 1, + .padding = 0, + }); + while (io.FREQM.STATUS.read().BUSY != 0) {} + if (io.FREQM.STATUS.read().OVF == 0) { + const freq = (@as(u32, io.FREQM.VALUE.read().VALUE) + 1) * 8; + const div = switch (config.DIVSEL.value) { + .DIV1 => switch (config.DIV) { + 0 => 1, + else => |div| div, + }, + .DIV2 => @as(u32, 1) << @min(config.DIV + 1, @as(u5, switch (gen_id) { + else => 9, + 1 => 17, + })), + }; + switch (gen_id) { + 0 => { + const hs_div = @min(io.MCLK.HSDIV.read().DIV.raw, 1); + clock_log.info( + "High-Speed Clock ({s} / {d}): {d} Hz", + .{ @tagName(config.SRC.value), div * hs_div, freq / hs_div }, + ); + const cpu_div = @min(io.MCLK.CPUDIV.read().DIV.raw, 1); + clock_log.info( + "CPU Clock ({s} / {d}): {d} Hz", + .{ @tagName(config.SRC.value), div * cpu_div, freq / cpu_div }, + ); + }, + else => {}, + } + clock_log.info( + "Generator #{d} ({s} / {d}): {d} Hz", + .{ gen_id, @tagName(config.SRC.value), div, freq }, + ); + } else clock_log.warn("Unable to measure generator #{d}", .{gen_id}); + } + + io.GCLK.PCHCTRL[GCLK.PCH.FREQM_MSR].write(.{ + .GEN = .{ .raw = 0 }, + .reserved6 = 0, + .CHEN = 0, + .WRTLOCK = 0, + .padding = 0, + }); + io.GCLK.PCHCTRL[GCLK.PCH.FREQM_REF].write(.{ + .GEN = .{ .raw = 0 }, + .reserved6 = 0, + .CHEN = 0, + .WRTLOCK = 0, + .padding = 0, + }); + + io.MCLK.APBAMASK.modify(.{ .FREQM_ = 0 }); + + for (0.., &io.GCLK.PCHCTRL) |pch_id, *pch_ctrl| { + const config = pch_ctrl.read(); + if (config.CHEN == 0) continue; + clock_log.info( + "Peripheral Channel #{d}: Generator #{d}", + .{ pch_id, config.GEN.raw }, + ); + } + }, '\r' => out.writeByte('\n') catch break :input, 'P' - '@' => @panic("user"), 'R' - '@' => utils.resetIntoApp(), + 'S' - '@' => for (0.., &io.PORT.GROUP) |group_i, *group| + std.log.info("IN{d} = 0x{X:0>8}", .{ group_i, group.IN.read().IN }), 0x7f => out.writeAll("\x1B[D\x1B[K") catch break :input, }; } @@ -244,15 +346,7 @@ const usb = struct { var endpoint_buffer: [8][2][64]u8 align(4) = .{.{.{0} ** 64} ** 2} ** 8; const setup = std.mem.bytesAsValue(Setup, endpoint_buffer[0][0][0..8]); - var endpoint_table: [8]io_types.USB.USB_DESCRIPTOR align(4) = .{.{ .DEVICE = .{ - .DEVICE_DESC_BANK = .{.{ .DEVICE = .{ - .ADDR = .{ .raw = 0 }, - .PCKSIZE = .{ .raw = 0 }, - .EXTREG = .{ .raw = 0 }, - .STATUS_BK = .{ .raw = 0 }, - .padding = .{0} ** 5, - } }} ** 2, - } }} ** 8; + var endpoint_table: [8]io_types.USB.USB_DESCRIPTOR align(4) = undefined; const endpoint_table_addr: *align(4) volatile [8]io_types.USB.USB_DESCRIPTOR = &endpoint_table; var current_configuration: u8 = 0; @@ -401,85 +495,314 @@ const usb = struct { }; }; - //:src/cdc_enumerate.c - fn AT91F_InitUSB() void { - const DM_PIN = PIN.PA24H_USB_DM; - const DM_MUX = MUX.PA24H_USB_DM; - const DP_PIN = PIN.PA25H_USB_DP; - const DP_MUX = MUX.PA25H_USB_DP; + /// One-time initialization + fn init() void { + Port.@"D-".setMux(.H); + Port.@"D+".setMux(.H); + io.MCLK.AHBMASK.modify(.{ .USB_ = 1 }); + io.MCLK.APBBMASK.modify(.{ .USB_ = 1 }); + } - io.PORT.GROUP[0].PINCFG[DM_PIN].modify(.{ .PMUXEN = 1 }); - io.PORT.GROUP[0].PMUX[@divExact(DM_PIN ^ 0b0, 2)].modify(.{ .PMUXE = .{ .raw = DM_MUX } }); - io.PORT.GROUP[0].PINCFG[DP_PIN].modify(.{ .PMUXEN = 1 }); - io.PORT.GROUP[0].PMUX[@divExact(DP_PIN ^ 0b1, 2)].modify(.{ .PMUXO = .{ .raw = DP_MUX } }); + /// Reinitialize into the specified mode + fn reinitMode(mode: io_types.USB.USB_CTRLA__MODE) void { + // Change the main clock to OSCULP32K while doing clock stuff + io.GCLK.GENCTRL[GCLK.GEN.@"120MHz".ID].write(.{ + .SRC = .{ .value = .OSCULP32K }, + .reserved8 = 0, + .GENEN = 1, + .IDC = 0, + .OOV = 0, + .OE = 0, + .DIVSEL = .{ .value = .DIV1 }, + .RUNSTDBY = 0, + .reserved16 = 0, + .DIV = 0, + }); + while (io.GCLK.SYNCBUSY.read().GENCTRL.raw & + GCLK.GEN.@"120MHz".SYNCBUSY_GENCTRL != 0) + {} + io.MCLK.HSDIV.write(.{ .DIV = .{ .value = .DIV1 } }); + io.MCLK.CPUDIV.write(.{ .DIV = .{ .value = .DIV1 } }); + io.OSCCTRL.DPLL[0].DPLLCTRLA.write(.{ + .reserved1 = 0, + .ENABLE = 0, + .reserved6 = 0, + .RUNSTDBY = 0, + .ONDEMAND = 0, + }); + while (io.OSCCTRL.DPLL[0].DPLLSYNCBUSY.read().ENABLE != 0) {} + io.GCLK.PCHCTRL[GCLK.PCH.OSCCTRL_FDPLL0].write(.{ + .GEN = .{ .raw = 0 }, + .reserved6 = 0, + .CHEN = 0, + .WRTLOCK = 0, + .padding = 0, + }); + io.GCLK.PCHCTRL[GCLK.PCH.OSCCTRL_FDPLL0_32K].write(.{ + .GEN = .{ .raw = 0 }, + .reserved6 = 0, + .CHEN = 0, + .WRTLOCK = 0, + .padding = 0, + }); + io.GCLK.GENCTRL[GCLK.GEN.@"1MHz".ID].write(.{ + .SRC = .{ .raw = 0 }, + .reserved8 = 0, + .GENEN = 0, + .IDC = 0, + .OOV = 0, + .OE = 0, + .DIVSEL = .{ .raw = 0 }, + .RUNSTDBY = 0, + .reserved16 = 0, + .DIV = 0, + }); + while (io.GCLK.SYNCBUSY.read().GENCTRL.raw & + GCLK.GEN.@"1MHz".SYNCBUSY_GENCTRL != 0) + {} - io.GCLK.PCHCTRL[USB.GCLK_ID].write(.{ - .GEN = .{ .value = .GCLK0 }, + // Disable USB + io.GCLK.PCHCTRL[GCLK.PCH.USB].write(.{ + .GEN = .{ .value = GCLK.GEN.@"120MHz".PCHCTRL_GEN }, .reserved6 = 0, .CHEN = 1, .WRTLOCK = 0, .padding = 0, }); - io.MCLK.AHBMASK.modify(.{ .USB_ = 1 }); - io.MCLK.APBBMASK.modify(.{ .USB_ = 1 }); - while ((io.GCLK.SYNCBUSY.read().GENCTRL.raw & @intFromEnum(io_types.GCLK.GCLK_SYNCBUSY__GENCTRL.GCLK0)) != 0) {} - - // Reset - io.USB.DEVICE.CTRLA.modify(.{ .SWRST = 1 }); - // Sync wait + io.USB.DEVICE.CTRLA.write(.{ + .SWRST = 1, + .ENABLE = 0, + .RUNSTDBY = 0, + .reserved7 = 0, + .MODE = .{ .raw = 0 }, + }); while (io.USB.DEVICE.SYNCBUSY.read().SWRST != 0) {} + // Disable 48MHz Generator + io.GCLK.PCHCTRL[GCLK.PCH.USB].write(.{ + .GEN = .{ .raw = 0 }, + .reserved6 = 0, + .CHEN = 0, + .WRTLOCK = 0, + .padding = 0, + }); + io.GCLK.GENCTRL[GCLK.GEN.@"48MHz".ID].write(.{ + .SRC = .{ .raw = 0 }, + .reserved8 = 0, + .GENEN = 0, + .IDC = 0, + .OOV = 0, + .OE = 0, + .DIVSEL = .{ .raw = 0 }, + .RUNSTDBY = 0, + .reserved16 = 0, + .DIV = 0, + }); + while (io.GCLK.SYNCBUSY.read().GENCTRL.raw & + GCLK.GEN.@"48MHz".SYNCBUSY_GENCTRL != 0) + {} + + // Switch the DFLL48M to open loop mode + io.OSCCTRL.DFLLCTRLA.write(.{ + .reserved1 = 0, + .ENABLE = 0, + .reserved6 = 0, + .RUNSTDBY = 0, + .ONDEMAND = 0, + }); + while (io.OSCCTRL.DFLLSYNC.read().ENABLE != 0) {} + io.GCLK.PCHCTRL[GCLK.PCH.OSCCTRL_DFLL48].write(.{ + .GEN = .{ .raw = 0 }, + .reserved6 = 0, + .CHEN = 0, + .WRTLOCK = 0, + .padding = 0, + }); + io.OSCCTRL.DFLLMUL.write(.{ + .MUL = 48_000_000 / 1_000, + .FSTEP = 1, + .reserved26 = 0, + .CSTEP = 1, + }); + while (io.OSCCTRL.DFLLSYNC.read().DFLLMUL != 0) {} + io.OSCCTRL.DFLLCTRLB.write(.{ + .MODE = 0, + .STABLE = 0, + .LLAW = 0, + .USBCRM = 0, + .CCDIS = 0, + .QLDIS = 0, + .BPLCKC = 0, + .WAITLOCK = 0, + }); + while (io.OSCCTRL.DFLLSYNC.read().DFLLCTRLB != 0) {} + io.OSCCTRL.DFLLCTRLA.write(.{ + .reserved1 = 0, + .ENABLE = 1, + .reserved6 = 0, + .RUNSTDBY = 0, + .ONDEMAND = 0, + }); + while (io.OSCCTRL.DFLLSYNC.read().ENABLE != 0) {} + while (io.OSCCTRL.STATUS.read().DFLLRDY == 0) {} + + // Change the reference clock + // Switch the DFLL48M to close loop mode + io.OSCCTRL.DFLLCTRLB.write(.{ + .MODE = 1, + .STABLE = 0, + .LLAW = 0, + .USBCRM = 1, + .CCDIS = 1, + .QLDIS = 0, + .BPLCKC = 0, + .WAITLOCK = 0, + }); + while (io.OSCCTRL.DFLLSYNC.read().DFLLCTRLB != 0) {} + while (io.OSCCTRL.STATUS.read().DFLLRDY == 0) {} + // Load Pad Calibration - const pads = USB.FUSES.read(); + const pads = NVMCTRL.SW0.SW0_WORD_1.read(); io.USB.DEVICE.PADCAL.write(.{ - .TRANSP = switch (pads.TRANSP) { - 0...std.math.maxInt(u5) - 1 => |transn| transn, + .TRANSP = switch (pads.USB_TRANSP) { + 0...std.math.maxInt(u5) - 1 => |transp| transp, std.math.maxInt(u5) => 29, }, .reserved6 = 0, - .TRANSN = switch (pads.TRANSN) { + .TRANSN = switch (pads.USB_TRANSN) { 0...std.math.maxInt(u5) - 1 => |transn| transn, std.math.maxInt(u5) => 5, }, .reserved12 = 0, - .TRIM = switch (pads.TRIM) { - 0...std.math.maxInt(u3) - 1 => |transn| transn, + .TRIM = switch (pads.USB_TRIM) { + 0...std.math.maxInt(u3) - 1 => |trim| trim, std.math.maxInt(u3) => 3, }, .padding = 0, }); - // Set the configuration - // Set mode to Device mode - // Enable Run in Standby - io.USB.DEVICE.CTRLA.modify(.{ - .MODE = .{ .value = .DEVICE }, - .RUNSTDBY = 1, - }); - // Set the descriptor address - io.USB.DEVICE.DESCADD.write(.{ - .DESCADD = @intFromPtr(&endpoint_table), + // Enable 48MHz Generator + io.GCLK.GENCTRL[GCLK.GEN.@"48MHz".ID].write(.{ + .SRC = .{ .value = .DFLL }, + .reserved8 = 0, + .GENEN = 1, + .IDC = 0, + .OOV = 0, + .OE = 0, + .DIVSEL = .{ .value = .DIV1 }, + .RUNSTDBY = 0, + .reserved16 = 0, + .DIV = 0, }); - // Set speed configuration to Full speed - // Attach to the USB host - io.USB.DEVICE.CTRLB.modify(.{ - .SPDCONF = .{ .value = .FS }, - .DETACH = 0, + while (io.GCLK.SYNCBUSY.read().GENCTRL.raw & + GCLK.GEN.@"48MHz".SYNCBUSY_GENCTRL != 0) + {} + io.GCLK.PCHCTRL[GCLK.PCH.USB].write(.{ + .GEN = .{ .value = GCLK.GEN.@"48MHz".PCHCTRL_GEN }, + .reserved6 = 0, + .CHEN = 1, + .WRTLOCK = 0, + .padding = 0, }); - } - pub fn init() void { - // Initialize USB - AT91F_InitUSB(); - io.USB.HOST.CTRLA.modify(.{ .ENABLE = 1 }); + // Enable USB + @memset(std.mem.sliceAsBytes(endpoint_table_addr), 0x00); + io.USB.DEVICE.DESCADD.write(.{ .DESCADD = @intFromPtr(endpoint_table_addr) }); + io.USB.DEVICE.CTRLA.write(.{ + .SWRST = 0, + .ENABLE = 0, + .RUNSTDBY = 0, + .reserved7 = 0, + .MODE = .{ .value = mode }, + }); + switch (mode) { + .DEVICE => io.USB.DEVICE.CTRLB.modify(.{ + .SPDCONF = .{ .value = .FS }, + .DETACH = 0, + }), + .HOST => io.USB.HOST.CTRLB.modify(.{ + .SPDCONF = .{ .value = .NORMAL }, + }), + } + io.USB.DEVICE.CTRLA.write(.{ + .SWRST = 0, + .ENABLE = 1, + .RUNSTDBY = 0, + .reserved7 = 0, + .MODE = .{ .value = mode }, + }); + while (io.USB.DEVICE.SYNCBUSY.read().ENABLE != 0) {} + + // Reinitialize main clock + io.GCLK.GENCTRL[GCLK.GEN.@"1MHz".ID].write(.{ + .SRC = .{ .value = .DFLL }, + .reserved8 = 0, + .GENEN = 1, + .IDC = 0, + .OOV = 0, + .OE = 0, + .DIVSEL = .{ .value = .DIV1 }, + .RUNSTDBY = 0, + .reserved16 = 0, + .DIV = 48, + }); + while (io.GCLK.SYNCBUSY.read().GENCTRL.raw & + GCLK.GEN.@"1MHz".SYNCBUSY_GENCTRL != 0) + {} + io.GCLK.PCHCTRL[GCLK.PCH.OSCCTRL_FDPLL0].write(.{ + .GEN = .{ .value = GCLK.GEN.@"1MHz".PCHCTRL_GEN }, + .reserved6 = 0, + .CHEN = 1, + .WRTLOCK = 0, + .padding = 0, + }); + io.OSCCTRL.DPLL[0].DPLLCTRLB.write(.{ + .FILTER = .{ .value = .FILTER1 }, + .WUF = 0, + .REFCLK = .{ .value = .GCLK }, + .LTIME = .{ .value = .DEFAULT }, + .LBYPASS = 0, + .DCOFILTER = .{ .raw = 0 }, + .DCOEN = 0, + .DIV = 0, + .padding = 0, + }); + io.OSCCTRL.DPLL[0].DPLLRATIO.write(.{ + .LDR = 120 - 2, + .reserved16 = 0, + .LDRFRAC = 0, + .padding = 0, + }); + while (io.OSCCTRL.DPLL[0].DPLLSYNCBUSY.read().DPLLRATIO != 0) {} + io.OSCCTRL.DPLL[0].DPLLCTRLA.write(.{ + .reserved1 = 0, + .ENABLE = 1, + .reserved6 = 0, + .RUNSTDBY = 0, + .ONDEMAND = 0, + }); + while (io.OSCCTRL.DPLL[0].DPLLSYNCBUSY.read().ENABLE != 0) {} + while (io.OSCCTRL.DPLL[0].DPLLSTATUS.read().CLKRDY == 0) {} + io.GCLK.GENCTRL[GCLK.GEN.@"120MHz".ID].write(.{ + .SRC = .{ .value = .DPLL0 }, + .reserved8 = 0, + .GENEN = 1, + .IDC = 0, + .OOV = 0, + .OE = 0, + .DIVSEL = .{ .value = .DIV1 }, + .RUNSTDBY = 0, + .reserved16 = 0, + .DIV = 0, + }); + while (io.GCLK.SYNCBUSY.read().GENCTRL.raw & + GCLK.GEN.@"120MHz".SYNCBUSY_GENCTRL != 0) + {} } fn tick() void { // Check for End Of Reset flag if (io.USB.DEVICE.INTFLAG.read().EORST != 0) { - // Toggle LED - //io.PORT.GROUP[0].OUT.write(.{ .OUT = 0 }); - // Clear the flag io.USB.DEVICE.INTFLAG.write(.{ .SUSPEND = 0, @@ -565,9 +888,6 @@ const usb = struct { // Check for End Of SETUP flag if (io.USB.DEVICE.DEVICE_ENDPOINT[0].DEVICE.EPINTFLAG.read().RXSTP != 0) setup: { - // Toggle LED - //io.PORT.GROUP[0].OUT.write(.{ .OUT = led_pad }); - // Clear the Received Setup flag io.USB.DEVICE.DEVICE_ENDPOINT[0].DEVICE.EPINTFLAG.write(.{ .TRCPT0 = 0, @@ -863,8 +1183,7 @@ const usb = struct { setup.bRequest == @intFromEnum(Setup.standard.Request.GET_DESCRIPTOR) and setup.wValue >> 8 == @intFromEnum(Setup.standard.DescriptorType.DEVICE_QUALIFIER)) {} else { - blink(setup.bRequest, 200_000, 100_000); - sleep(500_000); + std.log.scoped(.usb).err("Unhandled request: 0x{X:0<2}", .{setup.bRequest}); } } } @@ -938,34 +1257,3 @@ const usb = struct { while (ep_ctrl.EPINTFLAG.read().TRCPT1 == 0) {} } }; - -//:src/utils.c -const utils = struct { - const DBL_TAP_PTR: *volatile u32 = std.mem.bytesAsValue(u32, HSRAM.ADDR[HSRAM.ADDR.len - 4 ..]); - const DBL_TAP_MAGIC = 0xf01669ef; - const DBL_TAP_MAGIC_QUICK_BOOT = 0xf02669ef; - - pub fn resetIntoApp() noreturn { - DBL_TAP_PTR.* = DBL_TAP_MAGIC_QUICK_BOOT; - NVIC_SystemReset(); - } - - pub fn resetIntoBootloader() noreturn { - DBL_TAP_PTR.* = DBL_TAP_MAGIC; - NVIC_SystemReset(); - } - - fn NVIC_SystemReset() noreturn { - microzig.cpu.dsb(); - microzig.cpu.peripherals.SCB.AIRCR.write(.{ - .reserved1 = 0, - .VECTCLRACTIVE = 0, - .SYSRESETREQ = 1, - .reserved15 = 0, - .ENDIANESS = 0, - .VECTKEY = 0x5FA, - }); - microzig.cpu.dsb(); - microzig.hang(); - } -}; diff --git a/src/utils.zig b/src/utils.zig new file mode 100644 index 0000000..107fe87 --- /dev/null +++ b/src/utils.zig @@ -0,0 +1,39 @@ +//adafruit/uf2-samdx1:lib/cmsis/CMSIS/Include/core_cm7.h +fn NVIC_SystemReset() noreturn { + microzig.cpu.dsb(); + microzig.cpu.peripherals.SCB.AIRCR.write(.{ + .reserved1 = 0, + .VECTCLRACTIVE = 0, + .SYSRESETREQ = 1, + .reserved15 = 0, + .ENDIANESS = 0, + .VECTKEY = 0x5FA, + }); + microzig.cpu.dsb(); + microzig.hang(); +} + +//adafruit/uf2-samdx1:lib/samd51/include/samd51j19a.h +const HSRAM = struct { + const ADDR: *align(4) [SIZE]u8 = @ptrFromInt(0x20000000); + const SIZE = 0x00030000; +}; + +//adafruit/uf2-samdx1:inc/uf2.h +const DBL_TAP_PTR: *volatile u32 = std.mem.bytesAsValue(u32, HSRAM.ADDR[HSRAM.ADDR.len - 4 ..]); +const DBL_TAP_MAGIC = 0xf01669ef; +const DBL_TAP_MAGIC_QUICK_BOOT = 0xf02669ef; + +//adafruit/uf2-samdx1:src/utils.c +pub fn resetIntoApp() noreturn { + DBL_TAP_PTR.* = DBL_TAP_MAGIC_QUICK_BOOT; + NVIC_SystemReset(); +} + +pub fn resetIntoBootloader() noreturn { + DBL_TAP_PTR.* = DBL_TAP_MAGIC; + NVIC_SystemReset(); +} + +const microzig = @import("microzig"); +const std = @import("std");