type
status
date
slug
summary
tags
category
icon
password
断断续续把一生一芯推进到了实现单周期,吸取上次没有进行记录,导致停止一段时间工作后,再回来忘记了哪里有 Bug,被迫重新开始的教训,这次把实现的模块,接口都进行记录,以供之后参考。
架构设计
为了方便后续划分流水,按照经典的五级流水线对单周期处理器进行模块划分。分别是取指、译码、执行、访存、写回五个单元。除此之外还有一个寄存器组。总体架构及布线如图所示:
模块设计
本节介绍各个模块的功能和输入输出端口设计。
寄存器组
寄存器组由 32 个 64 位寄存器构成,提供两个异步读端口和一个同步写端口。可以在一个周期读取两个操作数并进行一次写回操作。表中的“寄存器 1/2 读地址”和“寄存器 1/2 读数据”是指操作数 1 和操作数 2 对寄存器的要求,而不是寄存器编号。
端口名 | 位宽 | input/output | 含义 |
clk | 1 | input | 工作时钟 |
rst | 1 | input | 复位信号 |
raddr1 | 5 | input | 寄存器 1 读地址 |
raddr2 | 5 | input | 寄存器 1 读地址 |
wen | 1 | input | 寄存器写使能 |
waddr | 5 | input | 寄存器写地址 |
wdata | 64 | input | 寄存器写数据 |
rdata1 | 64 | output | 寄存器 1 读数据 |
rdata2 | 64 | output | 寄存器 2 读数据 |
单击展开查看实现代码
取指
取指单元的主要工作是从存储中读取指令。取指单元由 PC(程序计数器) 和 IR(指令寄存器)组成
PC - 程序计数器
PC 是一个寄存器,负责指示程序的运行位置,输出当前指令存放的地址,复位值为 0x80000000。PC 值会被送入存储器。
端口名 | 位宽 | input/output | 含义 |
clk | 1 | input | 工作时钟 |
rst | 1 | input | 复位信号 |
pc_d | 64 | input | 上周期 PC 地址 |
jump_flag | 1 | input | 跳转控制信号 |
jump_addr | 64 | input | 跳转地址 |
pc | 64 | output | 当前 PC 地址 |
单击展开查看实现代码
IR - 指令寄存器
IR 是一个寄存器,接收存储器的输出,并从存储器中取出当前 PC 对应的指令输出,屏蔽掉多余的高字节的信息并输出指令。
端口名 | 位宽 | input/output | 含义 |
clk | 1 | input | 工作时钟 |
rst | 1 | input | 复位信号 |
pc | 64 | input | 当前 PC 地址 |
inst | 64 | output | 指令 |
单击展开查看实现代码
译码
译码单元接收前级取指模块的输出,进行译码,生成各个部件的控制信号和传向后级的源操作数。译码单元是一个组合逻辑单元。
端口名 | 位宽 | input/output | 含义 |
inst | 64 | input | 指令 |
pc | 64 | input | PC 地址 |
rdata1 | 64 | input | 寄存器 1 读数据 |
rdata2 | 64 | input | 寄存器 1 读数据 |
raddr1 | 5 | output | 寄存器 1 读地址 |
raddr2 | 5 | output | 寄存器 2 读地址 |
waddr | 5 | output | 寄存器写地址 |
wen | 1 | output | 寄存器写使能 |
jr_flag | 1 | output | JALR 指令信号 |
j_flag | 1 | output | J 型指令信号 |
br_flag | 1 | output | 分支指令信号 |
mem_flag | 1 | output | 访存信号 |
mem_mask | 2 | output | 访存掩码 |
mem_wdata | 64 | output | 访存写数据 |
imm_ex | 64 | output | 符号位拓展的立即数 |
src1 | 64 | output | 源操作数 1 |
src2 | 64 | output | 源操作数 2 |
alu_op | 14 | output | alu 执行的操作类型 |
bsrc1 | 64 | output | 分支指令源操作数 1 |
bsrc2 | 64 | output | 分支指令源操作数 2 |
branch_op | 14 | output | alu 执行的操作类型 |
- alu_op 是一个 19 位的独热码,为执行单元的 alu 指示操作类型,编码如下,其中 rv64 是标志指令是否为 rv64 指令: {rv64, add, sub, slt, sltu, and, or, xor, sll, srl, sra, mul, mulh, mulhsu, mulhu, div, divu, rem, remu}
- branch_op 与 alu_op 类似,是一个 6 位的独热码,为分支控制单元指示操作类型,编码如下: {beq, bge, bgeu, blt, bltu, bne}
- mem_mask 为访存写入时的掩码,宽度为 3 位,最高位指示访存类型。
- mem_mask[3]: 0, load; 1, store;
- mem_mask[2]: 0, 无符号拓展; 1, 符号拓展;
- mem_mask[1:0]: 00, 写入一字节; 01, 写入两字节; 10, 写入四字节; 11, 写入八字节。
单击展开查看实现代码
执行
执行单元有一个 alu(算术逻辑单元)和一个分支控制单元构成,主要负责接收源操作数并进行操作类型判断,得出结果后将执行结果送入后级,是组合逻辑单元
alu
alu 主要负责处理逻辑和算术运算。
端口名 | 位宽 | input/output | 含义 |
alu_op | 14 | input | alu 执行的操作类型 |
src1 | 64 | input | 源操作数 1 |
src2 | 64 | input | 源操作数 2 |
alu_result | 64 | output | alu 计算结果 |
单击展开查看实现代码
分支控制
分支控制单元为一个 32 位的比较器,给出分支控制信号
端口名 | 位宽 | input/output | 含义 |
branch_op | 14 | input | 分支操作类型 |
src1 | 64 | input | 源操作数 1 |
src2 | 64 | input | 源操作数 2 |
branch_flag | 1 | output | 比较器计算结果 |
单击展开查看实现代码
访存
访存单元也设计成一个寄存器,接收 alu 的输出和译码产生的 mem_flag 信号,执行 load/store 指令
端口名 | 位宽 | input/output | 含义 |
mem_flag | 4 | input | alu 执行的操作类型 |
mem_addr | 64 | input | 访存地址 |
mem_wdata | 64 | input | store 指令数据 |
mem_rdata | 64 | output | load 指令数据 |
单击展开查看实现代码
- 作者:Light-ly
- 链接:notion.light-liuyi.top/article/ysyx-npc-riscv-cpu-design
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。