Serial Adder in Verilog: Bit-Serial FSM with a Carry Flip-Flop
A parallel 8-bit adder spends 8 full-adders in one clock; a serial adder verilog code design spends a single full-adder across 8 clocks, processing one bit per cycle starting from the LSB. The running carry is remembered in a one-bit flip-flop that feeds the next bit’s addition, so the hardware shrinks dramatically at the cost of latency.
This page builds the classic bit-serial adder as a small FSM with two shift registers and a carry flip-flop, then shows the underlying full-adder bit-cell on its own. Both come with self-checking Icarus Verilog testbenches that add 100 + 55, 200 + 99 (note the carry-out when the result exceeds 255), and other cases.
Bit-serial adder: FSM + shift registers + carry FF
On load the operands are captured into LSB-first shift registers. For the next 8 clocks the design adds a_sr[0], b_sr[0] and the stored carry, shifts the sum bit into the MSB of sum, shifts the operands right, and updates the carry flip-flop. After 8 cycles done asserts and cout holds the 9th (carry-out) bit.
// Bit-serial adder: FSM + carry flip-flop, LSB-first
module serial_adder(
input clk, rst_n,
input load,
input [7:0] a_in, b_in,
output reg [7:0] sum,
output reg done,
output reg cout
);
reg [7:0] a_sr, b_sr; // shift registers (LSB-first)
reg carry; // carry flip-flop
reg [3:0] cnt; // 0..8 counter (FSM state)
reg busy;
wire bitsum = a_sr[0] ^ b_sr[0] ^ carry;
wire cnext = (a_sr[0] & b_sr[0]) | (a_sr[0] & carry) | (b_sr[0] & carry);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
a_sr <= 8'd0; b_sr <= 8'd0; carry <= 1'b0;
cnt <= 4'd0; busy <= 1'b0; sum <= 8'd0; done <= 1'b0; cout <= 1'b0;
end else if (load) begin
a_sr <= a_in; b_sr <= b_in; carry <= 1'b0;
cnt <= 4'd0; busy <= 1'b1; done <= 1'b0; cout <= 1'b0;
end else if (busy) begin
// compute one bit, shift it into MSB of sum, shift operands right
sum <= {bitsum, sum[7:1]};
carry <= cnext;
a_sr <= {1'b0, a_sr[7:1]};
b_sr <= {1'b0, b_sr[7:1]};
cnt <= cnt + 4'd1;
if (cnt == 4'd7) begin
busy <= 1'b0;
done <= 1'b1;
cout <= cnext; // carry-out after the 8th bit
end
end else begin
done <= 1'b0; // done is a 1-cycle pulse-ish; keep sum stable
end
end
endmodule
module tb;
reg clk, rst_n, load;
reg [7:0] a_in, b_in;
wire [7:0] sum;
wire done, cout;
integer errors = 0;
serial_adder dut(.clk(clk), .rst_n(rst_n), .load(load),
.a_in(a_in), .b_in(b_in), .sum(sum), .done(done), .cout(cout));
always #5 clk = ~clk;
task run_case;
input [7:0] A, B;
input [8:0] EXP; // 9-bit expected (incl carry-out)
begin
@(negedge clk); a_in = A; b_in = B; load = 1'b1;
@(negedge clk); load = 1'b0;
// 8 shift clocks
repeat (8) @(negedge clk);
if ({cout, sum} !== EXP) begin
errors = errors + 1;
$display("FAIL: %0d + %0d => {cout,sum}=%0d (expected %0d)", A, B, {cout,sum}, EXP);
end else begin
$display("PASS: %0d + %0d = %0d (cout=%b sum=%0d)", A, B, EXP, cout, sum);
end
end
endtask
initial begin
clk = 0; rst_n = 0; load = 0; a_in = 0; b_in = 0;
@(negedge clk); rst_n = 1;
run_case(8'd100, 8'd55, 9'd155); // 155 fits in 8 bits, cout=0
run_case(8'd200, 8'd99, 9'd299); // 299 > 255 -> cout=1, sum=43
run_case(8'd255, 8'd1, 9'd256); // overflow, cout=1 sum=0
run_case(8'd0, 8'd0, 9'd0);
if (errors == 0) $display("PASS: all serial-adder cases correct");
else $display("FAIL: %0d case(s) wrong", errors);
$finish;
end
endmodule
PASS: 100 + 55 = 155 (cout=0 sum=155) PASS: 200 + 99 = 299 (cout=1 sum=43) PASS: 255 + 1 = 256 (cout=1 sum=0) PASS: 0 + 0 = 0 (cout=0 sum=0) PASS: all serial-adder cases correct200 + 99 = 299 overflows 8 bits, so
cout=1and the low byte reads 43; 255 + 1 wraps tosum=0withcout=1.The heart of it: a full-adder bit-cell with a carry register
Strip away the shift registers and the serial adder is just one full adder whose carry-out is latched and fed back as the next carry-in. Here operands stream in LSB-first one bit per clock;
clrzeroes the carry before each new word.// Serial adder as a single full-adder bit-cell + carry flip-flop. // Operands arrive LSB-first, one bit per clock, on a and b. The cell adds // them with the stored carry and emits s; carry is held for the next bit. module serial_add_cell( input clk, rst_n, clr, input a, b, output s, output reg carry ); assign s = a ^ b ^ carry; // sum bit wire cnx = (a & b) | (a & carry) | (b & carry); // carry-out always @(posedge clk or negedge rst_n) begin if (!rst_n) carry <= 1'b0; else if (clr) carry <= 1'b0; // zero carry BEFORE a new word else carry <= cnx; end endmodule module tb; reg clk, rst_n, clr, a, b; wire s, carry; serial_add_cell dut(.clk(clk), .rst_n(rst_n), .clr(clr), .a(a), .b(b), .s(s), .carry(carry)); always #5 clk = ~clk; reg [8:0] result; // 8 sum bits + final carry-out integer i; integer errors = 0; task add_bytes; input [7:0] A, B; input [8:0] EXP; begin @(negedge clk); clr = 1'b1; // one cycle to clear carry @(negedge clk); clr = 1'b0; // carry is now 0 for (i = 0; i < 8; i = i + 1) begin a = A[i]; b = B[i]; // present bit i (LSB first) #1 result[i] = s; // sum bit = a^b^carry (combinational) @(negedge clk); // posedge latches carry for next bit end result[8] = carry; // carry-out after the last bit if (result !== EXP) begin errors = errors + 1; $display("FAIL: %0d + %0d => %0d (expected %0d)", A, B, result, EXP); end else $display("PASS: %0d + %0d = %0d", A, B, EXP); end endtask initial begin clk = 0; rst_n = 0; clr = 0; a = 0; b = 0; result = 0; @(negedge clk); rst_n = 1; add_bytes(8'd100, 8'd55, 9'd155); add_bytes(8'd200, 8'd99, 9'd299); add_bytes(8'd127, 8'd128, 9'd255); add_bytes(8'd255, 8'd255, 9'd510); if (errors == 0) $display("PASS: serial add-cell streams correct"); else $display("FAIL: %0d wrong", errors); $finish; end endmodulePASS: 100 + 55 = 155
PASS: 200 + 99 = 299
PASS: 127 + 128 = 255
PASS: 255 + 255 = 510
PASS: serial add-cell streams correctHow the bit-serial addition works
Binary addition is inherently serial from the LSB up: each column needs the carry produced by the column below it. A ripple-carry adder unrolls all 8 columns in space (8 full-adders); the serial adder folds them into time, reusing one full-adder for 8 consecutive clocks. The only state that must survive between columns is a single carry bit, held in the carry flip-flop. The operands live in shift registers so that bit i appears at position 0 on clock i, and each computed sum bit is shifted into the result register.
The full-adder equations
Every cycle the cell evaluates the two canonical full-adder expressions: the sum bit
s = a ^ b ^ carryand the carry-outcnext = (a&b) | (a&carry) | (b&carry). The sum is purely combinational, whilecarryis registered so the value used on clock i is the carry-out generated on clock i−1. For an N-bit addition the carry-out after N clocks is the (N+1)th bit of the true sum — exactly whatcoutcaptures in the first example.Synthesis notes
The area win is real: one full-adder, two N-bit shift registers, a carry flip-flop and a small modulo-(N+1) counter, versus N full-adders for a ripple adder. The trade-off is throughput — N clocks per addition instead of one — and a lower achievable clock period only helps if the surrounding datapath is also bit-serial. Keep the reset asynchronous and the carry update synchronous, and drive
load/clrfrom the controlling FSM so the carry is always cleared before the LSB is added.Common pitfalls
The classic bug is clearing the carry at the wrong time. The carry must be zeroed before the LSB is processed, never on the same edge that should latch the LSB's carry-out — do the latter and every multi-byte result loses its lowest carry. A second trap is forgetting the carry-out: an 8-bit
sumalone cannot represent 200 + 99, so expose a 9th bit (cout) or the testbench will wrongly flag a wrap as a failure. Finally, remember the latency: downstream logic must wait fordone, not samplesummid-stream.Interview angle
Interviewers love this design because it forces you to separate combinational logic (the full adder) from sequential state (the carry FF and shift registers), and to reason about latency vs. area. Be ready to state the cycle count (N clocks after load for an N-bit add), explain why the carry flip-flop is the only inter-column state, and sketch how the same idea extends to a serial subtractor (feed
~band initialise the carry to 1 for two's-complement) or a serial multiplier (add-and-shift).Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.▶ Try it in the ChipXpert LabRun it in the LabOpen this design in the free in-browser simulator — edit, compile and see the waveform/log instantly.
Learn Verilog with mentors, labs and placement support
This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.
Explore VLSI Courses Hi! Ask me about courses, fees, batches or discounts. ×ChipXpert AssistantAsk about courses, fees, batches and centres