503-运算指令的控制信号
# 503-运算指令的控制信号
通常,在一个指令系统中,运算指令是最为重要的。 而从另一个方面看,一般来说,运算指令的执行过程是最为简洁明了的。 所以我们先来看,运算指令所需的控制信号,是怎样构成的。
MIPS 的指令系统,从功能上划分可以分为运算指令,访存指令,和分支指令。 而从指令的格式上划分,只可分为 R 型指令,I 型指令,J 型指令。 那在我们现在用的这个示例中,经过简化之后 只有六条指令,分布在了这个表格的这四个地方
那首先,我们来看运算指令当中 R 型格式的指令,就是加法指令和减法指令。 这两条指令的操作过程非常的相似,因此我们就用其中的加法指令作为例子进行讲解。
加法指令的操作可以分为三个步骤,首先是从指令存储器当中取回指令, 然后将 rs 指定的寄存器和 rt 指定的寄存器 的内容相加,运算结果保存在 rd 所指定的寄存器当中。 最后是计算下一条指令的地址。 执行完加法指令,下一条指令是顺序执行的,因此将 pc+4 更新到 pc 当中。
那么就依次来看这三个步骤, 首先是从指令存储器中取回指令, 那么这个步骤并不是加法指令特有的,对于所有的指令都需要有这个步骤,这步操作是在取指部件,也就是 IFU 当中完成的。 我们假设在系统中的某一个时刻, 我们就选取 T0 所示的这个时钟上升沿之后很短的一段时间。 那这时 PC 寄存器的输出已经稳定,它的输出就是当前要取回的这条指令的地址。 这个信号会连接到指令存储器的地址输入端。 那经过一小段访问时间之后, 指令存储器就会将这个地址所指定的存储单元的内容放在其输出端,也就是我们要取回的指令的二进制编码。 我们用红色标出了这个步骤所关注的有效的信号。 同时我们注意左边的这个时钟信号。 我们用以标识当前时刻的这条虚线也向后移动了一小段。
现在我们已经有了这条指令的二进制编码,我们就可以进入第二步。 第二步是执行加法运算,但我们还得先取得操作数, 操作数所在的寄存器的编号,是放在指令编码中的。 现在 IFU 已经送出了指令编码。 我们根据 R 型指令的位域分布可以从 IFU 输出的这 32 位信号中选取对应的部分。 例如,第二十五位到二十一位组成一组信号,我们记为 rs,第二十位到十六位我们记为 rt。 第 15 位到 11 位我们记为 rd。 那这三个信号就会被连接到寄存器堆的输入端。 我们注意看左边的寄存器堆,rs 信号被连接到寄存器堆的 i 输入端。 那么注意实际上这根标了 rs 的信号和最右边这根标了 rs 的信号是连在一起的。 只是为了避免这张图显得过于杂乱,我们没有把这条线画出来。 以此类似,rt 和 rd 这两个信号, 也都分别连到了这张图左边标了 rt 和 rd 的地方。
那根据寄存器堆的设计特性, 它随时会根据 ra 和 rb 这两个端口输入的信号, 找到对应编号的寄存器,并放在输出端。因此一旦 IFU 取回了这条加法指令,那其中 rs 所对应的寄存器的内容就会放到上 busA 信号上, rt 所对应的寄存器的内容就会放到 busB 信号上。 那接下来对这两个数应该做怎样的操作那就由这些标成红色的控制信号来决定了。
首先我们来看给 IFU 的这个控制信号, 因为这是一条加法指令,所以我们给出下一个 PC 的选择信号,应该是选取 pc+4 的这个通道。 我们暂且用 +4 来表示 。然后我们来看寄存器堆的这两个输出,busA 已经被直接连到了 ALU 的一个输入端。 而 BusB 是连到了一个多选器,这个多选器负责为 ALU 的另一个输入端选择输入信号。 那对于这条加法指令,我们正是需要从寄存器堆输出的这个 BusB 信号。 所以我们来看这个多选器对应的选择信号它就应该是零。 那既然如此,这个多选器一号通道的输入的信号,就没有意义了。 而产生这个信号的是立即数的扩展部件。 这个部件也有一根控制信号,决定了它是进行零扩展还是符号扩展。 那现在这个控制信号设成什么都可以,我们用 x 来标记。 那好,现在 ALU 的两个输入端都已经连接上了正确的信号。 那么还需要给 ALU 一个控制信号,以便让 ALU 执行加法操作。 这个控制信号,我们就以 ADD 来标记。那经过一段运算的时间之后, ALU 就会输出这两个数相加的结果。 这个运算结果的信号会送到两个地方,其中一个是数据寄存器的地址输入端。 但是我们通过这条指令的分析我们可以知道,这条加法指令是不需要读写数据存储器的。 因此,我们首先需要把数据存储器的写使能信号置为无效, 否则在下一个时钟上升沿, 数据存储器会采样它的数据输入,从而改变其中的内容,这就是我们希望达到的结果。
但即使把写使能信号置为零, 数据存储器依然会根据地址的输入选择对应的存储单元,并将其内容送到数据输出的信号上。 因此我们还需要正确的设置最后这个多选器的控制信号。 现在我们需要把它设成零。 这样这个多选器就会把零号通道的内容,也就是 ALU 的运算结果放到这个多选器的输出上。 那这个信号最终会被连到了寄存器堆的输入端。 而我们希望在下一个时钟上升沿到来的时候,寄存器堆会采样其输入端的信号,并保存到 rd 所指定的寄存器当中去。 因此,我们首先需要将寄存器堆的写使能信号置为有效, 而要写入的寄存器的编号我们需要在 rd 和 rt 当中选一个。 根据这条指令,我们应该选择 rd,所以这个多选器的控制信号应该设为一。 那这样所有的控制信号都已经设置完成了。
而时间也过去了一小段,我们从这个时钟的波形上也可以看出来。 那只要所有的控制信号都设置正确了。 从寄存器堆输出的这两个寄存器的值就会经过 ALU 完成加法。 然后经过这个多选器,最终送到寄存器堆的输入端。 那我们也将在这个步骤中涉及的有效的信号用红色标出来。
那么注意到,又过了一小段时间之后, 寄存器堆输入端的信号就会变得稳定。 那在这之后,当时钟上升沿到来的时候,寄存器堆就会正确的采样 busw 的信号。 并选中 rd 所指定的寄存器,将采样的信号保存在其中, 这样就完成了这条加法指令这一步的操作。 不过这条指令的工作还没有完成,我们还有第三步,就是更新 pc 寄存器的内容。
除了分支指令,其他指令都应该是顺序执行的。 因此都应该用当前 PC 寄存器的值加上 4 作为下一条指令的地址,我们注意这个时钟信号的波形, 虽然我们这里说是第三步,实际上它和刚才第二步完成的时间是一样的。 我们来看对应的 IFU 的结构图,其实我们在第一步当中提到的, PC 将其输出送到了指令存储器的地址输入端, 与此同时 PC 的这个输出也会送到加法器的输入端, 加法器的另一个输入端固定连了常数 4, 这个加法器的输出就会计算出 PC+4。 而当指令取出之后,经过一小段时间,就会产生对应的控制信号。 对于这条加法指令,这个 nPC_sel 的信号就会被设为零,也就是我们刚才标记的 +4,那它所控制的这个多选器就会选中零号通道。 也就是将 PC+4 送到输出并最终连到了 PC 寄存器的输入端。
那在下一个时钟上升沿到来的时候,也就是这个图中标记的 T1 的时刻, PC 寄存器就会采样它的输入,从而把 PC+4 存到寄存器当中。 那再过一小段时间,也就到了 T1 这个上升沿之后的一小段时间,这个 PC 寄存器的输出就会稳定。 从而把递增了 4 之后的地址再次送到了指令存储器取出了下一条指令。 这样这个取指并执行然后再取指再执行的过程 ,就会自动的进行下去,而且每一条指令都是在一个时钟周期之内完成的。
我们再来看另一类 指令,也就是运算指令当中的 I 型格式的指令, 在我们现在所用的这个指令系统当中,这一类指令只有一条。 我们就来看这条指令的操作步骤,它同样也分为三步, 第一步就是从指令存储器中取回指令,第二步则是进行逻辑或运算, 这个运算的一个操作数是由指令编码中 rs 域所指的一个寄存器。 另一个操作数则是指令编码中的 16 位立即数经过零扩展之后得到的 32 位数。 那这个或运算的结果最终会保存到指令编码当中 rt 位域所指定的寄存器中。
然后第三步是将当前 PC 寄存器的值加上 4,再更新到 PC 寄存器当中去。 那第一步和第三步,与我们刚才介绍的 I 的指令 是一样的
因此我们只来看其中的第二步, 那这个表达式就是第二步操作的描述,我们结合这个数据通路来看。 刚才我们已经在 IFU 输出的指令编码中选出了三组信号, 那现在我们还需要再多一组信号,也就是低 16 位,我们把它组成一个 16 位的立即数这个信号。 那这个信号与我们之前连出的这三组信号一样也会被连接到数据通路上。 那我们注意,这个信号会被连接到这里。 这 16 位立即数就会被连接到扩展功能部件。 那对于这条指令,这些控制信号,又应该如何设置呢?
首先我们还是来看 IFU,对于这条 OI 指令,它的下一条指令,依然应该是顺序执行的,所以 NPC_sel 信号也应该被设置为 +4,这和 ADD 指令是一样的。 那这条指令与 ADD 指令最大的不同在于, ALU 的第二个操作数是一个立即数,而不是来自于寄存器堆。 所以归 ALU 的第二个输入端选择来源的多选器,它的控制信号现在就应该是 1,选择立即数作为来源,而这 32 位 的立即数是由指令编码当中的 16 位立即数扩展而得的。 根据这条指令的要求我们应该采用零扩展的形式。
所以这个扩展功能不接的控制信号,应该被设置为零扩展。 然后我们再来看,ALU 的运算功能选择信号。我们应该控制 ALU 进行或运算。 然后我们来看数据存储器的写使能信号。因为这条指令不需要写数据存储器,所以这个写使能信号应该被设为零。
然后我们再来看最右边的这个多选器, 那我们现在应该是将 ALU 的输出,也就是这个或运算的结果。 送到寄存器堆去,所以这个多选器应该选择零号通道。 那这样 ALU 的运算结果就会最终连接到了寄存器堆的输入端。 与此同时我们还要设置寄存器堆的写使能信号有效,而且从这个指令的要求可以看出目的寄存器是用 RT 这个位域指定的。 所以对于寄存器堆写入寄存器的编号,我们应该选择 rt 这个来源。 因此,需要将这个多选器的选择信号设为零号通道。 这样我们所有的控制信号都设置好了。 在这些控制信号的控制下,有效的数据通路就会呈现这样一个状态。 最后是 rs 位域所指定的一个寄存器碰到了 ALU 而 16 位的立即数经过零扩展之后也送到了 ALU, 这两个数在 ALU 中进过了或运算之后, 通过这个多选器最终送到了寄存器堆,然后在下一个时钟的上升沿, 寄存器堆会采样这个或运算的结果。 然后选择 rt 位域所指定的寄存器,把这个运算结果写进去。 当然我们也要记得,在同一个时钟的上升沿, IFU 中也会将 PC 寄存器更新为 PC+4, 这样我们就在一个周期中完成了这条 ORI 指令的操作。