简单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 beginPC = 32'b0;clk = 0;endalways #5clk = ~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)
beginif ( PCWre == 1'b1)beginif(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;
endalways@(posedge clk) //beq跳转指令后,将beq后一条pc保存,RET指令是读取beginif(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)beginif(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寄存器的值移动到hiregoutput reg mtlo, //MTLO(==1)指令信号,将rs寄存器的值移动到loregoutput 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)beginALUOp <= 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; endelse case(operation)ADD: beginALUOp <= 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; endADDI: beginALUOp <= 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: beginALUOp <= 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: beginALUOp <= 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: beginALUOp <= 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: beginALUOp <= 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; endMOVE: beginALUOp <=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: beginALUOp <= 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; endLW: beginALUOp <= 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; endBEQ: 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; endRET: beginALUOp <= 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: beginALUOp <= 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; endMFHI: beginALUOp <= 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: beginALUOp <= 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: beginALUOp <= 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: beginALUOp <= 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: beginALUOp <= 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; endDIVU: beginALUOp <= 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: beginALUOp <= 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: beginALUOp <= 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)beginif(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; //-------------------------------------------------------------------------//endend reg [31:0] register [31:0]; initial begin$readmemb("my_test_ram.txt", register);endassign readData1 = register[rs]; //read dataassign readData2 = register[rt]; always @(posedge clk)beginif (RegWre == 1'b1) begin register[save_addr] <= write_data; //write dataend 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;//ADD3'b01: begin result <= alu - readData1; add_overflow_exception <= 1'b0;end //SUBU3'b10: begin result <= readData1 | alu; add_overflow_exception <= 1'b0;end //OR3'b11: begin result <= readData1 & alu; add_overflow_exception <= 1'b0;end //ANDdefault:begin result <= 32'bZZZZ_ZZZZ;add_overflow_exception <= 1'b0;endendcase
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)beginif(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 beginBadVaddr = 32'b0;EPC = 32'b0;end always@(posedge clk) //BadVaddrbeginif(addr_erro_exception)BadVaddr <= badvaddr;else BadVaddr <= BadVaddr;end always@(posedge clk) //EPCbegin EPC <= pc_to_epc;end assign exc = addr_erro_exception | add_overflow_exception;always@(negedge clk)beginif(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 beginhi_mem = 32'b0;endalways@(negedge clk)begin if(clr == 1'b1)hi_out <= 32'bZZZZ_ZZZZ;else if(Hi_sel_out == 1'b1)beginhi_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 beginlo_mem = 32'b0;end always@(negedge clk)begin if(clr == 1'b1)lo_out <= 32'bZZZZ_ZZZZ;else if(lo_sel_out == 1'b1)beginlo_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 beginmuldiv_out[63:32] <= readData1/readData2;muldiv_out[31:0] <= readData1%readData2;endend 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) beginaddr_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;endelse begin addr_erro_exception = 1'b1; result_to_DataMem = 32'bZZZZ_ZZZZ;endend 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; endend 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)beginALUM2Reg <= 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 beginregw1 <= 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)beginif(PCS == 1'b1)begin if(readData1 == readData2) begin beq <= 1'b1;clr <= 1'b1;endelse begin beq <= 1'b0;clr <= 1'b0;endend else if(jmp_bk == 1'b1) begin beq <= 1'b0;clr <= 1'b1;endelse begin beq <= 1'b0;clr <= 1'b0;endend
endmodule
讲真,刚开始写时就是懵懵比比的,没有看别人的就急着动手,结果就是这都是些啥啊,理不清关系一样,跑出来的结果全是xxx。后来看了那位大佬写的才慢慢明白了点,然后就自己改,前前后后改了好几个版本,虽然还有瑕疵,但也算是我比较满意的版本了。吸取的教训就是磨刀不误砍柴工,改成流水线时,一点一点你理清脉络,含快就写好了,几个小错改改就弄好了。加油!小菜鸟。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!