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

相关推荐
璞致电子1 天前
基于 AD9361 (PZSDR‑P201Pro) 软件无线电实战综述:从板卡选型、硬件平台升级到 GNU Radio QPSK 完整收发链路
经验分享·嵌入式硬件·fpga开发·软件无线电·sdr·璞致电子
炎龙铠甲会合体1 天前
从 SPEC 到上板:在 Zynq-7020 上用 RTL 手写一个 YOLO 卷积加速器(完整教程)
yolo·fpga开发·axi·芯片验证
FPGA小徐1 天前
Vivado 按键消抖与复位模块代码修复详解:KEY_TOP、key、rst_gen 完整实战
fpga开发
炎龙铠甲会合体1 天前
AXI2ICACHE— AXI4-Full 四路组相联指令 Cache 设计与 UVM 验证
fpga开发·面试·芯片验证
bingcheby1 天前
FMQL15/20/45 (复旦微青龙系列 PSOC)新手使用说明
fpga开发
漫漫长远1 天前
低功耗UPF介绍(一)
笔记·嵌入式硬件·学习·fpga开发
自小吃多2 天前
【Verilog/FPGA】存储器(数组)
嵌入式硬件·fpga开发
szxinmai主板定制专家2 天前
RK3588+FPGA异构架构|高速数据采集+边缘AI落地应用全解析
人工智能·嵌入式硬件·fpga开发·架构·zynq
明德扬2 天前
FPGA MIPI 多路视频聚合方案
fpga开发·mipi视频拼接·mipi多路拼接·mipi协议·mipi视频拼接方案·fpga mipi方案·mipi csi
XMAIPC_Robot2 天前
为什么大型储能需要边缘 AI 协调控制器?RK3588+FPGA 高速采集方案
人工智能·嵌入式硬件·fpga开发·arm+fpga·rk3588+fpga·协调控制器