RiescueD User Guide

Welcome to RiescueD, the RiESCUE Directed Test Framework - a powerful Python library for generating RISC-V directed tests with randomization capabilities, memory management, and comprehensive environment simulation.

What is RiescueD?

RiescueD is a directed test framework that allows you to write assembly tests with special directives that provide:

  • Randomization: Generate random data and addresses

  • Memory Management: Automatic page table generation and memory mapping

  • Environment Simulation: Support for various privilege modes, paging modes, and virtualization

  • OS Code Simulation: Pseudo OS for scheduling and exception handling

Key Features

  • Random address and data generation with constraints

  • Automatic page table creation for virtual memory testing

  • Support for multiple paging modes (sv39, sv48, sv57, bare)

  • Multiple privilege levels (machine, supervisor, user)

  • Virtualization support (bare metal, virtualized)

  • Exception handling and pass/fail conditions

  • RISC-V extension support (RVA23 and more)

Getting Started

Basic Workflow

  1. Write Test: Create a .s file with assembly code and RiescueD directives

  2. Configure System: Provide CPU configuration in a JSON file

  3. Generate: Run RiescueD to produce ELF binary, assembly file, and linker script

  4. Simulate: Run the generated test on your target simulator

# Basic usage
python3 riescued.py run --testname test.s --cpuconfig cpu_config.json

# With specific environment settings
python3 riescued.py run --testname test.s \
  --cpuconfig cpu_config.json \
  --test_paging_mode sv39 \
  --test_privilege_mode supervisor \
  --test_env bare_metal

RiescueD Directives Reference

RiescueD directives are special comments that start with ;# and provide powerful test generation capabilities.

Test Header Directives

Test header directives define the overall test configuration and are placed at the beginning of your test file.

;#test.name       my_test
;#test.author     your.email@company.com
;#test.arch       rv64
;#test.priv       machine super user any
;#test.env        virtualized bare_metal any
;#test.cpus       1
;#test.paging     sv39 sv48 sv57 disable any
;#test.category   arch
;#test.class      vector
;#test.features   ext_v.enable ext_fp.disable
;#test.tags       vectors load_store

Available Test Header Options:

  • test.name: Unique test identifier

  • test.author: Author email address

  • test.arch: Target architecture (rv32, rv64)

  • test.priv: Privilege modes (machine, super, user, any)

  • test.env: Test environment (virtualized, bare_metal)

  • test.paging: Paging modes (sv39, sv48, sv57, disable, any)

  • test.features: Extension configuration (ext_name.enable/disable)

  • test.tags: Descriptive tags for categorization

Random Data Generation

Generate random data values with optional constraints:

;#random_data(name=data1, type=bits32, and_mask=0xfffffff0)
;#random_data(name=data2, type=bits64, and_mask=0xffffffffffffffff)
;#random_data(name=data3, type=bits20)

Parameters:

  • name: Variable name to reference in your code

  • type: Data width (bits8, bits16, bits32, bits64, bits20, bits22, etc.)

  • and_mask: Optional mask to constrain random values

Usage in Assembly:

.section .data
my_data:
    .dword data1    # Uses the random value generated
    .dword data2

Random Address Generation

Generate random addresses for memory operations:

;#random_addr(name=lin1, type=linear, size=0x1000, and_mask=0xfffffffffffff000)
;#random_addr(name=phys1, type=physical, size=0x1000, and_mask=0xfffffffffffff000)
;#random_addr(name=io_addr, type=physical, io=1, size=0x1000, and_mask=0xfffffffffffff000)

Parameters:

  • name: Address variable name

  • type: Address space type

    • linear: Virtual/linear address space

    • physical: Physical address space

  • size: Size of the memory region

  • and_mask: Address alignment mask

  • io: Mark as I/O region (optional, default=0)

Memory Reservation

Reserve specific memory regions:

;#reserve_memory(start_addr=0x600000, addr_type=linear, size=0x1000)
;#reserve_memory(start_addr=0x500000, addr_type=physical, size=0x1000)

