Every processor, graphics chip, network switch and FPGA design today starts as text. A modern chip contains billions of transistors, far too many to draw by hand as gates and wires. Engineers instead describe what the hardware should do in a language like Verilog, and tools turn that description into real circuits. In this tutorial, you'll see how a gate-level circuit becomes a few lines of Verilog, read your first design module, and learn how a testbench checks that it works.

Verilog is a text-based hardware description language (HDL). Instead of drawing gate schematics, engineers write Verilog code that describes the behavior of a circuit, and synthesis tools convert that code into gates and flip-flops for an ASIC or an FPGA. The same code can also be run in a simulator to check that it behaves correctly before any hardware is built.

12 min read | Beginner Level

What You'll Learn

  • What Verilog is and why hardware description languages revolutionized digital design
  • How Verilog abstracts gate-level schematics into behavioral code
  • The basic structure of a Verilog module including ports, signals, and behavioral blocks
  • How testbenches verify hardware designs through simulation

From Gates to Hardware Description

Digital elements such as a flip-flop can be built from combinational gates like NAND and NOR. The behavior of the circuit depends on which gates are used and how they are connected to each other. In a digital electronics course, you usually find these connections by writing a truth table, which lists the output for every combination of inputs, and then simplifying it with a Karnaugh map (K-map).

The figure below shows a D flip-flop circuit with an active-low reset rstn, next to its truth table. At each rising edge of the clock, output q becomes 1 only when rstn and d are both 1. In every other case, q becomes 0.

D flip-flop circuit built from NAND gates, shown next to its truth table

What is a hardware schematic ?

A schematic is a drawing that shows how gates are connected to each other to perform a function. The left side of the figure above is a schematic made of NAND gates.

Once we know which input combinations make the output 1 and which make it 0, we no longer need to look at the individual gates. The circuit can be hidden inside a black box that has the same inputs and outputs as the gate-level schematic. Anyone who uses the box only needs to know its behavior, not its wiring. This idea of hiding details behind a well-defined interface is called abstraction, and it is the key idea behind Verilog.

What is a Hardware Description Language ?

Drawing schematics works for a flip-flop, but not for a processor with millions of gates. It would be much easier to describe how the black box should behave and let a software tool work out the gates and connections. A hardware description language lets you do exactly that, and Verilog is one of the two most widely used HDLs (the other is VHDL).

Verilog looks a little like the C programming language, with modules, variables, operators and if statements, but there is an important difference. A software program runs one instruction after another on a processor. Verilog code describes hardware, where every gate and flip-flop works at the same time. Keeping this in mind will help you avoid many beginner mistakes.

SchematicVerilog (HDL)
What you createA drawing of gates and wiresText that describes behavior or structure
Who finds the gatesThe designer, often using K-mapsThe synthesis tool
Size of design it suitsA few dozen gatesFrom a few gates to billions of transistors
Can be simulated and reused easilyDifficultYes, the same file is simulated, synthesized and reused

Quick Check

If you write Verilog code for a circuit, who decides which gates are used to build it?

Show Answer

The synthesis tool. You describe the behavior, and the tool chooses and connects the gates from the cell library of the target chip or FPGA, minimizing the logic as it goes, much like a K-map would.

What is meant by design functionality ?

Before you write any code, you need to know exactly what the circuit should do. This is called the functionality or the specification of the design. For the D flip-flop above, the expected behavior is:

  • The design has a clock input clk, a data input d and an output q
  • It has an active-low reset input rstn: when rstn is 0, the design is in reset and q becomes 0
  • When the design is not in reset, q follows d
  • q changes only at the positive edge (0 to 1 transition) of clk

The next question is: once this behavior is written in Verilog, how do we make sure the code really behaves like this?

What is verification ?

Verification is the process of checking that a design does what its specification says. The most common method is simulation: you apply different values (called stimulus) to the inputs of the design, and compare its outputs against the values you expect.

Simulations run in software tools called simulators, which are part of the larger family of electronic design automation (EDA) tools. The design code, often called the RTL (register transfer level) code, is placed inside a testbench. The testbench is another piece of Verilog that creates the stimulus, connects it to the design and checks the outputs.

Testbench environment that drives stimulus into the design under test and observes its outputs

Sections of Verilog code

All Verilog code is written inside modules. A module is the Verilog version of the black box: it has a name, a list of ports that connect it to the outside world, and the code that describes what happens inside.

Verilog sections template

Most modules follow the same layout:


module [design_name] ( [port_list] );

  [list_of_input_ports]
  [list_of_output_ports]

  [declaration_of_other_signals]

  [other_module_instantiations_if_required]

  [behavioral_code_for_this_module]
endmodule
  • Module name and ports: the name of the block and the signals that go in and out of it
  • Other signals: internal wires and variables, declared with data types such as wire and reg
  • Module instantiations: copies of other modules used inside this one, like placing chips on a board
  • Behavioral code: assign statements and always blocks that describe what the module does

