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}