news 2026/7/18 19:22:12

二、实战篇-NVME SSD控制之ZYNQ实现(五)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
二、实战篇-NVME SSD控制之ZYNQ实现(五)

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

参考:

FPGA控制NVME开发流程-CSDN博客

一、理论篇-NVME协议学习笔记-CSDN博客

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

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

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

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

前面完成了PCIE的建链和枚举、FPGA和NVME SSD的PCIE CFG空间的配置以及NVME SSD的寄存器配置。创建提交队列和完成队列,并完成NvmeIdentifyCtrl信息读取。今天讲使用ADMIN命令创建IO提交和完成队列

Admin命令集(Admin Command Set)如下

常用的命令:

前面读取NvmeIdentifyCtrl信息使用的操作码为06h

宏定义所有需要使用的操作码

// ===================== NVMe 命令操作码 ===================== #define Admin_CMD_Create_SQ 0x01 #define Admin_CMD_Create_CQ 0x05 #define Admin_CMD_IDENTIFY 0x06 #define Admin_CMD_SET_FEAT 0x09 #define Admin_CMD_GET_FEAT 0x0A

前面完成了NvmeIdentifyCtrl信息读取,下面进行设置IO队列数量和IO队列的创建

1、设置IO队列数量

设置IO队列数量使用设置特征命令

DW11如上,实际写入值为:队列数量-1,创建2个队列:NCQR和NSQR都为1。

// Set Features: 设置 I/O 队列数量 int NvmeSetIoQueueNum(u16 sq_num, u16 cq_num) { NvmeSqe sqe = {0}; NvmeCqe cqe; sqe.cdw0 = Admin_CMD_SET_FEAT; sqe.cdw10 = FEAT_IO_QUEUES; sqe.cdw11 = (cq_num << 16) | sq_num; NvmeSubmitAdminCmd(&sqe); usleep(2000); if(NvmePollAdminCqe(&cqe) != 0) { xil_printf("NvmeSetIoQueueNum 4. CQE轮询超时,函数退出\r\n"); return -1; } xil_printf("NvmeSetIoQueueNum 5. 成功读到CQE\r\n"); u16 real_status = (cqe.status >> 1) & 0x7FFF; if(real_status != 0) { xil_printf("Set Features failed, status=0x%04X\r\n", real_status); return -1; } xil_printf("Set IO Queues Done: SQ=%d, CQ=%d\r\n", sq_num, cq_num); return 0; }

2、创建IO完成队列

定义IO队列存放在本地的基地址,

#define IO_WR_SQ_BASE 0x10102000 #define IO_WR_CQ_BASE 0x10103000 #define IO_RD_SQ_BASE 0x10104000 #define IO_RD_CQ_BASE 0x10105000
// 创建 I/O Completion Queue int NvmeCreateIoCq(u16 cid, u16 cq_id, u16 depth, u64 cq_base_addr) { NvmeSqe sqe = {0}; NvmeCqe cqe; // CID由外部传入,拼接Opcode sqe.cdw0 = (((u32)cid) << 16) | Admin_CMD_Create_CQ; sqe.nsid = 0; sqe.rsvd1 = 0; sqe.mptr = 0; sqe.dptr[0] = cq_base_addr; // PRP1:CQ物理基地址 sqe.dptr[1] = 0; // DW10:高16位QSIZE(depth-1),低16位QID(cq_id),按NVMe标准修正高低位 sqe.cdw10 = (((u32)(depth - 1)) << 16) | (cq_id & 0xFFFF); // DW11:PC=1(内存连续),IEN=0(纯轮询关闭中断),IV=0 sqe.cdw11 = 0x00000001; sqe.cdw12 = 0; sqe.cdw13 = 0; sqe.cdw14 = 0; sqe.cdw15 = 0; // 缓存刷新,确保SSD读取完整SQE Xil_DCacheFlushRange(&sqe, sizeof(NvmeSqe)); NvmeSubmitAdminCmd(&sqe); usleep(2000); if(NvmePollAdminCqe(&cqe) != 0) { xil_printf("NvmeCreateIoCq 4. CQE轮询超时,函数退出\r\n"); return -1; } xil_printf("NvmeCreateIoCq 5. 成功读到CQE\r\n"); u16 real_status = (cqe.status >> 1) & 0x7FFF; if(real_status != 0) { xil_printf("Create CQ %d failed, CID=%d status=0x%04X\r\n", cq_id, cid, real_status); return -1; } xil_printf("Create IO CQ %d Done, CID=%d\r\n", cq_id, cid); return 0; }

3、创建IO提交队列

