502-数据通路的建立
# 502-数据通路的建立
现在一方面我们已经有了指令系统的总体需求, 另一方面,我们也准备好了几个关键的组件, 那现在我们就来一起看一看能否根据这些需求,将这些关键的组件拼合起来, 构造出一个完整的数据通路。
要建立一个 数据通路,基本原则是分析指令系统当中每一条指令的需求,并根据 指令的需求连接我们已经选好的组件。那么指令的需求 又分为两大类,有一些需求是所有指令所共同需要的; 另外,不同的指令也有一些各自特殊的需求。
首先,我们来看所有指令的共同需求,我们要执行指令, 首先就需要取指令,我们知道,指令是放在指令存储器当中的, 而我们要访问指令存储器取得指令,就得有一个地址,这个地址则存放在PC寄存器当中。 我们已经有了一个32位的PC寄存器,那我们把PC寄存器 的输出就作为指令的地址,连接到指令存储器, 而指令存储器则会根据地址的输入选中对应的存储单元,并将其内容输出, 这样我们就得到了我们所需的那条指令的二进制编码。
那除了取得当前的指令之外,我们还得为取下一条 指令做好准备,这就需要更新PC寄存器, 这又分成了两种情况,大多数时候,指令是顺序执行的, 在这种情况下,PC只需要加上当前这条指令的长度,就可以得到下一条指令的地址了。 在MIPS指令系统当中,每一条指令都是4个字节的,所以只需要 简单地进行PC加4的运算,那在我们刚才的这个结构上面, 我们需要增加一个简单的加法器,其中一个输入是PC寄存器的输出, 另一个输入是一个常数4,然后我们将这个加法器的输出连接到PC寄存器的输入, 这样,当前PC寄存器的内容既会送到指令存储器,以获得指令的编码, 也会送到加法器的输入,从而计算出一个PC+4的值, 那在下一个时钟上升沿到来的时候, PC寄存器就会将PC+4的值存入其中, 然后再将这个更新后的内容,同时送到指令存储器和加法器,
如此周而复始,就完成了每个时钟上升沿时,更新PC寄存器 的内容,然后指令存储器就会送出新一条指令的二进制编码。 当然我们这说的是顺序执行时的情况,
而如果遇到分支指令, 那下一条指令的地址就不是简单的PC+4,而是用分支指令进行指定。 因此我们还需要继续修改这个结构,我们需要增加一个二选一的多选器, 在顺序执行时,我们选择这个多选器的0号输入端,也就是PC+4的内容, 而在发生分支时,我们就选择这个多选器的1号输入端,以至于由分支指定的目标地址。 那在下一个时钟上升沿到来的时候,PC寄存器就会采样这个多选器的输出, 并将其保存起来,那这样一个结构,就完成了不断取回指令的功能。 我们把它称为取址部件,也简称为IFU, IFU作为一个整体,从外界只有一个时钟信号的输入, 和一个多选器选择信号的输入,并且提供一个指令编码的输出, 我们只用在系统启动时,给PC寄存器一个合适的初始值, 并在指令存储器中存放好我们需要运行的指令, 然后在运行的过程中,给出合适的多选器的选择信号,这个 IFU就可以在时钟信号的驱动下自动的连续工作起来了,这些就是所有指令的共同需求。
然后我们再根据指令的不同类别,来分析他们的各自需求。 首先我们来看加法和减法指令, 这两条指令都是R型指令,它的主体操作是先用rs 和rt,这两个5位信号,访问寄存器堆获得两个寄存器的内容, 并对他们执行相应的运算,这个运算目前我们提供了加法和减法两种, 然后将运算的结果放到另一个5位信号rd所指定的寄存器当中。 因此,我们就需要用到寄存器堆这个组件, 这是一个两读一写的寄存器堆, 两个需要读出的寄存器的编号,是由指令编码当中rs和rt这两位域指定。 而要写入的寄存器的编号,则由rd这个位域指定的。 所以我们只需要把这三个位域的信号的值,连接到对应的输入上,这样在寄存器堆的输出端, busA就会输出rs所指定的寄存器的内容, busB则会输出rt所指定的寄存器的内容。 然后我们将busA和busB连到ALU的输入端, 并且我们根据指令编码当中的操作码和功能位域,就可以知道当前是加法还是减法指令, 我们要通过一个控制信号,来选择当前ALU提供的运算的类型。
然后我们还要将ALU的输出连接到寄存器堆的输入端,也就是busW信号, 那在下一个时钟上升沿到来的时候,如果寄存器堆的写使能信号是有效的, 在这里,就用这个RegWr信号等于1的时候, 寄存器堆就会采样busW信号上的内容,并将其存入到Rb这个信号所指定的寄存器当中去。 这样就在一个时钟周期完成了一条加法或者减法的指令,
我们要注意的是,这里用 红色表明的这两个信号,都成为控制信号。
然后我们来看逻辑运算指令的需求, 在我们的简化版本当中,提供了逻辑运算,就是这个对立即数进行或操作的指令, 这条指令是一条I型指令,那我们刚才已经建立的这个数据通路,是否能满足逻辑运算指令的需求呢? 经过分析,我们可以发现几个问题:第一,逻辑运算指令,它的目的寄存器是rt,而不是rd,而我们现在在寄存器堆的Rw输入端连接的是rd这个位域, 这样就没法满足这条逻辑运算指令,要把运算结果写入到rt所指向的寄存器当中这样一个目的; 第二,这条指令其中一个操作数是一个立即数, 而我们现在ALU的两个输入都是来自寄存器堆, 对这一点也无法支持;第三,我们在指令当中,只提供了一个16位的立即数, 而ALU的输入都是32位的,所以还需要对这个16位的立即数进行零扩展。
针对这个三个问题,我们就需要对现在的数据通路进行改造, 我们来注意这个数据通路的图,看我们需要增加哪些部件。
针对问题1,我们增加了一个二选一的多选器,当执行之前的加法或者减法指令时, 我们让多选器的选择信号为1,这时就会和刚才一样,将 rd信号的内容传递到寄存器堆的Rw信号端, 而在执行现在这条逻辑运算指令时,我们就可以选择这个多选器的零号输入端, 将rt这个信号的值传递到寄存器堆的Rw信号端, 这就为写入rt所指定的寄存器提供了支持。
针对第二个问题,我们在ALU的输入端增加了一个多选器, 对于之前的功能,我们通过这个多选器的零号输入端, 将寄存器堆的busB信号和ALU的输入端相连, 而对于这条逻辑运算指令提出了新的需求,我们可以通过这个多选器的一号输入端, 将立即数与ALU相连,当然,这个16位的立即数还需要通过 一个零扩展部件,扩展成32位,再接到这个多选器的输入上。 这样我们就通过增加了两个多选器和一个零扩展部件,来满足了逻辑运算指令提出的新需求。 我们需要注意的是,我们在满足新需求的时候,一定要保证原有的需求同样是得到满足的。 所以在这里,我们就需要增加多选器,而不是直接修改各个部件的输入。
那在现在这条数据通路的基础上,我们再来看访存指令的需求。 访存指令也是I型指令,我们先来看其中的Load指令, Load指令是要对数据存储器进行访问, 而访存的地址是由rs所指定的寄存器的内容加上立即数进行符号扩展后的值, 而且访存得到的数据也要保存在rt所指向的寄存器当中。 那现在的数据通路,已经支持了写入rt所指向的寄存器了。 而访存地址的运算, 是一个寄存器加上一个立即数, 那我们现在的数据通路,也大体支持这样的一个功能, 但仍然还是有问题的。
首先,我们还不支持符号扩展, 其次,这个ALU运算的结果,应该是作为地址去访问存储器,从而获得数据,而不是直接连到寄存器堆的写入端, 因此我们还需要对这个数据通路进行改造。
针对符号扩展的需求,我们将原先的零扩展的这个功能部件,改造为一个多功能的扩展部件, 通过控制信号,我们可以选择进行零扩展或者进行符号扩展, 对于这条load指令,我们可以选择将16位的立即数符号扩展为 32位的数,并通过这个多选器连接到ALU的一个输入端, 而ALU的另一个输入端,则是rs所指定的寄存器的内容, 然后执行加法运算之后,获得了存储器的地址, 我们在这里就需要新增一个数据存储器, 这个存储器根据地址就可以得到对应的存储单元中的数据,因为我们最终是要将这个数据写入到寄存器堆,所以还需要增加这样一个多选器,将数据存储器当中输出的内容传送到寄存器堆的输入数据端。那这就是load指令所提出的需求,我们的解决方案是将原有的零扩展部件增加符号扩展的功能,并增加 一个数据存储器,当然,还包括相应的多选器。
那我们还有另一条访存指令,那就是Store指令, Store指令的地址运算方式和Load指令是一样的, 所不同的是,Store指令是将rt所指定的寄存器当中的内容存放到数据存储器当中去。 那我们来看现在的这个数据通路,我们首先需要rt所指定的寄存器的内容, 在现在的这个数据通路中,rt所指定的寄存器的内容会从busB信号上出来, 那我们就需要将这个信号连接到数据存储器的数据输入端, 但是对于除了Store之外的其他指令,我们都不希望将busB上的信号写入到数据存储器当中, 所以我们还需要给数据存储器连接一个控制信号, 只有在这控制信号有效的时候,才会进行写入的操作, 这样我们就满足了Store指令的需求。
现在,除了比较特殊的分支指令之外,我们分析了其他指令的需求, 并将各个组件连接起来,再加上之前已经构造的IFU部件, 我们就初步完成了数据通路的建立工作。
现在,在处理器的设计步骤的这五步当中,我们已经完成了第三步。
现在,我们已经基本完成了一个数据通路,看起来也并不复杂嘛。不过,这个数据通路是否已经完整了呢? 而它又是否能够正确地工作呢? 这些都要通过一条一条指令来进行更细致的检查和确认。