nrf,sam,rp2040: add machine.HardwareID function
Этот коммит содержится в:
родитель
9fd9d9c05a
коммит
9fb5a5b9a4
9 изменённых файлов: 207 добавлений и 0 удалений
|
@ -506,6 +506,8 @@ smoketest:
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
|
$(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/watchdog
|
||||||
@$(MD5SUM) test.hex
|
@$(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
|
# test simulated boards on play.tinygo.org
|
||||||
ifneq ($(WASM), 0)
|
ifneq ($(WASM), 0)
|
||||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||||
|
|
21
src/examples/device-id/main.go
Обычный файл
21
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)
|
||||||
|
}
|
||||||
|
}
|
17
src/machine/deviceid.go
Обычный файл
17
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)
|
31
src/machine/machine_atsam.go
Обычный файл
31
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[:]
|
||||||
|
}
|
|
@ -18,6 +18,9 @@ import (
|
||||||
|
|
||||||
const deviceName = sam.Device
|
const deviceName = sam.Device
|
||||||
|
|
||||||
|
// DS40001882F, Section 10.3.3: Serial Number
|
||||||
|
var deviceIDAddr = []uintptr{0x0080A00C, 0x0080A040, 0x0080A044, 0x0080A048}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PinAnalog PinMode = 1
|
PinAnalog PinMode = 1
|
||||||
PinSERCOM PinMode = 2
|
PinSERCOM PinMode = 2
|
||||||
|
|
|
@ -18,6 +18,9 @@ import (
|
||||||
|
|
||||||
const deviceName = sam.Device
|
const deviceName = sam.Device
|
||||||
|
|
||||||
|
// DS60001507, Section 9.6: Serial Number
|
||||||
|
var deviceIDAddr = []uintptr{0x008061FC, 0x00806010, 0x00806014, 0x00806018}
|
||||||
|
|
||||||
func CPUFrequency() uint32 {
|
func CPUFrequency() uint32 {
|
||||||
return 120000000
|
return 120000000
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,34 @@ import (
|
||||||
|
|
||||||
const deviceName = nrf.Device
|
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 (
|
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)
|
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)
|
PinInputPullup PinMode = PinInput | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos)
|
||||||
|
|
|
@ -13,6 +13,32 @@ func EnterBootloader() {
|
||||||
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
|
// compile-time check for ensuring we fulfill BlockDevice interface
|
||||||
var _ BlockDevice = flashBlockDevice{}
|
var _ BlockDevice = flashBlockDevice{}
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,33 @@ static ram_func void flash_enable_xip_via_boot2() {
|
||||||
((void (*)(void))boot2_copyout+1)();
|
((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
|
// 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)
|
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();
|
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"
|
import "C"
|
||||||
|
|
||||||
|
@ -139,6 +202,19 @@ func enterBootloader() {
|
||||||
C.reset_usb_boot(0, 0)
|
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
|
// Flash related code
|
||||||
const memoryStart = C.XIP_BASE // memory start for purpose of erase
|
const memoryStart = C.XIP_BASE // memory start for purpose of erase
|
||||||
|
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче