Skip to content

Commit 5468750

Browse files
committed
RFC amaranth-lang#36: More clarifications. Add testbench_helper decorator.
1 parent f9fe446 commit 5468750

File tree

1 file changed

+29
-15
lines changed

1 file changed

+29
-15
lines changed

text/0036-async-testbench-functions.md

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ As an example, let's consider a simple stream interface with `valid`, `ready` an
2525
We can then implement `stream_send()` and `stream_recv()` functions like this:
2626

2727
```python
28+
@testbench_helper
2829
async def stream_recv(sim, stream):
2930
await sim.set(stream.ready, 1)
3031
await sim.tick().until(stream.valid)
@@ -36,6 +37,7 @@ async def stream_recv(sim, stream):
3637

3738
return value
3839

40+
@testbench_helper
3941
async def stream_send(sim, stream, value):
4042
await sim.set(stream.data, value)
4143

@@ -50,6 +52,8 @@ async def stream_send(sim, stream, value):
5052

5153
`sim.tick()` replaces the existing `Tick()`. It returns a trigger object that either can be awaited directly, or made conditional through `.until()`.
5254

55+
The `testbench_helper` decorator indicates that this function is only designed to be called from testbench processes and will raise an exception if called elsewhere.
56+
5357
> **Note**
5458
> This simplified example does not include any way of specifying the clock domain of the interface and as such is only directly applicable to single domain simulations.
5559
> A way to attach clock domain information to interfaces is desireable, but out of scope for this RFC.
@@ -156,10 +160,7 @@ The async function must accept an argument `sim`, which will be passed a simulat
156160
The new optional named argument `background` registers the testbench as a background process when true.
157161
Processes created through `add_process` are always registered as background processes (except when registering legacy non-async generator functions).
158162

159-
The simulator context has the following properties and methods:
160-
- `process_type`
161-
- Property, `ProcessType`, indicates the type of the current process.
162-
This property allows a generic simulation helper function to assert that it's being run under the appropriate process type.
163+
The simulator context has the following methods:
163164
- `get(expr: Value) -> int`
164165
- `get(expr: ValueCastable) -> any`
165166
- Returns the value of `expr` when awaited.
@@ -184,36 +185,45 @@ The simulator context has the following properties and methods:
184185
- Create a combinable trigger object for advancing simulation by `interval` seconds.
185186
- `changed(*signals)`
186187
- Create a combinable trigger object for advancing simulation until any signal in `signals` changes.
187-
- `edge(signal, value)`
188+
- `edge(signal, value: int)`
188189
- Create a combinable trigger object for advancing simulation until `signal` is changed to `value`.
189190
`signal` must be a 1-bit signal or a 1-bit slice of a signal.
190-
`value` is a boolean where true indicates rising edge and false indicates falling edge.
191+
Valid values for `value` are `1` for rising edge and `0` for falling edge.
191192
- `posedge(signal)`
192193
- `negedge(signal)`
193194
- Aliases for `edge(signal, 1)` and `edge(signal, 0)` respectively.
194195
- `critical()`
195196
- Context manager.
196197
If the current process is a background process, `async with sim.critical():` makes it a non-background process for the duration of the statement.
197198

198-
The `ProcessType` enum has the following members:
199-
- `BikeshedProcess`
200-
- The current process is a process added by `add_process`.
201-
- `BikeshedTestbench`
202-
- The current process is a testbench process added by `add_testbench`.
203-
204-
A domain trigger object has the following methods:
199+
A domain trigger object is immutable and has the following methods:
205200
- `__await__()`
206201
- Advance simulation. No value is returned.
207202
- `until(condition)`
208203
- Repeat the trigger until `condition` is true.
209204
`condition` is an arbitrary Amaranth expression.
210205
If `condition` is initially true, `await` will return immediately without advancing simulation.
211206
The return value is an unspecified awaitable with `await` as the only defined operation.
207+
It is only awaitable once and awaiting it returns no value.
208+
- Example implementation:
209+
```python
210+
async def until(self, condition):
211+
while not await self._sim.get(condition):
212+
await self
213+
```
212214
- `repeat(times: int)`
213215
- Repeat the trigger `times` times.
216+
Valid values are `times >= 0`.
214217
The return value is an unspecified awaitable with `await` as the only defined operation.
215-
216-
A combinable trigger object has the following methods:
218+
It is only awaitable once and awaiting it returns no value.
219+
- Example implementation:
220+
```python
221+
async def repeat(self, times):
222+
for _ in range(times):
223+
await self
224+
```
225+
226+
A combinable trigger object is immutable and has the following methods:
217227
- `__await__()`
218228
- Advance simulation and return the value(s) of the trigger(s).
219229
- `delay` and `edge` triggers return `True` when they are hit, otherwise `False`.
@@ -230,6 +240,10 @@ A combinable trigger object has the following methods:
230240
- Create a new trigger object by copying the current object and appending another trigger.
231241
- Awaiting the returned trigger object pauses the process until the first of the combined triggers hit, i.e. the triggers are combined using OR semantics.
232242

243+
To ensure testbench helper functions are only called from a testbench process, the `amaranth.sim.testbench_helper` decorator is added.
244+
The function wrapper expects the first positional argument (or second, after `self` or `cls` if decorating a method/classmethod) to be a simulator context, and will raise `TypeError` if not.
245+
If the function is called outside a testbench process, an exception will be raised.
246+
233247
`Tick()`, `Delay()`, `Active()` and `Passive()` as well as the ability to pass generator coroutines as `process` are deprecated and removed in a future version.
234248

235249
## Drawbacks

0 commit comments

Comments
 (0)