3. Python-UVM Integration - Test Patterns & Best Practices
In the previous post, we built the DPI-C bridge connecting SystemVerilog to Python. Now, let's look at how to write actual tests using this bridge. One of the biggest advantages of this approach is being able to use Python's clean syntax and powerful standard library for verification.
The Factory Pattern
To keep the C-to-Python bridge simple, we use a single entry point for all tests: the create_sequence() factory function. This allows the C layer to load any test module without knowing its internal details.
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#dbeafe', 'primaryTextColor': '#1e293b', 'primaryBorderColor': '#3b82f6', 'lineColor': '#64748b', 'secondaryColor': '#f1f5f9'}}}%%
flowchart LR
subgraph PY["Python Layer"]
FACT([create_sequence])
SEQ[APBSequence]
STIM[Stimulus]
end
subgraph BRIDGE["DPI-C Bridge"]
C_INIT[apb_init]
C_EXEC[dpi_get_transaction]
end
subgraph SV["SystemVerilog"]
DUT{{DUT}}
end
C_INIT -->|Calls| FACT
FACT -->|Returns| SEQ
SEQ o--o STIM
C_EXEC -->|Polls| SEQ
SEQ -->|Txn| C_EXEC
C_EXEC ==> DUT
style PY fill:#d1fae5,stroke:#10b981
style BRIDGE fill:#f1f5f9,stroke:#64748b
style SV fill:#dbeafe,stroke:#3b82f6
# apb_basic_test.py
from apb_base import APBSequence
def create_sequence():
"""Factory function called by DPI bridge"""
seq = APBSequence("Basic_Test")
# Test stimulus defined here
seq.add_write(0x10, 0x12345678)
seq.add_read(0x10)
return seq
Fluent Pattern for Stimulus
Instead of the verbose UVM macros (`uvm_do_with), we can use Python's method chaining (Fluent Interface) to make sequences readable and concise. The APBSequence class in apb_base.py returns self from its methods to enable this.
# apb_base.py snippet
class APBSequence:
def add_write(self, addr, data):
# ... create txn ...
return self # Enables chaining
def add_read(self, addr):
# ... create txn ...
return self
This allows us to write compact directed tests:
# Directed test sequence
seq.add_write(0x10, 0xAA) \
.add_write(0x14, 0xBB) \
.add_read(0x10) \
.add_read(0x14)
Constrained Random in Python
SystemVerilog's constraint solver is powerful, but for many block-level verification tasks, Python's random module is more than sufficient and significantly faster to write/debug.
# apb_random_test.py
from apb_base import APBRandomSequence
def create_sequence():
# Generate 20 random transactions
# No complex constraints, just Python logic
return APBRandomSequence(
num_transactions=20,
addr_range=(0x0, 0x1FF)
)
The implementation of APBRandomSequence shows how easy it is to implement custom randomization logic:
class APBRandomSequence(APBSequence):
def __init__(self, num_transactions, addr_range, name="Random"):
super().__init__(name)
import random
for _ in range(num_transactions):
# Align address to 4 bytes
addr = random.randint(*addr_range) & 0xFFFFFFFC
data = random.randint(0, 0xFFFFFFFF)
is_write = random.choice([True, False])
if is_write:
self.add_write(addr, data)
else:
self.add_read(addr)
Verification Pattern: Callbacks
In a standard UVM sequence, getting read data back can be tricky if you're mixing writes and reads. In our Python structure, we can register callbacks for read data. This separates the stimulus generation from the checking logic.
def check_status(data):
if (data & 0x1) == 0:
print("[ERROR] Status bit 0 not set!")
# Register callback with read
seq.add_write(0x00, 0x1) \
.add_read(0x00, callback=check_status)
The base class handles invoking the callback when data returns from the C bridge:
# apb_base.py
def send_read_data(self, sim_time, data):
# ... log data ...
if callback:
callback(data)
Key Takeaways
- Unified Entry Point: The factory pattern decouples the C bridge from specific tests.
- Readable Tests: Method chaining creates cleaner code than deeply nested UVM macros.
- Simple Randomization: Standard Python libraries often replace complex constraint solvers for driver-level randomness.
- Functional Checking: Passing functions as callbacks is a natural pattern in Python (first-class functions).
In the next post, we will look at the APB VIP Architecture and how to package this into a reusable Verification IP.
Comments (0)
Leave a Comment