作者 谭荣
在上电的过程中,每个Endpoint的内部地址空间都会通过内存映射(memory map)的方式映射到Host的内存中,SSD 内部与协议相关的所有寄存器都会被映射到Host的内存中(如图1所示)。Host访问的address只需为该寄存器在Host内存中映射的地址,就能准确地访问ssd的寄存器。注意Host不是往自己内存的那个物理地址读取或写入东西,而是用那个物理地址作为寻址用,最后访问SSD controller的寄存器。
图1:
图2是NVME Spec对Host端到device端内存映射(memory map)的英文说明,SSD内部所有相关寄存器的映射都是在这些模块的基地址上进行偏移的,如BAR0、BAR1、MTAB、MPBA等。
图2:
那么具体是如何实现的呢?如图3以一个read命令举例,看懂Host如何访问device的SQ Tail Doorbell寄存器。
图3:
从图1可以看出Host端到SSD端的内存映射框图,它是没有直接到SQ Tail Doorbell的映射指向的,结合图3(SQID 4)与图4我们可以知道,Host要访问的地址是submission queue 4 tail doorbell。
图4:
图1可以知道Doorbeel Reg模块在Host端内存的首地址是{BAR0,BAR1}+1000,图4可以得出在该模块内部寄存器的偏移是1000h+ (2y * (4 <<CAP.DSTRD),其中y=4,那CAP.DSTRD的值是多少呢?
图5为DSTRD的Spec解释,不同厂商的sdk在Initialize the NVMe Controller Capabilities (CAP) register时,会对CAP.DSTRD进行初始化。这个值也可以通过抓取device上电行为得到,如图6。
图5:
图6:
最后一个问题,bar0和bar1是多少?NVME device在上电的时候会做一堆操作。如图7,Host通过Configuration Write向寄存器0x10写了一个地址,即BAR0=0XD1100000。
图7:
如图8,Host通过Configuration Write向寄存器0x14写了一个地址,即BAR1=0X00000000。
图8:
从图9可以看出0x10与0x14实际上就是bar0与bar1的偏移。
图9:
最后结合上面的参数做一次验证:submission queue 4 tail doorbel=
{BAR0,BAR1}+1000+Doorbell(寄存器内部偏移)={0XD1100000}+1000h+(2y * (4 <<CAP.DSTRD)
=0XD1101000+(2*4* (4 <<0)= 0XD1101000+32=0XD1101000+20h=0XD1101020。与图10完全吻合!
图10: