fpga nvme 寄存器

fpga nvme 寄存器_第1张图片

图1所示的NVMe多队列,每个队列支持64K命令,最多支持64K队列。这些队列的设计使得IO命令和对命令的处理不仅可以在同一处理器内核上运行,也可以充分利用多核处理器的并行处理能力。每个应用程序或线程可以有自己的独立队列,因此不需要IO锁定。NVMe还支持MSI-X和中断控制,避免了CPU中断处理的瓶颈,实现了系统扩展的可伸缩性。NVMe采用简化的命令集,相比SAS或SATA,NVMe命令集使用的处理IO请求的指令数量减少了一半,从而在单位CPU指令周期内可以提供更高的IOPS,并且降低主机中IO软件堆栈的处理延迟。

2 NVMe寄存器

NVMe(Over PCIe)寄存器主要分为两类,一类是PCIe配置空间寄存器,一类是NVMe控制器相关的寄存器。

a.PCIe配置空间和功能

NVMe PCIe总线寄存器如表1所示,NVMe跟主机CPU的接口主要是基于PCIe总线,使用PCIe的Config和Capability机制。包括PCI/PCIe头、PCI功能和PCIe扩展功能。

表1 NVMe PCIe配置空间和功能

 fpga nvme 寄存器_第2张图片

b.NVMe控制器寄存器

NVMe控制器寄存器位于MLBAR/MUBAR寄存器(PCI BAR0和BAR1)中,这些寄存器应映射到支持顺序访问和可变访问宽度的内存空间。NVMe 1.3d版本的控制器寄存器列表如表2所示。

表2 NVMe 1.3d版本的控制器寄存器列表

起始 结束 缩写 描述
0h 7h CAP 控制功能
8h Bh VS 版本
Ch Fh INTMS 中断屏蔽设置
10h 13h INTMC 中断屏蔽清楚
14h 17h CC 控制器配置
18h 1Bh Reserved 保留
1Ch 1Fh CSTS 控制器状态
20h 23h NSSR NVM子系统重置(可选)
24h 27h AQA 管理队列属性
28h 2Fh ASQ 管理提交队列基地址
30h 37h ACQ 管理完成队列基地址
38h 3Bh CMBLOC 控制器存储缓冲位置(可选)
3Ch 3Fh CMBSZ 控制器存储缓冲大小(可选)
40h 43h BPINFO 引导分区信息(可选)
44h 47h BPRSEL 引导分区读选择(可选)
48h 4Fh BPMBL 引导分区存储缓冲位置(可选)
50h EFFh Reserved 保留
F00h FFFh Reserved 命令设置具体的寄存器
1000h 1003h SQ0TDBL 管理SQ0尾Db
1000h + (1 * (4 << CAP.DSTRD)) 1003h + (1 * (4 << CAP.DSTRD)) CQ0HDBL 管理CQ0头Db
1000h + (2 * (4 << CAP.DSTRD)) 1003h + (2 * (4 << CAP.DSTRD)) SQ1TDBL SQ1尾Db
1000h + (3 * (4 << CAP.DSTRD)) 1003h + (3 * (4 << CAP.DSTRD)) CQ1HDBL CQ1头Db
1000h+ (2y * (4 << CAP.DSTRD)) 1003h + (2y * (4 << CAP.DSTRD)) SQyTDBL SQy尾Db
1000h + ((2y + 1) * (4 << CAP.DSTRD)) 1003h + ((2y + 1) * (4 << CAP.DSTRD)) CQyHDBL CQy头Db
供应商定制寄存器(可选)
SQ:Submission Queue,提交队列;CQ:Completion Queue,完成队列;Db:Doorbell,门铃。

3 NVMe队列

NVMe的队列是经典的环形队列结构,通过提交/完成队列对来实现队列的传输交互。

a.队列概述

NVMe使用的是经典的循环队列结构来传递消息(例如,传递命令和命令完成通知)。队列可以映射到任何PCIe可访问的内存中,通常是放在主机内存。

如图2,队列是固定大小的,通过Tail和Head来分别指向写入和读取的指针。像通常的队列数据结构一样,队列实际可使用的大小是队列大小减1,并且用Head等于Tail指示队列空,用Head等于(Tail+1)除以队列大小的余数来指示队列满。

fpga nvme 寄存器_第3张图片

 如上一节的图1,根据用途,NVMe队列有两类:管理队列和IO队列;根据传输方向有两类:提交队列和完成队列。具体介绍见表3。

