实现多循环处理器时的迭代限制

Iteration limit when implementing a multicycled processor

我正在尝试实现一个简单的多周期处理器,我 运行 遇到了一些我似乎没有解决的问题。代码如下。我现在正在尝试让这个流动起来。完成后,我将开始执行指令和 ALU。但是,我被困在这一点上。在下面的代码中,我知道 data_memory 从未使用过(如果我能解决这个问题,我会到达那里),一些输入和输出现在也没有使用,x1x2 只是我创建的变量,用于查看实际情况。 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。所以周期不会在不合适的时候改变(就像锁定周期一样)。不幸的是,这导致了同样的错误。

我也尝试了另一个流程。我为循环创建了一个变量,而不是 cyclenext_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),我们必须记住我们描述的是并行语句,而不是串行执行的代码行。