Project 4: 4-bit Ripple Carry Adder
Build a chain of full adders and observe carry propagation.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Beginner-Intermediate |
| Time Estimate | 4-5 hours |
| Main Programming Language | Verilog (Alternatives: VHDL, SystemVerilog) |
| Alternative Programming Languages | VHDL, SystemVerilog |
| Coolness Level | Medium |
| Business Potential | Low |
| Prerequisites | Full adder logic, Module hierarchy |
| Key Topics | Carry propagation, Structural design |
1. Learning Objectives
- Implement a full adder
- Chain full adders into a ripple adder
- Validate carry propagation in waveforms
2. All Theory Needed (Per-Concept Breakdown)
Adders and Two’s Complement Arithmetic
Description/Expanded Explanation of the concept
Adders are the core of arithmetic hardware. Two’s complement lets the same adder perform subtraction by inverting and adding one. Carry propagation defines speed for ripple adders and is the main timing cost.
Definitions & Key Terms
- Full adder -> adds a, b, and carry-in
- Two’s complement -> negate by invert + 1
- Carry propagation -> carry ripples through bit positions
Mental Model Diagram (ASCII)
[A0+B0] -> c1 -> [A1+B1] -> c2 -> [A2+B2] -> c3

How It Works (Step-by-Step)
- Build a 1-bit full adder.
- Chain full adders for N-bit addition.
- For subtraction, invert B and add 1.
- Verify carry and overflow behavior.
Minimal Concrete Example
assign {c_out, sum} = a + b + c_in;
Common Misconceptions
- “Carry and overflow are the same.” -> They differ for signed numbers.
- “Ripple carry is always fine.” -> It can be too slow for high speeds.
Check-Your-Understanding Questions
- How do you compute a - b using an adder?
- Why is carry a timing bottleneck?
- What is the difference between carry and overflow?
Where You’ll Apply It
- This project: used in Section 3.2 and Section 4 (architecture)
- Also used in: P05-8-bit-alu-arithmetic-logic-unit.md, P19-calculator-with-7-segment-display.md
Combinational Logic Modeling
Description/Expanded Explanation of the concept
Combinational logic produces outputs that depend only on current inputs. In Verilog, this is modeled using assign or always @(*) blocks. The key rule is that every output must be assigned for every input condition, otherwise you create unintended storage (latches).
Definitions & Key Terms
- Combinational logic -> outputs depend only on current inputs
- Continuous assignment ->
assignfor wire-driven logic - Latch -> unintended storage inferred by incomplete assignments
Mental Model Diagram (ASCII)
inputs --> [logic] --> outputs

How It Works (Step-by-Step)
- Choose
assignfor simple logic oralways @(*)for complex logic. - Ensure outputs are assigned in all branches.
- Verify with exhaustive or directed tests.
Minimal Concrete Example
always @(*) begin
if (sel) y = b; else y = a;
end
Common Misconceptions
- “Missing default just means don’t care.” -> It creates a latch.
- “Blocking assignments are always wrong.” -> They are correct in combinational blocks.
Check-Your-Understanding Questions
- What hardware does an if/else create?
- Why does a missing default create storage?
- When should you use
assignvsalways @(*)?
Where You’ll Apply It
- This project: used in Section 3.2 and Section 5.3
- Also used in: P01-digital-gate-library.md, P03-7-segment-display-decoder.md, P04-4-bit-ripple-carry-adder.md
Verification with Testbenches and Waveforms
Description/Expanded Explanation of the concept
Testbenches are simulation-only modules that apply stimulus and check outputs. Waveforms (VCD) are the hardware engineer’s microscope; they reveal timing, glitches, and ordering problems. A good testbench is deterministic and covers edge cases.
Definitions & Key Terms
- Testbench -> a non-synthesizable module that drives a DUT
- VCD -> Value Change Dump waveform file
- Deterministic test -> same inputs produce same outputs every run
Mental Model Diagram (ASCII)
[Testbench] -> [DUT] -> [VCD] -> [GTKWave]