Not every module needs every section. A small design may have no internal signals or instantiations at all.

Example

The module dff below describes the D flip-flop from the beginning of this article. It has three inputs and one output, no internal signals and no instantiations of other modules. Its behavior is described in an always block, which runs every time the clock rises from 0 to 1.


// "dff" is the name of this module

module dff  ( input   d,            // Inputs to the design should start with "input"
                      rstn,
                      clk,
              output reg q);         // Outputs of the design should start with "output"

  always @ (posedge clk) begin      // This block is executed at the positive edge of clk 0->1
    if (!rstn)                      // At the posedge, if rstn is 0 then q should get 0
      q <= 0;
    else
      q <= d;                       // At the posedge, if rstn is 1 then q should get d
  end
endmodule                           // End of module

Compare the code with the specification: each bullet in the functionality list maps to a line of code. The <= operator is a non-blocking assignment, the standard way to update flip-flops in Verilog. Notice that there are no gates in this code. You only described the behavior, and the tool will work out the circuit.

Synthesis result: One D flip-flop clocked by clk, with an AND gate in front of its D input that computes rstn & d. Because the reset is checked only inside always @ (posedge clk), it is a synchronous reset: it takes effect at the next clock edge, not immediately. This is the same circuit as the gate-level schematic at the start of the article, where q becomes 1 only when both rstn and d are 1.

Real-World Application

Verilog is standardized as IEEE 1364, so code written for one tool works in tools from other vendors. It is used across the semiconductor industry, from small peripheral controllers such as a UART or a timer to processors, graphics chips and network devices with billions of transistors. Every flip-flop in those chips is described with a few lines like the dff module above, usually with many bits at once, for example a 64-bit register written as reg [63:0] q;.

Testbench code

The testbench is a module with no ports that sits at the top of the simulation. It declares signals to drive the design, creates an instance of the design and connects the signals to its ports, and then assigns values to those signals.


module tb;

  // 1. Declare input/output variables to drive to the design
  reg   tb_clk;
  reg   tb_d;
  reg   tb_rstn;
  wire  tb_q;

  // 2. Create an instance of the design
  // This is called design instantiation
  dff   dff0 ( .clk   (tb_clk),     // Connect clock input with TB signal
               .d     (tb_d),       // Connect data input with TB signal
               .rstn  (tb_rstn),    // Connect reset input with TB signal
               .q     (tb_q));      // Connect output q with TB signal

  // 3. The following is an example of a stimulus
  // Here we drive the signals tb_* with certain values
  // Since these tb_* signals are connected to the design inputs,
  // the design will be driven with the values in tb_*
  initial begin
    tb_rstn <= 1'b0;    // Start with reset asserted (active-low)
    tb_clk  <= 1'b0;    // Initialize clock to 0
    tb_d    <= 1'b0;    // Initialize data input to 0
  end
endmodule

Inputs of the design are driven by reg variables, because the testbench assigns values to them in an initial block. The output q is driven by the design, so it connects to a wire in the testbench. The syntax .clk (tb_clk) connects the port clk of the instance dff0 to the testbench signal tb_clk.

This testbench only sets the starting values. To actually exercise the flip-flop, it also needs a running clock, a reset that is released after a while, changes on d, a way to print the results, and a $finish to end the simulation. The complete version below adds these:


module tb;
  reg   tb_clk, tb_d, tb_rstn;
  wire  tb_q;

  dff   dff0 (.clk (tb_clk), .d (tb_d), .rstn (tb_rstn), .q (tb_q));

  always #5 tb_clk = ~tb_clk;       // Clock toggles every 5 units: rising edges at 5, 15, 25 ...

  initial begin
    // Print a line every time rstn, d or q changes
    $monitor ("T=%0t rstn=%b d=%b q=%b", $time, tb_rstn, tb_d, tb_q);

    tb_rstn = 1'b0;                 // Reset asserted
    tb_clk  = 1'b0;
    tb_d    = 1'b0;

    #12 tb_rstn = 1'b1;             // Release reset at T=12
    #10 tb_d    = 1'b1;             // T=22
    #10 tb_d    = 1'b0;             // T=32
    #10 tb_d    = 1'b1;             // T=42
    #20 $finish;                    // End the simulation at T=62
  end
endmodule
Output
T=0 rstn=0 d=0 q=x
T=5 rstn=0 d=0 q=0
T=12 rstn=1 d=0 q=0
T=22 rstn=1 d=1 q=0
T=25 rstn=1 d=1 q=1
T=32 rstn=1 d=0 q=1
T=35 rstn=1 d=0 q=0
T=42 rstn=1 d=1 q=0
T=45 rstn=1 d=1 q=1

