Architecture

System Architecture

The Zuspec HDLSim backend implements a dual-domain architecture that partitions verification logic between SystemVerilog (for hardware simulation) and Python (for test orchestration).

        graph TB
    subgraph Python["Python Domain (pytest via PyHDL-IF)"]
        PY1["• Test Orchestration"]
        PY2["• Top-level Component"]
        PY3["• Python-only Components"]
        PY4["• Test Sequences & Scenarios"]
    end

    subgraph SV["SystemVerilog Domain (HDL Simulator)"]
        SV1["• Generated Testbench Module"]
        SV2["• Generated Transactors (XtorComponent)"]
        SV3["• Extern Components (existing HDL)"]
        SV4["• Signal-level Connectivity"]
        SV5["• DUT (Design Under Test)"]
    end

    Python <-->|"PyHDL-IF Bridge<br/>(TLM/API calls)"| SV

    style Python fill:#e1f5ff
    style SV fill:#ffe1f5
    

Component Classification

The backend classifies components into three categories that determine their implementation domain:

Extern Components

Purpose: Wrap existing SystemVerilog modules for use in Zuspec testbenches

Implementation: SystemVerilog only (pre-existing or provided by user)

Characteristics:
  • Inherit from Extern protocol

  • Reference existing HDL modules

  • Provide SystemVerilog source filesets via @annotation_fileset

  • Connect to other SV components via signals/interfaces

Example:

@zdc.dataclass
class DutWrapper(zdc.Component, Extern):
    """Wrapper for existing DUT module."""

    @annotation_fileset(sources=["dut.sv"])
    def __post_init__(self):
        pass

XtorComponent Transactors

Purpose: Define transactors that generate both SystemVerilog and Python APIs

Implementation: SystemVerilog (generated) + Python proxy (runtime)

Characteristics:
  • Inherit from XtorComponent[Protocol]

  • Protocol defines the transaction API

  • SV code generated by zuspec-be-sv

  • Python API wrapper generated via PyHDL-IF

  • Connect via xtor_if interface

Example:

@zdc.dataclass
class ClkGenXtor(XtorComponent[ClkGenIf]):
    """Clock generator transactor."""

    clock: Signal = zdc.output()

    async def start(self, freq_mhz: int):
        """Start clock generation."""
        ...

Python Components

Purpose: Pure Python verification components

Implementation: Python only

Characteristics:
  • Standard Zuspec components

  • No signal-level connectivity to SV domain

  • Communicate via TLM/method calls

  • Run in pytest environment

Domain Separation Rules

The HDLTestbench profile enforces strict domain separation:

  1. No Signal-Level Crossing: Python components cannot connect directly to signals in the SV domain

  2. TLM Boundary: Communication between domains uses transaction-level modeling (method calls on xtor_if)

  3. Extern Isolation: Extern components can only connect to other SV components (Extern or XtorComponent)

Generation Flow

Build Time (GenTB Task)

        flowchart TD
    Start[Zuspec Component Class] --> Check[Profile Checking]
    Check --> |Valid| SVGen[SV Generation]
    Check --> |Errors| Fail[Report Errors]

    SVGen --> Trans[Generate Transactor Modules<br/>via zuspec-be-sv]
    SVGen --> TBMod[Generate Testbench Wrapper Module]
    SVGen --> API[Generate PyHDL-IF API Definitions JSON]

    Trans --> APIGen[PyHDL-IF API Generation]
    API --> APIGen
    APIGen --> Glue[Generate SV/Python API Glue Code]

    TBMod --> Fileset[Produce Ordered Compilation Fileset]
    Glue --> Fileset

    Fileset --> Done[Ready for Compilation]

    style Check fill:#ffffcc
    style SVGen fill:#ccffcc
    style APIGen fill:#ccccff
    style Done fill:#ccffcc
    

Runtime (Simulation)

        sequenceDiagram
    participant Sim as HDL Simulator
    participant TB as Testbench Module
    participant PyHDL as PyHDL-IF
    participant Runtime as HDLSimRuntime
    participant Pytest as pytest

    Sim->>TB: Load & Initialize
    TB->>PyHDL: Register Transactor APIs
    TB->>Runtime: configure_objfactory("MyTB")
    Runtime->>Runtime: Patch MyTB.__init__
    TB->>Pytest: pyhdl_pytest()
    Pytest->>Runtime: Create MyTB instance
    Runtime->>PyHDL: Lookup registered components
    PyHDL-->>Runtime: Return SV proxies
    Runtime-->>Pytest: Return TestbenchProxy
    Pytest->>Runtime: Call test methods
    Runtime->>PyHDL: Forward to SV APIs
    PyHDL->>TB: Execute in simulator
    TB-->>PyHDL: Return results
    PyHDL-->>Runtime: Return to Python
    Runtime-->>Pytest: Complete test
    Pytest->>Sim: $finish
    

Code Generation Details

Testbench Module Structure

The generated top-level testbench module has this structure:

module MyTB_tb;
    import pyhdl_if::*;
    import RVXtor::*;

    // Instance HDL components
    MyTB_hdl u_hdl();

    initial begin
        // Register transactors with PyHDL-IF
        // ... registration code ...

        // Configure Python ObjFactory
        pyhdl_configure_objfactory("mypackage.MyTB");

        // Launch pytest
        pyhdl_pytest();

        $finish;
    end
endmodule

HDL Module Structure

The _hdl module contains the hardware components:

module MyTB_hdl;
    // Extern component instances
    dut u_dut(...);

    // Generated transactor instances
    RVXtor u_xtor(...);

    // Signal bindings from __bind__
    assign u_dut.rv_if = u_xtor.xtor_if;
endmodule

Python Runtime Factory

At runtime, the PyTestbenchFactory creates proxy objects:

class TestbenchProxy:
    def __init__(self):
        # Look up registered SV components via PyHDL-IF
        self.xtor = HdlObjRgy.inst().find("top.xtor")
        self.dut = None  # Extern, no Python API

Key Classes

  • SVTestbenchGenerator: Generates SystemVerilog testbench modules

  • PyTestbenchFactory: Creates runtime proxy objects for Python tests

  • HDLTestbenchChecker: Validates profile rules

  • TransactorJsonApiGenerator: Generates PyHDL-IF API definitions

  • GenTB: DFM task for testbench generation

See API Reference for detailed API documentation.