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