FPGA

Verilog Modeling Styles: Structural to Behavioral

By CourseTron Team ·  4 min read ·  Updated Oct 2026 💬 𝕏 in f
Table of Contents
    ? Print / Save as PDF

    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.

    Same full adder, three abstraction levels Structural 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); You pick every gate Dataflow assign sum = a ^ b ^ cin; assign cout = (a&b) | (cin & (a^b)); You write equations Behavioral always @(*) {cout,sum} = a + b + cin; You state the intent More control More abstraction CourseTron · coursetron.com
    Figure 1: The three styles trade fine control for brevity. All describe identical behavior.

    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

    AspectGate / structuralDataflowBehavioral
    AbstractionLowestMiddleHighest
    Full adder size5 statements2 statements1 statement
    Scales to 64-bit adderNeeds generate loops, 320+ gatesOne assign with +One line
    Synthesis controlMaximumGoodTool-driven
    Typical useNetlists, custom cellsDatapath glueFSMs, testbenches, models
    RiskTypos in wiringWidth mismatchesInferred 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.

    Gate-level full adder (what the structural code builds) a b cin XOR g1 x1 XOR g2 sum AND g4 AND g3 OR g5 cout CourseTron · coursetron.com
    Figure 2: Two XORs form the sum; two ANDs and an OR form the carry. Inputs a and b also feed g3.

    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

    Animation: a=1, b=1, cin=0 gives sum=0, cout=1 Full adder inputs (1 + 1 + 0) sum = 0 cout = 1 Blue bit enters, gold carry bit leaves on the cout wire. CourseTron · coursetron.com
    Figure 3: Input bits travel into the adder; the carry-out appears on its own wire.

    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.

    Advertisement