I2C Master (VHDL)

Logic Home

Code Download

Version 2.2: i2c_master.vhd (14.1 KB)
Corrected small SDA glitch at the end of the transaction (introduced in version 2.1)

Version 2.1: i2c_master_v2_1.vhd (14.0 KB)
Replaced gated clock with clock enable
Adjusted timing of SCL during start and stop conditions

Version 2.0: i2c_master_v2_0.vhd (13.5 KB)
Added ability to interface with different slaves in the same transaction
Corrected ack_error bug where ack_error went ‘Z’ instead of ‘1’ on error
Corrected timing of when ack_error signal clears

Version 1.0: i2c_master_v1_0.vhd (12.9 KB)
Initial Public Release

Features

  • VHDL source code of a Inter-Integrated Circuit (I2C or IIC) master component
  • Meets the NXP UM10204 I2C-bus specification for single master buses
  • User definable system clock
  • User definable I2C serial clock frequency
  • Generates Start, Stop, Repeated Start, and Acknowledge conditions
  • Uses 7-bit slave addressing
  • Compatible with clock-stretching by slaves
  • Not recommended for multi-master buses (no arbitration or synchronization)
  • Notifies user logic of slave acknowledge errors

Introduction

This details an I2C master component for single master buses, written in VHDL for use in CPLDs and FPGAs. The component reads from and writes to user logic over a parallel interface. It was designed using Quartus II, version 11.1. Resource requirements depend on the implementation. Figure 1 illustrates a typical example of the I2C master integrated into a system. A design incorporating this I2C master to create an SPI to I2C Bridge is available here.

i2c_master_block_diagram

Figure 1. Example Implementation

Background

The I2C-bus is a 2-wire, half-duplex data link invented and specified by Philips (now NXP). The two lines of the I2C-bus, SDA and SCL, are bi-directional and open-drain, pulled up by resistors. SCL is a Serial Clock line, and SDA is a Serial Data line. Devices on the bus pull a line to ground to send a logical zero and release a line (leave it floating) to send a logical one.

For more information, see the I2C specification attached below in the Additional Information section. It explains the protocol in detail, the electrical specifications, how to size the pull-up resistors, etc.

Theory of Operation

The I2C master uses the state machine depicted in Figure 2 to implement the I2C-bus protocol. Upon start-up, the component immediately enters the ready state. It waits in this state until the ena signal latches in a command. The start state generates the start condition on the I2C bus, and the command state communicates the address and rw command to the bus. The slv_ack1 state then captures and verifies the slave’s acknowledge. Depending on the rw command, the component then proceeds to either write data to the slave (wr state) or receive data from the slave (rd state). Once complete, the master captures and verifies the slave’s response (slv_ack2 state) if writing or issues its own response (mstr_ack state) if reading. If the ena signal latches in another command, the master immediately continues with another write (wr state) or read (rd state) if the command is the same as the previous command. If different than the previous command (i.e. a read following a write or a write following a read or a new slave address), then the master issues a repeated start (start state) as per the I2C specification. Once the master completes a read or write and the ena signal does not latch in a new command, the master generates the stop condition (stop state) and returns to the ready state.

Figure 2. I2C Master State Machine

The timing for the state machine is derived from the GENERIC parameters input_clk and bus_clk, described in the “Setting the Serial Clock Speed” section below. A counter generates the data clock that runs the state machine, as well as scl itself.

Port Descriptions

Table 1 describes the I2C master’s ports.

Table 1. Port Descriptions

Setting the Serial Clock Speed

The component derives the serial clock scl from two GENERIC parameters declared in the ENTITY, input_clk and bus_clk. The input_clk parameter must be set to the input system clock clk frequency in Hz. The default setting in the example code is 50 MHz (the frequency at which the component was simulated and tested). The bus_clk parameter must be set to the desired frequency of the serial clock scl. The default setting in the example code is 400 kHz, corresponding to the Fast-mode bit rate in the I2C specification.

Transactions

A low logic level on the busy output port indicates that the component is ready to accept a command. To initiate a transaction, the user logic places the desired slave address, rw command, and write data on the addr, rw, and data_wr ports, respectively, and asserts the ena signal. The command is not clocked in on the following system clock clk. Rather, the component is already generating the I2C timing internally, and clocks in the command based on that timing. Therefore, the user logic should wait for the busy signal to assert to identify when these inputs are latched into the I2C master.

The I2C master then executes the command. Once complete, it deasserts the busy signal to indicate that any data read is available on the data_rd port and any errors are flagged on the ack_error port.

