三、实战篇-NVME SSD控制之FPGA实现(二)

三、实战篇-NVME SSD控制之FPGA实现(二)

本章节讲述如何使用FPGA(纯verilog语言)实现NVME SSD硬盘的读写控制。

参考:
三、实战篇-NVME SSD控制之FPGA实现(一)-CSDN博客

二、实战篇-NVME SSD控制之ZYNQ实现(三)-CSDN博客

前面完成了FPGA和NVME SSD的PCIE CFG空间的配置。下面进行NVME SSD的寄存器配置,NVME SSD内部寄存器位于BAR空间中。

上一节通过S_AXI_CTL总线完成了PCIE配置空间的配置,本节通过控制S_AXI总线完成NVME SSD内部寄存器配置。

S_AXI_CTL总线采用AXI_FULL接口,AXI_FUL接口信号比较多,这里使用一个本地转AXI_FUL接口的模块(LOCAL_2_AXI)方便读写。

通过这个模块,这样我们通过本地接口就可以进行S_AXI总线接口的访问

module LOCAL_2_AXI # ( parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000, parameter integer C_M_AXI_ID_WIDTH = 1, parameter integer C_M_AXI_ADDR_WIDTH = 32, parameter integer C_M_AXI_DATA_WIDTH = 128, parameter integer C_M_AXI_AWUSER_WIDTH = 0, parameter integer C_M_AXI_ARUSER_WIDTH = 0, parameter integer C_M_AXI_WUSER_WIDTH = 0, parameter integer C_M_AXI_RUSER_WIDTH = 0, parameter integer C_M_AXI_BUSER_WIDTH = 0 ) ( // 本地读写接口 input wire local_wr_en, input wire [C_M_AXI_ADDR_WIDTH-1:0]local_wr_addr, input wire [C_M_AXI_DATA_WIDTH-1:0]local_wr_data, input [C_M_AXI_DATA_WIDTH/8-1:0] i_WSTRB, output reg local_wr_done, input wire local_rd_en, input wire [C_M_AXI_ADDR_WIDTH-1:0]local_rd_addr, output reg [C_M_AXI_DATA_WIDTH-1:0]local_rd_data, output reg local_rd_valid, // AXI4 时钟复位 input wire M_AXI_ACLK, input wire M_AXI_ARESETN, // 写地址通道 output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID, output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR, output wire [7 : 0] M_AXI_AWLEN, output wire [2 : 0] M_AXI_AWSIZE, output wire [1 : 0] M_AXI_AWBURST, output wire M_AXI_AWLOCK, output wire [3 : 0] M_AXI_AWCACHE, output wire [2 : 0] M_AXI_AWPROT, output wire [3 : 0] M_AXI_AWQOS, output wire [C_M_AXI_AWUSER_WIDTH-1 : 0] M_AXI_AWUSER, output wire M_AXI_AWVALID, input wire M_AXI_AWREADY, // 写数据通道 output wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA, output wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB, output wire M_AXI_WLAST, output wire [C_M_AXI_WUSER_WIDTH-1 : 0] M_AXI_WUSER, output wire M_AXI_WVALID, input wire M_AXI_WREADY, // 写响应通道 input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID, input wire [1 : 0] M_AXI_BRESP, input wire [C_M_AXI_BUSER_WIDTH-1 : 0] M_AXI_BUSER, input wire M_AXI_BVALID, output wire M_AXI_BREADY, // 读地址通道 output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_ARID, output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR, output wire [7 : 0] M_AXI_ARLEN, output wire [2 : 0] M_AXI_ARSIZE, output wire [1 : 0] M_AXI_ARBURST, output wire M_AXI_ARLOCK, output wire [3 : 0] M_AXI_ARCACHE, output wire [2 : 0] M_AXI_ARPROT, output wire [3 : 0] M_AXI_ARQOS, output wire [C_M_AXI_ARUSER_WIDTH-1 : 0] M_AXI_ARUSER, output wire M_AXI_ARVALID, input wire M_AXI_ARREADY, // 读数据通道 input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_RID, input wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA, input wire [1 : 0] M_AXI_RRESP, input wire M_AXI_RLAST, input wire [C_M_AXI_RUSER_WIDTH-1 : 0] M_AXI_RUSER, input wire M_AXI_RVALID, output wire M_AXI_RREADY );

