Skip to content

Commit 9fb5a5b

Browse files
kenbelldeadprogram
authored andcommitted
nrf,sam,rp2040: add machine.HardwareID function
1 parent 9fd9d9c commit 9fb5a5b

File tree

9 files changed

+207
-0
lines changed

9 files changed

+207
-0
lines changed

GNUmakefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,8 @@ smoketest:
506506
@$(MD5SUM) test.hex
507507
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
508508
@$(MD5SUM) test.hex
509+
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/device-id
510+
@$(MD5SUM) test.hex
509511
# test simulated boards on play.tinygo.org
510512
ifneq ($(WASM), 0)
511513
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1

src/examples/device-id/main.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package main
2+
3+
import (
4+
"encoding/hex"
5+
"machine"
6+
"time"
7+
)
8+
9+
func main() {
10+
time.Sleep(2 * time.Second)
11+
12+
// For efficiency, it's best to get the device ID once and cache it
13+
// (e.g. on RP2040 XIP flash and interrupts disabled for period of
14+
// retrieving the hardware ID from ROM chip)
15+
id := machine.DeviceID()
16+
17+
for {
18+
println("Device ID:", hex.EncodeToString(id))
19+
time.Sleep(1 * time.Second)
20+
}
21+
}

src/machine/deviceid.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//go:build rp2040 || nrf || sam
2+
3+
package machine
4+
5+
// DeviceID returns an identifier that is unique within
6+
// a particular chipset.
7+
//
8+
// The identity is one burnt into the MCU itself, or the
9+
// flash chip at time of manufacture.
10+
//
11+
// It's possible that two different vendors may allocate
12+
// the same DeviceID, so callers should take this into
13+
// account if needing to generate a globally unique id.
14+
//
15+
// The length of the hardware ID is vendor-specific, but
16+
// 8 bytes (64 bits) and 16 bytes (128 bits) are common.
17+
var _ = (func() []byte)(DeviceID)

src/machine/machine_atsam.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//go:build sam
2+
3+
package machine
4+
5+
import (
6+
"runtime/volatile"
7+
"unsafe"
8+
)
9+
10+
var deviceID [16]byte
11+
12+
// DeviceID returns an identifier that is unique within
13+
// a particular chipset.
14+
//
15+
// The identity is one burnt into the MCU itself, or the
16+
// flash chip at time of manufacture.
17+
//
18+
// It's possible that two different vendors may allocate
19+
// the same DeviceID, so callers should take this into
20+
// account if needing to generate a globally unique id.
21+
//
22+
// The length of the hardware ID is vendor-specific, but
23+
// 8 bytes (64 bits) and 16 bytes (128 bits) are common.
24+
func DeviceID() []byte {
25+
for i := 0; i < len(deviceID); i++ {
26+
word := (*volatile.Register32)(unsafe.Pointer(deviceIDAddr[i/4])).Get()
27+
deviceID[i] = byte(word >> ((i % 4) * 8))
28+
}
29+
30+
return deviceID[:]
31+
}

