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模块的更改代码
PC模块的更改
RegFile模块的更改
像上一篇博客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
相关文章推荐
- 一步一步跟我学易语言之第二个易程序菜单设计
- 我的服务器SQL2000的sqlserver占用了90%的cpu,怎么查是那个库?
- MySQL服务器进程CPU占用100%的解决方法
- C#获取CPU编号的方法
- 基于逻辑运算的简单权限系统(原理,设计,实现) VBS 版
- C#中设计、使用Fluent API
- 使用MySQL Slow Log来解决MySQL CPU占用高的问题
- 笔记本下什么是迅驰处理器(cpu)相关资料第1/2页
- doscan.exe进程占用了大量的CPU和内存的解决方法
- 基于逻辑运算的简单权限系统(原理,设计,实现) VBS 版
- JavaScript设计模式初探
- JavaScript 组件之旅(一)分析和设计
- C# 事件的设计与使用深入理解
- 大型网站设计注意事项大全
- CPU也有远程攻击漏洞 英特尔匆匆修补
- w3wp.exe占用cpu过高的解决方法第1/2页
- Android中的脑残设计总结
- win2003中的w3wp.exe进程大量占用cpu资源的各种问题解决方法
- 服务器上w3wp.exe占用cpu过多有时候超过50%的原因分析
- 一个用了统计CPU 内存 硬盘 使用率的shell脚本