From e5ab476ed1ed11477564f58450cb0b0893463e9f Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Sun, 11 Aug 2024 14:51:10 +0300 Subject: [PATCH 1/4] mmaps: fields coverage --- CHANGELOG-rust.md | 1 + src/mmap/mmap_cli.rs | 96 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/CHANGELOG-rust.md b/CHANGELOG-rust.md index 90263f62..5c3dadd9 100644 --- a/CHANGELOG-rust.md +++ b/CHANGELOG-rust.md @@ -5,6 +5,7 @@ This changelog tracks the Rust `svdtools` project. See ## [Unreleased] +* Show number of covered fields in `mmaps` * Sugar for simple `_split` and `_merge` ## [v0.3.18] 2024-08-10 diff --git a/src/mmap/mmap_cli.rs b/src/mmap/mmap_cli.rs index 0a0edddd..c1484440 100644 --- a/src/mmap/mmap_cli.rs +++ b/src/mmap/mmap_cli.rs @@ -4,6 +4,13 @@ use anyhow::{Context, Result}; use std::{fs::File, io::Read, path::Path}; use svd::PeripheralInfo; use svd_parser::svd::{self, Cluster, Field, Peripheral, Register, RegisterCluster, RegisterInfo}; +use svd_rs::FieldInfo; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +struct CoveredFields { + all: u32, + covered: u32, +} /// Output sorted text of every peripheral, register, field, and interrupt /// in the device, such that automated diffing is possible. @@ -28,6 +35,7 @@ fn get_text(svd: &mut R) -> Result { fn to_text(peripherals: &[Peripheral]) -> String { let mut mmap: Vec = vec![]; + let mut coverage = CoveredFields::default(); for p in peripherals { match p { @@ -35,21 +43,38 @@ fn to_text(peripherals: &[Peripheral]) -> String { get_peripheral(p, &mut mmap); get_interrupts(p, &mut mmap); let registers = get_periph_registers(p, peripherals); - get_registers(p.base_address, registers.as_ref(), "", &mut mmap); + get_registers( + p.base_address, + registers.as_ref(), + "", + &mut mmap, + &mut coverage, + ); } Peripheral::Array(p, d) => { for pi in svd::peripheral::expand(p, d) { get_peripheral(&pi, &mut mmap); get_interrupts(&pi, &mut mmap); let registers = get_periph_registers(&pi, peripherals); - get_registers(pi.base_address, registers.as_ref(), "", &mut mmap); + get_registers( + pi.base_address, + registers.as_ref(), + "", + &mut mmap, + &mut coverage, + ); } } } } mmap.sort(); - mmap.join("\n") + let mut mmap = mmap.join("\n"); + mmap.push_str(&format!( + "\nCovered {} from {} fields.", + coverage.covered, coverage.all + )); + mmap } fn get_periph_registers<'a>( @@ -104,6 +129,7 @@ fn get_registers( registers: Option<&Vec>, suffix: &str, mmap: &mut Vec, + coverage: &mut CoveredFields, ) { if let Some(registers) = registers { for r in registers { @@ -121,7 +147,7 @@ fn get_registers( "{addr} B REGISTER {rname}{derived}{access}: {description}" ); mmap.push(text); - get_fields(r, &addr, mmap); + get_fields(r, &addr, mmap, coverage); } Register::Array(r, d) => { for ri in svd::register::expand(r, d) { @@ -134,7 +160,7 @@ fn get_registers( "{addr} B REGISTER {rname}{derived}{access}: {description}" ); mmap.push(text); - get_fields(&ri, &addr, mmap); + get_fields(&ri, &addr, mmap, coverage); } } } @@ -149,7 +175,7 @@ fn get_registers( let description = str_utils::get_description(&c.description); let text = format!("{addr} B CLUSTER {cname}{derived}: {description}"); mmap.push(text); - get_registers(caddr, Some(&c.children), "", mmap); + get_registers(caddr, Some(&c.children), "", mmap, coverage); } Cluster::Array(c, d) => { for (ci, idx) in svd::cluster::expand(c, d).zip(d.indexes()) { @@ -160,7 +186,7 @@ fn get_registers( let text = format!("{addr} B CLUSTER {cname}{derived}: {description}"); mmap.push(text); - get_registers(caddr, Some(&c.children), &idx, mmap); + get_registers(caddr, Some(&c.children), &idx, mmap, coverage); } } } @@ -170,7 +196,12 @@ fn get_registers( } } -fn get_fields(register: &RegisterInfo, addr: &str, mmap: &mut Vec) { +fn get_fields( + register: &RegisterInfo, + addr: &str, + mmap: &mut Vec, + coverage: &mut CoveredFields, +) { if let Some(fields) = ®ister.fields { for f in fields { let derived = derived_str(&f.derived_from); @@ -185,6 +216,12 @@ fn get_fields(register: &RegisterInfo, addr: &str, mmap: &mut Vec) { "{addr} C FIELD {bit_offset:02}w{bit_width:02} {fname}{derived}{access}: {description}" ); mmap.push(text); + if f.derived_from.is_none() { + coverage.all += 1; + if is_covered(f) { + coverage.covered += 1; + } + } } Field::Array(f, d) => { for fi in svd::field::expand(f, d) { @@ -195,7 +232,13 @@ fn get_fields(register: &RegisterInfo, addr: &str, mmap: &mut Vec) { let text = format!( "{addr} C FIELD {bit_offset:02}w{bit_width:02} {fname}{derived}{access}: {description}" ); - mmap.push(text); + if fi.derived_from.is_none() { + mmap.push(text); + coverage.all += 1; + if is_covered(&fi) { + coverage.covered += 1; + } + } } } } @@ -203,6 +246,10 @@ fn get_fields(register: &RegisterInfo, addr: &str, mmap: &mut Vec) { } } +fn is_covered(f: &FieldInfo) -> bool { + !f.enumerated_values.is_empty() || f.write_constraint.is_some() +} + #[cfg(test)] mod tests { use super::*; @@ -231,6 +278,12 @@ mod tests { Field 1 5 2 + + + 0 + 0x3 + + F2 @@ -261,6 +314,27 @@ mod tests { REG1 0x10 Register B1 + + + F3 + Field 3 + 10 + 1 + + EV_NAME + + VAL1 + Value description 1 + 0 + + + VAL2 + Value description 2 + 1 + + + + @@ -274,8 +348,10 @@ mod tests { 0x10000014 B REGISTER REG2: Register A2 0x10010000 A PERIPHERAL PeriphB 0x10010010 B REGISTER REG1: Register B1 +0x10010010 C FIELD 10w01 F3: Field 3 INTERRUPT 001: INT_A1 (PeriphA): Interrupt A1 -INTERRUPT 002: INT_B2 (PeriphB): Interrupt B2"; +INTERRUPT 002: INT_B2 (PeriphB): Interrupt B2 +Covered 2 from 3 fields."; #[test] fn mmap() { From b8d3f7dde84bcbfb44b56771255281cc0012c57f Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Mon, 12 Aug 2024 07:29:10 +0300 Subject: [PATCH 2/4] per-peripheral field coverage --- src/mmap/mmap_cli.rs | 70 +++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/mmap/mmap_cli.rs b/src/mmap/mmap_cli.rs index c1484440..45aeb2e7 100644 --- a/src/mmap/mmap_cli.rs +++ b/src/mmap/mmap_cli.rs @@ -12,6 +12,22 @@ struct CoveredFields { covered: u32, } +impl core::ops::Add for CoveredFields { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { + Self { + all: self.all + rhs.all, + covered: self.covered + rhs.covered, + } + } +} + +impl core::ops::AddAssign for CoveredFields { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs + } +} + /// Output sorted text of every peripheral, register, field, and interrupt /// in the device, such that automated diffing is possible. pub fn parse_device(svd_file: &Path) -> Result<()> { @@ -34,47 +50,50 @@ fn get_text(svd: &mut R) -> Result { } fn to_text(peripherals: &[Peripheral]) -> String { - let mut mmap: Vec = vec![]; + let mut mmap = Vec::new(); let mut coverage = CoveredFields::default(); for p in peripherals { match p { Peripheral::Single(p) => { - get_peripheral(p, &mut mmap); - get_interrupts(p, &mut mmap); + let mut pcov = CoveredFields::default(); let registers = get_periph_registers(p, peripherals); + let mut rmmap = Vec::new(); get_registers( p.base_address, registers.as_ref(), "", - &mut mmap, - &mut coverage, + &mut rmmap, + &mut pcov, ); + get_peripheral(p, &mut mmap, pcov); + get_interrupts(p, &mut mmap); + mmap.extend(rmmap); + coverage += pcov; } Peripheral::Array(p, d) => { + let mut pcov = CoveredFields::default(); for pi in svd::peripheral::expand(p, d) { - get_peripheral(&pi, &mut mmap); - get_interrupts(&pi, &mut mmap); let registers = get_periph_registers(&pi, peripherals); + let mut rmmap = Vec::new(); get_registers( pi.base_address, registers.as_ref(), "", - &mut mmap, - &mut coverage, + &mut rmmap, + &mut pcov, ); + get_peripheral(&pi, &mut mmap, pcov); + get_interrupts(&pi, &mut mmap); + mmap.extend(rmmap); + coverage += pcov; } } } } mmap.sort(); - let mut mmap = mmap.join("\n"); - mmap.push_str(&format!( - "\nCovered {} from {} fields.", - coverage.covered, coverage.all - )); - mmap + mmap.join("\n") } fn get_periph_registers<'a>( @@ -96,11 +115,13 @@ fn get_periph_registers<'a>( } } -fn get_peripheral(peripheral: &PeripheralInfo, mmap: &mut Vec) { +fn get_peripheral(peripheral: &PeripheralInfo, mmap: &mut Vec, coverage: CoveredFields) { let text = format!( - "{} A PERIPHERAL {}", + "{} A PERIPHERAL {} ({}/{} fields covered)", str_utils::format_address(peripheral.base_address), - peripheral.name + peripheral.name, + coverage.covered, + coverage.all, ); mmap.push(text); } @@ -135,6 +156,7 @@ fn get_registers( for r in registers { match &r { RegisterCluster::Register(r) => { + let mut rcov = CoveredFields::default(); let access = svd_utils::access_with_brace(r.properties.access); let derived = derived_str(&r.derived_from); match r { @@ -147,7 +169,7 @@ fn get_registers( "{addr} B REGISTER {rname}{derived}{access}: {description}" ); mmap.push(text); - get_fields(r, &addr, mmap, coverage); + get_fields(r, &addr, mmap, &mut rcov); } Register::Array(r, d) => { for ri in svd::register::expand(r, d) { @@ -160,10 +182,11 @@ fn get_registers( "{addr} B REGISTER {rname}{derived}{access}: {description}" ); mmap.push(text); - get_fields(&ri, &addr, mmap, coverage); + get_fields(&ri, &addr, mmap, &mut rcov); } } } + *coverage += rcov; } RegisterCluster::Cluster(c) => { let derived = derived_str(&c.derived_from); @@ -341,17 +364,16 @@ mod tests { "; - static EXPECTED_MMAP: &str = r"0x10000000 A PERIPHERAL PeriphA + static EXPECTED_MMAP: &str = r"0x10000000 A PERIPHERAL PeriphA (1/2 fields covered) 0x10000010 B REGISTER REG1: Register A1 0x10000010 C FIELD 05w02 F1: Field 1 0x10000010 C FIELD 10w01 F2: Field 2 0x10000014 B REGISTER REG2: Register A2 -0x10010000 A PERIPHERAL PeriphB +0x10010000 A PERIPHERAL PeriphB (1/1 fields covered) 0x10010010 B REGISTER REG1: Register B1 0x10010010 C FIELD 10w01 F3: Field 3 INTERRUPT 001: INT_A1 (PeriphA): Interrupt A1 -INTERRUPT 002: INT_B2 (PeriphB): Interrupt B2 -Covered 2 from 3 fields."; +INTERRUPT 002: INT_B2 (PeriphB): Interrupt B2"; #[test] fn mmap() { From 5479937c9e3781f41e76c2d5860e22a704234ab6 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Tue, 20 Aug 2024 12:06:42 +0300 Subject: [PATCH 3/4] ignore derived peripheral --- src/mmap/mmap_cli.rs | 54 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/src/mmap/mmap_cli.rs b/src/mmap/mmap_cli.rs index 45aeb2e7..f3f5cb04 100644 --- a/src/mmap/mmap_cli.rs +++ b/src/mmap/mmap_cli.rs @@ -66,7 +66,15 @@ fn to_text(peripherals: &[Peripheral]) -> String { &mut rmmap, &mut pcov, ); - get_peripheral(p, &mut mmap, pcov); + get_peripheral( + p, + &mut mmap, + if p.derived_from.is_some() { + CoveredFields::default() + } else { + pcov + }, + ); get_interrupts(p, &mut mmap); mmap.extend(rmmap); coverage += pcov; @@ -83,7 +91,15 @@ fn to_text(peripherals: &[Peripheral]) -> String { &mut rmmap, &mut pcov, ); - get_peripheral(&pi, &mut mmap, pcov); + get_peripheral( + &pi, + &mut mmap, + if pi.derived_from.is_some() { + CoveredFields::default() + } else { + pcov + }, + ); get_interrupts(&pi, &mut mmap); mmap.extend(rmmap); coverage += pcov; @@ -116,13 +132,21 @@ fn get_periph_registers<'a>( } fn get_peripheral(peripheral: &PeripheralInfo, mmap: &mut Vec, coverage: CoveredFields) { - let text = format!( - "{} A PERIPHERAL {} ({}/{} fields covered)", - str_utils::format_address(peripheral.base_address), - peripheral.name, - coverage.covered, - coverage.all, - ); + let text = if coverage.all > 0 { + format!( + "{} A PERIPHERAL {} ({}/{} fields covered)", + str_utils::format_address(peripheral.base_address), + peripheral.name, + coverage.covered, + coverage.all, + ) + } else { + format!( + "{} A PERIPHERAL {}", + str_utils::format_address(peripheral.base_address), + peripheral.name, + ) + }; mmap.push(text); } @@ -277,7 +301,7 @@ fn is_covered(f: &FieldInfo) -> bool { mod tests { use super::*; - static SVD: &str = r" + static SVD: &str = r##" dev @@ -361,8 +385,13 @@ mod tests { + + PeriphC + Peripheral C + 0x10020000 + -"; +"##; static EXPECTED_MMAP: &str = r"0x10000000 A PERIPHERAL PeriphA (1/2 fields covered) 0x10000010 B REGISTER REG1: Register A1 @@ -372,6 +401,9 @@ mod tests { 0x10010000 A PERIPHERAL PeriphB (1/1 fields covered) 0x10010010 B REGISTER REG1: Register B1 0x10010010 C FIELD 10w01 F3: Field 3 +0x10020000 A PERIPHERAL PeriphC +0x10020010 B REGISTER REG1: Register B1 +0x10020010 C FIELD 10w01 F3: Field 3 INTERRUPT 001: INT_A1 (PeriphA): Interrupt A1 INTERRUPT 002: INT_B2 (PeriphB): Interrupt B2"; From e7fa71579c19bc3042184e445d98bc3ed2c7cde1 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Thu, 22 Aug 2024 07:14:06 +0300 Subject: [PATCH 4/4] bump svd --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7b04f0e4..fa125ba2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,9 +29,9 @@ rust-version = "1.70" clap = { version = "4.4", features = ["derive", "cargo", "color"] } serde = { version = "1.0", features = ["derive"] } quick-xml = { version = "0.31", features = ["serialize"] } -svd-rs = { version = "0.14.8", features = ["serde", "derive-from"] } -svd-parser = { version = "0.14.5", features = ["expand"] } -svd-encoder = "0.14.4" +svd-rs = { version = "0.14.9", features = ["serde", "derive-from"] } +svd-parser = { version = "0.14.6", features = ["expand"] } +svd-encoder = "0.14.5" # serde_yaml 0.9.x looks broken serde_yaml = "0.8.26" serde_json = { version = "1.0", features = ["preserve_order"] }