From 540a52585f0b4448caa772f49e1664f3ad46f987 Mon Sep 17 00:00:00 2001 From: Matevz Mihalic Date: Thu, 25 Apr 2024 22:25:37 +0200 Subject: [PATCH] rp2040: add flash id function (#182) --- bsp/raspberrypi/rp2040/src/hal/flash.zig | 89 ++++++++++++++++++++ bsp/stmicro/stm32/build.zig | 2 +- examples/raspberrypi/rp2040/build.zig | 1 + examples/raspberrypi/rp2040/src/flash_id.zig | 41 +++++++++ 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 examples/raspberrypi/rp2040/src/flash_id.zig diff --git a/bsp/raspberrypi/rp2040/src/hal/flash.zig b/bsp/raspberrypi/rp2040/src/hal/flash.zig index f10315e..49bf300 100644 --- a/bsp/raspberrypi/rp2040/src/hal/flash.zig +++ b/bsp/raspberrypi/rp2040/src/hal/flash.zig @@ -1,6 +1,12 @@ //! See [rp2040 docs](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf), page 136. const rom = @import("rom.zig"); +const microzig = @import("microzig"); +const peripherals = microzig.chip.peripherals; + +const IO_QSPI = peripherals.IO_QSPI; +const XIP_SSI = peripherals.XIP_SSI; + pub const Command = enum(u8) { block_erase = 0xd8, ruid_cmd = 0x4b, @@ -108,3 +114,86 @@ export fn _range_program(offset: u32, data: [*]const u8, len: usize) linksection boot2.flash_enable_xip(); } + +/// Force the chip select using IO overrides, in case RAM-resident IRQs +/// are still running, and the FIFO bottoms out +pub inline fn force_cs(high: bool) void { + @call(.never_inline, _force_cs, .{high}); +} + +fn _force_cs(high: bool) linksection(".time_critical") void { + const value = v: { + var value: u32 = 0x2; + if (high) { + value = 0x3; + } + break :v value << 8; + }; + + const IO_QSPI_GPIO_QSPI_SS_CTRL: *volatile u32 = @ptrFromInt(@intFromPtr(IO_QSPI) + 0x0C); + IO_QSPI_GPIO_QSPI_SS_CTRL.* = (IO_QSPI_GPIO_QSPI_SS_CTRL.* ^ value) & 0x300; +} + +/// Execute a command on the flash chip +/// +/// Configures flash for serial mode operation, sends a command, receives response +/// and then configures flash back to XIP mode +pub inline fn cmd(tx_buf: []const u8, rx_buf: []u8) void { + @call(.never_inline, _cmd, .{ tx_buf, rx_buf }); +} + +fn _cmd(tx_buf: []const u8, rx_buf: []u8) linksection(".time_critical") void { + boot2.flash_init(); + asm volatile ("" ::: "memory"); // memory barrier + rom.connect_internal_flash()(); + rom.flash_exit_xip()(); + force_cs(false); + + // can't use peripherals, because its functions are not in ram + const XIP_SSI_SR: *volatile u32 = @ptrFromInt(@intFromPtr(XIP_SSI) + 0x28); + const XIP_SSI_DR0: *volatile u8 = @ptrFromInt(@intFromPtr(XIP_SSI) + 0x60); + + const len = tx_buf.len; + var tx_remaining = len; + var rx_remaining = len; + const fifo_depth = 16 - 2; + while (tx_remaining > 0 or rx_remaining > 0) { + const can_put = XIP_SSI_SR.* & 0x2 != 0; // TFNF + const can_get = XIP_SSI_SR.* & 0x8 != 0; // RFNE + + if (can_put and tx_remaining > 0 and rx_remaining < tx_remaining + fifo_depth) { + XIP_SSI_DR0.* = tx_buf[len - tx_remaining]; + tx_remaining -= 1; + } + if (can_get and rx_remaining > 0) { + rx_buf[len - rx_remaining] = XIP_SSI_DR0.*; + rx_remaining -= 1; + } + } + + force_cs(true); + rom.flash_flush_cache()(); + boot2.flash_enable_xip(); +} + +const id_dummy_len = 4; +const id_data_len = 8; +const id_total_len = 1 + id_dummy_len + id_data_len; +var id_buf: ?[id_data_len]u8 = null; + +/// Read the flash chip's ID which is unique to each RP2040 +pub fn id() [id_data_len]u8 { + if (id_buf) |b| { + return b; + } + + var tx_buf: [id_total_len]u8 = undefined; + var rx_buf: [id_total_len]u8 = undefined; + tx_buf[0] = @intFromEnum(Command.ruid_cmd); + cmd(&tx_buf, &rx_buf); + + id_buf = undefined; + @memcpy(&id_buf.?, rx_buf[1 + id_dummy_len ..]); + + return id_buf.?; +} diff --git a/bsp/stmicro/stm32/build.zig b/bsp/stmicro/stm32/build.zig index 8258479..3fcc52d 100644 --- a/bsp/stmicro/stm32/build.zig +++ b/bsp/stmicro/stm32/build.zig @@ -61,7 +61,7 @@ pub const chips = struct { }, }, .hal = .{ - .source_file = .{ .cwd_relative = build_root ++ "/src/hals/STM32F407.zig" }, + .root_source_file = .{ .cwd_relative = build_root ++ "/src/hals/STM32F407.zig" }, }, }; diff --git a/examples/raspberrypi/rp2040/build.zig b/examples/raspberrypi/rp2040/build.zig index fda3046..2777e30 100644 --- a/examples/raspberrypi/rp2040/build.zig +++ b/examples/raspberrypi/rp2040/build.zig @@ -7,6 +7,7 @@ const available_examples = [_]Example{ .{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_adc", .file = "src/adc.zig" }, .{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_blinky", .file = "src/blinky.zig" }, .{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_flash-program", .file = "src/flash_program.zig" }, + .{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_flash-id", .file = "src/flash_id.zig" }, .{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_gpio-clk", .file = "src/gpio_clk.zig" }, .{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_i2c-bus-scan", .file = "src/i2c_bus_scan.zig" }, .{ .target = rp2040.boards.raspberrypi.pico, .name = "pico_pwm", .file = "src/pwm.zig" }, diff --git a/examples/raspberrypi/rp2040/src/flash_id.zig b/examples/raspberrypi/rp2040/src/flash_id.zig new file mode 100644 index 0000000..fc9127c --- /dev/null +++ b/examples/raspberrypi/rp2040/src/flash_id.zig @@ -0,0 +1,41 @@ +const std = @import("std"); +const microzig = @import("microzig"); + +const rp2040 = microzig.hal; +const time = rp2040.time; +const gpio = rp2040.gpio; +const flash = rp2040.flash; + +const uart = rp2040.uart.num(0); +const baud_rate = 115200; +const uart_tx_pin = gpio.num(0); +const uart_rx_pin = gpio.num(1); + +pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { + std.log.err("panic: {s}", .{message}); + @breakpoint(); + while (true) {} +} + +pub const std_options = struct { + pub const log_level = .debug; + pub const logFn = rp2040.uart.log; +}; + +pub fn main() !void { + uart.apply(.{ + .baud_rate = baud_rate, + .tx_pin = uart_tx_pin, + .rx_pin = uart_rx_pin, + .clock_config = rp2040.clock_config, + }); + + rp2040.uart.init_logger(uart); + + while (true) { + const serial_number = flash.id(); + const hex_serial_number = std.fmt.fmtSliceHexLower(&serial_number); + std.log.info("serial number: {s}", .{hex_serial_number}); + time.sleep_ms(1000); + } +}