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.

I2C malfunction on TPR12

Hi,

I have a TPR12 EVM. I am trying to communicate with a microchip cryptoauth EVM using I2C. The TPR12 board is configured to be in master mode and the microchip EVM is in slave mode. Theoretically, my communication should look as following:

  1. Send 0x00 (data) to the address 0x00. I should receive a NACK for this address and therefore the data bytes 0x00 should never be sent. This is done to wake the device up.
  2. Next, I send a data array (0x03, 0x07, 0x30, 0x00, 0x00, 0x00, 0x03, 0x5D) to the device address (0x36). I should see an ACK for this. This is done to receive the silicon revision of the device.
  3. Lastly, I write 0x00 to the address 0x36 and start reading 7 bytes of silicon revision data from the microchip.

However, when I try to run this and get the return values from the function I2C_transfer() on TPR12, I get the error code I2C_STS_ERR_NO_ACK (-3) for all stages. The weird part is, it is still receiving the correct data as shown in the screenshot.

The silicon revision is 7-0-0-60-3-83-bb.

I also used a logic analyzer to see what's going on at every step.

On step 1, it sends the device address 0x00 and receives a positive acknowledgement (it should not) and sends the data 0x00 receiving a negative acknowledgement. This still wakes the device up.

On step 2, it sends the data buffer to 0x36 address of the device and microchip sends an ACK. However, the TPR12 still says it didn't receive an a acknowledgement as it gives -3 error code.

On the last step, it writes 0x00 to the address 0x36 and receives the data from the microchip but does not give an ACK back on the last byte.

