实现多循环处理器时的迭代限制
Iteration limit when implementing a multicycled processor
我正在尝试实现一个简单的多周期处理器,我 运行 遇到了一些我似乎没有解决的问题。代码如下。我现在正在尝试让这个流动起来。完成后,我将开始执行指令和 ALU。但是,我被困在这一点上。在下面的代码中,我知道 data_memory
从未使用过(如果我能解决这个问题,我会到达那里),一些输入和输出现在也没有使用,x1
和 x2
只是我创建的变量,用于查看实际情况。 definitions.v
文件中的内容不言而喻。
我正在使用带有 Verilog2001 的 Altera Quartus 15.1。这段代码编译得很好,除了一些由于未使用的东西而引起的警告,但是当我尝试用 20ns 的时钟周期模拟它时,它给出了一个错误,说 "Error (suppressible): (vsim-3601) Iteration limit 5000 reached at time 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
...
...
这样也编译好了,可以模拟不报错!但是,无法正常运行,会产生奇怪(或意外)的结果。我发现其他方法更直观。所以我会努力让它发挥作用。
我怎么能resolve/implement这个?
`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
当我们在 verilog 中写入 always @(signal)
时,一个指定的敏感度列表,逻辑会在该信号发生变化时触发。这可能会导致对硬件实际工作方式的误解。我们拥有的唯一在边沿上发生变化的硬件是触发器,您需要为此指定 posedge 或 negedge 关键字。
当 always @(signal)
被合成时,您实际上得到了一个组合块,其行为类似于 always @(*)
。这是一个自动敏感度列表。
所以我们将从评论中看一下这一小段代码:
always @ (*) begin
case (cycle)
3'b011: begin
ok = 0;
if (OPC==5'b01011) x1 = 2'b11;
PC = PC + 1;
next_cycle = 3'b000;
x2 = 2'b11;
ok = 1;
这是一个组合块,当任何可能影响他的输出发生变化时在模拟器中触发。大多数信号都分配给静态信号,或其他没有循环的已知值。
PC = PC +1;
上面的行虽然更新了 PC 的值,这个新的 PC 值应该触发 组合块重新计算,再次击中 PC 增量,等等。这一切都发生在模拟器的增量循环内。
使用像 Verilog 这样的硬件描述语言 (HDL),我们必须记住我们描述的是并行语句,而不是串行执行的代码行。
我正在尝试实现一个简单的多周期处理器,我 运行 遇到了一些我似乎没有解决的问题。代码如下。我现在正在尝试让这个流动起来。完成后,我将开始执行指令和 ALU。但是,我被困在这一点上。在下面的代码中,我知道 data_memory
从未使用过(如果我能解决这个问题,我会到达那里),一些输入和输出现在也没有使用,x1
和 x2
只是我创建的变量,用于查看实际情况。 definitions.v
文件中的内容不言而喻。
我正在使用带有 Verilog2001 的 Altera Quartus 15.1。这段代码编译得很好,除了一些由于未使用的东西而引起的警告,但是当我尝试用 20ns 的时钟周期模拟它时,它给出了一个错误,说 "Error (suppressible): (vsim-3601) Iteration limit 5000 reached at time 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
...
...
这样也编译好了,可以模拟不报错!但是,无法正常运行,会产生奇怪(或意外)的结果。我发现其他方法更直观。所以我会努力让它发挥作用。
我怎么能resolve/implement这个?
`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
当我们在 verilog 中写入 always @(signal)
时,一个指定的敏感度列表,逻辑会在该信号发生变化时触发。这可能会导致对硬件实际工作方式的误解。我们拥有的唯一在边沿上发生变化的硬件是触发器,您需要为此指定 posedge 或 negedge 关键字。
当 always @(signal)
被合成时,您实际上得到了一个组合块,其行为类似于 always @(*)
。这是一个自动敏感度列表。
所以我们将从评论中看一下这一小段代码:
always @ (*) begin
case (cycle)
3'b011: begin
ok = 0;
if (OPC==5'b01011) x1 = 2'b11;
PC = PC + 1;
next_cycle = 3'b000;
x2 = 2'b11;
ok = 1;
这是一个组合块,当任何可能影响他的输出发生变化时在模拟器中触发。大多数信号都分配给静态信号,或其他没有循环的已知值。
PC = PC +1;
上面的行虽然更新了 PC 的值,这个新的 PC 值应该触发 组合块重新计算,再次击中 PC 增量,等等。这一切都发生在模拟器的增量循环内。
使用像 Verilog 这样的硬件描述语言 (HDL),我们必须记住我们描述的是并行语句,而不是串行执行的代码行。