数字电路,嵌入式技术之数字电路,从零搭建一台计算机,设计自己的MCU

数字电路基础

引言

数字电路是现代科技和工程领域中不可或缺的基础。从计算机系统到通信设备,从家庭电子产品到工业自动化,数字电路无处不在,影响着我们的生活和工作。本章节旨在向读者介绍数字电路的基本概念、原理和应用,为后期学习单片机开发打基础。

二进制数据表达

二进制简介

二进制是一种数字表示法,它使用两个不同的数字符号,0和1,来表示数值。下面是一些二进制的基本概念:
1)位(bit)
不同于十进制中,每个位可以用0-9表示,二进制中每位都是0或1,每个二进制位我们称之为一个bit,可以表示两种状态。位一般用b代替,例如8b代表8个bit。
2)字节(byte)
8bit组成一个字节,总共可以表示256种状态。字节一般用B代替,例如8B一般表示8个字节。
3)其他计量单位(K,M,…)
K:表示2的十次方,例如,1KB就是1024B
M:表示2的20次方。例如:1MB就是1048576B
G:表示2的30次方。
T:表示2的40次方。
比这些更高的计量单位,尽管有定义,但是现实中我们很少用到,这里就不再赘述。
二进制是计算机科学的基础,现实中的绝大部分信息都可以完全数字化,并进一步用二进制来表示和存储。

用二进制表达文字

用二进制表达图片

图片数字化的过程,分为几步:
1)图片像素化
现实中的图片是连续的,但如果想用数字表示图像,就需要将一张完整的图像,横纵向分别切成很多份,从而拆成一个一个像素点,每个像素点是一个色块,而横纵切分的份数,我们称之为分辨率。例如,我们将一张4比3比例的图像,横向拆成800份,纵向拆成600份,那么我们就将一个图像拆成了480000个像素,而这张图像的分辨率为800x600。由此可见,图像的分辨率越高,图像就越清晰。
2)像素数字化
每个像素就是一个颜色块。具体的颜色,可以用红绿蓝三原色调配成。而每种原色,就可以用一个数字表示其深浅。例如,我们用一个字节(0-255)表示一种颜色的深浅,那么一个像素点就可以用三个字节表示。颜色的深浅是连续的,用0-255这种级别如果不够精确,我们可以选择用两个字节表示一个颜色(0-65535)。像素的深浅级别我们称为“位深”,例如0-255为8位深,0-65535是16位深。位深越大,颜色切分越连续,越不容易出现色阶。现今网络上的图像一般都是32位深。
在这里插入图片描述

用二进制表达声音

声音的数字化和图像的过程非常类似:
1)采样
声音来自于物体的振动,是一种连续的波形。我们如果想用数字表达声音,还是要将其离散化。首先我们将一段时间(例如1秒钟)的连续声波分成很多份,并将每一份记录一个平均振幅。拆分的份数,我们称之为采样率。拆分过程中必然会产生信息丢失,而采样率越大,信息丢失越少。目前比较常用的采样率是44100HZ或者48000HZ。
2)量化
每个采样我们还要用一个数字表示它的高度(振幅),这又是一个连续量离散化的过程,跟图像的颜色深浅类似,我们也可以用0-255或者0-65535表示振幅的高度。这个数字的取值范围叫做声音的位深。目前通用的位深一般是16bit或者24bit。

在这里插入图片描述

用二进制表达视频

视频就是连续的图片和声音。当然,如果只是耿直地将图片拼接到一起,那么视频的体积会非常大。实际上视频是通过一种有损压缩将很多图片压缩在一起。目前比较流行的视频编码标准为H.264、H.265、AV1。
在这里插入图片描述

数字电路

将信息数字化后,我们可以方便的用二进制表示信息。而现代计算机技术的基础——数字电路,研究的就是如何用电表示二进制。其实这件事非常简单,例如下面的一个电路图:
在这里插入图片描述

当开关闭合,信号输出就有5v电压;当开关断开,由于下拉电阻存在,信号输出电压变为0,我们可以将信号输出的高电平态视为1,低电平态视为0,即可用电表示二进制。所以数字电路就是对电路进行定性分析(高或者低),而不用去考虑精确电压是否会有误差。

数字电路仿真软件——Digital

为了学习数字电路,给大家介绍一款数字电路拟真软件。
官方网站:https://github.com/hneemann/Digital
最新版下载地址:https://github.com/hneemann/Digital/releases/download/v0.30/Digital.zip
该软件运行需要Java环境,下载地址为:https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.msi
安装完成后打开digital,看到如下页面即安装成功:
在这里插入图片描述

基础逻辑门电路

