只有一条指令的CPU

根据ISA规范设计CPU

约定一些寄存器的位宽:

  • PC位宽为4位, 初值为0
  • GPR有4个, 位宽均为8位
  • 支持如下3条指令
 7 6 5 4 3 2 1 0
+----+----+-----+-----+
| 00 | rd | rs1 | rs2 | R[rd]=R[rs1]+R[rs2] add指令, 寄存器相加
+----+----+-----+-----+
| 10 | rd | imm | R[rd]=imm li指令, 装入立即数, 高位补0
+----+----+-----+-----+
| 11 | addr | rs2 | if (R[0]!=R[rs2]) PC=addr bner0指令, 若不等于R[0]则跳转
+----+----------+-----+

将这个用数字电路实现的sISA指令集的CPU称为sCPU

指令周期(instruction cycle)

执行一条指令的步骤

  1. 取指(fetch): 根据当前PC, 在存储器中找到一条指令
  2. 译码(decode): 看这条指令具体是什么指令, 操作数是哪些
  • li指令为例, 操作数需要看立即数是多少, 需要写入哪个目的寄存器
  1. 执行(execute): 对操作数进行处理, 必要时更新指定的目的寄存器
  2. 更新PC: 让PC指向下一条指令

实现CPU=用数字电路实现上述过程的每一个步骤

Q:为什么叫”周期”?

这就好比人的”呼吸周期”:吸气(取指) \to 屏气(译码) \to 呼气(执行)。呼完气后,你必须回到吸气状态,不能停下来。

取指

一个PC寄存器 -> 数字电路的寄存器

根据PC在存储器中找到一条指令 ->需要实现存储器

存储器

可寻址的存储单元集合

  • 存储器中的内容按顺序排布
  • 给出一个地址,存储器可以读出该地址对应的内容

将存储器看成一个由比特构成的矩阵

矩阵的每一行称为一个存储字(word)

  • 地址=行的编号,行的数量=存储器的深度(depth)

  • 一个存储字包含多位数据, 其位宽称为存储器的宽度(width)

通常用深度x宽度表示一个存储器的规格. 例如, 一个2x3的存储器排布如下, 其中b(x,y)b_{(x,y)}表示第xx行第yy列所存储的比特

地址存储字
0b_{(0,2)}$$b_{(0,1)}$$b_{(0,0)}
1b_{(1,2)}$$b_{(1,1)}$$b_{(1,0)}

存储器可以分别只读存储器(Read-Only Memory, ROM) 和随机访问存储器(Random Access Memory, RAM),

前者不支持写入, 而后者支持

用ROM实现取指操作

ROM的结构

ROM结构

左上方的译码器称为地址译码器

和地址译码器输出相连的导线称为**“字线”(word line)**, 每条字线对应一个存储字

和或门输出相连的导线称为**“位线”(bit line)**, 每条位线对应存储字的一位

给定地址addr, 可以读出ROM中的相应存储字, 其工作过程如下:

  1. 地址译码器将输入的地址转换成一组独热码
  2. 由于独热码中只有一位有效, 故所有字线中, 只有地址addr对应的字线有效, 使得该行中存放的信息可以通过与门
  • 其余行因字线无效, 存放的信息均被与门过滤为0
  1. 被选中的存储字的每一位经过或门传输到位线, 向存储器外部输出

你可以把这个 ROM 想象成一个管理非常严格的图书馆档案室。

  1. 地址译码器 (1-2 Decoder) —— 管理员
  • 你给管理员一个编号(addr,比如 0 或 1)。
  • 管理员手里有两把钥匙,但他一次只能用一把
  • 如果 addr=0,他打开第 0 排架子的灯(第 0 条字线变亮/变高电平)。
  • 如果 addr=1,他打开第 1 排架子的灯(第 1 条字线变亮/变高电平)。
  • 重点:同一时间,只有一排架子是亮的,其他的全是黑的。
  1. 存储单元 (“0”/“1” 常数 + AND门) —— 架子上的书
  • 每一排架子上放着预先写好的书(也就是图中的常数 “0”/“1”)。
  • AND门的作用(过滤)
  • 如果灯没亮(字线=0):不管书上写啥,你都看不见(输出全是 0)。
  • 如果灯亮了(字线=1):书上写的是 1 你就看到 1,写的是 0 你就看到 0。
  1. 位线与或门 (Bit Line + OR Gate) —— 复印机出口
  • 因为一次只有一排架子是亮的,所以对于每一列(每一位)来说,要么是”0+0”,要么是”1+0”,要么是”0+1”。
  • OR门 把大家汇聚起来。因为没被选中的行全是 0,所以输出的结果 = 被选中那一行的内容

