编写一个单周期mips的主机

使用语言

我们接下来将使用verilog语言编写一个单周期的mips,话不多说让我们开始吧

编写指令

为了确保好上手,我们将有选择性的只完成mips中的八条指令

mips的指令格式形式主要为

•Instruction Syntax is rigid:

op dst, src1, src2

–1 operator, 3 operands

•op = operation name (“operator”)

•dst = register getting result (“destination”)

•src1 = first register for operation (“source 1”)

src2 = second register for operation (“source 2

实现的指令

addu

subu :主要实现寄存器内两数的加减

lw

sw :image-20220513130338181

beq:•Branch If Equal (beq)

–beq reg1,reg2,label

–If value in reg1 = value in reg2, go to label

jump:•Jump (j)

–j label

ori:

lui

指令格式

其中包括i-format , j-format,r-format

三种格式

•I-Format: instructions with immediates, lw/sw (offset is immediate), and beq/bne

–But not the shift instructions

•J-Format: j and jal

–But not jr

•R-Format: all other instructions

•It will soon become clear why the instructions have been partitioned in this way

r指令的格式

image-20220513131127181

addu的rformat格式

image-20220513131247706

i-format

image-20220513131338166

在这里插入图片描述

datapath

下面我们就开始写不同组件部分

ext(移位extender)

作为一个移位器,主要是用来对外界输入的immediate进行移位,根据输入的extop选项决定进行符号扩展还是0扩展,扩展好变成32位以后,进入下一步的运算,在我们所列的八条指令中,所有i指令的都会需要用到移位器进行运算 而(beq,jump指令会在ifu内置的移位器中完成操作,我们不作考虑)

module ext(imm16,imm32,ExtOp);
  input [15:0]imm16;
  input [1:0]ExtOp;
  output reg[31:0]imm32;
  
  //set ZERO,SIGN,LUI
  parameter ZERO=2'b00;
  parameter SIGN=2'b01;
  parameter LUI=2'b10;
  
  //two conditions
  always@(*)begin
    case(ExtOp)
      ZERO:imm32={16'b0,imm16};
      SIGN:imm32={{16{imm16[15]}},imm16};
      LUI: imm32= {imm16,16'b0};
    endcase
  end
endmodule

alu

接下来便是十分重要的累加器alu了,alu承担了大部分运算的功能与职责

alu主要用aluctr接受信号

可以执行 add sub or lui

四种操作

因为addu subu ori lui 最后的输出操作是输入到aluout去

而执行lw和sw的时候则需要addr进行访存,因此我们还需要一个输出端口addr

另外还需要产生一个zero信号来供pc判断是否beq的条件相等

module alu(busA,busB,ALUctr,zero,Alu_out,Addr);
  input [31:0]busA,busB;
  input [1:0]ALUctr;
  
  output [31:0]zero,Addr;
  output reg[31:0]Alu_out;
  
  //set ADD,SUB,OR,Lui
  parameter ADD=2'b00;
  parameter SUB=2'b01;
  parameter OR=2'b10;
  parameter LUI=2'b11;
  
  //three conditions
  always@(*)begin
    case(ALUctr)
      ADD:begin
        Alu_out=busA+busB;
      end
      SUB:begin
        Alu_out=busA-busB;
      end
      OR:begin
        Alu_out=busA|busB;
      end
      LUI:begin
        Alu_out=busB;
      end
    endcase
  end
  
  assign zero=Alu_out;
  assign Addr=Alu_out;
endmodule

mux 选择器

写一个用三次即可

这里不多加赘述

ifu instruction fetch unit

ifu是最为复杂的一个部件

在其中内置一个ext 我们主要将会用它来beq和jump的扩展,同时也会进行下一轮指令的选择

信号

ifu接受npc_sel的信号用来判定是否为j指令或beq指令,zero判断j指令的条件

接受clk作为时钟周期

用rst接受是否初始化的标志,在读取玩数据后,以instruction作为输出

存取指令

手下那我们需要知道,在ifu中的指令存取次奥那等是大端序,也就是说第一个位置放的是最大位数的数

因为pc在运行的时候每次运行

都要加4,这是因为每条指令会占四个格子

我们所有的操作也是在+4以后进行的

jump操作因为特定的只有26位,但又因为我们是需要直接跳到指令的头部进行操作,因此必须是四的倍数,所以我们直接移两位

从操作的逻辑中很明显我们也能看出来实现beq操作的时候的相对位移值也必须是4的倍数,这样更节约空间!!!!!

module ifu(nPC_sel,zero,clk,rst,instruction,j_sel,jValue);
  input clk,rst;
  input [1:0]nPC_sel;
  input [31:0]zero;
  input j_sel;
  input [25:0]jValue;
  output [31:0]instruction;
  
  reg [31:0]pc;
  reg [7:0]im[1023:0];
  reg [31:0]pcnew;
  wire [31:0]temp,t0,t1;
  wire [15:0]imm16;
  reg [31:0]extout;
  
  //give instruction a value
  assign instruction={im[pc[9:0]],im[pc[9:0]+1],im[pc[9:0]+2],im[pc[9:0]+3]}; 
  
  assign imm16=instruction[15:0];
  
  //set extout value
  assign temp={{16{imm16[15]}},imm16};
  
  
  //j condition
  always@(*)begin
    if(j_sel==1)begin
      extout={pc[31:28],jValue[25:0],2'b0};
    end
      if(j_sel==0)begin
      extout=temp[31:0]<<2;
    end
  end
    
  //set pcnew
  assign t0=pc+4;
  assign t1=t0+extout;
  
  always@(*)
  begin
    if(nPC_sel==2'b00)begin
      pcnew=t0;
    end
    else if(nPC_sel==2'b01)begin
      pcnew=extout;
    end
    else if(nPC_sel==2'b10)begin
      if(zero==0)begin
        pcnew=t1;
      end
    else begin
      pcnew=t0;
    end
    end
  end
  
  //reset
  always@(posedge clk,posedge rst)
  begin
    if(rst) pc=32'h0000_3000;
      else if(j_sel==0)pc=pcnew;
      else if(j_sel==1)pc=extout;
  end
  
endmodule

datamemory

内存部分的写法事实上和ifu类似

都涉及到数据的存储和提取

另外由于内存我们只需要1kb

所以我们仅仅需要地址线Addr的后10位表示地址的值就可以了

为了初始化内存数据我们设置在nedgedge rst的时候初始化所有内存的值

因为内存存储的时候是小端序,所以我们倒过来存储

module dm(Data_in,MemWr,Addr,clk,rst,Data_out);
  input [31:0]Data_in,Addr;
  input clk,rst,MemWr;
  output reg[31:0]Data_out;
  
  reg [7:0]DataMem[1023:0];
  wire [9:0]pointer;
  assign pointer=Addr[9:0];
  //reset
  integer i;
  always@(negedge rst)begin
    for(i=0;i<1024;i=i+1)
    DataMem[i]=0;
  end
  
  
  always@(posedge clk)begin
    //store word
    if(MemWr==1)begin
        DataMem[pointer+3]<=Data_in[31:24];
        DataMem[pointer+2]<=Data_in[23:16];
        DataMem[pointer+1]<=Data_in[15:8];
      DataMem[pointer]<=Data_in[7:0];
    end
  end
  
  always@(negedge clk)begin
    //load word
  if(MemWr==0)begin
      Data_out<={DataMem[pointer+3],DataMem[pointer+2],DataMem[pointer+1],DataMem[pointer]};
  end
end
endmodule

寄存器

在上升流的时候初始化缓存

module gpr(RegWr,ra,rb,rw,busW,clk,rst,busA,busB,Data_in);
  input clk,rst,RegWr;
  input [31:0]busW;
  input [4:0]ra,rb,rw;
  output [31:0]busA,busB,Data_in;
  
  reg [31:0]regi[31:0];
  
  //reset
  integer i;
  always@(posedge rst)
  begin
    if(rst)
      for(i=0;i<32;i=i+1)
      regi[i]=0;
    end
    
  //set busA & busB
  assign busA=regi[ra];
  assign busB=regi[rb];
  assign Data_in=busB;
  
  //Register write in
  always@(posedge clk)begin
    if(RegWr)begin
      regi[rw]<=busW;
      regi[0]<=0;
    end
  end
endmodule

控制单元

控制单元主要接受信号,然后依据给定的信号来完成输出

module ctrl(instruction,RegDst,RegWr,ExtOp,nPC_sel,ALUctr,MemtoReg,MemWr,ALUSrc,j_sel);
  input [31:0]instruction;
  output reg [1:0]ExtOp,ALUctr,nPC_sel;
  output reg RegDst,RegWr,MemtoReg,MemWr,ALUSrc,j_sel;
  
  initial begin
    nPC_sel=0;
    RegDst=0;
    RegWr=0;
    ExtOp=0;
    nPC_sel=0;
    ALUctr=0;
    MemtoReg=0;
    MemWr=0;
    ALUSrc=0;
    j_sel=0;
end

always@(*)begin
  //R-type
  if(instruction[31:26]==6'b000000)
    begin
      //ADDU
      if(instruction[5:0]==6'b100001)
        begin
          nPC_sel=2'b00;
          RegDst=1'b1;
          RegWr=1'b1;
          ExtOp=2'b00;
          ALUSrc=1'b0;
          ALUctr=2'b00;
          MemWr=1'b0;
          MemtoReg=1'b0;
          j_sel=1'b0;
      end
      //SUBU
      else if(instruction[5:0]==6'b100011)
        begin
          nPC_sel=2'b00;
          RegDst=1'b1;
          RegWr=1'b1;
          ExtOp=2'b00;
          ALUSrc=1'b0;
          ALUctr=2'b01;
          MemWr=1'b0;
          MemtoReg=1'b0;
          j_sel=1'b0;
        end
      end
      //ORI
    else if(instruction[31:26]==6'b001101)
      begin
        nPC_sel=2'b00;
          RegDst=1'b0;
          RegWr=1'b1;
          ExtOp=2'b00;
          ALUSrc=1'b1;
          ALUctr=2'b10;
          MemWr=1'b0;
          MemtoReg=1'b0;
          j_sel=1'b0;
        end
      //LW
    else if(instruction[31:26]==6'b100011)
      begin
        nPC_sel=2'b00;
          RegDst=1'b0;
          RegWr=1'b1;
          ExtOp=2'b01;
          ALUSrc=1'b1;
          ALUctr=2'b00;
          MemWr=1'b0;
          MemtoReg=1'b1;
          j_sel=1'b0;
        end
        //SW
      else if(instruction[31:26]==6'b101011)
        begin
          nPC_sel=2'b00;
          RegDst=1'b0;
          RegWr=1'b0;
          ExtOp=2'b01;
          ALUSrc=1'b1;
          ALUctr=2'b00;
          MemWr=1'b1;
          MemtoReg=1'b0;
          j_sel=1'b0;
        end
        //BEQ
      else if(instruction[31:26]==6'b000100)
        begin
          nPC_sel=2'b10;
          RegDst=1'b0;
          RegWr=1'b0;
          ExtOp=2'b01;
          ALUSrc=1'b0;
          ALUctr=2'b01;
          MemWr=1'b0;
          MemtoReg=1'b0;
          j_sel=1'b0;
        end
        //LUI
      else if(instruction[31:26]==6'b001111)
        begin
          nPC_sel=2'b00;
          RegDst=1'b0;
          RegWr=1'b1;
          ExtOp=2'b10;
          ALUSrc=1'b1;
          ALUctr=2'b11;
          MemWr=1'b0;
          MemtoReg=1'b0;
          j_sel=1'b0;
      end
    //J
     else if(instruction[31:26]==6'b000010)
       begin
          nPC_sel=2'b01;
          RegDst=1'b0;
          RegWr=1'b0;
          ExtOp=2'b01;
          ALUSrc=1'b0;
          ALUctr=2'b01;
          MemWr=1'b0;
          MemtoReg=1'b1;
          j_sel=1'b1;
    end
  end
endmodule

posted @ 2022-05-13 19:09  红石Hong  阅读(151)  评论(0)    收藏  举报