This repository contains a synthesizable I2C Master module written in SystemVerilog. The module serves as a communication bridge between a host system (such as an FPGA or microcontroller) and external I2C peripherals.
The architecture generates the SCL clock continuously and uses synchronous edge-detectors (scl_fall and scl_rise) internally. This allows the state machine to shift and sample data in alignment with the physical I2C clock edges.
For details about the I2C protocol, please refer to the official specification: NXP_I2C_Manual.pdf (https://www.nxp.com/docs/en/user-guide/UM10204.pdf).
- Configurable Clock Rates: The main system clock frequency and the target I2C bus frequency are parameterizable.
- Direct Interface: The module accepts standard I2C Read/Write bits (
0for Write,1for Read). - Error Handling: Asserts an
ack_errorflag if a target peripheral fails to acknowledge an address or data byte. - Zero-Latency Data Transfer: Solves 1-clock-cycle synchronous latency by pre-loading the SDA shift register prior to data phases.
clk: Main system clock (Default: 50 MHz).reset: Active-high synchronous reset.
start: Active-high pulse to initiate an I2C transaction.rw: Determines transaction type (0= Write,1= Read).slave_addr[6:0]: 7-bit address of the target I2C peripheral.data_in[7:0]: Data to be transmitted to the peripheral (ignored during Read operations).data_out[7:0]: Data received from the peripheral (valid whendoneis asserted).
busy: High while a transaction is actively occurring.done: Pulses high when a transaction terminates.ack_error: High if a Not Acknowledge (NACK) is detected.
sda(inout): Bidirectional serial data line.scl(output): Serial clock line.
i2c_master #(
.CLK_FREQ(50_000_000), // Main system clock frequency
.I2C_FREQ(100_000) // Target I2C bus frequency
) i2c_master_inst (
.clk(clk),
.reset(reset),
.start(start),
.rw(rw),
.slave_addr(slave_addr),
.data_in(data_in),
.data_out(data_out),
.busy(busy),
.done(done),
.ack_error(ack_error),
.sda(sda),
.scl(scl)
);To transmit 0xC3 to address 0x5A:
- Assign
slave_addr = 7'h5A. - Assign
data_in = 8'hC3. - Assign
rw = 0. - Pulse
starthigh for oneclkcycle. - Wait for
doneto assert. Ifack_erroris0, the transmission was successful.
To read one byte from address 0x5A:
- Assign
slave_addr = 7'h5A. - Assign
rw = 1. - Pulse
starthigh for oneclkcycle. - Wait for
doneto assert. - If
ack_erroris0, the received data is available ondata_out.
The module generates the scl clock by dividing the main clk and uses 1-cycle delayed signals (scl_prev) to create scl_fall and scl_rise trigger pulses. The state machine uses these triggers to ensure all SDA transitions happen on the falling edge of SCL, and sampling happens on the rising edge.
State Progression:
- IDLE: Waiting for the
startassertion. - START: Asserts the Start Condition.
- ADDRESS: Shifts out the 7-bit
slave_addrfollowed by therwbit. - ACK1: Samples the peripheral's acknowledge bit. For a Write, it pre-loads the first data bit onto SDA on the exiting
scl_fall. - DATA: Sequentially transmits or receives 8 bits of data.
- ACK2: Samples the peripheral's data ACK (Write) or generates a Master NACK (Read) to terminate.
- STOP_LOW / STOP_HIGH: Stages the SDA line low, then releases it high while SCL is high to assert a Stop Condition.
- DONE: Asserts the
doneflag and prepares to return toIDLE.
The project includes simulation scripts for quick testing. Running these scripts will:
- Compile the design and testbench (
design.sv,tb.sv) using Icarus Verilog (iverilog). - Execute the simulation and generate a waveform file (
vvp). - Automatically open the resulting
.vcdwaveform in GTKWave for visual inspection.
Requirements: Ensure Icarus Verilog and GTKWave are installed and added to your system's PATH.
Open a terminal in the project directory and run the batch file:
.\run.batOpen a terminal in the project directory, make the script executable, and run it:
chmod +x run.sh
./run.sh