1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use super::mcb_if::MCBInterface4GDDR3;
use super::mig7::MemoryInterfaceGenerator7Series;
use crate::core::prelude::*;
use crate::widgets::prelude::*;

#[derive(LogicState, Debug, Copy, Clone, PartialEq)]
pub enum DDR7FIFOState {
    Booting,
    Idle,
    Read,
    Write,
}

#[derive(LogicBlock, Default)]
pub struct DDR7FIFO<const N: usize> {
    // Reset - required
    pub reset: Signal<In, Bit>,
    // System clock
    pub sys_clock_p: Signal<In, Clock>,
    pub sys_clock_n: Signal<In, Clock>,
    // Interface to DDR3
    pub mcb: MCBInterface4GDDR3,
    // The write interface
    pub data_in: Signal<In, Bits<N>>,
    pub full: Signal<Out, Bit>,
    pub write: Signal<In, Bit>,
    pub write_clock: Signal<In, Clock>,
    // The read interface
    pub data_out: Signal<Out, Bits<N>>,
    pub empty: Signal<Out, Bit>,
    pub read: Signal<In, Bit>,
    pub read_clock: Signal<In, Clock>,
    // The mig
    mig: MemoryInterfaceGenerator7Series,
    // Front end fifo
    front_porch: CrossWidenFIFO<{ N }, 10, 11, 128, 5, 6>,
    // The write address of the FIFO
    write_address: DFF<Bits<28>>,
    // The read address of the FIFO
    read_address: DFF<Bits<28>>,
    // Back end fifo
    back_porch: CrossNarrowFIFO<128, 8, 9, { N }, 10, 11>,
    // Current state
    state: DFF<DDR7FIFOState>,
    // Is DDR FIFO empty
    is_empty: Signal<Local, Bit>,
    pub status: Signal<Out, Bits<8>>,
}

impl<const N: usize> Logic for DDR7FIFO<N> {
    #[hdl_gen]
    fn update(&mut self) {
        // Link the mcb interface
        MCBInterface4GDDR3::link(&mut self.mcb, &mut self.mig.mcb);
        // Forward the raw clocks
        self.mig.raw_pos_clock.next = self.sys_clock_p.val();
        self.mig.raw_neg_clock.next = self.sys_clock_n.val();
        // Connect the write interface to the front porch
        self.front_porch.write_clock.next = self.write_clock.val();
        self.front_porch.data_in.next = self.data_in.val();
        self.front_porch.write.next = self.write.val();
        self.full.next = self.front_porch.full.val();
        // Connect the read interface to the back porch
        self.back_porch.read_clock.next = self.read_clock.val();
        self.data_out.next = self.back_porch.data_out.val();
        self.back_porch.read.next = self.read.val();
        self.empty.next = self.back_porch.empty.val();
        // Connect the front porch to the MIG
        self.mig.write_data_in.next = self.front_porch.data_out.val();
        self.front_porch.read_clock.next = self.mig.clock.val();
        self.front_porch.read.next = false;
        // Connect the back porch to the MIG
        self.back_porch.data_in.next = self.mig.read_data_out.val();
        self.back_porch.write_clock.next = self.mig.clock.val();
        self.back_porch.write.next = false;
        // Clock the flops
        self.write_address.clk.next = self.mig.clock.val();
        self.read_address.clk.next = self.mig.clock.val();
        self.state.clk.next = self.mig.clock.val();
        // Latch prevention for the flops
        self.write_address.d.next = self.write_address.q.val();
        self.read_address.d.next = self.read_address.q.val();
        self.state.d.next = self.state.q.val();
        // Set default inputs for the mig
        self.mig.address.next = 0_u32.into();
        self.mig.enable.next = false;
        self.mig.write_enable.next = false;
        self.mig.reset.next = self.reset.val();
        self.mig.write_data_mask.next = 0_u32.into();
        self.mig.write_data_end.next = false;
        self.mig.command.next = 0_u8.into();
        // Compute the empty flag
        self.is_empty.next = self.read_address.q.val() == self.write_address.q.val();
        // State machine update
        match self.state.q.val() {
            DDR7FIFOState::Booting => {
                if self.mig.calib_done.val() & !self.reset.val() {
                    self.state.d.next = DDR7FIFOState::Idle;
                }
            }
            DDR7FIFOState::Idle => {
                if !self.is_empty.val() & !self.back_porch.full.val() & self.mig.ready.val() {
                    // We can read...
                    self.mig.command.next = 1_u8.into();
                    self.mig.enable.next = true;
                    self.mig.address.next = bit_cast::<29, 28>(self.read_address.q.val());
                    self.state.d.next = DDR7FIFOState::Read;
                } else if !self.front_porch.empty.val()
                    & self.mig.ready.val()
                    & self.mig.write_fifo_not_full.val()
                {
                    self.mig.write_enable.next = true;
                    self.mig.write_data_end.next = true;
                    self.front_porch.read.next = true;
                    self.state.d.next = DDR7FIFOState::Write;
                }
            }
            DDR7FIFOState::Write => {
                self.mig.command.next = 0_u8.into(); // Write command
                self.mig.enable.next = true;
                self.mig.address.next = bit_cast::<29, 28>(self.write_address.q.val());
                if self.mig.ready.val() {
                    self.write_address.d.next = self.write_address.q.val() + 8_u32; // This is the number of 16 bit words
                    self.state.d.next = DDR7FIFOState::Idle;
                }
            }
            DDR7FIFOState::Read => {
                self.back_porch.write.next = self.mig.read_data_valid.val();
                if self.mig.read_data_valid.val() {
                    self.read_address.d.next = self.read_address.q.val() + 8_u32;
                    self.state.d.next = DDR7FIFOState::Idle;
                }
            }
        }
        self.status.next = bit_cast::<8, 1>(self.front_porch.empty.val().into())
            | (bit_cast::<8, 1>(self.front_porch.full.val().into()) << 1_usize)
            | (bit_cast::<8, 1>(self.back_porch.empty.val().into()) << 2_usize)
            | (bit_cast::<8, 1>(self.back_porch.full.val().into()) << 3_usize)
            | (bit_cast::<8, 1>(self.is_empty.val().into()) << 4_usize)
            | (bit_cast::<8, 1>((self.state.q.val() == DDR7FIFOState::Idle).into()) << 5_usize)
            | (bit_cast::<8, 1>(self.mig.ready.val().into()) << 6_usize);
    }
}

#[test]
fn test_ddr7_fifo_gen() {
    let mut ddr = TopWrap::new(DDR7FIFO::<32>::default());
    ddr.uut.sys_clock_n.connect();
    ddr.uut.sys_clock_p.connect();
    ddr.uut.mcb.link_connect_dest();
    ddr.uut.data_in.connect();
    ddr.uut.write.connect();
    ddr.uut.read.connect();
    ddr.uut.write_clock.connect();
    ddr.uut.read_clock.connect();
    ddr.uut.reset.connect();
    ddr.connect_all();
    yosys_validate("ddr7", &generate_verilog(&ddr)).unwrap();
}