39079e929c346ba3787ce6fe4fec0fdf0b58c3c4
[DuneNvme.git] / src / NvmeStorage.vhd
1 --------------------------------------------------------------------------------
2 -- NvmeStorage.vhd Nvme storage access module
3 -------------------------------------------------------------------------------
4 --!
5 --! @class      NvmeStorage
6 --! @author     Terry Barnaby (terry.barnaby@beam.ltd.uk)
7 --! @date       2020-05-12
8 --! @version    0.5.1
9 --!
10 --! @brief
11 --! This is the main top level NvmeStorage module that provides access to the NVMe devices
12 --! over the Axil bus and Axis request/reply streams.
13 --!
14 --! @details
15 --! The main Nvme working module is NvmeStorageUnit. This NvmeStorage module splits the incomming
16 --! data stream into two at the NvmeStoargeBlock level (8k) passing alternate blocks into the two
17 --! NvmeStorageUnit engines.
18 --! At the moment, during development, it just passes the data through to a single NvmeStorageUnit module.
19 --! See the DuneNvmeStorageManual for more details.
20 --!
21 --! @copyright GNU GPL License
22 --! Copyright (c) Beam Ltd, All rights reserved. <br>
23 --! This code is free software: you can redistribute it and/or modify
24 --! it under the terms of the GNU General Public License as published by
25 --! the Free Software Foundation, either version 3 of the License, or
26 --! (at your option) any later version.
27 --! This program is distributed in the hope that it will be useful,
28 --! but WITHOUT ANY WARRANTY; without even the implied warranty of
29 --! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30 --! GNU General Public License for more details. <br>
31 --! You should have received a copy of the GNU General Public License
32 --! along with this code. If not, see <https://www.gnu.org/licenses/>.
33 --!
34 library ieee;
35 use ieee.std_logic_1164.all;
36 use ieee.numeric_std.all;
37
38 library unisim;
39 use unisim.vcomponents.all;
40
41 library work;
42 use work.NvmeStoragePkg.all;
43 use work.NvmeStorageIntPkg.all;
44
45 entity NvmeStorage is
46 generic(
47         Simulate        : boolean       := False;                       --! Generate simulation core
48         ClockPeriod     : time          := 8 ns;                        --! Clock period for timers (125 MHz)
49         BlockSize       : integer       := NvmeStorageBlockSize;        --! System block size
50         NumBlocksDrop   : integer       := 2;                           --! The number of blocks to drop at a time
51         UseConfigure    : boolean       := False                        --! The module configures the Nvme's on reset
52 );
53 port (
54         clk             : in std_logic;                         --! The interface clock line
55         reset           : in std_logic;                         --! The active high reset line
56
57         -- Control and status interface
58         axilIn          : in AxilToSlaveType;                   --! Axil bus input signals
59         axilOut         : out AxilToMasterType;                 --! Axil bus output signals
60
61         -- From host to NVMe request/reply streams
62         hostSend        : in AxisType;                          --! Host request stream
63         hostSend_ready  : out std_logic;                        --! Host request stream ready line
64         hostRecv        : out AxisType;                         --! Host reply stream
65         hostRecv_ready  : in std_logic;                         --! Host reply stream ready line
66
67         -- AXIS data stream input
68         dataDropBlocks  : in std_logic;                         --! If set to '1' drop complete input blocks and account for the loss
69         dataEnabledOut  : out std_logic;                        --! Indicates that data ingest is enabled
70         dataIn          : in AxisDataStreamType;                --! Raw data input stream
71         dataIn_ready    : out std_logic;                        --! Raw data input ready
72
73         -- NVMe interface
74         nvme_clk_p      : in std_logic;                         --! Nvme external clock +ve
75         nvme_clk_n      : in std_logic;                         --! Nvme external clock -ve
76         nvme_reset_n    : out std_logic;                        --! Nvme reset output to reset NVMe devices
77
78         nvme0_exp_txp   : out std_logic_vector(3 downto 0);     --! Nvme0 PCIe TX plus lanes
79         nvme0_exp_txn   : out std_logic_vector(3 downto 0);     --! Nvme0 PCIe TX minus lanes
80         nvme0_exp_rxp   : in std_logic_vector(3 downto 0);      --! Nvme0 PCIe RX plus lanes
81         nvme0_exp_rxn   : in std_logic_vector(3 downto 0);      --! Nvme0 PCIe RX minus lanes
82
83         nvme1_exp_txp   : out std_logic_vector(3 downto 0);     --! Nvme1 PCIe TX plus lanes
84         nvme1_exp_txn   : out std_logic_vector(3 downto 0);     --! Nvme1 PCIe TX minus lanes
85         nvme1_exp_rxp   : in std_logic_vector(3 downto 0);      --! Nvme1 PCIe RX plus lanes
86         nvme1_exp_rxn   : in std_logic_vector(3 downto 0);      --! Nvme1 PCIe RX minus lanes
87
88         -- Debug
89         leds            : out std_logic_vector(5 downto 0)
90 );
91 end;
92
93 architecture Behavioral of NvmeStorage is
94
95 component NvmeStorageUnit is
96 generic(
97         Simulate        : boolean       := Simulate;            --! Generate simulation core
98         ClockPeriod     : time          := ClockPeriod;         --! Clock period for timers (125 MHz)
99         BlockSize       : integer       := BlockSize;           --! System block size
100         PcieCore        : integer       := 0;                   --! The Pcie hardblock block to use
101         UseConfigure    : boolean       := False                --! The module configures the Nvme's on reset
102 );
103 port (
104         clk             : in std_logic;                         --! The interface clock line
105         reset           : in std_logic;                         --! The active high reset line
106
107         -- Control and status interface
108         regWrite        : in std_logic;                         --! Enable write to register
109         regAddress      : in unsigned(5 downto 0);              --! Register to read/write
110         regDataIn       : in std_logic_vector(31 downto 0);     --! Register write data
111         regDataOut      : out std_logic_vector(31 downto 0);    --! Register contents
112
113         -- From host to NVMe request/reply streams
114         hostSend        : inout AxisStreamType := AxisStreamInput;      --! Host request stream
115         hostRecv        : inout AxisStreamType := AxisStreamOutput;     --! Host reply stream
116
117         -- AXIS data stream input
118         dataEnabledOut  : out std_logic;                                --! Indicates that data ingest is enabled
119         dataIn          : inout AxisStreamType := AxisStreamInput;      --! Raw data to save stream
120
121         -- NVMe interface
122         nvme_clk        : in std_logic;                         --! Nvme external clock
123         nvme_clk_gt     : in std_logic;                         --! Nvme external GT clock
124         nvme_reset_n    : out std_logic;                        --! Nvme reset output to reset NVMe devices
125
126         nvme_exp_txp    : out std_logic_vector(3 downto 0);     --! nvme PCIe TX plus lanes
127         nvme_exp_txn    : out std_logic_vector(3 downto 0);     --! nvme PCIe TX minus lanes
128         nvme_exp_rxp    : in std_logic_vector(3 downto 0);      --! nvme PCIe RX plus lanes
129         nvme_exp_rxn    : in std_logic_vector(3 downto 0);      --! nvme PCIe RX minus lanes
130
131         -- Debug
132         leds            : out std_logic_vector(2 downto 0)
133 );
134 end component;
135
136 component AxisDataConvertFifo is
137 generic(
138         Simulate        : boolean       := False;               --! Enable simulation core
139         FifoSizeBytes   : integer       := BlockSize            --! The Fifo size in bytes
140 );
141 port (
142         clk             : in std_logic;
143         reset           : in std_logic;
144
145         streamRx        : in AxisDataStreamType;
146         streamRx_ready  : out std_logic;
147
148         streamTx        : inout AxisStreamType := AxisStreamOutput
149 );
150 end component;
151
152 component NvmeStreamMux is
153 port (
154         clk             : in std_logic;                         --! The interface clock line
155         reset           : in std_logic;                         --! The active high reset line
156         
157         hostIn          : inout AxisStreamType := AxisStreamInput;      --! Host multiplexed Input stream
158         hostOut         : inout AxisStreamType := AxisStreamOutput;     --! Host multiplexed Ouput stream
159
160         nvme0In         : inout AxisStreamType := AxisStreamInput;      --! Nvme0 Replies input stream
161         nvme0Out        : inout AxisStreamType := AxisStreamOutput;     --! Nvme0 Requests output stream
162
163         nvme1In         : inout AxisStreamType := AxisStreamInput;      --! Nvme1 Requests input stream
164         nvme1Out        : inout AxisStreamType := AxisStreamOutput      --! Nvme1 replies output stream
165 );
166 end component;
167
168 constant TCQ            : time := 1 ns;
169
170 signal nvme_clk         : std_logic := 'U';
171 signal nvme_clk_gt      : std_logic := 'U';
172
173 signal wvalid_delay     : std_logic := '0';
174 signal rvalid_delay     : unsigned(4 downto 0) := (others => '0');
175
176 signal hostSend0        : AxisStreamType;
177 signal hostRecv0        : AxisStreamType;
178
179 signal data0            : AxisStreamType := AxisStreamOutput;
180 signal nvme0Send        : AxisStreamType;
181 signal nvme0Recv        : AxisStreamType;
182
183 signal data1            : AxisStreamType := AxisStreamOutput;
184 signal nvme1Send        : AxisStreamType;
185 signal nvme1Recv        : AxisStreamType;
186
187 signal regWrite         : std_logic := '0';                                     --! Enable write to register
188 signal regAddress       : unsigned(9 downto 0) := (others => '0');              --! Register to read/write
189 signal regWrite0        : std_logic := '0';
190 signal regWrite1        : std_logic := '0';
191 signal readNvme1        : std_logic := '0';
192 signal regDataOut0      : std_logic_vector(31 downto 0);
193 signal regDataOut1      : std_logic_vector(31 downto 0);
194
195 signal enabled_n        : std_logic := '0';
196 signal dataSelect       : std_logic := '0';
197 signal dataIn_ready_l   : std_logic := 'U';
198 signal dataIn0          : AxisDataStreamType;
199 signal dataIn0_ready    : std_logic := 'U';
200 signal dataIn1          : AxisDataStreamType;
201 signal dataIn1_ready    : std_logic := 'U';
202
203 signal dataEnabledOut0  : std_logic := 'U';
204 signal dataEnabledOut1  : std_logic := 'U';
205
206 signal dropCount        : integer range 0 to NumBlocksDrop := 0;
207 signal dropBlocks       : std_logic := '0';
208 signal regBlocksLost    : unsigned(31 downto 0) := (others => '0');
209
210 begin
211         -- NVME PCIE Clock, 100MHz
212         nvme_clk_buf0 : IBUFDS_GTE3
213         port map (
214                 I       => nvme_clk_p,
215                 IB      => nvme_clk_n,
216                 O       => nvme_clk_gt,
217                 ODIV2   => nvme_clk,
218                 CEB     => '0'
219         );
220         
221         -- Register processing. Depending on the read or write address set, pass to appropriate NvmeStorageUnit module.
222         -- Bus ready returns            
223         axilOut.awready <= axilIn.awvalid;
224         axilOut.arready <= axilIn.arvalid;
225         axilOut.rvalid  <= rvalid_delay(4);
226         axilOut.wready  <= axilIn.wvalid and wvalid_delay;
227
228         -- Always return OK to read and write requests
229         axilOut.rresp   <= "00";
230         axilOut.bresp   <= "00";
231         axilOut.bvalid  <= '1';
232
233         regWrite        <= wvalid_delay;        -- Delayed to make sure bits are stable across CDC
234         
235         regWrite0       <= regWrite when(regAddress < 512) else '0';
236         regWrite1       <= regWrite when((regAddress < 256) or (regAddress >= 512)) else '0';
237         readNvme1       <= '1' when(regAddress >= 512) else '0';
238         axilOut.rdata   <= to_stl(regBlocksLost) when(regAddress = 16) else regDataOut1 when(readNvme1 = '1') else regDataOut0;
239
240         process(clk)
241         begin
242                 if(rising_edge(clk)) then
243                         if(reset = '1') then
244                                 regAddress      <= (others => '0');
245                                 rvalid_delay    <= (others => '0');
246                                 wvalid_delay    <= '0';
247                         else
248                                 if(axilIn.awvalid = '1') then
249                                         regAddress <= unsigned(axilIn.awaddr(9 downto 0));
250                                 elsif(axilIn.arvalid = '1') then
251                                         regAddress <= unsigned(axilIn.araddr(9 downto 0));
252                                         rvalid_delay(0) <= '1';
253                                 else
254                                         -- rvalid delay to handle clock domain crossing latency
255                                         rvalid_delay <= shift_left(rvalid_delay, 1);
256                                 end if;
257                                 
258                                 wvalid_delay <= axilIn.wvalid;
259                         end if;
260                 end if;
261         end process;
262         
263         -- Connect to local Axis stream style
264         enabled_n       <= not dataEnabledOut0;
265         dataEnabledOut  <= dataEnabledOut0;
266
267         -- Pass altenating blocks in the data input stream to the two NvmeStorageUnits
268         dataIn_ready_l  <= '0' when(enabled_n = '1') else '1' when(dropBlocks = '1') else dataIn0_ready when(dataSelect = '0') else dataIn1_ready;
269         dataIn_ready    <= dataIn_ready_l;
270
271         dataIn0.valid   <= dataIn.valid when((dropBlocks = '0') and (dataSelect = '0')) else '0';
272         dataIn0.last    <= dataIn.last;
273         dataIn0.data    <= dataIn.data;
274
275         dataIn1.valid   <= dataIn.valid when((dropBlocks = '0') and (dataSelect = '1')) else '0';
276         dataIn1.last    <= dataIn.last;
277         dataIn1.data    <= dataIn.data;
278
279         dataConvert0 : AxisDataConvertFifo
280         port map (
281                 clk             => clk,
282                 reset           => enabled_n,
283
284                 streamRx        => dataIn0,
285                 streamRx_ready  => dataIn0_ready,
286
287                 streamTx        => data0
288         );
289
290         dataConvert1 : AxisDataConvertFifo
291         port map (
292                 clk             => clk,
293                 reset           => enabled_n,
294
295                 streamRx        => dataIn1,
296                 streamRx_ready  => dataIn1_ready,
297
298                 streamTx        => data1
299         );
300         
301         process(clk)
302         begin
303                 if(rising_edge(clk)) then
304                         if(reset = '1') then
305                                 dataSelect      <= '0';
306                                 dropCount       <= 0;
307                                 dropBlocks      <= '0';
308                                 regBlocksLost   <= (others => '0');
309                         else
310                                 if((dataIn.valid = '1') and (dataIn.last = '1') and (dataIn_ready_l = '1')) then
311                                         dataSelect <= not dataSelect;
312                                         
313                                         -- Handle dropping of complete blocks
314                                         if(dropCount = 0) then
315                                                 -- Drop data starting with Nvme0
316                                                 if(dataSelect = '1') then
317                                                         if(dataDropBlocks = '1') then
318                                                                 dropCount       <= NumBlocksDrop - 1;
319                                                                 regBlocksLost   <= regBlocksLost + 1;
320                                                                 dropBlocks      <= '1';
321                                                         else
322                                                                 dropBlocks      <= '0';
323                                                         end if;
324                                                 end if;
325                                         else
326                                                 regBlocksLost   <= regBlocksLost + 1;
327                                                 dropCount       <= dropCount - 1;
328                                         end if;
329                                 end if;
330                         end if;
331                 end if;
332         end process;
333
334         -- Connect to local Axis stream style
335         axisConnect(hostSend0, hostSend, hostSend_ready);
336         axisConnect(hostRecv, hostRecv_ready, hostRecv0);
337
338         nvmeStreamMux0 : NvmeStreamMux
339         port map (
340                 clk             => clk,
341                 reset           => reset,
342
343                 hostIn          => hostSend0,
344                 hostOut         => hostRecv0,
345
346                 nvme0In         => nvme0Send,
347                 nvme0Out        => nvme0Recv,
348
349                 nvme1In         => nvme1Send,
350                 nvme1Out        => nvme1Recv
351         );
352
353         nvmeStorageUnit0 : NvmeStorageUnit
354         generic map (
355                 PcieCore        => 0                    --! The Pcie hardblock block to use
356         )
357         port map (
358                 clk             => clk,
359                 reset           => reset,
360
361                 regWrite        => regWrite0,   
362                 regAddress      => regAddress(7 downto 2),
363                 regDataIn       => axilIn.wdata,
364                 regDataOut      => regDataOut0,
365
366                 hostSend        => nvme0Recv,
367                 hostRecv        => nvme0Send,
368                 
369                 dataEnabledOut  => dataEnabledOut0,
370                 dataIn          => data0,
371
372                 -- NVMe interface
373                 nvme_clk        => nvme_clk,
374                 nvme_clk_gt     => nvme_clk_gt,
375                 nvme_reset_n    => nvme_reset_n,
376                 nvme_exp_txp    => nvme0_exp_txp,
377                 nvme_exp_txn    => nvme0_exp_txn,
378                 nvme_exp_rxp    => nvme0_exp_rxp,
379                 nvme_exp_rxn    => nvme0_exp_rxn,
380
381                 leds            => leds(2 downto 0)
382         );
383
384         nvmeStorageUnit1 : NvmeStorageUnit
385         generic map (
386                 PcieCore        => 1                    --! The Pcie hardblock block to use
387         )
388         port map (
389                 clk             => clk,
390                 reset           => reset,
391
392                 regWrite        => regWrite1,
393                 regAddress      => regAddress(7 downto 2),
394                 regDataIn       => axilIn.wdata,
395                 regDataOut      => regDataOut1,
396
397                 hostSend        => nvme1Recv,
398                 hostRecv        => nvme1Send,
399                 
400                 dataEnabledOut  => dataEnabledOut1,
401                 dataIn          => data1,
402
403                 -- NVMe interface
404                 nvme_clk        => nvme_clk,
405                 nvme_clk_gt     => nvme_clk_gt,
406                 nvme_exp_txp    => nvme1_exp_txp,
407                 nvme_exp_txn    => nvme1_exp_txn,
408                 nvme_exp_rxp    => nvme1_exp_rxp,
409                 nvme_exp_rxn    => nvme1_exp_rxn,
410
411                 leds            => leds(5 downto 3)
412         );
413 end;