airfrog_core/
lib.rs

1// Copyright (C) 2025 Piers Finlayson <piers@piers.rocks>
2//
3// MIT License
4
5//! Airfrog is the tiny wireless co-processor for ARM.
6//!
7//! <https://piers.rocks/u/airfrog>
8//!
9//! airfrog-core - Core protocol and MCU concepts used by Airfrog.
10//!
11//! Designed to be used in conjunction with the `airfrog-swd` library for ARM
12//! Serial Wire Debug (SWD) operations.
13//!
14//! This library is `no_std` compatible, and requires an `alloc`
15//! implementation.
16
17#![no_std]
18
19pub mod arm;
20pub mod rp;
21pub mod stm;
22
23extern crate alloc;
24use core::fmt;
25use core::ops::RangeInclusive;
26use static_assertions::const_assert;
27
28use crate::arm::ap::Idr;
29use crate::arm::dp::IdCode;
30
31/// Represents a target's microcontroller unit.
32///
33/// `airfrog-swd` can be used to identify the MCU type using this object.  See
34/// [`..::airfrog_swd::debug::DebugInterface::get_mcu()`].
35#[derive(Debug, Clone, Copy)]
36pub enum Mcu {
37    /// An STM32 MCU.
38    Stm32(stm::StmDetails),
39
40    /// An RP (Raspberry Pi) MCU.
41    Rp(rp::RpDetails),
42
43    /// An unknown MCU, identified by its IDCODE.
44    Unknown(IdCode),
45}
46
47impl Mcu {
48    /// Returns MCU's flash base address if available.
49    pub fn flash_base(&self) -> Option<u32> {
50        match self {
51            Mcu::Stm32(details) => details.flash_base(),
52            Mcu::Rp(details) => details.flash_base(),
53            Mcu::Unknown(_) => None,
54        }
55    }
56
57    /// Returns the MCU's RAM base address if available.
58    pub fn ram_base(&self) -> Option<u32> {
59        match self {
60            Mcu::Stm32(details) => details.ram_base(),
61            Mcu::Rp(details) => details.ram_base(),
62            Mcu::Unknown(_) => None,
63        }
64    }
65
66    /// Returns the MCU's flash size in bytes if available.
67    pub fn flash_size_bytes(&self) -> Option<u32> {
68        self.flash_size_kb().map(|size| size * 1024)
69    }
70
71    /// Returns the MCU's flash size in KB if available.
72    pub fn flash_size_kb(&self) -> Option<u32> {
73        match self {
74            Mcu::Stm32(details) => details.flash_size_kb().map(|size| size.raw() as u32),
75            Mcu::Rp(_) => None,
76            Mcu::Unknown(_) => None,
77        }
78    }
79
80    /// Returns the MCU's RAM size in bytes if available.
81    pub fn ram_size_bytes(&self) -> Option<u32> {
82        self.ram_size_kb().map(|size| size * 1024)
83    }
84
85    /// Returns the MCU's RAM size in KB if available.
86    pub fn ram_size_kb(&self) -> Option<u32> {
87        match self {
88            Mcu::Stm32(details) => details.mcu().line().ram_size_kb(),
89            Mcu::Rp(details) => details.line().ram_size_kb(),
90            Mcu::Unknown(_) => None,
91        }
92    }
93
94    /// Returns whether this is an STM32 MCU.
95    pub fn is_stm32(&self) -> bool {
96        matches!(self, Mcu::Stm32(_))
97    }
98
99    /// Returns whether this is an STM32F4 MCU.
100    pub fn is_stm32f4(&self) -> bool {
101        match self {
102            Mcu::Stm32(stm) => stm.is_stm32f4(),
103            Mcu::Rp(_) => false,
104            Mcu::Unknown(_) => false,
105        }
106    }
107
108    /// Returns whether this is an RP MCU.
109    pub fn is_rp(&self) -> bool {
110        matches!(self, Mcu::Rp(_))
111    }
112
113    /// Returns the size of the specified flash sector in bytes.
114    pub fn get_sector_size_bytes(&self, sector: u8) -> Option<u32> {
115        match self {
116            Mcu::Stm32(details) => details.get_sector_size_bytes(sector),
117            Mcu::Rp(_) => None,
118            Mcu::Unknown(_) => None,
119        }
120    }
121
122    /// Returns the size of the specified flash sector in words.
123    pub fn get_sector_size_words(&self, sector: u8) -> Option<u32> {
124        self.get_sector_size_bytes(sector).map(|size| size / 4)
125    }
126
127    /// Returns the size of the specified flash sector in KB.
128    pub fn get_sector_size_kb(&self, sector: u8) -> Option<u32> {
129        self.get_sector_size_bytes(sector).map(|size| size / 1024)
130    }
131
132    /// Maximum number of flash sectors for STM32 devices.
133    pub const MAX_SECTORS: u8 = 12;
134
135    /// Returns the sector number or numbers for the given word range.
136    ///
137    /// Note that the word range is relative to the start of flash.
138    pub fn get_sectors_from_word_range(
139        &self,
140        range: RangeInclusive<u32>,
141        sectors: &mut [u8; Self::MAX_SECTORS as usize],
142    ) -> Option<usize> {
143        const_assert!(Mcu::MAX_SECTORS <= stm::StmDetails::MAX_SECTORS);
144        match self {
145            Mcu::Stm32(details) => details.get_sectors_from_word_range(range, sectors),
146            Mcu::Rp(_) => None,
147            Mcu::Unknown(_) => None,
148        }
149    }
150
151    /// Returns the expected Access Port Identification Register (IDR) value
152    /// for this MCU.
153    pub fn expected_idr(&self) -> Option<Idr> {
154        match self {
155            Mcu::Stm32(details) => details.expected_idr(),
156            Mcu::Rp(details) => details.expected_idr(),
157            Mcu::Unknown(_) => None,
158        }
159    }
160}
161
162impl fmt::Display for Mcu {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        match self {
165            Mcu::Stm32(details) => {
166                if f.alternate() {
167                    write!(f, "STM32 MCU: {details:#}")
168                } else {
169                    write!(f, "{details}")
170                }
171            }
172            Mcu::Rp(details) => {
173                if f.alternate() {
174                    write!(f, "RP MCU: {details:#}")
175                } else {
176                    write!(f, "{details}")
177                }
178            }
179            Mcu::Unknown(idcode) => {
180                if f.alternate() {
181                    write!(f, "Unknown MCU (IDCODE: {idcode:#})")
182                } else {
183                    write!(f, "Unknown MCU (IDCODE: {idcode})")
184                }
185            }
186        }
187    }
188}