Vivado linter:信号“out_reg”的推断锁存器

问题描述 投票:0回答:2

我是 Verilog 新手。我想编写一个简单的模块来进行时钟分频而不使用 PLL。该模块被命名为“uart_brg”,因为我计划稍后在 uart 模块中使用它来进行练习。

我将源文件

uart_brg.v
设置为Vivado项目中的顶层模块。行为模拟看起来没问题,但 Vivado Linter (2023.1) 报告了两个问题:

  1. clk:发现 IO 端口“clk”未读取位,第一个未读位
    0
  2. out_reg:信号
    out_reg
    的推断锁存器。

第一个问题是一种预期,虽然我不确定如果我忽略它是否有任何风险。

我的问题是关于第二个问题:我确实在每个分支中分配给

out
。为什么还是抱怨?

以下是代码。请注意,为了保持输出时钟为 50% 占空比,并支持 1:1 时钟比,我在输入时钟的上升沿和下降沿增加计数,因此灵敏度列表为

(clk, rst_n)

module uart_brg #(
    parameter DIVISOR_WIDTH = 16                       // 16-bit integer, per 16550 standard
) (
    input  wire                             clk,       // input clock
    input  wire                             rst_n,     // reset
    input  wire [DIVISOR_WIDTH - 1 : 0]     divisor,   // 
    output reg                              out        // output clock
);

reg unsigned [DIVISOR_WIDTH : 0] count = 0; // count for half clk peroids
// to make duty cycle of out exactly 50%:
reg unsigned [DIVISOR_WIDTH : 0] div = 1;   // local divisor
reg unsigned [DIVISOR_WIDTH : 0] div2 = 2;  // double of div

