Skip to content

Commit

Permalink
Transactron redesign: better proxying and cleaner implementation (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
tilk authored Jan 3, 2025
1 parent 2d2df0d commit 18f9a0a
Show file tree
Hide file tree
Showing 26 changed files with 492 additions and 399 deletions.
6 changes: 3 additions & 3 deletions docs/transactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ Suppose we have the following layout, which is an input layout for a method call

```python
layout = [("foo", 1), ("bar", 32)]
method = Method(input_layout=layout)
method = Method(i=layout)
```

The method can be called in multiple ways.
Expand Down Expand Up @@ -170,7 +170,7 @@ Take the following definitions:

```python
layout2 = [("foobar", layout), ("baz", 42)]
method2 = Method(input_layout=layout2)
method2 = Method(i=layout2)
```

One can then pass the arguments using `dict`s in following ways:
Expand Down Expand Up @@ -208,7 +208,7 @@ The `dict` syntax can be used for returning values from methods.
Take the following method declaration:

```python
method3 = Method(input_layout=layout, output_layout=layout2)
method3 = Method(i=layout, o=layout2)
```

One can then define this method as follows:
Expand Down
42 changes: 25 additions & 17 deletions test/core/test_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from collections import deque
from typing import Iterable, Callable
from transactron.core.keys import TransactionManagerKey

from transactron.testing import TestCaseWithSimulator, TestbenchIO, data_layout

Expand All @@ -20,33 +21,36 @@
from transactron.core import Priority
from transactron.core.schedulers import trivial_roundrobin_cc_scheduler, eager_deterministic_cc_scheduler
from transactron.core.manager import TransactionScheduler
from transactron.utils.dependencies import DependencyContext
from transactron.utils.dependencies import DependencyContext, DependencyManager


class TestNames(TestCase):
def test_names(self):
mgr = TransactionManager()
mgr._MustUse__silence = True # type: ignore

class T(Elaboratable):
def __init__(self):
self._MustUse__silence = True # type: ignore
Transaction(manager=mgr)
with DependencyContext(DependencyManager()) as ctx:
ctx.manager.add_dependency(TransactionManagerKey(), mgr)

T()
assert mgr.transactions[0].name == "T"
class T(Elaboratable):
def __init__(self):
self._MustUse__silence = True # type: ignore
Transaction()

t = Transaction(name="x", manager=mgr)
assert t.name == "x"
T()
assert mgr.transactions[0].name == "T"

t = Transaction(manager=mgr)
assert t.name == "t"
t = Transaction(name="x")
assert t.name == "x"

m = Method(name="x")
assert m.name == "x"
t = Transaction()
assert t.name == "t"

m = Method()
assert m.name == "m"
m = Method(name="x")
assert m.name == "x"

m = Method()
assert m.name == "m"


class TestScheduler(TestCaseWithSimulator):
Expand Down Expand Up @@ -111,7 +115,7 @@ def __init__(self, scheduler):
def elaborate(self, platform):
m = TModule()
tm = TransactionModule(m, DependencyContext.get(), TransactionManager(self.scheduler))
adapter = Adapter(i=data_layout(32), o=data_layout(32))
adapter = Adapter.create(i=data_layout(32), o=data_layout(32))
m.submodules.out = self.out = TestbenchIO(adapter)
m.submodules.in1 = self.in1 = TestbenchIO(AdapterTrans(adapter.iface))
m.submodules.in2 = self.in2 = TestbenchIO(AdapterTrans(adapter.iface))
Expand Down Expand Up @@ -428,7 +432,11 @@ class SingleCallerTestCircuit(Elaboratable):
def elaborate(self, platform):
m = TModule()

method = Method(single_caller=True)
method = Method()

@def_method(m, method, single_caller=True)
def _():
pass

with Transaction().body(m):
method(m)
Expand Down
4 changes: 2 additions & 2 deletions test/lib/test_connectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,13 @@ def elaborate(self, platform):

get_results = []
for i in range(self.count):
input = TestbenchIO(Adapter(o=self.lay))
input = TestbenchIO(Adapter.create(o=self.lay))
get_results.append(input.adapter.iface)
m.submodules[f"input_{i}"] = input
self.inputs.append(input)

# Create ManyToOneConnectTrans, which will serialize results from different inputs
output = TestbenchIO(Adapter(i=self.lay))
output = TestbenchIO(Adapter.create(i=self.lay))
m.submodules.output = self.output = output
m.submodules.fu_arbitration = ManyToOneConnectTrans(get_results=get_results, put_result=output.adapter.iface)

Expand Down
4 changes: 2 additions & 2 deletions test/lib/test_reqres.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ def setup_method(self):

layout = [("field", self.data_width)]

self.req_method = TestbenchIO(Adapter(i=layout))
self.resp_method = TestbenchIO(Adapter(o=layout))
self.req_method = TestbenchIO(Adapter.create(i=layout))
self.resp_method = TestbenchIO(Adapter.create(o=layout))

self.test_circuit = SimpleTestCircuit(
Serializer(
Expand Down
6 changes: 3 additions & 3 deletions test/lib/test_simultaneous.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@
class ConditionTestCircuit(Elaboratable):
def __init__(self, target: Method, *, nonblocking: bool, priority: bool, catchall: bool):
self.target = target
self.source = Method(i=[("cond1", 1), ("cond2", 1), ("cond3", 1)], single_caller=True)
self.source = Method(i=[("cond1", 1), ("cond2", 1), ("cond3", 1)])
self.nonblocking = nonblocking
self.priority = priority
self.catchall = catchall

def elaborate(self, platform):
m = TModule()

@def_method(m, self.source)
@def_method(m, self.source, single_caller=True)
def _(cond1, cond2, cond3):
with condition(m, nonblocking=self.nonblocking, priority=self.priority) as branch:
with branch(cond1):
Expand All @@ -49,7 +49,7 @@ class TestCondition(TestCaseWithSimulator):
@pytest.mark.parametrize("priority", [False, True])
@pytest.mark.parametrize("catchall", [False, True])
def test_condition(self, nonblocking: bool, priority: bool, catchall: bool):
target = TestbenchIO(Adapter(i=[("cond", 2)]))
target = TestbenchIO(Adapter.create(i=[("cond", 2)]))

circ = SimpleTestCircuit(
ConditionTestCircuit(target.adapter.iface, nonblocking=nonblocking, priority=priority, catchall=catchall),
Expand Down
10 changes: 5 additions & 5 deletions test/lib/test_transformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def otransform_dict(_, v: MethodStruct) -> RecordDict:
itransform = itransform_rec
otransform = otransform_rec

m.submodules.target = self.target = TestbenchIO(Adapter(i=layout, o=layout))
m.submodules.target = self.target = TestbenchIO(Adapter.create(i=layout, o=layout))

if self.use_methods:
imeth = Method(i=layout, o=layout)
Expand Down Expand Up @@ -111,8 +111,8 @@ class TestMethodFilter(TestCaseWithSimulator):
def initialize(self):
self.iosize = 4
self.layout = data_layout(self.iosize)
self.target = TestbenchIO(Adapter(i=self.layout, o=self.layout))
self.cmeth = TestbenchIO(Adapter(i=self.layout, o=data_layout(1)))
self.target = TestbenchIO(Adapter.create(i=self.layout, o=self.layout))
self.cmeth = TestbenchIO(Adapter.create(i=self.layout, o=data_layout(1)))

async def source(self, sim: TestbenchContext):
for i in range(2**self.iosize):
Expand Down Expand Up @@ -165,7 +165,7 @@ def elaborate(self, platform):
methods = []

for k in range(self.targets):
tgt = TestbenchIO(Adapter(i=layout, o=layout))
tgt = TestbenchIO(Adapter.create(i=layout, o=layout))
methods.append(tgt.adapter.iface)
self.target.append(tgt)
m.submodules += tgt
Expand Down Expand Up @@ -280,7 +280,7 @@ def elaborate(self, platform):
methods = []

for k in range(self.targets):
tgt = TestbenchIO(Adapter(i=layout, o=layout))
tgt = TestbenchIO(Adapter.create(i=layout, o=layout))
methods.append(tgt.adapter.iface)
self.target.append(tgt)
m.submodules += tgt
Expand Down
22 changes: 11 additions & 11 deletions test/test_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ def elaborate(self, platform):
meth = Method(i=data_layout(1))

m.submodules.tb = self.tb = TestbenchIO(AdapterTrans(meth))
m.submodules.out = self.out = TestbenchIO(Adapter())
m.submodules.out = self.out = TestbenchIO(Adapter.create())

@def_method(m, meth)
def _(arg):
Expand Down Expand Up @@ -456,7 +456,7 @@ def elaborate(self, platform):
m = TModule()

self.ready = Signal()
m.submodules.tb = self.tb = TestbenchIO(Adapter())
m.submodules.tb = self.tb = TestbenchIO(Adapter.create())

with Transaction().body(m, request=self.ready):
self.tb.adapter.iface(m)
Expand All @@ -469,7 +469,7 @@ def elaborate(self, platform):
m = TModule()

self.ready = Signal()
m.submodules.tb = self.tb = TestbenchIO(Adapter())
m.submodules.tb = self.tb = TestbenchIO(Adapter.create())

with m.If(self.ready):
with Transaction().body(m):
Expand Down Expand Up @@ -538,9 +538,9 @@ def elaborate(self, platform):
self.running = Signal()
self.data = Signal(WIDTH)

method = Method(o=data_layout(WIDTH), nonexclusive=True)
method = Method(o=data_layout(WIDTH))

@def_method(m, method, self.ready)
@def_method(m, method, self.ready, nonexclusive=True)
def _():
m.d.comb += self.running.eq(1)
return {"data": self.data}
Expand Down Expand Up @@ -594,20 +594,20 @@ def elaborate(self, platform):
self.running1 = Signal()
self.running2 = Signal()

method1 = Method(o=data_layout(WIDTH), nonexclusive=True)
method2 = Method(o=data_layout(WIDTH), nonexclusive=self.two_nonexclusive)
method1 = Method(o=data_layout(WIDTH))
method2 = Method(o=data_layout(WIDTH))
method_in = Method(o=data_layout(WIDTH))

@def_method(m, method_in)
def _():
return {"data": 0}

@def_method(m, method1)
@def_method(m, method1, nonexclusive=True)
def _():
m.d.comb += self.running1.eq(1)
return method_in(m)

@def_method(m, method2)
@def_method(m, method2, nonexclusive=self.two_nonexclusive)
def _():
m.d.comb += self.running2.eq(1)
return method_in(m)
Expand Down Expand Up @@ -649,9 +649,9 @@ def combiner(m: Module, args: Sequence[MethodStruct], runs: Value) -> AssignArg:
result = result ^ Mux(runs[i], v.data, 0)
return {"data": result}

method = Method(i=data_layout(WIDTH), o=data_layout(WIDTH), nonexclusive=True, combiner=combiner)
method = Method(i=data_layout(WIDTH), o=data_layout(WIDTH))

@def_method(m, method, self.ready)
@def_method(m, method, self.ready, nonexclusive=True, combiner=combiner)
def _(data: Value):
m.d.comb += self.running.eq(1)
return {"data": data}
Expand Down
2 changes: 1 addition & 1 deletion test/test_simultaneous.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def elaborate(self, platform):

class TestTransitivity(TestCaseWithSimulator):
def test_transitivity(self):
target = TestbenchIO(Adapter(i=[("data", 2)]))
target = TestbenchIO(Adapter.create(i=[("data", 2)]))
req1 = Signal()
req2 = Signal()

Expand Down
2 changes: 1 addition & 1 deletion test/testing/test_validate_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ValidateArgumentsTestCircuit(Elaboratable):
def elaborate(self, platform):
m = Module()

self.method = TestbenchIO(Adapter(i=data_layout(1), o=data_layout(1)).set(with_validate_arguments=True))
self.method = TestbenchIO(Adapter.create(i=data_layout(1), o=data_layout(1)).set(with_validate_arguments=True))
self.caller1 = TestbenchIO(AdapterTrans(self.method.adapter.iface))
self.caller2 = TestbenchIO(AdapterTrans(self.method.adapter.iface))

Expand Down
1 change: 1 addition & 0 deletions transactron/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
from .transaction import * # noqa: F401
from .manager import * # noqa: F401
from .sugar import * # noqa: F401
from .body import * # noqa: F401
Loading

0 comments on commit 18f9a0a

Please sign in to comment.