将LOCAL_2_AXI模块的M_AXI连接到S_AXI总线,本地信号引出到外部进行控制。

新建一个nvme_reg_cfg模块用于控制LOCAL_2_AXI模块的本地接口,进行NVME SSD寄存器读写

module nvme_reg_cfg#( parameter integer C_M_AXI_ADDR_WIDTH = 32, parameter integer C_M_AXI_DATA_WIDTH = 128, parameter NVME_ADMIN_QSIZE =16'd16, parameter ADMIN_SQ_BASE =32'h1010_0000, parameter ADMIN_CQ_BASE =32'h1010_1000 )( input wire i_aclk, input wire i_aresetn, input i_cfg_start, output o_cfg_done, input i_wr_door_req, input [2:0] i_wr_door_type, input [31:0] i_wr_door_data, output o_wr_door_done, // 本地写接口 output o_wr_en, // 写使能,高有效 output [C_M_AXI_ADDR_WIDTH-1:0] o_wr_addr, // 本地写地址(偏移地址) output [C_M_AXI_DATA_WIDTH-1:0] o_wr_data, // 本地待写入数据 output [C_M_AXI_DATA_WIDTH/8-1:0] o_WSTRB, input i_wr_done, // 读接口 output o_rd_en, // 读使能,高有效 output [C_M_AXI_ADDR_WIDTH-1:0] o_rd_addr, // 本地读地址(偏移地址) input [C_M_AXI_DATA_WIDTH-1:0] i_rd_data, // 读回数据 input i_rd_valid // 读数据有效标志 );

寄存器偏移地址都是固定的,可以将其定义为parameter

parameter RegOffset_CAP_L =32'h0000_0000; parameter RegOffset_CAP_H =32'h0000_0004; parameter RegOffset_VS =32'h0000_0008; parameter RegOffset_CC =32'h0000_0014; parameter RegOffset_CSTS =32'h0000_001C; parameter RegOffset_AQA =32'h0000_0024; parameter RegOffset_ASQ_L =32'h0000_0028; parameter RegOffset_ASQ_H =32'h0000_002C; parameter RegOffset_ACQ_L =32'h0000_0030; parameter RegOffset_ACQ_H =32'h0000_0034; parameter RegOffset_Admin_SQTDBL =32'h0000_1000; parameter RegOffset_Admin_CQHDBL =32'h0000_1004; parameter RegOffset_IO_SQ0TDBL =32'h0000_1008; parameter RegOffset_IO_CQ0HDBL =32'h0000_100C; parameter RegOffset_IO_SQ1TDBL =32'h0000_1010; parameter RegOffset_IO_CQ1HDBL =32'h0000_1014;

主要状态流程如下

// cstate always@(posedge i_aclk or negedge i_aresetn)begin if(!i_aresetn)begin cstate<=0; end else begin cstate<=nstate; end end always@(*)begin case(cstate) 0:begin if(start_pdg) nstate=1; else nstate=0;end 1:begin if(rd_cnt>=4) nstate=2;//RD RC ID else nstate=1;end 2:begin if(i_rd_valid) nstate=3;//rd ready else nstate=2;end 3:begin if(rd_vaild && rdata[0]==0) nstate=4;// else nstate=2;end 4:begin if(i_wr_done) nstate=5;//wr cc else nstate=4;end 5:begin if(i_wr_done) nstate=6;//wr AQA else nstate=5;end 6:begin if(i_wr_done) nstate=7;//wr ASQ_L else nstate=6;end 7:begin if(i_wr_done) nstate=8;//wr ASQ_H else nstate=7;end 8:begin if(i_wr_done) nstate=9;//wr ACQ_L else nstate=8;end 9:begin if(i_wr_done) nstate=10;//wr ACQ_H else nstate=9;end 10:begin if(i_wr_done) nstate=11;//wr cc else nstate=10;end 11:begin if(i_rd_valid) nstate=12;//rd ready else nstate=11;end 12:begin if(rd_vaild && rdata[0]==1) nstate=13;// else nstate=11;end //------ 13:begin if(wr_door_req) nstate=14; else nstate=13;end 14:begin if(i_wr_done) nstate=15; else nstate=14;end 15:begin nstate=13;end endcase end

