37.2.9.1 IBI Payload and Mandatory Data Byte

The IBI Payload is any data sent by the Target that follows the Dynamic Address after an IBI request is ACKed by the Controller. The first byte of the payload that immediately follows the Dynamic Address is referred to as the Mandatory Data Byte and is accessed using the I3CxIBIMDB register. This Target module is designed to always send one (the Mandatory Data Byte) or more bytes of payload data once the IBI request is accepted by the Controller, which is indicated by the BCR2 bit (BCR2 = 1). This is communicated to the Controller during the Dynamic Address Assignment procedure or when the Controller requests for it using the GETBCR (Get Bus Characteristics Register) Direct Common Command Code (CCC).

The Controller sets the Maximum Payload Size using the SETMRL (Set Maximum Read Length) CCC which is stored in the I3CxIBIPSZ IBI Payload Size register. The firmware can choose to override this value by writing to the I3CxIBIPSZ register. The Controller can use the GETMRL (Get Maximum Read Length) CCC to read the value of the Maximum Payload Size stored in the I3CxIBIPSZ register.

Important: It is highly recommended for the firmware to write to the I3CxIBIPSZ register when there are no transactions happening on the bus. In the event of a race condition when the I3CxIBIPSZ register is being written by both firmware and hardware (SETMRL CCC), firmware writes will always have precedence.

Once the Controller ACKs the IBI request, it cannot decline the reception of the Mandatory Data Byte since it is transmitted in Push-Pull mode and must wait for the next T-bit. After receiving the Mandatory Data Byte, the Controller can continue to receive further payload data from the Target in Push-Pull mode or terminate any subsequent transmission by pulling the End-of-Data T-bit low (Restart condition on the bus) or transmitting a Stop. The frame format of a successful IBI transaction is shown in Figure 37-45.

The I3CxIBIMDB Mandatory Data Byte provides the Controller additional information about the event that has happened and is divided into two fields as defined by MIPI.

  • IBIMDB[7:5] – Interrupt Group Identifier (3 bits)
  • IBIMDB[4:0] – Specific Interrupt Identifier (5 bits)
Tip: MIPI Alliance maintains a web-based registry of defined Mandatory Data Byte use case values at https://www.mipi.org/MIPI_I3C_mandatory_data_byte_values_public.

The user can write the payload data to be sent to the I3CxTXB Transmit Buffer, which feeds into the Transmit FIFO. Once the Target sends the I3CxIBIMDB Mandatory Data Byte, it will start sending the payload from the Transmit FIFO. The Target continues to send the payload until one of the following conditions occurs (refer to the pseudo-code shown in IBI Request Pseudo-code Using Polling for more information):

  • Transmit FIFO becomes empty; Target pulls the End-of-Data T-bit low and sets the IBIDONEIF and TCOMPIF flags
  • Maximum Payload Size (I3CxIBIPSZ) limit is reached; Target pulls the End-of-Data T-bit low despite Transmit FIFO not being empty (TXFNE = 1) and sets the IBIDONEIF and TCOMPIF flags
  • Controller aborts the payload transmission by pulling the End-of-Data T-bit low (a Restart condition), in which case the Target sets the ABEIF error alongside the IBIDONEIF and TCOMPIF flags
The user software can use the IBI payload to communicate the specific details of an interrupt. If the Controller NACKs or disables the IBI request on the bus, then the user software can use the INTPEND bits to store the interrupt number of the pending interrupt (a value of 0 indicates no interrupts are pending). This is reported to the Controller when it inquires for the device status through GETSTATUS CCC. The encoding of the INTPEND bits allow the Target to report up to 15 numbered interrupts. If more than one interrupt is pending in the user software, it is recommended to return the interrupt number of the highest priority pending interrupt.
Tip: It is recommended for the user to use DMA to read and write from the I3C Transmit and Receive Buffers to ensure that the CPU is able to keep up with the higher I3C speeds. Refer to the Interrupts and DMA Triggers section and "DMA - Direct Memory Access" chapter for more information.
Figure 37-45. Successful IBI Transaction Frame Format

IBI Request Pseudo-code Using Polling

uint8_t payloadData[SIZE];  // payload data to be sent

void I3C1_Target_IBI_Setup()
{
    // Set bus available time
    I3C1BAVL = 64;              // 1us @ I3C1CLK=64MHz

    // Set Bus Timeout (optional; to recover from stalled Controller)
    I3C1CON0bits.BTOEN = 0;
    I3C1BTO = 164;              // 32*F_scl @ F_scl=12.5MHz

    // Set retry limit and mandatory byte
    I3C1RETRY = 3;              // 0=unlimited; 8-bit value
    I3C1IBIMDB = 0x00;          // IBI Mandatory Byte (0x00 = User Defined) 

    // Initialize payload to send
    for(uint8_t i=0; i<SIZE; i++) {
        txData[i] = i;
    }

    // Validate Payload Size
    if(I3C1IBIPSZ > SIZE || I3C1IBIPSZ == 0) { // 0=unlimited (8-bit value)
        I3C1IBIPSZ = SIZE;
    }

    // Pre-load Transmit buffer to prevent an EOD after mandatory byte
    while(!I3C1STAT0bits.TXBE);
    I3C1TXB = payloadData[0];
}

void I3C1_Target_IBI_SendPayload()
{
    uint8_t i = 1; // payloadData[0] is already loaded into TXB

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

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

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

        // Check for End of Transaction
        if(I3C1PIR1bits.TCOMPIF) {
            if(I3C1STAT1bits.TXFNE) {  
                return 0;   // end of transaction due to payload size overflow
            }
            else if(I3C1ERRIR1bits.ABEIF) {
                return 0;   // end of transaction due to Controller abort
            }
            else return 0;  // end of transaction due to TXFIFO empty
        }
    }
}

void I3C1_Target_IBI()
{
    // Check if Target is ready for IBI
    if(I3C1STAT0bits.OPMD == 0b01 && I3C1ECbits.IBIEN) {
        // Begin IBI request
        I3C1CON0bits.IBIREQ = 1;

        // Mandatory Data Byte in I3C1IBIMDB is sent automatically
        // Send IBI Payload
        I3C1_Target_IBI_SendPayload();

        // Check whether IBI process is complete
        while(I3C1CON0bits.IBIREQ);  // IBIREQ clears when process is complete

        // Check if IBI completed successfully
        if(I3C1PIR1bits.IBIDONEIF) { /* Successful IBI */ }
        else if(I3C1ERRIR0bits.IBIEIF || I3C1ERRIR1bits.ABEIF) { /* Error */ }
    }
}