Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nrf,sam,rp2040: add machine.DeviceID function #3968

Merged
merged 1 commit into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions src/examples/device-id/main.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
17 changes: 17 additions & 0 deletions src/machine/deviceid.go
Original file line number Diff line number Diff line change
@@ -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)
31 changes: 31 additions & 0 deletions src/machine/machine_atsam.go
Original file line number Diff line number Diff line change
@@ -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[:]
}
3 changes: 3 additions & 0 deletions src/machine/machine_atsamd21.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/machine/machine_atsamd51.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
28 changes: 28 additions & 0 deletions src/machine/machine_nrf.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
26 changes: 26 additions & 0 deletions src/machine/machine_rp2040_flash.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}

Expand Down
76 changes: 76 additions & 0 deletions src/machine/machine_rp2040_rom.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -132,13 +159,62 @@ 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"

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

Expand Down
Loading