Skip to main content

libtrace2power/
stats.rs

1// Copyright (c) 2024-2026 Antmicro <www.antmicro.com>
2// SPDX-License-Identifier: Apache-2.0
3
4use itertools::izip;
5use rayon::prelude::*;
6use std::fmt::Debug;
7use wellen::{Signal, SignalRef, SignalValue, TimeTableIdx, simple::Waveform};
8
9#[derive(Debug, Clone)]
10pub struct SignalStats {
11    //pub name: String,
12    pub trans_count_doubled: u32,
13    pub clean_trans_count: u32,
14    pub glitch_trans_count: u32,
15    pub high_time: u32,
16    pub low_time: u32,
17    pub x_time: u32,
18    pub z_time: u32,
19}
20
21impl Default for SignalStats {
22    fn default() -> Self {
23        Self {
24            trans_count_doubled: 0,
25            clean_trans_count: 0,
26            glitch_trans_count: 0,
27            high_time: 0,
28            low_time: 0,
29            x_time: 0,
30            z_time: 0,
31        }
32    }
33}
34
35impl SignalStats {
36    fn modify_time_stat_of_value<'s, F>(&'s mut self, val: char, f: F)
37    where
38        F: FnOnce(u32) -> u32,
39    {
40        match val {
41            '1' => self.high_time = f(self.high_time),
42            '0' => self.low_time = f(self.low_time),
43            'x' => self.x_time = f(self.x_time),
44            'z' => self.z_time = f(self.z_time),
45            _ => panic!("Invalid value"),
46        }
47    }
48}
49
50impl SignalStats {
51    fn is_glitch<'s>(&'s mut self) -> bool {
52        self.clean_trans_count >= 2 || self.glitch_trans_count >= 2 || self.trans_count_doubled > 2
53    }
54}
55
56impl SignalStats {
57    fn clear<'s>(&'s mut self) {
58        self.trans_count_doubled = 0;
59        self.clean_trans_count = 0;
60        self.glitch_trans_count = 0;
61        self.high_time = 0;
62        self.low_time = 0;
63        self.x_time = 0;
64        self.z_time = 0;
65    }
66}
67
68fn val_at(ti: TimeTableIdx, sig: &Signal) -> SignalValue<'_> {
69    let offset = sig
70        .get_offset(ti)
71        .expect("Signal change timestamp should be valid");
72    return sig.get_value_at(&offset, 0);
73}
74
75fn time_value_at(wave: &Waveform, ti: TimeTableIdx) -> u64 {
76    let time_stamp = wave.time_table()[ti as usize];
77    return time_stamp;
78}
79
80pub fn calc_stats_for_each_time_span(
81    wave: &Waveform,
82    glitches_only: bool,
83    clk_signal: Option<SignalRef>,
84    sig_ref: SignalRef,
85    num_of_iterations: u64,
86) -> Vec<PackedStats> {
87    let mut stats = Vec::with_capacity(num_of_iterations as usize);
88    let time_span = (*wave
89        .time_table()
90        .last()
91        .expect("Waveform shouldn't be empty"))
92        / num_of_iterations;
93
94    stats.extend(
95        (0..num_of_iterations)
96            .into_par_iter()
97            .map(|index| {
98                let first_time_stamp = index * time_span;
99                let last_time_stamp = (index + 1) * time_span;
100                return calc_stats(
101                    wave,
102                    glitches_only,
103                    clk_signal,
104                    sig_ref,
105                    first_time_stamp,
106                    last_time_stamp,
107                );
108            })
109            .collect::<Vec<PackedStats>>(),
110    );
111
112    return stats;
113}
114
115pub fn calc_stats(
116    wave: &Waveform,
117    glitches_only: bool,
118    clk_signal: Option<SignalRef>,
119    sig_ref: SignalRef,
120    first_time_stamp: wellen::Time,
121    last_time_stamp: wellen::Time,
122) -> PackedStats {
123    let sig = wave.get_signal(sig_ref).unwrap();
124
125    let n = sig.time_indices().len();
126    if n == 0 {
127        return PackedStats::Vector(Vec::new());
128    }
129
130    let mut prev_val = val_at(
131        sig.get_first_time_idx()
132            .expect("Signal should have at least one value change"),
133        sig,
134    );
135
136    let bits = prev_val.bits();
137
138    // Check if bits are valid, otherwise value is a real number
139    let bit_len = if let Some(bit_len) = bits {
140        bit_len
141    } else {
142        // TODO: add function handling real numbers
143        return PackedStats::Vector(Vec::new());
144    };
145
146    let mut ss = Vec::<SignalStats>::with_capacity(bit_len as usize);
147    // TODO: Consider rev on range
148    for _ in 0..bit_len {
149        ss.push(Default::default())
150    }
151
152    let mut current_value_entry_index: usize = 1;
153
154    // Fast forward to relevant starting time stamp
155    while current_value_entry_index < sig.time_indices().len() {
156        let time_idx = sig.time_indices()[current_value_entry_index];
157
158        if time_value_at(wave, time_idx) > first_time_stamp {
159            break;
160        }
161
162        prev_val = val_at(time_idx, sig);
163
164        current_value_entry_index += 1;
165    }
166
167    // For high time calculations to start only from specified first time stamp
168    let mut prev_ts = first_time_stamp;
169
170    // Accumulate statistics over desired time span
171    while current_value_entry_index < sig.time_indices().len() {
172        let time_idx = sig.time_indices()[current_value_entry_index];
173        let val = val_at(time_idx, sig);
174        let ts = time_value_at(wave, time_idx);
175        current_value_entry_index += 1;
176
177        if ts > last_time_stamp {
178            break;
179        }
180
181        let val_str = val.to_bit_string().expect("Signal's value should be valid");
182        let prev_val_str = prev_val
183            .to_bit_string()
184            .expect("Signal's previous value should be valid");
185        for (c, prev_c, i) in izip!(val_str.chars(), prev_val_str.chars(), 0..) {
186            match (prev_c, c) {
187                ('0', '1') | ('1', '0') => {
188                    ss[i].clean_trans_count += 1;
189                    ss[i].trans_count_doubled += 2;
190                }
191                (other @ _, 'x') | ('x', other @ _) => {
192                    if other != 'x' {
193                        ss[i].trans_count_doubled += 1;
194                        ss[i].glitch_trans_count += 1;
195                    }
196                }
197                (other @ _, 'z') | ('z', other @ _) => {
198                    if other != 'z' {
199                        ss[i].trans_count_doubled += 1;
200                        if other == '0' {
201                            ss[i].clean_trans_count += 1;
202                        }
203                    }
204                }
205                _ => {
206                    if prev_c != c {
207                        panic!("Unknown transition {prev_c} -> {c}")
208                    }
209                }
210            }
211
212            ss[i].modify_time_stat_of_value(prev_c, |v| v + (ts - prev_ts) as u32);
213        }
214        prev_ts = ts;
215        prev_val = val;
216    }
217
218    for (prev_c, i) in izip!(
219        prev_val
220            .to_bit_string()
221            .expect("Signal's previous value should be valid")
222            .chars(),
223        0..
224    ) {
225        ss[i]
226            .modify_time_stat_of_value(prev_c, |v| v + (last_time_stamp - (prev_ts as u64)) as u32);
227    }
228
229    if glitches_only {
230        for stat in ss.iter_mut() {
231            if !stat.is_glitch() || (clk_signal != None && sig_ref == clk_signal.unwrap()) {
232                stat.clear();
233            }
234        }
235    }
236
237    // TODO: Figure out how the indexing direction is denoted
238    ss.reverse();
239
240    return if ss.len() == 1 {
241        PackedStats::OneBit(
242            ss.into_iter()
243                .next()
244                .expect("Signal's value should be valid"),
245        )
246    } else {
247        PackedStats::Vector(ss)
248    };
249}
250
251pub fn empty_stats(wave: &Waveform, sig_ref: SignalRef) -> PackedStats {
252    let sig = wave.get_signal(sig_ref).unwrap();
253
254    if sig.time_indices().len() == 0 {
255        return PackedStats::Vector(Vec::new());
256    }
257
258    let bits = val_at(
259        sig.get_first_time_idx()
260            .expect("Signal should have at least one value change"),
261        sig,
262    )
263    .bits();
264
265    // Check if bits are valid, otherwise value is a real number
266    let bit_len: usize = if let Some(bit_len) = bits {
267        bit_len as usize
268    } else {
269        // TODO: add function handling real numbers
270        return PackedStats::Vector(Vec::new());
271    };
272
273    let ss = vec![Default::default(); bit_len];
274
275    return if ss.len() == 1 {
276        PackedStats::OneBit(
277            ss.into_iter()
278                .next()
279                .expect("Signal's value should be valid"),
280        )
281    } else {
282        PackedStats::Vector(ss)
283    };
284}
285
286#[derive(Clone)]
287pub enum PackedStats {
288    OneBit(SignalStats),
289    Vector(Vec<SignalStats>),
290}