自己动手写CPU_4_第一条指令ori的实现

目录

  4.1 ori指令的编码

  4.2 流水线模型的建立

    4.2.1 流水线的简单模型

    4.2.2 原始的OpenMIPS五级流水线结构

    4.2.3 一些宏定义

      全局的宏定义

      和具体指令相关的宏定义

      和通用寄存器regfile相关的宏定义

    4.2.4 取指令阶段的实现

      PC模块

      IF_ID模块

    4.2.5 译码阶段的实现

      Regfile模块

      ID模块

      ID_EX模块

    4.2.6 执行阶段的实现

      EX模块

      EX_MEM模块

    4.2.7 访存阶段的实现

      MEM模块

      MEM_WB模块

    4.2.8 回写阶段的实现

    4.2.9 顶层模块OpenMIPS的实现

参考书籍

  自己动手写CPU 雷思磊

4.1 ori指令的编码

ori指令的编码格式如下:

rs :源寄存器索引。

rt :目的寄存器索引。

immediate:16-bit的常数。

功能:ori指令把符号扩展的immediate与rs索引的寄存器的值相OR存入rt索引的寄存器中。

4.2 流水线模型的建立

4.2.1 流水线的简单模型

寄存器之间有连接没有环路的电路叫做流水线。

4.2.2 原始的OpenMIPS五级流水线结构

 

取指:取出指令存储器中的指令,PC值递增指向下一个指令。

译码:对指令进行译码,根据译码结果取出寄存器或其他运算数据。

执行:依据译码阶段送入的源操作数,操作码进行运算,结果传递到访存阶段。

访存:对ori指令来说没有操作,直接将运算结果传递到回写阶段。

回写:将运算结果保存到目的寄存器。

对于结构图如下:

4.2.3 一些宏定义

全局的宏定义

和具体指令相关的宏定义

和通用寄存器regfile相关的宏定义

4.2.4 取指令阶段的实现

取指令阶段取出指令存储器中的指令,同时PC值增加指向下一个指令,该阶段由PC、IF_ID两个模块实现[REF 4.2.2]。

PC模块

功能描述:

  PC的功能是给出指令地址。

接口描述:

Verilog代码描述:

