From 8b1980ad450ea2b34af4b831a8c655ba08dee800 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 8 Nov 2023 10:37:04 +1100 Subject: [PATCH] samd: Use unique id for USB serial number. Replaces the previous all-zeroes "TODO" serial number. Requires refactoring the low-level unique_id routine out from modmachine.c. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- ports/samd/modmachine.c | 42 +++------------------------------------ ports/samd/mpconfigport.h | 5 +++++ ports/samd/samd_soc.c | 36 +++++++++++++++++++++++++++++++++ ports/samd/samd_soc.h | 8 ++++++++ ports/samd/usbd.c | 9 +++++---- 5 files changed, 57 insertions(+), 43 deletions(-) diff --git a/ports/samd/modmachine.c b/ports/samd/modmachine.c index 7c3d96eb0136..8c4808075e7a 100644 --- a/ports/samd/modmachine.c +++ b/ports/samd/modmachine.c @@ -92,45 +92,9 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq); STATIC mp_obj_t machine_unique_id(void) { - // Each device has a unique 128-bit serial number which is a concatenation of four 32-bit - // words contained at the following addresses. The uniqueness of the serial number is - // guaranteed only when using all 128 bits. - // Atmel SAM D21E / SAM D21G / SAM D21J - // SMART ARM-Based Microcontroller - // DATASHEET - // 9.6 (SAMD51) or 9.3.3 (or 10.3.3 depending on which manual)(SAMD21) Serial Number - // - // EXAMPLE (SAMD21) - // ---------------- - // OpenOCD: - // Word0: - // > at91samd21g18.cpu mdw 0x0080A00C 1 - // 0x0080a00c: 6e27f15f - // Words 1-3: - // > at91samd21g18.cpu mdw 0x0080A040 3 - // 0x0080a040: 50534b54 332e3120 ff091645 - // - // MicroPython (this code and same order as shown in Arduino IDE) - // >>> binascii.hexlify(machine.unique_id()) - // b'6e27f15f50534b54332e3120ff091645' - - #if defined(MCU_SAMD21) - uint32_t *id_addresses[4] = {(uint32_t *)0x0080A00C, (uint32_t *)0x0080A040, - (uint32_t *)0x0080A044, (uint32_t *)0x0080A048}; - #elif defined(MCU_SAMD51) - uint32_t *id_addresses[4] = {(uint32_t *)0x008061FC, (uint32_t *)0x00806010, - (uint32_t *)0x00806014, (uint32_t *)0x00806018}; - #endif - uint8_t raw_id[16]; - - for (int i = 0; i < 4; i++) { - for (int k = 0; k < 4; k++) { - // 'Reverse' the read bytes into a 32 bit word (Consistent with Arduino) - raw_id[4 * i + k] = (*(id_addresses[i]) >> (24 - k * 8)) & 0xff; - } - } - - return mp_obj_new_bytes((byte *)&raw_id, sizeof(raw_id)); + samd_unique_id_t id; + samd_get_unique_id(&id); + return mp_obj_new_bytes((byte *)&id.bytes, sizeof(id.bytes)); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 497ca9a87004..3684457b48e0 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -67,6 +67,11 @@ #ifndef MICROPY_HW_USB_CDC #define MICROPY_HW_USB_CDC (1) #endif +// SAMD unique ID is 16 bytes (hex string == 32) +#ifndef MICROPY_HW_USB_DESC_STR_MAX +#define MICROPY_HW_USB_DESC_STR_MAX (32) +#endif + #endif // Control over Python builtins diff --git a/ports/samd/samd_soc.c b/ports/samd/samd_soc.c index ad5910301cfc..dcfff9dc2274 100644 --- a/ports/samd/samd_soc.c +++ b/ports/samd/samd_soc.c @@ -153,3 +153,39 @@ void sercom_deinit_all(void) { } #endif + +void samd_get_unique_id(samd_unique_id_t *id) { + // Atmel SAM D21E / SAM D21G / SAM D21J + // SMART ARM-Based Microcontroller + // DATASHEET + // 9.6 (SAMD51) or 9.3.3 (or 10.3.3 depending on which manual)(SAMD21) Serial Number + // + // EXAMPLE (SAMD21) + // ---------------- + // OpenOCD: + // Word0: + // > at91samd21g18.cpu mdw 0x0080A00C 1 + // 0x0080a00c: 6e27f15f + // Words 1-3: + // > at91samd21g18.cpu mdw 0x0080A040 3 + // 0x0080a040: 50534b54 332e3120 ff091645 + // + // MicroPython (this code and same order as shown in Arduino IDE) + // >>> binascii.hexlify(machine.unique_id()) + // b'6e27f15f50534b54332e3120ff091645' + + #if defined(MCU_SAMD21) + uint32_t *id_addresses[4] = {(uint32_t *)0x0080A00C, (uint32_t *)0x0080A040, + (uint32_t *)0x0080A044, (uint32_t *)0x0080A048}; + #elif defined(MCU_SAMD51) + uint32_t *id_addresses[4] = {(uint32_t *)0x008061FC, (uint32_t *)0x00806010, + (uint32_t *)0x00806014, (uint32_t *)0x00806018}; + #endif + + for (int i = 0; i < 4; i++) { + for (int k = 0; k < 4; k++) { + // 'Reverse' the read bytes into a 32 bit word (Consistent with Arduino) + id->bytes[4 * i + k] = (*(id_addresses[i]) >> (24 - k * 8)) & 0xff; + } + } +} diff --git a/ports/samd/samd_soc.h b/ports/samd/samd_soc.h index 90a5a57ffa14..707d2f8edfa9 100644 --- a/ports/samd/samd_soc.h +++ b/ports/samd/samd_soc.h @@ -30,6 +30,10 @@ #include "sam.h" #include "clock_config.h" +typedef struct _samd_unique_id_t { + uint8_t bytes[16]; +} samd_unique_id_t; + extern Sercom *sercom_instance[]; void samd_init(void); @@ -40,6 +44,10 @@ void USB_Handler_wrapper(void); void sercom_enable(Sercom *spi, int state); void sercom_register_irq(int sercom_id, void (*sercom_irq_handler)); +// Each device has a unique 128-bit serial number. The uniqueness of the serial number is +// guaranteed only when using all 128 bits. +void samd_get_unique_id(samd_unique_id_t *id); + #define SERCOM_IRQ_TYPE_UART (0) #define SERCOM_IRQ_TYPE_SPI (1) #define SERCOM_IRQ_TYPE_I2C (2) diff --git a/ports/samd/usbd.c b/ports/samd/usbd.c index 90f2e1bfd95e..2e3200ad7d2b 100644 --- a/ports/samd/usbd.c +++ b/ports/samd/usbd.c @@ -31,12 +31,13 @@ #include "mp_usbd.h" #include "string.h" #include "tusb.h" - -#define SERIAL_TODO "000000000000" // TODO +#include "samd_soc.h" void mp_usbd_port_get_serial_number(char *serial_buf) { - MP_STATIC_ASSERT(sizeof(SERIAL_TODO) <= MICROPY_HW_USB_DESC_STR_MAX); - strcpy(serial_buf, SERIAL_TODO); + samd_unique_id_t id; + samd_get_unique_id(&id); + MP_STATIC_ASSERT(sizeof(id.bytes) * 2 <= MICROPY_HW_USB_DESC_STR_MAX); + mp_usbd_hex_str(serial_buf, id.bytes, sizeof(id.bytes)); } void USB_Handler_wrapper(void) {