petard  

嵌入式技术之从零搭建计算机

第 1 章 数字电路基础

1.1 二进制数据表达

文字		用0 1表示文字信息
图片		分为多个像素,一个像素对应一个色块(24位;3字节)对应3原色 ——透明度占1个字节,只有png图片有透明度该选项,其他图片都是
声音
视频

1.2 数电基础

① 基础逻辑门电路

需要记住:口诀、符号、表达式!

1. 非门
   口诀:输出与输入相反

2. 与门
   口诀:有0出0,全1出1
   
3. 或门
   口诀:有1出1,全0出0

② 其他门电路

需要记住:口诀、符号!

1. 异或门
   口诀:相同出0,相异出1

2. 与非门
   口诀:有0出1,全1出0

3. 或非门
   口诀:有1出0,全0出1

4. 异或非门
   口诀:相同出1,相异出0

③ 运算器

1. 半加器
2. 1位加法器
3. 4位加法器

Digital自带的加法器组件:菜单 -> 组件 -> 运算器 -> 加法器

加法器内部都是使用二进制数加

半加法器:只考虑往前进位,不考虑后面数是否有进位
eg: 1 0 为1;1 1 为 0,0 1为 1,0 0 才为 0 ——异或

进位:1 0 为 0;0 0 为0,0 1为 0,1 1才为 1 ——与

总结:异或门 + 与门(进位的值)

一位加法器:需要考虑进位,(自己两数相加的进位 以及 后面两数相加的进位)

eg:110 + 101,在半加器的基础上,推测进位情况

下一位的相加情况:异或

进位:后一位需要进位的数 + 进位的数; 0 0 为0;1 0 为 1;0 1 为 1;1 1 为 0 ——异或

顺次的下一个进位:0 1 为1

总结:2个 半加法器 + 异或门 \或门都行

④ 锁存器和触发器

1. SR锁存器		——(置位、 复位)
   S`=1,R1`=1  状态不改变
   S`=0,R1`=1  置位,Q=1
   S`=1,R1`=0  复位,Q=0
   S`=0,R1`=0  无意义

2. D锁存器		——控制是否允许存储			
       en信号控制是否允许存储
       D输入:Q输出的状态 与 D输入 一致
   
3. D触发器				(作用:同频,防止外界干扰) ——在时钟上沿【存储1bit数据】						
   使用两个D锁存器
   在上升沿触发,允许存储

存在内置的D触发器组件!

⑤ 寄存器(Reg) ——在时钟上沿存储多个 bit数据(以4bit寄存器为例)

存几个bit的数据就 集合 几个D触发器

1. 使用4个D触发器实现了 4bit 寄存器

有内置寄存器组件!

第 2 章 计算机组成原理

3.1 冯诺依曼模型

1. 控制器(控制单元,又名缩写CU)	 ——Control Unit
2. 计算逻辑单元(ALU、运算器)
3. 存储器			——分为内存(运行内存)、外存(磁盘)
4. 输入/输出系统

3.2 计算机组成

① 中央处理器(CPU)

1. 控制器(CU)
2. 运算器(ALU)
3. 寄存器(Reg)
   		包括:数据寄存器、指令寄存器、程序计数器(PC)

② 主存储器(内存)

RAM: 随机存取存储区,特点:断电丢失,可读可写
ROM: 只读储存器,特点:断电不丢失,只读

③ 三大总线

控制总线
地址总线
数据总线

④ 程序周期

每个机器周期: 取指令、译码、执行
流水线

第 3 章 设计计算机硬件

3.1 实现一个ALU

1. 复用器的使用		——为了判断输出哪种结果(输出Q会跟随sel来决定是取输入A还是输入B)
2. 16位ALU的实现:
	加法器、减法器、与、或

3.2 使用ALU和寄存器完成算术计算

​ ——多个数字的运算,需要有寄存器存储数字

