验证过程

实现ori指令->建立最小SOPC->验证ori指令是否正确

ori指令说明

ori是进行逻辑“或”的运算指令

ori指令的指令码是6’b001101。处理器发现正在处理的指令的高6bit是6’b001101时,说明当时处理的指令是ori指令
根据格式可以得出这是一个I型指令,
指令的用法为ori rs,rt,immediate
作用为将指令中的16位立即数immediate进行无符号扩展至32位,然后与索引为rs的通用寄存器的值进行逻辑“或”运算,运算结果保存在索引为rt的通用寄存器中。

符号扩展和无符号扩展

符号扩展

将n位立即数的最高位复制到扩展后的32位数据的高(32-n)位

无符号扩展

将扩展后的32位数据的高(32-n)位都置为0

举例

以指令中16位立即数扩展位32位为例(加上一个知识点16进制转为2进制)

通用寄存器

MIPS指令集架构中定义了32个通用寄存器$0-$31,OpenMIPS实现了这32个通用寄存器,对于某一个通用寄存器只要给出相应索引,这个索引占用5bit,ori指令中rs,rt就是通用寄存器的索引
当rs=5’b00011,表示通用寄存器$3

流水线结构的建立

流水线的简单模型

寄存器按照给定的时间脉冲来进行时序同步操作,使时序逻辑电路具有记忆功能
组合逻辑电路由逻辑门组成,提供电路所有的逻辑功能,如果寄存器的输出端和输入端存在环路,这样的电路称为状态机,
如果寄存器之间有连接而无上述环路,这样的电路结构称为“流水线”
流水线结构中,信号在寄存器中传递,每传递到一级都会引起相应组合逻辑的变化,这种模型的抽象描述就是寄存器传输级

原始的OpenMIPS五级流水线结构

原始数据流图:


下图是为实现上述数据流图设计的五级流水线系统结构图(看不清)

一些宏定义

在OpenMIPS的实现过程中,为了提高代码的可读性和易懂性,使用了较多的宏,将所有文件定义在define.v中。
此处仅列举在ori指令编写中所需要的宏,随着OpenMIPS的功能不断完善,会有更多的宏添加进来,届时再说明及添加。

