You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
11 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 18 | 建立数据通路(中):指令+运算=CPU
上一讲我们看到要能够实现一个完整的CPU功能除了加法器这样的电路之外我们还需要实现其他功能的电路。其中有一些电路和我们实现过的加法器一样只需要给定输入就能得到固定的输出。这样的电路我们称之为**组合逻辑电路**Combinational Logic Circuit
但是光有组合逻辑电路是不够的。你可以想一下如果只有组合逻辑电路我们的CPU会是什么样的电路输入是确定的对应的输出自然也就确定了。那么我们要进行不同的计算就要去手动拨动各种开关来改变电路的开闭状态。这样的计算机不像我们现在每天用的功能强大的电子计算机反倒更像古老的计算尺或者机械计算机干不了太复杂的工作只能协助我们完成一些计算工作。
这样,我们就需要引入第二类的电路,也就是**时序逻辑电路**Sequential Logic Circuit。时序逻辑电路可以帮我们解决这样几个问题。
第一个就是**自动运行**的问题。时序电路接通之后可以不停地开启和关闭开关进入一个自动运行的状态。这个使得我们上一讲说的控制器不停地让PC寄存器自增读取下一条指令成为可能。
第二个是**存储**的问题。通过时序电路实现的触发器,能把计算结果存储在特定的电路里面,而不是像组合逻辑电路那样,一旦输入有任何改变,对应的输出也会改变。
第三个本质上解决了各个功能按照**时序协调**的问题。无论是程序实现的软件指令,还是到硬件层面,各种指令的操作都有先后的顺序要求。时序电路使得不同的事件按照时间顺序发生。
## 时钟信号的硬件实现
想要实现时序逻辑电路,第一步我们需要的就是一个**时钟**。我在[第3讲](https://time.geekbang.org/column/article/92215)说过CPU的主频是由一个晶体振荡器来实现的而这个晶体振荡器生成的电路信号就是我们的时钟信号。
实现这样一个电路,和我们之前讲的,通过电的磁效应产生开关信号的方法是一样的。只不过,这里的磁性开关,打开的不再是后续的线路,而是当前的线路。
在下面这张图里你可以看到我们在原先一般只放一个开关的信号输入端放上了两个开关。一个开关A一开始是断开的由我们手工控制另外一个开关B一开始是合上的磁性线圈对准一开始就合上的开关B。
于是一旦我们合上开关A磁性线圈就会通电产生磁性开关B就会从合上变成断开。一旦这个开关断开了电路就中断了磁性线圈就失去了磁性。于是开关B又会弹回到合上的状态。这样一来电路接通线圈又有了磁性。我们的电路就会来回不断地在开启、关闭这两个状态中切换。
![](https://static001.geekbang.org/resource/image/57/c0/57684c12e7bf8ef429220405b0e3bdc0.jpeg?wh=2602*2065)
开关A闭合也就是相当于接通电路之后开关B就会不停地在开和关之间切换生成对应的时钟信号
这个不断切换的过程对于下游电路来说就是不断地产生新的0和1这样的信号。如果你在下游的电路上接上一个灯泡就会发现这个灯泡在亮和暗之间不停切换。这个按照固定的周期不断在0和1之间切换的信号就是我们的**时钟信号**Clock Signal
一般这样产生的时钟信号就像你在各种教科书图例中看到的一样是一个振荡产生的0、1信号。
![](https://static001.geekbang.org/resource/image/6d/93/6dd534a167513c865dfe1921ebb6ae93.jpeg?wh=3322*1135)
时钟信号示意图
这种电路,其实就相当于把电路的输出信号作为输入信号,再回到当前电路。这样的电路构造方式呢,我们叫作**反馈电路**Feedback Circuit
接下来,我们还会看到更多的反馈电路。上面这个反馈电路一般可以用下面这个示意图来表示,其实就是一个输出结果接回输入的**反相器**Inverter也就是我们之前讲过的**非门**。
![](https://static001.geekbang.org/resource/image/d2/ca/d205493f6ff1aeba7a849575285bbeca.jpg?wh=2552*934)
通过一个反相器实现时钟信号
## 通过D触发器实现存储功能
有了时钟信号我们的系统里就有了一个像“自动门”一样的开关。利用这个开关和相同的反馈电路我们就可以构造出一个有“记忆”功能的电路。这个有记忆功能的电路可以实现在CPU中用来存储计算结果的寄存器也可以用来实现计算机五大组成部分之一的存储器。
![](https://static001.geekbang.org/resource/image/dc/de/dc6dcce612b2fd51939d7ec44b3fe1de.jpeg?wh=3502*1135)
我们先来看下面这个RS触发器电路。这个电路由两个或非门电路组成。我在图里面把它标成了A和B。
![](https://static001.geekbang.org/resource/image/7d/a9/7dd38282b8862cb6541ee82e76e1e0a9.jpg?wh=1523*427)
或非门的真值表
1. 在这个电路一开始输入开关都是关闭的所以或非门NORA的输入是0和0。对应到我列的这个真值表输出就是1。而或非门B的输入是0和A的输出1对应输出就是0。B的输出0反馈到A和之前的输入没有变化A的输出仍然是1。而整个电路的**输出Q**也就是0。
2. 当我们把A前面的开关R合上的时候A的输入变成了1和0输出就变成了0对应B的输入变成0和0输出就变成了1。B的输出1反馈给到了AA的输入变成了1和1输出仍然是0。所以把A的开关合上之后电路仍然是稳定的不会像晶振那样振荡但是整个电路的**输出Q**变成了1。
3. 这个时候如果我们再把A前面的开关R打开A的输入变成和1和0输出还是0对应的B的输入没有变化输出也还是1。B的输出1反馈给到了AA的输入变成了1和0输出仍然是0。这个时候电路仍然稳定。**开关R和S的状态和上面的第一步是一样的但是最终的输出Q仍然是1**和第1步里Q状态是相反的。我们的输入和刚才第二步的开关状态不一样但是输出结果仍然保留在了第2步时的输出没有发生变化。
4. 这个时候只有我们再去关闭下面的开关S才可以看到这个时候B有一个输入必然是1所以B的输出必然是0也就是电路的最终**输出Q**必然是0。
这样一个电路我们称之为触发器Flip-Flop。接通开关R输出变为1即使断开开关输出还是1不变。接通开关S输出变为0即使断开开关输出也还是0。也就是**当两个开关都断开的时候,最终的输出结果,取决于之前动作的输出结果,这个也就是我们说的记忆功能**。
这里的这个电路是最简单的RS触发器也就是所谓的复位置位触发器Reset-Set Flip Flop) 。对应的输出结果的真值表你可以看下面这个表格。可以看到当两个开关都是0的时候对应的输出不是1或者0而是和Q的上一个状态一致。
![](https://static001.geekbang.org/resource/image/f6/97/f616e82be0ea0bbcbc8f1e091f53b497.jpg?wh=1284x1284)
再往这个电路里加两个与门和一个小小的时钟信号我们就可以实现一个利用时钟信号来操作一个电路了。这个电路可以帮我们实现什么时候可以往Q里写入数据。
我们看看下面这个电路这个在我们的上面的R-S触发器基础之上在R和S开关之后加入了两个与门同时给这两个与门加入了一个**时钟信号CLK**作为电路输入。
这样当时钟信号CLK在低电平的时候与门的输入里有一个0两个实际的R和S后的与门的输出必然是0。也就是说无论我们怎么按R和S的开关根据R-S触发器的真值表对应的Q的输出都不会发生变化。
只有当时钟信号CLK在高电平的时候与门的一个输入是1输出结果完全取决于R和S的开关。我们可以在这个时候通过开关R和S来决定对应Q的输出。
![](https://static001.geekbang.org/resource/image/9e/d8/9e9bc411aa8c7bf2f080f306a0fb8bd8.jpeg?wh=2642*1175)
通过一个时钟信号我们可以在特定的时间对输出的Q进行写入操作
如果这个时候我们让R和S的开关也用一个反相器连起来也就是通过同一个开关控制R和S。只要CLK信号是1R和S就可以设置输出Q。而当CLK信号是0的时候无论R和S怎么设置输出信号Q是不变的。这样这个电路就成了我们最常用的D型触发器。用来控制R和S这两个开关的信号呢我们视作一个输入的数据信号D也就是Data这就是D型触发器的由来。
![](https://static001.geekbang.org/resource/image/d7/bb/d749acce21756d89c35ee19545cfebbb.jpeg?wh=2522*1445)
把R和S两个信号通过一个反相器合并我们可以通过一个数据信号D进行Q的写入操作
一个D型触发器只能控制1个比特的读写但是如果我们同时拿出多个D型触发器并列在一起并且把用同一个CLK信号控制作为所有D型触发器的开关这就变成了一个N位的D型触发器也就可以同时控制N位的读写。
CPU里面的寄存器可以直接通过D型触发器来构造。我们可以在D型触发器的基础上加上更多的开关来实现清0或者全部置为1这样的快捷操作。
## 总结延伸
好了,到了这里,我们可以顺一顺思路了。通过引入了时序电路,我们终于可以把数据“存储”下来了。我们通过反馈电路,创建了时钟信号,然后再利用这个时钟信号和门电路组合,实现了“状态记忆”的功能。
电路的输出信号不单单取决于当前的输入信号还要取决于输出信号之前的状态。最常见的这个电路就是我们的D触发器它也是我们实际在CPU内实现存储功能的寄存器的实现方式。
这也是现代计算机体系结构中的“冯·诺伊曼”机的一个关键,就是程序需要可以“存储”,而不是靠固定的线路连接或者手工拨动开关,来实现计算机的可存储和可编程的功能。
有了时钟信号和触发器之后,我们还差一个“自动”需求没有实现。我们的计算机还不能做到自动地不停地从内存里面读取指令去执行。这一部分,我们留在下一讲。下一讲里,我们看看怎么让程序自动运转起来。
## 推荐阅读
想要深入了解计算机里面的各种功能组件是怎么通过电路来实现的推荐你去阅读《编码隐匿在计算机软硬件背后的语言》这本书的第14章和16章。
如果对于数字电路和数字逻辑特别感兴趣,想要彻底弄清楚数字电路、时序逻辑电路,也可以看一看计算机学科的一本专业的教科书《数字逻辑应用与设计》。
## 课后思考
现在我们的CPU主频非常高了通常在几GHz了但是实际上我们的晶振并不能提供这么高的频率而是通过“外频+倍频“的方式来实现高频率的时钟信号。请你研究一下,倍频和分频的信号是通过什么样的电路实现的?
欢迎留言和我分享你的疑惑和见解,也欢迎你把今天的内容分享给你的朋友,和他一起学习和进步。