Skip to content

Fixing unique id DEVICE_ID3 on STM32L0 and STM32L1 - #3083

Merged
fpistm merged 1 commit into
stm32duino:mainfrom
acseka360:main
Sep 22, 2026
Merged

fpistm merged 1 commit into
stm32duino:mainfrom
acseka360:main

Conversation

@acseka360

Copy link
Copy Markdown
Contributor

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.

@fpistm

fpistm commented Sep 21, 2026

Copy link
Copy Markdown
Member

Hi @acseka360
Thanks for pointing this.

I guess an easiest way would be to use LL API:

  #define  DEVICE_ID1                  LL_GetUID_Word0()
  #define  DEVICE_ID2                  LL_GetUID_Word1()
  #define  DEVICE_ID3                  LL_GetUID_Word2()

@acseka360

Copy link
Copy Markdown
Contributor Author

true, should i modify the PR?

@fpistm

fpistm commented Sep 21, 2026

Copy link
Copy Markdown
Member

Yes, please.
I'm also wonder if we should kept the DEVICE_IDx definition and called directly the ll api in the Get_SerialNum() function.

@fpistm

fpistm commented Sep 21, 2026

Copy link
Copy Markdown
Member

@acseka360
In fact, I've updated your branch with the new fix.

@fpistm fpistm added the fix 🩹 Bug fix label Sep 21, 2026
@fpistm fpistm added this to the 3.0.1/3.1.0 milestone Sep 21, 2026
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**.

Signed-off-by: aron.cserkaszky <aron.cserkaszky@360.world>
@fpistm
fpistm merged commit 54b6389 into stm32duino:main Sep 22, 2026
29 checks passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in STM32 core based on ST HAL Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix 🩹 Bug fix

Projects

Development

Successfully merging this pull request may close these issues.

2 participants