三、实战篇-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总线接口的访问

cpp 复制代码
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寄存器读写

cpp 复制代码
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

cpp 复制代码
	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;	

主要状态流程如下

cpp 复制代码
//	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

读寄存器

cpp 复制代码
//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	

写寄存器

cpp 复制代码
//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配置一致。

相关推荐
Terasic友晶科技1 天前
【教程】Agilex 5 SOC FPGA的HPS端如何响应FPGA端中断(一)——GIC控制器介绍
fpga开发·hps·agilex 5·gic控制器
芯路行者2 天前
4.4 HDLBits —— 更多时序逻辑电路
fpga开发·verilog·fpga·hdlbits
HUSTCH20212 天前
有符号数无符号数加法器逻辑
fpga开发
LCMICRO-133108477462 天前
国产长芯微LP4807完全P2P替代ADA4807,1mA、300MHz 增益带宽、电压反馈运算放大器
arm开发·嵌入式硬件·fpga开发·硬件工程·运算放大器·ada4807
LCMICRO-133108477462 天前
国产长芯微LP8628完全P2P替代AD8628,零漂移、单电源、输入输出轨到轨高精度运放
arm开发·嵌入式硬件·fpga开发·硬件工程·精密运放·ad8628
芯路行者3 天前
7 HDLBits —— 验证:编写测试激励文件
fpga开发·verilog·fpga·hdlbits
芯路行者3 天前
4.1 HDLBits —— 时序逻辑电路之锁存器与触发器
fpga开发·verilog·fpga·hdlbits
芯路行者3 天前
5 HDLBits —— 构建更大的电路
fpga开发·verilog·fpga·hdlbits
156082072193 天前
直流耦合采集卡前端电路调试
嵌入式硬件·fpga开发
芯路行者3 天前
4.3 HDLBits —— 时序逻辑电路之移位寄存器
fpga开发·verilog·fpga·hdlbits