I2C Verilog Code and working
I had already made a post regarding I2C long ago, however, in this post I am reposting I2C but with various changes. Some changes involve the using of Acknowledgement Bit by the Slave and Master, Same SDA line for slave address, register address as well as data. No extra data line is required to read the data from the slave. Everything can be seen on the SDA line along. This version of I2C in Verilog has the full support of adding *multiple* slaves.
Yes!! You can use multiple slaves at the same time. The only feature lacking that I am working on right now is the RW bit. The RW or better say Read/Write bit is present here but I have focussed only on the read operation here. I am working on the write operation too and will update soon for the latter.
For this I2C I had to grasp myself with the knowledge of the inout signal line in Xilinx. The SDA has to be an inout line or else it won't be a proper I2C model, despite serving the same functionality.
The Master will send 7-bit address along with RW bit on the SDA line and the corresponding slave will respond back with an ACK bit. After that Master will send the register address which will be acknowledged
by the slave with an ACK bit. Then the slave will send the 8-bit data from the received register to the Master. After receiving the data, Master will respond back with ACK bit and after a CC the I2C operation will end with the STOP bit.
The SCL line changes only when SDA is stable. The testbench I have used here only acts as a supervisor which provides a clock signal to Master. The Master is connected to the slave only with SDA and SCL line.
*DISCLAIMER: This Verilog Code only supports "Read" Operation. I'll continue with the "Write" operation later.
I have worked on this code using a different approach. If the Slave address that Master sends doesn't match with the Slave then it will keep on sending the same address. However, this approach is only applicable to 1 Slave. Consider the case where there are two slaves S1 and S2. If S1 address is matched then data exchange takes place between Master and S1. However, S2 will then inflict as the address won't match here. This will lead to an error on the SDA lines inform of X.
Thus to overcome this difficulty I have re-changed the code to NOT to send address, again and again, i.e if the address doesn't match then Master won't resend the address. Although a Master should keep on sending the address in real, my code faces a problem which I'll deal later.
As I have mentioned that I have used inout command in this code. Inout command should only be used using a tri-state buffer.
A tristate buffer is coded somewhat like this:
x <= direction?data:1'bZ;
A tristate buffer has an enable pin. When enabled (here direction == TRUE) then data will be transmitted. On the other case, when disabled (here direction == FALSE) then a high impedance is
sent thereby disconnecting the output from the input circuit. Consider A and B. When A is true then it is at higher potential. When B is false it is at a lower potential. As current flows from High to Low, this signal will blow from A(TRUE - 1) to B (FALSE Z). Similarly, if B is true and A is false then signal will flow from B to A.
While I was coding, I faced tremendous problems with switching between TRUE and FALSE in both Master and Slave. If both A and B are set to TRUE then you will get ZZZZZZZ (in blue color) as output. If both A and B are low you will get XXXXXX (in red color) as output.
The signal line of type inout can only be a wire. It cannot be registered as reg type so to use it we have to use the "assign" keyword.
RW- 0 ACK = 1 Slave address matched
RW- 0 ACK = 0 Slave address not matched
Get Single Master Single Slave Code from here: Github I2C_Code
Master & Slave
Test BenchNOTE - To restart the I2C transmission all you have to do is give a fork join condition
Place this piece of code in the initial begin of the Master Code. Remember that you have to give sufficient #time condition to avoid conflict. With this you can you can pull SDA line low to restart I2C for a new data. However, data will too remain the same. Thus you will have to change the data by using #time syntax like this in Master
#160 alpha <= 0;
#160 direction <= 1;
#162 left_bits <= 1;
#160 register = 7'b00011001; // 00011001 = 25 in decimal
#160 reg_temp = register;
In Slave you will have to enter new data in the array at location 25.
Sim View of the code above in Xilinx
For Multi Slave I2C significant changes are required. Consider a case where we have two slaves named A and B. The master will send the address of A. In that case slave A will send acknowledgment bit (1 in this case) on the sda line. However, slave B will also send acknowledgment bit (0 in this case). This creates a problem as Verilog doesn't allow multiple drivers for a single wire. Even if I simulated I got ZZ as the acknowledgment because of that conflict.
It can be represented as follows
Thus to avoid this condition I used the Verilog keyword "wor". wor is logical OR of wires joint together.
In case if Slave A NACK = 0 and Slave B NACK = 1 then wor would output as 1(B) + 0(A) = 1
0(A) + 0(B) = 0
0(A) + 1(B) = 0
1(A) + 1(B) = NOT POSSIBLE FOR I2C WITH SAME ADDRESS.
The following piece of code has a single master and 3 slaves. One thing I came to notice that all slaves code can remain same except the module name or else any change made to any slave would result in a change is every slave.
Multi Slave Code (Works Pretty well. Comment or message for any error)
To change slave address one can change it to desired address by changing address at line 22 of Master
To fully understand my code click HERE