consortium_data/
chipmap.rs1use std::collections::HashMap;
27
28use crate::error::DataError;
29
30struct Embedded {
33 family: &'static str,
35 core: &'static str,
37 json: &'static str,
39}
40
41const CHIPS: &[Embedded] = &[
46 Embedded {
47 family: "stm32mp25",
48 core: "cm33",
49 json: include_str!("../database/chip/stm32mp25_cm33.json"),
50 },
51 Embedded {
52 family: "stm32mp25",
53 core: "cm0p",
54 json: include_str!("../database/chip/stm32mp25_cm0p.json"),
55 },
56 Embedded {
57 family: "stm32mp23",
58 core: "cm33",
59 json: include_str!("../database/chip/stm32mp23_cm33.json"),
60 },
61 Embedded {
62 family: "stm32mp21",
63 core: "cm33",
64 json: include_str!("../database/chip/stm32mp21_cm33.json"),
65 },
66 Embedded {
67 family: "mimx95",
68 core: "cm7",
69 json: include_str!("../database/chip/mimx95_cm7.json"),
70 },
71 Embedded {
72 family: "mimx93",
73 core: "cm33",
74 json: include_str!("../database/chip/mimx93_cm33.json"),
75 },
76];
77
78#[derive(serde::Deserialize)]
80struct RawChip {
81 irqs: Vec<RawIrq>,
82 peripherals: Vec<RawPeripheral>,
83}
84
85#[derive(serde::Deserialize)]
86struct RawIrq {
87 name: String,
88 value: u16,
89}
90
91#[derive(serde::Deserialize)]
92struct RawPeripheral {
93 name: String,
94 base_address: String,
95 block: String,
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct PeripheralEntry {
101 pub name: String,
105 pub base: u32,
107 pub block: String,
109}
110
111impl PeripheralEntry {
112 pub fn kind(&self) -> &str {
114 self.block.split('/').next().unwrap_or(&self.block)
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct IrqEntry {
121 pub number: u16,
123 pub name: String,
126}
127
128#[derive(Debug, Clone)]
130pub struct ChipMap {
131 peripherals: HashMap<String, PeripheralEntry>,
133 irqs: HashMap<String, IrqEntry>,
135}
136
137impl ChipMap {
138 pub fn peripheral(&self, name: &str) -> Option<&PeripheralEntry> {
140 self.peripherals.get(&name.to_ascii_lowercase())
141 }
142
143 pub fn contains_peripheral(&self, name: &str) -> bool {
145 self.peripherals.contains_key(&name.to_ascii_lowercase())
146 }
147
148 pub fn peripheral_names(&self) -> Vec<String> {
151 let mut names: Vec<String> = self.peripherals.values().map(|p| p.name.clone()).collect();
152 names.sort();
153 names
154 }
155
156 pub fn irq(&self, name: &str) -> Option<&IrqEntry> {
158 self.irqs.get(&name.to_ascii_lowercase())
159 }
160}
161
162pub fn load(chip: &str, core: &str) -> Result<ChipMap, DataError> {
174 let needle = normalize(chip);
175 let core_key = core.to_ascii_lowercase();
176 let entry = CHIPS
177 .iter()
178 .find(|entry| needle.starts_with(entry.family) && entry.core == core_key)
179 .ok_or_else(|| DataError::ChipMapUnavailable {
180 chip: chip.to_string(),
181 core: core.to_string(),
182 })?;
183
184 let raw: RawChip = serde_json::from_str(entry.json).map_err(|err| DataError::ChipMapParse {
185 chip: chip.to_string(),
186 core: core.to_string(),
187 detail: err.to_string(),
188 })?;
189
190 let mut peripherals = HashMap::with_capacity(raw.peripherals.len());
191 for p in raw.peripherals {
192 let base = parse_hex(&p.base_address).ok_or_else(|| DataError::ChipMapParse {
193 chip: chip.to_string(),
194 core: core.to_string(),
195 detail: format!(
196 "peripheral '{}' has invalid base '{}'",
197 p.name, p.base_address
198 ),
199 })?;
200 peripherals.insert(
201 p.name.to_ascii_lowercase(),
202 PeripheralEntry {
203 name: p.name,
204 base,
205 block: p.block,
206 },
207 );
208 }
209
210 let irqs = raw
211 .irqs
212 .into_iter()
213 .map(|irq| {
214 (
215 irq.name.to_ascii_lowercase(),
216 IrqEntry {
217 number: irq.value,
218 name: irq.name,
219 },
220 )
221 })
222 .collect();
223
224 Ok(ChipMap { peripherals, irqs })
225}
226
227fn parse_hex(value: &str) -> Option<u32> {
229 let trimmed = value.trim();
230 let digits = trimmed
231 .strip_prefix("0x")
232 .or_else(|| trimmed.strip_prefix("0X"))
233 .unwrap_or(trimmed);
234 u32::from_str_radix(digits, 16).ok()
235}
236
237fn normalize(chip: &str) -> String {
239 let lowered = chip.to_ascii_lowercase();
240 match lowered.strip_prefix("imx") {
241 Some(rest) => format!("mimx{rest}"),
242 None => lowered,
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn resolves_imx95_cm7_lpi2c_and_irq() {
252 let map = load("imx95", "cm7").expect("imx95 cm7 map should resolve");
253 let lpi2c1 = map.peripheral("LPI2C1").expect("LPI2C1 should exist");
254 assert_ne!(lpi2c1.base, 0);
255 assert_eq!(lpi2c1.kind(), "lpi2c");
256 assert!(map.irq("LPI2C1").is_some());
258 }
259
260 #[test]
261 fn resolves_stm32mp25_cm33_gpio() {
262 let map = load("stm32mp257fak3", "cm33").expect("stm32mp25 cm33 map should resolve");
263 let gpioa = map.peripheral("GPIOA").expect("GPIOA should exist");
264 assert_eq!(gpioa.kind(), "gpio");
265 assert!(map.contains_peripheral("i2c1"));
266 }
267
268 #[test]
269 fn rejects_unknown_core() {
270 assert!(matches!(
271 load("stm32mp257fak3", "cm4"),
272 Err(DataError::ChipMapUnavailable { .. })
273 ));
274 }
275}