Skip to main content

I2C Testbench Design & Test Cases

I2C Testbench Design & Test Cases


Verifying Your I2C Master

Part 4 of 5: SystemVerilog I2C Master Design

Testbench Architecture

A well-structured I2C testbench has three layers:

Testbench | +-- Test Tasks (test_idle, test_start, test_write_byte, ...) | | | +-- DUT (i2c_master_controller) | +-- Slave Simulator (i2c_slave_responder) | +-- Assertions & Checks | +-- Monitoring Layer (waveform capture) +-- Reporting (PASS/FAIL summary)

Testbench Layers

Layer Responsibility Example
Stimulus Generation Drive input signals (start_cond_req, data_to_send) Assert start_cond_req, wait for busy=0
Slave Simulation Model slave device responses (ACK, hold SCL) Instantiate i2c_slave_responder
Observation & Checks Sample outputs; verify expected behavior Check scl=1 at idle, sda_falling at START
Error Detection Flag failures; accumulate results If error != expected, increment fail_count

Test Task Structure

Each test is a task that follows this pattern:

task test_name; begin // 1. Setup: Initialize state, set inputs reset_all(); @(posedge clk); // 2. Stimulus: Apply test conditions start_cond_req = 1'b1; repeat(100) @(posedge clk); // 3. Observation: Check outputs if (scl_in == 1'b1 && sda_in == 1'b0) begin $display("[PASS] START condition detected: SCL=1, SDA=0"); pass_count++; end else begin $display("[FAIL] START condition: SCL=%b, SDA=%b", scl_in, sda_in); fail_count++; end // 4. Cleanup: Deassert signals start_cond_req = 1'b0; end endtask

Stimulus Generation

Stimulus is the input you drive to test the DUT. For I2C masters, key stimuli are:

Master-Controlled Stimuli

  • start_cond_req - Request START condition
  • stop_cond_req - Request STOP condition
  • tx_data - Byte to transmit
  • rx_en - Enable receive mode
  • ack_nack_sel - Send ACK (0) or NACK (1)

Slave-Controlled Stimuli

  • scl_held_low - Slave holds SCL for clock stretching
  • sda_held_low - Slave drives SDA (collision or slave ACK)
  • slave_addr - Address the slave will respond to
  • slave_data - Data byte slave sends in read transaction

Stimulus Application Patterns

// Pattern 1: Request-wait-complete start_cond_req = 1'b1; @(busy == 1'b1); // Wait for DUT to acknowledge @(busy == 1'b0); // Wait for completion start_cond_req = 1'b0; // Pattern 2: Pulse for N cycles start_cond_req = 1'b1; repeat(5) @(posedge clk); start_cond_req = 1'b0; // Pattern 3: Hold until signal changes tx_data_req = 1'b1; wait(tx_done == 1'b1); tx_data_req = 1'b0;
Best Practice: Use @(busy) transitions instead of fixed delays. Designs may have variable latencies, and tests must adapt.

Expected Results & Assertions

Assertions are inline checks that halt simulation on failure:

// Inline assertion assert (scl_in == 1'b1 && sda_in == 1'b0) else $error("START condition failed"); // Procedural assertion (collect results) if (scl_in == 1'b1 && sda_in == 1'b0) begin $display("[PASS] START detected"); pass_cnt++; end else begin $display("[FAIL] Expected START; got SCL=%b, SDA=%b", scl_in, sda_in); fail_cnt++; end

Common Assertion Patterns

Signal Condition Assertion Why It Matters
Idle state (no tx) busy=0, error=0, scl=1, sda=1 Ensures clean initial state
START condition scl=1 & sda=0 at rising edge Detects spec-compliant transition
STOP condition scl=1 & sda=1 at rising edge Ensures clean bus release
Data bit set-up SDA stable before SCL rise Verifies timing margins
Collision detected error=1 when SDA changes unexpectedly Tests arbitration logic
Clock stretching busy=1 while scl_held_low=1 Validates slave synchronization

