天天看点

MIPS单周期CPU的设计——I型指令的设计

一、一些概念

1.单周期CPU

       指令周期:CPU从内存取出一条指令并执行这条指令的时间总和。

       CPU周期:又称机器周期,CPU访问一次内存所花的时间较长,因此用从内存读取一条指令字的最短时间来定义。

       时钟周期:通常称为节拍脉冲或T周期。一个CPU周期包含若干个时钟周期。

       指令周期>CPU周期>时钟周期。

       单周期CPU:取出并执行一条指令在一个时钟周期内完成,即一条指令用一个周期。MIPS就是一个单周期CPU。

2.MIPS指令格式和通用寄存器定义

       MIPS所有的指令均为32位,MIPS指令的三种格式如下(op是指令码):

MIPS单周期CPU的设计——I型指令的设计

       R类型指令的op为0,具体操作由func指定。rs和rt是源寄存器号,rd是目的寄存器号。只有移位指令使用sa来指定移位位数。I型指令的低16位是立即数,计算时要把它扩展到32位。依指令的不同,有零扩展和符号扩展两种。零扩展是把32位的高16位置成0;符号位扩展是把高16位的每一位置成与立即数最高为相同的值,即保持立即数的正负符号不变。J型指令的指令格式最简单,右边的26位是字地址,用于产生跳转的目的地址。

       MIPS指令中的寄存器号(rs、rt和rd)有5位,因此它能访问2^5=32个寄存器。下表列出了这32个寄存器的名称和用途。

寄存器名 寄存器号 用途
$zero 常数0
$at 1 汇编器专用
v 0   v0~ v0 v1 2~3 表达式计算或者函数调用的返回结果
a   a~ a a3 4~7 函数调用参数1~3
t 0   t0~ t0 t7 8~15 临时变量,函数调用时不需要保存和恢复
s 0   s0~ s0 s7 16~23 函数调用时需要保存和恢复的寄存器变量
t 8   t8~ t8 t9 24~25 临时变量,函数调用时不需要保存和恢复
k 0   k0~ k0 k1 26~27 操作系统专用
$gp 28 全局变量指针(Global Poiner)
$sp 29 堆栈指针(Stack Pointer)
$fp 30 帧指针(Frame Pointer)
$ra 31 返回地址(Return Address)

       注意:

       ①0号寄存器的内容永远是0。

       ②32号寄存器用来保存返回地址。

       上表虽然给出了使用这些寄存器的一些约定,但除了以上两点,这些寄存器并无本质的区别。因此,描述这些寄存器时可以不使用带有$的寄存器名,可以直接在r后面加寄存器号:r0,r1,…,r31。

二、MIPS CPU

       框图:

MIPS单周期CPU的设计——I型指令的设计

       IF是取指模块(Instruction Fetch),ID是译码模块,InstMen是指存(指令存储器)模块,是一个Rom芯片,RagFile是寄存器堆,EX模块是执行指令,包括写指令(用来做运算,可以认为是ALU)。

       I型指令的执行过程:

       IF的pc将指令的地址送入InstMem(指存)中,读取相应指令,pc每过一个clk就会自加4(这个过程在IF里完成),指向下一个指令。取出的指令送到ID ,ID将源寄存器的地址给regaAddress,将目的寄存器的地址给regcAddress(在I型指令中是这样,其他指令里就不一定了),将ID中的regaAddress和regaRd(读信号)送给RegFile,然后读取数据RegFile中的regaData,将RegFile的regaData送给ID的regaData_i,regaData_i会作为ID的regData送给EX。指令中的立即数也是可以在ID中直接获得的,当regaRd无效时,就会将立即数的值进行扩展,然后送给regaData。

       而运算的功能是EX模块来做的,所以ID读取数据完成后,将数据regaData,和目的寄存器地址送给EX,op是操作码,用来决定将进行何种操作,也送给EX。EX获得数据和操作码后就进行运算,运算后的结果存到regcData中,运算的结果也是要写进RegFile中的,所以EX将regcData,regcAddr(要写入的地址)和regcWr(写信号)送给RegFile,将数据写入寄存器堆保存起来,到这里这条I型指令就执行完了。

MIPS单周期CPU的设计——I型指令的设计

       红色的线是IF要做的事,蓝色的是ID要做的事,绿色的是EX要做的事。

三、代码设计

       IF、ID、EX和RegFile都是子模块,我们需要写一个MIPS模块调用这几个子模块,InstMem是一个单独的模块,是在MIPS外面,MIPS和InstMem相结合就组成了一个更高一级的模块,称作Soc,我们可以写一个Soc模块调用MIPS和InstMem。

MIPS单周期CPU的设计——I型指令的设计

       I型指令很多,这里只举ori、addi、andi和xori指令的实现,每个模块可以参照模块图进行理解。

       ①define.v

`define RstEnable       1'b1
`define RstDisable      1'b0
`define RomEnable       1'b1 
`define RomDisable      1'b0
`define RamWrEnable     1'b1
`define RamWrDisable    1'b0
`define Zero	        32'b0
`define Valid	        1'b1
`define Invalid	        1'b0

`define Inst_addi       6'b001000
`define Inst_andi       6'b001100
`define Inst_ori        6'b001101
`define Inst_xori       6'b001110
`define Inst_lui        6'b001111