读寄存器

//rd always@(posedge i_aclk or negedge i_aresetn)begin if(!i_aresetn)begin rd_en<=0; rd_addr<=0; end else begin if(cstate==1)begin if(rd_cnt==0)begin rd_addr<=RegOffset_CAP_L; if(i_rd_valid) rd_en<=0; else rd_en<=1; end else if(rd_cnt==1)begin rd_addr<=RegOffset_CAP_H; if(i_rd_valid) rd_en<=0; else rd_en<=1; end else if(rd_cnt==2)begin rd_addr<=RegOffset_VS; if(i_rd_valid) rd_en<=0; else rd_en<=1; end else if(rd_cnt==3)begin rd_addr<=RegOffset_CC; if(i_rd_valid) rd_en<=0; else rd_en<=1; end else rd_en<=0; end else if(cstate==2||cstate==11)begin rd_addr<=RegOffset_CSTS; if(i_rd_valid) rd_en<=0; else rd_en<=1; end else begin rd_en<=0; end end end

写寄存器

//wr always@(posedge i_aclk or negedge i_aresetn)begin if(!i_aresetn)begin wr_en<=0; wr_addr<=0; wr_data<=0; end else begin if(cstate==4)begin wr_addr<=RegOffset_CC; wr_data<=32'h0046_0000; if(i_wr_done) wr_en<=0; else wr_en<=1; end else if(cstate==5)begin wr_addr<=RegOffset_AQA; wr_data<={NVME_ADMIN_QSIZE,NVME_ADMIN_QSIZE}; if(i_wr_done) wr_en<=0; else wr_en<=1; end else if(cstate==6)begin wr_addr<=RegOffset_ASQ_L; wr_data<=ADMIN_SQ_BASE; if(i_wr_done) wr_en<=0; else wr_en<=1; end else if(cstate==7)begin wr_addr<=RegOffset_ASQ_H; wr_data<=32'h0000_0000; if(i_wr_done) wr_en<=0; else wr_en<=1; end else if(cstate==8)begin wr_addr<=RegOffset_ACQ_L; wr_data<=ADMIN_CQ_BASE; if(i_wr_done) wr_en<=0; else wr_en<=1; end else if(cstate==9)begin wr_addr<=RegOffset_ACQ_H; wr_data<=32'h0000_0000; if(i_wr_done) wr_en<=0; else wr_en<=1; end else if(cstate==10)begin wr_addr<=RegOffset_CC; wr_data<=32'h0046_0001; if(i_wr_done) wr_en<=0; else wr_en<=1; end else if(cstate==14)begin wr_data<=i_wr_door_data; case(i_wr_door_type) 0:wr_addr<=RegOffset_Admin_SQTDBL; 1:wr_addr<=RegOffset_Admin_CQHDBL; 2:wr_addr<=RegOffset_IO_SQ0TDBL; 3:wr_addr<=RegOffset_IO_CQ0HDBL; 4:wr_addr<=RegOffset_IO_SQ1TDBL; 5:wr_addr<=RegOffset_IO_CQ1HDBL; default:wr_addr<=RegOffset_Admin_SQTDBL; endcase if(i_wr_done)begin wr_en<=0; end else begin wr_en<=1; end end else begin wr_en<=0; end end end

主要寄存器配置流程和ZYNQ控制NVME SSD配置一致。