This BSP provides the code to support running programs on the CV32 verification
target. It performs initialization tasks (crt0.S
), handles
interrupts/exceptions (vectors.S
, handlers.S
), provides syscall
implementations (syscalls.c
) and includes a linker script (link.ld
) to
control the placement of sections in the binary.
Each file is described in more detail below followed by instructions for building and using the BSP.
The C Runtime file crt0.S
provides the _start
function which is the entry
point of the program and performs the following tasks:
- Initialize global and stack pointer.
- Store the address of
vector_table
inmtvec
, setting the lower two bits to0x2
to select vectored interrupt mode. - Zero the BSS section.
- Invoke initialization of C constructors and set destructors to be called on exit.
- Zero
argc
andargv
(the stack is not initialized, so these are zeroed to prevent uninitialized values causing a mismatch against the reference result). - Call
main
. - If
main
returns, callexit
with its return code.
When a RISC-V core traps on an interrupt/exception, the pc
is stored in mepc
and the reason for the trap is stored in mcause
. The MSB
of mcause
is set to 0
for an exception and 1
for an interrupt; the remaining bits
mcause[MXLEN-2:0]
contain the exception code. The table of mcause
values is
defined in Table 3.6 of the RISC-V Instruction Set Manual Volume II: Privileged
Architecture Version 20190608-Priv-MSU-Ratified.
The core jumps to a location in the vector table according to the BASE
address
of the vector table stored in mtvec
and the value of the exception code in
mcause
. In vectored mode, all exceptions jump to BASE
and interrupts jump to
BASE+4*mcause[XLEN-2:0]
. Note that because user software interrupts have
exception code 0
, they jump to the same location as exceptions, therefore the
user software interrupt handler must also handle exceptions.
The vector table is defined in vectors.S
and may jump to one of the
following interrupt request handlers in handlers.S
:
u_sw_irq_handler
- handles user software interrupts and all exceptions. Saves all caller saved registers then checksmcause
and jumps to the appropriate handler as follows:- Breakpoint: jump to
handle_ebreak
. - Illegal instruction: jump to
handle_illegal
. - Environment call from M-mode: jump to
handle_ecall
. - Any other exception or user software interrupt: jump to
handle_unknown
.
- Breakpoint: jump to
m_software_irq_handler
- handles machine-mode software interrupts- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_timer_irq_handler
- handles machine-mode timer interrupts- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_external_irq_handler
- handles machine-mode external interrupts- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast0_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast1_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast2_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast3_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast4_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast5_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast6_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast7_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast8_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast9_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast10_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast11_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast12_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast13_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast14_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
m_fast15_irq_handler
- handles machine-mode fast external interrupts (platform extension for CV32)- Currently jumps to
__no_irq_handler
. Behavior to be defined in future commit.
- Currently jumps to
__no_irq_handler
- loops printing "no exception handler installed".
The following exception handlers may be called from u_sw_irq_handler
:
handle_ecall
- callshandle_syscall
which checks the syscall number and calls the corresponding syscall function.handle_ebreak
- currently just prints "ebreak exception handler entered"handle_illegal_insn
- prints "illegal instruction exception handler entered"unknown_handler
- called when there is no handler for the interrupt/ exception. This is the only case wheremepc
is not incremented, because we do not know the appropiate action to take.
Returning from the u_sw_irq_handler
. All handlers called by u_sw_irq_handler
increment mepc
before calling mret
, except for unknown_handler
. Handlers
that require mepc
to be incremented jump to end_handler_incr_mepc
otherwise
they jump to end_handler_ret
. All caller saved registers are restored before
finally calling mret
.
Some test cases require the ability to override the default handlers. In future,
these handlers will be made overridable by defining their labels as .weak
symbols. Test cases can then provide their own handlers where necessary.
On a bare-metal system there is no OS to handle system calls, therefore, we
define our own system calls in syscalls.c
. For example, the implementation of
_write
outputs a byte at a time to the virtual printer peripheral. Many of the
functions provide minimal implementations that simply fail gracefully due to
lack of necessary OS support e.g. no file system.
The RISC-V Instruction Set Manual Volume I: Unprivileged ISA Version 20191213 states that
for an ecall
the "ABI for the system will define how parameters for the
environment request are passed". This BSP follows the convention used for RISC-V
in newlib
. Parameters are passed in registers a0
to a5
and system call ID
in a7
(t0
on RV32E). When handling an ecall
, handle_ecall
calls
handle_syscall
which then calls the appropriate function that implements the
system call, passing parameters as necessary.
The linker script defines the memory layout and controls the mapping of input sections from object files to output sections in the output binary.
The link.ld
script is based on the standard upstream RV32 linker script, with
some changes required for CV32:
- Memory layout is defined as follows:
ram
start=0x0, length=4MBdbg
start=0x1A110800, length=2KB
- Changes to output section placement are as follows:
.vectors
start=ORIGIN(ram
).init
start=0x80.heap
starts at end of data and grows upwards.stack
starts at the end ofram
and grows downwards.debugger
start=ORIGIN(dbg
).debugger_exception
start=0x1A110C00.debugger_stack
follows.debugger_exception
The BSP can be built in this directory as follows:
make
This produces libcv-verif.a which can then be linked with a test program as follows:
gcc test-program.c -nostartfiles -T/path/to/bsp/link.ld -L/path/to/bsp/ -lcv-verif