使用寄存器存储上一步的计算结果,再作为下一步的输入(作为ALU的A输入)
ALU的B输入是手动		

3.3 数据存储器

​ ——存储数据及结果 都是在数据存储器中

自定义一个内存:
1. 38译码器 		功能:译码(3位编码译为8位编码) ——通过3位的数据来控制8个内存地址。
	
2. 使用寄存器实现一个内存
   str(set-reset)(输入)控制信号、ld(输出)控制信号       ——(驱动器就相当于一个继电器模块)
   		str:控制输入,ld:控制输出
3. 内置的内存组件(EEPROM)

将内存接入当前的计算机系统
1. 程序计数器 (从内存中取数据)	——(Program counter简称PC)	
							功能:计数(通过PC取地址),判断进入哪一轮运算
2. 使用内置组件 EEPROM 实现了一个数据内存

注:输出显示Z 表示该情况不确定,容易被干扰

3.4 指令存储器

​ ——使用了 Reg、ROM、ALU、PC

1. 添加一块内存用于存储指令,存储ALU的Sel信号
2. 指令内存与数据内存,一一对应(使用分裂器)
3. PC同时控制取指令和取数据

3.5 其他控制指令

数据位数 与 指令位数

1、数据位数(每个单元存储的数有几位(bit))、地址位数(有几个(种)单元并转换为2进制数,一位二进制数表示2个单元):
eg:
1、指令存储:存储 加(00)、减(01)、与(10)、或(11)——括号内为每个指令的数字表示
数字位数有2个——数据位数:2,有4种指令也就是有(2²种指令)——地址位数:2
输出的一般是数据,按数据位数计算

2、给出的bit数较多,需要的bit数少 ——使用分裂器(分裂期从左到右,先低位再高位)

① halt 信号位和停止指令

1. 把指令的最高位作为 halt 信号位(终止信号位)
2. 添加一条停止指令,该指令的 halt 信号是【0】, 其他指令 halt 信号位是1
3. 修改 指令内存 输出的分离器,将halt信号取出,与时钟做与运算

停止只是存储停止了,ALU没有影响

由于数据存储 ld初始值是1,所以会直接把第一个数值给ALU

② str 信号位和存储指令

1. 存储指令,用于将计算结果存入数据内存
2. 分配了 str 信号位,1表示数据内存允许写入,只有存储指令的str信号位是1,其他指令的str信号位是0

③ ld 信号位、sel_b 信号位

1. ld 信号控制数据内存是否允许输出, 存储指令和停止指令的ld信号位是0
2. sel_b 信号控制ALU的B端使用哪个输入,0表示使用数据内存获取的数据,1表示使用默认常数0

④ sel_a 信号位 和 ld_a 指令

连续计算多个表达式,后面表达式的第一个数如果用 加 指令,会把寄存器中的数(上一次的计算结果)加到一块。

1. ld_a 指定, 用于加载表达式的第一个数,内存中取出什么数,ALU算完还是什么数;控制ALU的A端输入是0。
2. sel_a 信号位,控制ALU的A端输入,0表示获取寄存器中的数,1表示获取默认值0;ld_a指令的 sel_a信号位是1

⑤ jmp跳转: en_pc信号位、en_jmp信号位、en_a 信号位、jmp指令

1. 修改程序计数器,添加addr输入,和jmp输入,变为可跳转的程序计数器
2. 添加了 en_pc 信号位,控制PC是否允许计数,目前,除了停止指令,所有的指令该信号位是1
3. 添加 en_jmp 信号位,控制PC是否允许跳转,1表示允许跳转
4. 添加 jmp 跳转指令,该指令的 en_jmp 信号位是 1
5. 添加 en_a 信号位,控制是否写入A寄存器, jmp 指令的 en_a 信号位是 0
				(由于数据内存中读取的是jmp跳转的地址,所以不需要写入寄存器)

⑥ je有条件跳转:en_je 信号