表3 NVMe队列类型

管理 IO
提交 用于提交管理命令,最大4K项;
用于配置控制器和IO队列等;
从主机侧到控制器侧。
用于传输IO命令,最大64K项;
用于提交IO操作命令;
从主机侧到控制器侧。
完成 管理命令的完成确认,最大4K项;
从控制器侧到主机侧;
独立的MSI-X中断处理。
IO命令的完成确认,最大64K项;
从控制器侧到主机侧;
独立的MSI-X中断处理。

b.队列处理流程

NVMe的驱动和设备交互跟Virtio不同:Virtio是在通过一个队列完成双向通知交互;而NVMe则采用提交队列和完成队列配合完成双向交互的方式。

如图3,NVMe队列处理流程如下(其中主机为软件驱动,控制器为硬件设备):

(1)主机写命令到提交队列项中。

(2)主机写DB(Doorbell)寄存器,通知控制器有新命令待处理。

(3)控制器从内存中的提交队列中读取命令。

(4)控制器执行命令。

(5)控制器更新完成队列,表示当前的SQ项已经处理。

(6)控制器发MSI-x中断到主机CPU。

(7)主机处理完成队列,同步更新提交队列中的已处理项。

(8)主机写完成队列Db到控制器,告知完成队列项已释放。

fpga nvme 寄存器_第4张图片

4 NVMe命令结构

我们通过如下一些概念来理解NVMe命令结构:

队列项的数据格式。NVMe的提交命令固定64字节,完成命令固定16字节。

命令。NVMe命令分为Admin和IO两类。

NVMe的数据块组织方式有PRP和SGL两种。

a.队列项的数据格式

提交队列和完成队列,组成队列对,协作完成NVMe驱动和设备之间的命令传输。提交队列每一项64字节固定大小,完成队列每一项16字节固定大小。

提交队列的数据格式如图4所示。NVMe提交队列项的数据格式属性如下:

Opcode:命令操作码

FUSE:熔合两个命令为一条命令

PSDT:PRP或SGL数据传输

Command Identifier:命令ID

Namespace Identifier:命名空间ID

Metadata Pointer:元数据指针

PRP entry 1/2:物理区域页项,对应的由PRP和PRP列表

SGL:散列聚合列表

fpga nvme 寄存器_第5张图片

 完成队列的数据格式如图5所示。

fpga nvme 寄存器_第6张图片

图5 完成队列项的数据格式

NVMe完成队列的数据格式属性如下:

SQ Header pointer:SQ头指针

SQ Identifier:SQ ID

Command Identifier:命令ID

P:相位标志phase tag,完成队列没有head/tail交互,通过相位标志实现完成队列项的释放

Status Field:状态域

b.NVMe命令

NVMe管理类的命令如表4所示。

表4 NVMe管理命令列表

NVMe IO类命令如表5所示。

表5 NVMe IO类命令列表

命令 必选或可选 类别
创建IO SQ 必选 队列管理
删除IO SQ 必选
创建IO CQ 必选
删除IO CQ 必选
鉴别 必选 配置
获取特征 必选
设置特征 必选
获取日志页 必选 状态报告
异步事件请求 必选
中止 必选 中止命令
固件镜像下载 可选 固件更新和管理
固件可用 可选
IO命令集定制命令 可选 IO命令集定制
供应商定制命令 可选 供应商定制

NVMe IO类命令如表5所示。

表5 NVMe IO类命令列表

命令 必选或可选 类别
必选 必选的数据命令
必选
清洗 必选
不可改正的写 可选 可选的数据命令
写0 可选
比较 可选
数据集管理 可选 数据提示
预约获取 可选 预约命令
预约寄存器 可选
预约释放 可选
预约报告 可选
供应商专用命令 可选 供应商专用

c.物理区域页PRP

PRP本质是一个链表,链表中的每一个指针都指向一个不超过页大小的数据块。PRP为8字节(64bits)固定大小,PRPList则最多可以占满一整个页。

PRP1和PRP2的格式如图6(a)所示。如果是首个PRP,则Offset(偏移量)可能是非零的数据,另外,偏移量是32bits对齐的(即末尾两位为0)。如图6(b)所示,在PRP列表中的所有PRP项的偏移量都为0,也即是PRP指针指向页面起始地址。