基础逻辑电路重点掌握表述该逻辑的四种方式:逻辑表达式、真值表、逻辑符号、口诀。
1)非门
(1)从“组件——输入输出”中,选择一个输入和一个输出;
(2)从“组件——导线”中选择一个地、一个电源和一个下拉电阻;
(3)从“组件——开关”中选择一个继电器;
(4)右键点击输入,标签输入A;
(5)右键点击输出,标签输入B;
(6)右键点击继电器,勾选“闭合继电器”;
在这里插入图片描述

按照如下电路图连接:
在这里插入图片描述

连接完成后,选择快捷菜单栏的仿真键开始仿真。

开始仿真后,尝试改变A的输入电平,观察B输出的电平变化。可以看到AB的信号关系如下:
A B
0 1
1 0
这种输入信号和输出信号完全相反的电路,叫做非门,上面的这张0-1关系表,我们称之为真值表。Digital软件也可以根据电路自动生成真值表:
选择“分析——分析”可以得到下图。
在这里插入图片描述

可以看到Digital软件不但给出了AB的真值表,还给出了B和A的逻辑关系。
在今后我们使用非门时,不会再画该电路,而是直接采用“组件——逻辑”里面的非门,如图示:非门逻辑符号。
在这里插入图片描述

口诀:输入与输出相反。
2)与门
(1)从“组件——输入输出”中,选择两个输入和一个输出;
(2)从“组件——导线”中选择两个地、一个电源和一个下拉电阻;
(3)从“组件——开关”中选择两个继电器;
给输入编号A、B,给输出编号C,按照如下图连接:
在这里插入图片描述

开启仿真,观察C与AB之间的关系,可得真值表如下图所示:
在这里插入图片描述

在今后我们使用与门时,不会再绘制该电路,而是直接采用“组件——逻辑”里面的与门,如图示:与门逻辑符号。
在这里插入图片描述

与门口诀:有0出0,全1出1。
3)或门
(1)从“组件——输入输出”中,选择两个个输入和一个输出;
(2)从“组件——导线”中选择两个地、一个电源和一个下拉电阻;
(3)从“组件——开关”中选择两个继电器;
给输入编号A、B,给输出编号C,按照如下图连接:
在这里插入图片描述

开启仿真,观察C与AB之间的关系,可得真值表如下图所示:
在这里插入图片描述

在今后我们使用或门时,不会再绘制该电路,而是直接采用“组件——逻辑”里面的或门,如图示:或门逻辑符号
在这里插入图片描述

口诀:有1出1,全0出0。

其他门电路

与或非三门我们一般称之为基础门电路,而其他门电路可以用这三种门电路组合得到。下面我们给出一些门电路真值表以及用与或非三门的组合实现。(这些实现并非最简实现,我们的这门数字电路基础,更多只考虑可行性,不考虑优化)
1)异或门
真值表和电路图:
在这里插入图片描述

简化符号:
在这里插入图片描述

口诀:奇数出1,偶数出0。
2)与非门
真值表和电路图:
在这里插入图片描述

简化符号:
在这里插入图片描述

口诀:全1出0,其他出1。
3)或非门
真值表和电路图:
在这里插入图片描述

简化符号:
在这里插入图片描述

口诀:有1出0,全0出1
4)异或非门
真值表和电路图:
在这里插入图片描述

简化符号:
在这里插入图片描述

口诀:相同出1,相异出0。
5)逻辑门电路总结
在这里插入图片描述

运算器

1)半加器
现在我们来考虑如何用电路来实现1位加法。假如有两个1位二进制数A、B,它们的和为1位二进制数S,那么存在如下枚举:
(1)如果A=0,B=0,那么S=0;
(2)如果A=1,B=0,那么S=1;
(3)如果A=0,B=1,那么S=1;
(4)如果A=1,B=1,那么S=0,而且产生一个进位C=1。
也就是说,对于加法运算,应该两个输入A和B,两个输出S和C,S表示和,C表示是否产生进位,也就是说真值表如下图所示:
在这里插入图片描述

其中A、B与S的关系为异或,与C的关系为与,得到如下电路图:
在这里插入图片描述

2)将半加器封装成一个组件
(1)将电路另存到Digital的安装目录的lib中
在这里插入图片描述

(2)保存半加器电路到class_code目录
在这里插入图片描述
在这里插入图片描述

(3)使用封装好的电路(如果没有,需要重启软件)

在这里插入图片描述
在这里插入图片描述

3)加法器
半加器实现一位全加器。
半加器只能处理两个数的运算,但在实际计算中,如果我们做加法的两个位不是最低的位,那就要考虑低位可能产生的进位。也就是说,一个功能完整的1位加法器,应该考虑三个输入,除了A、B两个加数,还要考虑是否存在低位进位Cin。
为了处理三个数相加,我们可以用两个半加器分别处理两个累加过程,同时两个累加器产生的进位C1和C2,不可能同时为1,(因为如果C1位1,那么S1=0,S1=0,C2不可能为1),且二者只要有一个为1,则输出进位为1,所以关系为或,最终呈现的电路如下:
在这里插入图片描述

