Skip to main content

hdl_cat_std/
shift_reg.rs

1//! `N`-bit left-shift register.
2//!
3//! Each cycle, shifts the state left by one and feeds the input
4//! bit into bit 0.  Output is the *current* state (pre-shift).
5
6use hdl_cat_bits::Bits;
7use hdl_cat_circuit::Obj;
8use hdl_cat_error::{Error, Width};
9use hdl_cat_ir::{HdlGraphBuilder, Op, WireTy};
10use hdl_cat_kind::BitSeq;
11use hdl_cat_sync::{machine, Sync};
12
13/// An `N`-bit left-shift register, `N >= 2`.
14///
15/// State: `Bits<N>`.
16/// Input: `bool` (new bit shifted into bit 0).
17/// Output: the current `Bits<N>` state before the shift.
18pub type ShiftRegisterLeftSync<const N: usize> = Sync<Obj<Bits<N>>, Obj<bool>, Obj<Bits<N>>>;
19
20/// Construct an `N`-bit left-shift register.  Requires `N >= 2`.
21///
22/// # Errors
23///
24/// Returns [`Error::Overflow`] when `N` exceeds `u32::MAX`, or
25/// [`Error::WidthMismatch`] when `N < 2` (unsupported).
26pub fn shift_register_left<const N: usize>() -> Result<ShiftRegisterLeftSync<N>, Error> {
27    (N >= 2)
28        .then_some(())
29        .ok_or_else(|| Error::WidthMismatch {
30            expected: Width::new(2),
31            actual: Width::new(u32::try_from(N).unwrap_or(u32::MAX)),
32        })?;
33
34    let width_n = u32::try_from(N).map_err(|_| Error::Overflow {
35        width: Width::new(u32::MAX),
36    })?;
37    let width_n_minus_1 = width_n - 1;
38
39    let (bld, state) = HdlGraphBuilder::new().with_wire(WireTy::Bits(width_n));
40    let (bld, new_bit) = bld.with_wire(WireTy::Bit);
41    let (bld, low_part) = bld.with_wire(WireTy::Bits(width_n_minus_1));
42    let (bld, next_state) = bld.with_wire(WireTy::Bits(width_n));
43
44    // low_part = state[0..N-1]  (the low N-1 bits)
45    let bld = bld.with_instruction(
46        Op::Slice {
47            lo: 0,
48            hi: width_n_minus_1,
49        },
50        vec![state],
51        low_part,
52    )?;
53    // next_state = concat(new_bit, low_part)  — new_bit at bit 0, low_part at bits 1..N
54    let bld = bld.with_instruction(
55        Op::Concat {
56            low_width: 1,
57            high_width: width_n_minus_1,
58        },
59        vec![new_bit, low_part],
60        next_state,
61    )?;
62
63    let graph = bld.build();
64    let initial_state: BitSeq = (0..N).map(|_| false).collect();
65    Ok(machine::from_raw(
66        graph,
67        vec![state, new_bit],
68        vec![next_state, state],
69        initial_state,
70        1,
71    ))
72}
73
74#[cfg(test)]
75mod tests {
76    use super::shift_register_left;
77
78    #[test]
79    fn shift_register_rejects_width_below_two() {
80        assert!(shift_register_left::<1>().is_err());
81        assert!(shift_register_left::<0>().is_err());
82    }
83
84    #[test]
85    fn shift_register_has_two_instructions() -> Result<(), hdl_cat_error::Error> {
86        let s = shift_register_left::<8>()?;
87        assert_eq!(s.graph().instructions().len(), 2);
88        Ok(())
89    }
90
91    #[test]
92    fn shift_register_state_is_n_bits() -> Result<(), hdl_cat_error::Error> {
93        let s = shift_register_left::<4>()?;
94        assert_eq!(s.initial_state().len(), 4);
95        Ok(())
96    }
97}