/************全局的宏定义***********/
`define RstEnable 1'b1
`define RstDisable 1'b0
`define ZeroWord 32'h00000000
`define WriteEnable 1'b1
`define WriteDisable 1'b0
`define ReadEnable 1'b1
`define ReadDisable 1'b0
`define AluOpBus 7:0
`define AluSelBus 2:0
`define InstValid 1'b0
`define InstInvalid 1'b1
`define Stop 1'b1
`define NoStop 1'b0
`define InDelaySlot 1'b1
`define NotInDelaySlot 1'b0
`define Branch 1'b1
`define NotBranch 1'b0
`define InterruptAssert 1'b1
`define InterruptNotAssert 1'b0
`define TrapAssert 1'b1
`define TrapNotAssert 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
`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 //ROM的实际大小128KB
`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

取指阶段的实现

取指阶段取出指令存储器中的指令,同时PC值递增,准备下一条指令,包括PC、IF/ID两个模块

PC模块

作用:给出指令地址

接口描述

对应文件

pc_reg.v

代码

`include "define.v"
//PC模块:给出指令地址
module pc_reg(
input wire clk,//时钟信号
input wire rst,//复位信号
output reg[`InstAddrBus]pc,//[31:0]要读取的指令地址,指令地址线宽度为32
output reg ce//指令存储器使能信号
);
always @(posedge clk)
beginif(rst==`RstEnable)//rst=1,复位信号有效,当输入rst为高电平时,复位信号有效begince<= `ChipDisable;//指令存储器禁用endelse //rst=0begince<=`ChipEnable;//复位结束后,指令存储器使能end
end
always @ (posedge clk)
beginif(ce==`ChipDisable)//指令存储器禁用beginpc<=32'h00000000;//指令存储器禁用时,PC为0endelsebeginpc<=pc+4'h4;//指令存储器使能时,PC的值每时钟周期加4end
end
endmodule

复位时,指令存储器禁用
其余时刻表示指令存储器使能
当指令存储器禁用时,PC为0
当指令存储器使用时,PC值在每时钟周期加4,表示下一条指令地址(一条指令32位,OpenMIPS按字节寻址,一条指令对应4字节,PC加4指向下一条地址)

IF/ID模块

作用:暂时保存取指阶段取得的指令,以及对应指令的地址,并在下一个时钟传递到译码阶段

接口描述

对应文件

if_id.v

代码

//IF/ID模块
`include "define.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) beginif (rst == `RstEnable) beginid_pc <= `ZeroWord;id_inst <= `ZeroWord;end else beginid_pc <= if_pc;id_inst <= if_inst;endendendmodule

IF/ID模块只是简单地将取指阶段的结果在每个时钟周期上升沿传递到译码阶段

至此,以上的框架图如下

译码阶段的实现

1:Regfile模块

实现了32个32位通用寄存器,可以同时进行两个寄存器的读操作和一个寄存器的写操作

接口描述

================================================================================================
分割线:隔了一个星期,我看不懂了,我累了

================================================================================================

分割线 一个学期过去了,我来继续补这个。

回写阶段实际是在Regfile模块中实现的。MEM/WB模块输出wb_wreg,wb_wd,wb_wdata连接到Regfile模块,分别连接到写使能端口we,写操作目的寄存器端口waddr,写入数据端口wdata,所以会将指令的运算结果写入目的寄存器,具体代码可以参考Regfile模块。

代码文件

refile.v

代码:

`include "define.v"
//回写阶段的实现实际上是在Regfile模块中实现的
//Regfile模块 寄存器堆
module regfile(
input wire clk,//时钟信号
input wire rst,//复位信号,高电平有效
//写端口
input wire we,//写使能信号
input wire[`RegAddrBus] waddr,//要写入的寄存器地址
input wire[`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//第二个读寄存器端口要输出的寄存器的值
);
/*******第一段: 定义32个32位寄存器*******/
/*******第二段:写操作******************/
/*******第三段:读端口1的读操作********/
/*******第四段:读端口2的读操作*******///第一段:定义32个32位寄存器
reg[`RegBus] regs[0:`RegNum-1];//第二段:实现写寄存器操作
always @ (posedge clk)
beginif(rst==`RstDisable)//rst==1,复位信号无效beginif((we==`WriteEnable)&&(waddr!=`RegNumLog2'h0))//写使能信号we有效 且 写操作目的寄存器不等于0的情况下begin//MIPS32架构规定 $0的值只能为0 所以不需要写入 regs[waddr]<=wdata;//将写输入数据保存到目的寄存器endend
end//第三段:读端口1的读操作 实现第一个读寄存器端口
always @(*)
begin if(rst==`RstEnable)//当复位信号有效时beginrdata1 <= `ZeroWord;//第一个读寄存器端口的输出始终为0endelse if(raddr1==`RegNumLog2'h0)//当复位信号无效时 如果读取的是$0 beginrdata1<=`ZeroWord;//直接给出0endelse if((raddr1==waddr)&&(we==`WriteEnable)&&(re1==`ReadEnable))//如果第一个读寄存器端口要读取的目标寄存器与要写入的目的寄存器是同一个beginrdata1<=wdata;//直接将要写入的值作为第一个寄存器端口的输出endelse if(re1==`ReadEnable)//上述情况都不满足时 beginrdata1<=regs[raddr1];//给出第一个读寄存器端口要读取的目标寄存器地址对应寄存器的值end else//当第一个寄存器端口不能使用时beginrdata1<=`ZeroWord;//直接输出0end
end
//第四段 读端口2的操作 实现第二个寄存器端口 具体过程和第三段相似 注意地址的变化
always @ (*)
beginif(rst==`RstEnable)beginrdata2<=`ZeroWord;endelse if(raddr2==`RegNumLog2'h0)beginrdata2<=`ZeroWord;endelse if((raddr2==waddr)&&(we==`WriteEnable)&&(re2==`ReadEnable))beginrdata2<=wdata;endelse if(re2==`ReadEnable)beginrdata2<=regs[raddr2];endelsebeginrdata2<=`ZeroWord;end
end
endmodule

2:ID模块

对指令进行译码,得到最终运算的类型、子类型、源操作数1、源操作数2、要写入的目的寄存器地址等信息,其中运算类型指的是逻辑运算、移位运算、算术运算等,子类型指的是更加详细的运算类型
比如:当运算类型是逻辑运算时,运算子类型可以是逻辑运算“或”“与”“异或”运算等。

接口描述

代码文件

id.v

代码

`include "define.v"
module id(input wire                                        rst,input wire[`InstAddrBus]           pc_i,input wire[`InstBus]          inst_i,input wire[`RegBus]           reg1_data_i,input wire[`RegBus]           reg2_data_i,//送到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,output reg[`RegBus]           reg2_o,output reg[`RegAddrBus]       wd_o,output reg                    wreg_o//处于执行阶段的指令的运算结果input wire ex_wreg_i,input wire[`RegBus] ex_wdata_i,input wire[`RegAddrBus] ex_wd_i,//处于访存阶段的指令的运算结果input wire mem_wreg_i,input wire[`RegBus] mem_wdata_i,input wire[`RegAddrBus] mem_wd_i
);wire[5:0] op = inst_i[31:26];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) beginaluop_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 beginaluop_o <= `EXE_NOP_OP;alusel_o <= `EXE_RES_NOP;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];reg2_addr_o <= inst_i[20:16];     imm <= `ZeroWord;          case (op)`EXE_ORI:         begin                        //ORI指令wreg_o <= `WriteEnable;        aluop_o <= `EXE_OR_OP;alusel_o <= `EXE_RES_LOGIC; reg1_read_o <= 1'b1;   reg2_read_o <= 1'b0;       imm <= {16'h0, inst_i[15:0]};      wd_o <= inst_i[20:16];instvalid <= `InstValid; end                              default:           beginendendcase       //case op         end       //ifend         //always/*给reg1_o赋值过程增加了两种情况
1:如果Regfile模块读端口1要读取的寄存器就是执行阶段要写的目的寄存器,那么直接把执行阶段的结果ex_wdata_i作为reg1_o的值
2:如果Regfile模块读端口1要读取的寄存器就是访存阶段要写的目的寄存器,那么直接把访存阶段的结果mem_wdata_i作为reg1_o的值*/always @ (*) beginif(rst == `RstEnable) beginreg1_o <= `ZeroWord;end else if(reg1_read_o == 1'b1) beginreg1_o <= reg1_data_i;end else if(reg1_read_o == 1'b0) beginreg1_o <= imm;end else if((reg1_read_o == 1'b1)&&(ex_wreg_i == 1'b1)&&(ex_wd_i == reg1_addr_o)) beginreg1_o <= ex_wdata_i;end else if((reg1_read_o == 1'b1)&&(mem_wreg_i == 1'b1)&&(mem_wd_i == reg1_addr_o)) beginreg1_o <= mem_wdata_i;end else beginreg1_o <= `ZeroWord;endendalways @ (*) beginif(rst == `RstEnable) beginreg2_o <= `ZeroWord;end else if(reg2_read_o == 1'b1) beginreg2_o <= reg2_data_i;end else if(reg2_read_o == 1'b0) beginreg2_o <= imm;end else if((reg2_read_o == 1'b1)&&(ex_wreg_i == 1'b1)&&(ex_wd_i == reg2_addr_o)) beginreg2_o <= ex_wdata_i;end else if((reg2_read_o == 1'b1)&&(mem_wreg_i == 1'b1)&&(mem_wd_i == reg2_addr_o)) beginreg2_o <= mem_wdata_i;end else beginreg2_o <= `ZeroWord;endendendmodule

3:id_ex模块

代码

`include "define.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,//执行阶段的指令要进行的运算的子类型
output reg[`AluSelBus]  ex_alusel,//执行阶段要进行的类型
output reg[`RegBus]  ex_reg1,//执行阶段的指令要进行运算的源操作数1
output reg[`RegBus]  ex_reg2,//执行阶段的指令要进行运算的源操作数2
output reg[`RegAddrBus]  ex_wd,//执行阶段的指令要写入的目的寄存器地址
output reg   ex_wreg //执行阶段的指令是否要写入的目的寄存器
);always @(posedge clk)
beginif(rst == `RstEnable)//复位有效beginex_aluop <= `EXE_NOP_OP;ex_alusel <= `EXE_RES_NOP;ex_reg1 <= `ZeroWord;ex_reg2 <= `ZeroWord;ex_wd <= `NOPRegAddr;ex_wreg <= `WriteDisable;end
else
beginex_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
//在时钟周期的上升沿,将译码阶段的结果传递到执行阶段

执行阶段的实现

1:ex模块

代码:

`include "define.v"
//ex.v 执行模块
module ex(
//译码阶段送到执行阶段的信息
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,
input wire rst,
//执行的结果
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o
);
//保存逻辑运算的结果
reg[`RegBus] logicout;
/*******************************************************************
**第一段:依据aluop_i指示的运算子类型进行运算,此处只有逻辑“或”运算**
*******************************************************************/
always @(*)
begin//1if(rst == `RstEnable)begin//2logicout <= `ZeroWord;end//2elsebegin//3case(aluop_i)//4`EXE_OR_OP:begin//5logicout <= reg1_i|reg2_i;end//5default:begin//6logicout <= `ZeroWord;end//6endcase//4end//3
end//1/******************************************************************
**第二段:依据alusel_i指示的运算类型,选择一个运算结果作为最终结果**
********************此处只有逻辑运算结果***************************/
always @ (*)
begin//10wd_o <= wd_i;//wd_o等于wd_i 为要写的目的寄存器地址wreg_o <= wreg_i;//wreg_o等于wreg_i 表示是否要写目的寄存器case(alusel_i)//9`EXE_RES_LOGIC:begin//7wdata_o <= logicout;//wdata_o中存放运算结果end//7default:begin//8wdata_o <= `ZeroWord;end//8endcase//9
end//10
endmodule

2:ex_mem模块

代码:

`include "define.v"
//ex_mem.v 将执行阶段取得的运算结果 在下一个时钟传递到流水线访存阶段
module ex_mem(
input wire clk,//时钟信号
input wire rst,//复位信号//来自执行阶段的信息input wire[`RegAddrBus] ex_wd,//执行阶段指令执行后要写入的寄存器地址
input wire ex_wreg,//执行阶段指令执行后是否要写入的目的寄存器
inout wire[`RegBus] ex_wdata,//执行阶段的指令执行后要写入的目的寄存器的值//送到访存阶段的信息
output reg[`RegAddrBus] mem_wd,//访存阶段的指令要写入的目的寄存器的地址
output reg mem_wreg,//访存阶段的指令是否有要写入的目的寄存器
output reg[`RegBus] mem_wdata//访存阶段的指令要写入的目的寄存器的值
);
always @ (posedge clk)//在时钟上升沿将执行阶段的结果传递到访存阶段
beginif(rst==`RstEnable)beginmem_wd <= `NOPRegAddr;mem_wreg <= `WriteDisable;mem_wdata <= `ZeroWord;endelsebeginmem_wd <= ex_wd;mem_wreg <= ex_wreg;mem_wdata <= ex_wdata;end
endendmodule

访存阶段的实现

1:mem模块

`include "define.v"
//mem.v 访存
//将输入的执行阶段的结果直接作为输出
module mem(
input wire rst,
input wire clk,
//来自执行阶段的信息
input wire[`RegAddrBus] wd_i,
input wire wreg_i,
input wire wdata_i,
//访存阶段的结果
output reg[`RegAddrBus] wd_o,
output reg wreg_o,
output reg[`RegBus] wdata_o
);always @ (posedge clk)
beginif(rst == `RstEnable)beginwd_o <= `NOPRegAddr;wreg_o <= `WriteEnable;wdata_o <= `ZeroWord;
end
else
beginwd_o <= wd_i;wreg_o <= wreg_i;wdata_o <= wdata_i;
end
end
endmodule

2:mem_wb模块

`include "define.v"
//mem_wb.v
//将访存阶段的运算结果在下一个时钟传递到回写阶段
module mem_wb(
input wire clk,
input wire rst,
//访存阶段的结果
input wire[`RegAddrBus] mem_wd,
input wire[`RegBus] mem_wdata,
input wire mem_wreg,
//送到回写阶段信息
output reg[`RegAddrBus] wb_wd,
output reg wb_wreg,
output reg[`RegBus] wb_wdata
);always @ (posedge clk)
beginif(rst==`RstEnable)beginwb_wd <= `NOPRegAddr;wb_wreg <= `WriteDisable;wb_wdata <= `ZeroWord;endelsebeginwb_wd <= mem_wd;wb_wreg <= mem_wreg;wb_wdata <= mem_wdata;end
end
endmodule
//mem_wb代码与mem模块代码相似,将输入信号传递到对应的输出端口,mem_wb是时序电路
//在时钟上升沿才发生信号传递,而mem模块中的是组合逻辑电路

回写阶段的实现

参考regfile模块。

顶层模块OpenMIPS的实现

该模块是对上面实现的流水线各个阶段的模块进行例化、连接。

接口描述

序号 接口名 宽度(bit) 输入/输出 作用
1 rst 1 输入 复位信号
2 clk 1 输入 时钟信号
3 rom_data_i 32 输入 从指令存储器取得的指令
4 rom_addr_o 32 输出 输出到指令存储器的地址
5 rom_ce_o 1 输出 指令存储器使能信号

代码

`include "define.v"
//openmips.v
module openmips(
input wire clk,
input wire rst,
input wire[`RegBus] rom_data_i,
input wire[`RegBus] rom_addr_o,
output wire rom_ce_o
);
//连接IF/ID模块与译码阶段ID模块的变量
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;//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),.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

验证OpenMIPS实现效果

指令存储器ROM的实现

主要验证两个部分
1:流水线
2:ori指令
在验证之前,首先实现指令存储器,以便OpenMIPS从中读取指令。指令存储器ROM模块是只读的。

接口描述

序号 接口名 宽度(bit) 输入/输出 作用
1 ce 1 输入 使能信号
2 addr 32 输入 要读取的指令地址
3 inst 32 输出 读出的指令

代码

inst_rom.v

`include "define.v"
//inst_rom.v
module inst_rom(
input wire ce,
input wire[`InstAddrBus] addr,
output reg[`InstBus] inst
);
//定义一个数组,大小是InstMemNum,元素宽度是InstBus
reg[`InstBus] inst_mem[0:`InstMemNum-1];
//使用文件inst_rom.data 初始化指令存储器
initial $readmemh ("D:\cpu\AsmTest\inst_rom.data",inst_mem);
always @ (*)
begin
if(ce == `ChipDisable)//当复位信号无效时,依据输入的地址,给出指令存储器ROM中对应的元素
begininst <= `ZeroWord;
end
else begininst <= inst_mem[addr[`InstMemNumLog2+1:2]];
end
end
endmodule

(1)初始化指令存储器时,使用initial语句,initial语句只执行一次,通常用于仿真模块中对激励向量的描述,或用于给变量赋初值,是面向模拟仿真的过程语句。
(2)初始化指令存储器时,使用了系统函数$readmemh,从inst_rom_data文件中读取数据,初始化数组inst_mem。
inst_rom.data是一个文本文件,里面存储的是指令,每行存储一条32位宽度的指令,用16进制表示, $readmemh会将inst_rom.data中的数据依次填写到inst_mem数组中。
(3)OpenMIPS按字节寻址,此处定义的指令存储器的每个地址是一个32bit的字,所以将OpenMIPS给出的指令地址除以4再使用。
e.g:读取0xC处的指令 C在十进制中表示12 将12/4=3 实际对应的就是ROM的inst_mem[3]。
除以4也就是将指令地址右移2位,所以在读取的时候给出地址就是addr[`InstMemNumLog2+1:2],InstMemNumLog2指的是指令存储器的实际地址宽度。比如如果inst_mem有1024个元素,InstMemNum=1024 InstMemNumLog2=10 则实际地址宽度为10。
我不理解

最小SOPC的实现

为了验证建立一个SOPC,其中只包含OpenMIPS、指令存储器ROM,所以是一个最小SOPC。OpenMIPS从ROM中读取指令。指令进入OpenMIPS开始执行。

代码:

`include "define.v"
//openmips_min_sopc.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(.ce(rom_ce), .inst(inst)
);
endmodule

测试

`include "define.v"
//建立TestBench文件
//时间单位是1ns,精度是1ps
`timescale 1ns/1ps
module openmips_min_sopc_tb();
reg CLOCK_50;
reg rst;
//每隔10ns,CLOCK_50信号翻转一次,所以下一个时钟周期是20ns,对应50MHz
initial beginCLOCK_50 = 1'b0;forever #10 CLOCK_50 = ~CLOCK_50;
end
//最初时刻,复位信号有效,在第195ns复位信号无效 最小SOPC开始运行
//运行1000ns后,暂停仿真
initial beginrst = `RstEnable;#195 rst = `RstDisable;#1000 $stop;
end
//例化最小SOPC
openmips_min_sopc openmips_min_sopc0(
.clk(CLOCK_50),
.rst(rst)
);
endmodule

写在最后

1:这些代码全部来自《自己动手写cpu》这本书。
2:这些代码在编译上没有问题(id模块加了数据前推,openmips模块没有加),但是没有通过仿真,大概是我还没有弄透怎么仿真,但是我又能看懂书上和别人正确的仿真。代码写一天半,仿真弄两天也没弄出来,拿书上的源代码还是不行。只能等之后慢慢试了。
3:第一次看这一部分是九月份,这个博客开头也是九月份。。。一晃已经过去四个多月了,该忘的不该忘的全忘了,再看着一部分比之前又明白了一点,许是我太笨了,还是没有完完全全记住每一个端口,每一个信号,时间原因只能通过看后面的章节再慢慢理解了。

【自己动手写CPU】第一条指令ori的实现相关推荐

  1. 自己动手写CPU(1)五级流水线及CPU第一条指令ori

    自己动手写CPU(1)五级流水线及CPU第一条指令ori 动机 不知为何研一的自由时间突然多起来,可能人一闲下来就容易焦虑吧,hhhhhh.正好之前看到一本<自己动手写CPU>,就按照此书 ...

  2. verilog实现多周期处理器之——(二)第一条指令ori的实现

    本博文希望对于OpenMIPS第一条指令ori加以实现并总结.会加入一些基本的理论以及博主的学习记录. 流水与五级流水 什么是流水:拆分,并行.将多条指令的执行相互重叠起来.就构成了流水,这样充分利用 ...

  3. 自己动手写CPU(4)移动操作指令的实现

    自己动手写CPU(4)移动操作指令的实现 指令说明 MIPS32指令集架构中定义的移动操作指令共有6条: movn.movz.mfhi.mthi.mflo.mtlo,后4条指令涉及对特殊寄存器HI.L ...

  4. 第4章:第一条指令ori的实现

    4.1 ori指令说明 ori指令格式: 索引为rs的通用寄存器的值与扩展后的立即数进行or运算,结果存储到rt (1) 符号扩展 (2)通用寄存器 32个通用寄存器,使用某个通用寄存器只需要给出相应 ...

  5. 【自己动手写CPU】除法指令的实现

    说明 除法指令有两条:div,divu SPECIAL=000000 31-26 25-21 20-16 15-11 10-6 5-0 useage function SPECIAL rs rt 00 ...

  6. 自己动手写CPU——第一篇

    1 设计目标 从本章开始将一步一步实现教学版 openMIPS处理器.首先介绍系统的设计目标,其中详细说明了openMIPS处理器计划实现的5级流水线. 1.1 设计目标 openmips 设计的目标 ...

  7. 自己动手写CPU(2)第一条ori指令

    本博客内容基于<自己动手写CPU>这本书 上一篇文章介绍了一下流水线思想.设计流程等,下面我们可以实操一下实现第一条ori指令. 其实实现一条ori指令不难,我目前对这一条指令的理解简单来 ...

  8. 自己动手写CPU(5)简单算术操作指令实现_1

    自己动手写CPU(5)简单算数操作指令实现_1 指令介绍 MIPS32指令集架构定义的所有算术操作指令,共有21条 共有三类,分别是: 简单算术指令 乘累加.乘累减指令 除法指令 算术指令操作介绍 一 ...

  9. cpu设计和实现(流水线上的第一条指令)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 读书的时候,<计算机组成原理>也看了,<计算机体系结构>也学了,老师也给 ...

最新文章

  1. python websocket 客户端_aiohttp Websocket客户端和HTTP
  2. 150名打工人被AI判定效率低遭开除,“属实是人工智能,能治工人“
  3. 常见Shell命令用法总结
  4. 魔幻的“净醛产品”:宜家被罚“亡羊”,欧派用视频“补牢”?
  5. 三维重建 几何方法 深度学习_Occupancy Networks:基于学习函数空间的三维重建表示方法...
  6. python 程序停止打印日志_停止 Spring Boot 服务的几种优雅姿势
  7. 出门问问CEO李志飞:当语音成为基石技术,消费场景如何进一步落地?
  8. iOS开发日记49-详解定位CLLocation
  9. 手把手教你学51单片机_第 一、二章
  10. ISIS SPF算法简单过程
  11. 怎样查询服务器中标信息,太极中标云服务器
  12. 【精华】PB函数大全
  13. pytorch版本下的yolov3训练实现火焰检测
  14. python集合如何去除重复数据_Python 迭代删除重复项,集合删除重复项
  15. 给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。 请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。如下实例。示例 :输入: 1->2->3->4-
  16. python抓取网页数据并截图_网络爬虫-使用Python抓取网页数据
  17. 有关Linux内核版本命名规则
  18. 股票、指数、快照、逐笔... 不同行情数据源的实时关联分析应用
  19. 台达协议转换网关WTGNet-DVP
  20. 巴塞尔枷锁与评级霸权制约中国

热门文章

  1. html5使用 callapp-lib 唤起app (教程)+ 踩坑系列
  2. UVC摄像头驱动程序框架
  3. cass参考手册_cass9.0 参 考 手 册.doc
  4. linux 汇编 perf,性能分析利器之perf浅析
  5. GLSL 详解(基础篇)
  6. Raspberry Pi下跑aircrack和reaver破解路由器PIN码
  7. Java设计模式之 抽象工厂模式实验报告书
  8. 送给社会格斗场上的暗夜孤星
  9. JKTD-1000型铁电材料测试仪
  10. Hilt 介绍 | MAD Skills