概述
NVM Express 或非易失性内存主机控制器接口(其先前名称为 NVMHCI,现在缩写为 NVMe)是一种基于主机的软件接口,旨在通过 PCIe 结构与固态存储设备进行通信。目前的 Synopsys NVMe 验证 IP (VIP) 是一个综合测试工具,由两个主要子系统组成——第一个是 SVC(系统验证组件),第二个是 SVT(系统验证技术)。SVC 层与实际的 NVMe(和 PCIe 等)协议层相关联。SVT 为 UVM 和其他方法(如 VMM 和 OVM)提供了验证方法接口。
尽管VIP支持多个版本的VIP,但我们最初将与版本无关,更多地谈论协议的一般情况,以便提供协议及其在VIP中的支持的10,000'视图。未来的讨论将更深入地探讨 NVMe 的特定细节和验证 IP 的功能。
您可以在此处了解有关 Synopsys 针对 NVMe 的 VC 验证 IP 的更多信息。
NVMe简介
与根和端点基本相等的PCIe不同,NVMe的非对称关系更接近其他存储协议(例如SATA,光纤通道)。
NVMe 命令(例如识别、读取、写入)在主机上启动并转换为 NVMe 请求,然后将其附加到主机内存中的特定提交队列。将命令插入队列后,主机将写入控制器上的每个队列门铃寄存器(控制器位于 PCIe 端点上)。此门铃写入唤醒控制器,然后控制器探测队列中是否有新请求。它读取队列条目,执行命令(可能从主机内存读取数据缓冲区),最后将完成追加到完成队列中,然后通过中断通知主机。主机唤醒,从队列中弹出该完成操作,并将结果返回给用户。
使用两种主要类型的队列:
管理队列 – 这些队列用于配置和管理控制器的各个方面。每个控制器只有一对管理队列。
I/O 队列 – 这些队列用于移动 NVMe 协议特定的命令(例如读取、写入)。每个控制器最多可以有 64K 个 I/O 队列。
队列的每个队列都有尾(生产者)和头(使用者)指针。尾部指针指向要向其添加条目的下一个可用条目。在生产者将条目添加到队列后,他会递增尾指针(考虑到一旦它到达队列的末尾,它将回绕到零 - 它们都是循环队列。如果头指针和尾指针相等,则队列被视为空。
使用者使用她的头指针来确定从队列中开始读取的位置;在检查尾指针并确定队列为非空后;她将在阅读每个条目后递增头部指针。
提交队列的尾指针由主机管理;将一个或多个条目推入队列后,尾指针(递增)通过提交队列门铃寄存器写入控制器。控制器维护头部指针,并在收到尾部指针更新的通知后开始读取队列。它可以继续读取队列,直到为空。当它使用条目时,头部指针会更新,并通过完成队列条目发送回主机(见下文)。
同样,完成队列的尾部由控制器管理,但与主机不同,控制器仅维护尾部指针的私有副本。存在新完成队列条目的唯一指示是可以轮询的完成队列条目中的位。一旦主机确定某个条目可用,它将读取该条目并更新头指针。控制器通过主机写入完成队列门铃寄存器来通知头指针更新。
请注意,NVMe 控制器完成的所有工作都由控制器本身拉入或推出该控制器。主持人只是将工作放入主机内存并按门铃(“您有一个提交条目要处理”)。稍后,它从完成队列中收集结果,再次按门铃(“我已完成这些完成条目”)。因此,控制器可以自由地与主机并行工作;例如,不需要对完成进行排序 - 控制器可以对它的工作进行排序,无论如何它感觉就像。
那么,我们在主机和控制器之间来回移动的这些队列条目是什么?
第一个是提交队列条目,这是一种 64 字节的数据结构,主机使用它将命令请求传输到控制器:
字节 | 描述 |
63:40 | 命令字 15-10 (CDW15-10):6 个命令特定信息的字。 |
39:32 | PRP 条目 2 (PRP2): 指向 PRP 条目或缓冲区或(与 PRP1 一起)SGL 段的指针。 |
31:24 | PRP 条目 1 (PRP1): 指向 PRP 条目、缓冲区或(与 PRP2 结合使用)SGL 段的指针。 |
23:16 | 元数据指针 (MPTR): 此字段包含 SGL 段或包含元数据的连续缓冲区的地址。 |
15:08 | 保留 |
07:04 | 命名空间标识符 (NSID): 此字段指定此命令适用的命名空间 ID。 |
03:00 | 命令字 0 (CDW0): 此字段对所有命令都是通用的,包含命令操作码 (OPC)、命令标识符 (CID) 和各种控制位。 |
每个命令一个提交队列条目排队到相应的管理员或 I/O 队列。操作码指定要执行的特定命令,命令标识符是命令的唯一标识符(与提交队列 ID 结合使用时)。
除了使用队列条目来回移动信息外,主机还可以在主机内存中分配数据缓冲区。这些缓冲区可以是连续的(由其基址和长度定义),也可以是分布在内存周围的一组数据缓冲区。后者使用称为PRP列表和分散-聚集列表(SGL)的数据结构来定义其位置。当主机需要将这些缓冲区移入/移出控制器时(例如,用于读取或写入命令),它将在主机内存中分配适当的数据结构,并在将队列条目写入该控制器之前,将这些缓冲区的这些数据结构的信息写入上述 PRP1 和 PRP2 字段。
元数据(例如端到端数据保护)也可以通过两种方式与 NVMe 命令一起传递。它可以与数据一起在带内发送(即它与每个扇区的数据相邻),也可以带外发送(即它作为单独的数据流发送)。在SCSI术语中,它们分别称为数据完整性字段(DIF)和数据完整性扩展(DIX)。后者使用上述元数据指针。我们将在以后的剧集中详细讨论这一点。
当我们实际向控制器上的非易失性存储写入/读取时,我们会写入命名空间。 在其他存储技术中,还有其他类似的容器 - 例如SCSI中的LUN。命名空间对于控制器是唯一的,也可以在多个控制器之间共享。无论如何,请求中的命名空间 ID 字段确定要访问的命名空间。某些命令不使用命名空间字段(然后设置为 0),其他命令可能需要处理所有命名空间(然后将命名空间 ID 设置为 0xffff_ffff)。
在完成端,有一个类似的数据结构,即完成队列条目:
字节 | 描述 |
15:12 | 命令特定信息:返回信息的一个字。(并不总是使用。 |
11:8 | 保留 |
7:6 | 提交队列 ID: 发送关联命令的提交队列。(16 位) |
需要注意的几点:
提交队列 ID 需要显式显示在完成队列条目中,因为多个提交队列可以共享一个完成队列。
如上所述,提交队列头指针由控制器维护。当控制器更新该头指针时,它需要将这些更新发送回主机,允许主机重用这些队列条目,直到并包括新头)。
状态字段实际上由几个子字段组成,并且有大量的错误状态代码。
相位标记提供了一个可以轮询的位(如果主机驱动程序软件愿意,而不是纯粹的中断驱动)。如果在检查队列的尾条目时阶段标记发生更改,则可以推断有新条目要检查。
为了介绍可用的命令,我们将在此处列出其中的一些命令:
创建管理队列 – 实际上本身不是一个命令,这是对 NVMe 控制器的寄存器写入,用于定义管理员提交和完成队列。NVMe 寄存器集具有少量用于基本配置、控制和状态的寄存器。
创建 I/O 队列 – 用于创建提交和完成队列的命令。
识别 – 几个命令允许请求有关控制器和附加到控制器的命名空间的各种信息。
设置/获取功能 – 设置 NVMe 特定“功能”(例如可配置设施)的方法。
读/写 – 这就是我们真正在这里的原因:从/向附加到控制器的命名空间读取和写入数据。
由于一个活跃的团队正在推动开发,NVMe 一直在添加更多的命令和功能。
审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !