1.PCIe初始化流程

PCIe硬件初始化完成的标志是盘进入最大速率L0状态,进入L0状态后,主机和盘就能正常使用TLP报文进行数据传输。参见图1。

pcie设备进bios识别 从pcie启动_初始化


从状态机可以看到,盘进入L0只能是通过Configuration或者Recovery进入(L0s只能通过L0状态进入,再退出到L0)。下图是抓取的一次盘的完整上电LTSSM跳转,左边是盘,右边是槽位。参见图2。

pcie设备进bios识别 从pcie启动_pci-e_02


从整体的LTSSM可以看到,盘是从Detect --> polling --> configuration -->G1 L0–>Recovery–>G3–>G4 L0;

接下来我们再来解释一下初始化过程中的每一个状态。
1)Detect
Detect状态是设备上电复位或者热复位后的第一个状态,也就是LTSSM的入口状态,当前设备检测到对端设备在位后,就会往下进入polling状态。检测方法是发送端改变链路电压对链路充电,根据充电时间长短来判断对端是否在位。

2)Polling
进入此状态,说明两者已经相遇了,接下来就需要打个招呼,看下是否能够正常交流。

在这个状态下,两端设备通过互相发送TS1和TS2来确认Bit Lock, Symbol Lock,Polarity Inversion等,参见图3。

pcie设备进bios识别 从pcie启动_nvme_03


从Trace中可以看到这个过程中发送的内容都是F7,也就是PAD,无实际意义,只是通过发送一定数量的序列看是否满足协议要求。参见图4。

pcie设备进bios识别 从pcie启动_ssd_04


3)Configuration

进入此状态,说明两者使用的是同一种语言,交流无障碍,为了能达成合作需要进行更深入的交流。该阶段两边通过TS1和TS2来确认Link number和Lane number。参见图5。

pcie设备进bios识别 从pcie启动_ssd_05


从Trace中可以看到TS1,TS2里面的Training Lanes和Training Links都是确切的值,通过这些值来进行信息交换。参见图6。

pcie设备进bios识别 从pcie启动_初始化_06


4)L0

进入此状态后,意味着双方交流完成了,可以开始愉快合作了。也就是可以进行DLLP和TLP通信了。TLP packet承载真正的命令(例如MRd,CfgRd等)和响应,每一个TLP发出后,接收端要回复一个DLLP ACK表示收到了TLP,如果接收端收到的TLP有bit error或者CRC error,那么就会回复NAK,参见图7一个CfgRd的一个transaction。

pcie设备进bios识别 从pcie启动_pcie设备进bios识别_07


第一次进入的时候是GEN1的状态,但期望的是最大协商能力的L0状态,因此就需要再次重新交流一下,达到最佳合作状态,也就是跳入Recovery状态。

5)Recovery
进入此状态,说明双方认为还能继续交流一下,争取达到合作共赢的最佳状态。

这是一个重新训练状态,目的是达到最佳链路状态,进入该状态比如链路异常,未达到最大速率或者最大宽度。参见图8。

pcie设备进bios识别 从pcie启动_pci-e_08


从Trace中可以看到,两端设备执行重协商动作都是通过TS序列中的一些特殊字段,比如Speed Change Bit,EqCmd Bit来指示实现的。参见图9和图10。

pcie设备进bios识别 从pcie启动_pcie设备进bios识别_09


pcie设备进bios识别 从pcie启动_ssd_10


经过Recovery后,盘和主机以GEN4X4的期望状态进入L0,达到了最佳合作状态。

以上就是一次正常上电协商的状态机跳转,至于状态机中的其他状态可以参考协议。

至此PCIe硬件初始化已经全部完成,接下来就是主机软件对设备的处理,主要是设备的枚举以及资源分配,设备设置等。

从Trace中看到主机下发的第一个TLP报文,配置读取设备的Device ID,这表明主机软件已经开始接管PCIe设备了。参见图11。

pcie设备进bios识别 从pcie启动_pci-e_11


