当前位置:   article > 正文

一、OpenMIPS指令集CPU的ori指令的实现

一、OpenMIPS指令集CPU的ori指令的实现

前言

根据“自己动手写CPU”这本书学习,自己动手实现一个MIPS指令集的CPU。
本文章实现了一个ori指令即“或”操作的五级流水线,后续会持续添加其他指令完善此CPU。

文章作为学习笔记持续更新,源代码也在github上持续更新
项目源码https://github.com/yizhixiaohuihui/OpenMIPS.git

一、设计实现ori指令的OpenMIPS处理器

实现ori指令的OpenMIPS五级流水线结构图
在这里插入图片描述

说明:

1.1、取指阶段:取出指令存储器中的指令,同时PC值递增,准备取下一条指令

  1. PC模块(pc_reg.v):给出指令地址
`include "../rtl/defines.v"

module pc_reg(
	input	wire							clk, 
	input wire								rst,
	output reg[`InstAddrBus]		pc,	// 要读取的指令地址
	output reg                    			ce // ָ指令存储器使能信号
);

	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;
		end 
		else begin
	 		pc <= pc + 4'h4;					// pc+4指向下一条指令地址(一条指令32位对应4字节)
		end
	end
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  1. IF/ID模块(if_id.v):暂时保存取指阶段取得的指令
`include "../rtl/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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

1.2、译码阶段:对取到的指令进行译码 -> 给出要进行的运算类型,以及参与运算的操作数

  1. Regfile模块(regfile.v):实现32个32位通用整数寄存器,可以同时进行两个寄存器的读操作和一个寄存器的写操作
`include "../rtl/defines.v"

module regfile(

	input	wire									clk,
	input wire										rst,

	// write port
	input wire										we,
	input wire[`RegAddrBus]					waddr,
	input wire[`RegBus]						   wdata,
	
	// read port 1
	input wire										re1,
	input wire[`RegAddrBus]			  		raddr1,
	output reg[`RegBus]           			  rdata1,
	
	// read port 2
	input wire										re2,
	input wire[`RegAddrBus]			  		raddr2,
	output reg[`RegBus]           			  rdata2
	
);
	// 1. define 32's 32bits registers
	reg[`RegBus]  regs[0:`RegNum-1];

	// 2. write operation
	always @ (posedge clk) begin
		if (rst == `RstDisable) begin
			// we enable and write operation destination register != 0
			if((we == `WriteEnable) && (waddr != `RegNumLog2'h0)) begin
				regs[waddr] <= wdata;
			end
		end
	end
	
	// Notice: read operation is Combinatorial logic
	// 3. read port1's read operation
	always @ (*) begin
		if(rst == `RstEnable) begin
			  rdata1 <= `ZeroWord;
	  	end 
		else if(raddr1 == `RegNumLog2'h0) begin
	  		rdata1 <= `ZeroWord;
	  	end 
		// if read port1 want read register is same as the register to write
	  	else if((raddr1 == waddr) && (we == `WriteEnable) && (re1 == `ReadEnable)) begin
	  	  rdata1 <= wdata;
	  	end 
		else if(re1 == `ReadEnable) begin
	      rdata1 <= regs[raddr1];
	  	end 
		// if read port1 can't be used
		else begin
	      rdata1 <= `ZeroWord;
	  	end
	end

	// 4. read port2's read operation
	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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  1. ID模块(id.v):对指令进行译码,得到最终运算的类型、子类型、源操作数1、源操作数2、要写入的目的寄存器地址等信息
`include "../rtl/defines.v"