How It Works (Step-by-Step)
- Initialize inputs to known values.
- Apply stimulus over time.
- Dump waveforms and check outputs.
- Add assertions or PASS/FAIL messages.
Minimal Concrete Example
initial begin
$dumpfile("wave.vcd");
$dumpvars(0, tb);
a = 0; b = 1; #10;
$finish;
end
Common Misconceptions
- “If it simulates once, it’s correct.” -> Cover all relevant cases.
- “Waveforms are optional.” -> They are often the only way to debug timing.
Check-Your-Understanding Questions
- Why keep testbench and DUT separate?
- What is the purpose of
$dumpvars? - How do you make a testbench deterministic?
Where You’ll Apply It
- This project: used throughout Section 6 (testing)
- Also used in: all other projects in this folder
3. Project Specification
3.1 What You Will Build
A 4-bit ripple-carry adder with carry-in and carry-out.
3.2 Functional Requirements
- Requirement 1: Support 4-bit inputs with carry-in
- Requirement 2: Produce sum and carry-out
- Requirement 3: Test with vectors that exercise long carries
3.3 Non-Functional Requirements
- Performance: Stable operation at the target clock and interfaces.
- Reliability: Deterministic outputs on all defined inputs.
- Usability: Clear ports and documented behavior.
3.4 Example Usage / Output
{p['example_usage']}
3.5 Data Formats / Schemas / Protocols
{p[‘data_format’]}
3.6 Edge Cases
- Carry-out on max values
- Overflow behavior
3.7 Real World Outcome
3.7.1 How to Run (Copy/Paste)
vvp adder_tb
3.7.2 Golden Path Demo (Deterministic)
Run the demo command above with the provided testbench and confirm the outputs match the golden transcript.
3.7.3 CLI Transcript
3 + 5 = 8 (carry=0)
15 + 1 = 0 (carry=1)
3.7.4 Failure Demo (Expected)
# Example failure case
ERROR: Output mismatch at vector 3
Expected: 0x0A, Got: 0x0B
EXIT CODE: 1
Notes:
- Exit code 0 indicates all tests passed
- Exit code 1 indicates a test failure
4. Solution Architecture
4.1 High-Level Design
[inputs] -> [core logic] -> [outputs]

4.2 Key Components
| Component | Responsibility |
|---|---|
| full_adder | 1-bit adder logic |
| adder4 | Chain of four full adders |
4.3 Data Structures (No Full Code)
// Example signals (adapt to your design)
reg [7:0] state_reg;
reg [7:0] data_reg;
4.4 Algorithm Overview
Key Algorithm: Core control flow
- Initialize state/reset conditions.
- Apply inputs and compute outputs.
- Update state on clock edges (if sequential).
Complexity Analysis:
- Time: O(1) per cycle
- Space: O(N) for registers and logic
5. Implementation Guide
5.1 Development Environment Setup
iverilog -v
# Ensure GTKWave is installed for waveform viewing
5.2 Project Structure
project-root/
|-- src/
| |-- top.v
| |-- core.v
|-- tb/
| |-- tb.v
|-- Makefile
|-- README.md

