1use num_bigint::BigUint;
8use serde::{Deserialize, Serialize};
9use std::path::PathBuf;
10
11mod format;
12mod vm;
13pub use format::{
14 DisplayFormatArg, FieldSpec, MAX_FIELD_WIDTH, format_display_arg, format_sized_display_arg,
15 format_sv_display_arg, format_veryl_display_arg, pad_veryl_field,
16};
17pub use vm::{CompiledExpr, TestbenchValue};
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
20pub struct StateLocation<A> {
21 pub address: A,
22 pub byte_offset: usize,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
26pub enum TestbenchOperator {
27 Add,
28 Sub,
29 Mul,
30 Div,
31 Rem,
32 Pow,
33 BitAnd,
34 BitOr,
35 BitXor,
36 BitXnor,
37 BitNand,
38 BitNor,
39 LogicShiftL,
40 LogicShiftR,
41 ArithShiftL,
42 ArithShiftR,
43 Eq,
44 EqWildcard,
45 Ne,
46 NeWildcard,
47 Less,
48 LessEq,
49 Greater,
50 GreaterEq,
51 LogicAnd,
52 LogicOr,
53 LogicNot,
54 BitNot,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
58pub struct SourceLocation {
59 pub file: String,
60 pub line: u32,
61 pub column: u32,
62}
63
64#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
65pub enum AssertMessage<Argument> {
66 Formatted {
67 template: String,
68 args: Vec<Argument>,
69 },
70 DynamicArgs(Vec<Argument>),
71}
72
73#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
74pub enum ClockCount<Expression> {
75 Static(u64),
76 Dynamic(Expression),
77}
78
79#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
80pub enum LoopBound<Expression> {
81 Static(usize),
82 Dynamic {
83 expr: Expression,
84 width: usize,
85 signed: bool,
86 },
87}
88
89#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
90pub enum ComponentParameterValue {
91 Bits { words: Vec<u64>, width: u32 },
92 String(String),
93}
94
95#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
96pub struct ComponentConnection {
97 pub port: String,
98 pub group: Option<String>,
99 pub member: Option<String>,
100 pub input: bool,
101 pub has_output: bool,
102 pub is_clock: bool,
103 pub is_reset: bool,
104 pub width: u32,
105}
106
107#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
108pub struct TestbenchComponent {
109 pub instance: String,
110 pub component: String,
111 pub params: Vec<(String, ComponentParameterValue)>,
112 pub connections: Vec<ComponentConnection>,
113 pub is_var_form: bool,
114 pub source: Option<SourceLocation>,
115}
116
117#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
118pub struct ComponentLibrary {
119 pub export: String,
120 pub type_name: String,
121 pub path: PathBuf,
122}
123
124#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
125pub struct ComponentConnectionBinding<Event, Signal, Expression> {
126 pub port: String,
127 pub input: Option<Expression>,
128 pub input_target: Option<TestbenchTarget<Signal, Expression>>,
132 pub output: Option<TestbenchTarget<Signal, Expression>>,
133 pub output_rtl_driven: bool,
134 pub event: Option<Event>,
135}
136
137#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
138pub struct ComponentBinding<Event, Signal, Expression> {
139 pub instance: String,
140 pub connections: Vec<ComponentConnectionBinding<Event, Signal, Expression>>,
141}
142
143#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
149pub enum TestbenchStatement<Event, Signal, Expression, Argument, Target = Signal> {
150 ClockNext {
151 clock_event: Event,
152 count: ClockCount<Expression>,
153 period: u64,
155 },
156 ResetAssert {
157 reset_signal: Signal,
158 reset_event: Option<Event>,
159 clock_event: Event,
160 duration: ClockCount<Expression>,
161 period: u64,
162 assert_value: u8,
163 deassert_value: u8,
164 },
165 Assert {
166 expr: Expression,
167 site_id: u32,
168 continue_on_fail: bool,
169 message: Option<AssertMessage<Argument>>,
170 location: Option<SourceLocation>,
171 },
172 Display {
173 message: Option<AssertMessage<Argument>>,
174 newline: bool,
175 },
176 If {
177 expr: Expression,
178 then_block: Vec<Self>,
179 else_block: Vec<Self>,
180 },
181 For {
182 loop_var: Option<(Signal, usize, bool)>,
183 start: LoopBound<Expression>,
184 end: LoopBound<Expression>,
185 inclusive: bool,
186 step: usize,
187 step_op: Option<TestbenchOperator>,
188 reverse: bool,
189 body: Vec<Self>,
190 },
191 Assign {
192 dst: Target,
193 expr: Expression,
194 },
195 RandomSeed {
196 handle: String,
197 value: Expression,
198 },
199 RandomGet {
200 handle: String,
201 width: u32,
202 signed: bool,
203 ret: Option<Target>,
204 },
205 RandomGetRange {
206 handle: String,
207 min: Expression,
208 max: Expression,
209 width: u32,
210 signed: bool,
211 ret: Option<Target>,
212 },
213 RandomGetSeed {
214 handle: String,
215 ret: Option<Target>,
216 },
217 ComponentMethod {
218 instance: String,
219 method: String,
220 args: Vec<Argument>,
221 ret: Option<Target>,
222 ret_width: Option<u32>,
223 ret_signed: bool,
224 ret_strict: bool,
225 },
226 Break,
227 Finish,
228 WriteMemory {
230 signal: Signal,
231 writes: Vec<celox_design::InitialStateWriteRun>,
232 },
233}
234
235#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
236pub struct SemanticSignal<A> {
237 pub address: A,
238 pub width: usize,
239}
240
241#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
242pub struct TestbenchSelection<Expression> {
243 pub offset: Expression,
244 pub width: usize,
245}
246
247#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
248pub struct TestbenchTarget<Signal, Expression> {
249 pub signal: Signal,
250 pub selection: Option<TestbenchSelection<Expression>>,
251 pub width: usize,
252}
253
254#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
255pub struct SemanticArgument<A> {
256 pub expr: ExprBytecode<StateLocation<A>>,
257 pub width: usize,
258 pub signed: bool,
259 pub is_string: bool,
260}
261
262pub type SemanticStatement<A> = TestbenchStatement<
263 A,
264 SemanticSignal<A>,
265 ExprBytecode<StateLocation<A>>,
266 SemanticArgument<A>,
267 TestbenchTarget<SemanticSignal<A>, ExprBytecode<StateLocation<A>>>,
268>;
269pub type SemanticComponentBinding<A> =
270 ComponentBinding<A, SemanticSignal<A>, ExprBytecode<StateLocation<A>>>;
271
272#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
273pub struct TestbenchProgram<A> {
274 statements: Vec<SemanticStatement<A>>,
275 process_lengths: Vec<usize>,
276 private_variables: Vec<A>,
277 random_seed: Option<u64>,
278 components: Vec<TestbenchComponent>,
279 component_libraries: Vec<ComponentLibrary>,
280 component_file_base: Option<PathBuf>,
281 component_bindings: Vec<SemanticComponentBinding<A>>,
282}
283
284impl<A> Default for TestbenchProgram<A> {
285 fn default() -> Self {
286 Self {
287 statements: Vec::new(),
288 process_lengths: Vec::new(),
289 private_variables: Vec::new(),
290 random_seed: None,
291 components: Vec::new(),
292 component_libraries: Vec::new(),
293 component_file_base: None,
294 component_bindings: Vec::new(),
295 }
296 }
297}
298
299impl<A> TestbenchProgram<A> {
300 pub fn new(statements: Vec<SemanticStatement<A>>) -> Self {
301 Self {
302 process_lengths: vec![statements.len()],
303 private_variables: Vec::new(),
304 statements,
305 random_seed: None,
306 components: Vec::new(),
307 component_libraries: Vec::new(),
308 component_file_base: None,
309 component_bindings: Vec::new(),
310 }
311 }
312
313 pub fn from_processes(processes: Vec<Vec<SemanticStatement<A>>>) -> Self {
315 let lengths = processes.iter().map(Vec::len).collect();
316 let mut program = Self::new(processes.into_iter().flatten().collect());
317 program.process_lengths = lengths;
318 program
319 }
320
321 pub fn with_private_variables(mut self, variables: Vec<A>) -> Self {
323 self.private_variables = variables;
324 self
325 }
326
327 pub fn private_variables(&self) -> &[A] {
328 &self.private_variables
329 }
330
331 pub fn process_lengths(&self) -> &[usize] {
332 &self.process_lengths
333 }
334
335 pub fn with_random_seed(mut self, random_seed: u64) -> Self {
336 self.random_seed = Some(random_seed);
337 self
338 }
339
340 pub fn with_random_seed_option(mut self, random_seed: Option<u64>) -> Self {
341 self.random_seed = random_seed;
342 self
343 }
344
345 pub fn statements(&self) -> &[SemanticStatement<A>] {
346 &self.statements
347 }
348
349 pub fn into_statements(self) -> Vec<SemanticStatement<A>> {
350 self.statements
351 }
352
353 pub fn random_seed(&self) -> u64 {
354 self.random_seed.unwrap_or_default()
355 }
356
357 pub fn configured_random_seed(&self) -> Option<u64> {
358 self.random_seed
359 }
360
361 pub fn with_components(mut self, components: Vec<TestbenchComponent>) -> Self {
362 self.components = components;
363 self
364 }
365
366 pub fn with_component_runtime(
367 mut self,
368 libraries: Vec<ComponentLibrary>,
369 file_base: Option<PathBuf>,
370 bindings: Vec<SemanticComponentBinding<A>>,
371 ) -> Self {
372 self.component_libraries = libraries;
373 self.component_file_base = file_base;
374 self.component_bindings = bindings;
375 self
376 }
377
378 pub fn components(&self) -> &[TestbenchComponent] {
379 &self.components
380 }
381
382 pub fn component_libraries(&self) -> &[ComponentLibrary] {
383 &self.component_libraries
384 }
385
386 pub fn component_file_base(&self) -> Option<&std::path::Path> {
387 self.component_file_base.as_deref()
388 }
389
390 pub fn component_bindings(&self) -> &[SemanticComponentBinding<A>] {
391 &self.component_bindings
392 }
393
394 pub fn try_map_paths<E>(
398 &mut self,
399 map: &mut impl FnMut(&std::path::Path) -> Result<PathBuf, E>,
400 ) -> Result<(), E> {
401 fn location<E>(
402 source: &mut Option<SourceLocation>,
403 map: &mut impl FnMut(&std::path::Path) -> Result<PathBuf, E>,
404 ) -> Result<(), E> {
405 if let Some(source) = source
406 && !source.file.is_empty()
407 {
408 source.file = map(std::path::Path::new(&source.file))?
409 .to_string_lossy()
410 .into_owned();
411 }
412 Ok(())
413 }
414 fn statements<A, E>(
415 block: &mut [SemanticStatement<A>],
416 map: &mut impl FnMut(&std::path::Path) -> Result<PathBuf, E>,
417 ) -> Result<(), E> {
418 for statement in block {
419 match statement {
420 TestbenchStatement::Assert {
421 location: source, ..
422 } => location(source, map)?,
423 TestbenchStatement::If {
424 then_block,
425 else_block,
426 ..
427 } => {
428 statements(then_block, map)?;
429 statements(else_block, map)?;
430 }
431 TestbenchStatement::For { body, .. } => statements(body, map)?,
432 _ => {}
433 }
434 }
435 Ok(())
436 }
437 for library in &mut self.component_libraries {
438 library.path = map(&library.path)?;
439 }
440 if let Some(base) = &mut self.component_file_base {
441 *base = map(base)?;
442 }
443 for component in &mut self.components {
444 location(&mut component.source, map)?;
445 }
446 statements(&mut self.statements, map)
447 }
448
449 pub fn is_empty(&self) -> bool {
450 self.statements.is_empty()
451 }
452}
453
454#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
455pub enum ExprOpcode<L = usize> {
456 ConstU64(u64),
457 ConstWide(BigUint),
458 LoadU64 {
459 location: L,
460 byte_size: usize,
461 mask: u64,
462 },
463 LoadWide {
464 location: L,
465 byte_size: usize,
466 width: usize,
467 },
468 BinOp(TestbenchOperator),
469 TypedBinOp {
470 op: TestbenchOperator,
471 lhs_width: usize,
472 rhs_width: usize,
473 result_width: usize,
474 lhs_signed: bool,
475 rhs_signed: bool,
476 },
477 TypedUnary {
478 op: TestbenchOperator,
479 operand_width: usize,
480 result_width: usize,
481 },
482 Resize {
483 source_width: usize,
484 target_width: usize,
485 signed: bool,
486 },
487 ConcatPart {
488 part_width: usize,
489 result_width: usize,
490 },
491 Ternary {
492 then_len: usize,
493 else_len: usize,
494 },
495 LoadIndexed {
496 location: L,
497 stride_bits: usize,
498 base_bit_offset: usize,
499 element_width: usize,
500 },
501 LoadBitSelect {
502 location: L,
503 base_byte_size: usize,
504 select_width: usize,
505 },
506 StoreU64 {
507 location: L,
508 byte_size: usize,
509 },
510}
511
512#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
513pub struct ExprBytecode<L = usize> {
514 ops: Vec<ExprOpcode<L>>,
515}
516
517impl<L> ExprBytecode<L> {
518 pub fn new(ops: Vec<ExprOpcode<L>>) -> Self {
519 Self { ops }
520 }
521
522 pub fn ops(&self) -> &[ExprOpcode<L>] {
523 &self.ops
524 }
525
526 pub fn is_empty(&self) -> bool {
527 self.ops.is_empty()
528 }
529}
530
531#[derive(Clone, Debug, PartialEq, Eq)]
532pub struct BindError<A> {
533 pub address: A,
534}
535
536pub struct ExecutableArgument {
537 pub expr: CompiledExpr,
538 pub width: usize,
539 pub signed: bool,
540 pub is_string: bool,
541}
542
543pub type ExecutableAssertMessage = AssertMessage<ExecutableArgument>;
544pub type ExecutableClockCount = ClockCount<CompiledExpr>;
545pub type ExecutableLoopBound = LoopBound<CompiledExpr>;
546pub type ExecutableStatement<Event, Signal> = TestbenchStatement<
547 Event,
548 Signal,
549 CompiledExpr,
550 ExecutableArgument,
551 TestbenchTarget<Signal, CompiledExpr>,
552>;
553pub type ExecutableComponentBinding<Event, Signal> = ComponentBinding<Event, Signal, CompiledExpr>;
554
555pub struct ExecutableTestbench<Event, Signal> {
556 statements: Vec<ExecutableStatement<Event, Signal>>,
557 process_lengths: Vec<usize>,
558 private_signals: Vec<Signal>,
559 random_seed: Option<u64>,
560 components: Vec<TestbenchComponent>,
561 component_libraries: Vec<ComponentLibrary>,
562 component_file_base: Option<PathBuf>,
563 component_bindings: Vec<ExecutableComponentBinding<Event, Signal>>,
564}
565
566impl<Event, Signal> ExecutableTestbench<Event, Signal> {
567 pub fn new(statements: Vec<ExecutableStatement<Event, Signal>>, random_seed: u64) -> Self {
568 Self::new_with_random_seed(statements, Some(random_seed))
569 }
570
571 pub fn new_with_random_seed(
572 statements: Vec<ExecutableStatement<Event, Signal>>,
573 random_seed: Option<u64>,
574 ) -> Self {
575 Self {
576 process_lengths: vec![statements.len()],
577 private_signals: Vec::new(),
578 statements,
579 random_seed,
580 components: Vec::new(),
581 component_libraries: Vec::new(),
582 component_file_base: None,
583 component_bindings: Vec::new(),
584 }
585 }
586
587 pub fn with_component_runtime(
588 mut self,
589 components: Vec<TestbenchComponent>,
590 libraries: Vec<ComponentLibrary>,
591 file_base: Option<PathBuf>,
592 bindings: Vec<ExecutableComponentBinding<Event, Signal>>,
593 ) -> Self {
594 self.components = components;
595 self.component_libraries = libraries;
596 self.component_file_base = file_base;
597 self.component_bindings = bindings;
598 self
599 }
600
601 pub fn from_processes(
603 processes: Vec<Vec<ExecutableStatement<Event, Signal>>>,
604 random_seed: Option<u64>,
605 ) -> Self {
606 let lengths = processes.iter().map(Vec::len).collect();
607 let mut program =
608 Self::new_with_random_seed(processes.into_iter().flatten().collect(), random_seed);
609 program.process_lengths = lengths;
610 program
611 }
612
613 pub fn with_private_signals(mut self, signals: Vec<Signal>) -> Self {
614 self.private_signals = signals;
615 self
616 }
617
618 pub fn private_signals(&self) -> &[Signal] {
619 &self.private_signals
620 }
621
622 pub fn processes(&self) -> impl Iterator<Item = &[ExecutableStatement<Event, Signal>]> {
623 self.process_lengths.iter().scan(0, |offset, &length| {
624 let start = *offset;
625 *offset += length;
626 Some(&self.statements[start..*offset])
627 })
628 }
629
630 pub fn statements(&self) -> &[ExecutableStatement<Event, Signal>] {
631 &self.statements
632 }
633
634 pub fn into_statements(self) -> Vec<ExecutableStatement<Event, Signal>> {
635 self.statements
636 }
637
638 pub fn random_seed(&self) -> u64 {
639 self.random_seed.unwrap_or_default()
640 }
641
642 pub fn configured_random_seed(&self) -> Option<u64> {
643 self.random_seed
644 }
645
646 pub fn components(&self) -> &[TestbenchComponent] {
647 &self.components
648 }
649
650 pub fn component_libraries(&self) -> &[ComponentLibrary] {
651 &self.component_libraries
652 }
653
654 pub fn component_file_base(&self) -> Option<&std::path::Path> {
655 self.component_file_base.as_deref()
656 }
657
658 pub fn component_bindings(&self) -> &[ExecutableComponentBinding<Event, Signal>] {
659 &self.component_bindings
660 }
661}
662
663impl<A> ExprBytecode<StateLocation<A>> {
664 pub fn bind_with(
665 self,
666 mut resolve: impl FnMut(&A) -> Option<usize>,
667 ) -> Result<ExprBytecode, BindError<A>> {
668 let mut ops = Vec::with_capacity(self.ops.len());
669 for op in self.ops {
670 let bound = match op {
671 ExprOpcode::ConstU64(value) => ExprOpcode::ConstU64(value),
672 ExprOpcode::ConstWide(value) => ExprOpcode::ConstWide(value),
673 ExprOpcode::LoadU64 {
674 location,
675 byte_size,
676 mask,
677 } => ExprOpcode::LoadU64 {
678 location: bind_location(location, &mut resolve)?,
679 byte_size,
680 mask,
681 },
682 ExprOpcode::LoadWide {
683 location,
684 byte_size,
685 width,
686 } => ExprOpcode::LoadWide {
687 location: bind_location(location, &mut resolve)?,
688 byte_size,
689 width,
690 },
691 ExprOpcode::BinOp(op) => ExprOpcode::BinOp(op),
692 ExprOpcode::TypedBinOp {
693 op,
694 lhs_width,
695 rhs_width,
696 result_width,
697 lhs_signed,
698 rhs_signed,
699 } => ExprOpcode::TypedBinOp {
700 op,
701 lhs_width,
702 rhs_width,
703 result_width,
704 lhs_signed,
705 rhs_signed,
706 },
707 ExprOpcode::TypedUnary {
708 op,
709 operand_width,
710 result_width,
711 } => ExprOpcode::TypedUnary {
712 op,
713 operand_width,
714 result_width,
715 },
716 ExprOpcode::Resize {
717 source_width,
718 target_width,
719 signed,
720 } => ExprOpcode::Resize {
721 source_width,
722 target_width,
723 signed,
724 },
725 ExprOpcode::ConcatPart {
726 part_width,
727 result_width,
728 } => ExprOpcode::ConcatPart {
729 part_width,
730 result_width,
731 },
732 ExprOpcode::Ternary { then_len, else_len } => {
733 ExprOpcode::Ternary { then_len, else_len }
734 }
735 ExprOpcode::LoadIndexed {
736 location,
737 stride_bits,
738 base_bit_offset,
739 element_width,
740 } => ExprOpcode::LoadIndexed {
741 location: bind_location(location, &mut resolve)?,
742 stride_bits,
743 base_bit_offset,
744 element_width,
745 },
746 ExprOpcode::LoadBitSelect {
747 location,
748 base_byte_size,
749 select_width,
750 } => ExprOpcode::LoadBitSelect {
751 location: bind_location(location, &mut resolve)?,
752 base_byte_size,
753 select_width,
754 },
755 ExprOpcode::StoreU64 {
756 location,
757 byte_size,
758 } => ExprOpcode::StoreU64 {
759 location: bind_location(location, &mut resolve)?,
760 byte_size,
761 },
762 };
763 ops.push(bound);
764 }
765 Ok(ExprBytecode::new(ops))
766 }
767}
768
769fn bind_location<A>(
770 location: StateLocation<A>,
771 resolve: &mut impl FnMut(&A) -> Option<usize>,
772) -> Result<usize, BindError<A>> {
773 let Some(base) = resolve(&location.address) else {
774 return Err(BindError {
775 address: location.address,
776 });
777 };
778 Ok(base + location.byte_offset)
779}
780
781#[cfg(test)]
782mod tests {
783 use super::*;
784
785 #[test]
786 fn bytecode_uses_source_independent_operators() {
787 let code: ExprBytecode = ExprBytecode::new(vec![
788 ExprOpcode::ConstU64(1),
789 ExprOpcode::ConstU64(2),
790 ExprOpcode::TypedBinOp {
791 op: TestbenchOperator::Add,
792 lhs_width: 8,
793 rhs_width: 8,
794 result_width: 8,
795 lhs_signed: false,
796 rhs_signed: false,
797 },
798 ]);
799 assert_eq!(code.ops().len(), 3);
800 assert!(!code.is_empty());
801 }
802
803 #[test]
804 fn semantic_state_locations_bind_after_layout() {
805 let code = ExprBytecode::new(vec![ExprOpcode::LoadU64 {
806 location: StateLocation {
807 address: 7u32,
808 byte_offset: 3,
809 },
810 byte_size: 2,
811 mask: 0xffff,
812 }]);
813
814 let bound = code
815 .bind_with(|address| (*address == 7).then_some(100))
816 .unwrap();
817 assert_eq!(
818 bound.ops(),
819 &[ExprOpcode::LoadU64 {
820 location: 103,
821 byte_size: 2,
822 mask: 0xffff,
823 }]
824 );
825 }
826
827 #[test]
828 fn binding_reports_an_unmapped_semantic_address() {
829 let code = ExprBytecode::new(vec![ExprOpcode::StoreU64 {
830 location: StateLocation {
831 address: 9u32,
832 byte_offset: 0,
833 },
834 byte_size: 1,
835 }]);
836
837 assert_eq!(code.bind_with(|_| None), Err(BindError { address: 9 }));
838 }
839
840 #[test]
841 fn control_program_is_independent_of_runtime_handles() {
842 type SemanticStatement = TestbenchStatement<u32, u32, ExprBytecode<StateLocation<u32>>, ()>;
843
844 let statement = SemanticStatement::If {
845 expr: ExprBytecode::new(vec![ExprOpcode::LoadU64 {
846 location: StateLocation {
847 address: 1,
848 byte_offset: 0,
849 },
850 byte_size: 1,
851 mask: 1,
852 }]),
853 then_block: vec![SemanticStatement::ClockNext {
854 clock_event: 2,
855 count: ClockCount::Static(1),
856 period: 2,
857 }],
858 else_block: vec![SemanticStatement::Finish],
859 };
860
861 assert!(matches!(statement, SemanticStatement::If { .. }));
862 }
863}