module id(

	input wire								 rst,
	input wire[`InstAddrBus]		  pc_i,
	input wire[`InstBus]          		 inst_i,

	// readed regfile's data
	input wire[`RegBus]           		reg1_data_i,
	input wire[`RegBus]           		reg2_data_i,

	// ouput to Regfile's message
	output reg                    			reg1_read_o,	// Regfile reg1's ReadEnable
	output reg                    			reg2_read_o,    // Regfile reg2's ReadEnable
	output reg[`RegAddrBus]       	reg1_addr_o,
	output reg[`RegAddrBus]       	reg2_addr_o, 	      
	
	// sent Execution stage message
	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
);
	// instructions code
    wire[5:0] op = inst_i[31:26]; // ori: judge 26-31bit can judge ori instruction
    wire[4:0] op2 = inst_i[10:6];
    wire[5:0] op3 = inst_i[5:0];
    wire[4:0] op4 = inst_i[20:16];

	// store immediate num that execute instructions need
    reg[`RegBus]	imm;

	// instruction is valid or not
    reg instvalid;
  
	// 1. decode instructions
	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];
			wreg_o <= `WriteDisable;
			instvalid <= `InstInvalid;	   
			reg1_read_o <= 1'b0;
			reg2_read_o <= 1'b0;
			reg1_addr_o <= inst_i[25:21];	// rs register's address
			reg2_addr_o <= inst_i[20:16];	// rt register's address
			imm <= `ZeroWord;			
		    case (op)
		  		`EXE_ORI:			begin   // judge is ori ins 
		  			wreg_o <= `WriteEnable;		// ori need write in destination register
					aluop_o <= `EXE_OR_OP;	// aiuop is or operation
		  			alusel_o <= `EXE_RES_LOGIC; // logic operation
					reg1_read_o <= 1'b1;	// only need read rs
					reg2_read_o <= 1'b0;	// immediate num not need read from reg
					imm <= {16'h0, inst_i[15:0]}; // extend immediate num to unsinged 32 bits		
					wd_o <= inst_i[20:16]; // rt: destination register's address to write 
					instvalid <= `InstValid;	
		  		end 							 
		    	default:			begin
		    	end
		  endcase		  //case op			
		end       //if
	end         //always
	
	// 2. choose source num2
	always @ (*) begin
		if(rst == `RstEnable) begin
			reg1_o <= `ZeroWord;
	  	end 
		else if(reg1_read_o == 1'b1) begin
	  		reg1_o <= reg1_data_i; // rs
	  	end 
		else if(reg1_read_o == 1'b0) begin
	  		reg1_o <= imm;
	  	end 
		else begin
	    	reg1_o <= `ZeroWord;
	  	end
	end
	
	// 3. choose source num2 
	always @ (*) begin
		if(rst == `RstEnable) begin
			reg2_o <= `ZeroWord;
	  	end 
		else if(reg2_read_o == 1'b1) begin
	  		reg2_o <= reg2_data_i;	// rt
	  	end 
		else if(reg2_read_o == 1'b0) begin
	  		reg2_o <= imm;
	  	end 
		else begin
	    	reg2_o <= `ZeroWord;
	  	end
	end

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  1. ID/EX模块(id_ex.v):将ID模块译码阶段取得的结果在下个时钟传递到流水线执行阶段
`include "../rtl/defines.v"

module id_ex(

	input	wire						clk,
	input wire							rst,

	
	// message from decodeStage
	input wire[`AluOpBus]        id_aluop,
	input wire[`AluSelBus]       id_alusel,
	input wire[`RegBus]           id_reg1,
	input wire[`RegBus]           id_reg2,
	input wire[`RegAddrBus]     id_wd,
	input wire                    		id_wreg,	
	
	// message sent to executeStage
	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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

1.3、执行阶段:依据译码阶段的结果,对源操作数1、源操作数2、进行指定的运算

  1. EX模块(ex.v):从ID/EX模块得到运算的类型、子类型、源操作数1、源操作数2、要写入的目的寄存器地址,并依据这些数据进行计算
`include "../rtl/defines.v"

module ex(

	input wire										rst,
	
	// message rom decodeStage
	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,

	// execute result
	output reg[`RegAddrBus]     wd_o,
	output reg                    		wreg_o,
	output reg[`RegBus]			   wdata_o
	
);
	// save logic op result
	reg[`RegBus] logicout;

	
	// 1. according aluop_i to operate
	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      


	// 2. according alusel_i to choose an op result as last result
	always @ (*) begin
		wd_o <= wd_i;	// detinationReg's address need write 
		wreg_o <= wreg_i; // write reg enable
		case ( alusel_i ) 
			`EXE_RES_LOGIC:		begin
				wdata_o <= logicout; // save op result
			end
			default:					begin
				wdata_o <= `ZeroWord;
			end
		endcase
	end	

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  1. EX/MEM模块(ex_mem.v):将执行阶段取得的运算结果在下个时钟传递到流水线访存阶段
`include "../rtl/defines.v"

module ex_mem(

	input wire						  	  clk,
	input wire							  rst,
	
	
	// message from executeStage
	input wire[`RegAddrBus]       ex_wd,
	input wire                    		  ex_wreg,
	input wire[`RegBus]				ex_wdata, 	
	
	// message sent to accessStage
	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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

1.4、访存阶段:由于ori指令不需要访问数据存储器,所以在访存阶段不做任何事,只是简单地将执行阶段的结果向写回阶段传递

  1. MEM模块(mem.v):将输入的执行阶段的结果直接作为输出
`include "../rtl/defines.v"

module mem(
	input wire							rst,
	
	// message from executeStage
	input wire[`RegAddrBus]     wd_i,
	input wire                    		wreg_i,
	input wire[`RegBus]			   wdata_i,
	
	// result in accessStage
	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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  1. MEM/WB模块(mem_wb.v):将访存阶段的运算结果,在下一个时钟传递到回写阶段
`include "../rtl/defines.v"

module mem_wb(

	input	wire						clk,
	input wire							rst,
	
	// accessStage result 
	input wire[`RegAddrBus]     mem_wd,
	input wire                    		mem_wreg,
	input wire[`RegBus]			   mem_wdata,

	// message sent to writeBackStage
	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    //if
	end      //always
			

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

1.5、回写阶段:将指令的运算结果写入目的寄存器

  1. Regfile模块(regfile.v):将指令的运算结果写入目的寄存器

ori指令格式
在这里插入图片描述

地址为rs的寄存器的值 = 立即数 or 地址为rt的寄存器的值
目的操作数 = 源操作数1 || 源操作数2

1.6、顶层模块

在这里插入图片描述

`include "../rtl/defines.v"

module openmips(

	input wire clk,
	input wire rst,
	
	input wire[`RegBus] rom_data_i, // instruction get from instructionRomemary
	output wire[`RegBus] rom_addr_o, // address ouput to instructionRomemary
	output wire rom_ce_o // instructionRomemary enable
	
);
	// PC -> IF/ID
	wire[`InstAddrBus] pc;

	// IF/ID -> ID
	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 -> 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_reg0(
		.clk(clk),
		.rst(rst),
		.pc(pc),
		.ce(rom_ce_o)	
			
	);
	
  assign rom_addr_o = pc;

	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 id0(
		.rst(rst),
		.pc_i(id_pc_i),
		.inst_i(id_inst_i),

		.reg1_data_i(reg1_data),
		.reg2_data_i(reg2_data),

		.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),
		.reg2_o(id_reg2_o),
		.wd_o(id_wd_o),
		.wreg_o(id_wreg_o)
	);

	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_ex0(
		.clk(clk),
		.rst(rst),
		
		.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_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 ex0(
		.rst(rst),
	
		.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),
	  
		.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(ex_wd_o),
		.ex_wreg(ex_wreg_o),
		.ex_wdata(ex_wdata_o),
	
		.mem_wd(mem_wd_i),
		.mem_wreg(mem_wreg_i),
		.mem_wdata(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(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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198

二、验证设计正确性

2.1 指令存储器ROM

在这里插入图片描述

  1. 在初始化指令存储器时使用了initial过程语句,不能被综合工具支持,若想被综合修改初始化指令存储器的方法
  2. $readmemh读取数据的系统函数,表示从inst_rom.data文件中读取数据以初始化inst_mem
  3. OpenMIPS是按字节寻址的,而此处定义的指令存储器的每个地址是一个32bit的字,所以要将OpenMIPS给出的地址除以4再使用
    在这里插入图片描述

2.2最小SOPC

为了验证建立一个SOPC:OpenMIPS从指令存储器读取指令,指令进入OpenMIPS开始执行
在这里插入图片描述

`include "../rtl/defines.v"

module openmips_min_sopc(

	input wire clk,
	input wire rst
	
);

	//openmips <-> ROM
	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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

2.3使用VCS和verdi联合仿真

“自己动手写CPU”这本书是在windows环境下用modelsim软件进行仿真验证,而众所周知工业界都是在Linux环境下使用VCSverdi进行仿真验证,因此本文使用Linux环境进行验证并可以学习一些VCSverdi软件的使用。

如何得到inst_rom.data文件参考文章https://blog.csdn.net/yvbycf/article/details/128359374

testbench文件


`include "../rtl/defines.v"
`timescale 1ns/1ps

module openmips_min_sopc_tb();

  reg CLOCK_50;
  reg rst;
  
       
  initial begin
    CLOCK_50 = 1'b0;
    // cycle is 20ns: 50Mhz
    forever #10 CLOCK_50 = ~CLOCK_50;
  end
      
  initial begin
    rst = `RstEnable;
    
    // min sopc start run
    #195 rst= `RstDisable;
    #1000 $stop;
  end
       
  openmips_min_sopc openmips_min_sopc0(
		.clk(CLOCK_50),
		.rst(rst)	
	);

  initial	begin
	    $fsdbDumpfile("tb.fsdb"); // generate "tb.fsdb"
	    // $fsdbDumpvars(0, openmips_min_sopc_tb,  "+mda"); // dump tb and dut ports, "+mda"->dump mem
	    $fsdbDumpvars(0, openmips_min_sopc_tb); // dump tb and dut ports
        $fsdbDumpMDA( ); // dump mem

end

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

注意:

  • 要在vedi中显示波形需要使用 $fsdbDumpfile( )函数指定波形文件名
  • 要使用函数$fsdbDumpvars( )去dump tb和dut的端口加载波形
  • 要加载寄存器数组的波形,需要使用$fsdbDumpMDA( )函数

我们使用Makefile文件控制VCS和verdi联合仿真
Makfile文件


all  : vcs verdi

vcs   :
	vcs  \
              -f filelist.f  \
              -timescale=1ns/1ps \
              -debug_acc+dmptf -debug_region+cell+encrypt  -full64  -R  +vc  +v2k  -sverilog  -debug_all  \
              -P ${LD_LIBRARY_PATH}/novas.tab  ${LD_LIBRARY_PATH}/pli.a  \
              |  tee  vcs.log  

verdi  :
	verdi -f filelist.f -ssf tb.fsdb 

clean  :
	 rm  -rf  *~  core  csrc  simv*  vc_hdrs.h  ucli.key  urg* *.log  novas.* *.fsdb* verdiLog  64* DVEfiles *.vpd
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在文件夹sim/下使用命令

make vcs
make verdi
  • 1
  • 2

即可仿真及打开verdi查看波形
如何编写Makefile进行vcs+verdi联合仿真可参考本人的另一篇博客VCS + verdi + Makefile

仿真波形图
在这里插入图片描述

  1. if_inst是取到的指令,从仿真可知,依次取出inst_rom.data中的指令
  2. 观察regs[1]、regs[2]、regs[3]、regs[4]的最终值,可知OpenMIPS正确执行了程序

三、链接汇总

项目源码https://github.com/yizhixiaohuihui/OpenMIPS.git
如何得到inst_rom.data文件参考文章https://blog.csdn.net/yvbycf/article/details/128359374
如何编写Makefile进行vcs+verdi联合仿真可参考本人的另一篇博客VCS + verdi + Makefile

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/411414
推荐阅读
相关标签
  

闽ICP备14008679号