A Python-embedded multi-abstraction language for modeling digital hardware from transfer-function level to register transfer level (RTL). Zuspec provides decorators and types to capture hardware model semantics with support for simulation, verification, and code generation.
Zuspec is a Python-based DSL that:
- Supports multiple abstraction levels (behavioral to RTL)
- Provides type-safe hardware modeling with static type checking
- Enables component-based design with ports, bundles, and interfaces
- Includes built-in support for timing, synchronization, and communication
- Features profile-based validation for different target platforms
Hardware Types (95 types):
- Width-annotated integers:
u1-u32,u64,u128,i8,i16,i32,i64,i128 - Bit types:
bit,bit1-bit8,bit16,bit32,bit64 - Variable-width types:
bitv,bv(width specified at field level) - Long-form type aliases:
uint8_t,uint32_t,int32_t, etc.
Structural Types:
Component- Base class for structural hardware componentsXtorComponent[T]- Transactor components with signal-level and operation-level interfacesBundle- Interface/port collections with directionalityStruct- Data-only types with C-like alignmentPackedStruct- Bitwise packed data structures
Timing:
Timeclass with units:s,ms,us,ns,ps,fsTimebaseprotocol for time management
Class Decorators:
@dataclass(profile=...)- Define hardware components with optional profile validation
Field Decorators:
field()- General field declaration with metadata supportconst()- Compile-time/construction-time parametersinput()/output()- Signal directionality for RTLbundle()/mirror()/monitor()- Interface instantiation with optional kwargsport()/export()- Method-based communication channelsinst()- Automatic instance constructiontuple()- Fixed-size tuples
Method Decorators:
@process- Always-running async processes@sync(clock=..., reset=...)- Synchronous (clocked) processes with deferred assignment@comb- Combinational (immediate) processes@invariant- Structural invariants for validationbind()- Connection specifications
Const Parameters:
@zdc.dataclass
class WishboneInitiator(zdc.Bundle):
DATA_WIDTH : zdc.u32 = zdc.const(default=32)
ADDR_WIDTH : zdc.u32 = zdc.const(default=32)Width Expressions:
dat_w : zdc.bitv = zdc.output(width=lambda s: s.DATA_WIDTH)
sel : zdc.bitv = zdc.input(width=lambda s: int(s.DATA_WIDTH/8))Bundle Instantiation with Kwargs:
init : WishboneInitiator = zdc.bundle(
kwargs=lambda s: dict(
DATA_WIDTH=s.DATA_WIDTH,
ADDR_WIDTH=s.ADDR_WIDTH))TLM Interfaces (6 protocols):
GetIF[T]/PutIF[T]- Basic get/put interfacesReqRspIF[Treq,Trsp]- Request-response interfaceChannel[T]- Bidirectional channelReqRspChannel[Treq,Trsp]- Initiator/target channelTransport[Treq,Trsp]- Callable transport function
Port/Export Pattern:
class ITarget(Protocol):
async def access(self, addr: zdc.u32, data: zdc.u32) -> zdc.u32: ...
@zdc.dataclass
class Target(zdc.Component):
api : ITarget = zdc.export()
def __bind__(self):
return {self.api.access: self.handle_access}Edge Detection (3 functions):
posedge(signal)- Wait for positive edgenegedge(signal)- Wait for negative edgeedge(signal)- Wait for any edge
Timing:
await self.wait(zdc.Time.ns(10)) # Wait 10 nanoseconds
await posedge(self.clock) # Wait for clock edgeResource Management:
Lock- Async mutex locksPool[T]/ClaimPool[T]- Resource pools with arbitrationMemory/AddressSpace- Memory modelingRegFile/Reg/RegFifo- Register modeling
Built-in Profiles:
PythonProfile- Permissive, standard Python types allowedRetargetableProfile- Strict, requires width-annotated types for hardware synthesis
Usage:
from zuspec.dataclasses import dataclass, profiles
@dataclass(profile=profiles.PythonProfile)
class SoftwareModel:
x: int # OK with PythonProfile
@dataclass(profile=profiles.RetargetableProfile)
class HardwareModel:
x: uint32_t # Width-annotated type requiredCustom Profiles:
Extend Profile and ProfileChecker to create custom validation rules.
MyPy Plugin Integration:
- Validates field types based on profile
- Checks width annotations for hardware types
- Enforces correct decorator usage
- Validates method bindings
Configure in pyproject.toml:
[tool.mypy]
plugins = ["zuspec.dataclasses.mypy.plugin"]# Using IVPM (recommended for developers)
uvx ivpm update
# Or install directly
pip install zuspec-dataclassesimport zuspec.dataclasses as zdc
@zdc.dataclass
class Counter(zdc.Component):
clock : zdc.bit = zdc.input()
reset : zdc.bit = zdc.input()
count : zdc.u32 = zdc.output()
@zdc.sync(clock=lambda s: s.clock, reset=lambda s: s.reset)
def _count_proc(self):
if self.reset:
self.count = 0
else:
self.count = self.count + 1@zdc.dataclass
class Producer(zdc.Component):
data : zdc.GetIF[zdc.u32] = zdc.port()
@zdc.process
async def run(self):
for i in range(10):
value = await self.data.get()
print(f"Received: {value}")PSS compound actions express complex test intent using an activity — a sequential or parallel composition of sub-action traversals.
import zuspec.dataclasses as zdc
@zdc.dataclass
class DataBuff(zdc.Buffer): # PSS buffer flow-object
seg: zdc.u32 = zdc.rand()
@zdc.dataclass
class DmaChannel(zdc.Resource): # PSS resource
priority: zdc.u4 = zdc.rand()| Base type | PSS concept | Field helpers |
|---|---|---|
Buffer |
Buffer flow-object | input(), output() |
Stream |
Stream flow-object | input(), output() |
State |
State flow-object | input(), output() |
Resource |
Resource | lock(), share() |
@zdc.dataclass
class WriteData(zdc.Action[DmaComponent]):
data: DataBuff = zdc.output()
chan: DmaChannel = zdc.lock()
size: zdc.u8 = zdc.rand()
async def body(self): ... # atomic action
@zdc.dataclass
class ReadData(zdc.Action[DmaComponent]):
data: DataBuff = zdc.input()
chan: DmaChannel = zdc.lock()
async def body(self): ...
@zdc.dataclass
class DmaXfer(zdc.Action[DmaComponent]):
wr: WriteData = zdc.field(default=None)
rd: ReadData = zdc.field(default=None)
async def activity(self):
self.wr() # traverse wr sequentially
with self.rd(): # traverse rd with inline constraint
self.rd.chan.priority > 5The @zdc.dataclass decorator detects async def activity(self), parses it
from the Python AST (never executing it), and stores the structured IR on
DmaXfer.__activity__.
async def activity(self):
with zdc.parallel(): # concurrent traversals
self.a()
self.b()
with zdc.parallel(join_first=1): # stop after first branch finishes
self.x()
self.y()
with zdc.schedule(): # PSS schedule block
self.p()
self.q()
with zdc.atomic(): # atomic (exclusive) region
self.z()async def activity(self):
# Repeat (count loop)
for i in range(self.count):
self.write()
# Foreach (collection iteration)
for item in self.data_array:
self.process()
# Select (non-deterministic choice)
with zdc.select():
with zdc.branch(weight=70):
self.fast_write()
with zdc.branch(weight=30):
self.slow_write()
# Conditional
if self.size > 64:
self.big_xfer()
else:
self.small_xfer()
# Bind flow objects
bind(self.producer.data_out, self.consumer.data_in)async def activity(self):
zdc.do(WriteData) # anonymous traversal
with zdc.do(ReadData) as rd: # anonymous with label + constraints
rd.size < 256@zdc.extend
class WriteDataExt(WriteData):
tag: zdc.u4 = zdc.rand() # adds a field to WriteData@zdc.extend sets __is_extension__ = True and __extends__ on the class.
Multiple extensions of the same base imply a PSS schedule composition.
from zuspec.dataclasses.ir.activity import ActivitySequenceBlock, ActivityTraversal
ir = DmaXfer.__activity__ # ActivitySequenceBlock
for stmt in ir.stmts:
if isinstance(stmt, ActivityTraversal):
print(stmt.handle, stmt.inline_constraints)- User Guide - Comprehensive language introduction
- Types Reference - Complete type system documentation
- Runtime Guide - Runtime and simulation details
- Profile Checker Guide - Profile system and validation
- Parameterization Guide - Parameter and configuration support
- Examples - SPI model, RTL counter, ALU, and more
Implemented Features:
- ✅ Core type system with 95+ hardware types
- ✅ 17 decorators for component definition
- ✅ Parameterization with const fields and lambda expressions
- ✅ Profile system with MyPy integration
- ✅ TLM communication primitives
- ✅ Port/Export binding mechanism
- ✅ Synchronization primitives (posedge, negedge, edge)
- ✅ Resource management (Lock, Pool, Memory)
- ✅ Pure Python runtime with async support
- ✅ Static type checking via MyPy plugin
- ✅ PSS Activities — compound actions, scheduling blocks, control flow, flow-objects, resources
- ✅
@zdc.extendfor PSS type extensions - ✅ Abstract counters (
Counter,ModuloCounter,WatchdogCounter,CounterBank) with lazy simulation, RTL synthesis, and formal verification support
In Progress:
- 🔄 RTL execution engine for sync/comb processes
- 🔄 Code generation backends (SystemVerilog, C++)
- 🔄 Randomization and constraint solving
- 🔄 Coverage collection
Zuspec consists of four independent layers:
- Language Facade (
src/zuspec/dataclasses/) - Decorators, types, and base classes - Pure-Python Runtime (
src/zuspec/dataclasses/rt/) - Async execution engine - IR Data Model (
src/zuspec/dataclasses/ir/) - AST-like representation - Processing Tools - Analysis and code generation (separate packages)
# Fetch dependencies
uvx ivpm update
# Run tests
pytest tests/
# Run type checking
mypy src/ --config-file pyproject.toml
pyright src/src/zuspec/dataclasses/
├── __init__.py # Main API exports with __all__
├── decorators.py # @dataclass, @sync, @comb, field(), lock(), share(), extend(), etc.
├── types.py # Type system (Component, Bundle, Buffer, Stream, State, Resource, u32, etc.)
├── activity_parser.py # ActivityParser — AST parser for activity() methods
├── activity_dsl.py # DSL stubs: do, parallel, schedule, select, branch, bind, …
├── tlm.py # TLM interfaces (GetIF, PutIF, Channel)
├── profiles.py # Profile system and validators
├── data_model_factory.py # IR construction
├── rt/ # Pure Python runtime
│ ├── obj_factory.py # Object construction
│ ├── comp_impl_rt.py # Component implementation
│ ├── edge.py # Edge detection
│ └── ...
├── ir/ # IR data model
│ ├── activity.py # Activity IR nodes (ActivitySequenceBlock, ActivityParallel, …)
│ └── ...
└── mypy/ # MyPy plugin for static checking
Contributions welcome! Key areas:
- RTL execution engine development
- Backend code generators
- Additional profiles for specific platforms
- Documentation improvements
- Example models
Apache License 2.0 - See LICENSE file