Parameters:

  • start_addr: Starting address (hexadecimal)

  • addr_type: Address space (linear, physical)

  • size: Size of reserved region

Page Table Generation

Automatically generate page table entries:

;#page_mapping(lin_name=lin1, phys_name=phys1, v=1, r=1, w=1, x=1, a=1, d=1, pagesize=['4kb', '2mb', '1gb', '512gb', '256tb', 'any'])
;#page_mapping(lin_addr=0x5000000, phys_addr=0x5000000, v=1, r=1, w=1, pagesize=['4kb'])
;#page_mapping(lin_name=lin2, phys_name=&random, v=1, r=1, w=1, pagesize=['2mb'])

Parameters:

  • lin_name / lin_addr: Linear (virtual) address or variable name

  • phys_name / phys_addr: Physical address or variable name

  • v: Valid bit (0 or 1)

  • r: Read permission (0 or 1)

  • w: Write permission (0 or 1)

  • x: Execute permission (0 or 1)

  • a: Accessed bit (0 or 1)

  • d: Dirty bit (0 or 1)

  • pagesize: Page size options

    • '4kb': 4KB pages

    • '2mb': 2MB pages

    • '1gb': 1GB pages

    • '512gb': 512GB pages

    • '256tb': 256TB pages

    • 'any': Let RiescueD choose

Special Values:

  • &random: Use a random physical address

  • modify_pt=1: Allow modification of page table entry during test. Creats page pointing to each level of the page table. These pages can be used to read the page table entries to do read modified write to pagetables.

Memory Initialization

Initialize memory sections with data:

;#init_memory @section_name
.section .section_name, "aw"
    .dword data1
    .dword data2

This directive initializes the memory region with the specified data.

Example Test Structure

Here’s a complete example showing how to structure a RiescueD test:

;#test.name       load_store_test
;#test.author     developer@company.com
;#test.arch       rv64
;#test.priv       supervisor
;#test.env        virtualized
;#test.cpus       1
;#test.paging     sv39
;#test.category   memory
;#test.class      load_store
;#test.features   ext_i.enable
;#test.tags       load store virtual_memory
;#test.summary    Test load/store operations with virtual memory

#####################
# Random Data Generation
#####################
;#random_data(name=test_data1, type=bits64, and_mask=0xffffffffffffffff)
;#random_data(name=test_data2, type=bits32, and_mask=0xfffffff0)

#####################
# Address Generation and Page Mapping
#####################
;#random_addr(name=data_region, type=linear, size=0x2000, and_mask=0xfffffffffffff000)
;#random_addr(name=data_phys, type=physical, size=0x2000, and_mask=0xfffffffffffff000)
;#page_mapping(lin_name=data_region, phys_name=data_phys, v=1, r=1, w=1, pagesize=['4kb'])

;#reserve_memory(start_addr=0x10000000, addr_type=linear, size=0x1000)
;#page_mapping(lin_addr=0x10000000, phys_name=&random, v=1, r=1, w=1, pagesize=['4kb'])

.section .text

#####################
# Test Setup
#####################
test_setup:
    # Executed before each test, exactly once
    li t0, 0x12345678
    j test01

#####################
# Discrete Tests
#####################
;#discrete_test(test=test01)
test01:
    # Load from virtual address
    la t1, data_region
    ld t2, 0(t1)

    # Store to virtual address
    sd t0, 8(t1)

    # Verify the store
    ld t3, 8(t1)
    beq t0, t3, test01_pass
    j failed

test01_pass:
    j passed

#####################
# Test Cleanup
#####################
test_cleanup:
    # Executed after all tests are run, exactly once
    li t0, 0x12345678
    j passed

#####################
# Memory Sections
#####################
;#init_memory @data_region
.section .data_region, "aw"
    .dword test_data1
    .dword test_data2

Advanced Features

Exception Handling with OS_SETUP_CHECK_EXCP

RiescueD provides powerful exception testing capabilities through the OS_SETUP_CHECK_EXCP macro. This macro allows you to set up expected exceptions and verify that they occur with the correct parameters.

