SURF  1.0
AxiLiteRingBuffer.vhd
Go to the documentation of this file.
1 -------------------------------------------------------------------------------
2 -- File : AxiLiteRingBuffer.vhd
3 -- Company : SLAC National Accelerator Laboratory
4 -- Created : 2014-05-02
5 -- Last update: 2016-02-05
6 -------------------------------------------------------------------------------
7 -- Description: Wrapper for simple BRAM based ring buffer with AXI-Lite interface
8 -------------------------------------------------------------------------------
9 -- This file is part of 'SLAC Firmware Standard Library'.
10 -- It is subject to the license terms in the LICENSE.txt file found in the
11 -- top-level directory of this distribution and at:
12 -- https://confluence.slac.stanford.edu/display/ppareg/LICENSE.html.
13 -- No part of 'SLAC Firmware Standard Library', including this file,
14 -- may be copied, modified, propagated, or distributed except according to
15 -- the terms contained in the LICENSE.txt file.
16 -------------------------------------------------------------------------------
17 
18 library ieee;
19 use ieee.std_logic_1164.all;
20 use ieee.std_logic_arith.all;
21 use ieee.std_logic_unsigned.all;
22 
23 use work.StdRtlPkg.all;
24 use work.AxiLitePkg.all;
25 
26 --! @see entity
27  --! @ingroup axi
29  generic (
30  -- General Configurations
31  TPD_G : time := 1 ns;
32  BRAM_EN_G : boolean := true;
33  REG_EN_G : boolean := true;
34  DATA_WIDTH_G : positive range 1 to 32 := 32;
35  RAM_ADDR_WIDTH_G : positive range 1 to 19 := 10;
37  port (
38  -- Data to store in ring buffer
39  dataClk : in sl;
40  dataRst : in sl := '0';
41  dataValid : in sl := '1';
42  dataValue : in slv(DATA_WIDTH_G-1 downto 0);
43  bufferEnable : in sl := '0';
44  bufferClear : in sl := '0';
45  -- AXI-Lite interface for readout
46  axilClk : in sl;
47  axilRst : in sl;
52 end AxiLiteRingBuffer;
53 
54 architecture rtl of AxiLiteRingBuffer is
55 
56  ------------------------------
57  -- Stream clock domain signals
58  ------------------------------
59  type DataRegType is record
60  ramWrEn : sl;
61  ramWrData : slv(DATA_WIDTH_G-1 downto 0);
62  bufferLength : slv(RAM_ADDR_WIDTH_G-1 downto 0);
63  firstAddr : slv(RAM_ADDR_WIDTH_G-1 downto 0);
64  nextAddr : slv(RAM_ADDR_WIDTH_G-1 downto 0);
65  end record;
66 
67  constant DATA_REG_INIT_C : DataRegType := (
68  ramWrEn => '0',
69  ramWrData => (others => '0'),
70  bufferLength => (others => '0'),
71  firstAddr => (others => '0'),
72  nextAddr => (others => '0'));
73 
74  signal dataR : DataRegType := DATA_REG_INIT_C;
75  signal dataRin : DataRegType;
76 
77  signal axilBufferEnable : sl; -- Synchronized AXI register
78  signal axilBufferClear : sl; -- Synchronized AXI register
79 
80  --------------------------------
81  -- AXI-Lite clock domain signals
82  --------------------------------
83  constant AXIL_ADDR_WIDTH_C : integer := RAM_ADDR_WIDTH_G+3;
84 
85  type AxilRegType is record
86  bufferEnable : sl;
87  bufferClear : sl;
88  ramRdAddr : slv(RAM_ADDR_WIDTH_G-1 downto 0);
89  axilRdEn : slv(2 downto 0);
92  end record;
93 
94  constant AXIL_REG_INIT_C : AxilRegType := (
95  bufferEnable => '0',
96  bufferClear => '0',
97  ramRdAddr => (others => '0'),
98  axilRdEn => "000",
101 
102  signal axilR : AxilRegType := AXIL_REG_INIT_C;
103  signal axilRin : AxilRegType;
104 
105  signal axilRamRdData : slv(DATA_WIDTH_G-1 downto 0);
106  signal axilFirstAddr : slv(RAM_ADDR_WIDTH_G-1 downto 0);
107  signal axilLength : slv(RAM_ADDR_WIDTH_G-1 downto 0);
108 
109  signal extBufferEnable : sl;
110  signal extBufferClear : sl;
111 
112 begin
113 
114  ----------------------
115  -- Instantiate the RAM
116  ----------------------
117  DualPortRam_1 : entity work.DualPortRam
118  generic map (
119  TPD_G => TPD_G,
120  BRAM_EN_G => BRAM_EN_G,
121  REG_EN_G => REG_EN_G,
122  MODE_G => "read-first",
123  DOB_REG_G => REG_EN_G,
126  port map (
127  clka => dataClk,
128  wea => dataR.ramWrEn,
129  rsta => dataRst,
130  addra => dataR.nextAddr,
131  dina => dataR.ramWrData,
132  douta => open,
133  clkb => axilClk,
134  rstb => axilRst,
135  addrb => axilR.ramRdAddr,
136  doutb => axilRamRdData);
137 
138  -------------------------------
139  -- Synchronize AXI registers to data clock dataClk
140  -------------------------------
141  Synchronizer_bufferEn : entity work.Synchronizer
142  generic map (
143  TPD_G => TPD_G)
144  port map (
145  clk => dataClk,
146  rst => dataRst,
147  dataIn => axilR.bufferEnable,
148  dataOut => axilBufferEnable);
149 
150  Synchronizer_bufferClear : entity work.SynchronizerOneShot
151  generic map (
152  TPD_G => TPD_G)
153  port map (
154  clk => dataClk,
155  rst => dataRst,
156  dataIn => axilR.bufferClear,
157  dataOut => axilBufferClear);
158 
159  --------------------------
160  -- Main AXI-Stream process
161  --------------------------
162  dataComb : process (axilBufferClear, axilBufferEnable, bufferClear, bufferEnable, dataR, dataRst,
163  dataValid, dataValue) is
164  variable v : DataRegType;
165  variable enable : sl;
166  variable clear : sl;
167  begin
168  -- Latch the current value
169  v := dataR;
170 
171  -- Reset strobes
172  v.ramWrEn := '0';
173 
174  -- Default assignment
175  v.ramWrData := dataValue;
176  enable := bufferEnable or axilBufferEnable;
177  clear := bufferClear or axilBufferClear;
178 
179  -- Increment the addresses on each valid if logging enabled
180  if (dataValid = '1') and (enable = '1') then
181  -- Trigger a write
182  v.ramWrEn := '1';
183 
184  -- Increment the address
185  v.nextAddr := dataR.nextAddr + 1;
186  -- Check if the write pointer = read pointer
187  if (v.nextAddr = dataR.firstAddr) then
188  v.firstAddr := dataR.firstAddr + 1;
189  end if;
190  -- Calculate the length of the buffer
191  v.bufferLength := dataR.nextAddr - dataR.firstAddr;
192  end if;
193 
194  -- Synchronous Reset
195  if (dataRst = '1') or (clear = '1') then
196  v := DATA_REG_INIT_C;
197  end if;
198 
199  -- Register the variable for next clock cycle
200  dataRin <= v;
201 
202  end process;
203 
204  dataSeq : process (dataClk) is
205  begin
206  if rising_edge(dataClk) then
207  dataR <= dataRin after TPD_G;
208  end if;
209  end process;
210 
211  -----------------------------------------------------
212  -- Synchronize write address across to AXI-Lite clock
213  -----------------------------------------------------
214  SynchronizerFifo_1 : entity work.SynchronizerFifo
215  generic map (
216  TPD_G => TPD_G,
218  port map (
219  rst => axilRst,
220  wr_clk => dataClk,
221  din => dataR.firstAddr,
222  rd_clk => axilClk,
223  dout => axilFirstAddr);
224 
225  SynchronizerFifo_2 : entity work.SynchronizerFifo
226  generic map (
227  TPD_G => TPD_G,
229  port map (
230  rst => axilRst,
231  wr_clk => dataClk,
232  din => dataR.bufferLength,
233  rd_clk => axilClk,
234  dout => axilLength);
235 
236  Synchronizer_dataBufferEn : entity work.Synchronizer
237  generic map (
238  TPD_G => TPD_G)
239  port map (
240  clk => axilClk,
241  rst => axilRst,
242  dataIn => bufferEnable,
243  dataOut => extBufferEnable);
244 
245  Synchronizer_dataBufferClr : entity work.Synchronizer
246  generic map (
247  TPD_G => TPD_G)
248  port map (
249  clk => axilClk,
250  rst => axilRst,
251  dataIn => bufferClear,
252  dataOut => extbufferClear);
253 
254  ------------------------
255  -- Main AXI-Lite process
256  ------------------------
257  axiComb : process (axilFirstAddr, axilLength, axilR, axilRamRdData, axilReadMaster, axilRst,
258  axilWriteMaster, extBufferClear, extBufferEnable) is
259  variable v : AxilRegType;
260  variable axilStatus : AxiLiteStatusType;
261  variable axiWriteResp : slv(1 downto 0);
262  variable axiReadResp : slv(1 downto 0);
263  begin
264  -- Latch the current value
265  v := axilR;
266 
267  -- Reset strobes
268  v.bufferClear := '0';
269 
270  -- Update Shift Register
271  v.axilRdEn(0) := '0';
272  v.axilRdEn(1) := axilR.axilRdEn(0);
273  v.axilRdEn(2) := axilR.axilRdEn(1);
274 
275  -- Determine the transaction type
276  axiSlaveWaitTxn(axilWriteMaster, axilReadMaster, v.axilWriteSlave, v.axilReadSlave, axilStatus);
277 
278  -- Check for write request
279  if (axilStatus.writeEnable = '1') then
280  -- Check for an out of 32 bit aligned address
281  axiWriteResp := ite(axilWriteMaster.awaddr(1 downto 0) = "00", AXI_RESP_OK_C, AXI_ERROR_RESP_G);
282  -- Check for first mapped address access (which is the control register)
283  if (axilWriteMaster.awaddr(RAM_ADDR_WIDTH_G+2-1 downto 2) = 0) then
286  else
287  -- Unmapped write register access
288  axiWriteResp := AXI_ERROR_RESP_G;
289  end if;
290  -- Set the Slave's response
291  axiSlaveWriteResponse(v.axilWriteSlave, axiWriteResp);
292  end if;
293 
294  -- Check for read request
295  if (axilStatus.readEnable = '1') then
296  -- Reset the read data bus
297  v.axilReadSlave.rdata := (others => '0');
298  -- Check for an out of 32 bit aligned address
299  axiReadResp := ite(axilReadMaster.araddr(1 downto 0) = "00", AXI_RESP_OK_C, AXI_ERROR_RESP_G);
300  -- Control register mapped at address 0
301  if (axilReadMaster.araddr(RAM_ADDR_WIDTH_G+2-1 downto 2) = 0) then
302  v.axilReadSlave.rdata(31) := axilR.bufferEnable;
303  v.axilReadSlave.rdata(30) := axilR.bufferClear;
304  v.axilReadSlave.rdata(29) := extBufferEnable;
305  v.axilReadSlave.rdata(28) := extBufferClear;
306  v.axilReadSlave.rdata(27 downto 20) := toSlv(RAM_ADDR_WIDTH_G, 8); -- Let the software know the configuration
307  v.axilReadSlave.rdata(RAM_ADDR_WIDTH_G-1 downto 0) := axilLength;
308  axiSlaveReadResponse(v.axilReadSlave, axiReadResp);
309  else
310  -- All other AXI-Lite addresses are automatically offset by firstAddr.
311  -- Thus axil word-address 1 always pulls from firstAddr, etc.
312  v.ramRdAddr := axilReadMaster.araddr(RAM_ADDR_WIDTH_G+2-1 downto 2) + axilFirstAddr - 1; -- minus 1 corrects for araddr=0x4 start offset
313 
314  -- Wait 3 cycles before placing the ram read data on the AXIL bus and responding.
315  -- This is enough time to cover every ram type
316  -- LUTRAM + !REG_EN_G = 1 Cycle
317  -- LUTRAM + REG_EN_G = 2 Cycles
318  -- BRAM + !REG_EN_G = 2 Cycles
319  -- BRAM + REG_EN_G = 3 Cycles
320  v.axilRdEn(0) := '1';
321  if (axilR.axilRdEn(2) = '1') then
322  -- Reset the shift register
323  v.axilRdEn := "000";
324  -- Update the read data bus
325  v.axilReadSlave.rdata(DATA_WIDTH_G-1 downto 0) := axilRamRdData;
326  -- Set the Slave's response
327  axiSlaveReadResponse(v.axilReadSlave, axiReadResp);
328  end if;
329  end if;
330  end if;
331 
332  -- Synchronous Reset
333  if (axilRst = '1') then
334  v := AXIL_REG_INIT_C;
335  end if;
336 
337  -- Register the variable for next clock cycle
338  axilRin <= v;
339 
340  -- Outputs
341  axilReadSlave <= axilR.axilReadSlave;
343 
344  end process;
345 
346  axiSeq : process (axilClk) is
347  begin
348  if rising_edge(axilClk) then
349  axilR <= axilRin after TPD_G;
350  end if;
351  end process;
352 
353 end rtl;
out axilWriteSlaveAxiLiteWriteSlaveType
in bufferEnablesl := '0'
BRAM_EN_Gboolean := true
Definition: DualPortRam.vhd:31
out axilReadSlaveAxiLiteReadSlaveType
AxiLiteWriteMasterType
Definition: AxiLitePkg.vhd:111
std_logic sl
Definition: StdRtlPkg.vhd:28
in rstsl :=not RST_POLARITY_G
in dinslv( DATA_WIDTH_G- 1 downto 0)
out doutbslv( DATA_WIDTH_G- 1 downto 0)
Definition: DualPortRam.vhd:57
TPD_Gtime := 1 ns
Definition: DualPortRam.vhd:29
slv( 31 downto 0) rdata
Definition: AxiLitePkg.vhd:89
DATA_WIDTH_Gpositive range 1 to 32:= 32
ADDR_WIDTH_Ginteger range 1 to ( 2** 24):= 4
Definition: DualPortRam.vhd:39
REG_EN_Gboolean := true
MODE_Gstring := "read-first"
Definition: DualPortRam.vhd:35
in clkasl := '0'
Definition: DualPortRam.vhd:43
slv( 31 downto 0) wdata
Definition: AxiLitePkg.vhd:117
out dataOutsl
out doutslv( DATA_WIDTH_G- 1 downto 0)
_library_ ieeeieee
Definition: AxiLitePkg.vhd:18
in rstsl :=not RST_POLARITY_G
in axilReadMasterAxiLiteReadMasterType
slv( 1 downto 0) := "11" AXI_RESP_DECERR_C
Definition: AxiLitePkg.vhd:49
in clkbsl := '0'
Definition: DualPortRam.vhd:53
DOB_REG_Gboolean := false
Definition: DualPortRam.vhd:34
RAM_ADDR_WIDTH_Gpositive range 1 to 19:= 10
out doutaslv( DATA_WIDTH_G- 1 downto 0)
Definition: DualPortRam.vhd:50
AxiLiteReadMasterType
Definition: AxiLitePkg.vhd:59
in addrbslv( ADDR_WIDTH_G- 1 downto 0) :=( others => '0')
Definition: DualPortRam.vhd:56
in weasl := '0'
Definition: DualPortRam.vhd:45
slv( 31 downto 0) awaddr
Definition: AxiLitePkg.vhd:113
TPD_Gtime := 1 ns
DATA_WIDTH_Ginteger range 1 to ( 2** 24):= 16
Definition: DualPortRam.vhd:37
in dataValueslv( DATA_WIDTH_G- 1 downto 0)
AxiLiteReadSlaveType :=(arready => '0',rdata =>( others => '0'),rresp =>( others => '0'),rvalid => '0') AXI_LITE_READ_SLAVE_INIT_C
Definition: AxiLitePkg.vhd:95
in rstasl :=not ( RST_POLARITY_G)
Definition: DualPortRam.vhd:47
in dinaslv( DATA_WIDTH_G- 1 downto 0) :=( others => '0')
Definition: DualPortRam.vhd:49
REG_EN_Gboolean := true
Definition: DualPortRam.vhd:32
in axilWriteMasterAxiLiteWriteMasterType
AxiLiteReadSlaveType
Definition: AxiLitePkg.vhd:85
BRAM_EN_Gboolean := true
slv( 1 downto 0) := "00" AXI_RESP_OK_C
Definition: AxiLitePkg.vhd:31
slv( 31 downto 0) araddr
Definition: AxiLitePkg.vhd:61
AXI_ERROR_RESP_Gslv( 1 downto 0) := AXI_RESP_DECERR_C
in addraslv( ADDR_WIDTH_G- 1 downto 0) :=( others => '0')
Definition: DualPortRam.vhd:48
in rstbsl :=not ( RST_POLARITY_G)
Definition: DualPortRam.vhd:55
AxiLiteWriteSlaveType :=(awready => '0',wready => '0',bresp =>( others => '0'),bvalid => '0') AXI_LITE_WRITE_SLAVE_INIT_C
Definition: AxiLitePkg.vhd:156
DATA_WIDTH_Ginteger range 1 to ( 2** 24):= 16
std_logic_vector slv
Definition: StdRtlPkg.vhd:29