#include #include #include "bsp/irq.h" #include "bsp/mmio.h" #include "bsp/plic.h" #include "bsp/pmod_spi.h" #include "bsp/soc.h" #include "bsp/uart.h" #ifdef PMOD_IRQ_TEST_QUIET #define TEST_PRINTF(...) ((void)0) #define TEST_UART_FLUSH() ((void)0) #else #define TEST_PRINTF(...) printf(__VA_ARGS__) #define TEST_UART_FLUSH() bsp_uart_wait_tx_idle() #endif static volatile uint64_t irq_count; static volatile uint64_t first_irq_timestamp; static volatile uint64_t unexpected_mcause; static volatile uint32_t unexpected_irq_id; void pmod_irq_handler(void) __attribute__((interrupt("machine"), aligned(4))); void pmod_irq_handler(void) { asm volatile( ".option push\n" ".option norelax\n" "la gp, __global_pointer$\n" ".option pop\n" : : : "memory"); const uint64_t timestamp = bsp_read64(BSP_CLINT_MTIME); const uint64_t mcause = bsp_read_mcause(); if(((mcause >> 63) == 0u) || ((mcause & 0xfffu) != BSP_MCAUSE_MACHINE_EXTERNAL)) { unexpected_mcause = mcause; return; } const uint32_t irq_id = bsp_plic_claim(); if(irq_id == BSP_PMOD_IRQ) { /* PLIC gateway is level-sensitive. Mask on the first assertion so a * hand-held HIGH wire cannot create an interrupt storm. */ bsp_plic_disable(BSP_PMOD_IRQ); if(irq_count == 0u) { first_irq_timestamp = timestamp; } irq_count++; } else if(irq_id != 0u) { unexpected_irq_id = irq_id; } if(irq_id != 0u) { bsp_plic_complete(irq_id); } } int main(void) { TEST_PRINTF("\nGenesys2 JB1 direct PLIC interrupt test\n"); TEST_PRINTF("PATH,JB1/V29,Rocket_interrupt_bus,PLIC%lu\n", (unsigned long)BSP_PMOD_IRQ); TEST_PRINTF("EXPECTED_RTL,wrapper_3FF,internal_pulldown; verify after synthesis\n"); TEST_PRINTF("INPUT,LVCMOS33,active_high,level_sensitive\n"); irq_count = 0u; first_irq_timestamp = 0u; unexpected_mcause = 0u; unexpected_irq_id = 0u; bsp_plic_disable_all(); bsp_plic_set_threshold(0u); bsp_plic_set_priority(BSP_PMOD_IRQ, 1u); bsp_pmod_spi_init(); bsp_pmod_spi_set_mode(BSP_PMOD_SPI_MODE_SINGLE); bsp_pmod_spi_set_clock_mode(BSP_PMOD_SPI_CLOCK_MODE0); bsp_set_mtvec(pmod_irq_handler); bsp_plic_enable(BSP_PMOD_IRQ); bsp_enable_machine_external_interrupts(); TEST_PRINTF("PMOD_IRQ_READY,disconnect_JB1_then_touch_JB1_to_3V3_once\n"); TEST_PRINTF("LOOPBACK_READY,wire_JC4_SCK_to_JB1_then_send_t\n"); TEST_UART_FLUSH(); while((irq_count == 0u) && (unexpected_mcause == 0u) && (unexpected_irq_id == 0u)) { const int command = bsp_uart_getc_nonblocking(); if((command == 't') || (command == 'T')) { TEST_PRINTF("LOOPBACK_TRIGGER,source=JC4,destination=JB1\n"); TEST_UART_FLUSH(); for(unsigned int transfer = 0u; transfer < 16u; transfer++) { (void)bsp_pmod_spi_xfer8((uint8_t)(0xa5u ^ transfer)); } if(irq_count == 0u) { TEST_PRINTF("LOOPBACK_NO_IRQ,check_JC4_to_JB1_jumper\n"); TEST_UART_FLUSH(); } } asm volatile("nop"); } if(irq_count != 0u) { TEST_PRINTF("PMOD_IRQ_PASS,id=%lu,count=%lu,tick=%lu,tick_hz=%lu\n", (unsigned long)BSP_PMOD_IRQ, (unsigned long)irq_count, (unsigned long)first_irq_timestamp, (unsigned long)BSP_CLINT_TIMEBASE_HZ); } else { TEST_PRINTF("PMOD_IRQ_FAIL,mcause=%lu,unexpected_id=%lu\n", (unsigned long)unexpected_mcause, (unsigned long)unexpected_irq_id); } TEST_PRINTF("PMOD_IRQ_MASKED,remove_3V3_before_reset_or_reload\n"); TEST_UART_FLUSH(); /* The ASIC testbench observes this through mscratch. Returning success is * therefore reserved for the expected PLIC device-11 ISR path. */ return (irq_count != 0u) ? 0 : 1; }