其中的黄色方块为子电路,内容就是我们上面实现的半加器。
4)4位加法器
多位加法器就是多个加法器的串联,下面我们以4位加法器为例来展示多位加法器电路:
添加输入Cin,A,B,右键点击A和B,将位数调整为4;
添加两个“组件——导线”中的分裂器,右键点击将其设置如下图所示。
在这里插入图片描述

将分裂器输入端与A、B相连,并添加8个“组件——导线”里面的隧道,分别命名为A0、A1、A2、A2、B0、B1、B2、B3,并接在分裂器的输出端,如图示:
说明:图中A0管道是给标签名起名位A_0来实现的。
在这里插入图片描述

添加输出S,并将位数调整为4,并添加分裂器,输入/输出设置为1,1,1,1/4,并将输出端连接到S,输出端连接四个隧道S0、S1、S2、S3,如图示:
在这里插入图片描述

添加四个加法器子电路,并将进位由低到高串联,如图所示:
在这里插入图片描述

添加12条隧道,分别与A、B、S的隧道对应,连接到加法器的输入端,如图示:
在这里插入图片描述

最后添加输入Cin和输出Cout,最终效果如图:
在这里插入图片描述

今后我们在用到多位加法器时,不会再次绘制该电路,而是会直接使用“组件——运算器”中的加法器,如图示:
在这里插入图片描述

锁存器和触发器

目前我们画了很多计算电路,这些电路都有一个特点,当输入信号发生变化,输出信号会立刻变化,无法对状态进行存储。这在某些情况下会给我们的计算带来麻烦,例如我们想计算这个算式:25+37+12-9+8。在没有状态存储的情况下,我们只能用4个计算电路串联得到最终结果,这显然是不现实的。
接下来的一系列电路,我们要给大家描述的是,如何储存电信号。

SR锁存器

1)SR锁存器的电路结构:
SR锁存器(set-reset-Latch)是静态存储单元中最基本、也是电路结构中最简单的一种电路。SR锁存器可以有两种构成方式。方式一,由两个或非门构成。方式二,由两个与非门构成。两种方式构成的SR锁存器功能相同。此处,我们以与非门构成的SR锁存器为例进行介绍。
2)由两个与非门组成的SR锁存器的工作原理
在这里插入图片描述

总结:
(1)当S为1,R为1, Q和Q状态不变; (2)当S为0,R为1, Q=1、Q=0;(置位)
(3)当S为1,R为0, Q=0、Q=1;(复位) (4)当S为0,R为0,电路无意义 S输入 R输入 Q输出 Q输出
1 1 维持不变 维持不变
0 1 1 0
1 0 0 1
0 0 1(无意义) 1(无意义)

带en输入的D锁存器

上面的SR锁存器尽管可以锁住输出状态,但是我们没法控制设置输出的时机。所以我们给这个电路加上一些其他组件。
1)电路结构:锁存器的基础上,增加一个触发信号输入端。
在这里插入图片描述

2)工作原理
(1)En = 0时,输出保持不变
在这里插入图片描述

En=0,与非门特性,G3输出S1=1,G4输出R’=1,因为S’、R’低电平有效,此处都为1,处于高电平,既不置0也不置1,输出保持不变。
(2)En=1,正常SR锁存器的功能
在这里插入图片描述

当en=1时,与非门G3输出就是S’,与非门G4的输出就是R’,正好是之前讲过的SR锁存器的输入。
3)真值特性表
在这里插入图片描述

4)带En输入的D锁存器
当en为1时,如果我们想将Q设为1,此时S应为1,R应为0;反之,如果我们想将Q设置为0,S应为0,R应为1。由此可得到,SR恰好为反相输入时,可以顺利的设置Q,所以我们对电路稍加改造,将S改名为D,并将其反相输入到R,如图示:
在这里插入图片描述

我们就得到了一个带en输入的D锁存器,其特性为:当en为高电平,Q和D的输入保持一致;当en为低电平,Q保持之前状态不变,从而起到存储作用。
(1)工作原理:
 En=0
在这里插入图片描述

 En=1,D=0
在这里插入图片描述

 En=1,D=1
在这里插入图片描述

总结:当en为1时,输出Q的值与D输入的值相同。
问题:当en为1时,D的输入直接影响Q
的输出,为了提高触发器的可靠性,增强抗干扰能力,希望触发器的次态仅仅取决于en的下降沿(或上升沿)到来时的输入信号状态。D触发器可以解决以上问题。

