Understand the four abstraction levels Verilog supports — from transistor switches all the way up to behavioral system descriptions.
The components of a target design can be described at different levels of abstraction using Verilog’s constructs. Each level trades off detail for compactness. One of Verilog’s key strengths is that all four can be freely mixed in the same design module.
nmos, pmos, cmos, tran, etc.module inverter ( output out, input in, input vdd, gnd ); // PMOS pulls output high when input is low pmos p1(out, vdd, in); // NMOS pulls output low when input is high nmos n1(out, gnd, in); endmodule
VDD
|
[pmos p1] ← gate = in
|
in ──┤── out ──→ output
|
[nmos n1] ← gate = in
|
GNDand, or, nand, nor, xor, xnor, not, buf.// 2-input AND gate and g1(y, a, b); // y = a & b // 3-input NAND gate nand g2(y, a, b, c); // y = ~(a & b & c) // Chaining gates to build an AOI function wire ab, cd; and g3(ab, a, b); // ab = a & b and g4(cd, c, d); // cd = c & d nor g5(y, ab, cd); // y = ~(ab | cd) → AOI
assign keyword — called a continuous assignment.assign must be a wire (net), not a reg.// Simple logic functions assign y = a & b; // AND assign y = a | b; // OR assign y = ~a; // NOT (inverter) assign y = a ^ b; // XOR // AOI: AND-OR-INVERT assign y = ~((a & b) | (c & d)); // Multiplexer using ternary operator assign out = sel ? data1 : data0; // 4-bit adder with carry assign {cout, sum} = a + b + cin; // All three assignments below run at the same time assign p = a & b; assign q = c | d; assign r = p ^ q; // p and q are always up-to-date
always and initial procedural blocks, with if/else, case, for, while — looks like a C program.// Combinational logic (always @* — sensitive to all inputs) always @(*) begin if (sel) out = data1; else out = data0; end // Sequential logic — D flip-flop with synchronous reset always @(posedge clk) begin if (reset) Q <= 1'b0; else Q <= D; end // Case statement — 4-to-1 mux always @(*) begin case (sel) 2'b00: out = in0; 2'b01: out = in1; 2'b10: out = in2; 2'b11: out = in3; endcase end // Initial block — testbench only (not synthesizable) initial begin clk = 0; reset = 1; #10 reset = 0; #100 $finish; end
One of Verilog’s most powerful characteristics is that all four levels can coexist seamlessly within a single design module. You are not forced to stay at one level.
A common real-world pattern:
module top ( ... ); // Behavioral — controller FSM always @(posedge clk) begin state <= next_state; end // Data Flow — combinational datapath assign result = op_a + op_b; // Gate Level — instantiate a critical cell manually nand g_fast(fast_out, p, q); // Module instantiation — structural composition my_adder u1 (.a(x), .b(y), .sum(s)); endmodule
A quick side-by-side reference of all four levels:
| Level | Basic Element | Key Construct | Synthesizable? | Compactness | Typical Use |
|---|---|---|---|---|---|
| Switch | MOS transistor | nmos, pmos | ✅ Yes | Very verbose | Standard cell design |
| Gate | Logic gate | and, nor, xor | ✅ Yes | Verbose | Post-synthesis netlists |
| Data Flow | Logical expression | assign | ✅ Yes | Compact | Combinational datapaths |
| Behavioral | Procedural statement | always, if, case | ⚠️ Mostly | Most compact | Controllers, testbenches |
initial, delays (#10), and some loop patterns are not synthesizable. Use them only in testbenches, not in RTL that will go through synthesis.In a real electronic circuit, all components are active and changing simultaneously. Verilog simulators are designed to model this parallel reality.
always blocks and assign statements run in parallel — unlike sequential C programs where one line executes after another.`timescale to match your technology node.always @(posedge clk) — concurrency and sequential operation are not mutually exclusive in Verilog.// These two blocks run CONCURRENTLY — both respond to clk at the same time always @(posedge clk) begin reg_a <= data_in; // Block 1 end always @(posedge clk) begin reg_b <= reg_a; // Block 2 — reg_a here is the OLD value end // ⚠️ Non-blocking (<=) ensures both blocks read old values before any update // This is why flip-flop chains work correctly in Verilog
<= (non-blocking) inside clocked always blocks to correctly model flip-flops. Use = (blocking) in combinational always @(*) blocks. Mixing them incorrectly is a common source of simulation vs synthesis mismatches.