【问题标题】:Multiple conflicting drivers for reg assigned in only one always block多个冲突的 reg 驱动程序仅分配在一个总是块中
【发布时间】:2019-09-30 02:24:38
【问题描述】:

我正在 Verilog 中开发一个简单的视频信号定时模块,作为一个学习项目。我从之前的研究中了解到,每个reg 只能从一个always 块分配,因此我将系统安排为两个状态机块,然后是一个用于填充输出寄存器的块,如下所示:

module video_timing
(
    input wire reset,
    input wire clk,

    output reg [15:0] x,
    output reg [15:0] y,
    output reg hsync,
    output reg vsync,
    output reg visible
);

    // State constants for our two timing state machines (one horizontal, one vertical)
    `define VIDEO_SYNC       2'd0
    `define VIDEO_BACKPORCH  2'd1
    `define VIDEO_ACTIVE     2'd2
    `define VIDEO_FRONTPORCH 2'd3

    // These settings are for 720p, assuming clk is running at 74.25 MHz
    `define VIDEO_H_SYNC_PIXELS   16'd40
    `define VIDEO_H_BP_PIXELS     16'd220
    `define VIDEO_H_ACTIVE_PIXELS 16'd1280
    `define VIDEO_H_FP_PIXELS     16'd110
    `define VIDEO_H_SYNC_ACTIVE   1'b1
    `define VIDEO_V_SYNC_LINES    16'd5
    `define VIDEO_V_BP_LINES      16'd20
    `define VIDEO_V_ACTIVE_LINES  16'd720
    `define VIDEO_V_FP_LINES      16'd5
    `define VIDEO_V_SYNC_ACTIVE   1'b1

    reg [1:0] state_h;
    reg [15:0] count_h; // 1-based so we will stop when count_h is the total pixels for the current state
    reg inc_v = 1'b0;
    reg [1:0] state_v;
    reg [15:0] count_v; // 1-based so we will stop when count_v is the total lines for the current state

    // Change outputs on clock.
    // (These update one clock step behind everything else below, but that's
    //  okay because the lengths of all the periods are still correct.)
    always @(posedge clk) begin
        if (reset == 1'b1) begin
            hsync   <= ~`VIDEO_H_SYNC_ACTIVE;
            vsync   <= ~`VIDEO_V_SYNC_ACTIVE;
            visible <= 1'b0;
            x       <= 16'd0;
            y       <= 16'd0;
        end else begin
            hsync   <= (state_h == `VIDEO_SYNC) ^ (~`VIDEO_H_SYNC_ACTIVE);
            vsync   <= (state_v == `VIDEO_SYNC) ^ (~`VIDEO_V_SYNC_ACTIVE);
            visible <= (state_h == `VIDEO_ACTIVE) && (state_v == `VIDEO_ACTIVE);
            x       <= count_h - 1;
            y       <= count_v - 1;
         end
    end

    // Horizontal state machine
    always @(posedge clk) begin
        if (reset == 1'b1) begin
            count_h <= 16'b1;
            state_h <= `VIDEO_FRONTPORCH;
        end else begin
            inc_v <= 0;
            count_h <= count_h + 16'd1;

            case (state_h)
                `VIDEO_SYNC: begin
                    if (count_h == `VIDEO_H_SYNC_PIXELS) begin
                        state_h <= `VIDEO_BACKPORCH;
                        count_h <= 16'b1;
                    end
                end
                `VIDEO_BACKPORCH: begin
                    if (count_h == `VIDEO_H_BP_PIXELS) begin
                        state_h <= `VIDEO_ACTIVE;
                        count_h <= 16'b1;
                    end
                end
                `VIDEO_ACTIVE: begin
                    if (count_h == `VIDEO_H_ACTIVE_PIXELS) begin
                        state_h <= `VIDEO_FRONTPORCH;
                        count_h <= 16'b1;
                    end
                end
                `VIDEO_FRONTPORCH: begin
                    if (count_h == `VIDEO_H_FP_PIXELS) begin
                        state_h <= `VIDEO_SYNC;
                        count_h <= 16'b1;
                        inc_v <= 1;
                    end
                end
            endcase
        end
    end

    // Vertical state machine
    always @(posedge clk) begin
        if (reset == 1'b1) begin
            count_v <= 16'b1;
            state_v <= `VIDEO_FRONTPORCH;
        end else begin
            if (inc_v) begin
                count_v <= count_v + 16'd1;
                case (state_v)
                    `VIDEO_SYNC: begin
                        if (count_v == `VIDEO_V_SYNC_LINES) begin
                            state_v <= `VIDEO_BACKPORCH;
                            count_v <= 16'b1;
                        end
                    end
                    `VIDEO_BACKPORCH: begin
                        if (count_v == `VIDEO_V_BP_LINES) begin
                            state_v <= `VIDEO_ACTIVE;
                            count_v <= 16'b1;
                        end
                    end
                    `VIDEO_ACTIVE: begin
                        if (count_v == `VIDEO_V_ACTIVE_LINES) begin
                            state_v <= `VIDEO_FRONTPORCH;
                            count_v <= 16'b1;
                        end
                    end
                    `VIDEO_FRONTPORCH: begin
                        if (count_v == `VIDEO_V_FP_LINES) begin
                            state_v <= `VIDEO_SYNC;
                            count_v <= 16'b1;
                        end
                    end
                endcase
            end
        end
    end

endmodule

当尝试使用 IceStorm 工具链进行合成时,yosys 会警告hsync 有多个驱动程序:

Warning: multiple conflicting drivers for top.\timing.hsync:
    port Q[0] of cell $techmap\timing.$procdff$109 ($adff)
    port Q[0] of cell $techmap\timing.$procdff$110 ($adff)

nextpnr-ice40 随后在同样的问题上失败:

ERROR: multiple drivers on net 'P1B4' ($auto$simplemap.cc:496:simplemap_adff$375.Q and $auto$simplemap.cc:496:simplemap_adff$376.Q)
ERROR: Loading design failed.

我尝试了几种不同的排列来尝试解决这个问题,并提出了一些意见:

  • 如果我将case (reset) 0: 块中的分配(在第一个总是)更改为只是hsync &lt;= 'VIDEO_H_SYNC_ACTIVE,那么结果是可综合的,并且确实在没有像我一样重置时不断断言hsync做出改变后的期望。
  • 如果我完全删除 hsync &lt;= (state_h == 'VIDEO_SYNC) ^ (~'VIDEO_H_SYNC_ACTIVE) 行但在重置期间保留 hsync &lt;= ~'VIDEO_H_SYNC_ACTIVE 则结果也是可合成的,并且 hsync 始终未断言。

(我在上面用' 替换了反引号,只是因为反引号是内联逐字文本的 Markdown 元字符。我在实际程序中使用反引号作为常量。)

这使我得出结论,hsync &lt;= 右侧的表达式就是问题所在。但是我很困惑为什么vsync &lt;= 之后的非常相似的行不会产生类似的错误:

hsync <= (state_h == `VIDEO_SYNC) ^ (~`VIDEO_H_SYNC_ACTIVE);
vsync <= (state_v == `VIDEO_SYNC) ^ (~`VIDEO_V_SYNC_ACTIVE);

iverilog 的模拟下,该模块的行为符合我的预期。

我正在使用由 git sha1 a6a4349 构建的 Yosys 0.8 和 nextpnr。我也尝试升级到 Yosys 0.9 并得到相同的结果,但我怀疑是我的问题,而不是工具链。

【问题讨论】:

  • 我建议你用if (reset)替换你的case (reset)。我从未见过以这种方式与案例一起使用的重置。我怀疑综合工具也会混淆。
  • 谢谢,@Oldfart。我尝试了一些不同的东西,所以忘了提到我还在那里尝试了ifcase;我最初写了if,但在其他地方看到了一些评论,表明一些合成工具只能理解case。不幸的是,这个特定的工具链似乎并没有产生不同的结果。我已经更新了问题,将修改后的版本包含在 if 语句中,结果相同。
  • 我刚刚通过 Vivado 2018.2 运行了您的最新代码,没有出现任何错误。我开始怀疑你的工具有问题。
  • 感谢@Oldfart!很高兴知道。也许我在yosys 中发现了一个极端情况或错误;我会看看我是否可以使用官方 Lattice 工具重现您的成功(因为我的目标是 Lattice 部分),如果可以,请参阅上游报告。
  • 许多工具对异步重置的允许程度非常宽容。但是if是唯一保证按照合成标准工作的方式。

标签: verilog yosys


【解决方案1】:

IEEE 综合标准 (1364.1) 不允许这种异步复位模式。因此只能使用if 语句:

【讨论】:

  • 谢谢。我还没有看到标准的那部分。
  • 谢谢!我看到了一些相互矛盾的例子,所以很高兴直接从标准中看到它。经过反思,我意识到无论如何,同步复位对于我的目的应该没问题,所以我将灵敏度降低到posedge clk,并在下一个时钟周期复位。不幸的是,这种简化(以及切换回使用if 语句而不是case 来测试reset)似乎并没有改变结果。我是否误解了您指的是我的模块的哪一部分? (我已经用最新的更新更新了我的问题。)
  • 我没有构建可用的 Yosys 0.8 或 0.9;但我似乎看不到更新后的代码和最新的(git master)工具链存在任何类型的多驱动程序问题 - 而且发布的代码现在看起来确实正确。
  • 再次感谢!当我明天回到我的电脑时,我将尝试从master 构建 yosys。
【解决方案2】:

这里的其他答案是重要的垫脚石:

  • 我的重置实现最初是异步的,并且以不符合 Verilog 综合标准的方式编写。
  • 我的条件重置逻辑是使用 switch 而不是 if 编写的(由于我在其他地方看到了一些令人困惑的建议)。

不幸的是,那里的路径变干了,因为在那之后这个timing.v 合成得很好。原来问题其实出在top.v上:

    video_timing timing(
        .reset(reset_loc),
        .clk(vga_ck),
        .hsync(vga_hs),
        .vsync(vga_hs),
        .visible(vga_de)
    );

我不小心将vga_hs 分配给了hsync vsync。 ??‍♂️

在将 vsync 行改为引用 vga_vs 后,设计现在可以合成并按预期运行。

经验教训:问题并不总是编译器认为的问题!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-04
    • 2016-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    相关资源
    最近更新 更多