reg 的多个冲突驱动程序仅在一个 always 块中分配

Multiple conflicting drivers for reg assigned in only one always block

作为一个学习项目,我正在使用 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.$procdff9 ($adff)
    port Q[0] of cell $techmap\timing.$procdff0 ($adff)

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

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

我尝试了几种不同的排列来尝试解决这个问题,并做了一些观察:

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

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

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

该模块在 iverilog.

模拟下的表现与我预期的一样

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

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

此处的其他答案是重要的垫脚石:

  • 我的重置实现最初是异步的,并且是以不符合 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 后,设计现在可以按预期进行综合和运行。

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