src/machine/machine_atsamd21.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import (
1818

1919
const deviceName = sam.Device
2020

21+
// DS40001882F, Section 10.3.3: Serial Number
22+
var deviceIDAddr = []uintptr{0x0080A00C, 0x0080A040, 0x0080A044, 0x0080A048}
23+
2124
const (
2225
PinAnalog PinMode = 1
2326
PinSERCOM PinMode = 2

src/machine/machine_atsamd51.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import (
1818

1919
const deviceName = sam.Device
2020

21+
// DS60001507, Section 9.6: Serial Number
22+
var deviceIDAddr = []uintptr{0x008061FC, 0x00806010, 0x00806014, 0x00806018}
23+
2124
func CPUFrequency() uint32 {
2225
return 120000000
2326
}

src/machine/machine_nrf.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,34 @@ import (
1212

1313
const deviceName = nrf.Device
1414

15+
var deviceID [8]byte
16+
17+
// DeviceID returns an identifier that is unique within
18+
// a particular chipset.
19+
//
20+
// The identity is one burnt into the MCU itself, or the
21+
// flash chip at time of manufacture.
22+
//
23+
// It's possible that two different vendors may allocate
24+
// the same DeviceID, so callers should take this into
25+
// account if needing to generate a globally unique id.
26+
//
27+
// The length of the hardware ID is vendor-specific, but
28+
// 8 bytes (64 bits) is common.
29+
func DeviceID() []byte {
30+
words := make([]uint32, 2)
31+
words[0] = nrf.FICR.DEVICEID[0].Get()
32+
words[1] = nrf.FICR.DEVICEID[1].Get()
33+
34+
for i := 0; i < 8; i++ {
35+
shift := (i % 4) * 8
36+
w := i / 4
37+
deviceID[i] = byte(words[w] >> shift)
38+
}
39+
40+
return deviceID[:]
41+
}
42+
1543
const (
1644
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)
1745
PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)

src/machine/machine_rp2040_flash.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,32 @@ func EnterBootloader() {
1313
enterBootloader()
1414
}
1515

16+
// 13 = 1 + FLASH_RUID_DUMMY_BYTES(4) + FLASH_RUID_DATA_BYTES(8)
17+
var deviceIDBuf [13]byte
18+
19+
// DeviceID returns an identifier that is unique within
20+
// a particular chipset.
21+
//
22+
// The identity is one burnt into the MCU itself, or the
23+
// flash chip at time of manufacture.
24+
//
25+
// It's possible that two different vendors may allocate
26+
// the same DeviceID, so callers should take this into
27+
// account if needing to generate a globally unique id.
28+
//
29+
// The length of the hardware ID is vendor-specific, but
30+
// 8 bytes (64 bits) is common.
31+
func DeviceID() []byte {
32+
deviceIDBuf[0] = 0x4b // FLASH_RUID_CMD
33+
34+
err := doFlashCommand(deviceIDBuf[:], deviceIDBuf[:])
35+
if err != nil {
36+
panic(err)
37+
}
38+
39+
return deviceIDBuf[5:13]
40+
}
41+
1642
// compile-time check for ensuring we fulfill BlockDevice interface
1743
var _ BlockDevice = flashBlockDevice{}
1844

src/machine/machine_rp2040_rom.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,33 @@ static ram_func void flash_enable_xip_via_boot2() {
9393
((void (*)(void))boot2_copyout+1)();
9494
}
9595
96+
#define IO_QSPI_BASE 0x40018000
97+
#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS 0x00000300
98+
#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_MSB 9
99+
#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB 8
100+
#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW 0x2
101+
#define IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH 0x3
102+
103+
#define XIP_SSI_BASE 0x18000000
104+
#define ssi_hw ((ssi_hw_t *)XIP_SSI_BASE)
105+
#define SSI_SR_OFFSET 0x00000028
106+
#define SSI_DR0_OFFSET 0x00000060
107+
#define SSI_SR_TFNF_BITS 0x00000002
108+
#define SSI_SR_RFNE_BITS 0x00000008
109+
110+
void ram_func flash_cs_force(bool high) {
111+
uint32_t field_val = high ?
112+
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH :
113+
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW;
114+
115+
// &ioqspi_hw->io[1].ctrl
116+
uint32_t *addr = (uint32_t*)(IO_QSPI_BASE + (1 * 8) + 4);
117+
118+
*addr = ((*addr) & !IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS)
119+
| (field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB);
120+
121+
}
122+
96123
// See https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_flash/flash.c#L86
97124
void ram_func flash_range_write(uint32_t offset, const uint8_t *data, size_t count)
98125
{
@@ -132,13 +159,62 @@ void ram_func flash_erase_blocks(uint32_t offset, size_t count)
132159
flash_enable_xip_via_boot2();
133160
}
134161
162+
void ram_func flash_do_cmd(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) {
163+
flash_connect_internal_fn flash_connect_internal_func = (flash_connect_internal_fn) rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH);
164+
flash_exit_xip_fn flash_exit_xip_func = (flash_exit_xip_fn) rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP);
165+
flash_flush_cache_fn flash_flush_cache_func = (flash_flush_cache_fn) rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE);
166+
167+
flash_init_boot2_copyout();
168+
169+
__compiler_memory_barrier();
170+
171+
flash_connect_internal_func();
172+
flash_exit_xip_func();
173+
174+
flash_cs_force(0);
175+
size_t tx_remaining = count;
176+
size_t rx_remaining = count;
177+
// We may be interrupted -- don't want FIFO to overflow if we're distracted.
178+
const size_t max_in_flight = 16 - 2;
179+
while (tx_remaining || rx_remaining) {
180+
uint32_t flags = *(uint32_t*)(XIP_SSI_BASE + SSI_SR_OFFSET);
181+
bool can_put = !!(flags & SSI_SR_TFNF_BITS);
182+
bool can_get = !!(flags & SSI_SR_RFNE_BITS);
183+
if (can_put && tx_remaining && rx_remaining - tx_remaining < max_in_flight) {
184+
*(uint32_t*)(XIP_SSI_BASE + SSI_DR0_OFFSET) = *txbuf++;
185+
--tx_remaining;
186+
}
187+
if (can_get && rx_remaining) {
188+
*rxbuf++ = (uint8_t)*(uint32_t*)(XIP_SSI_BASE + SSI_DR0_OFFSET);
189+
--rx_remaining;
190+
}
191+
}
192+
flash_cs_force(1);
193+
194+
flash_flush_cache_func();
195+
flash_enable_xip_via_boot2();
196+
}
197+
135198
*/
136199
import "C"
137200

138201
func enterBootloader() {
139202
C.reset_usb_boot(0, 0)
140203
}
141204

205+
func doFlashCommand(tx []byte, rx []byte) error {
206+
if len(tx) != len(rx) {
207+
return errFlashInvalidWriteLength
208+
}
209+
210+
C.flash_do_cmd(
211+
(*C.uint8_t)(unsafe.Pointer(&tx[0])),
212+
(*C.uint8_t)(unsafe.Pointer(&rx[0])),
213+
C.ulong(len(tx)))
214+
215+
return nil
216+
}
217+
142218
// Flash related code
143219
const memoryStart = C.XIP_BASE // memory start for purpose of erase
144220

0 commit comments

Comments
 (0)