Building the Beta

Building the Beta 课程地址:https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-004-computation-structures-spring-2017/c13/c13s1/ 本章主要是介绍如何实现一个beta处理器,应该是一个实验课,所以没啥要记录的( PS: 对着一堆逻辑门Debug了一整天终于把所有测试都通过了!累死人( Lab地址:https://computationstructures.org/exercises/beta/lab.html

十二月 4, 2021 · 1 分钟

Compilers

Compilers 课程地址:https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-004-computation-structures-spring-2017/c11/ 本章就是讲了一些简单的编译原理的内容,内容比较简单,就不多记录了。 控制流图(CFG)是常用的IR

十二月 1, 2021 · 1 分钟

Procedures and Stacks

Procedures and Stacks 课程地址:https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-004-computation-structures-spring-2017/c12/c12s1/ 本章的主要内容是讲解了为什么需要Procedures和Stacks, 应该是第一次这么清晰的理解栈( Procedures Procedures就是一组指令的集合,目的是为了重用指令,节约空间。当然,在高级变成语言里面都叫函数或者方法。 这里从汇编的角度来考虑如何实现Procedure, 一个最简单的办法是将Procedure在编译的时候展开,这样虽然重用了指令,但是并没有节省空间,而且无法递归调用! 另一个比较好的做法是记录Procedure的开始位置,在需要调用的时候只需要把PC设置成Procedure的地址就可以了,这样即能重用指令,也能节省空间,还能递归调用!不过问题也随之而来,当Procedure运行完后,如何知道应该返回到哪里继续执行呢?Procedure所需要的参数该如何传递呢? 首先,在执行分支指令的时候,会自动计算$PC + 4$并存在指定的寄存器,所以我们可以在Procedure里面拿到要返回的地址。其次,我们可以固定几个寄存器来传递参数,比如R3 ~ R6。这样做的话,似乎可以正确执行procedure, 但是,一旦有递归调用,那这些指定的寄存器都会被覆盖,当递归最后一层结束后返回上一层时,整个调用链就“断掉”了! 而且,当procedure的参数变多时,寄存器根本不够用! Stacks 有了上述的问题之后,让我们再来思考一下procedure的调用需要那些工作来完成。 首先我们需要知道Procedure的参数、返回值以及结束后PC的值,其次是procedure里面的局部变量应该存在哪里,因为在procedure执行完后,这些局部变量应该要被完全清除。我们的目标是在procedure执行过程中不会影响caller的数据,在执行完成后回到caller的时候,各个寄存器除了存放返回值的那个外,都应该是调用procedure前的状态!我们把这些在每次procedure调用时都需要记录的东西称为Activation Record。 下图演示了在递归调用时,Activation Record应该是如何变化的。 这不就是先进后出的Stack嘛!然后就是一系列的有关于Stack的定义了。 Stack frame 现在我们用Stack来解决之前的问题了! 我们可以把procedure的参数,局部变量,以及procedure要使用的寄存器的内容都存到stack了!除此之外,我们还需要一些特殊的记录,比如procedure执行完后该返回的地址以及一个可以随时在栈中寻址的指针。然后,约定一个顺序来存这些数据就可以了! 栈中一个procedure的相关数据都在连续的区块中,我们称这个区块为stack frame. 然后约定好caller和callee的职责,就可以开心地使用procedure啦! Compile procedure 如下是将C的一个递归调用编译成汇编的例子。 Summary

十二月 1, 2021 · 1 分钟

Assembly Language

Assembly Language 课程地址: https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-004-computation-structures-spring-2017/c10/c10s1/ 汇编语言是对指令集的抽象,用一些单词的缩写来替代指令集的opcode, 从而更容易记忆和理解。 本课程中使用的汇编语言叫USAM,课程Lab里面有详细的cookbook, 就不再记录了,下面主要记录图灵机相关的内容。 FSM的局限 FSM是一个很强大的计算模型,前面章节中我们已经用电路实现了通用FSM, 那么FSM是否可以计算所有能用数字设备计算的功能或者说函数呢? 答案是不能,比如简单的括号匹配。因为FSM要靠状态来记住(和)的个数,当有一个长度无穷大的序列时,FSM便无法构造。 为了解决这个问题,Alan turing提出了图灵机的模型。 图灵机 图灵机的核心还是FSM, 不过现在多了一条无限长的纸带,FSM可以通过存储在纸带上的符号来记录额外的信息,从而避免了状态数过多而无法构造FSM的问题。 因为一段纸带上的符号可以被编码为一个整数,所以图灵机就是一个整数函数$y = TM_I(x)$。 其中$I$是用来标识特定功能即有特定FSM的图灵机,我们可以将FSM的真值表编码成数字来表示$I$。 Church’s Thesis $f(x) computable <=> for some k, all x f(x) = T_k[x]$ 该定理由图灵的老师Church提出,但目前并没有证明,不过已经被普遍接受(可能是还没找到反例吧)。 通用图灵机 对于不同的计算任务,其构造的FSM不同,所构造的图灵机便也不同,那么为了各种计算任务,就需要构造各种各样的图灵机(实际上只有FSM不同)。那么存在一种或者一个图灵机,能够计算所有图灵机能计算的函数吗? 从21世纪的结果来看,当然是存在的! 如上图所示,$U(k, j) = T_k[j]$是一个Universal Function, 实现这个函数的图灵机便是通用图灵机。 实际上,这里的关键在于$k$。 $k$在这里是表示program,理论上讲就是对FSM的编码,比如用FSM的真值表来编码,而从上层计算机的角度来讲的话,$k$就是可执行的二进制程序,而PC本身就是一个通用图灵机!通用图灵机的计算便是对$k$的interpret,实际上就是把$k$本身的计算步骤走一遍。 现在的各种编码算法,实际上就是把一个FSM编码成整数从而输入给通用图灵机。 不过由于内存的空间是有限的,所以实际中的计算机还是只能计算数据量不超过内存大小的任务。 Uncomputability 如下是一个不可计算函数的例子,也就是所谓的图灵停机问题。

十一月 29, 2021 · 1 分钟

Designing an instruction set

Designing an instruction set 课程地址: https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-004-computation-structures-spring-2017/c9/c9s1/ 本章用前8章所学的知识设计了一个简单的通用计算机,采用冯诺伊曼结构,即main memory + CPU,而CPU则主要由datapath(即以ALU为主的计算电路)和control logic(即用ROM和Register实现的FSM控制电路)。整体内容很简单,但是很好玩! Datapaths and FSMs Let’s start by designing the logic that implements the desired computations — we call this part of the logic the “datapath”. 上图虽然实现了a和b的计算,但还不能控制其状态,即不能在b为0时结束循环,所以需要一个FSM来控制整个计算电路即datapath的状态。 Programmable Datapath 第一节中设计了一个可以计算介乘的datapath, 然后配上一个简单的FSM, 就可以完成计算任务。但实际中的计算任务有成千上万种,我们不能为每种任务都设计一个单独的datapath。 首先每种计算都可以分解为ALU的几个基本计算类型(Arithmetic, Bool, Comparable, Shift),所以就计算来说,一个ALU就差不多够用了。但有的任务可能需要更多的寄存器来保存中间结果,所以增加寄存器数量是必须的。如下是一个简单的可以进行通用计算的datapath. The von Neumann Model 在上一节的模型的基础上,冯诺伊曼引入了主存和输入/输出设备,从而执行数据量更大的计算任务,而且也可以有各种各样的输入输出设备,如下图所示。 Storage 冯诺伊曼模型的要点是用主存来存储指令和数据,主存相对于寄存器来说,最主要的优点就是容量大,所以能存储很多条指令。假如一条指令是32位也就是4bytes的话,那么1KB就能存256条指令,1M就能存256000条! 在冯诺伊曼结构中,有一个专门的PC寄存器,用来存储下一条指令在主存中的地址。 指令和指令集 指令就是用来告诉control logic该干什么,指令集就是按同一标准设计的一大堆指令,可以控制control logic干各种各样的事。指令集相关内容感觉没有太多需要记录的,所以就不记录啦。 Summary

十一月 24, 2021 · 1 分钟

Design Tradeoffs

Design Tradeoffs 课程地址 本章从CMOS的电能消耗出发,讲如何减少不必要的能量损失,同时也讲了很多时间和空间的tradeoff. Power dissipation 电路优化的几个metrics CMOS能耗 一个减小能耗的简单例子 Carry-select Adder 设计一个N位的加法器,最直观的设计就是把N个全加器串联。 但是这样的$t_{PD}$是$\theta(N)$级别的。 现在将一个32位的加法器分成两个16位的部分分别计算再组合,$t_{PD}$就差不多减小了一半。 按这个思路一直二分下去,$t_{PD}$便能达到$O(\log n)$。 Carry lookahead Adder(CLA) 从$C_{out}$的计算公式中可以看出,一个FA的输入可以转换成3个变量$P$,$G$和$C_{in}$ ,其中只有$C_{in}$需要该位的前一位的$C_{out}$。 如果知道了两个相邻位置的$P$和$G$,那么可以把这两位当作一个整体,计算出这个整体的$P$和$G$,这时$P$和$G$任然还保留着原来的性质,所以,我们可以用倍增来计算整个电路的$P$和$G$。 下图是一个8-bits的CLA的G&P generate的部分: 然后是计算Carry的方法: 完整版: Multiplier binary multiplication 就是普通的竖式乘法,但是要注意如果值是补码,那最高位的全值为$-2^{N-1}$而不是$2^{N-1}$。 如下是值为补码的情况: pipelined multiplier 虽然pipeline化了,但每个stage的$t_{PD}$还是$O(N)$的,主要问题在于要按行传递carry。 Carry-save multiplier 讲每行需要传递的carry降到下一行(反正都是加在同一列,所以对结果没影响),这样可以将每个stage的$t_{PD}$优化到$O(1)$,但是需要更多的行,也就是面积增加了。 用时序电路减少面积 求和->移位->求和。 Summary

十一月 18, 2021 · 1 分钟

Performance Measures

Performance Measures 本章主要是讲pipeline的原理以及如何将一般的逻辑电路转换为pipeline,最后简单提到了一下异步电路和同步电路的区别。 Latency and Throughput Latency: the delay from when an input is established until the output associated with that input becomes valid. Throughput: The rate at which inputs or outputs are processed. Pipelined Circuites 如上图所示,在计算$P(X)$时,F和G一直处于空闲状态,那么有没有办法把这段时间利用起来呢? 我们可以用寄存器来保持F和G的输出,从而在$P(X)$的工作时间内,可以让F和G去进行其他计算。 Pipelining Methodology 如上是将一般逻辑电路pipeline化的一般步骤 Circuit Interleaving Pipeline的瓶颈在于$t_{PD}$最大的那个module, 我们可以用如下的方式将两个相同的module组合在一起,提高throughput. 异步及同步 同一个module对于不同的输入,所需要的时间可能也不一样。按之前的CLK驱动来设计电路的话,计算CLK的时候需要用module的最长计算时间,才能保证正确性。但对于一些计算较快的输入,这样做肯定是浪费了时间的。 上图展示了用同步和异步两种方式来实现一个简单的握手协议,对于异步电路来说,不再需要全局的CLK,所以效率非常高,但实际设计非常困难;对于同步电路来说,不再需要靠CLK来在module之间传输数据,效率也提高了,虽然还是在CLK上升沿才会有数据输入输出,但CLK不需要用最长计算时间的输入来计算了。 这个简单的握手协议如下: In phase 1, when the upstream stage has a new output and GOT-X is deasserted, it asserts its HERE-IS-X signal and then waits to see the downstream stage’s reply on the GOT-X signal. In phase 2, the downstream stage, seeing that HERE-IS-X is asserted, asserts GOT-X when it has consumed the available input. In phase 3, the downstream stage waits to see the HERE-IS-X go low, indicating that the upstream stage has successfully received the GOT-X signal. In phase 4, once HERE-IS-X is deasserted, the downstream stage deasserts GOT-X and the transfer handshake is ready to begin again. Note that the upstream stage waits until it sees the GOT-X deasserted before starting the next handshake. ...

十一月 11, 2021 · 1 分钟

Finite State Machine

Finite State Machine 有穷自动机本身是一个比较简单的概念,不过这里的主要难点是考虑将异步信号转换为同步 课程地址: Finate State Machine 本课程中自动机的表示图 异步时钟 当电路中的时钟信号和输入变量的时钟信号不一样时,需要将输入的时钟信号转换为与电路时钟同步的信号,从而使得电路能正常工作。 The bounded-time Synchronizer 当IN和CLK的上升沿到来时间$t_{IN}$和$t_{CLK}$相差大于$t_{E}$时,Synchronizer将输出至少持续$t_{D}$时长的0或1。但如果IN和CLK的上升时间沿到来时间之差小于$t_E$时,就无法确定Synchronizer的输出。为什么无法确定?个人理解是因为上升沿实际是要一段时间才能完成的,如果两者上升沿到来时间相差太短,当CLK达到1时,IN可能还在中间。 那么该如何用逻辑电路来实现这个Synchronizer,当然要靠D-Register啦! 上图是用D-Register实现的Synchronizer, 用$t_{setup}$和$t_{hold}$来充当$t_{E}$,看起来很美好,但还没有解决IN和CLK的上升沿同时到来的问题! 当IN和CLK的上升沿同时到来时,可能会让D-latch进入metastable状态: 解决办法:加D-Register!

十一月 7, 2021 · 1 分钟

Sequential Logic

Sequential Logic 课程地址:https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-004-computation-structures-spring-2017/c5/c5s1/ Background 用简单的逻辑门与/或/非可以实现很多逻辑函数,但有一种很简单的电路,却没办法用与/或/非直接实现,如下图所示的开关电路。 普通的组合逻辑电路和开关电路最大的区别在于开关电路的输出还于当前的输出(状态)有关。 为了用逻辑电路来实现相同的功能,首先得让电路能记住上一次的状态。 Memory Using Capacitors 如上图所示,电容是可以存储 ”电压“ 的装置,所以很容易想到用电容来实现存储电路。不过在现实环境中,电容没办法做到完全不漏电,所以用电容实现的存储电路每隔一段时间要充一次电。 DRAM便是类似的原理,需要定时刷新电容上的电压,特点是体积小,便宜。 Using Feedback 另一个实现存储电路的思路便是正反馈,如下图所示。 上图中用两个invertor实现的正反馈电路可以使输出稳定保持,但除了高低两个状态,还有一个中间的状态,而且该电路也可能会永远保持在中间的状态,所以我们需要避免这种情况。 Settable Memory 我们可以用一个两位的MUX来实现一个可设定的Memory. 这样的元件叫做D-latch, 即D锁存器。 要理解D-latch输入输出和时间的关系,如下图。 Try it out 虽然D-锁存器能够存储状态,但当有一连串的状态输入时,如果要保证每次只有一个状态能通过,然后被其余逻辑电路读取,然后下一个状态才能通过的话,需要对时间做非常严格的限制,但在实际环境中,各种干扰和噪声让这种限制变得几乎不太可能。 Move to Register Flaky Control System 为了解决上面Try it out中提到的问题,这里先用一个现实中的例子来解释一下: D-Register 我们可以按如上的例子用D-latch来实现一个这样的系统。 各种时序图 理解D-Register的时序很重要!!! Summary

十一月 5, 2021 · 1 分钟

Combinational Logic

Combinational Logic 如何描述电子元件的功能 真值表 逻辑表达式 在输入变量比较多的时候,真值表就很难读/写了,所以逻辑表达式更加通用。 设计数字电路的步骤 写出真值表 写出逻辑表达式 用AND/OR/NOT三种基本逻辑门实现,或者用NAND/NOR/NOT三种逻辑门实现 因为CMOS本身就是Inverting的,所以实际中NAND/NOR更加通用。 Sum of products 将真值表中为1的项用OR连接起来就得到了相应的逻辑表达式。 在功能不变的情况下,为了减少逻辑门的数量,也就是减小$t_{pd}$和$t_{cd}$,就需要将逻辑表达式中的项数最小化。 卡诺图 卡诺图化简步骤 找出Implicants 合并 处理lenient Mux Decoder ROM Summary

十月 31, 2021 · 1 分钟