fpga nvme 寄存器_第7张图片

 如图7(a)所示,当数据只有一个或两个页面的时候,就不需要使用PRP列表数据结构,直接PRP1和PRP2指向内存页面。当一个命令指向的数据超过两个内存页面的时候,就需要使用PRP列表,图7(b)所显式的为使用PRP列表的数据结构。

fpga nvme 寄存器_第8张图片

(b) 范例2:PRP列表指针,指向PRP列表,再指向内存页面

图7 PRP数据结构范例

d.散列聚合列表SGL

PRP每个链表指针最多指向一个页大小的数据块,即使若干个页在内存连续放置,PRP也需要对应的多个PRP项。为了减少元数据规模,SGL不限制指针指向数据块的大小,这样连续的若干个页的数据,只需要一个SGL项就可以标识。

NVMe中SGL的长度为16字节固定长度,其格式如图8(a)所示,在最高的第15字节SGL描述符类型域和子类型域标识不同类型的SGL描述符,根据不同的描述符,字节14-0的格式各有不同。SGL描述符类型如图8(b)所示。

fpga nvme 寄存器_第9张图片

如图9,NVMe SGL的数据结构是链表形式,SQ中的首个SGL段只有1项,为指向下一个SGL段的指针。下一个SGL段包含若干SGL数据块描述符,SGL段的最后的一个SGL描述符为另一个SGL段指针,指向下一个SGL段。根据传输数据大小,在最后一个SGL 段中,所有的SGL描述符都是SGL数据块描述符。

PRP只能指向单个内存页,这样,当要传输的数据块非常大的时候,就需要非常多的PRP项。而SGL可以指向不同大小的数据块,处于连续内存区域的多个数据块只需要一个SGL描述符就可以标识。因此,一般情况下,SGL比PRP更高效,更节省描述符资源。

fpga nvme 寄存器_第10张图片

NVMe over PCIe协议,定义了NVMe协议的使用范围、指令集、寄存器配置规范等。

1.1 名词解释

1.1.1 Namespace

Namespace是一定数量逻辑块(LB)的集合,属性在Identify Controller中的数据结构中定义。

1.1.2 Fused Operations

Fused Operations可以理解为聚合操作,只能聚合两条命令,并且这两条命令在队列中应保持相邻顺序。协议中只有NVM指令才有聚合操作。还需要保证聚合操作的两条命令读写的原子性,参考Compare and Write例子。

1.1.3 指令执行顺序

除了聚合操作(Fused Operations),每一条SQ中的命令都是独立的,不必考虑RAW等数据相关问题,即使考虑,也是host应该解决的问题。

1.1.4 写单元的原子性

控制器需要支持写单元的原子性。但有时也能通过host配置Write Atomicity feature,减小原子性单元的大小,提高性能。

1.1.5 元数据

数据的额外信息,相当于提供校验功能。可选的方式。

1.1.6 仲裁机制

