From d6fd533f0cb2d7520a3dce5956ee0e1414e97c76 Mon Sep 17 00:00:00 2001 From: Niklas Eiling Date: Wed, 7 Feb 2024 15:13:41 +0100 Subject: [PATCH] ips/dma: make interrupt handling more robust Signed-off-by: Niklas Eiling --- fpga/lib/ips/i2c.cpp | 87 +++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/fpga/lib/ips/i2c.cpp b/fpga/lib/ips/i2c.cpp index 577700ac8..f2f71cc3e 100644 --- a/fpga/lib/ips/i2c.cpp +++ b/fpga/lib/ips/i2c.cpp @@ -77,24 +77,40 @@ bool I2c::reset() { void I2c::driverWriteBlocking(u8 *dataPtr, size_t size) { int ret; - int intrRetries = 1; + int intrRetries; + int sendRetries = 10; + int bbRetries; transmitIntrs = 0; - xIic.Stats.TxErrors = 0; - do { - ret = XIic_MasterSend(&xIic, dataPtr, size); - if (ret == XST_IIC_BUS_BUSY) { - waitForBusNotBusy(); - } else if (ret != XST_SUCCESS) { - throw RuntimeError("Failed to send I2C data. code: {}", ret); + // We retry the entire transmission a few times before giving up. + while (transmitIntrs == 0 && sendRetries > 0) { + xIic.Stats.TxErrors = 0; + bbRetries = 1; + intrRetries = 10; + // We retry once when the bus is busy. + do { + ret = XIic_MasterSend(&xIic, dataPtr, size); + if (ret == XST_IIC_BUS_BUSY) { + waitForBusNotBusy(); + } else if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to send I2C data. code: {}", ret); + } + } while (ret == XST_IIC_BUS_BUSY && --bbRetries >= 0); + // We need to wait for several interrupts as sending the stop condition involves generating + // multiple interrupts before the transmission is complete. + while (transmitIntrs == 0 && intrRetries > 0) { + ssize_t intrs = irqs[i2cInterrupt].irqController->waitForInterrupt( + irqs[i2cInterrupt].num); + if (intrs == -1) { + break; + } + // logger->trace("I2C write interrupt eventfd read: {}, ISR: {}", numIntr, + // irqStatus); + XIic_InterruptHandler(&xIic); + --intrRetries; } - } while (ret == XST_IIC_BUS_BUSY && --intrRetries >= 0); - intrRetries = 10; - while (transmitIntrs == 0 && intrRetries > 0) { - irqs[i2cInterrupt].irqController->waitForInterrupt(irqs[i2cInterrupt].num); - XIic_InterruptHandler(&xIic); - --intrRetries; + --sendRetries; } - if (intrRetries == 0) { + if (sendRetries == 0) { throw RuntimeError( "Failed to send I2C data: No transmit interrupt after 10 tries."); } @@ -106,23 +122,33 @@ void I2c::driverWriteBlocking(u8 *dataPtr, size_t size) { void I2c::driverReadBlocking(u8 *dataPtr, size_t max_read) { int ret; - int intrRetries = 1; + int intrRetries; + int readRetries = 10; + int bbRetries; receiveIntrs = 0; - do { - ret = XIic_MasterRecv(&xIic, dataPtr, max_read); - if (ret == XST_IIC_BUS_BUSY) { - waitForBusNotBusy(); - } else if (ret != XST_SUCCESS) { - throw RuntimeError("Failed to receive I2C data: code {}", ret); + while (receiveIntrs == 0 && readRetries > 0) { + intrRetries = 10; + bbRetries = 1; + do { + ret = XIic_MasterRecv(&xIic, dataPtr, max_read); + if (ret == XST_IIC_BUS_BUSY) { + waitForBusNotBusy(); + } else if (ret != XST_SUCCESS) { + throw RuntimeError("Failed to receive I2C data: code {}", ret); + } + } while (ret == XST_IIC_BUS_BUSY && --bbRetries >= 0); + while (receiveIntrs == 0 && intrRetries > 0) { + ssize_t intrs = irqs[i2cInterrupt].irqController->waitForInterrupt( + irqs[i2cInterrupt].num); + if (intrs == -1) { + break; + } + XIic_InterruptHandler(&xIic); + --intrRetries; } - } while (ret == XST_IIC_BUS_BUSY && --intrRetries >= 0); - intrRetries = 10; - while (receiveIntrs == 0 && intrRetries > 0) { - irqs[i2cInterrupt].irqController->waitForInterrupt(irqs[i2cInterrupt].num); - XIic_InterruptHandler(&xIic); - --intrRetries; + --readRetries; } - if (intrRetries == 0) { + if (readRetries == 0) { throw RuntimeError( "Failed to receive I2C data: No receive interrupt after 10 tries."); } @@ -136,9 +162,12 @@ void I2c::waitForBusNotBusy() { irqStatus = XIic_ReadIisr(xIic.BaseAddress) & XIic_ReadIier(xIic.BaseAddress); } while (!(irqStatus & XIIC_INTR_BNB_MASK) && --retries > 0); + // logger->trace("I2C bus not busy after {} interrupts", 10 - retries); //Deactivate BusNotBusy interrupt XIic_WriteIier(xIic.BaseAddress, XIic_ReadIier(xIic.BaseAddress) & ~(XIIC_INTR_BNB_MASK)); + uint32_t clear = XIIC_INTR_BNB_MASK; + XIic_WriteIisr(xIic.BaseAddress, clear); if (retries == 0) { throw RuntimeError("I2C bus stayed busy after 10 interrupts"); }