Runtime Implementation (Developer)¶
The zuspec.dataclasses.rt module provides the pure-Python runtime
implementation of Zuspec semantics. This module is primarily of interest
to developers extending or integrating Zuspec.
ObjFactory¶
ObjFactory is responsible for creating runtime instances of Zuspec types.
It handles component construction, field initialization, and binding resolution.
from zuspec.dataclasses.rt import ObjFactory
# Get the singleton factory instance
factory = ObjFactory.inst()
Key Methods¶
mkComponent(cls, **kwargs) -> ComponentCreates a runtime component instance. Automatically called when instantiating a Component subclass.
mkRegFile(cls, **kwargs) -> RegFileCreates a standalone register file instance.
Component Lifecycle¶
When a component is instantiated, the factory orchestrates:
Construction -
__new__intercepts and delegates to factory__comp_init__ - Wrapper around dataclass
__init____comp_build__ - Recursive tree initialization:
Creates
CompImplRTfor each componentSets timebase on root component
Discovers
@processdecorated methodsInitializes Memory, RegFile, and AddressSpace fields
Applies
__bind__mappings
Validation - Top-level ports must be bound
Process Start - Processes start lazily when
wait()is called
Binding System¶
The factory uses proxy objects to capture binding relationships:
BindPathRepresents a path in the binding system (e.g.,
self.p.prod)BindProxyProxy object that records attribute access during
__bind__evaluationInterfaceImplDynamic object that holds bound methods for exports/ports
Timebase¶
Timebase implements synthetic simulation time using an event queue.
from zuspec.dataclasses.rt import Timebase
tb = Timebase()
# In async context
await tb.wait(Time.ns(100)) # Wait 100ns
tb.after(Time.us(1), callback) # Schedule callback
current = tb.time() # Get current time
Internal Representation¶
Time is tracked internally in femtoseconds for maximum precision. The event queue uses a min-heap for efficient event scheduling.
Key Methods¶
wait(amt: Time)Suspend calling coroutine until specified time elapses. Creates a future and adds it to the event queue.
after(amt: Time, call: Callable)Schedule a callback to be invoked at a future time.
advance() -> boolAdvance simulation to next event and wake waiters. Returns True if more events remain.
run_until(amt: Time)Run simulation until specified time, then return. Main simulation loop driver.
stop()Stop the simulation loop.
Properties¶
current_time: intCurrent simulation time in femtoseconds.
time() -> TimeCurrent time as a Time object (nanoseconds).
CompImplRT¶
CompImplRT is the runtime implementation backing each Component instance.
It manages processes, timebase inheritance, and simulation control.
# Accessed via component._impl (internal use)
comp._impl.name # Instance name
comp._impl.parent # Parent component
comp._impl.timebase() # Get timebase (inherits from parent)
Key Methods¶
add_process(name, proc)Register a
@processdecorated method.start_processes(comp)Start all registered processes for a component.
start_all_processes(comp)Recursively start processes in the component tree.
set_timebase(tb)Set the timebase for this component (root only).
timebase() -> TimebaseGet timebase, inheriting from parent if not set locally.
wait(comp, amt)Wait implementation. If simulation not running, starts it.
shutdown()Cancel all running process tasks.
MemoryRT¶
MemoryRT provides the runtime implementation of Memory[T].
Uses sparse dictionary storage for efficiency.
from zuspec.dataclasses.rt import MemoryRT
mem = MemoryRT(_size=1024, _width=32, _element_type=int)
mem.write(0, 0xDEADBEEF)
val = mem.read(0)
Properties¶
_size- Number of elements_width- Bits per element_element_type- Python type of elements_data- Sparse storage dictionary
Methods¶
read(addr) -> TRead element at address. Returns 0 for unwritten locations.
write(addr, data)Write element to address. Value masked to element width.
RegFileRT¶
RegFileRT provides the runtime implementation of RegFile.
Manages a collection of registers with address-based access.
from zuspec.dataclasses.rt import RegFileRT, RegRT
regfile = RegFileRT()
reg = RegRT(_value=0, _width=32)
regfile.add_register("status", reg, offset=0)
Key Methods¶
add_register(name, reg, offset)Add a register at the specified byte offset.
read(addr) -> intRead register at byte offset.
write(addr, data)Write register at byte offset.
Properties¶
size- Total size in byteswidth- Access width (32 bits)
RegRT¶
RegRT implements individual register behavior with async access.
reg = RegRT(_value=0, _width=32, _element_type=MyPackedStruct)
value = await reg.read() # Returns unpacked struct if applicable
await reg.write(0x1234) # Accepts int or PackedStruct
PackedStruct Support¶
When _element_type is a PackedStruct:
read()unpacks the integer value into struct fieldswrite()accepts either int or struct, packing as needed
AddressSpaceRT¶
AddressSpaceRT implements address space with memory mapping.
from zuspec.dataclasses.rt import AddressSpaceRT, MemoryRT
aspace = AddressSpaceRT()
mem = MemoryRT(_size=4096, _width=32, _element_type=int)
aspace.add_mapping(0x0000, mem)
val = await aspace.read(0x100, 4) # Read 4 bytes at 0x100
await aspace.write(0x100, 0x1234, 4) # Write 4 bytes
Key Methods¶
add_mapping(base_addr, storage)Map storage (MemoryRT or RegFileRT) at base address.
read(addr, size) -> intAsync read from mapped storage.
write(addr, data, size)Async write to mapped storage.
Storage Resolution¶
The _find_storage() method locates the appropriate storage
for an address, returning the storage object and offset within it.
Raises RuntimeError for unmapped addresses or cross-boundary accesses.
AddrHandleRT¶
AddrHandleRT implements the MemIF protocol for an address space.
from zuspec.dataclasses.rt import AddrHandleRT
handle = aspace.base # Get handle from AddressSpace
val = await handle.read32(0x1000)
await handle.write32(0x1000, 0xABCD)
Provides convenience methods for sized accesses:
read8, read16, read32, read64,
write8, write16, write32, write64