【发布时间】:2016-06-06 21:03:03
【问题描述】:
我正在尝试实现一个简单的多周期处理器,但遇到了一些我似乎无法解决的问题。代码如下。我现在只是在尝试让它流动起来。完成后,我将开始执行指令和 ALU。但是,我被困在这一点上。在下面的代码中,我知道 data_memory 从未使用过(如果我能解决这个问题,我会到达那里),一些输入和输出现在也没有使用,x1 和 x2 只是我创建的变量是为了看看到底发生了什么。 definitions.v 文件中的内容是不言而喻的。
我正在使用 Altera Quartus 15.1 和 Verilog2001。这段代码编译得很好,除了由于未使用的东西而导致的一些警告,但是当我尝试用 20ns 的时钟周期模拟它时,它会给出一个错误,说“错误(可抑制):(vsim-3601)迭代限制 5000 在时间 100 ns 达到” .它还说这是可以抑制的,但我也不知道如何抑制。
我查找了这个错误,我了解到这是因为在某些时候代码进入了无限循环。我试图通过创建另一个变量ok 来解决这个问题。一个循环将从将ok 设置为0 开始,在该循环的微操作完成后,我将ok 设置为1。所以循环不会在不合适的时间改变(就像锁定循环一样)。不幸的是,这导致了同样的错误。
我也尝试了另一种流程。我为循环创建了一个变量,而不是cycle 和next_cycle。在时钟的每个上升沿,我都会检查当前状态并做相应的事情,然后为下一步设置周期。示例:
always @ (posedge clk) begin
case (cycle)
3'b000: begin
MAR <= PC;
cycle <= 3'b001;
ire <= 1'b1;
x2 <= 2'b00;
3'b001: begin
...
...
这也编译得很好,可以模拟没有错误!但是,无法正常运行,会产生奇怪(或意外)的结果。我发现其他方法更直观。所以我会努力让它发挥作用。
我该如何解决/实现这个?
`include "definitions.v"
module controller(
input clk,
input nres,
output reg ire,
output reg dwe,
output reg dre,
output reg [1:0] x2,
output reg [`IADR_WIDTH-1:0] i_address,
output reg [`DADR_WIDTH-1:0] d_address,
output reg [`DATA_WIDTH-1:0] data_out);
reg [2:0] cycle = 3'b000;
reg [2:0] next_cycle;
reg [`IADR_WIDTH-1:0] PC = 6'b000000;
reg [`INST_WIDTH-1:0] IR = 12'b00000_0000000;
reg [`DADR_WIDTH-1:0] MAR = 6'b000000;
reg [4:0] OPC = 5'b00000;
wire [`DATA_WIDTH-1:0] data_in;
wire [`INST_WIDTH-1:0] instruction;
reg [1:0] x1;
data_memory dmem ( .clk (clk),
.dwe (dwe),
.dre (dre),
.nres (nres),
.d_address (d_address),
.d_data (data_out),
.d_q (data_in));
instruction_memory imem ( .clk (clk),
.ire (ire),
.i_address (i_address),
.i_q (instruction));
reg ok = 1;
always @ (posedge clk) begin
cycle = (ok) ? next_cycle : cycle;
end
always @ (cycle) begin
case (cycle)
3'b000: begin
ok = 0;
MAR = PC;
next_cycle = 3'b001;
ire = 1'b1;
x2 = 2'b00;
ok = 1;
end
3'b001: begin
ok = 0;
i_address = MAR;
IR = instruction;
ire = 1'b0;
next_cycle = 3'b010;
x2 = 2'b01;
ok = 1;
end
3'b010: begin
ok = 0;
OPC = IR;
next_cycle = 3'b011;
x2 = 2'b10;
ok = 1;
end
3'b011: begin
ok = 0;
if (OPC==5'b01011) x1 = 2'b11;
PC = PC + 1;
next_cycle = 3'b000;
x2 = 2'b11;
ok = 1;
end
endcase
end
endmodule
【问题讨论】:
-
一些建议: 1) 使用
always@(*)而不是always@(cycle)- 它不会解决任何问题,但这是一个好习惯; 2) 添加defaultcase 以防止闩锁行为——否则,Quartus 将推断出闩锁(而不是导线); 3)在边缘触发的always块内使用非阻塞赋值(<=); 4) 发布您的测试平台代码,以防您的错误来源。 -
您在组合块中有
PC = PC + 1;。这可能会在时钟边沿提前。 -
@Morgan 但它由一个状态(周期)控制,并且该状态由时钟的上升沿控制。您认为这是造成问题的原因吗?
-
只是因为你写了
always@(cycle),它在合成时会像always@(*)一样工作。这就是为什么使用 * 是最佳实践的原因。自循环的组合加法可能会达到模拟器时间步长迭代限制。不确定这是否是您所看到的确切问题。 -
@Morgan 我注释掉了
PC = PC + 1声明,错误消失了,这对我来说是一个很好的进步。为了更好地理解问题:假设我们处于3'b011状态。在这种状态下,我们增加PC。如果当我们在这个状态下做完所有事情,但下一个时钟脉冲还没有到来,我们会再做一次吗?再次将ok设置为0,获取OPC,再增加一次PC等等。也许当我们再次增加PC时,下一个时钟脉冲将会触发,但我们已经将ok设置为@987654351 @。所以PC将增加很多倍,以至于我们达到迭代限制。
标签: verilog fpga cpu-architecture intel-fpga quartus