Skip to main content

airfrog_core/
stm.rs

1// Copyright (C) 2025 Piers Finlayson <piers@piers.rocks>
2//
3// MIT License
4
5//! airfrog-core - STM32 specific objects
6
7use alloc::{format, string::String};
8use core::fmt;
9use core::ops::RangeInclusive;
10
11use crate::arm::Cortex;
12use crate::arm::ap::Idr;
13use crate::arm::ap::{IDR_AHB_AP_CORTEX_M3, IDR_AHB_AP_CORTEX_M4};
14use crate::arm::dp::IdCode;
15
16// STM32F4 Flash memory base address
17const STM32F1_FLASH_BASE: u32 = 0x0800_0000;
18const STM32F4_FLASH_BASE: u32 = 0x0800_0000;
19
20// STM32F4 RAM memory base address
21const STM32F4_RAM_BASE: u32 = 0x2000_0000;
22const STM32F1_RAM_BASE: u32 = 0x2000_0000;
23
24// GPIO register offsets
25const GPIOX_MODER_OFFSET: u32 = 0x00;
26const GPIOX_OTYPER_OFFSET: u32 = 0x04;
27const GPIOX_OSPEEDR_OFFSET: u32 = 0x08;
28const GPIOX_PUPDR_OFFSET: u32 = 0x0C;
29const GPIOX_IDR_OFFSET: u32 = 0x10;
30const GPIOX_ODR_OFFSET: u32 = 0x14;
31const GPIOX_BSRR_OFFSET: u32 = 0x18;
32const GPIOX_LCKR_OFFSET: u32 = 0x1C;
33const GPIOX_AFRL_OFFSET: u32 = 0x20;
34const GPIOX_AFRH_OFFSET: u32 = 0x24;
35
36/// STM32F4 GPIO Port A register addresses
37const STM32F4_GPIOA_REG_BASE: u32 = 0x4002_0000;
38pub const STM32F4_GPIOA_MODER: u32 = STM32F4_GPIOA_REG_BASE + GPIOX_MODER_OFFSET;
39pub const STM32F4_GPIOA_OTYPER: u32 = STM32F4_GPIOA_REG_BASE + GPIOX_OTYPER_OFFSET;
40pub const STM32F4_GPIOA_OSPEEDR: u32 = STM32F4_GPIOA_REG_BASE + GPIOX_OSPEEDR_OFFSET;
41pub const STM32F4_GPIOA_PUPDR: u32 = STM32F4_GPIOA_REG_BASE + GPIOX_PUPDR_OFFSET;
42pub const STM32F4_GPIOA_IDR: u32 = STM32F4_GPIOA_REG_BASE + GPIOX_IDR_OFFSET;
43pub const STM32F4_GPIOA_ODR: u32 = STM32F4_GPIOA_REG_BASE + GPIOX_ODR_OFFSET;
44pub const STM32F4_GPIOA_BSRR: u32 = STM32F4_GPIOA_REG_BASE + GPIOX_BSRR_OFFSET;
45pub const STM32F4_GPIOA_LCKR: u32 = STM32F4_GPIOA_REG_BASE + GPIOX_LCKR_OFFSET;
46pub const STM32F4_GPIOA_AFRL: u32 = STM32F4_GPIOA_REG_BASE + GPIOX_AFRL_OFFSET;
47pub const STM32F4_GPIOA_AFRH: u32 = STM32F4_GPIOA_REG_BASE + GPIOX_AFRH_OFFSET;
48
49/// STM32F4 GPIO Port B register base address
50const STM32F4_GPIOB_REG_BASE: u32 = 0x4002_0400;
51pub const STM32F4_GPIOB_MODER: u32 = STM32F4_GPIOB_REG_BASE + GPIOX_MODER_OFFSET;
52pub const STM32F4_GPIOB_OTYPER: u32 = STM32F4_GPIOB_REG_BASE + GPIOX_OTYPER_OFFSET;
53pub const STM32F4_GPIOB_OSPEEDR: u32 = STM32F4_GPIOB_REG_BASE + GPIOX_OSPEEDR_OFFSET;
54pub const STM32F4_GPIOB_PUPDR: u32 = STM32F4_GPIOB_REG_BASE + GPIOX_PUPDR_OFFSET;
55pub const STM32F4_GPIOB_IDR: u32 = STM32F4_GPIOB_REG_BASE + GPIOX_IDR_OFFSET;
56pub const STM32F4_GPIOB_ODR: u32 = STM32F4_GPIOB_REG_BASE + GPIOX_ODR_OFFSET;
57pub const STM32F4_GPIOB_BSRR: u32 = STM32F4_GPIOB_REG_BASE + GPIOX_BSRR_OFFSET;
58pub const STM32F4_GPIOB_LCKR: u32 = STM32F4_GPIOB_REG_BASE + GPIOX_LCKR_OFFSET;
59pub const STM32F4_GPIOB_AFRL: u32 = STM32F4_GPIOB_REG_BASE + GPIOX_AFRL_OFFSET;
60pub const STM32F4_GPIOB_AFRH: u32 = STM32F4_GPIOB_REG_BASE + GPIOX_AFRH_OFFSET;
61
62/// STM32F4 GPIO Port C register base address
63const STM32F4_GPIOC_REG_BASE: u32 = 0x4002_0800;
64pub const STM32F4_GPIOC_MODER: u32 = STM32F4_GPIOC_REG_BASE + GPIOX_MODER_OFFSET;
65pub const STM32F4_GPIOC_OTYPER: u32 = STM32F4_GPIOC_REG_BASE + GPIOX_OTYPER_OFFSET;
66pub const STM32F4_GPIOC_OSPEEDR: u32 = STM32F4_GPIOC_REG_BASE + GPIOX_OSPEEDR_OFFSET;
67pub const STM32F4_GPIOC_PUPDR: u32 = STM32F4_GPIOC_REG_BASE + GPIOX_PUPDR_OFFSET;
68pub const STM32F4_GPIOC_IDR: u32 = STM32F4_GPIOC_REG_BASE + GPIOX_IDR_OFFSET;
69pub const STM32F4_GPIOC_ODR: u32 = STM32F4_GPIOC_REG_BASE + GPIOX_ODR_OFFSET;
70pub const STM32F4_GPIOC_BSRR: u32 = STM32F4_GPIOC_REG_BASE + GPIOX_BSRR_OFFSET;
71pub const STM32F4_GPIOC_LCKR: u32 = STM32F4_GPIOC_REG_BASE + GPIOX_LCKR_OFFSET;
72pub const STM32F4_GPIOC_AFRL: u32 = STM32F4_GPIOC_REG_BASE + GPIOX_AFRL_OFFSET;
73pub const STM32F4_GPIOC_AFRH: u32 = STM32F4_GPIOC_REG_BASE + GPIOX_AFRH_OFFSET;
74
75/// STM32F4 GPIO Port D register base address
76const STM32F4_GPIOD_REG_BASE: u32 = 0x4002_0C00;
77pub const STM32F4_GPIOD_MODER: u32 = STM32F4_GPIOD_REG_BASE + GPIOX_MODER_OFFSET;
78pub const STM32F4_GPIOD_OTYPER: u32 = STM32F4_GPIOD_REG_BASE + GPIOX_OTYPER_OFFSET;
79pub const STM32F4_GPIOD_OSPEEDR: u32 = STM32F4_GPIOD_REG_BASE + GPIOX_OSPEEDR_OFFSET;
80pub const STM32F4_GPIOD_PUPDR: u32 = STM32F4_GPIOD_REG_BASE + GPIOX_PUPDR_OFFSET;
81pub const STM32F4_GPIOD_IDR: u32 = STM32F4_GPIOD_REG_BASE + GPIOX_IDR_OFFSET;
82pub const STM32F4_GPIOD_ODR: u32 = STM32F4_GPIOD_REG_BASE + GPIOX_ODR_OFFSET;
83pub const STM32F4_GPIOD_BSRR: u32 = STM32F4_GPIOD_REG_BASE + GPIOX_BSRR_OFFSET;
84pub const STM32F4_GPIOD_LCKR: u32 = STM32F4_GPIOD_REG_BASE + GPIOX_LCKR_OFFSET;
85pub const STM32F4_GPIOD_AFRL: u32 = STM32F4_GPIOD_REG_BASE + GPIOX_AFRL_OFFSET;
86pub const STM32F4_GPIOD_AFRH: u32 = STM32F4_GPIOD_REG_BASE + GPIOX_AFRH_OFFSET;
87
88/// STM32F4 GPIO Port E register addresses
89const STM32F4_GPIOE_REG_BASE: u32 = 0x4002_1000;
90pub const STM32F4_GPIOE_MODER: u32 = STM32F4_GPIOE_REG_BASE + GPIOX_MODER_OFFSET;
91pub const STM32F4_GPIOE_OTYPER: u32 = STM32F4_GPIOE_REG_BASE + GPIOX_OTYPER_OFFSET;
92pub const STM32F4_GPIOE_OSPEEDR: u32 = STM32F4_GPIOE_REG_BASE + GPIOX_OSPEEDR_OFFSET;
93pub const STM32F4_GPIOE_PUPDR: u32 = STM32F4_GPIOE_REG_BASE + GPIOX_PUPDR_OFFSET;
94pub const STM32F4_GPIOE_IDR: u32 = STM32F4_GPIOE_REG_BASE + GPIOX_IDR_OFFSET;
95pub const STM32F4_GPIOE_ODR: u32 = STM32F4_GPIOE_REG_BASE + GPIOX_ODR_OFFSET;
96pub const STM32F4_GPIOE_BSRR: u32 = STM32F4_GPIOE_REG_BASE + GPIOX_BSRR_OFFSET;
97pub const STM32F4_GPIOE_LCKR: u32 = STM32F4_GPIOE_REG_BASE + GPIOX_LCKR_OFFSET;
98pub const STM32F4_GPIOE_AFRL: u32 = STM32F4_GPIOE_REG_BASE + GPIOX_AFRL_OFFSET;
99pub const STM32F4_GPIOE_AFRH: u32 = STM32F4_GPIOE_REG_BASE + GPIOX_AFRH_OFFSET;
100
101/// STM32F4 GPIO MODER values
102pub const STM32F4_MODER_OUTPUT: u32 = 0b01;
103pub const STM32F4_MODER_INPUT: u32 = 0b00;
104pub const STM32F4_MODER_AF: u32 = 0b10;
105pub const STM32F4_MODER_ANALOG: u32 = 0b11;
106pub const STM32F4_MODER_MASK: u32 = 0b11;
107
108/// STM32F4 GPIO PUPDR values
109pub const STM32F4_PUPDR_NONE: u32 = 0b00;
110pub const STM32F4_PUPDR_PU: u32 = 0b01;
111pub const STM32F4_PUPDR_PD: u32 = 0b10;
112pub const STM32F4_PUPDR_MASK: u32 = 0b11;
113
114// STM32F4 FLASH register base address
115const STM32F4_FLASH_REG_BASE: u32 = 0x4002_3C00;
116
117/// STM32F4 FLASH_CR register
118///
119/// Used to control flash erasing and programming operations.
120pub struct Stm32F4FlashCr;
121
122impl Stm32F4FlashCr {
123    /// STM32F4 memory address of this register
124    pub const ADDRESS: u32 = STM32F4_FLASH_REG_BASE + 0x10;
125
126    /// STM32F4 FLASH_CR register bit positions
127    pub const LOCK_BIT: u32 = 31;
128    pub const ERRIE_BIT: u32 = 25;
129    pub const EOPIE_BIT: u32 = 24;
130    pub const STRT_BIT: u32 = 16;
131    pub const MER_BIT: u32 = 2;
132    pub const SER_BIT: u32 = 1;
133    pub const PG_BIT: u32 = 0;
134
135    /// STM32F4 FLASH_CR register shift values
136    pub const SNB_SHIFT: u32 = 3;
137    pub const PSIZE_SHIFT: u32 = 8;
138
139    /// STM32F4 FLASH_CR register masks
140    pub const SNB_MASK: u32 = 0b1111;
141    pub const PSIZE_MASK: u32 = 0b11;
142
143    /// STM32F4 FLASH_CR PSIZE values.  `PSIZE_X64` is the fastest programming
144    /// size, but requires the highest VCC (3.3V is fine).
145    pub const PSIZE_X8: u32 = 0b00;
146    pub const PSIZE_X16: u32 = 0b01;
147    pub const PSIZE_X32: u32 = 0b10;
148    pub const PSIZE_X64: u32 = 0b11;
149}
150
151/// STM32F4 FLASH_SR register
152///
153/// Used to check the status of flash operations, including errors and busy
154/// state.
155pub struct Stm32F4FlashSr(u32);
156
157impl Stm32F4FlashSr {
158    /// STM32F4 memory address of this register
159    pub const ADDRESS: u32 = STM32F4_FLASH_REG_BASE + 0x0C;
160
161    /// STM32F4 FLASH_SR register bit positions
162    pub const EOP_BIT: u32 = 0;
163    pub const OPERR_BIT: u32 = 1;
164    pub const WRPERR_BIT: u32 = 4;
165    pub const PGAERR_BIT: u32 = 5;
166    pub const PGPERR_BIT: u32 = 6;
167    pub const PGSERR_BIT: u32 = 7;
168    pub const RDERR_BIT: u32 = 8;
169    pub const BSY_BIT: u32 = 16;
170
171    /// Whether a flash operation is in progress.
172    pub fn busy(&self) -> bool {
173        (self.0 >> Self::BSY_BIT) & 1 != 0
174    }
175
176    /// Whether there are any errors in the flash status register.
177    pub fn errors(&self) -> bool {
178        let error_mask = (1 << Self::OPERR_BIT)
179            | (1 << Self::WRPERR_BIT)
180            | (1 << Self::PGAERR_BIT)
181            | (1 << Self::PGPERR_BIT)
182            | (1 << Self::PGSERR_BIT)
183            | (1 << Self::RDERR_BIT);
184        self.0 & error_mask != 0
185    }
186}
187
188impl From<u32> for Stm32F4FlashSr {
189    fn from(value: u32) -> Self {
190        Self(value)
191    }
192}
193
194impl From<Stm32F4FlashSr> for u32 {
195    fn from(sr: Stm32F4FlashSr) -> Self {
196        sr.0
197    }
198}
199
200impl fmt::Display for Stm32F4FlashSr {
201    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202        write!(f, "0x{:08X}", self.0)
203    }
204}
205
206/// STM32F4 FLASH_KEYR register
207///
208/// Used to unlock the flash memory for programming and erasing operations.
209pub struct Stm32F4FlashKeyr;
210
211impl Stm32F4FlashKeyr {
212    /// STM32F4 memory address of this register
213    pub const ADDRESS: u32 = STM32F4_FLASH_REG_BASE + 0x04;
214
215    /// STM32F4 FLASH_KEYR keys used to unlock the flash memory
216    pub const KEY1: u32 = 0x45670123;
217    pub const KEY2: u32 = 0xCDEF89AB;
218}
219
220/// STM32 product family
221#[derive(Debug, Clone, Copy, PartialEq, Eq)]
222pub enum StmFamily {
223    /// STM32F4 family
224    F4,
225
226    /// STM32F1 family
227    F1,
228
229    /// Unknown STM32 family
230    Unknown,
231}
232
233impl StmFamily {
234    /// Whether this `airfrog-core` is familiar with this STM32 family
235    pub fn known(&self) -> bool {
236        !matches!(self, StmFamily::Unknown)
237    }
238}
239
240impl fmt::Display for StmFamily {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        match self {
243            StmFamily::F4 => write!(f, "STM32F4"),
244            StmFamily::F1 => write!(f, "STM32F1"),
245            StmFamily::Unknown => write!(f, "Unknown"),
246        }
247    }
248}
249
250/// STM32 product line
251#[allow(non_camel_case_types)]
252#[derive(Debug, Clone, Copy, PartialEq, Eq)]
253pub enum StmLine {
254    /// STM32F401B/C
255    F401BC,
256
257    /// STM32F401D/E
258    F401DE,
259
260    /// STM32F411
261    F411,
262
263    /// STM32F427/437
264    F427_F437,
265
266    /// STM32F413/423
267    F413_F423,
268
269    /// STM32F405/415/07/17
270    F4x5,
271
272    /// STM32F446
273    F446,
274
275    /// STM32F103
276    F103,
277
278    /// Unknown STM32 line
279    Unknown,
280}
281
282impl StmLine {
283    /// Returns the STM32's RAM size in bytes if available.
284    ///
285    /// Only main SRAM is reported.  CCM RAM, if present, is not included.
286    /// Use [`Self::ccm_ram_size_bytes`] to get the CCM RAM size.
287    pub fn ram_size_bytes(&self) -> Option<u32> {
288        self.ram_size_kb().map(|size| size * 1024)
289    }
290
291    /// Returns the STM32's RAM size in KB if available.
292    ///
293    /// Only main SRAM is reported.  CCM RAM, if present, is not included.
294    /// Use [`Self::ccm_ram_size_kb`] to get the CCM RAM size
295    pub fn ram_size_kb(&self) -> Option<u32> {
296        match self {
297            StmLine::F401BC => Some(96),
298            StmLine::F401DE => Some(96),
299            StmLine::F411 => Some(128),
300            StmLine::F427_F437 => Some(192),
301            StmLine::F413_F423 => Some(256),
302            StmLine::F4x5 => Some(128),
303            StmLine::F446 => Some(128),
304            StmLine::F103 => Some(20),
305            StmLine::Unknown => None,
306        }
307    }
308
309    /// Returns the STM32's CCM RAM size in bytes if available.
310    pub fn ccm_ram_size_bytes(&self) -> Option<u32> {
311        self.ccm_ram_size_kb().map(|size| size * 1024)
312    }
313
314    /// Returns the STM32's CCM RAM size in KB if available.
315    pub fn ccm_ram_size_kb(&self) -> Option<u32> {
316        match self {
317            StmLine::F401BC => None,
318            StmLine::F401DE => None,
319            StmLine::F411 => None,
320            StmLine::F427_F437 => None,
321            StmLine::F413_F423 => None,
322            StmLine::F4x5 => Some(64),
323            StmLine::F446 => None,
324            StmLine::F103 => None,
325            StmLine::Unknown => None,
326        }
327    }
328}
329
330impl fmt::Display for StmLine {
331    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332        match self {
333            StmLine::F401BC => write!(f, "STM32F401B/C"),
334            StmLine::F401DE => write!(f, "STM32F401D/E"),
335            StmLine::F4x5 => write!(f, "STM32F405/415/07/17"),
336            StmLine::F411 => write!(f, "STM32F411"),
337            StmLine::F413_F423 => write!(f, "STM32F413/423"),
338            StmLine::F427_F437 => write!(f, "STM32F427/437"),
339            StmLine::F446 => write!(f, "STM32F446"),
340            StmLine::F103 => write!(f, "STM32F103"),
341            StmLine::Unknown => write!(f, "Unknown STM32"),
342        }
343    }
344}
345
346/// STM32 MCU Device ID Code
347///
348/// Device ID format:
349/// - Bits 31:16: REV_ID (silicon revision)
350/// - Bits 15:0:  DEV_ID (device identifier)
351///
352/// # Examples
353///
354/// ```
355/// let device_id: StmDeviceId = 0x10006413.into();
356/// println!("{}", device_id);
357///
358/// // Or using new()
359/// let device_id = StmDeviceId::new(0x10006413);
360/// ```
361#[derive(Debug, Clone, Copy, PartialEq, Eq)]
362pub struct StmDeviceId {
363    raw: u32,
364}
365
366impl StmDeviceId {
367    /// STM32F4 memory address of the ROM location holding the device ID
368    pub const ADDRESS: u32 = 0xE004_2000;
369
370    /// Create new Device ID decoder from raw 32-bit value
371    pub fn new(raw: u32) -> Self {
372        Self { raw }
373    }
374
375    /// Get raw Device ID value
376    pub fn raw(&self) -> u32 {
377        self.raw
378    }
379
380    /// Get revision field (bits 31:16)
381    pub fn revision(&self) -> u16 {
382        ((self.raw >> 16) & 0xFFFF) as u16
383    }
384
385    /// Get device identifier (bits 15:0)
386    pub fn device_id(&self) -> u16 {
387        (self.raw & 0xFFF) as u16
388    }
389
390    /// Get STM32 product family
391    pub fn family(&self) -> StmFamily {
392        match self.device_id() {
393            0x423 | 0x433 | 0x431 | 0x413 | 0x419 | 0x463 | 0x421 => StmFamily::F4,
394            0x412 | 0x410 | 0x414 | 0x430 | 0x418 => StmFamily::F1,
395            _ => StmFamily::Unknown,
396        }
397    }
398
399    /// Get STM32 product line
400    pub fn line(&self) -> StmLine {
401        match self.device_id() {
402            0x423 => StmLine::F401BC,
403            0x433 => StmLine::F401DE,
404            0x431 => StmLine::F411,
405            0x413 => StmLine::F4x5,
406            0x419 => StmLine::F427_F437,
407            0x463 => StmLine::F413_F423,
408            0x421 => StmLine::F446,
409            0x410 => StmLine::F103,
410            _ => StmLine::Unknown,
411        }
412    }
413
414    /// Get revision letter if known.  These values are usually documented in
415    /// errata sheets or reference manuals for the product line.
416    pub fn revision_str(&self) -> &'static str {
417        match self.line() {
418            StmLine::F401BC | StmLine::F401DE => match self.revision() {
419                0x1000 => "A",
420                0x1001 => "1/Z",
421                _ => "unknown",
422            },
423            StmLine::F411 => match self.revision() {
424                0x1000 => "A/1/Z",
425                _ => "unknown",
426            },
427            StmLine::F4x5 => match self.revision() {
428                // Same for F407/417
429                0x1000 => "A",
430                0x1001 => "Z",
431                0x1003 => "1",
432                0x1007 => "2",
433                0x100F => "Y/4",
434                0x101F => "5/6",
435                _ => "unknown",
436            },
437            StmLine::F427_F437 => match self.revision() {
438                0x1000 => "A",
439                0x1003 => "Y",
440                0x2001 => "3",
441                0x2003 => "4/5/B",
442                _ => "unknown",
443            },
444            StmLine::F413_F423 => match self.revision() {
445                0x1000 => "A/1",
446                _ => "unknown",
447            },
448            StmLine::F446 => match self.revision() {
449                0x1000 => "A/1",
450                _ => "unknown",
451            },
452            StmLine::F103 => match self.revision() {
453                0x0000 => "A",
454                0x2000 => "B",
455                0x2001 => "Z",
456                0x2003 => "1/2/3/X/Y",
457                _ => "unknown",
458            },
459            StmLine::Unknown => "unknown",
460        }
461    }
462
463    /// Checks if this is an STM32 device `airfrog-core` is familiar with
464    pub fn is_known_device(&self) -> bool {
465        !matches!(self.line(), StmLine::Unknown)
466    }
467}
468
469impl From<u32> for StmDeviceId {
470    fn from(raw: u32) -> Self {
471        Self::new(raw)
472    }
473}
474
475impl fmt::Display for StmDeviceId {
476    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477        if f.alternate() {
478            write!(
479                f,
480                "STM32 Device ID: 0x{:08X} ({} {})",
481                self.raw(),
482                self.line(),
483                self.revision_str()
484            )
485        } else {
486            write!(f, "{}", self.line())
487        }
488    }
489}
490
491impl fmt::LowerHex for StmDeviceId {
492    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493        write!(f, "0x{:08x}", self.raw)
494    }
495}
496
497impl fmt::UpperHex for StmDeviceId {
498    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
499        write!(f, "0x{:08X}", self.raw)
500    }
501}
502
503/// STM32 Unique Device ID (UID)
504///
505/// This is a 96-bit unique identifier for each STM32 device, consisting of
506/// three 32-bit words.
507#[derive(Debug, Clone, Copy, PartialEq, Eq)]
508pub struct StmUniqueId {
509    uid: [u32; 3],
510}
511
512impl StmUniqueId {
513    pub const STM32F4_INITIAL_ADDRESS: u32 = 0x1FFF_7A10;
514    pub const STM32F1_INITIAL_ADDRESS: u32 = 0x1FFF_F7E8;
515
516    /// Get the initial address for reading the Unique ID based on the STM32#
517    /// family.
518    pub fn addr_from_family(family: StmFamily) -> Option<u32> {
519        match family {
520            StmFamily::F4 => Some(Self::STM32F4_INITIAL_ADDRESS),
521            StmFamily::F1 => Some(Self::STM32F1_INITIAL_ADDRESS),
522            StmFamily::Unknown => None,
523        }
524    }
525
526    /// Create new Unique ID from 3 32-bit words.  These are expected to be
527    /// provided LSB (UID31:0) first, read from the `INITIAL_ADDRESS`.
528    pub fn new(uid: [u32; 3]) -> Self {
529        Self { uid }
530    }
531
532    /// UID31:0 - X/Y co-ordinates on the wafer.  X co-ordinate is the lower
533    /// 16 bits, Y co-ordinate is the upper 16 bits.
534    // https://community.st.com/t5/stm32-mcus-products/parsing-uid-fields-on-stm32l476-wafer-x-y-coordinates-and-bcd/td-p/820338
535    pub fn x_y(&self) -> u32 {
536        self.uid[0]
537    }
538
539    /// UID15:0 X-coordinate on the wafer
540    pub fn x(&self) -> u16 {
541        (self.x_y() & 0xFFFF) as u16
542    }
543
544    /// UID31:16 Y-coordinate on the wafer
545    pub fn y(&self) -> u16 {
546        ((self.x_y() >> 16) & 0xFFFF) as u16
547    }
548
549    /// UID39:32 - Wafer number (reference manual claims this is ASCII encoded
550    /// but isn't - it is an 8-bit value).
551    pub fn wafer(&self) -> u8 {
552        (self.uid[1] & 0xFF) as u8
553    }
554
555    /// UID63:40 - Lot23:0, ASCII encoded
556    pub fn lot(&self) -> [u8; 7] {
557        let uid1_bytes = self.uid[1].to_le_bytes();
558        let uid2_bytes = self.uid[2].to_le_bytes();
559
560        [
561            uid2_bytes[3], // byte 11
562            uid2_bytes[2], // byte 10
563            uid2_bytes[1], // byte 9
564            uid2_bytes[0], // byte 8
565            uid1_bytes[3], // byte 7
566            uid1_bytes[2], // byte 6
567            uid1_bytes[1], // byte 5
568        ]
569    }
570
571    /// Wafer number as string
572    pub fn wafer_str(&self) -> String {
573        let byte = self.wafer();
574        format!("0x{byte:02X}")
575    }
576
577    /// Lot number as string  
578    pub fn lot_str(&self) -> String {
579        let bytes = self.lot();
580
581        let mut result = String::new();
582        for &byte in &bytes {
583            if byte.is_ascii_graphic() {
584                result.push(byte as char);
585            } else {
586                result.push('.')
587            }
588        }
589        result
590    }
591
592    /// Get the raw UID as an array of 3 u32 values, UID31:0 first
593    pub fn raw(&self) -> [u32; 3] {
594        self.uid
595    }
596}
597
598impl fmt::Display for StmUniqueId {
599    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
600        write!(
601            f,
602            "UID[95:0]: 0x{:08X}{:08X}{:08X} Lot: {} Wafer: {} X/Y: {}/{}",
603            self.raw()[2],
604            self.raw()[1],
605            self.raw()[0],
606            self.lot_str(),
607            self.wafer_str(),
608            self.x(),
609            self.y(),
610        )
611    }
612}
613
614/// STM Flash Size Register
615///
616/// This is a 16-bit value that indicates the size of the flash memory in
617/// kilobytes.
618#[derive(Debug, Clone, Copy, PartialEq, Eq)]
619pub struct StmFlashSize {
620    raw: u16,
621}
622
623impl StmFlashSize {
624    /// Address containing the 16-bit flash size value.  The flash size is
625    /// the upper 16 bits of this value, so stored at `0x1FFF_7A22`.  We store
626    /// a 4-bit aligned address.
627    pub const STM32F4_ADDRESS_OF_U16: u32 = 0x1FFF_7A20;
628    pub const STM32F1_ADDRESS_OF_U16: u32 = 0x1FFF_F7E0;
629
630    /// Get the initial address for reading the Flash Size based on the STM32
631    /// family.
632    pub fn addr_from_family(family: StmFamily) -> Option<u32> {
633        match family {
634            StmFamily::F4 => Some(Self::STM32F4_ADDRESS_OF_U16),
635            StmFamily::F1 => Some(Self::STM32F1_ADDRESS_OF_U16),
636            StmFamily::Unknown => None,
637        }
638    }
639
640    /// Create new Flash Size from raw 16-bit value
641    pub fn new(raw: u16) -> Self {
642        Self { raw }
643    }
644
645    /// Get raw Flash Size value
646    pub fn raw(&self) -> u16 {
647        self.raw
648    }
649
650    /// Get Flash size in bytes
651    pub fn size_bytes(&self) -> u32 {
652        (self.raw as u32) * 1024
653    }
654
655    /// Get Flash size in KB
656    pub fn size_kb(&self) -> u32 {
657        self.raw as u32
658    }
659}
660
661impl fmt::Display for StmFlashSize {
662    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
663        write!(f, "Flash Size: {} KB", self.size_kb())
664    }
665}
666
667/// STM32 device details
668#[derive(Debug, Clone, Copy, PartialEq, Eq)]
669pub struct StmDetails {
670    /// The MCU Device ID
671    mcu: StmDeviceId,
672
673    /// The IDCODE (DPIDR value)
674    idcode: IdCode,
675
676    /// The Unique ID if available
677    uid: Option<StmUniqueId>,
678
679    /// The Flash Size if available
680    flash_size: Option<StmFlashSize>,
681}
682
683impl StmDetails {
684    /// Create new a new `StmDetails` instance
685    pub fn new(
686        mcu: StmDeviceId,
687        idcode: IdCode,
688        uid: Option<StmUniqueId>,
689        flash_size: Option<StmFlashSize>,
690    ) -> Self {
691        Self {
692            mcu,
693            idcode,
694            uid,
695            flash_size,
696        }
697    }
698
699    pub fn idcode(&self) -> &IdCode {
700        &self.idcode
701    }
702
703    pub fn get_cortex(&self) -> Option<Cortex> {
704        Cortex::from_idcode(self.idcode)
705    }
706
707    /// Get the MCU Device ID
708    pub fn mcu(&self) -> &StmDeviceId {
709        &self.mcu
710    }
711
712    /// Get the Unique ID if available
713    pub fn uid(&self) -> Option<StmUniqueId> {
714        self.uid
715    }
716
717    /// Returns the flash size in bytes if available
718    pub fn flash_size_bytes(&self) -> Option<u32> {
719        self.flash_size.map(|size| size.size_bytes())
720    }
721
722    /// Returns the Flash Size in KB if available
723    pub fn flash_size_kb(&self) -> Option<StmFlashSize> {
724        self.flash_size
725    }
726
727    /// Get the base flash address for this MCU
728    pub fn flash_base(&self) -> Option<u32> {
729        match self.mcu.family() {
730            StmFamily::F4 => Some(STM32F4_FLASH_BASE),
731            StmFamily::F1 => Some(STM32F1_FLASH_BASE),
732            StmFamily::Unknown => None,
733        }
734    }
735
736    /// Get the base RAM address for this MCU
737    pub fn ram_base(&self) -> Option<u32> {
738        match self.mcu.family() {
739            StmFamily::F4 => Some(STM32F4_RAM_BASE),
740            StmFamily::F1 => Some(STM32F1_RAM_BASE),
741            StmFamily::Unknown => None,
742        }
743    }
744
745    /// Returns whether this is an STM32F4 family MCU
746    pub fn is_stm32f4(&self) -> bool {
747        matches!(self.mcu.family(), StmFamily::F4)
748    }
749
750    /// Returns whether this is an STM32F1 family MCU
751    pub fn is_stm32f1(&self) -> bool {
752        matches!(self.mcu.family(), StmFamily::F1)
753    }
754
755    /// Maximum number of flash sectors for STM32F4 family devices.
756    pub const MAX_SECTORS: u8 = 12;
757
758    // STM32F4 sector sizes in bytes.  The same for all supported F4
759    // devices.
760    const SECTOR_SIZES_BYTES: [u32; Self::MAX_SECTORS as usize] = [
761        16 * 1024,  // Sector 0
762        16 * 1024,  // Sector 1
763        16 * 1024,  // Sector 2
764        64 * 1024,  // Sector 3
765        128 * 1024, // Sector 4
766        128 * 1024, // Sector 5
767        128 * 1024, // Sector 6
768        128 * 1024, // Sector 7
769        128 * 1024, // Sector 8
770        128 * 1024, // Sector 9
771        128 * 1024, // Sector 10
772        128 * 1024, // Sector 11
773    ];
774
775    /// Returns the size of the indicated flash sector in bytes if available.
776    ///
777    /// Only returns the size of the sector if it is within the device's flash
778    /// size.
779    ///
780    /// Arguments:
781    /// - `sector`: The sector number to get the size for.
782    ///
783    /// Returns:
784    /// - `Some(size)`: The size of the sector in bytes if it is valid and
785    ///   within the device's flash size.
786    /// - `None`: If the device is not an STM32F4 family MCU, or if the sector
787    ///   is invalid for this device.
788    ///
789    /// Note that (currently unsupported) F42x and F43x lines have the option
790    /// of different sector organisations, not supported by this function.
791    pub fn get_sector_size_bytes(&self, sector: u8) -> Option<u32> {
792        if !self.is_stm32f4() {
793            return None;
794        }
795
796        // Get the value
797        let value = if sector < Self::SECTOR_SIZES_BYTES.len() as u8 {
798            Self::SECTOR_SIZES_BYTES[sector as usize]
799        } else {
800            return None;
801        };
802
803        // If we can't get the device's flash size, return None for the sector
804        // size, as we can't check.
805        let flash_size = self.flash_size_bytes()? as usize;
806
807        // Sum up all sector sizes up to the given sector, and check it's
808        // within the flash size.  This requires iteration
809        let mut total_size: usize = 0;
810        for size in Self::SECTOR_SIZES_BYTES.iter().take(sector as usize + 1) {
811            total_size += *size as usize;
812            if total_size > flash_size {
813                return None; // Sector exceeds flash size
814            }
815        }
816        Some(value)
817    }
818
819    /// Returns the size of the indicated flash sector in KB if available.
820    ///
821    /// This is a convenience function that uses
822    /// [`Self::get_sector_size_bytes()`] to return the flash sector size in
823    /// KB.
824    ///
825    /// Arguments:
826    /// - `sector`: The sector number to get the size for.
827    ///
828    /// Returns:
829    /// - `Some(size)`: The size of the sector in KB if it is valid and
830    ///   the device's flash size.
831    /// - `None`: If the device is not an STM32F4 family MCU, or if the sector
832    ///   is invalid for this device.
833    pub fn get_sector_size_kb(&self, sector: u8) -> Option<u32> {
834        self.get_sector_size_bytes(sector).map(|size| size / 1024)
835    }
836
837    /// Returns the flash sector number or numbers for the given word range.
838    ///
839    /// Note that the word range is relative to the start of the flash, and the
840    /// range is _inclusive_ of the entirety of the end word.
841    ///
842    /// Arguments:
843    /// - `range`: A range of words (inclusive) to get the sectors for.
844    /// - `sectors`: A mutable array of sectors to fill with the sector
845    ///   numbers.
846    ///
847    /// Returns:
848    /// - `Some(sector_count)`: The number of sectors found in the range,
849    ///   filled in the `sectors` array.
850    /// - `None`: If the device is not an STM32F4 family MCU, or if the range
851    ///   is invalid or exceeds the flash size.
852    pub fn get_sectors_from_word_range(
853        &self,
854        range: RangeInclusive<u32>,
855        sectors: &mut [u8; Self::MAX_SECTORS as usize],
856    ) -> Option<usize> {
857        if !self.is_stm32f4() {
858            return None;
859        }
860
861        // Convert the range to bytes
862        let start_word = *range.start();
863        let start_bytes = start_word * 4;
864        let end_word = *range.end();
865        let end_bytes = end_word * 4;
866
867        // Do some checking
868        if start_word > end_word {
869            return None; // Invalid range
870        }
871        let flash_size = self.flash_size_bytes()?;
872        if end_bytes + 4 > flash_size {
873            return None; // Range exceeds flash size
874        }
875
876        // Use SECTOR_SIZES_BYTES to determine the sector or sectors
877        let mut sector_count = 0;
878        let mut current_address = 0u32;
879        let range_end_bytes = end_bytes + 3; // Inclusive end of last word
880
881        for (sector_num, &sector_size) in Self::SECTOR_SIZES_BYTES.iter().enumerate() {
882            let sector_start = current_address;
883            let sector_end = current_address + sector_size - 1;
884
885            // Check if this sector overlaps with our byte range
886            if start_bytes <= sector_end && range_end_bytes >= sector_start {
887                sectors[sector_count] = sector_num as u8;
888                sector_count += 1;
889            }
890
891            current_address += sector_size;
892
893            // Early exit if we've gone past our range
894            if current_address > range_end_bytes {
895                break;
896            }
897        }
898
899        Some(sector_count)
900    }
901
902    /// Returns the expected IDR value for this STM32 family.
903    ///
904    /// Returns:
905    /// - `Some(Idr::IDR_AHB_AP_CORTEX_M4)`: If this is an STM32F4 family MCU.
906    /// - `None`: If this is not an STM32F4 family MCU.
907    pub fn expected_idr(&self) -> Option<Idr> {
908        if self.is_stm32f4() {
909            Some(IDR_AHB_AP_CORTEX_M4)
910        } else if self.is_stm32f1() {
911            Some(IDR_AHB_AP_CORTEX_M3)
912        } else {
913            None
914        }
915    }
916}
917
918impl fmt::Display for StmDetails {
919    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
920        if f.alternate() {
921            write!(f, "{:#}", self.mcu)?;
922
923            if let Some(flash_size) = self.flash_size {
924                write!(f, " {} KB", flash_size.size_kb())?;
925            } else {
926                write!(f, " Flash Size: unknown ")?;
927            };
928
929            if let Some(uid) = &self.uid {
930                write!(f, " {uid}")
931            } else {
932                write!(f, " UID: unknown ")
933            }
934        } else {
935            write!(f, "{}", self.mcu)
936        }
937    }
938}