Verilog is a hardware description language: every line of RTL ends up as gates and flip-flops. A consistent, well-organized coding style keeps code readable, maintainable and free of errors, but it does more than that. The way a circuit is described also affects synthesis, the step that turns RTL into a gate-level netlist for an FPGA or ASIC. Two descriptions with exactly the same behavior can lead the synthesis tool to different structures, with different area, timing and power.
10 min read | Beginner Level
What You'll Learn
- How three equivalent ways to write the same counter lead to different logic in the synthesis schematic
- Why the RTL schematic and the final optimized netlist can differ
- How writing style influences resource sharing and priority logic
- How to write RTL that is both readable and efficient
The three examples below describe a 2-bit counter with a synchronous, active-low reset that counts 0, 1, 2, 3 and then returns to 0. Every counter needs an adder to increment the value and flip-flops to store it, so those parts are the same in all three. The only difference is how the condition that returns the counter to 0 is written, and therefore how the reset logic is built.
Example #1
The first version combines the reset and the wrap-around condition into one expression: the counter is cleared when rstn is 0 or when both bits of out are 1.
module cntr_mod3 (input clk, rstn, output reg [1:0] out);
always @(posedge clk) begin
if (!rstn | out[1] & out[0])
out <= 0;
else
out <= out + 1;
end
endmodule
![Synthesis schematic of Example 1, with an AND gate for out[1] & out[0] and an OR gate that combines it with the reset condition](https://chipverify.com/images/verilog/3modcnt_andbits.png)
The tool builds the logic exactly as described: an AND gate for out[1] & out[0], and an OR gate that combines it with the inverted reset. Note that & has a higher precedence than |, so the condition reads as !rstn | (out[1] & out[0]).
Example #2
The second version keeps the reset and the wrap-around in separate, nested if statements, and tests the wrap-around with a comparison against 3:
module cntr_mod3 (input clk, rstn, output reg [1:0] out);
always @(posedge clk) begin
if (!rstn)
out <= 0;
else
if (out == 3)
out <= 0;
else
out <= out + 1;
end
endmodule

Each if becomes its own multiplexer, so the schematic has two multiplexers in a chain, along with the logic for the out == 3 comparison. Before optimization, this uses more gates than Example #1, and therefore more area and power.
Example #3
The third version keeps the nested structure, but tests the wrap-around with the reduction AND operator &out, which is 1 only when every bit of out is 1:
module cntr_mod3 (input clk, rstn, output reg [1:0] out);
always @(posedge clk) begin
if (!rstn)
out <= 0;
else
if (&out)
out <= 0;
else
out <= out + 1;
end
endmodule

Synthesis produces a single multiplexer and a reduction AND element, matching the RTL description. Each of the three styles has its trade-offs: Example #1 is the most compact but mixes reset and function in one expression, while Examples #2 and #3 keep the reset separate, which is easier to read and to reuse with a different reset scheme.
Quick Check
All three counters go from 3 back to 0. What would a plain out <= out + 1;, without any wrap-around condition, do at 3?
Show Answer
It would also go to 0. out is 2 bits wide, so 3 + 1 = 4 does not fit and wraps around to 0. The wrap-around conditions in all three examples are therefore redundant, which leads to the next point.
RTL Schematic vs Optimized Netlist
The schematics above are the RTL view that synthesis tools show right after reading the code, before optimization. They mirror the code structure, which makes them useful to see how a coding style is interpreted. The final netlist, however, is produced after optimization, which removes redundant logic.
Because a 2-bit counter wraps from 3 to 0 by itself, an optimizing tool can recognize that the wrap-around condition never changes the result, and all three examples can end up as the same small netlist: two flip-flops with synchronous reset and the increment logic. The differences in the schematics are real, but for logic this simple, the optimizer usually removes them.
Coding style matters most where the tool cannot prove that two descriptions are equivalent, or where the description fixes a structure that the tool will keep, such as an expensive operator that is written twice.
Despite the module name cntr_mod3, these counters have four states, 0 to 3. A mod-3 counter, with the states 0, 1 and 2, must return to 0 when out == 2. See Verilog Mod-N Counter.
Synthesis result: Use the area and timing reports of the optimized netlist, not the RTL schematic, to compare coding styles. The RTL view shows how the tool understood the code; the reports show what it finally built.
Resource Sharing
A clearer case where style changes the result is resource sharing. Both lines below select between two sums, but they describe different hardware:
// Style A: two adders, then a multiplexer
assign y = sel ? (a + b) : (c + d);
// Style B: two multiplexers, then one adder
assign y = (sel ? a : c) + (sel ? b : d);
For 32-bit values, an adder is much larger than a multiplexer, so Style B is smaller. Style A, on the other hand, is faster: both sums are computed in parallel and only a multiplexer follows them, while in Style B the multiplexers delay the start of the addition. Many synthesis tools can share resources automatically, but only within limits, so writing the shared version directly is the reliable way to get it when area matters.
Quick Check
A design computes a * b in one state of an FSM and c * d in another, and never both at the same time. How could the RTL be written so that only one multiplier is built?
Show Answer
Select the operands first and multiply once: prod = (state == S1 ? a : c) * (state == S1 ? b : d);. Because the two products are never needed at the same time, one multiplier with multiplexed inputs gives the same results with roughly half the multiplier area.
Area Budgets in Datapath Blocks
In signal processing blocks such as filters, multipliers dominate the area. Designers deliberately write RTL so that one multiplier is reused over several clock cycles for different coefficients, instead of instantiating one per coefficient. The same filter can be built small and slow or large and fast, purely by how the RTL schedules its operations.
Coding Guidelines in Design Teams
Design teams write down coding guidelines, such as "one register per always block", "reset in its own branch", "use case for mutually exclusive selections", so that RTL from different engineers reads the same way and synthesizes predictably. The guidelines are enforced with lint tools, and reviewed against synthesis area and timing reports when a block is too large or too slow.
Common Beginner Mistakes
Mistake #1: Judging area from the RTL schematic
Wrong: Choosing Example #1 over Example #2 only because its RTL schematic has fewer elements
Right: Compare the area and timing reports of the optimized netlist
Why? The RTL view is drawn before optimization. Small differences, such as those between the three counters, often disappear in the final netlist.
Mistake #2: Assuming shorter code means less hardware
Wrong: assign y = sel ? (a + b) : (c + d); because it fits on one line
Right: Think about which operators are built, and write the shared version when area matters
Why? Every operator in the code is a request for hardware. Two additions on one line still describe two adders, unless the tool manages to share them.
Mistake #3: Mixing reset and function logic without need
Wrong: Putting the reset into the same expression as the functional conditions, as in if (!rstn | out[1] & out[0]), throughout a large design
Right: Keep the reset as the first, separate branch: if (!rstn) ... else ...
Why? A separate reset branch is easier to read, to review, and to change later, for example from synchronous to asynchronous reset. Tools also recognize the standard pattern and map it to flip-flops with built-in reset.
Mistake #4: Using priority if-else chains for mutually exclusive conditions
Wrong: A long if ... else if ... chain on a one-hot select signal
Right: A case statement, or an AND-OR structure for one-hot selects
Why? An if-else-if chain describes priority, which becomes a chain of multiplexers whose delay grows with every branch. When the conditions can never be true at the same time, the priority is unnecessary, and a parallel structure is faster.
Practice Exercise
Challenge: A Real Mod-3 Counter in Two Styles
Write a counter that counts 0, 1, 2, 0, 1, 2, ... with a synchronous, active-low reset, in two styles: once with the wrap-around condition combined with the reset in one expression, as in Example #1, and once with a separate reset branch, as in Example #3. Explain why, unlike the 2-bit counters above, the wrap-around condition cannot be optimized away.
Hint
The counter must return to 0 when it is 2, which is out == 2, or out[1] & ~out[0]. Then compare what out + 1 alone would give at 2.
Solution
// Style 1: reset and wrap-around in one condition
module cntr_mod3_a (input clk, rstn, output reg [1:0] out);
always @(posedge clk)
if (!rstn | out[1] & ~out[0])
out <= 0;
else
out <= out + 1;
endmodule
// Style 2: separate reset branch
module cntr_mod3_b (input clk, rstn, output reg [1:0] out);
always @(posedge clk)
if (!rstn)
out <= 0;
else if (out == 2)
out <= 0;
else
out <= out + 1;
endmodule
At 2, out + 1 alone would give 3, so the wrap-around condition is needed to go back to 0, and the optimizer has to keep it. Both styles describe the same function, and after optimization they usually give the same netlist; Style 2 is easier to read and change. The value 3 is never reached after reset, which the tool may also use to simplify the logic further.
Summary
In this tutorial, you learned:
- The coding style of RTL affects which structures synthesis builds, and therefore area, timing and power
- Three equivalent counters produce different RTL schematics: AND and OR gates, two multiplexers with a comparator, or one multiplexer with a reduction AND
- The RTL schematic is drawn before optimization; compare styles using the reports of the optimized netlist
- Redundant logic, such as wrap-around in a full-range counter, is usually removed by optimization
- Style matters most for expensive operators, where sharing an adder or multiplier through multiplexed inputs saves area
- Readable, consistent patterns, such as a separate reset branch, synthesize predictably and are easier to maintain