低并发编程 , 周一很颓废 , 周四很硬核
我和小宇早恋了 , 我们家住隔壁 。
本文图片
一、编码与电路——信号的转换
晚上父母会把手机没收 , 但我们还想继续聊天 , 又不敢发出声音 , 于是我们想到了这个办法...
本文图片
我们把所有的中文都用灯泡的亮灭组合来表示 , 同时约定好每隔一秒读取一次灯泡的状态并记录下来 , 这是我们的暗号 。
我:亮亮灭灭亮
喜:灭亮亮灭灭
欢:亮灭亮灭亮
你:亮亮亮灭灭
这样 , 我们虽然没有了手机 , 依然可以日以继日地聊天 , 虽然效率很低 , 但依然很快乐 。
我和小宇就这样在不经意间 , 将语言转换成为了灯泡的亮灭组合 , 这个过程叫做编码 。
二、门电路——信号的关联
我和小宇就这样一直秘密保持着通话 , 直到上了大学 , 父母再也管不了我们用手机了 。
但这么多年的小灯泡通话 , 使我们总觉得事情没那么简单 , 于是我们开始了一些新的探索 。
本文图片
我们增加了一个开关 。 此时当两个开关同时闭合时 , 灯泡才会亮 。
这样两个开关与灯泡之间 , 不再是之前简单的对应关系了 , 而是有了逻辑 。
开关的断开与闭合分别对应着电路的断开与连通 。 而小灯泡的不亮与亮 , 也分别对应着电路的断开与连通 。 那这两者就可以统一 , 不再依赖于具体的实物表现了 。
本文图片
还有 , 开关的连通与断开 , 是主动的 。 而小灯泡的连通与断开 , 是被动的 , 是结果 。
我们把开关这里的连通与断开称为输入端 , 把灯泡的连通与断开称为输出端 , 并且将整个电路都封装在一个图形里 , 可以得到如下抽象:
本文图片
我们决定把这种电路叫做门电路 ,上面这个叫与门 。
为了今后更为抽象的探索 , 我们将电路连通表示为数字 1 , 电路断开表示为数字 0 。
我们将这种表示方式称为二进制 。
输入 A | 输入 B | 输出 |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |

本文图片
慢慢地 , 我们发现了越来越多的玩法 。

本文图片
上面这种电路 , 我把他抽象成如下门电路形状 , 叫做或门 。

本文图片
之后便一发不可收拾 , 我和小宇设计了越来越多的门电路 , 我们发现 , 只要是我们能想到的逻辑关系 , 都可以设计成对应的门电路 。
三、加法器——信号的计算
十进制数可以转换成二进制数 , 而二进制数又可以对应到门电路的输入端与输出端 。
于是我和小宇有了一个大胆的想法 , 能不能设计一个计算加法的电路呢?
我们首先从最简单的一位二进制数相加开始:
0+0=0;0+1=1;1+0=1;1+1=10
变成一张表格如下
加数 A | 加数 B | 加和输出 | 进位输出 |
0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 1 | 0 |
1 | 1 | 0 | 1 |
经过不懈努力 , 终于发现这个电路可以由异或门和与门两个门电路组成 。

本文图片
这个装置实现了二进制的一位加法 , 但它并不完美 , 因为只考虑了这两个数的进位输出 , 但没有考虑上一位的进位 , 所以只能叫半加器 。

本文图片
如果将前一个进位考虑进来 , 只需再多一个半加器 , 并且拼接一个或门即可 。

本文图片
此时我们已经建立好了一个完美的一位加法器 , 并自豪地称之为全加器 。

本文图片
全加器做出来之后 , 无论多少位的加法器就都可以做出来了 , 只需将全加器逐个拼起来即可 。 我们尝试做一个八位加法器 。

本文图片
OK , 大功告成 , 有了加法器 , 理论上就可以实现任何的数学运算了 。
因为我们知道乘法可以转换成加法 , 除法可以转换成减法 , 而减法又可以转换成补码的加法 。 现在我们可以自豪地称这个部件为 , 算术逻辑单元 ALU 。
四、时钟——信号的震荡
我和小宇都非常高兴 , 终于用电路的方式实现了计算功能 。
但慢慢的觉得没什么意思了 , 于是我们又突发奇想 , 设计了如下诡异的电路 。

本文图片
当闭合开关 A 时 , 整个电路联通 , 开关 B 将会被吸下来 , 整个电路断开 , 电磁铁失去磁性 , 开关 B 又会弹上去 , 此时电路又联通 , 开关 B 又被吸下来 。
就这样 , 开关 B 不断地快速地在开和闭之间循环进行 , 而我们始终没有去干预这个电路 , 因此该电路有了自反馈的特性 。
由于开关 B 的来回震荡 , 我们将这种电路称为振荡器 , 由于它可以产生不断变化的电信号 , 就像时钟一样不停且规律地跑着 , 我们将这个装置又称为时钟 。 它所产生的交替的电信号称为时钟信号 。
五、RAM——保存信号
虽然有了加法器 , 但是输入的数字从哪里来?能不能先保存在某个地方呢?
我和小宇经过多次实验 , 发明了一个非常复杂的电路:

本文图片
如果输入端为 1 , 改变"某控制端"信号(信号由 0 变化到 1 这个瞬间) , 则输出端变为 1 , 之后输出端仍然保持(存储)着刚刚的 1 。
如果输入端为 0 , 改变"某控制端"信号 , 则输出端变为 0 , 之后输出端仍然保持(存储)着刚刚的 0 。
如果想不明白也没关系 , 只要记住这个电路的设计 , 实现了一位的存储功能!我们叫它 1 位锁存器 。

