How to control I2C?

This week I’ve fully completed I2C master and slave and tested them by simulation. Having been over that I’ve moved to learning C++ object oriented programming to write the code that creates the system top level code. There isn’t much to say about C++ so I’ll use this blog post as a guide for whoever is going to create drivers for my I2C device in the future. Along with the comments found in my HDL code, I hope this guide proves itself helpful in understanding how to best control my I2C device.


The status register is holds 6 meaningful bits. When reading this register please notice that the 2 MSBs are meaningless. Out of the 6 bits, the MSB indicates error in the previous transaction. The second MSB indicates that the Master can receive data from the user, i.e. CAN_WRITE. The remaining four bits are used to show the status of the device. Their only use is to determine the kind of error whether it’s address error(No slave acknowledged the address) or data error(slave didn’t acknowledge the data). Other than that they are left there in case the programmer needs to gain more information about the current status of the core.

The driver should always check that the CAN_WRITE bit is high before writing data to the core. Once data has been written to the core this bit goes low again. To achieve back to back transfers, the driver need to send the data once it detects the CAN_WRITE bit high. Moreover, the driver must ensure that the ERROR bit is still low indicating the success of the  previous transaction. If the driver waits for too long after the CAN_WRITE is high, the core will give a stop condition after the current transfer concludes and hence lose arbitration.

After each transfer the driver should check the ERROR register to ensure it’s success. The driver should follow the following scheme to be able to detect the kind of error it received. Once a transfer starts and the CAN_WRITE goes high again the driver should check is the LSB of the status register which is high when the core is idle. If the CAN_WRITE is high while the IDLE register is still low then it means the core has written the address, the slave acknowledged it and it’s now in the middle of a data transfer but it can receive new data to be sent once the current one concludes. Any error that occurs after this state should be perceived as a data error.

However if an error has been received without the CAN_WRITE ever going high during the transaction then it can be considered an address error.

As it can be noticed, running the I2C core will require a constant polling for it’s CAN_WRITE bit. This problem can be solved by using an interrupt triggered when this bit has a rising edge.


The Slave status register has 4 meaningful bits which are it’s LSBs. The 4 MSBs are meaningless. The 2 LSBs of the status register indicate the current status of the device, idle , reading address or transferring data. These bits are only there to give information about the device. The driver doesn’t necessarily have to monitor them for basic functionality of the device. Bit 3 is DATA_REQUEST which goes high when the master requests data from the slave and will only go low when the user passes data to the slave. Bit 4 is NEW_DATA which goes high once the slave has completely received one byte from the master that is residing in the buffer. The user need to read the data from the slave buffer to clear this bit.

Here is how the slave core should be controlled. Under any circumstances the slave transactions are only triggered by the I2C master; the user can’t trigger a transaction. The user should always monitor the DATA_REQUEST bit and supply data once it goes high. As long as the DATA_REQUEST nit is high the slave will be introducing wait states on the SCL line until the user supplies it with valid data. Once the DATA_REQUEST goes down, the user can write new data to the slave which will be used after the current transfer is over if the master seeks more data. This way only two back to back transactions can take place. If the master request for a third byte the user can notice it by monitoring the DATA_REQUEST signal. If the user knows that the master will request a read it can load the data into the slave while it’s idle or reading address. This will prevent slave from introducing wait states.

As for the slave receiving data from the master, the driver should always monitor the NEW_DATA signal and once it goes high it should read the data in the buffer. If the driver takes too long to read the buffer the slave will still shift in the next data byte but it won’t load the data into the buffer and it’ll give a no acknowledge signal to the master.

To sum up, the I2C still needs to be properly tested on the FPGA. Until then, this guide should make the life of whoever is driving the I2C core easier.




You may also like...

Leave a Reply