= Tandem Programming Reference Manual: Spike Interface
:toc:


== Spike configuration parameter names and scopes

Parameters are referenced by their hierarchical names of the form `PREFIX/PARAM_NAME` that enable a tree-like hierarchical structure.  Supported values of `PREFIX` are:

* `/top`: parameter values apply to the complete platform (which may consist of several cores.)
* `/top/cores`: parameter values apply to all harts, and can be overridden on a core-by-core basis using core-specific values.
* `/top/core/<hartid>`: parameter values apply only to the core with ID `<number>`.

Parameters are explicitly typed.  Three parameter types are supported:

* `bool`: Boolean type with two values: `True` (or integer `1`) and `False` (or integer `0`).
* `uint64_t`: A 64-bit unsigned integer, represented using any of the formats supported in the given context (SystemVerilog or C/C++).
* `string`: A string, appropriately quoted depending on use context (SystemVerilog, C++ source code or Spike command line in Unix shell).

Parameter names are not constrained by the C/C++/SystemVerilog identifier syntax.  However, they cannot contain spaces, forward slash (`/`), backslash (`\`) nor double-quote (`"`) characters.

Some parameters are only meaningful in specific scopes: either platform (`/top/`), or core (`/top/cores/` or `/top/core/<hartid>/`).

== Parameter index

=== Platform-level parameters

[cols="1,1,1,5"]
|===
|Name |Type |Default |Description

|`bootrom`
|bool
|`True`
|`True` if platform contains a boot ROM

|`bootrom_base`
|uint64_t
|`0x10000`
|Base address of boot ROM (unused if boot ROM not present)

|`bootrom_size`
|uint64_t
|`0x1000`
|Size of boot ROM (unused if boot ROM not present)

|`dram_base`
|uint64_t
|`0x80000000`
|Base address of the DRAM

|`dram_enable`
|bool
|`True`
|Presence of the DRAM

|`dram_size`
|uint64_t
|`0x40000000`
|Size of the DRAM

|`generic_core_config`
|bool
|`True`
|Use a common core configuration for all cores.

|`isa`
|string
|`"RV64IMAFDC_zicntr_zihpm"`
|Default ISA string including any standard extensions, in canonical form.

|`log_commits`
|bool
|`True`
|If `True`, generate Spike log of instruction commits and exceptions.

|`max_steps`
|uint64_t
|`2000000UL`
|Maximum number of steps (instruction commits and trap entries) before terminating the ISS simulation if `max_steps_enable` is set to `True`.

|`max_steps_enabled`
|bool
|`False`
|If `True`, obey the limit set in `max_steps`.

|`num_procs`
|uint64_t
|`1`
|Number of cores (harts) in the platform.
|===

=== Per-CSR parameters

All CSRs of cores in the platform can be controlled wrt. their presence ("availability") using a per-CSR parameter.  For each CSR that is available, per-CSR parameters control the initial value, the writable bits and the overall writability of the register.

Depending on the parameter prefix used (`/top/core/<hardtid>/` or `/top/cores/`), the parameters will apply to the CSR of a specific core or they will set a default behavior for the given CSR in all cores.

The supported per-CSR parameters are listed in the table below.  `<CSR>` stands for any valid CSR name recognized by the reference model simulator, in lowercase (as per RISC-V specification).

[cols="1,1,1,5"]
|===
|Name |Type |Default |Description 

|`<CSR>_accessible`
|bool
|`True`
|`True` if register `<CSR>` should be accessible in machine mode. Value `False` will cause an "illegal instruction" exception to be raised on any access to the register.

|`<CSR>_implemented`
|bool
|`True`
|`True` if register `<CSR>` is implemented. Value `False` will cause any writes to this register to be discarded.

|`<CSR>_override_mask`
|uint64_t
|`0x0`
|Mask of bits that should be replaced in the default reset value of `<CSR>`.

|`<CSR>_override_value`
|uint64_t
|`0x0`
|Value to use as a replacement of the default reset value of `<CSR>`.

|`<CSR>_write_mask`
|uint64_t
|`~0x0` (all-ones)
|Mask of writable bits in register `<CSR>`.  Bits which are set allow writing into corresponding bits of `<CSR>`.
|===

=== Core-level parameters

Depending on the prefix used (`/top/cores/` or `/top/core/<hartid>`) the core-level parameters form a default for all cores or apply to a specific core with ID `<hartid>`.

[cols="1,1,1,5"]
|===
|Name |Type |Default |Description 

|`allow_unmapped_mem_access`
|bool
|`False`
|If `True`, do not raise exceptions upon accessing unmapped memory locations; instead, return zeroes on load and fetch accesses, and do nothing on store accesses.

r|`csr_counters_injection`
|bool
|`False`
|If `True`, inject values of microarchitecture-dependent counter CSRs from RTL into the reference model.  Affected CSRs are `cycle`, `cycleh`, `mcycle`, `mcycleh` and `mip`.

|`cvxif_x_num_rs`
|uint64_t
|`3`
|Maximum count of source registers that can be accessed by R4-type instructions of the CV-X-IF interface on this core.

|`debug_exception_handler_addr`
|uint64_t
|`0x1a140000`
|Address of exception handler for exceptions occurring in debug mode

|`debug_handler_addr`
|uint64_t
|`0x1a110800`
|Address of the debug handler

|`debug_injection`
|bool
|`True`
|If `True`, inject debug events from RTL.

|`extensions`
|string
|`""`
|Comma-separated list of Spike extensions to load. +
Extensions currently supported: +
- `cvxif`: implements the CV-X-IF interface; +
- `cv32a60x`: implements CSRs specific to the `CV32A6*X` cores.

|`hide_csrs_based_on_priv`
|bool
|`False`
|Allow to mark CSRs as "not available" based on the privilege modes available.  By default, Spike makes User and Supervisor mode registers accessible, even if Machine privilege level is the only level implemented.
 
|`interrupts_injection`
|bool
|`True`
|If `True`, inject interrupts according to value passed from the RTL simulation in `mcause` CSR into the reference mode.

|`isa`
|string
|`"RV32GC"`
|ISA string including any standard extensions, in canonical form.

|`marchid`
|uint64_t
|`0x3`
|Value to be returned when reading the read-only CSR `<marchid>`.

|`mhartid`
|uint64_t
|`0x0`
|Value to be returned when reading the read-only CSR `<mhartid>`.

|`misaligned`
|bool
|`False`
|If `True`, support misaligned memory accesses.

|`mmu_mode`
|string
|`"sv39"`
|MMU mode of the core(s).

|`mtvec_vectored_alignment`
|uint64_t
|`0x4`
|Default alignment of `mtvec` when using trap vector in vectored mode.

|`mvendorid`
|uint64_t
|`0x00000602`
|Value to be returned when reading the read-only CSR `<mvendorid>`.

|`nmi_mcause`
|uint64_t
|`0x00000020`
|Value of `mcause` which represents a non-maskable interrupt (NMI), without the interrupt bit.  Meaningful only if `interrupt_injection` == `True`.

|`non_standard_interrupts`
|bool
|`False`
|Support non-standard interrupt notification scheme in which arbitrary patterns can be written into the lower 16 bits of `mie` and `mip` CSRs.

|`override_custom_extensions`
|bool
|`True`
|Allow to override the presence of custom ISS extensions (see also `extensions` parameter above.)

|`override_custom_extensions_value`
|bool
|`False`
|If `False`, pretend no custom extensions were specified.  Used to disable all custom extensions at once.

|`pmmpaddr0`
|uint64_t
|`0x0`
|Default value of `pmpaddr0` CSR

|`pmpcfg0`
|uint64_t
|`0x0`
|Default value of `pmpcfg0` CSR

|`pmpregions_max`
|uint64_t
|`0x0`
|Total count of implemented PMP regions in the core(s).  The set of legal values is defined in RISC-V Privileged ISA specification: 0, 16 and 64.

|`pmpregions_writable`
|uint64_t
|`0x0`
|Number of PMP regions with modifiable properties in the core(s), starting from region with index 0.

|`priv`
|string
|`"MSU"`
|Supported privilege levels: "M", "MU" or "MSU"

|trigger_count
|uint64_t
|`0x4`
|Number of supported triggers

|unified_traps
|bool
|`False`
|Process all kinds of traps synchronously as if they were exceptions.
|===


== DPI Programming Interface

The DPI interface is implemented in file `vendor/riscv/riscv-isa-sim/riscv/riscv_dpi.cc` in `core-v-verif` repository (https://github.com/openhwgroup/core-v-verif/).

The shared library providing the implementation of the DPI functions to RTL simulators is named `libriscv.so`.  It is built during Spike build and is installed in the `lib` subdirectory of Spike installation directory, typically `<CVA6_ROOT>/tools/spike/lib`.


=== Create a new instance of Spike extended with DPI interfaces suitable for tandem operation

[,verilog]
----
import "DPI-C" function void spike_create(string filename);
----

[,c++]
----
extern "C" void spike_create(const char *filename);
----

Input args:

* `filename`: name of the ELF file to load into Spike memory.

Return value: NONE.

The instance of Spike will be available in global variable `Simulation *sim`.

=== Destroy an existing instance of Spike with DPI interfaces

[,verilog]
----
import "DPI-C" function void spike_delete();
----

[,c++]
----
extern "C" void spike_delete();
----

Input args: NONE.

Return value: NONE.

Deletes the instance of Spike pointed to by global variable `Simulation *sim`.

=== Step Spike execution through the DPI interface with RVFI state represented as vectors of logic signals

[,verilog]
----
import "DPI-C" function void spike_step_svLogic(inout vector_rvfi core, inout vector_rvfi reference_model);
----

[,c++]
----
extern "C" void spike_step_svLogic(svLogicVecVal* reference, svLogicVecVal* spike);
----

Input args:

* `core`: (mutable) RVFI state of the RTL core represented as signal vector.
* `reference_model`: (mutable) RVFI state of the reference model represented as signal vector.

Return value: NONE.

Before stepping, the entry state of the reference model will be adjusted according to the state of the RTL model.  After the step completes, the return state will represent the result of stepping the reference model for one commit (or exception) from the *updated* input state.

=== Set Boolean parameter `<base><name>` to `<value>`

[,verilog]
----
import "DPI-C" function void spike_set_param_bool(string base, string name, bit value);
----

[,c++]
----
extern "C" void spike_set_param_bool(const char *base, const char *name, bool value);
----

Input args:

* `base`: Base part of parameter name (up to last forward slash character, inclusive)
* `name`: Last member of parameter name (past last forward slash character)
* `value`: Truth value representable on a single bit.

Return value: NONE.

=== Set uint64_t parameter `<base><name>` to `<value>`

[,verilog]
----
import "DPI-C" function void spike_set_param_uint64_t(string base, string name, longint unsigned value);
----

[,c]
----
extern "C" void spike_set_param_uint64_t(const char *base, const char *name, uint64_t value);
----

Input args:

* `base`: Base part of parameter name (up to last forward slash character, inclusive)
* `name`: Last member of parameter name (past last forward slash character)
* `value`: 64-bit unsigned integer value.

Return value: NONE.

=== Set string parameter `<base><name>` to `<value>`

[,verilog]
----
import "DPI-C" function void spike_set_param_str(string base, string name, string value);
----

[,c++]
----
extern "C" void spike_set_param_str(const char *base, const char *name, string value);
----

Input args:

* `base`: Base part of parameter name (up to last forward slash character, inclusive)
* `name`: Last member of parameter name (past last forward slash character)
* `value`: String value.

Return value: NONE.

=== Return the value of Boolean parameter `<base><name>`

[,verilog]
----
import "DPI-C" function bit spike_get_param_bool(string base, string name);
----

[,c++]
----
extern "C" bit spike_get_param_bool(const char *base, const char *name);
----

Input args:

* `base`: Base part of parameter name (up to last forward slash character, inclusive)
* `name`: Last member of parameter name (past last forward slash character)

Return value: Current value of Boolean parameter `<base><name>`.

=== Return the value of uint64_t parameter `<base><name>`

[,verilog]
----
import "DPI-C" function longint unsigned spike_get_param_uint64_t(string base, string name);
----

[,c++]
----
extern "C" uint64_t spike_get_param_uint64_t(const char *base, const char *name);
----

Input args:

* `base`: Base part of parameter name (up to last forward slash character, inclusive)
* `name`: Last member of parameter name (past last forward slash character)

Return value: Current value of uint64_t parameter `<base><name>`

=== Return the value of string parameter `<base><name>`

[,verilog]
----
import "DPI-C" function string spike_get_param_str(string base, string name);
----

[,c++]
----
extern "C" string spike_get_param_str(const char *base, const char *name);
----

Input args:

* `base`: Base part of parameter name (up to last forward slash character, inclusive)
* `name`: Last member of parameter name (past last forward slash character)

Return value: Current value of string parameter `<base><name>`.

=== Set a collection of parameters from a Yaml configuration file

[,verilog]
----
import "DPI-C" function void spike_set_params_from_file(string paramFilePath);
----

[,c++]
----
extern "C" void spike_set_params_from_file(const char *paramFilePath);
----

Sets parameters of the current Spike instance according to the content of file named `<paramFilePath>`.

=== Get current value of a Spike CSR of a specific core

[,verilog]
----
import "DPI-C" function void spike_get_csr(input longint unsigned proc_id, input longint unsigned csr_addr, inout longint unsigned value);
----

[,c++]
----
extern "C" void spike_get_csr(uint64_t proc_id, uint64_t csr_addr, uint64_t &value);
----

Input args:

* `proc_id`: ID of the core to be queried
* `csr_addr`: Address of the CSR
* `value`: Upon return from the function holds the value of the requested CSR.

Return value: NONE (CSR value is passed in an in-out arg).

=== Set value of a Spike CSR for a specific core

[,verilog]
----
import "DPI-C" function void spike_put_csr(input longint unsigned proc_id, input longint unsigned csr_addr, input longint unsigned value);
----

[,c++]
----
extern "C" void spike_put_csr(uint64_t proc_id, uint64_t csr_addr, uint64_t value);
----

Input args:

* `proc_id`: ID of the core to be queried
* `csr_addr`: Address of the CSR
* `value`: Value to be placed in the requested CSR.

Return value: NONE.


//=== C++ Programming Interface

== Command-line interface

Command-line options added to Spike serve a dual purpose:

* run Spike in standalone mode using the same configurations as in tandem mode;
* (development aid) test the effect of individual parameters without editing configuration files.

=== Load Spike parameters from a configuration file

[,sh]
----
--param-file <NAME>
----

Load the parameter tree from Yaml file named `<NAME>`.

The file is expected to contain a valid parameter tree.  Parameters which are not recognized will be silently ignored.

=== Set a single Spike parameter

[,sh]
----
--param <NAME>:<TYPE>=<VALUE>
----

Set parameter `<NAME>` of type `<TYPE>` to value `<VALUE>`.

`<NAME>` must be a full hierarchical parameter name starting with `/top/`.

`<TYPE>` must be one of `bool`, `uint64_t` or `string`.

`<VALUE` must be a valid valid value for type `<TYPE>`.  Boolean values can be represented as numbers `0` and `1`, or symbols `True`, `true`, `False` or `false`.

Integer values will be converted automatically according to the C/C++ notation rules: numbers starting wit `0x` are treated as hexadecimal, numbers starting with a leading `0` followed by digits are treated as octal, and all other numbers are assumed in decimal notation.
