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 <angus@redyak.com.au>
This commit is contained in:
Angus Gratton 2023-11-08 10:37:04 +11:00 committed by Damien George
parent f567a9255a
commit 8b1980ad45
5 changed files with 57 additions and 43 deletions

View File

@ -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);

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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)

View File

@ -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) {