You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As an example, let's consider a simple stream interface with `valid`, `ready` and `data` members.
25
-
On the interface class, we can then implement `.send()` and `.recv()`methods like this:
25
+
We can then implement `stream_send()` and `stream_recv()`functions like this:
26
26
27
27
```python
28
-
classStreamInterface(PureInterface):
29
-
asyncdefrecv(self, sim):
30
-
await sim.set(self.ready, 1)
31
-
await sim.tick().until(self.valid)
28
+
asyncdefstream_recv(sim, stream):
29
+
await sim.set(stream.ready, 1)
30
+
await sim.tick().until(stream.valid)
32
31
33
-
value =await sim.get(self.data)
32
+
value =await sim.get(stream.data)
34
33
35
-
await sim.tick()
36
-
await sim.set(self.ready, 0)
34
+
await sim.tick()
35
+
await sim.set(stream.ready, 0)
37
36
38
-
return value
37
+
return value
39
38
40
-
asyncdefsend(self, sim, value):
41
-
await sim.set(self.data, value)
39
+
asyncdefstream_send(sim, stream, value):
40
+
await sim.set(stream.data, value)
42
41
43
-
await sim.set(self.valid, 1)
44
-
await sim.tick().until(self.ready)
42
+
await sim.set(stream.valid, 1)
43
+
await sim.tick().until(stream.ready)
45
44
46
-
await sim.tick()
47
-
await sim.set(self.valid, 0)
45
+
await sim.tick()
46
+
await sim.set(stream.valid, 0)
48
47
```
49
48
50
49
`await sim.get()` and `await sim.set()` replaces the existing operations `yield signal` and `yield signal.eq()` respectively.
@@ -68,8 +67,8 @@ A testbench could then look like this:
68
67
```python
69
68
asyncdeftest_rgb(sim, r, g, b):
70
69
rgb = {'r': r, 'g': g, 'b': b}
71
-
await dut.input.send(sim, rgb)
72
-
yuv =await dut.output.recv(sim)
70
+
awaitstream_send(sim, dut.input, rgb)
71
+
yuv =awaitstream_recv(sim, dut.output)
73
72
74
73
print(rgb, yuv)
75
74
@@ -81,11 +80,14 @@ async def testbench(sim):
81
80
await test_rgb(sim, 255, 255, 255)
82
81
```
83
82
84
-
Since `.send()` and `.recv()` invokes `sim.get()` and `sim.set()` that in turn will invoke the appropriate value conversions for a value castable (here `data.View`), it is general enough to work for streams with arbitrary shapes.
83
+
Since `stream_send()` and `stream_recv()` invokes `sim.get()` and `sim.set()` that in turn will invoke the appropriate value conversions for a value castable (here `data.View`), it is general enough to work for streams with arbitrary shapes.
85
84
86
85
`Tick()` and `Delay()` are replaced by `sim.tick()` and `sim.delay()` respectively.
87
86
In addition, `sim.changed()` and `sim.edge()` is introduced that allows creating triggers from arbitrary signals.
88
-
These all return a trigger object that can be made conditional through `.until()`.
87
+
88
+
`sim.tick()` return a domain trigger object that can be made conditional through `.until()` or repeated through `.repeat()`.
89
+
90
+
`sim.delay()`, `sim.changed()` and `sim.edge()` return a combinable trigger object that can be used to add additional triggers.
89
91
90
92
`Active()` and `Passive()` are replaced by an `background=False` keyword argument to `.add_testbench()`.
91
93
Processes created through `.add_process()` are always created as background processes.
When a trigger object is awaited, it'll return the value(s) of the trigger(s), and it can also be used as an async generator to repeatedly await the same trigger.
108
+
When a combinable trigger object is awaited, it'll return the value(s) of the trigger(s), and it can also be used as an async generator to repeatedly await the same trigger.
107
109
Multiple triggers can be combined.
108
110
Consider the following examples:
109
111
@@ -147,62 +149,86 @@ The following `Simulator` methods have their signatures updated:
147
149
*`add_process(process)`
148
150
*`add_testbench(process, *, background=False)`
149
151
150
-
The new optional named argument `background` registers the testbench as a background process when true.
151
-
152
152
Both methods are updated to accept an async function passed as `process`.
153
153
The async function must accept an argument `sim`, which will be passed a simulator context.
154
154
(Argument name is just convention, will be passed positionally.)
155
155
156
-
The simulator context have the following methods:
156
+
The new optional named argument `background` registers the testbench as a background process when true.
157
+
Processes created through `add_process` are always registered as background processes (except when registering legacy non-async generator functions).
158
+
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.
157
163
-`get(expr: Value) -> int`
158
164
-`get(expr: ValueCastable) -> any`
159
165
- Returns the value of `expr` when awaited.
160
-
When `expr` is a value-castable, the value will be converted through `.from_bits()`.
166
+
When `expr` is a value-castable, and its `shape()` is a `ShapeCastable`, the value will be converted through the shape's `.from_bits()`.
167
+
Otherwise, a plain integer is returned.
161
168
-`set(expr: Value, value: ConstLike)`
162
169
-`set(expr: ValueCastable, value: any)`
163
170
- Set `expr` to `value` when awaited.
164
-
When `expr` is a value-castable, the value will be converted through `.const()`.
- Write `value` to `address` in `instance` when awaited. If `mask` is given, only the corresponding bits are written.
169
-
-`delay(interval: float)`
170
-
- Create a trigger object for advancing simulation by `interval` seconds.
177
+
Like `MemoryInstance`, these two functions are an internal interface that will be usually only used via `lib.Memory`.
178
+
It comes without a stability guarantee.
171
179
-`tick(domain="sync", *, context=None)`
172
-
- Create a trigger object for advancing simulation until the next active edge of the `domain` clock.
180
+
- Create a domain trigger object for advancing simulation until the next active edge of the `domain` clock.
173
181
When an elaboratable is passed to `context`, `domain` will be resolved from its perspective.
174
182
- If `domain` is asynchronously reset while this is being awaited, `amaranth.sim.AsyncReset` is raised.
183
+
-`delay(interval: float)`
184
+
- Create a combinable trigger object for advancing simulation by `interval` seconds.
175
185
-`changed(*signals)`
176
-
- Create a trigger object for advancing simulation until any signal in `signals` changes.
186
+
- Create a combinable trigger object for advancing simulation until any signal in `signals` changes.
177
187
-`edge(signal, value)`
178
-
- Create a trigger object for advancing simulation until `signal` is changed to `value`.
188
+
- Create a combinable trigger object for advancing simulation until `signal` is changed to `value`.
179
189
`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.
180
191
-`posedge(signal)`
181
192
-`negedge(signal)`
182
193
- Aliases for `edge(signal, 1)` and `edge(signal, 0)` respectively.
183
194
-`critical()`
184
195
- Context manager.
185
196
If the current process is a background process, `async with sim.critical():` makes it a non-background process for the duration of the statement.
186
197
187
-
A trigger object has the following methods:
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:
205
+
-`__await__()`
206
+
- Advance simulation. No value is returned.
207
+
-`until(condition)`
208
+
- Repeat the trigger until `condition` is true.
209
+
`condition` is an arbitrary Amaranth expression.
210
+
If `condition` is initially true, `await` will return immediately without advancing simulation.
211
+
The return value is an unspecified awaitable with `await` as the only defined operation.
212
+
-`repeat(times: int)`
213
+
- Repeat the trigger `times` times.
214
+
The return value is an unspecified awaitable with `await` as the only defined operation.
215
+
216
+
A combinable trigger object has the following methods:
188
217
-`__await__()`
189
218
- Advance simulation and return the value(s) of the trigger(s).
190
-
-`delay`, `tick` and `edge` triggers return `True` when they are hit, otherwise `False`.
219
+
-`delay` and `edge` triggers return `True` when they are hit, otherwise `False`.
191
220
-`changed` triggers return the current value of the signals they are monitoring.
221
+
- At least one of the triggers hit will be reflected in the return value.
222
+
In case of multiple triggers occuring at the same time step, it is unspecified which of these will show up in the return value beyond “at least one”.
192
223
-`__aiter__()`
193
224
- Return an async generator that is equivalent to repeatedly awaiting the trigger object in an infinite loop.
194
225
-`delay(interval: float)`
195
-
-`tick(domain="sync", *, context=None)`
196
226
-`changed(*signals)`
197
227
-`edge(signal, value)`
198
228
-`posedge(signal)`
199
229
-`negedge(signal)`
200
230
- Create a new trigger object by copying the current object and appending another trigger.
201
-
-`until(condition)`
202
-
- Repeat the trigger until `condition` is true.
203
-
`condition` is an arbitrary Amaranth expression.
204
-
If `condition` is initially true, `await` will return immediately without advancing simulation.
205
-
The return value is an unspecified awaitable with `await` as the only defined operation.
231
+
- 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.
206
232
207
233
`Tick()`, `Delay()`, `Active()` and `Passive()` as well as the ability to pass generator coroutines as `process` are deprecated and removed in a future version.
208
234
@@ -231,6 +257,6 @@ Other python libraries like [cocotb](https://docs.cocotb.org/en/stable/coroutine
231
257
## Future possibilities
232
258
[future-possibilities]: #future-possibilities
233
259
234
-
- Add simulation helpers in the manner of `.send()` and `.recv()` to standard interfaces where it makes sense.
260
+
- Add simulation helper methods to standard interfaces where it makes sense.
261
+
- This includes `lib.memory.Memory`.
235
262
- There is a desire for a `sim.time()` method that returns the current simulation time, but it needs a suitable return type to represent seconds with femtosecond resolution and that is out of the scope for this RFC.
236
-
- We ought to have a way to skip a given number of triggers, so that we can tell the simulation engine to e.g. «advance by n cycles».
0 commit comments