rust_hdl_widgets/
mac_fir.rs

1use crate::dff::DFF;
2use crate::dff_setup;
3use crate::ramrom::ram::RAM;
4use crate::ramrom::sync_rom::SyncROM;
5use rust_hdl_core::prelude::*;
6use rust_hdl_core::signed::ToSignedBits;
7
8#[derive(Clone, Debug, LogicState, Copy, PartialEq)]
9enum MACFIRState {
10    Idle,
11    Dwell,
12    Compute,
13    CenterTap,
14    Write,
15}
16
17#[derive(LogicBlock)]
18pub struct MultiplyAccumulateSymmetricFiniteImpulseResponseFilter<const ADDR_BITS: usize> {
19    pub data_in: Signal<In, Signed<16>>,
20    pub strobe_in: Signal<In, Bit>,
21    pub data_out: Signal<Out, Signed<48>>,
22    pub strobe_out: Signal<Out, Bit>,
23    pub clock: Signal<In, Clock>,
24    pub busy: Signal<Out, Bit>,
25    coeff_memory: SyncROM<Signed<16>, ADDR_BITS>,
26    left_bank: RAM<Signed<16>, ADDR_BITS>,
27    right_bank: RAM<Signed<16>, ADDR_BITS>,
28    // Points to where the next data sample goes (delay 0)
29    head_ptr: DFF<Bits<ADDR_BITS>>,
30    // Points to where the left data sample comes from
31    left_ptr: Signal<Local, Bits<ADDR_BITS>>,
32    // Points to where the right data sample comes from
33    right_ptr: Signal<Local, Bits<ADDR_BITS>>,
34    // Index pointer used
35    index: DFF<Bits<ADDR_BITS>>,
36    // Number of iterations (taps-1/2)
37    iters: Constant<Bits<ADDR_BITS>>,
38    // Size of the data buffer (2**ADDR_BITS - 1)
39    bufsize: Constant<Bits<32>>,
40    // Number of taps
41    taps: Constant<Bits<32>>,
42    // Sample from left and right banks
43    left_sample: Signal<Local, Signed<16>>,
44    right_sample: Signal<Local, Signed<16>>,
45    // Accumulator
46    accum: DFF<Signed<48>>,
47    // FIR state
48    state: DFF<MACFIRState>,
49    // The output of the MAC slice
50    mac_output: Signal<Local, Signed<48>>,
51    // The next write location for data
52    data_write: Signal<Local, Bits<ADDR_BITS>>,
53}
54
55impl<const ADDR_BITS: usize> Logic
56    for MultiplyAccumulateSymmetricFiniteImpulseResponseFilter<ADDR_BITS>
57{
58    #[hdl_gen]
59    fn update(&mut self) {
60        // Connect the clocks
61        self.coeff_memory.clock.next = self.clock.val();
62        self.left_bank.read_clock.next = self.clock.val();
63        self.left_bank.write_clock.next = self.clock.val();
64        self.right_bank.read_clock.next = self.clock.val();
65        self.right_bank.write_clock.next = self.clock.val();
66        dff_setup!(self, clock, head_ptr, index, accum, state);
67        // Connect the head pointer to the write address of the two bank memories
68        self.left_bank.write_address.next = self.head_ptr.d.val();
69        self.right_bank.write_address.next = self.head_ptr.d.val();
70        // Both banks receive the same data...
71        self.left_bank.write_data.next = self.data_in.val();
72        self.right_bank.write_data.next = self.data_in.val();
73        // The write enable is controlled by the external strobe
74        self.left_bank.write_enable.next = self.strobe_in.val();
75        self.right_bank.write_enable.next = self.strobe_in.val();
76        // The read on the two banks is different...
77        self.left_ptr.next = bit_cast::<{ ADDR_BITS }, 32>(
78            bit_cast::<32, { ADDR_BITS }>(self.head_ptr.q.val()) + self.bufsize.val()
79                - self.taps.val()
80                + 1
81                + bit_cast::<32, { ADDR_BITS }>(self.index.q.val()),
82        );
83        // This is a bit awkward.  We want to do wrapping arithmetic, so we need an extra bit,
84        // but because of partial const generic support in Rust, we use 32 bits as an
85        // upper bound.  This should synthesize just fine.
86        self.right_ptr.next = bit_cast::<{ ADDR_BITS }, 32>(
87            bit_cast::<32, { ADDR_BITS }>(self.head_ptr.q.val()) + self.bufsize.val()
88                - bit_cast::<32, { ADDR_BITS }>(self.index.q.val()),
89        );
90        self.left_bank.read_address.next = self.left_ptr.val();
91        self.right_bank.read_address.next = self.right_ptr.val();
92        self.coeff_memory.address.next = self.index.q.val();
93        self.left_sample.next = self.left_bank.read_data.val();
94        self.right_sample.next = self.right_bank.read_data.val();
95        if self.state.q.val() == MACFIRState::CenterTap {
96            self.right_sample.next = 0.into();
97        }
98        // Wire up the accumulator
99        self.mac_output.next = signed_bit_cast::<48, 32>(
100            (self.left_sample.val() + self.right_sample.val()) * (self.coeff_memory.data.val()),
101        ) + self.accum.q.val();
102        if self.state.q.val() == MACFIRState::Idle {
103            self.mac_output.next = 0.into();
104        }
105        // Latch prevention...
106        self.data_write.next = self.head_ptr.q.val();
107        // The output is wired to the accumulator
108        self.data_out.next = self.accum.q.val();
109        self.strobe_out.next = false;
110        self.busy.next = self.state.q.val() != MACFIRState::Idle;
111        // State machine.
112        match self.state.q.val() {
113            MACFIRState::Idle => {
114                if self.strobe_in.val() {
115                    self.state.d.next = MACFIRState::Dwell;
116                }
117            }
118            MACFIRState::Dwell => {
119                self.index.d.next = self.index.q.val() + 1;
120                self.state.d.next = MACFIRState::Compute;
121            }
122            MACFIRState::Compute => {
123                self.index.d.next = self.index.q.val() + 1;
124                self.accum.d.next = self.mac_output.val();
125                if self.index.q.val() == self.iters.val() {
126                    self.state.d.next = MACFIRState::CenterTap;
127                }
128            }
129            MACFIRState::CenterTap => {
130                self.index.d.next = self.index.q.val() + 1;
131                self.accum.d.next = self.mac_output.val();
132                self.state.d.next = MACFIRState::Write;
133            }
134            MACFIRState::Write => {
135                self.strobe_out.next = true;
136                self.state.d.next = MACFIRState::Idle;
137                // Update the data write location (head pointer)
138                self.head_ptr.d.next = self.head_ptr.q.val() + 1;
139                // Reset the counter
140                self.index.d.next = 0.into();
141                self.accum.d.next = 0.into();
142            }
143            _ => {
144                self.state.d.next = MACFIRState::Idle;
145            }
146        }
147        self.data_write.next = self.head_ptr.q.val();
148    }
149}
150
151impl<const ADDR_BITS: usize> MultiplyAccumulateSymmetricFiniteImpulseResponseFilter<ADDR_BITS> {
152    pub fn new(coeffs: &[i16]) -> Self {
153        let taps = coeffs.len();
154        assert!({ ADDR_BITS } >= clog2(taps));
155        // Check for symmetry
156        for ndx in 0..coeffs.len() {
157            assert_eq!(coeffs[ndx], coeffs[taps - 1 - ndx]);
158        }
159        // Check for odd length
160        assert_eq!(coeffs.len() % 2, 1);
161        // Create the compact array
162        let clen = (coeffs.len() + 1) / 2;
163        let coeff_short = coeffs[0..clen].iter().map(|x| *x).collect::<Vec<_>>();
164        let coeffs = coeff_short
165            .iter()
166            .map(|x| x.to_signed_bits())
167            .collect::<Vec<_>>();
168        Self {
169            data_in: Default::default(),
170            strobe_in: Default::default(),
171            data_out: Default::default(),
172            strobe_out: Default::default(),
173            clock: Default::default(),
174            busy: Default::default(),
175            coeff_memory: coeffs.into_iter().into(),
176            left_bank: Default::default(),
177            right_bank: Default::default(),
178            head_ptr: Default::default(),
179            left_ptr: Default::default(),
180            right_ptr: Default::default(),
181            index: Default::default(),
182            iters: Constant::new(((taps - 1) / 2).to_bits()),
183            bufsize: Constant::new(Bits::<ADDR_BITS>::count().to_bits()),
184            left_sample: Default::default(),
185            right_sample: Default::default(),
186            accum: Default::default(),
187            state: Default::default(),
188            mac_output: Default::default(),
189            data_write: Default::default(),
190            taps: Constant::new(taps.to_bits()),
191        }
192    }
193}
194
195#[test]
196fn test_fir_is_synthesizable() {
197    let coeffs = [1, -2, 3, -2, 1];
198    let mut uut = MultiplyAccumulateSymmetricFiniteImpulseResponseFilter::<3>::new(&coeffs);
199    uut.connect_all();
200    let vlog = generate_verilog(&uut);
201    yosys_validate("fir", &vlog).unwrap();
202}