0% found this document useful (0 votes)
10 views

Tiny i2c Master

The document describes a System Verilog I2C master module designed for FPGA projects, detailing its operation, parameters, and state machine structure. It supports both 8-bit and 16-bit modes and includes a clock divider mechanism to manage I2C transactions effectively. The document also outlines the read and write operations, including state transitions and acknowledgment checks during communication with slave devices.

Uploaded by

958780978
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Tiny i2c Master

The document describes a System Verilog I2C master module designed for FPGA projects, detailing its operation, parameters, and state machine structure. It supports both 8-bit and 16-bit modes and includes a clock divider mechanism to manage I2C transactions effectively. The document also outlines the read and write operations, including state transitions and acknowledgment checks during communication with slave devices.

Uploaded by

958780978
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

Home Blog Github

Tiny But Mighty I2C Master Verilog Module


This easy to use System Verilog I2C master module allows easy drop in operation into any FPGA project and allows for effective and
reliable I2C master operation. In this post we will be discussing the workings and details of this module. System Verilog source and
test bench can be found on my Github page (direct link here).
Top Level Overview

Figure 1. Top level port diagram

Name TypeWidth DirectionDescription


Main system
clock (note:
clock wire 1 input not the
actual I2C
SCL)
reset wire 1 input Active
reset
high
Name TypeWidth DirectionDescription
Active high
enable signal
enable wire 1 input that begins a
transaction if
master is not
busy
read_write =
0 will signify
a WRITE
read_write wire 1 input request.
read_write =
1 will signify
a READ
request.
Data to be
written to a
mosi_data wire DATA_WIDTH input register
during an
I2C write
transaction
Register
where data
is
register_address wire REGISTER_WIDTH input read/written
during an
I2C
transaction
device_address wire ADDRESS_WIDTH input Targetaddress
slave
Divider value
used to
divider wire 16 input calculate the
I2C clock
rate.
Data read
miso_data reg DATA_WIDTH output during
READ
a
operation.
When high
master is
busy reg 1 output busy with a
READ/WRITE
transaction.
SDA
external_serial_data wire 1 inout conenction
for I2C bus
SCL
external_serial_clockwire 1 inout connection
for I2C bus
Ports

Name Typical Value


DATA_WIDTH 8/16
REGISTER_WIDTH 8/16
ADDRESS_WIDTH 7/15
Parameters
This module is tested and designed to run in either 8-bit mode or 16-bit mode. In 8-bit mode the data and registers are
represented with 8bit numbers while in 16bit mode they are represented with 16bit numbers. The mode can be changed by
overriding the DATA_WIDTH and REGISTER_WIDTH parameters. Alternatively a combination of 8-bit and 16-bit data/register widths
can be used as well.

State Machine Overview


This module is designed as to operate as a finite state machine. Although there are a number of states we can simplify and
represent the state machine as a machine consisting of three states.

Figure 2. State machine overview


1. Idle - Here the master checks its inputs for a command and executes the read/write command based on its input.
2. Write Device Address - To begin executing the command the master first sends the I2C address of the slave device with the r/w
operation bit appended.
3. Write / Read Data - Once the slave acknowledges its address, the reading or writing is executed.
Clock Divider
This module is designed to utilize the same system clock as the rest of your design. By doing this we can minimize situations where
they could be potential inter clock path timing violations. However, in most cases such clocks are in the megahertz range and
running I2C transactions at those rates are unrealistic, therefore we need a mechanism to divide our clock down. To do so we will be
using a simple counter-based divider. The “divider_counter” is a 16 bit register that is continuously incremented. When it is equal to
the “divider” input value, our divider tick wire will pulse high, and the counter will return to zero. This divider tick pulse will signal
the main state machine to execute an operation. It is important to still drive the main state machine’s flip flops at the system clock
and have the divider tick be a gate keeper with an if statement. Although driving the flip flops using the divider register directly as a
clock will look fine in simulation, the implemented design will have erratic behavior because FPGAs typically use dedicated
optimized paths for clocks that are different from register paths. The I2C SCL frequency can be calculated using the equation below.

fCLK
fSCL =
4(divider + 1)

Figure 3. Divider tick pulsing shown in red


Figure 4. State machine only executes when divider tick is high
State Partitioning
As seen in the Figure 5, each time our divider tick pulses process counter is incremented (the state executes a process). This is
because each state is partitioned into 4 processes (process_counter = 0, 1, 2, 3). SCL is toggled during the middle two processes ( 1
and 2 ). SDA bit data is asserted during process_counter = 0. This allows for a quarter SCL period of setup time

Figure 5. Process counter incremented as divider_tick pulses


Clock Stretching
When process_counter = 1, the SCL line is released and placed in the high Z state. During this time the master checks for any clock
stretching being attempted by the slave. If no clock stretching is detected, the master will iterate through the remaining processes
and continue as normal. However if clock stretching is detected, the master will stay in the process_counter = 1 process until SCL
line is high once more.
Detailed State Machine Flow
Below we will go through each state the state machine cycles through for a Read and then a Write operation.
Read Operation
1. S_IDLE (8’h00)
Save the following inputs into registers: device_address (spi slave device address), register_address (spi slave target addr),
mosi_data (spi slave data to write), read_write (read/write status bit). SDA and SCL are parked high in this state.
If enable state is high, we move to the S_START state
2. S_START (8’h01)
Here we execute the I2C start sequence by setting SDA low, followed by SCL low.