本文图片
然后我们把多个锁存器组合起来 , 再加上一些 3-8 译码器 , 8-1 选择器等电路 , 就可以实现一个能保存 8 位二进制的存储器 , 并且可以随机地读写它 ,我们把它叫做 RAM , 简称为内存 。

本文图片
这个组件通过再次组合 , 可以形成 N × M 的 RAM 阵列 。 比如我们可以表示一个 1024 * 8 的 RAM 阵列 。

本文图片
这表示存储容量为 1024 个单位 , 每个单位占 8 位 。
为了更方便地表示 , 我们规定 1024 = 1K , 8 位 = 1 字节(8 bit = 1 byte) , 那么我们就可以说 , 这个 RAM 的存储容量为 1K 个单位 , 每个单位占 1B 。 或者说 , 地址空间为 1K , 存储容量是 1KB 。
此时这个 RAM 模块已经近乎完美了 , 我们甚至可以单独对其进行使用 , 将数据存入某个地址 , 将某个地址中的数据读出 。
怎么方便人操作呢?只需要将地址输入、数据输入、写操作端分别接入一个控制面板 , 由开关来控制这些信号的输入是 1 还是 0 即可 , 然后再将数据输出接入一些灯泡方便观察 , 这样一个单独的可以手动操作的存储装置 , 就搞定啦 。

本文图片
(这里有个小错误彩蛋~聪明的读者发现了吗)
有了可读写的内存 , 我们就可以事先把几个数字存储内存中了 , 接下来 , 我们能否让算术逻辑单元 ALU 自动地读取这个数字 , 进行加法运算呢?
六、程序——自动化
我们先引入一个新的组件 , 10 位计数器 , 这里的 Clk 就接入我们在第四部分讲的时钟信号 , Clr 是清零端 , 具体效果下面动图一目了然 。

本文图片
计数器的输出就是 0 , 1 , 2 , 3 , 4 , 5 , 可以当作内存中的地址 。
我们把这个计数器 , 以及上面讲的 ALU 与 RAM 全部连在一起 , 尝试实现一个可以累积求和的装置 。
我们想计算的是 1+2+3+4+5+6+7, 这个自动化的计算器是这么运行的
1、用控制面板在 RAM 的地址 0~6 处存上 1~7 这几个数字的 , 在上一节已经实现了 。
2、当计数器的值是 0 时 , 数据 1 被输出到加法器进行计算 , 此时加法器 A=1 , B=0 , 计算结果为 1 , 但记住锁存器存储的是上一次的加法器输出 0 , 这次的计算结果要等下一次锁存器遇到上升沿信号 。
3、当计数器的值是 1 时 ,数据 2 被输入到加法器 , 此时锁存器存储了上一次的计算结果 1 , 并将这个 1 输出给小灯泡 , 并同时回传到加法器的B , 所以此时加法器 A=2 , B=1 , 计算结果为 3
4、当计数器的值是 3 时 , 以此类推 , 请看下图

本文图片
我们将累加求和这个过程自动化了!之后如果想计算累加和 , 只需要用控制面板事先在内存里存好数据就可以了!是不是很方便?
七、程序指令
我们还想要更多的自动化!
现在这个装置 , 只能无脑地将 RAM 中的数据从头到尾一直累加下去 , 无法选择加哪个不加哪个 , 也无法选择什么时候停止 。
比如我们 RAM 中的数据是这样的 。
数据(10进制) | |
0x00 | ... |
0x01 | 10 |
0x02 | ... |
0x03 | 20 |
0x04 | 30 |
0x05 | ... |
... | ... |
我们可以再增加一个 RAM , 这个 RAM 里存放的数据 , 表示"指令"的含义!
我们先发明三种指令 。
add:把 RAM 这个位置处的值进行累加
nop:忽略此处的值(也就是什么都不做)
halt:停止(禁止计数器的值加一)
那么要想达到上述功能 , 相应的这个指令 RAM 中的数据应该是这样的 。
注意:下面指令 RAM 的地址和上面数据 RAM 的地址之间有一一对应关系!
地址 (16 进制) | 指令RAM的值 | 指令含义 |
0x00 | nop | 什么都不做 |
0x01 | add | 累加 |
0x02 | nop | 什么都不做 |
0x03 | add | 累加 |
0x04 | add | 累加 |
0x05 | halt | 停止 |
... | ... | ... |

本文图片
遇到 nop 指令(0x00) , 那输出就将锁存器的 W 位禁止 , 不允许锁存器写操作 , 这样累加结果就不会录入 。
再比如遇到输入为 halt 指令(0x05) , 就将计数器的 EN 位禁止 , 不允许计数器 +1 , 这样就达到了停止的效果 。
此时再让时钟信号震荡起来 , 就可以达到有选择地求和过程 , 并且在指定位置悬停 。 那现在我们就让时钟信号震动起来 , 看看这个过程吧 。 (此处只留关键组件)

本文图片
这个控制单元该怎么实现呢?我们知道 , 只要给出输入 , 给出输出 , 任何组件都可以造出来 。 本文就不再展开了 。
有了三个指令 , 我们知道了通过指令这种方式 , 配合各种复杂的控制器 , 即可实现将所有操作统统自动化 。
接下来我们需要做的 , 就是设计控制器 , 以及约定好一大堆指令 , 使得通过这一大堆指令的排列组合 , 可以实现任何自动化的计算操作 。
我们将设计好的一大堆指令
称作指令集
我们将指令排列组合后可以实现的功能
称作程序
我们将指令的排列组合这个过程
称作编程
我们将排列组合这些指令的人
称作程序员
而我们将承载这一切的装置 , 叫做什么呢?
没错 , 这个破玩意 , 就是
计
算
机
原文标题:图解 | 你管这破玩意叫计算机?
来源:低并发编程
【加法器|你管这破玩意叫计算机?】编辑:hxg