用来选择下一次执行的命令的SQ的机制,三种仲裁方式:

  • RR(每个队列优先级相同,轮转调度)

  • 带权重的RR(队列有4种优先级,根据优先级调度)

  • 自定义实现

  • 1.1.8 Queue Pair

    由SQ(提交队列)与CQ(完成队列)组成,host通过SQ提交命令,NVMe Controller通过CQ提交完成命令。

    1.1.9 NVM 子系统

    NVM子系统包括控制器、NVM存储介质以及控制器与NVM之间的接口。

    1.2 NVMe SSD

    1.2.1基本架构

    整体来看,NVMe SSD可以分为三部分,host端的驱动(NVMe官网以及linux、Windows已经集成了相应的驱动)、PCIe+NVMe实现的控制器以及FTL+NAND Flash的存储介质。

  • fpga nvme 寄存器_第11张图片

     

    1.2.2 NVMe控制器

    NVMe控制器实质上为DMA + multi Queue,DMA负责数据搬运(指令+用户数据),多队列负责发挥闪存的并行能力。

  • fpga nvme 寄存器_第12张图片

     

    2. PCIe寄存器配置

    NVMe over PCIe,通过利用PCIe总线实现数据交互的功能,实现对物理层的抽象功能。

    2.1 PCIe总线的基本结构

    PCIe总线分为三层,物理层,数据链路层,处理层(类似于计算机网络的分层结构),通过包来转发数据。NVMe协议定义的内容相当于PCIe的上一层应用层,处于应用层。PCIe给NVMe提供了底层的抽象。

    NVMe SSD相当于一个PCIe的端设备(EP)。

  • fpga nvme 寄存器_第13张图片

     

    2.2 寄存器配置

    在协议中主要定义了PC header、PCI Capabilities和PCI Express Extended Capabilities三部分内容。

    具体在host内存中会占有4KB,结构如下:

  • fpga nvme 寄存器_第14张图片

     

    2.2.1 PCI header

    PCI header有两种类型,type0表示设备,type1表示桥。NVMe 控制器属于EP,所以定义为type0的类型。共64KB,如下图:

  • fpga nvme 寄存器_第15张图片

     

    2.2.2 PCI Capabilities

    这里配置了PCI Capbilities,包括电源管理、中断管理(MSI、MSI-X)、PCIe Capbilities。

    2.2.3 PCI Express Extended Capabilities

    这里配置有关错误恢复等高级功能。

    3. NVMe寄存器配置

    3.1 寄存器定义

    NVMe寄存器主要分为两部分,一部分定义了Controller整体属性,一部分用来存放每组队列的头尾DB寄存器。

  • CAP——控制器能力,定义了内存页大小的最大最小值、支持的I/O指令集、DB寄存器步长、等待时间界限、仲裁机制、队列是否物理上连续、队列大小;

  • VS——版本号,定义了控制器实现NVMe协议的版本号;

  • INTMS——中断掩码,每个bit对应一个中断向量,使用MSI-X中断时,此寄存器无效;

  • INTMC——中断有效,每个bit对应一个中断向量,使用MSI-X中断时,此寄存器无效;

  • CC——控制器配置,定义了I/O SQ和CQ队列元素大小、关机状态提醒、仲裁机制、内存页大小、支持的I/O指令集、使能;

  • CSTS——控制器状态,包括关机状态、控制器致命错误、就绪状态;

  • AQA——Admin 队列属性,包括SQ大小和CQ大小;

  • ASQ——Admin SQ基地址;

  • ACQ——Admin CQ基地址;

  • 1000h之后的寄存器定义了队列的头、尾DB寄存器。


  •  

    3.2 寄存器理解

  • CAP寄存器标识的是Controller具有多少能力,而CC寄存器则是指当前Controller选择了哪些能力,可以理解为CC是CAP的一个子集;如果重启(reset)的话,可以更换CC配置;

  • CC.EN置一,表示Controller已经可以开始处理NVM命令,从1到0表示Controller重启;

  • CC.EN与CSTS.RDY关系密切,CSTS.RDY总是在CC.EN之后由Controller改变,其他不符合执行顺序的操作都将产生未定义的行为;

  • Admin队列有host直接创建,AQA、ASQ、ACQ三个寄存器标识了Admin队列,而其他I/O队列则有Admin命令创建(eg,创建I/O CQ命令);

  • Admin队列的头、尾DB寄存器标识为0,其他I/O队列标识由host按照一定规则分配;只有16bit的有效位,是因为队列深度最大64K。
     

    4. 内存数据结构

    4.1 SQ与CQ的详细定义

    4.1.1 空队列

  • fpga nvme 寄存器_第16张图片

 

4.1.2 满队列

判断队列满可以有多种方法,协议中规定的是头指针比尾指针大一,所以队列满时,空余一个元素。

fpga nvme 寄存器_第17张图片

 

4.1.3 队列性质

1. 队列大小有16bit,最小队列大小为2个元素(因为满队列的定义方式,所以最小为2个元素),对于I/O队列,最大队列大小为64k;对于Admin队列,最大队列为4k;

2. QID来标识唯一ID,16bit,由host分配;

3. host可以修改队列优先级(如果支持的话),共四级,U、H、M、L;

4.2 仲裁机制

4.2.1 RR

RR仲裁,Admin SQ与I/O SQ优先级相同,控制器每次可以选择一个队列中的多个命令(Arbitration Burst setting)。

fpga nvme 寄存器_第18张图片

 

4.2.2 带有优先权的RR

有3个严格的优先权,Priority1 > Priority2 > Priority3,在这三个优先级队列中,高优先级的队列中如果有命令,则优先执行(非抢占式)。

fpga nvme 寄存器_第19张图片

 

4.2.3 其他仲裁方式

Vendor Specific。

4.3 数据寻址方式(PRP和SGL)

4.3.1 PRP

NVMe把Host的内存分为页的集合,页的大小在CC寄存器中配置,PRP是一个64位的内存物理地址指针,结构如下:

 