Macro Syntax:

OS_SETUP_CHECK_EXCP expected_cause, expected_pc, return_pc, expected_tval=0

Parameters:

  • expected_cause: The expected exception cause code (e.g., LOAD_PAGE_FAULT, STORE_PAGE_FAULT, ECALL)

  • expected_pc: Label where the exception should occur

  • return_pc: Label where execution should continue after exception handling

  • expected_tval: Expected trap value (optional, defaults to 0)

Exception Types:

Common exception causes that can be tested:

  • INSTRUCTION_ADDRESS_MISALIGNED: Misaligned instruction fetch

  • INSTRUCTION_ACCESS_FAULT: Instruction access violation

  • ILLEGAL_INSTRUCTION: Invalid instruction

  • LOAD_ADDRESS_MISALIGNED: Misaligned load operation

  • LOAD_ACCESS_FAULT: Load access violation

  • STORE_ADDRESS_MISALIGNED: Misaligned store operation

  • STORE_ACCESS_FAULT: Store access violation

  • ECALL: Environment call from various privilege modes

  • LOAD_PAGE_FAULT: Load page fault

  • STORE_PAGE_FAULT: Store page fault

  • INSTRUCTION_PAGE_FAULT: Instruction page fault

  • LOAD_GUEST_PAGE_FAULT: Guest load page fault (virtualization)

  • STORE_GUEST_PAGE_FAULT: Guest store page fault (virtualization)

  • VIRTUAL_INSTRUCTION: Virtual instruction exception

  • ECALL_FROM_USER, ECALL_FROM_SUPER, ECALL_FROM_MACHINE: Privilege-specific ecalls

Basic Exception Testing Example:

# Test ecall exception
OS_SETUP_CHECK_EXCP ECALL, ecall_instr, after_ecall

ecall_instr:
    ecall          # This instruction will cause an exception
    j failed       # Should never reach here

after_ecall:
    # Continue test execution here
    j passed

Page Fault Testing Example:

# Test store page fault on a non-writable page
;#page_mapping(lin_name=readonly_page, phys_name=readonly_phys, v=1, r=1, w=0, pagesize=['4kb'])

# Setup expected page fault
OS_SETUP_CHECK_EXCP STORE_PAGE_FAULT, fault_store, after_fault, readonly_page

fault_store:
    li t1, readonly_page
    sw t0, 0(t1)    # This will cause a store page fault
    j failed        # Should never reach here

after_fault:
    # Exception was handled correctly
    j passed

Page Map Feature

The page_maps parameter in page_mapping directives allows you to specify which page table map(s) a page should belong to. This is essential for advanced virtual memory testing, especially in virtualized environments.

Default Page Maps:

  • map_os: Operating system page map (default for all pages)

  • map_hyp: Hypervisor page map (used in virtualized environments)

Custom Page Maps:

You can define custom page maps for specialized testing scenarios:

# Page belongs to custom map
;#page_mapping(lin_name=custom_page, phys_name=custom_phys, v=1, r=1, w=1, pagesize=['4kb'], page_maps=['custom_map'])

# Page belongs to multiple maps
;#page_mapping(lin_name=shared_page, phys_name=shared_phys, v=1, r=1, w=1, pagesize=['4kb'], page_maps=['map_os', 'custom_map'])

Use Cases for Page Maps:

  1. Process Isolation Testing:

# Process 1 pages
;#page_mapping(lin_name=proc1_stack, phys_name=proc1_stack_phys, v=1, r=1, w=1, pagesize=['4kb'], page_maps=['proc1_map'])
;#page_mapping(lin_name=proc1_heap, phys_name=proc1_heap_phys, v=1, r=1, w=1, pagesize=['4kb'], page_maps=['proc1_map'])

# Process 2 pages
;#page_mapping(lin_name=proc2_stack, phys_name=proc2_stack_phys, v=1, r=1, w=1, pagesize=['4kb'], page_maps=['proc2_map'])
;#page_mapping(lin_name=proc2_heap, phys_name=proc2_heap_phys, v=1, r=1, w=1, pagesize=['4kb'], page_maps=['proc2_map'])
  1. Virtualization Testing:

