Lab4 V2.0
版本控制
版本 | 描述 |
---|---|
V0 | Lab3 |
V1.0 | Lab3 相对V0变化: 修改了文件名,各阶段以_stage结尾(因为if是关键词,所以module名不能叫if,遂改为if_stage,为了统一命名,将所有module后缀加上_stage) 删除了imm_sign信号(默认对立即数进行有符号数扩展) 由于对sw指令进行了重新理解:无论如何都是需要将rt_data传递给EXE阶段,故将部分译码逻辑进行后移至EXE阶段,避免id_to_exe_data总线过于庞大 将ins_shmat剔除出id_to_exe_data,因为imm包括ins_shamt 对信号进行重命名(例如在ID阶段有个信号叫rf_we,最终要传递给WB阶段,那么在EXE阶段,该信号叫作exe_rf_we,同理mem_rf_we,wb_rf_we),不然都叫rf_we,Debug的时候太痛苦了。 |
V2.0 | Lab4 相对V1.0的变化 引入`ifdef-`else-`endif来实现相对V1.0的代码增量 增加了旁路控制,减少流水线阻塞(因为增加了旁路,所以修改了ID、EXE、MEM的接口) 修改了ready_go命令,用于控制流水线的阻塞 |
Top顶层
接口信号
MYCPU_TOP.v(TOP)
名称 | 宽度 | 方向 | 描述 |
---|---|---|---|
时钟与复位 | |||
clk | 1 | I | 时钟信号,来自clk_pll的输出时钟 |
resetn | 1 | I | 复位信号,低电平同步复位 |
取指端访存接口 | |||
inst_sram_en | 1 | O | 指令RAM使能信号,高电平有效 |
inst_sram_wen | 4 | O | 指令RAM字节写使能信号,高电平有效 |
inst_sram_addr | 32 | O | 指令RMA读写地址,字节寻址 |
inst_sram_wdata | 32 | O | 指令RAM写数据 |
inst_sram_rdata | 32 | I | 指令RAM读数据 |
数据端访存接口 | |||
data_sram_en | 1 | O | 数据RAM使能信号,高电平有效 |
data_sram_wen | 4 | O | 数据RAM字节写使能信号,高电平有效 |
data_sram_addr | 32 | O | 数据RAM读写地址,字节寻址 |
data_sram_wdata | 32 | O | 数据RAM写数据 |
data_sram_rdata | 32 | I | 数据RAM读数据 |
debug信号,供验证平台使用 | |||
debug_wb_pc | 32 | O | 写回级(多周期最后一级)的PC,需要myCPU里将PC一路传递到写回级 |
debug_wb_rf_wen | 4 | O | 写回级写寄存器堆(regfiles)的写使能,为字节使能,如果myCPU写regfiles为单字节写使能,则将写使能扩展成4位即可 |
debug_wb_rf_wnum | 5 | O | 写回级写regfiles的目的寄存器号 |
debug_wb_rf_wdata | 32 | O | 写回级写regfiles的写数据 |
代码结构
MYCPU_TOP.v
|____IF.v
|____ID.v
|____RF.v(2个读端口,1个写端口)
|____EXE.v
|____ALU.v
|____MEM.v
|____WB.v
|____MYCPU.h
DATA_RAM.v
IF.v(修改为IF_STAGE,因为会与关键词if冲突)
接口信号
名称 | 宽度 | 方向 | 描述 |
---|---|---|---|
时钟与复位 | |||
clk | 1 | I | 时钟信号,来自clk_pll的输出时钟 |
resetn | 1 | I | 复位信号,低电平同步复位 |
与TOP | |||
inst_sram_en | 1 | O | RAM使能信号,高电平有效 |
inst_sram_wen | 4 | O | RAM字节写使能信号,高电平有效 |
inst_sram_addr | 32 | O | RMA读写地址,字节寻址 |
inst_sram_wdata | 32 | O | RAM写数据 |
inst_sram_rdata | 32 | I | RAM读数据 |
与ID | |||
id_to_if_allowin | 1 | I | pipe allowin |
if_to_id_vld | 1 | O | pipe valid |
if_to_id_data | 64 | O | pipe data(instruction 32-bits, pc 32-bits) |
jump_bus | 33 | I | branch instructions(enable 1bit,address 32-bits) |
接口时序
ID.v
接口信号
名称 | 宽度 | 方向 | 描述 |
---|---|---|---|
时钟与复位 | |||
clk | 1 | I | 时钟信号,来自clk_pll的输出时钟 |
resetn | 1 | I | 复位信号,低电平同步复位 |
与IF | |||
id_to_if_allowin | 1 | O | pipe allowin |
if_to_id_vld | 1 | I | pipe valid |
if_to_id_data | 64 | I | pipe data(instruction 32-bits, pc 32-bits) |
jump_bus | 33 | O | branch instructions(enable 1bit,address 32-bits) |
与EXE | |||
exe_to_id_allowin | 1 | I | pipe allowin |
id_to_exe_vld | 1 | O | pipe valid |
id_to_exe_data | 135 | O | {ins_R:1, ins_I:1, imm:16, alu_op:13, mem_rd:1, mem_we:1, rf_we:1, rf_dst_addr:5, data_1:32, data_2:32, pc:32} |
exe_bypass_bus | 38 | I | {exe_rf_we:1, exe_rf_dst_addr:5, exe_rf_data:32} |
与MEM | |||
mem_bypass_bus | 38 | I | {mem_rf_we:1, mem_rf_dst_addr:5, mem_rf_data:32} |
与WB | |||
wb_to_rf_bus | 38 | I | {rf_we:1, rf_addr:5, rf_data:32} |
接口信号(RF.v)
名称 | 宽度 | 方向 | 描述 |
---|---|---|---|
时钟与复位 | |||
clk | 1 | I | 时钟信号,来自clk_pll的输出时钟 |
与ID内部信号 | |||
rf_r_addr1 | 5 | I | RF读地址1 |
rf_r_data1 | 32 | O | RF读数据1 |
rf_r_addr2 | 5 | I | RF读地址2 |
rf_r_data2 | 32 | O | RF读数据2 |
rf_wen1 | 1 | I | RF写使能1 |
rf_w_addr1 | 5 | I | RF写地址1 |
rf_w_data1 | 32 | O | RF写数据1 |
接口时序
电路设计
图3-4-1 译码电路分组(注:黄线少画了两条)
根据附录——MIPS指令。由于跳转指令不传递给EXE阶段,直接传递给IF阶段,且为纯组合逻辑输出,有可能成为关键路径,故对跳转指令单独处理。除了跳转指令外,涉及加法(减法归为加法)的指令如图3-4-1所示,即ins_addu、ins_addiu、ins_subu、ins_lw、ins_sw。
对于图3-4-1的拼接运算,可以当作移位运算执行。
EXE.v
接口信号
名称 | 宽度 | 方向 | 描述 |
---|---|---|---|
时钟与复位 | |||
clk | 1 | I | 时钟信号,来自clk_pll的输出时钟 |
resetn | 1 | I | 复位信号,低电平同步复位 |
与TOP(外接的DATA_RAM) | |||
data_sram_en | 1 | O | 数据RAM使能信号,高电平有效 |
data_sram_wen | 4 | O | 数据RAM字节写使能信号,高电平有效(4个比特,应该代表32 = 4 bytes) |
data_sram_addr | 32 | O | 数据RAM读写地址,字节寻址 |
data_sram_wdata | 32 | O | 数据RAM写数据 |
与ID | |||
exe_to_id_allowin | 1 | O | pipe allowin |
id_to_exe_vld | 1 | I | pipe valid |
id_to_exe_data | 135 | I | {ins_R:1, ins_I:1, imm:16, alu_op:13, mem_rd:1, mem_we:1, rf_we:1, rf_dst_addr:5, data_1:32, data_2:32, pc:32} |
exe_bypass_bus | 38 | O | {exe_rf_we:1, exe_rf_dst_addr:5, exe_rf_data:32} |
与MEM | |||
mem_to_id_allowin | 1 | I | pipe allowin |
exe_to_mem_vld | 1 | O | pipe valid |
exe_to_mem_data | 71 | O | {mem_rd:1, rf_we:1, rf_dst_addr:5, pc:32(其实可以删掉pc,这里是debug显示用的,可以叫debug_pc), exe_result:32 |
接口信号(ALU.v)
暂时不需要时钟和复位,纯组合逻辑
名称 | 宽度 | 方向 | 描述 |
---|---|---|---|
时钟与复位 | |||
clk | 1 | I | 时钟信号,来自clk_pll的输出时钟 |
resetn | 1 | I | 复位信号,低电平同步复位 |
与ID内部信号 | |||
alu_shamt | 6 | I | ALU移位(R-指令的shamt部分) |
alu_op | 13 | I | ALU操作(加、减、乘除、位运算) |
alu_din1 | 32 | I | ALU输入1 |
alu_din2 | 32 | I | ALU输入2 |
alu_out | 32 | O | ALU输出 |
接口时序
MEM.v
接口信号
名称 | 宽度 | 方向 | 描述 |
---|---|---|---|
时钟与复位 | |||
clk | 1 | I | 时钟信号,来自clk_pll的输出时钟 |
resetn | 1 | I | 复位信号,低电平同步复位 |
与TOP(外接的DATA_RAM) | |||
data_sram_rdata | 32 | I | 数据RAM读数据 |
与EXE | |||
mem_to_exe_allowin | 1 | O | pipe allowin |
exe_to_mem_vld | 1 | I | pipe valid |
exe_to_mem_data | 71 | I | {mem_rd:1, rf_we:1, rf_dst_addr:5, pc:32(其实可以删掉pc,这里是debug显示用的,可以叫debug_pc), exe_result:32} |
与WB | |||
wb_to_mem_allowin | 1 | I | pipe allowin |
mem_to_wb_vld | 1 | O | pipe valid |
mem_to_wb_data | 70 | O | { rf_we:1, rf_dst_addr:5, mem_result:32, pc:32(其实可以删掉pc,这里是debug显示用的,可以叫debug_pc)} |
与ID | |||
mem_bypass_bus | 38 | O | {mem_rf_we:1, mem_rf_dst_addr:5, mem_rf_data:32} |
WB.v
接口信号
名称 | 宽度 | 方向 | 描述 |
---|---|---|---|
时钟与复位 | |||
clk | 1 | I | 时钟信号,来自clk_pll的输出时钟 |
resetn | 1 | I | 复位信号,低电平同步复位 |
与TOP | |||
debug_wb_pc | 32 | O | 写回级(多周期最后一级)的PC,需要myCPU里将PC一路传递到写回级(与原书保持一致) |
debug_wb_rf_wen | 4 | O | 写回级写寄存器堆(regfiles)的写使能,为字节使能,如果myCPU写regfiles为单字节写使能,则将写使能扩展成4位即可(与原书保持一致) |
debug_wb_rf_wnum | 5 | O | 写回级写regfiles的目的寄存器号(与原书保持一致) |
debug_wb_rf_wdata | 32 | O | 写回级写regfiles的写数据(与原书保持一致) |
与MEM | |||
wb_to_mem_allowin | 1 | O | pipe allowin |
mem_to_wb_vld | 1 | I | pipe valid |
mem_to_wb_data | 70 | I | { rf_we:1, rf_dst_addr:5, mem_result:32, pc:32(其实可以删掉pc,这里是debug显示用的,可以叫debug_pc)} |
与ID | |||
wb_to_rf_bus | 38 | O | {rf_we:1, rf_addr:5, rf_data:32} |
Lab4完结
原Lab3的指令是插入的NOP的花了1728025ns
Lab4花了1147805ns
Lab4 后记
- 对指令按冒险进行划分
以下只考虑第4条指令:
- 在不考虑分支指令的情况下,只需要分析第4条指令的ID阶段。如果前3条指令有任一条指令需要写寄存器,且写地址与第4条指令的读地址相同,则需要旁路。因此至少需要3级旁路
- 如果检测到第3条指令是lw指令,且存在写后读的问题,则需要阻塞流水线一个周期(只阻塞IF-ID)之后再执行第4条指令。
- 如果检测到第3条指令是分支跳转指令,没有影响,不做处理,因为Lab3设计的分支跳转是纯组合逻辑,没有影响。但是这里可能会出现个关键路径!EXE—旁路ID到IF
综上,Lab4相对Lab3只需要做以下修改:
- 增加3级旁路:EXE到ID、MEM到ID、WB到ID
- 增加1级判断:当上一条指令是load指令,且与之存在写后读冒险时,需要阻塞一周期再执行本条指令。
具体细节处理需要注意:
- 阻塞的时候,流水插入NOP指令(不需要,因为由ready_go控制)
- 注意0号寄存器永远是0,所以不需要对其进行判断
Lab4——Debug
复位位宽
意外发现了一个Lab3留下来的bug
ID、EXE、MEM、WB之间传递的总线复位均少1个比特:
以ID阶段为例:
修改为:
rt敲错了
现在已将rs更改为rt:
流水线阻塞
-
阻塞的时候我只将ready_go置0了,忘记将id_to_id_allow_in也置0了
-
旁路电路设计有问题,如果EXE旁路和MEM旁路写的都是同一个寄存器,这个时候旁路的选择应当选择EXE旁路。也就是说旁路是有优先级的,而我设计的电路是没有优先级的,故有bug
-
最后执行下面这两条命令的时候,报错:xor指令的写回数据报错:
反复看了我设计的电路,没有找到问题后,中午睡了个觉,想起来清零的问题,因为我是通过各级的rf_we来判断各级是否写寄存器的,但是流水线在ID阶段阻塞后,EXE的数据没有刷新,导致EXE阶段的rf_we还是1’b1。因此,应当在EXE阶段进行清零操作,即强行将NOP指令插入EXE阶段:
由于所设计的流水线阻塞只会在ID阶段阻塞,因此只在EXE阶段清零即可,其他阶段不需要清零。
-
-
综合问题
两台不同的电脑,vivado都是2019.2,两台电脑之前都安装卸载过其他版本的vivado,在A电脑上综合报错(simulation正常),如下图:
在B电脑上却没有报错,而且还正常进行了implementation。本人在A电脑上重新安装了再次vivado2019.2,还是报错。(目前该问题尚未解决)
现在是在B保存的vivado工程是在B下跑的综合。