最后两位为0,指四字节对齐;(n:2)位表示页内内偏移。

举个例子,内存页大小位4KB,则(11:2)表示页内偏移。

PRP寻址有两种方式,直接用PRP指针寻址,通过PRP List寻址。当使用PRP List寻址时,偏移必须为0h,每一个PRP条目表示一个内存页,如下:

 fpga nvme 寄存器_第20张图片

 

Admin命令的数据地址只能采取PRP的方式,I/O命令的数据地址既可以采取PRP的方式,又可以采取SGL的方式。Host在命令中会告诉Controller采用何种方式。具体来说,如果命令当中DW0[15:14]是0,就是PRP的方式,否则就是SGL的方式。

命令的Dword6~Dword9只定义了PRP1、PRP2两个数据指针,通过PRP条目可以指向PRP List。如下图:

 
fpga nvme 寄存器_第21张图片

 

在上面的例子中,PRP1直接指向内存页,PRP2指向PRP List存在的地址,在PRP List中存有数据的真正的地址。

更详细的说:

协议中PRP Entry是一个指向物理内存页的指针。PRP被用作NVMe Controller和PC内存之间进行数据传输。PRPEntry是固定大小的(8B)。

首先,明确两个概念,PRP Entry 为PRP指针,PRP List为PRP列表指针,示意图如下:

fpga nvme 寄存器_第22张图片 

 

根据每次传输数据的大小,以及PRP指针的偏移(offset)可以分为以下五种情况:

 

fpga nvme 寄存器_第23张图片 

 

4.3.2 SGL

SGL是另外一种索引内存的数据结构。SGL由若干个SGL段组成,SGL段又由若干个SGL描述符组成,所以SGL描述符是SGL数据结构的基本单位。

目前定义的SGL描述符有6种:

5.4 NVM指令

NVMe控制器读写的最小单元是LB,层次图如下:
fpga nvme 寄存器_第24张图片

 NVM指令与Admin指令结构完全相同,也是通过Dword0中的8位操作码来定义不同指令。

  1. SGL 数据描述符,用来索引数据块地址,host内存;

  2. SGL 垃圾数据描述符,用来索引无用数据;

  3. SGL 段描述符,用来索引下一个SGL段;

  4. SGL 最后一个段描述符,用来索引最后一个SGL段;

  5. keyed SGL 数据描述符;

  6. Transport SGL 数据描述符;
    fpga nvme 寄存器_第25张图片

     

    在上面SGL例子中,共有3个SGL段,用到了4种SGL描述符。Host需要往SSD中读取13KB的数据,其中真正只需要11KB数据,这11KB的数据需要放到3个大小不同的内存中,分别是:3KB,4KB和4KB。

    4.3.3 比较PRP与SGL

    无论是PRP还是SGL,本质都是描述内存中的一段数据空间,这段数据空间在物理上可能连续的,也可能是不连续的。Host在命令中设置好PRP或者SGL,告诉Controller数据源在内存的什么位置,或者从闪存上读取的数据应该放到内存的什么位置。

    SGL和PRP本质的区别在于,一段数据空间,对PRP来说,它只能映射到一个个物理页,而对SGL来说,它可以映射到任意大小的连续物理空间,具有更大的灵活性,也能够描述更大的数据空间。如下图:
    fpga nvme 寄存器_第26张图片

     

    5. NVMe协议定义的命令

    5.0 命令执行过程

    命令由host提交到内存中的SQ队列中,更新TDBxSQ后,NVMe控制器通过DMA的方式将SQ中的命令(怎么取,如何取,取多少,因设计而异)取到控制器缓冲区,执行命令;执行完成后,根据执行状态,组装完成命令,仍然通过DMA的方式将完成命令写入内存CQ的队列中;NVMe控制器通过MSI-X中断方式通知host已完成命令;最后,host处理CQ命令,更新控制器中HDBxCQ,标识着命令真正完成。

    5.1 命令分类

    命令分为Admin指令与NVM指令(I/O指令)。

    Admin指令只能提交到Admin Controller中,主要负责管理NVMe控制器,也包含对NVM的一些控制指令。

    NVM 指令只能提交到I/O Controller中,主要负责完成数据的传输。

    在1.0e版本中,Admin指令有15条(3条与NVM相关),NVM指令有6条;在1.3d版本中,Admin指令有15条(3条与NVM相关),NVM指令有11条。

    5.2 命令通用格式

    命令均为64字节,具有相同的格式,某些字段根据命令的不同有不同的定义。

    完成命令同样具有相同的格式,某些字段根据命令的不同有不同的定义。

    5.3 Admin 指令

    Admin指令与NVM指令根据放置的的队列组(Queue Pair)来区分,Admin指令在Admin CQ与SQ里,NVM指令在I/O CQ与SQ里。

    通过Dword0中的8位操作码定义不同指令,注意并不是绝对的顺序增加(eg,没有03h)。每一种指令都对应有其完成命令,通过SQID(提交队列ID)+CID(命令ID)唯一标识完成的命令。

    Note:

  7. Admin队列是通过配置ASQ等寄存器创建的

  8. 先创建CQ再创建SQ