I am not sure why it is behaving like this. Can someone with more insight on this help me out please?

  • Hi Parth,

    What SDK version are you using, and which CPU core are you using on the TPR12?

    Thanks,
    Frank

  • Hi Frank,

    I am using the PDK version: pdk_tpr12_07_01_04_04 and cores CoretxR5_0 for MSS and C66xx_DSP for DSS (but I don't suppose DSS is relevant here).

  • Hi Parth,

    I'm reaching out to internal experts. We'll get back to you shortly.

    Regards,
    Frank

  • Hi Parth,

    I see there are three devices on the I2C bus, and you're trying to communicate with the ATECC608B-TFLXTLS. It see jumpers on the DM320118 for connecting the three devices to the I2C bus (J7,J8,J9). For debug, can you disconnect all devices from the bus aside from the ATECC608B-TFLXTLS?

    Send 0x00 (data) to the address 0x00. I should receive a NACK for this address and therefore the data bytes 0x00 should never be sent. This is done to wake the device up.
    On step 1, it sends the device address 0x00 and receives a positive acknowledgement (it should not) and sends the data 0x00 receiving a negative acknowledgement. This still wakes the device up.

    I gather you're trying to use the I2C general call address (UM10204, 3.1.13 General call address). For this first write:

    • Which devices are driving ACK for the address part of the transfer?
    • It appears no device on the bus ACKs the data part.

    This behavior could be correct, depending on how the devices on the bus react to general call. If the behavior is correct, it makese sense for I2C_transfer() to return I2C_STS_ERR_NO_ACK.

    Is this step necessary? Is there some other mechanism to wake up ATECC608B-TFLXTLS by writing directly to I2C address 0x36?

    For the I2C driver, are you using BLOCKING, CALLBACK, or POLLING mode?

    The I2C_STS_ERR_NO_ACK error is returned when ICSTR:NACK is set. I see several places in the driver code which return I2C_STS_ERR_NO_ACK:

    • I2C_v0_hwiFxnMaster()
    • I2C_primeTransfer_v0(): polling mode, write or read
    • I2C_transfer_v0(), blocking mode

    Regards,
    Frank

  • Hi Frank,

    I don't believe those jumpers (J7, J8, J9) are removable. It is shown here.

    Is this step necessary? Is there some other mechanism to wake up ATECC608B-TFLXTLS by writing directly to I2C address 0x36?

    Yes, this step is necessary. The way to wake the device up is sending a low pulse on SDA for 60 microseconds and then a high pulse for 1.5 ms. Sending a 0x00 signal at 100kHz does that for us.

    For the I2C driver, are you using BLOCKING, CALLBACK, or POLLING mode?

    I am using the I2C_MODE_BLOCKING transfer mode.

    Thank you,

    Parth Sarthi Sharma

  • Hi Parth,

    I don't believe those jumpers (J7, J8, J9) are removable. It is shown here.

    I think you intended to share something, but I don't see anything. Can the J7,J8,J9 connections be de-soldered, and then replaced with jumpers once debug is complete?

    Yes, this step is necessary.

    Ok, like I said, the behavior for the first transfer could be correct.

    Have you tried sending the 2nd transfer more than once? Does the driver always return NACK despite the ACK on the I2C bus?

    For the 2nd transfer failure (ACK detected as NACK), do you know where the failure occurs in the driver code (I2C_v0_hwiFxnMaster(), I2C_primeTransfer_v0(), I2C_transfer_v0())? When the failure occurs, what is the state of the I2C registers?

    I am using the I2C_MODE_BLOCKING transfer mode

    Have you experimented with I2C_MODE_POLLING mode to see if it makes a difference?

    Regards,
    Frank

  • Parth,

    I think you intended to share something, but I don't see anything.

    Sorry, I see it now. Can you cut the traces and solder jumpers into J7,J8,J9?

  • Hi Frank,

    I cut out the wire traces and soldered just the jumper to address 0x36. This didn't make a difference.

    I tried sending the 2nd transfer again, it didn't make a difference. I tried to trace on CCS and I did see the function I2C_transfer() call the function "I2C_transfer_v0()". I am not sure what you mean when you say "failure". Do you mean receiving NACK when I should be receiving ACK?

    I looked for the I2C_MODE_POLLING mode but couldn't find any. These are the only 2 modes available in the file I2C.h.

    Thank you,

    Parth Sarthi Sharma

  • Hi Parth,

    I am not sure what you mean when you say "failure". Do you mean receiving NACK when I should be receiving ACK?

    Yes, I mean the I2C driver is reporting NACK when you observe ACK on the bus with your logic analyzer.

    These are the only 2 modes available in the file I2C.h.

    I see this in <PDK>\packages\ti\drv\i2c\src\v0\I2C_v0.c:I2C_open_v0():

            hwAttrs = (I2C_HwAttrs const *)handle->hwAttrs;
    
            if(I2C_MODE_BLOCKING == object->i2cParams.transferMode)
            {
                if(true == hwAttrs->enableIntr)
                {
                    object->operMode = I2C_OPER_MODE_BLOCKING;
                }
                else
                {
                    object->operMode = I2C_OPER_MODE_POLLING;
                }
    

    Hence, if enableIntr is set to false, the driver will using POLLING mode.

    There is a global I2C driver configuration array in <PDK>\packages\ti\drv\i2c\soc\tpr12\I2C_soc.c:

            /* I2C configuration structure */
            I2C_HwAttrs i2cInitCfg[CSL_I2C_CNT] =
            {
                {
                    CSL_RCSS_I2CA_U_BASE,
                    OSAL_REGINT_INTVEC_EVENT_COMBINER,  /* default DSP Interrupt vector number */
                    CSL_DSS_INTR_RCSS_I2CA_INT, /*  DSP INTC I2C Event ID */
                    INVALID_INTC_MUX_NUM, /* CIC num not used for TPR12 */
                    0, /* cicEventId not used for TPR12 */
                    0, /* HostIntNum not used for TPR12 */
                    I2C_MODULE_FREQ_200M,  /* default I2C frequency, system clock/6 */
                    true, /* interrupt mode enabled */
                    I2C_OWN_ADDR, /* default I2C own slave addresse */
                },
                {
                    CSL_RCSS_I2CB_U_BASE,
                    OSAL_REGINT_INTVEC_EVENT_COMBINER,  /* default DSP Interrupt vector number */
                    CSL_DSS_INTR_RCSS_I2CB_INT, /*  DSP INTC I2C Event ID */
                    INVALID_INTC_MUX_NUM, /* CIC num not used for TPR12 */
                    0, /* cicEventId not used for TPR12 */
                    0, /* HostIntNum not used for TPR12 */
                    I2C_MODULE_FREQ_200M,  /* default I2C frequency, system clock/6 */
                    true, /* interrupt mode enabled */
                    I2C_OWN_ADDR, /* default I2C own slave addresse */
                }
            };
    

    This array defines the default configuration for each I2C instance supported by the driver. The "true" setting next to the comment /* interrupt mode enabled */ is for the enableIntr flag.

    These APIs can be used to read/write the default configuration for an I2C instance in the configuration table before the instance is opened with I2C_open():

    • <PDK>/packages/ti/drv/i2c/soc/I2C_soc.h:88:extern int32_t I2C_socGetInitCfg(uint32_t index, I2C_HwAttrs *cfg);
    • <PDK>/packages/ti/drv/i2c/soc/I2C_soc.h:89:extern int32_t I2C_socSetInitCfg(uint32_t index, const I2C_HwAttrs *cfg);

    You can see an example of using these functions in: <PDK>/packages/ti/board/diag/current_monitor/src/current_monitor_test.c:516:        I2C_socSetInitCfg(index, &i2cConfig);

    Regards,
    Frank

  • Hi Frank,

    Thank you so much for explaining this in detail. I modified my I2C init task by adding stI2cConfig.enableIntr = false; just before the i32Status = I2C_socSetInitCfg(0U, &stI2cConfig); function call. This led to an interesting behavior.

    I performed 2 tests. In the first test, I repeated exactly what I was doing earlier.

    For the first part, it sends 0x00 to the address 0x00. TPR12 should receive a negative acknowledgement in the end as is shown in the logic analyzer.

    However, it prints out I2C_STS_SUCCESS ("1") which it shouldn't.

    For the second part, I send a data array (0x03, 0x07, 0x30, 0x00, 0x00, 0x00, 0x03, 0x5D) to the device address (0x36). As expected, it sends the data and receives a positive acknowledgement. TPR12 also gives out 1 as the return status.

    Lastly, I try to read data from the address 0x36. It receives the data but does not send an ACK back to microchip for the last byte.

    However, as a return status it prints out 1.

    Here are the three return statuses. (Please ignore the 4th one).

    For my second test, I modified the slave address to be 0x22 so that I know for sure that it returns NACK. My observations are as follows:

    For the first part, it sends 0x00 to the address 0x00. TPR12 should receive a negative acknowledgement in the end as is shown in the logic analyzer. 

    However, it prints out I2C_STS_SUCCESS ("1") which it shouldn't.

    For the second part, I send a data array (0x03, 0x07, 0x30, 0x00, 0x00, 0x00, 0x03, 0x5D) to the device address (0x22). As expected, it does not send the data since 0x22 address does not exist on the bus. TPR12 also gives out -3 as the return status.

    Lastly, I try to read data from the address 0x22. Again, as expected I read nothing since 0x22 address does not exist on the bus. TPR12 again gives out -3 as the return status.

    Here are the three return statuses. (Please ignore the 4th one).

    This makes me believe that current configurations make the return status as I2C_STS_SUCCESS as long as even a single byte is acknowledged during the entire transaction.

  • Update: I read on stack overflow (linked here) that on a slave-to-master transfer, the master must send a NACK just before sending a STOP condition to end the transfer. This might be there reason for NACK on logic analyzer after reading all the bytes (but a 1 on screen). But still doesn't explain receiving I2C_STS_SUCCESS in the first part (0x00).

  • Hi Parth,

    Ok, now we understand the NACK sent by the AM273x for the master read. I notice this in the I2C_transfer() header:

                *  After all the data has been transmitted, the driver will evaluate if any
                *  data needs to be read from the device.
                *  If so, a Re-START bit is sent, along with the same 7-bit I2C slave address
                *  (with the Read bit). Else, the transfer is concluded with a STOP bit.
                *  After the specified number of bytes have been read by the I2C, the transfer
                *  is ended with a NACK and STOP bit.
    

    In summary, we now have as below. Can you please confirm?

    Driver Mode Xfer Number Xfer Type I2C Slave Address TPR12 Behavior
    BLOCKING 1 General Call 0x00 Correct
    BLOCKING 2 Write 0x36 Incorrect, Rx NACK
    BLOCKING 3 Read 0x36 Correct
    POLLING 1 General Call 0x00 Incorrect, Rx ACK
    POLLING 2 Write 0x36 Correct
    POLLING 3 Read 0x36 Correct
    POLLING 1 General Call 0x00 Incorrect, Rx ACK
    POLLING 2 Write 0x22 Correct
    POLLING 3 Read 0x22 Correct

    I noticed in your logic analyzer captures that the I2C slave addresses seem to be LSB first. All bytes should be transmitted MSB first according to this: https://i2c.info/i2c-bus-specification.

    I also noticed in the I2C spec that 0x00 isn't allowed for the 2nd byte in the general call. Have you tried a byte other than 0x00?

    This looks like a software driver issue to me, but just to be sure, I wanted to ask you a few questions related to the hardware:

    • Which I2C HW module are you using, and how are you connecting it to the DM320118?
    • How are the IOMUX configuration registers set for the I2C signals?
    • What voltage level is the logic analyzer set for, and do you observe the correct pull up voltage on SCL / SDA?

    Regards,
    Frank

  • Hi Frank,

    Yes, that tables seems correct. How can I switch between the MSB first and LSB first implementation? Since I am getting the data I thought I was implementing it correctly?

    I did try 0x11 once instead of 0x00 but that didn't work. I can probably try 0xFF data with 0x00 address to see if that works.

    • What do you mean by I2C HW module? I am running jumpers from the TPR12 board to the Microchip board.
    • I am not sure what IOMUX configuration I am using. How can I check that? (I am very new to the system).
    • The logic analyzer is set for 1.2V threshold. I believe I did see the correct pull up voltage (3.3V?) the last time I checked on an MSO.

    Regards,

    Parth Sarthi Sharma

  • Hi Parth,

    Sorry for the delayed response.

    How can I switch between the MSB first and LSB first implementation?

    Reviewing your captures more closely, I realize I was mistaken. The 7-bit address is being correctly transmitted MSB first.

    What do you mean by I2C HW module?

    I mean which instance of the I2C on the TPR12 (AM273x) are you using? According to the TRM (spruiu0) Table 1-1. Module Allocation and Instances within the Device, there are three instances: MSS_I2CA, RCSS_I2CA, RCSS_I2CB

    I am running jumpers from the TPR12 board to the Microchip board.

    Which header/pins/signals on the AM273x EVM are you using to access the I2C signals? The EVM schematic is located in this design package: https://www.ti.com/lit/zip/sprr442

    I am not sure what IOMUX configuration I am using. How can I check that?

    The AM273x datasheet (www.ti.com/.../GUID-5D70DAAF-99C7-4C2A-8B0D-4656D5A573C9, am2732, 6.2 Pin Attributes) shows the signal options for the device balls.

    If you trace the I2C signal from the header/pins/signals you're using back to the SOC, you'll see which balls are being used. Then lookup the balls in the datasheet table. The pad name and pin control register address is located in the table.

    Next, open a CCS memory window and set it to view the address of the pin control register. The definitions for the IOMUX registers are located in the TRM, 5.2.9 MSS_IOMUX Registers.

    The logic analyzer is set for 1.2V threshold

    It looks like you're using a Saleae logic analyzer. I would set the voltage threshold to 3.3 V, see https://support.saleae.com/user-guide/supported-voltages.

    Regards,
    Frank

  • Hi Frank,

    Thank you so much for such a patient and detailed reply.

    I mean which instance of the I2C on the TPR12 (AM273x) are you using?

    According to my TPR12_EVM.syscfg I am using MSS_I2CA (MSS_I2C1.peripheral.$assign = "MSS_I2CA").

    Which header/pins/signals on the AM273x EVM are you using to access the I2C signals?

    I am using pin F18 for MSS_I2CA_SCL and F16 for MSS_I2CA_SDA.

    I also followed your guide to check the IOMUX configuration. Here is what I found:

    The pins are configured in the file TPR12_pinmux_data.c in the following manner:

    Also the pins F16 and F18 have the following pin control address:

    Lastly, checking data at the address 0x020C00C8 and 0x020C00CC reveals the following:

    I have also changed my threshold level to 3.3V. Thank you for pointing that out.

    Regards,

    Parth

  • Hi Parth,

    I don't see anything wrong with your hardware connections, or the pad configuration registers.

    At this point, I'd focus on the general call failure (ACK instead of expected NACK) for POLLING mode.

    You can rebuild the driver in debug mode for R5F as follows:

    • cd c:\ti\coresdk_rtos_tpr12_07_00_03_09\pdk_tpr12_07_00_03_09\packages\ti\drv\i2c
    • gmake SOC=tpr12 BOARD=tpr12_evm CORE=mcu1_0 BUILD_PROFILE=debug

    Build outputs will be in these folders:

    • Binaries    : <SDK>\pdk_tpr12_07_00_03_09\packages\ti\binary\ti\drv\i2c
    • Library     : <SDK>\pdk_tpr12_07_00_03_09\packages\ti\drv\i2c\lib\tpr12\r5f

    You application will then need to be modified to link to the debug version of the library.

    This will allow you to step through the code and inspect the I2C registers at key points in the transfer. You can also modify the driver code to snapshot the I2C registers to memory at key points in the transfer.

    I believe you should enter the polling loop in I2C_primeTransfer_v0(). What does I2CMasterIntStatusEx() return? What are the contents of the ICSTR register?

    Regards,
    Frank