为什么加4:因为MIPS按照字节寻址,32bit是4个字节,所以每次加4。
译码阶段,会涉及到读2个寄存器以及立即数扩展,并最终通过两个mux确定两个立即数输出
这其实就是一个帧格式
instvalid手动改为了1’b1
PC:给出指令地址
`include "defines.v"
module pc_reg(
input wire clk,
input wire rst,
output reg [`InstAddrBus] pc, //InstAddrBus为宏定义 31:0 代表ROM的地址总线宽度 输出给ROM的addr
output reg ce //给到ROM的使能
);
//如果复位,则使能信号ce拉0,否则置1
always @ (posedge clk) begin
if (rst == `RstEnable) begin //复位信号有效 1'b1
ce <= `ChipDisable;
end
else begin
ce <= `ChipEnable;
end
end
//如果使能信号为低,则指令地址拉0;使能有效,则指令地址自加4'h4
always @ (posedge clk) begin
if (ce == `ChipDisable) begin //芯片禁止宏定义 1'b0
pc <= 32'h00000000;
end
else begin
pc <= pc + 4'h4; //自加4 字节寻址
end
end
endmodule
if/id:保存取的指令,下一拍传给译码阶段,此处为寄存器
实际上,中间还有指令存储器,即pc模块的指令地址要给到存储器ROM中作为地址,ce作为ROM读使能,ROM的输出才给到IF/ID的if_inst
`include "defines.v"
module if_id(
input wire clk,
input wire rst,
input wire [`InstBus] if_inst, //InstBus为ROM数据宽度,同样为31:0
output reg [`InstBus] id_inst,
output reg [`InstAddrBus] id_pc
);
//复位有效 输出为32位的0;否则则寄存传递来的取指指令地址和指令
always @ (posedge clk) begin
if (rst == `RstEnable) begin
id_pc <= `ZeroWord; //32位的数值0
id_inst <= `ZeroWord; //32位的数值0
end
else begin
id_pc <= if_pc ;
id_inst <= if_inst;
end
end
endmodule
重点:实现32个32位通用整数寄存器;同时可进行两个寄存器的读操作和一个寄存器的写操作
`include "defines.v"
module regfile(
input wire clk,
input wire rst,
input wire re1, //1读使能
input wire [`RegAddrBus] raddr1, //寄存器地址,32个只需要4:0
input wire re2, //1读使能
input wire [`RegAddrBus] raddr2, //寄存器地址,32个只需要4:0
input wire we, //写使能
input wire [`RegAddrBus] waddr,
input wire [`RegBus] wdata, //写数据
output reg [`RegBus] rdata1, //读出数据1
output reg [`RegBus] rdata2 //读出数据1
);
//定义32个通用寄存器
reg [`RegBus] regs [0:`RegNum - 1];
//写操作,用时序描述;地址不能为寄存器0,MIPS规定寄存器0值只能为0
always@(posedge clk)
if(rst == `RstDisable) begin
if((we == `WriteEnable)&&(waddr != `RegNumLog2'h0))
regs[waddr] <= wdata;
end
//读操作 复位和寄存器0两种 写读同地址 普通读 其他时候为0
//组合逻辑
always@(*)
if(rst == `RstEnable)
rdata1 <= `ZeroWord;
else if(raddr1 == `RegNumLog2'h0)
rdata1 <= `ZeroWord;
else if((re1 == `ReadEnable)&&(we == `WriteEnable)&&(waddr == raddr1))
rdata1 <= wdata;
else if(re1 == `ReadEnable)
rdata1 <= regs[raddr1];
else
rdata1 <= `ZeroWord;
always@(*)
if(rst == `RstEnable)
rdata2 <= `ZeroWord;
else if(raddr2 == `RegNumLog2'h0)
rdata2 <= `ZeroWord;
else if((re2 == `ReadEnable)&&(we == `WriteEnable)&&(waddr == raddr2))
rdata2 <= wdata;
else if(re1 == `ReadEnable)
rdata2 <= regs[raddr2];
else
rdata2 <= `ZeroWord;
endmodule
module id(
input wire rst,
input wire[`InstAddrBus] pc_i, //[31:0]
input wire[`InstBus] inst_i, //[31:0] 接收IF/ID模块给的指令
//从regfile里输入的
input wire[`RegBus] reg1_data_i, //[31:0]
input wire[`RegBus] reg2_data_i, //[31:0]
//送到regfile的信息
output reg reg1_read_o, //使能
output reg reg2_read_o, //使能
output reg[`RegAddrBus] reg1_addr_o, //[4:0]
output reg[`RegAddrBus] reg2_addr_o, //[4:0]
//送到执行阶段的信息
output reg[`AluOpBus] aluop_o, //译码阶段的输出aluop_o的宽度 7:0 运算子类型
output reg[`AluSelBus] alusel_o, //译码阶段的输出alusel_o的宽度 2:0 运算类型
//结果写入寄存器
output reg[`RegAddrBus] wd_o, //[4:0] 要写入的目的寄存器地址
output reg wreg_o, //是否有要写入的目的寄存器
//这两个是最终输出的操作数
output reg[`RegBus] reg1_o, //[31:0] 源操作数1
output reg[`RegBus] reg2_o //[31:0] 源操作数2
);
//取得指令的指令码,功能码 即分解帧格式
//对于ori指令只需通过判断第26-31bit的值,即可判断是否是ori指令;对应I类型指令
wire[5:0] op = inst_i[31:26]; //op指令段
wire[4:0] op2 = inst_i[10:6];
wire[5:0] op3 = inst_i[5:0];
wire[4:0] op4 = inst_i[20:16];
//保存指令执行需要的立即数[31:0]
reg[`RegBus] imm;
//指示指令是否有效
reg instvalid;
//译码操作 组合逻辑实时译码
always @ (*) begin
if (rst == `RstEnable) begin
aluop_o <= `EXE_NOP_OP; //8'b0000_0000
alusel_o <= `EXE_RES_NOP; //3'b000
wd_o <= `NOPRegAddr; //5'b00000
wreg_o <= `WriteDisable;
instvalid <= `InstValid; //指令有效
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
reg1_addr_o <= `NOPRegAddr; //5'b00000
reg2_addr_o <= `NOPRegAddr; //5'b00000
imm <= `ZeroWord;
end
else begin
//以下是非复位且无法判断指令时的默认情况
aluop_o <= `EXE_NOP_OP; //8'b0000_0000
alusel_o <= `EXE_RES_NOP; //3'b000
wd_o <= inst_i[15:11];
wreg_o <= `WriteDisable;
instvalid <= `InstInvalid; //指令无效
reg1_read_o <= 1'b0;
reg2_read_o <= 1'b0;
//默认
reg1_addr_o <= inst_i[25:21]; //rs寄存器
reg2_addr_o <= inst_i[20:16]; //rt寄存器
imm <= `ZeroWord;
case (op)
`EXE_ORI: begin //ORI指令 用立即数和rs计算后写入rt
alusel_o <= `EXE_RES_LOGIC;
aluop_o <= `EXE_OR_OP;
wreg_o <= `WriteEnable;
wd_o <= inst_i[20:16]; //立即数与rs的结果写入rt
imm <= {16'h0,inst_i[15:0]};
reg1_read_o <= 1'b1; //要去读一个rs
reg2_read_o <= 1'b0;
instvalid <= `InstValid;
end
default:;
endcase
end
end
//确定2个源操作数
always@(*)
if(rst == `RstEnable)
reg1_o <= `ZeroWord;
else if(reg1_read_o == 1'b1)
reg1_o <= reg1_data_i;
else if(reg1_read_o == 1'b0)
reg1_o <= imm;
else
reg1_o <= `ZeroWord;
always@(*)
if(rst == `RstEnable)
reg2_o <= `ZeroWord;
else if(reg2_read_o == 1'b1)
reg2_o <= reg2_data_i;
else if(reg2_read_o == 1'b0)
reg2_o <= imm;
else
reg2_o <= `ZeroWord;
endmodule
`include "defines.v"
module id_ex(
input wire clk,
input wire rst,
//从译码阶段传递的信息
input wire[`AluOpBus] id_aluop, //[7:0]
input wire[`AluSelBus] id_alusel, //[2:0]
input wire[`RegBus] id_reg1,
input wire[`RegBus] id_reg2,
input wire[`RegAddrBus] id_wd,
input wire id_wreg,
//传递到执行阶段的信息
output reg[`AluOpBus] ex_aluop,
output reg[`AluSelBus] ex_alusel,
output reg[`RegBus] ex_reg1,
output reg[`RegBus] ex_reg2,
output reg[`RegAddrBus] ex_wd,
output reg ex_wreg
);
//打一拍传过去
always @ (posedge clk) begin
if (rst == `RstEnable) begin
ex_aluop <= `EXE_NOP_OP;
ex_alusel <= `EXE_RES_NOP;
ex_reg1 <= `ZeroWord;
ex_reg2 <= `ZeroWord;
ex_wd <= `NOPRegAddr;
ex_wreg <= `WriteDisable;
end
else begin
ex_aluop <= id_aluop;
ex_alusel <= id_alusel;
ex_reg1 <= id_reg1;
ex_reg2 <= id_reg2;
ex_wd <= id_wd;
ex_wreg <= id_wreg;
end
end
endmodule
`include "defines.v"
module ex(
input wire rst,
//送到执行阶段的信息
input wire[`AluOpBus] aluop_i,
input wire[`AluSelBus] alusel_i,
input wire[`RegBus] reg1_i,
input wire[`RegBus] reg2_i,
input wire[`RegAddrBus] wd_i,
input wire wreg_i,
//执行结果
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o
);
//保存逻辑运算的结果
reg[`RegBus] logicout;
//根据大类型判断输出
always @ (*) begin
wd_o <= wd_i; //传递写目的寄存器地址
wreg_o <= wreg_i; //传递寄存器写使能信号
case ( alusel_i )
`EXE_RES_LOGIC: begin
wdata_o <= logicout;
end
default: begin
wdata_o <= `ZeroWord;
end
endcase
end
//根据小类型作具体运算
always@(*)
if(rst == `RstEnable)
logicout <= 0;
else begin
case(aluop_i)
`EXE_OR_OP:
logicout <= reg1_i | reg2_i;
default: wdata_o <= 0;
endcase
end
endmodule
`include "defines.v"
module ex_mem(
input wire clk,
input wire rst,
//来自执行阶段的信息
input wire[`RegAddrBus] ex_wd,
input wire ex_wreg,
input wire[`RegBus] ex_wdata,
//送到访存阶段的信息
output reg[`RegAddrBus] mem_wd,
output reg mem_wreg,
output reg[`RegBus] mem_wdata
);
always @ (posedge clk) begin
if(rst == `RstEnable) begin
mem_wd <= `NOPRegAddr;
mem_wreg <= `WriteDisable;
mem_wdata <= `ZeroWord;
end else begin
mem_wd <= ex_wd;
mem_wreg <= ex_wreg;
mem_wdata <= ex_wdata;
end
end
endmodule
`include "defines.v"
module mem(
input wire rst,
//来自执行阶段的信息
input wire[`RegAddrBus] wd_i,
input wire wreg_i,
input wire[`RegBus] wdata_i,
//送到回写阶段的信息
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o
);
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
module mem_wb(
input wire clk,
input wire rst,
//来自访存阶段的信息
input wire[`RegAddrBus] mem_wd,
input wire mem_wreg,
input wire[`RegBus] mem_wdata,
//送到回写阶段的信息
output reg[`RegAddrBus] wb_wd,
output reg wb_wreg,
output reg[`RegBus] wb_wdata
);
always @ (posedge clk) begin
if(rst == `RstEnable) begin
wb_wd <= `NOPRegAddr;
wb_wreg <= `WriteDisable;
wb_wdata <= `ZeroWord;
end
else begin
wb_wd <= mem_wd;
wb_wreg <= mem_wreg;
wb_wdata <= mem_wdata;
end
end
endmodule
`include "defines.v"
module openmips(
input wire clk,
input wire rst,
input wire[`RegBus] rom_data_i,
output wire[`RegBus] rom_addr_o,
output wire rom_ce_o
);
wire[`InstAddrBus] pc;
wire[`InstAddrBus] id_pc_i;
wire[`InstBus] id_inst_i;
//连接译码阶段ID模块的输出与ID/EX模块的输入
wire[`AluOpBus] id_aluop_o;
wire[`AluSelBus] id_alusel_o;
wire[`RegBus] id_reg1_o;
wire[`RegBus] id_reg2_o;
wire id_wreg_o;
wire[`RegAddrBus] id_wd_o;
//连接ID/EX模块的输出与执行阶段EX模块的输入
wire[`AluOpBus] ex_aluop_i;
wire[`AluSelBus] ex_alusel_i;
wire[`RegBus] ex_reg1_i;
wire[`RegBus] ex_reg2_i;
wire ex_wreg_i;
wire[`RegAddrBus] ex_wd_i;
//连接执行阶段EX模块的输出与EX/MEM模块的输入
wire ex_wreg_o;
wire[`RegAddrBus] ex_wd_o;
wire[`RegBus] ex_wdata_o;
//连接EX/MEM模块的输出与访存阶段MEM模块的输入
wire mem_wreg_i;
wire[`RegAddrBus] mem_wd_i;
wire[`RegBus] mem_wdata_i;
//连接访存阶段MEM模块的输出与MEM/WB模块的输入
wire mem_wreg_o;
wire[`RegAddrBus] mem_wd_o;
wire[`RegBus] mem_wdata_o;
//连接MEM/WB模块的输出与回写阶段的输入
wire wb_wreg_i;
wire[`RegAddrBus] wb_wd_i;
wire[`RegBus] wb_wdata_i;
//连接译码阶段ID模块与通用寄存器Regfile模块
wire reg1_read;
wire reg2_read;
wire[`RegBus] reg1_data;
wire[`RegBus] reg2_data;
wire[`RegAddrBus] reg1_addr;
wire[`RegAddrBus] reg2_addr;
//pc_reg例化
pc_reg pc_reg0(
.clk(clk),
.rst(rst),
.pc(pc),
.ce(rom_ce_o)
);
assign rom_addr_o = pc; //指令存储器的输入地址就是pc的值
//IF/ID模块例化
if_id if_id0(
.clk(clk),
.rst(rst),
.if_pc(pc),
.if_inst(rom_data_i),
.id_pc(id_pc_i),
.id_inst(id_inst_i)
);
//译码阶段ID模块
id id0(
.rst(rst),
.pc_i(id_pc_i),
.inst_i(id_inst_i),
//来自regfile的输入
.reg1_data_i(reg1_data),
.reg2_data_i(reg2_data),
//送到regfile的信息
.reg1_read_o(reg1_read),
.reg2_read_o(reg2_read),
.reg1_addr_o(reg1_addr),
.reg2_addr_o(reg2_addr),
//送到ID/EX模块的信息
.aluop_o(id_aluop_o),
.alusel_o(id_alusel_o),
.reg1_o(id_reg1_o),
.reg2_o(id_reg2_o),
.wd_o(id_wd_o),
.wreg_o(id_wreg_o)
);
//通用寄存器Regfile例化
regfile regfile1(
.clk (clk),
.rst (rst),
.we (wb_wreg_i),
.waddr (wb_wd_i),
.wdata (wb_wdata_i),
.re1 (reg1_read),
.raddr1 (reg1_addr),
.rdata1 (reg1_data),
.re2 (reg2_read),
.raddr2 (reg2_addr),
.rdata2 (reg2_data)
);
//ID/EX模块
id_ex id_ex0(
.clk(clk),
.rst(rst),
//从译码阶段ID模块传递的信息
.id_aluop(id_aluop_o),
.id_alusel(id_alusel_o),
.id_reg1(id_reg1_o),
.id_reg2(id_reg2_o),
.id_wd(id_wd_o),
.id_wreg(id_wreg_o),
//传递到执行阶段EX模块的信息
.ex_aluop(ex_aluop_i),
.ex_alusel(ex_alusel_i),
.ex_reg1(ex_reg1_i),
.ex_reg2(ex_reg2_i),
.ex_wd(ex_wd_i),
.ex_wreg(ex_wreg_i)
);
//EX模块
ex ex0(
.rst(rst),
//送到执行阶段EX模块的信息
.aluop_i(ex_aluop_i),
.alusel_i(ex_alusel_i),
.reg1_i(ex_reg1_i),
.reg2_i(ex_reg2_i),
.wd_i(ex_wd_i),
.wreg_i(ex_wreg_i),
//EX模块的输出到EX/MEM模块信息
.wd_o(ex_wd_o),
.wreg_o(ex_wreg_o),
.wdata_o(ex_wdata_o)
);
//EX/MEM模块
ex_mem ex_mem0(
.clk(clk),
.rst(rst),
//来自执行阶段EX模块的信息
.ex_wd(ex_wd_o),
.ex_wreg(ex_wreg_o),
.ex_wdata(ex_wdata_o),
//送到访存阶段MEM模块的信息
.mem_wd(mem_wd_i),
.mem_wreg(mem_wreg_i),
.mem_wdata(mem_wdata_i)
);
//MEM模块例化
mem mem0(
.rst(rst),
//来自EX/MEM模块的信息
.wd_i(mem_wd_i),
.wreg_i(mem_wreg_i),
.wdata_i(mem_wdata_i),
//送到MEM/WB模块的信息
.wd_o(mem_wd_o),
.wreg_o(mem_wreg_o),
.wdata_o(mem_wdata_o)
);
//MEM/WB模块
mem_wb mem_wb0(
.clk(clk),
.rst(rst),
//来自访存阶段MEM模块的信息
.mem_wd(mem_wd_o),
.mem_wreg(mem_wreg_o),
.mem_wdata(mem_wdata_o),
//送到回写阶段的信息
.wb_wd(wb_wd_i),
.wb_wreg(wb_wreg_i),
.wb_wdata(wb_wdata_i)
);
endmodule
`include "defines.v"
module inst_rom(
input wire ce,
input wire[`InstAddrBus] addr,
output reg[`InstBus] inst
);
//InstMemNum=131071 ROM实际大小为128KB,这儿扩充了4倍
//因为OpenMIPS按字节寻址,实际使用时需要/4,131071 是按8bit字个数看的
reg[`InstBus] inst_mem[0:`InstMemNum-1];
initial $readmemh ( "inst_rom.data", inst_mem );
always @ (*) begin
if (ce == `ChipDisable) begin
inst <= `ZeroWord;
end
else begin
//131071即是17bit,/4再右移2位,才是实际读的
inst <= inst_mem[ addr[`InstMemNumLog2+1:2] ];
end
end
endmodule
`include "defines.v"
module openmips_min_sopc(
input wire clk,
input wire rst
);
//连接指令存储器
wire[`InstAddrBus] inst_addr;
wire[`InstBus] inst;
wire rom_ce;
openmips openmips0(
.clk(clk),
.rst(rst),
.rom_addr_o(inst_addr),
.rom_data_i(inst),
.rom_ce_o(rom_ce)
);
inst_rom inst_rom0(
.addr(inst_addr),
.inst(inst),
.ce(rom_ce)
);
endmodule
测试过程见书。至此完成了第一条指令与基础五级流水的搭建