FPGA

Verilog Full Adder Gate Dataflow Behavioral

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

    In short: A Verilog full adder computes sum = a ^ b ^ cin and cout = (a & b) | (cin & (a ^ b)), and you can write it with primitives, a continuous assignment, or an always block, or compose it from two half adders and an OR gate. All four produce the same logic, so the real lesson is when each style is the right one.

    Why the full adder is the interview favourite

    Every arithmetic unit you will ever synthesize, from a 4-bit counter to a 64-bit multiplier in a GPU, is a pile of full adders wearing different clothes. A half adder adds two bits. A full adder adds three: operand bits a and b plus a carry-in cin, producing a sum bit and a carry-out. That third input is what lets you chain adders into a multi-bit word. If you need a refresher on the theory, see half adder and full adder basics first.

    Interviewers at EDA and chip companies like this module because it is small enough to write on a whiteboard but rich enough to expose how you think: do you know the Boolean equations, can you pick a modeling style on purpose, and do you understand what the synthesis tool will actually build?

    The truth table and the two equations

    Three inputs give eight rows. The sum is 1 when an odd number of inputs are 1 (that is the XOR of all three), and the carry is 1 when at least two inputs are 1 (majority function).

    abcinsumcout
    00000
    00110
    01010
    01101
    10010
    10101
    11001
    11111

    Key fact: The last row, 1 + 1 + 1 = 3, is binary 11, so both sum and cout are 1. Always test this row; it is the one broken designs get wrong.

    The carry equation can be written as the majority form ab + bc + ac or in the "generate/propagate" form ab + cin(a ^ b). The second form reuses the XOR that the sum already needs, which is exactly what the half-adder construction exploits.

    Style 1: gate-level modeling

    Gate-level code instantiates Verilog primitives (and, or, xor) and wires them with nets. It mirrors the schematic one to one, which is why it is used for small leaf cells and for learning. The gate level modeling in Verilog article covers primitive syntax and delays in depth.

    module fa_gate (input a, b, cin, output sum, cout);
      wire t, g, p;
      xor x1 (t,   a, b);
      xor x2 (sum, t, cin);
      and a1 (g,   a, b);
      and a2 (p,   t, cin);
      or  o1 (cout, g, p);
    endmodule

    That is five gates: two XOR, two AND, one OR. Primitives also accept delays such as xor #(2) x1(...), which makes this style useful for teaching propagation delay. In practice you rarely hand-write gates for a datapath, because the synthesis tool restructures logic anyway.

    Style 2: dataflow modeling

    Dataflow uses continuous assignments. The right-hand side is re-evaluated whenever any operand changes, which is how combinational hardware behaves. The left-hand side must be a net, which is the wire versus reg distinction explained in reg vs wire in Verilog.

    module fa_dataflow (input a, b, cin, output sum, cout);
      assign sum  = a ^ b ^ cin;
      assign cout = (a & b) | (cin & (a ^ b));
    endmodule

    Two lines, no internal nets, and the intent is obvious. This is the style most RTL reviewers prefer for small Boolean functions.

    One full adder, three coding styles Gate-level xor x1(t,a,b); xor x2(sum,t,cin); and a1(g,a,b); and a2(p,t,cin); or o1(cout,g,p); You draw the schematic Dataflow assign sum = a ^ b ^ cin; assign cout = (a&b)|(cin&(a^b)); You write the equations Behavioral always @(*) begin {cout,sum} = a + b + cin; end You state the arithmetic All three synthesize to the same 3-input, 2-output function. CourseTron · coursetron.com
    Same truth table, three levels of abstraction. Pick the one that matches the information you want to communicate.

    Style 3: behavioral modeling

    Behavioral code describes what the circuit does, not how it is wired. You use an always block, so the output must be a reg.

    module fa_behav (input a, b, cin, output reg sum, cout);
      always @(*) begin
        {cout, sum} = a + b + cin;
      end
    endmodule

    There is a classic trap here. Verilog sizes an expression to the widest operand in the whole statement, including the left-hand side. Because the concatenation {cout, sum} is 2 bits wide, the addition is carried out in 2 bits and the carry survives. Write sum = a + b + cin with a 1-bit sum and the carry is silently truncated. Inside the block we use blocking assignments because this is combinational logic; the rules are covered in blocking vs non-blocking assignments.

    Key fact: Use always @(*) (or always_comb in SystemVerilog) so the sensitivity list is complete. A hand-written list that misses cin simulates wrongly yet may still synthesize, a mismatch that gate-level simulation will catch late.

    Building a full adder from two half adders

    Half adder 1 adds a and b, producing s1 = a ^ b and c1 = a & b. Half adder 2 adds s1 and cin, producing the final sum and c2 = s1 & cin. An OR gate merges the two carries. The two partial carries can never both be 1 (if a & b is 1 then a ^ b is 0), so a plain OR is correct. The walk-through in full adder using half adders shows the derivation on paper.

    module half_adder (input a, b, output s, c);
      assign s = a ^ b;
      assign c = a & b;
    endmodule
    
    module fa_from_ha (input a, b, cin, output sum, cout);
      wire s1, c1, c2;
      half_adder h1 (.a(a),  .b(b),   .s(s1),  .c(c1));
      half_adder h2 (.a(s1), .b(cin), .s(sum), .c(c2));
      assign cout = c1 | c2;
    endmodule

    This is structural modeling: modules instantiated inside a parent and connected by name. If hierarchy is new to you, read structural modeling in Verilog. Named port connections (.a(a)) are strongly preferred over positional ones because swapping two ports then becomes a compile-visible error instead of a silent bug.

    Full adder = 2 half adders + OR cin a b Half adder 1 s1 = a ^ b c1 = a & b s1 Half adder 2 sum = s1 ^ cin c2 = s1 & cin sum c2 c1 OR cout CourseTron · coursetron.com
    The first half adder's XOR is reused by the second, so the whole cell needs only five gates.

    Watching the carry ripple

    Chain four full adders, feeding each cout into the next cin, and you have a 4-bit ripple-carry adder. The carry must travel through every stage in order, which is the central cost of this structure. In the animation below the carry moves from the least significant bit (right) to the most significant (left).

    Carry rippling through a 4-bit adder FA3 bit 3 (MSB) FA2 bit 2 FA1 bit 1 FA0 bit 0 (LSB) cin = 0 cout carry wire (orange dot = carry) CourseTron · coursetron.com
    Each stage waits for the carry from the stage to its right, so delay grows linearly with word length.

    What synthesis really does

    Run all four versions through Synopsys Design Compiler, Cadence Genus, or Vivado and the result is functionally identical. Logic optimization flattens the hierarchy, so the "two half adders" structure does not survive; the tool finds its own minimal mapping. On a Xilinx 7-series FPGA the sum and carry each fit in a single 6-input LUT, and a chain of adders is steered to the dedicated CARRY4 primitive, which is several times faster than routing carries through general fabric. In a standard-cell flow the tool often maps the cell to a library full-adder cell (for example an FADDX1-style cell) when one exists.

    StyleBest forRisk
    Gate-levelLeaf cells, delay teaching, netlistsVerbose, hard to scale
    DataflowSmall Boolean functionsEquation typos
    BehavioralWide adders, readable RTLWidth truncation, latches
    Structural (2 HA)Hierarchy, reuse, teachingPort miswiring

    Key fact: A 32-bit ripple-carry adder has roughly 64 gate delays on its carry path (two per bit through the AND-OR). Production designs replace it with carry-lookahead or prefix adders, and tools infer them from a plain + operator.

    Testing it properly

    With only three inputs, exhaustive simulation is cheap: 8 vectors cover everything. Make the testbench self-checking rather than eyeballing waveforms. Remember to set a time unit; see the timescale directive in Verilog.

    `timescale 1ns/1ps
    module tb;
      reg a, b, cin; wire sum, cout; integer i;
      fa_dataflow dut (a, b, cin, sum, cout);
      initial begin
        for (i = 0; i < 8; i = i + 1) begin
          {a, b, cin} = i[2:0]; #10;
          if ({cout, sum} !== a + b + cin)
            $display("FAIL %b%b%b", a, b, cin);
        end
        $display("done"); $finish;
      end
    endmodule

    Use !== so an x output counts as a failure. Run the same bench against all four modules to prove they are equivalent. Teams using Siemens Questa, Synopsys VCS or Cadence Xcelium do exactly this as a smoke test before moving up to formal equivalence checking.

    Ready to go beyond one cell? Join the hands-on Verilog and FPGA tracks in our CourseTron courses and build adders, ALUs and a small CPU with guided labs.

    If you want instructor-led placement-oriented training, ChipXpert runs full VLSI batches, and Avecas provides design services for teams.

    FAQ

    Why does the behavioral version use {cout, sum}?

    The 2-bit left-hand side forces Verilog to evaluate a + b + cin in 2 bits, so the carry is preserved. With a 1-bit target the carry would be dropped.

    Can the two carries from the half adders both be 1?

    No. c1 = a & b being 1 forces s1 = a ^ b to be 0, so c2 = s1 & cin is 0. That is why a simple OR combines them.

    Which coding style is fastest after synthesis?

    None. After optimization all styles reduce to the same logic. Choose by readability and maintainability, then control timing with constraints.

    Is the full adder a sequential circuit?

    No. It is purely combinational: outputs depend only on current inputs, with no clock or memory.

    Advertisement