Figure 6. Start condition being executed


3. S_WRITE_ADDR_W (8’h02)
The device address Is sent with a 0 appended to the LSB to signify a write operation. Although the end goal is to read a i2c address
we must first write the I2C address value we want to read from to the I2C slave. Most significant bit is transmitted first.
4. S_CHECK ACK (8’h03)
Checks for the ACK bit response from salve. A low bit from the slave will signify a ACK. If a NACK bit is sent instead (high bit) the
state machine will return to IDLE. The “last_acknowledge” register is set high if ACK is received and low if a NACK bit is received.
This can be used as an error flag.
Figure 7. ACK being received from slave

5. S_WRITE_REG_ADDR_MSB/S_WRITE_REG_ADDR (8’h0B / 8’h04)


Writes the address of the register to be read one bit at a time. Most significant bit is transmitted first. In 16 bit mode we write the
upper byte first, check for ACK and then write the lower byte. Most significant bit is transmitted first.
6. S_CHECK_ACK (8’h03)
Checks for the ACK bit response from salve. A low bit from the slave will signify a ACK. If a NACK bit is sent instead (high bit) the
state machine will return to IDLE. The “last_acknowledge” register is set high if ACK is received and low if a NACK bit is received.
This can be used as an error flag.
7. S_RESTART (8’h05)
Send Restart sequence. SDA High followed by SCL High.

Figure 8. Restart sequence being executed

8. S_WRITE_ADDR_R (8’h06)
The device address Is sent with a 1 appended to the LSB to signify a read operation.
9. S_CHECK_ACK (8’h03)
Checks for the ACK bit response from salve. A low bit from the slave will signify a ACK. If a NACK bit is sent instead (high bit) the
state machine will return to IDLE. The “last_acknowledge” register is set high if ACK is received and low if a NACK bit is received.
This can be used as an error flag.
10. S_READ_REG_MSB /S_READ_REG (8’h0D / 8’h07)
The register is finally read and stored into the "miso_data" output register. Data is shifted in most significant bit first. In 16 bit mode
the upper byte is sent first. Afterwards the master sends an ACK bit then sends the lower byte.
10a. S_SEND_ACK (8’h0E)
Sends an ACK bit (low bit). This is done when reading 16 bit data. The most significant byte is read, then this ACK bit is sent by the
master which tells the slave to continue with the least significant byte of data.
11. S_SEND_NACK (8’h08)
Sends a NACK bit (high). This is done when all reading is completed. Sending a ACK bit instead at point will allow for burst reading if
the slave supports it.
12. S_SEND_STOP (8’h09)
Sends the stop sequence to end the I2C transaction. The stop sequence is done by setting SCL high followed by SDA high.

Figure 9. Stop sequence being sent


Write Operation
1. S_IDLE (8’h00)
Save the following inputs into registers: device_address (spi slave device address), register_address (spi slave target addr),
mosi_data (spi slave data to write), read_write (read/write status bit). SDA and SCL are parked high in this state.
2. S_START (8’h01)
Here we execute the I2C start sequence by setting SDA low, followed by SCL low.

3. S_WRITE_ADDR_W (8’h02)
The device address Is sent with a 0 appended to the LSB to signify a write operation. Although the end goal is to read an address
we must first write the address value we want to read from to the slave.
4. S_CHECK ACK (8’h03)
Checks for the ACK bit response from salve. A low bit from the slave will signify a ACK. If a NACK bit is sent instead (high bit) the
state machine will return to IDLE. The “last_acknowledge” register is set high if ACK is received and low if a NACK bit is received.
This can be used as an error flag.

5. S_WRITE_REG_ADDR_MSB/S_WRITE_REG_ADDR (8’h0B / 8’h04)


Writes the address of the register to be read one bit at a time. Most significant bit is transmitted first. In 16-bit mode we write the
upper byte first, check for ACK and then write the lower byte. Most significant bit is transmitted first.
6. S_CHECK ACK (8'h03)
Checks for the ACK bit response from salve. A low bit from the slave will signify a ACK. If a NACK bit is sent instead (high bit) the
state machine will return to IDLE. The “last_acknowledge” register is set high if ACK is received and low if a NACK bit is received.
This can be used as an error flag.

7. S_WRITE_REG_DATA/S_WRITE_REG_DATA_MSB (8'h0A / 8'h0C)


Writes the data to the target register one bit at a time. Most significant bit is transmitted first. In 16-bit mode we write the upper
byte first, check for ACK and then write the lower byte. Most significant bit is transmitted first.
8. S_CHECK ACK (8'h03)
Checks for the ACK bit response from salve. A low bit from the slave will signify a ACK. If a NACK bit is sent instead (high bit) the
state machine will return to S_IDLE. The “ack_recieved” register is set high if ACK is received and low if a NACK bit is received. This
can be used as an error flag.
9. S_SEND_STOP (8'h09)
Sends the stop sequence to end the I2C transaction.

Recommended Usage
When using this master the "busy" output is a cirtical flag to monitor in order to easily keep track of what the master is doing. It is
recommend to first set the configuration inputs of the master. This includes "read_write", "register_address", "mosi", "device_address",
and "divider". Then query the "busy" signal, once it is low set "enable" high. Once "busy" output goes high, we set "enable" low.
Finally once "busy" is low again, we know the transaction has been completed. If a read operation was requested, the read word will
be available via the "miso_data" output.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy