rust_hdl_widgets/fifo/
sync_fifo.rs

1use rust_hdl_core::prelude::*;
2
3use crate::fifo::fifo_logic::{FIFOReadLogic, FIFOWriteLogic};
4use crate::ramrom::ram::RAM;
5
6#[macro_export]
7macro_rules! declare_sync_fifo {
8    ($name: ident, $kind: ty, $count: expr, $block: expr) => {
9        pub type $name = SynchronousFIFO<$kind, { clog2($count) }, { clog2($count) + 1 }, $block>;
10    };
11}
12
13#[derive(LogicBlock, Default)]
14pub struct SynchronousFIFO<D: Synth, const N: usize, const NP1: usize, const BLOCK_SIZE: u32> {
15    pub clock: Signal<In, Clock>,
16    // Read interface
17    pub read: Signal<In, Bit>,
18    pub data_out: Signal<Out, D>,
19    pub empty: Signal<Out, Bit>,
20    pub almost_empty: Signal<Out, Bit>,
21    pub underflow: Signal<Out, Bit>,
22    // Write interface
23    pub write: Signal<In, Bit>,
24    pub data_in: Signal<In, D>,
25    pub full: Signal<Out, Bit>,
26    pub almost_full: Signal<Out, Bit>,
27    pub overflow: Signal<Out, Bit>,
28    // Internal RAM
29    ram: RAM<D, N>,
30    // Read logic
31    read_logic: FIFOReadLogic<D, N, NP1, BLOCK_SIZE>,
32    // write logic
33    write_logic: FIFOWriteLogic<D, N, NP1, BLOCK_SIZE>,
34}
35
36// Ported from fifo.luc in AlchitryLabs under MIT license
37// Modified to use an extra bit for the read/write address
38// pointers per:
39// http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf
40// That modification allows you to fill the FIFO all the way
41// since you have 2 different conditions for full and empty.
42// The design was also split into sub-circuits to allow reuse,
43// following the suggestion in the Cummings paper.
44// Note that we could skip the read_if, and write_if headers
45// and connect directly to the read/write circuitry from outside
46// but that this makes the FIFO less pleasant to use, since
47// you then need fifo.write.sig.write, instead of
48// fifo.write_if.write
49impl<D: Synth, const N: usize, const NP1: usize, const BLOCK_SIZE: u32> Logic
50    for SynchronousFIFO<D, N, NP1, BLOCK_SIZE>
51{
52    #[hdl_gen]
53    fn update(&mut self) {
54        clock!(self, clock, read_logic, write_logic);
55        // Connect up the read interface
56        self.read_logic.read.next = self.read.val();
57        self.empty.next = self.read_logic.empty.val();
58        self.almost_empty.next = self.read_logic.almost_empty.val();
59        self.data_out.next = self.read_logic.data_out.val();
60        self.underflow.next = self.read_logic.underflow.val();
61        // Connect up the write interface
62        self.overflow.next = self.write_logic.overflow.val();
63        self.almost_full.next = self.write_logic.almost_full.val();
64        self.full.next = self.write_logic.full.val();
65        self.write_logic.write.next = self.write.val();
66        self.write_logic.data_in.next = self.data_in.val();
67        // Connect the RAM to the two blocks
68        self.ram.write_clock.next = self.clock.val();
69        self.ram.write_enable.next = self.write_logic.ram_write_enable.val();
70        self.ram.write_address.next = self.write_logic.ram_write_address.val();
71        self.ram.write_data.next = self.write_logic.ram_write_data.val();
72        self.ram.read_clock.next = self.clock.val();
73        self.ram.read_address.next = self.read_logic.ram_read_address.val();
74        self.read_logic.ram_read_data.next = self.ram.read_data.val();
75        // Connect the two blocks
76        self.read_logic.write_address_delayed.next = self.write_logic.write_address_delayed.val();
77        self.write_logic.read_address.next = self.read_logic.read_address_out.val();
78    }
79}
80
81#[test]
82fn component_fifo_is_synthesizable() {
83    let mut dev: SynchronousFIFO<Bits<8>, 4, 5, 1> = Default::default();
84    dev.connect_all();
85    yosys_validate("fifo", &generate_verilog(&dev)).unwrap();
86}
87
88#[test]
89fn test_fifo_macro() {
90    declare_sync_fifo!(FIFOTest, Bits<8>, 32, 1);
91    let _dev = FIFOTest::default();
92}