@@ -81,6 +81,7 @@ def __init__(self, toplevel, *, engine="pysim"):
81
81
self ._design = Fragment .get (toplevel , platform = None ).prepare ()
82
82
self ._engine = engine (self ._design )
83
83
self ._clocked = set ()
84
+ self ._running = False
84
85
85
86
def add_clock (self , period , * , phase = None , domain = "sync" , if_exists = False ):
86
87
"""Add a clock to the simulation.
@@ -103,14 +104,17 @@ def add_clock(self, period, *, phase=None, domain="sync", if_exists=False):
103
104
a clock domain with that name, and :py:`if_exists` is :py:`False`.
104
105
:exc:`~amaranth.hdl.DriverConflict`
105
106
If :py:`domain` already has a clock driving it.
107
+ :exc:`RuntimeError`
108
+ If the simulation has been advanced since its creation or last reset.
106
109
"""
110
+ if self ._running :
111
+ raise RuntimeError (r"Cannot add a clock to a running simulation" )
107
112
if isinstance (domain , ClockDomain ):
108
113
if (domain .name in self ._design .fragment .domains and
109
114
domain is not self ._design .fragment .domains [domain .name ]):
110
115
warnings .warn (
111
- f"Adding a clock process that drives a clock domain object "
112
- f"named { domain .name !r} , which is distinct from an identically named domain "
113
- f"in the simulated design" ,
116
+ f"Adding a clock that drives a clock domain object named { domain .name !r} , "
117
+ f"which is distinct from an identically named domain in the simulated design" ,
114
118
UserWarning , stacklevel = 2 )
115
119
elif domain in self ._design .fragment .domains :
116
120
domain = self ._design .fragment .domains [domain ]
@@ -166,7 +170,14 @@ async def testbench(ctx):
166
170
At each point in time, all of the non-waiting testbenches are executed in the order in
167
171
which they were added. If two testbenches share state, or must manipulate the design in
168
172
a coordinated way, they may rely on this execution order for correctness.
173
+
174
+ Raises
175
+ ------
176
+ :exc:`RuntimeError`
177
+ If the simulation has been advanced since its creation or last reset.
169
178
"""
179
+ if self ._running :
180
+ raise RuntimeError (r"Cannot add a testbench to a running simulation" )
170
181
constructor = self ._check_function (constructor , kind = "testbench" )
171
182
if inspect .iscoroutinefunction (constructor ):
172
183
self ._engine .add_async_testbench (self , constructor , background = background )
@@ -216,7 +227,14 @@ async def process(ctx):
216
227
if it is not intended to be a part of a circuit), with access to it synchronized using
217
228
:py:`await ctx.tick().sample(...)`. Such state is visible in a waveform viewer,
218
229
simplifying debugging.
230
+
231
+ Raises
232
+ ------
233
+ :exc:`RuntimeError`
234
+ If the simulation has been advanced since its creation or last reset.
219
235
"""
236
+ if self ._running :
237
+ raise RuntimeError (r"Cannot add a process to a running simulation" )
220
238
process = self ._check_function (process , kind = "process" )
221
239
if inspect .iscoroutinefunction (process ):
222
240
self ._engine .add_async_process (self , process )
@@ -311,6 +329,7 @@ def advance(self):
311
329
Returns :py:`True` if the simulation contains any critical testbenches or processes, and
312
330
:py:`False` otherwise.
313
331
"""
332
+ self ._running = True
314
333
return self ._engine .advance ()
315
334
316
335
def write_vcd (self , vcd_file , gtkw_file = None , * , traces = (), fs_per_delta = 0 ):
@@ -390,3 +409,4 @@ def reset(self):
390
409
* Each clock, testbench, and process is restarted.
391
410
"""
392
411
self ._engine .reset ()
412
+ self ._running = False
0 commit comments