SAST weekly是由电子工程系学生科协推出的科技系列推送,内容涵盖信息领域技术科普、研究前沿热点介绍、科技新闻跟进探索等多个方面,帮助同学们增长姿势,开拓眼界,每周更新,欢迎关注,欢迎愿意分享知识的同学投稿eesast@mail.tsinghua.edu.cn
![61885040559fe599b5d1005d4ad773bc.gif](https://i-blog.csdnimg.cn/blog_migrate/cf1cf64237bc58dcc450094bd968a4fb.gif)
![78816367a0939cb3540cfae63f616f8b.gif](https://i-blog.csdnimg.cn/blog_migrate/c18162a6569c06599d1af1dd350f549e.gif)
![61885040559fe599b5d1005d4ad773bc.gif](https://i-blog.csdnimg.cn/blog_migrate/cf1cf64237bc58dcc450094bd968a4fb.gif)
![779f3fa8f94dc4e2dfd21ba40a1348e2.gif](https://i-blog.csdnimg.cn/blog_migrate/746f7881b0f4664717808053cdc1be41.gif)
什么是 计算机组成 ?
一般提到计算机底层的实现,我们会回想起清华信院学生被《电子电路与系统基础》《计算机组成原理》《计算机体系结构》所支配的岁月。
可以看到,计算机的“底层”其实也可以再细分成许多不同层级。从下而上,有基础电子电路、逻辑门、组合与时序逻辑、有限状态机、CPU与Memory;从上而下,有高级语言、虚拟机、汇编语言、机器语言、指令集架构(ISA);而CPU和ISA之间还有一些微架构、互联策略、资源分配等等内容,才能从上至下连成一个完整的计算机。
从学术的角度上来讲,计算机的硬件部分主要分为三大层:Hardware是地地道道的硬件层、Organization(也叫Microarchitecture)是基于一定抽象的模块化电路设计、ISA(Instruction Set Architecture)则提供了软硬件的接口。
作为一名不学无术的微纳电子系学生,在软件上整活是不可能了,基础的逻辑电路又没啥意思,所以我把魔爪伸向了中间CPU设计和体系结构的部分。
![779f3fa8f94dc4e2dfd21ba40a1348e2.gif](https://i-blog.csdnimg.cn/blog_migrate/746f7881b0f4664717808053cdc1be41.gif)
为什么要聊 计算机组成 ?
很本质的理由:下学期要学体系结构,搞点组成的事情可以作为背景知识;假期看了Nand2Tetris,感觉自己萌萌哒。
很历史的原因:我一直想搞清楚从基础电子电路到一台可以跑的计算机中间到底经历了什么。
在做这个小项目的过程中,我一方面了解了计算机的组成部分和一些思想,另一方面熟悉了FPGA的开发流程,所以想跟大家简单分享一下。
![61885040559fe599b5d1005d4ad773bc.gif](https://i-blog.csdnimg.cn/blog_migrate/cf1cf64237bc58dcc450094bd968a4fb.gif)
本篇推送前五章介绍如何用用Verilog写出一台极其简单的微型计算机,并实际跑在FPGA上。第一章先给出一个非常简易的ISA;根据ISA设计出CPU内的电路;根据冯诺依曼架构建立CPU和Memory的互联,并通过Verilog实现;最后添加I/O设备,并解决一个重要的时序问题。
整篇推送默认读者有一些数字电路和Verilog语言的基础,不会在基础的部分过多停留。
Credit to Nand2Tetris,强推好课。推送第一部分的指令集和电路设计大多来源于它,一些逻辑解释和FPGA的实现代码是手写的。
![61885040559fe599b5d1005d4ad773bc.gif](https://i-blog.csdnimg.cn/blog_migrate/cf1cf64237bc58dcc450094bd968a4fb.gif)
零
冯诺依曼架构
![3d743f9014734fbdcc973a1b0e44527b.gif](https://i-blog.csdnimg.cn/blog_migrate/41689ac33e557e9fb39eb6a9192de8e1.gif)
![9c65820a35f303e3a59e30b0afa57f9f.png](https://i-blog.csdnimg.cn/blog_migrate/248d11a17b34017a5648aea8da8be3c0.png)
在讨论具体设计之前,我们先鸟瞰一下计算机的整个硬件部分应当是什么样子的。
从上层看,计算机主要的功能是由指令控制的运算和存储,所以有运算、存储、指令三个大单元,它们分别是CPU、数据内存、指令内存。这样的结构也叫做哈佛架构,而冯诺依曼架构中两个内存是一体的,不过我们暂不区分。
这样的架构在逻辑上可以如下图表示。
![6d0f1f15026fd3b91dd9f75c2bffdad3.png](https://i-blog.csdnimg.cn/blog_migrate/33926198f44f6500c8015b925ca9761d.png)
而具体到模块化电路的实现,则可以参照下图的互联方式。
![52882ae4f3ebc3573f0b43252177ea63.png](https://i-blog.csdnimg.cn/blog_migrate/e651a6e08d73f2190c1ba4b362a16e19.jpeg)
两个Memory在这个简单的小实验中实际上是简单的寄存器堆,但是在现代计算机中,一般是多层级的存储系统。
CPU是核心的译码和计算单元,其中译码在硬件上主要通过MUX和各个寄存器的控制端实现,而计算单元则以ALU(算术逻辑单元)为核心。简单来说,ALU是一个高级加法器——它被指令控制,可以进行变量加减、常数加减、按位逻辑等等,从而达成多样的计算。ALU的设计需要考虑到指令集如何、需要达成什么样的计算目标,所以我们需要先给出高层的指令集架构设计。
壹
指令集架构
![3d743f9014734fbdcc973a1b0e44527b.gif](https://i-blog.csdnimg.cn/blog_migrate/41689ac33e557e9fb39eb6a9192de8e1.gif)
![9b474f1d8a37b939c358299bcbefac30.png](https://i-blog.csdnimg.cn/blog_migrate/b41f7121baf842801e15d36afd8125d4.png)
在讨论指令集之前,先给出一些约束:指令宽度16b,内存32K字,地址宽度15b。在CPU中有两个寄存器,分别称为AReg(地址寄存器)和DReg(数据寄存器);数据内存中当前被选中的内存块称为MReg。其中,AReg的输出端会直接连接至数据内存的地址选择端;同时会连接到程序计数器,从而在特定跳转条件下,会对指令内存的选址产生影响。
![da8f3e9189b26bb9a9f28e4ef2434f12.png](https://i-blog.csdnimg.cn/blog_migrate/ca430a5c5478c8f8dea9d7a4bf5e9d12.jpeg)
在给出上述约束后,讨论CPU所能执行的基础运算(也就是ALU的功能)。通过宽度为6b的指令,第一位表示是否将x置零,第二位表示是否将x按位取反;三四位同前两位,只不过作用与y;第五位决定进行加法操作还是按位与操作;最后一位控制计算结果是否取反后再输出。
通过这六位的控制,我们可以做出两个变量的加减、变量自身加减一、变量的按位逻辑操作等等。具体指令与功能的对照可见下图。
![81ad21e92f37981d4d9bf861e22534df.png](https://i-blog.csdnimg.cn/blog_migrate/979c7e03a79c1cb5b1c083d246bb7201.png)
最后来说我们的指令集,它包含两条指令。
指令一的格式为0aaaaaaaaaaaaaaa,它的功能是将15位的a直接存储到AReg中。进而根据连接约束,MReg也会变为地址所对应的内存块。
指令二的格式位1xxaaabccccccddd,功能是使用A/M/DReg内的数据进行计算,并根据计算结果与零的大小关系判断是否进行跳转。具体来说,三位a分别控制计算结果是否要写入A/M/DReg,b控制ALU的两位输入是AD还是MD,六位c直接作为ALU的控制指令,三位d分别控制小于、等于、大于时是否进行跳转。
通过这两个指令,我们可以实现所有基础操作,进而成为一个完备的系统。
![7f5b95458738341859cbb7d03b75cbcd.png](https://i-blog.csdnimg.cn/blog_migrate/2909f6891d41b5720ea6aa69eae3d184.jpeg)
贰
CPU设计
![3d743f9014734fbdcc973a1b0e44527b.gif](https://i-blog.csdnimg.cn/blog_migrate/41689ac33e557e9fb39eb6a9192de8e1.gif)
![9c65820a35f303e3a59e30b0afa57f9f.png](https://i-blog.csdnimg.cn/blog_migrate/248d11a17b34017a5648aea8da8be3c0.png)
根据所给的指令集,我们已经知道CPU需要处理什么样的指令、需要完成什么功能。根据这些设定,可以很自然的做出CPU的设计。
首先,根据AReg的约束,其输出直接作为数据内存的选址,以及程序计数器的输入。
![d04c667dae275969cae346a028970715.png](https://i-blog.csdnimg.cn/blog_migrate/c08d8a7df078557b48bc1f43e3506351.jpeg)
然后,ALU的输入为DA/DM,通过指令控制,所以中间插入一个MUX。
![683e4b4bdca619ddba7e03f52c6aac90.png](https://i-blog.csdnimg.cn/blog_migrate/2631b16b396b518e07a5617c5a295ada.jpeg)
第三,ALU的输出会作为A/M/DReg的输入。
![426c5526326a1c77298d17f66fd84717.png](https://i-blog.csdnimg.cn/blog_migrate/dad0e7258b1f246fd82616390a64857d.jpeg)
第四,AReg的输入还可以是指令一的内容。
![786b69769f6361d28a6cf1adf74b0825.png](https://i-blog.csdnimg.cn/blog_migrate/8909dac4d563e9f0c1f0274d6eda5bb4.jpeg)
最后,将指令分布到MUX和Reg的控制端。
![12f6074d12f564d77d947a60922b879d.png](https://i-blog.csdnimg.cn/blog_migrate/dd7d537fe0307fc95655b2b89b084670.jpeg)
这样,(在不考虑时序问题的情况下)我们就做出来一个满足前提约束,并可以按照指令集工作的CPU了。
叁
FPGA实现
![3d743f9014734fbdcc973a1b0e44527b.gif](https://i-blog.csdnimg.cn/blog_migrate/41689ac33e557e9fb39eb6a9192de8e1.gif)
![9b474f1d8a37b939c358299bcbefac30.png](https://i-blog.csdnimg.cn/blog_migrate/b41f7121baf842801e15d36afd8125d4.png)
先进行CPU的设计。按照前面叙述的CPU连接关系,需要先设计出一个ALU模块和一个PC模块(一个简单的累加寄存器)。
![df83eb6f6fe34ecb43ee2f6448f220fb.png](https://i-blog.csdnimg.cn/blog_migrate/d61651af3d4a9b7f31be4f1f5399f8b7.png)
![388dbbd5e2b229ec862c18f4a7822902.png](https://i-blog.csdnimg.cn/blog_migrate/f614cdbd44dfdc2b46c3eda6fa8df8e6.png)
再将各个组成部分相连,就得到了CPU。
![44f82fd3ef8805ec9781c175e8ec4790.png](https://i-blog.csdnimg.cn/blog_migrate/d367cdb3787000f51a933e335c8b0f83.png)
而Memory的部分则需要用CoreGenerator工具的辅助加入两个Block RAM的IP核,按照需要进行配置后直接调用模块即可。
![4c8c6d5f2fd1908784e3e50289eb73c8.png](https://i-blog.csdnimg.cn/blog_migrate/acd8073d2580f941b49d61940319ed97.png)
最后,将三大部分按照冯诺依曼架构连接。
![1fab7c4267721258cbce44a06c31fec5.png](https://i-blog.csdnimg.cn/blog_migrate/d8174fb9ce622e84f0d79f2c9ba5aee5.png)
这样,一个看起来可以工作的微处理器就造好了。接下来的问题是加入I/O设备,以及对其中的一些问题进行修正。
肆
加入I/O设备
![3d743f9014734fbdcc973a1b0e44527b.gif](https://i-blog.csdnimg.cn/blog_migrate/41689ac33e557e9fb39eb6a9192de8e1.gif)
![9c65820a35f303e3a59e30b0afa57f9f.png](https://i-blog.csdnimg.cn/blog_migrate/248d11a17b34017a5648aea8da8be3c0.png)
对于这里的I/O设备(按钮和八段数码管),我们选择一种简单的DMA的控制方式。我们分别给输入和输出配置一个硬件的驱动接口,并且将相应的内存访问权限完全下放给I/O设备,即输入设备直接控制RAM,输出设备由RAM中的某块内存内的值直接控制。具体代码如下。
![59deebb489ceff321b6da7fb11335903.png](https://i-blog.csdnimg.cn/blog_migrate/e3941c1dd5a2e9a6be935c7774bd757c.png)
![9bac4841fa49b8854364a9502d362f1c.png](https://i-blog.csdnimg.cn/blog_migrate/77a38c336b0802e38214268e5a87a1bc.png)
伍
解决时序问题
![3d743f9014734fbdcc973a1b0e44527b.gif](https://i-blog.csdnimg.cn/blog_migrate/41689ac33e557e9fb39eb6a9192de8e1.gif)
![9b474f1d8a37b939c358299bcbefac30.png](https://i-blog.csdnimg.cn/blog_migrate/b41f7121baf842801e15d36afd8125d4.png)
所有以上的设计都仅在逻辑上成立,但没有仔细考虑时序的问题。如果RAM的访问时间远小于一个时钟周期,则上述结构运行顺利,可以视为一个“取指-运算”的二级流水线。但是如果RAM需要一个时钟周期进行访问,那么AReg改变后,取到正确MReg的值需要多一个周期,这样就会出现竞争问题。所以我们在遇到AReg值改变时,需要令整个系统halt一个周期,保证RAM的输出正确。这样就成为了一个“取指-运算-访存”的三级流水线。
![69e5cc7482fafe1d323d986648121461.png](https://i-blog.csdnimg.cn/blog_migrate/abf04c2f95cd3b70f3b5a8c2494badcd.jpeg)
那么以上,就是我在假期里学了前半部分Nand2Tetris后,用FPGA做的一个微型计算机项目。在这个过程中,我从最底层的电子电路逐渐向上,认识到一个最简单计算机硬件的全流程。在此后,讨论到现代计算机体系结构时,其中很多概念都可以在这个微型计算机中找到雏形,从而更好地理解计算机体系结构的内容。
![c3f159db6cae257eec9ce8506dfa41fe.png](https://i-blog.csdnimg.cn/blog_migrate/a38a4600aff57d4521684d6012b331c6.png)
![e8c5dcc9a86ef3e53bfa2d11540ec369.png](https://i-blog.csdnimg.cn/blog_migrate/c83df819f859fdc050c8c600d5e56a3b.png)
参考文献
Nand2Tetris: https://www.nand2tetris.org/
![04c1e48305d6183b5b72066c3c6aa1cb.gif](https://i-blog.csdnimg.cn/blog_migrate/0b65b57935d0adc5e92ee665c0689d26.gif)
撰稿人:张亦弛
审稿人:钟宏涛
![1dcd51a2b0d54ddc14cabf887100773c.png](https://i-blog.csdnimg.cn/blog_migrate/ec95db6e12c4fac3e30a3077249b86a0.jpeg)