From 712dd5096dac5dbc4ac4698143befa96236e23f5 Mon Sep 17 00:00:00 2001 From: Adrian Lees Date: Wed, 6 Nov 2024 13:55:00 +0000 Subject: [PATCH] Clear IRQ in the event of a failure Extend the 'IRQ did not fire' improvement to tidy up the interrupt state in the event of a failure to avoid confusion with follow-on problems. If the `intr_test` or `intr_enable` register(s) remain non-zero then subsequent test runs without an intervening system reset will behave unexpectedly. --- sw/cheri/tests/plic_tests.hh | 119 +++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/sw/cheri/tests/plic_tests.hh b/sw/cheri/tests/plic_tests.hh index ae86a1ca..82dbf581 100644 --- a/sw/cheri/tests/plic_tests.hh +++ b/sw/cheri/tests/plic_tests.hh @@ -92,27 +92,28 @@ struct PlicTest { plic_test->error_count += wrong_irq; plic_test->error_count += wrong_local; plic_test->log(irq, uart->interruptEnable & uart->interruptState, wrong_irq || wrong_local); - if (plic_test->is_irq_clearable) { - uart->interruptState = plic_test->ip_irq_id; - } else { - // Ensure that the `intr_test` bit does not keep the Status-type interrupt asserted. - uart->interruptTest = 0; - } - // Disable interrupt to prevent interference with other tests. - uart->interrupt_disable(static_cast(plic_test->ip_irq_id)); + plic_test->uart_irq_clear(uart); }; // Enable and trigger the interrupt now that the handler has been registered. uart->interrupt_enable(uartMap[i].id); uart->interruptTest = ip_irq_id; - wfi(); - if (!irq_fired) { - error_count++; - log(static_cast(0), 0, true); + if (!irq_caught()) { + uart_irq_clear(uart); } - irq_fired = false; } } + void uart_irq_clear(UartPtr uart) { + if (is_irq_clearable) { + uart->interruptState = ip_irq_id; + } else { + // Ensure that the `intr_test` bit does not keep the Status-type interrupt asserted. + uart->interruptTest = 0; + } + // Disable interrupt to prevent interference with other tests. + uart->interrupt_disable(static_cast(ip_irq_id)); + } + void i2c_test(size_t i2c_instance) { instance = i2c_instance; @@ -161,27 +162,28 @@ struct PlicTest { plic_test->error_count += wrong_irq; plic_test->error_count += wrong_local; plic_test->log(irq, i2c->interruptEnable & i2c->interruptState, wrong_irq || wrong_local); - if (plic_test->is_irq_clearable) { - i2c->interruptState = 0x1 << plic_test->ip_irq_id; - } else { - // Ensure that the `intr_test` bit does not keep the Status-type interrupt asserted. - i2c->interruptTest = 0; - } - // Disable interrupt to prevent interference with other tests. - i2c->interrupt_disable(static_cast(plic_test->ip_irq_id)); + plic_test->i2c_irq_clear(i2c); }; // Enable and trigger the interrupt now that the handler has been registered. i2c->interrupt_enable(i2cMap[i].id); i2c->interruptTest = 0x01 << ip_irq_id; - wfi(); - if (!irq_fired) { - error_count++; - log(static_cast(0), 0, true); + if (!irq_caught()) { + i2c_irq_clear(i2c); } - irq_fired = false; } } + void i2c_irq_clear(I2cPtr i2c) { + if (is_irq_clearable) { + i2c->interruptState = 0x1 << ip_irq_id; + } else { + // Ensure that the `intr_test` bit does not keep the Status-type interrupt asserted. + i2c->interruptTest = 0; + } + // Disable interrupt to prevent interference with other tests. + i2c->interrupt_disable(static_cast(ip_irq_id)); + } + void spi_test(size_t spi_instance) { instance = spi_instance; @@ -220,27 +222,28 @@ struct PlicTest { plic_test->error_count += wrong_irq; plic_test->error_count += wrong_local; plic_test->log(irq, spi->interruptEnable & spi->interruptState, wrong_irq || wrong_local); - if (plic_test->is_irq_clearable) { - spi->interruptState = plic_test->ip_irq_id; - } else { - // Ensure that the `intr_test` bit does not keep the Status-type interrupt asserted. - spi->interruptTest = 0; - } - // Disable interrupt to prevent interference with other tests. - spi->interrupt_disable(static_cast(plic_test->ip_irq_id)); + plic_test->spi_irq_clear(spi); }; // Enable and trigger the interrupt now that the handler has been registered. spi->interrupt_enable(spiMap[i].id); spi->interruptTest = ip_irq_id; - wfi(); - if (!irq_fired) { - error_count++; - log(static_cast(0), 0, true); + if (!irq_caught()) { + spi_irq_clear(spi); } - irq_fired = false; } } + void spi_irq_clear(SpiPtr spi) { + if (is_irq_clearable) { + spi->interruptState = ip_irq_id; + } else { + // Ensure that the `intr_test` bit does not keep the Status-type interrupt asserted. + spi->interruptTest = 0; + } + // Disable interrupt to prevent interference with other tests. + spi->interrupt_disable(static_cast(ip_irq_id)); + } + void usbdev_test() { instance = 0; @@ -292,28 +295,31 @@ struct PlicTest { plic_test->error_count += wrong_irq; plic_test->error_count += wrong_local; plic_test->log(irq, usbdev->interruptEnable & usbdev->interruptState, wrong_irq || wrong_local); - if (plic_test->is_irq_clearable) { - usbdev->interruptState = plic_test->ip_irq_id; - } else { - // Ensure that the `intr_test` bit does not keep the Status-type interrupt asserted. - usbdev->interruptTest = 0; - } - // Disable interrupt to prevent interference with other tests. - usbdev->interrupt_disable(static_cast(plic_test->ip_irq_id)); + plic_test->usbdev_irq_clear(usbdev); }; // Enable and trigger the interrupt now that the handler has been registered. usbdev->interrupt_enable(usbdevMap[i].id); usbdev->interruptTest = ip_irq_id; - wfi(); - if (!irq_fired) { - error_count++; - log(static_cast(0), 0, true); + if (!irq_caught()) { + usbdev_irq_clear(usbdev); } - irq_fired = false; } } - inline void wfi() { + void usbdev_irq_clear(UsbdevPtr usbdev) { + if (is_irq_clearable) { + usbdev->interruptState = ip_irq_id; + } else { + // Ensure that the `intr_test` bit does not keep the Status-type interrupt asserted. + usbdev->interruptTest = 0; + } + // Disable interrupt to prevent interference with other tests. + usbdev->interrupt_disable(static_cast(ip_irq_id)); + } + + // Wait with timeout until an interrupt occurs, logging any mismatch. + // Returns true iff the expected interrupt occurred within the timeout interval. + inline bool irq_caught() { ASM::Ibex::global_interrupt_set(true); ASM::Ibex::global_interrupt_set(false); if (!irq_fired) { @@ -323,6 +329,13 @@ struct PlicTest { asm volatile(""); } } + if (!irq_fired) { + error_count++; + log(static_cast(0), 0, true); + return false; + } + irq_fired = false; + return true; } // Log the expected and observed interrupts at both the PLIC and the IP block itself;