图中的地址译码器, 与门和或门, 在功能上共同构成了一个3位的2选1多路选择器

因此ROM的读操作也可以看作是从多个存储字中选择一个

  • 地址addr就是多路选择器的选择端
  • 所有存储字分别作为多路选择器的数据端

ROM中存储的信息是直接通过高低电平编码的, 因此ROM从功能上也可以看作是数据端为常数的多路选择器

电路组件存储器术语形象比喻逻辑功能
译码器输出线字线 (Word Line)这行架子的灯开关决定哪一行被激活 (独热码)
OR门输入线位线 (Bit Line)竖着的传送带收集这一列的数据
常数+AND门存储单元 (Cell)书本内容 + 可视窗口只有被选中时才展示内容
整个结构ROM自动翻书机硬连线的多路选择器 (MUX)

译码

根据指令的编码识别指令的功能

用电路在二进制层面实现译码功能

  • 操作码译码:根据指令的操作码识别指令的功能
  • 目前只需要实现一条li指令,无需进行
  • 操作数译码:从指令的编码中识别出相应的操作数
  • 解析出li指令中的rdimm字段(位抽取操作)

执行

li指令的功能:将立即数imm写入rd寄存器

考虑如何实现ISA的GPR

  • GPR需要支持寻址
  • GPR电路的本质也是一个存储器
  • GPR需要作为目标寄存器被指令写入
  • 支持写入的存储器,RAM

RAM的读操作结构

RAM读操作

存储单元采用带使能端的D触发器

给定地址addr,读出存储器中相应的内容

结构类似于ROM

RAM的写操作结构

RAM写操作

待写入数据D

并非所有时刻都需要进行写入操作, 因此还需要一个写使能信号EN, 指示当前是否需要写入

写入RAM的工作原理

待写入数据D通过位线将每一位分别连接到每一个存储字中相应位的D

写使能EN通过与门对地址译码器输出的独热码进行过滤, 并与字线连接

将每一行的字线分别连接到对应存储器中相应的EN

被选中的存储字将更新为待写入数据D

RAM的完整结构

RAM完整结构

  • 单端口RAM”(single port RAM):在同一时刻只能通过一个地址访问其中的一个存储字
  • 多端口RAM”(multi-port RAM):在同一时刻可以通过多个地址访问其中的多个存储字

更新PC

对PC寄存器加1即可

通过计数器实现

实现完整的sCPU

添加add指令

取指

和li一样

译码

操作码译码
  • 检查指令的opcode来确定是什么指令
  • 识别输入的取值 -> n选1译码器
  • opcode只有2位, 使用一个2-4译码器
  • 输出的独热码可以指示当前指令属于何种指令, 这样的译码器称为指令译码器
  • 这组独热码通常作为控制信号, 用于控制部分电路如何工作
操作数译码

读出rs1rs2作为源操作数

还需要为GPR添加两个读端口

GPR模块应至少包含如下端口信号:

  • 第1个读端口: raddr1(读地址), rdata1(读数据)
  • 第2个读端口: raddr2, rdata2
  • 写端口: waddr(写地址), wdata(写数据), wen(写使能), clk(时钟)

执行

用加法器实现加法操作

采用多路选择器,根据指令的类别对写入GPR的数据进行选择

  • 如果当前指令是加法指令, 就选择加法器的结果
  • 否则选择li指令的立即数

更新PC

li

添加bner0指令

取指,操作码译码同之前

操作数译码

bner0还需要读出R[0], 因此可以把0作为GPR的raddr1端口的输入

通过多路选择器解决问题

执行

比较器实现比较

更新PC

  • 只有当前指令为bner0指令, 且比较结果不相等, 才将PC更新为addr字段
  • 其余情况应将PC更新为PC加1

需要将GPR的wen置为无效

重新审视CPU设计

添加指令的过程

  1. 分析指令的预期行为
  2. 根据指令的行为在数据流动的方向上依次添加所需的部件
  • 数据在CPU中流动的路径和路径上的相关部件, 称为CPU的数据通路(data path)
  • 有GPR, 加法器, 存储器, 比较器等
  1. 当多条指令的数据通路出现冲突时, 通常需要引入一些额外的电路, 来控制数据如何流动
  • 这些额外的电路属于CPU中的控制逻辑
  • 其中决定控制逻辑行为的信号称为控制信号

控制信号的设计

指令wdata的选择wenraddr1的选择更新PC的选择
add加法器的输出1指令的rs1PC + 1
li立即数1XPC + 1
bner0X00指令的addr

其中, X表示无关项(don’t care), 选择什么都可以

在CPU上执行程序 = 用程序编译出的指令序列控制CPU电路进行状态转移