This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Spurrious ICXRDY interrupts

Hi,

I'm writing my own low level interrupt driven interface for the i2c peripheral in the the C6748 and am getting spurrious ICXRDY interrupts on the first character I transmit.  I've read the I2C Tips Wiki page and am setting up the device consistent with the recommendations.  After clearing the interrupt status and vector registers, I set the mask register to allow NACK, ARDY and ICXRDY interrupts through.  I set the count, address and mode registers.  I then go off and loop, writing a character and waiting for the ICXRDY interrupt prior to sending the next character.  Sometimes I get an ICXRDY interrupt between the step where I set the mode register (enabling the interface for Tx mode) and sending the first character.  This fools my code into thinking I can send the second character directly after the first, and the second character overwrites the first in the transmit buffer with the end effect that only the second character appears on the i2c bus.

I've tried always waiting on ICXRDY interrupt after setting up the mode register, but an ICXRDY interrupt is not always issued.  I can detect the spurrious interrupt in one of two manners:

  1. When I receive the interrupt (with the mask set as above) and read the vector register in the ISR, it returns 0.  This is easy to identify and ignore.
  2. When I receive the interrupt, use the 32-bit timer to check how quickly the interrupt occurs after the mode register was written.  Normally it takes about 90uS.  If it's a spurrious interrupt it takes only about 10uS.  Although relatively easy to identify, the code for this is a kludge.

I've searched for erratta and found nothing like this described.  So, any suggestions on what I might be doing wrong?  Or is there erratta that I simply haven't uncovered.

Thanks,

