# Verilog Modelling Styles
## Introduction to Modeling Styles
In Verilog, hardware can be described at different levels of abstraction. The two primary modeling styles are Behavioral and Structural.
- **Behavioral Modeling**: This is the highest level of abstraction. It describes what a circuit does, its function, without specifying how it is implemented with logic gates. It focuses on the algorithm and the flow of data. Behavioral models often use procedural blocks (`always`, `initial`) and high-level continuous assignments.
- **Structural Modeling**: This describes the implementation of a circuit. It specifies how a module is composed of smaller sub-modules and/or primitive logic gates, and how these components are interconnected. It is analogous to a netlist or a schematic diagram.
A typical design process involves starting with a behavioral description to define and verify the functionality, followed by a process of hierarchical refinement to create a detailed structural model that can be synthesized into hardware.
---
## Hierarchical Design Example 1: A 16-to-1 Multiplexer
This example illustrates the design of a 16-to-1 multiplexer (MUX) by progressively refining the design from a pure behavioral model to a full structural one.
### Version 1: Pure Behavioral Model
The simplest way to model a MUX is with a single behavioral statement. The vector `in` is treated as an array, and the `sel` input is used as the index to select one of the input bits.
```verilog
// Pure behavioral model of a 16-to-1 MUX
module mux16to1 (in, sel, out);
input [15:0] in;
input [3:0] sel;
output out;
assign out = in[sel];
endmodule
Version 2: Structural Model using 4-to-1 MUX Components
A 16-to-1 MUX can be built from five 4-to-1 MUXes. Four MUXes select one output from four groups of four inputs, and a final MUX selects the ultimate output from the results of the first four. This version is structural at the top level, but the 4-to-1 MUX component is modeled behaviorally.
// Behavioral model of a 4-to-1 MUX component
module mux4to1 (in, sel, out);
input [3:0] in;
input [1:0] sel;
output out;
assign out = in[sel];
endmodule
// Structural model of a 16-to-1 MUX
module mux16to1 (in, sel, out);
input [15:0] in;
input [3:0] sel;
output out;
wire [3:0] t; // Intermediate lines
mux4to1 M0 (in[3:0], sel[1:0], t[0]);
mux4to1 M1 (in[7:4], sel[1:0], t[1]);
mux4to1 M2 (in[11:8], sel[1:0], t[2]);
mux4to1 M3 (in[15:12], sel[1:0], t[3]);
mux4to1 M4 (t, sel[3:2], out);
endmodule
Version 3: Deeper Structural Hierarchy
The refinement continues by modeling the 4-to-1 MUX structurally using three 2-to-1 MUX components. The 2-to-1 MUX itself is modeled behaviorally.
// Behavioral model of a 2-to-1 MUX component
module mux2to1 (in, sel, out);
input [1:0] in;
input sel;
output out;
assign out = in[sel];
endmodule
// Structural model of a 4-to-1 MUX
module mux4to1 (in, sel, out);
input [3:0] in;
input [1:0] sel;
output out;
wire [1:0] t;
mux2to1 M0 (in[1:0], sel[0], t[0]);
mux2to1 M1 (in[3:2], sel[0], t[1]);
mux2to1 M2 (t, sel[1], out);
endmodule
Version 4: Gate-Level Structural Model
The final step in the refinement is to describe the base component, the 2-to-1 MUX, using primitive logic gates. This results in a fully structural design, from the top-level 16-to-1 MUX down to the gate level.
// Gate-level structural model of a 2-to-1 MUX
module mux2to1 (in, sel, out);
input [1:0] in;
input sel;
output out;
wire t1, t2, t3;
NOT G1 (t1, sel);
AND G2 (t2, in[0], t1);
AND G3 (t3, in[1], sel);
OR G4 (out, t2, t3);
endmodule
Hierarchical Design Example 2: A 16-bit ALU
This example demonstrates modeling a 16-bit Arithmetic Logic Unit (ALU) that performs addition and generates five common status flags.
- Sign: Whether the sum is negative (MSB is 1).
- Zero: Whether the sum is zero.
- Carry: Whether there was a carry out of the last stage.
- Parity: Whether the number of 1s in the sum is even or odd.
- Overflow: Whether the signed sum cannot fit in 16 bits.
Version 1: Pure Behavioral Model
The ALU is described behaviorally using high-level continuous assignments for the addition and all flag generation logic.
module ALU (X, Y, Z, Sign, Zero, Carry, Parity, Overflow);
input [15:0] X, Y;
output [15:0] Z;
output Sign, Zero, Carry, Parity, Overflow;
// 16-bit addition
assign {Carry, Z} = X + Y;
// Generation of status flags
assign Sign = Z[15];
assign Zero = ~|Z; // Reduction NOR
assign Parity = ~^Z; // Reduction XNOR
assign Overflow = (X[15] & Y[15] & ~Z[15]) |
(~X[15] & ~Y[15] & Z[15]);
endmodule
Version 2: Structural Model (Ripple-Carry using Behavioral Blocks)
A 16-bit ripple-carry adder is built by structurally connecting four 4-bit adder blocks. The 4-bit adder component is described behaviorally. This is an example of mixed-style modeling.
// Behavioral description of a 4-bit adder
module adder4 (S, cout, A, B, cin);
input [3:0] A, B;
input cin;
output [3:0] S;
output cout;
assign {cout, S} = A + B + cin;
endmodule
// Structural ALU using 4-bit adder blocks
module ALU (...); // ports as before
...
wire c[3:1];
// Instantiation of 4-bit adders
adder4 A0 (Z[3:0], c[1], X[3:0], Y[3:0], 1'b0);
adder4 A1 (Z[7:4], c[2], X[7:4], Y[7:4], c[1]);
adder4 A2 (Z[11:8], c[3], X[11:8], Y[11:8], c[2]);
adder4 A3 (Z[15:12], Carry,X[15:12], Y[15:12], c[3]);
// Flag logic remains behavioral
assign Sign = Z[15];
...
endmodule
Version 3: Structural Model (Ripple-Carry using Gate-Level Full Adders)
The design is refined further by building the 4-bit adder from four gate-level full-adder components.
// Gate-level structural model of a full adder
module fulladder (s, cout, a, b, c);
input a, b, c;
output s, cout;
wire s1, c1, c2;
xor G1 (s1, a, b);
xor G2 (s, s1, c);
and G3 (c1, a, b);
and G4 (c2, s1, c);
or G5 (cout, c2, c1);
endmodule
// Structural 4-bit adder using full adders
module adder4 (S, cout, A, B, cin);
...
wire c1,c2,c3;
fulladder FA0 (S[0], c1, A[0], B[0], cin);
fulladder FA1 (S[1], c2, A[1], B[1], c1);
fulladder FA2 (S[2], c3, A[2], B[2], c2);
fulladder FA3 (S[3], cout, A[3], B[3], c3);
endmodule
Version 4: Dataflow Model (Carry Lookahead Adder)
An alternative to the slow ripple-carry architecture is the Carry Lookahead Adder (CLA). It computes all carry bits simultaneously using two-level logic, making it much faster. This is modeled using pure dataflow (assign
statements).
The theory relies on carry propagate ($p_i = A_i \oplus B_i$) and carry generate ($g_i = A_i \cdot B_i$) functions. The carry for each stage is calculated directly from these functions and the initial carry-in.
// 4-bit Carry Lookahead Adder using dataflow modeling
module adder4 (S, cout, A, B, cin);
input [3:0] A, B;
input cin;
output [3:0] S;
output cout;
wire [3:0] p, g;
wire c1, c2, c3;
// Generate p and g terms
assign p = A ^ B;
assign g = A & B;
// Generate carry bits using lookahead logic
assign c1 = g[0] | (p[0] & cin);
assign c2 = g[1] | (p[1] & g[0]) | (p[1] & p[0] & cin);
assign c3 = g[2] | (p[2] & g[1]) | (p[2] & p[1] & g[0]) | (p[2] & p[1] & p[0] & cin);
assign cout = g[3] | (p[3] & g[2]) | (p[3] & p[2] & g[1]) | (p[3] & p[2] & p[1] & g[0]) | (p[3] & p[2] & p[1] & p[0] & cin);
// Generate sum bits
assign S[0] = p[0] ^ cin;
assign S[1] = p[1] ^ c1;
assign S[2] = p[2] ^ c2;
assign S[3] = p[3] ^ c3;
endmodule
Verification using a Testbench
A testbench is a Verilog module written to test another module, referred to as the Device Under Test (DUT). It is not synthesizable. A testbench typically includes:
- Instantiation of the DUT.
reg
variables to model the inputs to the DUT.wire
variables to capture the outputs from the DUT.- An
initial
block to provide a sequence of input stimuli. - System tasks like
$monitor
,$display
,$dumpfile
, and$dumpvars
to observe and record the DUT’s response.
A key advantage of hierarchical design is that the same testbench can be used to verify all versions of a module, from the most abstract behavioral model to the most detailed structural one, ensuring functional equivalence throughout the design process.
// Example testbench for the 16-bit ALU
module alutest;
reg [15:0] X, Y;
wire [15:0] Z;
wire S, ZR, CY, P, V;
ALU DUT (X, Y, Z, S, ZR, CY, P, V);
initial
begin
$dumpfile("alu.vcd"); $dumpvars(0,alutest);
$monitor($time," X=%h, Y=%h, Z=%h, S=%b, Z=%b, CY=%b, P=%b, V=%b",
X, Y, Z, S, ZR, CY, P, V);
#5 X = 16'h8fff; Y = 16'h8000;
#5 X = 16'hfffe; Y = 16'h0002;
#5 X = 16'hAAAA; Y = 16'h5555;
#5 $finish;
end
endmodule