Skip to main content

vj_sim/sva/
future.rs

1//! The global clocking FUTURE sampled value functions (LRM 1800-2023 16.9.4):
2//! `$future_gclk`, `$rising_gclk`, `$falling_gclk`, `$steady_gclk`,
3//! `$changing_gclk`, as a pure specification over sampled values.
4//!
5//! The past family (`$past_gclk` and friends) needs none of this: it is
6//! literally `$past(v,,,@$global_clock)`, `$rose(v,@$global_clock)`, ... and
7//! `sva::sampled` decodes it that way. The future family reads the sampled
8//! value at the global clock tick that FOLLOWS the time step it is evaluated
9//! in, so the value is not known until that tick; this module fixes what each
10//! function returns once it is, and when the consumer may act on it:
11//!
12//! * the value of the expression "at the next tick" is its sampled
13//!   (Preponed) value at the first global tick STRICTLY after the time step
14//!   ([`first_tick_after`]), the mirror of `$past`'s "strictly before";
15//! * `$rising_gclk`/`$falling_gclk` compare the LSB at that tick with the
16//!   expression's own sampled value now, as `$rose`/`$fell` compare now with
17//!   the previous tick; `$steady_gclk` is equality of the two, and
18//!   `$changing_gclk` its complement;
19//! * the evaluation attempt still ends at the last tick of the ASSERTION's
20//!   clock, as though the future values were known in advance (so
21//!   `disable iff` and `$assertcontrol` act only up to it), while its action
22//!   block, and the default `$error`, run at the first global tick strictly
23//!   after that ([`action_tick`]); a message that reports the attempt's end
24//!   time reports the attempt's, not the action's. `sched::maturation::
25//!   report_at_global_tick` is the scheduler half of the delay.
26//!
27//! Everything here is on a 4-state SCALAR (`b'0'`, `b'1'`, `b'x'`, `b'z'`),
28//! the shape the LRM's tables and examples use; a wider expression is the
29//! LSB for the edge functions and the whole value for equality, which the
30//! caller reduces to `(lsb, equal)` before it gets here.
31
32/// A simulation time, in the units of the global clock's tick list.
33pub type Tick = u64;
34
35/// The five future functions.
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum FutureFn {
38    Future,
39    Rising,
40    Falling,
41    Steady,
42    Changing,
43}
44
45/// The first element of `ticks` (ascending global clock tick times) strictly
46/// greater than `t`, or `None` when the clock does not tick again.
47pub fn first_tick_after(ticks: &[Tick], t: Tick) -> Option<Tick> {
48    ticks.iter().copied().find(|&g| g > t)
49}
50
51/// When the action block (or default `$error`) of an attempt that ended at
52/// assertion-clock tick `attempt_end` runs: the global clock tick that follows
53/// it (16.9.4). A tick at `attempt_end` itself does not count, which is why
54/// the LRM's `a3` fails at 80 -- itself a global tick -- and reports at 90.
55pub fn action_tick(global_ticks: &[Tick], attempt_end: Tick) -> Option<Tick> {
56    first_tick_after(global_ticks, attempt_end)
57}
58
59/// The function's value given the expression's sampled value now (`cur`) and
60/// at the next global tick (`next`), as `b'0'`/`b'1'` -- or, for
61/// [`FutureFn::Future`], the next value itself.
62pub fn eval(f: FutureFn, cur: u8, next: u8) -> u8 {
63    let truth = |b: bool| if b { b'1' } else { b'0' };
64    match f {
65        FutureFn::Future => next,
66        FutureFn::Rising => truth(next == b'1' && cur != b'1'),
67        FutureFn::Falling => truth(next == b'0' && cur != b'0'),
68        FutureFn::Steady => truth(cur == next),
69        FutureFn::Changing => truth(cur != next),
70    }
71}
72
73/// `samples[i]` is the sampled value at global tick `i`; the function at tick
74/// `i`, or `None` for the last tick, whose successor does not exist (the
75/// attempt cannot be evaluated: the simulation ends first).
76pub fn eval_at(f: FutureFn, samples: &[u8], i: usize) -> Option<u8> {
77    let next = *samples.get(i + 1)?;
78    Some(eval(f, samples[i], next))
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    /// The waveform of `sig` at the global ticks 0, 10, ..., 110 that Table
86    /// 16-2 and Figure 16-4 describe. The figure is a drawing, so it is
87    /// reconstructed from the table (its sampled values at 10, 30, 40, 50 and
88    /// 80 and the futures they imply, i.e. the value at 20, 60 and 90) and
89    /// from the text of Examples 1 and 3 (changes only on a falling clock, but
90    /// at 80). The remaining samples are chosen to keep to that.
91    const SIG: &[u8] = b"110000111000";
92    /// `clk` (a period of 40) at the same ticks: high at 0..=10, low 20..=30,
93    /// high 40..=50, ... falling between the ticks 10-20, 50-60 and 90-100.
94    const CLK: &[u8] = b"110011001100";
95    const TICKS: [Tick; 12] = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110];
96
97    fn at(time: Tick) -> usize {
98        TICKS
99            .iter()
100            .position(|&t| t == time)
101            .expect("a global tick")
102    }
103
104    fn row(time: Tick) -> [u8; 6] {
105        let i = at(time);
106        [
107            SIG[i],
108            eval_at(FutureFn::Future, SIG, i).unwrap(),
109            eval_at(FutureFn::Rising, SIG, i).unwrap(),
110            eval_at(FutureFn::Falling, SIG, i).unwrap(),
111            eval_at(FutureFn::Changing, SIG, i).unwrap(),
112            eval_at(FutureFn::Steady, SIG, i).unwrap(),
113        ]
114    }
115
116    /// Table 16-2, all five rows, columns `$sampled`, `$future_gclk`,
117    /// `$rising_gclk`, `$falling_gclk`, `$changing_gclk`, `$steady_gclk`.
118    #[test]
119    fn table_16_2_rows() {
120        assert_eq!(&row(10), b"100110");
121        assert_eq!(&row(30), b"000001");
122        assert_eq!(&row(40), b"000001");
123        assert_eq!(&row(50), b"011010");
124        assert_eq!(&row(80), b"100110");
125    }
126
127    /// The last tick has no successor: nothing is evaluated there.
128    #[test]
129    fn the_last_tick_has_no_future() {
130        assert_eq!(eval_at(FutureFn::Future, SIG, SIG.len() - 1), None);
131        assert_eq!(eval_at(FutureFn::Steady, SIG, SIG.len() - 1), None);
132        assert!(eval_at(FutureFn::Steady, SIG, SIG.len() - 2).is_some());
133    }
134
135    /// 4-state: an unknown on either side is neither a rise nor a fall, is
136    /// `steady` only when both are the same unknown (case equality), and
137    /// `changing` is always `steady`'s complement.
138    #[test]
139    fn four_state_edges_and_equality() {
140        for (cur, next, rising, falling, steady) in [
141            (b'0', b'1', b'1', b'0', b'0'),
142            (b'1', b'0', b'0', b'1', b'0'),
143            (b'x', b'1', b'1', b'0', b'0'),
144            (b'1', b'x', b'0', b'0', b'0'),
145            (b'x', b'0', b'0', b'1', b'0'),
146            (b'x', b'x', b'0', b'0', b'1'),
147            (b'z', b'x', b'0', b'0', b'0'),
148            (b'1', b'1', b'0', b'0', b'1'),
149        ] {
150            let case = format!("{} -> {}", cur as char, next as char);
151            assert_eq!(eval(FutureFn::Rising, cur, next), rising, "{case}");
152            assert_eq!(eval(FutureFn::Falling, cur, next), falling, "{case}");
153            assert_eq!(eval(FutureFn::Steady, cur, next), steady, "{case}");
154            assert_eq!(eval(FutureFn::Changing, cur, next), steady ^ 1, "{case}");
155            assert_eq!(eval(FutureFn::Future, cur, next), next, "{case}");
156        }
157    }
158
159    /// The tick lookup is strictly-after, and absent past the last tick.
160    #[test]
161    fn first_tick_after_is_strict() {
162        assert_eq!(first_tick_after(&TICKS, 80), Some(90));
163        assert_eq!(first_tick_after(&TICKS, 85), Some(90));
164        assert_eq!(first_tick_after(&TICKS, 79), Some(80));
165        assert_eq!(first_tick_after(&TICKS, 110), None);
166        assert_eq!(first_tick_after(&[], 0), None);
167    }
168
169    /// The ticks at which Example 1's
170    /// `a1: assert property (@$global_clock $changing_gclk(sig) |->
171    /// $falling_gclk(clk))` is violated, given `rst` sampled at each tick
172    /// (`disable iff (rst)`, Example 3, when `Some`).
173    fn violations(rst: Option<&[u8]>) -> Vec<Tick> {
174        (0..SIG.len())
175            .filter(|&i| rst.is_none_or(|r| r[i] != b'1'))
176            .filter(|&i| {
177                let (Some(ch), Some(fall)) = (
178                    eval_at(FutureFn::Changing, SIG, i),
179                    eval_at(FutureFn::Falling, CLK, i),
180                ) else {
181                    return false;
182                };
183                ch == b'1' && fall != b'1'
184            })
185            .map(|i| TICKS[i])
186            .collect()
187    }
188
189    /// Example 1: "The assertion a1 is violated at time 80 because
190    /// `$changing_gclk(sig)` is true and `$falling_gclk(clk)` is false" -- and
191    /// at no other tick -- and its `$error` is executed at time 90, while a
192    /// message that reports the attempt's failing time reports 80.
193    #[test]
194    fn example_1_fails_at_80_and_the_error_executes_at_90() {
195        assert_eq!(violations(None), [80]);
196        let end = violations(None)[0];
197        assert_eq!(action_tick(&TICKS, end), Some(90));
198        // The attempt interval is not stretched to the action's time.
199        assert_eq!(end, 80);
200    }
201
202    /// Example 3: `rst` is high between 82 and 84 and low elsewhere. The
203    /// sampled value at every global tick is low, so the attempt at 80 is not
204    /// disabled, and the later activity of `rst` before the action block at 90
205    /// does not cancel it.
206    #[test]
207    fn example_3_an_ended_attempt_is_not_disabled_by_a_later_rst() {
208        let rst_at = |t: Tick| u8::from((82..=84).contains(&t)) + b'0';
209        let sampled: Vec<u8> = TICKS.iter().map(|&t| rst_at(t)).collect();
210        assert!(sampled.iter().all(|&b| b == b'0'));
211        assert_eq!(violations(Some(&sampled)), [80]);
212        // rst active at the tick of the attempt does disable it.
213        let mut at_80 = sampled.clone();
214        at_80[at(80)] = b'1';
215        assert_eq!(violations(Some(&at_80)), Vec::<Tick>::new());
216    }
217
218    /// Example 4: `##1 $stable_gclk(sig)` and `$steady_gclk(sig)` are
219    /// equivalent; the `##1` is there because the plain `$stable_gclk(sig)`
220    /// at the first tick compares with the default sampled value. Each
221    /// attempt `i` of a4 looks at the past function of tick `i + 1`.
222    #[test]
223    fn example_4_stable_one_tick_later_is_steady() {
224        let stable = |i: usize| SIG[i] == if i > 0 { SIG[i - 1] } else { b'x' };
225        for i in 0..SIG.len() - 1 {
226            let a4 = stable(i + 1);
227            let a5 = eval_at(FutureFn::Steady, SIG, i) == Some(b'1');
228            assert_eq!(a4, a5, "attempt at tick {i}");
229        }
230        // Cycle 0: a plain `$stable_gclk(sig)` compares with the default
231        // sampled value of a `logic`, x, so it is false although the signal
232        // does not change at the first tick.
233        assert!(!stable(0));
234        assert_eq!(eval_at(FutureFn::Steady, SIG, 0), Some(b'1'));
235    }
236}