NVMe控制器寄存器
- 1 内存映射寄存器
- 1.1 Offset 00h: CAP – Controller Capabilities
- 1.2 Offset 08h: VS – Version
- 1.3 Offset 0Ch: INTMS – Interrupt Mask Set
- 1.4 Offset 10h: INTMC – Interrupt Mask Clear
- 1.5 Offset 14h: CC – Controller Configuration
- 1.6 Offset 1Ch: CSTS – Controller Status
- 1.7 Offset 20h: NSSR – NVM Subsystem Reset
- 1.8 Offset 24h: AQA – Admin Queue Attributes
- 1.9 Offset 28h: ASQ – Admin Submission Queue Base Address
- 1.10 Offset 30h: ACQ – Admin Completion Queue Base Address
- 1.11 Offset (1000h + ((2y) * (4 << CAP.DSTRD))): SQyTDBL – Submission Queue y Tail Doorbell
- 1.12 Offset (1000h + ((2y + 1) * (4 << CAP.DSTRD))): CQyHDBL – Completion Queue y Head Doorbell
- 1.13 Doorbell寄存器组织结构
- 2 I/O空间寄存器(可选)
本文属于《 NVMe协议基础系列教程》之一,欢迎查看其它文章。
1 内存映射寄存器
NVMe控制器寄存器,位于配置空间BAR0与BAR1,所映射的内存空间中。
在PCI Header中,有如下定义:
BAR0为低32位,BAR1为高32位,一起组合为64位内存地址,表示PCIe设备内存空间的基址。
NVMe控制器寄存器,就位于该内存空间中,并且host访问这些寄存器,应按原始宽度或32位对齐来访问。
NVMe控制器寄存器,定义如下:
由于NVMe控制器寄存器,位于PCIe设备内存空间中,因此上表中Start和End列,是相对于PCIe设备内存空间基址的偏移。
NVMe协议中,常见术语缩写,如下所示:
- RO:只读
- RW:读写
- R/W:读写,读取的值可能不是最后写入的值
- RWC:读/写’1’,来清除寄存器
- RWS:读/写’1’,来设置寄存器
- Impl Spec:实现细节,控制器可以自由选择其实现
- HwInit:默认状态,取决于设备和系统配置。该值在复位时初始化,例如,在集成设备的情况下,由平台BIOS在扩展ROM上初始化。
1.1 Offset 00h: CAP – Controller Capabilities
Controller Capabilities寄存器,共8字节,表示控制器对Host软件的基础能力。
Bit | Type | Reset | Description |
---|---|---|---|
63:56 | RO | 0h | 保留 |
55:52 | RO | Impl Spec | Memory Page Size Maximum (MPSMAX):该字段表示,控制器支持的最大主机内存页大小。 最大内存页大小为(2 ^ (12 + MPSMAX)),主机不能在CC.MPS中配置,大于此值的内存页大小。 |
51:48 | RO | Impl Spec | Memory Page Size Minimum (MPSMIN):该字段表示,控制器支持的最小主机内存页大小。 最小内存页大小为(2 ^ (12 + MPSMIN)),主机不能在CC.MPS中配置,小于此值的内存页大小。 |
47:45 | RO | 0h | 保留 |
44:37 | RO | Impl Spec | Command Sets Supported (CSS):该字段表示,控制器支持的l/O命令集,至少支持一个命令集。 这个字段有点重要,如果一个位被设置为’1’,则支持相应的l/O命令集; 如果一个位被清除为’0’,则不支持相应的l/O命令集。 |
36 | RO | Impl Spec | NVM Subsystem Reset Supported (NSSRS):该字段表示,控制器是否支持NVM Subsystem Reset特性。 如果控制器支持NVM Subsystem Reset特性,该字段设置为“1”; 如果控制器不支持NVM子系统复位特性,该字段清除为“0”。 |
35:32 | RO | Impl Spec | Doorbell Stride (DSTRD):每个提交队列和完成队列Doorbell寄存器的大小是32位。 这个寄存器指示,Doorbell寄存器之间的间隔,间隔被指定为(2 ^ (2 + DSTRD))字节。 值0h表示4字节的间隔,其中Doorbell寄存器被打包,每个寄存器之间没有保留空间。 |
31:24 | RO | Impl Spec | Timeout (TO):这是主机软件等待CSTS.RDY从以下过渡的超时时间: a)CC.EN从’ 0 ‘转换到’ 1 ‘; b)CC.EN从’ 1 ‘转换到’ 0 '; 这种最坏的情况,可能发生在突然关机或激活新固件镜像等事件之后;预计典型时间会短得多。该字段的单位是500毫秒。 |
23:19 | RO | 0h | 保留 |
18:17 | RO | Impl Spec | Arbitration Mechanism Supported (AMS):该字段的位是重要的,表示控制器支持的可选仲裁机制。如果一个位设置为“1”,则控制器支持相应的仲裁机制。 没有列出轮转仲裁机制,因为所有控制器都应支持该仲裁机制。 |
16 | RO | Impl Spec | Contiguous Queues Required (CQR): 如果控制器要求,I/O提交队列和I/O完成队列,在物理上是连续的,则该字段设置为’ 1 ‘。 如果控制器支持物理上,不连续的I/O提交队列和I/O完成队列,该字段清空为“0”。 如果这个字段设置为’ 1 ‘,那么,在创建I/O提交队列和I/O完成队列命令中的物理连续位(CDW11.PC),应该设置为’ 1 '。 |
15:00 | RO | Impl Spec | Maximum Queue Entries Supported (MQES):该字段表示,控制器支持的最大单个队列长度。 这个值适用于,主机软件可能创建的每一个I/O提交队列和I/O完成队列。这是一个基于0的值。最小值是1h,表示两个条目。 |
1.2 Offset 08h: VS – Version
Version寄存器,共4字节,表示控制器支持的NVM Express规范主和从版本。
上面两个字节,表示主版本号;下面两个字节,表示从版本号。
例如:版本3.12将表示为00030102h。本规范的有效版本是1.0。
Bit | Type | Reset | Description |
---|---|---|---|
31:16 | RO | 0001h | Major Version Number (MJR):主版本号为1 |
15:00 | RO | 0000h | Minor Version Number (MNR):次版本号为0 |
1.3 Offset 0Ch: INTMS – Interrupt Mask Set
Interrupt Mask Set寄存器,共4字节。当使用基于引脚的中断、单消息MSI或多消息MSI时,该寄存器用于屏蔽中断。
- 当使用MSI-X时,应该使用定义为MSI-X一部分的中断掩码表来屏蔽中断。
- 当为MSI-X配置时,主机软件不得访问此寄存器;为MSI-X配置的任何访问都是未定义的。
Bit | Type | Reset | Description |
---|---|---|---|
31:00 | RW1S | 0h | Interrupt Vector Mask Set (IVMS):这个字段的位是重要的。 如果将’ 1 ‘写入到比特位,则对应的中断向量被屏蔽;将’ 0 ‘写入到一个比特位没有任何影响。 当读取时,该字段返回控制器内当前的中断掩码值(不是该寄存器的值);如果某个比特位的值为’ 1 ‘,则对应的中断向量被屏蔽;如果某个比特位的值为’ 0 ',则对应的中断向量不会被屏蔽。 |
1.4 Offset 10h: INTMC – Interrupt Mask Clear
Interrupt Mask Clear寄存器,共4字节。当使用基于引脚的中断、单消息MSI或多消息MSI时,该寄存器用于解除屏蔽中断。
- 当使用MSI-X时,应该使用定义为MSI-X一部分的中断掩码表来解除中断掩码。
- 当为MSI-X配置时,主机软件不得访问此寄存器;为MSI-X配置的任何访问都是未定义的。
Bit | Type | Reset | Description |
---|---|---|---|
31:00 | RW1S | 0h | Interrupt Vector Mask Set (IVMS):这个字段的位是重要的。 如果将’ 1 ‘写入到比特位,则对应的中断向量被屏蔽;将’ 0 ‘写入到一个比特位没有任何影响。 当读取时,该字段返回控制器内当前的中断掩码值(不是该寄存器的值);如果某个比特位的值为’ 1 ‘,则对应的中断向量被屏蔽;如果某个比特位的值为’ 0 ',则对应的中断向量不会被屏蔽。 |
1.5 Offset 14h: CC – Controller Configuration
Controller Configuration寄存器,共4字节。该寄存器修改控制器的设置。
主机软件应将Arbitration Mechanism(CC.AMS)、Memory Page Size(CC.MPS)和Command Set(CC.CSS)设置为有效值,然后通过将CC.EN设置为’ 1 '来启用控制器。
Bit | Type | Reset | Description |
---|---|---|---|
31:24 | RO | 0h | 保留 |
23:20 | RW | 0h | I/O Completion Queue Entry Size (IOCQES):该字段定义了,用于所选I/O命令集的I/O Completion Queue条目的大小。Identify Controller数据结构,为每个I/O命令集指定了,该字段的所需值和最大值。该值以字节为单位,以2的幂(2^n)指定。 |
19:16 | RW | 0h | I/O Submission Queue Entry Size (IOSQES):该字段定义了,用于所选I/O命令集的I/O Submission Queue条目的大小。Identify Controller数据结构,为每个I/O命令集指定了,该字段的所需值和最大值。该值以字节为单位,以2的幂(2^n)指定。 |
15:14 | RW | 0h | Shutdown Notification (SHN):该字段用于在关机发生时,启动关机处理(即,预期出现掉电条件)。 对于正常的关机通知,期望控制器有时间来处理关机通知。对于突然关机通知,主机可能没有等到关机处理完成才失去电源。 关闭通知的值定义为: 这个字段应该在,任何电源关闭条件之前和PCI电源管理状态的任何改变之前,由主机软件编写。建议在热重启之前,也写入该字段。要确定关闭处理,何时完成,请参考CSTS.SHST。 |
13:11 | RW | 0h | Arbitration Mechanism Selected (AMS):该字段用于选择要使用的仲裁机制。 只有当EN被清除为“0”时,这个值才会改变。主机软件只能将此字段设置为CAP.AMS中所示的支持的仲裁机制。如果将该字段设置为不支持的值,则行为未定义。 |
10:07 | RW | 0h | Memory Page Size (MPS):这个字段,表示主机内存页面大小,内存页面大小为(2 ^ (12 + MPS))。 因此,最小的主机内存页面大小是4KB,最大的主机内存页面大小是128MB。 主机软件设置的值,应该是CAP.MPSMAX和CAP.MPSMIN字段所表示支持的值。该字段描述了,用于PRP条目大小的值。只有当EN被清除为“0”时,该字段才会被修改。 |
06:04 | RW | 0h | I/O Command Set Selected (CSS):该字段指定,用于I/O Submission Queue的I/O命令集。 主机软件只能选择支持的I/O命令集,如CAP.CSS所示。该字段只能在,控制器被禁用时更改(CC.EN被清除为“0”)。所选I/O命令集应用于所有I/O Submission Queue。 |
03:01 | RO | 0h | 保留 |
00 | RW | 0h | Enable (EN): a)当设置为’ 1 ‘时,控制器根据Submission Queue Tail doorbell写的命令进行处理。 b)当清除为’ 0 ‘时,控制器将不会处理命令,也不会将完成队列条目发送到Completion Queues。 c)当这个字段从 1 转换到0 时,控制器被重置(称为Controller Reset)。重置删除所有I/O Submission Queues和I/O Completion Queues,重置Admin Submission Queue和Completion Queues,并使硬件处于空闲状态。 复位不影响PCIe寄存器或Admin Queue寄存器(AQA、ASQ或ACQ)。在本节中定义的,所有其他控制器寄存器和控制器内部状态(例如,不跨电源状态持久化的特征值),将重置为默认值。 控制器应确保,在复位操作之前,已将相应的完成队列条目发送到I/O Completion Queue的命令,没有数据丢失。 d)当该字段被清除为“0”时,一旦控制器准备重新启用,控制器将把CSTS.RDY位清除为“0”。 当该字段设置为’ 1 ‘时,控制器在准备处理命令时,将CSTS.RDY设置为’ 1 '。 当CSTS.RDY为 1 时,将该字段从0 设置为1 ,或者当CSTS.RDY为0 时将该字段从1 设置为0 ,将产生未定义的结果。管理队列寄存器(AQA, ASQ和ACQ),只有在EN被清除为“0”时才会被修改。 |
1.6 Offset 1Ch: CSTS – Controller Status
Controller Status寄存器,共4字节。
Bit | Type | Reset | Description |
---|---|---|---|
31:05 | RO | 0h | 保留 |
04 | RW1C | HwInit | NVM Subsystem Reset Occurred (NSSRO): 如果最后一次NVM Subsystem Reset,发生在NVM子系通电时,则此字段的初始值为“1”。 此字段的初始值为“0”,这是由于NVM子系统通电而导致的NVM Subsystem Reset。 该字段,仅在控制器支持NVM Subsystem Reset特性时有效,如CAP.NSSRS设置为’ 1 '。 如果NVM Subsystem Reset导致激活新的固件镜像,此字段的重置值为“0”。 |
03:02 | RO | 0h | Shutdown Status (SHST):此字段表示由主机设置CC.SHN字段,而启动的关机处理的状态。 关机状态的值,定义如下:在执行关机操作(CSTS.SHST设置为10b)后,要在控制器上执行命令,需要复位(CC.EN清除为“0”)。如果主机软件向控制器提交命令而不发出复位,则该行为是未定义的。 |
01 | RO | HwInit | Controller Fatal Status (CFS):当发生无法在适当的完成队列中传递的,致命控制器错误时,此字段设置为’ 1 '。 当没有发生致命控制器错误时,该字段被清除为“0”。 当控制器初始化过程中,检测到致命错误时,该字段的重置值为“1”。 |
00 | RO | 0h | Ready (RDY):CC.EN设置为“1”后,当控制器准备好接受Submission Queue Tail doorbell写入时,此字段设置为“1”。 当CC.EN清除为“0”时,此字段应清除为“零”。 在CC.EN位设置为“1”之后,此字段设置为“1”之前,不得向控制器提交命令。不遵守此要求会产生未定义的结果。 在将CC.EN从以前的值“0”设置为“1”后,主机软件应等待至少CAP.TO秒,以便将该字段设置为“1”。 |
1.7 Offset 20h: NSSR – NVM Subsystem Reset
NVM Subsystem Reset寄存器,共4字节。
此可选寄存器,为主机软件提供初始化NVM Subsystem Reset的能力。对该寄存器的支持由NVM Subsystem Reset Supported(CAP.NSSRS)字段的状态表示。如果寄存器不被支持,则保留寄存器占用的地址范围。
Bit | Type | Reset | Description |
---|---|---|---|
31:00 | RW | 0h | NVM Subsystem Reset Control (NSSRC):将值4E564D65h (“NVMe”)写入该字段,以初始化NVM Subsystem Reset。 写入任何其他值,对NVM子系统的操作,都没有功能上的影响。 该字段在读取时,返回值为0h。 |
1.8 Offset 24h: AQA – Admin Queue Attributes
Admin Queue Attributes寄存器,共4字节。
这个寄存器定义了Admin Submission Queue和Admin Completion Queue的属性。Admin Submission Queue和Admin Completion Queue的队列标识符是0h。Admin Submission Queue的优先级由所选择的仲裁机制决定。
Admin Submission Queue和Admin Completion Queue需要在物理上连续的内存中。
注意:
建议引导操作使用UEFI。在低内存环境中(比如legacy BIOS环境中的Option ROMs),可能没有足够的可用内存来分配必要的Submission和Completion队列。在这些类型的条件下,控制器的低内存操作是特定于供应商的。
Bit | Type | Reset | Description |
---|---|---|---|
31:28 | RO | 0h | 保留 |
27:16 | RW | 0h | Admin Completion Queue Size (ACQS):定义管理完成队列的大小,最小是2个条目,最大是4096个条目。这是一个基于0的值。 |
15:12 | RO | 0h | 保留 |
11:00 | RW | 0h | Admin Submission Queue Size (ASQS):定义管理提交队列的大小,最小是2个条目,最大是4096个条目。这是一个基于0的值。 |
1.9 Offset 28h: ASQ – Admin Submission Queue Base Address
Admin Submission Queue Base Address寄存器,共8字节。
这个寄存器定义了,管理提交队列的内存基地址。
Bit | Type | Reset | Description |
---|---|---|---|
63:12 | RW | Impl Spec | Admin Submission Queue Base (ASQB):管理提交队列的64位物理地址,此地址应与内存页对齐(基于CC.MPS中的值)。 所有的管理命令,包括I/O提交队列和I/O完成队列的创建,都必须提交到这个队列。 |
11:00 | RO | 0h | 保留 |
1.10 Offset 30h: ACQ – Admin Completion Queue Base Address
Admin Completion Queue Base Address寄存器,共8字节。
此寄存器定义了,管理完成队列的内存基地址。
Bit | Type | Reset | Description |
---|---|---|---|
63:12 | RW | Impl Spec | Admin Completion Queue Base (ACQB):管理完成队列的64位物理地址,此地址应与内存页对齐(基于CC.MPS中的值)。 所有提交到Admin Submission Queue的命令的完成队列条目,都应该提交到这个完成队列。该队列总是关联到中断向量0。 |
11:00 | RO | 0h | 保留 |
1.11 Offset (1000h + ((2y) * (4 << CAP.DSTRD))): SQyTDBL – Submission Queue y Tail Doorbell
Submission Queue y Tail Doorbell寄存器,共4字节。
这个寄存器定义了doorbell寄存器,用于更新Submission Queue y的尾部条目指针(Tail),y的值等于队列标识符。这向控制器表明,已经提交了新的命令待处理。
- Host不应该读取doorbell寄存器,如果读取doorbell寄存器,则返回特定于供应商的值。
- 向不存在的Submission Queue Tail Doorbell写入,结果未定义。
Bit | Type | Reset | Description |
---|---|---|---|
31:16 | RO | 0 | 保留 |
15:00 | RW | 0h | Submission Queue Tail (SQT):表示Submission Queue尾部条目指针的新值,该值将覆盖先前Submission Queue尾部条目指针的值。 上一次SQT写和当前SQT写之间的差值,表示添加到Submission Queue的命令数量。 注意:需要考虑,提交队列翻转。 |
1.12 Offset (1000h + ((2y + 1) * (4 << CAP.DSTRD))): CQyHDBL – Completion Queue y Head Doorbell
Completion Queue y Head Doorbell寄存器,共4字节。
这个寄存器定义了doorbell寄存器,用于更新Completion Queue y的头部条目指针(Head),y的值等于队列标识符。这表示已被Host软件处理完毕的,完成队列条目。
- Host不应该读取doorbell寄存器,如果读取doorbell寄存器,则返回特定于供应商的值。
- 写入一个不存在的Completion Queue Head Doorbell,有未定义的结果。
- Host软件应该确保,它继续处理Completion Queues中的完成队列条目,而不管在特定或任何Submission Queue中是否有可用条目。
Bit | Type | Reset | Description |
---|---|---|---|
31:16 | RO | 0 | 保留 |
15:00 | RW | 0h | Completion Queue Head (CQH):表示Completion Queue头部条目指针的新值,该值将覆盖先前Completion Queue头部条目指针的值。 上一次CQH写和当前CQH写之间的差值,表示Completion Queue中可供控制器重用的条目数量。 注意:需要考虑,完成队列翻转。 |
1.13 Doorbell寄存器组织结构
每个NVMe控制器有一个Admin Queue。因此,协议规定,Admin Queue的Doorbell,默认固定从1000h开始:
- 1000h ~ 1003h,Submission Queue 0 Tail Doorbell (Admin)
- 1000h + (1 * (4 << CAP.DSTRD)) ~ 1003h + (1 * (4 << CAP.DSTRD)),Completion Queue 0 Head Doorbell (Admin)
CAP.DSTRD,就是Controller Capabilities的Doorbell Stride (DSTRD)寄存器,表示Doorbell寄存器之间的间隔,间隔被指定为(2 ^ (2 + DSTRD))字节。DSTRD=0h表示4字节的间隔,其中Doorbell寄存器被打包,每个寄存器之间没有保留空间。
每个NVMe控制器最多64K个I/O Queue。Admin Submission Queue和Admin Completion Queue的队列标识符均为0h;因此,IO Queue标识符从1开始,一直到y,y最大可以为64k。
因此,协议规定,I/O Queue的Doorbell偏移范围为:
- 从1000h + (2 * (4 << CAP.DSTRD)) ~ 1003h + (2 * (4 << CAP.DSTRD))开始,Submission Queue 1 Tail Doorbell
- 至1000h + ((2y + 1) * (4 << CAP.DSTRD)) ~ 1003h + ((2y + 1) * (4 << CAP.DSTRD))结束,Completion Queue y Head Doorbell
2 I/O空间寄存器(可选)
Index/Data Pair registers(索引/数据对寄存器),是一对I/O空间寄存器。
Index/Data Pair registers,为Host软件提供了一种基于I/O空间寄存器,来访问NVMe内存映射寄存器的机制。如果支持此特性的话,这些寄存器将位于BAR2对应内存空间中。
在基于PC的平台上,如果PCIe Function的地址空间,被内存映射,并且映射到1MB以上,那么,Host软件(BIOS, Option ROMs, OSes)在实模式下的写操作,不能访问该地址空间中的寄存器。
索引/数据对机制,允许Host软件使用间接I/O寻址代替直接内存映射,来访问所有内存映射的NVMe寄存器。
注意:
UEFI驱动没有1MB的限制,因此在使用EFI时,不需要索引/数据对机制。因此,这个特性对于控制器来说是可选的,并且随着UEFI的普及,此特性可能会被淘汰。
2.1 限制
Host软件不得在,基于索引/数据对的存取和直接内存映射存取方法之间交替使用。在使用对控制器寄存器的直接内存映射访问后,索引/数据对机制将不再使用。
2.2 Index/Data Pair registers
下列寄存器描述了,实现索引/数据对所需的寄存器。
- Index register:索引寄存器,占4字节,偏移为00h~03h;
- Data register:数据寄存器,占4字节,偏移为04h~07h。
2.3 Offset 00h: IDX – Index Register
Bit | Type | Reset | Description |
---|---|---|---|
31:02 | RW | 0h | Index (IDX):该寄存器表示,MLBAR/MUBAR寄存器(PCI BAR0和BAR1)内存映射空间中的NVMe寄存器双字偏移。 |
01:00 | RO | 0h | 保留 |
2.4 Offset 04h: DAT – Data Register
Bit | Type | Reset | Description |
---|---|---|---|
31:00 | RW | na | Data (DAT):该寄存器是一个“窗口”,通过它可以向索引寄存器,指向的内存映射寄存器读取或写入数据。没有实现物理寄存器,因为数据实际上存储在内存映射寄存器中。由于这不是一个物理寄存器,复位值与索引寄存器当前指向的寄存器复位值相同。 |
举两个例子,就明白了。
假设,欲将4E564D65h值,写入NVMe控制器的20h(NVM Subsystem Reset)寄存器,那么:
- 将偏移20h,写入Index Register;
- 将值4E564D65h,写入Data Register;
- 机制会自动将Data Register寄存器值,写入Index Register指向的寄存器。
假设,欲读取NVMe控制器的20h(NVM Subsystem Reset)寄存器,那么:
- 将偏移20h,写入Index Register;
- 对Data Register进行读取操作;
- 机制会自动读取Index Register指向的寄存器,并将结果作为Data Register寄存器值返回。
如此,便可以通过索引/数据对寄存器,实现对NVMe内存映射寄存器的访问。