10 Core Test Cases

Here are the 10 tests included in the reference project:

Test 1: Idle State Verification
Purpose: Verify clean idle state after reset
Steps: Reset DUT, wait 10 cycles
Expected: busy=0, error=0, scl=1, sda=1
Why: Baseline health check
Test 2: START Condition
Purpose: Generate valid START condition
Steps: Assert start_cond_req, wait for completion
Expected: scl=1, then sda falls to 0 (START)
Why: Core I2C protocol requirement
Test 3: STOP Condition
Purpose: Generate valid STOP condition
Steps: Send START, then stop_cond_req
Expected: scl=1, then sda rises to 1 (STOP)
Why: Releases bus; all slaves return to idle
Test 4: Repeated START
Purpose: START without STOP
Steps: Send START, immediately send START again
Expected: SDA transitions twice while SCL remains high
Why: Multi-phase transactions (write addr, then read data)
Test 5: Write Byte 0xA5 with ACK
Purpose: Transmit data byte; slave acknowledges
Steps: Send START, drive tx_data=8'hA5, simulate slave ACK on bit 9
Expected: All 8 bits transmitted; sda_in pulled LOW on 9th clock (ACK)
Why: Core write transaction
Test 6: Read Byte (Slave Drives)
Purpose: Master reads 8 bits from slave
Steps: Send START+addr(read), simulate slave driving sda bits
Expected: Master samples all 8 bits correctly
Why: Read transaction; slave is data source
Test 7: Collision Detection
Purpose: Detect when SDA changes unexpectedly
Steps: Release SDA (sda_out=1) but set sda_held_low (simulate collision)
Expected: error=1, indicating arbitration loss
Why: Collision avoidance for multi-master systems
Test 8: Clock Stretching
Purpose: Slave holds SCL LOW; master waits
Steps: Release SCL, slave holds scl_held_low=1 for 50 cycles
Expected: busy=1 while SCL is held; transitions back after release
Why: Slave synchronization; master must be patient
Test 9: Multi-Byte Transaction
Purpose: Send multiple bytes in one transaction
Steps: START, write 0x50, 0xA5, 0x5A, 0xC3, STOP
Expected: All 4 bytes transmitted without errors
Why: Real-world pattern; tests state persistence
Test 10: Bus Busy Detection
Purpose: Verify busy flag during active transactions
Steps: Assert start_cond_req, monitor busy
Expected: busy=1 during START, busy=0 after STOP
Why: Prevents master from starting transaction on occupied bus

Debugging Failed Tests

Systematic Debugging Approach

  1. Read the error message: Does it say "FAIL: START condition" or "FAIL: Collision"?
  2. Check waveforms: Plot SCL/SDA vs. time. Look for timing violations.
  3. Verify stimulus: Did you drive the right signals at the right time?
  4. Check FSM state: Print state transitions: `$display("State: %s", state.name())`
  5. Review spec: Re-read the I2C timing constraints. Is setup time violated?

Common Failure Patterns

// Pattern 1: START never happens (scl stays 0) // Cause: FSM stuck in state transition // Fix: Check edge detection logic; verify scl_sync is toggling // Pattern 2: Collision detected on every write // Cause: Open-drain pull-up model is broken // Fix: Verify assign scl_in = (scl_out==0) ? 0 : 1; // Pattern 3: Data bits corrupted (0xA5 becomes 0xFF) // Cause: Slave ACK interfering with bit transmission // Fix: Ensure ACK happens only after all 8 bits // Pattern 4: Clock stretching ignored (busy never goes 1) // Cause: FSM doesn't monitor scl_held_low // Fix: Add wait condition: wait(scl_stable == 1'b1)
Debugging Golden Rule: Look at waveforms FIRST. Bus-level signals (SCL, SDA) tell the truth; logic inside the DUT might be opaque. If SCL never rises, the FSM is stuck—dive into register changes.

