Skip to content

Commit

Permalink
samd: Use unique id for USB serial number.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
projectgus authored and dpgeorge committed Nov 16, 2023
1 parent f567a92 commit 8b1980a
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 43 deletions.
42 changes: 3 additions & 39 deletions ports/samd/modmachine.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
5 changes: 5 additions & 0 deletions ports/samd/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions ports/samd/samd_soc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
8 changes: 8 additions & 0 deletions ports/samd/samd_soc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
Expand Down
9 changes: 5 additions & 4 deletions ports/samd/usbd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 8b1980a

Please sign in to comment.