svdtools/mmap/
mmap_cli.rs

1use 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
8/// Output sorted text of every peripheral, register, field, and interrupt
9/// in the device, such that automated diffing is possible.
10pub 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) = &register.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}