简单32位,3级流水线,MIPS CPU设计

我是一个数字设计的新人,做得不对的地方烦请大佬指正。

基本信息

工具:vivado 2019.1

FPGA型号:xc7k480tlffv1156-2L (Xilinx,7系列)

设计包含的指令:ADD,ADDI,SUBU,ORI,OR,AND,SW,LW,BEQ,MULTU,DIVU,MFHI,MFLO,MTHI,MTLO,以上都是MIPS指令集里有的,我自己又弄了MOVE,HALT,INITIAL,RET这几条,一共19条指令。MIPS有的就不做解释,我自己弄的解释一下,MOVE rs,rd(将rs寄存器的值移动到rd寄存器中);HALT系统停机;INITIAL这个是系统刚开始运行执行的,后续无用;RET写过汇编的知道,从call(调用)中返回主程序。

有参考这位大佬的文章(更确切的说是从这篇改过来的,改着改着就从单周期改成了流水线…),我刚开始也是想设计成单周期CPU,后来想着给自己增加难度,即放弃使用寄存器来当ram和rom,改用vivado自带的ip core,使用block memory来作为存储单元。因为block memory的读写有延迟没办法在一个时钟周期完成,就被迫改成了流水线型。

包含的模块:top, instructionsaver, controlunit, registerflie, extend, alu, datasaver, cp, tlb, hireg, loreg, muldiv, lwsw, cp, dcu, beq;

top模块

顶层模块,嗯,同样,pc也在这个里面,没有单独成块。top的功能简单没必要多说。

module CPU_CPU_sch_tb();

    wire [5:0] operation;
    wire [4:0] rs;
    wire [4:0] rt;
    wire [4:0] rd;
    wire [15:0] immediate_16;
    reg clk;
    wire [31:0] result;
    wire  [31:0] write_data;
    wire PCWre;
    wire ALUSrcB;
    wire ALUM2Reg;
    wire RegWre;
    wire DataMemRW;
    wire ExtSel;
    wire PCSrc;
    wire RegOut;
    wire Hi_sel_in;
    wire Hi_sel_out;
    wire lo_sel_in;
    wire lo_sel_out;
    wire Mul;
    wire Div;
    wire [31:0] instruction;
    reg [31:0] PC;
    wire [31:0] immediate_32;
    wire [31:0] imm_pc;
    wire [31:0] readData1;
    wire [31:0] readData2;
    wire  [63:0] muldiv_out;
    wire lw;
    wire sw;
     wire  beq;
    wire move;
    wire [31:0] result_to_DataMem;
    wire addr_erro_exception;
    wire add_overflow_exception;
    wire rom_en;
    wire ram_en;
    wire [14:0] addra;
    wire [31:0] epc_to_pc;
    wire pc_sel;
    wire clean_exc;
        wire ALUM;
        wire RegW;
        wire Hi_in;
        wire lo_in;
        wire hi_out;
        wire lo_out;
        wire mul;
        wire div;
        wire [1:0] ALUOp;
        reg [31:0] move_to_write_data;
        reg [31:0] move_to_write_data1;        
        wire mthi;
        wire mtlo;
        wire PCS;
        wire save_pc;
        reg [31:0] PC_reg;
        wire jmp_bk;
        wire clr;
        
    initial begin
    PC = 32'b0;
    clk = 0;
    end
   always #5
        clk = ~clk;

InstructionSave instructionsave(PC[7:2],clk,rom_en, save_pc,instruction);

