1use std::collections::BTreeMap;
8
9use fxhash::FxHashMap;
10use num_bigint::BigUint;
11use serde::{Deserialize, Serialize};
12use thiserror::Error;
13
14pub const ARTIFACT_FORMAT_VERSION: u32 = 1;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
19pub struct SignalId(u32);
20
21impl SignalId {
22 pub const fn index(self) -> u32 {
24 self.0
25 }
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
30pub struct ExprId(u32);
31
32impl ExprId {
33 pub const fn index(self) -> u32 {
35 self.0
36 }
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
41#[non_exhaustive]
42pub enum Direction {
43 Input,
44 Output,
45 Inout,
47 Internal,
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
52pub enum Edge {
53 Posedge,
54 Negedge,
55}
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
59pub enum ActiveLevel {
60 High,
61 Low,
62}
63
64#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
66pub struct ValueType {
67 width: usize,
68 signed: bool,
69 four_state: bool,
70}
71
72impl ValueType {
73 pub fn bits(width: usize) -> Result<Self, BuildError> {
74 Self::new(width, false, false)
75 }
76
77 pub fn logic(width: usize) -> Result<Self, BuildError> {
78 Self::new(width, false, true)
79 }
80
81 pub fn new(width: usize, signed: bool, four_state: bool) -> Result<Self, BuildError> {
82 if width == 0 {
83 return Err(BuildError::ZeroWidth);
84 }
85 Ok(Self {
86 width,
87 signed,
88 four_state,
89 })
90 }
91
92 pub const fn width(self) -> usize {
93 self.width
94 }
95
96 pub const fn is_signed(self) -> bool {
97 self.signed
98 }
99
100 pub const fn is_four_state(self) -> bool {
101 self.four_state
102 }
103}
104
105#[derive(Clone, Debug, Serialize, Deserialize)]
107pub struct Signal {
108 id: SignalId,
109 name: String,
110 direction: Direction,
111 value_type: ValueType,
112 initial: Option<Constant>,
113}
114
115impl Signal {
116 pub const fn id(&self) -> SignalId {
117 self.id
118 }
119
120 pub fn name(&self) -> &str {
121 &self.name
122 }
123
124 pub const fn direction(&self) -> Direction {
125 self.direction
126 }
127
128 pub const fn value_type(&self) -> ValueType {
129 self.value_type
130 }
131
132 pub fn initial(&self) -> Option<&Constant> {
133 self.initial.as_ref()
134 }
135}
136
137#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
139pub struct SignalSlice {
140 signal: SignalId,
141 lsb: usize,
142 width: usize,
143}
144
145impl SignalSlice {
146 pub const fn signal(self) -> SignalId {
147 self.signal
148 }
149
150 pub const fn lsb(self) -> usize {
151 self.lsb
152 }
153
154 pub const fn width(self) -> usize {
155 self.width
156 }
157}
158
159#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
161pub struct Constant {
162 payload: BigUint,
163 mask: BigUint,
164 value_type: ValueType,
165}
166
167impl Constant {
168 pub fn new(payload: BigUint, mask: BigUint, value_type: ValueType) -> Self {
169 let bit_mask = (BigUint::from(1u8) << value_type.width()) - BigUint::from(1u8);
170 let mask = if value_type.is_four_state() {
171 mask & &bit_mask
172 } else {
173 BigUint::default()
174 };
175 Self {
176 payload: payload & &bit_mask,
177 mask,
178 value_type,
179 }
180 }
181
182 pub fn two_state(payload: impl Into<BigUint>, width: usize) -> Result<Self, BuildError> {
183 Ok(Self::new(
184 payload.into(),
185 BigUint::default(),
186 ValueType::bits(width)?,
187 ))
188 }
189
190 pub fn four_state(
191 payload: impl Into<BigUint>,
192 mask: impl Into<BigUint>,
193 width: usize,
194 ) -> Result<Self, BuildError> {
195 Ok(Self::new(
196 payload.into(),
197 mask.into(),
198 ValueType::logic(width)?,
199 ))
200 }
201
202 pub fn payload(&self) -> &BigUint {
203 &self.payload
204 }
205
206 pub fn mask(&self) -> &BigUint {
207 &self.mask
208 }
209
210 pub const fn value_type(&self) -> ValueType {
211 self.value_type
212 }
213}
214
215fn signal_name_prefixes(name: &str) -> impl Iterator<Item = &str> {
216 name.match_indices('.').map(|(index, _)| &name[..index])
217}
218
219fn insert_driver_target(
220 driver_ranges: &mut FxHashMap<SignalId, BTreeMap<usize, usize>>,
221 target: SignalSlice,
222 signal_name: &str,
223) -> Result<(), BuildError> {
224 let end = target.lsb + target.width;
225 let ranges = driver_ranges.entry(target.signal).or_default();
226 if ranges
227 .range(..end)
228 .next_back()
229 .is_some_and(|(_, existing_end)| *existing_end > target.lsb)
230 {
231 return Err(BuildError::OverlappingDrivers {
232 name: signal_name.to_string(),
233 });
234 }
235 ranges.insert(target.lsb, end);
236 Ok(())
237}
238
239fn validate_constant_state(value: &Constant) -> Result<(), BuildError> {
240 let width = value.value_type().width() as u64;
241 if value.payload().bits() > width || value.mask().bits() > width {
242 return Err(BuildError::ConstantOutOfRange {
243 width: value.value_type().width(),
244 });
245 }
246 if !value.value_type().is_four_state() && value.mask() != &BigUint::default() {
247 return Err(BuildError::TwoStateConstantMask);
248 }
249 Ok(())
250}
251
252#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
254#[non_exhaustive]
255pub enum BinaryOp {
256 Add,
257 Sub,
258 Mul,
259 DivUnsigned,
260 DivSigned,
261 RemUnsigned,
262 RemSigned,
263 And,
264 Or,
265 Xor,
266 ShiftLeft,
267 ShiftRight,
268 ArithmeticShiftRight,
269 Equal,
270 NotEqual,
271 CaseEqual,
272 CaseNotEqual,
273 LessUnsigned,
274 LessSigned,
275 LessEqualUnsigned,
276 LessEqualSigned,
277 GreaterUnsigned,
278 GreaterSigned,
279 GreaterEqualUnsigned,
280 GreaterEqualSigned,
281 LogicAnd,
282 LogicOr,
283}
284
285#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
287#[non_exhaustive]
288pub enum UnaryOp {
289 ToTwoState,
290 Negate,
291 BitNot,
292 LogicNot,
293 ReduceAnd,
294 ReduceOr,
295 ReduceXor,
296 PopCount,
297 CountLeadingZeros,
298 CountTrailingZeros,
299}
300
301#[derive(Clone, Debug, Serialize, Deserialize)]
304#[non_exhaustive]
305pub enum ExprNode {
306 Signal(SignalSlice),
307 Constant(Constant),
308 Binary {
309 op: BinaryOp,
310 lhs: ExprId,
311 rhs: ExprId,
312 },
313 Unary {
314 op: UnaryOp,
315 input: ExprId,
316 },
317 Mux {
318 condition: ExprId,
319 then_expr: ExprId,
320 else_expr: ExprId,
321 },
322 Concat(Vec<ExprId>),
323 Slice {
324 input: ExprId,
325 lsb: usize,
326 },
327}
328
329#[derive(Clone, Debug, Serialize, Deserialize)]
331pub struct Expression {
332 id: ExprId,
333 node: ExprNode,
334 value_type: ValueType,
335}
336
337impl Expression {
338 pub const fn id(&self) -> ExprId {
339 self.id
340 }
341
342 pub fn node(&self) -> &ExprNode {
343 &self.node
344 }
345
346 pub const fn value_type(&self) -> ValueType {
347 self.value_type
348 }
349}
350
351#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
353pub struct Assignment {
354 target: SignalSlice,
355 value: ExprId,
356}
357
358impl Assignment {
359 pub const fn target(self) -> SignalSlice {
360 self.target
361 }
362
363 pub const fn value(self) -> ExprId {
364 self.value
365 }
366}
367
368#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
370pub struct AsyncReset {
371 signal: SignalId,
372 active: ActiveLevel,
373 value: ExprId,
374}
375
376impl AsyncReset {
377 pub const fn signal(self) -> SignalId {
378 self.signal
379 }
380
381 pub const fn active(self) -> ActiveLevel {
382 self.active
383 }
384
385 pub const fn value(self) -> ExprId {
386 self.value
387 }
388}
389
390#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
392pub struct Enable {
393 signal: SignalId,
394 active: ActiveLevel,
395}
396
397impl Enable {
398 pub const fn signal(self) -> SignalId {
399 self.signal
400 }
401
402 pub const fn active(self) -> ActiveLevel {
403 self.active
404 }
405}
406
407#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
409pub struct Register {
410 target: SignalSlice,
411 next: ExprId,
412 clock: SignalId,
413 edge: Edge,
414 async_reset: Option<AsyncReset>,
415 enable: Option<Enable>,
416}
417
418impl Register {
419 pub const fn target(self) -> SignalSlice {
420 self.target
421 }
422
423 pub const fn next(self) -> ExprId {
424 self.next
425 }
426
427 pub const fn clock(self) -> SignalId {
428 self.clock
429 }
430
431 pub const fn edge(self) -> Edge {
432 self.edge
433 }
434
435 pub const fn async_reset(self) -> Option<AsyncReset> {
436 self.async_reset
437 }
438
439 pub const fn enable(self) -> Option<Enable> {
440 self.enable
441 }
442}
443
444#[derive(Clone, Debug, Serialize, Deserialize)]
448pub struct FrontendArtifact {
449 format_version: u32,
450 module_name: String,
451 signals: Vec<Signal>,
452 expressions: Vec<Expression>,
453 assignments: Vec<Assignment>,
454 registers: Vec<Register>,
455 port_order: Vec<SignalId>,
456}
457
458impl FrontendArtifact {
459 pub const fn format_version(&self) -> u32 {
460 self.format_version
461 }
462
463 pub fn to_json(&self) -> Result<String, ArtifactJsonError> {
465 Ok(serde_json::to_string(self)?)
466 }
467
468 pub fn from_json(json: &str) -> Result<Self, ArtifactJsonError> {
470 let artifact: Self = serde_json::from_str(json)?;
471 if artifact.format_version != ARTIFACT_FORMAT_VERSION {
472 return Err(ArtifactJsonError::UnsupportedVersion {
473 expected: ARTIFACT_FORMAT_VERSION,
474 actual: artifact.format_version,
475 });
476 }
477 artifact.validate()?;
478 Ok(artifact)
479 }
480
481 pub fn validate(&self) -> Result<(), BuildError> {
484 if self.module_name.is_empty() {
485 return Err(BuildError::EmptyModuleName);
486 }
487 let mut names: FxHashMap<String, SignalId> = FxHashMap::default();
488 let mut namespace_prefixes: FxHashMap<String, String> = FxHashMap::default();
489 for (index, signal) in self.signals.iter().enumerate() {
490 if signal.id.index() as usize != index {
491 return Err(BuildError::InvalidSignalIdentity {
492 expected: index as u32,
493 actual: signal.id.index(),
494 });
495 }
496 if signal.name.is_empty() {
497 return Err(BuildError::EmptySignalName);
498 }
499 if matches!(signal.direction, Direction::Inout) {
500 return Err(BuildError::UnsupportedInout {
501 name: signal.name.clone(),
502 });
503 }
504 if names.contains_key(&signal.name) {
505 return Err(BuildError::DuplicateSignal(signal.name.clone()));
506 }
507 if let Some(existing) = namespace_prefixes.get(&signal.name) {
508 return Err(BuildError::SignalNamespaceCollision {
509 first: existing.clone(),
510 second: signal.name.clone(),
511 });
512 }
513 if let Some(existing) =
514 signal_name_prefixes(&signal.name).find(|prefix| names.contains_key(*prefix))
515 {
516 return Err(BuildError::SignalNamespaceCollision {
517 first: existing.to_string(),
518 second: signal.name.clone(),
519 });
520 }
521 names.insert(signal.name.clone(), signal.id);
522 for prefix in signal_name_prefixes(&signal.name) {
523 namespace_prefixes
524 .entry(prefix.to_string())
525 .or_insert_with(|| signal.name.clone());
526 }
527 if signal.value_type.width() == 0 {
528 return Err(BuildError::ZeroWidth);
529 }
530 if let Some(initial) = &signal.initial {
531 validate_constant_state(initial)?;
532 if initial.value_type().width() != signal.value_type.width() {
533 return Err(BuildError::WidthMismatch {
534 expected: signal.value_type.width(),
535 actual: initial.value_type().width(),
536 });
537 }
538 }
539 }
540 let validate_slice = |slice: SignalSlice| -> Result<(), BuildError> {
541 let signal = self
542 .signal(slice.signal)
543 .ok_or(BuildError::UnknownSignal(slice.signal.index()))?;
544 if slice.width == 0 {
545 return Err(BuildError::ZeroWidth);
546 }
547 if slice
548 .lsb
549 .checked_add(slice.width)
550 .is_none_or(|end| end > signal.value_type.width())
551 {
552 return Err(BuildError::InvalidSlice {
553 lsb: slice.lsb,
554 width: slice.width,
555 signal_width: signal.value_type.width(),
556 });
557 }
558 Ok(())
559 };
560 for (index, expression) in self.expressions.iter().enumerate() {
561 if expression.id.index() as usize != index {
562 return Err(BuildError::InvalidExpressionIdentity {
563 expected: index as u32,
564 actual: expression.id.index(),
565 });
566 }
567 if expression.value_type.width() == 0 {
568 return Err(BuildError::ZeroWidth);
569 }
570 let mut references = Vec::new();
571 match &expression.node {
572 ExprNode::Signal(slice) => {
573 validate_slice(*slice)?;
574 let signal_type = self
575 .signal(slice.signal)
576 .ok_or(BuildError::UnknownSignal(slice.signal.index()))?
577 .value_type();
578 let expected_type = ValueType::new(
579 slice.width,
580 signal_type.is_signed() && slice.width == signal_type.width(),
581 signal_type.is_four_state(),
582 )?;
583 if expression.value_type() != expected_type {
584 return Err(BuildError::TypeMismatch {
585 expected: expected_type,
586 actual: expression.value_type(),
587 });
588 }
589 }
590 ExprNode::Constant(value) => {
591 validate_constant_state(value)?;
592 if value.value_type() != expression.value_type() {
593 return Err(BuildError::TypeMismatch {
594 expected: value.value_type(),
595 actual: expression.value_type(),
596 });
597 }
598 }
599 ExprNode::Binary { lhs, rhs, .. } => references.extend([*lhs, *rhs]),
600 ExprNode::Unary { input, .. } => {
601 references.push(*input);
602 }
603 ExprNode::Slice { input, lsb } => {
604 if input.index() as usize >= index {
605 return Err(BuildError::ForwardExpressionReference {
606 expression: expression.id.index(),
607 referenced: input.index(),
608 });
609 }
610 let input_type = self
611 .expression(*input)
612 .ok_or(BuildError::UnknownExpression(input.index()))?
613 .value_type();
614 if lsb
615 .checked_add(expression.value_type().width())
616 .is_none_or(|end| end > input_type.width())
617 {
618 return Err(BuildError::InvalidSlice {
619 lsb: *lsb,
620 width: expression.value_type().width(),
621 signal_width: input_type.width(),
622 });
623 }
624 let expected_type = ValueType::new(
625 expression.value_type().width(),
626 false,
627 input_type.is_four_state(),
628 )?;
629 if expression.value_type() != expected_type {
630 return Err(BuildError::TypeMismatch {
631 expected: expected_type,
632 actual: expression.value_type(),
633 });
634 }
635 }
636 ExprNode::Mux {
637 condition,
638 then_expr,
639 else_expr,
640 } => {
641 for reference in [*condition, *then_expr, *else_expr] {
642 if reference.index() as usize >= index {
643 return Err(BuildError::ForwardExpressionReference {
644 expression: expression.id.index(),
645 referenced: reference.index(),
646 });
647 }
648 }
649 let condition_type = self
650 .expression(*condition)
651 .ok_or(BuildError::UnknownExpression(condition.index()))?
652 .value_type();
653 if condition_type.width() != 1 {
654 return Err(BuildError::WidthMismatch {
655 expected: 1,
656 actual: condition_type.width(),
657 });
658 }
659 let then_type = self
660 .expression(*then_expr)
661 .ok_or(BuildError::UnknownExpression(then_expr.index()))?
662 .value_type();
663 let else_type = self
664 .expression(*else_expr)
665 .ok_or(BuildError::UnknownExpression(else_expr.index()))?
666 .value_type();
667 if then_type.width() != else_type.width() {
668 return Err(BuildError::WidthMismatch {
669 expected: then_type.width(),
670 actual: else_type.width(),
671 });
672 }
673 let expected_type = ValueType {
674 width: then_type.width(),
675 signed: then_type.is_signed() && else_type.is_signed(),
676 four_state: condition_type.is_four_state()
677 || then_type.is_four_state()
678 || else_type.is_four_state(),
679 };
680 if expression.value_type() != expected_type {
681 return Err(BuildError::TypeMismatch {
682 expected: expected_type,
683 actual: expression.value_type(),
684 });
685 }
686 }
687 ExprNode::Concat(parts) => {
688 if parts.is_empty() {
689 return Err(BuildError::ZeroWidth);
690 }
691 let mut width = 0usize;
692 let mut four_state = false;
693 for part in parts {
694 if part.index() as usize >= index {
695 return Err(BuildError::ForwardExpressionReference {
696 expression: expression.id.index(),
697 referenced: part.index(),
698 });
699 }
700 let part_type = self
701 .expression(*part)
702 .ok_or(BuildError::UnknownExpression(part.index()))?
703 .value_type();
704 width = width
705 .checked_add(part_type.width())
706 .ok_or(BuildError::ZeroWidth)?;
707 four_state |= part_type.is_four_state();
708 }
709 let expected_type = ValueType::new(width, false, four_state)?;
710 if expression.value_type() != expected_type {
711 return Err(BuildError::TypeMismatch {
712 expected: expected_type,
713 actual: expression.value_type(),
714 });
715 }
716 }
717 }
718 for reference in references {
719 if reference.index() as usize >= index {
720 return Err(BuildError::ForwardExpressionReference {
721 expression: expression.id.index(),
722 referenced: reference.index(),
723 });
724 }
725 }
726 }
727 let mut driver_ranges = FxHashMap::default();
728 for assignment in &self.assignments {
729 validate_slice(assignment.target)?;
730 let expression = self
731 .expression(assignment.value)
732 .ok_or(BuildError::UnknownExpression(assignment.value.index()))?;
733 if assignment.target.width != expression.value_type.width() {
734 return Err(BuildError::WidthMismatch {
735 expected: assignment.target.width,
736 actual: expression.value_type.width(),
737 });
738 }
739 let signal = self
740 .signal(assignment.target.signal)
741 .ok_or(BuildError::UnknownSignal(assignment.target.signal.index()))?;
742 validate_driver_target(signal)?;
743 insert_driver_target(&mut driver_ranges, assignment.target, &signal.name)?;
744 }
745 for register in &self.registers {
746 validate_slice(register.target)?;
747 let target = self
748 .signal(register.target.signal)
749 .ok_or(BuildError::UnknownSignal(register.target.signal.index()))?;
750 validate_driver_target(target)?;
751 if register.target.lsb != 0 || register.target.width != target.value_type.width() {
752 return Err(BuildError::PartialRegisterTarget {
753 name: target.name.clone(),
754 });
755 }
756 let next = self
757 .expression(register.next)
758 .ok_or(BuildError::UnknownExpression(register.next.index()))?;
759 if next.value_type.width() != register.target.width {
760 return Err(BuildError::WidthMismatch {
761 expected: register.target.width,
762 actual: next.value_type.width(),
763 });
764 }
765 for control in std::iter::once(register.clock)
766 .chain(register.async_reset.map(|reset| reset.signal))
767 .chain(register.enable.map(|enable| enable.signal))
768 {
769 let signal = self
770 .signal(control)
771 .ok_or(BuildError::UnknownSignal(control.index()))?;
772 if signal.value_type.width() != 1 {
773 return Err(BuildError::InvalidControlWidth {
774 name: signal.name.clone(),
775 });
776 }
777 }
778 if let Some(reset) = register.async_reset {
779 let value = self
780 .expression(reset.value)
781 .ok_or(BuildError::UnknownExpression(reset.value.index()))?;
782 if value.value_type.width() != register.target.width {
783 return Err(BuildError::WidthMismatch {
784 expected: register.target.width,
785 actual: value.value_type.width(),
786 });
787 }
788 }
789 insert_driver_target(&mut driver_ranges, register.target, &target.name)?;
790 }
791 let mut ordered_ports = FxHashMap::default();
792 for signal in &self.port_order {
793 let signal = self
794 .signal(*signal)
795 .ok_or(BuildError::UnknownSignal(signal.index()))?;
796 if matches!(signal.direction, Direction::Internal) {
797 return Err(BuildError::InternalSignalInPortOrder {
798 name: signal.name.clone(),
799 });
800 }
801 if ordered_ports.insert(signal.id, ()).is_some() {
802 return Err(BuildError::DuplicatePortOrder {
803 name: signal.name.clone(),
804 });
805 }
806 }
807 for signal in &self.signals {
808 if !matches!(signal.direction, Direction::Internal)
809 && !ordered_ports.contains_key(&signal.id)
810 {
811 return Err(BuildError::MissingPortOrder {
812 name: signal.name.clone(),
813 });
814 }
815 }
816 Ok(())
817 }
818
819 pub fn module_name(&self) -> &str {
820 &self.module_name
821 }
822
823 pub fn signals(&self) -> &[Signal] {
824 &self.signals
825 }
826
827 pub fn expressions(&self) -> &[Expression] {
828 &self.expressions
829 }
830
831 pub fn assignments(&self) -> &[Assignment] {
832 &self.assignments
833 }
834
835 pub fn registers(&self) -> &[Register] {
836 &self.registers
837 }
838
839 pub fn port_order(&self) -> &[SignalId] {
840 &self.port_order
841 }
842
843 pub fn signal(&self, id: SignalId) -> Option<&Signal> {
844 self.signals.get(id.index() as usize)
845 }
846
847 pub fn expression(&self, id: ExprId) -> Option<&Expression> {
848 self.expressions.get(id.index() as usize)
849 }
850}
851
852#[derive(Clone, Debug, Error, PartialEq, Eq)]
854#[non_exhaustive]
855pub enum BuildError {
856 #[error("signal and expression widths must be non-zero")]
857 ZeroWidth,
858 #[error("duplicate signal name `{0}`")]
859 DuplicateSignal(String),
860 #[error("unknown signal id {0}")]
861 UnknownSignal(u32),
862 #[error("unknown expression id {0}")]
863 UnknownExpression(u32),
864 #[error("bit range {lsb} +: {width} exceeds signal width {signal_width}")]
865 InvalidSlice {
866 lsb: usize,
867 width: usize,
868 signal_width: usize,
869 },
870 #[error("width mismatch: expected {expected}, got {actual}")]
871 WidthMismatch { expected: usize, actual: usize },
872 #[error("type mismatch: expected {expected:?}, got {actual:?}")]
873 TypeMismatch {
874 expected: ValueType,
875 actual: ValueType,
876 },
877 #[error("control signal `{name}` must be one bit wide")]
878 InvalidControlWidth { name: String },
879 #[error("register target `{name}` must cover the complete signal")]
880 PartialRegisterTarget { name: String },
881 #[error("module name must not be empty")]
882 EmptyModuleName,
883 #[error("signal name must not be empty")]
884 EmptySignalName,
885 #[error("signal identity mismatch: expected {expected}, got {actual}")]
886 InvalidSignalIdentity { expected: u32, actual: u32 },
887 #[error("expression identity mismatch: expected {expected}, got {actual}")]
888 InvalidExpressionIdentity { expected: u32, actual: u32 },
889 #[error("expression {expression} references non-prior expression {referenced}")]
890 ForwardExpressionReference { expression: u32, referenced: u32 },
891 #[error("internal signal `{name}` appears in the module port order")]
892 InternalSignalInPortOrder { name: String },
893 #[error("signal `{name}` appears more than once in the module port order")]
894 DuplicatePortOrder { name: String },
895 #[error("public signal `{name}` is missing from the module port order")]
896 MissingPortOrder { name: String },
897 #[error("signal `{name}` has overlapping continuous or register drivers")]
898 OverlappingDrivers { name: String },
899 #[error("input signal `{name}` cannot be driven by artifact logic")]
900 InvalidDriverTarget { name: String },
901 #[error("two-state constants cannot contain X/Z mask bits")]
902 TwoStateConstantMask,
903 #[error("constant payload or mask does not fit its declared width {width}")]
904 ConstantOutOfRange { width: usize },
905 #[error("signal names `{first}` and `{second}` collide in the DUT namespace")]
906 SignalNamespaceCollision { first: String, second: String },
907 #[error("inout signal `{name}` is not supported by frontend artifact format version 1")]
908 UnsupportedInout { name: String },
909}
910
911#[derive(Debug, Error)]
913pub enum ArtifactJsonError {
914 #[error("invalid frontend artifact JSON: {0}")]
915 Json(#[from] serde_json::Error),
916 #[error("unsupported frontend artifact version {actual}; expected {expected}")]
917 UnsupportedVersion { expected: u32, actual: u32 },
918 #[error("invalid frontend artifact: {0}")]
919 InvalidArtifact(#[from] BuildError),
920}
921
922pub struct ModuleBuilder {
924 name: String,
925 signals: Vec<Signal>,
926 signal_names: FxHashMap<String, SignalId>,
927 signal_namespace_prefixes: FxHashMap<String, String>,
928 expressions: Vec<Expression>,
929 assignments: Vec<Assignment>,
930 registers: Vec<Register>,
931 driver_ranges: FxHashMap<SignalId, BTreeMap<usize, usize>>,
932 port_order: Vec<SignalId>,
933}
934
935impl ModuleBuilder {
936 pub fn new(name: impl Into<String>) -> Result<Self, BuildError> {
937 let name = name.into();
938 if name.is_empty() {
939 return Err(BuildError::EmptyModuleName);
940 }
941 Ok(Self {
942 name,
943 signals: Vec::new(),
944 signal_names: FxHashMap::default(),
945 signal_namespace_prefixes: FxHashMap::default(),
946 expressions: Vec::new(),
947 assignments: Vec::new(),
948 registers: Vec::new(),
949 driver_ranges: FxHashMap::default(),
950 port_order: Vec::new(),
951 })
952 }
953
954 pub fn signal(
955 &mut self,
956 name: impl Into<String>,
957 direction: Direction,
958 value_type: ValueType,
959 ) -> Result<SignalId, BuildError> {
960 let name = name.into();
961 if name.is_empty() {
962 return Err(BuildError::EmptySignalName);
963 }
964 if matches!(direction, Direction::Inout) {
965 return Err(BuildError::UnsupportedInout { name });
966 }
967 if self.signal_names.contains_key(&name) {
968 return Err(BuildError::DuplicateSignal(name));
969 }
970 if let Some(existing) = self.signal_namespace_prefixes.get(&name) {
971 return Err(BuildError::SignalNamespaceCollision {
972 first: existing.clone(),
973 second: name,
974 });
975 }
976 if let Some(existing) =
977 signal_name_prefixes(&name).find(|prefix| self.signal_names.contains_key(*prefix))
978 {
979 return Err(BuildError::SignalNamespaceCollision {
980 first: existing.to_string(),
981 second: name.clone(),
982 });
983 }
984 let id = SignalId(self.signals.len() as u32);
985 self.signal_names.insert(name.clone(), id);
986 for prefix in signal_name_prefixes(&name) {
987 self.signal_namespace_prefixes
988 .entry(prefix.to_string())
989 .or_insert_with(|| name.clone());
990 }
991 self.signals.push(Signal {
992 id,
993 name,
994 direction,
995 value_type,
996 initial: None,
997 });
998 if !matches!(direction, Direction::Internal) {
999 self.port_order.push(id);
1000 }
1001 Ok(id)
1002 }
1003
1004 pub fn input(
1005 &mut self,
1006 name: impl Into<String>,
1007 value_type: ValueType,
1008 ) -> Result<SignalId, BuildError> {
1009 self.signal(name, Direction::Input, value_type)
1010 }
1011
1012 pub fn output(
1013 &mut self,
1014 name: impl Into<String>,
1015 value_type: ValueType,
1016 ) -> Result<SignalId, BuildError> {
1017 self.signal(name, Direction::Output, value_type)
1018 }
1019
1020 pub fn internal(
1021 &mut self,
1022 name: impl Into<String>,
1023 value_type: ValueType,
1024 ) -> Result<SignalId, BuildError> {
1025 self.signal(name, Direction::Internal, value_type)
1026 }
1027
1028 pub fn set_initial(&mut self, signal: SignalId, value: Constant) -> Result<(), BuildError> {
1029 let signal_info = self.signal_info(signal)?;
1030 if signal_info.value_type.width() != value.value_type().width() {
1031 return Err(BuildError::WidthMismatch {
1032 expected: signal_info.value_type.width(),
1033 actual: value.value_type().width(),
1034 });
1035 }
1036 self.signals[signal.index() as usize].initial = Some(value);
1037 Ok(())
1038 }
1039
1040 pub fn whole(&self, signal: SignalId) -> Result<SignalSlice, BuildError> {
1041 let info = self.signal_info(signal)?;
1042 Ok(SignalSlice {
1043 signal,
1044 lsb: 0,
1045 width: info.value_type.width(),
1046 })
1047 }
1048
1049 pub fn slice(
1050 &self,
1051 signal: SignalId,
1052 lsb: usize,
1053 width: usize,
1054 ) -> Result<SignalSlice, BuildError> {
1055 let info = self.signal_info(signal)?;
1056 if width == 0 {
1057 return Err(BuildError::ZeroWidth);
1058 }
1059 if lsb
1060 .checked_add(width)
1061 .is_none_or(|end| end > info.value_type.width())
1062 {
1063 return Err(BuildError::InvalidSlice {
1064 lsb,
1065 width,
1066 signal_width: info.value_type.width(),
1067 });
1068 }
1069 Ok(SignalSlice { signal, lsb, width })
1070 }
1071
1072 pub fn read(&mut self, signal: SignalId) -> Result<ExprId, BuildError> {
1073 let slice = self.whole(signal)?;
1074 self.read_slice(slice)
1075 }
1076
1077 pub fn read_slice(&mut self, slice: SignalSlice) -> Result<ExprId, BuildError> {
1078 let info = self.signal_info(slice.signal)?;
1079 self.validate_slice(slice)?;
1080 let value_type = ValueType::new(
1081 slice.width,
1082 info.value_type.is_signed() && slice.width == info.value_type.width(),
1083 info.value_type.is_four_state(),
1084 )?;
1085 Ok(self.push_expr(ExprNode::Signal(slice), value_type))
1086 }
1087
1088 pub fn constant(&mut self, value: Constant) -> ExprId {
1089 let value_type = value.value_type();
1090 self.push_expr(ExprNode::Constant(value), value_type)
1091 }
1092
1093 pub fn binary(
1094 &mut self,
1095 op: BinaryOp,
1096 lhs: ExprId,
1097 rhs: ExprId,
1098 result_type: ValueType,
1099 ) -> Result<ExprId, BuildError> {
1100 self.expr_info(lhs)?;
1101 self.expr_info(rhs)?;
1102 Ok(self.push_expr(ExprNode::Binary { op, lhs, rhs }, result_type))
1103 }
1104
1105 pub fn unary(
1106 &mut self,
1107 op: UnaryOp,
1108 input: ExprId,
1109 result_type: ValueType,
1110 ) -> Result<ExprId, BuildError> {
1111 self.expr_info(input)?;
1112 Ok(self.push_expr(ExprNode::Unary { op, input }, result_type))
1113 }
1114
1115 pub fn mux(
1116 &mut self,
1117 condition: ExprId,
1118 then_expr: ExprId,
1119 else_expr: ExprId,
1120 ) -> Result<ExprId, BuildError> {
1121 let condition_type = self.expr_info(condition)?.value_type;
1122 if condition_type.width() != 1 {
1123 return Err(BuildError::WidthMismatch {
1124 expected: 1,
1125 actual: condition_type.width(),
1126 });
1127 }
1128 let then_type = self.expr_info(then_expr)?.value_type;
1129 let else_type = self.expr_info(else_expr)?.value_type;
1130 if then_type.width() != else_type.width() {
1131 return Err(BuildError::WidthMismatch {
1132 expected: then_type.width(),
1133 actual: else_type.width(),
1134 });
1135 }
1136 let result_type = ValueType::new(
1137 then_type.width(),
1138 then_type.is_signed() && else_type.is_signed(),
1139 condition_type.is_four_state()
1140 || then_type.is_four_state()
1141 || else_type.is_four_state(),
1142 )?;
1143 Ok(self.push_expr(
1144 ExprNode::Mux {
1145 condition,
1146 then_expr,
1147 else_expr,
1148 },
1149 result_type,
1150 ))
1151 }
1152
1153 pub fn concat(&mut self, parts: Vec<ExprId>) -> Result<ExprId, BuildError> {
1154 let mut width = 0usize;
1155 let mut four_state = false;
1156 for part in &parts {
1157 let value_type = self.expr_info(*part)?.value_type;
1158 width = width
1159 .checked_add(value_type.width())
1160 .ok_or(BuildError::ZeroWidth)?;
1161 four_state |= value_type.is_four_state();
1162 }
1163 let result_type = ValueType::new(width, false, four_state)?;
1164 Ok(self.push_expr(ExprNode::Concat(parts), result_type))
1165 }
1166
1167 pub fn expr_slice(
1168 &mut self,
1169 input: ExprId,
1170 lsb: usize,
1171 width: usize,
1172 ) -> Result<ExprId, BuildError> {
1173 let input_type = self.expr_info(input)?.value_type;
1174 if width == 0 {
1175 return Err(BuildError::ZeroWidth);
1176 }
1177 if lsb
1178 .checked_add(width)
1179 .is_none_or(|end| end > input_type.width())
1180 {
1181 return Err(BuildError::InvalidSlice {
1182 lsb,
1183 width,
1184 signal_width: input_type.width(),
1185 });
1186 }
1187 let result_type = ValueType::new(width, false, input_type.is_four_state())?;
1188 Ok(self.push_expr(ExprNode::Slice { input, lsb }, result_type))
1189 }
1190
1191 pub fn assign(&mut self, target: SignalSlice, value: ExprId) -> Result<(), BuildError> {
1192 self.validate_slice(target)?;
1193 validate_driver_target(self.signal_info(target.signal)?)?;
1194 let value_width = self.expr_info(value)?.value_type.width();
1195 if target.width != value_width {
1196 return Err(BuildError::WidthMismatch {
1197 expected: target.width,
1198 actual: value_width,
1199 });
1200 }
1201 self.record_driver_target(target)?;
1202 self.assignments.push(Assignment { target, value });
1203 Ok(())
1204 }
1205
1206 pub fn register(
1207 &mut self,
1208 target: SignalSlice,
1209 next: ExprId,
1210 clock: SignalId,
1211 edge: Edge,
1212 async_reset: Option<AsyncReset>,
1213 enable: Option<Enable>,
1214 ) -> Result<(), BuildError> {
1215 self.validate_slice(target)?;
1216 let target_signal = self.signal_info(target.signal)?;
1217 validate_driver_target(target_signal)?;
1218 if target.lsb != 0 || target.width != target_signal.value_type.width() {
1219 return Err(BuildError::PartialRegisterTarget {
1220 name: target_signal.name.clone(),
1221 });
1222 }
1223 let next_width = self.expr_info(next)?.value_type.width();
1224 if next_width != target.width {
1225 return Err(BuildError::WidthMismatch {
1226 expected: target.width,
1227 actual: next_width,
1228 });
1229 }
1230 self.validate_control(clock)?;
1231 if let Some(reset) = async_reset {
1232 self.validate_control(reset.signal)?;
1233 let reset_width = self.expr_info(reset.value)?.value_type.width();
1234 if reset_width != target.width {
1235 return Err(BuildError::WidthMismatch {
1236 expected: target.width,
1237 actual: reset_width,
1238 });
1239 }
1240 }
1241 if let Some(enable) = enable {
1242 self.validate_control(enable.signal)?;
1243 }
1244 self.record_driver_target(target)?;
1245 self.registers.push(Register {
1246 target,
1247 next,
1248 clock,
1249 edge,
1250 async_reset,
1251 enable,
1252 });
1253 Ok(())
1254 }
1255
1256 pub fn async_reset(
1257 &self,
1258 signal: SignalId,
1259 active: ActiveLevel,
1260 value: ExprId,
1261 ) -> Result<AsyncReset, BuildError> {
1262 self.validate_control(signal)?;
1263 self.expr_info(value)?;
1264 Ok(AsyncReset {
1265 signal,
1266 active,
1267 value,
1268 })
1269 }
1270
1271 pub fn enable(&self, signal: SignalId, active: ActiveLevel) -> Result<Enable, BuildError> {
1272 self.validate_control(signal)?;
1273 Ok(Enable { signal, active })
1274 }
1275
1276 pub fn finish(self) -> FrontendArtifact {
1277 FrontendArtifact {
1278 format_version: ARTIFACT_FORMAT_VERSION,
1279 module_name: self.name,
1280 signals: self.signals,
1281 expressions: self.expressions,
1282 assignments: self.assignments,
1283 registers: self.registers,
1284 port_order: self.port_order,
1285 }
1286 }
1287
1288 fn signal_info(&self, signal: SignalId) -> Result<&Signal, BuildError> {
1289 self.signals
1290 .get(signal.index() as usize)
1291 .ok_or(BuildError::UnknownSignal(signal.index()))
1292 }
1293
1294 fn expr_info(&self, expression: ExprId) -> Result<&Expression, BuildError> {
1295 self.expressions
1296 .get(expression.index() as usize)
1297 .ok_or(BuildError::UnknownExpression(expression.index()))
1298 }
1299
1300 fn validate_slice(&self, slice: SignalSlice) -> Result<(), BuildError> {
1301 let signal = self.signal_info(slice.signal)?;
1302 if slice.width == 0 {
1303 return Err(BuildError::ZeroWidth);
1304 }
1305 if slice
1306 .lsb
1307 .checked_add(slice.width)
1308 .is_none_or(|end| end > signal.value_type.width())
1309 {
1310 return Err(BuildError::InvalidSlice {
1311 lsb: slice.lsb,
1312 width: slice.width,
1313 signal_width: signal.value_type.width(),
1314 });
1315 }
1316 Ok(())
1317 }
1318
1319 fn validate_control(&self, signal: SignalId) -> Result<(), BuildError> {
1320 let signal = self.signal_info(signal)?;
1321 if signal.value_type.width() != 1 {
1322 return Err(BuildError::InvalidControlWidth {
1323 name: signal.name.clone(),
1324 });
1325 }
1326 Ok(())
1327 }
1328
1329 fn record_driver_target(&mut self, target: SignalSlice) -> Result<(), BuildError> {
1330 let signal_name = self.signal_info(target.signal)?.name.clone();
1331 insert_driver_target(&mut self.driver_ranges, target, &signal_name)
1332 }
1333
1334 fn push_expr(&mut self, node: ExprNode, value_type: ValueType) -> ExprId {
1335 let id = ExprId(self.expressions.len() as u32);
1336 self.expressions.push(Expression {
1337 id,
1338 node,
1339 value_type,
1340 });
1341 id
1342 }
1343}
1344
1345fn validate_driver_target(signal: &Signal) -> Result<(), BuildError> {
1346 if matches!(signal.direction, Direction::Output | Direction::Internal) {
1347 Ok(())
1348 } else {
1349 Err(BuildError::InvalidDriverTarget {
1350 name: signal.name.clone(),
1351 })
1352 }
1353}
1354
1355#[cfg(test)]
1356mod tests {
1357 use super::*;
1358
1359 #[test]
1360 fn builds_flat_combinational_artifact() {
1361 let mut module = ModuleBuilder::new("Adder").unwrap();
1362 let ty = ValueType::bits(8).unwrap();
1363 let a = module.input("a", ty).unwrap();
1364 let b = module.input("b", ty).unwrap();
1365 let y = module.output("y", ty).unwrap();
1366 let a = module.read(a).unwrap();
1367 let b = module.read(b).unwrap();
1368 let sum = module.binary(BinaryOp::Add, a, b, ty).unwrap();
1369 let y = module.whole(y).unwrap();
1370 module.assign(y, sum).unwrap();
1371
1372 let artifact = module.finish();
1373 assert_eq!(artifact.module_name(), "Adder");
1374 assert_eq!(artifact.signals().len(), 3);
1375 assert_eq!(artifact.assignments().len(), 1);
1376 assert_eq!(artifact.port_order().len(), 3);
1377 }
1378
1379 #[test]
1380 fn rejects_partial_register_target() {
1381 let mut module = ModuleBuilder::new("Counter").unwrap();
1382 let bit = ValueType::bits(1).unwrap();
1383 let byte = ValueType::bits(8).unwrap();
1384 let clock = module.input("clock", bit).unwrap();
1385 let q = module.output("q", byte).unwrap();
1386 let q_expr = module.read(q).unwrap();
1387 let partial = module.slice(q, 0, 4).unwrap();
1388 let error = module
1389 .register(partial, q_expr, clock, Edge::Posedge, None, None)
1390 .unwrap_err();
1391 assert!(matches!(error, BuildError::PartialRegisterTarget { .. }));
1392 }
1393
1394 #[test]
1395 fn rejects_inout_in_builder_and_json_artifacts() {
1396 let bit = ValueType::bits(1).unwrap();
1397 let mut module = ModuleBuilder::new("InoutBuilder").unwrap();
1398 let error = module.signal("bus", Direction::Inout, bit).unwrap_err();
1399 assert!(matches!(error, BuildError::UnsupportedInout { .. }));
1400
1401 let mut module = ModuleBuilder::new("InoutJson").unwrap();
1402 module.input("bus", bit).unwrap();
1403 let json = module.finish().to_json().unwrap().replace("Input", "Inout");
1404 let error = FrontendArtifact::from_json(&json).unwrap_err();
1405 assert!(matches!(
1406 error,
1407 ArtifactJsonError::InvalidArtifact(BuildError::UnsupportedInout { .. })
1408 ));
1409 }
1410
1411 #[test]
1412 fn rejects_out_of_bounds_expression_slice_from_json() {
1413 let byte = ValueType::bits(8).unwrap();
1414 let mut module = ModuleBuilder::new("InvalidSlice").unwrap();
1415 let input = module.input("input", byte).unwrap();
1416 let input = module.read(input).unwrap();
1417 module.expr_slice(input, 0, 4).unwrap();
1418 let artifact = module.finish();
1419 let mut json = serde_json::to_value(&artifact).unwrap();
1420 json["expressions"][1]["node"]["Slice"]["lsb"] = 7.into();
1421
1422 let error = FrontendArtifact::from_json(&json.to_string()).unwrap_err();
1423 assert!(matches!(
1424 error,
1425 ArtifactJsonError::InvalidArtifact(BuildError::InvalidSlice {
1426 lsb: 7,
1427 width: 4,
1428 signal_width: 8,
1429 })
1430 ));
1431 }
1432
1433 #[test]
1434 fn revalidates_expression_slice_type_from_json() {
1435 let logic = ValueType::logic(8).unwrap();
1436 let mut module = ModuleBuilder::new("SliceType").unwrap();
1437 let input = module.input("input", logic).unwrap();
1438 let input = module.read(input).unwrap();
1439 module.expr_slice(input, 0, 4).unwrap();
1440 let artifact = module.finish();
1441
1442 let mut signed = serde_json::to_value(&artifact).unwrap();
1443 signed["expressions"][1]["value_type"]["signed"] = true.into();
1444 let error = FrontendArtifact::from_json(&signed.to_string()).unwrap_err();
1445 assert!(matches!(
1446 error,
1447 ArtifactJsonError::InvalidArtifact(BuildError::TypeMismatch { .. })
1448 ));
1449
1450 let mut two_state = serde_json::to_value(&artifact).unwrap();
1451 two_state["expressions"][1]["value_type"]["four_state"] = false.into();
1452 let error = FrontendArtifact::from_json(&two_state.to_string()).unwrap_err();
1453 assert!(matches!(
1454 error,
1455 ArtifactJsonError::InvalidArtifact(BuildError::TypeMismatch { .. })
1456 ));
1457 }
1458
1459 #[test]
1460 fn rejects_overlapping_register_and_assignment_drivers() {
1461 let bit = ValueType::bits(1).unwrap();
1462 let mut module = ModuleBuilder::new("DriverConflict").unwrap();
1463 let clock = module.input("clock", bit).unwrap();
1464 let d = module.input("d", bit).unwrap();
1465 let q = module.output("q", bit).unwrap();
1466 let d_expr = module.read(d).unwrap();
1467 let q_target = module.whole(q).unwrap();
1468 module.assign(q_target, d_expr).unwrap();
1469 let error = module
1470 .register(q_target, d_expr, clock, Edge::Posedge, None, None)
1471 .unwrap_err();
1472 assert!(matches!(error, BuildError::OverlappingDrivers { .. }));
1473
1474 let mut module = ModuleBuilder::new("DuplicateRegisterJson").unwrap();
1475 let clock = module.input("clock", bit).unwrap();
1476 let d = module.input("d", bit).unwrap();
1477 let q = module.output("q", bit).unwrap();
1478 let d_expr = module.read(d).unwrap();
1479 let q_target = module.whole(q).unwrap();
1480 module
1481 .register(q_target, d_expr, clock, Edge::Posedge, None, None)
1482 .unwrap();
1483 let mut json = serde_json::to_value(module.finish()).unwrap();
1484 let duplicate = json["registers"][0].clone();
1485 json["registers"].as_array_mut().unwrap().push(duplicate);
1486 let error = FrontendArtifact::from_json(&json.to_string()).unwrap_err();
1487 assert!(matches!(
1488 error,
1489 ArtifactJsonError::InvalidArtifact(BuildError::OverlappingDrivers { .. })
1490 ));
1491 }
1492
1493 #[test]
1494 fn rejects_incomplete_or_duplicate_json_port_order() {
1495 let bit = ValueType::bits(1).unwrap();
1496 let mut module = ModuleBuilder::new("PortOrder").unwrap();
1497 module.input("a", bit).unwrap();
1498 module.output("b", bit).unwrap();
1499 let artifact = module.finish();
1500
1501 let mut duplicate = serde_json::to_value(&artifact).unwrap();
1502 duplicate["port_order"] = serde_json::json!([0, 0]);
1503 let error = FrontendArtifact::from_json(&duplicate.to_string()).unwrap_err();
1504 assert!(matches!(
1505 error,
1506 ArtifactJsonError::InvalidArtifact(BuildError::DuplicatePortOrder { .. })
1507 ));
1508
1509 let mut missing = serde_json::to_value(&artifact).unwrap();
1510 missing["port_order"] = serde_json::json!([0]);
1511 let error = FrontendArtifact::from_json(&missing.to_string()).unwrap_err();
1512 assert!(matches!(
1513 error,
1514 ArtifactJsonError::InvalidArtifact(BuildError::MissingPortOrder { .. })
1515 ));
1516 }
1517
1518 #[test]
1519 fn revalidates_mux_types_from_json() {
1520 let logic = ValueType::logic(1).unwrap();
1521 let byte = ValueType::bits(8).unwrap();
1522 let mut module = ModuleBuilder::new("MuxJson").unwrap();
1523 let condition = module.input("condition", logic).unwrap();
1524 let a = module.input("a", byte).unwrap();
1525 let b = module.input("b", byte).unwrap();
1526 let condition = module.read(condition).unwrap();
1527 let a = module.read(a).unwrap();
1528 let b = module.read(b).unwrap();
1529 module.mux(condition, a, b).unwrap();
1530 let artifact = module.finish();
1531 assert!(artifact.expressions()[3].value_type().is_four_state());
1532
1533 let mut wide_condition = serde_json::to_value(&artifact).unwrap();
1534 wide_condition["expressions"][3]["node"]["Mux"]["condition"] = 1.into();
1535 let error = FrontendArtifact::from_json(&wide_condition.to_string()).unwrap_err();
1536 assert!(matches!(
1537 error,
1538 ArtifactJsonError::InvalidArtifact(BuildError::WidthMismatch {
1539 expected: 1,
1540 actual: 8,
1541 })
1542 ));
1543
1544 let mut wrong_result = serde_json::to_value(&artifact).unwrap();
1545 wrong_result["expressions"][3]["value_type"]["width"] = 4.into();
1546 let error = FrontendArtifact::from_json(&wrong_result.to_string()).unwrap_err();
1547 assert!(matches!(
1548 error,
1549 ArtifactJsonError::InvalidArtifact(BuildError::TypeMismatch { .. })
1550 ));
1551
1552 let mut wrong_state = serde_json::to_value(&artifact).unwrap();
1553 wrong_state["expressions"][3]["value_type"]["four_state"] = false.into();
1554 let error = FrontendArtifact::from_json(&wrong_state.to_string()).unwrap_err();
1555 assert!(matches!(
1556 error,
1557 ArtifactJsonError::InvalidArtifact(BuildError::TypeMismatch { .. })
1558 ));
1559 }
1560
1561 #[test]
1562 fn normalizes_two_state_constant_masks_and_rejects_them_in_json() {
1563 let byte = ValueType::bits(8).unwrap();
1564 let value = Constant::new(BigUint::from(0xa5u8), BigUint::from(0xffu8), byte);
1565 assert_eq!(value.mask(), &BigUint::default());
1566
1567 let mut module = ModuleBuilder::new("ConstantMaskJson").unwrap();
1568 module.constant(value);
1569 let mut json = serde_json::to_value(module.finish()).unwrap();
1570 json["expressions"][0]["node"]["Constant"]["mask"] =
1571 serde_json::to_value(BigUint::from(1u8)).unwrap();
1572 let error = FrontendArtifact::from_json(&json.to_string()).unwrap_err();
1573 assert!(matches!(
1574 error,
1575 ArtifactJsonError::InvalidArtifact(BuildError::TwoStateConstantMask)
1576 ));
1577 }
1578
1579 #[test]
1580 fn rejects_out_of_range_constant_bits_from_json() {
1581 let value = Constant::four_state(0xa5u8, 0u8, 8).unwrap();
1582 let mut module = ModuleBuilder::new("ConstantRange").unwrap();
1583 module.constant(value);
1584 let artifact = module.finish();
1585
1586 let mut payload = serde_json::to_value(&artifact).unwrap();
1587 payload["expressions"][0]["node"]["Constant"]["payload"] =
1588 serde_json::to_value(BigUint::from(0x100u16)).unwrap();
1589 let error = FrontendArtifact::from_json(&payload.to_string()).unwrap_err();
1590 assert!(matches!(
1591 error,
1592 ArtifactJsonError::InvalidArtifact(BuildError::ConstantOutOfRange { width: 8 })
1593 ));
1594
1595 let mut mask = serde_json::to_value(&artifact).unwrap();
1596 mask["expressions"][0]["node"]["Constant"]["mask"] =
1597 serde_json::to_value(BigUint::from(0x100u16)).unwrap();
1598 let error = FrontendArtifact::from_json(&mask.to_string()).unwrap_err();
1599 assert!(matches!(
1600 error,
1601 ArtifactJsonError::InvalidArtifact(BuildError::ConstantOutOfRange { width: 8 })
1602 ));
1603 }
1604
1605 #[test]
1606 fn rejects_signal_namespace_prefix_collisions() {
1607 let bit = ValueType::bits(1).unwrap();
1608 let mut module = ModuleBuilder::new("NamespaceBuilder").unwrap();
1609 module.input("bus", bit).unwrap();
1610 let error = module.input("bus.member", bit).unwrap_err();
1611 assert!(matches!(error, BuildError::SignalNamespaceCollision { .. }));
1612
1613 let mut reverse = ModuleBuilder::new("ReverseNamespaceBuilder").unwrap();
1614 reverse.input("bus.member", bit).unwrap();
1615 let error = reverse.input("bus", bit).unwrap_err();
1616 assert!(matches!(error, BuildError::SignalNamespaceCollision { .. }));
1617
1618 let mut module = ModuleBuilder::new("NamespaceJson").unwrap();
1619 module.input("first", bit).unwrap();
1620 module.input("second", bit).unwrap();
1621 let mut json = serde_json::to_value(module.finish()).unwrap();
1622 json["signals"][0]["name"] = "bus".into();
1623 json["signals"][1]["name"] = "bus.member".into();
1624 let error = FrontendArtifact::from_json(&json.to_string()).unwrap_err();
1625 assert!(matches!(
1626 error,
1627 ArtifactJsonError::InvalidArtifact(BuildError::SignalNamespaceCollision { .. })
1628 ));
1629 }
1630
1631 #[test]
1632 fn revalidates_leaf_expression_types_from_json() {
1633 let byte = ValueType::bits(8).unwrap();
1634 let mut module = ModuleBuilder::new("LeafTypes").unwrap();
1635 let input = module.input("input", byte).unwrap();
1636 module.read(input).unwrap();
1637 module.constant(Constant::two_state(0xa5u8, 8).unwrap());
1638 let artifact = module.finish();
1639
1640 let mut signal = serde_json::to_value(&artifact).unwrap();
1641 signal["expressions"][0]["value_type"]["width"] = 4.into();
1642 let error = FrontendArtifact::from_json(&signal.to_string()).unwrap_err();
1643 assert!(matches!(
1644 error,
1645 ArtifactJsonError::InvalidArtifact(BuildError::TypeMismatch { .. })
1646 ));
1647
1648 let mut constant = serde_json::to_value(&artifact).unwrap();
1649 constant["expressions"][1]["value_type"]["four_state"] = true.into();
1650 let error = FrontendArtifact::from_json(&constant.to_string()).unwrap_err();
1651 assert!(matches!(
1652 error,
1653 ArtifactJsonError::InvalidArtifact(BuildError::TypeMismatch { .. })
1654 ));
1655 }
1656
1657 #[test]
1658 fn revalidates_concat_types_from_json() {
1659 let bit = ValueType::bits(1).unwrap();
1660 let logic = ValueType::logic(1).unwrap();
1661 let mut module = ModuleBuilder::new("ConcatTypes").unwrap();
1662 let a = module.input("a", bit).unwrap();
1663 let b = module.input("b", logic).unwrap();
1664 let a = module.read(a).unwrap();
1665 let b = module.read(b).unwrap();
1666 module.concat(vec![a, b]).unwrap();
1667 let artifact = module.finish();
1668
1669 let mut wrong_type = serde_json::to_value(&artifact).unwrap();
1670 wrong_type["expressions"][2]["value_type"]["four_state"] = false.into();
1671 let error = FrontendArtifact::from_json(&wrong_type.to_string()).unwrap_err();
1672 assert!(matches!(
1673 error,
1674 ArtifactJsonError::InvalidArtifact(BuildError::TypeMismatch { .. })
1675 ));
1676
1677 let mut empty = serde_json::to_value(&artifact).unwrap();
1678 empty["expressions"][2]["node"]["Concat"] = serde_json::json!([]);
1679 let error = FrontendArtifact::from_json(&empty.to_string()).unwrap_err();
1680 assert!(matches!(
1681 error,
1682 ArtifactJsonError::InvalidArtifact(BuildError::ZeroWidth)
1683 ));
1684 }
1685
1686 #[test]
1687 fn rejects_input_driver_targets_in_builder_and_json() {
1688 let bit = ValueType::bits(1).unwrap();
1689 let mut module = ModuleBuilder::new("InputDriver").unwrap();
1690 let clock = module.input("clock", bit).unwrap();
1691 let input = module.input("input", bit).unwrap();
1692 let value = module.constant(Constant::two_state(1u8, 1).unwrap());
1693 let target = module.whole(input).unwrap();
1694 let error = module.assign(target, value).unwrap_err();
1695 assert!(matches!(error, BuildError::InvalidDriverTarget { .. }));
1696 let error = module
1697 .register(target, value, clock, Edge::Posedge, None, None)
1698 .unwrap_err();
1699 assert!(matches!(error, BuildError::InvalidDriverTarget { .. }));
1700
1701 let mut valid = ModuleBuilder::new("InputDriverJson").unwrap();
1702 let input = valid.input("input", bit).unwrap();
1703 let output = valid.output("output", bit).unwrap();
1704 let value = valid.read(input).unwrap();
1705 valid.assign(valid.whole(output).unwrap(), value).unwrap();
1706 let mut json = serde_json::to_value(valid.finish()).unwrap();
1707 json["assignments"][0]["target"]["signal"] = input.index().into();
1708 let error = FrontendArtifact::from_json(&json.to_string()).unwrap_err();
1709 assert!(matches!(
1710 error,
1711 ArtifactJsonError::InvalidArtifact(BuildError::InvalidDriverTarget { .. })
1712 ));
1713
1714 let mut valid = ModuleBuilder::new("InputRegisterJson").unwrap();
1715 let clock = valid.input("clock", bit).unwrap();
1716 let input = valid.input("input", bit).unwrap();
1717 let output = valid.output("output", bit).unwrap();
1718 let value = valid.read(input).unwrap();
1719 let output_target = valid.whole(output).unwrap();
1720 valid
1721 .register(output_target, value, clock, Edge::Posedge, None, None)
1722 .unwrap();
1723 let mut json = serde_json::to_value(valid.finish()).unwrap();
1724 json["registers"][0]["target"]["signal"] = input.index().into();
1725 let error = FrontendArtifact::from_json(&json.to_string()).unwrap_err();
1726 assert!(matches!(
1727 error,
1728 ArtifactJsonError::InvalidArtifact(BuildError::InvalidDriverTarget { .. })
1729 ));
1730 }
1731}