505-分支指令的控制信号
# 505-分支指令的控制信号
分支指令,是一类特殊的指令, 它能够改变程序的流向,因此,想要执行分支指令, 我们还需要对现有的结构,进行进一步的改造。
在我们现在这个事例的指令系统当中,分支指令只有一条, 它的格式是I型的,那我们首先来看一看分支指令是如何工作的。
# 汇编代码
左边是一段C语言的代码,是一段典型的if else语句, 那我们看对应如何产生MIPS的汇编语言代码,会是怎么样的。 首先就是一条beq的分支指令, 看来编译器已经把 i 和 j 这两个局部变量 分别放到了s3和s4这两个寄存器当中, 那么这条指令就是比较s3和s4的值, 如果它们相等,则会跳转到True这个标号所标明的地址。 那么这里是一条加法指令,这条指令就是执行了f=g+h这条语句, 也就是C语言代码当中,if条件为真时,所要执行的语句。 执行完这条语句之后,程序将顺序地执行后面的内容。 而如果if语句的判断条件不成立,那对应的这条beq 指令在执行时,也会发现s3和s4寄存器的内容不相等, 从而不发生分支转移,而是执行顺序的后一条指令。 后一条指令是一条减法指令, 那与刚才我们看到的那条加法指令相对应,实际上,它执行的就是f=g-h这条语句, 也就是C语言当中,else的这条语句。 那么执行完这条指令之后,下一条指令是一条无条件的转移指令, 直接跳到Next,那这就是应用条件分支指令的实例。 那我们就来看一看beq这条指令的控制信号是如何生成的。
# beq指令
beq指令的操作,同样也可以分为三步, 第一是取指令;第二条,是判断rs和rt两个寄存器的内容是否相等, 那我们可以用一个减法来进行判断;第三步,是更新PC寄存器。 那么对于beq指令来说,所谓的分支,就是如何去改写PC寄存器, 所以它的重点在于第三步。
我们先来看条件不成立的 情况,也就是else对应的PC=PC+4, 那在转移条件不成立的时候,我们是顺序执行后一条指令, 这就和其他的运算指令、访存指令是一样的。
那如果条件成立的时候,PC的更新条件则相对复杂一些, 其中也有PC+4,然后需要加上这个16位的立即数的符号扩展,并乘以4, 也就是说,在beq指令当中,所带的这个立即数, 也就是刚才在事例中出现的那个目标地址的标号True, 它实际的数值,是转移目标地址和下一条指令地址之间的差值, 而且这个差值是以4个字节,也就是32位为一个单位的, 那么这个规则,是在指定MIPS指令系统的时候约定的。 我们现在重点是看如何去实现相应的控制信号。
同样,我们直接来看第二步,那么对于这一步, 要做的操作包括,从寄存器堆当中,取出两个寄存器的内容,而且进行减法运算, 那这个操作和我们之前学习的减法指令需求是一样的, 因此,现有的结构不需要修改,就可以完成这个功能。 我们注意,当取回一条指令之后,rs的位域被连接到寄存器堆, 它所指定的寄存器的内容,会放到busA上,然后连接到ALU的一个输入端, rt位域的信号会被连接到寄存器堆的Rb的输入端, 它所指定的寄存器的内容,会通过busB信号,再经过这个多选器 传递到ALU的另一个输入端,然后这个ALU就可以执行这个减法,
不过问题在于,之前的减法运算指令,会将这个ALU 运算的结果,通过这个多选器之后,写回到寄存器堆当中去, 而beq指令是不需要写回寄存器堆的,而且也不应该写回。 我们希望通过这个ALU,得出一个判断,就是这个减法操作的结果是不是0。 因此,我们还需要增加一个新的功能,来完成这样的一个判断,
判断一个数是否等于0,是非常简单的,所以我们可以很轻松地在ALU当中增加这个功能, 并让ALU提供一个信号的输出,标明当前的运算结果是否为0, 我们把这个信号命名为zero。 如果运算结果为0,ALU会把zero信号置为1,否则,置为0。 那因为运算结果是否为0,将会影响到IFU如何去更新PC寄存器, 所以我们需要把zero信号连接到IFU, 这样,我们就可以把第二步操作的描述补充完整。 那么在这一步操作中,这些控制信号又是如何设置的呢?
首先,下一个PC的选择方式,我们就不能再设置为+4了, 但是究竟如何更新,IFU还需要做一些工作,这个我们一会儿 再说。所以我们先把这个选择信号标记为branch, 然后我们再来看其他的控制信号。 现在我们已经知道,ALU要执行一个减法运算, 而且它的两个操作数都应该来自寄存器堆, 所以这一个多选器就应该选择通道0。 由此,扩展部件功能选择信号可以任意设置, 而ALU的功能选择信号,则需要设置为减法,那现在虽然我们新增了这个zero信号, 但是ALU原本的功能还是必须保持的。所以,当我们设置ALU执行减法运算时,它的输出 依然会是减法运算的结果,并送到数据存储器的地址端和下一个多选器的0号通道。 那为了保证数据存储器不被改写,那我们还要设置数据存储器的写使能信号为0。
那对于条件分支指令来说,它是不要回写寄存器堆的, 所以这个多选器无论选择哪一条通道,都是没有意义的, 那我们可以把它的选择信号任意设置为0或者1。 最后我们来看寄存器堆这一边,因为不需要回写寄存器堆, 所以我们必须要设置寄存器堆的写使能信号为0,以免错误地更改其中的内容, 那因为写使能信号已经设为0,那寄存器堆的写入寄存器的编号,则可以任意的设置。 这样,我们就可以看出,beq指令执行时真正有效的信号了。
不过我们要注意,这一步仅仅是完成了判断, 那我们还要根据判断的结果,对PC寄存器进行更新, 因此,这条指令的第三步和其他指令是不一样的。 那好,现在对于IFU来说,它有了两个输入的信号, 一个是之前就有的nPC_ select,还有一个是我们后来增加的zero, 而我们知道,对于IFU如何更新PC寄存器, 其关键,就是这个多选器如何选择的问题。 它的0号通道连接的是PC+4,1号通道连接的是分支指令的目标地址。 那现在我们这个选择信号应该如何生成呢? 我们不妨把输入列一个表,来进行观察。 当nPC_select的信号为0的时候,就代表当前在执行的指令是运算指令,或者是访存指令,而不是分支指令,那在这个时候,无论zero信号是0还是1, 这个多选器都应该选择0号通道,从而顺序地执行下一条指令。
而当nPC_select的信号等于1时,说明当前正在执行一条分支指令, 但如果此时zero信号为0,表示分支的判断条件不成立, 那这个多选器仍然应该选择0号通道,从而顺序地执行下一条指令。 只有当nPC_select的信号为1,说明当前是一条分支指令, 而且sero信号也为1,说明当前的判断条件成立, 这时,这个多选器才可以选择1号通道,从而将分支的目标地址更新到PC寄存器当中去, 这样在下一个时钟周期, 指令存储器就会将分支目标地址所指向的那条指令的编码送出来,从而实现指令执行流向的改变。
那通过这张表,我们是否能够得出,这个多选器的控制信号的生成方法呢? 请你想一想。 给一点提示,实际上只需要一个逻辑门就可以了。
如果还没有想出来,那我们不妨回来再观察一下,对于这个多选器的选择信号,只有在nPC_select和zero信号都为1时,它才会为1, 在其他时候,这个选择信号均为0。 那这个描述大家是不是很熟悉呢?这是哪个逻辑门的功能描述? 我们还是来看最右边吧, 其实,只需要一个与门就可以了。
那基于这样的分析,我们就可以对IFU进行进一步的改造, 从而支持beq指令的需求。 不过这里还有一个问题,那就是分支目标地址究竟是如何生成的? 那现在我们就来完成最后这一项工作,这个分支目标地址有两个部分, 一部分是PC+4, 一部分是对立即数进行符号扩展,然后乘以4, 而这个立即数就是指令编码当中的低16位, 因此,我们先把这一部分信号连出来。 现在,我们把这个立即数取出来,连接到一个符号扩展的部件上, 对于这个符号扩展的部件,我们再增加一个很简单的小功能,就是向左再移动两位, 左移两位就相当于乘以4, 因此经过这个部件,我们将这16位的立即数扩展成了32位,并且完成了乘以4的操作。 那现在我们有了这个算式的后半部分,而前半部分是PC+4, 幸运的是,我们现在已经有了PC+4,就是这个加法器的输出, 那我们只需要直接把它连出来,然后再增加一个加法器,这样就可以得到了分支指令的目- 标地址, 现在这个IFU,我们也已经补充完整了。
# 小结
现在,我们已经分析完了这个指令系统当中的最后一条指令了。 那我们已经知道,对于每一条指令, 每一个控制信号,我们应该赋予什么样的值。 但是还有一个问题,我们依然不是很清楚, 就是这些信号的值,是如何自动地产生的呢? 我们在下一节就来一起探讨这个问题。