assign operation[5:0] = instruction[31:26];
assign rs = instruction[25:21];
assign rt = instruction[20:16];
assign rd = instruction[15:11];
assign immediate_16 = instruction[15:0];

    hi_reg hireg(muldiv_out[63:32],  readData1,Hi_sel_in,Hi_sel_out,clk,mthi,clr,write_data);
    
    lo_reg loreg(muldiv_out[31:0], readData1 ,lo_sel_in,lo_sel_out,clk,mtlo,clr,write_data);

        muldiv Muldiv(readData1, readData2, Mul, Div,clk, clr, muldiv_out);
        
        lwsw Lwsw(readData1, immediate_32, lw, sw, clean_exc,clr, addr_erro_exception,  result_to_DataMem);

    ControlUnit controlunit (operation, clk,clr, ALUSrcB, ALUM, RegW,PCWre, DataMemRW,
                                   ExtSel, PCS , Hi_in, hi_out, lo_in, lo_out, mul, div,lw, sw,
                                   move,  RegOut,ram_en,rom_en,mthi,mtlo,jmp_bk,ALUOp);

    RegisterFile registerfile (rs, rt, rd, write_data, RegWre, RegOut,clk,clr, readData1,readData2);

    Extend extend(immediate_16, ExtSel, imm_pc,immediate_32);

    ALU alu(readData1, readData2, immediate_32, ALUSrcB,clean_exc, ALUOp,clk, clr,add_overflow_exception, result);

    DataSaver datasaver(result,addra, readData2, DataMemRW, addr_erro_exception,ALUM2Reg,clk, ram_en,clr,write_data);
    
    CP0_register cp(
                            .clk(clk),
                            .badvaddr(result_to_DataMem),
                            .pc_to_epc(PC),
                            .addr_erro_exception(addr_erro_exception),
                            .add_overflow_exception(add_overflow_exception),
                            .epc_to_pc(epc_to_pc),
                            .pc_sel(pc_sel),
                            .clean_exc(clean_exc)
                            );
       
       TLB tlb(
                    .clk(clk),
                    .clr(clr),
                    .result_to_DataMem(result_to_DataMem),
                    .addra(addra)
                    );
                
         dcu DCU(
                        .clk(clk),
                        .clr(clr),
                        .ALUM(ALUM),
                       . RegW(RegW),
                       .Hi_in(Hi_in),
                       . lo_in(lo_in),
                       .hi_out(hi_out),
                       .lo_out(lo_out),
                      . mul(mul),
                      .div(div),
                    . ALUM2Reg(ALUM2Reg),
                    . RegWre(RegWre),
                    . Hi_sel_in(Hi_sel_in),
                      .lo_sel_in(lo_sel_in),
                      .Hi_sel_out(Hi_sel_out),
                      .lo_sel_out(lo_sel_out),
                    . Mul(Mul),
                    .PCS(PCS),
                    .PCSrc(PCSrc),
                    .Div(Div)              
                        );
                BEQ     beq1(
                                    .clk(clk),
                                    .jmp_bk(jmp_bk),
                                    .PCS(PCS),
                                    .readData1(readData1),
                                    .readData2(readData2),
                                    .beq(beq),
                                    .clr(clr)
                                    );
  
              always@(posedge clk) 
              begin 
                            if(move == 1'b1)            //用来执行move指令
                                    move_to_write_data <= readData1;
                                    else move_to_write_data <= 32'bZZZZ_ZZZZ; 
            end            
            always@(negedge clk)
                    begin               //这里是因为时许问题,所以延迟了数据传递
                                    move_to_write_data1 <= move_to_write_data;                     
                    end                         
        assign     write_data = move_to_write_data1;  
  
always@(posedge clk) 
begin
   if  ( PCWre == 1'b1)
   begin
            if(pc_sel == 1'b1)      
                        PC <= epc_to_pc;    //处理例外的pc返回值
                        else if(jmp_bk == 1'b1)
                                    PC <= PC_reg;       //ret将beq的下一条pc附值给pc,即返回主程序继续执行
                                else if(beq == 1'b0)
                                PC <= PC + 4;
                                else PC <= PC + 4 + imm_pc; //beq跳转
       end           else PC <= PC;
end
            always@(posedge clk)                //beq跳转指令后,将beq后一条pc保存,RET指令是读取
            begin
                    if(save_pc == 1'b1)
                            PC_reg <= PC;
                            else PC_reg <= PC_reg;
            end  

endmodule

instructionsaver

其实就是ROM,用来存储要执行的指令的。这里我用了vivado自带的IP core,生成了block memory(single port ROM,width 32,depth 64),怎么用IP core以及用.coe文件初始化ROM站内有很多文章,我就不重复了。

这是我测试用的coe文件,很简单的。

memory_initialization_radix=2;
memory_initialization_vector=
0_00000000000000000000000000000000
4_100111_00101_11111_0000000000000000
8_100111_00101_11110_0000000000000100
12_110011_01101_00010_11101_00000000000
16_000010_11111_11101_00110_00000000000
20_100000_00111_00000_00110_00000000000
24_100110_00101_00110_0000000000001000
28_100111_00101_01000_0000000000001100
32_010010_01000_11101_11101_00000000000
36_010001_11101_11111_11101_00000000000
40_100111_00101_01001_0000000000010000
44_110000_11111_11110_00000000000000011
48_100111_00101_01100_0000000000100100
52_100111_00101_01101_0000000000101000
56_110000_11111_11110_0000000000000100
60_11111100000000000000000000000000

64_001000_11101_01001_0000000000000000
68_111000_00000_00000_01010_00000000000
72_111100_00000_00000_01011_00000000000
76_001001_00000000000000000000000000

80_110111_01101_01100_0000000000000000
84_111000_00000_00000_01110_00000000000
88_111100_00000_00000_01111_00000000000
92_001001_00000000000000000000000000

第一个_前的0,4,8,12—92,表示该条指令的pc值,真正作为coe文件时要删掉,还有指令中的下划线“_”也要删掉。64—76和80—92算是两个子程序(虽然就是一个乘法,一个除法),乘法在44被调用,但此时48也被读到了流水线内执行,所以要清空流水线,换句话说就是48想被执行但被clr(beq模块产生,用来清空流水线)一巴掌拍回去了,即pc:40–>44–>48–>64–>68…;同样除法在56被调用,pc:52–>56–>60–>80–>84…
用RET指令返回主程序时,pc:72–>76–>80(clr:啪!给我滚回去)–>48–>52…
如图:
在这里插入图片描述

module InstructionSave(
    input [5:0] pc_addr,
    input clk,rom_en,
    output reg save_pc,
    output  [31:0] instruction
    );
    wire [31:0] ins;
    reg [5:0] inst;

blk_mem_gen_1 ROM (
  .clka(clk),    // input wire clka
  .ena(rom_en),      // input wire ena
  .addra(pc_addr),  // input wire [4 : 0] addra
  .douta(ins)  // output wire [31 : 0] douta  输出端居然只能是wire型,不明白为啥
);
        assign instruction = ins;
        always@(ins)
                inst <= ins[31:26];
        always@(inst)
        begin
                    
                if(inst == 6'b110000)       //监测到beq指令就给出保存下一条pc的信号
                        save_pc <= 1'b1;
                        else save_pc <= 1'b0;
        end                    
            
                   
endmodule

controlunit

根据不同的指令来产生不同控制信号的模块,具体每一条信号的含义,在代码中有注释。

module ControlUnit(
    input [5:0] operation,
    input clk,clr,
    
    output reg ALUSrcB,     //选择alu的一个输入数据是readata2(==0)还是immediate_32(==1)
    output reg ALUM,        //选择往registerfile输入的是来自alu(==0)还是datasaver(==1)的数据
    output reg RegW,        //registerfile(==1)的写使能信号
    output reg PCWre,       //pc(==1)自增的使能信号
    output reg  DataMemRW,      //SRAM(==1)的写使能信号
    output reg ExtSel,      //extend模块的控制信号 ,符号扩展(==1)和无符号扩展(==0)
    output reg PCS,         //beq(==1)指令的控制信号
    output reg Hi_in,       //hireg(==1)的写入控制信号
    output reg hi_out,      //hireg(==1)的输出控制信号
    output  reg lo_in,      //loreg(==1)的写入控制信号
    output  reg lo_out,     //loreg(==1)的输出控制信号
    output reg mul,         //muldiv模块的控制信号,mul==1 && div==0无符号乘法运算
    output reg div,         //                                      mul==0 && div==1无符号除法运算
    output reg lw,          //lwsw(==1)控制信号,
    output reg sw,          //lwsw(==1)控制信号,
    output reg move,        //move (==1)
    output reg  RegOut,     //registerfile模块中用来选择rt(==0)或者rd(==1)
    output reg ram_en,      //SRAM(==1)使能
    output reg rom_en,      //ROM(==1)使能
    output reg mthi,        //MTHI(==1)指令信号,将rs寄存器的值移动到hireg
    output reg mtlo,        //MTLO(==1)指令信号,将rs寄存器的值移动到loreg
    output reg jmp_bk,      //RET(==1)指令,用来从调用返回会主程序
    output  reg [1:0] ALUOp     //alu计算控制信号
    );
    parameter ADD = 6'b110011, ADDI = 6'b000001, SUBU = 6'b000010, ORI = 6'b010000,
                 AND = 6'b010001, OR = 6'b010010, MOVE = 6'b100000, SW = 6'b100110,
                 LW = 6'b100111, BEQ = 6'b110000, HALT = 6'b111111,MFHI = 6'b111000,
                 MFLO = 6'b111100,MTHI = 6'b101010,MTLO = 6'b010101, MULTU = 6'b001000,
                DIVU = 6'b110111,INITIAL = 6'b000000,RET = 6'b001001;
    always@( operation) 
    begin         
                if(clr == 1'b1)
                              begin
                                        ALUOp <= 3'bzz; jmp_bk <= 1'b0;
                                        PCWre <= 1'b1; rom_en <= 1'b1;
                                        mthi <= 1'bz; mtlo <= 1'bz;
                                        { ALUSrcB, ALUM, RegW,  lw} <= 4'b00zz;
                                        {DataMemRW, ExtSel,  RegOut, sw} <= 4'b001z; 
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;
                                         PCS <= 1'b0;     move <= 1'b0;     ram_en <= 1'b1;                              
                                end
        else     case(operation)
                    ADD:    begin
                                        ALUOp <= 3'b00; jmp_bk <= 1'b0;
                                        PCWre <= 1'b1;rom_en <= 1'b1;
                                        mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW,  lw} <= 4'b001z;
                                        {DataMemRW, ExtSel,  RegOut, sw} <= 4'b001z; 
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;
                                         PCS <= 1'b0;     move <= 1'b0;     ram_en <= 1'b1;                              
                                end
                     ADDI:      begin
                                              ALUOp <= 2'b00; jmp_bk <= 1'b0;
                                              PCWre <= 1'b1;rom_en <= 1'b1;
                                              mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW, lw} <= 4'b101z;
                                        {DataMemRW, ExtSel,  RegOut, sw} <= 4'b010z;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;          
                                         PCS <= 1'b0;          move <= 1'b0;       ram_en <= 1'b1;                                                      
                                    end 
                     SUBU:         begin
                                             ALUOp <= 2'b01; jmp_bk <= 1'b0;
                                             PCWre <= 1'b1;rom_en <= 1'b1;
                                             mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW,  lw} <= 4'b001z;
                                        {DataMemRW, ExtSel,  RegOut, sw} <= 4'b001z;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;        
                                         PCS <= 1'b0;         move <= 1'b0;         ram_en <= 1'b1;                                                          
                                    end 
                      ORI:         begin
                                             ALUOp <= 2'b10; jmp_bk <= 1'b0;
                                             PCWre <= 1'b1;rom_en <= 1'b1;
                                             mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW,  lw} <= 4'b101z;
                                        {DataMemRW, ExtSel,  RegOut, sw} <= 4'b000z; 
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;            
                                         PCS <= 1'b0;         move <= 1'b0;           ram_en <= 1'b1;                                                      
                                    end 
                      AND:         begin
                                              ALUOp <= 2'b11; jmp_bk <= 1'b0;
                                              PCWre <= 1'b1;rom_en <= 1'b1;
                                              mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW,  lw} <= 4'b001z;
                                        {DataMemRW, ExtSel,  RegOut, sw} <= 4'b001z;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;            
                                         PCS <= 1'b0;         move <= 1'b0;       ram_en <= 1'b1;                                                         
                                    end  
                      OR:         begin
                                             ALUOp <= 2'b10; jmp_bk <= 1'b0;
                                             PCWre <= 1'b1;rom_en <= 1'b1;
                                             mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW,  lw} <= 4'b001z;
                                       {DataMemRW, ExtSel,  RegOut, sw} <= 4'b001z;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;       
                                         PCS <= 1'b0;          move <= 1'b0;   ram_en <= 1'b1;                                                                  
                                    end
                      MOVE:         begin
                                            ALUOp <=2'bzz; jmp_bk <= 1'b0;
                                            PCWre <= 1'b1;rom_en <= 1'b1;
                                            mthi <= 1'b0; mtlo <= 1'b0;
                                        {ALUSrcB, ALUM, RegW, lw} <= 4'bzz1z;
                                       {DataMemRW, ExtSel,  RegOut, sw} <= 4'b001z;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;   
                                         PCS <= 1'b0;   move <= 1'b1;      ram_en <= 1'b1;                                                                    
                                    end 
                     SW:         begin
                                             ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                             PCWre <= 1'b1;rom_en <= 1'b1;
                                             mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW,  lw} <= 4'bzz0z;
                                       {DataMemRW, ExtSel,  RegOut, sw} <= 4'b1101; 
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;        
                                         PCS <= 1'b0;       move <= 1'b0;    ram_en <= 1'b1;                                                                        
                                    end
                     LW:         begin
                                              ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                              PCWre <= 1'b1;rom_en <= 1'b1;
                                              mthi <= 1'b0; mtlo <= 1'b0;
                                        {ALUSrcB, ALUM, RegW, lw} <= 4'bz111;
                                        {DataMemRW, ExtSel,  RegOut, sw} <= 4'b010z;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;       
                                         PCS <= 1'b0;         move <= 1'b0;     ram_en <= 1'b1;                                                                  
                                    end
                     BEQ:         begin         
                                             ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                             PCWre <= 1'b1;rom_en <= 1'b1;
                                             mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW, lw} <= 4'bzzzz;
                                        {DataMemRW, ExtSel, RegOut, sw} <= 4'b01zz;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;       
                                         PCS <= 1'b1;       move <= 1'b0;         ram_en <= 1'b1;                                                                 
                                    end
                      RET:         begin
                                             ALUOp <= 2'b00; jmp_bk <= 1'b1;
                                             PCWre <= 1'b1;rom_en <= 1'b1;
                                             mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW,  lw} <= 4'bzzzz;
                                       {DataMemRW, ExtSel,  RegOut, sw} <= 4'b00zz;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;       
                                         PCS <= 1'b0;          move <= 1'b0;   ram_en <= 1'b1;                                                                  
                                    end                                    
                     HALT:         begin
                                              ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                              PCWre <= 1'b0;rom_en <= 1'b0;
                                              mthi <= 1'b0; mtlo <= 1'b0;
                                        {ALUSrcB, ALUM, RegW, lw} <= 4'bzzzz;
                                       {DataMemRW, ExtSel, RegOut, sw} <= 4'bz0zz;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;       
                                         PCS <= 1'b0;       move <= 1'b0;        ram_en <= 1'b1;                                                               
                                    end
                     MFHI:         begin
                                             ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                             PCWre <= 1'b1;rom_en <= 1'b1;
                                             mthi <= 1'b0; mtlo <= 1'b0;
                                        {ALUSrcB, ALUM, RegW, lw} <= 4'bzz1z;
                                       {DataMemRW, ExtSel, RegOut, sw} <= 4'b001z;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bz1zzzz;         
                                         PCS <= 1'b0;         move <= 1'b0;      ram_en <= 1'b1;                                                             
                                    end   
                     MFLO:         begin
                                            ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                            PCWre <= 1'b1;rom_en <= 1'b1;
                                            mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW, lw} <= 4'bzz1z;
                                        {DataMemRW, ExtSel, RegOut, sw} <= 4'b001z;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzz1zz;         
                                         PCS <= 1'b0;       move <= 1'b0;     ram_en <= 1'b1;                                                                 
                                    end   
                     MTHI:         begin
                                             ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                             PCWre <= 1'b1;rom_en <= 1'b1;
                                             mthi <= 1'b1; mtlo <= 1'b0;
                                        {ALUSrcB, ALUM, RegW, lw} <= 4'bzzzz;
                                        {DataMemRW, ExtSel,  RegOut, sw} <= 4'b00zz; 
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;      
                                         PCS <= 1'b0;       move <= 1'b0;        ram_en <= 1'b1;                                                              
                                    end 
                     MTLO:         begin
                                            ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                            PCWre <= 1'b1;rom_en <= 1'b1;
                                            mthi <= 1'b0; mtlo <= 1'b1;
                                        {ALUSrcB, ALUM, RegW, lw} <= 4'bzzzz;
                                        {DataMemRW, ExtSel, RegOut, sw} <= 4'b00zz;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzzz;      
                                         PCS <= 1'b0;        move <= 1'b0;    ram_en <= 1'b1;                                                                     
                                    end   
                     MULTU:         begin
                                              ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                              PCWre <= 1'b1;rom_en <= 1'b1;
                                              mthi <= 1'b0; mtlo <= 1'b0;
                                        {ALUSrcB, ALUM, RegW, lw} <= 4'bzzzz;
                                        {DataMemRW, ExtSel,  RegOut, sw} <= 4'b00zz;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'b1z1z10;       
                                         PCS <= 1'b0;        move <= 1'b0;     ram_en <= 1'b1;                                                                 
                                    end
                     DIVU:         begin
                                             ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                             PCWre <= 1'b1;rom_en <= 1'b1;
                                             mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW, lw} <= 4'bzzzz;
                                       {DataMemRW, ExtSel, RegOut, sw} <= 4'b00zz;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'b1z1z01;    
                                         PCS <= 1'b0;     move <= 1'b0;     ram_en <= 1'b1;                                                                       
                                    end  
                    INITIAL:       begin
                                             ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                             PCWre <= 1'b1;rom_en <= 1'b1;
                                             mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW, lw} <= 4'bzzzz;
                                       {DataMemRW, ExtSel, RegOut, sw} <= 4'bz0zz;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzz;   
                                         PCS <= 1'b0;        move <= 1'b0;     ram_en <= 1'b1;                                                                      
                                    end          
                    default:       begin
                                             ALUOp <= 2'bzz; jmp_bk <= 1'b0;
                                             PCWre <= 1'b0;rom_en <= 1'b1;
                                             mthi <= 1'b0; mtlo <= 1'b0;
                                        { ALUSrcB, ALUM, RegW, lw} <= 4'bzzzz;
                                       {DataMemRW, ExtSel, RegOut, sw} <= 4'b00zz;  
                                        {Hi_in, hi_out, lo_in, lo_out, mul, div} <= 6'bzzzzz;   
                                         PCS <= 1'b0;        move <= 1'b0;     ram_en <= 1'b0;                                                                      
                                    end                                                                   
                        endcase  
                        end                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