`define Or              6'b000001
`define Add             6'b000010
`define And             6'b000100
`define Xor             6'b000101

`define Nop     6'b000000
`define Or      6'b000001
           

       ②IF.v

MIPS单周期CPU的设计——I型指令的设计
`include "define.v"
module IF(
    input wire clk,
    input wire rst,
    output reg romCe, 
output reg [31:0] pc
);
    [email protected](*)
        if(rst == `RstEnable)
            romCe = `RomDisable;
        else
            romCe = `RomEnable;
    [email protected](posedge clk)
        if(romCe == `RomDisable)
            pc = `Zero;
        else
            pc = pc + 4;
endmodule
           

       ③ID.v

MIPS单周期CPU的设计——I型指令的设计
`include "define.v"
module  ID (
    input wire rst,    
    input wire [31:0] inst,
    input wire [31:0] regaData_i,
    input wire [31:0] regbData_i,
    output reg [5:0] op,
    output reg [4:0] regaAddr,
    output reg [4:0] regbAddr,    
    output reg [4:0] regcAddr,    
    output reg [31:0] regaData,
    output reg [31:0] regbData,
    output reg regaRd,
    output reg regbRd,
    output reg regcWr  
);
    wire [5:0] inst_op = inst[31:26];    
    reg [31:0] imm;
    [email protected](*)
        if(rst == `RstEnable)
          begin
            op = `Nop;
            regaRd = `Invalid;
            regbRd = `Invalid;
            regcWr = `Invalid;
            regaAddr = `Zero;
            regbAddr = `Zero;
            regcAddr = `Zero;
            imm = `Zero;
          end
        else  
            case(inst_op)
                `Inst_ori:
                  begin
                    op = `Or;
                    regaRd = `Valid;
                    regbRd = `Invalid;
                    regcWr = `Valid;
                    regaAddr = inst[25:21];
                    regbAddr = `Zero;
                    regcAddr = inst[20:16];
                    imm = {16'h0, inst[15:0]};
                  end
                `Inst_addi:
                    begin
                        op = `Add;
                        regaRd = `Valid;
                        regbRd = `Invalid;
                        regcWr = `Valid;
                        regaAddr = inst[25:21];
                        regbAddr = `Zero;
                        regcAddr = inst[20:16];
                        imm = {16'b0,inst[15:0]};
                    end
                `Inst_andi:
                    begin
                        op = `And;
                        regaRd = `Valid;
                        regbRd = `Invalid;
                        regcWr = `Valid;
                        regaAddr = inst[25:21];
                        regbAddr = `Zero;
                        regcAddr = inst[20:16];
                        imm = {16'b0,inst[15:0]};
                    end
                `Inst_xori:
                    begin
                        op = `Xor;
                        regaRd = `Valid;
                        regbRd = `Invalid;
                        regcWr = `Valid;
                        regaAddr = inst[25:21];
                        regbAddr = `Zero;
                        regcAddr = inst[20:16];
                        imm = {16'b0,inst[15:0]};
                    end
                default:
                  begin
                    op = `Nop;
                    regaRd = `Invalid;
                    regbRd = `Invalid;
                    regcWr = `Invalid;
                    regaAddr = `Zero;
                    regbAddr = `Zero;
                    regcAddr = `Zero;
                    imm = `Zero;
                  end
            endcase
    [email protected](*)
      if(rst == `RstEnable)
          regaData = `Zero;
      else if(regaRd == `Valid)
          regaData = regaData_i;
      else
          regaData = imm;
    [email protected](*)
      if(rst == `RstEnable)
          regbData = `Zero;      
      else if(regbRd == `Valid)
          regbData = regbData_i;
      else
          regbData = imm; 
endmodule



           

       ④EX.v

MIPS单周期CPU的设计——I型指令的设计
`include "define.v"
module EX(
    input wire rst,
    input wire [5:0] op,    
    input wire [31:0] regaData,
    input wire [31:0] regbData,
    input wire regcWr_i,
    input wire [4:0]regcAddr_i,
    output reg [31:0] regcData,
    output wire regcWr,
    output wire [4:0] regcAddr
);    
    [email protected](*)
        if(rst == `RstEnable)
            regcData = `Zero;
        else
          begin
            case(op)
                `Or:
                    regcData = regaData | regbData;
                `Add:
                    regcData = regaData + regbData;
                `And:
                    regcData = regaData & regbData;
                `Xor:
                    regcData = regaData ^ regbData;
                default:
                    regcData = `Zero;
            endcase
          end
    assign regcWr = regcWr_i;
    assign regcAddr = regcAddr_i;
endmodule

           

       ⑤InstMem.v

MIPS单周期CPU的设计——I型指令的设计
`include "define.v"
module InstMem(
    input wire ce,
    input wire [31:0] addr,
    output reg [31:0] data
);
    reg [31:0] instmem [1023 : 0];    
    [email protected](*)      
        if(ce == `RomDisable)
          data = `Zero;
        else
          data = instmem[addr[11 : 2]];  //??????? 
    initial
      begin
        instmem [0] = 32'h34011100;//ori:32'h00000000 or 32'h00001100 =32'h00001100
        instmem [1] = 32'h20430000;//addi:32'h00000011 add 32'h00000000 = 32'h00000011
        instmem [2] = 32'h30850001;//andi:32'h00000001 and 32'h00000101 = 32'h00000001
        instmem [3] = 32'h38C70001;//xori:32'h00000001 xori 32'h00000011 = 32'h00000010
      end
endmodule
           

       ⑥RegFile.v

MIPS单周期CPU的设计——I型指令的设计
`include "define.v"
module RegFile(
    input wire clk,
    input wire rst,
    input wire we,
    input wire [4:0] waddr,
    input wire [31:0] wdata,
    input wire regaRd,
    input wire regbRd,
    input wire [4:0] regaAddr,
    input wire [4:0] regbAddr,
    output reg [31:0] regaData,
    output reg [31:0] regbData
);


    reg [31:0] reg32 [31 : 0];    
    [email protected](*)
        if(rst == `RstEnable)
            regaData = `Zero;
        else if(regaAddr == `Zero)
            regaData = `Zero;
        else
            regaData = reg32[regaAddr];
    [email protected](*)
        if(rst == `RstEnable)          
            regbData = `Zero;
        else if(regbAddr == `Zero)
            regbData = `Zero;
        else
            regbData = reg32[regbAddr];

    [email protected](*)
        if(we == `RamWrEnable)
            reg32[waddr] = wdata;
        else
            reg32[waddr] = `Zero;

    initial
        begin
            reg32[0] = 32'h00000001; //ori
            reg32[2] = 32'h00000011; //addi
            reg32[4] = 32'h00000101; //andi
            reg32[6] = 32'h00000011; //xori
        end  
endmodule
           

       ⑦MIPS.v

`include "define.v"
module MIPS(
    input wire clk,
    input wire rst,
    input wire [31:0] instruction,
    output wire romCe,
    output wire [31:0] instAddr
);
    wire [31:0] regaData_regFile, regbData_regFile;
    wire [31:0] regaData_id, regbData_id; 
    wire [31:0] regcData_ex;
    wire [5:0] op;    
    wire regaRd, regbRd;
    wire [4:0] regaAddr, regbAddr;
    wire regcWr_id, regcWr_ex;
    wire [4:0] regcAddr_id, regcAddr_ex;
    IF if0(
        .clk(clk),
        .rst(rst),
        .romCe(romCe), 
        .pc(instAddr)
    );
    ID id0(
        .rst(rst),        
        .inst(instruction),
        .regaData_i(regaData_regFile),
        .regbData_i(regbData_regFile),
        .op(op),
        .regaData(regaData_id),
        .regbData(regbData_id),
        .regaRd(regaRd),
        .regbRd(regbRd),
        .regaAddr(regaAddr),
        .regbAddr(regbAddr),
        .regcWr(regcWr_id),
        .regcAddr(regcAddr_id)
    );
    EX ex0(
        .rst(rst),
        .op(op),        
        .regaData(regaData_id),
        .regbData(regbData_id),
        .regcWr_i(regcWr_id),
        .regcAddr_i(regcAddr_id),
        .regcData(regcData_ex),
        .regcWr(regcWr_ex),
        .regcAddr(regcAddr_ex)
    );    
    RegFile regfile0(
        .clk(clk),
        .rst(rst),
        .we(regcWr_ex),
        .waddr(regcAddr_ex),
        .wdata(regcData_ex),
        .regaRd(regaRd),
        .regbRd(regbRd),
        .regaAddr(regaAddr),
        .regbAddr(regbAddr),
        .regaData(regaData_regFile),
        .regbData(regbData_regFile)
    );

endmodule


           

       ⑧Soc.v

MIPS单周期CPU的设计——I型指令的设计
module SoC(
    input wire clk,
    input wire rst
);
    wire [31:0] instAddr;
    wire [31:0] instruction;
    wire romCe;    
    MIPS mips0(
        .clk(clk),
        .rst(rst),
        .instruction(instruction),
        .instAddr(instAddr),
        .romCe(romCe)
    );    
    InstMem instrom0(
        .ce(romCe),
        .addr(instAddr),
        .data(instruction)
    );
endmodule


           

       ⑨soc_tb.v

`include "define.v"
module soc_tb;
    reg clk;
    reg rst;
    initial
      begin
        clk = 0;
        rst = `RstEnable;
        #100
        rst = `RstDisable;
        #10000 $stop;        
      end
    always #10 clk = ~ clk;
    SoC soc0(
        .clk(clk), 
        .rst(rst)
    );
endmodule


           

       仿真波形图:

MIPS单周期CPU的设计——I型指令的设计

       由左到右指令1、2、3、4依次是ori指令、addi指令、andi指令、xori指令的测试数据以及结果。因为没有加MEM模块,所以在EX中计算好的值,直接通过RegcData、RegcAddr和RegcWr三条线送入到RegFile里的we、waddr和w的data。

继续阅读