Skip to content

Commit d128d81

Browse files
committed
mmaps: fields coverage
1 parent 0ad6714 commit d128d81

File tree

2 files changed

+155
-23
lines changed

2 files changed

+155
-23
lines changed

CHANGELOG-rust.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ This changelog tracks the Rust `svdtools` project. See
5353

5454
* Fix deletion childrens on cluster modify
5555
* Sugar for simple `_split` and `_merge`
56+
* Show number of covered fields in `mmaps`
57+
* Sugar for simple `_split` and `_merge`
5658

5759
## [v0.3.18] 2024-08-10
5860

src/mmap/mmap_cli.rs

Lines changed: 153 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,29 @@ use anyhow::{Context, Result};
44
use std::{fs::File, io::Read, path::Path};
55
use svd::PeripheralInfo;
66
use svd_parser::svd::{self, Cluster, Field, Peripheral, Register, RegisterCluster, RegisterInfo};
7+
use svd_rs::FieldInfo;
8+
9+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
10+
struct CoveredFields {
11+
all: u32,
12+
covered: u32,
13+
}
14+
15+
impl core::ops::Add for CoveredFields {
16+
type Output = Self;
17+
fn add(self, rhs: Self) -> Self::Output {
18+
Self {
19+
all: self.all + rhs.all,
20+
covered: self.covered + rhs.covered,
21+
}
22+
}
23+
}
24+
25+
impl core::ops::AddAssign for CoveredFields {
26+
fn add_assign(&mut self, rhs: Self) {
27+
*self = *self + rhs
28+
}
29+
}
730

831
/// Output sorted text of every peripheral, register, field, and interrupt
932
/// in the device, such that automated diffing is possible.
@@ -27,22 +50,59 @@ fn get_text<R: Read>(svd: &mut R) -> Result<String> {
2750
}
2851

2952
fn to_text(peripherals: &[Peripheral]) -> String {
30-
let mut mmap: Vec<String> = vec![];
53+
let mut mmap = Vec::new();
54+
let mut coverage = CoveredFields::default();
3155

3256
for p in peripherals {
3357
match p {
3458
Peripheral::Single(p) => {
35-
get_peripheral(p, &mut mmap);
36-
get_interrupts(p, &mut mmap);
59+
let mut pcov = CoveredFields::default();
3760
let registers = get_periph_registers(p, peripherals);
38-
get_registers(p.base_address, registers.as_ref(), "", &mut mmap);
61+
let mut rmmap = Vec::new();
62+
get_registers(
63+
p.base_address,
64+
registers.as_ref(),
65+
"",
66+
&mut rmmap,
67+
&mut pcov,
68+
);
69+
get_peripheral(
70+
p,
71+
&mut mmap,
72+
if p.derived_from.is_some() {
73+
CoveredFields::default()
74+
} else {
75+
pcov
76+
},
77+
);
78+
get_interrupts(p, &mut mmap);
79+
mmap.extend(rmmap);
80+
coverage += pcov;
3981
}
4082
Peripheral::Array(p, d) => {
83+
let mut pcov = CoveredFields::default();
4184
for pi in svd::peripheral::expand(p, d) {
42-
get_peripheral(&pi, &mut mmap);
43-
get_interrupts(&pi, &mut mmap);
4485
let registers = get_periph_registers(&pi, peripherals);
45-
get_registers(pi.base_address, registers.as_ref(), "", &mut mmap);
86+
let mut rmmap = Vec::new();
87+
get_registers(
88+
pi.base_address,
89+
registers.as_ref(),
90+
"",
91+
&mut rmmap,
92+
&mut pcov,
93+
);
94+
get_peripheral(
95+
&pi,
96+
&mut mmap,
97+
if pi.derived_from.is_some() {
98+
CoveredFields::default()
99+
} else {
100+
pcov
101+
},
102+
);
103+
get_interrupts(&pi, &mut mmap);
104+
mmap.extend(rmmap);
105+
coverage += pcov;
46106
}
47107
}
48108
}
@@ -71,12 +131,22 @@ fn get_periph_registers<'a>(
71131
}
72132
}
73133

