I am new to Verilog. I want to write a simple module to do clock dividing without using PLL. The module is named "uart_brg" since I plan to use it later in an uart module for exercise purpose.
I set the source file uart_brg.v
as the top module in Vivado project. The behavior simulations seem ok, but Vivado Linter (2023.1) reports two issues:
0
.out_reg
.The first issue is a kind of expected, although I am not sure if there are any risk if I just ignore it.
My question is about the second issue: I did assign to out
in every branch. Why does it still complain?
The following is the code. Note that in an attempt to keep the out clock of 50% duty cycle, and also to support 1:1 clock ratio, I increment the count at both rising and falling edges of the input clock, thus the sensitivity list is (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
This is the screen shot of behavior simulation when divisor is set to 3:
Please give some hints on the reported issues.
Update:
Based on advices from @Mikef and @toolic, the revised module is much simpler without error report from 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
The only "limitation" is that, for odd divisor values, the duty cycle of the output will be slightly less than 50%. I guess this should not be a problem for uart tx/rx modules.
Always blocks have the ability to infer registers or combinational logic; the syntax is different for each purpose.
The code in the post always @(clk, rst_n)
infers combinational logic.
To model registers, use always @(posedge clk, negedge rst_n)
This style infers registers with asynchronous reset.
The posted code seems designed to model registers because of the use of non blocking <=
assignments. However, the keyword posedge
is missing, so combinational logic is inferred which is subject to latches. Add the keyword posedge
to eliminate latches.
To model combinational logic, use always @(*)
with blocking assignments =
rather than non blocking <=
assignments.
These concepts (processes which infer comb logic vs registers, and blocking vs non blocking assignments) are mixed up in the post because of the use of always @(clk, rst_n)
(infers combinational logic) with non blocking assignments (should be used in processes intended to infer registers).
Its fine or even appropriate to omit assignments (for example the else part of an if statement) to variables that are correctly modeled as registers, because it holds its value until the next clock edge by design. When the design intent is clear that a register is modeled, then the tools will not infer latches.
After removing the unsigned
keyword (default is unsigned), and adding the posedge
and negedge
keywords needed to model synchronous logic\w active low asynchronous reset like this:
// 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 on the modified post produces:
...
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.
...
Answering follow-up question asked in the comments here:
Why it's generally not recommended to put both edges into the sensitivity list?
In Verilog synthesis deign flows, designers model physical hardware. Physical flip flops are either positive edge triggered on clk or negative edge triggered on clk, not both. Same with level sensitive latches,. they are activated by either a logic high or logic low, not both.