Skip to main content

celox_frontend_sdk/
lib.rs

1//! Stable, source-language-independent authoring API for Celox frontends.
2//!
3//! This crate deliberately does not depend on Celox compiler internals. A
4//! frontend parses and elaborates its input, constructs a [`FrontendArtifact`],
5//! and hands that artifact to the public `celox` compiler API.
6
7use std::collections::BTreeMap;
8
9use fxhash::FxHashMap;
10use num_bigint::BigUint;
11use serde::{Deserialize, Serialize};
12use thiserror::Error;
13
14/// Current JSON interchange version of [`FrontendArtifact`].
15pub const ARTIFACT_FORMAT_VERSION: u32 = 1;
16
17/// Identity of one signal in the elaborated module.
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
19pub struct SignalId(u32);
20
21impl SignalId {
22    /// Return the stable module-local numeric identity.
23    pub const fn index(self) -> u32 {
24        self.0
25    }
26}
27
28/// Identity of one expression in the elaborated module.
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
30pub struct ExprId(u32);
31
32impl ExprId {
33    /// Return the stable module-local numeric identity.
34    pub const fn index(self) -> u32 {
35        self.0
36    }
37}
38
39/// Public direction of a signal.
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
41#[non_exhaustive]
42pub enum Direction {
43    Input,
44    Output,
45    /// Reserved for a future artifact version; version 1 validation rejects it.
46    Inout,
47    Internal,
48}
49
50/// Edge polarity used by clocked storage.
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
52pub enum Edge {
53    Posedge,
54    Negedge,
55}
56
57/// Active level of a reset or enable signal.
58#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
59pub enum ActiveLevel {
60    High,
61    Low,
62}
63
64/// Source-independent bit-vector type.
65#[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/// One declared signal retained for runtime reflection and TypeScript access.
106#[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/// A fixed bit range of a signal.
138#[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/// Constant payload and four-state mask. Set mask bits represent X/Z.
160#[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/// Binary operation in the frontend expression vocabulary.
253#[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/// Unary operation in the frontend expression vocabulary.
286#[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/// One expression node. Node result types are explicit so a netlist frontend
302/// does not inherit source-language width inference rules from Celox.
303#[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/// Typed expression entry.
330#[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/// One continuous/combinational assignment.
352#[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/// Optional asynchronous reset configuration for a storage element.
369#[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/// Optional synchronous enable configuration for a storage element.
391#[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/// One edge-triggered storage element in an elaborated netlist.
408#[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/// Fully elaborated frontend result. The first SDK version intentionally
445/// models one flattened module; hierarchical netlists can be flattened by the
446/// producing frontend without affecting runtime signal names.
447#[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    /// Serialize the artifact for transport to the Celox N-API runtime.
464    pub fn to_json(&self) -> Result<String, ArtifactJsonError> {
465        Ok(serde_json::to_string(self)?)
466    }
467
468    /// Decode and version-check an artifact produced by an external frontend.
469    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    /// Recheck all identities, ranges, widths, and expression ordering at a
482    /// trust boundary. Compiler consumers call this even for in-process values.
483    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/// Errors detected while constructing a frontend artifact.
853#[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/// JSON interchange failures for frontend artifacts.
912#[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
922/// Builder for one flattened, elaborated netlist module.
923pub 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}