74-
fn 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-
);
134+
fn get_peripheral(peripheral: &PeripheralInfo, mmap: &mut Vec<String>, coverage: CoveredFields) {
135+
let text = if coverage.all > 0 {
136+
format!(
137+
"{} A PERIPHERAL {} ({}/{} fields covered)",
138+
str_utils::format_address(peripheral.base_address),
139+
peripheral.name,
140+
coverage.covered,
141+
coverage.all,
142+
)
143+
} else {
144+
format!(
145+
"{} A PERIPHERAL {}",
146+
str_utils::format_address(peripheral.base_address),
147+
peripheral.name,
148+
)
149+
};
80150
mmap.push(text);
81151
}
82152

@@ -104,11 +174,13 @@ fn get_registers(
104174
registers: Option<&Vec<RegisterCluster>>,
105175
suffix: &str,
106176
mmap: &mut Vec<String>,
177+
coverage: &mut CoveredFields,
107178
) {
108179
if let Some(registers) = registers {
109180
for r in registers {
110181
match &r {
111182
RegisterCluster::Register(r) => {
183+
let mut rcov = CoveredFields::default();
112184
let access = svd_utils::access_with_brace(r.properties.access);
113185
let derived = derived_str(&r.derived_from);
114186
match r {
@@ -121,7 +193,7 @@ fn get_registers(
121193
"{addr} B REGISTER {rname}{derived}{access}: {description}"
122194
);
123195
mmap.push(text);
124-
get_fields(r, &addr, mmap);
196+
get_fields(r, &addr, mmap, &mut rcov);
125197
}
126198
Register::Array(r, d) => {
127199
for ri in svd::register::expand(r, d) {
@@ -134,10 +206,11 @@ fn get_registers(
134206
"{addr} B REGISTER {rname}{derived}{access}: {description}"
135207
);
136208
mmap.push(text);
137-
get_fields(&ri, &addr, mmap);
209+
get_fields(&ri, &addr, mmap, &mut rcov);
138210
}
139211
}
140212
}
213+
*coverage += rcov;
141214
}
142215
RegisterCluster::Cluster(c) => {
143216
let derived = derived_str(&c.derived_from);
@@ -149,7 +222,7 @@ fn get_registers(
149222
let description = str_utils::get_description(&c.description);
150223
let text = format!("{addr} B CLUSTER {cname}{derived}: {description}");
151224
mmap.push(text);
152-
get_registers(caddr, Some(&c.children), "", mmap);
225+
get_registers(caddr, Some(&c.children), "", mmap, coverage);
153226
}
154227
Cluster::Array(c, d) => {
155228
for (ci, idx) in svd::cluster::expand(c, d).zip(d.indexes()) {
@@ -160,7 +233,7 @@ fn get_registers(
160233
let text =
161234
format!("{addr} B CLUSTER {cname}{derived}: {description}");
162235
mmap.push(text);
163-
get_registers(caddr, Some(&c.children), &idx, mmap);
236+
get_registers(caddr, Some(&c.children), &idx, mmap, coverage);
164237
}
165238
}
166239
}
@@ -170,7 +243,12 @@ fn get_registers(
170243
}
171244
}
172245

173-
fn get_fields(register: &RegisterInfo, addr: &str, mmap: &mut Vec<String>) {
246+
fn get_fields(
247+
register: &RegisterInfo,
248+
addr: &str,
249+
mmap: &mut Vec<String>,
250+
coverage: &mut CoveredFields,
251+
) {
174252
if let Some(fields) = &register.fields {
175253
for f in fields {
176254
let derived = derived_str(&f.derived_from);
@@ -185,6 +263,12 @@ fn get_fields(register: &RegisterInfo, addr: &str, mmap: &mut Vec<String>) {
185263
"{addr} C FIELD {bit_offset:02}w{bit_width:02} {fname}{derived}{access}: {description}"
186264
);
187265
mmap.push(text);
266+
if f.derived_from.is_none() {
267+
coverage.all += 1;
268+
if is_covered(f) {
269+
coverage.covered += 1;
270+
}
271+
}
188272
}
189273
Field::Array(f, d) => {
190274
for fi in svd::field::expand(f, d) {
@@ -195,19 +279,29 @@ fn get_fields(register: &RegisterInfo, addr: &str, mmap: &mut Vec<String>) {
195279
let text = format!(
196280
"{addr} C FIELD {bit_offset:02}w{bit_width:02} {fname}{derived}{access}: {description}"
197281
);
198-
mmap.push(text);
282+
if fi.derived_from.is_none() {
283+
mmap.push(text);
284+
coverage.all += 1;
285+
if is_covered(&fi) {
286+
coverage.covered += 1;
287+
}
288+
}
199289
}
200290
}
201291
}
202292
}
203293
}
204294
}
205295

296+
fn is_covered(f: &FieldInfo) -> bool {
297+
!f.enumerated_values.is_empty() || f.write_constraint.is_some()
298+
}
299+
206300
#[cfg(test)]
207301
mod tests {
208302
use super::*;
209303

210-
static SVD: &str = r"
304+
static SVD: &str = r##"
211305
<device>
212306
<name>dev</name>
213307
<peripherals>
@@ -231,6 +325,12 @@ mod tests {
231325
<description>Field 1</description>
232326
<bitOffset>5</bitOffset>
233327
<bitWidth>2</bitWidth>
328+
<writeConstraint>
329+
<range>
330+
<minimum>0</minimum>
331+
<maximum>0x3</maximum>
332+
</range>
333+
</writeConstraint>
234334
</field>
235335
<field>
236336
<name>F2</name>
@@ -261,19 +361,49 @@ mod tests {
261361
<name>REG1</name>
262362
<addressOffset>0x10</addressOffset>
263363
<description>Register B1</description>
364+
<fields>
365+
<field>
366+
<name>F3</name>
367+
<description>Field 3</description>
368+
<bitOffset>10</bitOffset>
369+
<bitWidth>1</bitWidth>
370+
<enumeratedValues>
371+
<name>EV_NAME</name>
372+
<enumeratedValue>
373+
<name>VAL1</name>
374+
<description>Value description 1</description>
375+
<value>0</value>
376+
</enumeratedValue>
377+
<enumeratedValue>
378+
<name>VAL2</name>
379+
<description>Value description 2</description>
380+
<value>1</value>
381+
</enumeratedValue>
382+
</enumeratedValues>
383+
</field>
384+
</fields>
264385
</register>
265386
</registers>
266387
</peripheral>
388+
<peripheral derivedFrom="PeriphB">
389+
<name>PeriphC</name>
390+
<description>Peripheral C</description>
391+
<baseAddress>0x10020000</baseAddress>
392+
</peripheral>
267393
</peripherals>
268-
</device>";
394+
</device>"##;
269395

270-
static EXPECTED_MMAP: &str = r"0x10000000 A PERIPHERAL PeriphA
396+
static EXPECTED_MMAP: &str = r"0x10000000 A PERIPHERAL PeriphA (1/2 fields covered)
271397
0x10000010 B REGISTER REG1: Register A1
272398
0x10000010 C FIELD 05w02 F1: Field 1
273399
0x10000010 C FIELD 10w01 F2: Field 2
274400
0x10000014 B REGISTER REG2: Register A2
275-
0x10010000 A PERIPHERAL PeriphB
401+
0x10010000 A PERIPHERAL PeriphB (1/1 fields covered)
276402
0x10010010 B REGISTER REG1: Register B1
403+
0x10010010 C FIELD 10w01 F3: Field 3
404+
0x10020000 A PERIPHERAL PeriphC
405+
0x10020010 B REGISTER REG1: Register B1
406+
0x10020010 C FIELD 10w01 F3: Field 3
277407
INTERRUPT 001: INT_A1 (PeriphA): Interrupt A1
278408
INTERRUPT 002: INT_B2 (PeriphB): Interrupt B2";
279409

0 commit comments

Comments
 (0)