《计算机组成原理》复习笔记
抽代真的很善良,但考完还是麻了两天,今天开始复习!(课件没有给出每个大章节的名字,我自己编的)以及这篇笔记是直接用Markdown编辑的,所以字体不是很好看,但写公式真的很方便。
写了一部分后发现吐槽太多了,所以尽量改成灰色的引用格式了,不想看的请……自己去看PPT
第一章 概述
感觉是计系概+数电
1.1 Intro
计算机:一种高速运行的电子设备,用于进行数据的算术或者逻辑运算,可接受输入信息,根据用户要求对信息进行加工,并输出结果。
计算机的基本组成:CPU,Memory,IO
因为我是后面才复习的第一章,刚发现后面几章的顺序就是CPU,Memory,IO,感觉混乱中透露出一丝逻辑
\(高级语言\xrightarrow{编译器} 汇编语言 \xrightarrow{汇编器}机器语言\xrightarrow{控制信号生成}控制信号\)
图灵机:有穷符号集,有穷状态集,转移函数和无限长的纸带。
如何使用计算机:单/多用户,单/多程序。
操作系统相当于支持了多用户和多程序。
1.2 Inst-thinpad
程序的最小单位是指令,指令也是计算机硬件执行程序的最小单位。
冯诺依曼结构:
- 存储程序计算机:程序由指令组成,程序功能通过指令序列描述,指令序列在存储器中顺序存放。
- 顺序执行指令:PC指向要执行的指令,从存储器中读出指令执行,读取完成后PC自增指向下一条指令。
指令的分类:
- 数据运算指令:算数、逻辑运算
- 数据传输指令:内存-寄存器,寄存器-寄存器
- 控制指令:无条件跳转,条件跳转,子程序调用和返回
- 输入输出指令:与输入输出端口的数据传输
- 其他指令:停机,开/关中断,特权指令等
指令格式:操作码、操作地址的二进制分配方案。
指令字:完整的一条指令的二进制表示。
指令字长:指令字中二进制的位数。
机器字长:计算机能够直接处理的二进制数据的位数。
有时候也用指令字长=0.5,1,2个机器字长这样的方法来表示指令长度。
存在很多不同的指令集,如x86,ARM,MIPS
复杂指令集CISC-精简指令集RISC(如果需要复杂操作,则由软件将几条简单指令组合起来来实现)
RISC-V的特点:
- 指令集架构简单,指令数量少;
- 模块化的指令集设计:不同模式下的指令集互相兼容;
- 最基本的指令集部分是 I 表示的基本整数指令子集,已足够实现完整的软件编译器。其他指令子集如M/A/F均可选。
规整的指令编码:
- 所有通用寄存器在指令码的位置是一样的,方便译码
- 指令都是32位字长,有六种格式(第二章有图)
RISC-V的特权模式:机器模式(M态),监督模式(S态),用户模式(U态)。其中M态为必选模式,另外两种可选。
ThinPAD RISC-V指令系统:
可以理解为课程特供吗(?
其中有RV32I的19条+RV64的3条+优先级异常中断的6条(csrrc,csrrs,csrrw,ebreak,ecall,mret)+虚拟地址1条(sfence.vma)。
监控程序:实验特供,只是感觉这个配色挺好看的所以贴上来
题外话:页表不一定要是单射,比如监控3里面就有两个虚拟地址映射到同一个实际地址的情况。
1.3.1 Inst-general
感觉这课的PPT非常重章叠句……每个PPT先有五六页是前情回顾,最后几页是下节预告,还有些内容不知为何多次出现在不同PPT里。比如这章又开始讲指令分类了,还在上面的基础上把转移指令和子程序调用指令分成了两个大类,又加了一个“堆栈操作指令”。
存在一些指令集,他们的操作码长度也是不固定的,就是说最高位的固定长度是基本操作码,而对于部分指令,会将多位辅助操作码放到操作数地址字段。这样在不增加指令字长的情况下可以表示更多指令,但让译码变得更复杂。
可以根据有几个操作数来对指令分类:无操作数指令、单操作数指令等
操作数的来源和去向:CPU内部的通用寄存器、输入输出设备接口的寄存器、主储存器的存储单元等。
指令操作码扩展技术:
大概就是说需要地址更多的指令留出一些操作码(如三地址指令不用1111作为opcode),当opcode为1111时视为二地址指令,此时占用一个操作数地址用来表示二地址指令的opcode。注意不要出现二义性。
寻址方式:
- 立即数寻址:就是指令中直接给出运算所用的立即数;
- 直接寻址:在指令中直接给出所需操作数的地址;
- 寄存器寻址/寄存器间接寻址:寄存器内的为操作数,或寄存器数据指向的地址里存着操作数;
- 变址寻址:操作数地址为变址寄存器的值+指令中offset;
- 相对寻址:指令的地址为pc+指令给出的offset,偏移量用补码表示;
- 间接寻址:指令地址段给出的是地址的地址,需要多读一次内存来得到地址;
- 基址寻址:操作数地址为基址寄存器的值+指令中offset,一般用于为程序或数据分配存储区;
- 堆栈寻址:出栈入栈,需要用到sp。
x86不考,不过x86的指令格式复杂,指令级别若干种,寻址方式也非常多,甚至可以 \(E=B+I\times S + offset\),是典型的CISC指令集。(上网搜索了一下,x86可以设置两个寄存器为字符串的起始位置,第三个寄存器为长度,然后用一条指令比较两个字符串是否相等,确实复杂。
1.3.2 Inst-Riscv
寄存器组主要包括通用寄存器和控制状态寄存器(csr)
记录一下双操作数的操作顺序
x1 = x2-x3 -> sub x1, x2, x3
x1 = x2+5 -> addi x1, x2 ,5
x1 = x2/x3 -> div x1, x2, x3
A为int数组,起始地址在x3中,x10=A[3] -> lw x10, 12(x3)
特殊寄存器x0, 也叫zero,对它的写入会被直接忽略,读取它永远为0.
溢出:RISC-V会忽略溢出,高位截断,低位写入目标寄存器。
移位操作:
- 左移相当于相当于乘以2的次幂,即二进制下右边补0,运算速度更快
- 逻辑右移:在最高位添加0
- 算数右移:在最高位添加符号位
- 移位最多只能移到全都没有(32/64),如果移位指令给出了一个更大的数,则截断取后几位。就是lab3里那个 short_b=b[3:0]
数据传输:
- RISCV专用内存到寄存器之间传输数据的指令,其他指令都只能操作寄存器
- 支持字节(8bit),半字(16bit),字(32bit)的数据传输,如果是64位则额外支持双字(64bit)
- 推荐但不强制数据对齐
store:寄存器到内存;
load:内存到寄存器;
计算机操作以8bits,即一个字节为单位,一个字(word)=4字节。字的地址为其最低位字节的地址,地址之间有四个字节的距离,所以地址最后两位为0。
lw必须地址是4的倍数,但lb之类的可以不是。
大端序:最高的字节在最低的地址;小端序:最低的字节在最低的地址。
RISCV是小端序。
lb/sb使用最低的字节,如果是sb指令,高24位被忽略;如果是lb指令,高24位做符号扩展。
比较:slt,slti是带符号比较;sltu,sltiu是无符号数比较。
跳转:beq(branch if equal),rs1=rs2时跳转;bne(branch not equal),不相等的时候跳转。
伪指令:让程序员看起来更直观,但指令集里没有,翻译成机器码的时候会变。如move, li, la, nop.
call会被翻译成 auipc+jalr.
函数调用:
x1(ra),返回地址寄存器,用于返回到起始点。
x2(sp),栈指针。
pc是程序员不可见的,但jal可以访问。所有的分支跳转本质上都是修改pc
寄存器使用惯例: 寄存器约定的方案,调用者保存的寄存器(caller)在函数调用前后可能会被改变;被调用者保存的寄存器(callee)在调用前后不会被改变. 如果寄存器不够用,则可以将原值保存在栈上,待函数返回的时候恢复
Callee Saved Registers: \(s0-s11(x8,x9,x18-x27),以及sp(x2)\)
caller Saved Registers:\(t0-t6(x5-x7 + x28-x31),a0-a1(x10,x11)+ra(x1)\)
计系概,很神奇吧。以及说一下为什么当时计系概没写复习笔记,因为是躺在被子里看csapp复习的(
1.4 data-ecc
数据表示以及检错纠错
编码表示:0/1两个基本符号,但可以表示各种各样的数据。
ASCII:使用7位二进制编码表示128个字符,但占用一个字节。
unicode:跨语言跨平台,表示的字符很多。
utf-8:变长字符编码,长度由首字节决定,可扩展性强。
整数:
原码反码补码
浮点数:
s+exp+frac
检错纠错码:
奇偶校验、海明码、循环冗余校验码
看起来ppt只讲了一点点,但去年甚至考了最大的非规格数这种东西,打算把csapp拿回来看看
1.5 ALU
运算器的基本逻辑电路:逻辑门,加法器,触发器,多路选择器、移位器。
输入 a,b,opcode,输出 ans。假设只存在与和或两种运算,可以两种都算,然后用opcode做选择器取出需要的结果。
具体怎么做运算,就是列一下真值表,卡诺图之类的然后用各种门连起来。笑死,已经忘光了,不过应该不会考具体的电路设计。
超前进位:通过单独的进位电路提前算进位,好像设计比较精妙,可以减少几级门延迟。
补码减法:\((a-b)_补=(a)_补-(b)_补\)
\((-b)_补=(b)_补的各位取反再+1\)
所以可以用加法器来做减法。
二进制乘法:有点像快速幂,考虑乘数每一位,如果是0就 \(sum=sum\times 2\), 否则 \(sum=sum\times 2 + a\).
如果被乘数和alu也都使用64位就有点浪费。
一种很省的方法:因为每乘1位,乘数就有1位不需要了,而答案也最多增长一位,所以可以把乘数和答案共用一个64位寄存器。
补码乘法:
- 方案1:补码转成原码绝对值,乘完加上符号再转回来
- 方案2:布斯算法
布斯算法:
困困,睡醒写
除法:
看起来也有点复杂,睡醒写
实现上也可以省,即32位除数寄存器,32位ALU,64位余数+被除数寄存器。可能也没省,毕竟除的答案本来就不会超过32位。
1.6 AM2901
这 是 什 么
据说不考,而且“计算器的电路实现肯定也是不考的”
第二章 CPU
2.1 Datapath
Riscv指令格式:每条指令32位二进制,相当于4个字节,1个字。
- R 格式,寄存器指令,包括指令类型,操作寄存器和目的寄存器
- I 格式,指令中包含立即数,用于带常数的计算以及加载指令
- S 格式,store指令
- B 格式,分支指令
- U 格式,涉及较大立即数的指令
- J 格式,用于无条件跳转
R 指令
- opcode=0110011
- funct3,funct7指定指令的具体功能
- funct共有10位,理论上可以有1024种R指令
I指令
- imm为十二位立即数,需要符号扩展成32位后再运算
- imm表示范围 \([-2048,2047]\)
- 不一定都是用来做运算的,有可能有
lw x14, 8(x2)
即x14 = mem[ 8 + x2 ]
S指令
- 为什么imm拆成两段:希望rs1和rs2的位置固定,这样每次都可以直接去读寄存器而不需要做判断,不用的话丢了就行,也没有损失
- 其余部分和load非常类似
B指令
- rs1和rs2用来比较,判断是否跳转
- imm指定跳转的相对地址,如
if(rs1<rs2) pc+=imm
- 相对寻址可以指定从pc开始的 \(\plusmn 2^{11}\) 个地址,但因为地址的最后一位总是0(其实riscv32I中最后两位都是0),所以imm中省略这一位,表示的地址范围是 \(pc \plusmn 2^{12}\) 字节
- 相对寻址的好处是如果整块代码移动则这些值都不用改,不过如果只修改几行的话还是要改。
- 如果需要移动的范围比较大,可以考虑 B 指令和 J 指令一起用(满足条件则执行J,J的范围大得多)
U指令
- LUI:将20位的立即数装入到寄存器rd的高20位,低12位清空;然后再通过别的方法设置低12位比如addi
- AUIPC:
rd = PC + (imm<<12)
J指令
- jal:
rd = PC + 4; PC += imm
- 这样相当于用rd记录了返回地址,然后跳转
- 给imm留了19位,但最后一位默认填0,所以imm20位,跳转范围 \(pc \plusmn 2^{18}\) 个指令
JALR指令:属于I型指令。rd = PC + 4; PC = rs1 + imm
之后是数据通路部分,经常造CPU的朋友们都知道……哦不知道,感觉会写CPU和会做CPU考试题还是差别挺大。
以R指令为例,取指令-分析指令-执行指令(取操作数-运算-写回),可以设计如下数据通路:
以下构造方法省略,想必造完机不至于连这个都忘了:
执行指令五阶段:
- 读取指令(IF), 从存储器读来指令并形成下条指令地址
- 指令译码(ID), 指令译码,读寄存器堆为ALU准备数据
- 执行运算(EXE),ALU 执行数据运算或计算存储器地址
- 存储器读写(MEM),完成存储器的读操作或者写操作
- 写回(WB), 写ALU的结果或存储器读出数据到寄存器堆
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240111193330707-166526105.png)
计算机一条指令的执行时间被称为指令周期,一个CPU时钟时间被称为CPU周期,执行每条指令平均使用的CPU周期个数称为CPI。
不同类型CPU:
- 单周期CPU:每条指令使用一个CPU周期,前一条指令执行完再执行下一条。
- 多周期CPU:每条指令使用的CPU周期数不同,相邻指令可以完全串行,也可能存在一定的时间重叠。
- 流水线CPU:每条指令都使用5个步骤,但相邻指令间时间重叠,称为指令流水线技术。尖峰CPI=1。是当前计算机的普遍方案。
2.2 single cycle
好像没什么好说的,其实就是分析了一下时序逻辑要用D触发器……?
D触发器就是每个上升沿判断如果WE=1,则D等于目前的Q,否则不变;
然后就是分析每条指令需要的控制信号,可能需要注意这里的ImmSel要写的是指令类型,如 I,R,S.
单周期CPU:
- 优点:每条指令占用一个时钟周期,逻辑设计简单,时序设计也简单;
- 缺点1:各组成部件的利用率不高,因为整个周期都要维持控制信号不变;
- 缺点2:时钟周期要满足所需时间最长的指令的要求,比如Load(又要用ALU,又要读内存,又要写寄存器堆)
但单周期CPU其实CPI=1啦!
注意单周期CPU无需状态信息,而且控制信号生成要用组合逻辑。
2.3 multi cycle
难懂,为什么这个课件是pptx格式,使我不能在浏览器上看www
单周期CPU的性能问题:
时钟周期要按照最长的周期来,如Load指令需要读指令,取寄存器,运算,读数据,写寄存器;但branch指令可能就只需要读指令,取寄存器,运算。这里可能可以出计算题,ppt上有一个就是算理论最小时间和优化率的。
多周期CPU:
- 将指令执行步骤分成多个部分,每条指令占用他所需要的步骤数
- 每个步骤占用一个时钟周期,尽量限制每个步骤使用单一的主要部件
- 由存储器,寄存器堆,ALU,控制部件(节拍发生器+控制信号生成电路)组成
有两种不同的控制器:
- 硬连线控制器(组合逻辑):由PC,IR(指令寄存器),节拍发生器Timer(给出并维护执行步骤)和控制信号产生部件组成。可以理解为 \(信号=f(指令内容,执行步骤)\)
- 微程序控制器:采用控制存储器存储每条指令每个步骤所需要的全部控制信号。运行方法为,根据pc映射出该指令的首条微指令的地址,每条微指令给出其下一步骤的微地址。不是特别懂,感觉是直接把每个步骤拆开,然后视为单周期来搞?
后面有好多页都是多周期执行细节。
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240112194110119-14501860.png)
2.4 pipeline
流水线:任务分解为多个步骤,各个部件可以同时执行不同任务的不同步骤。流水线并没有提高单个任务的延迟,但提高了吞吐率。
可能的加速比=流水段数,流水线效率受限于用时最长的流水段。如果每个阶段用时不同会降低流水线效率,装入和排空流水线也会降低加速比,冲突会引起流水线暂停。
提高处理器内部的并行性:
- 空间并行性,即在一个处理器内设置多个独立的操作部件,使他们并行工作。
- 时间并行性,即流水线技术,不需要添加很多硬件就能大幅提速,好好好
计算机的各个部分都可以使用流水线技术:
- 指令流水线:指令的执行过程采用流水线;
- 操作部件流水线:运算器中的操作部件如加法器乘法器采用流水线;
- 宏流水线:多个计算机间通过存储器相连,采用流水线。
如果每个步骤的时间都为t,则n条指令使用的时间:
- 顺序执行 \(T=3nt\)
- 一次重叠执行 \(T=(1+2n)t\)
- 两次重叠执行 \(T=(n+2)t\)
流水线的特点:任务分成几个有联系的子任务,每个子任务由一个专门的部件来实现。流水线每个阶段后面都要有一个缓冲寄存器,保存本段流水的结果。
要求时钟周期不能短于最慢的流水段。
流水线的装入时间指第一个任务进流水线-出流水线的时间;排空时间指最后一个任务进流水线-出流水线的时间。
衡量流水线性能的重要指标是吞吐率和加速比,所以流水线的最佳段数选择也是很重要的。
下面是具体的流水线CPU相关内容:
每个时刻,每条指令都只在一个流水段上是活动的,因此,任何指令所作的任何动作都发生在一对流水线寄存器之间,具体操作由指令类型决定。
1ps=\(1^{-12}s\),1G就按照 \(10^9\) 理解就可以,和1024啥的无关。
指令系统的配合:指令字长固定,简化流水的实现(否则都不知道下个指令在哪);指令格式固定,可以尽早开始访问寄存器;访存方式保证不需要访问内存后再运算。
流水线的控制:
因为每个部件同一时间执行的是不同的指令,所以要把控制信号和数据一起流动起来。每个时钟周期往下一步骤传递控制信号,使正确的控制信号到达正确的位置。
在ID段需要生成:一个周期后exe要用的信号,两个周期后mem要用的信号,三个周期后wb要用的信号。
问题:
需要保证指令重叠执行时不会出现资源冲突,即流水线各段在同一个时钟周期里不使用相同的数据通路资源。
事实上如果指令和数据存在不同的地方(IF和MEM不冲突)会减少很多冲突。
2.5 hazards1
有三种冲突:
- 结构冲突:重叠执行的过程中,硬件资源无法满足。
- 数据冲突:指令依赖前面指令的结果,但无法及时得到。
- 控制冲突:分支指令或其他需要改写pc的指令导致的冲突。
冲突可能导致性能问题,或者出错。
结构冲突:
- 两条指令访问相同的物理资源。
- 解决方法:指令依次使用资源,一些指令暂停stall;增加硬件资源
- 寄存器结构冲突,可以设置两个独立的读端口和一个写端口,就可以在一个周期完成三次访问。
- 内存结构冲突:由于内存结构的限制,这个没法同时读写。(所以单周期CPU其实也是会冲突的)可以通过暂停流水线来解决,插入气泡。另一种解决方案是将指令内存和数据内存分开。
数据冲突:
- 写后读:指令j在i后,但需要使用i的计算结果。可能在i写前就进行了读,错误。
- 写后写:ij的目的操作数相同,但j先写了i才写,把正确的覆盖了。
- 读后写:j已经写入了结果,但i需要的是之前的数据,错误。
- 注意,因为riscv的指令流水只会在id段读寄存器,wb段写寄存器,所以不会出现写后写,读后写的问题。
如果我们认为寄存器堆不能双边沿访问,则AXXB中的AB有可能出现数据冲突。
解决方法:数据旁路,结果可用的时候先前传,无需保存到Regfile. 可以加一个Forward Unit.
检测冲突:
- 检测当前指令的ID/EXE段和上一指令的EXE/MEM段,如果上一指令的目的寄存器和当前指令的源寄存器一样则有冲突。
- 不是所有的数据冲突都能用数据旁路解决,如上一条读内存,下一条接着要用,只能暂停
- 流水线暂停等于多了一条nop指令,让当前指令的所有控制信号均为0,pc不变,if/id段寄存器保持不变。
- 另一个解决方法:让汇编器放一个nop,然后就不用管了(
- 静态调度(汇编器调度):有时候换换指令顺序就可以避免冲突,而且也不会导致错误
- 动态调度(处理器调度):由硬件动态调整指令顺序来减少暂停的影响,可以简化编译器;主要思想是指令顺序发射-乱序执行-乱序流出。主要问题在于会导致异常处理的不精确(可能后面的指令已经执行完了)
2.6 hazards2
控制冲突:因为程序执行转移类指令而引起的冲突。如无条件转移、条件转移、调用子程序、中断等,可能导致程序的运行方向改变。
控制冲突影响的范围大,且IF在流水线第一段,使流水线损失更多的性能,所以称为全局冲突。
当执行条件转移指令时有两种情况:
1.发生转移,pc=目标值
2.不发生转移,pc指向下一条指令
因为每条指令IF段都要用到PC,但对于转移指令,至少到exe才能知道是否转移,所以会暂停流水线1或2个周期。
控制冲突的处理:
- 暂停流水线:直到得到正确的转移地址,会引起性能降低。
- 预测-分支不成功:顺序执行下一条指令,如果预测失败需要清除错误启动的指令;
- 预测-分支成功:比较复杂,因为需要计算跳转地址,如果预测失败需要清除错误启动的指令;
- 动态预测:根据上次的结果预测。
如何减少暂停的周期数:
- 尽早判断分支转移是否成功,尽早计算出成功转移时的PC值;
- 具体来说,数据通路增加比较器,且在ID段加一个加法器。
- 如果跳转,则
pc<-目的地址,IF_ID<-nop,ID_EXE<-0
控制冲突的动态调度:
- 分支目标缓冲技术(BTB)
- 将分支转移成功的分支指令的地址和它的分支目标地址都放到一个缓冲区中保存起来,在IF段把pc放到BTB中比一比,如果命中,则认为这条是分支指令,且分支目标是BTB中缓存的值。
- 上一段基本抄自PPT,评价为怪,其实就是弄一个(pc,next_pc)的缓存
减少BTB容量:
更怪,看PPT没懂在干啥,找了上课的回放感觉大受震撼。
大概意思就是“基础版BTB”甚至会缓存非跳转指令的next_pc,把这些删了就可以减少容量。啊……你说得对但是……?谁会用基础版……?
统计规律表明,上次转移则这次转移的概率更大,反之亦然。由于程序的局部性,根据上一次的结果进行动态预测的准确率会高很多。
所以BTB的一个表项是这样的(tag,0/1,btb),表示这个tag对应的上次跳没跳,这个01位叫做BHT. 就是,并不是看到这次没跳就直接把btb改成pc+4,否则好不容易算的next_pc就没了。这个优化感觉非常有道理。
动态预测-2位预测:
连续两次预测错误时才改变预测方向,对于多重循环可以进一步提高预测成功率。
异常:CPU运行时会发生的突发不正常事件,比如执行过程中出现错误(取指,译码,计算,访存都可能出问题),外部设备提出服务请求,多进程资源冲突等。
有两种中断程序正常执行流程的事件:如果来自CPU则称为异常;如果来自外部则称为中断。
异常响应和处理:
- 很难在程序中进行处理,因为时间空间的不确定性
- 对程序“透明”处理:即程序正常运行,硬件检测异常或中断,"透明"地自动转换到服务程序进行响应和处理,再返回到程序执行。
2.7 RISCV Exception
意识流启动
- 精确异常处理
- mepc中保存有发生异常指令的地址
- 操作系统处理简单
- 指令流水情况下实现比较复杂
- 非精确异常处理
- mepc中保存当前PC或者近似的PC
- 由操作系统处理
RISCV的特权级模式:
- 用户模式(user mode):执行用户应用程序的模式,本学期的大部分指令都执行在用户模式
- 机器模式(machine mode):运行最可信的代码,直接接触硬件
- 监管者模式(supervisor mode):为现代操作系统例如Linux等提供支持
- M>S>U
更高权限的模式可以使用低权限模式的功能,还有一些额外的功能。
RISC-V要求实现精确异常:保证异常之前的所有指令都完整执行,后续指令都没有开始执行。
- 同步异常:执行期间产生,访问无效的寄存器地址,或者执行了具有无效操作码的指令
- 访问错误异常:物理内存不支持访问类型(写入ROM)时发生
- 断点异常:执行ebreak指令,或者地址与数据与调试触发器匹配时发生
- 环境调用异常:在执行ecall的时候发生,相当于系统调用
- 非法指令异常:在译码中发现无效操作码时发生
- 非对齐地址异常:有效地址不能被访问大小整除时发生
- 异步异常:指令流异步的外部事件,中断,如鼠标点击
- 软件中断:通过向内存映射寄存器写入数据来触发,用户一个hart中断另外一个hart(处理器间中断)
- 时钟中断:实时计数器mtime大于hart的时间比较寄存器mtimecmp,会触发时钟中断
- 外部中断:由中断控制器触发,大部分情况下的外设都会连到这个中断控制器
中断时mcause的最高有效位置1,同步异常时置0,且低有效位标识了中断或异常的具体原因。
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240114115354861-2054162937.png)
处理过程:
- 异常指令的PC被保存在mepc中,pc被置为mtvec。(对于同步异常,mepc指向导致异常的指令;对于中断它指向中断处理后应该恢复执行的位置)
- 根据异常来源设置mcause,并将mtval设置为出错的地址或者其它使用特定异常的信息字
- 把控制状态寄存器mstatus中的MIE位置零以禁用中断,并把先前的MIE值保留到MPIE中
- 发生异常之前的权限模式保留在mstatus的MPP域中,再把权限模式更改为M
可抢占式异常处理:处理异常的过程中可以转到处理优先级更高的中断。但csr寄存器只有一份,所以要先放到栈里。
物理内存保护PMP:M模式指定U模式可以访问的内存地址,以相对较低的成本提供了内存保护。
S下的程序不能使用M态的CSR和指令,也会受到PMP的限制。默认情况下异常应该移交给M态,但RISCV提供一种机制,使得部分中断异常被委托给S处理。
异常委托寄存器:mideleg
发生异常时控制权都不会移交给权限更低的模式:
(1)M模式下发生的异常总是在M模式下处理
(2)S模式下发生的异常总是在M模式,或者在S模式下处理
(3)上述两种模式发生的异常不会由U模式处理
S态异常处理:
- 发生异常的指令PC被存入sepc, 且PC被设置为stvec
- scause根据异常设置类型,stval被设置为出错的地址或者其它特定异常的信息字
- 把sstatus CSR中的SIE置零,屏蔽中断,且SIE之前的值被保存在SPIE中
- 发生例外时的权限模式被保存在sstatus的SPP域,然后设置当前模式为S模式
与中断相关的指令:ecall, ebreak, mret, sret
操作csr:
csrrw dst, csr, src 同时读写的原子操作
csrr dst, csr 仅读取
csrw dst, csr 仅写入
第三章 各种存储
3.1 dram-lu
存储器:计算机中用来存放程序和数据的部件。程序和数据的共同点是都是二进制数据。
对存储介质的基本要求:能有稳定的状态表示0/1,容易识别,两个状态能方便地进行转换。(常见存储介质:磁颗粒,半导体,光)
磁芯存储器:陶瓷上涂磁粉,手工穿线,消磁后重写。
半导体存储器:原理是MOS管和触发器,访问机制为随机访问。
按照访问方式分类,有
- RAM:随机访问存储器,访问时间与存储位置无关
- SAM:顺序访问存储器,按照存储位置依次访问
- DAM:直接访问存储器,随机+顺序,磁盘存储器
- CAM:关联访问存储器,根据内容访问,Cache和TLB
存储系统设计目标:尽可能快的速度、尽可能大的空间、尽可能低的成本、较高的可靠性。
摩尔定律:处理器性能每年约增长60%. 但动态存储器性能每年仅增长9%左右。
层次存储器系统:
- 高速度:静态存储器速度高,设置较小容量的高速缓冲存储器
- 大容量:动态存储器速度价格都适中,作为主存储器
- 低成本:磁盘存储器价格低,作为辅助存储器,作为虚拟存储器的载体
局部性:
- 时间:最近被访问过的程序和数据很可能再次被访问
- 空间:在空间上这些被访问的程序和数据往往集中在一小片存储区
- 顺序:指令顺序执行比转移执行的可能性大(大约5:1)
时间局部性指的是同一个,空间局部性指的是相近的
层次间需要满足的原则:
(1)一致性:处在不同层次存储器中的同一个信息应保持相同的值
(2)包含性:处在内层的信息一定被包含在其外层的存储器中
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240113220115609-914587942.png)
DRAM:
原理是MOS管,或许学电子学时期的我是懂的,可惜现在不懂。
特点:破坏性读出(读完后必须立即写回),需定期刷新(不进行读写的情况下会自己漏电);
读写过程:给出地址-给出片选和读指令/数据-保存读出内容/给出写命令
不是,这后面都是啥啊,纯英文标题+一些意味不明的示意图,好像是讲了DRAM的分解
主存储器的作用和连接:存储正处在运行中的程序和数据(或一部分)的部件,通过地址数据控制三类总线与CPU、与其它部件连通。
总线:
- 地址总线:用于选择主存储器的一个存储单元,其位数决定了能够访问的存储单元的最大数目,称为最大可寻址空间。
- 数据总线:用于在计算机各功能部件之间传送数据,数据总线的位数(总线的宽度)与总线时钟频率的乘积,与该总线所支持的最高数据吞吐(输入/输出)能力成正比。
- 控制总线:用于指明总线的工作周期类型和本次入/出完成的时刻。即用不同的总线周期来区分要用哪个部件和操作的性质。
有点难懂的形容,是否可以理解为addr,data,en
动态存储器集成度高,存储容量大,为了节约管脚数->地址分为行地址和列地址。根据时序图,大概是先维持一会行地址,然后维持列地址(同时数据总线放上数据),使能信号也需要对应变化。
3.2 cache1
sram有种亲切的感觉
静态存储器(sram):速度快,存储密度低,数据出入共用管脚,能耗和成本高。
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240113222434279-2093800025.png)
时间局部性:将最近被访问的信息项装入到Cache中。
空间局部性:将最近被访问的信息项临近的信息一起装入到Cache中。
高速缓冲器Cache:
- 设置在主存和CPU之间的存储器,用高速的静态存储器实现,缓存了CPU频繁访问的信息。
- 特点是高速,透明
Cache参数:
- 块(Line):数据交换的最小单位
- 命中(Hit):在较高层次中发现要访问的内容
- 命中率(Hit Rate):命中次数/访问次数
- 命中时间:访问在较高层次中数据的时间
- 缺失(Miss):需要在较低层次中访问块
- 缺失率(Miss Rate):1-命中率
- 缺失损失(Miss Penalty):替换较高层次数据块的时间+将该块交付给处理器的时间
平均命中时间=命中率×命中时间+缺失率×缺失损失
典型数值:
- Line=4-128Bytes
- 命中时间=1-4cycles
- 失效损失:
- 访问时间:6-10周期
- 传输时间:2-22周期
- 命中率:80%-99%
- Cache容量:1KB-256KB
全相联方式:
- 每个缓存块可能放每个数据,用的时候比一比
- 主存的字块可以和Cache的任何字块对应,利用率高,方式灵活。
- 成本高:标志位较长,比较电路的成本太高。如果主存空间有 \(2^m\) 块,则标志位要有m位。同时,如果Cache有n块,则需要有n个比较电路。
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240113232409460-442788822.png)
直接映射方式:
- 每个主存位置如果出现在缓存里,那他的位置是固定的。
- 内存中的每个单元在Cache中只会有一个唯一的位置和它对应。
- 标志位较短,比较电路的成本低。如果主存空间有 \(2^m\) 块,Cache中字块有 \(2^c\)块,则标志位只要有m-c位。且仅需要比较一次。
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240113232604283-1170136615.png)
emm,总感觉这个标志位的问题和映射方法没啥关系,如果全相联也对齐再找的话其实也可以标志位短一些?(比如line=4,则访问10的时候直接转化为访问8)
3.3 cache2
组相联方式:
组内为全相连,组间为直接映射。
大概就是先映射到某一个组,然后组内每一条都有可能是所需的,此时再一个一个比较。
不同方式比较:
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240114014054092-1402588878.png)
一致性保证:
这段PPT太抽象了,看csapp懂了一些,不记了
提高存储访问的性能:
平均访问时间 = 命中时间 x 命中率 + 缺失损失 x 缺失率
所以方法有:提高命中率,缩短缺失时的访问时间,提高Cache本身的速度。
Cache缺失的四种原因:
- 必然缺失
- 开机或者是进程切换,首次访问
- 方法:要不算了
- 容量缺失
- 活动数据集超出了Cache的大小
- 方法:可以增加容量
- 冲突缺失
- 多个内存块映射到同一Cache块
- 方法:增加容量/增加路数(路数是指一组有几条)
- 无效缺失
- 其他进程修改了主存数据
块太大会导致缺失损失增大(装入数据慢),块太小则不能充分利用时间局部性。
块替换策略:
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240114010026846-1606245856.png)
多级Cache也可以提高命中率;或者指令数据分开缓存也可以提高命中率。
多核一致性保证:应该不会考吧,但还挺好玩的。
3.4 vm-lu
复习麻了。据统计,拉帝奥教授共开设五十二门课程,那他有时间好好做PPT吗!
虚拟地址(逻辑地址):程序员编程使用的地址
物理地址(实地址):物理存储器的地址
虚拟地址的思想:
- 缓存的思想(虚拟化)- 如果程序的工作集>物理内存大小,程序还能执行
- 程序员编程使用的空间与程序运行空间相互独立 - 解决多个程序共享使用的问题
通过页表将虚拟地址转换成实际地址:
\(CPU\xrightarrow{Virtual Address} Page Table \xrightarrow{Physical Address} Memory\)
每个进程有独立的页表,但多个虚拟页面可以映射到同一个共享物理页面上。
页表还可以实现内存的保护:页表中存放有访问权限,如可读/可写等。
虚拟内存的好处:
- 容量:获得运行比物理存储器更大空间程序的能力
- 存储管理:内存的分配以及虚实地址转换
- 保护:操作系统可以对虚拟存储空间进行特定的保护
- 灵活:程序的某部分可以装入主存的任意位置
- 提高存储效率:只在主存储器中保留最重要的部分
- 提高并行度:在进行段页替换的同时可以执行其它进程
- 可扩展:为对象提供了扩展空间的能力
页式存储:
将主存和虚存划分为固定大小的页,以页为单位进行管理和数据交换。
虚地址=虚页号+页内地址;实地址=实页号+页内地址;
如何减少页表自己占的空间,而且还要实现简单:层次页表;反转页表。
访问过程:
- 得到程序给出的虚地址;
- 由虚地址得到虚页号;
- 访问页表,得到对应的实页号;
- 若该页已在内存中,则根据实页号得到实地址,访问内存;
- 否则,启动I/O,读出对应页装入主存,再进行访问
TLB 可以组织成全相联,组相联或直接映射方式。
TLB 通常容量较小,甚至在高端计算机上也一般不超过128 - 256 个表项。这样,可以使用全相连映射方式。在大多数中档计算机上,一般采用N路组相联映射方式。
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240114102304303-1426509615.png)
页面大小的选择:
减小页面大小可以减少内部碎片,但需要更大的页表。
现在的趋势是增大页表。
页面替换算法:最近最少使用(LRU)
将页帧按照 最近最多使用 到 最近最少使用 进行排序,再次访问一个页帧时,将该页帧移到表头,替换时将表尾的页帧换出。
一点改进:替换出其中一个“干净”的页帧。
访问未被映射的页或者访问权限不足会导致页面错误异常(page fault exception).
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240114103043921-1453867440.png)
段式内存访问,段页式内存访问不考,因为是x86才有的。
3.5 disk
好长,等会看
第四章 与外界交互
4.1 io
外部设备很多而且差异大,如何管理。
输入输出方式:
- 程序直接控制:
- CPU直接使用输入/输出指令来控制外部设备
- 如实验里处理串口的方法
- 程序中断:
- 外部设备请求,CPU暂停当前的程序进行响应,处理完后继续原来的程序
- 好处是可以提高CPU效率,可以同时管理多个设备
- 中断源:外中断,异常(内中断),中断触发器,中断状态寄存器
- 每个中断源有1个中断触发器,同时可设置1个中断屏蔽触发器
- 直接存储访问(DMA):
- 专用输入/输出控制器
- I/O设备和主存储器之间的直接数据通路,为专设的硬件,用于高速I/O设备和主存储器之间成组传送数据
- 通道
- 外围处理器
DMA:
问题:
- 如果DMA使用虚地址,则需要进行地址转换;
- 如果DMA使用实地址,则会有虚地址连续但实地址不连续的问题。
- Cache一致性:主存中的数据可能不是最新的
使用方式:
- 独占使用:当外设要求传送一批数据时,由DMA控制器发一个信号给CPU。DMA控制器获得总线控制权后,开始进行数据传送。一批数据传送完毕后,DMA控制器通知CPU可以使用内存,并把总线控制权交还给CPU。
- 周期挪用:当I/O设备没有 DMA请求时,CPU按程序要求访问内存:一旦 I/O设备有DMA请求,则I/O设备挪用一个或几个周期。(随时,一旦冲突,DMA优先)
- 交替使用:一个CPU周期分成两个时钟周期,一个专供DMA控制器访内,另一个专供CPU访问。
- DMA的特点是与设备一对一服务,对CPU的打扰适中。
通道:
- 根据CPU要求选择某一指定外设与系统相连,向该外设发出操作命令,进行初始化
- 控制外设与主存之间的数据交换
通道的类型:
- 字节多路通道:简单共享,分时处理;
- 选择通道:选择一台外设独占通道,成组传送;
- 数组多路通道:以上两种的结合
外围处理器:
实在没明白这是啥……难道是说外设
设计输入输出系统时要考虑性能、可扩展性、可适应性。
4.2 bus
总线:
- 一种共享的信息通道,用于连接计算机多个子系统。
- 好处:解决外部设备杂的问题,降低成本,简化设计
- 不足:容易成为瓶颈,总线的总带宽限制了整条总线的吞吐量。
现在主流使用三总线系统:处理器-主存总线,输入/输出总线,主板总线。
总线的一般组成:控制线+数据线。
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240114112206028-2029590866.png)
仲裁:
- 总线的重要问题:如何为需要使用总线的设备保留总线
- 可以只允许主设备发请求,从设备响应,这样就不会冲突,但坏处是“处理器被卷入到每一个总线事务中”
- 总线仲裁器在以下两方面取得平衡:
- 优先权:优先级高的设备应该得到优先服务
- 公平性:最低优先级的设备也不能永远被排除在总线服务之外
- 仲裁方式:集中仲裁/分布仲裁,按优先级仲裁/轮询仲裁
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240114112452967-1275129689.png)
很怪的词,菊链仲裁看起来就是单纯的链,总线授权信号按照优先级一个一个传
![](https://img2024.cnblogs.com/blog/1397708/202401/1397708-20240114112830801-1630491918.png)
异步总线:
增加总线带宽:
- 增加总线的宽度:可增加每个周期传送数据的量,但提高了成本
- 分别设置数据总线和地址总线:提高了成本
- 采用成组传送方式:一个总线事务传送多个数据,每次只需要在开始的时候传送一个地址,直到数据传送完毕才释放总线;(复杂度提高,延长后续总线请求的等待时间)
后面讲了不少PCI总线……的细节,不看了,没意思
4.3 device
接口:
- 提供主机识别(指定、找到)使用的I/O设备的支持。
- 建立主机和设备之间的控制与通信机制
- 提供主机和设备之间信息交换过程中的数据缓冲机构
- 提供主机和设备之间信息交换过程中的其他特别需求支持
- 通过主线和设备通信
8251A:
串行接口,可用于同步/异步传送。
还有若干细节,不看了www
USB的优点:
- 不必打开机箱来安装新的输入输出设备
- 应该只需要一根电缆线就可以将所有设备连接起来
- 输入/输出设备应可以从电缆上得到电源
- ……
- 总之好处多多
USB:
- 由4根线组成,电源、地和双数据线
- 采用同步传输方式
- 只有1个主设备,不需要仲裁,采用轮询方式,适合低速设备使用。
- 怎么也是一堆细节,不看了
还是来看看远方的外设吧
外部设备功能:
- 完成数据的输入和/或输出
- 信号转换
- 数据采样
- 与接口进行连接
- 接口信号,电平标准等
- 与主机进行通信
- 通过总线进行
- 速度
- 控制方式
比如,键盘可以输入字符(甚至有好几页讲键盘原理),鼠标(可以使用串口,USB口等等)
输出设备:
- 点阵式输出设备(视觉),以点阵的组合来表示不同的形状
- 音乐语音输出
- 打印机(?
CRT显示器:
- 光栅扫描和随机扫描
- 电子束从左到右,从上到下扫描整个屏幕
- 只扫描需要显示的点
- 真彩色是每个点3Byte(大概是说rgb吗)
- 需要连接PCI总线
输入/输出设备:
- 种类多样,功能繁杂,速度不一
- 满足计算机和外界进行信息交换的需要
- 人机交互的界面
考完后记
考的都是些啥啊