第8章 流水线
本章描述了TMS320C54x DSP流水线的操作,列出了对不同寄存器操作时的流水线延迟周期。(对应英语原文第7章)
8.1 流水线操作
TMS320C54x DSP有一个6段的指令流水线。流水线的6个段彼此独立,允许指纹重叠执行。在任意给定的周期内,可以有1~6条指纹被激活,每一条指纹处于不同的白执行阶段。
流水线的6个段以及它们的功能分别是:
Program prefetch. Program address bus (PAB) is loaded with the address of the next instruction to be fetched.
Program fetch. An instruction word is fetched from the program bus (PB) and loaded into the instruction register (IR). This completes an instruction fetch sequence that consists of this and the previous cycle.
Decode. The contents of the instruction register (IR) are decoded to determine the type of memory access operation and the control sequence at the data-address generation unit (DAGEN) and the CPU.
Access. DAGEN outputs the read operand’s address on the data address bus, DAB. If a second operand is required, the other data address bus, CAB, is also loaded with an appropriate address. Auxiliary registers in indirect addressing mode and the stack pointer (SP) are also updated. This is considered the first of the 2-stage operand read sequence.
Read. The read data operand(s), if any, are read from the data buses, DB and CB. This completes the two-stage operand read sequence. At the same time, the two-stage operand write sequence begins. The data address of the write operand, if any, is loaded into the data write address bus (EAB). For memory-mapped registers, the read data operand is read from memory and written into the selected memory-mapped registers using the DB.
Execute. The operand write sequence is completed by writing the data using the data write bus (EB). The instruction is executed in this phase.
Figure 7–1 shows the six stages of the pipeline and the events that occur in each stage.
任何写回操作都要涉及流水线的两段:读取段和执行段。在读取段,要写回的操作数地址被送上EAB总线,在下一个周期,操作数通过EB总线写入存储器。
在以下的例子中,流水线是用一组错开的行来描述的,其中每一行表示一条指令通过流水线各个段的情况。
在上面的例子中,每一行的左边都有一个标注,这个标注可以是一条指令、一个操作数、一条多周期指令或是流水线冲洗,最上面一行标注的数字代表单指令的周期数。
8.1.1 流水线中的跳转指令
例8-2和例8-3分别给出了在执行一条跳转指令(B)和一条延迟跳转指令(BD)时流水线的状态。因为一条跳转指令包括两个指令,所以它的执行最少需要两个指纹周期。但是,一条标准的跳转指令实际需要4个周期来执行,这个过程用例8-2来说明。
在全8-2中的指令完全执行的过程中会产生以下的事件:
周期1:PAB装载了跳转指令的地址。
周期2和周期3:跳转指令的指纹字被读取。
周期4和5:两条后续指令i3和i4被读取。虽然跳转指令后面的两条指令i3和i4被C54x DSP读取,但是它们不允许通过译码段并且最终被丢掉。当跳转指令的第二个指令字被译码后(左 边标注了b1的行),PAB就装载这个新的地址b1(在周期5中)。
周期6和7:两个字的跳转指令在周期6和7进入流水线的执行段。同时,j1也在周期6从地址b1中读取出来。
周期8和9:这两个周期被同一条跳转指令使用,因为其后的两条指令i3和i4是不允许执行的。这也是为什么跳转指令执行时需要4个周期(哪4个?6~9?)的原因。
周期10:j1执行完成。
例8-3给出了执行一条延迟跳转指令(BD)时流水线的状态。
在延迟跳转的情况下,流水线的操作与执行一般的跳转指令相同。但是,跳转指令之后的两条指令,i3和i4可以执行。因为,只有周期6和7是跳转指令所用的周期,这样就使得延迟跳转变成了两个周期的指令。
8.1.2 流水线中的调用指令
一个标准的调用指令的执行需要4个周期。虽然一个标准调用指令是两个字的指令,似乎只需要两个周期,但由于冲洗流水线需要两个周期,因此执行一共需要4个周期。例8-4给出了执行一条调用指令时流水线的状态。在例8-4的过程中会产生以下的事件:
周期1:PAB装载了调用指令的地址。
周期2和3:两个字的调用指令被读取。
周期4:因为返回地址被压入堆栈,所以SP递减(用SP--表示)。指令i3被读取,但不允许它通过译码段。周期5:写回地址总线(EAB)装载SP的内容并且片内的返回寄存器(RTN)装载了返回地址a3。当调用指令(b1)的第二 被译码后,PAB就在周期5中装载这个新的地址(图中的j1行)。
周期6和7:在周期6,RTN的内容通过EB写入堆栈,地址b1处的指令j1被读取。两个字的调用指令在周期6和周期7进入流水线的执行阶段。
周期8和9:这两个周期也被同一条调用指令使用,因为其后的两条指令是不允许执行的。
周期10:j1执行完成。
例8-5给出了执行一条延迟调用指令时流水线的状态。在延迟调用的情况下,流水线的操作与执行一般的调用指令相同。但是,这种情况下,后面两条指令i3和i4可以执行。因此,只有周期6和7是延迟调用指令所用的周期,这样就使得延迟调用变成了两个周期的指令。
INTR指令的执行过程与CALL指令类似,但由于INTR是一个字长的指令,因此可以提前一个索赔期计算向量表地址并读取指令。例8-6说明了INTR的执行只需要3个周期。
8.1.3 流水线中的返回指令
因为返回操作是一条单字长指令,所以用户会认为它的执行最少需要一个周期。实际上一条标准的返回指令需要5个周期来执行。例8-7给出了执行一条返回指令时流水线的状态。
在例8-7的过程中会产生以下事件:
周期1:PAB装载了返回指令的地址。
周期2:返回指令的操作码被读取。
周期3和4:两条后续指令i2和i3被读取。虽然这两条指令被读取,但不允许它们通过译码段,而是被丢弃。在周期4,SP递增(用SP++表示),DAB装载了SP的内容,以便从堆栈中读取返回地址。
周期5:使用DB将栈顶内容读出。
周期6:返回指令进入流水线的执行段。从堆栈取出的地址被送上PAB,以便从返回地坛中取下一条指令j1。
周期7和8:这两个周期也被返回指令使用,因为其后的两条指令i2和i3是不允许执行的。
周期9和10:因为在周期4和5没有取指令,所以周期9和10是哑周期。
周期11:j1执行完成。
例8-9和例8-10分别给出了执行一条带有中断使能的返回指令(RETE)、带有中断使能的延迟返回指令(RETED)时流水线的状态。执行这些指令时,流水线的操作分别与执行标准的返回指令、延迟返回指令相同,并且使用的周期数也分别相同。不同的是这两条指令在流水线的执行段通过复位INTM位来开放全局中断。
例8-11和例8-12分别给出了执行一条快速返回指令(RETF)和一条延迟快速返回指令(RETFD)时流水线的状态。RETF指令与RETE指令不同,它不从堆栈中读取返回地址,而是从RTN寄存器中读取返回地址。这样就使得RETF将返回地址装入PAB时比RETE指令早两个周期。正如这两个例子所示,RETF指令的执行只需3个周期,延迟咫尺天涯返回指令(RETFD)的指令只需一个周期。
8.1.4 流水线中的条件执行指令
因为XC指令是一条单字长指令,所以它要完全执行至少需要一个指令周期。
在例8-13中产生以下事件:
周期4:PAB装载了XC指令的地址。
周期5:XC指令的指令码读取。
周期7:在周期7中,当XC指令进入流水线的访问,XC指令中说明的任何条件都被检测。如果被测试的条件为真,其后的两条指令i5和i6被译码并允许执行。如果被测试的条件为假,i5和i6不被译码。
为了使XC指令在下一个周期中执行,CPU在流水线的访问段测试条件,这就意味着XC之前的两条一个字长的指令(或一条两个字长的指令)在条件被测试之前不能完全执行(怎么处理呢?之后接着执行?)。因为条件码只爱执行段中的指令影响,所以这两条指令对XC的操作没有影响。
8.1.5 流水线中的条件调用指令和条件跳转指令
因为一条调用指令包含两个指令字,一般认为它的执行最少需要两个周期。实际上,一条标准的条件调用指令在调用成功时需要5个周期来执行,而在调用不成功时,需要3个周期来执行。
例8-4给出了在执行一条条件调用指令(CC)时流水线的状态。
一条条件调用指令执行时流水线的状态与一条非条件调用指令执行时流水线的状态相似,唯一不同的是条件调用指令中的条件在流水线的执行段被测试。正如例8-14所示,在周期7中,当条件被测试时,前面一条指令i1已经执行完毕,而且,CC后面的两条指令i4和i5也被读取。如果条件被测试为假,这两条指令由流水线处理,否则,它们就被丢弃。在周期7中预取的指令也依赖于条件的测试,如果条件为真,PAB就被装载调用地址(b1);否则,PAB被装载下一条地址(a6)。
如果测试的条件为真,那么在周期10和11中,i4和i5就不执行。在这种情况下,CC指令就变成了一条5个周期的指令。如果测试条件为假,i4和i5则执行,此时,CC变成一条3周期的指令。
例8-15给出了在执行一条延迟条件调用指令(CCD)时流水线的状态。
执行CCD指令时,流水线的状态与执行CC指令时流水线的状态相似。但是,不管测试条件是否为真,后续两条指令i3和i4都允许执行,因此,只有周期7、8和9被CCD指令使用,这样就使得该指令变成一条3周期指令。
例8-16和例8-17给出了在执行一条条件跳转指令(BC)和一条延迟条件跳转指令(BCD)时流水线的状态。
条件跳转指令(BC)和延迟条件跳转指令(BCD)在流水线中的执行过程分别与CC和CCD指令类似,所不同的是没有返回地址被写入堆栈。正如例8-16所示,一条BC指令的执行需要3个或5个周期,这主要依赖于跳转成功与否。一条BCD指令的执行需要3个周期。
8.2 中断和流水线
例8-18给出了发生中断时流水线的状态。
正如例8-18所示,如果在周期3结束时中断被响应,那么在下一个周期(周期4),一条INTR指令会自动插入流水线的译码段。指令i2就不会被译码,因为INTR指令被放在流水线的这一段了。在其后的3个周期里,已经译码的指令执行。周期7、8和9被INTR指令占用。ISR中的第一条指令RETFD在周期10中执行。周期11和12被两条单字长指令占用,这两条指令替代了RETFD指令的两个延迟空隙。在下一个周期中,指令i2执行,完成从ISR的返回操作。
如例8-18所示,中断带来的附加开销是3个周期(即跳转去ISR所需的周期数)。中断返回只需要一个周期,因为RETFD是一条单周期指令。由于每个中断在中断向量表中只能存放4个字,因此,如果一个ISR需要存放多于4个字的指令,那么这些指令必须放在其它的地方。在这种情况下,中断向量表中存放一条跳转指令,这就导致了中断带来的附加开销要稍微高一些。
8.3 双存取存储器的流水线
C54x DSP片内存储器的特点之一是在单周期里支持再次存取。这种双重存取存储器被分成几个独立的块,支持对不同存储器块的同时存取,不会发生冲突。也就是说,当流水线中的一条指令访问一块存储器时,相同流水线段中的另一条指令可以访问另一块存储器而不发生冲突。每一块存储器在单周期中支持存取,即处于流水线不同段的两条指令可以同时访问相同的存储器块(总之,同一段可以访问不同块,不同段可以访问相同块)。一般来讲,当一块存储器同时执行两个存取时会发生冲突,C54x CPU可以自动解决这些冲突。表8-1列出了一些C54x器件的块的大小和块的数量。
每一块双存取存储器支持单周期的两次存取,一次存取是在前半个周期,另一次存取是在后半个周期。表8-2列出了在每半个周期中的存取情况,图8-3所示为不同类型的操作,为了简洁明了,图中省略了地址总线的装载。
因为存在两种类型的存取,并且在每半个周期内只能执行一个存取,所以会发生存取的冲突。这些存取冲突问题可以由CPU自动解决,解决的方法可以是重新安排存取的顺序,也可以是将其中一个存取延迟一个周期。值得注意的是,只有当所有存取是对同一块双存取存储器进行时,才会发生存储器存取的冲突问题。
8.3.1 解决取指令和读操作数之间的冲突
当一块双存取存储器既映射到程序空间又映射到数据空间时,如果对该存储器同时进行一个取指令操作和一个读数据操作,则这两个操作会发生。C54x DSP自动将取指令操作延迟一个周期以解决冲突,这个过程如例8-19所示。例8-19中假设指令i2和i3不会访问程序代码所驻留的双存取存储器块。
8.3.2 写操作数和读双操作数之间的冲突
如果一条单操作指令后跟着一条不执行写访问的指令,并且后面这条指令是一条双操作数读指令,就会发生冲突。这种情况如例8-20所示,其中AR3和AR5指向同一块双存取存储器。
在操作数写操作)EB总线)和第二个数据读操作(CB总线)之间有冲突。这种冲突的解决方法是自动将写操作延迟一个周期。因为被延迟的写操作是在第二条指令处于执行段时进行的,所以这些指令的实际执行时间并没增加。
如果有任何读操作(通过DB或CB)去访问发生写操作的相同的片内存储器单元,那么CPU实际上就不对存储器单元进行读操作,而是从内部总线上直接读取数据(所读的数据是正确的吗?)。这使得流水线将写操作推迟到下一周期,而周期是从相同存储块读数据的操作。
8.3.3 解决写操作数和读双操作数之间的冲突
在8.3.2小节所描述的情况中,如果第二条指令是一条写操作的指令,那么第一条指令的写访问就不能被推迟到下一个周期。CPU解决这种冲突的方法是在第一条指令之后插入一个哑周期。这个过程如例9-21所示,其中AR3和AR5指向相同的双存取存储器块。
8.4 单存取存储器和流水线
C54x DSP片内单存取存储器的特点是在每个周期内支持一次对存储器块的存取。C54x器件有两种不同类型的单存取存储器:
单存取读写存储器(SARAM)。
单存取只读存储器(ROM或DROM)。
在流水式的访问中,这两种单次访问存储器的访问过程相同,只是ROM和DROM不能进行写操作。这些存储器块在存储空间中是连续安排的,第一块就从SARAM或ROM的起始地址开始。
单存取存储器支持无冲突的同时访问,只要这些访问是对不同存储器块进行的。也就是说,当流水线中的一条指令访问一块存储器时,另一条指令可以在相同周期内无冲突地访问另一块存储器。当两个访问同时对一块存储器进行时,就会出现冲突,此时在该周期中只有一个访问可以进行,而第二个访问要延迟到下一个周期,这就导致了一个周期的流水延迟。
单存取存储器引起的一个流水线冲突可能会有以下几种不同的情形:
Dual-Operand instructions. Many instructions have two memory operands to read or write data. If both operands are pointing to the same singleaccess memory block, a pipeline conflict occurs. The CPU automatically delays the execution of that instruction by one cycle to resolve the conflict.
For example:
MAC *AR2+, *AR3+%,A,B ; This instruction will take two
; cycles if both operands are in
; same SARAM or DROM block.
32-bit operand instructions. Instructions that read 32-bit memory operands still take only one cycle to execute, even if their operand is in single-access memory. Single-access memory blocks are designed to allow a 32-bit read to occur in one cycle. Instructions that write 32-bit operands take two cycles to execute.
DLD *AR2, A ; This instruction only takes 1 cycle even
; if the operand is in single–access memory.
Read-write conflict. If an instruction that writes to a single-access memory block is followed by an instruction that reads from the same single-access memory block, a conflict occurs because both instructions try to access the same memory block simultaneously. In this case, the read access is delayed automatically by one cycle. For example:
STL A, *AR1+ ; AR1 and AR3 points at the same SARAM
; block.
LD *AR3, B ; This instruction takes 1 additional
; cycle due to a memory access conflict.
On the other hand, a dual-operand instruction that has a read operand and a write operand does not cause this conflict because the two accesses are done in two different pipeline stages. For example:
ST A, *AR2+ ; This instruction does not take any
||ADD *AR3+, B ; extra cycles, even if AR2/AR3 point
; at the same single access memory
; block.
Code-data conflict. Another type of memory access conflict can occur when SARAM or ROM is mapped in both program and data spaces. In this case, if instructions are fetched from a memory block and data accesses (read or write) are also performed on the same memory block, the instruction fetch is delayed by one cycle. For example:
LD *AR1+, A ; This read data access delays a
; subsequent instruction fetch.
STH A, *AR2 ; This write data access delays a
; subsequent instruction fetch
This situation causes significantly higher pipeline latency than the cases described previously. This is because each time there is a read or write access to the memory block, the pipeline is stalled for one cycle. It is generally recommended that each single-access memory block be reserved for either data or program storage to avoid hits each time a data access is made to that block.
8.5 流水线延迟
略……