一、ORI指令
二、状态机与流水线
三、五级流水线模型
四、流水线验证
五、处理数据相关问题
六、MIPS编译环境建立——GNU工具链
//******************************************全局宏定义******************************************************
`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'b1 //指令有效
`define InstInvalid 1'b0 //指令无效
`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指令
`define EXE_NOP_OP 8'b00000000 //空指令
//AluSe
`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 //ROM的实际大小为128KB = 2^17
`define InstMemNumLog2 17 //ROM实际使用的地址线宽度
//**********************************与通用寄存器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
/***************************************寄存器读写配置模块*********************************************/
`include "defines.v"
module regfile( input wire clk, input wire rst, //时钟 复位
input wire we, input wire[`RegAddrBus] waddr, input wire[`RegBus] wdata, //写端口
input wire re1, input wire[`RegAddrBus] raddr1,output reg[`RegBus] rdata1, //读端口1
input wire re2, input wire[`RegAddrBus] raddr2,output reg[`RegBus] rdata2 //读端口2
);
/**************************************32个32位寄存器********************************************/
reg [`RegBus] regs [`RegNum-1:0];
/***************************************寄存器写操作*********************************************/
always @( posedge clk )
begin
if(rst == `RstDisable) //复位时钟无效时
begin
if( (waddr != `RegNumLog2'h0) && we == `WriteEnable)
//若当前寄存器不为0(MIPS规定$0为32bit0 不允许写入) 并且写使能有效 则将数据写入其中
begin
regs[waddr] <= wdata;
end
end
end
/***************************************寄存器读操作1*********************************************/
always @(*)
begin
if(rst == `RstEnable) //复位有效
begin
rdata1 <= `ZeroWord;
end
//复位无效
else if(raddr1 == `RegNumLog2'h0) //所读寄存器为$0
rdata1 <= `ZeroWord;
else if( (raddr1 == waddr) && (we ==`WriteEnable) && (re1 == `ReadEnable) )
rdata1 <= wdata; //读写为同一寄存器
else if( re1 == `ReadEnable ) //正常读取
rdata1 <= regs[raddr1];
else
rdata1 <= `ZeroWord; //无法使用
end
/***************************************寄存器读操作2*********************************************/
always @(*)
begin
if(rst == `RstEnable) //复位有效
begin
rdata2 <= `ZeroWord;
end
else //复位无效
begin
if(raddr2 == `RegNumLog2'h0) //
rdata2 <= `ZeroWord;
else if( (raddr2 == waddr) && (we ==`WriteEnable) && (re2 == `ReadEnable) )
rdata2 <= wdata;
else if( re2 == `ReadEnable )
rdata2 <= regs[raddr2];
else
rdata2 <= `ZeroWord;
end
end
endmodule
/***********************************取指模块**************************************/
`include "defines.v"
module pc_reg( input wire rst, input wire clk, output reg[`InstAddrBus] pc, output reg ce );
always @(posedge clk)
begin
if(rst == `RstEnable) //复位开始,指令存储器禁用
ce <= `ChipDisable;
else
ce <= `ChipEnable; //复位结束,指令存储器使能
end
always @(posedge clk)
begin
if(ce == `ChipDisable) //指令寄存器禁用
pc <= `ZeroWord; //PC始终为0 32位
else
pc <= pc + 4'h4; //指令寄存器使能,PC值每时钟周期+4
end
endmodule
/*************************************取值-译码模块****************************************/
`include "defines.v"
module if_id( input wire clk, input wire rst,
input wire[`InstAddrBus] if_pc,
input wire[`InstBus] if_inst,
output reg[`InstAddrBus] id_pc,
output reg[`InstBus] id_inst );
always @(posedge clk)
begin
if(rst == `RstEnable) //复位开始
begin
id_pc <= `ZeroWord;
id_inst <= `ZeroWord;
end
else
begin
id_pc <= if_pc;
id_inst <= if_inst;
end
end
endmodule
/***************************************译码模块**********************************************************/
`include "defines.v"
module id( input wire rst, //复位
input wire[`InstAddrBus] pc_i, //程序计数器
input wire[`InstBus] inst_i, //指令
//读取的regfile的值
input wire[`RegBus] reg1_data_i, //寄存器堆中寄存器1数据
input wire[`RegBus] reg2_data_i, //寄存器堆中寄存器2数据
//输出到regfile的信息
output reg reg1_read_o, //
output reg reg2_read_o,
output reg[`RegAddrBus] reg1_addr_o,
output reg[`RegAddrBus] reg2_addr_o,
//送到执行阶段的信息
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 //写目的寄存器使能
);
//取得指令的指令码和功能码
wire[5:0] op = inst_i[31:26]; //ORI指令操作码
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 <= `InstInvalid;
reg1_read_o <= `ReadDisable;
reg2_read_o <= `ReadDisable;
reg1_addr_o <= `NOPRegAddr;
reg2_addr_o <= `NOPRegAddr;
imm <= `ZeroWord;
end
else //复位无效
begin
aluop_o <= `EXE_NOP_OP; //初始化
alusel_o <= `EXE_RES_NOP;
wd_o <= inst_i[15:11];
wreg_o <= `WriteDisable;
instvalid <= `InstValid;
reg1_read_o <= `ReadDisable;
reg2_read_o <= `ReadDisable;
reg1_addr_o <= inst_i[25:21]; //寄存器1地址
reg2_addr_o <= inst_i[20:16]; //寄存器2地址
imm <= `ZeroWord;
case(op) //指令译码
`EXE_ORI: //ori指令
begin
aluop_o <= `EXE_OR_OP; //逻辑运算中的“或”运算
alusel_o <= `EXE_RES_LOGIC; //逻辑运算
wd_o <= inst_i[20:16]; //目的寄存器地址
wreg_o <= `WriteEnable; //ORI指令要将运算结果写入目的寄存器,故写寄存器使能有效
instvalid <= `InstValid; //有效指令
reg1_read_o <= `ReadEnable; //ORI指令需要读第一个寄存器的值
reg2_read_o <= `ReadDisable; //ORI指令无需读第二个寄存器的值
imm <= {16'h0,inst_i[15:0]}; //立即数扩展为32bit
end
default:
begin
end
endcase
end
end
/***************************************确定源操作数1*****************************************/
always @(*)
begin
if(rst == `RstEnable) //复位有效
reg1_o <= `ZeroWord;
else if(reg1_read_o ==`ReadEnable) //读使能
reg1_o <= reg1_data_i;
else if(reg1_read_o ==`ReadDisable) //读无效时,将立即数放到操作数中,传给执行部分
reg1_o <= imm;
else
reg1_o <= `ZeroWord;
end
/***************************************确定源操作数2*****************************************/
always @(*)
begin
if(rst == `RstEnable)
reg2_o <= `ZeroWord;
else if(reg2_read_o ==`ReadEnable)
reg2_o <= reg2_data_i;
else if(reg2_read_o ==`ReadDisable)
reg2_o <= imm;
else
reg2_o <= `ZeroWord;
end
endmodule
/****************************************译码-执行模块***********************************************/
/***********************作用:在时钟上升沿,将译码模块输出传递给执行模块*****************************/
`include "defines.v"
module id_ex( input wire clk,
input wire rst,
input wire[`AluOpBus] id_aluop, //具体运算:或、与...
input wire[`AluSelBus] id_alusel, //运算类型:算数、逻辑、移位...
input wire[`RegBus] id_reg1, //操作数1
input wire[`RegBus] id_reg2, //操作数2
input wire[`RegAddrBus] id_wd, //写入目的寄存器的地址
input wire id_wreg, //写目的寄存器使能
output reg[`AluOpBus] ex_aluop_o, //具体运算:或、与...
output reg[`AluSelBus] ex_alusel_o, //运算类型:算数、逻辑、移位...
output reg[`RegBus] ex_reg1_o, //操作数1
output reg[`RegBus] ex_reg2_o, //操作数2
output reg[`RegAddrBus] ex_wd_o, //写入目的寄存器的地址
output reg ex_wreg_o //写目的寄存器使能
);
always @(posedge clk)
begin
if(rst == `RstEnable)
begin
ex_aluop_o <= `EXE_NOP_OP; //具体运算:或、与...
ex_alusel_o <= `EXE_RES_NOP; //运算类型:算数、逻辑、移位...
ex_reg1_o <= `ZeroWord; //操作数1
ex_reg2_o <= `ZeroWord; //操作数2
ex_wd_o <= `NOPRegAddr;
ex_wreg_o <= `WriteDisable;
end
else
begin
ex_aluop_o <= id_aluop; //具体运算:或、与...
ex_alusel_o <= id_alusel; //运算类型:算数、逻辑、移位...
ex_reg1_o <= id_reg1; //操作数1
ex_reg2_o <= id_reg2; //操作数2
ex_wd_o <= id_wd;
ex_wreg_o <= 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, //操作数1
input wire[`RegBus] reg2_i, //操作数2
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; //保存逻辑运算的结果
/******************根据aluop_i指示的运算子类型进行运算,结果保存至logicout*****************/
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
end
end
/*****************根据alusel_i指示的运算子类型进行运算,将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
endmodule
/******************************************执行-访存模块*********************************************/
/***********************作用:在时钟上升沿,将执行模块输出传递给访存模块*****************************/
`include "defines.v"
module ex_mem( input wire clk,
input wire rst,
input wire[`RegAddrBus] ex_wd_i,
input wire ex_wreg_i,
input wire[`RegBus] ex_wdata_i,
output reg[`RegAddrBus] mem_wd_o,
output reg mem_wreg_o,
output reg[`RegBus] mem_wdata_o
);
always @(posedge clk)
begin
if(rst == `RstEnable) //复位
begin
mem_wd_o <= `NOPRegAddr;
mem_wreg_o <= `WriteDisable;
mem_wdata_o <= `ZeroWord;
end
else //传递
begin
mem_wd_o <= ex_wd_i;
mem_wreg_o <= ex_wreg_i;
mem_wdata_o <= ex_wdata_i;
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
/******************************************访存-回写模块*********************************************/
/***********************作用:在时钟上升沿,将访存模块输出传递给回写模块*****************************/
`include "defines.v"
module mem_wb( input wire clk,
input wire rst,
input wire[`RegAddrBus] mem_wd_i,
input wire mem_wreg_i,
input wire[`RegBus] mem_wdata_i,
output reg[`RegAddrBus] wb_wd_o,
output reg wb_wreg_o,
output reg[`RegBus] wb_wdata_o
);
always @(posedge clk)
begin
if(rst == `RstEnable) //复位
begin
wb_wd_o <= `NOPRegAddr;
wb_wreg_o <= `WriteDisable;
wb_wdata_o <= `ZeroWord;
end
else //传递
begin
wb_wd_o <= mem_wd_i;
wb_wreg_o <= mem_wreg_i;
wb_wdata_o <= mem_wdata_i;
end
end
endmodule
/*************************************MIPS五级流水线顶层*****************************************/
`include "defines.v"
`include "pc_reg.v"
`include "if_id.v"
`include "id.v"
`include "regfile.v"
`include "ex.v"
`include "ex_mem.v"
`include "mem.v"
`include "mem_wb.v"
module openmips( input wire rst,
input wire clk,
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;
/*** 译码模块输出 && 译码-执行模块输入 ***/
wire[`AluOpBus] id_aluop_o; //具体运算:或、与...
wire[`AluSelBus] id_alusel_o; //运算类型:算数、逻辑、移位...
wire[`RegBus] id_reg1_o; //操作数1
wire[`RegBus] id_reg2_o; //操作数2
wire[`RegAddrBus] id_wd_o; //写入目的寄存器的地址
wire id_wreg_o; //写目的寄存器使能
/*** 译码-执行模块输出 && 执行模块输入 ***/
wire[`AluOpBus] ex_aluop_i;
wire[`AluSelBus] ex_alusel_i;
wire[`RegBus] ex_reg1_i;
wire[`RegBus] ex_reg2_i;
wire[`RegAddrBus] ex_wd_i;
wire ex_wreg_i;
/*** 执行模块输出 && 执行-访存模块输入 ***/
wire[`RegAddrBus] ex_wd_o;
wire ex_wreg_o;
wire[`RegBus] ex_wdata_o;
/*** 执行-访存模块输出 && 访存模块输入 ***/
wire[`RegAddrBus] mem_wd_i;
wire mem_wreg_i;
wire[`RegBus] mem_wdata_i;
/*** 访存模块输出 && 回写模块输入 ***/
wire[`RegAddrBus] mem_wd_o;
wire mem_wreg_o;
wire[`RegBus] mem_wdata_o;
/*** 回写模块输出 && 寄存器模块输入 ***/
wire[`RegAddrBus] wb_wd_i;
wire wb_wreg_i;
wire[`RegBus] wb_wdata_i;
/*** 连接译码模块与通用寄存器模块 ***/
wire reg1_read;
wire[`RegAddrBus] reg1_addr;
wire[`RegBus] reg1_data; //读端口1
wire reg2_read;
wire[`RegAddrBus] reg2_addr;
wire[`RegBus] reg2_data; //读端口2
/***********************************各模块实例化************************************/
//实例化pc_reg模块
pc_reg pc_reg0( .rst(rst), .clk(clk), .pc(pc), .ce(rom_ce_o) );
assign rom_addr_o = pc; //指令存储器的输入地址就是pc
//实例化regfile模块
regfile regfile0( .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), //读端口1
.re2(reg2_read), .raddr2(reg2_addr), .rdata2(reg2_data) //读端口2
);
//实例化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), //寄存器堆中寄存器1数据
.reg2_data_i(reg2_data), //寄存器堆中寄存器2数据
//输出到regfile的信息
.reg1_read_o(reg1_read),
.reg2_read_o(reg2_read),
.reg1_addr_o(reg1_addr),
.reg2_addr_o(reg2_addr),
//送到执行阶段的信息
.aluop_o(id_aluop_o), //具体运算:或、与...
.alusel_o(id_alusel_o), //运算类型:算数、逻辑、移位...
.reg1_o(id_reg1_o), //操作数1
.reg2_o(id_reg2_o), //操作数2
.wd_o(id_wd_o), //写入目的寄存器的地址
.wreg_o(id_wreg_o) //写目的寄存器使能
);
id_ex id_ex0 ( .clk(clk),
.rst(rst),
.id_aluop(id_aluop_o), //具体运算:或、与...
.id_alusel(id_alusel_o),//运算类型:算数、逻辑、移位...
.id_reg1(id_reg1_o), //操作数1
.id_reg2(id_reg2_o), //操作数2
.id_wd(id_wd_o), //写入目的寄存器的地址
.id_wreg(id_wreg_o), //写目的寄存器使能
.ex_aluop_o(ex_aluop_i), //具体运算:或、与...
.ex_alusel_o(ex_alusel_i), //运算类型:算数、逻辑、移位...
.ex_reg1_o(ex_reg1_i), //操作数1
.ex_reg2_o(ex_reg2_i), //操作数2
.ex_wd_o(ex_wd_i), //写入目的寄存器的地址
.ex_wreg_o(ex_wreg_i) //写目的寄存器使能
);
ex ex0( .rst(rst), //复位
.aluop_i(ex_aluop_i), //具体运算:或、与...
.alusel_i(ex_alusel_i),//运算类型:算数、逻辑、移位...
.reg1_i(ex_reg1_i), //操作数1
.reg2_i(ex_reg2_i), //操作数2
.wd_i(ex_wd_i), //写入目的寄存器的地址
.wreg_i(ex_wreg_i), //写目的寄存器使能
.wd_o(ex_wd_o),
.wreg_o(ex_wreg_o),
.wdata_o(ex_wdata_o)
);
ex_mem ex_mem0( .clk(clk),
.rst(rst),
.ex_wd_i(ex_wd_o),
.ex_wreg_i(ex_wreg_o),
.ex_wdata_i(ex_wdata_o),
.mem_wd_o(mem_wd_i),
.mem_wreg_o(mem_wreg_i),
.mem_wdata_o(mem_wdata_i)
);
mem mem0( .rst(rst),
.wd_i(mem_wd_i),
.wreg_i(mem_wreg_i),
.wdata_i(mem_wdata_i),
.wd_o(mem_wd_o),
.wreg_o(mem_wreg_o),
.wdata_o(mem_wdata_o)
);
mem_wb mem_wb0( .clk(clk),
.rst(rst),
.mem_wd_i(mem_wd_o),
.mem_wreg_i(mem_wreg_o),
.mem_wdata_i(mem_wdata_o),
.wb_wd_o(wb_wd_i),
.wb_wreg_o(wb_wreg_i),
.wb_wdata_o(wb_wdata_i)
);
endmodule
编译: mips-sde-elf-as -mips32 inst_rom.S -o inst_rom.o
链接: mips-sde-elf-ld -T ram.ld inst_rom.o -o inst_rom.om
bin: mips-sde-elf-objcopy -O binary inst_rom.om inst_rom.bin
格式转化: .Bin2Mem.exe -f inst_rom.bin -o inst_rom.data