1. 改造ALU,可以判等
2. 添加比较寄存器
3. 分配 en_je 信号位,控制有条件跳转
   en_je是1 并且 比较寄存器中也是1,PC才能跳转
4. 添加je指令,en_je信号位是1,en_jmp信号位是0
  • 说明:
  • 分割器输出:先低位再高位
  • PC设置为通用组件,对应的命令:this.Bits = args.Bits ; ——(java语言)

3.6 控制器

① 5 位opcode

​ ——指令的编号

将已有的指令进行 编号 ,使用5bit的二进制数字表示
最多可以表示32条指令

00000	ld_a
00001	add
00010	sub
...

② 控制器

输入:5位的opcode
输出:13(2,2,2,1,1,1,1,1,1,1,1,1,1)个控制信号
								——相当于译码

核心:查找表(LUT)		——输入端口数为:opcode

.hex文件可以导入查找表中

② Qcmp 输入

需求:

  1. en_jmp 信号和 en_je 信号合并为一个跳转信号 ld_pc
  2. je指令是否跳转交给控制器判断,如果上一步两个操作数相等,je指令的 ld_pc 信号位是 1; 如果上一步两个操作数不相等,je指令的 ld_pc 信号位是 0;
  3. jmp指令,不论上一步两个操作数是否相等,ld_pc 信号位都是 1
1. 控制器增加 Qcmp 输入
2. 控制器中的查找表输入6bit,最高位是Qcmp,低5位是opcode  	——可以有32种
3. 修改查找表
   根据Qcmp是0还是1,决定 je 指令的 ld_pc信号位是 0 还是 1
4. 删除 en_je 信号位,修改 en_jmp 信号位为 ld_pc 
5. 总电路中,直接将 ld_pc 信号接入 PC 的跳转使能

注:Qcmp的(0/1)两种结果,指令内存可以对应两部分:一个是Qcmp全是0——对应的;一个是Qcmp全是1——对应的

(jmp的ld_pc的信号位都为1,je是两数相等的时候才为1)

3.8 两块内容合并为一块

① 操作码(opcode)和操作数

内存数据两种类型:
指令类型: 5位操作码(opcode)+11位操作数(地址opaddr)
数据类型: 16位的数据

② 取指令和执行指令

取指令:  将指令(opcode+地址 组合形式)取出来放在指令寄存器,PC计数
执行指令:从指令寄存器中取出,opcode给控制器得到16位指令,地址给内存取数据, PC不计数

③ 添加11位地址的内存并添加数据

1. 将PC改为 11 位
2. 添加一块内存,地址11位,数据16位
3. 将原来的指令内存和数据内存删掉
4. 将数据添加到内存中

④ 添加指令寄存器

1. 添加指令寄存器,数据位数16
2. 内存输出D作为指令寄存器的输入, en信号控制是否允许写入(只有取指令阶段才允许写入)
3. 输出地址分两部分,高5位是opcode给控制器,低11位是数据地址,给内存
4. 内存地址输入,从PC产生的地址和指令寄存器给出的地址中选一个 (取指令阶段使用PC的地址,执行指令阶段使用指令寄存器给出的地址, en_pc在取指令阶段为1,执行指令阶段为0)

⑤ 分配信号控制位 en_cmp、en_addr

en_cmp  比较寄存器的输入使能, 10位置
en_addr 指令寄存器的输入使能, 13位置

⑥ 控制器改造

1. state=0 取指令阶段
   信号控制位,不会受到Qcmp和opcode的影响,所有控制信号位都相同
   halt:1		
   en_addr:1	允许指令寄存器写入
   ld:1		    允许内存输出			——当其为高阻态时,输出随机数——(所以使用了)sel_b
   en_pc: 1		允许PC计数
   sel_b: 01	ALU B端默认输入0
   sel_a: 01    ALU A端默认输入0
   其他控制器都是0

2. state=1 执行指令阶段 
   en_addr: 0  不允许指令寄存器写入
   en_pc: 0	   PC不计数


