Legacy I2C Transaction on I3C Bus

Until the Dynamic Address is assigned, the I3C Target operates in I2C Target Mode (OPMD = 0b00) and uses the Static Address stored in the I3CxSADR register to represent itself on the bus. When in this mode, the Controller can use an I2C Write Transfer to write data to the Target directly and an I2C Read Transfer to read data from the Target directly.

Once the Target participates in the Dynamic Address Assignment procedure and receives a Dynamic Address in its I3CxDADR register, it starts to operate in I3C SDR Mode (OPMD = 0b01). The Target will no longer respond to its Static Address in I3CxSADR register and will NACK any request to its Static Address.

Important:
  1. 1.The I3C Target module does not support 10-bit I2C addressing and clock stretching when operating in I2C mode. Refer to 2IC Backward Compatibility for details.
  2. 2.Additional pin configuration is required for the SDA and SCL pads to be I2C/SMBus-compatible. Refer to 2I3C Pad Compatibility with IC/SMBus Levels for more information.
  3. 3.It is possible for an I2C Transaction to take place even when the bus is configured to operate in I3C SDR Mode or when an I3C SDR Transaction is in progress. For instance, the Controller can choose to transmit I3C Broadcast Address 7’h7E/W, followed by a Restart and I2C Static Address to begin an I2C Transaction while in I3C SDR Mode.
To address the Target directly, the Controller sends a Start or a Restart, followed by the I2C Address Header. The address header consists of the Target’s 7-bit Static Address, followed by a R/W bit from the Controller and an ACK/NACK bit from the Target. The R/W bit is ‘0’ (write) for I2C Write Transfer and ‘1’ (read) for I2C Read Transfer. If the Static Address in the address header matches with the Target’s Static Address in the I3CxSADR register, the Target ACKs the request and sets the SADRIF interrupt flag bit. The RNW status bits are also set according to the R/W bit in the address header. The Target NACKs the request if the Static Address in the address header does not match the address in I3CxSADR register.
Important: The RNW status bits only apply to Private I3C/I2C Transfers. The R/W bit in the address header is not captured during non-Private Transactions (like CCC, Hot-Join or IBI).
In I2C Write Transfer, following the address header, the Controller will continue to send the 8-bit Data on the bus. The data becomes available for the user to read from the I3CxRXB Receive Buffer and the Target sends an ACK on the bus. The Target responds with a NACK on the bus if a Receive Overrun error (RXOIF bit) occurs. This process continues until the Controller ends the transaction with a Stop or Restart, after which the RNW status bits reset to 0b00. The general frame format for I2C Write Transaction is shown in Figure 1 and a pseudo-code is shown in Pseudo-code for Legacy I2C Write Transaction below.
In I2C Read Transfer, following the address header from the Controller, the Target sends an 8-bit Data on the bus. The user writes the data to be sent to the I3CxTXB Transmit Buffer. If the Controller accepts the data and sends an ACK on the bus, I2CACKIF interrupt flag is set. If the Controller declines the data and sends a NACK on the bus, I2CNACKIF interrupt flag is set. The Target can continue to send the data until the Controller terminates the transaction with a Stop or Restart, after which the RNW status bits reset to 0b00. The general frame format for I2C Read Transaction is shown in Figure 2 and a pseudo-code is shown in Pseudo-code for Legacy I2C Read Transaction below.
Figure 1. Legacy I2C Write Transfer Frame Format
Figure 2. Legacy I2C Read Transfer Frame Format

Pseudo-code for Legacy I2C Write Transaction

uint8_t rxData[SIZE];

uint8_t I3C1_Target_LegacyI2CWrite()
{
    uint16_t i = 0;

    // Check Target's operating mode first
    if(I3C1STATbits.OPMD != 0b00) {
        return 1;   // Target is operating in I3C mode, abort
    }

    // Check if Static Address Match occured in Write mode
    if(!I2C1STATbits.SADRIF || !I3C1STATbits.RNW == 0b10) {
        return 1;   // abort if no static address match or Read mode
    }

    while(i < SIZE) {
        // Wait for data to become available in Rx Buffer
        while(!I3C1STATbits.RXBF);

        // Read data from Receive buffer and increment pointer
        rxData[i++] = I3C1RXB;

        // Perform error checking if desired
        // Check for Rx Overrun Error or Rx Read Error
        // if(I3C1ERRIR0bits.RXOIF || I3C1ERRIR1bits.RXREIF) { ... }

        // Check for Stop or Restart for End of Transaction
        if(I3C1PIR0bits.PCIF || I3C1PIR0bits.RSCIF || I3C1PIR1bits.TCOMPIF) {
            return 0;  // end of transaction with successful write
        }
    }

    // Code execution reaches here when rxData[] is full 
    // Wait for Stop or Restart for End of Transaction
    while(!I3C1PIR0bits.PCIF && !I3C1PIR0bits.RSCIF && !I3C1PIR1bits.TCOMPIF) {
        // While waiting, check for additional errors (optional)
        // if(I3C1ERRIR1bits.MWLOEIF || I3C1ERRIR0bits.RXOIF || I3C1ERRIR1bits.RXREIF) { ... }
    }

    return 1;   // end of transaction with possible data overflow
}

Pseudo-code for Legacy I2C Read Transaction

uint8_t txData[SIZE];

uint8_t I3C1_Target_LegacyI2CRead()
{
    uint16_t i = 0;

    // Check Target's operating mode first
    if(I3C1STATbits.OPMD != 0b00) {
        return 1;   // Target is operating in I3C mode, abort
    }

    // Check if Static Address Match occured in Read mode
    if(!I2C1STATbits.SADRIF || !I3C1STATbits.RNW == 0b01) {
        return 1;   // abort if no static address match or Write mode
    }

    while(i < SIZE) {
        // Wait for Tx Buffer to become empty
        while(!I3C1STATbits.TXBE);

        // Write data to Transmit buffer and increment pointer
        I3C1TXB = txData[i++];

        // Perform error checking if desired
        // Check for Tx Underrun Error or Tx Write Error
        // if(I3C1ERRIR0bits.TXUIF || I3C1ERRIR1bits.TXWEIF) { ... }

        // Check for Controller abort (possibly premature)
        if(I3C1PIR0bits.PCIF || I3C1PIR0bits.RSCIF || I3C1PIR1bits.TCOMPIF) {
            return 1;  // end of transaction with Controller abort
        }
    }

    // Code execution reaches here when all data from txData[] has been sent 
    // Wait for Stop or Restart for End of Transaction
    while(!I3C1PIR0bits.PCIF && !I3C1PIR0bits.RSCIF && !I3C1PIR1bits.TCOMPIF) {
        // While waiting, check for additional errors (optional)
        // if(I3C1ERRIR0bits.TXUIF || I3C1ERRIR1bits.TXWEIF) { ... }
    }

    return 0;   // end of transaction with possible data underflow
}