Read the log against the specification:

  • At T=0, q is x (unknown), because no clock edge has happened yet and a flip-flop has no defined value at power-up
  • At the first rising edge (T=5), reset is active, so q becomes 0
  • d changes at T=22, but q only follows at the next rising edge, T=25. The same happens at T=32/35 and T=42/45

Every output matches the expected behavior, so this short test passes. A real testbench would check the values automatically instead of relying on someone reading the log, as you will see in later articles.

Quick Check

In the log, why does q stay 0 at T=22 even though d has just become 1?

Show Answer

Because q only changes at a positive edge of the clock. The next rising edge after T=22 is at T=25, and that is when q becomes 1.

Verification Before Fabrication

Manufacturing a chip costs a lot of money and takes several months, and a bug found after fabrication can mean building the chip again. That is why design teams often have more verification engineers than design engineers. Every block is simulated with thousands of tests, using testbenches built on the same idea as the one above: drive the inputs, then check the outputs.

Common Beginner Mistakes

Mistake #1: Assuming "It Synthesizes" Means "It Works"

Wrong: Running synthesis, seeing no errors and concluding the design is correct
Right: Write a testbench and simulate the design before taking it to hardware

Why? In software, a program that compiles and runs is often close to correct. Synthesis, however, is only a translation step: it will faithfully turn wrong logic into wrong hardware. Only simulation against the specification shows whether the behavior is right. In industry, verification often takes around 70% of the total design effort.

Mistake #2: Thinking Verilog is "Overkill" for Simple Logic

Wrong: Solving K-maps by hand for a multiplexer or an encoder, then typing in the resulting gates
Right: Describe the behavior and let the tool do the minimization


assign out = (select) ? a : b;    // Synthesizes to a 2-to-1 multiplexer

Why? K-maps and Boolean algebra are important for understanding hardware, but HDLs are not only for large designs like processors. The synthesis tool minimizes logic better and faster than a person, and the behavioral line above is easier to read and change. The designer's job is to get the architecture and behavior right, not to compete with the tool on Boolean math.

Mistake #3: Not Initializing Signals in Testbenches

Wrong: Declaring reg tb_clk, tb_d, tb_rstn; and starting the stimulus without giving them values
Right: In an initial block, set the clock low, assert reset and give every data input a known value, typically 0, before the test starts

Why? A reg that has never been assigned is x (unknown) in simulation. An x clock never produces a clean rising edge, and an x on rstn or d spreads to q and everything connected to it, so you cannot confirm that the design works.

Mistake #4: Reading Verilog Like a Software Program

Wrong: Assuming that the lines in a module execute one after another, from top to bottom


assign y = a & b;     // Uses a ...
assign a = x | z;     // ... which is computed "later"? No.

Right: Think of each assign statement and each always block as a separate piece of hardware that works all the time, in parallel with all others

Why? The two lines above describe two gates that are wired together, an OR gate feeding an AND gate. Their order in the file does not matter, just as the order in which you draw two gates on a schematic does not matter.

Practice Exercise

Challenge: Find the Sections of a Module

The module below is a 2-bit register built from two copies of the dff module, with an extra output that is 1 when both bits are 1. Identify each section from the Verilog sections template, and predict the gates and flip-flops that synthesis creates.


module reg2 ( input  [1:0] d,
              input        rstn,
              input        clk,
              output [1:0] q,
              output       both);

  wire [1:0] q_int;

  dff  bit0 (.d (d[0]), .rstn (rstn), .clk (clk), .q (q_int[0]));
  dff  bit1 (.d (d[1]), .rstn (rstn), .clk (clk), .q (q_int[1]));

  assign q    = q_int;
  assign both = q_int[0] & q_int[1];
endmodule
Hint

Go through the template from top to bottom: name and ports, other signals, instantiations, behavioral code. Each dff instance is one flip-flop.

Solution
SectionIn reg2
Module namereg2
Input portsd[1:0], rstn, clk
Output portsq[1:0], both
Other signalswire [1:0] q_int
Instantiationsbit0 and bit1, two instances of dff
Behavioral codeThe two assign statements

Synthesis creates two D flip-flops with synchronous reset (one per dff instance, each with its own AND gate on the D input) and one 2-input AND gate for both. The statement assign q = q_int; is just a connection and creates no gates.

Summary

In this tutorial, you learned:

  • Verilog is a hardware description language that describes a circuit as code, which tools can synthesize into gates and simulate to verify
  • A schematic shows every gate connection, while a Verilog module hides those details behind defined inputs and outputs
  • A module contains port and signal declarations, instances of other modules, and behavioral code such as assign statements and always blocks
  • A testbench instantiates the design, drives its inputs and checks its outputs in simulation, so bugs are found before fabrication
  • Unlike software, Verilog describes hardware where everything works in parallel, and flip-flops are updated with non-blocking assignments (<=)

Ready to run your first simulation? Continue with Verilog Hello World.

Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5