Skip to main content

hdl_cat_std/
adder.rs

1//! Half- and full-adder circuits.
2
3use hdl_cat_circuit::{CircuitArrow, CircuitTensor, Obj};
4use hdl_cat_error::Error;
5use hdl_cat_ir::{BinOp, HdlGraphBuilder, Op, WireTy};
6
7/// A half-adder arrow: `(bool ⊗ bool) -> (bool ⊗ bool)`.
8/// Output is `(sum, carry)` packed as a tensor.
9pub type HalfAdderArrow = CircuitArrow<
10    CircuitTensor<Obj<bool>, Obj<bool>>,
11    CircuitTensor<Obj<bool>, Obj<bool>>,
12>;
13
14/// A full-adder arrow:
15/// `((bool ⊗ bool) ⊗ bool) -> (bool ⊗ bool)`.
16/// Inputs: `((a, b), carry_in)`.  Outputs: `(sum, carry_out)`.
17pub type FullAdderArrow = CircuitArrow<
18    CircuitTensor<CircuitTensor<Obj<bool>, Obj<bool>>, Obj<bool>>,
19    CircuitTensor<Obj<bool>, Obj<bool>>,
20>;
21
22/// Construct a single-bit half-adder.
23///
24/// Two inputs (`a`, `b`) drive both an XOR gate (the sum) and an
25/// AND gate (the carry), wired directly at the IR level.
26///
27/// # Errors
28///
29/// Infallible in practice; [`Error`] is returned only if the
30/// IR builder rejects an instruction.
31pub fn half_adder() -> Result<HalfAdderArrow, Error> {
32    let (bld, a) = HdlGraphBuilder::new().with_wire(WireTy::Bit);
33    let (bld, b) = bld.with_wire(WireTy::Bit);
34    let (bld, sum) = bld.with_wire(WireTy::Bit);
35    let (bld, carry) = bld.with_wire(WireTy::Bit);
36    let bld = bld.with_instruction(Op::Bin(BinOp::Xor), vec![a, b], sum)?;
37    let bld = bld.with_instruction(Op::Bin(BinOp::And), vec![a, b], carry)?;
38    Ok(CircuitArrow::from_raw_parts(
39        bld.build(),
40        vec![a, b],
41        vec![sum, carry],
42    ))
43}
44
45/// Construct a single-bit full-adder.
46///
47/// Three inputs (`a`, `b`, `cin`) produce `sum = a ^ b ^ cin` and
48/// `cout = (a & b) | (cin & (a ^ b))`.
49///
50/// # Errors
51///
52/// Infallible in practice; [`Error`] is returned only if the
53/// IR builder rejects an instruction.
54pub fn full_adder() -> Result<FullAdderArrow, Error> {
55    let (bld, a) = HdlGraphBuilder::new().with_wire(WireTy::Bit);
56    let (bld, b) = bld.with_wire(WireTy::Bit);
57    let (bld, cin) = bld.with_wire(WireTy::Bit);
58    let (bld, ab) = bld.with_wire(WireTy::Bit);     // a ^ b
59    let (bld, ab_and) = bld.with_wire(WireTy::Bit); // a & b
60    let (bld, c_and) = bld.with_wire(WireTy::Bit);  // cin & (a ^ b)
61    let (bld, sum) = bld.with_wire(WireTy::Bit);    // ab ^ cin
62    let (bld, cout) = bld.with_wire(WireTy::Bit);   // ab_and | c_and
63    let bld = bld.with_instruction(Op::Bin(BinOp::Xor), vec![a, b], ab)?;
64    let bld = bld.with_instruction(Op::Bin(BinOp::And), vec![a, b], ab_and)?;
65    let bld = bld.with_instruction(Op::Bin(BinOp::And), vec![cin, ab], c_and)?;
66    let bld = bld.with_instruction(Op::Bin(BinOp::Xor), vec![ab, cin], sum)?;
67    let bld = bld.with_instruction(Op::Bin(BinOp::Or), vec![ab_and, c_and], cout)?;
68    Ok(CircuitArrow::from_raw_parts(
69        bld.build(),
70        vec![a, b, cin],
71        vec![sum, cout],
72    ))
73}
74
75#[cfg(test)]
76mod tests {
77    use super::{full_adder, half_adder};
78
79    #[test]
80    fn half_adder_builds() -> Result<(), hdl_cat_error::Error> {
81        let ha = half_adder()?;
82        assert_eq!(ha.inputs().len(), 2);
83        assert_eq!(ha.outputs().len(), 2);
84        assert_eq!(ha.graph().instructions().len(), 2);
85        Ok(())
86    }
87
88    #[test]
89    fn half_adder_sum_and_carry_exist() -> Result<(), hdl_cat_error::Error> {
90        let ha = half_adder()?;
91        assert_eq!(ha.graph().wires().len(), 4);
92        Ok(())
93    }
94
95    #[test]
96    fn full_adder_builds() -> Result<(), hdl_cat_error::Error> {
97        let fa = full_adder()?;
98        assert_eq!(fa.inputs().len(), 3);
99        assert_eq!(fa.outputs().len(), 2);
100        assert_eq!(fa.graph().instructions().len(), 5);
101        Ok(())
102    }
103}