Multiple reads and writes (and any combination thereof) may be executed during a transaction. If the ena signal is asserted at the completion of the current command, the I2C master latches in new values of addr, rw, and data_wr and immediately executes the new command. The busy signal still deasserts for one scl cycle to indicate that any read data is available on data_rd, and it reasserts to indicate that the new command is latched in and being executed.

Example Transaction

Figure 3 shows the timing diagram for a typical transaction. The user logic presents the address “1010101”, the rw command ‘0’ indicating a write, and the write data “10011001”. It asserts the ena signal to latch in these values. Once the busy signal asserts, the user logic issues a new command. The address remains “1010101”, but the following command is a read (rw = ‘1’). ena remains asserted. When the I2C master finishes the first command, it deasserts the busy signal to indicate its completion. Since ena is asserted, the I2C master latches in the new command and reasserts the busy signal. Once the busy signal re-asserts, the user logic recognizes that the second command is being executed and deasserts the ena signal to end the transaction following this command. The I2C master finishes executing the read command, outputs the data read (“11001100”) onto the data_rd port and deasserts the busy signal again.

Figure 3. Typical Transaction Timing Diagram

To continue a transaction with a new command, the ena signal and new inputs must be present no later than the last data bit of the current command. Therefore, it is recommended to issue the new command as soon as the busy signal indicates that the current command is latched in. The ena signal can simply be left asserted until the last command of the transaction has been latched in.

Example User Logic to Control the I2C Master

A snippet of user logic code below demonstrates a simple technique for managing transactions with multiple reads and writes. This example implementation counts the busy signal transitions in order to issue commands to the I2C master at the proper time. The state “get_data” does everything necessary to send a write, a read, a second write, and a second read over the I2C bus in a single transaction, such as might be done to retrieve data from multiple registers in a slave device.


example_code.txt (2.3 KB)

Acknowledge Errors

After each transmitted byte, the receiving end of the I2C bus must issue an acknowledge or no-acknowledge signal. If the I2C master receives an incorrect response from the slave, it notifies the user logic by flagging the error on the ack_error port. The I2C master does not automatically attempt to resend the information, so the user logic must decide whether or not to reissue the command and/or take any additional action. The ack_error port is cleared once the next transaction begins.

Clock Stretching

Section 3.1.9 of the I2C specification defines an optional feature where a slave can hold scl low to essentially pause the transaction. Some slaves are designed to do this if, for instance, they need more time to store received data before continuing. This I2C master component is compatible with slaves that implement this feature. It requires no action by the user logic controlling the I2C master.

Reset

The reset_n input port must have a logic high for the I2C master component to operate. A low logic level on this port asynchronously resets the component. During reset, the component holds the busy port high to indicate that the I2C master is unavailable. The scl and sda ports assume a high impedance state, and the data_rd and ack_error output ports clear. Once released from reset, the busy port deasserts when the I2C master is ready to communicate again.

Conclusion

This I2C master is a programmable logic component that accommodates communication with I2C slaves via a straightforward parallel interface. It adheres to the NXP I2C specification in regard to single master buses and also incorporates the optional feature of clock stretching.

Additional Information

UM10204, I2C-bus specification and user manual, NXP Semiconductors N.V. (1.3 MB)

Related Topics

SPI to I2C Bridge (VHDL) - This design uses the I2C Master described above to implement an SPI to I2C Bridge.

Temperature Sensor TCN75A Pmod Controller (VHDL) - This design uses the I2C Master described above to configure and gather data from a Microchip TCN75A temperature sensor.

Temperature Sensor ADT7420 Pmod Controller (VHDL) - This design uses the I2C Master described above to configure and gather data from an Analog Devices ADT7420 temperature sensor.

Capacitive Sensing AD7156 Pmod Controller (VHDL) - This design uses the I2C Master described above to configure and gather data from an Analog Devices AD7156 capacitance-to-digital converter.

Color Sensor Pmod Controller (VHDL) - This design uses the I2C Master described above to configure and gather data from an AMS TCS3472 Color Light-to-Digital Converter.

Compass Pmod Controller (VHDL) - This design uses the I2C Master described above to configure and gather data from a Memsic MMC34160PJ magnetometer.

ADC AD7991 Pmod Controller (VHDL) - This design uses the I2C Master above to gather data from an Analog Devices AD7991 4-channel, 12-bit analog-to-digital converter.

Humidity and Temperature Sensor Pmod Controller (VHDL) - This design uses the I2C Master above to configure and retrieve temperature and humidity data from a Texas Instruments HDC1080 hygrometer.

Real-Time Clock MCP79410 Pmod Controller (VHDL) - This design uses the I2C Master above to control a Microchip MCP79410 real-time clock/calendar.

