The short answer: Good Verilog RTL follows a few firm rules: non-blocking assignments in clocked blocks and blocking in combinational blocks; every combinational output assigned on every path; one consistent reset style with synchronised release; no logic on clocks (use enables); no delays or initial blocks in synthesisable code; and clear names, named port connections and parameters instead of magic numbers. Run lint on every change and read the synthesis warnings.
Coding guidelines aren’t about style for its own sake. Each rule here exists because breaking it has caused a real bug: a simulation-synthesis mismatch, a latch, a race, a timing problem or a design nobody else could maintain.
This post closes my Verilog series and replaces three older posts on coding guidelines, Verilog for RTL design and Verilog for synthesis.
- The six areas
- Clocks
- Resets
- Combinational and sequential logic
- Synthesis rules
- Readability
- Tools that enforce the rules
Table of Contents
Part 1: The six areas

Part 2: Clocks
- Use as few clocks as possible, and keep each block on one clock where you can.
- Never put logic on a clock path in RTL (no gating with AND gates, no muxing). Use a clock enable instead; clock gating is inserted by tools with proper ICG cells.
- Don’t create clocks with dividers in RTL. Generate an enable pulse with a counter.
- Every signal that crosses between clocks goes through a proper CDC synchroniser.
Part 3: Resets
- Pick one style (asynchronous assert, synchronous release is common) and use it everywhere.
- Reset FSM state and control registers. Large datapaths that are always written before use often don’t need a reset.
- Never use a reset as data, and never generate resets from combinational logic without synchronising them.
Part 4: Combinational and sequential logic
// sequential: only <=, only clock and reset in the list
always @(posedge clk or negedge rst_n)
if (!rst_n) state <= IDLE;
else state <= next;
// combinational: only =, defaults first, complete case
always @(*) begin
next = state;
busy = 1'b0;
case (state)
IDLE: if (start) next = RUN;
RUN: begin busy = 1'b1; if (done) next = IDLE; end
default: next = IDLE;
endcase
end
- One always block writes each register; one assign or always block drives each net.
- No combinational loops.
- Keep combinational and sequential logic in separate blocks.
Part 5: Synthesis rules
| Avoid | Why | Instead |
|---|---|---|
| # delays in RTL | Ignored by synthesis: mismatch | No delays; check timing with STA |
| initial blocks (ASIC) | Ignored by synthesis | A reset |
| Incomplete sensitivity lists | Mismatch | always @(*) |
| casex, full_case / parallel_case | Hide x bugs or change only synthesis | casez with ?, explicit default |
| Latches you didn’t plan | Hard to time and test | Default assignments |
| Unsized constants and mixed signedness | Unexpected widths and signs | Sized constants, explicit casts |
| Division and modulo by variables | Huge, slow logic | Shifts, or a sequential divider |
After synthesis, always read the warnings: inferred latches, multiple drivers, unconnected ports, width mismatches and constant-propagated flops. They’re often real bugs.
Part 6: Readability
- Lowercase names with underscores; suffixes like _n (active low), _q (registered), _nxt (next value).
- Named port connections, one per line.
- Parameters and localparams instead of magic numbers.
- One module per file, file named after the module.
- A short header comment: what the block does, its clocks and resets, and any assumptions.
`default_nettype noneat the top of every file.
Part 7: Tools that enforce the rules
Run a linter on every change. Verilator’s lint mode is free and catches width mismatches, unused signals and many of the problems above. Commercial teams use tools like Spyglass Lint or Ascent Lint with company rule sets. A clean lint run before every commit saves far more time than it costs.
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
RTL Design problems · Daily challenge
Previous in my Verilog series: System tasks. That’s the end of my Verilog series. Next, I’d move on to SystemVerilog.
FAQ
What are the most important Verilog coding guidelines?
Non-blocking in clocked blocks, blocking in combinational blocks, complete assignments in combinational logic, one consistent reset style, and no delays in RTL.
Why should I avoid gated clocks in RTL?
Gating a clock with logic can create glitches and timing problems. Use enables in RTL and let tools insert proper clock gating cells.
How do I check my code follows the guidelines?
Use a linter (Verilator –lint-only is free) and read every synthesis warning.
Should every flip-flop have a reset?
Control and state registers, yes. Datapath registers that are always written before use often don’t need one, which saves area and power.
