本章节讲述如何使用ZYNQ(纯C语言)实现NVME SSD硬盘的读写控制。
参考:
二、实战篇-NVME SSD控制之ZYNQ实现(一)-CSDN博客
二、实战篇-NVME SSD控制之ZYNQ实现(二)-CSDN博客

前面完成了PCIE的建链和枚举,以及FPGA和NVME SSD的PCIE CFG空间的配置。
下面进行NVME SSD的寄存器配置,NVME SSD内部寄存器位于BAR空间中,完成地址映射后就可以进行访问了。
寄存器表如下:

先定义寄存器偏移地址
cpp
#define RegOffset_CAP_L 0x00
#define RegOffset_CAP_H 0x04
#define RegOffset_VS 0x08
#define RegOffset_INTMS 0x0C
#define RegOffset_INTMC 0x10
#define RegOffset_CC 0x14
#define RegOffset_CSTS 0x1C
#define RegOffset_AQA 0x24
#define RegOffset_ASQ_L 0x28
#define RegOffset_ASQ_H 0x2C
#define RegOffset_ACQ_L 0x30
#define RegOffset_ACQ_H 0x34
#define RegOffset_Admin_SQTDBL 0x1000
#define RegOffset_Admin_CQHDBL 0x1004
#define RegOffset_IO_SQ0TDBL 0x1008
#define RegOffset_IO_CQ0HDBL 0x100c
#define RegOffset_IO_SQ1TDBL 0x1010
#define RegOffset_IO_CQ1HDBL 0x1014
1 、读取寄存器
先进行寄存器读取,验证链路以及寄存器映射是否正确
获取CAP,VS,CC寄存器数据,对应地址分别为0x80000000(CAP低32位),0x80000004(CAP高32位)0x80000008,0x80000014。
cpp
u32 acpl,acph,VS,CC,CSTS;
acpl = XAxiPcie_ReadReg((XPAR_AXI_PCIE_0_AXIBAR_0),RegOffset_CAP_L);
xil_printf("acpl is %08X \r\n", acpl);
acph = XAxiPcie_ReadReg((XPAR_AXI_PCIE_0_AXIBAR_0),RegOffset_CAP_H);
xil_printf("acph is %08X \r\n", acph);
VS = XAxiPcie_ReadReg((XPAR_AXI_PCIE_0_AXIBAR_0),RegOffset_VS);
xil_printf("VS is %08X \r\n", VS);
CC = XAxiPcie_ReadReg((XPAR_AXI_PCIE_0_AXIBAR_0),RegOffset_CC);
xil_printf("CC is %08X \r\n", CC);
2 、配置寄存器(初始化)
①等待CSTS寄存器(0x8000001C)ready位为0

cpp
// 循环查询,直到最低位CSTS.RDY(Bit0)为 0
while(1)
{
CSTS = XAxiPcie_ReadReg(XPAR_AXI_PCIE_0_AXIBAR_0, RegOffset_CSTS);
xil_printf("CSTS: 0x%08X\r\n", CSTS);
// 判断 Bit0 是否为 0
if( (CSTS & 0x00000001) == 0 )
{
break;
}
usleep(1000); // 延时1ms,避免频繁读寄存器占用总线
}
xil_printf("Controller Ready!\r\n");
②配置CC寄存器(0x80000014)为0x00460000 :配置Admin提交队列单条大小为64byte, Admin完成队列单条大小为16byte。

cpp
//配置CC寄存器(不设置EN位)
XAxiPcie_WriteReg(XPAR_AXI_PCIE_0_AXIBAR_0, RegOffset_CC, 0x00460000) ;
usleep(1000);
CC = XAxiPcie_ReadReg((XPAR_AXI_PCIE_0_AXIBAR_0),RegOffset_CC);
xil_printf("CC is %08X \r\n", CC);
③配置AQA寄存器(0x80000024)为0x00100010:配置Admin提交、完成队列深度为16。


cpp
u32 aqa_val, asq_val, acq_val;
// AQA寄存器:Admin队列属性
// [15:0] ASQS (Admin Submission Queue Size)
// [31:16] ACQS (Admin Completion Queue Size)
aqa_val = ((NVME_ADMIN_QSIZE - 1) << 16) | (NVME_ADMIN_QSIZE - 1);
XAxiPcie_WriteReg(XPAR_AXI_PCIE_0_AXIBAR_0, RegOffset_AQA, aqa_val) ;
xil_printf("AQA set to 0x%08X\r\n", aqa_val);
④配置ASQ寄存器(0x80000028、0x8000002C)分别为0x10100000,0x00000000:配置Admin提交队列基地址高32位为0,低32位为0x10100000。


cpp
// ASQ寄存器:Admin Submission Queue Base Address(低64位)
asq_val = ADMIN_SQ_BASE & 0xFFFFFFFF;
XAxiPcie_WriteReg(XPAR_AXI_PCIE_0_AXIBAR_0, RegOffset_ASQ_L, asq_val) ;
XAxiPcie_WriteReg(XPAR_AXI_PCIE_0_AXIBAR_0, RegOffset_ASQ_H, (ADMIN_SQ_BASE >> 32) & 0xFFFFFFFF) ;
xil_printf("ASQ set to 0x%08X\r\n", asq_val);
⑤配置ACQ寄存器(0x80000030、0x80000034)分别为0x10101000,0x00000000:配置Admin完成队列基地址高32位为0,低32位为0x10101000。


cpp
// ACQ寄存器:Admin Completion Queue Base Address(低64位)
acq_val = ADMIN_CQ_BASE & 0xFFFFFFFF;
XAxiPcie_WriteReg(XPAR_AXI_PCIE_0_AXIBAR_0, RegOffset_ACQ_L, acq_val) ;
XAxiPcie_WriteReg(XPAR_AXI_PCIE_0_AXIBAR_0, RegOffset_ACQ_H, (ADMIN_CQ_BASE >> 32) & 0xFFFFFFFF) ;
xil_printf("ACQ set to 0x%08X\r\n", acq_val);
⑥配置CC寄存器(设置EN)地址0x80000014写0x00460001

cpp
//配置CC寄存器(设置EN)
XAxiPcie_WriteReg(XPAR_AXI_PCIE_0_AXIBAR_0, RegOffset_CC, 0x00460001) ;
usleep(1000);
CC = XAxiPcie_ReadReg((XPAR_AXI_PCIE_0_AXIBAR_0),RegOffset_CC);
xil_printf("CC is %08X \r\n", CC);
⑦等待CSTS寄存器(0x8000001C)ready位为1
cpp
// 循环查询,直到最低位CSTS.RDY(Bit0)为 1
while(1)
{
CSTS = XAxiPcie_ReadReg(XPAR_AXI_PCIE_0_AXIBAR_0, RegOffset_CSTS);
xil_printf("CSTS: 0x%08X\r\n", CSTS);
// 判断 Bit0 是否为 1
if( (CSTS & 0x00000001) == 1 )
{
break;
}
usleep(1000); // 延时1ms,避免频繁读寄存器占用总线
}
xil_printf("Controller Ready!\r\n");
至此寄存器就配置完成