Dale

  • Hi Dale,

    Just wondering, when the (1st) spurious interrupt occurs, do you see any data on the bus? Is it causing some other onboard components to fail?

    Maybe your initialization is not in the right order? Please double check.

    Dale Wade said:
    Sometimes I get an ICXRDY interrupt between the step where I set the mode register (enabling the interface for Tx mode) and sending the first character.

    Just wondering, what if you always write to ICDXR before enableing transmit?

    regards,

    Paul

  • Paul,

    I tried your suggestion and unfortunately no change in behavior.  I'm pretty sure I have the correct order of initialization as I've based it on the "I2C Tips" Wiki document.  If you'd like I can list the order that I'm doing things.

    The odd thing is that the spurrious interrupts only happen sometimes. This seems to indicte a race somewhere.  Thinking it may be the state of the i2c module prior to my code setting up for transmitting I've done the simple check of setting a breakpoint in the debugger just prior to setting up for transmitting.  I then compared the state of the i2c registers in the case that doesn't get the spurrious interrupt against the case that does.  No difference in the pre-setup condition of the registers.

    I'll keep poking at it for differences, but I'm not seeing why I should get that spurrious interrupt.

    Thanks,

    Dale

  • Dale Wade said:
    I set the count, address and mode registers.  I then go off and loop, writing a character and waiting for the ICXRDY interrupt prior to sending the next character.

    The wiki page shows polling for XRDY and then writing to DXR.  Your description above says the opposite.  Have you changed this?

  • Dale,

    First, I would recommend you verify whether the spurrious interrupt is caused by XRDY (ICSTR_XRDY set)?

    I believe this is what Brad is referring to.

    /* Check for Bus Busy */
      while ((*I2C_ICSTR & ICSTR_BB));
     
      /* Disable I2C during configuration */
      *I2C_ICMDR = 0;
     
      /* Set the I2C controller to write len bytes */
      *I2C_ICCNT = write_len;
      *I2C_ICSAR = slaveaddr;
      *I2C_ICMDR = ICMDR_IRS | ICMDR_STT | ICMDR_TRX | ICMDR_MST | ICMDR_FREE;
     
      /* Transmit data */
      for (i = 0; i < write_len; i++)
      {
    // Wait for "XRDY" flag to transmit data or "ARDY" if we get NACKed
    while ( !(*I2C_ICSTR & (ICSTR_ICXRDY|ICSTR_ARDY)) );
     
    // If a NACK occurred then SCL is held low and STP bit cleared
    if ( *I2C_ICSTR & ICSTR_NACK )
    {
    *I2C_ICMDR = 0; // reset I2C so SCL isn't held low
    return RRET__FAIL;
    }
        *I2C_ICDXR = write_data[i];
      }

    Could you please first see if the polling method works, and then go back to the interrupt case?

     

  • Sorry about my delay in followup. I had a development system meltdown and have been distracted with other things (also didn't get auto-email on the two most recent responses so I though the thread had died).

    We had been using a polling scheme prior to my shifting to the interrupt driven mode.  Polling did not appear to experience problems, but it wasn't as rigorously tested (it was early in the prototyping).  However, as the problem appears to be a spurious interrupt, going back to polling doesn't seem to make sense.   What I have done is added a few lines of code to check the status register between setting the ICMDR to start the transfer and transmitting the first byte.  So the check for the interface ready for first byte is now polled, with all subsequent bytes being interrupt driven.  I am still getting the spurrious interrupts.

    Here is the applicable part of the Tx logic and the ISR:

    // Send an I2C command and get the reply

    Bool I2C_SendCommand (

           I2C_PARAMS*   msg)

    {

    // ... additional setup removed

    // Initiate transfer (start + address)

    I2C->ICIMR    =

    CSL_FMKT (I2C_ICIMR_NACK,  ENABLE)       |      // Enable NACK interrupt

    CSL_FMKT (I2C_ICIMR_ARDY,  ENABLE)       |      // Enable ARDY interrupt

    CSL_FMKT (I2C_ICIMR_ICXRDY, ENABLE);            // Enable Tx-data-ready interrupt

     

    I2C->ICCNT    = msg->CmdLen;                    // Set the byte count

    I2C->ICSAR    = I2Caddr;                        // Set the slave address to write to

                        

    //~DEBUG: Capture time to i2c interrupt - throw out TXRDY irqs that occur impossibly quickly

    i2cTxTsPrev [Zone] = Timestamp_get32();

                        

    // Configure the I2C for master transmit mode and release from reset

    I2C->ICMDR    =

           CSL_FMKT (I2C_ICMDR_FREE, TRUE)          |      // Allow I2C to free run on bkpt

           CSL_FMKT (I2C_ICMDR_STT,  SET)           |      // Send START before transfer

                                                           // No STOP - Hold bus and restart for read below

           CSL_FMKT (I2C_ICMDR_MST,  MASTER_MODE)   |      // I2C master mode

           CSL_FMKT (I2C_ICMDR_TRX,  TX_MODE)       |      // Transmit mode

           CSL_FMKT (I2C_ICMDR_IRS,  ENABLE);              // I2C Enabled

     

    // Can't check for NAK here - It's a race in the I2C module - so check after first

    // byte sent [DAW]

     

    // Wait for “XRDY” flag to transmit data or “ARDY” if we get NACKed

    count = 0;

    while ( (count < 100) &&

             !((I2C->ICSTR & CSL_I2C_ICSTR_ICXRDY_MASK|CSL_I2C_ICSTR_ARDY_MASK)) ) {

    count += 1;

    }

     

    if (count >= 100) {

           // timeout

    bRet = FALSE;

    }

    else if (I2C->ICSTR & CSL_I2C_ICSTR_NACK_MASK) {

           // NACK

    bRet = FALSE;

    }

     

    // Transmit all data, waiting on shift register ready for next byte (ARDY for last)

    for (i = 0; bRet && (i < msg->CmdLen); ++i) {

    I2C->ICDXR    = msg->szCmd [i];                   // Transmit a data byte

                              

           // Wait for the shift register ready to accept the byte

           bRet = Semaphore_pend (i2cZones [Zone].semWait, I2C_BYTE_XFER_TIMEOUT);

     

           // Check for NACK

           if (i2cZones[Zone].i2cIrqNak) {

                  i2cZones[Zone].i2cIrqNak   = FALSE;        // Clear NAK flag

    bRet                                     = FALSE;

           }

    }

    // ... Restart and Rx data removed

     

    // I2C hardware interrupt handler

    #pragma       CODE_SECTION (hwiI2C, ".acqCode");

    void hwiI2C (

           UArg   Zone)

    {

           CSL_I2cRegsOvly      I2C;

           Uint32               i2cIrqVec;

           Uint32               i2cTs;

           Uint32               i2cTxTsDiff;

     

           I2C = i2cZones[Zone].I2C;

          

           // ~DEBUG:    The following timestamping code is used only with

           //                   the transmit logic as a workaround to eliminate

           //                   spurrious ICXRDY interrupts. [DAW]

           i2cTs                = Timestamp_get32();

           i2cTxTsDiff          = i2cTs - i2cTxTsPrev [Zone];

           i2cTxTsPrev[Zone]    = i2cTs;

     

           i2cIrqVec = I2C->ICIVR;    // Clear interrupt

     

           // Check that vector is one we requested, else spurrious so ignore

           // (although they shouldn't occur, spurrious interrupts have been

           // observed at least during i2c data Tx with a vector of 0 being

           // read after the interrupt is taken [DAW])

           if ( i2cIrqVec && ((1 << (i2cIrqVec-1)) & I2C->ICIMR) ) {

                  if ( (i2cIrqVec == CSL_I2C_ICIVR_INTCODE_ICXRDY) &&    // spurrious ICXRDY IRQ?

                       (i2cTxTsDiff < I2C_TXDIFF_MAX) ) {

                       i2cSpurrious[Zone] += 1;

                       return;

                  }

                      

                  if (i2cIrqVec == CSL_I2C_ICIVR_INTCODE_NACK) {         // Set task level flag on NAK

                         i2cZones[Zone].i2cIrqNak = TRUE;

                  }

     

                  Semaphore_post (i2cZones [Zone].semWait);              // Unblock the waiting task

           }

    }

     

     

     

    }

  • Dale Wade said:
    // Initiate transfer (start + address)
    I2C->ICIMR    =
    CSL_FMKT (I2C_ICIMR_NACK,  ENABLE)       |      // Enable NACK interrupt
    CSL_FMKT (I2C_ICIMR_ARDY,  ENABLE)       |      // Enable ARDY interrupt
    CSL_FMKT (I2C_ICIMR_ICXRDY, ENABLE);            // Enable Tx-data-ready interrupt
     
    I2C->ICCNT    = msg->CmdLen;                    // Set the byte count
    I2C->ICSAR    = I2Caddr;                        // Set the slave address to write to
                        
    //~DEBUG: Capture time to i2c interrupt - throw out TXRDY irqs that occur impossibly quickly
    i2cTxTsPrev [Zone] = Timestamp_get32();
     

    What is the value of ICSTR.ICXRDY before you enable the interrupt in the code above?  Any time the I2C peripheral is reset (e.g. by writing ICMDR.IRS=0) then the ICSTR.ICXRDY bit will be set to its default value 1.  If you then enable the ICIMR.ICXRDY bit then it would not be surprising that you would get an interrupt.  Is this the cause of your spurious interrupt?

  • I think the problem is resolved. Blocking on an ICXRDY interrupt after setting up the transfer via the ICIMR register and prior to sending the first byte resolves the problem.  This is essentially what you've all been saying (and it does seem like the spurious interrupt must have been caused by the ICXRDY condition).  I thought I had tried reordering as suggested at one point and found that I didn't always get the ICXRDY interrupt, but that no longer seems to be the case. I am now running without my workaround code in place.

    Thanks much for the help!

  • That should have read, "Blocking on an ICXRDY interrupt after setting up the transfer via the ICMDR register and prior to sending the first byte resolves the problem."