svdtools/mmap/
mmap_cli.rs1use crate::common::svd_reader;
2use crate::common::{str_utils, svd_utils};
3use anyhow::{Context, Result};
4use std::{fs::File, io::Read, path::Path};
5use svd::PeripheralInfo;
6use svd_parser::svd::{self, Cluster, Field, Peripheral, Register, RegisterCluster, RegisterInfo};
7
8pub fn parse_device(svd_file: &Path) -> Result<()> {
11 let mut file = File::open(svd_file).expect("svd file doesn't exist");
12 match get_text(&mut file) {
13 Err(e) => {
14 let path_str = svd_file.display();
15 Err(e).with_context(|| format!("Parsing {path_str}"))
16 }
17 Ok(text) => {
18 println!("{text}");
19 Ok(())
20 }
21 }
22}
23
24fn get_text<R: Read>(svd: &mut R) -> Result<String> {
25 let peripherals = svd_reader::peripherals(svd)?;
26 Ok(to_text(&peripherals))
27}
28
29fn to_text(peripherals: &[Peripheral]) -> String {
30 let mut mmap: Vec<String> = vec![];
31
32 for p in peripherals {
33 match p {
34 Peripheral::Single(p) => {
35 get_peripheral(p, &mut mmap);
36 get_interrupts(p, &mut mmap);
37 let registers = get_periph_registers(p, peripherals);
38 get_registers(p.base_address, registers.as_ref(), "", &mut mmap);
39 }
40 Peripheral::Array(p, d) => {
41 for pi in svd::peripheral::expand(p, d) {
42 get_peripheral(&pi, &mut mmap);
43 get_interrupts(&pi, &mut mmap);
44 let registers = get_periph_registers(&pi, peripherals);
45 get_registers(pi.base_address, registers.as_ref(), "", &mut mmap);
46 }
47 }
48 }
49 }
50
51 mmap.sort();
52 mmap.join("\n")
53}
54
55fn get_periph_registers<'a>(
56 peripheral: &'a PeripheralInfo,
57 peripheral_list: &'a [Peripheral],
58) -> &'a Option<Vec<RegisterCluster>> {
59 match &peripheral.derived_from {
60 None => &peripheral.registers,
61 Some(father) => {
62 let mut registers = &None;
63 for p in peripheral_list {
64 if &p.name == father {
65 registers = &p.registers;
66 break;
67 }
68 }
69 registers
70 }
71 }
72}
73
74fn get_peripheral(peripheral: &PeripheralInfo, mmap: &mut Vec<String>) {
75 let text = format!(
76 "{} A PERIPHERAL {}",
77 str_utils::format_address(peripheral.base_address),
78 peripheral.name
79 );
80 mmap.push(text);
81}
82
83fn get_interrupts(peripheral: &PeripheralInfo, mmap: &mut Vec<String>) {
84 for i in &peripheral.interrupt {
85 let description = str_utils::get_description(&i.description);
86 let text = format!(
87 "INTERRUPT {:03}: {} ({}): {description}",
88 i.value, i.name, peripheral.name
89 );
90 mmap.push(text);
91 }
92}
93
94fn derived_str(dname: &Option<String>) -> String {
95 if let Some(dname) = dname.as_ref() {
96 format!(" (={dname})")
97 } else {
98 String::new()
99 }
100}
101
102fn get_registers(
103 base_address: u64,
104 registers: Option<&Vec<RegisterCluster>>,
105 suffix: &str,
106 mmap: &mut Vec<String>,
107) {
108 if let Some(registers) = registers {
109 for r in registers {
110 match &r {
111 RegisterCluster::Register(r) => {
112 let access = svd_utils::access_with_brace(r.properties.access);
113 let derived = derived_str(&r.derived_from);
114 match r {
115 Register::Single(r) => {
116 let addr =
117 str_utils::format_address(base_address + r.address_offset as u64);
118 let rname = r.name.to_string() + suffix;
119 let description = str_utils::get_description(&r.description);
120 let text = format!(
121 "{addr} B REGISTER {rname}{derived}{access}: {description}"
122 );
123 mmap.push(text);
124 get_fields(r, &addr, mmap);
125 }
126 Register::Array(r, d) => {
127 for ri in svd::register::expand(r, d) {
128 let addr = str_utils::format_address(
129 base_address + ri.address_offset as u64,
130 );
131 let rname = &ri.name;
132 let description = str_utils::get_description(&ri.description);
133 let text = format!(
134 "{addr} B REGISTER {rname}{derived}{access}: {description}"
135 );
136 mmap.push(text);
137 get_fields(&ri, &addr, mmap);
138 }
139 }
140 }
141 }
142 RegisterCluster::Cluster(c) => {
143 let derived = derived_str(&c.derived_from);
144 match c {
145 Cluster::Single(c) => {
146 let caddr = base_address + c.address_offset as u64;
147 let addr = str_utils::format_address(caddr);
148 let cname = &c.name;
149 let description = str_utils::get_description(&c.description);
150 let text = format!("{addr} B CLUSTER {cname}{derived}: {description}");
151 mmap.push(text);
152 get_registers(caddr, Some(&c.children), "", mmap);
153 }
154 Cluster::Array(c, d) => {
155 for (ci, idx) in svd::cluster::expand(c, d).zip(d.indexes()) {
156 let caddr = base_address + ci.address_offset as u64;
157 let addr = str_utils::format_address(caddr);
158 let cname = &ci.name;
159 let description = str_utils::get_description(&ci.description);
160 let text =
161 format!("{addr} B CLUSTER {cname}{derived}: {description}");
162 mmap.push(text);
163 get_registers(caddr, Some(&c.children), &idx, mmap);
164 }
165 }
166 }
167 }
168 }
169 }
170 }
171}
172
173fn get_fields(register: &RegisterInfo, addr: &str, mmap: &mut Vec<String>) {
174 if let Some(fields) = ®ister.fields {
175 for f in fields {
176 let derived = derived_str(&f.derived_from);
177 let access = svd_utils::access_with_brace(f.access);
178 match f {
179 Field::Single(f) => {
180 let bit_offset = f.bit_offset();
181 let bit_width = f.bit_width();
182 let fname = &f.name;
183 let description = str_utils::get_description(&f.description);
184 let text = format!(
185 "{addr} C FIELD {bit_offset:02}w{bit_width:02} {fname}{derived}{access}: {description}"
186 );
187 mmap.push(text);
188 }
189 Field::Array(f, d) => {
190 for fi in svd::field::expand(f, d) {
191 let bit_offset = fi.bit_offset();
192 let bit_width = fi.bit_width();
193 let fname = &fi.name;
194 let description = str_utils::get_description(&fi.description);
195 let text = format!(
196 "{addr} C FIELD {bit_offset:02}w{bit_width:02} {fname}{derived}{access}: {description}"
197 );
198 mmap.push(text);
199 }
200 }
201 }
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 static SVD: &str = r"
211<device>
212 <name>dev</name>
213 <peripherals>
214 <peripheral>
215 <name>PeriphA</name>
216 <description>Peripheral A</description>
217 <baseAddress>0x10000000</baseAddress>
218 <interrupt>
219 <name>INT_A1</name>
220 <description>Interrupt A1</description>
221 <value>1</value>
222 </interrupt>
223 <registers>
224 <register>
225 <name>REG1</name>
226 <addressOffset>0x10</addressOffset>
227 <description>Register A1</description>
228 <fields>
229 <field>
230 <name>F1</name>
231 <description>Field 1</description>
232 <bitOffset>5</bitOffset>
233 <bitWidth>2</bitWidth>
234 </field>
235 <field>
236 <name>F2</name>
237 <description>Field 2</description>
238 <bitOffset>10</bitOffset>
239 <bitWidth>1</bitWidth>
240 </field>
241 </fields>
242 </register>
243 <register>
244 <name>REG2</name>
245 <addressOffset>0x14</addressOffset>
246 <description>Register A2</description>
247 </register>
248 </registers>
249 </peripheral>
250 <peripheral>
251 <name>PeriphB</name>
252 <description>Peripheral B</description>
253 <baseAddress>0x10010000</baseAddress>
254 <interrupt>
255 <name>INT_B2</name>
256 <description>Interrupt B2</description>
257 <value>2</value>
258 </interrupt>
259 <registers>
260 <register>
261 <name>REG1</name>
262 <addressOffset>0x10</addressOffset>
263 <description>Register B1</description>
264 </register>
265 </registers>
266 </peripheral>
267 </peripherals>
268</device>";
269
270 static EXPECTED_MMAP: &str = r"0x10000000 A PERIPHERAL PeriphA
2710x10000010 B REGISTER REG1: Register A1
2720x10000010 C FIELD 05w02 F1: Field 1
2730x10000010 C FIELD 10w01 F2: Field 2
2740x10000014 B REGISTER REG2: Register A2
2750x10010000 A PERIPHERAL PeriphB
2760x10010010 B REGISTER REG1: Register B1
277INTERRUPT 001: INT_A1 (PeriphA): Interrupt A1
278INTERRUPT 002: INT_B2 (PeriphB): Interrupt B2";
279
280 #[test]
281 fn mmap() {
282 let mut svd = SVD.as_bytes();
283 let actual_mmap = get_text(&mut svd).unwrap();
284 assert_eq!(EXPECTED_MMAP, actual_mmap);
285 }
286}