sdrr_fw_parser/
types.rs

1// Copyright (C) 2025 Piers Finlayson <piers@piers.rocks>
2//
3// MIT License
4
5//! sdrr-fw-parser
6//!
7//! Enums and types used in SDRR firmware parsing
8
9use core::fmt;
10use deku::prelude::*;
11
12#[cfg(not(feature = "std"))]
13use alloc::{format, string::String};
14
15use crate::SdrrInfo;
16
17/// Enum used to identify the source of an information object
18pub enum Source {
19    Flash,
20    Ram,
21}
22
23/// STM32F4 product line options
24///
25/// Relflects `stm_line_t` from `sdrr/include/config_base.h`
26#[derive(
27    Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite, serde::Serialize, serde::Deserialize,
28)]
29#[deku(id_type = "u16", ctx = "endian: deku::ctx::Endian")]
30pub enum McuLine {
31    /// F401D/E - 96KB RAM
32    #[deku(id = "0x0000")]
33    F401DE,
34
35    /// F405
36    #[deku(id = "0x0001")]
37    F405,
38
39    /// F411
40    #[deku(id = "0x0002")]
41    F411,
42
43    /// F446
44    #[deku(id = "0x0003")]
45    F446,
46
47    /// F401B/C - 64KB RAM
48    #[deku(id = "0x0004")]
49    F401BC,
50
51    /// RP2350
52    #[deku(id = "0x0005")]
53    Rp2350,
54}
55
56impl fmt::Display for McuLine {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        match self {
59            McuLine::F401DE => write!(f, "STM32F401DE"),
60            McuLine::F405 => write!(f, "STM32F405"),
61            McuLine::F411 => write!(f, "STM32F411"),
62            McuLine::F446 => write!(f, "STM32F446"),
63            McuLine::F401BC => write!(f, "STM32F401BC"),
64            McuLine::Rp2350 => write!(f, "RP2350"),
65        }
66    }
67}
68
69impl McuLine {
70    /// Returns the amount of SRAM of the device (not including any CCM RAM)
71    pub fn ram_kb(&self) -> &str {
72        match self {
73            McuLine::F401DE => "96",
74            McuLine::F401BC => "64",
75            McuLine::F405 | McuLine::F411 | McuLine::F446 => "128",
76            McuLine::Rp2350 => "520",
77        }
78    }
79}
80
81/// STM32F4 package flash storage code
82///
83/// For example "E" in STM32F401RET6 means 512KB of flash storage.
84///
85/// Reflects `stm_storage_t` from `sdrr/include/config_base.h`
86#[derive(
87    Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite, serde::Serialize, serde::Deserialize,
88)]
89#[deku(id_type = "u16", ctx = "endian: deku::ctx::Endian")]
90pub enum McuStorage {
91    /// 8 = 64KB
92    #[deku(id = "0")]
93    Storage8,
94
95    /// B = 128KB
96    #[deku(id = "1")]
97    StorageB,
98
99    /// C = 256KB
100    #[deku(id = "2")]
101    StorageC,
102
103    /// D = 384KB
104    #[deku(id = "3")]
105    StorageD,
106
107    /// E = 512KB
108    #[deku(id = "4")]
109    StorageE,
110
111    /// F = 768KB
112    #[deku(id = "5")]
113    StorageF,
114
115    /// G = 1024KB
116    #[deku(id = "6")]
117    StorageG,
118
119    /// 2MB
120    #[deku(id = "7")]
121    Storage2MB,
122}
123
124impl McuStorage {
125    /// Returns the storage size in kilobytes
126    pub fn kb(&self) -> &str {
127        match self {
128            McuStorage::Storage8 => "64",
129            McuStorage::StorageB => "128",
130            McuStorage::StorageC => "256",
131            McuStorage::StorageD => "384",
132            McuStorage::StorageE => "512",
133            McuStorage::StorageF => "768",
134            McuStorage::StorageG => "1024",
135            McuStorage::Storage2MB => "2048",
136        }
137    }
138
139    /// Returns the storage package code
140    pub fn package_code(&self) -> &str {
141        match self {
142            McuStorage::Storage8 => "8",
143            McuStorage::StorageB => "B",
144            McuStorage::StorageC => "C",
145            McuStorage::StorageD => "D",
146            McuStorage::StorageE => "E",
147            McuStorage::StorageF => "F",
148            McuStorage::StorageG => "G",
149            McuStorage::Storage2MB => "2MB",
150        }
151    }
152}
153
154impl fmt::Display for McuStorage {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        write!(f, "{}", self.package_code())
157    }
158}
159
160/// Type of ROMs supported by SDRR
161///
162/// Reflects `sdrr_rom_type_t` from `sdrr/include/config_base.h`
163#[derive(
164    Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite, serde::Serialize, serde::Deserialize,
165)]
166#[deku(id_type = "u8")]
167pub enum SdrrRomType {
168    /// 2316 ROM, 11-bit address, 3 CS lines, 2KB size
169    #[deku(id = "0")]
170    Rom2316,
171
172    /// 2332 ROM, 12-bit address, 2 CS lines, 4KB size
173    #[deku(id = "1")]
174    Rom2332,
175
176    /// 2364 ROM, 13-bit address, 1 CS line, 8KB size
177    #[deku(id = "2")]
178    Rom2364,
179}
180
181impl fmt::Display for SdrrRomType {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        match self {
184            SdrrRomType::Rom2316 => write!(f, "2316"),
185            SdrrRomType::Rom2332 => write!(f, "2332"),
186            SdrrRomType::Rom2364 => write!(f, "2364"),
187        }
188    }
189}
190
191impl SdrrRomType {
192    /// Returns the size of the ROM in bytes
193    pub fn rom_size(&self) -> usize {
194        self.rom_size_kb() * 1024
195    }
196
197    /// Returns the size of the ROM in KB
198    pub fn rom_size_kb(&self) -> usize {
199        match self {
200            SdrrRomType::Rom2316 => 2,
201            SdrrRomType::Rom2332 => 4,
202            SdrrRomType::Rom2364 => 8,
203        }
204    }
205
206    /// Returns the maximum addressable location in the ROM
207    pub fn max_addr(&self) -> u32 {
208        (self.rom_size() - 1) as u32
209    }
210
211    /// Checks if the ROM type supports the CS2 line
212    pub fn supports_cs2(&self) -> bool {
213        match self {
214            SdrrRomType::Rom2316 => true,
215            SdrrRomType::Rom2332 => true,
216            SdrrRomType::Rom2364 => false,
217        }
218    }
219
220    /// Checks if the ROM type supports the CS3 line
221    pub fn supports_cs3(&self) -> bool {
222        match self {
223            SdrrRomType::Rom2316 => true,
224            SdrrRomType::Rom2332 => false,
225            SdrrRomType::Rom2364 => false,
226        }
227    }
228}
229
230/// SDRR chip select active options
231///
232/// Reflects `sdrr_cs_state_t` from `sdrr/include/config_base.h`
233#[derive(
234    Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite, serde::Serialize, serde::Deserialize,
235)]
236#[deku(id_type = "u8")]
237pub enum SdrrCsState {
238    /// Chip select line is active low
239    #[deku(id = "0")]
240    ActiveLow,
241
242    /// Chip select line is active high
243    #[deku(id = "1")]
244    ActiveHigh,
245
246    /// Chip select line is not used
247    #[deku(id = "2")]
248    NotUsed,
249}
250
251impl fmt::Display for SdrrCsState {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        match self {
254            SdrrCsState::ActiveLow => write!(f, "Active Low"),
255            SdrrCsState::ActiveHigh => write!(f, "Active High"),
256            SdrrCsState::NotUsed => write!(f, "Not Used"),
257        }
258    }
259}
260
261/// SDRR serving algorithm options
262///
263/// Reflects `sdrr_serve_t` from `sdrr/include/config_base.h`
264#[derive(
265    Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite, serde::Serialize, serde::Deserialize,
266)]
267#[deku(id_type = "u8")]
268pub enum SdrrServe {
269    /// Original algorithm - two CS checks for every address check, checks
270    /// while CS is inactive as well as active
271    #[deku(id = "0")]
272    TwoCsOneAddr,
273
274    /// Default algorithm from v0.2.1 as it is more performant in every case
275    /// tested - checks address only on CS active, and at same frequency
276    #[deku(id = "1")]
277    AddrOnCs,
278
279    /// Multi-ROM set algorithm - as `AddrOnCs`, but ROM is considered active
280    /// when _any_ ROM's CS line is active
281    #[deku(id = "2")]
282    AddrOnAnyCs,
283}
284
285impl fmt::Display for SdrrServe {
286    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287        match self {
288            SdrrServe::TwoCsOneAddr => write!(f, "A = Two CS checks for every address check"),
289            SdrrServe::AddrOnCs => write!(f, "B = Check address only when CS active"),
290            SdrrServe::AddrOnAnyCs => write!(f, "C = Check address on any CS active"),
291        }
292    }
293}
294
295/// SDRR STM32 port options
296///
297/// Reflects `sdrr_stm_port_t` from `sdrr/include/config_base.h`
298#[derive(
299    Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite, serde::Serialize, serde::Deserialize,
300)]
301#[deku(id_type = "u8")]
302pub enum SdrrMcuPort {
303    /// No port (pin set is not exposed/used)
304    #[deku(id = "0x00")]
305    None,
306
307    /// Port A
308    #[deku(id = "0x01")]
309    PortA,
310
311    /// Port B
312    #[deku(id = "0x02")]
313    PortB,
314
315    /// Port C
316    #[deku(id = "0x03")]
317    PortC,
318
319    /// Port D
320    #[deku(id = "0x04")]
321    PortD,
322
323    /// Port 0 (RP2350 only)
324    #[deku(id = "0x05")]
325    Port0,
326}
327
328impl fmt::Display for SdrrMcuPort {
329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330        match self {
331            SdrrMcuPort::None => write!(f, "None"),
332            SdrrMcuPort::PortA => write!(f, "A"),
333            SdrrMcuPort::PortB => write!(f, "B"),
334            SdrrMcuPort::PortC => write!(f, "C"),
335            SdrrMcuPort::PortD => write!(f, "D"),
336            SdrrMcuPort::Port0 => write!(f, "0"),
337        }
338    }
339}
340
341/// Struct representing the state of the chip select (CS) lines in a given
342/// logical address
343#[derive(Debug, Clone, Copy, PartialEq, Eq)]
344pub struct SdrrCsSet {
345    /// The state of CS1 (1 = high, 0 = low)
346    cs1: bool,
347
348    /// The state of CS2 (1 = high, 0 = low) - 2332/2316 ROM types only
349    cs2: Option<bool>,
350
351    /// The state of CS3 (1 = high, 0 = low) - 2316 ROM type only
352    cs3: Option<bool>,
353
354    /// The state of X1 (1 = high, 0 = low) - rom sets only
355    x1: Option<bool>,
356
357    /// The state of X2 (1 = high, 0 = low) - rom sets only
358    x2: Option<bool>,
359}
360
361impl SdrrCsSet {
362    /// Constructs a new CS set
363    pub fn new(
364        cs1: bool,
365        cs2: Option<bool>,
366        cs3: Option<bool>,
367        x1: Option<bool>,
368        x2: Option<bool>,
369    ) -> Self {
370        Self {
371            cs1,
372            cs2,
373            cs3,
374            x1,
375            x2,
376        }
377    }
378
379    /// Returns the state of CS1
380    pub fn cs1(&self) -> bool {
381        self.cs1
382    }
383
384    /// Returns the state of CS2
385    pub fn cs2(&self) -> Option<bool> {
386        self.cs2
387    }
388
389    /// Returns the state of CS3
390    pub fn cs3(&self) -> Option<bool> {
391        self.cs3
392    }
393
394    /// Returns the state of X1
395    pub fn x1(&self) -> Option<bool> {
396        self.x1
397    }
398
399    /// Returns the state of X2
400    pub fn x2(&self) -> Option<bool> {
401        self.x2
402    }
403}
404
405/// Struct representing the information required to construct a logical address
406/// to access a byte in an SDRR rom set.
407#[derive(Debug, Clone, Copy, PartialEq, Eq)]
408pub struct SdrrLogicalAddress {
409    /// The logical address of the ROM to access - i.e the value of the address
410    /// pins on the original ROM chip.
411    addr: u32,
412
413    /// The CS state associated with this address
414    cs_set: SdrrCsSet,
415}
416
417impl SdrrLogicalAddress {
418    /// Constructs a new logical address
419    pub fn new(addr: u32, cs_set: SdrrCsSet) -> Self {
420        Self { addr, cs_set }
421    }
422
423    /// Mangles the logical address into a raw address using the pin config from
424    /// the given `SdrrInfo`.
425    pub fn mangle(&self, info: &SdrrInfo) -> Result<u32, String> {
426        info.mangle_address(self)
427    }
428
429    /// Returns the logical address pins state from the original ROM
430    pub fn addr(&self) -> u32 {
431        self.addr
432    }
433
434    /// Returns the CS set associated with this logical address
435    pub fn cs_set(&self) -> &SdrrCsSet {
436        &self.cs_set
437    }
438}
439
440#[derive(Debug, Clone, Copy, PartialEq, Eq)]
441pub enum SdrrAddress {
442    /// Raw address - used to index directly into the ROM set
443    Raw(u32),
444
445    /// Logical address - the logical address on the original ROM, plus CS
446    /// and X1/X2 lines
447    Logical(SdrrLogicalAddress),
448}
449
450impl SdrrAddress {
451    /// Creates a new raw address from the given value.
452    pub fn from_raw(addr: u32) -> Self {
453        Self::Raw(addr)
454    }
455
456    /// Creates a new logical address from the given components.
457    pub fn from_logical(addr: u32, cs_set: &SdrrCsSet) -> Self {
458        Self::Logical(SdrrLogicalAddress::new(addr, *cs_set))
459    }
460}
461
462impl fmt::Display for SdrrAddress {
463    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464        if f.alternate() {
465            match self {
466                SdrrAddress::Raw(addr) => write!(f, "Raw address: 0x{:04X}", addr),
467                SdrrAddress::Logical(logical) => write!(
468                    f,
469                    "Logical address: 0x{:04X} (CS1: {}, CS2: {:?}, CS3: {:?}, X1: {:?}, X2: {:?})",
470                    logical.addr,
471                    logical.cs_set.cs1(),
472                    logical.cs_set.cs2(),
473                    logical.cs_set.cs3(),
474                    logical.cs_set.x1(),
475                    logical.cs_set.x2()
476                ),
477            }
478        } else {
479            match self {
480                SdrrAddress::Raw(addr) => write!(f, "0x{:04X}", addr),
481                SdrrAddress::Logical(logical) => write!(f, "0x{:04X}", logical.addr),
482            }
483        }
484    }
485}