|
| 1 | +------------------------------------------------------------------------------- |
| 2 | +-- Company : SLAC National Accelerator Laboratory |
| 3 | +------------------------------------------------------------------------------- |
| 4 | +-- Description: |
| 5 | +-- Block serves as an FIFO that buffers a user-selectable number of frames |
| 6 | +-- (defined by TLAST). Useful for benchmarking latency of IPs. |
| 7 | +------------------------------------------------------------------------------- |
| 8 | +-- This file is part of 'SLAC Firmware Standard Library'. |
| 9 | +-- It is subject to the license terms in the LICENSE.txt file found in the |
| 10 | +-- top-level directory of this distribution and at: |
| 11 | +-- https://confluence.slac.stanford.edu/display/ppareg/LICENSE.html. |
| 12 | +-- No part of 'SLAC Firmware Standard Library', including this file, |
| 13 | +-- may be copied, modified, propagated, or distributed except according to |
| 14 | +-- the terms contained in the LICENSE.txt file. |
| 15 | +------------------------------------------------------------------------------- |
| 16 | + |
| 17 | +library ieee; |
| 18 | +use ieee.std_logic_1164.all; |
| 19 | +use ieee.std_logic_unsigned.all; |
| 20 | +use ieee.std_logic_arith.all; |
| 21 | + |
| 22 | +library surf; |
| 23 | +use surf.StdRtlPkg.all; |
| 24 | +use surf.AxiStreamPkg.all; |
| 25 | +use surf.AxiLitePkg.all; |
| 26 | + |
| 27 | +entity AxiStreamBatchingFifo is |
| 28 | + generic ( |
| 29 | + -- General Configurations |
| 30 | + TPD_G : time := 1 ns; |
| 31 | + FIFO_ADDR_WIDTH_G : integer range 4 to 48 := 9; |
| 32 | + -- AXI Stream Port Configurations |
| 33 | + SLAVE_AXI_CONFIG_G : AxiStreamConfigType; |
| 34 | + MASTER_AXI_CONFIG_G : AxiStreamConfigType); |
| 35 | + port ( |
| 36 | + -- Control Port |
| 37 | + axilClk : in sl; |
| 38 | + axilRst : in sl; |
| 39 | + sAxilWriteMaster : in AxiLiteWriteMasterType; |
| 40 | + sAxilWriteSlave : out AxiLiteWriteSlaveType; |
| 41 | + sAxilReadMaster : in AxiLiteReadMasterType; |
| 42 | + sAxilReadSlave : out AxiLiteReadSlaveType; |
| 43 | + |
| 44 | + -- Slave Port |
| 45 | + sAxisClk : in sl; |
| 46 | + sAxisRst : in sl; |
| 47 | + sAxisMaster : in AxiStreamMasterType; |
| 48 | + sAxisSlave : out AxiStreamSlaveType; |
| 49 | + |
| 50 | + -- Master Port |
| 51 | + mAxisClk : in sl; |
| 52 | + mAxisRst : in sl; |
| 53 | + mAxisMaster : out AxiStreamMasterType; |
| 54 | + mAxisSlave : in AxiStreamSlaveType); |
| 55 | +end AxiStreamBatchingFifo; |
| 56 | + |
| 57 | +architecture rtl of AxiStreamBatchingFifo is |
| 58 | + |
| 59 | + signal batchSizeAxiL : slv(31 downto 0); |
| 60 | + signal batchSize : slv(31 downto 0); |
| 61 | + |
| 62 | + signal axisMasterSync : AxiStreamMasterType; |
| 63 | + signal axisSlaveSync : AxiStreamSlaveType; |
| 64 | + |
| 65 | + signal axisMasterFifo : AxiStreamMasterType; |
| 66 | + signal axisSlaveFifo : AxiStreamSlaveType; |
| 67 | + |
| 68 | + type RegType is record |
| 69 | + frameBatched : slv(31 downto 0); |
| 70 | + frameToSend : slv(31 downto 0); |
| 71 | + sending : sl; |
| 72 | + end record; |
| 73 | + |
| 74 | + constant REG_INIT_C : RegType := ( |
| 75 | + frameBatched => (others => '0'), |
| 76 | + frameToSend => (others => '0'), |
| 77 | + sending => '0' |
| 78 | + ); |
| 79 | + |
| 80 | + signal r : RegType := REG_INIT_C; |
| 81 | + signal rin : RegType; |
| 82 | + |
| 83 | + signal rAxilWriteSlave : AxiLiteWriteSlaveType := AXI_LITE_WRITE_SLAVE_INIT_C; |
| 84 | + signal rAxilReadSlave : AxiLiteReadSlaveType := AXI_LITE_READ_SLAVE_INIT_C; |
| 85 | + signal rinAxilWriteSlave : AxiLiteWriteSlaveType := AXI_LITE_WRITE_SLAVE_INIT_C; |
| 86 | + signal rinAxilReadSlave : AxiLiteReadSlaveType := AXI_LITE_READ_SLAVE_INIT_C; |
| 87 | + |
| 88 | + signal combAxisMaster : AxiStreamMasterType; |
| 89 | + signal combAxisSlave : AxiStreamSlaveType; |
| 90 | + |
| 91 | +begin |
| 92 | + |
| 93 | + ---------------------------------- |
| 94 | + ------- CONTROL INTERFACE ------- |
| 95 | + ---------------------------------- |
| 96 | + |
| 97 | + comb_axil : process (sAxilReadMaster, sAxilWriteMaster, axilRst, rAxilWriteSlave, rAxilReadSlave) is |
| 98 | + variable vAxilWriteSlave : AxiLiteWriteSlaveType := AXI_LITE_WRITE_SLAVE_INIT_C; |
| 99 | + variable vAxilReadSlave : AxiLiteReadSlaveType := AXI_LITE_READ_SLAVE_INIT_C; |
| 100 | + variable regCon : AxiLiteEndPointType; |
| 101 | + variable vBatchSize : slv(31 downto 0); |
| 102 | + begin |
| 103 | + |
| 104 | + -- Latch input values |
| 105 | + vAxilWriteSlave := rAxilWriteSlave; |
| 106 | + vAxilReadSlave := rAxilReadSlave; |
| 107 | + |
| 108 | + -- Determine the transaction type |
| 109 | + axiSlaveWaitTxn(regCon, sAxilWriteMaster, sAxilReadMaster, vAxilWriteSlave, vAxilReadSlave); |
| 110 | + |
| 111 | + -- Read batch size |
| 112 | + axiSlaveRegister(regCon, "0000", 0, vBatchSize); -- 2-bit wide because only one reg |
| 113 | + |
| 114 | + -- Closeout the transaction |
| 115 | + axiSlaveDefault(regCon, vAxilWriteSlave, vAxilReadSlave, AXI_RESP_DECERR_C); |
| 116 | + |
| 117 | + -- Synchronous reset |
| 118 | + if (axilRst = '1') then |
| 119 | + vAxilWriteSlave := AXI_LITE_WRITE_SLAVE_INIT_C; |
| 120 | + vAxilReadSlave := AXI_LITE_READ_SLAVE_INIT_C; |
| 121 | + end if; |
| 122 | + |
| 123 | + -- Combinatory output |
| 124 | + rinAxilReadSlave <= vAxilReadSlave; |
| 125 | + rinAxilWriteSlave <= vAxilWriteSlave; |
| 126 | + batchSizeAxiL <= vBatchSize; |
| 127 | + |
| 128 | + -- Outputs |
| 129 | + sAxilReadSlave <= rAxilReadSlave; |
| 130 | + sAxilWriteSlave <= rAxilWriteSlave; |
| 131 | + end process comb_axil; |
| 132 | + |
| 133 | + seq_axil : process (axilClk) is |
| 134 | + begin |
| 135 | + if rising_edge(axilClk) then |
| 136 | + rAxilReadSlave <= rinAxilReadSlave after TPD_G; |
| 137 | + rAxilWriteSlave <= rinAxilWriteSlave after TPD_G; |
| 138 | + end if; |
| 139 | + end process seq_axil; |
| 140 | + |
| 141 | + ---------------------------------- |
| 142 | + ----- END CONTROL INTERFACE ----- |
| 143 | + ---------------------------------- |
| 144 | + |
| 145 | + |
| 146 | + ---------------------------------- |
| 147 | + ----- CLOCK DOMAIN CROSSINGS ----- |
| 148 | + ---------------------------------- |
| 149 | + -- All control signals need to be brought into the mAxisClk domain |
| 150 | + -- State of the main FIFO becomes more consistent |
| 151 | + |
| 152 | + U_Axis_CDC : entity surf.AxiStreamFifoV2 |
| 153 | + generic map( |
| 154 | + TPD_G => TPD_G, |
| 155 | + MEMORY_TYPE_G => "auto", |
| 156 | + GEN_SYNC_FIFO_G => false, |
| 157 | + FIFO_ADDR_WIDTH_G => 5, -- Shallow, just for sync |
| 158 | + SLAVE_AXI_CONFIG_G => SLAVE_AXI_CONFIG_G, |
| 159 | + MASTER_AXI_CONFIG_G => SLAVE_AXI_CONFIG_G) -- Do not change shape |
| 160 | + port map( |
| 161 | + sAxisClk => sAxisClk, |
| 162 | + sAxisRst => sAxisRst, |
| 163 | + sAxisMaster => sAxisMaster, |
| 164 | + sAxisSlave => sAxisSlave, |
| 165 | + |
| 166 | + mAxisClk => mAxisClk, |
| 167 | + mAxisRst => mAxisRst, |
| 168 | + mAxisMaster => axisMasterSync, |
| 169 | + mAxisSlave => axisSlaveSync ); |
| 170 | + |
| 171 | + U_BatchSize_CDC : entity surf.SynchronizerFifo |
| 172 | + generic map( |
| 173 | + TPD_G => TPD_G, |
| 174 | + DATA_WIDTH_G => 32, |
| 175 | + INIT_G => x"0000_0001") |
| 176 | + port map( |
| 177 | + wr_clk => axilClk, |
| 178 | + din => batchSizeAxiL, |
| 179 | + rd_clk => mAxisClk, |
| 180 | + dout => batchSize); |
| 181 | + |
| 182 | + ---------------------------------- |
| 183 | + --- END CLOCK DOMAIN CROSSINGS --- |
| 184 | + ---------------------------------- |
| 185 | + |
| 186 | + |
| 187 | + ---------------------------------- |
| 188 | + --------- MAIN DATA FIFO --------- |
| 189 | + ---------------------------------- |
| 190 | + |
| 191 | + U_Data_FIFO : entity surf.AxiStreamFifoV2 |
| 192 | + generic map( |
| 193 | + TPD_G => TPD_G, |
| 194 | + FIFO_ADDR_WIDTH_G => FIFO_ADDR_WIDTH_G, |
| 195 | + GEN_SYNC_FIFO_G => true, |
| 196 | + SLAVE_AXI_CONFIG_G => SLAVE_AXI_CONFIG_G, |
| 197 | + MASTER_AXI_CONFIG_G => MASTER_AXI_CONFIG_G) |
| 198 | + port map( |
| 199 | + -- Slave Port |
| 200 | + sAxisClk => mAxisClk, |
| 201 | + sAxisRst => mAxisRst, |
| 202 | + sAxisMaster => axisMasterSync, |
| 203 | + sAxisSlave => axisSlaveSync, |
| 204 | + |
| 205 | + -- Master Port |
| 206 | + mAxisClk => mAxisClk, |
| 207 | + mAxisRst => mAxisRst, |
| 208 | + mAxisMaster => axisMasterFifo, |
| 209 | + mAxisSlave => axisSlaveFifo ); |
| 210 | + |
| 211 | + ---------------------------------- |
| 212 | + ------- END MAIN DATA FIFO ------- |
| 213 | + ---------------------------------- |
| 214 | + |
| 215 | + -- These signals are not responsible for hanshakes and can |
| 216 | + -- just be forwarded |
| 217 | + combAxisMaster.tData <= axisMasterFifo.tData; |
| 218 | + combAxisMaster.tStrb <= axisMasterFifo.tStrb; |
| 219 | + combAxisMaster.tKeep <= axisMasterFifo.tKeep; |
| 220 | + combAxisMaster.tLast <= axisMasterFifo.tLast; |
| 221 | + combAxisMaster.tDest <= axisMasterFifo.tDest; |
| 222 | + combAxisMaster.tId <= axisMasterFifo.tId; |
| 223 | + combAxisMaster.tUser <= axisMasterFifo.tUser; |
| 224 | + |
| 225 | + comb : process (r, axisMasterFifo, axisSlaveFifo, axisMasterSync, axisSlaveSync, batchSize, combAxisSlave) is |
| 226 | + variable v : RegType; |
| 227 | + variable isAcceptedFrame : sl; |
| 228 | + variable isOutputFrame : sl; |
| 229 | + begin |
| 230 | + -- Latch current state |
| 231 | + v := r; |
| 232 | + |
| 233 | + -- Check if there is a frame accepted |
| 234 | + isAcceptedFrame := axisMasterSync.tValid and axisMasterSync.tLast and axisSlaveSync.tReady; |
| 235 | + isOutputFrame := r.sending and axisMasterFifo.tValid and axisMasterFifo.tLast and axisSlaveFifo.tReady; |
| 236 | + |
| 237 | + -- Update counters |
| 238 | + if isAcceptedFrame = '1' then |
| 239 | + v.frameBatched := r.frameBatched + 1; |
| 240 | + else |
| 241 | + v.frameBatched := r.frameBatched; |
| 242 | + end if; |
| 243 | + |
| 244 | + if isOutputFrame = '1' then |
| 245 | + v.frameBatched := v.frameBatched - 1; |
| 246 | + v.frameToSend := r.frameToSend - 1; |
| 247 | + end if; |
| 248 | + |
| 249 | + if v.frameToSend = 0 then |
| 250 | + v.sending := '0'; |
| 251 | + end if; |
| 252 | + |
| 253 | + if r.sending = '0' and r.frameBatched >= batchSize then |
| 254 | + v.sending := '1'; |
| 255 | + v.frameToSend := batchSize; |
| 256 | + end if; |
| 257 | + |
| 258 | + combAxisMaster.tValid <= r.sending and axisMasterFifo.tValid; |
| 259 | + axisSlaveFifo.tReady <= r.sending and combAxisSlave.tReady; |
| 260 | + |
| 261 | + rin <= v; |
| 262 | + end process comb; |
| 263 | + |
| 264 | + seq : process(mAxisClk) is |
| 265 | + begin |
| 266 | + if rising_edge(mAxisClk) then |
| 267 | + if mAxisRst = '1' then |
| 268 | + r <= REG_INIT_C after TPD_G; |
| 269 | + else |
| 270 | + r <= rin after TPD_G; |
| 271 | + end if; |
| 272 | + end if; |
| 273 | + end process seq; |
| 274 | + |
| 275 | + -- Avoid having combinatorial outputs on the output |
| 276 | + -- AxiStream by adding a pipeline stage |
| 277 | + U_Output_Pipeline : entity surf.AxiStreamPipeline |
| 278 | + generic map( |
| 279 | + TPD_G => TPD_G) |
| 280 | + port map( |
| 281 | + axisClk => mAxisClk, |
| 282 | + axisRst => mAxisRst, |
| 283 | + |
| 284 | + sAxisMaster => combAxisMaster, |
| 285 | + sAxisSlave => combAxisSlave, |
| 286 | + |
| 287 | + mAxisMaster => mAxisMaster, |
| 288 | + mAxisSlave => mAxisSlave); |
| 289 | + |
| 290 | +end rtl; |
0 commit comments