Key Takeaways

Key Takeaways

  • Testbench structure: Stimulus → DUT → Observation → Assert/Report.
  • Task-based testing: Each test is a self-contained task with clear pass/fail criteria.
  • Stimulus patterns: Use @(busy) handshakes instead of fixed delays for robustness.
  • Assertions: Inline assertions halt on failure; procedural checks accumulate pass/fail counts.
  • 10 core tests cover idle, START/STOP, data transmission, ACK/NACK, collision, clock stretching.
  • Debugging: Always start with waveforms. Bus-level signals reveal the truth.
  • Slave simulation: Simple responder beats complex UVM for quick validation.

What's Next?

In Part 5, we'll tackle Interview Prep Q&A and advanced topics: multi-master arbitration, performance optimization, UVM methodology, and how to discuss this project confidently in interviews.

Final part coming up—don't miss it!

About the author: Upalparna is a VLSI engineer and design verification specialist with expertise in functional verification, UVM testbenches, and protocol design. She runs The Silicon Stack, a VLSI education platform, and teaches embedded systems and semiconductor design.

Questions or feedback? Drop a comment below or connect on LinkedIn.

Series Navigation:
Part 1: Protocol Fundamentals →
Part 2: Master Controller FSM →
Part 3: Slave Responder & Pull-up Logic →
Part 4: Testbench Design & Test Cases (You are here)
Part 5: Interview Prep & Optimization

Comments

Popular posts from this blog

Module 1 — SystemVerilog Basics

