In short: Structural, dataflow and behavioral Verilog describe the same hardware at three abstraction levels (gates, Boolean equations, algorithmic intent), and for a one-bit full adder all three synthesize to nearly the same netlist, so the choice is about readability, control and verification effort rather than silicon.
Why three styles exist at all
Verilog was built to describe hardware at several levels of detail. A designer working on a standard-cell block cares about every gate; an architect modelling a processor pipeline wants to say "add these two numbers" and move on. The language therefore lets you write one circuit as an instantiation of primitives, as continuous equations, or as procedural code. Interviewers love this topic because it tests whether you understand what the synthesis tool actually infers, not just which keyword to type.
We will use a one-bit full adder throughout: inputs a, b, cin; outputs sum = a XOR b XOR cin and cout = ab + cin(a XOR b). It is small enough to draw, yet rich enough to show every difference.
Style 1: gate-level (structural) modeling
Here you instantiate built-in primitives (and, or, xor, not, nand) or your own sub-modules and wire them with nets. Output comes first in the port list of a primitive. Our adder needs two XORs, two ANDs and one OR.
module fa_gate(input a, b, cin, output sum, cout);
wire x1, a1, a2;
xor g1(x1, a, b);
xor g2(sum, x1, cin);
and g3(a1, a, b);
and g4(a2, x1, cin);
or g5(cout, a1, a2);
endmodule
This is the closest Verilog gets to a schematic. It is the style of post-synthesis netlists, where each cell is a library instance such as NAND2X1. Strictly, "gate-level" means primitives; "structural" is broader and includes hierarchical module instances. Learn the distinction in gate level modeling in Verilog and structural modeling in Verilog. Primitives also accept delays such as xor #2 g1(...), which is why this style is common in timing-aware testbench models.
Style 2: dataflow modeling
Dataflow uses continuous assignments. The right-hand side is re-evaluated whenever any operand changes, exactly like combinational logic.
module fa_df(input a, b, cin, output sum, cout);
assign sum = a ^ b ^ cin;
assign cout = (a & b) | (cin & (a ^ b));
endmodule
Targets of assign must be nets (wire), a rule explained in reg vs wire in Verilog. Expressions use bitwise, reduction, shift, conditional (?:) and concatenation operators. A 4:1 mux is one line with nested conditionals, versus roughly ten gate instances. For deeper operator coverage, see dataflow modeling in Verilog.
Key fact: An assign statement is not executed once; it is a permanent connection that reacts to every input change, which is exactly how a wire driven by logic behaves.
Style 3: behavioral modeling
Behavioral code uses always blocks (or initial in testbenches) with procedural statements: if, case, loops, and arithmetic operators. Assigned variables are declared reg (or logic in SystemVerilog) even though no flip-flop results.
module fa_beh(input a, b, cin, output reg sum, cout);
always @(*) begin
{cout, sum} = a + b + cin; // 2-bit result
end
endmodule
The tool sees an addition of three 1-bit operands, widens the context to two bits (the concatenation on the left sets the width) and maps the result to adder logic. On an FPGA, Vivado or Quartus will typically place this in a LUT pair or the dedicated carry chain. In ASIC flow, Synopsys Design Compiler or Cadence Genus chooses cells from the library to meet timing. Use = (blocking) for combinational blocks and <= for clocked ones; the reasoning is in blocking vs non-blocking assignments. More on this style: behavioral modeling in Verilog.
Side-by-side comparison
| Aspect | Gate / structural | Dataflow | Behavioral |
|---|---|---|---|
| Abstraction | Lowest | Middle | Highest |
| Full adder size | 5 statements | 2 statements | 1 statement |
| Scales to 64-bit adder | Needs generate loops, 320+ gates | One assign with + | One line |
| Synthesis control | Maximum | Good | Tool-driven |
| Typical use | Netlists, custom cells | Datapath glue | FSMs, testbenches, models |
| Risk | Typos in wiring | Width mismatches | Inferred latches |
What synthesis actually does
A common myth is that behavioral code is "slower" or "bigger". Logic optimization (Boolean minimization, technology mapping) erases most differences. After mapping to a library, our three adders will usually collapse to the same few cells, perhaps a single full-adder cell if the library has one. Where styles genuinely differ is in what you can hand-guide. If you must preserve a specific XOR structure for glitch-free or hazard-covered logic, structural code with a keep attribute (or a DONT_TOUCH constraint) protects it. Behavioral code gives the optimizer freedom, which is usually what you want.
Verification speed differs too. Event-driven simulators such as Siemens Questa, Cadence Xcelium and Synopsys VCS evaluate a behavioral adder as one operation, while the gate version schedules five events per input change. At millions of instances, that gap shows up in regression runtime.
Key fact: Writing a behavioral always @(*) block with an if that lacks an else infers a latch, not gates. Always assign every output on every path.
Watch the carry ripple
Which style should you choose?
- RTL for synthesis: mix dataflow for datapath and behavioral for control (FSMs with
case). This is what most FPGA and ASIC teams ship. - Netlists and cell models: structural, because the library cells are the gates.
- Testbenches and reference models: behavioral, often with delays declared via a timescale directive.
- Mixed in one module: perfectly legal. Many designs instantiate a hand-tuned structural cell inside behavioral RTL.
If you want guided practice taking these from simulation through synthesis, our CourseTron courses include lab exercises, and ChipXpert offers instructor-led VLSI training for the physical-design side that follows RTL.
Ready to write RTL that synthesizes cleanly the first time? Explore the Verilog and FPGA learning paths in the CourseTron course catalog.
FAQ
Do the three styles produce different hardware?
Rarely. After optimization, a one-bit full adder maps to nearly identical logic in all three. Differences appear mainly in code size, simulation speed and how much control you retain.
Can I mix styles in one module?
Yes. A module can contain gate primitives, assign statements and always blocks together, as long as each signal has legal drivers of the correct type.
Is behavioral code always synthesizable?
No. Constructs such as #10 delays, initial blocks for logic and file I/O are for simulation only. Synthesizable behavioral code must describe a clear combinational or clocked structure.
Which style is asked about most in interviews?
Expect to write one small block (mux, adder, comparator) in all three styles and explain why a behavioral block infers a latch when a branch is missing.