Skip to content

Fixing unique id DEVICE_ID3 on STM32L0 and STM32L1 - #3083

Open
acseka360 wants to merge 1 commit into
stm32duino:mainfrom
acseka360:main
Open

acseka360 wants to merge 1 commit into
stm32duino:mainfrom
acseka360:main

Conversation

@acseka360

Copy link
Copy Markdown

fix(usb): read UID word 2 at its documented offset on STM32L0/L1

Summary

On STM32L0 and STM32L1, DEVICE_ID3 points at a reserved memory location
instead of the third word of the 96-bit unique device ID. The USB serial
number string is therefore derived from only 64 bits of the ID plus a
constant, and devices from the same production lot and wafer enumerate with
byte-identical USB serial numbers
.

The bug

libraries/USBDevice/inc/usbd_desc.h:

#define  DEVICE_ID1                  (UID_BASE)
#define  DEVICE_ID2                  (UID_BASE + 0x4U)
#define  DEVICE_ID3                  (UID_BASE + 0x8U)   // wrong on L0/L1

+0x8U is correct for every STM32 family except STM32L0 and STM32L1,
where the three UID words are not contiguous. The reference manuals place
bits 95:64 at offset 0x14:

word bits offset
0 31:0 0x00
1 63:32 0x04
2 95:64 0x14

(RM0376 §"Unique device ID registers (96 bits)" for STM32L0x2; likewise
RM0377 L0x1, RM0367 L0x3, RM0451 L011/L021, and RM0038 for STM32L1.)

UID_BASE + 0x8U on these parts is reserved space. On the STM32L072 I tested
it reads a constant 0x00000011.

This repository already disagrees with itself

ST's HAL, vendored in this same tree, gets it right:

// system/Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal.c
uint32_t HAL_GetUIDw2(void)
{
  return(READ_REG(*((uint32_t *)(UID_BASE + 0x14U))));   // 0x14 here
}

I scanned HAL_GetUIDw2() across every HAL driver in the tree. STM32L0xx
and STM32L1xx are the only two families using 0x14U; all 22 others use
8U.
So the conditional below is exactly as narrow as it needs to be.

ST has confirmed the offset publicly. Their moderator acknowledged the same
bug in the LL driver and stated the reference-manual values are correct:
https://community.st.com/t5/stm32-mcus-products/how-to-report-issues-on-ll-hal-driver-for-stm32l0-incorrect/td-p/457949

Why it matters in practice

Per RM0451 §25.2 the L0 UID fields are laid out as:

  • bits 23:0 — LOT_NUM (ASCII)
  • bits 31:24 — WAF_NUM
  • bits 63:32 — LOT_NUM continued (ASCII)
  • bits 95:64 — the remaining unique bits

Words 0 and 1 are therefore lot and wafer identifiers, shared by every die
from the same wafer
. The bits that distinguish individual dies live in word
2 — precisely the word DEVICE_ID3 fails to read. Get_SerialNum() computes
serial = hex8(ID1 + ID3) + hex4(ID2), so with the wrong ID3 the serial is
a function of lot and wafer only.

ST's moderator makes the same point: "If you want to use the Unique ID, you
would actually have to use the full 96bit"
, warning that partial fields may
be identical across devices from one batch.
https://community.st.com/stm32-mcus-products-25/stm32l0x0-are-unique-id-bits-95-64-of-unique-device-id-registers-at0x1ff8-0050-uniques-21113

Concretely: I have three physically distinct boards, reel-soldered from one
lot, all running STM32L072KBUx. Before this change all three enumerated as
USB serial 034733453934
, which broke udev rules, /dev/serial/by-id
paths, and every attempt to tell one board from another on the same host.

This also produces false counterfeit reports. Users have concluded their
parts were fake after seeing identical "unique" IDs, when the cause was this
offset:
https://community.st.com/t5/stm32-mcus-products/non-unique-unique-device-id-or-counterfeit-parts/td-p/407850

The fix

#if defined(STM32L0xx) || defined(STM32L1xx)
  #define  DEVICE_ID3                (UID_BASE + 0x14U)
#else
  #define  DEVICE_ID3                (UID_BASE + 0x8U)
#endif

No change on any other family.

Verification

Hardware: STM32L072KBUx, USB CDC, core 4.21200.0.

Read over SWD, the true UID is:

UID[31:0]  @0x1FF80050 = 0x03473334   LOT_NUM "43G", WAF_NUM 3
UID[63:32] @0x1FF80054 = 0x39343436   LOT_NUM "6449"
UID[95:64] @0x1FF80064 = 0x002D002D   <- the distinguishing word
           @0x1FF80058 = 0x00000011   <- reserved; what DEVICE_ID3 read

Get_SerialNum() predicts hex8(0x03473334 + 0x002D002D) + hex4(0x39343436)
= 03743361 + 3934.

CDC serial
before 034733453934
after 037433613934
predicted 037433613934

Matches, and the serial now depends on the word that actually varies between
dies.

On STM32L0 and STM32L1, `DEVICE_ID3` points at a reserved memory location
instead of the third word of the 96-bit unique device ID. The USB serial
number string is therefore derived from only 64 bits of the ID plus a
constant, and **devices from the same production lot and wafer enumerate with
byte-identical USB serial numbers**.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant