In the previous blog post we left the CVA6 porting in LiteX basically without interrupt support. Since this is an unacceptable solution, I’ve decided to tackle this problem as second step.

What I had to do:

  1. remove the definition of the UART_POLLING macro (in core.py)
  2. make sure that the interrupt sources were routed as input in the PLIC, included in the cva6_wrapper (in core.py)
  3. define the PLIC’s register map and irq helper functions (in irq.h)
  4. implement the isr() and plic_init() functions for CVA6 (in isr.c) and use them (in crt0.S)

The first 2 points are trivial. The last 2 are not particularly complex, since comparable implementations were already available for other cores (e.g. Rocket or BlackParrot).

However, this is what happened when launching a simulation

        __   _ __      _  __
       / /  (_) /____ | |/_/
      / /__/ / __/ -_)>  <
     /____/_/\__/\__/_/|_|
   Build your hardware, easily!

 (c) Copyright 2012-2022 Enjoy-Digital
 (c) Copyright 2007-2015 M-Labs

 BIOS built on Jul 11 2022 15:32:12
 BIOS CRC passed (30b7046a)

 LiteX git sha1: --------

--=============== SoC ==================--
CPU:		CVA6 @ 1MHz
BUS:		WISHBONE 32-bit @ 4GiB
CSR:		32-bit data
ROM:		128KiB
SRAM:		8KiB


--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
Timeout
No boot medium found

--============= Console ================--

litex> hheellpp

As you notice from the console prompt, every key received via UART was displayed twice. The problem was not simply graphical, but the command was also interpreted incorrectly.

Some debugging led me to the uart_isr() function as source of the problem

void uart_isr(void)
{
	unsigned int stat, rx_produce_next;
	stat = uart_ev_pending_read();
	if(stat &amp; UART_EV_RX) {
		while(!uart_rxempty_read()) {
			rx_produce_next = (rx_produce + 1) &amp; UART_RINGBUFFER_MASK_RX;
			if(rx_produce_next != rx_consume) {
				rx_buf[rx_produce] = uart_rxtx_read();
				rx_produce = rx_produce_next;
			}
			uart_ev_pending_write(UART_EV_RX);
		}
	}
    ...
}

In particular, uart_ev_pending_write() was executed twice, which meant that uart_rxempty_read() returned 0 (i.e. not empty RX FIFO) twice. The waveforms obtained in simulation indicated that the execution of uart_rxempty_read() started before uart_ev_pending_write() has been able to finish its execution and clear the status. Adding a fence instruction right after uart_ev_pending_write() helped solving the problem

        __   _ __      _  __
       / /  (_) /____ | |/_/
      / /__/ / __/ -_)>  <
     /____/_/\__/\__/_/|_|
   Build your hardware, easily!

 (c) Copyright 2012-2022 Enjoy-Digital
 (c) Copyright 2007-2015 M-Labs

 BIOS built on Jul 11 2022 21:08:06
 BIOS CRC passed (30b7046a)

 LiteX git sha1: --------

--=============== SoC ==================--
CPU:		CVA6 @ 1MHz
BUS:		WISHBONE 32-bit @ 4GiB
CSR:		32-bit data
ROM:		128KiB
SRAM:		8KiB


--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
Timeout
No boot medium found

--============= Console ================--

litex> help

LiteX BIOS, available commands:

help                     - Print this help
ident                    - Identifier of the system
crc                      - Compute CRC32 of a part of the address space
flush_cpu_dcache         - Flush CPU data cache

boot                     - Boot from Memory
reboot                   - Reboot
serialboot               - Boot from Serial (SFL)

mem_list                 - List available memory regions
mem_read                 - Read address space
mem_write                - Write address space
mem_copy                 - Copy address space
mem_test                 - Test memory access
mem_speed                - Test memory speed
mem_cmp                  - Compare memory content

The PR is here.

On the bitter side, I’ve not been able to test the implementation on FPGA. I have a Nexys A7 board, hosting a Xilinx Arti7 100T, which is apparently too small for the latest and greatest version of CVA6. An idea for the next iteration could be to investigate this problem.