always @(clk, rst_n) begin // use level-sensitive of both signals
    // make sure that divisor is > 0
    if (divisor > 0) 
        div <= divisor;
    else
        div <= 1;
    div2 <= {div, 1'b0};
    
    if (~rst_n) begin
        count <= {(DIVISOR_WIDTH+1){1'b0}};
        out <= 1'b0;
    end else begin
        if (div == div2 - 1) begin // special case: 1:1 clock ratio
            out <= ~out;
            count <= {(DIVISOR_WIDTH+1){1'b0}};  // preventing "inferred latch for signal xxx"
        end else begin
            count <= count + 1;
            if (count == 0 || count == div) begin
                out <= ~out;
            end else begin
                out <= out;  
                if (count == (div2 - 1))
                    count <= 0;
            end
        end
    end        
end

endmodule

这是除数设置为3时行为模拟的屏幕截图:

请就所报告的问题提供一些提示。

更新

根据@Mikef和@toolic的建议,修改后的模块更加简单,没有Vivado Linter的错误报告。

module uart_brg #(
    parameter DIVISOR_WIDTH = 16                       // 16-bit integer, per 16550 standard
) (
    input  wire                             clk,       // input clock
    input  wire                             rst_n,     // reset
    input  wire [DIVISOR_WIDTH - 1 : 0]     divisor,   // >=2
    output reg                              out        // output clock
);

reg [DIVISOR_WIDTH - 1 : 0] count = 0;

/*
 * the following block is not necessary. when divisor=1,
 * the output is the same as divisor=2, upon simulation.
 */
// make sure that divisor is > 1
//reg [DIVISOR_WIDTH - 1 : 0] div = 2;   // local divisor
//always @(*) begin
//    if (divisor > 1)
//        div = divisor;
//    else
//        div = 2;
//end

// update count
always @(posedge clk, negedge rst_n) begin
    if (~rst_n) 
        count <= {DIVISOR_WIDTH{1'b0}};
    else begin
        if (count == divisor - 1) 
            count <= 0;
        else
            count <= count + 1;
    end    
end

// update output
always @(posedge clk, negedge rst_n) begin
    if (~rst_n) 
        out <= 1'b0;
    else begin
        if (count == 0 || count == divisor / 2)  
            out <= ~out;
    end        
end

endmodule

唯一的“限制”是,对于奇数除数值,输出的占空比将略小于 50%。我想这对于 uart tx/rx 模块来说应该不是问题。

verilog vivado synthesis
2个回答
1
投票

块始终具有推断寄存器或组合逻辑的能力;每个目的的语法都不同。

帖子中的代码

always @(clk, rst_n)
推断出组合逻辑。

要对寄存器进行建模,请使用

always @(posedge clk, negedge rst_n)

这种风格推断寄存器具有异步复位。

由于使用了非阻塞

<=
赋值,因此发布的代码似乎是为寄存器建模而设计的。但是,缺少关键字
posedge
,因此推断出受锁存器影响的组合逻辑。添加关键字
posedge
以消除闩锁。

要对组合逻辑进行建模,请使用

always @(*)
和阻塞赋值
=
而不是非阻塞
<=
赋值。

这些概念(推断梳逻辑与寄存器的过程,以及阻塞与非阻塞分配的过程)在帖子中混合在一起,因为使用

always @(clk, rst_n)
(推断组合逻辑)和非阻塞分配(应该在旨在推断寄存器的过程中使用)。

省略对正确建模为寄存器的变量的赋值(例如 if 语句的 else 部分)是可以的,甚至是适当的,因为它按照设计将其值保留到下一个时钟沿。当设计意图明确,即对寄存器进行建模时,工具将不会推断锁存器。

删除

unsigned
关键字(默认为无符号),并添加建模同步逻辑所需的
posedge
negedge
关键字\w 低电平有效异步复位,如下所示:

// CODE SNIP
reg  [DIVISOR_WIDTH : 0] count = 0; // count for half clk peroids
// to make duty cycle of out exactly 50%:
reg  [DIVISOR_WIDTH : 0] div = 1;   // local divisor
reg  [DIVISOR_WIDTH : 0] div2 = 2;  // double of div

always @(posedge clk, negedge rst_n) begin // +edge trig flop\w async active low reset    
//CODE SNIP

修改后的 Vivado 综合会产生:

...
synthesis finished with 0 errors, 0 critical warnings and 0 warnings.
Synthesis Optimization Runtime : Time (s): cpu = 00:00:14 ; elapsed = 00:00:25 . Memory (MB): peak = 2448.074 ; gain = 56.746 ; free physical = 107522 ; free virtual = 203345
Synthesis Optimization Complete : Time (s): cpu = 00:00:14 ; elapsed = 00:00:25 . Memory (MB): peak = 2448.074 ; gain = 56.746 ; free physical = 107522 ; free virtual = 203345
INFO: [Project 1-571] Translating synthesized netlist
Netlist sorting complete. Time (s): cpu = 00:00:00 ; elapsed = 00:00:00 . Memory (MB): peak = 2455.000 ; gain = 0.000 ; free physical = 107608 ; free virtual = 203431
INFO: [Netlist 29-17] Analyzing 16 Unisim elements for replacement
INFO: [Netlist 29-28] Unisim Transformation completed in 0 CPU seconds
INFO: [Project 1-570] Preparing netlist for logic optimization
INFO: [Opt 31-138] Pushed 0 inverter(s) to 0 load pin(s).
Netlist sorting complete. Time (s): cpu = 00:00:00 ; elapsed = 00:00:00 . Memory (MB): peak = 2596.621 ; gain = 0.000 ; free physical = 107500 ; free virtual = 203324
INFO: [Project 1-111] Unisim Transformation Summary:
No Unisim elements were transformed.

INFO: [Common 17-83] Releasing license: Synthesis
14 Infos, 0 Warnings, 0 Critical Warnings and 0 Errors encountered.
...

回答此处评论中发布的后续问题:

为什么一般不建议将两条边都放入敏感列表?

在 Verilog 综合设计流程中,设计人员对物理硬件进行建模。物理触发器要么在 clk 上触发正边沿,要么在 clk 上触发负边沿,但不能同时触发两者。与电平敏感锁存器相同。它们由逻辑高电平或逻辑低电平激活,而不是同时被两者激活。


1
投票

我的问题是关于第二个问题:我确实在每个 分支。为什么还是抱怨?


您在每个分支中
没有

分配给out。这是一个没有分配给

out
的分支:
out

对于 Verilog 新手来说,一个常见问题是试图将所有逻辑塞进一个 
if (divisor > 0) div <= divisor; else

块中。如果您使用多个

always
块,您的代码会更容易理解。例如,在自己的块中驱动
always
逻辑会更好。

div


您的 linter 和综合工具可能会对
always @(clk, rst_n) begin // use level-sensitive of both signals

不满意。您应该将自己限制在一组通用的

可合成构造
中。

© www.soinside.com 2019 - 2024. All rights reserved.