你好!本文章为该博客的第8篇文章!

本文章是对课上部分的题目回忆(真的只有部分,关于T1我能说很大一段),不保证回忆内容一定正确,请各位注意甄别!

以及,如果本文章涉及学术诚信问题,请第一时间和我联系,我将及时做出修改,谢谢支持!

P6课上题目(T1)回忆

三题仍然为互相独立的添加指令题,这次我只写T1的回忆。

准确来说,我还是只想写这次上机里有含金量的题,T2有含金量的部分几乎为0,T3和第一次P5课上的T3考点重合度太高(需要了解的可自行查阅我之前写的博客),于是我只想完整回忆T1。

T1添加指令为 fmultu,R型指令。具体操作为进行一个需要在乘除模块计算3个周期的计算指令,操作细节如下:

首先,对 GPR[rs]GPR[rt] 进行64位无符号乘法,结果记为 prod

prod 高32位为0,则把 prod[63:32] 保存到 HI 寄存器中,把 prod[31:0] 保存到 LO 寄存器中。

否则,我们找到 prod 中最高位1的出现位置 highbit_1prod 中最低位0的出现位置 lowbit_0 (可以证明,这种情况下 prod 中必定存在至少一个1和一个0) 。之后,记 reverse_bithighbit_1-lowbit_0 的绝对值,可以证明 reverse_bit 必定在 0630\sim 63 内。最后,把 prod[reverse_bit] 进行翻转(0变成1,1变成0),将结果的高32位保存到 HI 寄存器中,将结果的低32位保存到 LO 寄存器中。

对CPU模块编写的几点建议

  • 尽可能让主模块仅起到连接各模块的作用(除转发和阻塞信号对各级流水线寄存器的控制外),这样在对电路进行修改时,我们可以把精力只放在对各个模块内部的修改,而无需过度置疑CPU连接的正确性。
  • 课下的时候控制信号请务必集成到控制器里(包括Tuse和Tnew),能不新开模块就别新开模块生成控制信号。
  • 转发和阻塞的判断请务必使用各类控制信号和rs/rt/写入地址等等进行判断,而不使用完整的指令在内部做判断。(除非你是临时加指令)
  • 为了防止Verilog的特性导致产生笔误,从而在无意中创造出一个和你预期的信号不同的wire型变量,建议在每个模块名前加上``default_nettype none`。
  • 由于数据存储器改为外置,并且在D级时你无法得知要读写的具体位置和具体字节,所以一部分信息只能独立于控制器,在M级使用指令本身的opcode进行额外的处理。
  • 加入乘除模块时,可以将ALU的输出结果与HI寄存器,LO寄存器塞入一个多选器里,以多选器结果作为本周期ALU的“实际输出”,该多选器的控制信号在控制器里就可以处理出来。
  • 在考虑由乘除模块引起的阻塞条件增加时,我们可以考虑处理出该指令是否使用乘除模块(注意,这个和start信号还不是一个信号,后者只需要在处理需要多个周期的指令时才会用到),用D级的该信号,E级乘除模块的start信号和busy信号作为判断乘除法阻塞的依据。

FMULTU 题目分析

这题其实写起来也不算太困难,主要是讲一下对于乘除模块的修改,以及如何处理这种比较大号的计算型指令的添加。

首先,依然是常规的控制器里填充控制信号,这个指令的控制信号其实和MULTU差不了多少,基本上没啥可说的。在乘除模块里设置周期数也很好说。

并且,如果你是照着我给的建议搭的CPU,那么你不需要额外处理这条指令带来的额外的阻塞可能情况。

这题唯一的问题在于,这个东西算起来着实有些麻烦,有种以前计算指令的大杂烩的感觉。

对于这种情况,我的建议其实是:把计算过程独立于时序逻辑之外(防止出现时序问题),用always块建模组合逻辑(便于编写),多开reg变量存储中间结果(便于调试)。

比如说这题吧。我应该是开了这些reg型变量: [63:0]prod[31:0]highbit_1[31:0]lowbit_0[31:0]reverse_bit[63:0]temp[63:0]result

其实,我在之前描述题面的时候,就已经用了很多原题面根本没有明确提出的中间变量了(比如说,|highbit_1-lowbit_0| 在原题面里就直接被带着用了)。所以,这种适度的拆解有助于编写代码的时候快速理清思路。

同时,多开变量还有一个好处,就是你可以在调试的时候清晰的看到每一次计算的时候的中间运行结果,这样不管是哪一步开始出了问题,你都可以快速的发现问题。

然后,我们就可以照着题面要求一步步组合这些变量了。

具体的大部分实现细节都很好说,只提一个 highbit_1lowbit_0 的具体实现吧。我以 lowbit_0 举例,我的实现如下:

1
2
3
for (lowbit_0 = 0; lowbit_0 < 32'd64 && prod[lowbit_0] != 1'b0; lowbit_0 = lowbit_0 + 1) begin
//这里是个空循环,该干的活在循环条件里已经干完了
end

然后……?没了,恭喜AC!

反思课下设计模块的问题

没有。这次除了写的有点不是很美观之外,一点没有。

反思课上表现的问题

T2和T3的表现都没有大问题。最多来说就是,T3的时候我没有处理清楚判断WD和DM读取结果判断的位置,导致一开始多尝试了几次修改。

不过小问题还是挺多的,比如说T1的时候我一开始把处理 lowbit 的终止条件写反了,步进方向也写反了。比如说T2我统计一个数字里1的个数的时候,一个是存储个数的寄存器大小没开够(实际上是忘了写位宽),另一个是需要逐位比较是否是1的时候我直接把整个数据拿来比较了……令人汗颜!

当然,结果是好的,1h解决战斗。

希望以上内容能够让各位有一点新的收获吧。