Skip to content

xiic_l.c: XIic_Send() / XIic_DynSend() can miss NACK on final transmitted byte and still report full byte count #379

@ttobsen

Description

@ttobsen

Bug Report

The low-level polled transmit path in the Xilinx IIC driver appears to miss a slave NACK when it occurs on the final transmitted data byte.

This affects both:

  • XIic_Send() via SendData()
  • XIic_DynSend() via DynSendData()

Observed behavior

XIic_Send() can return the full requested byte count even though the slave NACKed the last transmitted byte.

In our case this shows up with an EEPROM write into a write-protected region:

  • the target device does not actually update the memory cell
  • the write should be rejected by the slave on the bus
  • but XIic_Send() still reports success, so higher layers interpret the transfer as successful

Why this looks like a driver bug

In SendData() the code checks XIIC_INTR_TX_ERROR_MASK only while waiting for the next TX_EMPTY condition before writing the next byte.

Relevant flow:

  1. wait for TX_EMPTY or error
  2. write one byte to XIIC_DTR_REG_OFFSET
  3. decrement ByteCount
  4. if ByteCount == 0, exit loop
  5. if XIIC_STOP, wait only for XIIC_INTR_BNB_MASK
  6. return success (ByteCount == 0)

Because of this, if the slave NACK happens on the final transmitted byte, there is no final check for XIIC_INTR_TX_ERROR_MASK after the last byte has been written. The code only waits for bus-not-busy and then returns success.

The same pattern exists in DynSendData().

Affected code paths

SendData():

  • error is checked here before the next byte:
if (IntrStatus & (XIIC_INTR_TX_ERROR_MASK |
                  XIIC_INTR_ARB_LOST_MASK |
                  XIIC_INTR_BNB_MASK)) {
    return ByteCount;
}
  • but after the final byte is written:
XIic_WriteReg(BaseAddress, XIIC_DTR_REG_OFFSET, *BufferPtr++);
ByteCount--;
  • there is no final TX_ERROR check, only:
if (Option == XIIC_STOP) {
    while (1) {
        if (XIic_ReadIisr(BaseAddress) & XIIC_INTR_BNB_MASK) {
            break;
        }
    }
}

DynSendData() has the same issue.

Expected behavior

If the slave NACKs the final transmitted byte, XIic_Send() / XIic_DynSend() should not report the full requested byte count as sent.

A final-byte NACK should be reflected as an incomplete transfer, just like NACK on earlier bytes.

Impact

Higher-level drivers that use:

  • bytes_sent == requested_length as success, or
  • wrappers such as register writes built on top of XIic_Send()

can falsely report success even though the slave rejected the final byte.

This is especially visible with devices that:

  • ACK address and earlier bytes
  • NACK only the final data byte in specific protocol situations

One real example is EEPROM write protection, where the device may ACK device address and internal address bytes, but NACK the protected data byte.

Suggested fix

After transmitting the final byte, and before returning success, explicitly check XIIC_INTR_TX_ERROR_MASK once more.

This should be done in:

  • SendData()
  • DynSendData()

A fix would likely involve checking for TX_ERROR after the final byte has left the FIFO and before treating the transfer as successful, instead of only polling for BNB.

Version

The issue is present in the current xiic_l.c implementation containing:

  • XIic_Send()
  • SendData()
  • XIic_DynSend()
  • DynSendData()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions