UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Explorer
Explorer
1,173 Views
Registered: ‎10-05-2010

Zynq I2C Slave Readback

Jump to solution

My Zynq I2C slave interface is connected to a master that performs a readback by using a repeated start. I am using interrupts, and can successfully accept data written by the I2C master. However, it looks like when the master issues the repeated start, the interrupt driver continuously issues an  XIICPS_EVENT_ERROR event. What is the correct way to perform a slave read?

 

I have a ZC706 board and Vivado 2014.4.

 

---

Joe Samson

Tags (3)
0 Kudos
1 Solution

Accepted Solutions
Explorer
Explorer
884 Views
Registered: ‎10-05-2010

Re: Zynq I2C Slave Readback

Jump to solution

I was finally able to get my repeated start slave readback working. I have several hints for anyone following later.

1. As shown in Xilinx's example code, buffers need to be assigned for reading and writing. The only way I've had reliable transfers is when the buffer is sized exactly to the amount of data being sent or received. On my project, we decided that the master will transfer 6 bytes to the slave (2 bytes for command and a 4-byte data word). If the receive buffer is bigger than the transfer size, it leads to the XIICPS_EVENT ERROR.

2. XIicPs_SlaveRecv prepares the slave to receive data from the master; XIicPs_SlaveSend prepares the slave to send data to the master. Executing one 'cancels' the other (Can't think of another way to describe it). My problem was that I was executing the SlaveSend, but I wasn't waiting until I got an interrupt that the send was complete.

 

So here's the sequence of events that works for me:

1. Initialize the I2C and interrupt controller

 

2. XIicPs_SlaveRecv

 

3. When the I2C interrupt handler detects the XIICPS_EVENT_COMPLETE_RECV event, the buffer specified in the SlaveRecv call has the received data. In my case, the received data is a command that is decoded with a Switch statement. If the command is not a readback, then it is executed, followed by another  XIicPs_SlaveRecv to await the next write from the master.

However, if the command is for a readback, the appropriate data is fetched and placed in the send buffer and the XiicPs_SlaveSend is executed. In my code, there is a while loop that waits until the I2C interrupt handler detects the  XIICPS_EVENT_COMPLETE_SEND. Only then is it safe to execute another XIicPs_SlaveRecv to await the next write from the master.

 

When the I2C IP detects a slave read, it starts the timeout counter; when the counter expires, the I2C interrupt handler detects the XIICPS_EVENT_ERROR event. If bit 3 of the interrupt status register is set, then the error is a timeout. This prevents the control program from hanging if the readback is not set up correctly. 

 

The only way that I've found to recover from a timeout is to do a hard reset of the I2C IP by unlocking the Zynq SCLR registers ( Xil_Out32(0xF8000008,0xDF0D)) and asserting I2C_RST_CTRL ( Xil_Out32(0xF8000224,1)), then deasserting ( Xil_Out32(0xF8000224,0)).

 

That's how I got the Zynq I2C slave readback with repeated start to work.

 

---

Joe Samson

 

 

 

0 Kudos
8 Replies
Xilinx Employee
Xilinx Employee
1,116 Views
Registered: ‎06-27-2017

Re: Zynq I2C Slave Readback

Jump to solution

Hi @josephsamson,

 

If you are trying at bare metal, see below example as  referecne.

 

https://github.com/Xilinx/embeddedsw/tree/xilinx-v2014.4/XilinxProcessorIPLib/drivers/iicps/examples

 

Regards
Kranthi
--------------------------
Don't forget to reply, kudo, and accept as solution.

Best Regards
Kranthi
--------------------------
Don't forget to reply, kudo, and accept as solution.
0 Kudos
Explorer
Explorer
1,102 Views
Registered: ‎10-05-2010

Re: Zynq I2C Slave Readback

Jump to solution

Thank you for the link, but this just leads to the examples from the drivers section of SDK; I should have mentioned that I used these examples to write my initial code. However, I can believe that I might have missed an example that shows a slave readback when the host issues a repeated start. If so, please point out where that is because I still don't see it.

 

Using the slave interrupt example code, I am able to reliably receive data from the host. The problem comes when the host attempts to read the slave. In this case, the host issues a start followed by the device address with the r/w bit indicating write. After this are several bytes that tell the slave what data the host expects to receive, with the slave issuing ACKs after each byte. If this were just a host write, the host would issue a stop condition, and the interrupt handler would issue the XIICPS_EVENT_COMPLETE_RECV event. However, with a host read, the host instead issues another start (called the repeated start) followed by the device address with the r/w bit indicating read. I can see all this with chipscope, so I know that my host is following the protocol. If everything worked right, the slave would drive the requested data onto the SDA line.

 

In my case, the only interrupt that I see when the host tries to read from the slave is from the  XIICPS_EVENT_ERROR event. This event is issued continuously (the only way I've found to recover is to cycle power!). I suspect that the I2C IP cannot handle the repeated start.

 

Fortunately the I2C host is a Cypress FX3; on Monday I will ask the FX3 programmer if he can split the read into two different cycles to avoid the repeated start.

 

---

Joe Samson

0 Kudos
Explorer
Explorer
1,060 Views
Registered: ‎10-05-2010

Re: Zynq I2C Slave Readback

Jump to solution

My I2C host now requests a slave read without the repeated start. It issues the start condition followed by the device address with the r/w bit set to read (high). The slave responds with an ACK, then drives SCL and SDA low. There is a constant interrupt with the XIICPS_EVENT_ERROR event. When I see the XIICPS_EVENT_ERROR, I execute an XIicPs_SlaveSend.

 

Looking at the I2C signals with chipscope, I see the signals as described above, but the XIicPs_SlaveSend isn't putting data on the I2C bus.

 

In xiicps_intr_slave_example.c, after the I2c interface and interrupt controller are set up, the slave interface sends a buffer of data to the master. I decided to try this, but chipscope shows that no data is sent, and there are no interrupts generated.

 

So my question remains: how do I do a Zynq I2C slave readback?

 

Here is my I2C initialization code:

/******************* Initialize the Zynq I2C Controller ************************/
#define I2C_DEVICE_ID XPAR_PS7_I2C_0_DEVICE_ID
#define I2C_INTERRUPT_VECT XPAR_FABRIC_PS7_I2C_0_INTERRUPT_INTR
	
XIicPs_Config *I2C_Config;

// Initialize the IIC driver so that it is ready to use.
I2C_Config = XIicPs_LookupConfig(I2C_DEVICE_ID);
  if (NULL == I2C_Config) {xil_printf("I2C Initialization Failed\r\n");}

Status = XIicPs_CfgInitialize(&Iic, I2C_Config, I2C_Config->BaseAddress);
    if (Status != XST_SUCCESS) {xil_printf("I2C Config Initialization 
             Failed\r\n");}

 // Perform a self test
Status = XIicPs_SelfTest(&Iic);
    if (Status != XST_SUCCESS) {xil_printf("I2C Self Test Failed\r\n");}
  
// There is a single interrupt handler
XIicPs_SetStatusHandler(&Iic, (void *) &Iic, I2CIRQHandler);

 // Set up the slave address
XIicPs_SetupSlave(&Iic, IIC_SLAVE_ADDR);
 
// Set the IIC serial clock rate.
XIicPs_SetSClk(&Iic, IIC_SCLK_RATE);
  
i2cOptions = XIICPS_7_BIT_ADDR_OPTION;

XIicPs_SetOptions(&Iic, i2cOptions);
  
// Enable interrupts
XIicPs_EnableInterrupts(XPAR_XIICPS_0_BASEADDR,0x2ff);

---

Joe

 

 

0 Kudos
Xilinx Employee
Xilinx Employee
1,021 Views
Registered: ‎06-27-2017

Re: Zynq I2C Slave Readback

Jump to solution

@josephsamson,

 

Similar issue is observed when address was written before HOLD bit is released when running low speeds <100kHz.

Repeated start is interpreted as Stop and then start.

Below is the Linux patch which fixed issue.

https://github.com/Xilinx/linux-xlnx/commit/9e90cc17df450120bbb110e83e8a66bba9e36855#diff-e222fad360c0565320b083d69112b596

So in your standalone driver, Can you check whether  hold bit is still set?

Repeated start is working fine in Linux kernel.

 

Regards
Kranthi
--------------------------
Don't forget to reply, kudo, and accept as solution.

Best Regards
Kranthi
--------------------------
Don't forget to reply, kudo, and accept as solution.
0 Kudos
Explorer
Explorer
951 Views
Registered: ‎10-05-2010

Re: Zynq I2C Slave Readback

Jump to solution

Thanks for the reply, but this looks like a patch for a master operation. What I need is example code that shows how a slave receives an I2C message and issues a reply.

 

to recap: I am successful having my I2C master write messages to the slave, and the slave interpreting those messages. I am trying to get the slave to respond to a slave read command that has a repeated start. In this type of readback, the master performs a write to the slave (identifying the address of the data that the master wants to read), followed by a repeated start, followed by the slave address with the read bit set. The slave then replies with the requested data.

 

I have been doing a lot of experimenting, and I have successfully performed a readback. This is my sequence:

1. The I2C master  writes a message to the slave identifying the address of the data that the master wants to read. This is just a slave write, so it ends with a stop sequence.

2. The slave receives the XIICPS_EVENT_COMPLETE_RECV event from the interrupt handler.

3. The slave reads the receive buffer, decodes the command (a register read), reads the register, places the 4-byte reply in the transmit buffer and executes the XIicPs_SlaveSend command.

4. The I2C master sends just the slave address with the read/write bit set to 'read'

5. The 4 byte data transfers to the host.

6. The slave receives the XIICPS_EVENT_COMPLETE_SEND event from the interrupt handler.

 

If I want to do it again, I have to power cycle the FPGA. If I go back to step 1 above, instead of getting the XIICPS_EVENT_COMPLETE_RECV , I get another XIICPS_EVENT_COMPLETE_SEND  event.

 

so:

1. Why do I keep receiving XIICPS_EVENT_COMPLETE_SEND events?

2. Is there some register or bit that I should clear after receiving the XIICPS_EVENT_COMPLETE_SEND event?

 

---

Joe

 

0 Kudos
Explorer
Explorer
938 Views
Registered: ‎10-05-2010

Re: Zynq I2C Slave Readback

Jump to solution

@josephsamson wrote:

1. Why do I keep receiving XIICPS_EVENT_COMPLETE_SEND events?

2. Is there some register or bit that I should clear after receiving the XIICPS_EVENT_COMPLETE_SEND event? 


The fix to this problem is that I needed to execute an XIicPs_SlaveRecv after the XIICPS_EVENT_COMPLETE_SEND event. Since I started doing this, i've been able to do slave writes and reads.

 

Here is a simplified interrupt handler:

 

void I2CIRQHandler(void *CallBackRef, u32 Event)
{
  u32 bytecount;
  u32 isrReg;
  int Status = 1L;
XIicPs_Config *I2C_Config; /* * Data transfer finishes. */ if (0 != (Event & XIICPS_EVENT_COMPLETE_RECV)){ // Event 2
i2cRecvComplete = 1; XIicPs_SlaveRecv(&Iic, i2cRecvBuffer, I2C_RECEIVE_COUNT); } else if (0 != (Event & XIICPS_EVENT_COMPLETE_SEND)) { // Event 1 while (XIicPs_BusIsBusy(&Iic)) {/* NOP */} XIicPs_SlaveRecv(&Iic, i2cRecvBuffer, I2C_RECEIVE_COUNT); } else if (0 != (Event & XIICPS_EVENT_ERROR)) { // Event 8 XIicPs_SlaveRecv(&Iic, i2cRecvBuffer, I2C_RECEIVE_COUNT); isrReg = XIicPs_ReadReg(XPAR_XIICPS_0_BASEADDR, XIICPS_ISR_OFFSET); if ((isrReg & 8) == 8) { // Timing Error // The I2C interface doesn't have any way to recover from a timeout error except to // reset and reinitialize xil_printf("\r\nI2C Timeout; resetting I2C"); Xil_Out32(0xF8000008,0xDF0D); // Unlock Zynq SLCR registers Xil_Out32(0xF8000224,1); // Assert I2C_RST_CTRL Xil_Out32(0xF8000224,0); // Deassert I2C_RST_CTRL // Initialize the IIC driver so that it is ready to use. I2C_Config = XIicPs_LookupConfig(I2C_DEVICE_ID); if (NULL == I2C_Config) {xil_printf("I2C Initialization Failed\r\n");}
Status = XIicPs_CfgInitialize(&Iic, I2C_Config, I2C_Config->BaseAddress); if (Status != XST_SUCCESS) {xil_printf("I2C Config Initialization Failed\r\n");} // Perform a self test Status = XIicPs_SelfTest(&Iic); if (Status != XST_SUCCESS) {xil_printf("I2C Self Test Failed\r\n");} // There is a single interrupt handler XIicPs_SetStatusHandler(&Iic, (void *) &Iic, I2CIRQHandler); XIicPs_WriteReg(XPAR_XIICPS_0_BASEADDR, XIICPS_CR_OFFSET, 0x00000c10c); XIicPs_WriteReg(XPAR_XIICPS_0_BASEADDR, XIICPS_TIME_OUT_OFFSET, 0x000000ff); // Set up the slave address XIicPs_SetupSlave(&Iic, IIC_SLAVE_ADDR); // Set the IIC serial clock rate. XIicPs_SetSClk(&Iic, IIC_SCLK_RATE); XIicPs_SetOptions(&Iic, XIICPS_7_BIT_ADDR_OPTION);
// Enable interrupts XIicPs_EnableInterrupts(XPAR_XIICPS_0_BASEADDR,0x2ff); // Establish the read buffer XIicPs_SlaveRecv(&Iic, i2cRecvBuffer, I2C_RECEIVE_COUNT); } } else { /* * Data was received with an error. */ TotalI2CErrorCount++; } }

Notice the extra code for event 8, the error event.  I am doing slave reads by first sending a slave write command that identifies the data to be read. My I2C command interpreter (a SELECT statement) decodes the register read command, fetches the data and executes the XIicPs_SlaveSend command. Then the I2C master sends a slave read request by issuing the slave address with the read/write bit set to read, and the XIicPs_SlaveSend transfers the data to the master.

 

So what happens if the master sends the slave read request (slave address with the read/write bit set to read) but there wasn't an XIicPs_SlaveSend done beforehand? The I2C slave drives SCL and SDA low and eventually a timeout interrupt occurs (event 8, with the status register bit 3 set). I have not found any way to recover from a timeout except to do a complete I2C reset and reinitialize (XIicPs_Abort and XIicPs_Reset do nothing).

 

However, I still want to do the slave read by using the repeated start technique.

 

---

Joe

0 Kudos
Explorer
Explorer
896 Views
Registered: ‎10-05-2010

Re: Zynq I2C Slave Readback

Jump to solution

Mods: Isn't there anyone at Xilinx who understands how to code a slave readback using a repeated start? How do I resolve this problem?

 

---

Joe

0 Kudos
Explorer
Explorer
885 Views
Registered: ‎10-05-2010

Re: Zynq I2C Slave Readback

Jump to solution

I was finally able to get my repeated start slave readback working. I have several hints for anyone following later.

1. As shown in Xilinx's example code, buffers need to be assigned for reading and writing. The only way I've had reliable transfers is when the buffer is sized exactly to the amount of data being sent or received. On my project, we decided that the master will transfer 6 bytes to the slave (2 bytes for command and a 4-byte data word). If the receive buffer is bigger than the transfer size, it leads to the XIICPS_EVENT ERROR.

2. XIicPs_SlaveRecv prepares the slave to receive data from the master; XIicPs_SlaveSend prepares the slave to send data to the master. Executing one 'cancels' the other (Can't think of another way to describe it). My problem was that I was executing the SlaveSend, but I wasn't waiting until I got an interrupt that the send was complete.

 

So here's the sequence of events that works for me:

1. Initialize the I2C and interrupt controller

 

2. XIicPs_SlaveRecv

 

3. When the I2C interrupt handler detects the XIICPS_EVENT_COMPLETE_RECV event, the buffer specified in the SlaveRecv call has the received data. In my case, the received data is a command that is decoded with a Switch statement. If the command is not a readback, then it is executed, followed by another  XIicPs_SlaveRecv to await the next write from the master.

However, if the command is for a readback, the appropriate data is fetched and placed in the send buffer and the XiicPs_SlaveSend is executed. In my code, there is a while loop that waits until the I2C interrupt handler detects the  XIICPS_EVENT_COMPLETE_SEND. Only then is it safe to execute another XIicPs_SlaveRecv to await the next write from the master.

 

When the I2C IP detects a slave read, it starts the timeout counter; when the counter expires, the I2C interrupt handler detects the XIICPS_EVENT_ERROR event. If bit 3 of the interrupt status register is set, then the error is a timeout. This prevents the control program from hanging if the readback is not set up correctly. 

 

The only way that I've found to recover from a timeout is to do a hard reset of the I2C IP by unlocking the Zynq SCLR registers ( Xil_Out32(0xF8000008,0xDF0D)) and asserting I2C_RST_CTRL ( Xil_Out32(0xF8000224,1)), then deasserting ( Xil_Out32(0xF8000224,0)).

 

That's how I got the Zynq I2C slave readback with repeated start to work.

 

---

Joe Samson

 

 

 

0 Kudos