endmodule

registerfile

实际就是定义了一堆寄存器,用来暂存数据。其中clr信号用来清空流水线,BEQ和RET指令会使clr=1,BEQ和RET之前的指令要执行完毕,但BEQ和RET指令后紧跟的一条指令不能执行,因为在执行跳转时整个cpu的存储单元的数值不能有任何变化。

 module RegisterFile(
   input    [4:0]   rs,rt,rd,
    input   [31:0] write_data,
    input       RegWre,RegOut, clk,clr,
    output [31:0] readData1,readData2
    );

   reg [4:0] save;
   reg [4:0] save_addr;
   
   always@(negedge clk)
   begin
                if(clr == 1'b1)         //clr信号用来清空流水线,
                begin 
                        save <= 5'bzzzzz;       //对reg写入高阻态,不知道实际应用有没有问题
                        save_addr <= 5'bzzzzz;              
                end 
             else begin   save <= (RegOut == 0) ? rt : rd;      //因为是3级流水,所以要对写回的地址进行2级缓存//
                                 save_addr <= save;                     //-------------------------------------------------------------------------//
                                 end
   end 
   
    reg     [31:0]  register [31:0]; 

    initial begin
        $readmemb("my_test_ram.txt", register);
   end
   
    assign readData1 =  register[rs]; //read data
    assign readData2 =  register[rt]; 

    always @(posedge clk)
     begin
            
            if (RegWre == 1'b1) 
             begin 
            register[save_addr] <= write_data;      //write data
                  end 
    end
endmodule

extend

对16位立即数扩展而已,没啥好说的

module Extend(
    input [15:0] immediate_16,
    input ExtSel,
    output [31:0] imm_pc,
    output [31:0] immediate_32 
    );
    wire [15:0] imm;
        assign imm = immediate_16 << 2;
        assign imm_pc =  (ExtSel==1'b1) ? {{16{imm[15]}}, imm[15:0]} : {{16{1'b0}}, imm[15:0]};
    assign immediate_32 = (ExtSel==1'b1) ? {{16{immediate_16[15]}}, immediate_16[15:0]} : {{16{1'b0}}, immediate_16[15:0]};

