您的位置:首页 > 其它

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

2016-05-25 19:56 1031 查看
请勿转载,本人对该文章保留有所有权利,如果需要转载请联系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
end

endmodule


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
end
endmodule


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
end
endmodule
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  cpu 设计