三、实战篇-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配置一致。

相关推荐
szxinmai主板定制专家5 小时前
驱控一体新范式:RK3576+FPGA+CODESYS工业实时AI控制架构全解
人工智能·嵌入式硬件·计算机视觉·fpga开发·架构·工业ai·驱控一体
国科安芯6 小时前
ASC0101S自动双向电平转换原理深剖
单片机·嵌入式硬件·fpga开发·云计算·信息与通信
落chen7 小时前
7 Series FPGAs Clocking Resources
fpga开发
落chen1 天前
基于FPGA的IIC协议通信-强化篇
fpga开发
梦开始的地方 �1 天前
实例化和句柄
fpga开发
lf2824814311 天前
11 FM调制与解调的仿真
fpga开发
蓝悦无人机1 天前
Altium Designer 2024 PCB高级功能:不同Pitch的BGA设计要求
fpga开发·pcb·硬件设计·ad2024
ALINX技术博客2 天前
【黑金云课堂】FPGA技术教程Linux开发:系统定制
linux·运维·fpga开发
塔能物联运维2 天前
**塔能两相液冷:量化交易场景下的硬核验证,±1.5℃控温如何转化为真金白银**
人工智能·fpga开发·两相液冷