3. 设置查找表最高位是 state 输入,随着时钟在0和1之间来回切换
4. 修改控制器的输出标签名:en_cmp(比较-寄存器)、 en_addr(地址-pc)
5. 重新编辑查找表

⑦ 跳转

1. 给PC的 addr 输入结束 opaddr(要跳转到的指令地址)信号
   jmp 指令的后面不是数据地址,而是要跳转到的指令地址(直接数,不需像其他指令一样从内存中再获取了)

2. 修改PC电路
   在执行指令阶段,en_pc 信号都是0,PC无法跳转
   增加或门,实现 ld_pc(对应跳转信号jmp、je)如果是1,即使en_pc是0,也能跳转

注:跳转:指令jmp——(addr-opaddr)——pc

整个cpu:iROM 与控制器,状态都是从0开始

3.9 添加立即数

​ ——优点:计算效率高,缺点:存放的数最大只能为 2的11次方

1. 立即数形式: opcode+立即数(11位)	——一共16位
   传统形式: opcode+地址
   
2. 添加立即数指令: ld_ia、add_ia、sub_ia、or_ia、and_ia
3. 立即数指令特点:
   添加en_i信号位,都是1; 传统指令都是0
   ld信号位(内存输出使能),都是0; 传统指令大部分是1

注:当为立即数时,就可以直接把数给D,不过需要在opaddr前加5位0,因为D为16位数据

ld为0,内存 高阻态,不输出

3.10 添加B寄存器

1. 每个寄存器代表一个变量
2. 添加B寄存器,当前系统支持两个变量
3. ALU 的两个输入端AB,都有4个选项:A寄存器数据、B寄存器数据、0、内存数据(D),分别使用 sel_a和sel_b控制选择哪个
4. 添加了更多指令
   ld_a 将内存数据存入A寄存器
   ld_b 将内存数据存入B寄存器
   save_a 将A寄存器数据存入内存
   save_b 将B寄存器数据存入内存
   ....

操作码与指令的对照表如下:

*指令* *操作码* *注释*
*halt* 0b00000 终止程序
*ld_a* 0b00001 将内存RAM中的值载入a寄存器
*ld_b* 0b00010 将内存RAM中的值载入b寄存器
*save_a* 0b00011 将寄存器a的值保存到内存RAM
*save_b* 0b00100 将寄存器b的值保存到内存RAM
*add* 0b00101 将寄存器ab相加,保存到a
*sub* 0b00110 将寄存器ab相减,保存到a
*and* 0b00111 将寄存器ab按位与,保存到a
*or* 0b01000 将寄存器ab按位或,保存到a
*ld_ia* 0b01001 将立即数载入寄存器a
*ld_ib* 0b01010 将立即数载入寄存器b
*add_ia* 0b01011 将立即数加到寄存器a
*add_ib* 0b01100 将立即数加到寄存器b
*sub_ia* 0b01101 从寄存器a中减去立即数
*sub_ib* 0b01110 从寄存器b中减去立即数
*and_ia* 0b01111 将寄存器a与立即数取与
*and_ib* 0b10000 将寄存器b与立即数取与
*or_ia* 0b10001 将寄存器a与立即数取或
*or_ib* 0b10010 将寄存器b与立即数取或
*cmp_a* 0b10011 比较寄存器a和立即数是否相等
*cmp_b* 0b10100 比较寄存器b和立即数是否相等
*jmp* 0b10101 无条件跳转到指定地址执行
*je* 0b10110 如果比较结果相等才跳转
*jne* 0b10111 如果比较结果不等才跳转
*nop* 0b11000 什么都不干,跳过一步指令

第4章 设计汇编语言

4.1 设计汇编语言

C源码 --> 汇编语言 --> 机器码(二进制指令) 

注:

汇编:nop——跳到下一步

汇编都需要经过ALU

汇编语言的不同会受到架构(eg:x86)的影响,所以汇编语言可移植性不高