module pc_reg (
    input wire clk,
    input wire rst,
    output reg[`InstAddrBus] pc,  //InstAddrBus为指令地址的宽度
    output reg ce  // chip enable
);
    always @ (posedge clk)
        ce <= rst == `RstEnable ? `ChipDisable : ChipEnable;

    always @ (posedge clk)
        if (ce == `ChipDisable)
            pc <= 32'b0;
        else
            pc <= pc + 4'h4;

endmodule

RTL模型:

IF_ID模块(Instruction Fetching Instruction Decoding)

功能描述:

  IF_ID模块的作用是暂时保存取指令阶段取得的指令,及其对应的指令地址,并在下一个时钟传递到译码阶段。

接口描述:

Verilog代码描述:

module example (
    input wire clk,
    input wire rst,
    
    //来自取指令阶段的信号
    input wire[`InstAddrBus] if_pc, //InstAddrBus指令地址宽度
    input wire[`InstBus] if_inst,
    
    //对应译码阶段的信号
    output reg[`InstAddrBus] id_pc,
    output reg[`InstBus] id_inst
);

    always @ (posedge clk)
        if (rst == `RstEnable) begin
            id_pc <= `ZeroWord;
            id_inst <= `ZeroWord;
        end else begin
            id_pc <= if_pc;
            id_inst <= if_inst;
        end

endmodule 

RTL模型:

4.2.5 译码阶段的实现

  在译码阶段,CPU会对取到的指令进行译码,即:给出要进行运算的运算类型,以及参与运算的操作数。译码阶段由模块Regfile、ID(Instruction Decoding)和ID_EX(EXecution)构成。

Regfile模块

功能描述:

  Regfile实现了32个32-bit通用整数寄存器,可以同时进行两个寄存器的读操作和一个寄存器的写操作。

接口描述:

Verilog描述:

module regfile (
    input wire clk,
    input wire rst,
    
    //写端口,当we使能时,将wdata写入地址为waddr的寄存器中
    input wire we, // 写使能(WriteEnable)
    input wire[`RegAddrBus] waddr, //被写入数据的地址
    input wire[`RegBus] wdata, //被写入的数据
    
    //读端口1,当re1使能,将地址为raddr1的寄存器数据输出到rdata1上
    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-bit的寄存器
    **/
    
    reg[`RegBus]  regs[0:`RegNum-1];

    
    /**
        实现了写端口操作,当rst没有使能时,若we使能且waddr并非0地址,那么将数据wdata写入waddr对应的寄存器中。
    **/
    always @ (posedge clk) begin
        if (rst == `RstDisable) begin
            if((we == `WriteEnable) && (waddr != `RegNumLog2'h0)) begin
                regs[waddr] <= wdata;
            end
        end
    end
    
    /**
        实现了端口1读操作
    **/
    
    always @ (*) begin
        if(rst == `RstEnable) begin // 若rst使能,那么将会输出数据0。
              rdata1 <= `ZeroWord;
      end else if(raddr1 == `RegNumLog2'h0) begin // 若raddr1引用$0 reg,那么直接输出0。
              rdata1 <= `ZeroWord;
            end else if((raddr1 == waddr) && (we == `WriteEnable) // 若要读取的寄存器是要写入的数据的寄存器,那么直接复制
                      && (re1 == `ReadEnable)) begin
            rdata1 <= wdata;
      end else if(re1 == `ReadEnable) begin // 读取使能且读取地址有效非特殊地址,复制相应数据。
          rdata1 <= regs[raddr1];
      end else begin //无效情况,输出0
          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

 RTL模型:

ID模块

功能描述:

  ID模块的作用是对指令进行译码,得到最终运算的类型、子类型、源操作数1、源操作数2、目的寄存器地址等信息。

    运算类型:逻辑运算、位移运算、算数运算等。

    算数子类型:对运算类型更进一步的说明,例如逻辑运算-与等。

接口描述:

 

 Verilog描述:

`include "defines.v"

module id(

	input wire					rst,
	input wire[`InstAddrBus]	pc_i,			//译码阶段指令对应的地址
	input wire[`InstBus]		inst_i,			//译码阶段的指令

	input wire[`RegBus]			reg1_data_i,	//从regfile输入的第一个寄存器端口的输入
	input wire[`RegBus]			reg2_data_i,	//从regfile输入的第二个寄存器端口的输入

	//送到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,		//Written Destination Out,译码阶段将要写入目的的寄存器地址。
	output reg					wreg_o		//译码阶段是否有要写入的目的地址。
);
	
	//获取指令的指令码和功能码
	//对于ori指令只需要通过判断26-31bit的值即可确定是否为ori
	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) 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];	//默认通过Regfile读端口1读取的寄存器地址
			reg2_addr_o <= inst_i[20:16];	//默认通过Regfile读端口2读取的寄存器地址
			imm <= `ZeroWord;			
			case (op)
				`EXE_ORI:					//根据op的值判断是否为ori指令
				begin                        //ORI指令
					wreg_o <= `WriteEnable;			// ori需要将结果写入目的寄存器,所以wreg_o为WriteEnable
					aluop_o <= `EXE_OR_OP;			//算数类型
					alusel_o <= `EXE_RES_LOGIC;		//子运算类型
					reg1_read_o <= 1'b1;			// rs,需要读取
					reg2_read_o <= 1'b0;	  		// rt,不需要读取。
					imm <= {16'h0, inst_i[15:0]};	//指令执行需要的立即数
					wd_o <= inst_i[20:16];			// rt的寄存器地址
					instvalid <= `InstValid;		//指令有效
				end
				
				default:
					begin end
			endcase		  //case op			
		end       //if
	end         //always
	
	/**
		第二部分--确定进行运算的源操作数1
	**/
	always @ (*) begin
		if(rst == `RstEnable) begin
			reg1_o <= `ZeroWord;
		end else if(reg1_read_o == 1'b1) begin	//regfile读端口1的输出值
			reg1_o <= reg1_data_i;
		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	//regfile读端口2的输出值
	  	reg2_o <= reg2_data_i;
	  end else if(reg2_read_o == 1'b0) begin	//立即数
	  	reg2_o <= imm;
	  end else begin
	    reg2_o <= `ZeroWord;
	  end
	end

endmodule

ID_EX模块

功能描述:

  将ID模块对指令的译码结果缓存,在下一个时钟沿传递到流水线执行模块。

接口描述:

Verilog描述

`include "defines.h"

module id_ex (
    input wire clk,
    input wire rst,

    //从译码阶段传递的信息
    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,

    //传递到执行阶段的信息
    output reg[`AluOpBus] ex_aluop,
    output reg[`AluSelBus] ex_alusel,
    output reg[`RegBus] ex_reg1,
    output reg[`RegBus] ex_reg2,
    output reg[`RegBus] 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

4.2.6 执行阶段的实现

  现在指令已经进入流水线的执行阶段,在此期间将根据译码的结果,对数据进行运算,该阶段包含EX、EX_MEM两个模块。

EX模块

功能描述:

  EX从ID_EX模块得到指令的译码信息,然后根据这些信息进行运算。

接口描述:

Verilog描述

`include "defines.v"

module example
(
	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;
	
	//根据aluop_i指示的算子类型进行运算。
	always @ (*) begin
		if (rst == `RstEnable)
			logicout <= `ZeroWord;
		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指示的运算类型,选择一个运算结果作为最终结果。
	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

EX_MEM模块

接口描述

  EX模块的输出连接到EX_MEM模块,EX_MEM模块的作用是将执行阶段取得的运算结果在下一个时钟传递到流水线访存阶段。

接口描述

Verilog描述

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

4.2.7 访存阶段的实现

  访存阶段包括MEM、MEM_WB两个模块。

MEM模块

功能描述:

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

接口描述:

Verilog描述:

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

MEM_WB模块

功能描述:

  该模块将访存阶段的运算结果在下一个时钟传递到回写阶段。

接口描述:

 

 Verilog描述:

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
			wd_wd <= NOPRegAddr;
			wb_wreg <= `WriteDisable;
			wb_wdata <= `ZeroWord;
		end else begin
			wb_wd <= mem_wd;
			wb_reg <= mem_wreg;
			wb_wdata <= mem_wdata;
		end
	end

endmodule

4.2.8 回写阶段的实现

  MEM_WB模块的输出连接到Regfile模块将指令的运算结果写入目的寄存器以实现回写阶段。

4.2.9 顶层模块OpenMIPS的实现

功能描述:

  顶层模块OpenMIPS在文件openmips.v中实现,主要对上面实现的流水线各阶段的模块进行连接。

接口描述:

Verilog模型:

`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;

  //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

猜你喜欢

转载自www.cnblogs.com/ycc1997/p/12098846.html