边沿触发的D触发器

D锁存器尽管可以起到保存数据的作用,但是当en信号为1时,D输入和Q输出相当于是联通的,此时如果D信号有波动,Q会跟随波动。我们希望能得到更稳定的输出Q,不希望en高电平时Q随D波动,而是希望Q只在en信号由0变成1的一瞬间随D输入变化,其他时间都保持不变。看下面的电路图
在这里插入图片描述

其中输入C为时钟输入,添加路径为“组件——输入输出”里面的时钟输入。关于时钟的作用,我们在之后计算机组成原理里面再为大家详细介绍,现在我们可以仅仅把时钟输入当作一个普通输入来用。
在这里插入图片描述

1)工作原理
(1)C=0
在这里插入图片描述

(2)C=1
在这里插入图片描述

(3)C从10
在这里插入图片描述

(4)C从01
在这里插入图片描述

开启仿真后,经过实验我们得知,C信号不论是高电平还是低电平,Q都不会随D变化;只有当C信号由0变1的一瞬间,D的值能够传递到Q。这个电路我们称为上升沿触发的D触发器。其实对电路稍作改变,我们还能得到下降沿触发的D触发器,但之后我们如果不做特殊说明,凡是用到D触发器,都是指上升沿触发器。
今后我们如果使用D触发器,不会再绘制该电路,而是直接使用“组件——触发器”中的D触发器。有一点需要注意,Digital软件中的D触发器,Q输入和标准触D发器是反相的,所以我们将Q`当作Q来用,如图示:
在这里插入图片描述

寄存器

D触发器可以在时钟上沿存储1bit数据,如果我们想存储多个bit的数据,就需要用多个D触发器并联实现,这种电路我们称之为寄存器。以4bit寄存器为例,看如下电路:
在这里插入图片描述

4bit输入D会在时钟输入上升沿存储到Q。再给这个寄存器加上en信号,最终效果如图:
在这里插入图片描述

今后我们只用寄存器时,不会在绘制该电路,而是直接使用“组件——存储器”中的寄存器,如图示:
在这里插入图片描述

计算机组成原理

冯诺依曼模型的计算机

由科学家冯诺依曼提出的模型理论。基于通用图灵机建造的计算机都是在存储器(内存/寄存器)上存储数据。鉴于程序和数据在逻辑上是相同的,因此程序也能存储在计算机的存储器中。
1)冯诺依曼模型的四个子系统
(1)存储器:用来存储数据和程序的区域
(2)算数逻辑单元(ALU):用来进行计算(算数运算、逻辑运算、位运算等)的地方。
(3)控制单元:对存储器、算数逻辑单元、输入/输出等子系统进行控制操作。
(4)输入/输出单元:输入子系统负责从计算机外部接收输入数据,输出子系统负责将计算机处理结果输出到计算机外部。
2)冯诺依曼模型-存储程序概念
冯诺依曼模型要求程序也必须存储在存储器(内存)中,现代计算机的存储单元用来存储程序和数据,这意味着程序和数据应该有相同的格式,实际上他们都是以位模式(0和1)存储在内存中。
3)冯诺依曼模型-指令的顺序执行
冯诺依曼模型中的一段程序是由一组数量有限的指令组成。
控制单元从内存中提取一条指令,解释指令,接着执行指令,也就是说指令是一条接着一条顺序执行的。

计算机组成部件

计算机组成部件可以分为三大类。
(1)中央处理单元(CPU)
(2)主存储器(内存)
(3)输入/输出子系统
在这里插入图片描述

中央处理单元CPU

CPU用于数据的运算【算数逻辑单元(ALU)、控制单元、寄存器组】。
在这里插入图片描述

1)CPU中的算数逻辑单元
算数逻辑单元:对数据进行逻辑、移位和算数运算。
(1)算术运算:整数和浮点数的加减运算。
(2)位移运算:逻辑移位运算和算术移位运算。
(3)逻辑运算:非、与、或、异或,这些运算的输入数据为二进制模式,运算结果也是二进制模式。
2)CPU中的寄存器
寄存器:用来存放临时数据的高速独立存储单元。
(1)数据存储寄存器:保存运算的中间结果。
(2)指令存储器(IR):CPU从内存中逐条取出指令,并存储在指令存储器中,解释并执行指令。
(3)程序计数器(PC):保存当前正在执行的指令地址,当前指令执行完成后,计数器自动加1,指向下一条指令的内存地址。
3)CPU控制单元:
控制单元:控制各个子系统的操作,控制是通过从控制单元到其他子系统的信号来进行的。

内存

内存是存储单元的集合,每个存储单元都有唯一的标识,称为地址。
数据以“字”的形式在内存中传入传出,字可以是8位、16位、32位、64位。如果字是8位,一般称为一个字节。

1)内存类型
主要有两种类型:RAM和ROM。
(1)随机存取存储器(RAM)
特点:系统断电后,信息(程序或数据)丢失。
(2)只读存储器(ROM):里面的数据由制造商写进去,用户只能读不能写
特点:系统断电数据不会丢失。常用来存储那些在开机时运行的程序。
(例如:系统开机时的引导程序)。
2)存储器的层次
在这里插入图片描述

3)高速缓冲存储器
存储数据的速度比内存快,比寄存器慢。通常容量较小,被置于CPU和主存储器(内存之间)。
4)CPU和存储器的连接
CPU与主存储器之间通常由称为总线的三组线路进行连接。他们分别是:数据总线、地址总线、控制总线。
(1)数据总线:由多根线组成,每根线每次传送1个位的数据。线的数量取决于计算机字的大小。例如:计算机的字是32位(4个字节),那么需要32根线的数据总线,以便同一时刻同时传送32位的数据。
(2)地址总线:允许访问存储器中的某个字的。地址总线的线数取决于存储空间的大小。例如:存储器的容量为2的n次方个字,那么地址总线一次需要传送n位的地址数据,因此需要n根线。
(3)控制总线:负责传送指令的。例如:如果计算机有2的m次方条控制命令,那么控制总线就需要有m根。
在这里插入图片描述

输入/输出(I/O)系统

可以使计算机与外界进行通信,并在断电情况下存储程序和数据,分为两大类:非存储设备和存储设备。
(1)非存储设备:键盘、鼠标、显示器、打印机等。
(2)存储设备:也称为辅助存储设备,通常有磁介质和光介质两种。
特点:便宜,断电后数据不丢失。
1)I / O 设备的连接
(1)输入/输出设备不能直接与CPU和内存的总线相连接,因为输入/输出设备本质与CPU和内存的本质不同,输入/输出设备都是磁性或光学设备,而CPU和内存是电子设备。与CPU和内存相比,输入/输出设备的数据读取速度要慢的多,因此必须要有一个中介来处理这种差异,输入/输出控制器。
(2)输入/输出控制器:连接输入/输出设备到总线上,每一个输入/输出设备都有一个特定的控制器。
(3)I/O设备的连接控制器:控制器清除了输入/输出设备与CPU以及内存在本质上的障碍,控制器可以是串行或并行的设备。
 串行控制器:只有一根数据线连接在设备上。
 并行控制器:有多根数据线连接到设备上,一次能同时传送多个位。
(4)常用控制器:SCSI、火线、USB和HDMI。

程序的执行

通用计算机使用程序的一系列指令来处理数据,通过执行程序,将输入数据转换为输出数据。程序和数据都放在内存中。
(1)机器周期。
(2)输入/输出操作。
1)程序执行:机器周期
CPU利用重复的机器周期来执行程序中的指令,一步一条,从开始到结束。
一个周期包括3步:取指令译码执行。
在这里插入图片描述

2)取指令
CPU的控制单元命令系统将下一条将要执行的指令复制到CPU的指令寄存器中,被复制的指令地址保存到程序计数器中,复制完成后,程序计数器自动加1,指向内存中的下一条指令。
3)译码
当指令置于指令寄存器后,该指令将由控制单元负责译码,指令译码的结果是产生一系列系统可执行的二进制代码。
4)执行
指令译码完毕后,控制单元发送任务命令到CPU的某个部件,例如:控制单元告知系统,让它从内存中读取数据。这就是执行阶段。

不同的体系结构

1)CISC(复杂指令集计算机)体系结构
设计策略:是使用大量的指令,包括复杂指令。
优点:程序设计更容易,因为每个简单或复杂的任务都有一条对应的指令。程序员不需要写一大堆的指令去完成复杂的任务。
缺点:指令集的复杂性使得CPU和控制单元电路非常复杂。
优化方案:程序在两个层面上运行,CPU不直接执行机器语言指令,CPU只执行被称为微操作的简单操作,复杂指令被转化为一系列简单操作后由CPU执行,使用微操作的程序设计被称为微程序设计。
应用:英特尔公司开发的奔腾系列CPU。
2)RISC (精简指令集计算机)体系结构
设计策略:是使用少量的指令完成最少的简单操作。
缺点:程序设计更难,复杂指令需要用简单指令模拟。
3)流水线
计算机对每条指令使用取指令、译码、执行三个阶段,早期计算机每条指令的这三个阶段需要串行完成,现代计算机使用流水线技术改善吞吐量(单位时间内完成的指令总数)。
如果控制单元能同时执行两个或三个阶段,那么下一条指令就可以在前一条指令完成前开始。
在这里插入图片描述

简单计算机

为了解释计算机的体系结构和指令处理,引入一台简单(非真实)计算机。简单计算机有三部分组成。
(1)CPU
(2)存储器
(3)输入/输出(I/O)系统

设计简单的单片机

实现一个ALU

ALU(算术逻辑单元)是计算机中的一种关键组件,负责执行算术运算和逻辑运算。它是中央处理器(CPU)内部的一个重要功能模块,用于处理数据和执行指令。简单的ALU,可以看作是计算电路的大杂烩,例如,接下来我们打算实现一个ALU,它能够实现A、B两个16bit数字之间的加法、减法、按位与和按位或运算,同时有一个ZF(Zero Flag)指示A、B两数是否相等。
1)选择器
在ALU中我们会用到复用器,两相复用器原理和封装如下图:
在这里插入图片描述

输出Q会跟随sel来决定是取输入A还是输入B。而四相复用器就是两相复用器的叠加:
在这里插入图片描述

今后我们在使用多相复用器时,不会再绘制该电路图。
2)8位ALU实现
查看如下电路:
在这里插入图片描述

各个组件数据位宽如下:
 A、B、S、加法器、减法器、与门、或门、两个移位器、8选1复用器为8bit;
 Cin、Cout、2选1复用器为1位;
 sel为3位。
该电路可以求A、B两数的加,减,按位与和按位或,最终的输出取决于sel。映射关系如下表:
sel 结果
0b000 A
0b001 B
0b010 A+B
0b011 A-B
0b100 A&B
0b101 A|B
0b110 A<<B
0b111 A>>B

使用ALU和寄存器实现计算

现在我们希望使用刚刚创建的ALU来计算一下13+6+27+6,实现的步骤如下:
(1)A输入13,B输入6,然后可以看到S是累加后的结果。
(2)记录S的结果,并将其输入到A,在B输入27,那么S的结果就是13+6+27,记录这个结果。
(3)将上一步S的结果输入到A,B输入6,最终得到S结果 13+6+27+6。
在这里插入图片描述

上面的操作需要我们每次手动记录S运算后的结果,用这个结果作为输入进行下一步骤的计算。如果我们给ALU添加存储功能就可以解决人工记录的问题。
接下来我们将ALU和寄存器连接在一起,来实现一个简单的计算单元。
 添加一个寄存器组件,并设置为8位;
 添加一个刚刚实现的ALU组件;
 从“组件——输入输出”添加一个按钮组件;
 从“组件——导线”添加三个常量组件;
 添加一个输入和输出,并设置为8位,数据模式设置位decimal。
按照下图连接电路:
在这里插入图片描述

注意:与sel连接的常量设置为3bit,值为2,表示我们暂时只算加法。接下来我们来尝试计算13+6+27+6,开启仿真后,执行如下步骤:
 B输入13,拍下按钮。
 B输入6,拍下按钮。
 B输入27,拍下按钮。
 B输入6,拍下按钮。
最终可以看到寄存器输出显示52,即是此加法算式的结果。在刚刚的计算过程中,我们拍按钮的动作模拟的是计算机中的时钟信号。时钟信号就是计算机电路中的节拍器,由晶振统一产生,CPU的计算就是以时钟信号为单位进行的,时钟信号频率越高,CPU计算速度越快,但相对的单位时间内电路中的电流就越大,发热也越大,这也是现实中限制CPU提升频率的一个重要因素。

添加一块数据存储

刚刚的这个累加过程,我们完全是通过手动操作进行的。而现实情况是,我们的计算一般都是通过预设的程序自动完成。而预设的程序,需要一个外部存储。对于一般的个人计算机来说,这个存储是内存和硬盘,但是此时我们为了简化计算机的外部架构,专注于CPU内部设计,我们就统一只使用一种外部存储,一块由多个8bit寄存器组成的内存。

用寄存器实现一块内存

目标:实现一块内存,存储空间是8*8bit。
实现思路:添加8个寄存器(8bit),并给每个寄存器编号(地址),通过地址操作对应的寄存器存取数据。8个寄存器就要有8个寄存器的地址,可以用3位数据来控制。因此,需要先创建一个3-8译码器。通过3位的数据来控制8个内存地址。
 添加8个寄存器,并设置为8bit。
 添加输入A(地址位),str(写允许),Din(数据),ld(输出允许),将A设置为3bit,Din设置为8bit。
 添加输出Dout,设置为8bit。
 添加下拉电阻。
 添加复用器,数据位数设置为8,选择位数设置为3。
 从“组件——导线”中添加驱动器,驱动器就相当于一个继电器模块。
首先将时钟输入,数据输入和数据输出按照如下方式连接:
在这里插入图片描述

数据输入直接并联了8个寄存器,此时我们可以通过控制en信号来决定Din实际写入哪个寄存器;同时我们可以通过控制复用器的选择信号决定哪个寄存器的数据可以输出到Dout。例如,我们让第寄存器1号的en为1,其余的en为0,即可写入寄存器1号;我们让复用器的选择器输入0b000,即可选择从寄存器1号输出。
现在的问题是,我们可以通过复用器控制哪个寄存器输出,但是我们需要一个选择器帮我们选择输入的寄存器。这个选择器应该是3bit输入,8bit输出,封装和真值表类似下图:
在这里插入图片描述

这个经典电路就是3-8译码器,电路如下:
在这里插入图片描述

我们将封装好的3-8译码器放入电路图,效果如下:
在这里插入图片描述

最后我们在输出端加上一个控制电路:
在这里插入图片描述

我们就实现了一个读写可控的8x8bit内存。
现实中的内存不可能用寄存器这种高成本电路,但为了简化外部存储结构,我们并不会讲解SRAM和DRAM架构。
之后我们在使用外部存储的时候,不会再次绘制该电路。为了之后编程方便,我们选择封装好的EEPROM,如下图:
在这里插入图片描述

该封装可以非常方便的预编辑其内部存储,便于我们之后的调试。

给计算单元接入外部存储

对我们之前的计算单元做一点小小的改造,我们添加一块EEPROM,将数据位数设置为8,地址位数设置为4,并将我们刚刚的四个数写到这块存储中。如下图所示。
在这里插入图片描述

关于该数据存储的输入和输出,按照如下说明链接:
 由于我们要从该存储中读取数据,数据输出接ALU的B输入;
 ALU的计算结果可能会需要写入存储,ALU的输出接Din;
 由于我们需要不断从该存储中读取数据,ld为常量1;
 由于我们需要控制ALU到存储的写入时机,str接一个1bit输入;
 地址的数据我们通过一个4bit输入控制。
最终如图所示:
在这里插入图片描述

点击运行后,我们关闭en_DATA信号,然后依次执行以下操作:
 A_DATA输入0,拍下时钟;
 A_DATA输入1,拍下时钟;
 A_DATA输入2,拍下时钟;
 A_DATA输入1,拍下时钟;
最终我们同样可以得到13+6+27+6的结果。

添加一块指令存储

添加指令存储

这一次尽管我们不用输入B,但是我们需要关闭en_DATA,还需要不断输入数据存储的地址。如果我们想实现自动化控制输入信号,则需要再添加一块指令存储,我们使用预设的指令来控制数据存储的地址和en信号。按照下图添加指令存储:
在这里插入图片描述

这里,注意到指令存储的输出,低4bit用于数据存储的地址输入,最高位用作数据存储的str控制信号。如果我们想让数据存储输出地址1的数据,我们只需要在指令存储中预先设定0xxx0001的指令即可。按照这个原则,进行如下预编程(将指令存储设为Bin数据格式即可显示二进制数值):
在这里插入图片描述

完成编程后,我们进行如下操作:
 指令地址输入0,拍下时钟;
 指令地址输入1,拍下时钟;
 指令地址输入2,拍下时钟;
 指令地址输入3,拍下时钟;
 最终我们同样可以得到13+6+27+6的结果。

添加PC计数器

上面的例子中,我们还是要手动输入指令的索引。观察到指令的执行是按照时钟周期递增,我们可以实现一个每个时钟周期递增的计数器作为指令存储的地址输入,免去手动输入地址的麻烦。如下图所示,寄存器中的数据每个时钟周期都会累加1。
在这里插入图片描述

将上面的子电路保存为“PC计数器”,并添加到我们的电路中:
在这里插入图片描述

开启仿真,连续拍4下时钟信号,我们就能够得到13+6+27+6的结果。

添加其他运算

前面的电路通过预编程,我们已经能够计算任意长度的加法运算了。现在来思考一个问题:如果我们想计算13+6+27-6呢?
控制计算方法的信号是ALU的sel信号。之前我们只有加法运算,所以我们将这个信号恒定为2。现在考虑到我们有不同运算的需求,这个信号就不能用常量,而是需要用指令存储去控制。如下图所示,将指令存储的4-6连接到ALU的sel信号:
在这里插入图片描述

这样,我们就能通过指令的4-6位来控制计算逻辑。将刚刚程序的前4条指令修改为加、加、加、减,如图所示:
在这里插入图片描述

保存后开始拟真,连续拍下4次时钟按钮,即可得到13+6+27-6的结果。

设计指令集

之前我们都是将计算的结果直接展示到输出端,但在实际编程中,我们很可能遇到将中间的计算结果保存,并用到之后的计算中。如何实现将计算的结果保存到数据存储中呢?
实际上计算结果都保存在寄存器中,我们只要将寄存器的值保存到数据存储即可。想要完成这个操作,需要满足下面的条件:
 寄存器的en信号为0(防止结果重复写入寄存器);
 数据存储的str信号为1(可以写入数据存储);
 ALU的sel信号为000(直出寄存器A的值)。
满足上面的条件,当我们拍下时钟信号,即可完成寄存器的值保存到数据存储的某个地址,然而我们面临一个尴尬的处境:指令存储的控制位不够了。
这个问题在真实的CPU中也存在,并且更为明显:复杂的CPU中控制信号通常有几百个,我们不能将所有的控制信号都直接交由指令存储控制;反过来说,通常我们需要做的操作(指令集)并没有那么多(例如存储数据,读取数据,计算加减等等),只是每种操作会对应一种特定的控制信号组合。
于是,我么可以预先设计好我们需要用到的操作并编号,并使用一组逻辑电路将其翻译成控制信号组合即可。下面我们来设计自己的指令集,并将其对应的控制信号一并写下来:
指令集 编号 enREG enDATA sel 作用
save 000 0 1 000 将寄存器的值保存到数据存储
ld 001 1 0 001 将数据存储的值加载到寄存器
add 010 1 0 010 寄存器累加数据存储的值
sub 011 1 0 011 寄存器减去数据存储的值
and 100 1 0 100 寄存器的值和数据存储的值取与
or 101 1 0 101 寄存器的值和数据存储的值取或
lshift 110 1 0 110 寄存器左移n位(n由数据存储指定)
rshift 111 1 0 111 寄存器右移n位(n由数据存储指定)
这样,我们只要做一个组件,使其只需输入编号,便能给我们输出控制信号,那么指令内存就只需要输入3位编号,即可完成控制。这个编号的正式名称为操作码(opcode)。

添加控制器

能将opcode转化为控制信号的模块,我们称其为控制器。根据上面我们的指令集需求,我们可以实现如下的控制器模块:
在这里插入图片描述

将该控制器模块添加到我们的电路,如下图所示:
在这里插入图片描述

此时指令内存的高3bit为opcode,低4bit为数据存储的addr,中间有一位暂时没有使用。按照我们新的指令集编写程序来计算13+6+27-6,程序如下:
001 0 0000 ld 0
010 0 0001 add 1
010 0 0010 add 2
011 0 0001 sub 1
将程序写入指令存储,如下图所示:
在这里插入图片描述

开启拟真,拍4次时钟按钮,我们同样可以得到13+6+27-6的结果。

添加外设控制寄存器(以GPIO为例)

在单片机中,除了数据存储和指令存储外,还有很多外设寄存器,这些外设寄存器同样通过总线和计算单元相连,也同样通过指令存储进行寻址。这里我们用指令中空闲的bit作为标记位,当这个bit为0,表示在数据存储中寻址;当这个bit为1,表示在外设中寻址。除了这个标记bit,地址位一共有4位,所以我们一共可以放16个外设寄存器,这里我们以一个GPIO寄存器为例。
我们将这个GPIO寄存器设置位外设地址的第一位:0b10000。那么需要完成以下工作:
 标记位为0,激活数据存储
 标记位为1,地址位0000时激活GPIO寄存器
 标记位为1,地址位高于0000没有任何效果
最终按照如下电路图改造我们的电路:(过程比较恶心、难以理解,建议先尝试着自己画出来,再尝试着理解、再尝试搜教学视频)
在这里插入图片描述

即可得到一个封装好的单片机,带有8个GPIO引脚。

流水灯实验

下面,我们来写一个流水灯程序。由于我们的单片机不支持循环,所以程序如下:
001 0 0000 ld 0
000 1 0000 save 16
110 0 0000 lshift 0
000 1 0000 save 16
110 0 0000 lshift 0
000 1 0000 save 16
110 0 0000 lshift 0
000 1 0000 save 16
110 0 0000 lshift 0
000 1 0000 save 16
110 0 0000 lshift 0
000 1 0000 save 16
110 0 0000 lshift 0
000 1 0000 save 16
110 0 0000 lshift 0
000 1 0000 save 16
程序很简单,就是首先从数据内存0位置读取数据到寄存器A,然后写入到GPIO寄存器,之后就是不断循环“将寄存器A左移1位,写入到GPIO寄存器”的操作。将程序写入指令存储,保存电路。并新建一个电路使用我们刚刚的单片机电路,按照下图接线:
在这里插入图片描述

将时钟设置位20HZ,开启拟真,即可看到流水灯效果。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值