Variables
=========
..
_comment: Generated using Opus 4.5
The Variable module manages runtime environment variables used by the test runtime.
It handles both hart-local (per-hart) and shared (cross-hart) variable storage, providing
assembly code generation for loading, storing, and manipulating these variables.
Architecture
------------
The variable system consists of three memory containers managed by a central ``VariableManager``:
.. mermaid::
graph TD
VM[VariableManager]
HC[HartContext
Hart-local variables]
HS[HartStack
Hart-local stack]
SM[SharedMemory
Cross-hart variables]
VM --> HC
VM --> HS
VM --> SM
classDef manager fill:#5164e0,stroke:#5164e0,stroke-width:3px,color:#fff
classDef memory fill:#fa512e,stroke:#fa512e,stroke-width:3px,color:#000
class VM manager
class HC,HS,SM memory
Hart-Local vs Shared Variables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Hart-local variables are stored in a per-hart context accessed via the ``tp`` register.
Each hart has its own copy of these variables, making them safe for concurrent access without synchronization.
Shared variables are stored in a global ``os_data`` section accessible by all harts.
These require atomic operations (AMO) when accessed concurrently in multiprocessor configurations.
Variable
--------
The :py:class:`riescue.dtest_framework.runtime.variable.variable.Variable` class represents a single runtime variable.
Each variable tracks:
- ``name``: Symbol name for the variable
- ``value``: Initial value
- ``size``: Size in bytes (1, 2, 4, or 8)
- ``offset``: Byte offset from the base pointer
- ``hart_variable``: Whether the variable is hart-local (uses ``tp``) or shared
Code Generation Methods
~~~~~~~~~~~~~~~~~~~~~~~
``load(dest_reg)``
Generates assembly to load the variable value into the destination register.
``store(src_reg, temp_reg)``
Generates assembly to store a register value into the variable.
``load_immediate(dest_reg)``
Generates assembly to load the variable address into a register.
``increment(dest_reg, addr_reg)``
Loads, increments by 1, and stores. Uses ``amoadd`` if AMO is enabled.
``load_and_clear(dest_reg)``
Atomically loads the value and clears it. Uses ``amoswap`` if AMO is enabled.
VariableManager
---------------
The :py:class:`riescue.dtest_framework.runtime.variable.manager.VariableManager` class coordinates all variable storage.
API
~~~
``register_hart_variable(name, value, **kwargs)``
Register a new hart-local variable.
``register_shared_variable(name, value, **kwargs)``
Register a new shared variable.
``get_variable(name)``
Retrieve a variable by name from any memory container.
``initialize(scratch_regs)``
Generate code to initialize the hart context pointer into scratch CSRs.
``enter_hart_context(scratch)``
Generate code to enter the runtime context by swapping ``tp`` with a scratch CSR.
``exit_hart_context(scratch)``
Generate code to exit the runtime context and restore test context.
``allocate()``
Generate assembly directives to allocate all variable storage sections.
``equates(offset)``
Generate ``.equ`` directives for shared variables.
HartContext
-----------
The :py:class:`riescue.dtest_framework.runtime.variable.hart_memory.HartContext` class manages per-hart context storage.
Each hart context contains:
- Hart stack pointer
- Test stack pointer swap space
- ``mhartid`` (automatically set per-hart)
- User-registered variables
Contexts are aligned to 64-byte boundaries.
For multi-hart configurations, a ``hart_context_table`` is generated containing pointers to each hart's context.
The correct context is loaded at runtime based on ``mhartid``.
HartStack
---------
The :py:class:`riescue.dtest_framework.runtime.variable.hart_memory.HartStack` class allocates per-hart stack space.
Follows RISC-V ABI 2.1:
- Aligned to 128-bit (16 bytes)
- Stack grows downward (initialized with ``{stack_name}_end``)
- Default size: 4096 bytes
SharedMemory
------------
The :py:class:`riescue.dtest_framework.runtime.variable.shared_memory.SharedMemory` class manages cross-hart shared variables.
Variables are allocated in the ``os_data`` section with ``.equ`` directives generated for symbol access.
Variable names in memory are suffixed with ``_mem`` to avoid conflicts with the equate symbols.
Configuration
-------------
- ``xlen``: Register width (32 or 64 bits)
- ``hart_count``: Number of harts
- ``amo_enabled``: Enable atomic memory operations for shared variable access
- ``hart_stack_size``: Size of per-hart stack (default 0x1000)
Example Usage
-------------
.. code-block:: python
from riescue.dtest_framework.runtime.variable import VariableManager
import riescue.lib.enums as RV
vm = VariableManager(
data_section_name="runtime_data",
xlen=RV.Xlen.XLEN64,
hart_count=4,
amo_enabled=True
)
# Register variables
vm.register_hart_variable("trap_count", value=0, description="Number of traps")
vm.register_shared_variable("global_lock", value=0)
# Generate allocation code
alloc_code = vm.allocate()
# Get variable and generate load code
trap_var = vm.get_variable("trap_count")
load_code = trap_var.load("t0")