A small loadable counter shows the difference. The two listings describe identical hardware.
module counter_v2001 (
input wire clk, rst_n, load, en,
input wire [7:0] din,
output reg [7:0] count,
output reg at_max
);
always @(posedge clk or negedge rst_n)
if (!rst_n) count <= 8'd0;
else if (load) count <= din;
else if (en) count <= count + 8'd1;
always @(count) // sensitivity list written by hand
at_max = (count == 8'hFF);
endmodulemodule counter_sv (
input logic clk, rst_n, load, en,
input logic [7:0] din,
output logic [7:0] count,
output logic at_max
);
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) count <= '0;
else if (load) count <= din;
else if (en) count <= count + 1'b1;
always_comb at_max = (count == '1); // sensitivity is automatic; '1 means "all ones"
endmoduleDifference | Benefit |
|---|---|
| Simpler variable declarations; |
| Intent is explicit; mistakes produce warnings or errors. |
| Correct width automatically; no more |
Complete sensitivity automatically | No stale-value bugs from a forgotten signal. |
Check tool support first. Synthesis flows accept a large SystemVerilog subset, but not all of it. Designs should stay within the documented subset.
Start with the cheap wins. logic, always_ff, always_comb and typedef enum change little and catch many bugs.
Turn on lint (Verilator -Wall, or the signoff lint tool). Intent violations are then reported on every compile.
Keep one style. Mixed old and new styles in one file are harder to read than either style alone.
Bring in interfaces last. They pay off where signal counts justify them, after the team agrees on connection and verification.
Not every SystemVerilog feature is synthesizable. Classes, constrained randomization, mailboxes and most program blocks are verification constructs, so RTL stays within the documented design subset.
'0 and '1 better than 8'd0 and 8'hFF in a parameterized module?They adapt to the width of the target automatically. With a fixed 8'hFF a widened register would only have its low byte set; '1 always sets every bit.