goto语言来自于汇编

4.2 封装CPU

设置CPU对外输出
	A(隧道) -> A(输出端)				——对应pc输出的地址
	str(隧道) -> str(输出端)
	ld(隧道) -> ld(输出端)
	Q(隧道) -> D_out(输出端)

设置CPU输入
	D_in(输入端) -> D(隧道)


将原来的内存改为 iROM(内置存储器)

4.3 总线

1. 控制总线;str、ld
2. 地址总线:A
3. 数据总线:D_in、D_out

4.4 地址

​ A原本地址为:11位——分为:高3位+低8位

高3位: 设备地址
低8位: 存储设置的内部地址

iROM:	000 00000000 ~ 000 11111111  (0~255)
RAM:    001 00000000 ~ 001 11111111  (256~511)
硬盘:   010 00000000 ~ 010 11111111  (512 ~ 767)
终端:	  011 00000000	(768)

第5章 设计引导程序

1. iROM(写死的程序)
负责将硬盘中前32条指令拷贝到内存中执行

2. RAM(内存)
断电丢失,但开机后,所有程序需加载进内存才执行

3. 硬盘(用户可以改的程序)
前32条指令,作为引导程序,负责将后面的自定义程序拷贝进内存并执行(这32条指令用户也可以改,在这里指定自定义程序的位置,指令数量小于等于32即可)
自定义的程序放在后面

