A complete RTL-to-verification project featuring:
- Parameterized synchronous FIFO design in Verilog HDL
- Industry-standard UVM 1.2 testbench environment (full env/agent/driver/monitor/scoreboard/coverage)
- SVA assertion suite embedded in the interface
- FPGA synthesis on Xilinx Spartan-6
- Automated simulation flow via GNU Makefile
- Project Overview
- Architecture
- Directory Structure
- DUT Interface
- UVM Testbench Architecture
- Sequences & Tests
- SVA Assertions
- Functional Coverage
- Running on EDA Playground
- Running Locally (ModelSim/QuestaSim)
- FPGA Synthesis
- Key Results
| Parameter | Value |
|---|---|
| Design | Synchronous FIFO (single-clock domain) |
| Data Width | 8-bit (parameterized) |
| Address Width | 4-bit → FIFO Depth: 16 entries (parameterized) |
| Full/Empty Flags | Item-count–based (glitch-free) |
| Target FPGA | Xilinx Spartan-6 (XC6SLX9) |
| Verification | UVM 1.2, Constrained-Random, SVA |
| EDA Tools | ModelSim / QuestaSim / EDA Playground |
┌─────────────────────────────────────────────────────────────┐
│ fifo_sync (DUT) │
│ │
│ wr_en ──►┌───────────┐ ┌───────────┐◄── rd_en │
│ w_data──►│ Write │ │ Read │──► r_data │
│ │ Logic │ │ Logic │ │
│ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │
│ wr_ptr│ rd_ptr│ │
│ ▼ ▼ │
│ ┌──────────────────────────┐ │
│ │ mem[FIFO_DEPTH-1:0] │ (Register Array) │
│ └──────────────────────────┘ │
│ │
│ item_count ──► full / empty flags │
└─────────────────────────────────────────────────────────────┘
Key Design Decisions:
item_countregister (not Gray-code comparison) for clean flag logicr_datais registered (clocked output) — no combinational read path- Simultaneous read+write supported when not empty/full
Spartan6-Synchronous-FIFO/
│
├── src/ # RTL Design Files
│ ├── fifo_sync.v # ← Main DUT: Parameterized Synchronous FIFO
│ ├── tb_fifo.v # Legacy directed testbench (Verilog)
│ ├── top_fifo.v # FPGA top-level wrapper (7-segment display)
│ ├── debounce.v # Button debounce module for FPGA board
│ └── top_fifo.ucf # Xilinx constraint file (pin assignments)
│
├── uvm_tb/ # UVM 1.2 Testbench Environment
│ ├── fifo_if.sv # Interface (clocking blocks + SVA assertions)
│ ├── fifo_seq_item.sv # Transaction / Sequence Item
│ ├── fifo_sequences.sv # 6 Sequences (reset, fill, drain, R+W, random, stress)
│ ├── fifo_driver.sv # UVM Driver
│ ├── fifo_monitor.sv # UVM Monitor (passive observer)
│ ├── fifo_scoreboard.sv # Self-checking scoreboard (golden model)
│ ├── fifo_coverage.sv # Functional coverage collector
│ ├── fifo_agent.sv # UVM Agent (bundles driver+monitor+sequencer)
│ ├── fifo_env.sv # UVM Environment
│ ├── fifo_test.sv # 3 UVM Tests (directed, random, stress)
│ ├── tb_top.sv # Top-level testbench module (local sim)
│ ├── edaplayground_FULL_TB.sv # ← Single-file TB for EDA Playground
│ └── Makefile # Compile / Sim / Coverage automation
│
└── docs/ # Documentation
├── SELF_CHECKING_TESTBENCH_GUIDE.md
├── DV_UPGRADE_GUIDE.md
└── PROJECT_EXPLAINED.md
module fifo_sync #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 4 // FIFO Depth = 2^ADDR_WIDTH = 16
)(
input wire clk,
input wire rst_n, // Active-low synchronous reset
input wire wr_en,
input wire [DATA_WIDTH-1:0] w_data,
output wire full,
input wire rd_en,
output reg [DATA_WIDTH-1:0] r_data,
output wire empty
); ┌────────────────────────────────────┐
│ fifo_env │
│ │
│ ┌──────────────────────────────┐ │
│ │ fifo_agent │ │
│ │ │ │
│ │ ┌────────┐ ┌───────────┐ │ │
Sequences ───────────► │ seqr │ │ driver │──┼──┼──► DUT
│ │ └────────┘ └───────────┘ │ │
│ │ │ │
│ │ ┌───────────┐ │ │
DUT outputs ──────────────────────►│ monitor │ │ │
│ │ └─────┬─────┘ │ │
│ └────────────────────┼─────────┘ │
│ analysis_port │ │
│ ┌──────────┴──────────┐ │
│ │ │ │
│ ┌───────▼──────┐ ┌──────────▼─┐│
│ │ scoreboard │ │ coverage ││
│ │ (ref model) │ │ collector ││
│ └──────────────┘ └────────────┘│
└────────────────────────────────────┘
| Component | File | Role |
|---|---|---|
fifo_if |
fifo_if.sv |
Interface with clocking blocks + SVA |
fifo_seq_item |
fifo_seq_item.sv |
Constrained-random transaction |
fifo_driver |
fifo_driver.sv |
Drives transactions onto DUT pins |
fifo_monitor |
fifo_monitor.sv |
Passively samples DUT, broadcasts via AP |
fifo_scoreboard |
fifo_scoreboard.sv |
SV-queue reference model, self-checking |
fifo_coverage |
fifo_coverage.sv |
Functional coverage collection |
fifo_agent |
fifo_agent.sv |
Active agent (driver+monitor+sequencer) |
fifo_env |
fifo_env.sv |
Top-level environment |
| Sequence | Purpose |
|---|---|
fifo_reset_seq |
5-cycle idle / reset sanity |
fifo_fill_seq |
Write until FULL (corner case) |
fifo_drain_seq |
Read until EMPTY (corner case) |
fifo_sim_rw_seq |
Simultaneous read+write (32 cycles) |
fifo_rand_seq |
500 constrained-random transactions |
fifo_stress_seq |
10× fill → sim-RW → drain cycles |
| Test | Sequences Run | Coverage Target |
|---|---|---|
fifo_directed_test |
reset → fill → sim-RW → drain | Corner cases |
fifo_rand_test |
500 random transactions | Broad coverage sweep |
fifo_stress_test |
10 fill-drain stress cycles | Flag transition coverage |
Three SVA properties are synthesized directly into the interface (fifo_if.sv):
// 1. Writing when FULL must not increase fill level
property p_no_write_when_full;
@(posedge clk) disable iff (!rst_n)
(full && wr_en) |=> full;
endproperty
// 2. Reading when EMPTY must not decrease fill level
property p_no_read_when_empty;
@(posedge clk) disable iff (!rst_n)
(empty && rd_en) |=> empty;
endproperty
// 3. Post-reset: must be empty and not full
property p_reset_state;
@(posedge clk) $fell(rst_n) |=> (!full && empty);
endpropertyThese assertions caught 2 flag logic bugs during development before synthesis.
Coverage groups defined in fifo_coverage.sv:
| Covergroup | Coverpoints | Description |
|---|---|---|
cg_operations |
op_type, full, empty | All 4 operation types × flag states |
cg_data_values |
w_data boundaries | Zero, max, low/mid/high ranges |
cg_flag_transitions |
full/empty rise/fall | Fill and drain edge detection |
| Cross coverage | op_type × full, op_type × empty | Operations under boundary conditions |
Coverage Result: >95% functional coverage achieved with fifo_stress_test
(Run make coverage and check the COVERAGE SUMMARY printout)
Fastest way to run — no install required
- Go to edaplayground.com (free account)
- Settings → Simulator:
Aldec Riviera-PRO| UVM/OVM:UVM 1.2 - Left panel (Design): paste contents of
src/fifo_sync.v - Right panel (Testbench): paste contents of
uvm_tb/edaplayground_FULL_TB.sv - Click Run
- Change test by editing this line in
tb_top:run_test("fifo_directed_test"); // or fifo_rand_test / fifo_stress_test
cd uvm_tb/
# Run directed corner-case test
make all
# Run constrained-random test (500 transactions)
make sim TEST=fifo_rand_test
# Run stress test (10 fill-drain cycles)
make sim TEST=fifo_stress_test
# Run with code + functional coverage and generate HTML report
make coverage TEST=fifo_stress_test
make report
# Clean all build artifacts
make clean- Tool: Xilinx ISE 14.7
- Device: Spartan-6 XC6SLX9 (Nexys 3 / equivalent board)
- Top-level:
top_fifo.v(includes 7-segment display output and button debounce) - Constraints:
top_fifo.ucf
The FPGA demo allows interactive push-button write/read operations with the current FIFO state (full, empty, data) displayed on the 7-segment display.
| Metric | Result |
|---|---|
| Functional Coverage | >95% (stress test) |
| SVA Assertion Failures Caught | 2 flag logic bugs pre-synthesis |
| Scoreboard Mismatches | 0 (all tests pass) |
| FPGA Synthesis | Timing closure achieved, Spartan-6 |
| Test Corpus | 500+ constrained-random + directed corner cases |
Kushal Pitaliya — Electronics & Communication Engineering, CHARUSAT
LinkedIn | GitHub