6 控制器结构

控制器从功能上可以分为三类,I/O、Admin和Discovery
fpga nvme 寄存器_第27张图片

 

在实现过程中,Admin 控制器只有一个,负责管理控制器及其他控制功能。控制器只是抽象的概念,应用于具体的实现中,可能是一个具体的模块,也可能多个模块。

控制器主要的作用是实现对NVMe定义命令的翻译,从而实现数据传输、状态控制等功能。

6.1 命令执行过程

1. host将命令(1条或者多条)写入提前分配好的SQ中;

2. 更新对应SQ的DB寄存器;

3. NVMe控制器取SQ中命令(通过HDB和TDB可以判断是否有未完成命令);

4. NVMe控制器执行命令;

5. NVMe 控制器在命令完成后,将完成命令(可能执行成功,也可能失败,但都会返回完成命令)写入host内存SQ对应的CQ中;

6. NVMe 控制器根据实现的中断方式,提醒host命令已完成;

7. host响应中断,处理完成命令;

8. host 更新对应CQ的DB寄存器。
fpga nvme 寄存器_第28张图片

 

6.2 重启(Reset)

6.2.1 Controller level

Controller重启可能发生在PCIe总线重启、PCI重启、控制器CC.EN从1到0重启。当重启发生时:

  1. 所有的I/O SQ和CQ都被删除;

  2. 所有未完成的指令(Admin和I/O)应该执行撤销操作;

  3. Controller处于idele状态,CSTS.RDY清0;

  4. AQA、ASQ、ACQ不受影响。

重启后,host操作:

  1. 更新寄存器状态;

  2. 将CC.EN置1;

  3. 等待CSTS.RDY置1;

  4. 使用Admin命令配置Controller;

  5. 创建I/O CQ和SQ;

  6. 执行正常的I/O指令。

6.2.2 Queue level

队列水平的重启,即,删除该队列,再重新创建一个新队列。删除队列的时候,host应该保证队列处于idle状态(所有命令均已完成——接收到了完成命令),否则的话,可能会导致CQ接收不到提交命令的完成命令。

6.3 中断

在Controller完成SQ命令后,根据执行状态,将结果组装成完成命令写入CQ中,Controller通过中断机制通知Host处理完成命令。

NVMe协议中支持的中断方式有4种,pin-based、Single MSI、Multi-message MSI和MSI-X,协议推荐采用MSI-X中断方式,能够支持更多的中断向量(2K)。

MSI-X允许每一个CQ发送自己的中断信息(相比于发一条中断信息提醒全部CQ队列有很大的优势)。在产生MSI-X中断信息前,需要检查该中断在相应寄存器种不被屏蔽。

6.4 Controller初始化

Controller的初始化过程:

  1. 设置PCI和PCIe寄存器;

  2. 等待CSTS.RDY变为;

  3. 配置AQA、ASQ、ACQ寄存器;

  4. 配置CC寄存器;

  5. 将CC.EN置1;

  6. 等待CSTS.RDY置1

  7. Host通过Identify命令,确定Controller的数据结构、确定Namespace的数据结构;

  8. Host通过get features(协议中是set features,待研究)获取I/O SQ和CQ信息,然后配置中断机制;

  9. Host分配适当的I/O CQ、SQ队列;

  10. 如果Host希望获取Controller的错误或健康信息,可以添加异步事件请求命令。

Controller 关机

正常关机:

  1. Host停止提交新的I/O命令,但允许未完成的命令继续完成;

  2. Host删除所有I/O SQ,删除所有SQ队列后,所有未完成的命令将被撤销;

  3. Host删除所有I/O CQ;

  4. Host将CC.SHN置01b,表示正常关机;关机程序完成时,将CSTS.SHST置10b。

