本文完成了以 FPGA 为主控、无 CPU 的固态硬盘存储控制器设计,可实现FPGA 对 PCIe SSD 的直接数据读写控制。该存储控制器由 PCIe Root Complex、PCIe 控制逻辑和 NVMe 控制模块组成,其中 PCIe Root Complex 模块为 Xilinx公司的 IP 核,本文在其基础上完成了用于配置 PCIe 配置空间和发送 TLP 报文的相关控制逻辑,以实现 RC 和 EP 两端的数据通信;NVMe 控制模块为本文依据 NVMe 协议设计的核心控制模块,其主要实现了 NVMe 初始化、指令提交和处理机制以及 Admin 和 I/O 指令的发送控制等功能。
经过实验验证,各主要功能模块逻辑正确,存储控制器功能无误。在 PCIe链路状态为 2.0 x4 的条件下,读数据速度可达约 842MByte/s,写数据速度可达约 1.17GByte/s。
随着 PCIe(Peripheral Component Interconnect express)接口被成功应用于固态硬盘,其上层应用层协议——NVMe(Non-Volatile Memory express)协议也应运而生。基于 NVMe 协议的 PCIe SSD(Solid State Drives)兼具体积小、重量轻、有读写速度超高等优势,其商业化产品的出现也为实现高速大容量嵌入式存储系统提供了新的方向。本项目研究设计基于 FPGA 的 PCIe SSD 存储控制器,为实现基于 PCIe SSD 的高速大容量存储系统奠定基础。PCIe 总线就是一种采用端到端连接方式的高速差分总线,它由 PCI 总线发展而来。
PCIe 总线采用端到端的连接方式,在一条 PCIe 链路的两端只能各自连接一个设备。这两个设备互为数据的发送端和接收端,发送端和接收端均包含发送(TX)和接收(RX)逻辑。
PCIe 总线的物理链路
PCIe 总线作为处理器系统的内部总线,其主要作用是为了连接处理器系统中的外部设备。在大多数 PCIe 体系结构实现方案中,都使用 RC、Switch 和 PCIe-to-PCI 桥这些基本模块连接 PCIe 或 PCI 设备。在 PCIe 总线系统中,基于 PCIe总线的设备也被称为 Endpoint(EP)。
基于 PCIe 总线的系统结构
PCIe 设备,如 PCIe SSD、PCIe 网卡等可通过 RC 上的 PCIe 端口直接与 RC相连,且每一个
PCIe 端口只能连接一个 EP。PCIe 端口可通过 Switch 进行链路扩展,它为 EP 提供了更多可连接的端口。Switch 中靠近 RC 的端口被称为上游端口,而分出来的其它端口被称为下游端口。通过 Switch 扩展出来的链路可以继续挂接其它 EP 或者 Switch。Switch 接收到上游端口发送而来的数据,并判断数据是要传送给下游的哪个设备,而后进行转发;下游设备发送给 RC 的数据也经由 Switch 转发。
每一个 PCIe 设备均有一段用于存放设备信息或配置设备参数的寄存器空间,该空间被为 PCIe 的配置空间。PCIe 设备的配置空间在继承了 PCI 设备的256Byte 配置空间的基础上,又扩展了 PCIe 的一些独有的配置寄存器,其配置空间总共为 4Kbyte。
PCIe 设备的 4KB 配置空间
NVMe 协议是一种用于连接主机软件层与 EP 端非易失性存储器系统的命令层接口协议,其提出主要是为了适应存储系统中底层闪存带宽的提升和访问延时的降低。NVMe 协议主要定义了主从系统之间通信的方式、寄存器接口和相应的指令集。作为较高层次的协议,NVMe 协议负责将命令和地址等参数准备好,交由PCIe 接口去传输;其定义的一系列控制寄存器主要功能包括标志 NVMe 控制器功能属性以及工作状态、配置 Admin 队列相关参数属性、以及作为提交和完成队列 Doorbell 寄存器等;NVMe 指令分为 Admin 指令集和 I/O 指令集两大类,其每一类均由若干条指令组成;NVMe 控制器的相关参数可通过发送 Admin 指令集中的 Identify 指令获取;一个 NVMe 控制器又可以支持若干个 namespace,namespace 由一定数量的逻辑块组成,它可由 Admin 指令集中的 Namespace Management 和 Namespace Attachment 管理和创建,每一个 namespace 有其对应的 ID 号,可通过 Identify 指令查看其参数和属性。除了上述控制寄存器和指令集之外,NVMe 协议实现中的另一个重要机制为提交(Submission)和完成(Completion)队列,提交队列用于存放主机提交的 NVMe 指令,完成队列用于存放 NVMe 控制器返回的指令处理完成信息;Admin 提交和完成队列分别只有一个,且不需要创建;I/O 队列的创建和参数配置通过发送相关 Admin 指令完成,最高可支持 65535 个 I/O 队列,且每个队列支持 64K 条指令,具体的配置情况根据应用需求而定;每一个提交队列可以分别对应一个完成队列,也可以多个提交队列共用一个完成队列。
所有的 NVMe 指令均由 64byte(16DW)数据组成,其指令组成格式如下图所示。
在每一条指令处理完成之后,NVMe 控制器均会返回一条规定格式的完成报文用以表征指令的处理情况。所有的 NVMe指令完成报文均由16byte 数据组成,其格式如图所示。
在本项目中使用的是 Identify、Set Feature、Create I/O Compl- etion Queues、Create I/O Submission Queues、Read 和 Write 指令的功能及指令组成格式。
Identify 指令用于确认控制器和各 namespace 的配置属性信息,其仅使用了DW0 至 DW10 字段,DW11 至 DW15 字段为保留字段。Set Feature 指令用于配置 NVMe 控制器的一些特性。Create I/O Completion Queues 和 Create I/O Submission Queues 指令分别用于创建 I/O 完成队列和提交队列。Read 和 Write 指令分别用于从 EP 端读和往 EP 端写入数据,根据闪存介质的特性,其数据读写以逻辑块(logical block)为单位;逻辑块的大小可通过分析Identify 指令返回的数据结构获知。
在 NVMe 协议中,主机内存中的寻址方式主要有两种,一种是 PRP(Physical Region Page,物理区域页),另一种是 SGL(Scatter/Gather List,分散/聚集列表)。PRP 条目其实际上为一个 64 位的内存物理地址,由页起始地址和页内偏移两个部分组成;一个 PRP 条目描述的是一个物理页空间,若传输数据量较大,需要多个物理页存放,则需要多个 PRP 条目对其进行表示;将这若干个 PRP 条目连接起来,就形成了 PRP 链表(list)。SGL 是一个链表,其由一个或多个 SGL 段组成,每个 SGL 段又由一个或多个 SGL 描述符组成。SGL 描述符是 SGL 最基本的单元,它根据起始地址和空间大小等两个参数描述了一段连续的物理内存空间;因而 SGL 寻址方式可以描述一块任意大小的内存空间,而不仅仅是一块物理页。
NVMe 子系统在系统中的位置
NVMe 指令提交及完成机制为主机端首先将待发送的 NVMe 指令提交至提交队列中,同时提交队列尾指针随之改变;主机端修改 NVMe 控制器中相应队列的 SQ Tail Doorbell 寄存器;NVMe 控制器检测到该寄存器发生变化之后,从主机提交队列中将 NVMe 指令取回;随后执行 NVMe 指令;不论指令执行成功与否,控制器均将向完成队列中返回一个完成条目;之后控制器将产生一个中断信号,通知主机指令处理完成;主机端随后处理完成条目,查看指令的完成状态;最后主机端修改 CQ Head Doorbell 寄存器以通知控制器相关完成条目已成功处理,整个指令提交和完成机制也到此结束。
本项目选用 Xilinx 公司的 7 系列 FPGA 芯片进行 PCIe SSD 存储控制器的设计,并使用了其 7 系列 PCIe IP 核解决方案。本项目与其它现有研究成果的区别在于使用 FPGA 为主控、设计了无CPU 的存储控制器。使用 Xilinx 的 Micro Blaze 嵌入式软核或者 Zynq 系列产品中的 ARM 处理器,结合 FPGA 硬件逻辑实现的存储控制器需要占用更多的逻辑资源,并且软硬件协同设计较为复杂;此外纯硬件逻辑对 TLP 包译码以及 NVMe指令完成机制的处理更加灵敏,比 CPU 处理消耗更少的空闲周期。
基于 PCIe SSD 的存储方案
NVMe 控制模块根据 NVMe 协议实现,其透过 PCIe 接口向 SSD 发送 Identify、Write、Read 等 NVMe 指令,以完成对 SSD 的数据读写控制。NVMe 控制器的逻辑设计主要实现 NVMe 初始化流程、NVMe 指令的提交及完成机制和 NVMe的相关 Admin 和 I/O 指令。具体逻辑功能包括 NVMe 寄存器的配置、Admin 指令逻辑的实现、I/O 指令队列的创建和 I/O 指令逻辑的实现。
硬盘存储控制器模块示意图
存储控制器工作流程图
本项目设计的硬盘存储控制器特性总结如下:
1)根据 NVMe 协议,设计应用层控制逻辑,以 FPGA 为主控(无 CPU)直接对NVMe PCIe SSD 进行数据读写控制;
2)使用 Xilinx PCIe Root Complex IP 核,其通过 64 位的 AXI-4 总线接口直接与应用层 NVMe 控制模块相连;
3)实际使用的链路状态是 PCIe 2.0 x4,时钟频率为 250MHz;
4)支持的 NVMe 命令包括:Identify,Set Feature,Create I/O Completion Queues,Create I/O Submission Queues,Read 和 Write 等;
5)系统中没有 CPU 和主机内存,因而涉及到寄存器映射到主机内存空间、内存空间中创建队列、读写主机内存中数据等相关操作时,我们为其分配虚拟的内存地址值。
Series Integrated Block for PCI Express 顶层模块及接口示意图
从示意图中可以看出,该 IP 核的主要接口包括 System 接口、PCI Express 接口、Configuration 接口、AXI-4 Stream 接口、物理层控制及状态接口以及一些Debug 接口等。System 接口包括了系统复位及时钟信号;PCI Express 接口包括了 用 于 发 射 和 接 收 的 差 分 信 号 接 口 ,其 接 口 数 量 根 据 配 置 情 况 而 定 ;Configuration 接口提供了让用户对 IP 核的 PCIe 配置空间进行配置及监测的方法;AXI-4 Stream 接口即为事务层接口,它提供了用户逻辑与 IP 核之间产生及接收 TLP 包的机制;物理层控制及状态接口为用户提供了监测和控制物理层链路状态的机制。本项目 PCIe 控制逻辑中的 RC 端配置空间的配置、EP 端配置空间的配置以及 TLP 包封包逻辑均是基于上述接口及机制完成的。其中 RC 端配置空间的配置通过 Configuration 接口完成;TLP 包的发送与接收通过 AXI-4 Stream 接口完成;EP 端配置空间的配置通过发送配置读写 TLP 包完成;另外,我们通过物理层控制及状态接口以及 Debug 接口对 IP 核进行监测和调试。
NVMe 控制模块是实现固态硬盘数据读写的核心模块,其主要实现的功能包括 NVMe 寄存器配置、Admin 指令相关逻辑实现、I/O 队列的创建和 I/O 指令相关逻辑的实现等。本项目依据 2016 年 6 月推出的 NVMe v1.2.1 协议进行相关设计。
NVMe 控制器初始化流程
NVMe 控制寄存器初始化模块的状态机
Admin 指令封包发送模块状态机
Admin 指令处理机制控制模块状态机