VLSI Verification  •  SystemVerilog Series Module 1 — SystemVerilog Basics A complete, example-driven walkthrough of SystemVerilog fundamentals: data types, variables vs nets, arrays of every flavor, strings, enums, structs, unions, and typedef — the building blocks every verification engineer needs before UVM. 🧩 Beginner → Intermediate ⏱ ~25 min read 💻 Code Examples Included 📑 Table of Contents Introduction to SystemVerilog Verilog vs SystemVerilog Data Types: 2-State vs 4-State Logic Variables and Nets Arrays: Packed vs Unpacked Dynamic Arrays Queues Associative Arrays Strings Enumerations Structures Unions Typedef 1 Introduction to SystemVerilog SystemVerilog (IEEE 1800) is a unified hardware description and verification language (HDVL). It extends Verilog (IEEE 1364) with powerful constructs for design (interfaces, always_comb/always_ff, enhanced d...

GPU Memory System & Interconnect: How Data Travels Inside a Modern GPU

GPU Memory System & Interconnect: How Data Travels Inside a Modern GPU Modern GPUs deliver incredible computing power by processing thousands of operations simultaneously. While compute cores often receive the spotlight, the real performance of a GPU depends heavily on how efficiently data moves between memory and compute units. A GPU memory system is designed to provide extremely high bandwidth while keeping latency as low as possible. This is achieved through a collection of specialized hardware components working together, including the Memory Controller, HBM/GDDR memory, Crossbar, Network-on-Chip (NoC), DMA Engine, PCIe Interface, and NVLink. In this article, we'll explore each component in detail and understand how they collectively enable high-performance parallel computing. Why Does GPU Memory Matter? Every GPU application—from gaming and graphics rendering to AI model training and scientific simulations—requires continuous movement of enormous amounts of data. For examp...

Clock Domain Crossing (CDC): A Complete Beginner's Guide to Metastability, Synchronizers & Safe Data Transfer

Clock Domain Crossing (CDC): A Complete Beginner's Guide to Metastability, Synchronizers & Safe Data Transfer Modern System-on-Chip (SoC) designs rarely operate with a single clock. Instead, different modules such as CPUs, GPUs, memory controllers, communication interfaces, and peripherals often run at different clock frequencies or even use completely independent clock sources . When data moves from one clock domain to another, timing uncertainty can cause serious problems such as metastability , data corruption , and unpredictable system behaviour . This challenge is known as Clock Domain Crossing (CDC) . CDC is one of the most important topics in ASIC Design , RTL Design , ASIC Verification , FPGA Design , and SoC Architecture . Every modern chip—from smartphones to automotive processors—uses CDC techniques to ensure reliable communication between multiple clock domains. In this article, we'll explore CDC fundamentals, metastability, synchronization techniques, asynchro...

AXI Verification Interview Questions and Real-World Debugging

AMBA Protocol Series · Part 10 of 10 AXI Verification Interview Questions and Real-World Debugging Common protocol violations, how to read them off a waveform, and a curated set of interview questions with concise, correct answers — the capstone of the core 10-day series. AMBA Protocol Series: From APB & AHB to AXI — A Complete Guide for VLSI Engineers This wraps up the core 10-day series. Everything here draws directly on Days 1–9 — if any answer below feels unfamiliar, that's a good signal for which earlier post to revisit. (If you want to go further, see the 20-day extended list at the end of this post.) In This Article Common Protocol Violations Debugging From a Waveform Interview Questions and Answers Series Recap Extending the Series 1. Common Protocol Violations VALID deasserted before handshake — a source drops VALID before READY ever went high (violates the Day 8 stabi...

Cache Basics and Cache Mapping Techniques

  Cache Basics and Cache Mapping Techniques  Modern processors execute billions of instructions every second. However, processor speed has increased much faster than main memory (RAM) speed. This difference, known as the memory wall , creates a performance bottleneck because the CPU often has to wait for data from slower memory. To solve this problem, computer systems use cache memory —a small, high-speed memory located between the CPU and the main memory. In this article, we will understand the fundamentals of cache memory, the principle of locality, cache organization, and the three major cache mapping techniques. What is Cache Memory? Cache memory is a small, high-speed memory that stores copies of frequently accessed data from the main memory. Since accessing cache is much faster than accessing RAM, the CPU first checks whether the required data is available in the cache. If the data is found in the cache, it is called a cache hit . Otherwise, the CPU fetches the dat...

Timing Basics in Static Timing Analysis (STA): Delay Models, Sequential Timing, and Wire Delays

Timing Basics in Static Timing Analysis (STA) Static Timing Analysis (STA) is one of the most important verification techniques used in modern VLSI design. Before a chip is manufactured, engineers must verify that every signal reaches its destination within the required clock period. Even if a design passes functional simulation, it may still fail after fabrication because of timing violations. Unlike simulation, Static Timing Analysis mathematically analyzes every timing path without applying input test vectors. This makes STA significantly faster and capable of analyzing millions of timing paths efficiently. Table of Contents What is Static Timing Analysis? Why STA is Required Propagation Delay Contamination Delay Cell Delay Models Combinational Gate Delay Sequential Timing Setup Time Hold Time Wire Delay RC Delay Model PVT Corners Worked Examples Interview Questions Key Takeaways What is Static Timing Analysis? Static Ti...

Computer Architecture Interview Question Part 1

Skip to content Computer Architecture Interview Series Part 1 · CPU Basics · RISC vs CISC · ISA 50 Most Asked Computer Architecture Interview Questions (Part 1) The foundational round every VLSI, ASIC, and CPU design interview opens with: instruction set architecture, RISC vs CISC trade-offs, Von Neumann vs Harvard design, registers, endianness, and the fetch-decode-execute cycle — explained the way interviewers actually expect you to answer.  40 Questions (Q1–Q40) ⌛ 22 min read  VLSI & Computer Architecture Interviews On this page Foundations of Computer Architecture RISC vs CISC — Core Differences Von Neumann, Harvard & the Semantic Gap Architecture Styles, Endianness & Registers ILP, VLIW, Superscalar & Amdahl's Law Register Files, Microcontrollers & the Fetch Cycle Tip...