5.3 The Core Question You’re Answering
“Why does carry ripple, and how does that affect timing?”
5.4 Concepts You Must Understand First
- Full adder logic
- Module hierarchy
5.5 Questions to Guide Your Design
- Do you want behavioral or structural style?
- How will you expose carry-out?
5.6 Thinking Exercise
Draw the 4 full adders and mark the carry chain.
5.7 The Interview Questions They’ll Ask
- Why is ripple carry slower than carry-lookahead?
- What is the critical path?
- How do you detect overflow?
5.8 Hints in Layers
- Write a 1-bit full adder first.
- Chain carries from bit 0 to bit 3.
5.9 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Adders | Digital Design and Computer Architecture | Ch. 2 |
5.10 Implementation Phases
Phase 1: Foundation
Goals:
- Establish core module structure
- Implement minimal behavior
Tasks:
- Scaffold module ports and internal signals
- Write a minimal testbench that compiles
Checkpoint: Simulation runs without errors
Phase 2: Core Functionality
Goals:
- Implement full logic
- Verify edge cases
Tasks:
- Complete core logic
- Add directed tests for edge cases
Checkpoint: All tests pass and waveforms match expectations
Phase 3: Polish & Edge Cases
Goals:
- Improve readability
- Document behavior
Tasks:
- Add comments and README notes
- Expand tests for unusual inputs
Checkpoint: Design is deterministic and documented
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| Reset strategy | Sync / Async | Sync | Simpler timing closure |
| Test coverage | Directed / Exhaustive | Exhaustive for small logic | Prevents missed cases |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Unit Tests | Test core logic | Small vectors |
| Integration Tests | Test modules together | Full system |
| Edge Case Tests | Boundary conditions | Max/min values |
6.2 Critical Test Cases
- Test 1: Exhaustive test for all 256 input pairs
- Test 2: Check carry-out for 0xF + 0x1
6.3 Test Data
Use deterministic vectors and document expected outputs.
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution |
|---|---|---|
| Carry wired wrong | Sum incorrect on high values | Verify carry chain |
7.2 Debugging Strategies
- Inspect waveforms at key internal signals
- Add temporary debug outputs to verify state
- Reduce testcases to the smallest failing case
7.3 Performance Traps
- Overly wide counters or combinational paths can reduce max clock
8. Extensions & Challenges
8.1 Beginner Extensions
- Add parameterization for widths
- Add optional features (enable, reset)
8.2 Intermediate Extensions
- Add configuration registers
- Build a simple driver or demo program
8.3 Advanced Extensions
- Integrate with another project in this series
- Implement a hardware demo on FPGA
9. Real-World Connections
9.1 Industry Applications
- Digital control systems and embedded peripherals
- FPGA prototyping and validation
9.2 Related Open Source Projects
- Yosys / nextpnr toolchain for open-source FPGA flow
- Example HDL projects in the FPGA community
9.3 Interview Relevance
- Demonstrates RTL thinking and verification skills
10. Resources
10.1 Essential Reading
- Digital Design and Computer Architecture - Focus on Ch. 2
10.2 Video Resources
- Search for project-specific HDL walkthroughs and waveforms
10.3 Tools & Documentation
- Icarus Verilog
- GTKWave
10.4 Related Projects in This Series
- See adjacent projects in
VERILOG_FROM_ZERO_PROJECTS/
11. Self-Assessment Checklist
11.1 Understanding
- I can explain the core concept without notes
- I can predict waveform behavior for basic inputs
11.2 Implementation
- All functional requirements are met
- All tests pass
- Edge cases are documented
11.3 Growth
- I can explain this project in an interview
- I documented at least one lesson learned
12. Submission / Completion Criteria
Minimum Viable Completion:
- Functional requirements implemented
- Testbench passes
- Waveforms inspected
Full Completion:
- All minimum criteria plus
- Edge cases covered and documented
Excellence (Going Above & Beyond):
- Hardware demo on FPGA
- Clear write-up of lessons learned
Appendix A: Deep Dive Walkthrough
A.1 Signal Map and Invariants
- Inputs:
a[3:0],b[3:0],cin - Outputs:
sum[3:0],cout
Invariant: {cout,sum} == a + b + cin for all input values.
A.2 Ripple Chain Diagram
cin -> FA0 -> c1 -> FA1 -> c2 -> FA2 -> c3 -> FA3 -> cout
^ a0,b0 ^ a1,b1 ^ a2,b2 ^ a3,b3

A.3 Critical Path Note
- Worst-case carry propagates through all 4 full adders.
- This is why ripple-carry is simple but slow for wide adders.
A.4 Deterministic Vector Set
| a | b | cin | sum | cout | |——|——|—–|——|——| | 0000 | 0000 | 0 | 0000 | 0 | | 0001 | 0001 | 0 | 0010 | 0 | | 1111 | 0001 | 0 | 0000 | 1 | | 1010 | 0101 | 1 | 0000 | 1 |
A.5 Waveform Debug Tips
- Probe every internal carry bit (c1,c2,c3) to see propagation.
- If sum is wrong for only some inputs, check bit ordering.
13. Deep Dive Appendix
13.1 Timing and Resource Budget
- Ripple carry delay grows linearly with width. For 4 bits, it is small; for 32 bits, it dominates.
- On FPGAs, adders often map to fast carry chains, reducing delay but still sequential in nature.
- Use this project to reason about critical path and why faster adder families exist.
13.2 Waveform Interpretation Guide
- Watch carry propagate from bit 0 to bit 3 on an input that triggers it (e.g., 0x0F + 0x01).
- Confirm that sum updates immediately after inputs change.
Example:
a=1111 b=0001 -> sum=0000 carry_out=1
13.3 Hardware Bring-Up Notes
- Map a[3:0], b[3:0] to switches and sum[3:0] to LEDs.
- Optionally show carry_out on a separate LED.
- Use a fixed clock? Not needed, this is combinational.
13.4 Alternate Implementations and Trade-offs
- Structural: chain full adders explicitly.
- Behavioral:
assign {cout,sum} = a + b;(simple, synthesizes to carry chain). - Carry lookahead: implement a 4-bit CLA and compare timing.
13.5 Additional Exercises
- Add a signed overflow flag for two’s complement addition.
- Parameterize the width and generate N-bit adders.
- Create a subtractor using two’s complement and reuse the adder.