我是 Verilog 新手。我想编写一个简单的模块来进行时钟分频而不使用 PLL。该模块被命名为“uart_brg”,因为我计划稍后在 uart 模块中使用它来进行练习。
我将源文件
uart_brg.v
设置为Vivado项目中的顶层模块。行为模拟看起来没问题,但 Vivado Linter (2023.1) 报告了两个问题:
0
。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 模块来说应该不是问题。
块始终具有推断寄存器或组合逻辑的能力;每个目的的语法都不同。
帖子中的代码
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 上触发负边沿,但不能同时触发两者。与电平敏感锁存器相同。它们由逻辑高电平或逻辑低电平激活,而不是同时被两者激活。
没有我的问题是关于第二个问题:我确实在每个 分支。为什么还是抱怨?
您在每个分支中
分配给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
不满意。您应该将自己限制在一组通用的
可合成构造中。