【问题标题】:Iteration limit when implementing a multicycled processor实现多周期处理器时的迭代限制
【发布时间】:2016-06-06 21:03:03
【问题描述】:

我正在尝试实现一个简单的多周期处理器,但遇到了一些我似乎无法解决的问题。代码如下。我现在只是在尝试让它流动起来。完成后,我将开始执行指令和 ALU。但是,我被困在这一点上。在下面的代码中,我知道 data_memory 从未使用过(如果我能解决这个问题,我会到达那里),一些输入和输出现在也没有使用,x1x2 只是我创建的变量是为了看看到底发生了什么。 definitions.v 文件中的内容是不言而喻的。

我正在使用 Altera Quartus 15.1 和 Verilog2001。这段代码编译得很好,除了由于未使用的东西而导致的一些警告,但是当我尝试用 20ns 的时钟周期模拟它时,它会给出一个错误,说“错误(可抑制):(vsim-3601)迭代限制 5000 在时间 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
            ...
            ...

这也编译得很好,可以模拟没有错误!但是,无法正常运行,会产生奇怪(或意外)的结果。我发现其他方法更直观。所以我会努力让它发挥作用。

我该如何解决/实现这个?

`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) 添加default case 以防止闩锁行为——否则,Quartus 将推断出闩锁(而不是导线); 3)在边缘触发的always块内使用非阻塞赋值(&lt;=); 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


【解决方案1】:

当我们在 verilog 中写入 always @(signal) 时,指定的敏感度列表,逻辑会在该信号发生变化时触发。这可能会导致对硬件实际工作方式的误解。我们拥有的唯一在边缘发生变化的硬件是触发器,您需要为此指定 posedge 或 negedge 关键字。

always @(signal) 被合成时,你实际上得到了一个组合块,它的行为类似于always @(*)。这是一个自动敏感度列表。

所以从cmets我们来看这一小段代码:

 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 增量等。这一切都发生在 delta 内模拟器循环。

对于像 Verilog 这样的硬件描述语言 (HDL),我们必须记住,我们描述的是并行语句,而不是串行执行的代码行。

【讨论】:

    猜你喜欢
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-16
    相关资源
    最近更新 更多