From 620c75b9b85ca160d929a3a5df3c96a8d433f3be Mon Sep 17 00:00:00 2001 From: Luke Wren Date: Sun, 24 Jan 2021 16:42:14 +0000 Subject: [PATCH] Add flash_get_unique_id to hardware_flash --- src/rp2_common/hardware_flash/flash.c | 78 +++++++++++++++++++ .../hardware_flash/include/hardware/flash.h | 19 +++++ 2 files changed, 97 insertions(+) diff --git a/src/rp2_common/hardware_flash/flash.c b/src/rp2_common/hardware_flash/flash.c index 59f2cc2..dfab95b 100644 --- a/src/rp2_common/hardware_flash/flash.c +++ b/src/rp2_common/hardware_flash/flash.c @@ -7,8 +7,17 @@ #include "hardware/flash.h" #include "pico/bootrom.h" +#include "hardware/structs/ssi.h" +#include "hardware/structs/ioqspi.h" + #define FLASH_BLOCK_ERASE_CMD 0xd8 +// Standard RUID instruction: 4Bh command prefix, 32 dummy bits, 64 data bits. +#define FLASH_RUID_CMD 0x4b +#define FLASH_RUID_DUMMY_BYTES 4 +#define FLASH_RUID_DATA_BYTES 8 +#define FLASH_RUID_TOTAL_BYTES (1 + FLASH_RUID_DUMMY_BYTES + FLASH_RUID_DATA_BYTES) + #define __compiler_barrier() asm volatile("" ::: "memory") //----------------------------------------------------------------------------- @@ -99,3 +108,72 @@ void __no_inline_not_in_flash_func(flash_range_program)(uint32_t flash_offs, con flash_flush_cache(); // Note this is needed to remove CSn IO force as well as cache flushing flash_enable_xip_via_boot2(); } + +//----------------------------------------------------------------------------- +// Lower-level flash access functions + +// Bitbanging the chip select using IO overrides, in case RAM-resident IRQs +// are still running, and the FIFO bottoms out. (the bootrom does the same) +static void flash_cs_force(bool high) { + uint32_t field_val = high ? + IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH : + IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW; + hw_write_masked(&ioqspi_hw->io[1].ctrl, + field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB, + IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS + ); +} + +// May want to expose this at some point but this is unlikely to be the right +// interface to do so. Keep it static +static void __no_inline_not_in_flash_func(flash_do_cmd)(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) { + void (*connect_internal_flash)(void) = (void(*)(void))rom_func_lookup(rom_table_code('I', 'F')); + void (*flash_exit_xip)(void) = (void(*)(void))rom_func_lookup(rom_table_code('E', 'X')); + void (*flash_flush_cache)(void) = (void(*)(void))rom_func_lookup(rom_table_code('F', 'C')); + assert(connect_internal_flash && flash_exit_xip && flash_flush_cache); + flash_init_boot2_copyout(); + __compiler_barrier(); + connect_internal_flash(); + flash_exit_xip(); + + flash_cs_force(0); + size_t tx_remaining = count; + size_t rx_remaining = count; + // We may be interrupted -- don't want FIFO to overflow if we're distracted. + const size_t max_in_flight = 16 - 2; + while (tx_remaining || rx_remaining) { + uint32_t flags = ssi_hw->sr; + bool can_put = !!(flags & SSI_SR_TFNF_BITS); + bool can_get = !!(flags & SSI_SR_RFNE_BITS); + if (can_put && tx_remaining && rx_remaining - tx_remaining < max_in_flight) { + ssi_hw->dr0 = *txbuf++; + --tx_remaining; + } + if (can_get && rx_remaining) { + *rxbuf++ = ssi_hw->dr0; + --rx_remaining; + } + } + flash_cs_force(1); + + flash_flush_cache(); + flash_enable_xip_via_boot2(); +} + +// Use standard RUID command to get a unique identifier for the flash (and +// hence the board) + +static_assert(FLASH_UNIQUE_ID_SIZE_BYTES == FLASH_RUID_DATA_BYTES); + +void flash_get_unique_id(uint8_t *id_out) { +#if PICO_NO_FLASH + panic_unsupported(); +#else + uint8_t txbuf[FLASH_RUID_TOTAL_BYTES] = {0}; + uint8_t rxbuf[FLASH_RUID_TOTAL_BYTES] = {0}; + txbuf[0] = 0x4b; + flash_do_cmd(txbuf, rxbuf, FLASH_RUID_TOTAL_BYTES); + for (int i = 0; i < FLASH_RUID_DATA_BYTES; i++) + id_out[i] = rxbuf[i + 1 + FLASH_RUID_DUMMY_BYTES]; +#endif +} diff --git a/src/rp2_common/hardware_flash/include/hardware/flash.h b/src/rp2_common/hardware_flash/include/hardware/flash.h index 7ea96b4..40e2949 100644 --- a/src/rp2_common/hardware_flash/include/hardware/flash.h +++ b/src/rp2_common/hardware_flash/include/hardware/flash.h @@ -18,6 +18,8 @@ #define FLASH_SECTOR_SIZE (1u << 12) #define FLASH_BLOCK_SIZE (1u << 16) +#define FLASH_UNIQUE_ID_SIZE_BYTES 8 + /** \file flash.h * \defgroup hardware_flash hardware_flash * @@ -28,6 +30,10 @@ * synchronisation to make sure no XIP accesses take place during flash * programming. * + * Likewise they are *unsafe* if you have interrupt handlers or an interrupt + * vector table in flash, so you must disable interrupts before calling in + * this case. + * * If PICO_NO_FLASH=1 is not defined (i.e. if the program is built to run from * flash) then these functions will make a static copy of the second stage * bootloader in SRAM, and use this to reenter execute-in-place mode after @@ -54,6 +60,19 @@ void flash_range_erase(uint32_t flash_offs, size_t count); * \param data Pointer to the data to program into flash * \param count Number of bytes to program. Must be a multiple of 256 bytes (one page). */ + void flash_range_program(uint32_t flash_offs, const uint8_t *data, size_t count); +/*! \brief Get flash unique 64 bit identifier + * \ingroup hardware_flash + * + * Use a standard 4Bh RUID instruction to retrieve the 64 bit unique + * identifier from a flash device attached to the QSPI interface. Since there + * is a 1:1 association between the MCU and this flash, this also serves as a + * unique identifier for the board. + * + * \param id_out Pointer to an 8-byte buffer to which the ID will be written + */ +void flash_get_unique_id(uint8_t *id_out); + #endif