# Guest OS pages
;#page_mapping(lin_name=guest_kernel, phys_name=guest_kernel_phys, v=1, r=1, w=1, x=1, pagesize=['4kb'], page_maps=['map_os'])

# Hypervisor pages
;#page_mapping(lin_name=hyp_pages, phys_name=hyp_phys, v=1, r=1, w=1, x=1, pagesize=['4kb'], page_maps=['map_hyp'])
  1. Shared Memory Testing:

# Shared between processes
;#page_mapping(lin_name=shared_mem, phys_name=shared_phys, v=1, r=1, w=1, pagesize=['4kb'], page_maps=['proc1_map', 'proc2_map'])

Advanced Page Map Features:

  • Automatic Map Selection: RiescueD automatically adds appropriate maps based on test environment

  • Map Inheritance: Pages can inherit properties from their parent maps

  • Cross-Map References: Pages in different maps can reference each other for complex scenarios

Debugging Page Maps:

When debugging page map issues, RiescueD generates detailed page table information in the output files:

  • .ld file contains memory layout for all maps

  • .dis file shows the final page table entries

  • Log files detail which pages belong to which maps

Environment Randomization

RiescueD can randomize various aspects of the test environment:

  • Privilege Modes: Automatically switch between machine, supervisor, and user modes

  • Paging Modes: Test different virtual memory configurations

  • Extension Configuration: Enable/disable RISC-V extensions randomly

Best Practices

Test Organization

  1. Use Clear Headers: Always include comprehensive test headers

  2. Group Directives: Organize random data, addresses, and page mappings in sections

  3. Document Tests: Use test.summary to explain test intent

  4. Use Meaningful Names: Choose descriptive names for variables and tests

Memory Management

  1. Align Addresses: Use appropriate and_mask values for alignment

  2. Size Appropriately: Ensure memory regions are sized correctly

  3. Test Boundaries: Include tests for page boundaries and edge cases

  4. Consider Caching: Be aware of cache line effects in your tests

Randomization Strategy

  1. Constrain Wisely: Use masks to ensure valid address ranges

  2. Test Multiple Scenarios: Use any options to test various configurations

  3. Verify Assumptions: Don’t assume specific random values

  4. Handle Edge Cases: Consider what happens with extreme random values

Debugging Tips

Common Issues

  1. Address Alignment: Ensure addresses are properly aligned for their access size

  2. Page Permissions: Verify page mappings have correct read/write/execute permissions

  3. Address Space Conflicts: Avoid overlapping memory regions

  4. Missing Mappings: Ensure all accessed addresses have corresponding page mappings

Debug Output

RiescueD generates several helpful files:

  • .S file: Final assembly with all substitutions

  • .ld file: Linker script with memory layout

  • .dis file: Disassembly for verification

  • Log files: Detailed generation information

Performance Considerations

Test Generation Speed

  • Minimize Complex Mappings: Large page tables slow generation

  • Use Appropriate Page Sizes: Larger pages reduce table complexity

  • Limit Random Iterations: Don’t over-randomize in tight loops

Simulation Performance

  • Optimize Hot Paths: Keep frequently executed code efficient

  • Consider Memory Hierarchy: Be aware of cache and TLB effects

  • Use Appropriate Test Lengths: Balance coverage with simulation time

Configuration Files

CPU Configuration

Create a cpu_config.json file to specify your target system:

{
    "memory_map": {
        "ram": {
            "start": "0x80000000",
            "size": "0x10000000"
        },
        "io": {
            "start": "0x10000000",
            "size": "0x1000000"
        }
    }
}

Integration with Simulators

RiescueD works with popular RISC-V simulators:

  • Spike: RISC-V ISA simulator

  • Whisper: TenstorrentTT’s RISC-V simulator

  • QEMU: Full system emulation (coming soon)

  • Custom RTL: Integration with RTL simulators

Further Resources

Need Help?

Happy testing with RiescueD! 🚀