总:iROM(把指令写入 RAM 内存(指令目的读取 硬盘 的前32条指令)

总结:(方便理解)

1、查找表:1+1+5=7位 —— 16位

控制器输入7位 ——(译码) 输出16位
7位
state(状态【取指令0与执行状态1】)1位
Qcmp(控制跳转)1位
opcode(给指令编号)5位
对应的16位
终止信号:halt
指令寄存器输入使能:en_addr (address地址)
数据RAM输出使能:ld 数据RAM存储使能:str (storage储存)
比较寄存器输入使能:en_cmp
PC跳转使能:ld_pc PC计数器计数使能:en_pc
A寄存器输入使能:en_a
(该行每个各占2位)ALU B输入选择控制:sel_b ALU A输入选择控制:sel_a ALU功能选择位:sel_alu

2、内存

分为两部分(都是16位): 指令(opcode 5位+addr【操作数】11位) 与 数据(16位)

state 是通过D触发器来实现的,通过时钟控制state的状态

控制器的 state=0,取指令阶段:(是从0到1的一个过程)

控制器的:en_pc、ld、en_addr、halt 都为1;比较寄存器:cmp也为1

①可跳转pc:en_pc=1,允许计数 —— A使用pc的地址

②内存:D是opcode+opaddr

③ALU:sel_b=01,此时的D不参与运算

④指令寄存器:en_addr=1,将取到的指令存在寄存器中

控制器的state=1,执行指令阶段:(是从0到1的一个过程)

控制器的:en_a、en_cmp、ld、halt都为1;比较器寄存器的:en_cmp为1

①PC不能计数;指令寄存器不能写入

②从指令寄存器中取出opcode与opaddr:

​ opcode给控制器,译码为16位指令

​ opaddr给PC,得出A是指令寄存器取出的地址

附录

字母表示:

pc(程序计数器):C时钟、en控制输入、out输出、ovf进位

ALU(运算器):(A、B)输入、C_in加减法前的进位、Sel选择哪个运算、S输出、C_out加减法的进位

Reg(寄存器):D输入、C时钟、en控制输入、Q输出

隧道:
ZF比较运算器输出


信号位、指令字母说明

有内置的 加法器、D触发器、寄存器

str:控制存储; ld:控制输出; halt:停止与时钟有关的 jmp:无条件跳,je:有条件跳

ld_a:加载ALU的A端的输入

1、半加器:异或门、与门
2、一位加法器:2个半加器、或门((A、B)输入、C_in后面的进位、S输出、C_out 进位)
3、4位加法器:4个一位加法器、隧道、分裂器((A、B)输入、S输出、**C_out **进位)


4、SR锁存器:2个与非门((S'、R')输入、(Q、Q')输出)
5、D锁存器:(对SR锁存器的升级改造)两个与非门、非门+(SR锁存器) ((D、en(控制是否允许)输入、(Q、Q')输出)
6、D触发器(标号:D):2个D锁存器、非门(D输入、C时钟(提供上升沿信号)、Q输出)
7、寄存器(标号:Reg):四个触发器、与门、分裂器((D、en(控制是否允许)输入、C时钟(提供上升沿信号)、Q输出)


8、复位器:Sel选择位(表示:一位有两种选择0和1,2位就有4种)
9、16位ALU:加法器Add、减法器Sub、与、或、两个复位器((A、B、C_in(进位)、Sel(选择位))输入,(S、C_out(进位)输出)

10、使用RegALU实现数学计算:(C按钮、B输入、Q输出)

11、38译码器:使用分析表直接生成(把3位编码——转为对应想要转换的8位编码)
12、使用Reg(寄存器)实现内存:38译码器、8个与、8个寄存器、复位器、下拉电阻、驱动器(clock按钮,(D_in(数据)、A(使用3位数控制8位寄存器地址)、str(控制是否输入数字)、ld(控制数据是否输出))输出,D_out数据输出)
13、内置的内存组件:((A(3位控制8个寄存器的地址)、D_in(数据)、str(控制数据是否输入)、ld(控制数据是否输出),C(时钟),D输出数据)
分数据位数(输入的数据为几位)、地址位数(有几种数据 eg:3位—对应2的三次方 种数据)


14、(1)程序计数器(标号:PC):寄存器(Reg)、Add ** 【C时钟、en(控制是否允许)输入,out输出,ovf进位】
(2)可跳转程序计数器:在
PC基础上,多一个复位器 【(addr(对应的跳转的地址)、jmp(控制是否可以跳转:1为跳转,0为自增1)、en(控制是否允许)输入,C时钟,out输出、ovf进位】
15、数据内存:程序计数器(
PC)、EEPROMALUReg、与、非(clock时钟、Q输出)
16、指令内存+(数据内存):与、非、程序计数器pc、数据内存、指令内存(
EEPROM)、ALU、Reg(clock时钟、Q**输出)


在16的基础上给指令内存添加:
17、halt信号位(除了ALU外-停止):在16的基础上,多一个分裂器;把指令数据D的输出(最高位作为halt信号位,(0、1)末两位为ALUSel选择位);haltclock时钟:决定是否执行或停止
18、str信号位(控制数据是否输入——数据内存-存储):在17的基础上;把指令数据D(16位数据)的输出(从0开始的11位,作为控制数据内存是否存储该时刻数据的信号)
19、ldsel_b信号位:在18的基础上,多一个复用器(用在数据内存的输出处);把指令数据D(16位数据)的输出(从0开始的12位:ld,决定数据内存的数据是否输出(存储与停止为0)),(从0开始的4,5位:sel_b,决定数据内存是输出(复用器对应的)D数据还是常数)
20、sel_a信号与ld_a指令:在19的基础上;把指令数据D(16位数据)的输出(从0开始的2,3位:sel_a,控制寄存器的数据是否被取出;ld_a指令对应sel_a,当为ld_a指令时sel_a为1;(ld_a 指定, 用于加载表达式的第一个数;数据内存中取出什么数,ALU算完还是什么数)
21、有条件跳转(en_je信号位、je指令):ALUZF为比较结果(0/1)——cmp——输入比较寄存器(留存上一步的比较结果)的D——从Q_cmp输出,刚好cmpQ_cmp差一步,jmp指令也是在两数比较之后

posted on 2024-04-22 19:14  岌岌无名  阅读(30)  评论(0编辑  收藏  举报