Synthesis recognises reset, enable and memory intent from a small set of supported patterns. Write each storage element in its canonical template, then read the mapping report to see what was built.
Pattern | Semantic intent | What to check in the report |
|---|---|---|
| Reset-priority storage with hold when disabled | Enable cell versus feedback mux in the mapped netlist |
Array with clocked write and registered read | Synchronous memory with explicit collision behaviour | Selected RAM resource, supported ports and collision mode |
| Combinational function | No unintended storage; exact gates are target-dependent |
Both counters below describe the same hardware: an 8-bit register with asynchronous reset, an enable, and an incrementer. The first splits next-state logic into its own combinational block; the second uses the single-block template. The comments mark the three things that decide the netlist.
module cnt_before (
input logic clk, rst_n, en,
output logic [7:0] q
);
logic [7:0] next;
always_comb begin
next = q; // hold path: en low keeps q (an enable, not a latch)
if (en) next = q + 8'd1; // incrementer: 8 bits, wraps from ff to 00
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) q <= '0; // reset first: wins over en
else q <= next;
end
endmodule
module cnt_after (
input logic clk, rst_n, en,
output logic [7:0] q
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) q <= '0; // reset first: wins over en
else if (en) q <= q + 8'd1; // no else: the register holds, which is the enable
end
endmoduleRead the two side by side: reset priority, the hold path when en is low, and the increment width are the same in both. Those three facts, not the number of processes, set the hardware. On an iCE40 target both counters map to the same 23 cells: 8 flip-flops with enable and asynchronous reset (SB_DFFER), 9 LUTs and 6 carry cells. In both, synthesis found the hold path and folded it into the flops' enable pin.
yosys -p "read_verilog -sv cnt.sv; synth_ice40 -top cnt_before; stat"
yosys -p "read_verilog -sv cnt.sv; synth_ice40 -top cnt_after; stat"The testbench under Testbench drives both counters with the same reset and enable sequence and flags any cycle where they disagree.
Check initial or reset state, reset priority, disabled hold behaviour, arithmetic width and observation timing. Splitting the processes is not itself a functional difference. The bench compares settled outputs after each sampled edge.
Each element uses its canonical template: a clocked process for flops, an array with synchronous ports for RAM, and full assignment for combinational logic.
A directed regression over selected reset, hold, count and wraparound histories.
module cnt_before (
input logic clk, rst_n, en,
output logic [7:0] q
);
logic [7:0] next;
always_comb begin
next = q;
if (en) next = q + 8'd1;
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) q <= '0;
else q <= next;
end
endmodule
module cnt_after (
input logic clk, rst_n, en,
output logic [7:0] q
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) q <= '0;
else if (en) q <= q + 8'd1;
end
endmodule
Testbench run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It is a directed regression over selected reset, hold, count and wraparound histories. It is not an equivalence proof.
Cell counts from Yosys 0.69 (2026-10-17), synth_ice40 with default options, each counter as its own top: 6 SB_CARRY, 8 SB_DFFER and 9 SB_LUT4 for both. This is a mapping count only: no place and route, timing or power was run.