计组P6设计文档
P6建议:在P5基础上添加乘除槽即可
设计要求
处理器应为五级流水线设计,支持如下指令集:
1 |
|
直接通过顶层模块 mips.v 的 output 端口传出相应信号,不允许出现
display
语句,具体要求见“在线测试相关说明”小节。要求存储器外置,即将 IM 和 DM 放置在 CPU 之外。P6 的 IM 和 DM 两个模块被内置于官方评测的 testbench 中(官方使用的 tb 已在“存储器外置”小节公开),不再需要大家自行实现 IM 和 DM 模块。官方 tb 中实现的指令存储器(IM,instruction memory)和数据存储器(DM,data memory)的容量如下:
IM:容量为 16KB (32bit/word × 4096word)
DM:容量为 12KB (32bit/word × 3072word)
需有单独的乘除法模块和数据扩展模块,我们会分别在“乘除模块”和“支持按字节访存”两个小节中中予以详细说明。
最外层的 mips 模块的文件名必须为 mips.v ,该文件中的 module 也必须命名为 mips 。
架构
模块规格
命名规则
流水线信号共5级,IF/ID ID/EX EX/MEM MEM/WB,两个寄存器间的信号用
级别_数据/功能(_去向)
命名。
共有信号:
变量 | 方向 | 位宽 | 解释 | 来源/去向 |
---|---|---|---|---|
clk | in | 1 | 时钟信号 | 顶层输入 |
rst | in | 1 | 复位信号 | 顶层输入 |
xx_pc | in | [31:0] | 当前级的pc | 上一级(IF为out) |
xx_pc_yy | out | [31:0] | 要传给下一级的pc | 下一级yy(WB无) |
xx_instr | in | [31:0] | 当前级的指令 | 上一级 |
xx_instr_yy | out | [31:0] | 要传给下一级的指令 | 下一级yy(WB无) |
IF
变量 | 方向 | 位宽 | 解释 | 来源/去向 |
---|---|---|---|---|
stall | in | 1 | 阻塞信号,使pc保持不变 | staller |
IF_j | in | 1 | 是否跳转 | ID_j_IF |
IF_pc4 | in | [31:0] | 跳转指令地址 | ID_pc_IF |
IF_instr | in | [31:0] | 从tb获取的指令 | tb |
IF_pc | out | [31:0] | IF级当前指令 | tb |
ID
接口
变量 | 方向 | 位宽 | 解释 | 来源/去向 |
---|---|---|---|---|
stall | in | 1 | 阻塞信号,清空D级 | staller |
WB_pc_ID | in | [31:0] | 写入指令的pc | WB_pc_ID |
ID_we | in | 1 | 寄存器写入信号 | WB_we_ID |
ID_addr | in | [4:0] | 要写入的寄存器 | WB_addr_ID |
ID_data | in | [31:0] | 要写入的值 | WB_data_ID |
ID_rs_sign | in | [2:0] | 转发选择,0为原值 | |
ID_rt_sign | in | [2:0] | 转发选择,0为原值 | |
ID_rs_data | in | [31:0] | 转发值 | EX_out,EX_data_WB,MEM_data_WB |
ID_rt_data | in | [31:0] | 转发值 | |
ID_rs_base | out | [31:0] | rs寄存器的值 | EX_rs_base |
ID_rt | out | [31:0] | rt寄存器的值 | EX_rt |
ID_j_IF | out | 1 | 跳转指示信号 | IF_j |
ID_pc_IF | out | [31:0] | 跳转指令地址 | IF_pc4 |
内部变量
1 |
|
EX
接口
变量 | 方向 | 位宽 | 解释 | 来源/去向 |
---|---|---|---|---|
EX_rs_base | in | [31:0] | rs寄存器的值 | ID_rs_base |
EX_rt | in | [31:0] | 参与运算的rt寄存器的值,可能从转发处来 | |
EX_new | out | 1 | 表示E级产生了新的要写入寄存器的值 | instr |
EX_addr | out | [4:0] | E级产生新值的寄存器地址 | instr |
EX_out | out | [31:0] | 运算结果 | MEM_addr |
EX_rt_MEM | out | [31:0] | rt寄存器的值 | MEM_data |
busy | out | 1 | mul是否正忙 | mul |
内部变量
1 |
|
内部模块——mul
变量 | 方向 | 位宽 | 解释 | 来源/去向 |
---|---|---|---|---|
clk | in | 1 | 时钟信号 | EX |
rst | in | 1 | 复位信号 | EX |
start | in | [2:0] | 开始运算信号 | EX |
A | in | [31:0] | 运算数rs | EX_rs_base |
B | in | [31:0] | 运算数rt | EX_rt_use |
busy | out | 1 | 正忙信号 | busy |
hi | out | [31:0] | 乘法高位/除法余数 | out |
lo | out | [31:0] | 乘法低位/除法的商 | out |
MEM
接口
变量 | 方向 | 位宽 | 解释 | 来源/去向 |
---|---|---|---|---|
MEM_addr | in | [31:0] | 要读出的地址 | EX_out |
MEM_data | in | [31:0] | 读出的数据 | m_data_rdata |
MEM_new | out | 1 | 表示M级有了新的要写入寄存器的值 | instr |
MEM_new_addr | out | [4:0] | M级新值的寄存器地址 | instr |
MEM_data_WB | out | [31:0] | 传向下一级的数据 | WB_lw_data |
EX_data_WB | out | [31:0] | 运算结果 | WB_alu_data |
内部变量
1 |
|
内部模块
变量 | 方向 | 位宽 | 解释 | 来源/去向 |
---|---|---|---|---|
A | in | A | 读出地址的低位 | MEM_addr |
Din | in | Din | 原始数据 | MEM_data |
Op | in | Op | 扩展选择信号 | op |
Dout | out | Dout | 扩展后的数据 | MEM_data_WB |
WB
输出均不是reg型
变量 | 方向 | 位宽 | 解释 | 来源/去向 |
---|---|---|---|---|
WB_lw_data | in | [31:0] | 从内存取出的数 | MEM_data_WB |
WB_alu_data | in | [31:0] | 运算结果 | EX_data_WB |
WB_we_ID | out | 1 | 寄存器写入信号 | ID_we |
WB_addr_ID | out | [4:0] | 要写入的寄存器地址 | ID_addr |
WB_data_ID | out | [31:0] | 要写入寄存器的值 | ID_data |
冒险处理
转发
转发共5个接受:
1 |
|
共2处提供:
1 |
|
其中,2处提供接口还给出new信号与addr,代表新值产生且可用、要写入的寄存器编号
接受者只需判断new与addr即可决定转发与否(有新值且可用就转发)
阻塞
\(T_{use}\) 与 \(T_{new}\) 的计算见表格
采用Time模块传递计算结果,[31:0] use_new
具体每位对应结果如下表
[2][1][0]分别对应rs、rt、rd
rs | rt | rd | [4] | [2] | [1] | [0] |
---|---|---|---|---|---|---|
-> | use或new的数值 | <- | 1代表正在算乘除法 | -> | 0为use,1为new | <- |
若 \(T_{use}<T_{new}\) ,或乘除类指令遇上busy,则执行阻塞操作:
冻结IF/ID
清除ID/EX
禁止PC
注意事项
从W级转发到D级,采用D级内部转发(如beq)
转发时如果地址是0寄存器应剔除
添加指令
添加到const里
填写TIME表格,根据use和new更改M级中的MEM_new与MEM_new_addr,添加Time中的use与new值
根据操作在每个模块添加行为
IF:指令获取与延迟槽
ID:给出跳转指令
EX:各种计算行为、存储地址计算
MEM:给出写入内存的地址、byte信号,对读出的数据进行加工
WB:给出写寄存器信号、寄存器编号、数据
每类指令对应行为(P5)
cal:修改EX计算过程、添加WB回写信号,加入相关阻塞指令中(带cal的),加入相关转发指令中(同上,注意供需接口都有)。
lw:修改EX计算过程、添加WB回写信号,……
sw:修改EX计算过程、MEM写入,………
j:修改ID跳转判断与跳转地址计算,加入阻塞、转发指令
jal:修改ID跳转判断与跳转地址计算、EX算pc等、WB回写信号,阻塞转发
是否有新的转发通路、阻塞可能
思考题与测试
为什么需要有单独的乘除法部件而不是整合进 ALU?为何需要有独立的 HI、LO 寄存器?
乘除法相较其他运算,复杂性更高且时间更长(延迟更大),与ALU分离有利于保持模块的清晰关系、缩短时钟周期。便于在本级存储运算结果而不是回写到寄存器,否则其他使用寄存器的指令均需要被阻塞,性能下降严重。
真实的流水线 CPU 是如何使用实现乘除法的?请查阅相关资料进行简单说明。
类似于竖式乘法,乘数最低位*被乘数并累加右移直至每位乘数都算完,32位需5次加法-100周期的时间。
类似于算式除法,每次尝试减去能减去的最大值(含比较过程),重复计算得商、余数
请结合自己的实现分析,你是如何处理 Busy 信号带来的周期阻塞的?
当D级是乘除类指令且E级是乘除类指令或busy时,阻塞
请问采用字节使能信号的方式处理写指令有什么好处?(提示:从清晰性、统一性等角度考虑)
可读性好,当前执行的指令一目了然。字、半字、字节的处理具有统一性。
请思考,我们在按字节读和按字节写时,实际从 DM 获得的数据和向 DM 写入的数据是否是一字节?在什么情况下我们按字节读和按字节写的效率会高于按字读和按字写呢?
不是。写入低位时,按字读和按字写效率近似,写入低位效率高于写入高位
为了对抗复杂性你采取了哪些抽象和规范手段?这些手段在译码和处理数据冲突的时候有什么样的特点与帮助?
const中定义指令编码,减少代码量、提高易读性
time计算指令单独成一模块,采用列举法,便于新增指令
在本实验中你遇到了哪些不同指令类型组合产生的冲突?你又是如何解决的?相应的测试样例是什么样的?
见转发
如果你是手动构造的样例,请说明构造策略,说明你的测试程序如何保证覆盖了所有需要测试的情况;如果你是完全随机生成的测试样例,请思考完全随机的测试程序有何不足之处;如果你在生成测试样例时采用了特殊的策略,比如构造连续数据冒险序列,请你描述一下你使用的策略如何结合了随机性达到强测的效果。
测试部分
1 |
|
part1——P4的数据测试
1 |
|
part2——冒险测试
\(A_9^4\)?
1 |
|
part3——新增指令测试
重点测试边缘数据
1 |
|