单周期ORI指令的实现
写在最前面,后续的一些指令我还在慢慢学,在这个框架上继续增加,有兴趣可以私信我,代码可以互相分享学习
ORI指令说明
这是一个I型指令,ORI指令的指令码为6’b00_1101
ORI指令作用:将16位立即数immediate进行无符号扩展至32位,然后与索引为rs的通用寄存器里的值进行“或”运算,运算结果保存到索引为rt的通用寄存器中
扩展1:立即数扩展
n位立即数扩展到3位。符号扩展是将n位立即数的最高为复制到扩展后的32位数据的高(32-n)位,无符号扩展是将扩展后的32位数据的高(32-n)位都置0
16位立即数: 0x8000、0x8000
符号扩展: 0xFFFF8000、0x00001000
无符号扩展: 0x00008000、0x00001000
扩展2:寄存器
MIPS架构定义了32个通用寄存器,使用$0、$1、…、%31表示,索引5位。其中%0一般用作常量0。
寄存器名字 | 约定命名 | 用途 |
---|---|---|
$0 | zero | 总是为0 |
$1 | at | 留作汇编器生成一些合法指令 |
$2、$3 | v0、v1 | 用来存放程序返回值 |
$4-$7 | a0-a3 | 调用子程序时,使用这4个寄存器传输前4个非浮点参数 |
$8-$15 | t0-t7 | 临时寄存器 、子程序使用时可以不用存储和恢复 |
$16-$23 | s0-s7 | 子程序寄存器变量,改变这些寄存器值的子程序必须存储旧的值并在退出前恢复,对调用程序来说值不变 |
$24、$25 | t7、t9 | 临时寄存器,子程序使用时可以不用存储和恢复 |
$26、$27 | k 0 、 k0、 k0、k1 | 由异常处理程序使用 |
28 或 28或 28或gp | gp | 全局指针 |
29 或 29或 29或sp | sp | 堆栈指针 |
30 或 30或 30或fp | fp | 子程序可以用来作堆栈帧指针 |
$31 | ra | 存放程序返回地址 |
单周期系统结构框图
模块划分以及功能代码实现
1. 宏定义define
// ***************** 全局的宏定义 ***************
`define RstEnable 1'b1 // 复位信号有效
`define RstDisable 1'b0 // 复位信号无效
`define ZeroWord 32'h00000000 // 32位的数值0
`define WriteEnable 1'b1 // 使能写
`define WriteDisable 1'b0 // 禁止写
`define ReadEnable 1'b1 // 使能读
`define ReadDisable 1'b0 // 禁止读
`define AluOpBus 7:0 // 译码阶段的输出aluop_o的宽度 子类型
`define AluSelBus 2:0 // 译码阶段的输出alusel_o的宽度 类型
`define InstValid 1'b0 // 指令有效
`define InstInvalid 1'b1 // 指令无效
`define True_v 1'b1 // 逻辑真
`define False_v 1'b0 // 逻辑假
`define ChipEnable 1'b1 // 芯片使能
`define ChipDisable 1'b0 // 芯片禁止
// *************** 与具体指令有关的宏定义 ***************
`define EXE_ORI 6'b001101 // 指令ori的指令码
`define EXE_NOP 6'b000000 //
//AluOp 子类型
`define EXE_OR_OP 8'b00100101 // or_func
`define EXE_NOP_OP 8'b00000000
//AluSel 类型
`define EXE_RES_LOGIC 3'b001 // 逻辑运算
`define EXE_RES_NOP 3'b000
// *************** 与指令存储器ROM有关的宏定义 ***************
`define InstAddrBus 31:0 // ROM的地址总线宽度
`define InstBus 31:0 // ROM数的据总线宽度
`define InstMemNum 131071
`define InstMemNumLog2 17
// ************** 与通用寄存器Regfile有关的宏定义 ***************
`define RegAddrBus 4:0 // Regfile模块的地址线宽度
`define RegBus 31:0 // Regfile模块的数据线宽度
`define RegWidth 32
`define DoubleRegWidth 64
`define DoubleRegBus 63:0
`define RegNum 32 // 通用寄存器的数量
`define RegNumLog2 5 // 寻址通用寄存器使用的地址位数
`define NOPRegAddr 5'b00000
2. 取指pc
// 取出指令存储器中的指令、PC增值,准备取下一条指令
`include "defines.v"
module pc_reg (
output reg [`InstAddrBus] pc, // 要读取的指令地址
output reg ce, // 指令存储器使能信号
input wire clk, // 时钟信号
input wire rst // 复位信号
);
always @ (posedge clk) begin
if (rst == `RstEnable) begin
ce <= `ChipDisable; // 复位的时候指令存储器禁用
end else begin
ce <= `ChipEnable; // 复位结束后指令存储器使能
end
end
always @ (posedge clk) begin
if (ce == `ChipDisable) begin
pc <= 32'h00000000; // 指令存储器禁用的时候,pc为0
end else begin
pc <= pc + 4'h4; // 指令存储器使能的时候,pc值每时钟周期+4字节
end
end
endmodule
3. 译码id
// 对指令进行译码,得到最终运算的类型、子类型、源操作数1、源操作数2、要写入的目的寄存器地址信息等
`include "defines.v"
module id (
// 输出到regfile的信息
output reg reg1_read_o, // regfile模块第一个读寄存器端口的读使能信号
output reg reg2_read_o, // regfile模块第二个读寄存器端口的读使能信号
output reg [`RegAddrBus] reg1_addr_o, // regfile模块第一个读寄存器端口的读地址信号
output reg [`RegAddrBus] reg2_addr_o, // regfile模块第二个读寄存器端口的读地址信号
// 送到执行阶段的信息
output reg [`AluOpBus] aluop_o, // 译码阶段的指令要进行的运算的子类型
output reg [`AluSelBus] alusel_o, // 译码阶段的指令要进行的运算的类型
output reg [`RegBus] reg1_o, // 译码阶段的指令要进行的运算的源操作数1
output reg [`RegBus] reg2_o, // 译码阶段的指令要进行的运算的源操作数2
output reg [`RegAddrBus] wd_o, // 译码阶段的指令要写入的目的寄存器地址
output reg wreg_o, // 译码阶段的指令是否有要写入的目的寄存器
input wire rst, // 复位
input wire [`InstAddrBus] pc_i, // 译码阶段指令对应的地址
input wire [`InstBus] inst_i, // 译码阶段的指令
// 读取的regfile的值
input wire [`RegBus] reg1_data_i, // 从regfile输入的第一个读寄存器端口的输出
input wire [`RegBus] reg2_data_i // 从regfile输入的第一个读寄存器端口的输出
);
// 取得指令的指令码,功能码
// 对于ori指令只需通过判断26-31bits的值,即可判断是否是ori指令,6'b00_1101
wire [5:0] op = inst_i[31:26]; // 高6位决定具体操作
wire [4:0] op2 = inst_i[10:6];
wire [5:0] op3 = inst_i[5:0];
wire [4:0] op4 = inst_i[20:16];
// 保存指令执行需要的立即数
reg [`RegBus] imm
// 指令指示是否有效
reg instvalid;
// ****************************************************************************
// **************************第一阶段:对指令进行译码******************************
// ****************************************************************************
always @ (*) begin
if (rst == `RstEnable) begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= `NOPRegAddr; // 写入地址赋值为无效
wreg_o <= `WriteDisable; // 没有要写入的目的寄存器
instvalid <= `InstValid; // 指令有效
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= `NOPRegAddr;
reg2_addr_o <= `NOPRegAddr;
imm <= 32'h0;
end else begin
aluop_o <= `EXE_NOP_OP;
alusel_o <= `EXE_RES_NOP;
wd_o <= inst_i[15:11]; // rd,目的寄存器编号
wreg_o <= `WriteDisable;
instvalid <= `InstInvalid; // 指令无效
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= inst_i[25:21]; // 默认通过regfile读端口1读取的寄存器地址,rs,源寄存器地址
reg2_addr_o <= inst_i[20:16]; // 默认通过regfile读端口2读取的寄存器地址,rt,源寄存器地址
imm <= `ZeroWord;
case (op)
`EXE_ORI: begin // 依据op的值判断指令是否为ori指令
// ori指令需要将结果写入目的寄存器
wreg_o <= `WriteEnable;
// 运算子类型是逻辑'or'运算
aluop_o <= `EXE_OR_OP;
// 运算类型是逻辑运算
alusel_o <= `EXE_RES_LOGIC;
// 需要通过regfile的读端口1读取寄存器
reg1_read_o <= `WriteEnable;
// 不需要通过regfile的读端口2读取寄存器
reg2_read_o <= `WriteDisable;
// 指令执行需要的立即数,无符号扩展
imm <= {16'h0, inst_i[15:0]};
// 指令执行要写的目的寄存器地址
wd_o <= inst_i[20:16];
// ori指令是有效指令
instvalid <= `InstValid;
end
default: begin
end
endcase // case
end //if
end //always
// ****************************************************************************
// ********************第二阶段:确定进行运算的操作数1 ******************************
// ****************************************************************************
always @ (*) begin
if (rst == `RstEnable) begin
reg1_o <= `ZeroWord;
end else if (reg1_read_o == 1'b1) begin
reg1_o <= reg1_data_i; // regfile读端口1的输出值
end else if (reg1_read_o == 1'b0) begin
reg1_o <= imm; // 立即数
end else begin
reg1_o <= `ZeroWord;
end
end
// ****************************************************************************
// ********************第三阶段:确定进行运算的操作数2 ******************************
// ****************************************************************************
always @ (*) begin
if (rst == `RstEnable) begin
reg2_o <= `ZeroWord;
end else if (reg2_read_o == 1'b1) begin
reg2_o <= reg2_data_i; // regfile读端口2的输出值
end else if (reg2_read_o == 1'b0) begin
reg2_o <= imm; // 立即数
end else begin
reg2_o <= `ZeroWord;
end
end
endmodule
4. 通用寄存器RegFile
// 寄存器堆栈,实现了32个32位通用整数寄存器,可以同时进行两个寄存器的读操作和一个寄存器的写操作
`include "defines.v"
module regfile (
// 写端口
input wire we, // 写使能信号
input wire [`RegAddrBus] waddr, // 要写入的寄存器地址
input reg [`RegBus] wdata, // 要写入的数据
// 读端口1
input wire re1, // 第一个读寄存器端口读使能信号
input wire [`RegAddrBus] raddr1, // 第一个读寄存器端口要读取的寄存器的地址
output reg [`RegBus] rdata1, // 第一个读寄存器端口输出的寄存器值
// 读端口2
input wire re2, // 第二个读寄存器端口读使能信号
input wire [`RegAddrBus] raddr2, // 第二个读寄存器端口要读取的寄存器的地址
output reg [`RegBus] rdata2, // 第二个读寄存器端口输出的寄存器值
input wire clk,
input wire rst
);
// **********************第一阶段:定义32个32位寄存器************************
reg [`RegBus] regs[0:`RegNum-1];
// **************************第二阶段:写操作******************************
always @ (posedge clk) begin
if (rst == `RstDisable) begin // 复位信号无效
if ((we ==`WriteEnable) && (waddr != `RegNumLog2'h0)) begin // 写使能&&写目的寄存器≠$0
regs[waddr] <= wdata;
end
end
end
// *************************第三阶段:读端口1的读操作******************************
always @ (*) begin
if (rst == `RstEnable) begin
rdata1 <= `ZeroWord; // 复位信号有效,输出0
end else if (raddr1 == `RegNumLog2'h0) begin // 读目的寄存器为$0,输出0
rdata1 <= `ZeroWord;
end else if (re1 == `ReadEnable) begin // 输出要读取的目的寄存器地址对应的值
rdata1 <= regs[raddr1];
end else if ((raddr1 == waddr) && (we == `WriteEnable) && (re1 == `ReadEnable)) begin // 读目的寄存器是写目的寄存器&&写使能&&读使能,输出写入的值
rdata1 <= wdata;
end else begin
rdata1 <= `ZeroWord;
end
end
// *************************第四阶段:读端口2的读操作******************************
always @ (*) begin
if (rst == `RstEnable) begin
rdata2 <= `ZeroWord;
end else if (raddr2 == `RegNumLog2'h0) begin
rdata2 <= `ZeroWord;
end else if ((raddr2 == waddr) && (we == `WriteEnable) && (re2 == `ReadEnable)) begin
rdata2 <= wdata;
end else if (re2 == `ReadEnable) begin
rdata2 <= regs[raddr2];
end else begin
rdata2 <= `ZeroWord;
end
end
endmodule
/*
注:
读操作时组合逻辑电路,输入的要读取的寄存器地址raddr1和raddr2发生变化,立刻得到新地址对应的寄存器的值,这样可以保证在译码阶段取得要读取的寄存器的值
写操作时时序逻辑电路,发生在时钟上升沿
*/
5. 执行ex
// 从ID模块得到运算类型alusel_i、运算子类型aluse_i、源操作数reg1_i、源操作数reg2_i、要写入的目的寄存器地址wd_i
// EX模块依据这些数据进行运算
`include "defines.v"
module ex (
// 执行的结果
output reg [`RegAddrBus] wd_o, // 最终要写入的目的寄存器地址
output reg wreg_o, // 最终是否有要写入的目的寄存器
output reg [`RegBus] wdata_o, // 最终要写入目的寄存器的值
input wire rst,
// 译码阶段送到执行阶段的信息
input wire [`AluOpBus] aluop_i, // 执行阶段运算子类型
input wire [`AluSelBus] alusel_i, // 执行阶段运算类型
input wire [`RegBus] reg1_i, // 执行阶段源操作数1
input wire [`RegBus] reg2_i, // 执行阶段源操作数2
input wire [`RegAddrBus] wd_i, // 执行阶段要写入的目的寄存器地址
input wire wreg_i // 执行阶段是否有要写入的目的寄存器
);
// 保存逻辑运算的结果
reg [`RegBus] logicout;
// ****************************************************************************
// ***************第一阶段:依据aluop_i指示的运算子类型进行运算*********************
// ****************************************************************************
always @ (*) begin
if (rst == `RstEnable) begin
logicout <= `ZeroWord;
end else begin
case (aluop_i)
`EXE_OR_OP: begin
logicout <= reg1_i | reg2_i;
end
default: begin
logicout <= `ZeroWord;
end
endcase // case
end // if
end // always
// ****************************************************************************
// **********第二阶段:依据alusel_i指示的运算类型,选择一个运算结果作为最终结果********
// ****************************************************************************
always @ (*) begin
wd_o <= wd_i; // 要写入的目的寄存器地址
wreg_o <= wreg_i; // 是否要写入目的寄存器
case (alusel_i)
`EXE_RES_LOGIC: begin
wdata_o <= logicout; // wdata_o中存放运算结果
end
default: begin
wdata_o <= `ZeroWord;
end
endcase // case
end // always
endmodule
6.访存mem
// mem访存模块
`include "defines.v"
module mem (
output reg [`RegAddrBus] wd_o, // 传到回写阶段的寄存器地址
output reg wreg_o, // 是否有写入
output reg [`RegBus] wdata_o, // 传到回写阶段的数据
input wire rst,
input wire [`RegAddrBus] wd_i,
input wire wreg_i,
input wire [`RegBus] wdata_i
);
always @ (*) begin
if (rst == `RstEnable) begin
wd_o <= `NOPRegAddr;
wreg_o <= `WriteDisable;
wdata_o <= `ZeroWord;
end else begin
wd_o <= wd_i;
wreg_o <= wreg_i;
wdata_o <= wdata_i;
end
end
endmodule
7. 回写wb
// 将输入的执行阶段的结果直接作为输出
`include "defines.v"
module wb (
// 访存阶段的结果
output reg [`RegAddrBus] wd_o, // 访存阶段的指令最终要写入的目的寄存器地址
output reg wreg_o, // 访存阶段的指令最终是否有要写入的目的寄存器
output reg [`RegBus] wdata_o, // 访存阶段的指令最终要写入目的寄存器的值
input wire rst,
// 来自执行阶段的信息
input wire [`RegAddrBus] wd_i,
input wire wreg_i,
input wire [`RegBus] wdata_i
);
always @ (*) begin
if (rst == `RstEnable) begin
wd_o <= `NOPRegAddr;
wreg_o <= `WriteDisable;
wdata_o <= `ZeroWord;
end else begin
wd_o <= wd_i;
wreg_o <= wreg_i;
wdata_o <= wdata_i;
end
end
endmodule
8 .顶模块OpenMIPS
// 顶层模块
`include "defines.v"
module openmips (
output wire [`RegBus] rom_addr_o, // 输出到指令存储器的地址
output wire rom_ce_o, // 指令寄存器使能信号
input wire [`RegBus] rom_data_i, // 从指令寄存器取得的指令
input wire rst,
input wire clk
);
// ****************************************************************************
// **************************第一阶段:物理连线声明******************************
// ****************************************************************************
// 连接ID模块和EX模块输入的变量
wire [`AluOpBus] id_aluop;
wire [`AluSelBus] id_alusel;
wire [`RegBus] id_reg1;
wire [`RegBus] id_reg2;
wire id_wreg;
wire [`RegAddrBus] id_wd;
// 连接译码阶段ID模块和通用寄存器Regfile模块的变量
wire reg1_read;
wire reg2_read;
wire [`RegBus] reg1_data;
wire [`RegBus] reg2_data;
wire [`RegAddrBus] reg1_addr;
wire [`RegAddrBus] reg2_addr;
// 连接EX模块输出和MEM模块的变量
wire [`RegAddrBus] ex_wd;
wire ex_wreg;
wire [`RegBus] ex_wdata;
// 连接MEM模块输出和WB模块的变量
wire [`RegAddrBus] mem_wd;
wire mem_wreg;
wire [`RegBus] mem_wdata;
// 连接WB模块和RegFIle模块输入的变量
wire [`RegAddrBus] wb_wd;
wire wb_wreg;
wire [`RegBus] wb_wdata;
// ****************************************************************************
// **********************第二阶段:对各个模块进行实例化*****************************
// ****************************************************************************
// pc_reg例化
pc_reg pc_reg0 (
.pc(rom_addr_o), .ce(rom_ce_o),
.clk(clk), .rst(rst)
);
// 译码阶段ID模块例化
id id0 (
// 送到RegFile模块的信息
.reg1_read_o(reg1_read), .reg2_read_o(reg2_read),
.reg1_addr_o(reg1_addr), .reg2_addr_o(reg2_addr),
// 送到EX模块的信息
.aluop_o(id_aluop), .alusel_o(id_alusel),
.reg1_o(id_reg1), .reg2_o(id_reg2),
.wd_o(id_wd), .wreg_o(id_wreg),
.rst(rst), .pc_i(rom_addr_o), .inst_i(rom_data_i),
// 来自RegFile模块的输出
.reg1_data_i(reg1_data), .reg2_data_i(reg2_data)
);
// 通用寄存器Regfile模块例化
regfile regfile0 (
.we(wb_wreg), .waddr(wb_wd), .wdata(wb_wdata),
.re1(reg1_read), .raddr1(reg1_addr), .rdata1(reg1_data),
.re2(reg2_read), .raddr2(reg2_addr), .rdata2(reg2_data),
.clk(clk), .rst(rst)
);
// EX模块例化
ex ex0 (
// 输出到MEM模块的信息
.wd_o(ex_wd), .wreg_o(ex_wreg), .wdata_o(ex_wdata),
.rst(rst),
// 从ID模块传递过来的信息
.aluop_i(id_aluop), .alusel_i(id_alusel),
.reg1_i(id_reg1), .reg2_i(id_reg2),
.wd_i(id_wd), .wreg_i(id_wreg)
);
// MEM模块例化
mem mem0 (
// 输出到WB模块的信息
.wd_o(mem_wd), .wreg_o(mem_wreg), .wdata_o(mem_wdata),
.rst(rst),
// 从EX模块接收到的信息
.wd_i(ex_wd), .wreg_i(ex_wreg), .wdata_i(ex_wdata)
);
// WB模块例化
wb wb0 (
// 输出的信息
.wd_o(wb_wd), .wreg_o(wb_wreg), .wdata_o(wb_wdata),
.rst(rst),
// 从EX模块接收到的信息
.wd_i(ex_wd), .wreg_i(ex_wreg), .wdata_i(ex_wdata)
);
endmodule
9 .指令存储器
// 指令存储器ROM模块
`include "defines.v"
module inst_rom (
output reg [`InstBus] inst, // 读出的指令
input wire ce, // 使能信号
input wire [`InstAddrBus] addr // 要读取的指令地址
);
// 定义一个数组,大小是InstMemNum(131071),元素宽度是InstBus
reg [`InstBus] inst_mem[0:`InstMemNum-1];
// 使用文件inst_rom.data初始化指令存储器
initial $readmemh ("inst_rom.dat", inst_mem);
// 当复位信号无效时,依据输入的地址,给出指令存储器ROM中对应的元素
always @ (*) begin
if (ce == `ChipDisable) begin
inst = `ZeroWord;
end else begin
inst <= inst_mem[addr[`InstMemNumLog2+1:2]]; // inst_mem[addr[5+1:2]]
end
end
endmodule
10 .最小SOPC实现
// 最小SOPC模块,仅包括OpenMIPS和指令存储器ROM,例化处理这个两个模块
`include "defines.v"
module OpenMIPS_min_SOPC (
input wire clk,
input wire rst
);
// 连接指令存储器
wire [`InstAddrBus] inst_addr;
wire [`InstBus] inst;
wire rom_ce;
// 例化处理器OpenMIPS
openmips openmips0 (
.rom_addr_o(inst_addr), .rom_ce_o(rom_ce),
.rom_data_i(inst),
.clk(clk), .rst(rst)
);
// 例化指令存储器
inst_rom inst_rom0 (
.inst(inst),
.ce(rom_ce), .addr(inst_addr)
);
endmodule
测试模块以及仿真波形
// testbench of "OpenMIPS_mini_SOPC_tb"
`include "defines.v"
module OpenMIPS_min_SOPC_tb ();
reg clk;
reg rst;
module OpenMIPS_min_SOPC_tb ();
reg clk;
reg rst;
// 例化最小SOPC
OpenMIPS_min_SOPC OpenMIPS_min_SOPC0 (
.clk(clk), .rst(rst)
);
initial begin
clk = 1'b0;
end
always #10 clk = ~clk;
// 最初时刻,复位信号有效,在第195ns,复位信号无效,最小SOPC开始运行
initial begin
rst = `RstEnable;
#195 rst = `RstDisable;
// #1000 $stop;
end
endmodule
指令存储文件inst_rom.dat
记事本中存放数据:
34011100 // $1 = 0000_0000 | 0000_1100 = 0000_1100
34020020 // $2 = 0000_0000 | 0000_0020 = 0000_0020
3403ff00 // $3 = 0000_0000 | 0000_ff00 = 0000_ff00
3404ffff // $4 = 0000_0000 | 0000_ffff = 0000_ffff
34210020 // $1 = 0000_1100 | 0000_0020 = 0000_1120
34214400 // $1 = 0000_1120 | 0000_4400 = 0000_5520
34210044 // $1 = 0000_5520 | 0000_0040 = 0000_5564
------------------------------------------------------------------------------------------------------------------------------------------------------
遇到的问题以及解决
1. 仿真波形输错_1
- 这里的reg1在执行完第五个周期时变为不定值是什么原因?
修改所有错误后,reg1_i等信号在第五个周期变为不定值都得到纠正
- 有部分仿真波形从始至终都是蓝色直线?
在openmips.v中的实例化中端口对应没有匹配好,已修改,蓝线消失
ex ex0 (
.wreg_i(id_wreg),
// 错误代码:.wreg_i(ex_wreg),
);
2. 仿真波形出错_2
- sim:/OpenMIPS_min_SOPC_tb/OpenMIPS_min_SOPC0/openmips0/wb_wdata 和sim:/OpenMIPS_min_SOPC_tb/OpenMIPS_min_SOPC0/openmips0/regfile0/wdata 两条仿真波形始终都是红色直线?
对于没有初始化的信号,输入信号为高阻态蓝线(Z),输出信号为不定态红线(X)
regfile.v模块中的信号输入输出没有定义好,已修改,红线消失
module regfile (
input reg ['RegBus] wdata, // 要写入的数据
// 错误代码:output reg ['RegBus] wdata,
);
3. 顶层模块段端口定义
两个输出信号rom_addr_o、rom_ce_o需要定义成wire。定义成reg会报错,编译可通过,仿真不通过。代码已修改
4. 解决取操作数不准确
指令5开始,rdata1本应该从寄存器$1读出上次写入的数据0x0000_1100,因为读写地址都相同,同时满足regfile.v中的读端口1读数据的结果值,实际读出指令5的执行结果0x0000_1120(0x0000_0000 ori ox0000_0020)
流水添加的是数据前推端口,没有这种困扰
解决方法1:
regfile.v读端口1模块使用if-case分支,顺序执行,交换分支顺序,优先判断是否从寄存器直接取到源操作数。当读写地址相同时,两个else都满足,但实际我们想要的是从寄存器中读出来的数据,为了防止被"被短路",需要交换顺序,先判断是否从寄存器取数
解决方法2:
在EX模块新添加寄存器模块reg [`RegBus] result;连线到regfile.v模块
若想实现,必须添加在指定位置。
(太乱了,实在看不懂为什么都是哪些值,只能保证正确的值存在result中,但其他信号还会有取错的,我投降)
2021/05/09
再回来看看读取操作数不正确的原因,方法1看不懂,方法2当时跑通测试,但是没有写笔记…
救命,以后我一定好好写笔记,尽量不省略
我还写的没有这种困扰…现在都不知道是啥困扰了,还别说解决
.
.
.
会了
这里是单周期
// *************************第三阶段:读端口1的读操作******************************
always @ (*) begin
if (rst == `RstEnable) begin
rdata1 <= `ZeroWord; // 复位信号有效,输出0
end else if (raddr1 == `RegNumLog2'h0) begin // 读目的寄存器为$0,输出0
rdata1 <= `ZeroWord;
end else if (re1 == `ReadEnable) begin // 输出要读取的目的寄存器地址对应的值
rdata1 <= regs[raddr1];
end else if ((raddr1 == waddr) && (we == `WriteEnable) && (re1 == `ReadEnable)) begin // 读目的寄存器是写目的寄存器&&写使能&&读使能,输出写入的值
rdata1 <= wdata;
end else begin
rdata1 <= `ZeroWord;
end
end
// *************************第四阶段:读端口2的读操作******************************
always @ (*) begin
if (rst == `RstEnable) begin
rdata2 <= `ZeroWord;
end else if (raddr2 == `RegNumLog2'h0) begin
rdata2 <= `ZeroWord;
end else if ((raddr2 == waddr) && (we == `WriteEnable) && (re2 == `ReadEnable)) begin
rdata2 <= wdata;
end else if (re2 == `ReadEnable) begin
rdata2 <= regs[raddr2];
end else begin
rdata2 <= `ZeroWord;
end
end
endmodule
这是交换后的代码,只需要交换读端口1的操作。因为指令如果是寄存器和立即数进行运算,默认通过读端口1读取寄存器数据
因为是单周期,在一个节拍下就要进行取数、执行、回写,交换之后就能保证,读取的数据是之前写入寄存器的数据,而不是本条指令执行后的运算结果。
流水线我也忘了需不需要处理这个读数据错误