How to add a processor in LiteX?
Introduction
Two methods can be done:
- Including the processor code in LiteX framework (see CV32E40P [4]).
- Importing the processor code from an external repository (see FemtoRV [5]).
The choice mainly depends on how the HDL code is written. This tutorial focuses on the first method as targeted processors will be composed of several HDL files. We will see later how it is configured.
LiteX workflow for custom processors
This tutorial is based on the work done by Antmicro for the CV32E40P [4].
LiteX configuration for the CV32E40P
Adding a new processor can be done in 4 steps as for [4].
- Adding the CPU in LiteX CPU catalog.
- Adding the Python CPU wrapper address in
litex_setup.py
. - Adding support for the CPU in the LiteX BIOS.
- Adding CPU specific code in the software + SoC builder scripts.
Following section explain the integration flow in details for another processor.
Case study - Integration of the CV32E41P [1]
The main goal of this section is to describe precisely steps needed to include a new processor in the LiteX framework. The OpenHwGroup CV32E41P has been chosen as it has a structure similar to the upstream CV32E40P.
CV32E41P architecture
Top-level schematic
CV32E41P and CV32E40P are represented by the same block diagram in OpenHwGroup documentations.
Interrupt controller
Document about interrupts are given at [6] and [7] (for instance, sections 3.1.6.1 and 3.1.9).
Things to be aware of:
The
irq_i[31:0]
interrupts are controlled via themstatus
,mie
andmip
CSRs. CV32E41P uses the upper 16 bits ofmie
andmip
for custom interrupts (irq_i[31:16]
), which reflects an intended custom extension in the RISC-V Basic (a.k.a. CLINT) interrupt architecture.
Here is the structure for the mip
register (cf. 3.1.9 of the RISC-V privileged specification):
- MEIP/SEIP are for external interrupts.
- MTIP/STIP for timer interrupts.
- MSIP/SSIP for software interrupts.
There is a similar structure for the mie
containing enable bits for those interrupts.
LiteX configuration for the CV32E41P [3]
Adding the CPU in LiteX CPU catalog
The goal is to create the wrapper between the HDL code and Python. Fortunately, this step can be done in a single commit.
|
|
Afterwards, one of the maintainers will create the https://github.com/litex-hub/pythondata-cpu-cv32e41p repository:
-
It includes a list of SystemVerilog needed for the Python SoC builder: https://github.com/litex-hub/pythondata-cpu-cv32e41p/blob/master/pythondata_cpu_cv32e41p/system_verilog/cv32e41p_manifest.flist.
-
The repository includes a copy of the HDL source which is updated on a regular basis.
Adding the Python CPU wrapper address in litex_setup.py
In this step, we just had the URL of the previously generated wrapper into the main LiteX repository:
|
|
Adding support for the CPU in the LiteX BIOS
Here we have to add some code in order to handle interrupts. mcause
is the “Machine Cause Register”:
The goal is to catch an exception coming from the UART.
unsigned int cause = csrr(mcause) & IRQ_MASK
gives the exception code field frommcause
.- LiteX uses fast interrupts instead of existing external/timer/software interrupts. As a consequence, for an UART interrupt, the ISR needs
UART_INTERRUPT+FIRQ_OFFSET
to get the first fast interrupt. - It will finally call the existing UART ISR.
Adding CPU specific code in the software + SoC builder scripts
The most important step as it includes the top-level of the CPU in Python and add other software elements. Changes are described file by file. The pull request for the CV32E41P is at: https://github.com/enjoy-digital/litex/pull/1207/commits/d4448827ac109454acb0427a4e204a1b27b98849
cores/cpu/cv32e41p/init.py
|
|
cores/cpu/cv32e41p/boot-helper.S
|
|
cores/cpu/cv32e41p/core.py
This file may be the most important one as it describes the top-level of the CPU in Python.
GCC_FLAGS
: the CV32E41P is a RV32IMC (the floating point unit isn’t implemented by default).- OPI/APB: LiteX uses the OBI/APB interfaces to connect to the Wishbone bus. Part related to buses and bridges were copy-pasted from existing CPUs.
add_manifest_sources
: it will list SystemVerilog files given at: https://github.com/litex-hub/pythondata-cpu-cv32e41p/blob/master/pythondata_cpu_cv32e41p/system_verilog/cv32e41p_manifest.flistclass CV32E41P(CPU)
: the Python class describing the processor. This class is quite easy to understand when we compare it with the top-level SystemVerilog file:- Only input ports are given in this file. Not for instruction/data buses. Example:
i_pulp_clock_en_i
is related topulp_clock_en_i
in SystemVerilog.- Syntax: a prefix
i_
is added on the SystemVerilog port name for inputs.
i_irq_i = Cat(self.interrupt_padding,self.interrupt)
was a trick due to the structure of the interrupt controller in the CV32E41P.- https://github.com/openhwgroup/cv32e41p/blob/master/rtl/cv32e41p_int_controller.sv
- https://docs.openhwgroup.org/projects/openhw-group-cv32e41p/exceptions_interrupts.html (for fast interrupts).
- 16 fast interrupt lines + a padding of 16 to match the 32-bit width of the port in the interrupt controller.
- Finally, the developer has to take care of values given for inputs (as for HDL testbenches). A deep reading of CPU specifications may be needed here.
- Only input ports are given in this file. Not for instruction/data buses. Example:
cores/cpu/cv32e41p/crt0.S
This file is a set of startup routines. In the vector table, interrupt numbers can be found in the CPU specifications. For instance, in the Machine Interrupt Enable Register: https://docs.openhwgroup.org/projects/openhw-group-cv32e41p/control_status_registers.html#machine-interrupt-enable-register-mie
Bit # | Mode | Description |
---|---|---|
31:16 | RW | Machine Fast Interrupt Enables: Set bit x to enable interrupt irq_i[x]. |
11 | RW | Machine External Interrupt Enable (MEIE): If set, irq_i[11] is enabled. |
7 | RW | Machine Timer Interrupt Enable (MTIE): If set, irq_i7 is enabled. |
3 | RW | Machine Software Interrupt Enable (MSIE): if set, irq_i3 is enabled. |
|
|
cores/cpu/cv32e41p/csr-defs.h
Some constants related to CSR. For instance, #define CSR_MSTATUS_MIE 0x8
is related to the position of the mie
bit in the mstatus
CSR:
cores/cpu/cv32e41p/irq.h
API to control interrupts. Copy-pasted from existing CPUs.
cores/cpu/cv32e41p/system.h
API to control interrupts. Copy-pasted from existing CPUs.
References
- CV32E41P documentation. https://docs.openhwgroup.org/projects/openhw-group-cv32e41p/index.html
- CV32E41P HDL repository. https://github.com/openhwgroup/cv32e41p
- CV32E41P pull request in LiteX. https://github.com/enjoy-digital/litex/pull/1207
- CV32E40P pull request in LiteX. https://github.com/enjoy-digital/litex/pull/535
- FemtoRV support in LiteX. https://github.com/enjoy-digital/litex/commit/b1d8fe61f8613babeb486877a094bff759e31fd0
- CORE-V CV32E41P User Manual - Exceptions and Interrupts. https://docs.openhwgroup.org/projects/openhw-group-cv32e41p/exceptions_interrupts.html
- Andrew Waterman, Krste Asanovic, John Hauser.The RISC-V Instruction Set Manual - Volume II: Privileged Architecture. https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf