计算机组成与系统结构——MIPS多周期处理器-7指令

源代码放在7指令CPU设计

1. 文件结构说明

在这里插入图片描述

  1. mips为顶层模块,负责调用其他模块。完成各模块后,在mips统一实例化。
  2. DataPath为数据通路,主要实现计数器、存储器、寄存器堆、算术逻辑单元(ALU),符号扩展单元等基本单元。
  3. Control为控制器,主要是实现多周期CPU中状态的转移,以及各状态中控制信号的选择。

1.1 数据通路

在这里插入图片描述

1.2 指令分类

  1. R型指令
助记符oprsrtrdshamtfunct
addu000000rsrtrd00000100001
subu000000rsrtrd00000100011
  1. I型指令
助记符oprsrtimmediate
ori001101rsrtimmediate
lw100011basertoffset
sw101011basertoffset
beq000100rsrtoffset
  1. J型指令
助记符jalinstr_index
jal000011instr_index

2. 模块定义

2.1 flopr(异步复位触发器)

基本描述

32位的异步复位触发器,主要用于暂存数据。

模块接口

信号名方向描述
clkI时钟信号
rstI复位信号
flopr_data_inI数据输入
WrI写使能信号
flopr_data_outO数据输出

模块代码

module flopr(
    input            clk,
    input            rst,
    input [31:0]    flopr_data_in,
    input Wr,
    output [31:0]   flopr_data_out
);

reg [31:0]flopr;