// 创建 I/O Submission Queue int NvmeCreateIoSq(u16 cid, u16 sq_id, u16 cq_id, u16 depth, u64 sq_base_addr) { NvmeSqe sqe = {0}; NvmeCqe cqe; // DW0:高16bit CID,低8bit Opcode(0x01 Create SQ) sqe.cdw0 = (((u32)cid) << 16) | Admin_CMD_Create_SQ; sqe.nsid = 0; sqe.rsvd1 = 0; sqe.mptr = 0; // DW10:Bit31:16=QSIZE(depth-1),Bit15:0=SQID(sq_id) sqe.cdw10 = (((u32)(depth - 1)) << 16) | (sq_id & 0xFFFF); // DW11:Bit31:16=CQID,保留位0,QPRIO=0,PC=1 sqe.cdw11 = (((u32)cq_id) << 16) | 0x0001; // PRP1存放SQ物理基地址,PRP2置0 sqe.dptr[0] = sq_base_addr; sqe.dptr[1] = 0; // 其余保留DWORD全部清零 sqe.cdw12 = 0; sqe.cdw13 = 0; sqe.cdw14 = 0; sqe.cdw15 = 0; // 刷新数据缓存,保证SSD读取完整SQE Xil_DCacheFlushRange(&sqe, sizeof(NvmeSqe)); NvmeSubmitAdminCmd(&sqe); usleep(2000); if(NvmePollAdminCqe(&cqe) != 0) { xil_printf("NvmeCreateIoSq 4. CQE轮询超时,函数退出\r\n"); return -1; } xil_printf("NvmeCreateIoSq 5. 成功读到CQE\r\n"); u16 real_status = (cqe.status >> 1) & 0x7FFF; if(real_status != 0) { xil_printf("Create SQ %d failed, CID=%d status=0x%04X\r\n", sq_id, cid, real_status); return -1; } xil_printf("Create IO SQ %d Done, bind CQ=%d, CID=%d\r\n", sq_id, cq_id, cid); return 0; }

main函数中调用

NvmeSetIoQueueNum(1,1); // NSQR=1, NCQR=1(0基偏移),实际分配 2套I/O队列(2SQ+2CQ) //创建 I/O 队列 NvmeCreateIoCq(0x0300,1, IO_Q_DEPTH,IO_WR_CQ_BASE); NvmeCreateIoCq(0x0301,2, IO_Q_DEPTH, IO_RD_CQ_BASE); usleep(1000); NvmeCreateIoSq(0x0302,1, 1, IO_Q_DEPTH,IO_WR_SQ_BASE); NvmeCreateIoSq(0x0303,2, 2, IO_Q_DEPTH, IO_RD_SQ_BASE);

IO队列深度这里设置的是8

到这里初始化及配置基本上已经完成,后面就可以进行数据读写了

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 19:20:25

提示词精准度提升83%的关键法则,从模糊描述到工业级可控生成——DALL-E 3时代不可错过的7维提示框架

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;DALL-E 3提示词工程的范式跃迁 DALL-E 3不再将提示词视为静态指令字符串&#xff0c;而是将其建模为多模态语义对齐的动态接口——用户意图、视觉先验与文本结构三者需在扩散模型的隐空间中完成联合优化…

作者头像 李华
网站建设 2026/7/18 19:16:44

PHP 出现 The requested URL was not found on this server 的解决办法

自己刚配置好的环境&#xff0c;出现了重定向的问题&#xff0c;原因可能在于.htaccess文件和apache的配置问题。1&#xff0c;找到apache配置文件httpd.conf配置文件中的AllowOverride none Require all denied这两行&#xff0c;改成Options Indexes FollowSymLinks …

作者头像 李华
网站建设 2026/7/18 19:15:18

光刻胶技术解析:正胶与负胶的差异与应用

1. 光刻胶的基本概念与分类 在半导体制造的光刻工艺中&#xff0c;光刻胶&#xff08;Photoresist&#xff09;是核心材料之一。它是一层对光敏感的有机化合物薄膜&#xff0c;涂覆在硅片表面&#xff0c;通过曝光和显影过程将掩模版上的图形转移到硅片上。根据光化学反应特性的…

作者头像 李华
网站建设 2026/7/18 19:14:18

高速PCB设计中信号走线等长控制的关键技术与实践

1. 高速信号走线等长控制的必要性在PCB设计中&#xff0c;高速信号走线的等长控制是一个看似简单但极其关键的技术细节。我第一次真正意识到它的重要性是在设计一个DDR3内存接口时&#xff0c;当时系统频繁出现数据校验错误&#xff0c;经过两周的排查才发现问题出在地址线和数…

作者头像 李华
网站建设 2026/7/18 19:12:58

高速ADC架构解析与5G通信应用实践

1. 高速ADC的核心价值与挑战 在数字信号处理领域&#xff0c;模数转换器&#xff08;ADC&#xff09;扮演着桥梁角色&#xff0c;将现实世界的连续模拟信号转化为数字系统能够处理的离散数值。当采样速率突破100MS/s&#xff08;百万次采样/秒&#xff09;时&#xff0c;我们便…

作者头像 李华
网站建设 2026/7/18 19:11:08

发现Umi-OCR:离线OCR文字识别的革新之道

发现Umi-OCR&#xff1a;离线OCR文字识别的革新之道 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片&#xff0c;PDF文档识别&#xff0c;排除水印/页眉页脚&#xff0c;扫描/生成二维码。内置多国语言库。 项目地…

作者头像 李华