Skip to main content

Verilog RTL Design & Testbench Best Practices

· loading · loading · ·
Hardware Design Verilog RTL RTL Design Rules Verilog Testbench Hardware Design Best Practices Synthesizable Verilog
Hardware Design
Axolot Logic
Author
Axolot Logic
Digital Design Engineer
Table of Contents
Verilog HDL Series - This article is part of a series.
Part 23: This Article

🛠️ Verilog RTL Design – Writing Rules & Best Practices
#

When writing RTL modules in Verilog, adhering to consistent structure and syntax is crucial for readability, synthesizability, and scalability. Here are the golden rules to follow:


📌 1. Module Definition
#

Start every design block with a module and end with endmodule.

In the image below, you can see an abstract representation of this module. It includes only the module declaration and port definitions. At this stage, it can now be connected to another module or instantiated inside another module.

module module_name(
  input  wire clk,
  input  wire rst,
  input  wire a,
  output wire y
);
// Internal logic here
endmodule

📌 2. Use Meaningful Port Names
#

Avoid single-letter names in real designs — use clk, rst_n, data_in, valid, etc.


📌 3. Port Direction & Type Declaration
#

Use input, output, and optionally inout. Ports should be typed as wire by default. Use reg only for values driven inside always blocks.

input  wire clk;
output reg  valid_out;  // Driven inside always block

📌 4. Consistent Formatting
#

Keep clean indentation and consistent naming. Example:

assign y = a & b;

📌 5. Combinational Logic → assign or always @(*)
#

Use assign for continuous assignments (preferred for simple logic):

assign y = a & b;

Or, for more complex logic:

always @(*) begin
  case (sel)
    2'b00: out = in0;
    2'b01: out = in1;
    ...
  endcase
end

📌 6. Sequential Logic → always @(posedge clk)
#

Use reg type and non-blocking assignment (<=) for sequential logic.

always @(posedge clk or posedge rst) begin
  if (rst)
    count <= 0;
  else
    count <= count + 1;
end

📌 7. Don’t Mix Blocking and Non-blocking
#

In the same always block, use only one type:

  • = for combinational logic (always @(*))
  • <= for sequential logic (always @(posedge clk))

📌 8. Reset Logic
#

Always define reset behavior if required by synthesis/initialization.

if (rst) begin
  state <= IDLE;
end

📌 9. Avoid Delay # in RTL
#

Delays like #10 are for testbenches only. They are not synthesizable and should never appear in RTL.


📌 10. Avoid initial in RTL
#

Use initial only in testbenches or simulation-only code.


✅ Summary Table
#

RuleDo ThisAvoid This
Use module and endmodulemodule mymod(...);❌ Unnamed or inline code
Use assign or alwaysassign y = a & b;always y = a & b;
Use <= for seq logiccount <= count + 1;count = count + 1;
Use reg inside alwaysreg valid; always @(...)wire assigned in always
Avoid delays in RTLassign #no_delay#10, #5 in logic
Clear reset logicif (rst) q <= 0;❌ No reset, or floating states

🧪 Verilog Testbench – Writing Rules & Best Practices
#

A testbench is a simulation-only Verilog module used to stimulate and observe the behavior of your RTL design. It is not synthesizable and is essential for verifying correctness before hardware implementation.


📌 1. No Ports in Testbench Module
#

Testbenches are not connected to external pins, so no input/output.

module tb_my_design;

📌 2. Declare Signals to Connect to DUT
#

Define reg for inputs and wire for outputs of your DUT.

reg clk, rst, a, b;
wire y;

📌 3. Instantiate the DUT (Design Under Test)
#

Connect declared signals to your RTL module using named mapping.

my_design dut (
  .clk(clk),
  .rst(rst),
  .a(a),
  .b(b),
  .y(y)
);

📌 4. Generate Clock Signal
#

Use an always block to toggle clock.