Hello, the code works really well, but when I want to implement it in an FPGA, I have to make a Synchronizer mechanism (which is the number of D-FF gates) to overcome the Metastability phenomenon.
Is it necessary to include it here as well? Where in the code can it be integrated?

Hello,
Welcome to the Digikey tech forum. I’m not familiar with this but perhaps someone who has worked with this can jump in with information.

Hello ozanaidan,

Yes, use of a synchronizer is generally a good idea when crossing clock boundaries. An example is the SPI clock signal which should be synchronized upon entering the FPGA.

Please accept this Verilog implementation. Most FPGA synthesis tools will accept Verilog or VHDL.

Best Wishes,

APDahlen

First, thank you very much for the quick and detailed response. In SPI it is easier to see where to implement the synchronizer but in I2C where the lines are inout it is a little more difficult. I think that wherever I do a reading there I will integrate it (both in the sda ​​line and in the scl). Do you agree? If there is an example code that implements a synchronizer for the I2C block - I would love to see it. Anyway, thanks a lot for the response.

Hello ozanaidan,

The SDA synchronizer is probably not necessary as the I2C has an implicit setup time before sending the clock. On the other hand, the amount of FPGA fabric consumed by a synchronizer is decimal dust.

For clarification:

  • FPGA as I2C master - no synchronizer required as SDA setup times are implicit with the clock to SDA time relationship. This also assumes we are not pushing the speed envelope.

  • FPGA as I2C slave - synchronizer the clock signal. Again, assume we are not seeking excessive speed. Also note the RC timing consideration of the pull-up resistors working against board and circuit capacitance. Stated another way, it’s not so much about “synchronization” as it is about hysteresis and false triggering as we slowly transition across the logic 0 to logic 1 boundary.

Yes, I agree, SPI is easier. The unidirectional I/O allows us to add synchronizers at the top level outside the SPI module itself.

Sorry, at this point, I do not have an I2C module worth presenting. Unfortunately, my VHDL skills are lacking so I can offer little to no assistance with the code on this page. It’s on my list of skills to develop…

Best Wishes,

APDahlen

Hello APDahlen, I have splitted and buffered the sda line with two level translators in my design. sda_in for reading and sda_out for writing the data. Can you provide me a logic to control the direction of the buffers for reading a writing ?

Hello @unnikrishgr8,

As I understand your question, you are asking about adding a synchronizer at the edge of the FPGA where the SCL and SDC lines are defined.

Block diagram of the I2C master.

No action is required for master out as the FPGA is in control of the I/O and we are not crossing clock domains within the FPGA itself. You may or may not want to use a synchronizer for data entering the FPGA on the SDA line.

If you chose to do so, the synchronizer would be coordinated by the states within the I2C master. Specifically, when the I2C master is transmitting the synchronizer is off. When the I2C master is receiving, the synchronizer is turned on.

This implies that the synchronizer must be deeply embedded within the I2C structure. You will need to reverse engineer the I2C master and determine how to add the synchronizer.

Best wishes,

APDahlen

Thanks for the reply. I tried to define the direction of sda_in and sda_out based on the states defined inside the i2c_master,vhd. Following is the process i added to your i2c_master.vhd

process(state)
begin
if( state = ready or state = start or state = command or state = wr or state = mstr_ack or state = stop) then –
SDA_DIR_CTRL <= ‘1’;
elsif(state = slv_ack1 or state = slv_ack2 or state = rd ) then
SDA_DIR_CTRL <= ‘0’;

    end if;
end process;

Following is part of my top module, where I wanted to define the direction of data flow. ‘I2C_SDA_LOCAL’ is a signal in top module which is port mapped to i2c_master.vhd’s sda port.
process(SDA_DIR_CTRL)
begin

if(SDA_DIR_CTRL = '1') then
    I2C_SDA_1      <= I2C_SDA_LOCAL;
elsif(SDA_DIR_CTRL = '0') then
	I2C_SDA_LOCAL  <= I2C_SDA_2;
end if;

end process;

But when I implement the design, I am getting multiple driver issue in sda line.

I2C_SDA_1 is output and I2C_SDA_2 is input

Hello @unnikrishgr8,

The multiple drivers warning is usually a sign that a multiplexer is required to feed two signals to a common I/O pin.

Unfortunately, I will not be able to assist you. At best I’m a mediocre FPGA programmer in Verilog. I have not developed VHDL skills.

Sincerely,

APDahlen

P.S. Is this a mapping question? For example, declaration of a pin as Input/Output as opposed to Input or Output.

1 Like

I just want the combinatorial logic using internal signals which is ‘1’ when sda is output and is ‘0’ when sda is input