ok,今天我们来教大家如何搓一个计算机出来。
众所周知,计算机的架构比较经典的是冯诺依曼架构,即它包含五个部分的东西,
- 输入设备,它用于输入数据或者程序
- 寄存器,用于储存数据
- 运算器,用于各种运算,例如and, or
- 控制器,控制程序执行
- 输出设备,处理结果输出
为了造出计算机,我们首先得知道计算机最基础的组成部分是什么。我们抽象这么一种元器件,它叫做NAND,我们也叫与非门。对于与非门,他有两个输出,一个输入,它判断,当输入两端都为$0$的或者任意一端为1的时候,输出为1,两端输入为1的时候输出为0。我们列出它的真值表
输入1 | 输入2 | 输出 |
---|---|---|
0 | 0 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 0 |
然后我们来认识一个重要的器件,非门。对于非门,它将输入反转,输入为1的时候输出0,输入为0的时候输出1。
我们可以使用两个与非门来创造,思路如下:
它的计算公式为:Y = Not A
我们将1个输入分为两个端子,其次,我们设置两个与非门A,B,并让与非门A的输出端接入到B的端口1。然后让输入1的两个端子一端接到A的端口1,另一个接入B的端口2,那我们就完成搭建了。
然后还有三个更一般的门,分别是:AND,OR,NOR
对于AND,它的真值表如下:
输入1 | 输入2 | 输出 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 0 |
0 | 1 | 0 |
1 | 1 | 1 |
对于OR,有
输入1 | 输入2 | 输出 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 1 |
对于NOR,则有
输入1 | 输入2 | 输出 |
---|---|---|
0 | 0 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
1 | 1 | 0 |
看到这应该发现了,对于NOR和OR,他们的真值表是反过来的,也就是说,只要我们能造出OR门,我们就可以在OR的输出前面加一个非门来得到NOR门,为此,AND门实际上就是将NAND的输出加了非门得到的。但还有另一个比较隐晦的关系,我们引入德摩根定理,德摩根定理告诉我们四个基础逻辑门之间的关系。
我们只需要将NAND的两个输入反转即可得到OR,同样的,将AND的输入反转可以得到NOR,然后NADN和AND,OR和NOR之间通过输入反转可以得到,所以这就是我们的德摩根定理。
然后我们再来建立一个比较重要的逻辑门,它是异或门。
真值表如下:
输入1 | 输入2 | 输出 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 0 |
可以这样搭建:
最后是同或门XNOR,同或门通过对异或门输出加非门实现。
接下来,我们要搭建的是半加器,一个半加器具备两个输出端口,和两个输入端口,一个是和,一个是进位,和指的是相加之后得到的结果,例如1+1=10(二进制) 其中和指的是0的位置,而进位是1。
输入1 | 输入2 | 和 | 进位 |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 0 | 1 | 0 |
0 | 1 | 1 | 0 |
1 | 1 | 0 | 1 |
我们的思路很简单,既然$1+1$产生一个进位和0,而进位只有全1的时候实现,我们用and门就可以了,而除此之外,和的表现就和XOR门一模一样,因此,搭建一个半加器只需要一个XOR门和一个AND门
那么什么是全加器呢,全加器有三个输入,2个输出,输出和半加器一样,一个和,一个进位。但就是因为加了这一个输入,我们的电路更复杂了。
思路如下,三个输入,则有$2^3$个情况,我们先从和的情况入手,和的输入2个则输出0,若3个和1个就输出1,因此我们需要一个电路,他能检测信号个数是否为奇数,若为奇数输出1。那么我们引入两个拓展的门,三路AND门和三路OR门,他们的真值表如下:
输入1 | 输入2 | 输入3 | 输出 |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 |
0 | 1 | 0 | 0 |
0 | 0 | 1 | 0 |
1 | 1 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 1 | 0 |
1 | 1 | 1 | 1 |
而三路OR门如下:
输入1 | 输入2 | 输入3 | 输出 |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 0 | 0 | 1 |
0 | 1 | 0 | 1 |
0 | 0 | 1 | 1 |
1 | 1 | 0 | 1 |
0 | 1 | 1 | 1 |
1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 |
检测原理:
输入111:只需要使用一个3路AND门连接到输出就行。并且产生一个进位。和一个和。
输入110,011,,101
为了检测输入1的个数为2的情况,我们使用三路OR门,但这有一个问题,三路OR门只要有一个输入为1则输出1,因此我们需要一个电路来检测1的个数。即:若为奇数输出1到和,否则输出0到和。
为了达成这个目的,我们从进位下手,我们在输入俩俩之间设置一个二路的AND门,这样就可以检测2个输入是否为1了。若为1AND门输出1,再把三个and门接到另一个三路或门,最后和三路OR门接到XOR门上即可,当输入2个1,则三路或门都输出1,到XOR上输出0到和,这样我们就完成检测了。最后把AND门连接到的三路或门接入进位输出即可。
- 输入只有一个1
这是上面的特殊情况,若只有一个1,则检测进位的AND门输出0,但XOR这时候只有一个1因此输入1到和的输出。
电路原理:
那么我们对于基本的输入已经有了大概的了解,接下来我们加大难度,将输入从2个提高到8个。即,我们把两位的二进制数变成8位的二进制数。
那么我们现在学到的足够让我们开始制作计算机了,首先我们来解决运算器的问题。
我们来搭建一个一位的解码器
一个一位解码器可以让我们切换线路,例如0控制一个状态,1控制一个状态。那么这时候存在两个输出口,一个输入口,电路如下:
当输入0的时候,通过非门就会在输出端口2输出1,否则在输出1输出。
接着我们搭建一个3位的解码器,他让我们可以通过3个输入在8个线之间控制8个状态。只需要用到简单的非门和AND门就行了。因此,对于一个给定的3位输入,我们可以控制8个输出哪里输出1,不存在有两根或以上的线输出1的情况,也就是说我们得到了一个选择器,可以选择8条线任意一条线输出1。
思路:我们列举出8个二进制数,然后根据输入去控制具体哪个线是1。由于有3个数,8种状态,若3个端口输入一个1,则我们在其他两个输入0的端口接非门,这样就能使得三路AND门输出1了,这就是我们能够把输入翻译成选择一个线输出的原理。
输入只有一个1的电路:
输入为2个1或3个1的电路:
合起来就是我们要找的3位解码器了。
接下来,我们建造一个可以通过给定的输入来进行特定运算的元件。
我们接下来要建造的元器件叫算术引擎,它可以根据我们给出的指令码,来对输入的两个字节进行 OR,NOR,AND,NAND, ADD,SUB的情况。
我们把刚才得到的三位解码器给画成一个元器件,我们的思路是:通过一个3位解码器,在6个输出口接入相关的算术电路来实现根据对应的指令做运算。
电路如下:
每个运算有3个输入口,连接到解码器(Decoder)的是开关,也就是说,只有当解码器给8个端口中的某一个输出1时,才能做我们想要做的运算,否则因为开关的原因不会输出数据。那么我们完成了运算器的实现。
到了这里,实际上我猜你也大概知道了控制器(指令解码器)的原理了,没错、指令解码器也是相同的原理,不过它实现的指令和我们刚才做的运算器不一样,我们要实现的只有4种,即:立即数、计算、复制、条件这四种指令
- 立即数:给程序赋值
- 计算:连接到运算器,若发送计算指令,则根据指令让运算器计算数据然后输出
- 复制:把一个数复制到另一个地方
- 条件:若满足条件则干预先决定的程序。
那么它实际上就是一个二位的解码器,因为二位解码器刚刚好就只有4个状态。
那么我们现在来解决其他问题,我们需要一个器件来储存我们的数据,即寄存器。那么一个D触发器可以看成是一个一位的寄存器。它可以保持一刻的值。也就是说它可以保持一个时钟周期不变。但这不够,我们开始搭建一个可以储存更多时刻的寄存器。并且它应该满足有两个输入,一个是启用写入,一个是待写入数据和一个输出。当我们对启用写入输入1时,则寄存器开始写入数据。
为了搭建,我们默认D触发器只有一个输入和一个输出,我们舍去时钟线和另一个输出。
电路:
$S_{enable}$指的是是否启用数据写入。
我们将他分为两部分,保存和写入新数据,如果不写入数据,则我们构造一个电路使得它会一直保存输出的值。在D触发器那边,若不启用写入,则N门开启,D触发器前面的开关启用,这时候输出的值返回到开关,经由开关直接输入到D触发器中使得它一直保持电平。
现在我们已经有一个可以粗存一个字节的触发器了,我们来继续写一个可以一次性储存8个字节的寄存器。
这次我们更进一步,我们选择的输入有2个,第一个输入控制输出是否要读取我们给的数据,第二个输入是控制8个寄存器的写入开关的。为此我们引入一个器件,分线器,它将一个8位输入划分为8个输出。然后再用一个集线器,将一个8位的二进制输入转为一个输出,这个输出是二进制转为10进制的表示。这是为了方便我们了解原理。
电路:
那么我们就得到了一个可以存储8位二进制的寄存器了。它拥有三个输入,分别是读取、写入、待写入数据,有一个输出,当读取启用时输出寄存器中的数据
为了制作计算机,一个寄存器肯定是不够用的,我们制作一个神秘的小盒子,它由4个寄存器和2个选择器组成,它的出现可以让我们在特定的指令下指定一个寄存器储存数据。
那么我们分别使用两个一位解码器控制使用特定寄存器,我们把4个寄存器分为2组A,B,其中一个组有2个寄存器A0,A1和B0,B1。然后定义另一个输入来控制读取和写入开关。最后定义一个8位输入来写入所需数据。
思路和选择器一样,我们利用大量的AND门来控制选择我们要的寄存器。
首先,我们构造一个能够根据指令选择特定寄存器执行命令的元件。
我们简化这个元件,并称为Dec of 4 reg(4寄存器选择器),接着,由于我们的8位寄存器有4个,并且每个寄存器有3个输入口,我们先处理读写口。
我设计了一个功能,由一个控制读取和写入的输入口和8个and门组成的,将4个and门的一个口连接到选择器上,再将另一个口接入读取开关的口上,这样我们就实现了特定寄存器的读取功能,写入也是一样的想法,它的电路如下:
然后我们等效为一个元器件Swich of regs ,它实现我们刚才得到的电路功能。它有8个输出口,分别是控制4个寄存器读取的功能和4个寄存器写入的功能。
最后,若我们想要读取某个寄存器里面的数据,只需要把读取线接到输出,这样读取开启的时候我们就能读取到特定的寄存器里的数据了。
那么我们就实现了数据的读取功能
寄存器到这里就告一段落了。接着,我们已经有了能通过指令做特定运算的算术引擎,还有了编码器可以解释指令。最后我们还差一个东西,也就是可以让我们在任意地方做任意操作的一个东西。即跳转,我们要来实现跳转,一旦有了这个,就代表我们能够造出图灵完备的计算机了。
那么我们马上开始下一个工作,建造一个能执行if指令的器件出来。
这个元件的输入为1个数值和3个条件位,我们规定,根据输入的3个条件,若判断为真,则输出1,否则输出0.
三位代码 | 代表的条件 |
---|---|
000 | 恒输出0 |
001 | value =0 |
010 | value < 0 |
011 | value$\leq$0 |
100 | 恒输出1 |
101 | value $\neq $0 |
110 | value $\geq $0 |
111 | value >0 |
现在我们来设计一个电路可以实现这样的功能。
我们让条件输入控制开关,按照条件的严格性排序。最宽松的限制比较严,悠闲判断最严的条件。
注意后4个条件,这4个条件和前面没有关系,所以我们为了判断它,我们可以单独的让第一个位置1或0去判断,那么我们就把条件限制到前2个位,只需要做4个判断即可。因为这时候只有4种状态。而这分割的四个条件都是互补的,我们先做完一半的判断电路。
那么,首先我们把输入给的数字全部转为1位二进制,那么只需要利用分线器去把线引出来用OR门接一起就行了,所以我们实际并不是判断大小,而是看输出的是否为0,不同的点就在于任意的数字输出的一定是高电平,而只有0输出低电平。所以我们的判断是很明显的了,对指令101,我们判断是否输出高电平,对110,判断是否高,低电平至少存在一个。我们从001开始,这种情况最简单,我们给输出接非门,在指令给出001时控制开关开启,这样就能直接判断value =0的情况了、
接着,对101,我们只需要和0做判断即可,在001的开关输出接一个非门,若001开启,则非门关闭,但指令接在的开关位上是恒定的,因此,开关开启,但由于非门,输出0,反之由于非门的存在001输出0的时候说明不是低电平,这时候输出高电平,实现value $\neq $0的判断。
而对value $\geq $0,我们只需要保证每个开关都输出0即可,因为我们一开始留下的非门就会输出1,这时候就一直拉高电平。
最后,对于value $>0$的情况,在选择器输出111的时候开关输出1就行了,否则我们一开始留下的非门会控制它输出0。
电路:
问题来了,负数的情况怎么办呢?这个留到下个章节我们继续。在下个章节,我们讲述计算机如何做负数的运算,并且我们会把计算机组装起来。