Lab05: 熟悉logisim
setup
- 本次lab我们将使用一个叫做logisim的工具来设计和模拟我们的电路,这个工具十分好用,只需要java -jar logisim-evolution.jar即可使用。
- 鄙人在基于archlinux的niri界面上使用时遇到了一些问题,特此记录一下。
- 故障现象:启动后界面可以正常显示1s,之后会变成白屏,无法操作。
- 起初我以为和之前的一些jar一样是年久失修的问题,于是下载了最新的logisim-evolution,但是问题依旧,于是我开始怀疑是不是平台问题,毕竟niri存在一些小毛病很正常,于是我在windows上跑了一下,正常,说明就是niri的问题。
- 根本原因:java程序默认用一种老窗口管理器(reparenting)(源于古老的x11),但是新的niri使用wayland合成器,要求程序各扫门前雪,所以就导致了这个问题。
- 解决方法:export _JAVA_AWT_WM_NONREPARENTING=1,这个指令要求java的awt窗口使用non-reparenting的方式来管理窗口,这个就符合niri了。
- 说多了都是泪啊😰
电路设计
- 这次lab让我们通过一个模拟软件来体验电路设计的乐趣,大大减轻了实际操作的困难,具体内容不加以赘述,just try it,lab设计的很有意思,并且最后提到在第三个project中会继续用到,这让我对cs61c的课程设计更加膜拜,在上完cs61c课程后我将出一篇比较cs61c和csapp的文章,锐评一下😈
Control Logic
Control and Status Registers (CSRs)
- CSRs are separate from the register file (x0-x31).
- Used for monitoring the status and performance, and they can up to 4096 CSRs.
- Not in the base ISA , but almost mandatory in every implementation, which is necessary for counters and timers, and communication with peripherals. 本来CSRs是基础ISA的一部分,但是后来由于ISA的模块化,就把它删了,现在属于是extension部分。
|
|
|
|
System Instructions
Instruction Timing
通过在脑子中假想一条指令执行过程中control logic所做的事情,我们发现时序是非常重要的,对此我们以add指令为例来分析一下时序:
-
之前我们提到过前往内存是很慢的,但是这里的Instruction Fetch是从指令cache中取的,所以和加法器差不多。
-
Critical path = $t_{clk-q} + \max{t_{Add}+t_{mux},t_{Imem}+t_{Reg}+t_{mux}+t_{ALU}+t_{mux}} + t_{setup}$,由于Control logic的delay很小(在这个公式中直接没标注),所以那一条路线直接忽略,而上式进一步简化为 Critical path = $t_{clk-q} + t_{Imem} + t_{Reg} + t_{mux} + t_{ALU} + t_{mux} + t_{setup}$。
-
换一个例子,比如lw指令(5个阶段),Critical path = $t_{clk-q} + \max{t_{Add}+t_{mux},t_{Imem} + t_{imm} + t_{mux} + t_{ALU} + t_{mux} + t_{Dmem} + t_{mux}} + t_{setup}$ = $t_{clk-q}+t_{Imem}+t_{imm}+t_{mux}+t_{ALU}+t_{mux}+t_{Dmem}+t_{mux}+t_{setup}$,可以看到lw指令的critical path比add指令更长,多了一个Dmm的delay。
-
下图是更抽象意义上的各个阶段的时序图。
- 可以注意到的是不同类型的指令的时序是不同的,有的会经历5阶段,有的只有3或4阶段,这些会影响cpu的频率。
Control Logic Truth Table
- 这个图详细的展示了每条指令的control signal如何控制,非常全面,现在的问题是,我们该如何实现这个呢?
- ROM
- Read-Only memory,我们只需要提前填充好每一个指令对应的信号word,在使用时我们判断出指令的类型然后选择对应的即可。
- 这种方式很方便,如果有扩展指令或者压缩指令需要添加,我们只需要在ROM中添加对应的信号word即可。
- Combinatorial
- 在现代芯片工艺中,通常直接设计一堆gates,这样更快也更紧凑。
|
|
- 对于opcode的最低两位默认都是11,如果有扩展指令集或者压缩指令集,可能需要考虑,但是对于RV32I来说,可以不考虑,总共需要考虑9位(1+3+5)。
- 除了这个9位指令特征外,我们还有Compare bit,即BrEq和BrLt,一共是11位。
-
这个ROM的输入是11位的判断信息,输出是15位的control singal word,可以计算就算11位全都用的到,那么最多存储量是2048*15=30720位,约等于3.75KB,这个存储量是完全可以接受的。
-
在真正读取ROM的内容之前,肯定需要有一个decoder来根据指令的11位特征然后选择ROM的地址(one-hot)。
|
|
- 上述的方法会有很多冗余,我们可以有很大的优化空间,比如下图是针对无符号的一个优化,对于branch类,想要判断是BrUn其实很简单,像Inst[14]就不需要判断,BrUn = Inst[13] * Branch(Inst[6] * Inst[5] * …),如果想要判断具体是Lt还是Eq,在看inst[12]即可,节省了一位。
- 通过这个我们也可以理解直接用combinatorial logic的方式来实现control logic了,针对每一个输出信号位,写出所有成立的输入组合即可,和ROM相比是每一个每一个输出信号复杂了,但是不不需要先判断指令类型了,而ROM是输出都搞好了,但是得先复杂地判断指令类型,互有利弊吧!
- 一般来说对于某一个指令想要解码,首先是type opcode的5位,然后是func3的3位,最后是func7的1位,对于RV32I , 只需Inst[0]*Inst[1]即可。
总结
- 泪目了,我们从高级语言c出发,逐步搞清楚了代码是如何一步步变成机器码在机器上执行的,然后我们从布尔单元出发,逐步设计了一个处理器,硬件和软件相遇了!
- 用伟大的Bora教授的话来说,我们应该和父母朋友打个电话,庆祝这一伟大的时刻,WE MADE CONTACT !
- 当然这肯定不是性能好的处理器,毕竟它有着长长的关键路径,但我们会持续优化的!