Verifying an I2C Multiple Bus Controller using SystemVerilog

Design Verification ECE 745
GitHub - SantoshSrivatsan24/ece745: Verifiying a Wishbone to I2C Multiple Bus Controller using SystemVerilog

Verifiying a Wishbone to I2C Multiple Bus Controller using SystemVerilog - GitHub - SantoshSrivatsan24/ece745: Verifiying a Wishbone to I2C Multiple Bus Controller using SystemVerilog

Introduction

The goal of this project was to design a SystemVerilog testbench around a Wishbone to I2C Multiple Bus Controller. i.e., A DUT that takes in a Wishbone command and produces an I2C signal. This is a project I worked on through the semester and it was divided into four phases.

Phase 1

We were provided with the DUT and a BFM for its wishbone side. The first step was to build a BFM that could understand the I2C protocol. This involved pouring over the I2C spec to learn how a data transfer took place. I had to learn how to detect START, STOP, ACK, and NACK commands and how to piece them together to form a data transfer.

A write to the I2C slave begins with a START command followed by the slave address, the data, and a STOP command. The slave acknowledges (ACKs) every byte of data sent from the master.

A read from the I2C slave begins with a START command and is followed by the slave address. The master (DUT) then gives up the bus for the slave to drive with the data to be received (by the master).

We tested the functionality of our BFM with a stream of continuous writes, reads, and alternating reads and writes.

Phase 2

Once we had a working I2C BFM, we had to construct a full testbench around the design. I started by drawing a block diagram of all the SV components to understand which blocks needed to communicate with each other.

The BFM which was previously driven from the top-level testbench is now controlled by the Driver and Monitor. The Driver and Monitor are encapsulated into an Agent - a single component to handle the transaction level abstraction for our I2C protocol. The Agent forwards transactions from the Monitor to a Scoreboard which in turn receives “golden” transactions from a Predictor. The Agents for each protocol (Wishbone and I2C) along with the Predictor and Scoreboard make up the Environment for our testbench. The Environment is largely a component for analyzing transactions and is separate from the Generator which stimulates the design. The testbench also includes a Configuration component for the design.

🗒️

- If the Configuration is in the environment, we’ve entangled the two. We can’t randomize the configuration structure first. - An example configuration could be whether the DUT operates with or without interrupts enabled.

Fig. 1: Reusable layered testbench

With all these blocks, I had to decide when it was appropriate to use a blocking or non-blocking method to connect them. For instance, the Driver interfaces with the BFM through blocking methods bl_put and bl_get. But the Monitor can send transactions to its Agent through a nb_put. Likewise, the Predictor/golden model can send transactions to the Scoreboard using a nb_put.

The predictor was the hardest component to implement as it models the byte-level FSM in the DUT. It receives Wishbone commands from the Wishbone monitor and updates its internal state. Once it receives a Wishbone STOP command, it constructs an I2C transaction that it transports to the Scoreboard for checking.

Fig. 2: Byte-level FSM in the DUT

Phase 3

The next step in verifying our design was coming up with a test plan. My testplan included:

Phase 4

I implemented the stimulus for various parts of the test plan in classes inherited from the base Generator class. A derived object of the Generator class is created at runtime using an object factory.

The status bits (DON, NAK, AL, ERR) are internal to the design and aren’t visible when the DUT is viewed as a black box. To verify the correct operation of such internal signals, I created a probe interface that I bound to a block inside the design. I verified some features using SystemVerilog Assertions (SVA) in the probe interface.

I used a SV construct randsequence to generate Wishbone transactions in a random but meaningful order to try and hit as many FSM states as I could.