突然关机:

  1. Host停止提交新的I/O命令;

  2. Host将CC.SHN置10b,表示突然关机;关机程序完成时,将CSTS.SHST置10b

6.5 host端命令实例

6.5.1 创建命令
fpga nvme 寄存器_第29张图片

 6.5.2 处理完成命令
fpga nvme 寄存器_第30张图片

 

6.6 NVMe与PCIe交互实例(分析包结构)

以Host发出read命令为例。

  1. Host准备了一个Read命令给SSD:

     

    分析该包,Host需要从起始LBA 0x20E0448(SLBA)上读取128个DWORD (512字节)的数据,读到哪里去呢?PRP1给出内存地址是0x14ACCB000。这个命令放在编号为3的SQ里 (SQID = 3),CQ编号也是3 (CQID = 3)

  2. Host通过写SQ的Tail DB,通知Controller来取命令:

     上图中,上层是NVMe层,下层是PCIe传输层的TLP。Host想往SQ Tail DB中写入的值是5。PCIe是通过一个Memory Write TLP来实现Host写CQ的Tail DB的。该Tail DB寄存器映射在Host的内存地址为F7C11018,由于NVMe 的寄存器映射到了Host内存中,所以可以根据这个地址写入寄存器值
    SSD收到通知,去Host端的SQ中取指
    fpga nvme 寄存器_第31张图片

     

    PCIe是通过发一个Memory Read TLP到Host的SQ中取指的。可以看到,PCIe需要往Host内存中读取16个DWORD的数据(一个NVMe指令大小),

  3. SSD执行读命令,把数据从闪存中读到缓存中,然后把数据传给Host:
    fpga nvme 寄存器_第32张图片

     

    SSD是通过Memory write TLP 把Host命令所需的128个DWORD数据写入到Host命令所要求的内存中去。SSD每次写入32个DWORD,一共写了4次。

  4. SSD往Host的CQ中返回状态:

     

    SSD是通过Memory write TLP 把16个字节的命令完成状态信息写入到Host的CQ中。

  5. SSD采用中断的方式告诉Host去处理CQ

     

上图使用的是MSI-X中断方式。这种方式将中断信息和正常的数据信息一样,PCIe打包把中断信息告知Host。SSD还是通过Memory Write TLP把中断信息告知Host,这个中断信息长度是1DWORD。

  1. Host处理相应的CQ

  2. Host处理完相应的CQ后,需要更新SSD端的CQ Head DB告知SSD处理

完成:

 

Host还是通过Memory Write TLP更新SSD端的CQ Head DB。

该过程完整的包流程如下:
fpga nvme 寄存器_第33张图片

 

7. NVMe features

7.1 固件(Firmware)更新过程

1. 将固件下载到Controller中(使用 Firmware Image Download命令);

2. Host提交Firmware Activate命令(也可以激活之前版本的Controller镜像);

3. Controller reset;

4. reset完成后,Host重新初始化Controller,包括Host重新分配I/O队列,与reset步骤相同。

7.2 元数据(Metadata)传输

元数据的使用并没有强制规定,最经常的使用方法是用做端到端数据的保护信息。有两种传输元数据的方式,一种可以作为LB数据块的一部分,如下图:
fpga nvme 寄存器_第34张图片

 

7.3 端到端的数据保护

端到端,一端指主机的内存空间,一端指闪存空间(NVM)。数据传输的两个环节如下图:

 fpga nvme 寄存器_第35张图片

 

数据在PCIe上传输的时候,由于信道噪声的存在(说白了就是存在干扰),可能导致数据出错;另外,Controller闪存之间,数据也可能发生错误。采用元数据进行数据的保护是最常用的一种手段。

充当保护数据角色的元数据结构如下:
fpga nvme 寄存器_第36张图片

 其中,Guard为16bit的CRC校验码,Application Tag与LBAT相关,Reference Tag将用户数据和地址(LBA)相关联。下图为以512bytes的数据块为例:
fpga nvme 寄存器_第37张图片

 

那么按照排列组合,共有四种保护情况(1带2带、1不带2不带、1带2不带、1不带2带)。但由于协议中控制保护信息的只有两个字段(1. 是否采用保护 2. PRACT位),只有三种情况,如下图(是以写命令为例,读命令相同):

fpga nvme 寄存器_第38张图片

 

 

你可能感兴趣的:(SSD,android,FPGA/ASCI,fpga开发)