计算机组成原理
电子管计算机 -> 晶体管计算机 -> 集成电路计算机 -> 超大规模集成电路计算机
未来: 生物计算机, 量子计算机(量子力学)
微型计算机发展
摩尔定律: 集成电路的性能, 每18-24个月就会提升一倍.
计算机的体系与结构
冯诺依曼模型
将程序指令和数据一起存储的计算机设计概念结构.
- 必须有一个存储器
- 必须有一个控制器
- 必须有一个运算器
- 必须有输入设备
- 必须有输出设备
CPU和存储器的速率之间的问题无法调和.
冯诺依曼瓶颈: CPU经常空转等待数据传输.
现代计算机结构
构成计算机的元素
- 状态
- 输入
- 时钟作为一种标准输入
- 另一种最好可以读取人类的想法
- 状态转换函数F((将输入和当前状态转换为下一个状态))
- 输出
状态描述
- 数字即可–一切皆可用数字表示
- ASCII
- 商品
- 一个数字不够(需要数组)
状态转换函数
一个表格就可以, 接收到的输入I变成新的状态S
输入\状态 | 哭 | 笑 |
---|---|---|
逗 | 笑 | 笑 |
打 | 哭 | 哭 |
实际上可以用0和1表示
输入\状态 | 0 | 1 |
---|---|---|
0 | 1 | 1 |
1 | 0 | 1 |
状态转换表
思考:鼠标在1024*768的屏幕上有786432种位置, 因此对应了786432种状态。如果用表格来描述需要很大的一个空间。如何优化?
方案:用程序描述状态转换表
- 每次鼠标位置更新, 都调用一段程序去计算需要显示的位置
- 程序需要预先在机器中安装好
- 需要有执行程序的工具(一个计算器)
输入
- 时间是最重要的输入
- 晶振(不是输入设备)
- 可以通过设备读取外界信息
- 鼠标、键盘、手柄
- 头盔
- 脑电波
输出
从计算机中读取状态
- 找根线连接芯片的引脚
- 打印机
- 显示器
计算机的层次与编程语言
硬件逻辑层 -> 微程序机器层 -> 传统机器层 -> 操作系统层 -> 汇编语言层 -> 高级语言层 -> 应用层
- 一个机器指令对应一个微程序
- 一个微程序对应一组微指令
操作系统是软件和硬件层的适配层.
计算机的计算单位
容量单位
硬盘商一般使用十进制标记容量.
CPU速度
- 一般体现为CPU的时钟频率
- 单位一般是赫兹(Hz)
- 主流CPU的时钟频率都在2GHz以上
赫兹: 秒分之一. 每秒中的周期性变动重复次数的计量.
CPU通过高低电平表示0和1. 所以频率指CPU高低电平每秒变换的次数.
计算机字符与编码集
ASCII 码.
GB2312. GBK.
Unicode: 兼容全球的字符集, 统一码, 万国码, 单一码.
Unicode 定义了世界通用的符号集*, UTF-*实现编码**.
计算机速度单位
TFlop/s.
1TFlop/s = 每秒一万亿次浮点计算.
1024G=1T
计算机的总线
解决不同设备之间的通信问题.
USB: Universal Serial Bus, 通用串行总线. 连接标准, 统一外围设备.
PCI总线, ISA总线, Thunderbolt总线
片内总线
高集成度芯片内部的信息传输线.
芯片内部的总线, 寄存器与寄存器之间. 寄存器与控制器, 运算器之间.
系统总线
三种.
- 数据总线: 双向传输各个部件的数据信息. 数据总线的位数(总线宽度)是数据总线的重要参数. (一般与CPU位数相同)如果是32位, 一次可以运输4字节的数据; 64位则是8字节.
- 地址总线: 用于传输数据的地址.
- 地址总线位数=n, 寻址范围: 0~2^n^.
- 指定源数据或目的数据在内存中的地址.
- 地址总线的位数与存储单元有关.
- 控制总线
- 发出各种控制信号的传输线.
- 控制信号经由控制总线从一个组件发送到另外一个组件.
- 控制总线可以监视不同组件之间的状态(就绪/未就绪)
总线仲裁: 解决不同设备使用这个总线优先顺序的设备(仲裁控制器). 解决总线使用权的冲突问题.
总线仲裁方法
- 链式查询
- 电路复杂度低, 仲裁方式简单
- 优先级低的设备难以获得总线使用权限
- 计时器定时查询
- 仲裁控制器对设备编号并使用计数器累计计数
- 接收到仲裁信号后, 往所有设备发出计数值
- 计数值与设备编号一致则获得总线使用权
- 独立请求
- 每个设备均有总线独立连接仲裁器
- 设备单独向仲裁器发送请求和接收请求
- 当同时收到多个请求信号, 仲裁有权按优先级分配使用权
链式查询
定时器定时查询
计算机输入输出设备
输入输出接口的通用设计
- 数据线
- 单向/双向IO设备与主机传输数据
- 状态线
- IO设备向主机报告的信号线
- 查询设备是否已经正常连接就绪
- 设备是否被占用了
- 命令线
- CPU向设备发送命令的信号线
- 发送读写/启动/停信号止
- 设备选择线
- 主机选择IO设备进行操作的信号线
- 对连在总线上的设备进行选择
CPU与IO设备的通信
- 程序中断
- DMA(Direct Memory Access, 直接存储器访问)
中断和中断向量
- 当外围IO设备就绪时, 向CPU发出中断信号(事件)
- CPU有专门的电路响应中断信号
- 当收到中断信号后, 就会停止当前工作, 保存当前执行状态, 转去处理IO设备的事件
提供低速设备通知CPU的一种异步的方式.
中断让CPU可以高速运转的同时兼顾低速设备的响应.
中断过程
- OS加载时, 写入中断向量表(IVT, 内存中的一块区域)
- 产生中断请求, 发送给CPU(鼠标, 键盘, 软件都可能发生中断)
- 查询中断向量表(Interrupt Vector Table)确定中断向量(Interrupt Vector)
- 根据中断向量定位中断响应程序(ISR)
- OS接管中断
中断请求
Interrupt Request
- 硬件设备发给主板(打印机, 键盘, 鼠标, 磁盘等)
- 硬件中断: CPU异常(除以0), 时钟信号等
- 软件中断: 发出(异常, 切换到内核态等)
中断向量表
- 一般是内存中的一块区域, 存储了中断类型和中断响应程序(程序执行片段的地址)的对应关系. 每一行叫做一个中断向量.
单步中断: 调试用.
中断是可编程的.
sleep 也要中断的, 需要时钟.
中断可以节省资源. 例如在等待打印机准备就绪的时候, 可以进行中断去干其它事情, 等准备完成才进行打印(异步).
中断的意义
提供工作效率(polling问题)
故障恢复(异常处理, 紧急事件等)
32bit的机器上中响应路径(ISR)只有4byte, 如何实现复杂的程序?
- 存的是一个地址, 有点像函数调用.
按键程序中断响应路径到操作系统再到应用, 哪个按键被按了是怎么区分的?
- 键盘会在内存中存有一份映射, 中断相当于通知.
中断响应后, 如何恢复到中断执行前的状态?
- 在中断前会把之前所在的PC指针下一行存在栈中, 在执行完后, 清栈后, 上一条就是返回地址了
出错后, 为什么要直接跳到错误的处理位置?
- 错误发生后, 错误对应的处理者不是一对一的关系. 降低耦合程度, 不关心的程序可以不监听这个中断. 这种消息的模型, 也是现在软件架构的主流方向.
DMA
- DMA 直接连接主存与IO设备
- DMA 工作时不需要CPU的参与
硬盘和外置显卡都有DMA.
计算机存储器
随机存取存储器–RAM(Random Access Memory), 随机读写数据可以在几乎相同的时间内完成, 与数据的物理位置无关.
内存寻址: 32位的机器, 每次都操作4个字节, 而64位的操作8个字节. 所以32位系统最大支持4GB内存.
串行存储器, 与位置相关, 按顺序查找
只读存储器(ROM), bios就是一种
存储器的层次结构 – Memory Hierarchy
- 缓存, 速度快, 位格高
- 主存, 适中
- 辅存, 慢
实现: 在 CPU 和主存直接增加一层速度快的Cache
缓存: 解决主存速度不够的问题.
局部性原理: 指 CPU 访问存储器时, 无论是存取指令还是存取数据, 所访问的存储单元都趋于聚集在一个较少的连续区域中.
例如: 程序经常访问的内存, 把这内存置换至缓存即可.
主存之外增加辅助存储器.
辅存: 主存是为了解决主存容量不足的问题.
整体结构
cycle: 一个CPU的周期. CPU由晶振驱动, 晶振一上一下就产生一个周期. 纳米级别的.
一个数据如果频繁被读取, 会缓存在L1,L2或L3.
主存储器
主存储器–内存.
RAM通过电容存储数据的, 必须隔一段时间就刷新一次.
如果掉电, 那么一段时间后就会丢失所有数据.
32位系统: 地址总线32位, 所以最大寻址是 2^32^ = 4 x 2^30^ = 4GB
64位系统: 地址总线64位, 所以最大寻址是 2^64^ = 2^34^ x 2^30^ = 2^34^GB
辅存存储器
机械硬盘
- 磁道(Track) 一个环
- 柱面(Cylinder) 多个环形成空心的圆柱体
- 扇区(Sector)
- 簇(Cluster), 虚拟概念, 如果是4kb的话, 就8个扇区(512byte). 主要是因为扇区太小了, 很多数据跨扇区, 所以抽象出更大的一个区域出来.
表面是可磁化的硬磁性特性材料
移动磁头径向运动读取磁道信息
先来先服务算法
- 排队读取(简单粗暴)
最短寻道时间优先
- 磁头离磁道最近的先读取
扫描算法(电梯算法)
- 和电梯的算法一致, 所以也叫电梯算法
- 每次只往一个方向移动
- 到达需要服务的尽头再反方向移动(4-3-2-1-1-5)
- 不公平
循环扫描算法
- 只能往一个方向移动. 从内到外或者从外到内
- 4-5-1-1-2-3
- 相对更公平
固态硬盘
使用集成电路读数据进行永久存储.
- 控制器, 寻址用
- 存储芯片, 一般是闪存
高速缓存
工作原理
- 字: 是指存放在一个存储单元中的二进制代码组合
- 字块: 存储在连续的存储单元中而被看作是一个单元的一组字
所以选址就包括两部分了, 一个是字块的位置, 一个是字的位置.
命中率: 访问主存次数 / 访问Cache次数 + 访问主存次数
访问效率 也能衡量高速缓存的性能.
所以要高的命中率和访问效率, 就需要良好的缓存替换策略.
置换策略
- 随机算法
- 先进先出算法
- 看作是一个队列
- 先替换最先进入的字块
- 最不经常使用算法(LFU)
- 优先淘汰最不经常使用的字块
- 需要额外空间记录字块的使用频率
- 最近最少使用算法(LRU)
- 优先淘汰掉一段时间内没有使用的字块
- 一般使用双向链表实现
- 把当前访问节点置于链表前面(保证链表头部节点是最近使用的)
计算机指令系统
- 计算机通过指针指挥计算机工作.
- CPU被时钟驱动, 不断地读取PC指针指向的指令, 并增加PC指针, 从内存中读取指令并执行(周而复始).
- 不同CPU架构使用不同的指令, 都符合图灵规范. 目前使用最广泛的是RISC(Reduced instruction set computer, 精简指令集), 这个指令集的位宽都一致.
机器指令组成: 操作码+地址码
可分为三地址指令, 二地址指令和一地址指令(自增)
零地址指令, 无地址码, 空操作, 停机操作, 中断返回操作等.
指令操作类型
- 数据传输(load/store 指令, 从内存中读/写入内存)
- 寄存器之间, 寄存器与存储单元之间, 存储单元之间
- 数据读写, 交换地址数据, 清零置一等操作
- load类: lw(加载一个字), lb(加载一个byte), lh(加载半个byte)
- store类: sw, sb, sh
- 算术逻辑
- 操作数之间加减乘除(指令后有个 u 是 unsigned 的意思)
- 立即寻址: addi, subi, divi, multi 等…
- 寄存器寻址: add, sub, div, mult 等….
- 操作数之间与或非( and/or/xor 等)
- 操作数之间加减乘除(指令后有个 u 是 unsigned 的意思)
- 移位操作
- 左移, 右移
- 完成数据在算术逻辑单元的必要操作
- 控制指令
- 等待指令
- 停机指令
- 空操作指令
- 中断指令
- 比较与分支
- beq(等于), bne(不等于), slt(set if less than)
- 跳转
- jump j 1000 (相对选址)
- jump register jr $31 (寄存器间接选址)
- jump and link jal 1000 (多合一, 函数实现)
地址指令的寻址模式
Addressing Model
- 指令集的一部分, 决定指令有几个操作符, 地址如何计算
- opcode 代表指令的类型; opcode 也决定寻址模式.
分类
- 指令选址
- 顺序寻址
- 跳跃寻址
- 数据选址
- 立即选址, 直接获得操作数, 无需访问存储器. 但是地址的位数限制了操作数的表示范围.
- 直接选址, 直接给出操作数在主存的地址, 无需计算操作数的地址. 缺点同上.
- 间接选址, 指令给出的地址是操作数地址的地址(指向主存, 主存中存的是操作数的地址), 需要一次或多次获取操作数. 缺点是速度较慢.
寄存器寻址. 寄存器从内存地址读取数据.
立即数选址. 操作符中有值, 数值部分占据了部分, 所以会有大小的限制.
偏移量寻址. 根据基地址和偏移量进行选址. &sp
是base寄存器.
PC相对寻址
![PC相对寻址`](../assets/Snipaste_2021-07-31_11-49-37.jpg)
MIPS-32指令实例
MIPS-32是 RISC 的. opcode 是操作符的意思.
target 左移两位.
rs(register source): 源寄存器, rt(register target): 目标寄存器, rd(register destination): 目的寄存器
funct=0x20
是说使用 add 操作.
从内存地址读取数据到寄存器的指令.
助记符, 也称作汇编语言.
计算机的控制器
控制器是协调和控制计算机运行的.
程序计数器
- 存储下一条指令的地址
- 循环不断从程序计数器中拿出指令
- 指令拿出是, 指向下一条指令
时序发生器
- 电器工程领域, 用于发送时序脉冲
- CPU 依据不同的时序脉冲有节奏地进行工作
指令译码器
- 指令译码器是控制器地主要部件之一
- 计算机指令由操作码和地址码组成的
- 翻译操作码对应的操作以及控制传输地址码对应的数据
指令寄存器(Register)
可以是控制器的主要部件
从主存或高速缓存取计算机指令
主存地址寄存器: 保存当前CPU正要访问的内存单元的地址
主存数据寄存器: 保存当前CPU正要读或写的主存数据
通用寄存器: 用于暂时存放或传输数据或指令; 可保存 ALU 运算的中间结果; 容量比一般专用寄存器要大
计算机的运算器
用于进行数据运算加工的.
数据缓冲器
- 用来暂时存放 输入/输出 外设发 送过来/送出去 的数据
ALU
- 算术逻辑单元, 是运算器的主要组成
- 常见的位运算(左右移, 与或非)
- 算术运算(加减乘除)
内存到CPU还要经过缓存(L1, L2, L3)才到寄存器.
划分2个空间, 一个数据空间地址, 一个指令空间地址.
指令占的字节数根据系统有关系, 通常32位占4个字节, 64位8个字节.
寄存器
状态字寄存器
- 存放运算状态(条件码, 进位, 溢出, 结果正负等)
- 存放运算控制信息(调试跟踪标记位, 允许中断位等)
通用寄存器
- 用于暂时存放或传送数据或指令
- 可保存ALU的运算中间结果
- 容量比一般专用寄存器大
运算过程
5000 * 0.2 的计算过程
地址 1000 的数据 load 进 寄存器. CPU 都是用寄存器中的值进行计算的.
程序指针(PC) – 指向指令空间中要指向的指令, 一步一步往下执行
计算机指令的执行过程
CPU指令集分类
机器循环
CPU周期. CPU 有时钟, 晶振来驱动的.
晶振 产生波, 有波峰有波谷. 指令操作的周期一般是固定的.
PC++是指移动到下一个指令, +的是一个指令宽度, RISC的每一个位宽都是一样的, 在32位系统通常是+4.
指令周期
execute – 计算 store – 存储, 控制
指令执行过程
取指令 -> 分析指令 -> 执行指令 -> 程序指针++
- 把指令和数据缓存起来.
- 程序计数器只知道执行指令的地址, 需要通过 总线 从指令缓冲获取执行指令.
- 取出的指令通过 总线 来到 指令寄存器, 指令寄存器缓存指令内容.
- 把指令内容发送给 指令译码器, 这时候 程序计数器+1.
- 指令译码器解析之后, 通过总线发出 控制信号 给 运算器.
- 运算器中的 寄存器 转载数据.
- 通过 ALU 进行计算, 记录运算状态.
- 最后把结果放在数据缓存器.
数据缓存也叫做数据空间, 指令缓存也叫做指令空间.
因为这个过程, 控制器和运算器不是一起工作了, 导致了一个问题, CPU 综合利用率不高(串行).
所以提出了 CPU的流水线设计
计算机的计算
计算机的进制
计算机用二进制, 但是二进制表达太长了, 所以使用十六进制缩短表达.
八进制和十六进制满足2的n次方的要求.
二进制转换成十进制
十进制转成二进制
小数二进制转换十进制
小数十进制转换二进制
原码, 补码, 反码
原码表示法
- 使用0表示正数, 1表示负数
- 规定符号位位于第一位
计算机使用原码进行计算很复杂, 特别是两个操作数符号不一致的时候
- 判断两个操作数绝对值大小
- 使用绝对值大的值减去绝对值小的数
- 符号值要以大的为准
二进制的补码表示法. n表示寄存器的位数, 其实是x的位数
大于0, 补码等于原码
例如: n=4, x=-7的补码
负数为原码的反码加一.
- 使用正数代替负数的方法
- 使用加法操作代替减法操作, 从而消除减法
补码计算过程中还是使用了减法.
反码
目的是找出原码和补码之间的规律, 消除转换过程中的减法.
小数的补码
定点数和浮点数
定点数
小数点固定在某个位置的数.
一个数如果不是纯小数或纯整数, 就要 乘以比例因子以满足定点数保存格式
左移或右移
浮点数
浮点数在计算机里是通过整数来表示.
- 计算机处理的很大程度上不是纯小数或纯整数
- 数据范围很大, 定点数难以表达
举个例子
- 十进制0.1表示1/10, 二进制里0.1代表1/2, 0.01代表1/4.
- 所以十进制的 0.375(3/8) 在二进制中表示 0.011= 1/4 + 1/8.
-1^0^(1.1)^1^ = 1.1 = 1.5
科学计数法: 尾数, 基数, 阶码
尾数必须是纯小数.
浮点数表示范围
浮点数的上溢, 下溢
单精度浮点数: 使用4字节, 32位来表示的浮点数(float)
双精度浮点数: 使用8字节, 64位来表示的浮点数(double)
浮点数规格化
- 尾数规定使用纯小数
- 尾数最高位必须是1
对比
- 当定点数与浮点数位数相同时, 浮点数表示的范围更大
- 当浮点数位数为规格化数时, 浮点数的精度更高.
- 浮点数的运算包含阶码和尾数, 浮点数的运算更为复杂
浮点数在数的表示范围, 精度, 溢出处理, 编程等方面都优于定点数.
定点数在运算规则, 运算速度, 硬件成本方面优于浮点数.
定点数的加减法
定点数加法
数值位与符号位一同运算, 并将符号位产生的进位自然丢掉.
例子
溢出, 如果两个操作数的位数少于结构, 就会发生溢出现象.
判断溢出
- 双符号位判断法
- 单符号位表示变成双符号位: 0=>00, 1=>11
- 双符号位产生的进位丢弃
- 结果的双符号位不同则表示溢出
定点数减法
浮点数的加减法
对阶 -> 尾数求和 -> 尾数规格化 -> 舍入 -> 溢出判断
对阶的目的是使得两个浮点数阶码一致, 使得尾数可以进行运算.
尾数右移使阶码对齐.
尾数求和
- 使用补码进行运算
- 减法运算转换为加法运算: A - B = A + (-B)
尾数规格化
- 一般情况下都是左移
- 双符号不一致下需要右移(定点运算的溢出情况)
舍入
- 0舍1入 法(二进制的四舍五入)
因为舍去的是1, 所以要+1. 如果舍去的是0就不需要加.
可能会出现溢出现象.
溢出判断
- 定点运算双符号位不一致为溢出
- 浮点运算双符号位不一致不算溢出
- 浮点数运算主要是通过阶码的双符号位判断是否溢出
阶码符号位一致就没有溢出.
整体流程
计算机组成原理实践
汇编
使用汇编实现递归函数.
实现思路
函数就是先跳到一个标签再跳回来.
函数传参–栈. $sp
寄存器, 也称为栈指针.
压栈逻辑, 实现函数传参. 栈是往小的方向减少的. 出栈是++. 往上地址越大.
实现函数返回. 注意要在函数体之前把函数的返回位置压栈. 压栈就伴随栈指针–. 出栈++.
栈: 图解
- lw $r0 8(&sp) 取出参数 栈指针+8
- lw &t1 4($sp)
- jr $t1 t1存了函数调用的位置, 所以跳回去
实现返回值. 先初始化一个返回值, 把返回值进行压栈.
压栈了3个参数.
上一级压栈下一个参数. 也就是说函数的控制权有一个交叠的关系.