Skip to main content

vj_sim/sched/
violation.rs

1//! `unique`/`unique0`/`priority` violation reports (LRM 1800-2023 §12.4.2.1,
2//! §12.5.3.1).
3//!
4//! The check is made where the `if`/`case` executes, but a violation is not
5//! reported there: the report is held on the executing process's violation
6//! report queue and matures in the Observed region of the same time step, so a
7//! zero-delay glitch in the active region set never reports. The queue is
8//! cleared at a violation report flush point (an event control or `wait`
9//! resumption, or an `always_comb`/`always_latch` triggered again), and every
10//! execution of a checked statement reports independently
11//! ([`maturation::report_independent`]).
12//!
13//! The report's text is captured when the check fails (compiled code prints it
14//! with the ordinary severity framing between `sv_rt_deferred_begin` and
15//! `sv_rt_deferred_end`, which hands it here).
16
17use vj_abi::Region;
18
19use crate::sched::assert_control::{self, SiteId};
20use crate::sched::maturation::{self, ReportKind};
21
22/// Which qualifier's violation report is raised: the three `assertion_type`
23/// bits of Table 20-6 that name violation report types.
24#[derive(Clone, Copy, PartialEq, Eq, Debug)]
25pub enum ViolationType {
26    /// `unique if` / `unique case` (`assertion_type` 32).
27    Unique,
28    /// `unique0 if` / `unique0 case` (`assertion_type` 64).
29    Unique0,
30    /// `priority if` / `priority case` (`assertion_type` 128).
31    Priority,
32}
33
34/// `sv_rt_deferred_end`'s `mode` for an `assert #0` report (Reactive region).
35pub const DEFERRED_MODE_OBSERVED: u32 = 0;
36/// `sv_rt_deferred_end`'s `mode` for an `assert final` report (Postponed
37/// region).
38pub const DEFERRED_MODE_FINAL: u32 = 1;
39/// `sv_rt_deferred_end`'s `mode` for `disable <deferred assertion label>`
40/// (LRM 16.4.4): cancels the running process's pending reports of the control
41/// site instead of queueing one. (2..=4 are the violation modes.)
42pub const DEFERRED_MODE_CANCEL: u32 = 5;
43
44impl ViolationType {
45    /// The Table 20-6 `assertion_type` bit.
46    pub const fn assertion_type_bit(self) -> u32 {
47        match self {
48            ViolationType::Unique => 32,
49            ViolationType::Unique0 => 64,
50            ViolationType::Priority => 128,
51        }
52    }
53
54    /// The `sv_rt_deferred_end` `mode` that hands a report of this type to
55    /// [`violation_report`] (after [`DEFERRED_MODE_OBSERVED`] and
56    /// [`DEFERRED_MODE_FINAL`]).
57    pub const fn deferred_mode(self) -> u32 {
58        match self {
59            ViolationType::Unique => 2,
60            ViolationType::Unique0 => 3,
61            ViolationType::Priority => 4,
62        }
63    }
64
65    /// The type a `sv_rt_deferred_end` `mode` names, `None` for an
66    /// `assert #0`/`assert final` mode or a stray value.
67    pub const fn from_deferred_mode(mode: u32) -> Option<ViolationType> {
68        match mode {
69            2 => Some(ViolationType::Unique),
70            3 => Some(ViolationType::Unique0),
71            4 => Some(ViolationType::Priority),
72            _ => None,
73        }
74    }
75}
76
77/// Sets where matured violation reports are written (the run's output buffer).
78pub fn set_violation_writer(f: fn(&[u8])) {
79    maturation::set_writer(ReportKind::Violation, f);
80}
81
82/// Holds `text`, the violation report of a `ty` check the running process just
83/// made, until the Observed region of this time step.
84pub fn violation_report(ty: ViolationType, text: Vec<u8>) {
85    violation_report_for(ty, text, None);
86}
87
88/// [`violation_report`] of the checker registered as the control site
89/// `control` ([`crate::sched::assert_control`]): dropped when the site is not
90/// checking (`$assertoff`/`$assertkill`: "no new violation reports", LRM
91/// 20.11), else tagged with the site so that a later `$assertkill` can discard it
92/// while it is pending.
93pub fn violation_report_for(_ty: ViolationType, text: Vec<u8>, control: Option<SiteId>) {
94    if let Some(id) = control
95        && !assert_control::state(id).checking
96    {
97        return;
98    }
99    maturation::report_independent_for_site(ReportKind::Violation, Region::Observed, text, control);
100}
101
102/// Discards `proc`'s pending violation reports (it reached a violation report
103/// flush point, §12.4.2.1).
104pub(crate) fn flush_violation_reports(proc: crate::sched::ProcessId) {
105    maturation::flush_violations(proc);
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    /// The Table 20-6 bits and the `sv_rt_deferred_end` modes round-trip, and
113    /// the assert #0 / assert final modes and strays name no violation type.
114    #[test]
115    fn violation_types_round_trip_through_their_deferred_modes() {
116        let all = [
117            (ViolationType::Unique, 32),
118            (ViolationType::Unique0, 64),
119            (ViolationType::Priority, 128),
120        ];
121        for (ty, bit) in all {
122            assert_eq!(ty.assertion_type_bit(), bit);
123            assert_eq!(
124                ViolationType::from_deferred_mode(ty.deferred_mode()),
125                Some(ty)
126            );
127        }
128        assert_eq!(
129            ViolationType::from_deferred_mode(DEFERRED_MODE_OBSERVED),
130            None
131        );
132        assert_eq!(ViolationType::from_deferred_mode(DEFERRED_MODE_FINAL), None);
133        assert_eq!(ViolationType::from_deferred_mode(5), None);
134    }
135
136    /// LRM 20.11: while a violation site is off (`$assertoff`/`$assertkill`) no
137    /// report is made; `$asserton` of the violation types restores reporting, and
138    /// `$assertkill` discards a report still pending.
139    #[test]
140    fn a_report_of_a_site_that_is_off_is_dropped_and_kill_flushes_a_pending_one() {
141        use crate::sched::assert_control::{self as ac, Control, Op, SiteDesc};
142        ac::reset();
143        let id = ac::intern(SiteDesc {
144            assertion_type: ac::ASSERT_UNIQUE,
145            directive_type: 0,
146            scope: "top".into(),
147            label: String::new(),
148        });
149        let op = |control| Op {
150            control,
151            assertion_type: ac::ASSERT_ALL,
152            directive_type: ac::DIR_ALL,
153            levels: 0,
154            targets: Vec::new(),
155        };
156        let before = maturation::pending_len();
157        violation_report_for(ViolationType::Unique, b"a\n".to_vec(), Some(id));
158        assert_eq!(maturation::pending_len(), before + 1, "checking: reported");
159
160        for killed in ac::control(op(Control::Kill)) {
161            assert_eq!(killed, id);
162            maturation::flush_sites(&[killed]);
163        }
164        assert_eq!(maturation::pending_len(), before, "the pending one is gone");
165        violation_report_for(ViolationType::Unique, b"b\n".to_vec(), Some(id));
166        assert_eq!(maturation::pending_len(), before, "off: not reported");
167
168        ac::control(op(Control::On));
169        violation_report_for(ViolationType::Unique, b"c\n".to_vec(), Some(id));
170        assert_eq!(maturation::pending_len(), before + 1, "on again: reported");
171        // A report with no control site is never gated.
172        violation_report(ViolationType::Unique, b"d\n".to_vec());
173        assert_eq!(maturation::pending_len(), before + 2);
174    }
175}