Now supports multiple NVMe clock inputs.
[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          := 4 ns;                        --! Clock period for timers (250 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_reset_n    : out std_logic;                        --! Nvme reset output to reset NVMe devices
75
76         nvme0_clk       : in std_logic;                         --! Nvme0 external clock
77         nvme0_clk_gt    : in std_logic;                         --! Nvme0 external GT clock
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_clk       : in std_logic;                         --! Nvme1 external clock
84         nvme1_clk_gt    : in std_logic;                         --! Nvme1 external GT clock
85         nvme1_exp_txp   : out std_logic_vector(3 downto 0);     --! Nvme1 PCIe TX plus lanes
86         nvme1_exp_txn   : out std_logic_vector(3 downto 0);     --! Nvme1 PCIe TX minus lanes
87         nvme1_exp_rxp   : in std_logic_vector(3 downto 0);      --! Nvme1 PCIe RX plus lanes
88         nvme1_exp_rxn   : in std_logic_vector(3 downto 0);      --! Nvme1 PCIe RX minus lanes
89
90         -- Debug
91         leds            : out std_logic_vector(5 downto 0)
92 );
93 end;
94
95 architecture Behavioral of NvmeStorage is
96
97 component NvmeStorageUnit is
98 generic(
99         Simulate        : boolean       := Simulate;            --! Generate simulation core
100         ClockPeriod     : time          := ClockPeriod;         --! Clock period for timers (250 MHz)
101         BlockSize       : integer       := BlockSize;           --! System block size
102         PcieCore        : integer       := 0;                   --! The Pcie hardblock block to use
103         UseConfigure    : boolean       := False                --! The module configures the Nvme's on reset
104 );
105 port (
106         clk             : in std_logic;                         --! The interface clock line
107         reset           : in std_logic;                         --! The active high reset line
108
109         -- Control and status interface
110         regWrite        : in std_logic;                         --! Enable write to register
111         regAddress      : in unsigned(5 downto 0);              --! Register to read/write
112         regDataIn       : in std_logic_vector(31 downto 0);     --! Register write data
113         regDataOut      : out std_logic_vector(31 downto 0);    --! Register contents
114
115         -- From host to NVMe request/reply streams
116         hostSend        : inout AxisStreamType := AxisStreamInput;      --! Host request stream
117         hostRecv        : inout AxisStreamType := AxisStreamOutput;     --! Host reply stream
118
119         -- AXIS data stream input
120         dataEnabledOut  : out std_logic;                                --! Indicates that data ingest is enabled
121         dataIn          : inout AxisStreamType := AxisStreamInput;      --! Raw data to save stream
122
123         -- NVMe interface
124         nvme_clk        : in std_logic;                         --! Nvme external clock
125         nvme_clk_gt     : in std_logic;                         --! Nvme external GT clock
126         nvme_reset_n    : out std_logic;                        --! Nvme reset output to reset NVMe devices
127
128         nvme_exp_txp    : out std_logic_vector(3 downto 0);     --! nvme PCIe TX plus lanes
129         nvme_exp_txn    : out std_logic_vector(3 downto 0);     --! nvme PCIe TX minus lanes
130         nvme_exp_rxp    : in std_logic_vector(3 downto 0);      --! nvme PCIe RX plus lanes
131         nvme_exp_rxn    : in std_logic_vector(3 downto 0);      --! nvme PCIe RX minus lanes
132
133         -- Debug
134         leds            : out std_logic_vector(2 downto 0)
135 );
136 end component;
137
138 component AxisDataConvertFifo is
139 generic(
140         Simulate        : boolean       := False;               --! Enable simulation core
141         FifoSizeBytes   : integer       := BlockSize            --! The Fifo size in bytes
142 );
143 port (
144         clk             : in std_logic;
145         reset           : in std_logic;
146
147         streamRx        : in AxisDataStreamType;
148         streamRx_ready  : out std_logic;
149
150         streamTx        : inout AxisStreamType := AxisStreamOutput
151 );
152 end component;
153
154 component NvmeStreamMux is
155 port (
156         clk             : in std_logic;                         --! The interface clock line
157         reset           : in std_logic;                         --! The active high reset line
158         
159         hostIn          : inout AxisStreamType := AxisStreamInput;      --! Host multiplexed Input stream
160         hostOut         : inout AxisStreamType := AxisStreamOutput;     --! Host multiplexed Ouput stream
161
162         nvme0In         : inout AxisStreamType := AxisStreamInput;      --! Nvme0 Replies input stream
163         nvme0Out        : inout AxisStreamType := AxisStreamOutput;     --! Nvme0 Requests output stream
164
165         nvme1In         : inout AxisStreamType := AxisStreamInput;      --! Nvme1 Requests input stream
166         nvme1Out        : inout AxisStreamType := AxisStreamOutput      --! Nvme1 replies output stream
167 );
168 end component;
169
170 constant TCQ            : time := 1 ns;
171
172 signal wvalid_delay     : std_logic := '0';
173 signal rvalid_delay     : unsigned(4 downto 0) := (others => '0');
174
175 signal hostSend0        : AxisStreamType;
176 signal hostRecv0        : AxisStreamType;
177
178 signal data0            : AxisStreamType := AxisStreamOutput;
179 signal nvme0Send        : AxisStreamType;
180 signal nvme0Recv        : AxisStreamType;
181
182 signal data1            : AxisStreamType := AxisStreamOutput;
183 signal nvme1Send        : AxisStreamType;
184 signal nvme1Recv        : AxisStreamType;
185
186 signal regWrite         : std_logic := '0';                                     --! Enable write to register
187 signal regAddress       : unsigned(9 downto 0) := (others => '0');              --! Register to read/write
188 signal regWrite0        : std_logic := '0';
189 signal regWrite1        : std_logic := '0';
190 signal readNvme1        : std_logic := '0';
191 signal regDataOut0      : std_logic_vector(31 downto 0);
192 signal regDataOut1      : std_logic_vector(31 downto 0);
193
194 signal enabled_n        : std_logic := '0';
195 signal dataSelect       : std_logic := '0';
196 signal dataIn_ready_l   : std_logic := 'U';
197 signal dataIn0          : AxisDataStreamType;
198 signal dataIn0_ready    : std_logic := 'U';
199 signal dataIn1          : AxisDataStreamType;
200 signal dataIn1_ready    : std_logic := 'U';
201
202 signal dataEnabledOut0  : std_logic := 'U';
203 signal dataEnabledOut1  : std_logic := 'U';
204
205 signal dropCount        : integer range 0 to NumBlocksDrop := 0;
206 signal dropBlocks       : std_logic := '0';
207 signal regBlocksLost    : unsigned(31 downto 0) := (others => '0');
208
209 begin
210         -- Register processing. Depending on the read or write address set, pass to appropriate NvmeStorageUnit module.
211         -- Bus ready returns            
212         axilOut.awready <= axilIn.awvalid;
213         axilOut.arready <= axilIn.arvalid;
214         axilOut.rvalid  <= rvalid_delay(4);
215         axilOut.wready  <= axilIn.wvalid and wvalid_delay;
216
217         -- Always return OK to read and write requests
218         axilOut.rresp   <= "00";
219         axilOut.bresp   <= "00";
220         axilOut.bvalid  <= '1';
221
222         regWrite        <= wvalid_delay;        -- Delayed to make sure bits are stable across CDC
223         
224         regWrite0       <= regWrite when(regAddress < 512) else '0';
225         regWrite1       <= regWrite when((regAddress < 256) or (regAddress >= 512)) else '0';
226         readNvme1       <= '1' when(regAddress >= 512) else '0';
227         axilOut.rdata   <= to_stl(regBlocksLost) when(regAddress = 16) else regDataOut1 when(readNvme1 = '1') else regDataOut0;
228
229         process(clk)
230         begin
231                 if(rising_edge(clk)) then
232                         if(reset = '1') then
233                                 regAddress      <= (others => '0');
234                                 rvalid_delay    <= (others => '0');
235                                 wvalid_delay    <= '0';
236                         else
237                                 if(axilIn.awvalid = '1') then
238                                         regAddress <= unsigned(axilIn.awaddr(9 downto 0));
239                                 elsif(axilIn.arvalid = '1') then
240                                         regAddress <= unsigned(axilIn.araddr(9 downto 0));
241                                         rvalid_delay(0) <= '1';
242                                 else
243                                         -- rvalid delay to handle clock domain crossing latency
244                                         rvalid_delay <= shift_left(rvalid_delay, 1);
245                                 end if;
246                                 
247                                 wvalid_delay <= axilIn.wvalid;
248                         end if;
249                 end if;
250         end process;
251         
252         -- Connect to local Axis stream style
253         enabled_n       <= not dataEnabledOut0;
254         dataEnabledOut  <= dataEnabledOut0;
255
256         -- Pass altenating blocks in the data input stream to the two NvmeStorageUnits
257         dataIn_ready_l  <= '0' when(enabled_n = '1') else '1' when(dropBlocks = '1') else dataIn0_ready when(dataSelect = '0') else dataIn1_ready;
258         dataIn_ready    <= dataIn_ready_l;
259
260         dataIn0.valid   <= dataIn.valid when((dropBlocks = '0') and (dataSelect = '0')) else '0';
261         dataIn0.last    <= dataIn.last;
262         dataIn0.data    <= dataIn.data;
263
264         dataIn1.valid   <= dataIn.valid when((dropBlocks = '0') and (dataSelect = '1')) else '0';
265         dataIn1.last    <= dataIn.last;
266         dataIn1.data    <= dataIn.data;
267
268         dataConvert0 : AxisDataConvertFifo
269         port map (
270                 clk             => clk,
271                 reset           => enabled_n,
272
273                 streamRx        => dataIn0,
274                 streamRx_ready  => dataIn0_ready,
275
276                 streamTx        => data0
277         );
278
279         dataConvert1 : AxisDataConvertFifo
280         port map (
281                 clk             => clk,
282                 reset           => enabled_n,
283
284                 streamRx        => dataIn1,
285                 streamRx_ready  => dataIn1_ready,
286
287                 streamTx        => data1
288         );
289         
290         process(clk)
291         begin
292                 if(rising_edge(clk)) then
293                         if(reset = '1') then
294                                 dataSelect      <= '0';
295                                 dropCount       <= 0;
296                                 dropBlocks      <= '0';
297                                 regBlocksLost   <= (others => '0');
298                         else
299                                 if((dataIn.valid = '1') and (dataIn.last = '1') and (dataIn_ready_l = '1')) then
300                                         dataSelect <= not dataSelect;
301                                         
302                                         -- Handle dropping of complete blocks
303                                         if(dropCount = 0) then
304                                                 -- Drop data starting with Nvme0
305                                                 if(dataSelect = '1') then
306                                                         if(dataDropBlocks = '1') then
307                                                                 dropCount       <= NumBlocksDrop - 1;
308                                                                 regBlocksLost   <= regBlocksLost + 1;
309                                                                 dropBlocks      <= '1';
310                                                         else
311                                                                 dropBlocks      <= '0';
312                                                         end if;
313                                                 end if;
314                                         else
315                                                 regBlocksLost   <= regBlocksLost + 1;
316                                                 dropCount       <= dropCount - 1;
317                                         end if;
318                                 end if;
319                         end if;
320                 end if;
321         end process;
322
323         -- Connect to local Axis stream style
324         axisConnect(hostSend0, hostSend, hostSend_ready);
325         axisConnect(hostRecv, hostRecv_ready, hostRecv0);
326
327         nvmeStreamMux0 : NvmeStreamMux
328         port map (
329                 clk             => clk,
330                 reset           => reset,
331
332                 hostIn          => hostSend0,
333                 hostOut         => hostRecv0,
334
335                 nvme0In         => nvme0Send,
336                 nvme0Out        => nvme0Recv,
337
338                 nvme1In         => nvme1Send,
339                 nvme1Out        => nvme1Recv
340         );
341
342         nvmeStorageUnit0 : NvmeStorageUnit
343         generic map (
344                 PcieCore        => 0                    --! The Pcie hardblock block to use
345         )
346         port map (
347                 clk             => clk,
348                 reset           => reset,
349
350                 regWrite        => regWrite0,   
351                 regAddress      => regAddress(7 downto 2),
352                 regDataIn       => axilIn.wdata,
353                 regDataOut      => regDataOut0,
354
355                 hostSend        => nvme0Recv,
356                 hostRecv        => nvme0Send,
357                 
358                 dataEnabledOut  => dataEnabledOut0,
359                 dataIn          => data0,
360
361                 -- NVMe interface
362                 nvme_clk        => nvme0_clk,
363                 nvme_clk_gt     => nvme0_clk_gt,
364                 nvme_reset_n    => nvme_reset_n,
365                 nvme_exp_txp    => nvme0_exp_txp,
366                 nvme_exp_txn    => nvme0_exp_txn,
367                 nvme_exp_rxp    => nvme0_exp_rxp,
368                 nvme_exp_rxn    => nvme0_exp_rxn,
369
370                 leds            => leds(2 downto 0)
371         );
372
373         nvmeStorageUnit1 : NvmeStorageUnit
374         generic map (
375                 PcieCore        => 1                    --! The Pcie hardblock block to use
376         )
377         port map (
378                 clk             => clk,
379                 reset           => reset,
380
381                 regWrite        => regWrite1,
382                 regAddress      => regAddress(7 downto 2),
383                 regDataIn       => axilIn.wdata,
384                 regDataOut      => regDataOut1,
385
386                 hostSend        => nvme1Recv,
387                 hostRecv        => nvme1Send,
388                 
389                 dataEnabledOut  => dataEnabledOut1,
390                 dataIn          => data1,
391
392                 -- NVMe interface
393                 nvme_clk        => nvme1_clk,
394                 nvme_clk_gt     => nvme1_clk_gt,
395                 nvme_exp_txp    => nvme1_exp_txp,
396                 nvme_exp_txn    => nvme1_exp_txn,
397                 nvme_exp_rxp    => nvme1_exp_rxp,
398                 nvme_exp_rxn    => nvme1_exp_rxn,
399
400                 leds            => leds(5 downto 3)
401         );
402 end;