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

本文介绍了作者作为数字设计新手,如何设计一个3级流水线的MIPS CPU,包括使用Vivado 2019.1,FPGA型号为xc7k480tlffv1156-2L。设计覆盖了19条MIPS指令,并额外添加了几条自定义指令。由于Block Memory的延迟,CPU被改为了流水线型。文章详细阐述了各个模块的功能,如ROM(instructionsaver)、控制单元(controlunit)、寄存器文件(registerfile)等,并分享了设计过程中的思考和经验教训。
摘要由CSDN通过智能技术生成

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

基本信息

工具: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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值