endmodule

alu

算术逻辑单元,这个不会看不懂吧。

module ALU( 
    input [31:0] readData1,
    input [31:0] readData2,
    input [31:0] immediate_32,
    input ALUSrcB,clean_exc,
    input [1:0] ALUOp,
    input clk,clr,
    
    output  reg add_overflow_exception,
    output reg [31:0] result
    );
        
    wire [31:0] alu;

        assign alu = (ALUSrcB == 0) ? readData2 : immediate_32;
        
    always@(posedge clk) 
    begin  
                if(clr == 1'b1)
                begin 
                        result <= 32'bZZZZ_ZZZZ;   
                        add_overflow_exception <= 1'b0;
                end 
            else if(clean_exc == 1'b1)
                add_overflow_exception <= 1'b0;
    else     case (ALUOp)
            3'b00: {add_overflow_exception,result} <= readData1 + alu;//ADD
            3'b01: begin result <= alu - readData1; add_overflow_exception <= 1'b0;end  //SUBU
            3'b10: begin result <= readData1 | alu; add_overflow_exception <= 1'b0;end  //OR
            3'b11: begin result <= readData1 & alu; add_overflow_exception <= 1'b0;end  //AND
            default:begin  result <= 32'bZZZZ_ZZZZ;
                             add_overflow_exception <= 1'b0;
                     end
        endcase
end
endmodule

datasaver

就是SRAM(single port RAM,width 32,depth 1024),不知道为啥depth超过1024就读不出来,所以深度1024的话,addr就只有10位,而我的位宽是32位,所以32位地址得砍掉12位。。。。,看着网上虚拟地址转物理地址脑子懵懵的,所以就瞎写了一个tlb,真的是瞎写的,不然直接砍掉12位感觉有点亏。

module DataSaver(
    input [31:0] from_alu_result,   
    input [14:0] addra,
    input [31:0] readData2,
    input DataMemRW,
    input addr_erro_exception,
    input ALUM2Reg,
    input clk,ram_en,clr,
    
    output  reg [31:0] to_write_data
    );
        wire [31:0] dout;

        wire [9:0] addr;
        assign addr = addra[9:0];
blk_mem_gen_0 SRAM (
  .clka(clk),    // input wire clka
  .ena(ram_en),      // input wire ena
  .wea(DataMemRW),      // input wire [0 : 0] wea
  .addra(addr),  // input wire [9 : 0] addra
  .dina(readData2),    // input wire [31 : 0] dina
  .douta(dout)  // output wire [31 : 0] douta
);

    always@(negedge clk)
    begin       
                if(clr == 1'b1)
                to_write_data = 32'bZZZZ_ZZZZ;
              else  if(ALUM2Reg == 1'b0)
                            to_write_data =from_alu_result;
                            else if(ALUM2Reg == 1'b1)
                                begin
                                        if(addr_erro_exception == 1'b1)
                                        to_write_data = 32'bZZZZ_ZZZZ;
                                            else   to_write_data = dout;
                                            end 
                                    else to_write_data = 32'bZZZZ_ZZZZ;
    end       
endmodule

cp

CP0寄存器组,用来处理中断例外,以及存储cpu的一些基本信息,不过我没写那么多,处理中断?没有中断,所以不用处理(滑稽);例外包括两种:算术溢出例外和地址错例外。前一种出现后应该跳转到处理例外的程序入口的,但这只是一个裸奔的CPU,就没搞程序入口,直接是有算术溢出例外,直接跳转到零地址从新开始跑,所以看见转圈首先看看是不是add_overflow_exception = 1了;后一种地址错例外完全就是指令没写好,写指令时就应该避免的。

module CP0_register(
                                input clk,
                                input [31:0] badvaddr,
                                input [31:0] pc_to_epc,
                                input addr_erro_exception,
                                input add_overflow_exception,
                                
                                output reg [31:0] epc_to_pc,
                                output reg pc_sel,
                                output reg clean_exc
                             );
                             reg [31:0] BadVaddr;
                             reg [31:0] EPC;
                             wire  exc;
                             initial 
                             begin
                                            BadVaddr = 32'b0;
                                            EPC = 32'b0;
                             end 
                             
                             always@(posedge clk)           //BadVaddr
                             begin
                                        if(addr_erro_exception)
                                                BadVaddr <= badvaddr;
                                                else BadVaddr <= BadVaddr;
                             end 
          
                            always@(posedge clk)                       //EPC
                             begin  EPC <= pc_to_epc;
                             end 
                             
                            assign exc = addr_erro_exception | add_overflow_exception;
                            
                            always@(negedge clk)
                            begin
                                        if(exc == 1'b1)
                                          begin      epc_to_pc <= 32'b0;
                                                        pc_sel <= 1'b1;
                                                        clean_exc <= 1'b1;
                                          end 
                                          else begin 
                                                        epc_to_pc <= 32'bZZZZ_ZZZZ;
                                                        clean_exc <= 1'b0;   
                                                        pc_sel <= 1'b0;                                                   
                                                    end 
                                                        
                            end 

endmodule

tlb

这个真的是瞎写的,不用看了。

module TLB(
                    input [31:0] result_to_DataMem,
                    input clk,clr,
                    
                    output  reg [14:0] addra
                     );
                     wire [4:0] num;
                     assign num = result_to_DataMem[31] +result_to_DataMem[30] + result_to_DataMem[29] + result_to_DataMem[28]
                                          + result_to_DataMem[27] +result_to_DataMem[26] + result_to_DataMem[25] + result_to_DataMem[24]
                                          + result_to_DataMem[23] +result_to_DataMem[22] + result_to_DataMem[21] + result_to_DataMem[20]
                                          + result_to_DataMem[19] +result_to_DataMem[18] + result_to_DataMem[17] + result_to_DataMem[16]
                                          + result_to_DataMem[15] +result_to_DataMem[14] + result_to_DataMem[13] + result_to_DataMem[12];
                     always@(negedge clk)
                            begin       
                                        if(clr == 1'b1)
                                        addra <= 15'bZZZZZ;
                                   else     casex(num)
                                                5'b00000:    addra <= {3'b000,result_to_DataMem[11:0]};
                                                5'b00010:    addra <= {3'b001,result_to_DataMem[11:0]};
                                                5'b00001:    addra <= {3'b010,result_to_DataMem[11:0]};
                                                5'b00011:    addra <= {3'b011,result_to_DataMem[11:0]};
                                                5'b001x0:    addra <= {3'b100,result_to_DataMem[11:0]};
                                                5'b0100x:    addra <= {3'b101,result_to_DataMem[11:0]};
                                                5'b011x0:    addra <= {3'b110,result_to_DataMem[11:0]};
                                                default:        addra <= {3'b111,result_to_DataMem[11:0]};        
                                        endcase 
                            end 
endmodule

hireg

HI寄存器,用来存储乘法和除法的高32位结果。不明白为啥MIPS要专门定义这么一个寄存器。

module hi_reg(
                        input [31:0] hi_in,
                        input [31:0] readData1,
                        input Hi_sel_in,Hi_sel_out,clk,mthi,clr,
                        
                        output  reg [31:0] hi_out
                 );                
                 reg [31:0] hi_mem;
                 
                 initial begin
                        hi_mem = 32'b0;
                 end
                 
            always@(negedge clk)
            begin   
                    if(clr == 1'b1)
                            hi_out <= 32'bZZZZ_ZZZZ;
                        else if(Hi_sel_out == 1'b1)
                            begin
                                        hi_out <= hi_mem;
                            end else hi_out <= 32'bZZZZ_ZZZZ;
            end 
            
           always@(posedge clk)
           begin        
                            if(clr == 1'b0)
                           begin 
                            if(Hi_sel_in == 1'b1)
                                        hi_mem <= hi_in;
                                        else    if(mthi == 1'b1)
                                                      hi_mem <= readData1;
                                                      else hi_mem <= hi_mem;
           end  end                                  
endmodule

loreg

LO寄存器,用来存储乘法和除法的低32位结果。

module lo_reg(
                        input [31:0] lo_in,
                        input [31:0] readData1,
                        input lo_sel_in,lo_sel_out,clk,mtlo,clr,
                        output reg  [31:0] lo_out
                 );          
                 reg [31:0] lo_mem;
                 
                 initial begin
                        lo_mem = 32'b0;
                 end            
                          
            always@(negedge clk)
            begin       
                    if(clr == 1'b1)
                    lo_out <= 32'bZZZZ_ZZZZ;
                   else     if(lo_sel_out == 1'b1)
                            begin
                                        lo_out <= lo_mem;
                            end else lo_out <= 32'bZZZZ_ZZZZ;
            end 
            
           always@(posedge clk)
           begin       if(clr == 1'b0)
                          begin  if(lo_sel_in == 1'b1)
                                        lo_mem <= lo_in;
                                        else if(mtlo == 1'b1)
                                                    lo_mem <= readData1;
                                                    else lo_mem <= lo_mem;
           end                     end                     
endmodule

muldiv

乘除法模块,没啥需要解释的。

module muldiv(
                    input [31:0]  readData1,
                    input [31:0] readData2,
                    input Mul,Div,clk,clr,
                    output reg [63:0] muldiv_out
                    );
                    
                    always@(posedge clk)
                    begin       
                                    if(clr == 1'b1)
                                    muldiv_out <= 64'bZZZZ_ZZZZ_ZZZZ_ZZZZ;
                                  else  if((Mul==1'b1) && (Div==1'b0))
                                            muldiv_out <= readData1 * readData2;
                                            else  if((Mul==1'b0) && (Div==1'b1))
                                                   begin        if(readData2==32'b0)
                                                                    muldiv_out <= 64'bZZZZ_ZZZZ_ZZZZ_ZZZZ;
                                                                    else begin
                                                                muldiv_out[63:32] <= readData1/readData2;
                                                                muldiv_out[31:0] <= readData1%readData2;end
                                                   end 
                                                   else muldiv_out <= 64'bZZZZ_ZZZZ_ZZZZ_ZZZZ;
                    end 

endmodule

lwsw

lw:从SRAM取数据到registerfile
sw:将registerfile数据存到SRAM
其实这里有一点我没想明白,lw和sw指令要求地址必须是4的倍数,换成二进制就是最低两位必须是00;32位是4字节,难道读写数据不是整个32位4字节,还能对4个字节中的某些字节读写???或许我太菜了搞不明白,或者理解错了。

module lwsw(
                    input [31:0] readData1,
                    input [31:0] immediate_32,
                    input lw,sw,clean_exc,clr,

                    output  reg addr_erro_exception,
                    output reg   [31:0] result_to_DataMem
                 );
                reg  [31:0] lw_in;
                reg    [31:0] sw_in;
                
        always@(lw, sw,clean_exc)
                    begin   
                    if(clr ==1'b1)  
                    begin
                                addr_erro_exception = 1'b0;
                                 result_to_DataMem = 32'bZZZZ_ZZZZ;
                    end       
                  else      if(clean_exc == 1'b1)
                            addr_erro_exception = 1'b0;
                         else begin   if(lw ==1'b1)  
                                 begin  lw_in = readData1 + immediate_32;
                                              if((lw_in[1]==1'b0) && (lw_in[0]==1'b0))
                                             begin     
                                                  addr_erro_exception = 1'b0; 
                                                 result_to_DataMem =  lw_in;
                                                 end
                                        else 
                                                begin   addr_erro_exception = 1'b1; 
                                              result_to_DataMem = 32'bZZZZ_ZZZZ;
                                              end
                                                          end                                  
                            else  if(sw == 1'b1)
                                                 begin sw_in = readData1 + immediate_32;
                                                                  if((sw_in[1] == 1'b0) && (sw_in[0] == 1'b0))
                                                                      begin       
                                                                          addr_erro_exception = 1'b0;     
                                                                         result_to_DataMem = sw_in; 
                                                                         end 
                                                        else begin 
                                                                    addr_erro_exception = 1'b1; 
                                                                    result_to_DataMem = 32'bZZZZ_ZZZZ; 
                                                                    end   
                                                                       end                                                                                 
                                    else    begin addr_erro_exception = 1'bz;   
                                                       result_to_DataMem = 32'bZZZZ_ZZZZ; 
                                              end
                                                                                     
                      end end 
endmodule

dcu

其实是delay controlunit的简写啦,对某些控制信号进行缓存输出用。

module dcu(
                    input clk,clr,
                    input ALUM,
                    input RegW,
                    input Hi_in,  lo_in, hi_out,lo_out,
                    input mul,div,PCS,
                    
                    output reg ALUM2Reg,
                    output reg RegWre,
                    output reg Hi_sel_in, lo_sel_in,Hi_sel_out,lo_sel_out,
                    output reg Mul,Div,PCSrc
                  );
                  
                  reg alum;
                  reg regw1;
                  reg HI_IN,LO_IN;
                  reg HI_OUT,LO_OUT;
                  reg MUL,DIV;
                  reg pcs;

                  always@(posedge clk)
                  begin
                                      ALUM2Reg <= ALUM;
                  end 
                 always@(negedge clk)
                            if(clr == 1'b1)
                            begin 
                                    regw1 <= 1'bz;
                                    RegWre <= 1'bz;
                                    Hi_sel_out <= 1'bz;
                                    lo_sel_out <= 1'bz;
                                    HI_IN <= 1'Bz;
                                    Hi_sel_in <= 1'b1;
                                    LO_IN <= 1'B1;
                                    lo_sel_in <= 1'b1;
                                    Mul <= 1'b1;
                                    Div <= 1'b1;
                            end 
                else begin
                                regw1 <= RegW;
                                RegWre <= regw1;
                                Hi_sel_out <= hi_out;
                                lo_sel_out <= lo_out;
                                HI_IN <= Hi_in;
                                Hi_sel_in <= HI_IN;
                                LO_IN <= lo_in;
                                lo_sel_in <= LO_IN;
                                Mul <= mul;
                                Div <= div;                            
                 end 
endmodule

BEQ

用来执行跳跃的模块,clr信号就是这里产生的。

module BEQ(
                    input clk,jmp_bk,
                    input PCS,
                    input [31:0] readData1,readData2,
                    
                    output reg beq,clr
                  );
                  always@(negedge clk)
                        begin
                                    if(PCS == 1'b1)
                                          begin                
                                                if(readData1 == readData2) 
                                                        begin    beq <= 1'b1;clr <= 1'b1;end
                                                            else begin beq <= 1'b0;clr <= 1'b0;end
                                        end 
                                        else if(jmp_bk == 1'b1) 
                                                          begin beq <= 1'b0;clr <= 1'b1;end
                                                               else begin  beq <= 1'b0;clr <= 1'b0;end
                        end 
endmodule

讲真,刚开始写时就是懵懵比比的,没有看别人的就急着动手,结果就是这都是些啥啊,理不清关系一样,跑出来的结果全是xxx。后来看了那位大佬写的才慢慢明白了点,然后就自己改,前前后后改了好几个版本,虽然还有瑕疵,但也算是我比较满意的版本了。吸取的教训就是磨刀不误砍柴工,改成流水线时,一点一点你理清脉络,含快就写好了,几个小错改改就弄好了。加油!小菜鸟。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值