amaranth
amaranth copied to clipboard
`io.SimulationPort` invites undetected driver-driver conflicts that nondeterministically break semantics
Repro:
from amaranth import *
from amaranth.lib import io
from amaranth.sim import Simulator
port = io.SimulationPort("io", 2)
m = Module()
m.submodules.p0 = p0 = io.FFBuffer("io", port[0])
m.submodules.p1 = p1 = io.FFBuffer("io", port[1])
m.d.comb += p0.o.eq(1)
m.d.comb += p1.o.eq(1)
async def testbench(ctx):
await ctx.tick()
print(f"{ctx.get(port.o):02b}")
sim = Simulator(m)
sim.add_clock(1e-6)
sim.add_testbench(testbench)
sim.run()
This should always output 11, but outputs either 01 or 10.
The cause is a driver-driver conflict that isn't detected by pysim (since nothing in pysim can do that). Running verilog.convert(m, ports=[]) on it detects the conflict.
Although it looks like it would be a valid workaround (there is no driver-driver conflict both according to language semantics and according to verilog.convert()), this still fails with the same nondeterministic output:
port_0 = io.SimulationPort("io", 1)
port_1 = io.SimulationPort("io", 1)
port = port_0 + port_1
m = Module()
m.submodules.p0 = p0 = io.FFBuffer("io", port[0])
m.submodules.p1 = p1 = io.FFBuffer("io", port[1])
m.d.comb += p0.o.eq(1)
m.d.comb += p1.o.eq(1)
This does produce the expected 11 output:
port_0 = io.SimulationPort("io", 1)
port_1 = io.SimulationPort("io", 1)
port = port_0 + port_1
m = Module()
m.submodules.p0 = p0 = io.FFBuffer("io", port_0)
m.submodules.p1 = p1 = io.FFBuffer("io", port_1)
m.d.comb += p0.o.eq(1)
m.d.comb += p1.o.eq(1)