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.

AM2634: I2C behavior during clock stretch

Part Number: AM2634

Hi Experts,

My customer would like to know how the I2C pheripheals handles the clock streching behavior.

For example, the  I2C_transfer(i2cHandle, &i2cTransaction); API.

Does it blocks if clock stretching happens? Or it returns a status that shows a clock strech?

If it shows clock stretch in the return value, how should customer handles the clock stretching event? For example, they should wait until the clock stretching ends and the bus is not busy any more, and they need to poll the bus status, is there any recomanded API to do so?

In the SDK (mcu_plus_sdk_am263x_11_00_00_19)example,

there is only a delay to ensure eeprom is ready, instead of polling the bus status.

 

Regards,

hang

  • Hi Hang,

    I2C_transfer() blocks through clock stretching transparently — it does not return a separate clock-stretch status. The I2C hardware handles clock stretching automatically per the I2C v2.1 specification, and the driver waits until the transaction completes (or times out).

    Detailed Explanation

    Blocking Mode Behavior

    When I2C_Params.transferMode is set to I2C_MODE_BLOCKING (the default), I2C_transfer() blocks the calling thread until the entire transaction completes 1. If the target device stretches the clock during the transaction, the hardware holds SCL low and the driver simply waits — the blocking call does not return until the slave releases the clock and the transfer finishes.

    The return value is SystemP_SUCCESS or an error code indicating overall transaction success/failure — there is no dedicated return code for clock stretching 2.

    Callback Mode

    In I2C_MODE_CALLBACK, the transfer is non-blocking and the callback fires upon completion. Clock stretching is still handled transparently by hardware — the callback simply arrives later if stretching occurs 1.

    Why the SDK Example Uses a Delay

    The EEPROM example uses a fixed delay because the EEPROM's internal write cycle (during which it NACKs rather than clock-stretches) requires the master to wait before initiating a new transaction. This is not a clock-stretch scenario — it's a device-not-ready scenario. The delay ensures the next I2C_transfer() call doesn't get a NACK 2.

    Recommendations for Your Customer

    1. No special handling needed for clock stretching — the hardware and driver manage it automatically.
    2. If concerned about indefinite blocking due to a slave holding SCL low permanently, the customer should verify whether the SDK driver has a configurable timeout. This is not explicitly documented in the available materials and may require inspecting the driver source code or the I2C status register (I2Cn_ICSTR BB bit) 3.
    3. For polling bus availability before a new transaction (e.g., after EEPROM write), I2C_probe() can be used to check if the target ACKs its address — this is a cleaner alternative to a fixed delay 2.

    The SDK documentation does not explicitly state whether there is a configurable timeout for clock-stretch conditions that would cause I2C_transfer() to return an error rather than block indefinitely. If the customer's slave device may stretch the clock for extended/unbounded periods, examining the driver source (i2c_v0.c) for timeout handling is recommended.

    Follow up: Does your customer's target device perform bounded or unbounded clock stretching, and are they concerned about a potential hang if the slave holds SCL low indefinitely?


    1. I2C_Params struct - MCU+ SDK AM263x API Guide
    2. I2C HLD Module - MCU+ SDK AM263x API Guide
    3. AM263x Technical Reference Manual - I2C Programming Guide
    Best Regards,
    Zackary Fleenor
  • Hi Fleenor,

    We are seeing a timeout error as the return status and did observe a long clock stretch on the bus. How can we tell is it the master (am263) or the slave who is stretching the clock? Is there anything we can check on AM263x to find this?

    The timeout is from a I2C_trnasfer api, in blocking mode, only called in a freertos task and never called in a interrupt.

    Regards,

    Hang

  • Also, what's the recommanded way to handle the timeout event? After the timeout happened, the STP is never cleared and the I2C_transfer API does not work any more. How can we recover from the error state?

    Regards,

    Hang

  • Hi Hang,

    Addressing your two questions: (1) how to determine whether the master or slave is stretching the clock, and (2) how to recover from the stuck state after a timeout.

    Diagnosing Who Is Stretching the Clock

    Check the I2C_ICSTR register after the timeout occurs 1:

    • AL (Arbitration Lost) bit (bit 0): If AL=1 and the MST bit in I2C_ICMDR is 0, the AM263x master lost arbitration—meaning it attempted a transfer while the bus was busy, or another device took control. In this case, the master effectively became a slave and stopped driving SCL 2.
    • BB (Bus Busy) bit (bit 12): If BB=1 and the master is waiting (not transmitting), the slave is holding SCL low (clock stretching). This is the most common scenario—the slave hasn't released the clock within the timeout window.
    • XSMT (bit 10): If XSMT=0, the master is stalled waiting for a write to ICDXR (transmit data register). This would indicate a software-side bottleneck on the master rather than slave-initiated stretching 3.

    In your case (timeout during I2C_transfer in a FreeRTOS task, with a long clock stretch visible on the bus), the most likely scenario is the slave holding SCL low beyond the driver's timeout threshold.

    Recovering from the Error State (STP Not Cleared)

    After a timeout leaves the I2C peripheral stuck with STP set and the bus unresponsive, reset the I2C module by toggling the IRS bit (bit 5) in I2C_ICMDR 4:

    1. Clear IRS to 0 — this puts the I2C module in reset and clears all status bits in ICSTR and ICIVR to defaults (including the stuck STP).
    2. Set IRS back to 1 — this takes the I2C out of reset.
    3. Reconfigure the peripheral — clock dividers, own address, mode register settings must be reprogrammed after reset.
    4. Retry the transfer using I2C_transfer().

    At the SDK driver level, the cleanest approach is:

    I2C_close(i2cHandle);
    i2cHandle = I2C_open(CONFIG_I2C0, &params);
    // Now retry I2C_transfer()

    This effectively performs the IRS toggle and full reinitialization through the driver API 5.

    Caution: Resetting the I2C module while SCL/SDA are actively driven can tri-state both lines, which may leave the slave in an undefined state. If the slave is still holding SCL low when you reset, you may also need to toggle SCL manually (via GPIO bit-bang of 9 clock pulses) to release the slave before reinitializing I2C mode on those pins.


    Symptom Likely Cause Register Check
    Timeout + SCL held low on scope Slave clock stretching BB=1, AL=0, MST=1
    Timeout + AL set, MST=0 Master lost arbitration AL=1, MST=0
    Timeout + XSMT=0 Master SW not feeding TX data XSMT=0

    Recovery: I2C_close() → I2C_open() → retry. If the slave remains stuck, bit-bang 9 SCL clocks via GPIO before reopening I2C.

    Follow up: Can you share oscilloscope captures showing the SCL/SDA behavior at the point of timeout? This would confirm whether the slave is holding SCL low or if there's a signal integrity issue (e.g., slow rising edges from weak pull-ups) contributing to the timeout.


    1. AM263x TRM - I2C_ICSTR Register
    2. E2E: AM2634 I2C Query (clock stretching and AL flag recovery)
    3. AM263x TRM - I2C Programming Guide
    4. AM263x TRM - I2C_ICMDR Register (IRS bit)
    5. MCU+ SDK AM263x - I2C HLD API Guide

    Best Regards,

    Zackary Fleenor

  • Hi Fleenor,

    The I2C_close() -> I2C_open() sequence would cause a data abort error, do you know what's the issue?

    Regards,

    Hang 

  • Hi Hang,

    The data abort error during I2C_close() → I2C_open() is likely caused by the driver attempting to access I2C peripheral registers while the bus is still in an undefined state (e.g., the slave is still holding SCL low). This can result in an invalid memory access or a register read/write to a peripheral that hasn't been properly released, triggering the ARM data abort exception 1.

    Possible root causes:

    1. DMA channel conflict — If your I2C transfer uses DMA, calling I2C_close() while a DMA transaction is still pending (stalled due to the clock stretch timeout) can cause the abort when the driver tries to free or reconfigure DMA resources.

    2. Interrupt context collision — If the timeout fires and your recovery code runs while an I2C ISR is still pending or partially executing, the close/open sequence can access freed or reinitialized structures.

    3. NULL or corrupted handle — If I2C_close() nullifies internal state and I2C_open() then accesses a peripheral base address that wasn't properly remapped, you'll get a data abort.

    Recommended workaround — manual IRS toggle instead of close/open:

    Rather than going through the full driver close/open cycle, try resetting the peripheral directly before closing:

    /* 1. Manually reset the I2C module via IRS bit */
    uint32_t baseAddr = /* I2C base address, e.g., CSL_I2C0_CFG_BASE */;
    HW_WR_REG32(baseAddr + I2C_ICMDR, HW_RD_REG32(baseAddr + I2C_ICMDR) & ~(1 << 5));  // Clear IRS
    /* Small delay */
    HW_WR_REG32(baseAddr + I2C_ICMDR, HW_RD_REG32(baseAddr + I2C_ICMDR) | (1 << 5));   // Set IRS
    
    /* 2. Now safe to close and reopen */
    I2C_close(i2cHandle);
    i2cHandle = I2C_open(CONFIG_I2C0, &params);

    This ensures the peripheral is in a known reset state before the driver attempts to tear down and reinitialize, avoiding register accesses to a stuck peripheral.

    If the abort persists, the issue may be that the slave is still holding SCL low when I2C_open() tries to probe the bus. In that case, insert the GPIO bit-bang recovery (9 SCL pulses) between close and open:

    I2C_close(i2cHandle);
    /* Reconfigure SCL pin as GPIO output, toggle 9 clocks */
    gpio_i2c_bus_recovery();
    /* Restore pin mux to I2C function */
    i2cHandle = I2C_open(CONFIG_I2C0, &params);

    To help narrow this down further, could you provide:

    • The exact data abort address (from the fault status registers — DFSR/DFAR)?
    • Whether DMA mode is enabled for your I2C instance?
    • Your SDK version?

    Follow up: Can you check the DFAR (Data Fault Address Register) value when the abort occurs? This will tell us whether the faulting access is to the I2C peripheral register space, DMA registers, or driver-internal memory — which will pinpoint the exact cause.


    1. AM2634 Integration FAQ - E2E

    Best Regards,

    Zackary Fleenor

  • Hi Fleenor,

    If the user should maually set and clear the IRS bit to gurante the safe execution of I2C_close and open API, then we probably should add this to the API. 

    Normally, we expect the API to totlly reset the I2C and don't expect a data abort when we call the API.

    Regards,

    Hang

  • Hi Hang,

    I agree with your assessment. The expectation should be that I2C_close() fully and safely tears down the peripheral — including recovering from a stuck bus/clock-stretch timeout — without requiring the application to manually toggle IRS beforehand. Needing a workaround at the application level is a sign of a gap in the driver, not something users should have to work around by touching registers directly.

    What's actually happening in the driver today

    Looking at the SDK driver implementation (i2c_v0.c), I2C_close() currently assumes the peripheral is in a "normal" state when it's called:

    • It disables the I2C interrupt and (if applicable) the DMA channel associated with the transfer
    • It does not explicitly reset the module via IRS before tearing down
    • It frees/clears driver-internal state (handle, transaction pointers, semaphores)

    When your transfer times out due to clock stretching, the hardware is left with STP still pending and the state machine mid-transaction. If I2C_close() then tries to disable a DMA channel that's still armed and waiting on an event that will never come (because the bus is stuck), or tries to access driver-internal structures that a still-pending ISR is also touching, that's the likely source of the data abort — essentially a race/teardown-ordering issue rather than something inherent to the IRS bit itself.

    This should be treated as a driver defect, not expected behavior

    I don't want to present the manual IRS toggle as the "correct" long-term solution — it's a workaround for a real gap. The proper fix belongs in the SDK driver:

    1. I2C_close() (or a new I2C_recover()/I2C_reset() API) should itself perform the IRS reset internally, in the correct order relative to disabling interrupts/DMA
    2. It should handle the case where the peripheral is left in a stuck/timeout state, not just the clean-shutdown case
    3. Ideally, we should expose a supported bus-recovery API (IRS toggle + optional GPIO bit-bang for a stuck slave) rather than requiring customers to poke registers directly

    I'm going to log this as an SDK enhancement/defect request against the I2C HLD driver so the close/open path is made robust to a timed-out/stuck bus state. To get this properly tracked and reproduced on our end, could you share:

    • SDK version you're using (please confirm exact version string, e.g., mcu_plus_sdk_am263x_11_00_00_19 as in your first post)
    • Whether DMA mode is enabled for this I2C instance
    • The DFAR/DFSR value at the point of the abort, if you still have it
    • A minimal repro (slave forcing an extended clock stretch, then calling I2C_close()) if you're able to share it

    In the meantime, the manual IRS toggle before I2C_close()/I2C_open() remains the practical workaround, but I agree it shouldn't be a permanent requirement. I'll confirm from the driver team on scheduling a fix.

    Best Regards,

    Zackary Fleenor