sim: represent time internally as 1ps units
Using floats to represent simulation time internally isn't ideal instead use 1ps internal units while continuing to use a floating point based interface for compatibility. Fixes #535.
This commit is contained in:
parent
fab9fb1fea
commit
1ee2482c6b
|
@ -31,4 +31,4 @@ class PyClockProcess(BaseProcess):
|
|||
else:
|
||||
clk_state = self.state.slots[self.slot]
|
||||
clk_state.set(not clk_state.curr)
|
||||
self.state.wait_interval(self, self.period / 2)
|
||||
self.state.wait_interval(self, self.period // 2)
|
||||
|
|
|
@ -95,7 +95,9 @@ class PyCoroProcess(BaseProcess):
|
|||
return
|
||||
|
||||
elif type(command) is Delay:
|
||||
self.state.wait_interval(self, command.interval)
|
||||
# Internal timeline is in 1ps integeral units, intervals are public API and in floating point
|
||||
interval = int(command.interval * 1e12) if command.interval is not None else None
|
||||
self.state.wait_interval(self, interval)
|
||||
return
|
||||
|
||||
elif type(command) is Passive:
|
||||
|
|
|
@ -130,12 +130,15 @@ class Simulator:
|
|||
if domain in self._clocked:
|
||||
raise ValueError("Domain {!r} already has a clock driving it"
|
||||
.format(domain.name))
|
||||
|
||||
# We represent times internally in 1 ps units, but users supply float quantities of seconds
|
||||
period = int(period * 1e12)
|
||||
|
||||
if phase is None:
|
||||
# By default, delay the first edge by half period. This causes any synchronous activity
|
||||
# to happen at a non-zero time, distinguishing it from the reset values in the waveform
|
||||
# viewer.
|
||||
phase = period / 2
|
||||
phase = period // 2
|
||||
self._engine.add_clock_process(domain.clk, phase=phase, period=period)
|
||||
self._clocked.add(domain)
|
||||
|
||||
|
@ -181,6 +184,8 @@ class Simulator:
|
|||
|
||||
If the simulation stops advancing, this function will never return.
|
||||
"""
|
||||
# Convert deadline in seconds into internal 1 ps units
|
||||
deadline = deadline * 1e12
|
||||
assert self._engine.now <= deadline
|
||||
while (self.advance() or run_passive) and self._engine.now < deadline:
|
||||
pass
|
||||
|
@ -204,7 +209,7 @@ class Simulator:
|
|||
traces : iterable of Signal
|
||||
Signals to display traces for.
|
||||
"""
|
||||
if self._engine.now != 0.0:
|
||||
if self._engine.now != 0:
|
||||
for file in (vcd_file, gtkw_file):
|
||||
if hasattr(file, "close"):
|
||||
file.close()
|
||||
|
|
|
@ -48,10 +48,6 @@ class _NameExtractor:
|
|||
|
||||
|
||||
class _VCDWriter:
|
||||
@staticmethod
|
||||
def timestamp_to_vcd(timestamp):
|
||||
return timestamp * (10 ** 10) # 1/(100 ps)
|
||||
|
||||
@staticmethod
|
||||
def decode_to_vcd(signal, value):
|
||||
return signal.decoder(value).expandtabs().replace(" ", "_")
|
||||
|
@ -65,7 +61,7 @@ class _VCDWriter:
|
|||
self.vcd_vars = SignalDict()
|
||||
self.vcd_file = vcd_file
|
||||
self.vcd_writer = vcd_file and VCDWriter(self.vcd_file,
|
||||
timescale="100 ps", comment="Generated by Amaranth")
|
||||
timescale="1 ps", comment="Generated by Amaranth")
|
||||
|
||||
self.gtkw_names = SignalDict()
|
||||
self.gtkw_file = gtkw_file
|
||||
|
@ -127,16 +123,15 @@ class _VCDWriter:
|
|||
if vcd_var is None:
|
||||
return
|
||||
|
||||
vcd_timestamp = self.timestamp_to_vcd(timestamp)
|
||||
if signal.decoder:
|
||||
var_value = self.decode_to_vcd(signal, value)
|
||||
else:
|
||||
var_value = value
|
||||
self.vcd_writer.change(vcd_var, vcd_timestamp, var_value)
|
||||
self.vcd_writer.change(vcd_var, timestamp, var_value)
|
||||
|
||||
def close(self, timestamp):
|
||||
if self.vcd_writer is not None:
|
||||
self.vcd_writer.close(self.timestamp_to_vcd(timestamp))
|
||||
self.vcd_writer.close(timestamp)
|
||||
|
||||
if self.gtkw_save is not None:
|
||||
self.gtkw_save.dumpfile(self.vcd_file.name)
|
||||
|
@ -158,11 +153,11 @@ class _VCDWriter:
|
|||
|
||||
class _Timeline:
|
||||
def __init__(self):
|
||||
self.now = 0.0
|
||||
self.now = 0
|
||||
self.deadlines = dict()
|
||||
|
||||
def reset(self):
|
||||
self.now = 0.0
|
||||
self.now = 0
|
||||
self.deadlines.clear()
|
||||
|
||||
def at(self, run_at, process):
|
||||
|
|
|
@ -571,6 +571,19 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
|||
self.fail()
|
||||
sim.add_process(process)
|
||||
|
||||
def test_run_until_fail(self):
|
||||
m = Module()
|
||||
s = Signal()
|
||||
m.d.sync += s.eq(0)
|
||||
with self.assertRaises(AssertionError):
|
||||
with self.assertSimulation(m, deadline=100e-6) as sim:
|
||||
sim.add_clock(1e-6)
|
||||
def process():
|
||||
for _ in range(99):
|
||||
yield Delay(1e-6)
|
||||
self.fail()
|
||||
sim.add_process(process)
|
||||
|
||||
def test_add_process_wrong(self):
|
||||
with self.assertSimulation(Module()) as sim:
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
|
|
Loading…
Reference in a new issue