From 9fb5a5b9a480c1fa407748b7defd8249ae94d537 Mon Sep 17 00:00:00 2001 From: Kenneth Bell Date: Fri, 20 Oct 2023 11:47:05 +0100 Subject: [PATCH] nrf,sam,rp2040: add machine.HardwareID function --- GNUmakefile | 2 + src/examples/device-id/main.go | 21 ++++++++ src/machine/deviceid.go | 17 +++++++ src/machine/machine_atsam.go | 31 ++++++++++++ src/machine/machine_atsamd21.go | 3 ++ src/machine/machine_atsamd51.go | 3 ++ src/machine/machine_nrf.go | 28 +++++++++++ src/machine/machine_rp2040_flash.go | 26 ++++++++++ src/machine/machine_rp2040_rom.go | 76 +++++++++++++++++++++++++++++ 9 files changed, 207 insertions(+) create mode 100644 src/examples/device-id/main.go create mode 100644 src/machine/deviceid.go create mode 100644 src/machine/machine_atsam.go diff --git a/GNUmakefile b/GNUmakefile index aacfe4b7c9..718e458075 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -506,6 +506,8 @@ smoketest: @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id + @$(MD5SUM) test.hex # test simulated boards on play.tinygo.org ifneq ($(WASM), 0) $(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1 diff --git a/src/examples/device-id/main.go b/src/examples/device-id/main.go new file mode 100644 index 0000000000..31e62baef6 --- /dev/null +++ b/src/examples/device-id/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "encoding/hex" + "machine" + "time" +) + +func main() { + time.Sleep(2 * time.Second) + + // For efficiency, it's best to get the device ID once and cache it + // (e.g. on RP2040 XIP flash and interrupts disabled for period of + // retrieving the hardware ID from ROM chip) + id := machine.DeviceID() + + for { + println("Device ID:", hex.EncodeToString(id)) + time.Sleep(1 * time.Second) + } +} diff --git a/src/machine/deviceid.go b/src/machine/deviceid.go new file mode 100644 index 0000000000..cb2e1d0c1e --- /dev/null +++ b/src/machine/deviceid.go @@ -0,0 +1,17 @@ +//go:build rp2040 || nrf || sam + +package machine + +// DeviceID returns an identifier that is unique within +// a particular chipset. +// +// The identity is one burnt into the MCU itself, or the +// flash chip at time of manufacture. +// +// It's possible that two different vendors may allocate +// the same DeviceID, so callers should take this into +// account if needing to generate a globally unique id. +// +// The length of the hardware ID is vendor-specific, but +// 8 bytes (64 bits) and 16 bytes (128 bits) are common. +var _ = (func() []byte)(DeviceID) diff --git a/src/machine/machine_atsam.go b/src/machine/machine_atsam.go new file mode 100644 index 0000000000..ad2f073786 --- /dev/null +++ b/src/machine/machine_atsam.go @@ -0,0 +1,31 @@ +//go:build sam + +package machine + +import ( + "runtime/volatile" + "unsafe" +) + +var deviceID [16]byte + +// DeviceID returns an identifier that is unique within +// a particular chipset. +// +// The identity is one burnt into the MCU itself, or the +// flash chip at time of manufacture. +// +// It's possible that two different vendors may allocate +// the same DeviceID, so callers should take this into +// account if needing to generate a globally unique id. +// +// The length of the hardware ID is vendor-specific, but +// 8 bytes (64 bits) and 16 bytes (128 bits) are common. +func DeviceID() []byte { + for i := 0; i < len(deviceID); i++ { + word := (*volatile.Register32)(unsafe.Pointer(deviceIDAddr[i/4])).Get() + deviceID[i] = byte(word >> ((i % 4) * 8)) + } + + return deviceID[:] +} diff --git a/src/machine/machine_atsamd21.go b/src/machine/machine_atsamd21.go index 3ad93dccf0..d84a852551 100644 --- a/src/machine/machine_atsamd21.go +++ b/src/machine/machine_atsamd21.go @@ -18,6 +18,9 @@ import ( const deviceName = sam.Device +// DS40001882F, Section 10.3.3: Serial Number +var deviceIDAddr = []uintptr{0x0080A00C, 0x0080A040, 0x0080A044, 0x0080A048} + const ( PinAnalog PinMode = 1 PinSERCOM PinMode = 2 diff --git a/src/machine/machine_atsamd51.go b/src/machine/machine_atsamd51.go index bdc00c9aff..7118792a73 100644 --- a/src/machine/machine_atsamd51.go +++ b/src/machine/machine_atsamd51.go @@ -18,6 +18,9 @@ import ( const deviceName = sam.Device +// DS60001507, Section 9.6: Serial Number +var deviceIDAddr = []uintptr{0x008061FC, 0x00806010, 0x00806014, 0x00806018} + func CPUFrequency() uint32 { return 120000000 } diff --git a/src/machine/machine_nrf.go b/src/machine/machine_nrf.go index 3b79e376a3..cc68c1d916 100644 --- a/src/machine/machine_nrf.go +++ b/src/machine/machine_nrf.go @@ -12,6 +12,34 @@ import ( const deviceName = nrf.Device +var deviceID [8]byte + +// DeviceID returns an identifier that is unique within +// a particular chipset. +// +// The identity is one burnt into the MCU itself, or the +// flash chip at time of manufacture. +// +// It's possible that two different vendors may allocate +// the same DeviceID, so callers should take this into +// account if needing to generate a globally unique id. +// +// The length of the hardware ID is vendor-specific, but +// 8 bytes (64 bits) is common. +func DeviceID() []byte { + words := make([]uint32, 2) + words[0] = nrf.FICR.DEVICEID[0].Get() + words[1] = nrf.FICR.DEVICEID[1].Get() + + for i := 0; i < 8; i++ { + shift := (i % 4) * 8 + w := i / 4 + deviceID[i] = byte(words[w] >> shift) + } + + return deviceID[:] +} + const ( PinInput PinMode = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) diff --git a/src/machine/machine_rp2040_flash.go b/src/machine/machine_rp2040_flash.go index f3d24e8e7d..8ee881e19a 100644 --- a/src/machine/machine_rp2040_flash.go +++ b/src/machine/machine_rp2040_flash.go @@ -13,6 +13,32 @@ func EnterBootloader() { enterBootloader() } +// 13 = 1 + FLASH_RUID_DUMMY_BYTES(4) + FLASH_RUID_DATA_BYTES(8) +var deviceIDBuf [13]byte + +// DeviceID returns an identifier that is unique within +// a particular chipset. +// +// The identity is one burnt into the MCU itself, or the +// flash chip at time of manufacture. +// +// It's possible that two different vendors may allocate +// the same DeviceID, so callers should take this into +// account if needing to generate a globally unique id. +// +// The length of the hardware ID is vendor-specific, but +// 8 bytes (64 bits) is common. +func DeviceID() []byte { + deviceIDBuf[0] = 0x4b // FLASH_RUID_CMD + + err := doFlashCommand(deviceIDBuf[:], deviceIDBuf[:]) + if err != nil { + panic(err) + } + + return deviceIDBuf[5:13] +} + // compile-time check for ensuring we fulfill BlockDevice interface var _ BlockDevice = flashBlockDevice{} diff --git a/src/machine/machine_rp2040_rom.go b/src/machine/machine_rp2040_rom.go index fdb05bb627..2cf96d89d4 100644 --- a/src/machine/machine_rp2040_rom.go +++ b/src/machine/machine_rp2040_rom.go @@ -93,6 +93,33 @@ static ram_func void flash_enable_xip_via_boot2() { ((void (*)(void))boot2_copyout+1)(); } +#define IO_QSPI_BASE 0x40018000 +#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS 0x00000300 +#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_MSB 9 +#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB 8 +#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW 0x2 +#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH 0x3 + +#define XIP_SSI_BASE 0x18000000 +#define ssi_hw ((ssi_hw_t *)XIP_SSI_BASE) +#define SSI_SR_OFFSET 0x00000028 +#define SSI_DR0_OFFSET 0x00000060 +#define SSI_SR_TFNF_BITS 0x00000002 +#define SSI_SR_RFNE_BITS 0x00000008 + +void ram_func 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; + + // &ioqspi_hw->io[1].ctrl + uint32_t *addr = (uint32_t*)(IO_QSPI_BASE + (1 * 8) + 4); + + *addr = ((*addr) & !IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS) + | (field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB); + +} + // See https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_flash/flash.c#L86 void ram_func flash_range_write(uint32_t offset, const uint8_t *data, size_t count) { @@ -132,6 +159,42 @@ void ram_func flash_erase_blocks(uint32_t offset, size_t count) flash_enable_xip_via_boot2(); } +void ram_func flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) { + flash_connect_internal_fn flash_connect_internal_func = (flash_connect_internal_fn) rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH); + flash_exit_xip_fn flash_exit_xip_func = (flash_exit_xip_fn) rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP); + flash_flush_cache_fn flash_flush_cache_func = (flash_flush_cache_fn) rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE); + + flash_init_boot2_copyout(); + + __compiler_memory_barrier(); + + flash_connect_internal_func(); + flash_exit_xip_func(); + + 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 = *(uint32_t*)(XIP_SSI_BASE + SSI_SR_OFFSET); + 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) { + *(uint32_t*)(XIP_SSI_BASE + SSI_DR0_OFFSET) = *txbuf++; + --tx_remaining; + } + if (can_get && rx_remaining) { + *rxbuf++ = (uint8_t)*(uint32_t*)(XIP_SSI_BASE + SSI_DR0_OFFSET); + --rx_remaining; + } + } + flash_cs_force(1); + + flash_flush_cache_func(); + flash_enable_xip_via_boot2(); +} + */ import "C" @@ -139,6 +202,19 @@ func enterBootloader() { C.reset_usb_boot(0, 0) } +func doFlashCommand(tx []byte, rx []byte) error { + if len(tx) != len(rx) { + return errFlashInvalidWriteLength + } + + C.flash_do_cmd( + (*C.uint8_t)(unsafe.Pointer(&tx[0])), + (*C.uint8_t)(unsafe.Pointer(&rx[0])), + C.ulong(len(tx))) + + return nil +} + // Flash related code const memoryStart = C.XIP_BASE // memory start for purpose of erase