计算机组成学习笔记(四)
目录
摘要
计算机组成学习笔记(四)。
5 单周期处理器
5.1 处理器的设计步骤
- 要深刻理解这几点。
- 分析指令系统,得出对数据通路的需求。
- 指令系统,是在处理器设计之前,就由软件和硬件的设计人员共同协商决定的。
- 通过分析指令系统,我们可以得出指令所要操作的数据,需要通过怎样的一个电路的结构, 这就是数据通路。
- 为数据通路选择合适的组件。
- 加法器、减法器、寄存器等等。
- 连接组件建立数据通路。
- 选好了合适的组件之后,我们就按照最开始分析出的需求,把这些组件连接起来,就构成了完整的数据通路。
- 分析每条指令的实现,以确定控制信号。
- 还需要控制这个数据通路应该如何工作。
- 要分析每条指令的实现,以确定控制数据通路工作的控制信号。
- 集成控制信号,形成完整的控制逻辑。
- 把这些控制信号汇总起来,形成完整的控制逻辑,也可以称之为控制器。
- 无符号的加法和减法指令(R型指令)。
- 立即数的逻辑或。
- 这三条都是运算类指令,操作数要么是寄存器,要么是立即数,因此我们还需要有访问存储器的指令。
- 这就是Load指令和Store指令, 它可以在寄存器和存储器之间传送操作数。
- 条件分支指令。
- 通过对指令的各个位域进行分解,来看这些指令的含义。
- 对于R型指令,它一共分为6个位域,最高的6个比特,称为操作码,接下去连续3个位域都是5个比特,各自标明了一个寄存器的编号,然后5个比特在完整的MIPS指令系统当中,是用作标记移位的数量,在我们简化后的版本当中没有使用,因此我们可以看作是保留的位域。最后6个比特是功能位域,因此,当我们取得一条R型指令之后,就可以将它分解为这样6组控制信号。
- I型指令的32个二进制位只会被切分为4组信号。
- 这些指令的编码都是从存储器当中取得的,因此,我们首先需要一个存放指令的存储器,对于指令存储器来说,它不需要支持写入的功能,只要可读就可以了,而且我们希望对这个存储器,外界给它32位的地址,它就会给出对应的32位的数据。
- 这个32位地址又从哪里来呢?所以这就是我们另一个需求,我们需要有一个存放指令地址的32位寄存器,称为PC,也就是程序计数器。
- 程序计数器(储存指令地址,并提供给指令存储器),与指令存储器(通过指令地址,获取指令内容,并提转换为控制信号),共同构成指令部件。
- 这两条指令的主体功能,都是选择两个不同的寄存器,对它进行加法和减法的运算,然后让结果存到另一个寄存器当中去。
- 因此,首先我们需要有一组存放数据的通用寄存器,每个寄存器都是32位的,而且我们可以约定,这一组寄存器总共有32个,那这样一组通用的寄存器,我们就称为寄存器堆。
- 从加法和减法指令的操作,我们还可以看出,在运算时,我们需要同时读取两个寄存器的内容,这两个寄存器分别由指令位域当中的rs和rt这两个域所指定。在运算完成后,我们还需要写入另一个寄存器,这个寄存器的编号由rd或者rt来进行指定, 那么对于这个加法和减法运算,它都是由rd位域指定的。
- 而对于立即数的运算,运算结果需要改写的寄存器是由rt这个位域指定的。而立即数运算指令的操作数,除了rs所指定 的寄存器外,另一个操作数是一个立即数,其中16位是直接填写在指令位域当中的, 但是我们的运算需要是32位的,因此,我们还需要一个功能,就是将16位的立即数扩展到32位,对于这个运算,我们需要的扩展方法是零扩展,也就是将高16位都填0,从而构成一个32位的数。
- 这三条都是运算指令,因此我们还需要支持不同的运算类型,我们需要提供加法、减法和逻辑或三种功能。因此,我们还需要一个这样的运算器,这个运算器的操作数可以是两个寄存器,也可以是一个寄存器加一个扩展后的立即数,这些就是运算指令的需求了。
- 访存指令:
- 对于
LOAD
指令 来说,它需要从存储器当中读出一个字, 而这个字所在的存储单元的地址,是由一个寄存器的内容 加上一个立即数来决定的,取出这个字之后,会把它存放到寄存器堆当中,由rt所指定的寄存器。 STORE
操作,则是将rt所指定的寄存器的内容,传送到内存的指令的存储单元中。- 对于这两条访存指令,我们需要一个能够存放数据的存储器,这个存储器既要可读,也要可写,它的地址输入,以及输入和输出的数据,都应该是32位的。
- 另外我们从这个地址的计算方法还可以看出,我们也需要将16位的立即数扩展到32位,但扩展方法是符号扩展,也就是将这个立即数作为低16位,并将其最高位复制到高16位当中去,从而形成一个32位的立即数,那这就是访存指令的主要需求。
- 对于
- 分支指令:(BEQ = branch-equal)
- 对于分支指令,首先要判断两个寄存器当中的内容是否相等, 如果相等,那就将指令位域中立即数的这一部分经过一个简单的变化,加到PC上,从而得到新的PC。
- 如果刚才那次比较的结果是不相等, 那就直接将当前的PC加上4,从而产生新的PC。
- 分支指令的需求,首先是要能够比较两个寄存器的内容,并判断是否相等,然后还需要PC寄存器支持两种自增的方式,一种是加4,一种是加一个指定的立即数,当然,对于PC加4这个需求,前面介绍的其他指令也都是需要的。
- 算数逻辑单元(ALU):它要支持加法、减法、逻辑或和比较相等这样的操作;它有两个32位的输入,可以来自寄存器,也可以来自扩展后的立即数。
- 立即数的扩展部件:可以将一个16位的立即数扩展为32位,而且扩展方式可以是零扩展,也可以是符号扩展。
- 程序计数器:这是一个32位的寄存器, 由时钟控制,而且我们还要给它支持两种加法运算,要么加4,要么加一个立即数,这样的需求我们可以用ALU来实现,也可以只给它配上简单的加法器。
- 寄存器堆:一共需要32个32位的寄存器,需要支持同时读出两个寄存器,和写入一个寄存器, 这样的寄存器堆特征,我们称为“两读一写”。
- 存储器:需要一个只读的指令存储器,地址和数据都是32位的,还需要一个可读写的数据存储器,地址和数据也都是32位的。我们从这个需求本身来看,实际上提供一个可读写的存储器,就可以满足指令和数据的需求。
- 外界将两个编号分别放到Ra和Rb的信号输入中,寄存器堆就会将对应寄存器的内容,分别放到busA和busB的信号线上, 这就完成了同时读取两个寄存器的功能。
- 在时钟的上升沿,WriteEnable信号等于1,那么寄存器堆就会将busW信号上的内容存入Rw信号所指定的寄存器,这就是寄存器堆所提供的写的功能。
- 给入一个地址,存储器就会将对应的存储单元中的数送到数据输出信号上。
- 与寄存器堆不同在于,我们只给入一组地址信号, 而不是寄存器堆所提供的两组寄存器编号的输入,这个存储器是一个一读一写的存储器。
- 在时钟上升沿到来的时候,如果写使能信号为1,那么存储器就会将数据输入信号的内容存入地址信号所指定的存储单元。
5.2 数据通路的建立
- 我们已经有了一个32位的PC寄存器,那我们把PC寄存器 的输出就作为指令的地址,连接到指令存储器,而指令存储器则会根据地址的输入选中对应的存储单元,并将其内容输出,这样我们就得到了我们所需的那条指令的二进制编码。
- 在MIPS指令系统当中,每一条指令都是4个字节的,所以只需要简单地进行PC加4的运算。
- 在我们刚才的这个结构上面,我们需要增加一个简单的加法器,其中一个输入是PC寄存器的输出,另一个输入是一个常数4,然后我们将这个加法器的输出连接到PC寄存器的输入。
- 当前PC寄存器的内容既会送到指令存储器,以获得指令的编码,也会送到加法器的输入,从而计算出一个PC+4的值, 那在下一个时钟上升沿到来的时候,PC寄存器就会将PC+4的值存入其中,然后再将这个更新后的内容,同时送到指令存储器和加法器,如此周而复始,就完成了每个时钟上升沿时,更新PC寄存器的内容,然后指令存储器就会送出新一条指令的二进制编码。
- 增加一个二选一的多选器,在顺序执行时,我们选择这个多选器的0号输入端,也就是PC+4的内容。
- 在发生分支时,我们就选择这个多选器的1号输入端,即分支指令指定的目标地址。 在下一个时钟上升沿到来的时候,PC寄存器就会采样这个多选器的输出,并将其保存起来。
- 这样一个结构,就完成了不断取回指令的功能。我们把它称为取址部件,也简称为IFU(Instruction Fetch Unit)。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这个信号所指定的寄存器当中去。
- 红色表明的这两个信号,都为控制信号。
- 问题一:逻辑运算指令,它的目的寄存器是rt,而不是rd,而我们现在在寄存器堆的Rw输入端连接的是rd这个位域,这样就没法满足这条逻辑运算指令,要把运算结果 写入到rt所指向的寄存器当中。
- 问题二:这条指令其中一个操作数是一个立即数, 而我们现在ALU的两个输入都是来自寄存器堆,对这一点也无法支持。
- 问题三:我们在指令当中,只提供了一个16位的立即数,而ALU的输入都是32位的,所以还需要对这个16位的立即数进行零扩展。
- 增加了一个二选一的多选器:
- 当执行之前的加法或者减法指令时,我们让多选器的选择信号为1,这时就会和刚才一样,将 rd信号的内容传递到寄存器堆的Rw信号端。
- 执行现在这条逻辑运算指令时,我们就可以选择这个多选器的零号输入端,将rt这个信号的值传递到寄存器堆的Rw信号端。
- 在ALU的输入端增加了一个多选器:
- 之前的功能,我们通过这个多选器的零号输入端,将寄存器堆的busB信号和ALU的输入端相连。
- 对于这条逻辑运算指令,我们可以通过这个多选器的一号输入端,将立即数与ALU相连。
- 这个16位的立即数还需要通过一个零扩展部件,扩展成32位,再接到这个多选器的输入上。
- 不支持符号扩展。
- ALU运算的结果,应该是作为地址去访问存储器,从而获得数据,而不是直接连到寄存器堆的写入端。
- 针对符号扩展的需求,我们将原先的零扩展 的这个功能部件,改造为一个多功能的扩展部件,通过控制信号,我们可以选择进行零扩展或者进行符号扩展。
- 需要新增一个数据存储器,这个存储器根据地址就可以得到对应的存储单元中的数据,因为我们最终是要将这个数据写入到寄存器堆,所以还需要增加这样一个多选器,将数据存储器当中输出的内容传送到寄存器堆的输入数据端。
- Store指令是将rt所指定的寄存器当中的内容存放到数据存储器当中去。
- 现在的这个数据通路中,rt所指定的寄存器的内容会从busB信号上出来,那我们就需要将这个信号连接到数据存储器的数据输入端,.
- 但是对于除了
Store
之外的其他指令,我们都不希望将busB上的信号写入到数据存储器当中,所以我们还需要给数据存储器连接一个控制信号, 只有在这控制信号有效的时候,才会进行写入的操作。
5.3 运算指令的控制信号
- 从指令存储器中取回指令。
- 指令指定的操作。
- 计算下一条指令的地址
- 这步操作是在取指部件,也就是 IFU 当中完成的。
- 假设在系统中的某一个时刻, 我们就选取 T0 所示的这个时钟上升沿之后很短的一段时间。那这时 PC 寄存器的输出已经稳定,它的输出就是当前要取回的这条指令的地址。这个信号会连接到指令存储器的地址输入端。
- 经过一小段访问时间之后, 指令存储器就会将这个地址所指定的存储单元的内容放在其输出端。
- 操作数所在的寄存器的编号,是放在指令编码中的。现在 IFU 已经送出了指令编码。我们根据 R 型指令的位域分布可以从 IFU 输出的这 32 位信号中选取对应的部分。
- 例如,第二十五位到二十一位组成一组信号,我们记为 rs,第二十位到十六位我们记为 rt。第 15 位到 11 位我们记为 rd。那这三个信号就会被连接到寄存器堆的输入端。
- rs 信号被连接到寄存器堆的 Ra 输入端。那么注意实际上这根标了 rs 的信号和最右边这根标了 rs 的信号是连在一起的。以此类似,rt 和 rd 这两个信号, 也都分别连到了这张图左边标了 rt 和 rd 的地方。
- 根据寄存器堆的设计特性, 它随时会根据 Ra 和 Rb 这两个端口输入的信号,找到对应编号的寄存器,并放在输出端。
- 其中 rs 所对应的寄存器的内容就会放到上 busA 信号上, rt 所对应的寄存器的内容就会放到 busB 信号上。
- 首先我们来看给 IFU 的这个控制信号,因为这是一条加法指令,所以我们给出下一个 PC 的选择信号。应该是选取 pc+4 的这个通道。
- busA 已经被直接连到了 ALU 的一个输入端。
- 而 BusB 是连到了一个多选器,这个多选器负责为 ALU 的另一个输入端选择输入信号。那对于这条加法指令,我们正是需要从寄存器堆输出的这个 BusB 信号。所以我们来看这个多选器对应的选择信号它就应该是零。
- 那既然如此,这个多选器一号通道的输入的信号,就没有意义了。
- 而产生这个信号的是立即输的扩展部件。这个部件也有一根控制信号,决定了它是进行零扩展还是符号扩展。那现在这个控制信号设成什么都可以,我们用 x 来标记。
- 还需要给 ALU 一个控制信号,以便让 ALU 执行加法操作。
- 那经过一段运算的时间之后, ALU 就会输出这两个数相加的结果。这个运算结果的信号会送到两个地方,其中一个是数据寄存器的地址输入端。但是我们通过这条指令的分析我们可以知道,这条加法指令是不需要读写数据存储器的。因此,我们首先需要把数据存储器的写使能信号作为无效。
- 还需要正确的设置最后这个多选器的控制信号。现在我们需要把它设成零。这样这个多选器就会把零号通道的内容,也就是 ALU 的运算结果放到这个多选器的输出上。
- 这个信号最终会被连到了寄存器堆的输入端。而我们希望在下一个时钟上升沿到来的时候,寄存器堆会 采样其输入端的信号,并保存到 rd 所指定的寄存器当中去。
- 因此,我们首先需要将寄存器堆的写使能信号置为有效, 而要写入的寄存器的编号我们需要在rd 和 rt 当中选一个。根据这条指令,我们应该选择 rd,所以这个多选器的控制信号应该设为1。
- 虽然我们这里说是第三步,实际上它和刚才第二步完成的时间是一样的。
- 在第一步当中提到的, PC 将其输出送到了指令存储器的地址输入端, 与此同时 PC 的这个输出也会送到加法器的输入端,加法器的另一个输入端固定连了常数 4, 这个加法器的输出就会计算出 PC+4。
- 当指令取出之后,经过一小段时间,就会产生对应的控制信号。对于这条加法指令,这个 nPC_sel 的信号就会被设为零,也就是我们刚才标记的 +4,那它所控制的这个多选器就会选中零号通道。也就是将 PC+4 送到输出并最终连到了 PC 寄存器的输入端。
- 那在下一个时钟上升沿到来的时候,也就是这个图中标记的 T1 的时刻, PC 寄存器就会采样它的输入,从而把 PC+4 存到寄存器当中。那再过一小段时间,也就到了 T1 这个上升沿之后 的一小段时间,这个 PC 寄存器的输出就会稳定。从而把递增了4 之后的地址再次送到了指令存储器取出了下一条指令。这样这个取指在并执行然后再取指再执行的过程就会自动的进行下去,而且每一条指令都是在一个时钟周期之内完成的。
- 刚才我们已经在 IFU 输出的指令编码中选出了三组信号, 那现在我们还需要再多一组信号,也就是第 16 位,我们把它组成一个 16 位的立即数这个信号。那这个信号与我们之前连出的这三组信号一样也会被连接到数据通路上。这 16 位立即数就会被连接到扩展功能部件。
- IFU,对于这条 ori 指令,它的下一条指令,依然应该是 顺序执行的,所以 NPC_sel 信号也应该被设置为 +4,这和 ADD 指令是一样的。
- 这条指令与 ADD 指令最大的不同在于, ALU 的第二个操作数是一个立即数,而不是来自于寄存器堆。所以 ALU 的第二个输入端选择来源的多选器,它的控制信号现在就应该是 1,选择立即数作为来源。
- 这 32 位 的立即数是由指令编码当中的 16 位立即数扩展而得的,根据这条指令的要求我们应该采用零扩展的形式。所以这个扩展功能部件的控制信号,应该被设置为零扩展。
- ALU 的运算功能选择信号,我们应该控制 ALU 进行或运算。
- 数据存储器的写使能信号。因为这条 指令不需要写数据存储器,所以这个写使能信号应该被设为零。
- 最右边的这个多选器, 那我们现在应该是将 ALU 的输出,也就是这个或运算的结果。送到寄存器堆去,所以这个多选器应该选择零号通道。
- 还要设置寄存器堆的写使能信号有效。
- 目的寄存器是用 RT 这个位域指定的,所以对于寄存器堆写入寄存器的编号,我们应该选择 rt 这个来源,需要将这个多选器的选择信号设为零号通道。
5.4 访存指令的控制信号
- 首先,下一条指令的地址应该还是采用PC+4的方式。
- 寄存器堆这一边,rs和rt都是固定连接到了指令编码的对应位域,因此,busA和busB这两组信号上,分别是由rs和rt所指定的寄存器的内容。
- 要注意,对于这条指令,我们想要运算的是rs寄存器中的内容和立即数的符号扩展进行加法。
- 因此,对于ALU的第二个操作数的来源,我们应该通过这个多选器,选择通道1,并且设置扩展部件为符号扩展的功能。这样,指令位域中的16位立即数就会经过符号扩展,最终连接到了ALU的第二个输入端。
- 发送到ALU的控制信号,则是要设置为加法运算。这样,ALU就会完成了访存地址的运算, 并将地址信号送到数据存储器的地址输入端。
- 同时也要注意,busB上的信号,也会被连接到数据存储器的数据输入端,虽然我们并不需要它,但是这根信号仍然会把 rt 所指定的寄存器的内容送过来。所以我们需要设置数据存储器的写使能信号, 把它设为0,保证了数据存储器的内容不会发生改变。
- 最后这个多选器, 这个多选器的两个输入,一个是ALU的运算结果,就是访存的地址;另一个是从数据存储器当中,读出的数据。那么对于这条指令,我们显然希望把后者送到寄存器堆当中去, 因此对于这个多选器来说,我们需要设置它的选择信号为1,这样我们就可以把数据存储器的输出传送到寄存器堆的数据输入端,。
- 因为我们要写入rt所指定的寄存器,所以我们需要设置寄存器堆的写使能信号为有效, 并把写入寄存器编号的来源设置为rt。
- 这样,在下一个时钟上升沿到来的时候,数据存储器输出的内容,就会被写入到rt所指定的寄存器当中去了,而且在同样的这个时钟上升沿,PC寄存器的内容也不会被更新为PC+4。
- 对于
store
指令,我们也需要将rs寄存器中的内容和立即数的符号扩展进行加法运算,并以此作为地址访问数据存储器,不过不是读出,而是写入。 - 对于IFU,我们要选择下一条指令的地址,仍然是PC+4。
- ALU的数据的输入, 现在我们选的是立即数的符号扩展。
- 对于ALU,我们依然要选择加法的运算类型。
- ALU的运算结果,依然是一个地址,会被送到数据存储器上,但是与之前不同的是,这条指令要完成向数据存储器的写操作,所以在这里,我们会把数据存储器的写使能信号置为有效。
- 在下一个时钟的上升沿,数据存储器就会采样其数据输入端的信号,但这个输入端的信号又是什么呢? 我们可以看一看,实际上它连到了busB,而 busB是由rt这个信号所指定的寄存器中的内容。
- 因此,在下一个时钟上升沿到来的时候, 数据存储器会根据ALU算出来的地址,选中对应的存储单元,并采样这个来自rt寄存器当中的内容, 将它存到对应的存储单元中去。对于
store
指令来说,它就完成了它所需要做的主体的操作。 - 因为对
store
指令,我们不需要改写寄存器堆的内容,所以无论选择哪个通路,都是没有意义的。 - 需要设置寄存器堆的写使能信号为无效,在时钟上升沿到来的时候,寄存器堆的内容就不会发生改变。
- 也正因为如此,这个要写入的寄存器编号,无论设成rb还是rt,也都是可以的。
5.5 分支指令的控制信号
- 左边是一段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,那这就是应用条件分支指令的实例。
- 判断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了,我们先把这个选择信号标记为branch。
- 现在我们已经知道,ALU要执行一个减法运算, 而且它的两个操作数都应该来自寄存器堆,所以这一个多选器就应该选择通道0。由此,扩展部件功能选择信号可以任意设置,而ALU的功能选择信号,则需要设置为减法。
- 当我们设置ALU执行减法运算时,它的输出依然会是减法运算的结果,并送到数据存储器的地址端和下一个多选器的0号通道。那为了保证数据存储器不被改写,那我们还要设置数据存储器的写使能信号为0。那对于条件分支指令来说,它是不要回写寄存器堆的,所以这个多选器无论选择哪一条通道,都是没有意义的, 那我们可以把它的选择信号任意设置为0或者1。
- 因为不需要回写寄存器堆,所以我们必须要设置寄存器堆的写使能信号为0,以免错误地更改其中的内容,那因为写使能信号已经设为0,那寄存器堆的写入寄存器的编号,则可以任意的设置。
- 不过我们要注意,这一步仅仅是完成了判断, 那我们还要根据判断的结果,对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寄存器当中去,这样在下一个时钟周期, 指令存储器就会将分支目标地址所指向的那条指令的编码送出来,从而实现指令执行流向的改变。
- 不过这里还有一个问题,那就是分支目标地址究竟是如何生成的?
- 这个分支目标地址有两个部分, 一部分是PC+4,一部分是对立即数进行符号扩展,然后乘以4, 而这个立即数就是指令编码当中的低16位。
- 我们先把这一部分信号连出来,把这个立即数取出来,连接到一个符号扩展的部件上。
- 对于这个符号扩展的部件,我们再增加一个很简单的小功能,就是向左再移动两位, 左移两位就相当于乘以4,因此经过这个部件,我们将这16位的立即数扩展成了32位,并且完成了乘以4的操作。
- 我们现在已经有了PC+4,就是这个加法器的输出,那我们只需要直接把它连出来,然后再增加一个加法器,这样就可以得到了分支指令的目标地址。
5.6 控制信号的集成
- 把之前的数据通路中的实现细节隐藏起来,就由一个方框来表示。
- 控制这个数据通路正常运转的控制信号,一共有这样8个。
- opcode和function,而正是这两个位域,是用来表明指令执行什么样的操作。
- 因此,我们正是需要用这两个信号,来产生下面这些控制信号。实现这一部分功能的电路,就被称为控制逻辑, 也就是我们之前提到的控制器。
- 以add指令为例,当我们取回一条指令的编码,一是将两个寄存器当中的数相加,并保存到另一个寄存器当中,然后就将PC+4更新到PC寄存器当中。
- 把这些信号的值都摘出来,汇总成一张表格,就得到了一张完整的表格,描述了每条指令和每个控制信号之间的关系。
- 只有当前指令是加法或者减法指令时,这个控制信号才需要为1,其他时候,都为0,就可以了。
- 那这个RegDst的控制信号就等于add和sub这两个信号进行或操作。
- 还得进一步分析,add和sub这两个信号应该如何生成:
- add指令:
- add指令是一条R型指令,根据R型指令的位域划分,其opcode域都应该为0,而function域则指明了在R型指令当中,这细分的指令类别。
- 对于add指令来说,就是opcode域为全0,而function域的第0位到第4位为0,第5位为1。
- 我们将function的0位到4位,每一位都先取非, 然后与function域的第5位,以及这个rtype信号,一起进行与操作,这样就可以得到了add信号。
- sub指令:同add指令。
- rtype信号:因为R型指令就是opcode全为0, 所以我们只需要把opcode中的每一位都先取非,然后进行与操作,那么当opcode全为0时,这个rtype信号就会为1。
- add指令:
- 把这些逻辑表达式连起来,就可以得到了RegDst这个控制信号的逻辑表达式,有了这样的逻辑表达式,我们就可以用与门和非门画出产生这个控制信号的电路图来,这样,对于这一个控制信号来说,它的控制逻辑就已经是确定的了。
- 我们用同样的方法还可以得到其他控制信号的逻辑表达式,就可以得到所有控制信号的逻辑表达式。
- 在下面这个区域,所有的信号都是来自指定的编码了,而且,在对一些信号进行取非操作之后,这些逻辑表达式全都只有与操作。
- 而上面这个区域的表达式都是在下面这些信号的基础上,进行了或操作,从而产生了最终的控制信号。
- 把指令编码中opcode的这六位和function的这六位 连出来,先接到一组由与门构成的逻辑电路中,那么这一个逻辑电路就实现了上一页中,下面这片 区域的那些表达式,从而产生了一组中间信号。
- 再将这些信号接入一组由或门组成的逻辑电路,这组 逻辑电路就实现了上一页中上半部分那些逻辑表达式,最后又可以得到了我们想要的所有的控制信号。
- 这样我们就有了控制器的实现,在这样的电路实现下,我们固定地将取回指令的最高六位和最低六位,连接到控制逻辑中,那经过了这些与门和或门之后,就会自动地生成对应的控制信号。