MIPS多周期CPU设计之使用时钟上升沿触发PC模块的实现方案

来源:互联网 发布:get it和got it 编辑:程序博客网 时间:2024/05/28 09:31

请勿转载,本人对该文章保留有所有权利,如果需要转载请联系gavingog@qq.com,经本人同意后才可转载


  • 像上一篇博客MIPS多周期CPU设计所说的那样,如果我们要实现使用CLK时钟上升沿来触发PC模块的话要怎么解决呢?这次这篇博客就分析这一个问题。
  • 首先第一点我们要明确时钟上升沿是同时作用于控制模块和PC模块的,所以如果等到IF阶段时钟上升沿来了,接着控制模块再发出PCWre的信号的话是完全没有作用的,因为这个时候在PC模块的时钟上升沿已经过去了,因此也就检测不到这个控制信号了。
  • 接着如果要解决这个问题的话,在上一篇博客中我的解决方法是不使用上升沿触发PC模块,这是第一个方法;而在这里的话我们介绍第二种方案:可以通过在IF阶段的前一个阶段就发出PCWre控制信号,也就是aWB,cWB, (MEM && opcode==sw), ID(opcode != halt)这四种情况下置PCWre,而在IF阶段就把PCWre复位,这样子就可以正常检测到了时钟上升沿的到来了。
  • 最后我们在这里只需要更改的有三个地方:ouputFunc模块,PC模块和RegFile模块(RegFile模块更改是为了解决下个时钟上升沿才会写回数据的bug)。

控制信号的改变(看红色的信号

这里写图片描述

OutputFunc模块的更改代码

`timescale 1ns / 1ps// 输出函数模块module OutputFunc(state, opcode, zero, PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcB, DataMemRW, ALUM2Reg, ExtSel, RegOut, PCSrc, ALUOp);    input [2:0]state;    input [5:0]opcode;    input zero;    output reg PCWre, InsMemRW, IRWre, WrRegData, RegWre, ALUSrcB, DataMemRW, ALUM2Reg;    output reg[1:0]ExtSel, RegOut, PCSrc;    output reg[2:0]ALUOp;    parameter [2:0] IF = 3'b000, // IF状态                         ID = 3'b001, // ID状态                         aEXE = 3'b110, // add等指令的EXE状态                         bEXE = 3'b101, // beq指令的EXE状态                         cEXE = 3'b010, // sw,lw指令的EXE状态                         MEM = 3'b011, // MEM状态                         aWB = 3'b111, //add等指令的WB状态                         cWB = 3'b100; // lw指令的WB状态    parameter [5:0] addi = 6'b000010,                         ori = 6'b010010,                         sll = 6'b011000,                         add = 6'b000000,                         sub = 6'b000001,                         move = 6'b100000,                         slt = 6'b100111,                         sw = 6'b110000,                         lw = 6'b110001,                         beq = 6'b110100,                         j = 6'b111000,                         jr = 6'b111001,                         Or = 6'b010000,                         And = 6'b010001,                         jal = 6'b111010,                         halt = 6'b111111;    always @(state) begin        // 对PCWre定值        ////////////////////// 注意这里更改了 /////////////////////        if (state == aWB || state == cWB || state == bEXE|| (state == MEM && opcode == sw)) PCWre = 1;        else if (state == ID && (opcode == j || opcode == jal || opcode == jr)) PCWre = 1;        else PCWre = 0;        // 对InsMemRW定值        InsMemRW = 1;        // 对IRWre定值        if (state == IF) IRWre = 1;        else IRWre = 0;        // 对WrRegData定值        if (state == aWB || state == cWB) WrRegData = 1;        else WrRegData = 0;        // 对RegWre定值        if (state == aWB || state == cWB || opcode == jal) RegWre = 1;        else RegWre = 0;        // 对ALUSrcB定值        if (opcode == addi || opcode == ori || opcode == sll || opcode == sw || opcode == lw) ALUSrcB = 1;        else ALUSrcB = 0;        // 对DataMemRW定值        if (state == MEM && opcode == sw) DataMemRW = 1;        else DataMemRW = 0;        // 对ALUM2Reg定值        if (state == cWB) ALUM2Reg = 1;        else ALUM2Reg = 0;        // 对ExtSel定值        if (opcode == ori) ExtSel = 2'b01;        else if (opcode == sll) ExtSel = 2'b00;        else ExtSel = 2'b10;        // 对RegOut定值        if (opcode == jal) RegOut = 2'b00;        else if (opcode == addi || opcode == ori || opcode == lw) RegOut = 2'b01;        else RegOut = 2'b10;        // 对PCSrc定值        case(opcode)            j: PCSrc = 2'b11;            jal: PCSrc = 2'b11;            jr: PCSrc = 2'b10;            beq: begin                if (zero) PCSrc = 2'b01;                else PCSrc = 2'b00;            end            default: PCSrc = 2'b00;        endcase        // 对ALUOp定值        case(opcode)            sub: ALUOp = 3'b001;            Or: ALUOp = 3'b101;            And: ALUOp = 3'b110;            ori: ALUOp = 3'b101;            slt: ALUOp = 3'b010;            sll: ALUOp = 3'b100;            beq: ALUOp = 3'b001;            default: ALUOp = 3'b000;        endcase        // 防止在IF阶段写数据        if (state == IF) begin            RegWre = 0;            DataMemRW = 0;        end    endendmodule

PC模块的更改

`timescale 1ns / 1ps// PC模块module PC(clk, i_pc, pcWre, reset, o_pc, outside_pc);  input wire clk, pcWre, reset;  input wire [31:0] i_pc, outside_pc;  output reg [31:0] o_pc;  always @(posedge clk) begin // 这里有更改,使用上升沿触发    if (reset) begin      o_pc = outside_pc;    end else if (pcWre) begin      o_pc = i_pc;    end else if (!pcWre) begin //停机时候指令不变        o_pc = o_pc;     end  endendmodule

RegFile模块的更改

`timescale 1ns / 1ps// 寄存器组module RegFile (rs, rt, rd, i_data, we, clk, o_data_1, o_data_2);  input [4:0] rs, rt, rd;  input [31:0] i_data;  input we, clk;  output [31:0] o_data_1, o_data_2;  reg [31:0] register [0:31];  initial begin    register[0] = 0; // 只需要确定零号寄存器的值就好,$0恒等于0  end  assign o_data_1 = register[rs];  assign o_data_2 = register[rt];  /////////////  这里有更改,使用下降沿触发,否则会产生下个时钟周期上升沿才会更改寄存器值的bug,在我们的流水线CPU设计中也是在后半段才写数据的,所以这里应该没有问题    //////////////  always @(negedge clk) begin    if ((rd != 0) && (we == 1)) begin // rd != 0 是确保零号寄存器不会改变的作用      register[rd] = i_data;    end  endendmodule
1 0
原创粉丝点击