JumpStart provides a bare-metal kernel, APIs and build infrastructure for writing directed diags for RISC-V CPU/SoC validation.
A Diag is expected to provide sources (C and assembly files) and it's attributes in a YAML file.
For a Quick Start Guide, see Anatomy of a Diag which provides a detailed explanation of test021 which is a 2-core diag that modifies a shared page table in memory and checks that the change is visible to both cores.
- Diag Sources
- Diag Attributes
- JumpStart APIs
- Building and Running Diags
- Running Unit Tests
- Debugging with GDB
Diags are written in C and/or Assembly.
The diag sources are required to contain a main() function which is the entry point for the diag that JumpStart jumps to. The linker will automatically place diag code (including main()) in the .text section. JumpStart expects that the main() function is always in the .text section. A mapping for the .text section should be present in the diag's attribute file.
Machine, Supervisor and User mode cannot share code so the code for different modes have to be placed in different linker sections. The linker_script_section in the mappings diag attribute should be used by the diag to place the code for different modes in different sections.
JumpStart provides a set of basic API functions that the diag can use. Details HERE.
The diag exits by returning from main() with a DIAG_PASSED or DIAG_FAILED return code. Alternatively, the diag can call jumpstart_mmode_fail() or jumpstart_smode_fail() functions if a clean return from main() is not possible. On return from the diag, JumpStart will exit the simulation with the appropriate exit code and exit sequence for the simulation environment.
The JumpStart Unit Tests are a good reference on writing diags:
Diags are expected to follow the RISC-V ABI Calling Convention.
The Thread Pointer (x4) and Global Pointer (x3) registers are reserved for JumpStart purposes and should not be used in diags. TP is used to point to a per cpu attributes structure and GP is used as a temporary in JumpStart routines.
The Diag Attributes file specifies the memory layout and various attributes of the diag such as the MMU mode, number of active cpus, etc.
The default diag attribute values are defined in the Source Attributes YAML file.
Binary bitmask controlling how many active cpus are in the diag. Any cpu that is not part of the bitmask will be sent to wfi.
Default: 0b1 or 1 cpu active.
Specifies the active cpus in the diag. The default is 0b1 or 1 cpu active.
Enable the Virtualization extension.
Default: False.
The MMU mode that will be programmed into the corresponding *ATP register.
Valid values: bare, sv39, sv48, sv39x4, sv48x4.
Controls whether the diag's main() will be called in M-mode or S-mode.
NOTE: Diags that run in sbi_firmware_boot mode (where JumpStart starts in S-mode after SBI Firmware runs) cannot start in M-mode.
Default: False. The diag's main() will be called in S-mode.
The address at which the start of the Machine, Supervisor and User mode sections will be placed by the linker.
The maximum number of 4K pages that can be used to allocate Page Tables for each translation stage.
The number of 4K pages allowed for the .bss and .rodata sections respectively.
Allows the diag to modify the page tables.
Default: False. The page tables regions are marked Read Only in the page table map. This prevents accidental modification of the page tables from supervisor mode.
Example: test021.
Controls the memory layout and attributes of all the sections of the diag.
Controls the virtual, guest physical, physical and supervisor physical addresses of the mapping.
Specifies the list of MMUs that this mapping will be set up for.
MMUs currently supported: cpu, iommu.
Default: ["cpu"]
Controls the translation stage (S, VS, G) that this mapping will be used in. The S stage is the single stage translation and the VS and G stages are the two stage translation.
Default: If not explicitly specified, the stage will be inferred based on the va, gpa, pa, spa attributes. A bare mapping will only have a gpa, pa or a spa attribute.
Controls the values of the xwr, umode and valid bits in the page table entry for the section.
Controls the page size of the section.
The page size has to conform to the sizes supported by the SATP mode.
Controls the number of page_size pages allocated for the section.
Controls the number of page_size pages allocated per CPU for the section. The total number of pages allocated will be num_pages_per_cpu multiplied by max_num_cpus_supported.
This attribute is mutually exclusive with num_pages - only one of them can be specified for a mapping. When num_pages_per_cpu is used, the memory allocation scales automatically with the number of CPUs supported by the system.
Example: If num_pages_per_cpu: 2 and max_num_cpus_supported: 4, then 8 total pages will be allocated for the section.
Indicates whether this is a VA alias. It's PA should be contained in the PA range of another mapping.
Controls whether the diag will allocate page table entries for the section.
If not explicitly specified, this will be inferred based on the translation stage. It will be set to True for bare mappings and False for non-bare mappings.
Default: None.
The memory type of the section. This is used to set the memory type for the PMARR region that holds this section.
The name of the linker script section that this section will be placed in.
Machine, Supervisor and User mode cannot share code so the code for different modes have to be placed in different linker sections.
This takes a comma separated list of all the sections that should be placed together. For example:
linker_script_section: ".text,.text.end"The sections .text and .text.end will be placed together in the .text linker script section - the name of the first section in the list is used as the linker script section name:
. = 0x80000000;
.text : {
*(.text)
*(.text.end)
}
scripts/build_diag.py is the preferred way to build diags and optionally run them on spike.
It will place the build and run artifacts into --diag_build_dir. It produces the ELFs, run traces (for spike), build_manifest.repro.yaml file to reproduce the build, etc.
It will produce a summary indicating status for each diag.
Summary
Build root: /tmp/diag
Build Repro Manifest: /tmp/diag/build_manifest.repro.yaml
┏━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Diag ┃ Build ┃ Run [spike] ┃ Result ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ test000 │ PASS (4.45s) │ PASS (0.38s) │ /tmp/diag/test000/test000.elf │
├─────────┼──────────────┼──────────────┼───────────────────────────────┤
│ test010 │ PASS (4.52s) │ PASS (0.38s) │ /tmp/diag/test010/test010.elf │
├─────────┼──────────────┼──────────────┼───────────────────────────────┤
│ test002 │ PASS (4.49s) │ PASS (0.39s) │ /tmp/diag/test002/test002.elf │
└─────────┴──────────────┴──────────────┴───────────────────────────────┘
Run Manifest:
/tmp/diag/run_manifest.yaml
STATUS: PASSED
The script uses an environment-based configuration system that determines the run_target, boot configuration, and other build settings. Environments are defined in scripts/build_tools/environments.yaml and can inherit from other environments.
Available environments include:
spike: Run on Spike simulator with fw-none boot configuration
Each environment can specify:
run_target: The run_target to run the diag on (spike, etc.)override_meson_options: Meson options to override for this environmentoverride_diag_attributes: Diag attributes to override for this environmentextends: Parent environment to inherit from
The preferred way to build and run using JumpStart is to use the scripts/build_diag.py script.
A list of diag source directories containing the diag's sources and attributes file.
A manifest file containing a list of multiple diags to be built. The manifest file can also contain global overrides for override_meson_options, override_diag_attributes and diag_custom_defines that are applied to all diags in a manifest. See diags/sival/ddr.diag_manifest.yaml in the ctest repo for an example.
Required. The environment to build and run for.
Available environments can be listed by running:
jumpstart/scripts/build_diag.py --helpEnvironment related extra arguments can be listed by running:
jumpstart/scripts/build_diag.py -e <environment> --helpThe environment determines:
- The run_target (spike, etc.)
- Boot configuration (fw-none)
- Default meson options and diag attributes
Used to override the meson options specified in meson.options or those set by the environment.
Used to override the diag attributes specified in the attributes file or those set by the environment. This will override the attributes specified in the diag's attributes file.
Override per diag custom defines.
Filter diagnostics when using a manifest. Only valid with --build_manifest and incompatible with --diag_src_dir.
--include_diags name1 name2: Build only the listed diagnostics from the manifest; errors if a name is not present.--exclude_diags name1 name2: Build all diagnostics except the listed ones; errors if a name is not present.
Meson build type to use. Choices: release, minsize, debug, debugoptimized. Defaults to release if not specified.
Compiler toolchain. Choices: gcc. Default: gcc.
Builds the diag but does not run it on the run_target (skips trace generation/run phase).
Required. Output directory for built artifacts. A subdirectory is used for Meson build artifacts.
Keep the temporary Meson build directory (useful for inspecting logs/artifacts on failures). Default: false.
Seed for randomized build/run behavior. Accepts Python int literals (e.g., 1234, 0xdeadbeef, 0b1010). If not provided, uses rng_seed from the manifest or auto-generates a random seed.
Enable verbose logging.
Number of parallel compile jobs.
See --help for all options.
Use the justfile to build and run unit tests during development.
Run just --list to see all the available commands.
Examples:
# Build all unit tests with GCC targeting release build and run on Spike.
just test gcc release spikeThese are listed in the header files in the include directory.
Functions with names that end in _from_smode() or _from_mmode() can only be called from the respective modes.
JumpStart provides a heap-based memory management system that supports allocations from DDR memory with different memory attributes (WB, WC, UC).
If the diag attribute enable_heap is set to True a DDR WB heap will be initialized for use.
Custom heaps (of any memory type and size) must be explicitly set up to point to memory regions in the memory map of the diag. Note that multiple heaps can be active at a time but only one heap of a particular type (memory backing and memory attribute) can be set up at at time.
malloc(),free(),calloc(),memalign(): Default memory allocation functions that use DDR WB memory.
malloc_from_memory(),free_from_memory(),calloc_from_memory(),memalign_from_memory(): Memory allocation functions that allow specifying the backing memory and memory type.
setup_heap(): Initialize a new heap with specified backing memory and memory type.deregister_heap(): Clean up and remove a previously initialized heap. All allocations from this heap have to be freed before deregistering the heap.get_heap_size(): Get the total size of a specific heap.
The following constants are defined for use with these functions:
Backing Memory Types:
BACKING_MEMORY_DDR: Standard DDR memory
Memory Types:
MEMORY_TYPE_WB: Write-Back cached memoryMEMORY_TYPE_WC: Write-Combining memoryMEMORY_TYPE_UC: Uncached memory
Example usage:
// Set up a 4MB uncached DDR heap
setup_heap(0xA0200000, 0xA0200000 + 4 * 1024 * 1024,
BACKING_MEMORY_DDR, MEMORY_TYPE_UC);
// Allocate from the uncached heap
void* buf = malloc_from_memory(size, BACKING_MEMORY_DDR, MEMORY_TYPE_UC);
// Clean up when done
free_from_memory(buf, BACKING_MEMORY_DDR, MEMORY_TYPE_UC);
deregister_heap(BACKING_MEMORY_DDR, MEMORY_TYPE_UC);Returns the cpu id of the cpu calling the function. Can only be called from S-mode.
read_csr(), write_csr(), read_write_csr(), set_csr(), clear_csr(), read_set_csr() and read_clear_csr()
Operates on the specified CSR. The CSR names are passed to the RISC-V csrr and csrw instructions so the names should match what GCC expects.
run_function_in_smode(), run_function_in_umode(), run_function_in_vsmode() and run_function_in_vumode()
Diags can use these functions to run functions in the corresponding modes. Each function can be passed up to 6 arguments.
run_function_in_smode() can only be called from M-mode.
run_function_in_umode() and run_function_in_vsmode() can only be called from S-mode.
run_function_in_vumode() can only be called from VS-mode.
The different modes cannot share the same pages so the functions belonging to each mode should be tagged with the corresponding linker script section name to place them in different sections.
IMPORTANT: The return values of these functions should be checked. The only way to tell if the function ran successfully is to check the return value.
Refer to Unit Tests test002, test011, test018, test045, test048 for examples of how these functions can be called and how the memory map can be set up.
Disables the MMU. The page tables are set up and the MMU is enabled by default when the diag starts.
Synchronization point for all active cpus in the diag.
Synchronization point for a specific subset of CPUs specified by a CPU mask. This function provides more flexible synchronization than sync_all_cpus_from_smode() by allowing diags to synchronize only specific CPUs.
Parameters:
cpu_mask: A bitmask specifying which CPUs should participate in the synchronization. Each bit represents a CPU ID (bit 0 = CPU 0, bit 1 = CPU 1, etc.)sync_point_address: The address of a 4-byte aligned memory location to use as the synchronization point. Each CPU combination should use its own unique sync point to avoid conflicts.
Important Notes:
- Each CPU combination must use its own dedicated sync point to prevent synchronization conflicts
- The sync point must be 4-byte aligned and placed in a memory section accessible to all participating CPUs
- Only CPUs specified in the mask will participate in the synchronization
- The primary CPU (lowest CPU ID in the mask) coordinates the synchronization process
See test019 for examples of how the sync functions can be used.
Allows the diag to register a trap handler override function for M-mode traps. The registered function will be called when the trap occurs in M-mode.
Allows the diag to register a trap handler override function for S-mode traps. The registered function will be called when the trap occurs in S-mode.
Allows the diag to register a trap handler override function for VS-mode traps. The registered function will be called when the trap occurs in VS-mode.
These functions can be used to get and set the MEPC/SEPC during an exception. Allows modification of the EPC before returning from the exception.