关于主机软件对PCIe的初始化,我们暂且跳过,这是对所有PCIe设备的通用流程,接下来我们直接看一下NVMe层的初始化。

2.NVMe初始化流程

主机软件初始化完PCIe后,开始加载NVMe驱动,也就是初始化NVMe。(因为抓取的是上电Trace,而这个主板BIOS支持NVMe设备,因此下面的Trace是BIOS下NVMe初始化流程,和OS下的NVMe驱动稍微有点差异,但整体原理和流程是一样的)。

首先看一下NVMe Controller的寄存器定义,有助于对照Trace解析。这些寄存器的基地址是设备的BAR0地址。参见图12。

pcie设备进bios识别 从pcie启动_初始化_12


1)获取NVMe设备的基本信息

参见图13,可以看到BAR0基地址是0Xfc80000,读取了偏移0,8,14,1c寄存器,从寄存器状态可以看到这是一个支持NVMe 1.3协议的控制器,并且NVMe层 Not Ready。

pcie设备进bios识别 从pcie启动_pcie设备进bios识别_13


2)配置NVMe设备的Admin Queue

参见图14-1,从Trace中可以看到主机写了偏移14(Controller Configuration),24(Admin Queue Attributes),28(Admin SQ Base Addr Low),2C(Admin CQ Base Addr High),30(Admin CQ Base Addr Low),34(Admin CQ Base Addr High)寄存器,其中Admin SQ的基地址为0xbd551000,Admin CQ的基地址为0Xbd54c000。

pcie设备进bios识别 从pcie启动_nvme_14


参见图14-2,这是一个典型的NVMe storage的架构图,从图中可以看出需要有admin submission queue以及completion queue, 然后创建IO submission queue 和completion queue。

pcie设备进bios识别 从pcie启动_pcie设备进bios识别_15


3)做NVMe Controller Reset,等待Reset完成

参见图15,写偏移14寄存器的Bit0,做NVMe Controller Reset,然后轮询1C寄存器的Bit0,等待status为1,为1表明盘侧NVMe reset完成,NVMe Controller Ready。

这一步完成后,主机和盘之间可以通过Admin Queue进行管理消息通信。

pcie设备进bios识别 从pcie启动_pci-e_16


4)初始化NVMe字符设备

参见图16-1&2,盘硬件NVMe初始化完成后,盘能执行Admin命令,主机给盘发送一些管理命令从而获取到盘的信息,包括set-feature和identify这些命令。主机通过盘返回的信息,创建字符设备,完成NVMe字符设备初始化。下面是一个Admin命令(set-feature)的Trace,从这个Trace中我们可以看到一个完整命令的执行过程,这些地址都能和前面Trace看到的设置地址一致。其他命令,包括IO命令也是类似的。

pcie设备进bios识别 从pcie启动_pcie设备进bios识别_17


pcie设备进bios识别 从pcie启动_ssd_18


5)初始化NVMe块设备

参见图17~19,主机对盘数据的读写IO操作是通过块设备来完成的,盘的每个NS在主机上就是一个块设备,并且IO是通过IO Queue来通信,和Admin Queue分离。

首先主机会创建IO CQ和IO SQ(queue的个数以及SQ/CQ绑定关系由主机软件决定),然后发送identify ns枚举所有的ns,并且为每个ns创建一个块设备,完成主机块设备初始化。

图17-1为一个创建IO submission queue 的解码。

pcie设备进bios识别 从pcie启动_nvme_19


图17-2为初始化过程中创建了多个IO submission queue和completion queue, 然后才开始进行read读操作。

pcie设备进bios识别 从pcie启动_nvme_20


完成后主机就可以对盘上数据进行读写操作了,到此整个NVMe SSD初始化完成。

以下是一个Read Cmd,其中图18为transaction图,图19-1为NVMe command处理的过程,图19-2为该Read Cmd的transaction 过程解码。

pcie设备进bios识别 从pcie启动_初始化_21


pcie设备进bios识别 从pcie启动_pcie设备进bios识别_22


pcie设备进bios识别 从pcie启动_pcie设备进bios识别_23