initial clk = 0;
always #5 clk = ~clk;  // 10ns clock period

📌 5. Apply Stimulus Using initial Block
#

Use initial to define your test sequence.

initial begin
  rst = 1; a = 0; b = 0;
  #10 rst = 0;
  #10 a = 1;
  #10 b = 1;
  #20 $finish;
end

📌 6. Monitor Outputs with $monitor or $display
#

Use these for observing signal values in the terminal or waveform.

initial begin
  $monitor("Time=%0t : a=%b b=%b y=%b", $time, a, b, y);
end

📌 7. Use $dumpfile and $dumpvars for Waveform (GTKWave etc.)
#

Enable waveform generation for viewing in tools like GTKWave.

initial begin
  $dumpfile("wave.vcd");
  $dumpvars(0, tb_my_design);
end

✅ Sample Testbench Template
#

module tb_and_gate;

  reg a, b;
  wire y;

  // Instantiate DUT
  and_gate dut (
    .a(a),
    .b(b),
    .y(y)
  );

  initial begin
    $dumpfile("wave.vcd");
    $dumpvars(0, tb_and_gate);
    $monitor("Time: %0t | a=%b b=%b y=%b", $time, a, b, y);

    // Stimulus
    a = 0; b = 0; #10;
    a = 1; b = 0; #10;
    a = 0; b = 1; #10;
    a = 1; b = 1; #10;
    $finish;
  end

endmodule

🎯 Summary Table
#

RuleDo ThisAvoid This
No ports in testbenchmodule tb_my_module;❌ Using input/output
Clock generationalways #5 clk = ~clk;❌ Manual toggling inside initial
Use initial for stimulusa = 1; #10; b = 0;❌ Using always with delays
Monitor output$monitor or $display❌ No visibility into values
Dump waveform$dumpfile("wave.vcd")❌ No waveform = no debug

📚 Recommended Verilog Coding Style Guide#

When writing Verilog code—whether for FPGA prototyping, ASIC design, or formal verification—following a clear and consistent coding style is crucial. It not only improves readability and maintainability but also helps teams collaborate more effectively.

For this reason, I highly recommend referring to the lowRISC Verilog Coding Style Guide as a baseline for best practices.

🔗 Guide Link: 👉

Why follow this guide?
#

  • ✅ Clear conventions for signal naming and module hierarchy
  • ✅ Guidelines for formatting, spacing, and indentation
  • ✅ Recommendations for synthesizable vs non-synthesizable constructs
  • ✅ Practices for simulation clarity and debugging ease

Whether you’re just getting started with Verilog or you’re building a large-scale design project, adopting this guide can save time and prevent costly misunderstandings.


Verilog HDL Series - This article is part of a series.
Part 23: This Article

Related

Verilog initial Block: Testbench & Simulation Essentials
· loading · loading
Hardware Design Verilog Initial Verilog Testbench Simulation Hardware Verification Digital Design
Hardware Design
Verilog Assignments: Procedural vs. Continuous Explained
· loading · loading
Hardware Design Verilog Assignments Continuous Assignment Procedural Assignment RTL Design Hardware Description Language
Hardware Design
Verilog Command-Line Input: $plusargs for Testbench Control
· loading · loading
Hardware Design Verilog Command Line Plusargs Testbench Simulation Control Hardware Verification
Hardware Design
Verilog Control Flow: if, case, loops & RTL Guidelines
· loading · loading
Hardware Design Verilog Control Flow Verilog If-Else Verilog Case Verilog Loops RTL Design
Hardware Design
Verilog Modules, Ports, Assignments & Best Practices
· loading · loading
Hardware Design Verilog Module Verilog Ports Verilog Assign Verilog Always Hardware Description Language
Hardware Design
Verilog Synthesis: From RTL to Gate-Level Netlist
· loading · loading
Hardware Design Verilog Synthesis RTL Design Gate Level Netlist FPGA Design ASIC Design
Hardware Design