MIPS单周期CPU-Verilog

MIPS单周期CPUVerilog设计

设计草稿

模块设计

mips

顶层模块,将各个模块连接,设置clk,reset输入端口。

PC

  1. 输入端口设置
    • [31:0]imm:PC跳转值
    • PCset:beq类PC跳转选择
    • PCjump:J指令跳转选择
    • clk:时钟信号
    • reset:同步复位信号
  2. 输出端口设置
    • [31:0]PCout:当前PC值
  3. 模块内部实现:根据PCset与PCjump选择PC寄存器值的变化行为,reset有效时PC寄存器值设置为0x3000。
  4. 部分代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    always @(posedge clk) begin
    if (reset==1) begin
    pc<=16'h3000;
    end
    else begin
    if (PCset==1) begin
    pc<=pc+4+imm;
    end
    else if (PCjump==1) begin
    pc<=imm;
    end
    else begin
    pc<=pc+4;
    end
    end
    end

IM

  1. 输入端口:[31:0] pc
  2. 输出端口:[31:0] code
  3. 模块内部实现:设置32位寄存器数组Rom[0:4095]用于存放指令,根据pc值选择相应指令输出到code。
  4. 部分代码
    1
    2
    3
    4
    5
    reg [31:0] Rom[0:12'd4095];
    initial begin
    $readmemh("code.txt",Rom);
    end
    assign code = Rom[(pc-16'h3000)>>2];

Control

根据输入的opcode和func判断当前指令为add,sub,ori,beq,lw,sw,jr,jal或lui,并据此判断各输出端口输出情况,具体如下表。

类型输出 add sub ori beq lw sw jr jal lui
RegDst 1 1 0 0 0 0 0 0 0
ALUsrc 0 0 1 0 1 1 0 0 0
MemtoReg 0 0 0 0 1 0 0 0 0
RegWrite 1 1 1 0 1 0 0 1 1
MemWrite 0 0 0 0 0 1 0 0 0
PCset 0 0 0 1 0 0 0 0 0
PCjump 0 0 0 0 0 0 1 1 0
EXTop 0 0 0 0 1 1 0 0 0
lui 0 0 0 0 0 0 0 0 1
PCtoReg 0 0 0 0 0 0 0 1 0
RegtoPC 0 0 0 0 0 0 1 0 0
ALUop 00 01 10 11 00 00 00 00 00
  • 部分代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    assign RegDst = ((opcode==`add&&func==`addfun)||(opcode==`sub&&func==`subfun));
    assign ALUsrc = (opcode==`ori||opcode==`lw||opcode==`sw);
    assign MemtoReg = (opcode==`lw);
    assign RegWrite = ((opcode==`add&&func==`addfun)||(opcode==`sub&&func==`subfun)||opcode==`ori||opcode==`lw||opcode==`jal||opcode==`lui);
    assign MemWrite = (opcode==`sw);
    assign PCset = (opcode==`beq);
    assign PCjump = (opcode==`jal||(opcode==`jr&&func==`jrfun));
    assign EXTop = (opcode==`lw||opcode==`sw);
    assign lui = (opcode==`lui);
    assign PCtoReg = (opcode==`jal);
    assign RegtoPC = (opcode==`jr&&func==`jrfun);
    assign ALUop = ((opcode==`add&&func==`addfun)||opcode==`lw||opcode==`sw)?0:
    ((opcode==`sub&&func==`subfun))?1:
    (opcode==`ori)?2:
    (opcode==`beq)?3:0;

GRF

  1. 输入端口设置
    • [4:0]A1:PD1输出对应寄存器地址
    • [4:0]A2:PD2输出对应寄存器地址
    • [31:0]WD:写入寄存器的值
    • [4:0]A3:写入寄存器地址
    • [31:0]pc:当前pc值,用于输出测试
    • clk:时钟信号
    • reset:同步复位信号
    • WE:写使能信号
  2. 输出端口设置
    • [31:0]PD1:A1寄存器对应值
    • [31:0]PD2:A2寄存器对应值
  3. 模块内部实现
    设置一32位寄存器数组grf[0:31]作为寄存器堆,reset有效时全部归零,若写使能信号有效则系统调用输出所需测试的信息,若A3!=0则写入到对应位置(0号寄存器恒为0)。
  4. 部分代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    always @(posedge clk) begin
    if (reset==1) begin
    for (i = 0;i<32 ;i=i+1 ) begin
    grf[i]<=0;
    end
    end
    else begin
    if (WE==1) begin
    if (A3!=0) begin
    grf[A3]<=WD;
    end
    $display("@%h: $%d <= %h", pc, A3, WD);
    end
    end
    end
    assign PD1

EXT

  1. 输入端口设置
    • EXTop:0时零扩展,1时符号扩展
    • [15:0]input1:需要扩展的数
  2. 输出端口设置
    • [31:0]output1:扩展后的数
  3. 根据上述信息实现即可
  4. 部分代码
    1
    2
    assign output1 = (EXTop == 1)?{{16{input1[15]}},input1}:
    {{16{1'b0}},input1};

ALU

  1. 输入端口设置
    • [31:0]input1:输入数
    • [31:0]input2:输入数
    • [1:0]op:为00时做加法,01时做减法,10时做或运算,11比较两个输入相等输出1,否则输出0
  2. 输出端口设置
    • [31:0]output1:输出对应计算结果
  3. 根据上述信息实现即可
  4. 部分代码
    1
    2
    3
    4
    5
    assign output1 = (op==0)?(input1+input2):
    (op==1)?(input1-input2):
    (op==2)?(input1|input2):
    (op==3)?(input1==input2):
    0;

DM

  1. 输入端口设置
    • [13:0]addr:读写内存地址
    • [31:0]W:写入内存的数据
    • WR:读写控制信号,为1时写入,0时读出
    • clk:时钟信号
    • reset:同步复位信号
    • [31:0]pc:当前pc值,用于输出测试数据
  2. 输出端口设置
    • [31:0]out:读出内存的值
  3. 具体实现
    使用一个8位寄存器数组Ram[0:0x3fff]作为内存,reset有效时所有内存清零,读写控制信号为1时,将addr,addr+4,addr+8,addr+12的寄存器值写为输入值,输出同理。
  4. 部分代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    always @(posedge clk) begin
    if (reset==1) begin
    for (i = 0;i<=14'h3fff ;i=i+1 ) begin
    Ram[i]<=0;
    end
    end
    else begin
    if (WR==1) begin
    Ram[addr] <= W[7:0];
    Ram[addr+1] <= W[15:8];
    Ram[addr+2] <= W[23:16];
    Ram[addr+3] <= W[31:24];
    $display("@%h: *%h <= %h", pc, {{18{1'b0}},addr}, W);
    end
    end
    end

综合

根据模块信息及Control模块的输出信息进行端口选择,并将选择后的端口接入至其他模块

备注

Control各输出端口含义

  • RegDst:有效时选择rd值作为GRF写入地址
  • ALUsrc:有效时选择EXT输出值作为ALU其中一个输入,反之为寄存器输出其中一个输出值
  • MemtoReg:有效时选择DM读出值作为寄存器写入值
  • RegWrite:作为GRF写使能信号
  • MemWrite:作为DM读写控制信号
  • PCset:PC模块对应输入,有效时,若ALU输出结果为1则将imm符号扩展后左移2为的值作为PC模块输入。
  • PCjump:PC模块对应输入,有效时将jump地址作为PC模块输入
  • EXTop:EXT模块op端口输入
  • lui:有效时选择imm加载到高位的值作为寄存器写入数据
  • PCtoReg:有效时将PC值写入31号寄存器
  • RegtoPC:有效时选择相应位置寄存器输出作为jump地址
  • ALUop:作为ALU模块op端口输入