From 2ef1cf80f6005bd9aa449c2fcf9a9e0ce81f4886 Mon Sep 17 00:00:00 2001 From: Shupei Fan Date: Tue, 1 Oct 2024 14:10:13 +0000 Subject: [PATCH] [profiler] refactor ... --- profiler/Cargo.lock | 81 +++++++++++++++++ profiler/Cargo.toml | 2 + profiler/src/input_hier.rs | 89 ++++++++++++++++-- profiler/src/main.rs | 181 +++++++++++++++---------------------- profiler/src/vcd_util.rs | 178 ++++++++++++++++++++++++++++++++++++ 5 files changed, 414 insertions(+), 117 deletions(-) create mode 100644 profiler/src/vcd_util.rs diff --git a/profiler/Cargo.lock b/profiler/Cargo.lock index 114ece4f2..c2f16bf6f 100644 --- a/profiler/Cargo.lock +++ b/profiler/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "anstream" version = "0.6.15" @@ -103,18 +112,59 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "proc-macro2" version = "1.0.86" @@ -130,6 +180,8 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", + "env_logger", + "log", "vcd", ] @@ -142,6 +194,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "strsim" version = "0.11.1" diff --git a/profiler/Cargo.toml b/profiler/Cargo.toml index 9f96548a2..93f2a28f4 100644 --- a/profiler/Cargo.toml +++ b/profiler/Cargo.toml @@ -6,4 +6,6 @@ edition = "2021" [dependencies] anyhow = "1.0.87" clap = { version = "4.5.17", features = ["derive"] } +env_logger = "0.11.5" +log = "0.4.22" vcd = "0.7.0" diff --git a/profiler/src/input_hier.rs b/profiler/src/input_hier.rs index 9f4792513..d4f6dac6f 100644 --- a/profiler/src/input_hier.rs +++ b/profiler/src/input_hier.rs @@ -1,14 +1,52 @@ -use std::collections::HashMap; +use std::{cell::OnceCell, collections::HashMap, rc::Rc}; use vcd::IdCode; +pub enum SignalData { + Scalar { + data: Rc>>, + }, + Vector { + width: u32, + data: Rc>>, + }, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct ValueRecord { + pub cycle: u32, + pub is_x: bool, + pub value: T, +} + pub struct SVar { code: IdCode, + record: OnceCell>>>, +} + +impl SVar { + pub fn debug_print(&self) { + match self.record.get() { + None => println!("signal {} not set", self.code), + Some(record) => { + println!("signal {}:", self.code); + for r in &record[..] { + let value = match (r.is_x, r.value) { + (true, _) => "x", + (false, true) => "1", + (false, false) => "0", + }; + println!("- {} : {}", r.cycle, value); + } + } + } + } } pub struct VVar { code: IdCode, width: u32, + record: OnceCell>>>, } fn s(vars: &HashMap)>, name: &str) -> SVar { @@ -17,7 +55,10 @@ fn s(vars: &HashMap)>, name: &str) -> SVar { .unwrap_or_else(|| panic!("unable to find var '{name}'")); assert!(width.is_none()); - SVar { code } + SVar { + code, + record: OnceCell::new(), + } } fn v(vars: &HashMap)>, name: &str, width: u32) -> VVar { @@ -26,7 +67,11 @@ fn v(vars: &HashMap)>, name: &str, width: u32) -> V .unwrap_or_else(|| panic!("unable to find var '{name}'")); assert_eq!(width_, Some(width)); - VVar { code, width } + VVar { + code, + width, + record: OnceCell::new(), + } } fn ct<'a>(c: &mut VarCollector<'a>, s: &'a impl Collect) { @@ -51,8 +96,32 @@ impl<'a> VarCollector<'a> { }) .collect() } + pub fn set_with_signal_map(&self, signal_map: &HashMap) { + for &var in &self.vars { + match var { + VarRef::SVar(var) => match &signal_map[&var.code] { + SignalData::Scalar { data } => { + var.record + .set(data.clone()) + .expect("signal record already set"); + } + SignalData::Vector { .. } => unreachable!(), + }, + VarRef::VVar(var) => match &signal_map[&var.code] { + SignalData::Vector { width, data } => { + assert_eq!(*width, var.width); + var.record + .set(data.clone()) + .expect("signal record already set"); + } + SignalData::Scalar { .. } => unreachable!(), + }, + } + } + } } +#[derive(Clone, Copy)] enum VarRef<'a> { SVar(&'a SVar), VVar(&'a VVar), @@ -75,7 +144,7 @@ impl Collect for VVar { } pub struct InputVars { - issue_enq: IssueEnq, + pub issue_enq: IssueEnq, } impl Collect for InputVars { @@ -85,12 +154,12 @@ impl Collect for InputVars { } pub struct IssueEnq { - valid: SVar, - ready: SVar, - pc: VVar, - inst: VVar, - rs1: VVar, - rs2: VVar, + pub valid: SVar, + pub ready: SVar, + pub pc: VVar, + pub inst: VVar, + pub rs1: VVar, + pub rs2: VVar, } impl Collect for IssueEnq { diff --git a/profiler/src/main.rs b/profiler/src/main.rs index a7b3f53a1..89333ebd1 100644 --- a/profiler/src/main.rs +++ b/profiler/src/main.rs @@ -5,13 +5,18 @@ use std::{ path::PathBuf, }; -use anyhow::{anyhow, bail, ensure}; +use anyhow::{anyhow, ensure}; use clap::Parser; -use input_hier::{InputVars, VarCollector}; -use vcd::{IdCode, ScopeItem}; +use input_hier::InputVars; +use vcd::IdCode; +use vcd_util::{ + collect_vars, process_signal_map, process_vcd, time_to_cycle, ProcessReport, RawValueRecord, + Value, FIRST_CYCLE, RESET_DEASSERT_TIME, +}; mod input_hier; -use input_hier::Collect as _; + +mod vcd_util; #[derive(Parser)] struct Cli { @@ -20,6 +25,7 @@ struct Cli { fn main() -> anyhow::Result<()> { let cli = Cli::parse(); + env_logger::builder().format_timestamp(None).init(); let input = File::open(cli.input_vcd)?; @@ -34,131 +40,92 @@ fn main() -> anyhow::Result<()> { // Though `Scope` has `find_var` method, however it uses linear search. // Collect to a hashmap to avoid quadratic behavior. - let vars = collect_vars(&prof_scope); + let vars = collect_vars(prof_scope); let input_vars = InputVars::from_vars(&vars); let c = input_vars.collect(); - let mut signal_map = HashMap::>::new(); + let clock = vars["clock"]; + let reset = vars["reset"]; + let mut clock_values = vec![]; + let mut reset_values = vec![]; + assert_eq!(clock.1, None); + assert_eq!(reset.1, None); + + let mut signal_map = HashMap::, Vec)>::new(); for (code, width) in c.id_list() { match signal_map.entry(code) { - hash_map::Entry::Occupied(e) => assert_eq!(*e.get(), width), + hash_map::Entry::Occupied(e) => assert_eq!(e.get().0, width), hash_map::Entry::Vacant(e) => { - e.insert(width); + e.insert((width, vec![])); } } } - process(&mut input, |time, code, value| { - if signal_map.contains_key(&code) { - println!("[T={time}] {value:?}"); + let ProcessReport { max_time } = process_vcd(&mut input, |time, code, value| { + if code == clock.0 { + match value { + Value::Scalar(value) => clock_values.push(RawValueRecord { + time, + is_x: value.is_none(), + value: value.unwrap_or(false) as u32, + }), + _ => unreachable!(), + } } - })?; - - Ok(()) -} -fn collect_vars(scope: &vcd::Scope) -> HashMap)> { - let mut signals = HashMap::new(); - for item in &scope.items { - match item { - ScopeItem::Var(var) => { - let name = &var.reference; - let width = match var.index { - None => { - // scalar - assert_eq!(var.size, 1); - None - } - Some(index) => { - // vector - assert_eq!(index, vcd::ReferenceIndex::Range(var.size as i32 - 1, 0)); - Some(var.size) - } - }; - - signals.insert(name.into(), (var.code, width)); + if code == reset.0 { + match value { + Value::Scalar(value) => reset_values.push(RawValueRecord { + time, + is_x: value.is_none(), + value: value.unwrap_or(false) as u32, + }), + _ => unreachable!(), } - ScopeItem::Scope(_) => { - // ignore subscopes - } - _ => {} } - } - - signals -} - -#[derive(Debug)] -enum Value { - Scalar(Option), - Vector { width: u8, data: Option }, -} - -impl Value { - fn from_scalar(value: vcd::Value) -> Self { - let value = match value { - vcd::Value::V1 => Some(true), - vcd::Value::V0 => Some(false), - _ => None, - }; - Value::Scalar(value) - } - fn from_vector(value: vcd::Vector) -> Self { - let width = value.len().try_into().unwrap(); - assert!(1 <= width && width <= 32); - - let mut data: u32 = 0; - - // value[0] is MSB, value[width-1] is LSB - // here we reverse the bit order - for s in &value { - match s { - vcd::Value::V1 => { - data = (data << 1) + 1; + if let Some(v) = signal_map.get_mut(&code) { + match value { + Value::Scalar(value) => { + assert_eq!(v.0, None); + v.1.push(RawValueRecord { + time, + is_x: value.is_none(), + value: value.unwrap_or(false) as u32, + }); } - vcd::Value::V0 => { - data = data << 1; + Value::Vector { width, data } => { + assert_eq!(v.0, Some(width as u32)); + v.1.push(RawValueRecord { + time, + is_x: data.is_none(), + value: data.unwrap_or(0), + }); } - _ => return Value::Vector { width, data: None }, } } + })?; - Value::Vector { - width, - data: Some(data), + let max_cycle = time_to_cycle(max_time); + log::info!("first_cycle = {FIRST_CYCLE}"); + log::info!("max_cycle = {max_cycle}"); + + assert_eq!(reset_values.len(), 2); + assert_eq!( + reset_values[1], + RawValueRecord { + time: RESET_DEASSERT_TIME, + is_x: false, + value: 0, } - } -} + ); + + let signal_map = process_signal_map(signal_map); + + c.set_with_signal_map(&signal_map); + + // input_vars.issue_enq.valid.debug_print(); -fn process( - vcd: &mut vcd::Parser, - mut callback: Callback, -) -> anyhow::Result<()> -where - Callback: FnMut(u64, vcd::IdCode, Value), -{ - let mut time: u64 = 0; - while let Some(cmd) = vcd.next().transpose()? { - // println!("{cmd:?}"); - match cmd { - vcd::Command::Begin(_) | vcd::Command::End(_) => { - // ignored - } - vcd::Command::Timestamp(new_time) => { - time = new_time; - } - vcd::Command::ChangeScalar(id, value) => { - callback(time, id, Value::from_scalar(value)); - } - vcd::Command::ChangeVector(id, value) => { - callback(time, id, Value::from_vector(value)); - } - _ => { - unreachable!("unexpected command: {cmd:?}") - } - } - } Ok(()) } diff --git a/profiler/src/vcd_util.rs b/profiler/src/vcd_util.rs new file mode 100644 index 000000000..31a72259c --- /dev/null +++ b/profiler/src/vcd_util.rs @@ -0,0 +1,178 @@ +use std::{collections::HashMap, io::BufRead, rc::Rc}; + +use vcd::{IdCode, ScopeItem}; + +use crate::input_hier::{SignalData, ValueRecord}; + +pub fn time_to_cycle(time: u64) -> u32 { + let cycle = (time / 20000) as u32; + assert_eq!(time, 20000 * (cycle as u64) + 10000); + cycle +} + +// sync with t1rocketemu/TestBench +pub const RESET_DEASSERT_TIME: u64 = 100000; +pub const FIRST_CYCLE: u32 = 5; + +pub fn process_signal_map( + signal_map: HashMap, Vec)>, +) -> HashMap { + signal_map + .into_iter() + .map(|(code, (width, data))| { + let data = match width { + None => { + let mut data2 = vec![]; + for x in data { + if x.time >= RESET_DEASSERT_TIME { + let cycle = time_to_cycle(x.time); + data2.push(ValueRecord { + cycle, + is_x: x.is_x, + value: x.value != 0, + }); + } + } + SignalData::Scalar { + data: Rc::new(data2), + } + } + Some(width) => { + let mut data2 = vec![]; + for x in data { + if x.time >= RESET_DEASSERT_TIME { + let cycle = time_to_cycle(x.time); + data2.push(ValueRecord { + cycle, + is_x: x.is_x, + value: x.value as u64, + }); + } + } + SignalData::Vector { + width, + data: Rc::new(data2), + } + } + }; + (code, data) + }) + .collect() +} + +pub fn collect_vars(scope: &vcd::Scope) -> HashMap)> { + let mut signals = HashMap::new(); + for item in &scope.items { + match item { + ScopeItem::Var(var) => { + let name = &var.reference; + let width = match var.index { + None => { + // scalar + assert_eq!(var.size, 1); + None + } + Some(index) => { + // vector + assert_eq!(index, vcd::ReferenceIndex::Range(var.size as i32 - 1, 0)); + Some(var.size) + } + }; + + signals.insert(name.into(), (var.code, width)); + } + ScopeItem::Scope(_) => { + // ignore subscopes + } + _ => {} + } + } + + signals +} + +#[derive(Debug, PartialEq, Eq)] +pub struct RawValueRecord { + pub time: u64, + pub is_x: bool, + pub value: u32, +} + +#[derive(Debug)] +pub enum Value { + Scalar(Option), + Vector { width: u8, data: Option }, +} + +impl Value { + fn from_scalar(value: vcd::Value) -> Self { + let value = match value { + vcd::Value::V1 => Some(true), + vcd::Value::V0 => Some(false), + _ => None, + }; + Value::Scalar(value) + } + + fn from_vector(value: vcd::Vector) -> Self { + let width = value.len().try_into().unwrap(); + assert!(1 <= width && width <= 32); + + let mut data: u32 = 0; + + // value[0] is MSB, value[width-1] is LSB + // here we reverse the bit order + for s in &value { + match s { + vcd::Value::V1 => { + data = (data << 1) + 1; + } + vcd::Value::V0 => { + data = data << 1; + } + _ => return Value::Vector { width, data: None }, + } + } + + Value::Vector { + width, + data: Some(data), + } + } +} + +pub struct ProcessReport { + pub max_time: u64, +} + +// return max time +pub fn process_vcd( + vcd: &mut vcd::Parser, + mut callback: Callback, +) -> anyhow::Result +where + Callback: FnMut(u64, vcd::IdCode, Value), +{ + let mut time: u64 = 0; + while let Some(cmd) = vcd.next().transpose()? { + // println!("{cmd:?}"); + match cmd { + vcd::Command::Begin(_) | vcd::Command::End(_) => { + // ignored + } + vcd::Command::Timestamp(new_time) => { + time = new_time; + } + vcd::Command::ChangeScalar(id, value) => { + callback(time, id, Value::from_scalar(value)); + } + vcd::Command::ChangeVector(id, value) => { + callback(time, id, Value::from_vector(value)); + } + _ => { + unreachable!("unexpected command: {cmd:?}") + } + } + } + Ok(ProcessReport { max_time: time }) +}