1#[derive(Debug, Clone)]
11pub enum SvaExpr {
12 Signal(String),
14 Const(u64, u32),
16 Rose(Box<SvaExpr>),
18 Fell(Box<SvaExpr>),
20 Past(Box<SvaExpr>, u32),
22 And(Box<SvaExpr>, Box<SvaExpr>),
24 Or(Box<SvaExpr>, Box<SvaExpr>),
26 Not(Box<SvaExpr>),
28 Eq(Box<SvaExpr>, Box<SvaExpr>),
30 Implication {
32 antecedent: Box<SvaExpr>,
33 consequent: Box<SvaExpr>,
34 overlapping: bool,
35 },
36 Delay {
38 body: Box<SvaExpr>,
39 min: u32,
40 max: Option<u32>,
41 },
42 Repetition {
44 body: Box<SvaExpr>,
45 min: u32,
46 max: Option<u32>, },
48 SEventually(Box<SvaExpr>),
50 SAlways(Box<SvaExpr>),
52 Stable(Box<SvaExpr>),
54 Changed(Box<SvaExpr>),
56 DisableIff {
58 condition: Box<SvaExpr>,
59 body: Box<SvaExpr>,
60 },
61 Nexttime(Box<SvaExpr>, u32),
63 IfElse {
65 condition: Box<SvaExpr>,
66 then_expr: Box<SvaExpr>,
67 else_expr: Box<SvaExpr>,
68 },
69 NotEq(Box<SvaExpr>, Box<SvaExpr>),
72 LessThan(Box<SvaExpr>, Box<SvaExpr>),
74 GreaterThan(Box<SvaExpr>, Box<SvaExpr>),
76 LessEqual(Box<SvaExpr>, Box<SvaExpr>),
78 GreaterEqual(Box<SvaExpr>, Box<SvaExpr>),
80 Ternary {
82 condition: Box<SvaExpr>,
83 then_expr: Box<SvaExpr>,
84 else_expr: Box<SvaExpr>,
85 },
86 Throughout {
88 signal: Box<SvaExpr>,
89 sequence: Box<SvaExpr>,
90 },
91 Within {
93 inner: Box<SvaExpr>,
94 outer: Box<SvaExpr>,
95 },
96 FirstMatch(Box<SvaExpr>),
98 Intersect {
100 left: Box<SvaExpr>,
101 right: Box<SvaExpr>,
102 },
103 OneHot0(Box<SvaExpr>),
106 OneHot(Box<SvaExpr>),
108 CountOnes(Box<SvaExpr>),
110 IsUnknown(Box<SvaExpr>),
112 Sampled(Box<SvaExpr>),
114 Bits(Box<SvaExpr>),
116 Clog2(Box<SvaExpr>),
118 CountBits(Box<SvaExpr>, Vec<char>),
121 IsUnbounded(Box<SvaExpr>),
123 GotoRepetition {
126 body: Box<SvaExpr>,
127 count: u32,
128 },
129 NonConsecRepetition {
131 body: Box<SvaExpr>,
132 min: u32,
133 max: Option<u32>,
134 },
135 AcceptOn {
138 condition: Box<SvaExpr>,
139 body: Box<SvaExpr>,
140 },
141 RejectOn {
143 condition: Box<SvaExpr>,
144 body: Box<SvaExpr>,
145 },
146 PropertyNot(Box<SvaExpr>),
151 PropertyImplies(Box<SvaExpr>, Box<SvaExpr>),
154 PropertyIff(Box<SvaExpr>, Box<SvaExpr>),
157 Always(Box<SvaExpr>),
161 AlwaysBounded { body: Box<SvaExpr>, min: u32, max: Option<u32> },
164 SAlwaysBounded { body: Box<SvaExpr>, min: u32, max: u32 },
167 EventuallyBounded { body: Box<SvaExpr>, min: u32, max: u32 },
170 SEventuallyBounded { body: Box<SvaExpr>, min: u32, max: Option<u32> },
173 Until { lhs: Box<SvaExpr>, rhs: Box<SvaExpr>, strong: bool, inclusive: bool },
179 Strong(Box<SvaExpr>),
182 Weak(Box<SvaExpr>),
184 SNexttime(Box<SvaExpr>, u32),
186 FollowedBy { antecedent: Box<SvaExpr>, consequent: Box<SvaExpr>, overlapping: bool },
188 PropertyCase { expression: Box<SvaExpr>, items: Vec<(Vec<SvaExpr>, Box<SvaExpr>)>, default: Option<Box<SvaExpr>> },
190 SyncAcceptOn { condition: Box<SvaExpr>, body: Box<SvaExpr> },
192 SyncRejectOn { condition: Box<SvaExpr>, body: Box<SvaExpr> },
194 SequenceAnd(Box<SvaExpr>, Box<SvaExpr>),
197 SequenceOr(Box<SvaExpr>, Box<SvaExpr>),
199 ImmediateAssert {
202 expression: Box<SvaExpr>,
203 deferred: Option<ImmediateDeferred>,
204 },
205 FieldAccess { signal: Box<SvaExpr>, field: String },
208 EnumLiteral { type_name: Option<String>, value: String },
210 Triggered(String),
213 Matched(String),
215 BitAnd(Box<SvaExpr>, Box<SvaExpr>),
217 BitOr(Box<SvaExpr>, Box<SvaExpr>),
218 BitXor(Box<SvaExpr>, Box<SvaExpr>),
219 BitNot(Box<SvaExpr>),
220 ReductionAnd(Box<SvaExpr>),
221 ReductionOr(Box<SvaExpr>),
222 ReductionXor(Box<SvaExpr>),
223 BitSelect { signal: Box<SvaExpr>, index: Box<SvaExpr> },
224 PartSelect { signal: Box<SvaExpr>, high: u32, low: u32 },
225 Concat(Vec<SvaExpr>),
226 SequenceAction {
230 expression: Box<SvaExpr>,
231 assignments: Vec<(String, Box<SvaExpr>)>,
232 },
233 LocalVar(String),
235 ConstCast(Box<SvaExpr>),
238 Clocked { clock: String, edge: ClockEdge, body: Box<SvaExpr> },
241 ArrayMap { array: Box<SvaExpr>, iterator: String, with_expr: Box<SvaExpr> },
244 TypeThis,
246 RealConst(f64),
248}
249
250#[derive(Debug, Clone, PartialEq)]
252pub enum ClockEdge {
253 Posedge,
254 Negedge,
255 Edge, }
257
258#[derive(Debug, Clone, PartialEq)]
260pub enum ImmediateDeferred {
261 Observed,
263 Final,
265}
266
267#[derive(Debug, Clone, PartialEq)]
269pub enum SvaDirectiveKind {
270 Assert,
271 Assume,
272 Cover,
273 CoverSequence,
274 Restrict,
275}
276
277#[derive(Debug, Clone)]
279pub struct SvaDirective {
280 pub kind: SvaDirectiveKind,
281 pub property: SvaExpr,
282 pub label: Option<String>,
283 pub clock: Option<String>,
284 pub disable_iff: Option<SvaExpr>,
285 pub action_pass: Option<String>,
286 pub action_fail: Option<String>,
287}
288
289#[derive(Debug, Clone, PartialEq)]
291pub enum SvaPortType {
292 Untyped,
293 Bit,
294 Sequence,
295 Property,
296}
297
298#[derive(Debug, Clone)]
300pub struct SvaPort {
301 pub name: String,
302 pub port_type: SvaPortType,
303 pub default: Option<SvaExpr>,
304}
305
306#[derive(Debug, Clone)]
308pub struct SequenceDecl {
309 pub name: String,
310 pub ports: Vec<SvaPort>,
311 pub body: SvaExpr,
312}
313
314#[derive(Debug, Clone)]
316pub struct PropertyDecl {
317 pub name: String,
318 pub ports: Vec<SvaPort>,
319 pub body: SvaExpr,
320}
321
322#[derive(Debug, Clone)]
324pub struct LetDecl {
325 pub name: String,
326 pub ports: Vec<SvaPort>,
327 pub body: SvaExpr,
328}
329
330#[derive(Debug, Clone, PartialEq)]
332pub enum DistKind {
333 PerValue,
335 PerRange,
337}
338
339#[derive(Debug, Clone)]
341pub struct DistItem {
342 pub min: u64,
343 pub max: Option<u64>,
344 pub weight: u64,
345 pub kind: DistKind,
346}
347
348#[derive(Debug, Clone)]
350pub struct CheckerDecl {
351 pub name: String,
352 pub ports: Vec<SvaPort>,
353 pub rand_vars: Vec<RandVar>,
354 pub assertions: Vec<SvaDirective>,
355}
356
357#[derive(Debug, Clone, PartialEq, Eq)]
359pub enum RandVarType {
360 BitVec(u32),
362 Real,
364}
365
366#[derive(Debug, Clone)]
368pub struct RandVar {
369 pub name: String,
370 pub var_type: RandVarType,
371 pub is_const: bool,
372}
373
374pub fn resolve_sequence_instance(
376 decls: &[SequenceDecl],
377 name: &str,
378 args: &[SvaExpr],
379) -> Result<SvaExpr, SvaParseError> {
380 let decl = decls.iter().find(|d| d.name == name).ok_or_else(|| SvaParseError {
381 message: format!("undeclared sequence: '{}'", name),
382 })?;
383
384 let expected = decl.ports.len();
385 let provided = args.len();
386 let min_args = decl.ports.iter().filter(|p| p.default.is_none()).count();
388 if provided < min_args || provided > expected {
389 return Err(SvaParseError {
390 message: format!(
391 "sequence '{}' expects {}-{} arguments, got {}",
392 name, min_args, expected, provided
393 ),
394 });
395 }
396
397 let mut result = decl.body.clone();
398 for (i, port) in decl.ports.iter().enumerate() {
399 let actual = if i < args.len() {
400 args[i].clone()
401 } else if let Some(ref default) = port.default {
402 default.clone()
403 } else {
404 return Err(SvaParseError {
405 message: format!("missing argument for port '{}' in sequence '{}'", port.name, name),
406 });
407 };
408 result = substitute_signal(&result, &port.name, &actual);
409 }
410 Ok(result)
411}
412
413fn substitute_signal(expr: &SvaExpr, name: &str, replacement: &SvaExpr) -> SvaExpr {
416 let sub = |e: &SvaExpr| Box::new(substitute_signal(e, name, replacement));
417 let sub_vec = |v: &[SvaExpr]| v.iter().map(|e| substitute_signal(e, name, replacement)).collect::<Vec<_>>();
418
419 match expr {
420 SvaExpr::Signal(s) if s == name => replacement.clone(),
422 SvaExpr::Signal(_) | SvaExpr::Const(_, _) => expr.clone(),
423 SvaExpr::LocalVar(s) if s == name => replacement.clone(),
424 SvaExpr::LocalVar(_) => expr.clone(),
425 SvaExpr::Triggered(s) => SvaExpr::Triggered(s.clone()),
426 SvaExpr::Matched(s) => SvaExpr::Matched(s.clone()),
427 SvaExpr::EnumLiteral { type_name, value } => SvaExpr::EnumLiteral {
428 type_name: type_name.clone(), value: value.clone(),
429 },
430
431 SvaExpr::Rose(inner) => SvaExpr::Rose(sub(inner)),
433 SvaExpr::Fell(inner) => SvaExpr::Fell(sub(inner)),
434 SvaExpr::Not(inner) => SvaExpr::Not(sub(inner)),
435 SvaExpr::Stable(inner) => SvaExpr::Stable(sub(inner)),
436 SvaExpr::Changed(inner) => SvaExpr::Changed(sub(inner)),
437 SvaExpr::SEventually(inner) => SvaExpr::SEventually(sub(inner)),
438 SvaExpr::SAlways(inner) => SvaExpr::SAlways(sub(inner)),
439 SvaExpr::Always(inner) => SvaExpr::Always(sub(inner)),
440 SvaExpr::FirstMatch(inner) => SvaExpr::FirstMatch(sub(inner)),
441 SvaExpr::Strong(inner) => SvaExpr::Strong(sub(inner)),
442 SvaExpr::Weak(inner) => SvaExpr::Weak(sub(inner)),
443 SvaExpr::PropertyNot(inner) => SvaExpr::PropertyNot(sub(inner)),
444 SvaExpr::OneHot0(inner) => SvaExpr::OneHot0(sub(inner)),
445 SvaExpr::OneHot(inner) => SvaExpr::OneHot(sub(inner)),
446 SvaExpr::CountOnes(inner) => SvaExpr::CountOnes(sub(inner)),
447 SvaExpr::IsUnknown(inner) => SvaExpr::IsUnknown(sub(inner)),
448 SvaExpr::Sampled(inner) => SvaExpr::Sampled(sub(inner)),
449 SvaExpr::Bits(inner) => SvaExpr::Bits(sub(inner)),
450 SvaExpr::Clog2(inner) => SvaExpr::Clog2(sub(inner)),
451 SvaExpr::IsUnbounded(inner) => SvaExpr::IsUnbounded(sub(inner)),
452 SvaExpr::BitNot(inner) => SvaExpr::BitNot(sub(inner)),
453 SvaExpr::ReductionAnd(inner) => SvaExpr::ReductionAnd(sub(inner)),
454 SvaExpr::ReductionOr(inner) => SvaExpr::ReductionOr(sub(inner)),
455 SvaExpr::ReductionXor(inner) => SvaExpr::ReductionXor(sub(inner)),
456 SvaExpr::ConstCast(inner) => SvaExpr::ConstCast(sub(inner)),
457
458 SvaExpr::Past(inner, n) => SvaExpr::Past(sub(inner), *n),
460 SvaExpr::Nexttime(inner, n) => SvaExpr::Nexttime(sub(inner), *n),
461 SvaExpr::SNexttime(inner, n) => SvaExpr::SNexttime(sub(inner), *n),
462 SvaExpr::CountBits(inner, chars) => SvaExpr::CountBits(sub(inner), chars.clone()),
463 SvaExpr::GotoRepetition { body, count } => SvaExpr::GotoRepetition {
464 body: sub(body), count: *count,
465 },
466
467 SvaExpr::And(l, r) => SvaExpr::And(sub(l), sub(r)),
469 SvaExpr::Or(l, r) => SvaExpr::Or(sub(l), sub(r)),
470 SvaExpr::Eq(l, r) => SvaExpr::Eq(sub(l), sub(r)),
471 SvaExpr::NotEq(l, r) => SvaExpr::NotEq(sub(l), sub(r)),
472 SvaExpr::LessThan(l, r) => SvaExpr::LessThan(sub(l), sub(r)),
473 SvaExpr::GreaterThan(l, r) => SvaExpr::GreaterThan(sub(l), sub(r)),
474 SvaExpr::LessEqual(l, r) => SvaExpr::LessEqual(sub(l), sub(r)),
475 SvaExpr::GreaterEqual(l, r) => SvaExpr::GreaterEqual(sub(l), sub(r)),
476 SvaExpr::PropertyImplies(l, r) => SvaExpr::PropertyImplies(sub(l), sub(r)),
477 SvaExpr::PropertyIff(l, r) => SvaExpr::PropertyIff(sub(l), sub(r)),
478 SvaExpr::SequenceAnd(l, r) => SvaExpr::SequenceAnd(sub(l), sub(r)),
479 SvaExpr::SequenceOr(l, r) => SvaExpr::SequenceOr(sub(l), sub(r)),
480 SvaExpr::BitAnd(l, r) => SvaExpr::BitAnd(sub(l), sub(r)),
481 SvaExpr::BitOr(l, r) => SvaExpr::BitOr(sub(l), sub(r)),
482 SvaExpr::BitXor(l, r) => SvaExpr::BitXor(sub(l), sub(r)),
483
484 SvaExpr::Implication { antecedent, consequent, overlapping } => SvaExpr::Implication {
486 antecedent: sub(antecedent), consequent: sub(consequent), overlapping: *overlapping,
487 },
488 SvaExpr::Delay { body, min, max } => SvaExpr::Delay {
489 body: sub(body), min: *min, max: *max,
490 },
491 SvaExpr::Repetition { body, min, max } => SvaExpr::Repetition {
492 body: sub(body), min: *min, max: *max,
493 },
494 SvaExpr::NonConsecRepetition { body, min, max } => SvaExpr::NonConsecRepetition {
495 body: sub(body), min: *min, max: *max,
496 },
497 SvaExpr::DisableIff { condition, body } => SvaExpr::DisableIff {
498 condition: sub(condition), body: sub(body),
499 },
500 SvaExpr::IfElse { condition, then_expr, else_expr } => SvaExpr::IfElse {
501 condition: sub(condition), then_expr: sub(then_expr), else_expr: sub(else_expr),
502 },
503 SvaExpr::Ternary { condition, then_expr, else_expr } => SvaExpr::Ternary {
504 condition: sub(condition), then_expr: sub(then_expr), else_expr: sub(else_expr),
505 },
506 SvaExpr::Throughout { signal, sequence } => SvaExpr::Throughout {
507 signal: sub(signal), sequence: sub(sequence),
508 },
509 SvaExpr::Within { inner, outer } => SvaExpr::Within {
510 inner: sub(inner), outer: sub(outer),
511 },
512 SvaExpr::Intersect { left, right } => SvaExpr::Intersect {
513 left: sub(left), right: sub(right),
514 },
515 SvaExpr::AcceptOn { condition, body } => SvaExpr::AcceptOn {
516 condition: sub(condition), body: sub(body),
517 },
518 SvaExpr::RejectOn { condition, body } => SvaExpr::RejectOn {
519 condition: sub(condition), body: sub(body),
520 },
521 SvaExpr::SyncAcceptOn { condition, body } => SvaExpr::SyncAcceptOn {
522 condition: sub(condition), body: sub(body),
523 },
524 SvaExpr::SyncRejectOn { condition, body } => SvaExpr::SyncRejectOn {
525 condition: sub(condition), body: sub(body),
526 },
527 SvaExpr::FollowedBy { antecedent, consequent, overlapping } => SvaExpr::FollowedBy {
528 antecedent: sub(antecedent), consequent: sub(consequent), overlapping: *overlapping,
529 },
530 SvaExpr::Until { lhs, rhs, strong, inclusive } => SvaExpr::Until {
531 lhs: sub(lhs), rhs: sub(rhs), strong: *strong, inclusive: *inclusive,
532 },
533 SvaExpr::AlwaysBounded { body, min, max } => SvaExpr::AlwaysBounded {
534 body: sub(body), min: *min, max: *max,
535 },
536 SvaExpr::SAlwaysBounded { body, min, max } => SvaExpr::SAlwaysBounded {
537 body: sub(body), min: *min, max: *max,
538 },
539 SvaExpr::EventuallyBounded { body, min, max } => SvaExpr::EventuallyBounded {
540 body: sub(body), min: *min, max: *max,
541 },
542 SvaExpr::SEventuallyBounded { body, min, max } => SvaExpr::SEventuallyBounded {
543 body: sub(body), min: *min, max: *max,
544 },
545 SvaExpr::FieldAccess { signal, field } => SvaExpr::FieldAccess {
546 signal: sub(signal), field: field.clone(),
547 },
548 SvaExpr::BitSelect { signal, index } => SvaExpr::BitSelect {
549 signal: sub(signal), index: sub(index),
550 },
551 SvaExpr::PartSelect { signal, high, low } => SvaExpr::PartSelect {
552 signal: sub(signal), high: *high, low: *low,
553 },
554 SvaExpr::ImmediateAssert { expression, deferred } => SvaExpr::ImmediateAssert {
555 expression: sub(expression), deferred: deferred.clone(),
556 },
557
558 SvaExpr::Concat(items) => SvaExpr::Concat(sub_vec(items)),
560
561 SvaExpr::PropertyCase { expression, items, default } => SvaExpr::PropertyCase {
563 expression: sub(expression),
564 items: items.iter().map(|(vals, prop)| {
565 (sub_vec(vals), sub(prop))
566 }).collect(),
567 default: default.as_ref().map(|d| sub(d)),
568 },
569 SvaExpr::SequenceAction { expression, assignments } => SvaExpr::SequenceAction {
570 expression: sub(expression),
571 assignments: assignments.iter().map(|(var_name, rhs)| {
572 (var_name.clone(), sub(rhs))
573 }).collect(),
574 },
575 SvaExpr::Clocked { clock, edge, body } => SvaExpr::Clocked {
576 clock: clock.clone(),
577 edge: edge.clone(),
578 body: sub(body),
579 },
580 SvaExpr::ArrayMap { array, iterator, with_expr } => SvaExpr::ArrayMap {
581 array: sub(array),
582 iterator: iterator.clone(),
583 with_expr: sub(with_expr),
584 },
585 SvaExpr::TypeThis => SvaExpr::TypeThis,
586 SvaExpr::RealConst(v) => SvaExpr::RealConst(*v),
587 }
588}
589
590#[derive(Debug)]
592pub struct SvaParseError {
593 pub message: String,
594}
595
596impl std::fmt::Display for SvaParseError {
597 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
598 write!(f, "SVA parse error: {}", self.message)
599 }
600}
601
602pub fn parse_sva_directive(input: &str) -> Result<SvaDirective, SvaParseError> {
609 let input = input.trim().trim_end_matches(';');
610 let input = input.trim();
611
612 let (label, rest) = if let Some(colon_pos) = input.find(':') {
614 let potential_label = input[..colon_pos].trim();
615 if !potential_label.is_empty()
617 && potential_label.chars().all(|c| c.is_alphanumeric() || c == '_')
618 && !potential_label.starts_with("assert")
619 && !potential_label.starts_with("assume")
620 && !potential_label.starts_with("cover")
621 && !potential_label.starts_with("restrict")
622 {
623 (Some(potential_label.to_string()), input[colon_pos + 1..].trim())
624 } else {
625 (None, input)
626 }
627 } else {
628 (None, input)
629 };
630
631 let (kind, after_kind) = if rest.starts_with("assert property") {
633 (SvaDirectiveKind::Assert, rest["assert property".len()..].trim())
634 } else if rest.starts_with("assume property") {
635 (SvaDirectiveKind::Assume, rest["assume property".len()..].trim())
636 } else if rest.starts_with("cover sequence") {
637 (SvaDirectiveKind::CoverSequence, rest["cover sequence".len()..].trim())
638 } else if rest.starts_with("cover property") {
639 (SvaDirectiveKind::Cover, rest["cover property".len()..].trim())
640 } else if rest.starts_with("restrict property") {
641 (SvaDirectiveKind::Restrict, rest["restrict property".len()..].trim())
642 } else {
643 return Err(SvaParseError {
644 message: format!("expected assertion directive, got: '{}'", rest),
645 });
646 };
647
648 if !after_kind.starts_with('(') {
650 return Err(SvaParseError {
651 message: format!("expected '(' after directive keyword, got: '{}'", after_kind),
652 });
653 }
654 let close = find_balanced_close(after_kind, 0).ok_or_else(|| SvaParseError {
655 message: "unbalanced parentheses in directive".to_string(),
656 })?;
657 let prop_str = &after_kind[1..close];
658 let after_prop = after_kind[close + 1..].trim();
659
660 let (clock, prop_rest) = if prop_str.trim().starts_with("@(") {
662 if let Some(clock_close) = prop_str.find(')') {
663 let clock_str = prop_str[2..clock_close].trim().to_string();
664 (Some(clock_str), prop_str[clock_close + 1..].trim())
665 } else {
666 (None, prop_str.trim())
667 }
668 } else {
669 (None, prop_str.trim())
670 };
671
672 let (disable_iff, prop_body) = if prop_rest.starts_with("disable iff") {
674 let after_disable = prop_rest["disable iff".len()..].trim();
675 if after_disable.starts_with('(') {
676 if let Some(di_close) = find_balanced_close(after_disable, 0) {
677 let di_str = &after_disable[1..di_close];
678 let body = after_disable[di_close + 1..].trim();
679 (Some(parse_sva(di_str)?), body)
680 } else {
681 (None, prop_rest)
682 }
683 } else {
684 (None, prop_rest)
685 }
686 } else {
687 (None, prop_rest)
688 };
689
690 let property = parse_sva(prop_body)?;
691
692 let mut action_pass = None;
694 let mut action_fail = None;
695 let remaining = after_prop;
696 if !remaining.is_empty() {
697 if let Some(else_pos) = find_else_outside_strings(remaining) {
700 let pass_part = remaining[..else_pos].trim();
701 let fail_part = remaining[else_pos + 4..].trim();
702 if !pass_part.is_empty() {
703 action_pass = Some(pass_part.trim_end_matches(';').trim().to_string());
704 }
705 if !fail_part.is_empty() {
706 action_fail = Some(fail_part.trim_end_matches(';').trim().to_string());
707 }
708 } else if remaining.starts_with("$") || remaining.starts_with("else") {
709 let part = remaining.trim_end_matches(';').trim();
710 if remaining.starts_with("else") {
711 action_fail = Some(part["else".len()..].trim().to_string());
712 } else {
713 action_pass = Some(part.to_string());
714 }
715 }
716 }
717
718 if kind == SvaDirectiveKind::Restrict && (action_pass.is_some() || action_fail.is_some()) {
720 return Err(SvaParseError {
721 message: "restrict property cannot have action blocks".to_string(),
722 });
723 }
724
725 Ok(SvaDirective {
726 kind,
727 property,
728 label,
729 clock,
730 disable_iff,
731 action_pass,
732 action_fail,
733 })
734}
735
736pub fn parse_sva(input: &str) -> Result<SvaExpr, SvaParseError> {
741 let input = input.trim();
742
743 if input.starts_with("@(") {
746 if let Some(pos) = input.find(')') {
747 let clock_spec = input[2..pos].trim();
748 let rest = input[pos + 1..].trim();
749
750 let (edge, clock_name) = if clock_spec.starts_with("posedge ") {
751 (ClockEdge::Posedge, clock_spec[8..].trim().to_string())
752 } else if clock_spec.starts_with("negedge ") {
753 (ClockEdge::Negedge, clock_spec[8..].trim().to_string())
754 } else if clock_spec.starts_with("edge ") {
755 (ClockEdge::Edge, clock_spec[5..].trim().to_string())
756 } else {
757 (ClockEdge::Posedge, clock_spec.to_string())
759 };
760
761 let body = parse_toplevel(rest)?;
762 return Ok(SvaExpr::Clocked {
763 clock: clock_name,
764 edge,
765 body: Box::new(body),
766 });
767 }
768 }
769
770 parse_toplevel(input)
771}
772
773fn parse_toplevel(input: &str) -> Result<SvaExpr, SvaParseError> {
774 let input = input.trim();
775
776 if input.starts_with("assert ") || input.starts_with("assert(") || input.starts_with("assert#") {
778 let rest = input["assert".len()..].trim();
779 if rest.starts_with("#0") {
780 let body = rest[2..].trim();
781 if body.starts_with('(') {
782 if let Some(close) = find_balanced_close(body, 0) {
783 let inner = &body[1..close];
784 return Ok(SvaExpr::ImmediateAssert {
785 expression: Box::new(parse_implication(inner)?),
786 deferred: Some(ImmediateDeferred::Observed),
787 });
788 }
789 }
790 } else if rest.starts_with("final") {
791 let body = rest[5..].trim();
792 if body.starts_with('(') {
793 if let Some(close) = find_balanced_close(body, 0) {
794 let inner = &body[1..close];
795 return Ok(SvaExpr::ImmediateAssert {
796 expression: Box::new(parse_implication(inner)?),
797 deferred: Some(ImmediateDeferred::Final),
798 });
799 }
800 }
801 } else if rest.starts_with('(') {
802 if let Some(close) = find_balanced_close(rest, 0) {
803 let inner = &rest[1..close];
804 return Ok(SvaExpr::ImmediateAssert {
805 expression: Box::new(parse_implication(inner)?),
806 deferred: None,
807 });
808 }
809 }
810 }
811
812 if input.starts_with("sync_accept_on") {
814 let rest = input["sync_accept_on".len()..].trim();
815 if rest.starts_with('(') {
816 if let Some(close) = find_balanced_close(rest, 0) {
817 let cond = &rest[1..close];
818 let body = rest[close + 1..].trim();
819 return Ok(SvaExpr::SyncAcceptOn {
820 condition: Box::new(parse_implication(cond)?),
821 body: Box::new(parse_toplevel(body)?),
822 });
823 }
824 }
825 }
826
827 if input.starts_with("sync_reject_on") {
829 let rest = input["sync_reject_on".len()..].trim();
830 if rest.starts_with('(') {
831 if let Some(close) = find_balanced_close(rest, 0) {
832 let cond = &rest[1..close];
833 let body = rest[close + 1..].trim();
834 return Ok(SvaExpr::SyncRejectOn {
835 condition: Box::new(parse_implication(cond)?),
836 body: Box::new(parse_toplevel(body)?),
837 });
838 }
839 }
840 }
841
842 if input.starts_with("accept_on") {
844 let rest = input["accept_on".len()..].trim();
845 if rest.starts_with('(') {
846 if let Some(close) = find_balanced_close(rest, 0) {
847 let cond = &rest[1..close];
848 let body = rest[close + 1..].trim();
849 return Ok(SvaExpr::AcceptOn {
850 condition: Box::new(parse_implication(cond)?),
851 body: Box::new(parse_toplevel(body)?),
852 });
853 }
854 }
855 }
856
857 if input.starts_with("reject_on") {
859 let rest = input["reject_on".len()..].trim();
860 if rest.starts_with('(') {
861 if let Some(close) = find_balanced_close(rest, 0) {
862 let cond = &rest[1..close];
863 let body = rest[close + 1..].trim();
864 return Ok(SvaExpr::RejectOn {
865 condition: Box::new(parse_implication(cond)?),
866 body: Box::new(parse_toplevel(body)?),
867 });
868 }
869 }
870 }
871
872 if input.starts_with("disable iff") {
874 let rest = input["disable iff".len()..].trim();
875 if rest.starts_with('(') {
876 if let Some(close) = find_balanced_close(rest, 0) {
877 let cond = &rest[1..close];
878 let body = rest[close + 1..].trim();
879 return Ok(SvaExpr::DisableIff {
880 condition: Box::new(parse_implication(cond)?),
881 body: Box::new(parse_implication(body)?),
882 });
883 }
884 }
885 }
886
887 if input.starts_with("if ") || input.starts_with("if(") {
889 let rest = input[2..].trim();
890 if rest.starts_with('(') {
891 if let Some(close) = find_balanced_close(rest, 0) {
892 let cond = &rest[1..close];
893 let after_cond = rest[close + 1..].trim();
894 if let Some(else_pos) = find_else_keyword(after_cond) {
895 let then_part = after_cond[..else_pos].trim();
896 let else_part = after_cond[else_pos + 4..].trim();
897 return Ok(SvaExpr::IfElse {
898 condition: Box::new(parse_implication(cond)?),
899 then_expr: Box::new(parse_implication(then_part)?),
900 else_expr: Box::new(parse_implication(else_part)?),
901 });
902 } else {
903 return Ok(SvaExpr::IfElse {
904 condition: Box::new(parse_implication(cond)?),
905 then_expr: Box::new(parse_implication(after_cond)?),
906 else_expr: Box::new(SvaExpr::Signal("1".to_string())),
907 });
908 }
909 }
910 }
911 }
912
913 parse_implication(input)
914}
915
916fn parse_implication(input: &str) -> Result<SvaExpr, SvaParseError> {
917 let mut depth = 0i32;
920 let chars: Vec<char> = input.chars().collect();
921 for i in 0..chars.len().saturating_sub(2) {
922 match chars[i] {
923 '(' => depth += 1,
924 ')' => depth -= 1,
925 '|' if depth == 0 => {
926 if i + 2 < chars.len() && chars[i + 1] == '-' && chars[i + 2] == '>' {
927 let lhs = input[..i].trim();
928 let rhs = input[i + 3..].trim();
929 return Ok(SvaExpr::Implication {
930 antecedent: Box::new(parse_property_implies(lhs)?),
931 consequent: Box::new(parse_property_implies(rhs)?),
932 overlapping: true,
933 });
934 }
935 if i + 2 < chars.len() && chars[i + 1] == '=' && chars[i + 2] == '>' {
936 let lhs = input[..i].trim();
937 let rhs = input[i + 3..].trim();
938 return Ok(SvaExpr::Implication {
939 antecedent: Box::new(parse_property_implies(lhs)?),
940 consequent: Box::new(parse_property_implies(rhs)?),
941 overlapping: false,
942 });
943 }
944 }
945 '#' if depth == 0 && i + 2 < chars.len() => {
947 if chars[i + 1] == '-' && chars[i + 2] == '#' {
948 let lhs = input[..i].trim();
949 let rhs = input[i + 3..].trim();
950 if !lhs.is_empty() {
951 return Ok(SvaExpr::FollowedBy {
952 antecedent: Box::new(parse_property_implies(lhs)?),
953 consequent: Box::new(parse_property_implies(rhs)?),
954 overlapping: true,
955 });
956 }
957 }
958 if chars[i + 1] == '=' && chars[i + 2] == '#' {
959 let lhs = input[..i].trim();
960 let rhs = input[i + 3..].trim();
961 if !lhs.is_empty() {
962 return Ok(SvaExpr::FollowedBy {
963 antecedent: Box::new(parse_property_implies(lhs)?),
964 consequent: Box::new(parse_property_implies(rhs)?),
965 overlapping: false,
966 });
967 }
968 }
969 }
970 _ => {}
971 }
972 }
973 parse_property_implies(input)
974}
975
976fn parse_property_implies(input: &str) -> Result<SvaExpr, SvaParseError> {
979 let input = input.trim();
980 if let Some(pos) = find_keyword_at_depth_0(input, "implies") {
981 let lhs = input[..pos].trim();
982 let rhs = input[pos + 7..].trim();
983 return Ok(SvaExpr::PropertyImplies(
984 Box::new(parse_property_iff(lhs)?),
985 Box::new(parse_property_implies(rhs)?), ));
987 }
988 parse_property_iff(input)
989}
990
991fn parse_property_iff(input: &str) -> Result<SvaExpr, SvaParseError> {
994 let input = input.trim();
995 if let Some(pos) = find_keyword_at_depth_0(input, "iff") {
996 let lhs = input[..pos].trim();
997 let rhs = input[pos + 3..].trim();
998 return Ok(SvaExpr::PropertyIff(
999 Box::new(parse_until(lhs)?),
1000 Box::new(parse_property_iff(rhs)?), ));
1002 }
1003 parse_until(input)
1004}
1005
1006fn parse_until(input: &str) -> Result<SvaExpr, SvaParseError> {
1009 let input = input.trim();
1010 for (keyword, strong, inclusive) in &[
1012 ("s_until_with", true, true),
1013 ("until_with", false, true),
1014 ("s_until", true, false),
1015 ("until", false, false),
1016 ] {
1017 if let Some(pos) = find_keyword_at_depth_0(input, keyword) {
1018 let lhs = input[..pos].trim();
1019 let rhs = input[pos + keyword.len()..].trim();
1020 return Ok(SvaExpr::Until {
1021 lhs: Box::new(parse_or(lhs)?),
1022 rhs: Box::new(parse_until(rhs)?), strong: *strong,
1024 inclusive: *inclusive,
1025 });
1026 }
1027 }
1028 parse_or(input)
1029}
1030
1031fn parse_or(input: &str) -> Result<SvaExpr, SvaParseError> {
1032 let mut depth = 0i32;
1033 let chars: Vec<char> = input.chars().collect();
1034 for i in 0..chars.len().saturating_sub(1) {
1035 match chars[i] {
1036 '(' => depth += 1,
1037 ')' => depth -= 1,
1038 '|' if depth == 0 && i + 1 < chars.len() && chars[i + 1] == '|' => {
1039 let lhs = input[..i].trim();
1040 let rhs = input[i + 2..].trim();
1041 return Ok(SvaExpr::Or(
1042 Box::new(parse_seq_ops(lhs)?),
1043 Box::new(parse_or(rhs)?),
1044 ));
1045 }
1046 _ => {}
1047 }
1048 }
1049 parse_seq_ops(input)
1050}
1051
1052fn parse_seq_ops(input: &str) -> Result<SvaExpr, SvaParseError> {
1056 let input_trimmed = input.trim();
1057
1058 for keyword in &["throughout", "within", "intersect"] {
1062 if let Some(pos) = find_keyword_at_depth_0(input_trimmed, keyword) {
1063 let lhs = input_trimmed[..pos].trim();
1064 let rhs = input_trimmed[pos + keyword.len()..].trim();
1065 return match *keyword {
1066 "throughout" => Ok(SvaExpr::Throughout {
1067 signal: Box::new(parse_and(lhs)?),
1068 sequence: Box::new(parse_and(rhs)?),
1069 }),
1070 "within" => Ok(SvaExpr::Within {
1071 inner: Box::new(parse_and(lhs)?),
1072 outer: Box::new(parse_and(rhs)?),
1073 }),
1074 "intersect" => Ok(SvaExpr::Intersect {
1075 left: Box::new(parse_and(lhs)?),
1076 right: Box::new(parse_and(rhs)?),
1077 }),
1078 _ => unreachable!(),
1079 };
1080 }
1081 }
1082
1083 if let Some(pos) = find_keyword_at_depth_0(input_trimmed, "or") {
1086 let lhs = input_trimmed[..pos].trim();
1087 let rhs = input_trimmed[pos + 2..].trim();
1088 return Ok(SvaExpr::SequenceOr(
1089 Box::new(parse_seq_and(lhs)?),
1090 Box::new(parse_seq_ops(rhs)?), ));
1092 }
1093
1094 parse_seq_and(input)
1095}
1096
1097fn parse_seq_and(input: &str) -> Result<SvaExpr, SvaParseError> {
1100 let input_trimmed = input.trim();
1101 if let Some(pos) = find_keyword_at_depth_0(input_trimmed, "and") {
1102 let lhs = input_trimmed[..pos].trim();
1103 let rhs = input_trimmed[pos + 3..].trim();
1104 return Ok(SvaExpr::SequenceAnd(
1105 Box::new(parse_and(lhs)?),
1106 Box::new(parse_seq_and(rhs)?), ));
1108 }
1109 parse_and(input)
1110}
1111
1112fn find_keyword_at_depth_0(input: &str, keyword: &str) -> Option<usize> {
1114 let mut depth = 0i32;
1115 let bytes = input.as_bytes();
1116 let klen = keyword.len();
1117 for i in 0..input.len() {
1118 match bytes[i] {
1119 b'(' => depth += 1,
1120 b')' => depth -= 1,
1121 _ if depth == 0 && i + klen <= input.len() => {
1122 if &input[i..i + klen] == keyword {
1123 let before_ok = i == 0 || !bytes[i - 1].is_ascii_alphanumeric();
1125 let after_ok = i + klen >= input.len() || !bytes[i + klen].is_ascii_alphanumeric();
1126 if before_ok && after_ok {
1127 return Some(i);
1128 }
1129 }
1130 }
1131 _ => {}
1132 }
1133 }
1134 None
1135}
1136
1137fn parse_and(input: &str) -> Result<SvaExpr, SvaParseError> {
1138 let mut depth = 0i32;
1139 let chars: Vec<char> = input.chars().collect();
1140 for i in 0..chars.len().saturating_sub(1) {
1141 match chars[i] {
1142 '(' => depth += 1,
1143 ')' => depth -= 1,
1144 '&' if depth == 0 && i + 1 < chars.len() && chars[i + 1] == '&' => {
1145 let lhs = input[..i].trim();
1146 let rhs = input[i + 2..].trim();
1147 return Ok(SvaExpr::And(
1148 Box::new(parse_sequence(lhs)?),
1149 Box::new(parse_and(rhs)?),
1150 ));
1151 }
1152 _ => {}
1153 }
1154 }
1155 parse_sequence(input)
1156}
1157
1158fn parse_sequence(input: &str) -> Result<SvaExpr, SvaParseError> {
1162 let input = input.trim();
1163 let bytes = input.as_bytes();
1164 let mut depth = 0i32;
1165
1166 for i in 0..input.len().saturating_sub(1) {
1169 match bytes[i] {
1170 b'(' => { depth += 1; continue; }
1171 b')' => { depth -= 1; continue; }
1172 b'#' if depth == 0 && i > 0 && i + 1 < input.len() && bytes[i + 1] == b'#' => {
1173 let lhs = input[..i].trim();
1175 if lhs.is_empty() { continue; }
1176 let delay_and_rhs = &input[i..]; let rest = &delay_and_rhs[2..];
1179 if rest.starts_with('[') {
1180 if let Some(bracket_end) = rest.find(']') {
1182 let range_str = &rest[1..bracket_end];
1183 let rhs = rest[bracket_end + 1..].trim();
1184 let parts: Vec<&str> = range_str.split(':').collect();
1185 if parts.len() == 2 {
1186 let min = parts[0].trim().parse::<u32>().unwrap_or(0);
1187 let max_str = parts[1].trim();
1188 let max = if max_str == "$" {
1189 None } else {
1191 Some(max_str.parse::<u32>().unwrap_or(0))
1192 };
1193 return Ok(SvaExpr::Implication {
1194 antecedent: Box::new(parse_eq(lhs)?),
1195 consequent: Box::new(SvaExpr::Delay {
1196 body: Box::new(parse_sequence(rhs)?),
1197 min,
1198 max,
1199 }),
1200 overlapping: true,
1201 });
1202 }
1203 }
1204 } else {
1205 let mut num_end = 0;
1207 for c in rest.chars() {
1208 if c.is_ascii_digit() { num_end += 1; } else { break; }
1209 }
1210 if num_end > 0 {
1211 let n = rest[..num_end].parse::<u32>().unwrap_or(0);
1212 let rhs = rest[num_end..].trim();
1213 return Ok(SvaExpr::Implication {
1214 antecedent: Box::new(parse_eq(lhs)?),
1215 consequent: Box::new(SvaExpr::Delay {
1216 body: Box::new(parse_sequence(rhs)?),
1217 min: n,
1218 max: Some(n), }),
1220 overlapping: true,
1221 });
1222 }
1223 }
1224 }
1225 _ => {}
1226 }
1227 }
1228 parse_eq(input)
1229}
1230
1231fn parse_eq(input: &str) -> Result<SvaExpr, SvaParseError> {
1232 let mut depth = 0i32;
1233 let chars: Vec<char> = input.chars().collect();
1234 let len = chars.len();
1235
1236 for i in 0..len {
1238 match chars[i] {
1239 '(' => depth += 1,
1240 ')' => depth -= 1,
1241 '?' if depth == 0 => {
1242 let cond = input[..i].trim();
1243 let rest = &input[i + 1..];
1244 let mut d2 = 0i32;
1246 for j in 0..rest.len() {
1247 match rest.as_bytes()[j] {
1248 b'(' => d2 += 1,
1249 b')' => d2 -= 1,
1250 b':' if d2 == 0 => {
1251 let then_part = rest[..j].trim();
1252 let else_part = rest[j + 1..].trim();
1253 return Ok(SvaExpr::Ternary {
1254 condition: Box::new(parse_eq(cond)?),
1255 then_expr: Box::new(parse_eq(then_part)?),
1256 else_expr: Box::new(parse_eq(else_part)?),
1257 });
1258 }
1259 _ => {}
1260 }
1261 }
1262 }
1263 _ => {}
1264 }
1265 }
1266
1267 depth = 0;
1268 for i in 0..len {
1272 match chars[i] {
1273 '(' | '[' => depth += 1,
1274 ')' | ']' => depth -= 1,
1275 _ if depth != 0 => {}
1276 '!' if i + 1 < len && chars[i + 1] == '=' => {
1277 let lhs = input[..i].trim();
1278 let rhs = input[i + 2..].trim();
1279 return Ok(SvaExpr::NotEq(
1280 Box::new(parse_unary(lhs)?),
1281 Box::new(parse_unary(rhs)?),
1282 ));
1283 }
1284 '=' if i + 1 < len && chars[i + 1] == '=' => {
1285 let lhs = input[..i].trim();
1286 let rhs = input[i + 2..].trim();
1287 return Ok(SvaExpr::Eq(
1288 Box::new(parse_unary(lhs)?),
1289 Box::new(parse_unary(rhs)?),
1290 ));
1291 }
1292 '<' if i + 1 < len && chars[i + 1] == '=' => {
1293 let lhs = input[..i].trim();
1294 let rhs = input[i + 2..].trim();
1295 return Ok(SvaExpr::LessEqual(
1296 Box::new(parse_unary(lhs)?),
1297 Box::new(parse_unary(rhs)?),
1298 ));
1299 }
1300 '>' if i + 1 < len && chars[i + 1] == '=' => {
1301 let lhs = input[..i].trim();
1302 let rhs = input[i + 2..].trim();
1303 return Ok(SvaExpr::GreaterEqual(
1304 Box::new(parse_unary(lhs)?),
1305 Box::new(parse_unary(rhs)?),
1306 ));
1307 }
1308 '<' if depth == 0 => {
1309 let lhs = input[..i].trim();
1310 let rhs = input[i + 1..].trim();
1311 return Ok(SvaExpr::LessThan(
1312 Box::new(parse_unary(lhs)?),
1313 Box::new(parse_unary(rhs)?),
1314 ));
1315 }
1316 '>' if depth == 0 => {
1317 let lhs = input[..i].trim();
1318 let rhs = input[i + 1..].trim();
1319 return Ok(SvaExpr::GreaterThan(
1320 Box::new(parse_unary(lhs)?),
1321 Box::new(parse_unary(rhs)?),
1322 ));
1323 }
1324 _ => {}
1325 }
1326 }
1327 parse_unary(input)
1328}
1329
1330fn parse_unary(input: &str) -> Result<SvaExpr, SvaParseError> {
1331 let input = input.trim();
1332
1333 if let Some(result) = try_parse_function_call(input, "strong", |inner| {
1335 Ok(SvaExpr::Strong(Box::new(parse_implication(inner)?)))
1336 })? { return Ok(result); }
1337
1338 if let Some(result) = try_parse_function_call(input, "weak", |inner| {
1340 Ok(SvaExpr::Weak(Box::new(parse_implication(inner)?)))
1341 })? { return Ok(result); }
1342
1343 if input.starts_with("s_nexttime[") {
1345 if let Some(bracket_end) = input.find(']') {
1346 let n_str = &input[11..bracket_end];
1347 if let Ok(n) = n_str.parse::<u32>() {
1348 let rest = input[bracket_end + 1..].trim();
1349 if rest.starts_with('(') {
1350 if let Some(close) = find_balanced_close(rest, 0) {
1351 let inner = &rest[1..close];
1352 return Ok(SvaExpr::SNexttime(
1353 Box::new(parse_implication(inner.trim())?),
1354 n,
1355 ));
1356 }
1357 }
1358 }
1359 }
1360 }
1361
1362 if let Some(result) = try_parse_function_call(input, "s_nexttime", |inner| {
1364 Ok(SvaExpr::SNexttime(Box::new(parse_implication(inner)?), 1))
1365 })? { return Ok(result); }
1366
1367 if input.starts_with("not ") || input.starts_with("not(") {
1370 let rest = input[3..].trim();
1371 return Ok(SvaExpr::PropertyNot(Box::new(parse_unary(rest)?)));
1372 }
1373
1374 if input.starts_with("always [") || input.starts_with("always[") {
1377 let rest = input["always".len()..].trim();
1378 if rest.starts_with('[') {
1379 if let Some(bracket_end) = rest.find(']') {
1380 let range_str = &rest[1..bracket_end];
1381 let body_str = rest[bracket_end + 1..].trim();
1382 let parts: Vec<&str> = range_str.split(':').collect();
1383 if parts.len() == 2 {
1384 let min = parts[0].trim().parse::<u32>().map_err(|_| SvaParseError {
1385 message: format!("invalid always min: '{}'", parts[0]),
1386 })?;
1387 let max_str = parts[1].trim();
1388 let max = if max_str == "$" {
1389 None } else {
1391 Some(max_str.parse::<u32>().map_err(|_| SvaParseError {
1392 message: format!("invalid always max: '{}'", max_str),
1393 })?)
1394 };
1395 return Ok(SvaExpr::AlwaysBounded {
1396 body: Box::new(parse_unary(body_str)?),
1397 min,
1398 max,
1399 });
1400 }
1401 }
1402 }
1403 }
1404
1405 if input.starts_with("always ") || input.starts_with("always(") {
1407 let rest = input["always".len()..].trim();
1408 return Ok(SvaExpr::Always(Box::new(parse_unary(rest)?)));
1409 }
1410
1411 if input.starts_with("s_always [") || input.starts_with("s_always[") {
1414 let rest = input["s_always".len()..].trim();
1415 if rest.starts_with('[') {
1416 if let Some(bracket_end) = rest.find(']') {
1417 let range_str = &rest[1..bracket_end];
1418 let body_str = rest[bracket_end + 1..].trim();
1419 let parts: Vec<&str> = range_str.split(':').collect();
1420 if parts.len() == 2 {
1421 let min = parts[0].trim().parse::<u32>().map_err(|_| SvaParseError {
1422 message: format!("invalid s_always min: '{}'", parts[0]),
1423 })?;
1424 let max_str = parts[1].trim();
1425 if max_str == "$" {
1426 return Err(SvaParseError {
1427 message: "s_always range must be bounded ($ not allowed)".to_string(),
1428 });
1429 }
1430 let max = max_str.parse::<u32>().map_err(|_| SvaParseError {
1431 message: format!("invalid s_always max: '{}'", max_str),
1432 })?;
1433 return Ok(SvaExpr::SAlwaysBounded {
1434 body: Box::new(parse_unary(body_str)?),
1435 min,
1436 max,
1437 });
1438 }
1439 }
1440 }
1441 }
1442
1443 if input.starts_with("eventually [") || input.starts_with("eventually[") {
1445 let rest = input["eventually".len()..].trim();
1446 if rest.starts_with('[') {
1447 if let Some(bracket_end) = rest.find(']') {
1448 let range_str = &rest[1..bracket_end];
1449 let body_str = rest[bracket_end + 1..].trim();
1450 let parts: Vec<&str> = range_str.split(':').collect();
1451 if parts.len() == 2 {
1452 let min = parts[0].trim().parse::<u32>().map_err(|_| SvaParseError {
1453 message: format!("invalid eventually min: '{}'", parts[0]),
1454 })?;
1455 let max_str = parts[1].trim();
1456 if max_str == "$" {
1457 return Err(SvaParseError {
1458 message: "weak eventually range must be bounded ($ not allowed)".to_string(),
1459 });
1460 }
1461 let max = max_str.parse::<u32>().map_err(|_| SvaParseError {
1462 message: format!("invalid eventually max: '{}'", max_str),
1463 })?;
1464 return Ok(SvaExpr::EventuallyBounded {
1465 body: Box::new(parse_unary(body_str)?),
1466 min,
1467 max,
1468 });
1469 }
1470 }
1471 }
1472 }
1473
1474 if input.starts_with("s_eventually [") || input.starts_with("s_eventually[") {
1477 let rest = input["s_eventually".len()..].trim();
1478 if rest.starts_with('[') {
1479 if let Some(bracket_end) = rest.find(']') {
1480 let range_str = &rest[1..bracket_end];
1481 let body_str = rest[bracket_end + 1..].trim();
1482 let parts: Vec<&str> = range_str.split(':').collect();
1483 if parts.len() == 2 {
1484 let min = parts[0].trim().parse::<u32>().map_err(|_| SvaParseError {
1485 message: format!("invalid s_eventually min: '{}'", parts[0]),
1486 })?;
1487 let max_str = parts[1].trim();
1488 let max = if max_str == "$" {
1489 None } else {
1491 Some(max_str.parse::<u32>().map_err(|_| SvaParseError {
1492 message: format!("invalid s_eventually max: '{}'", max_str),
1493 })?)
1494 };
1495 return Ok(SvaExpr::SEventuallyBounded {
1496 body: Box::new(parse_unary(body_str)?),
1497 min,
1498 max,
1499 });
1500 }
1501 }
1502 }
1503 }
1504
1505 if input.starts_with("##") {
1507 let rest = &input[2..];
1508 if rest.starts_with('[') {
1509 if rest.starts_with("[*]") {
1512 let body_str = rest[3..].trim();
1513 return Ok(SvaExpr::Delay {
1514 body: Box::new(parse_unary(body_str)?),
1515 min: 0,
1516 max: None, });
1518 }
1519 if rest.starts_with("[+]") {
1520 let body_str = rest[3..].trim();
1521 return Ok(SvaExpr::Delay {
1522 body: Box::new(parse_unary(body_str)?),
1523 min: 1,
1524 max: None, });
1526 }
1527 if let Some(bracket_end) = rest.find(']') {
1529 let range_str = &rest[1..bracket_end];
1530 let body_str = rest[bracket_end + 1..].trim();
1531 let parts: Vec<&str> = range_str.split(':').collect();
1532 if parts.len() == 2 {
1533 let min = parts[0].trim().parse::<u32>().map_err(|_| SvaParseError {
1534 message: format!("invalid delay min: '{}'", parts[0]),
1535 })?;
1536 let max_str = parts[1].trim();
1537 let max = if max_str == "$" {
1538 None } else {
1540 Some(max_str.parse::<u32>().map_err(|_| SvaParseError {
1541 message: format!("invalid delay max: '{}'", max_str),
1542 })?)
1543 };
1544 return Ok(SvaExpr::Delay {
1545 body: Box::new(parse_unary(body_str)?),
1546 min,
1547 max,
1548 });
1549 }
1550 }
1551 } else {
1552 let mut num_end = 0;
1554 for c in rest.chars() {
1555 if c.is_ascii_digit() {
1556 num_end += 1;
1557 } else {
1558 break;
1559 }
1560 }
1561 if num_end > 0 {
1562 let n = rest[..num_end].parse::<u32>().map_err(|_| SvaParseError {
1563 message: format!("invalid delay number: '{}'", &rest[..num_end]),
1564 })?;
1565 let body_str = rest[num_end..].trim();
1566 return Ok(SvaExpr::Delay {
1567 body: Box::new(parse_unary(body_str)?),
1568 min: n,
1569 max: Some(n), });
1571 }
1572 }
1573 }
1574
1575 if input.starts_with('!') {
1577 let inner = input[1..].trim();
1578 let inner = strip_parens(inner);
1579 return Ok(SvaExpr::Not(Box::new(parse_implication(inner)?)));
1580 }
1581
1582 if let Some(result) = try_parse_function_call(input, "$onehot0", |inner| {
1584 Ok(SvaExpr::OneHot0(Box::new(parse_implication(inner)?)))
1585 })? { return Ok(result); }
1586
1587 if let Some(result) = try_parse_function_call(input, "$onehot", |inner| {
1588 Ok(SvaExpr::OneHot(Box::new(parse_implication(inner)?)))
1589 })? { return Ok(result); }
1590
1591 if let Some(result) = try_parse_function_call(input, "$countones", |inner| {
1592 Ok(SvaExpr::CountOnes(Box::new(parse_implication(inner)?)))
1593 })? { return Ok(result); }
1594
1595 if let Some(result) = try_parse_function_call(input, "$isunknown", |inner| {
1596 Ok(SvaExpr::IsUnknown(Box::new(parse_implication(inner)?)))
1597 })? { return Ok(result); }
1598
1599 if let Some(result) = try_parse_function_call(input, "$sampled", |inner| {
1600 Ok(SvaExpr::Sampled(Box::new(parse_implication(inner)?)))
1601 })? { return Ok(result); }
1602
1603 if let Some(result) = try_parse_function_call(input, "$bits", |inner| {
1604 Ok(SvaExpr::Bits(Box::new(parse_implication(inner)?)))
1605 })? { return Ok(result); }
1606
1607 if let Some(result) = try_parse_function_call(input, "$clog2", |inner| {
1608 Ok(SvaExpr::Clog2(Box::new(parse_implication(inner)?)))
1609 })? { return Ok(result); }
1610
1611 if input.starts_with("$countbits(") {
1613 if let Some(close) = find_balanced_close(input, "$countbits".len()) {
1614 let inner = &input["$countbits".len() + 1..close];
1615 let parts: Vec<&str> = inner.split(',').collect();
1617 if !parts.is_empty() {
1618 let sig = parse_implication(parts[0].trim())?;
1619 let mut control_chars = Vec::new();
1620 for part in &parts[1..] {
1621 let trimmed = part.trim().trim_matches('\'');
1622 if let Some(c) = trimmed.chars().next() {
1623 control_chars.push(c);
1624 }
1625 }
1626 return Ok(SvaExpr::CountBits(Box::new(sig), control_chars));
1627 }
1628 }
1629 }
1630
1631 if let Some(result) = try_parse_function_call(input, "$isunbounded", |inner| {
1633 Ok(SvaExpr::IsUnbounded(Box::new(parse_implication(inner)?)))
1634 })? { return Ok(result); }
1635
1636 if let Some(result) = try_parse_function_call(input, "$rose", |inner| {
1639 Ok(SvaExpr::Rose(Box::new(parse_implication(inner)?)))
1640 })? { return Ok(result); }
1641
1642 if let Some(result) = try_parse_function_call(input, "$fell", |inner| {
1643 Ok(SvaExpr::Fell(Box::new(parse_implication(inner)?)))
1644 })? { return Ok(result); }
1645
1646 if let Some(result) = try_parse_function_call(input, "$stable", |inner| {
1647 Ok(SvaExpr::Stable(Box::new(parse_implication(inner)?)))
1648 })? { return Ok(result); }
1649
1650 if let Some(result) = try_parse_function_call(input, "$changed", |inner| {
1651 Ok(SvaExpr::Changed(Box::new(parse_implication(inner)?)))
1652 })? { return Ok(result); }
1653
1654 if let Some(result) = try_parse_function_call(input, "s_eventually", |inner| {
1655 Ok(SvaExpr::SEventually(Box::new(parse_implication(inner)?)))
1656 })? { return Ok(result); }
1657
1658 if let Some(result) = try_parse_function_call(input, "s_always", |inner| {
1659 Ok(SvaExpr::SAlways(Box::new(parse_implication(inner)?)))
1660 })? { return Ok(result); }
1661
1662 if input.starts_with("nexttime[") {
1664 if let Some(bracket_end) = input.find(']') {
1665 let n_str = &input[9..bracket_end];
1666 if let Ok(n) = n_str.parse::<u32>() {
1667 let rest = input[bracket_end + 1..].trim();
1668 if rest.starts_with('(') {
1669 if let Some(close) = find_balanced_close(rest, 0) {
1670 let inner = &rest[1..close];
1671 return Ok(SvaExpr::Nexttime(
1672 Box::new(parse_implication(inner.trim())?),
1673 n,
1674 ));
1675 }
1676 }
1677 }
1678 }
1679 }
1680
1681 if let Some(result) = try_parse_function_call(input, "nexttime", |inner| {
1683 Ok(SvaExpr::Nexttime(Box::new(parse_implication(inner)?), 1))
1684 })? { return Ok(result); }
1685
1686 if let Some(result) = try_parse_function_call(input, "$nexttime", |inner| {
1687 Ok(SvaExpr::Nexttime(Box::new(parse_implication(inner)?), 1))
1688 })? { return Ok(result); }
1689
1690 if let Some(result) = try_parse_function_call(input, "first_match", |inner| {
1691 Ok(SvaExpr::FirstMatch(Box::new(parse_implication(inner)?)))
1692 })? { return Ok(result); }
1693
1694 if let Some(result) = try_parse_function_call(input, "$past", |inner| {
1695 if let Some(comma) = inner.find(',') {
1697 let sig = inner[..comma].trim();
1698 let n_str = inner[comma + 1..].trim();
1699 let n = n_str.parse::<u32>().unwrap_or(1);
1700 Ok(SvaExpr::Past(Box::new(parse_atom(sig)?), n))
1701 } else {
1702 Ok(SvaExpr::Past(Box::new(parse_atom(inner)?), 1))
1703 }
1704 })? { return Ok(result); }
1705
1706 if input.starts_with('(') && input.ends_with(')') {
1708 return parse_implication(&input[1..input.len() - 1]);
1709 }
1710
1711 parse_atom(input)
1712}
1713
1714fn find_else_outside_strings(input: &str) -> Option<usize> {
1718 let bytes = input.as_bytes();
1719 let len = bytes.len();
1720 let mut i = 0;
1721 while i < len {
1722 if i + 2 < len && bytes[i] == b'"' && bytes[i + 1] == b'"' && bytes[i + 2] == b'"' {
1724 i += 3; while i < len {
1726 if bytes[i] == b'\\' && i + 1 < len {
1727 i += 2; } else if i + 2 < len && bytes[i] == b'"' && bytes[i + 1] == b'"' && bytes[i + 2] == b'"' {
1729 i += 3; break;
1731 } else {
1732 i += 1;
1733 }
1734 }
1735 continue;
1736 }
1737 if bytes[i] == b'"' {
1739 i += 1; while i < len {
1741 if bytes[i] == b'\\' && i + 1 < len {
1742 i += 2; } else if bytes[i] == b'"' {
1744 i += 1; break;
1746 } else {
1747 i += 1;
1748 }
1749 }
1750 continue;
1751 }
1752 if i + 4 <= len && &input[i..i + 4] == "else" {
1754 let before_ok = i == 0 || !bytes[i - 1].is_ascii_alphanumeric();
1756 let after_ok = i + 4 >= len || !bytes[i + 4].is_ascii_alphanumeric();
1757 if before_ok && after_ok {
1758 return Some(i);
1759 }
1760 }
1761 i += 1;
1762 }
1763 None
1764}
1765
1766fn find_balanced_close(input: &str, start: usize) -> Option<usize> {
1769 let chars: Vec<char> = input.chars().collect();
1770 let mut depth = 0i32;
1771 for i in start..chars.len() {
1772 match chars[i] {
1773 '(' => depth += 1,
1774 ')' => {
1775 depth -= 1;
1776 if depth == 0 {
1777 return Some(i);
1778 }
1779 }
1780 _ => {}
1781 }
1782 }
1783 None
1784}
1785
1786fn try_parse_function_call<F>(
1793 input: &str,
1794 prefix: &str,
1795 parse_inner: F,
1796) -> Result<Option<SvaExpr>, SvaParseError>
1797where
1798 F: FnOnce(&str) -> Result<SvaExpr, SvaParseError>,
1799{
1800 let full_prefix = format!("{}(", prefix);
1801 if !input.starts_with(&full_prefix) {
1802 return Ok(None);
1803 }
1804 let paren_start = full_prefix.len() - 1; if let Some(close) = find_balanced_close(input, paren_start) {
1806 let inner = &input[full_prefix.len()..close];
1807 let remaining = input[close + 1..].trim();
1808 if remaining.is_empty() {
1809 return Ok(Some(parse_inner(inner.trim())?));
1811 }
1812 return Ok(None);
1821 }
1822 Err(SvaParseError {
1823 message: format!("unbalanced parens in {}", prefix),
1824 })
1825}
1826
1827fn find_else_keyword(input: &str) -> Option<usize> {
1829 let mut depth = 0i32;
1830 let bytes = input.as_bytes();
1831 for i in 0..input.len().saturating_sub(3) {
1832 match bytes[i] {
1833 b'(' => depth += 1,
1834 b')' => depth -= 1,
1835 b'e' if depth == 0 => {
1836 if input[i..].starts_with("else") {
1837 let before_ok = i == 0 || !bytes[i - 1].is_ascii_alphanumeric();
1839 let after_ok = i + 4 >= input.len() || !bytes[i + 4].is_ascii_alphanumeric();
1840 if before_ok && after_ok {
1841 return Some(i);
1842 }
1843 }
1844 }
1845 _ => {}
1846 }
1847 }
1848 None
1849}
1850
1851fn parse_atom(input: &str) -> Result<SvaExpr, SvaParseError> {
1852 let input = input.trim();
1853 if input.is_empty() {
1854 return Err(SvaParseError {
1855 message: "empty expression".to_string(),
1856 });
1857 }
1858
1859 if let Some(bracket_pos) = input.find("[+]") {
1861 let signal_part = input[..bracket_pos].trim();
1862 let body = parse_atom(signal_part)?;
1863 return Ok(SvaExpr::Repetition {
1864 body: Box::new(body),
1865 min: 1,
1866 max: None,
1867 });
1868 }
1869
1870 if let Some(bracket_pos) = input.find("[*") {
1872 let signal_part = input[..bracket_pos].trim();
1873 let rep_part = &input[bracket_pos + 2..];
1874 if let Some(close_bracket) = rep_part.find(']') {
1875 let range_str = &rep_part[..close_bracket].trim();
1876 if range_str.is_empty() {
1878 let body = parse_atom(signal_part)?;
1879 return Ok(SvaExpr::Repetition {
1880 body: Box::new(body),
1881 min: 0,
1882 max: None,
1883 });
1884 }
1885 let body = parse_atom(signal_part)?;
1886 if let Some(colon) = range_str.find(':') {
1887 let min_str = range_str[..colon].trim();
1888 let max_str = range_str[colon + 1..].trim();
1889 let min = min_str.parse::<u32>().map_err(|_| SvaParseError {
1890 message: format!("invalid repetition min: '{}'", min_str),
1891 })?;
1892 let max = if max_str == "$" {
1893 None
1894 } else {
1895 Some(max_str.parse::<u32>().map_err(|_| SvaParseError {
1896 message: format!("invalid repetition max: '{}'", max_str),
1897 })?)
1898 };
1899 return Ok(SvaExpr::Repetition {
1900 body: Box::new(body),
1901 min,
1902 max,
1903 });
1904 } else {
1905 let n = range_str.trim().parse::<u32>().map_err(|_| SvaParseError {
1907 message: format!("invalid repetition count: '{}'", range_str),
1908 })?;
1909 return Ok(SvaExpr::Repetition {
1910 body: Box::new(body),
1911 min: n,
1912 max: Some(n),
1913 });
1914 }
1915 }
1916 }
1917
1918 if let Some(bracket_pos) = input.find("[->") {
1920 let signal_part = input[..bracket_pos].trim();
1921 let rep_part = &input[bracket_pos + 3..];
1922 if let Some(close_bracket) = rep_part.find(']') {
1923 let count_str = rep_part[..close_bracket].trim();
1924 let count = count_str.parse::<u32>().map_err(|_| SvaParseError {
1925 message: format!("invalid goto repetition count: '{}'", count_str),
1926 })?;
1927 return Ok(SvaExpr::GotoRepetition {
1928 body: Box::new(parse_atom(signal_part)?),
1929 count,
1930 });
1931 }
1932 }
1933
1934 if let Some(bracket_pos) = input.find("[=") {
1936 let signal_part = input[..bracket_pos].trim();
1937 let rep_part = &input[bracket_pos + 2..];
1938 if let Some(close_bracket) = rep_part.find(']') {
1939 let range_str = &rep_part[..close_bracket];
1940 let body = parse_atom(signal_part)?;
1941 if let Some(colon) = range_str.find(':') {
1942 let min = range_str[..colon].trim().parse::<u32>().map_err(|_| SvaParseError {
1943 message: format!("invalid non-consec repetition min: '{}'", &range_str[..colon]),
1944 })?;
1945 let max_str = range_str[colon + 1..].trim();
1946 let max = if max_str == "$" {
1947 None
1948 } else {
1949 Some(max_str.parse::<u32>().map_err(|_| SvaParseError {
1950 message: format!("invalid non-consec repetition max: '{}'", max_str),
1951 })?)
1952 };
1953 return Ok(SvaExpr::NonConsecRepetition {
1954 body: Box::new(body),
1955 min,
1956 max,
1957 });
1958 } else {
1959 let n = range_str.trim().parse::<u32>().map_err(|_| SvaParseError {
1960 message: format!("invalid non-consec repetition count: '{}'", range_str),
1961 })?;
1962 return Ok(SvaExpr::NonConsecRepetition {
1963 body: Box::new(body),
1964 min: n,
1965 max: Some(n),
1966 });
1967 }
1968 }
1969 }
1970
1971 if (input.contains('.') || input.contains('e') || input.contains('E'))
1974 && input.chars().next().map_or(false, |c| c.is_ascii_digit())
1975 {
1976 if let Ok(v) = input.parse::<f64>() {
1977 return Ok(SvaExpr::RealConst(v));
1978 }
1979 }
1980
1981 if let Ok(n) = input.parse::<u64>() {
1983 return Ok(SvaExpr::Const(n, 32));
1984 }
1985 if let Some(tick_pos) = input.find('\'') {
1987 let width_str = &input[..tick_pos];
1988 let rest = &input[tick_pos + 1..];
1989 if let Ok(width) = width_str.parse::<u32>() {
1990 let (radix, value_str) = if rest.starts_with('d') || rest.starts_with('D') {
1991 (10, &rest[1..])
1992 } else if rest.starts_with('h') || rest.starts_with('H') {
1993 (16, &rest[1..])
1994 } else if rest.starts_with('b') || rest.starts_with('B') {
1995 (2, &rest[1..])
1996 } else if rest.starts_with('o') || rest.starts_with('O') {
1997 (8, &rest[1..])
1998 } else {
1999 (10, rest)
2000 };
2001 if let Ok(value) = u64::from_str_radix(value_str, radix) {
2002 return Ok(SvaExpr::Const(value, width));
2003 }
2004 }
2005 }
2006
2007 if input == "type(this)" {
2009 return Ok(SvaExpr::TypeThis);
2010 }
2011
2012 if let Some(dot_map_pos) = input.find(".map(") {
2014 let array_part = &input[..dot_map_pos].trim();
2015 let after_map = &input[dot_map_pos + 5..]; if let Some(iter_close) = after_map.find(')') {
2018 let iter_part = after_map[..iter_close].trim();
2019 let iterator = if iter_part.is_empty() {
2020 "item".to_string() } else {
2022 iter_part.split(',').next().unwrap_or("item").trim().to_string()
2024 };
2025 let after_iter_close = after_map[iter_close + 1..].trim();
2026 if after_iter_close.starts_with("with") {
2028 let with_body = after_iter_close["with".len()..].trim();
2029 let with_expr_str = strip_parens(with_body);
2030 let array_expr = parse_atom(array_part)?;
2031 let with_expr = parse_sva(with_expr_str)?;
2032 return Ok(SvaExpr::ArrayMap {
2033 array: Box::new(array_expr),
2034 iterator,
2035 with_expr: Box::new(with_expr),
2036 });
2037 }
2038 }
2039 }
2040
2041 if input
2043 .chars()
2044 .all(|c| c.is_alphanumeric() || c == '_')
2045 {
2046 return Ok(SvaExpr::Signal(input.to_string()));
2047 }
2048
2049 Err(SvaParseError {
2050 message: format!("unexpected token: '{}'", input),
2051 })
2052}
2053
2054pub fn sva_expr_to_string(expr: &SvaExpr) -> String {
2057 match expr {
2058 SvaExpr::Signal(name) => name.clone(),
2059 SvaExpr::Const(value, width) => format!("{}'d{}", width, value),
2060 SvaExpr::Rose(inner) => format!("$rose({})", sva_expr_to_string(inner)),
2061 SvaExpr::Fell(inner) => format!("$fell({})", sva_expr_to_string(inner)),
2062 SvaExpr::Past(inner, n) => format!("$past({}, {})", sva_expr_to_string(inner), n),
2063 SvaExpr::And(left, right) => {
2064 format!("({} && {})", sva_expr_to_string(left), sva_expr_to_string(right))
2065 }
2066 SvaExpr::Or(left, right) => {
2067 format!("({} || {})", sva_expr_to_string(left), sva_expr_to_string(right))
2068 }
2069 SvaExpr::Not(inner) => format!("!({})", sva_expr_to_string(inner)),
2070 SvaExpr::Eq(left, right) => {
2071 format!("({} == {})", sva_expr_to_string(left), sva_expr_to_string(right))
2072 }
2073 SvaExpr::Implication {
2074 antecedent,
2075 consequent,
2076 overlapping,
2077 } => {
2078 let op = if *overlapping { "|->" } else { "|=>" };
2079 format!(
2080 "{} {} {}",
2081 sva_expr_to_string(antecedent),
2082 op,
2083 sva_expr_to_string(consequent)
2084 )
2085 }
2086 SvaExpr::Delay { body, min, max } => match (min, max) {
2087 (0, None) => format!("##[*] {}", sva_expr_to_string(body)),
2089 (1, None) => format!("##[+] {}", sva_expr_to_string(body)),
2090 (_, None) => format!("##[{}:$] {}", min, sva_expr_to_string(body)),
2091 (_, Some(max_val)) if min == max_val => format!("##{} {}", min, sva_expr_to_string(body)),
2092 (_, Some(max_val)) => format!("##[{}:{}] {}", min, max_val, sva_expr_to_string(body)),
2093 },
2094 SvaExpr::Repetition { body, min, max } => {
2095 let body_str = sva_expr_to_string(body);
2096 match (min, max) {
2097 (0, None) => format!("{}[*]", body_str),
2098 (1, None) => format!("{}[+]", body_str),
2099 (_, Some(m)) if *m == *min => format!("{}[*{}]", body_str, min),
2100 (_, Some(m)) => format!("{}[*{}:{}]", body_str, min, m),
2101 (_, None) => format!("{}[*{}:$]", body_str, min),
2102 }
2103 }
2104 SvaExpr::SEventually(inner) => format!("s_eventually({})", sva_expr_to_string(inner)),
2105 SvaExpr::SAlways(inner) => format!("s_always({})", sva_expr_to_string(inner)),
2106 SvaExpr::Stable(inner) => format!("$stable({})", sva_expr_to_string(inner)),
2107 SvaExpr::Changed(inner) => format!("$changed({})", sva_expr_to_string(inner)),
2108 SvaExpr::Nexttime(inner, n) => {
2109 if *n == 1 {
2110 format!("nexttime({})", sva_expr_to_string(inner))
2111 } else {
2112 format!("nexttime[{}]({})", n, sva_expr_to_string(inner))
2113 }
2114 }
2115 SvaExpr::DisableIff { condition, body } => {
2116 format!("disable iff ({}) {}", sva_expr_to_string(condition), sva_expr_to_string(body))
2117 }
2118 SvaExpr::IfElse { condition, then_expr, else_expr } => {
2119 format!(
2120 "if ({}) {} else {}",
2121 sva_expr_to_string(condition),
2122 sva_expr_to_string(then_expr),
2123 sva_expr_to_string(else_expr),
2124 )
2125 }
2126 SvaExpr::NotEq(l, r) => format!("({} != {})", sva_expr_to_string(l), sva_expr_to_string(r)),
2128 SvaExpr::LessThan(l, r) => format!("({} < {})", sva_expr_to_string(l), sva_expr_to_string(r)),
2129 SvaExpr::GreaterThan(l, r) => format!("({} > {})", sva_expr_to_string(l), sva_expr_to_string(r)),
2130 SvaExpr::LessEqual(l, r) => format!("({} <= {})", sva_expr_to_string(l), sva_expr_to_string(r)),
2131 SvaExpr::GreaterEqual(l, r) => format!("({} >= {})", sva_expr_to_string(l), sva_expr_to_string(r)),
2132 SvaExpr::Ternary { condition, then_expr, else_expr } => {
2133 format!("{} ? {} : {}",
2134 sva_expr_to_string(condition),
2135 sva_expr_to_string(then_expr),
2136 sva_expr_to_string(else_expr),
2137 )
2138 }
2139 SvaExpr::Throughout { signal, sequence } => {
2140 format!("{} throughout ({})",
2141 sva_expr_to_string(signal),
2142 sva_expr_to_string(sequence),
2143 )
2144 }
2145 SvaExpr::Within { inner, outer } => {
2146 format!("({}) within ({})",
2147 sva_expr_to_string(inner),
2148 sva_expr_to_string(outer),
2149 )
2150 }
2151 SvaExpr::FirstMatch(inner) => format!("first_match({})", sva_expr_to_string(inner)),
2152 SvaExpr::Intersect { left, right } => {
2153 format!("({}) intersect ({})",
2154 sva_expr_to_string(left),
2155 sva_expr_to_string(right),
2156 )
2157 }
2158 SvaExpr::OneHot0(inner) => format!("$onehot0({})", sva_expr_to_string(inner)),
2160 SvaExpr::OneHot(inner) => format!("$onehot({})", sva_expr_to_string(inner)),
2161 SvaExpr::CountOnes(inner) => format!("$countones({})", sva_expr_to_string(inner)),
2162 SvaExpr::IsUnknown(inner) => format!("$isunknown({})", sva_expr_to_string(inner)),
2163 SvaExpr::Sampled(inner) => format!("$sampled({})", sva_expr_to_string(inner)),
2164 SvaExpr::Bits(inner) => format!("$bits({})", sva_expr_to_string(inner)),
2165 SvaExpr::Clog2(inner) => format!("$clog2({})", sva_expr_to_string(inner)),
2166 SvaExpr::CountBits(inner, chars) => {
2167 let char_args: Vec<String> = chars.iter().map(|c| format!("'{}'", c)).collect();
2168 format!("$countbits({}, {})", sva_expr_to_string(inner), char_args.join(", "))
2169 }
2170 SvaExpr::IsUnbounded(inner) => format!("$isunbounded({})", sva_expr_to_string(inner)),
2171 SvaExpr::GotoRepetition { body, count } => {
2173 format!("{}[->{}]", sva_expr_to_string(body), count)
2174 }
2175 SvaExpr::NonConsecRepetition { body, min, max } => {
2176 let body_str = sva_expr_to_string(body);
2177 match max {
2178 Some(m) if *m == *min => format!("{}[={}]", body_str, min),
2179 Some(m) => format!("{}[={}:{}]", body_str, min, m),
2180 None => format!("{}[={}:$]", body_str, min),
2181 }
2182 }
2183 SvaExpr::AcceptOn { condition, body } => {
2185 format!("accept_on({}) {}", sva_expr_to_string(condition), sva_expr_to_string(body))
2186 }
2187 SvaExpr::RejectOn { condition, body } => {
2188 format!("reject_on({}) {}", sva_expr_to_string(condition), sva_expr_to_string(body))
2189 }
2190 SvaExpr::PropertyNot(inner) => format!("not {}", sva_expr_to_string(inner)),
2192 SvaExpr::PropertyImplies(l, r) => {
2193 format!("{} implies {}", sva_expr_to_string(l), sva_expr_to_string(r))
2194 }
2195 SvaExpr::PropertyIff(l, r) => {
2196 format!("{} iff {}", sva_expr_to_string(l), sva_expr_to_string(r))
2197 }
2198 SvaExpr::Always(inner) => format!("always({})", sva_expr_to_string(inner)),
2200 SvaExpr::AlwaysBounded { body, min, max } => match max {
2201 Some(m) => format!("always [{}:{}] {}", min, m, sva_expr_to_string(body)),
2202 None => format!("always [{}:$] {}", min, sva_expr_to_string(body)),
2203 },
2204 SvaExpr::SAlwaysBounded { body, min, max } => {
2205 format!("s_always [{}:{}] {}", min, max, sva_expr_to_string(body))
2206 }
2207 SvaExpr::EventuallyBounded { body, min, max } => {
2208 format!("eventually [{}:{}] {}", min, max, sva_expr_to_string(body))
2209 }
2210 SvaExpr::SEventuallyBounded { body, min, max } => match max {
2211 Some(m) => format!("s_eventually [{}:{}] {}", min, m, sva_expr_to_string(body)),
2212 None => format!("s_eventually [{}:$] {}", min, sva_expr_to_string(body)),
2213 },
2214 SvaExpr::Until { lhs, rhs, strong, inclusive } => {
2215 let op = match (strong, inclusive) {
2216 (false, false) => "until",
2217 (true, false) => "s_until",
2218 (false, true) => "until_with",
2219 (true, true) => "s_until_with",
2220 };
2221 format!("{} {} {}", sva_expr_to_string(lhs), op, sva_expr_to_string(rhs))
2222 }
2223 SvaExpr::Strong(inner) => format!("strong({})", sva_expr_to_string(inner)),
2225 SvaExpr::Weak(inner) => format!("weak({})", sva_expr_to_string(inner)),
2226 SvaExpr::SNexttime(inner, n) => {
2227 if *n == 1 {
2228 format!("s_nexttime({})", sva_expr_to_string(inner))
2229 } else {
2230 format!("s_nexttime[{}]({})", n, sva_expr_to_string(inner))
2231 }
2232 }
2233 SvaExpr::FollowedBy { antecedent, consequent, overlapping } => {
2234 let op = if *overlapping { "#-#" } else { "#=#" };
2235 format!("{} {} {}", sva_expr_to_string(antecedent), op, sva_expr_to_string(consequent))
2236 }
2237 SvaExpr::PropertyCase { expression, items, default } => {
2238 let mut s = format!("case({})", sva_expr_to_string(expression));
2239 for (vals, prop) in items {
2240 let vs: Vec<String> = vals.iter().map(sva_expr_to_string).collect();
2241 s.push_str(&format!(" {}: {};", vs.join(", "), sva_expr_to_string(prop)));
2242 }
2243 if let Some(d) = default {
2244 s.push_str(&format!(" default: {};", sva_expr_to_string(d)));
2245 }
2246 s.push_str(" endcase");
2247 s
2248 }
2249 SvaExpr::SyncAcceptOn { condition, body } => {
2250 format!("sync_accept_on({}) {}", sva_expr_to_string(condition), sva_expr_to_string(body))
2251 }
2252 SvaExpr::SyncRejectOn { condition, body } => {
2253 format!("sync_reject_on({}) {}", sva_expr_to_string(condition), sva_expr_to_string(body))
2254 }
2255 SvaExpr::SequenceAnd(l, r) => {
2257 format!("({}) and ({})", sva_expr_to_string(l), sva_expr_to_string(r))
2258 }
2259 SvaExpr::SequenceOr(l, r) => {
2260 format!("({}) or ({})", sva_expr_to_string(l), sva_expr_to_string(r))
2261 }
2262 SvaExpr::ImmediateAssert { expression, deferred } => {
2264 match deferred {
2265 None => format!("assert({})", sva_expr_to_string(expression)),
2266 Some(ImmediateDeferred::Observed) => format!("assert #0({})", sva_expr_to_string(expression)),
2267 Some(ImmediateDeferred::Final) => format!("assert final({})", sva_expr_to_string(expression)),
2268 }
2269 }
2270 SvaExpr::FieldAccess { signal, field } => format!("{}.{}", sva_expr_to_string(signal), field),
2272 SvaExpr::EnumLiteral { type_name: Some(t), value } => format!("{}::{}", t, value),
2273 SvaExpr::EnumLiteral { type_name: None, value } => value.clone(),
2274 SvaExpr::Triggered(name) => format!("{}.triggered", name),
2276 SvaExpr::Matched(name) => format!("{}.matched", name),
2277 SvaExpr::BitAnd(l, r) => format!("({} & {})", sva_expr_to_string(l), sva_expr_to_string(r)),
2279 SvaExpr::BitOr(l, r) => format!("({} | {})", sva_expr_to_string(l), sva_expr_to_string(r)),
2280 SvaExpr::BitXor(l, r) => format!("({} ^ {})", sva_expr_to_string(l), sva_expr_to_string(r)),
2281 SvaExpr::BitNot(inner) => format!("~{}", sva_expr_to_string(inner)),
2282 SvaExpr::ReductionAnd(inner) => format!("&{}", sva_expr_to_string(inner)),
2283 SvaExpr::ReductionOr(inner) => format!("|{}", sva_expr_to_string(inner)),
2284 SvaExpr::ReductionXor(inner) => format!("^{}", sva_expr_to_string(inner)),
2285 SvaExpr::BitSelect { signal, index } => format!("{}[{}]", sva_expr_to_string(signal), sva_expr_to_string(index)),
2286 SvaExpr::PartSelect { signal, high, low } => format!("{}[{}:{}]", sva_expr_to_string(signal), high, low),
2287 SvaExpr::Concat(items) => {
2288 let parts: Vec<String> = items.iter().map(sva_expr_to_string).collect();
2289 format!("{{{}}}", parts.join(", "))
2290 }
2291 SvaExpr::SequenceAction { expression, assignments } => {
2293 let assigns: Vec<String> = assignments.iter()
2294 .map(|(name, rhs)| format!("{} = {}", name, sva_expr_to_string(rhs)))
2295 .collect();
2296 format!("({}, {})", sva_expr_to_string(expression), assigns.join(", "))
2297 }
2298 SvaExpr::LocalVar(name) => name.clone(),
2299 SvaExpr::ConstCast(inner) => format!("const'({})", sva_expr_to_string(inner)),
2300 SvaExpr::Clocked { clock, edge, body } => {
2301 let edge_str = match edge {
2302 ClockEdge::Posedge => "posedge",
2303 ClockEdge::Negedge => "negedge",
2304 ClockEdge::Edge => "edge",
2305 };
2306 format!("@({} {}) {}", edge_str, clock, sva_expr_to_string(body))
2307 }
2308 SvaExpr::ArrayMap { array, iterator, with_expr } => {
2310 format!("{}.map({}) with ({})", sva_expr_to_string(array), iterator, sva_expr_to_string(with_expr))
2311 }
2312 SvaExpr::TypeThis => "type(this)".to_string(),
2313 SvaExpr::RealConst(v) => format!("{}", v),
2314 }
2315}
2316
2317fn strip_parens(input: &str) -> &str {
2318 let input = input.trim();
2319 if input.starts_with('(') && input.ends_with(')') {
2320 &input[1..input.len() - 1]
2321 } else {
2322 input
2323 }
2324}
2325
2326pub fn sva_exprs_structurally_equivalent(a: &SvaExpr, b: &SvaExpr) -> bool {
2328 match (a, b) {
2329 (SvaExpr::Signal(sa), SvaExpr::Signal(sb)) => sa == sb,
2330 (SvaExpr::Const(va, wa), SvaExpr::Const(vb, wb)) => va == vb && wa == wb,
2331 (SvaExpr::Rose(ia), SvaExpr::Rose(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2332 (SvaExpr::Fell(ia), SvaExpr::Fell(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2333 (SvaExpr::Past(ia, na), SvaExpr::Past(ib, nb)) => {
2334 na == nb && sva_exprs_structurally_equivalent(ia, ib)
2335 }
2336 (SvaExpr::And(la, ra), SvaExpr::And(lb, rb)) => {
2337 sva_exprs_structurally_equivalent(la, lb)
2338 && sva_exprs_structurally_equivalent(ra, rb)
2339 }
2340 (SvaExpr::Or(la, ra), SvaExpr::Or(lb, rb)) => {
2341 sva_exprs_structurally_equivalent(la, lb)
2342 && sva_exprs_structurally_equivalent(ra, rb)
2343 }
2344 (SvaExpr::Not(ia), SvaExpr::Not(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2345 (SvaExpr::Eq(la, ra), SvaExpr::Eq(lb, rb)) => {
2346 sva_exprs_structurally_equivalent(la, lb)
2347 && sva_exprs_structurally_equivalent(ra, rb)
2348 }
2349 (
2350 SvaExpr::Implication {
2351 antecedent: aa,
2352 consequent: ca,
2353 overlapping: oa,
2354 },
2355 SvaExpr::Implication {
2356 antecedent: ab,
2357 consequent: cb,
2358 overlapping: ob,
2359 },
2360 ) => {
2361 oa == ob
2362 && sva_exprs_structurally_equivalent(aa, ab)
2363 && sva_exprs_structurally_equivalent(ca, cb)
2364 }
2365 (
2366 SvaExpr::Delay {
2367 body: ba,
2368 min: mna,
2369 max: mxa,
2370 },
2371 SvaExpr::Delay {
2372 body: bb,
2373 min: mnb,
2374 max: mxb,
2375 },
2376 ) => mna == mnb && mxa == mxb && sva_exprs_structurally_equivalent(ba, bb),
2377 (
2378 SvaExpr::Repetition { body: ba, min: mna, max: mxa },
2379 SvaExpr::Repetition { body: bb, min: mnb, max: mxb },
2380 ) => mna == mnb && mxa == mxb && sva_exprs_structurally_equivalent(ba, bb),
2381 (SvaExpr::SEventually(ia), SvaExpr::SEventually(ib)) => {
2382 sva_exprs_structurally_equivalent(ia, ib)
2383 }
2384 (SvaExpr::SAlways(ia), SvaExpr::SAlways(ib)) => {
2385 sva_exprs_structurally_equivalent(ia, ib)
2386 }
2387 (SvaExpr::Stable(ia), SvaExpr::Stable(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2388 (SvaExpr::Changed(ia), SvaExpr::Changed(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2389 (SvaExpr::Nexttime(ia, na), SvaExpr::Nexttime(ib, nb)) => {
2390 na == nb && sva_exprs_structurally_equivalent(ia, ib)
2391 }
2392 (
2393 SvaExpr::DisableIff { condition: ca, body: ba },
2394 SvaExpr::DisableIff { condition: cb, body: bb },
2395 ) => {
2396 sva_exprs_structurally_equivalent(ca, cb)
2397 && sva_exprs_structurally_equivalent(ba, bb)
2398 }
2399 (
2400 SvaExpr::IfElse { condition: ca, then_expr: ta, else_expr: ea },
2401 SvaExpr::IfElse { condition: cb, then_expr: tb, else_expr: eb },
2402 ) => {
2403 sva_exprs_structurally_equivalent(ca, cb)
2404 && sva_exprs_structurally_equivalent(ta, tb)
2405 && sva_exprs_structurally_equivalent(ea, eb)
2406 }
2407 (SvaExpr::NotEq(la, ra), SvaExpr::NotEq(lb, rb)) => {
2409 sva_exprs_structurally_equivalent(la, lb) && sva_exprs_structurally_equivalent(ra, rb)
2410 }
2411 (SvaExpr::LessThan(la, ra), SvaExpr::LessThan(lb, rb)) => {
2412 sva_exprs_structurally_equivalent(la, lb) && sva_exprs_structurally_equivalent(ra, rb)
2413 }
2414 (SvaExpr::GreaterThan(la, ra), SvaExpr::GreaterThan(lb, rb)) => {
2415 sva_exprs_structurally_equivalent(la, lb) && sva_exprs_structurally_equivalent(ra, rb)
2416 }
2417 (SvaExpr::LessEqual(la, ra), SvaExpr::LessEqual(lb, rb)) => {
2418 sva_exprs_structurally_equivalent(la, lb) && sva_exprs_structurally_equivalent(ra, rb)
2419 }
2420 (SvaExpr::GreaterEqual(la, ra), SvaExpr::GreaterEqual(lb, rb)) => {
2421 sva_exprs_structurally_equivalent(la, lb) && sva_exprs_structurally_equivalent(ra, rb)
2422 }
2423 (
2424 SvaExpr::Ternary { condition: ca, then_expr: ta, else_expr: ea },
2425 SvaExpr::Ternary { condition: cb, then_expr: tb, else_expr: eb },
2426 ) => {
2427 sva_exprs_structurally_equivalent(ca, cb)
2428 && sva_exprs_structurally_equivalent(ta, tb)
2429 && sva_exprs_structurally_equivalent(ea, eb)
2430 }
2431 (
2432 SvaExpr::Throughout { signal: sa, sequence: qa },
2433 SvaExpr::Throughout { signal: sb, sequence: qb },
2434 ) => {
2435 sva_exprs_structurally_equivalent(sa, sb) && sva_exprs_structurally_equivalent(qa, qb)
2436 }
2437 (
2438 SvaExpr::Within { inner: ia, outer: oa },
2439 SvaExpr::Within { inner: ib, outer: ob },
2440 ) => {
2441 sva_exprs_structurally_equivalent(ia, ib) && sva_exprs_structurally_equivalent(oa, ob)
2442 }
2443 (SvaExpr::FirstMatch(ia), SvaExpr::FirstMatch(ib)) => {
2444 sva_exprs_structurally_equivalent(ia, ib)
2445 }
2446 (
2447 SvaExpr::Intersect { left: la, right: ra },
2448 SvaExpr::Intersect { left: lb, right: rb },
2449 ) => {
2450 sva_exprs_structurally_equivalent(la, lb) && sva_exprs_structurally_equivalent(ra, rb)
2451 }
2452 (SvaExpr::OneHot0(ia), SvaExpr::OneHot0(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2454 (SvaExpr::OneHot(ia), SvaExpr::OneHot(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2455 (SvaExpr::CountOnes(ia), SvaExpr::CountOnes(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2456 (SvaExpr::IsUnknown(ia), SvaExpr::IsUnknown(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2457 (SvaExpr::Sampled(ia), SvaExpr::Sampled(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2458 (SvaExpr::Bits(ia), SvaExpr::Bits(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2459 (SvaExpr::Clog2(ia), SvaExpr::Clog2(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2460 (SvaExpr::CountBits(ia, ca), SvaExpr::CountBits(ib, cb)) =>
2461 ca == cb && sva_exprs_structurally_equivalent(ia, ib),
2462 (SvaExpr::IsUnbounded(ia), SvaExpr::IsUnbounded(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2463 (
2465 SvaExpr::GotoRepetition { body: ba, count: ca },
2466 SvaExpr::GotoRepetition { body: bb, count: cb },
2467 ) => ca == cb && sva_exprs_structurally_equivalent(ba, bb),
2468 (
2469 SvaExpr::NonConsecRepetition { body: ba, min: mna, max: mxa },
2470 SvaExpr::NonConsecRepetition { body: bb, min: mnb, max: mxb },
2471 ) => mna == mnb && mxa == mxb && sva_exprs_structurally_equivalent(ba, bb),
2472 (
2474 SvaExpr::AcceptOn { condition: ca, body: ba },
2475 SvaExpr::AcceptOn { condition: cb, body: bb },
2476 ) => {
2477 sva_exprs_structurally_equivalent(ca, cb)
2478 && sva_exprs_structurally_equivalent(ba, bb)
2479 }
2480 (
2481 SvaExpr::RejectOn { condition: ca, body: ba },
2482 SvaExpr::RejectOn { condition: cb, body: bb },
2483 ) => {
2484 sva_exprs_structurally_equivalent(ca, cb)
2485 && sva_exprs_structurally_equivalent(ba, bb)
2486 }
2487 (SvaExpr::PropertyNot(ia), SvaExpr::PropertyNot(ib)) => {
2489 sva_exprs_structurally_equivalent(ia, ib)
2490 }
2491 (SvaExpr::PropertyImplies(la, ra), SvaExpr::PropertyImplies(lb, rb)) => {
2492 sva_exprs_structurally_equivalent(la, lb)
2493 && sva_exprs_structurally_equivalent(ra, rb)
2494 }
2495 (SvaExpr::PropertyIff(la, ra), SvaExpr::PropertyIff(lb, rb)) => {
2496 sva_exprs_structurally_equivalent(la, lb)
2497 && sva_exprs_structurally_equivalent(ra, rb)
2498 }
2499 (SvaExpr::Always(ia), SvaExpr::Always(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2501 (
2502 SvaExpr::AlwaysBounded { body: ba, min: mna, max: mxa },
2503 SvaExpr::AlwaysBounded { body: bb, min: mnb, max: mxb },
2504 ) => mna == mnb && mxa == mxb && sva_exprs_structurally_equivalent(ba, bb),
2505 (
2506 SvaExpr::SAlwaysBounded { body: ba, min: mna, max: mxa },
2507 SvaExpr::SAlwaysBounded { body: bb, min: mnb, max: mxb },
2508 ) => mna == mnb && mxa == mxb && sva_exprs_structurally_equivalent(ba, bb),
2509 (
2510 SvaExpr::EventuallyBounded { body: ba, min: mna, max: mxa },
2511 SvaExpr::EventuallyBounded { body: bb, min: mnb, max: mxb },
2512 ) => mna == mnb && mxa == mxb && sva_exprs_structurally_equivalent(ba, bb),
2513 (
2514 SvaExpr::SEventuallyBounded { body: ba, min: mna, max: mxa },
2515 SvaExpr::SEventuallyBounded { body: bb, min: mnb, max: mxb },
2516 ) => mna == mnb && mxa == mxb && sva_exprs_structurally_equivalent(ba, bb),
2517 (
2518 SvaExpr::Until { lhs: la, rhs: ra, strong: sa, inclusive: ia },
2519 SvaExpr::Until { lhs: lb, rhs: rb, strong: sb, inclusive: ib },
2520 ) => {
2521 sa == sb && ia == ib
2522 && sva_exprs_structurally_equivalent(la, lb)
2523 && sva_exprs_structurally_equivalent(ra, rb)
2524 }
2525 (SvaExpr::Strong(ia), SvaExpr::Strong(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2527 (SvaExpr::Weak(ia), SvaExpr::Weak(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2528 (SvaExpr::SNexttime(ia, na), SvaExpr::SNexttime(ib, nb)) => {
2529 na == nb && sva_exprs_structurally_equivalent(ia, ib)
2530 }
2531 (
2532 SvaExpr::FollowedBy { antecedent: aa, consequent: ca, overlapping: oa },
2533 SvaExpr::FollowedBy { antecedent: ab, consequent: cb, overlapping: ob },
2534 ) => {
2535 oa == ob
2536 && sva_exprs_structurally_equivalent(aa, ab)
2537 && sva_exprs_structurally_equivalent(ca, cb)
2538 }
2539 (
2540 SvaExpr::PropertyCase { expression: ea, items: ia, default: da },
2541 SvaExpr::PropertyCase { expression: eb, items: ib, default: db },
2542 ) => {
2543 sva_exprs_structurally_equivalent(ea, eb)
2544 && ia.len() == ib.len()
2545 && ia.iter().zip(ib.iter()).all(|((va, pa), (vb, pb))| {
2546 va.len() == vb.len()
2547 && va.iter().zip(vb.iter()).all(|(a, b)| sva_exprs_structurally_equivalent(a, b))
2548 && sva_exprs_structurally_equivalent(pa, pb)
2549 })
2550 && match (da, db) {
2551 (Some(a), Some(b)) => sva_exprs_structurally_equivalent(a, b),
2552 (None, None) => true,
2553 _ => false,
2554 }
2555 }
2556 (
2557 SvaExpr::SyncAcceptOn { condition: ca, body: ba },
2558 SvaExpr::SyncAcceptOn { condition: cb, body: bb },
2559 ) => {
2560 sva_exprs_structurally_equivalent(ca, cb)
2561 && sva_exprs_structurally_equivalent(ba, bb)
2562 }
2563 (
2564 SvaExpr::SyncRejectOn { condition: ca, body: ba },
2565 SvaExpr::SyncRejectOn { condition: cb, body: bb },
2566 ) => {
2567 sva_exprs_structurally_equivalent(ca, cb)
2568 && sva_exprs_structurally_equivalent(ba, bb)
2569 }
2570 (SvaExpr::SequenceAnd(la, ra), SvaExpr::SequenceAnd(lb, rb)) => {
2572 sva_exprs_structurally_equivalent(la, lb)
2573 && sva_exprs_structurally_equivalent(ra, rb)
2574 }
2575 (SvaExpr::SequenceOr(la, ra), SvaExpr::SequenceOr(lb, rb)) => {
2576 sva_exprs_structurally_equivalent(la, lb)
2577 && sva_exprs_structurally_equivalent(ra, rb)
2578 }
2579 (
2581 SvaExpr::ImmediateAssert { expression: ea, deferred: da },
2582 SvaExpr::ImmediateAssert { expression: eb, deferred: db },
2583 ) => da == db && sva_exprs_structurally_equivalent(ea, eb),
2584 (SvaExpr::FieldAccess { signal: sa, field: fa }, SvaExpr::FieldAccess { signal: sb, field: fb }) => {
2586 fa == fb && sva_exprs_structurally_equivalent(sa, sb)
2587 }
2588 (SvaExpr::EnumLiteral { type_name: ta, value: va }, SvaExpr::EnumLiteral { type_name: tb, value: vb }) => {
2589 ta == tb && va == vb
2590 }
2591 (SvaExpr::Triggered(a), SvaExpr::Triggered(b)) => a == b,
2593 (SvaExpr::Matched(a), SvaExpr::Matched(b)) => a == b,
2594 (SvaExpr::BitAnd(la, ra), SvaExpr::BitAnd(lb, rb)) => {
2596 sva_exprs_structurally_equivalent(la, lb) && sva_exprs_structurally_equivalent(ra, rb)
2597 }
2598 (SvaExpr::BitOr(la, ra), SvaExpr::BitOr(lb, rb)) => {
2599 sva_exprs_structurally_equivalent(la, lb) && sva_exprs_structurally_equivalent(ra, rb)
2600 }
2601 (SvaExpr::BitXor(la, ra), SvaExpr::BitXor(lb, rb)) => {
2602 sva_exprs_structurally_equivalent(la, lb) && sva_exprs_structurally_equivalent(ra, rb)
2603 }
2604 (SvaExpr::BitNot(ia), SvaExpr::BitNot(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2605 (SvaExpr::ReductionAnd(ia), SvaExpr::ReductionAnd(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2606 (SvaExpr::ReductionOr(ia), SvaExpr::ReductionOr(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2607 (SvaExpr::ReductionXor(ia), SvaExpr::ReductionXor(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2608 (SvaExpr::BitSelect { signal: sa, index: ia }, SvaExpr::BitSelect { signal: sb, index: ib }) => {
2609 sva_exprs_structurally_equivalent(sa, sb) && sva_exprs_structurally_equivalent(ia, ib)
2610 }
2611 (SvaExpr::PartSelect { signal: sa, high: ha, low: la }, SvaExpr::PartSelect { signal: sb, high: hb, low: lb }) => {
2612 ha == hb && la == lb && sva_exprs_structurally_equivalent(sa, sb)
2613 }
2614 (SvaExpr::Concat(ia), SvaExpr::Concat(ib)) => {
2615 ia.len() == ib.len() && ia.iter().zip(ib.iter()).all(|(a, b)| sva_exprs_structurally_equivalent(a, b))
2616 }
2617 (SvaExpr::SequenceAction { expression: ea, assignments: aa },
2619 SvaExpr::SequenceAction { expression: eb, assignments: ab }) => {
2620 sva_exprs_structurally_equivalent(ea, eb)
2621 && aa.len() == ab.len()
2622 && aa.iter().zip(ab.iter()).all(|((na, ra), (nb, rb))|
2623 na == nb && sva_exprs_structurally_equivalent(ra, rb))
2624 }
2625 (SvaExpr::LocalVar(a), SvaExpr::LocalVar(b)) => a == b,
2626 (SvaExpr::ConstCast(ia), SvaExpr::ConstCast(ib)) => sva_exprs_structurally_equivalent(ia, ib),
2627 (SvaExpr::Clocked { clock: ca, edge: ea, body: ba },
2628 SvaExpr::Clocked { clock: cb, edge: eb, body: bb }) => {
2629 ca == cb && ea == eb && sva_exprs_structurally_equivalent(ba, bb)
2630 }
2631 (SvaExpr::ArrayMap { array: aa, iterator: ia, with_expr: wa },
2633 SvaExpr::ArrayMap { array: ab, iterator: ib, with_expr: wb }) => {
2634 ia == ib && sva_exprs_structurally_equivalent(aa, ab) && sva_exprs_structurally_equivalent(wa, wb)
2635 }
2636 (SvaExpr::TypeThis, SvaExpr::TypeThis) => true,
2637 (SvaExpr::RealConst(a), SvaExpr::RealConst(b)) => a.to_bits() == b.to_bits(),
2638 _ => false,
2639 }
2640}
2641
2642#[derive(Debug, Clone, Default)]
2651pub struct ElaborationContext {
2652 pub default_clocking: Option<String>,
2653 pub default_disable_iff: Option<SvaExpr>,
2654}
2655
2656pub fn elaborate_directives(
2664 directives: &[SvaDirective],
2665 ctx: &ElaborationContext,
2666) -> Vec<SvaDirective> {
2667 directives.iter().map(|d| {
2668 let mut elaborated = d.clone();
2669
2670 if elaborated.clock.is_none() {
2672 if let Some(ref default_clk) = ctx.default_clocking {
2673 elaborated.clock = Some(default_clk.clone());
2674 }
2675 }
2676
2677 if elaborated.disable_iff.is_none() {
2679 if let Some(ref default_dis) = ctx.default_disable_iff {
2680 elaborated.disable_iff = Some(default_dis.clone());
2681 }
2682 }
2683
2684 elaborated
2685 }).collect()
2686}
2687
2688pub fn resolve_let_instance(
2691 decls: &[LetDecl],
2692 name: &str,
2693 args: &[SvaExpr],
2694) -> Result<SvaExpr, SvaParseError> {
2695 let decl = decls.iter().find(|d| d.name == name).ok_or_else(|| SvaParseError {
2696 message: format!("undeclared let: '{}'", name),
2697 })?;
2698
2699 let required_count = decl.ports.iter().filter(|p| p.default.is_none()).count();
2700 if args.len() < required_count || args.len() > decl.ports.len() {
2701 return Err(SvaParseError {
2702 message: format!(
2703 "let '{}' expects {} to {} arguments, got {}",
2704 name, required_count, decl.ports.len(), args.len()
2705 ),
2706 });
2707 }
2708
2709 let mut result = decl.body.clone();
2710 for (i, port) in decl.ports.iter().enumerate() {
2711 let actual = if i < args.len() {
2712 args[i].clone()
2713 } else {
2714 port.default.clone().ok_or_else(|| SvaParseError {
2715 message: format!("missing required argument '{}' for let '{}'", port.name, name),
2716 })?
2717 };
2718 result = substitute_signal(&result, &port.name, &actual);
2719 }
2720 Ok(result)
2721}
2722
2723pub fn translate_dist_to_ranges(items: &[DistItem]) -> Vec<(u64, u64)> {
2726 items.iter().map(|item| {
2727 let max = item.max.unwrap_or(item.min);
2728 (item.min, max)
2729 }).collect()
2730}
2731
2732pub fn validate_dist(items: &[DistItem]) -> Result<(), SvaParseError> {
2734 if items.is_empty() {
2735 return Err(SvaParseError { message: "empty dist list".to_string() });
2736 }
2737 Ok(())
2738}
2739
2740pub fn resolve_checker(
2742 checker: &CheckerDecl,
2743 port_bindings: &[(String, SvaExpr)],
2744) -> Result<Vec<SvaDirective>, SvaParseError> {
2745 checker.assertions.iter().map(|directive| {
2746 let mut resolved_prop = directive.property.clone();
2747 for (port_name, actual) in port_bindings {
2748 resolved_prop = substitute_signal(&resolved_prop, port_name, actual);
2749 }
2750 Ok(SvaDirective {
2751 kind: directive.kind.clone(),
2752 property: resolved_prop,
2753 label: directive.label.clone(),
2754 clock: directive.clock.clone(),
2755 disable_iff: directive.disable_iff.clone(),
2756 action_pass: directive.action_pass.clone(),
2757 action_fail: directive.action_fail.clone(),
2758 })
2759 }).collect()
2760}
2761
2762pub fn checker_quantifier_structure(checker: &CheckerDecl) -> (Vec<&RandVar>, Vec<&RandVar>) {
2765 let const_vars: Vec<&RandVar> = checker.rand_vars.iter().filter(|v| v.is_const).collect();
2766 let nonconst_vars: Vec<&RandVar> = checker.rand_vars.iter().filter(|v| !v.is_const).collect();
2767 (const_vars, nonconst_vars)
2768}