always @(posedge clk,posedge rst) begin
    if(rst==1'b1)flopr<=32'h0000_0000;
    else if(Wr==1'b1)flopr<=flopr_data_in;
    else flopr<=flopr;
end
assign flopr_data_out=flopr;
endmodule

2.2 dm(数据存储器)

基本描述

用于读取和写入数据,当写使能有效时,按照地址写入数据,当读有效时,按照地址读取出数据。

模块接口

信号名方向描述
clkI时钟信号
addrI地址
dinI数据输入
DMWrI写使能信号
doutO数据输出

功能定义

序号功能名称功能描述
1读数据寄存器输出地址对应的数据
2写数据寄存器将数据写入对应地址

模块代码

module dm_4k( addr, din, DMWr, clk, dout );
   
   input  [11:2] addr;
   input  [31:0] din;
   input         DMWr;
   input         clk;
   output [31:0] dout;
     
   reg [31:0] dmem[1023:0];
   
   always @(posedge clk) begin
      if (DMWr)
         dmem[addr] <= din;
   end // end always
   
   assign dout = dmem[addr];
    
endmodule    

2.3 im(指令存储器)

基本描述

用于读取指令,在本程序中,指令存储器通过直接读取文本文件获取所需要执行的指令。

模块接口

信号名方向描述
addrI地址
doutO指令

功能定义

序号功能名称功能描述
1读指令寄存器输出地址对应的指令

模块代码

module im_4k( addr, dout );
    
    input [11:2] addr;
    output [31:0] dout;
    
    reg [31:0] imem[1023:0];
    
    assign dout = imem[addr];
    
endmodule    

2.4 RF(寄存器堆)

基本描述

寄存器堆用于实现MIPS指令集中的32个寄存器。

模块接口

信号名方向描述
clkI时钟信号
RaI读寄存器1的地址
RbI读寄存器2的地址
RwI写寄存器的地址
WDI写寄存器的内容
RFWrI写使能信号
RD1O读寄存器1的内容
RD2O读寄存器2的内容

功能定义

序号功能名称功能描述
1读寄存器输出对应寄存器的内容
2写寄存器将数据写入对应的寄存器

模块代码

module RF(
    input [4:0]Ra,     //读寄存器1
    input [4:0]Rb,     //读寄存器2
    input [4:0]Rw,     //写寄存器1
    input [31:0]WD,     //写入数据
    input RFWr,         //写使能信号
    input clk,          //时钟信号
    output reg [31:0] RD1,  //读寄存器1读到的数据
    output reg [31:0] RD2   //读寄存器2读到的数据
);

reg [31:0]RF[31:0]; //寄存器堆
always @(posedge clk) begin
    if(RFWr)
        RF[Rw] <= WD;
    else
        RF[Rw] <= RF[Rw];
end

always @(posedge clk) begin
    RF[0] <= 32'b0;
end

always @(Ra,RF) begin
    RD1 = RF[Ra];
end

always @(Rb,RF) begin
    RD2 = RF[Rb];
end

endmodule

2.5 ALU(算术逻辑单元)

基本描述

在7指令中用到的运算只有加法、减法、和或运算。
所以需要一个2位宽的操作码Opcode

模块接口

信号名方向描述
Operand_XI操作数1
Operand_YI操作数2
OpcodeI操作码
ResultO计算结果
zeroO零信号,用于分支指令

功能定义

序号功能名称功能描述
1加法两个操作数相加
2减法两个操作数相减
3两个操作数相或

模块代码

module ALU(
    input [31:0] Operand_X,
    input [31:0] Operand_Y,
    input [1:0]Opcode,
    output reg[31:0] Result,
    output reg zero
);
/*
Opcode 字段:
00: add
01: sub
10: or
*/

always @(Result) begin
    if(Result == 32'b0)
        zero = 1;
    else
        zero = 0;
end

always @(Opcode,Operand_X,Operand_Y) begin
    case(Opcode)
        2'b00: Result = Operand_X + Operand_Y;
        2'b01: Result = Operand_X - Operand_Y;
        2'b10: Result = Operand_X | Operand_Y;
    endcase
end

endmodule

2.6 ALUControl(算术逻辑控制单元)

基本描述

根据指令选择需要的操作码字段,进而控制ALU的运算结果。

模块接口

信号名方向描述
ALUopI控制器产生控制ALU的信号
functI指令的funct字段
OpcodeO产生使ALU

模块代码

module ALUControl(
    input [2:0]ALUop,
    input [5:0]funct,
    output reg[1:0]Opcode
);
parameter addu = 6'b100001;
parameter subu = 6'b100011;

always @(ALUop,funct) begin
    case(ALUop)
        3'b000: Opcode = 2'b00;
        3'b001: Opcode = 2'b01;
        3'b010: Opcode = 2'b10;
    default:
        case(funct)
            addu:Opcode = 2'b00;
            subu:Opcode = 2'b01;
            default:Opcode = 2'b10;
        endcase
    endcase
end
endmodule

2.7 EXT(扩展器)

基本描述

根据是否需要符号扩展,将16位数扩展为32位数。

模块接口

信号名方向描述
Imm16I16位立即数
ExtOpI符号扩展信号
Imm32O32位立即数

模块代码

module EXT(
    input [15:0] Imm16,
    input [1:0]ExtOp,
    output reg [31:0] Imm32
);

always @(Imm16,ExtOp) begin
    case(ExtOp)
        2'b00:Imm32 = {{16{0}},Imm16};
        2'b01:Imm32 = {{16{Imm16[15]}},Imm16};
        default: Imm32 = {{16{0}},Imm16};
    endcase
end

endmodule

2.8 NPC(下一条指令地址产生逻辑)

基本描述

根据本指令的类型,计算出下一条指令地址。

模块接口

信号名方向描述
PCI当前指令地址
Imm32I扩展器得到的32位数
Imm26Itarget字段
branchI分支指令
jalI跳转指令
clkI时钟信号
zeroI零信号
NPCO下一条指令地址

模块代码

module NPC(
    input [31:0] PC,	//当前指令地址
    input [31:0] Imm32, //由扩展器扩展得到的32位数
    input [25:0] Imm26,	//jal指令的target字段
    input branch,		//branch指令
    input jal,			//jal指令
    input clk,			
    input zero,			//ALU计算出的跳转指令,若zero不为1,则不跳转
    output reg [31:0]NPC//NPC组合逻辑的输出,下一条指令的地址
);
reg [31:0]PC_4;
always@(PC)
begin
  PC_4 = PC + 4;    //当指令计数器改变时,立即计算出该指令加4的值
end

reg [31:0]temp1;    //jal计算出的地址
reg [31:0]temp2;    //branch计算出的地址

always@(PC,Imm26)
begin
  temp1 = {PC[31:28],Imm26,2'b00};
end

always@(PC,Imm32)
begin
  temp2 = PC + {Imm32[29:0],2'b00};
end

always@(temp1,temp2,zero,PC_4,branch,jal)
begin
  if(branch && zero)
	NPC = temp2;
  else if(jal)
	NPC = temp1;
  else
	NPC = PC_4;
end
endmodule

2.9 PC(程序计数器)

基本描述

用与更新当前指令的地址,当使能信号有效时,将下一条地址加载进来。

模块接口

信号名方向描述
clkI时钟信号
rstI复位信号
PCWrI写使能信号
NPCI下一条指令地址
PCO当前指令地址

模块代码

module PC(
    input clk,
    input rst,
    input PCWr,             //PC计数器的写使能信号
    input [31:0] NPC,       //下一条指令的地址
    output reg [31:0] PC    //当前指令地址
);

always@(posedge clk,posedge rst)
begin
  if(rst)
  begin
    PC <= 32'h0000_3000;
  end
  else
  begin
    if(PCWr)
        PC <= NPC;
    else
        PC <= PC;
  end
end
endmodule

2.10 ctrl(控制器)

控制器是多周期CPU的核心部件,它规定了多周期CPU所处的状态以及状态转移情况,同时给出了不同状态时各个控制信号的值。

基本描述

控制状态转移以及信号的值。

模块接口

信号名方向描述
clkI时钟信号
rstI复位信号
zeroI零信号
OpcodeI操作码
RegDstO选择寄存器的数据来源
ALUOpOALU的控制信号
IRWrO指令寄存器的写使能信号
PCWrO指令计数器的写使能信号
RFWrO寄存器堆的写使能信号
ALUSrcOALU第二个操作数的来源
RFDataSelO寄存器堆的数据选择信号
branchO分支信号
jalO跳转信号
ExtOpO拓展信号
DMWrO数据存储器的写使能信号

功能定义

在这里插入图片描述
七条指令的状态转移图如图所示,其中lw指令是五个状态都要经历的,其余指令都在4个周期以内完成。

针对每个状态,选择相应的控制信号。
IF(Instruction Fetch):取指阶段只需要程序计数器计算出下一条指令的地址,故此时PCWr = 1。

ID(Instruction Decode):译码阶段jal指令需要跳转故需要计算出下一条指令的地址故PCWr = 1,jal = 1,RFWr = 1(此时需要将返回地址保存在第31号寄存器中),并且下一个阶段为IF,其余指令不需要进行任何操作,进入EXE。

EXE(Execute):执行阶段对于r型指令,控制其ALUop = 100,并进入写回阶段。对于beq指令需要将ALUop = 001(减法)并且branch = 1和PCWr
= 1。对于ori指令需要将ALUop = 010(或运算),并且将ALUSrc = 1(以扩展器得到的32位数作为对象)。其余为lw和sw指令,ALUSrc = 1.

MEM(Memory Access):进入MEM阶段的只有lw和sw指令,sw需要将DMWr = 1,进而将数据写入数据寄存器中,并进入IF阶段。lw指令需要直接进入WB阶段。

WB(Write Back):将数据写回到寄存器中,其中r型指令和ori指令选择ALU的计算结果写入,lw选择内存的访问结果写入。

模块代码

module ctrl(
    input clk,
    input rst,
    input zero,
    input [5:0]Opcode,
    output reg[1:0]RegDst,      //寄存器堆入口处的多路选择器
    output reg[2:0]ALUOp,       //ALU的操作码
    output reg IRWr,            //指令寄存器的写使能信号
    output reg PCWr,            //PC计数器的写使能信号
    output reg RFWr,            //寄存器堆的写使能信号
    output reg ALUSrc,          //ALU的选择信号
    output reg [1:0]RFDataSel,
    output reg branch,          //分支信号
    output reg jal,             //跳转信号
    output reg [1:0]ExtOp,      //扩展信号
    output reg DMWr,             //数据寄存器的写使能信号
    output reg [2:0]CurrentState
);

    parameter  IF=3'b000;    //取指
    parameter  ID=3'b001;    //译码
    parameter  EXE=3'b010;   //执行
    parameter  MEM=3'b011;   //读取
    parameter  WB=3'b100;    //写回
    parameter j = 6'b000011; //jal
    parameter r = 6'b000000; //R型指令
    parameter lw =6'b100011; //lw
    parameter sw =6'b101011; //sw
    parameter beq =6'b000100;//beq
    parameter ori =6'b001101;//ori


//reg [2:0]CurrentState;
reg [2:0]NextState;
/*时序部分*/
always @(posedge clk,posedge rst) begin
    if(rst)
        CurrentState <= IF;
    else
        CurrentState <= NextState;
end


/*组合逻辑部分*/
always @(CurrentState,zero,Opcode) begin
    case (CurrentState)
    IF:begin
      NextState = ID;   //下一个状态调整为ID
      PCWr = 1'b1;      //在第一个状态就计算出下一条PC的值
      IRWr = 1'b1;      
      branch = 1'b0;    
      jal = 1'b0;       
      RFWr = 1'b0;
      DMWr = 1'b0;      
    end 
    ID:begin
      case(Opcode)
      j:begin
        NextState = IF;
        PCWr = 1'b1;
        IRWr = 1'b0;
        branch=1'b0;
        jal=1'b1;
        RFWr=1'b1;
        DMWr=1'b0;
        RegDst=2'b00;
        RFDataSel=2'b00;
      end
      default:begin
        NextState=EXE;
        PCWr=1'b0;
        IRWr=1'b0;
        branch=1'b0;
        jal=1'b0;
        RFWr=1'b0;
        DMWr=1'b0;
      end
      endcase
      if(Opcode == 6'b001101)
        ExtOp = 1'b0;
      else
        ExtOp = 1'b1;
    end
    EXE:begin
      case(Opcode)
        r:begin
          NextState = WB;
          PCWr = 1'b0;
          IRWr = 1'b0;
          branch = 1'b0;
          jal = 1'b0;
          RFWr = 1'b0;                
          ALUSrc = 1'b0;
          DMWr = 1'b0;
          ALUOp = 3'b100;
        end
        beq:begin
          NextState = IF;
          PCWr = 1'b1;
          IRWr = 1'b0; 
          RFWr = 1'b0;            
          jal = 1'b0;
          DMWr = 1'b0;  
          ALUOp = 3'b001;
          branch = 1'b1;
          ALUSrc=1'b0; 
        end
        ori:begin
          NextState = WB;
          PCWr = 1'b0;
          IRWr = 1'b0; 
          RFWr = 1'b0;            
          DMWr = 1'b0;  
          ALUOp = 3'b010;
          branch = 1'b0;
          jal = 1'b0;
          ALUSrc=1'b1; 
        end
        default:begin
          NextState = MEM;
          PCWr = 1'b0;
          IRWr = 1'b0; 
          RFWr = 1'b0;            
          DMWr = 1'b0;  
          ALUOp = 3'b000;
          branch = 1'b0;
          jal = 1'b0;
          ALUSrc=1'b1; 
        end
      endcase
    end
    MEM:begin
      case(Opcode)
        lw:begin
          NextState = WB;
          PCWr = 1'b0;
          IRWr = 1'b0; 
          RFWr = 1'b0;            
          DMWr = 1'b0;  
          branch = 1'b0;
          jal = 1'b0;
        end
        sw:begin
          NextState = IF;
          PCWr = 1'b0;
          IRWr = 1'b0; 
          RFWr = 1'b0;            
          DMWr = 1'b1;  
          branch = 1'b0;
          jal = 1'b0;
        end
      endcase
    end
    WB:begin
      case(Opcode)
        r:begin
            NextState=IF;
            PCWr = 1'b0;
            IRWr = 1'b0;
            branch = 1'b0;
            jal = 1'b0;
            DMWr = 1'b0; 
            RFWr = 1'b1;
            RFDataSel = 2'b01;
            RegDst = 2'b01;   
        end
        ori:begin
            NextState=IF;
            PCWr=1'b0;
            IRWr=1'b0;
            branch=1'b0;
            jal=1'b0;
            DMWr=1'b0; 
            RFWr=1'b1;
            RFDataSel=2'b01;//选择ALU数据 
            RegDst=2'b11; 
        end
        default:begin
            NextState=IF;
            PCWr=1'b0;
            IRWr=1'b0;
            branch=1'b0;
            jal=1'b0;
            DMWr=1'b0; 
            RFWr=1'b1;
            RegDst=2'b10;
            RFDataSel=2'b10;//选择从内存中读取的数据   
      end
    endcase
    end
    endcase
end
endmodule

mips(顶层模块)

基本描述

对上述所有模块做实例化。

模块接口

信号名方向描述
clkI时钟信号
rstI复位信号

模块代码

module mips(
   input clk,
   input rst
);
   
   wire PCWr;
   wire [31:0]PC;
   wire [31:0]NPC;
   wire IRWr;

   PC U_PC(.clk(clk),.rst(rst),.PCWr(PCWr),.NPC(NPC),.PC(PC)); //实例化指令计数器

   wire [31:0]im_dout;
   im_4k U_IM (.addr(PC[11:2]) , .dout(im_dout));               //实例化指令存储器

   wire [31:0]instr;
   flopr U_IR(.clk(clk),.rst(rst),.flopr_data_in(im_dout),.flopr_data_out(instr),.Wr(IRWr));
   /*使用一个寄存器保存从指令寄存器取到的结果instr*/

   wire zero;
   wire jal;
   wire branch;
   wire [31:0]Imm32;
   wire [1:0] ExtOp;
   wire [31:0] Result_r;

   EXT U_EXT(.Imm16(instr[15:0]),.ExtOp(ExtOp),.Imm32(Imm32)); //实例化符号扩展器

   NPC U_NPC(.PC(PC),.Imm32(Imm32),.Imm26(instr[25:0]),.branch(branch),.jal(jal),.clk(clk),.zero(zero),.NPC(NPC));
   /*实例化下一条指令地址的计算单元*/

   wire [1:0]RegDst;
   wire [2:0]ALUop;
   reg [4:0]reg1;

   always @(instr,RegDst) begin
      if(RegDst==2'b00)reg1=5'b11111;
      else if(RegDst==2'b01)reg1=instr[15:11];
      else reg1=instr[20:16];
   end

   wire RFWr;
   wire[31:0] RD1;
   wire[31:0] RD2;
   reg [31:0] reg2;
   wire[31:0] mem_data_out;
   wire[31:0] Result;
   wire [1:0]RFDataSel;

   always @(mem_data_out,PC,Result_r,RFDataSel) begin
      if(RFDataSel==2'b00) reg2 = PC;  
      else if(RFDataSel==2'b01)reg2 = Result_r;
      else reg2=mem_data_out;
   end
   
   RF U_RF(
      .Ra(instr[25:21]),.Rb(instr[20:16]),.Rw(reg1),.WD(reg2),
      .RFWr(RFWr),.clk(clk),.RD1(RD1),.RD2(RD2)
   );

   wire[31:0] data1;
   wire[31:0] data2;

   flopr U_rfr1(
      .clk(clk),.rst(rst),.flopr_data_in(RD1),
      .Wr(1'b1),.flopr_data_out(data1)
   );

   flopr U_rfr2(
      .clk(clk),.rst(rst),.flopr_data_in(RD2),
      .Wr(1'b1),.flopr_data_out(data2)
   );

   wire ALUSrc;
   reg [31:0]reg3;

   always @(data2,Imm32,ALUSrc)begin
     if(!ALUSrc)
         reg3 = data2;
      else
         reg3 = Imm32;
   end

   wire[1:0] Opcode;

   ALU U_ALU(.Operand_X(data1), .Operand_Y(reg3), .Opcode(Opcode),.Result(Result),.zero(zero));
   
   ALUControl U_ALUControl(.ALUop(ALUop),.funct(instr[5:0]),.Opcode(Opcode));

   flopr U_rfr3(.clk(clk),.rst(rst),.flopr_data_in(Result),.Wr(1'b1),.flopr_data_out(Result_r));

   wire DMWr;
   wire[2:0] State;
   ctrl U_Control(
      .clk(clk),.rst(rst),.zero(zero),.Opcode(instr[31:26]),
      .RegDst(RegDst), .ALUOp(ALUop),.IRWr(IRWr),.PCWr(PCWr),.RFWr(RFWr),.ALUSrc(ALUSrc),
      .RFDataSel(RFDataSel),.branch(branch),.jal(jal),.ExtOp(ExtOp),.DMWr(DMWr),.CurrentState(State)
   );

   wire[31:0] data3;
   flopr U_rfr5(.clk(clk),.rst(rst),.flopr_data_in(data2),.Wr(1'b1),.flopr_data_out(data3));

   wire [31:0]dm_out;
   
   dm_4k U_DM ( 
      .addr(Result_r[11:2]), .din(data3), .DMWr(DMWr), .clk(clk), .dout(dm_out)
   );
   
   flopr U_rfr4(
      .clk(clk),.rst(rst),.flopr_data_in(dm_out),
      .Wr(1'b1),.flopr_data_out(mem_data_out)
   );
   
endmodule

mips_tb(测试模块)

基本描述

将文本文件中的指令读入到指令寄存器中,然后开始执行。

模块代码

 module mips_tb();
    
   reg clk, rst;
    
   mips U_MIPS(
      .clk(clk), .rst(rst)
   );
   integer i;
   initial begin
      $readmemh( "code.txt" , U_MIPS.U_IM.imem ) ;
      $monitor("PC = 0x%8X, IR = 0x%8X", U_MIPS.U_PC.PC, U_MIPS.instr ); 
      clk = 1 ;
      rst = 0 ;
      for (i = 0;i<=31 ;i=i+1 )  U_MIPS.U_RF.RF[i]=32'h0000_0000;
      #5 ;
      rst = 1 ;
      #20 ;
      rst = 0 ;
   end
   
   always
	   #(50) clk = ~clk;
   
endmodule

3. 仿真结果

用Mars汇编器查看每条指令的执行结果。
在这里插入图片描述
结合Modelsim的时序仿真图查看结果。
在这里插入图片描述

最终得到正确的计算结果,完成7指令CPU的验证。

  • 10
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alfred young

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值