一、故事前传
在之前的文章中, 我们已经针对SATA相关内容进行了较为详细的解析,如果感兴趣的话,请见之前文章:
1,浅析SATA Physical Layer物理层OOB信号;
2,SATA Link Layer链路层解析2.0-2.3;
3,SATA Transport Layer传输层解析3.0-3.4;
4,SATA Command Layer命令层解析4.0-4.1;
5,SATA Link Power Managment解析;
我们这里主要解析一下SATA NCQ(Native Command Queuing)原生指令序列的相关内容。
二、SATA NCQ 原生指令序列
NCQ是SATA中的命令协议,允许同时在Drive中执行多个命令。 当用户的应用程序发送多条指令到用户的硬盘,NCQ可以优化完成这些指令的顺序,从而降低负荷达到提升性能的目的。
此外,SATA Spec中还定义了三个特殊的功能来加强NCQ的性能:
Race-Free Status Return: 无竞争状态返回机制,允许任何指令任何时间报告执行状态,此外,多个命令执行完毕信息可以打包一起回传。
Interrupt Aggregation: 在DMA传输模式下,硬盘通知传输结束,会引起一个中断(Interrupt),造成延迟。所以,SATA spec提供中断聚集机制。如果硬盘同时间内完成多组命令,这些命令完成所引起的中断就可以聚集在一起,大幅减少中断的数目,这对于降低中断延迟有极大的贡献。
First-Party DMA(FPDMA): SATA允许硬盘端通过DMA setup FIS直接对Host控制器送出数据传输请求,DMA引擎就可以直接进行资料传输,这个过程中并不需要Host端软件的介入。
当Drive收到一个Command,是要将其重新排列?还是立即执行呢?这怎么区分呢?
不怕,SATA Spec定义了两个特殊的NCQ指令:
Read FPDMA Queued;
Write FPDMA Queued;
从上面Read FPMA Queued Inputs和Write FPMA Queued Inputs我们可以看到红色框里面有一个特别的5-bit参数TAG(bit3~bit7). 这个参数就代表了NCQ一次可以最多执行32个Commands(如下图红色框Command List)。
在Device端,当Queued Commands(最多32)中的一个write或者read准备好传输数据时,通过发送DMA setup FIS告知Host已准备好数据传输,其中要附带更新Tag Value(如下图红色框)。
在Device端,当Queued Commands执行完成后, 通过发送Set Device Bits FIS告知Host完成状态,此时SActive区域对应TAG的相关内容(最多32个Command)。
FPDMA Read Command(60h) Protocol如下:
我们抓取了一个FPDMA Read Command(60h)的SATA trace,如下图:
从上面的SATA trace我们可以得到:
这个FPDMA read cmd对应的Tag=8;
这个FPDMA read cmd要读取32768 bytes的数据;
在之前的文章“SATA Transport Layer传输层解析”中,我们提到过,Data FIS中的数据长度最大为2048 DWs,也就是8192 Bytes。所以,我们可以看到在上面的sata trace中,分4次发送Data FIS来实现32768 bytes数据的传输。
FPDMA Write Command(61h) Protocol如下:
我们抓取了一个FPDMA Write Command(61h)的SATA trace,如下图:
从上面的SATA trace我们可以得到:
这个FPDMA write cmd对应的Tag=0xEh=14;
这个FPDMA write cmd要写入131072 bytes的数据;
在之前的文章“SATA Transport Layer传输层解析”中,我们提到过:Data FIS中的数据长度最大为2048 DWs,也就是8192 Bytes。所以,我们可以看到在上面的sata trace中,分16次发送Data FIS。但是每发送一个DMA DATA FIS后,要再次收到DMA Activate FIS才能发送下一个DMA DATA FIS;
下面我们再看个完整的例子加深一下对NCQ的理解:
举例:Host向Device发送两个Read FPDMA Queued指令
针对这个例子,我们作两个假设:
假设1:
第一个Read FPDMA Queued指令 Tag=0;
第二个Read FPDMA Queued指令 Tag=5;
假设2:
Device要先执行第二个指令(Tag=5), 然后在执行第一个指令(Tag=0)
上图中指令下发流程如下:
Host先下发第一个Read FPDMA Queued指令,并且SActive bit0=1, 写入NCQ buffer,这时NCQ队列深度=1;
Host先下发第二个Read FPDMA Queued指令,并且SActive bit0 & bit5=1, 写入NCQ buffer,这时NCQ队列深度=2;
在假设2中,我们要求先执行第二个指令(Tag=5),那么,这个时候就需要NCQ对这两个指令重新排序,如下图红色框显示:
NCQ对这个两个指令重新排序后,就开始执行依次这两个Read FPDMA Queued指令, 指令执行流程见前面介绍的“FPDMA Read Command(60h) Protocol”内容。
上图是执行Tag=5指令的示意图,下图为执行Tag=0指令的示意图: