tests: stop using implicit ports.

This commit is contained in:
Wanda 2024-02-12 01:15:50 +01:00 committed by Catherine
parent 18e5bcd6f7
commit 2d42d649ee
3 changed files with 77 additions and 41 deletions

View file

@ -75,16 +75,18 @@ class DecoderTestCase(FHDLTestCase):
class ReversibleSpec(Elaboratable): class ReversibleSpec(Elaboratable):
def __init__(self, encoder_cls, decoder_cls, args): def __init__(self, encoder_cls, decoder_cls, i_width, args):
self.encoder_cls = encoder_cls self.encoder_cls = encoder_cls
self.decoder_cls = decoder_cls self.decoder_cls = decoder_cls
self.coder_args = args self.coder_args = args
self.i = Signal(i_width)
def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
enc, dec = self.encoder_cls(*self.coder_args), self.decoder_cls(*self.coder_args) enc, dec = self.encoder_cls(*self.coder_args), self.decoder_cls(*self.coder_args)
m.submodules += enc, dec m.submodules += enc, dec
m.d.comb += [ m.d.comb += [
enc.i.eq(self.i),
dec.i.eq(enc.o), dec.i.eq(enc.o),
Assert(enc.i == dec.o) Assert(enc.i == dec.o)
] ]
@ -92,16 +94,20 @@ class ReversibleSpec(Elaboratable):
class HammingDistanceSpec(Elaboratable): class HammingDistanceSpec(Elaboratable):
def __init__(self, distance, encoder_cls, args): def __init__(self, distance, encoder_cls, i_width, args):
self.distance = distance self.distance = distance
self.encoder_cls = encoder_cls self.encoder_cls = encoder_cls
self.coder_args = args self.coder_args = args
self.i1 = Signal(i_width)
self.i2 = Signal(i_width)
def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
enc1, enc2 = self.encoder_cls(*self.coder_args), self.encoder_cls(*self.coder_args) enc1, enc2 = self.encoder_cls(*self.coder_args), self.encoder_cls(*self.coder_args)
m.submodules += enc1, enc2 m.submodules += enc1, enc2
m.d.comb += [ m.d.comb += [
enc1.i.eq(self.i1),
enc2.i.eq(self.i2),
Assume(enc1.i + 1 == enc2.i), Assume(enc1.i + 1 == enc2.i),
Assert(sum(enc1.o ^ enc2.o) == self.distance) Assert(sum(enc1.o ^ enc2.o) == self.distance)
] ]
@ -110,9 +116,10 @@ class HammingDistanceSpec(Elaboratable):
class GrayCoderTestCase(FHDLTestCase): class GrayCoderTestCase(FHDLTestCase):
def test_reversible(self): def test_reversible(self):
spec = ReversibleSpec(encoder_cls=GrayEncoder, decoder_cls=GrayDecoder, args=(16,)) spec = ReversibleSpec(encoder_cls=GrayEncoder, decoder_cls=GrayDecoder, i_width=16,
self.assertFormal(spec, mode="prove") args=(16,))
self.assertFormal(spec, [spec.i], mode="prove")
def test_distance(self): def test_distance(self):
spec = HammingDistanceSpec(distance=1, encoder_cls=GrayEncoder, args=(16,)) spec = HammingDistanceSpec(distance=1, encoder_cls=GrayEncoder, i_width=16, args=(16,))
self.assertFormal(spec, mode="prove") self.assertFormal(spec, [spec.i1, spec.i2], mode="prove")

View file

@ -123,18 +123,31 @@ class FIFOModelEquivalenceSpec(Elaboratable):
signals, the behavior of the implementation under test exactly matches the ideal model, signals, the behavior of the implementation under test exactly matches the ideal model,
except for behavior not defined by the model. except for behavior not defined by the model.
""" """
def __init__(self, fifo, r_domain, w_domain): def __init__(self, fifo, *, is_async=False):
self.fifo = fifo self.fifo = fifo
self.is_async = is_async
self.r_domain = r_domain if is_async:
self.w_domain = w_domain self.cd_read = ClockDomain()
self.cd_write = ClockDomain()
else:
self.cd_sync = ClockDomain()
self.cd_read = self.cd_write = self.cd_sync
@_ignore_deprecated @_ignore_deprecated
def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
if self.is_async:
m.domains += self.cd_read
m.domains += self.cd_write
else:
m.domains += self.cd_sync
m.submodules.dut = dut = self.fifo m.submodules.dut = dut = self.fifo
m.submodules.gold = gold = FIFOModel(width=dut.width, depth=dut.depth, m.submodules.gold = gold = FIFOModel(width=dut.width, depth=dut.depth,
r_domain=self.r_domain, w_domain=self.w_domain) r_domain=self.cd_read.name,
w_domain=self.cd_write.name)
m.d.comb += [ m.d.comb += [
gold.r_en.eq(dut.r_rdy & dut.r_en), gold.r_en.eq(dut.r_rdy & dut.r_en),
@ -158,30 +171,35 @@ class FIFOContractSpec(Elaboratable):
consecutively, they must be read out consecutively at some later point, no matter all other consecutively, they must be read out consecutively at some later point, no matter all other
circumstances, with the exception of reset. circumstances, with the exception of reset.
""" """
def __init__(self, fifo, *, r_domain, w_domain, bound): def __init__(self, fifo, *, is_async=False, bound):
self.fifo = fifo self.fifo = fifo
self.r_domain = r_domain self.is_async = is_async
self.w_domain = w_domain
self.bound = bound self.bound = bound
self.cd_sync = ClockDomain()
if is_async:
self.cd_read = ClockDomain()
self.cd_write = ClockDomain()
else:
self.cd_read = self.cd_write = self.cd_sync
@_ignore_deprecated @_ignore_deprecated
def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
m.submodules.dut = fifo = self.fifo m.submodules.dut = fifo = self.fifo
m.domains += ClockDomain("sync") m.domains += self.cd_sync
m.d.comb += ResetSignal().eq(0) m.d.comb += self.cd_sync.rst.eq(0)
if self.w_domain != "sync": if self.is_async:
m.domains += ClockDomain(self.w_domain) m.domains += self.cd_read
m.d.comb += ResetSignal(self.w_domain).eq(0) m.domains += self.cd_write
if self.r_domain != "sync": m.d.comb += self.cd_write.rst.eq(0)
m.domains += ClockDomain(self.r_domain) m.d.comb += self.cd_read.rst.eq(0)
m.d.comb += ResetSignal(self.r_domain).eq(0)
entry_1 = AnyConst(fifo.width) entry_1 = AnyConst(fifo.width)
entry_2 = AnyConst(fifo.width) entry_2 = AnyConst(fifo.width)
with m.FSM(domain=self.w_domain) as write_fsm: with m.FSM(domain=self.cd_write.name) as write_fsm:
with m.State("WRITE-1"): with m.State("WRITE-1"):
with m.If(fifo.w_rdy): with m.If(fifo.w_rdy):
m.d.comb += [ m.d.comb += [
@ -199,7 +217,7 @@ class FIFOContractSpec(Elaboratable):
with m.State("DONE"): with m.State("DONE"):
pass pass
with m.FSM(domain=self.r_domain) as read_fsm: with m.FSM(domain=self.cd_read.name) as read_fsm:
read_1 = Signal(fifo.width) read_1 = Signal(fifo.width)
read_2 = Signal(fifo.width) read_2 = Signal(fifo.width)
with m.State("READ"): with m.State("READ"):
@ -225,18 +243,18 @@ class FIFOContractSpec(Elaboratable):
with m.If(cycle == self.bound): with m.If(cycle == self.bound):
m.d.comb += Assert(read_fsm.ongoing("DONE")) m.d.comb += Assert(read_fsm.ongoing("DONE"))
with m.If(ResetSignal(domain=self.w_domain)): with m.If(self.cd_write.rst):
m.d.comb += Assert(~fifo.r_rdy) m.d.comb += Assert(~fifo.r_rdy)
if self.w_domain != "sync" or self.r_domain != "sync": if self.is_async:
# rose_w_domain_clk = Rose(ClockSignal(self.w_domain)) # rose_w_domain_clk = Rose(self.cd_write.clk)
past_w_domain_clk = Signal() past_w_domain_clk = Signal()
m.d.sync += past_w_domain_clk.eq(ClockSignal(self.w_domain)) m.d.sync += past_w_domain_clk.eq(self.cd_write.clk)
rose_w_domain_clk = (past_w_domain_clk == 0) & (ClockSignal(self.w_domain) == 1) rose_w_domain_clk = (past_w_domain_clk == 0) & (self.cd_write.clk == 1)
# rose_r_domain_clk = Rose(ClockSignal(self.r_domain)) # rose_r_domain_clk = Rose(self.cd_read.clk)
past_r_domain_clk = Signal() past_r_domain_clk = Signal()
m.d.sync += past_r_domain_clk.eq(ClockSignal(self.r_domain)) m.d.sync += past_r_domain_clk.eq(self.cd_read.clk)
rose_r_domain_clk = (past_r_domain_clk == 0) & (ClockSignal(self.r_domain) == 1) rose_r_domain_clk = (past_r_domain_clk == 0) & (self.cd_read.clk == 1)
m.d.comb += Assume(rose_w_domain_clk | rose_r_domain_clk) m.d.comb += Assume(rose_w_domain_clk | rose_r_domain_clk)
@ -245,10 +263,13 @@ class FIFOContractSpec(Elaboratable):
class FIFOFormalCase(FHDLTestCase): class FIFOFormalCase(FHDLTestCase):
def check_sync_fifo(self, fifo): def check_sync_fifo(self, fifo):
self.assertFormal(FIFOModelEquivalenceSpec(fifo, r_domain="sync", w_domain="sync"), spec_equiv = FIFOModelEquivalenceSpec(fifo, is_async=False)
mode="bmc", depth=fifo.depth + 1) self.assertFormal(spec_equiv, [
self.assertFormal(FIFOContractSpec(fifo, r_domain="sync", w_domain="sync", spec_equiv.cd_sync.clk, spec_equiv.cd_sync.rst,
bound=fifo.depth * 2 + 1), fifo.w_en, fifo.w_data, fifo.r_en,
], mode="bmc", depth=fifo.depth + 1)
spec_contract = FIFOContractSpec(fifo, is_async=False, bound=fifo.depth * 2 + 1)
self.assertFormal(spec_contract, [spec_contract.cd_sync.clk],
mode="hybrid", depth=fifo.depth * 2 + 1) mode="hybrid", depth=fifo.depth * 2 + 1)
def test_sync_pot(self): def test_sync_pot(self):
@ -272,11 +293,19 @@ class FIFOFormalCase(FHDLTestCase):
def check_async_fifo(self, fifo): def check_async_fifo(self, fifo):
# TODO: properly doing model equivalence checking on this likely requires multiclock, # TODO: properly doing model equivalence checking on this likely requires multiclock,
# which is not really documented nor is it clear how to use it. # which is not really documented nor is it clear how to use it.
# self.assertFormal(FIFOModelEquivalenceSpec(fifo, r_domain="read", w_domain="write"), # spec_equiv = FIFOModelEquivalenceSpec(fifo, is_async=True)
# self.assertFormal(spec_equiv, [
# spec_equiv.cd_write.clk, spec_equiv.cd_write.rst,
# spec_equiv.cd_read.clk, spec_equiv.cd_read.rst,
# fifo.w_en, fifo.w_data, fifo.r_en,
# ],
# mode="bmc", depth=fifo.depth * 3 + 1) # mode="bmc", depth=fifo.depth * 3 + 1)
self.assertFormal(FIFOContractSpec(fifo, r_domain="read", w_domain="write", spec_contract = FIFOContractSpec(fifo, is_async=True, bound=fifo.depth * 4 + 1)
bound=fifo.depth * 4 + 1), self.assertFormal(spec_contract, [
mode="hybrid", depth=fifo.depth * 4 + 1) spec_contract.cd_sync.clk,
spec_contract.cd_write.clk,
spec_contract.cd_read.clk,
], mode="hybrid", depth=fifo.depth * 4 + 1)
def test_async(self): def test_async(self):
self.check_async_fifo(AsyncFIFO(width=8, depth=4)) self.check_async_fifo(AsyncFIFO(width=8, depth=4))

View file

@ -26,7 +26,7 @@ class FHDLTestCase(unittest.TestCase):
return repr_str.strip() return repr_str.strip()
self.assertEqual(prepare_repr(repr(obj)), prepare_repr(repr_str)) self.assertEqual(prepare_repr(repr(obj)), prepare_repr(repr_str))
def assertFormal(self, spec, mode="bmc", depth=1): def assertFormal(self, spec, ports=None, mode="bmc", depth=1):
stack = traceback.extract_stack() stack = traceback.extract_stack()
for frame in reversed(stack): for frame in reversed(stack):
if os.path.dirname(__file__) not in frame.filename: if os.path.dirname(__file__) not in frame.filename:
@ -72,7 +72,7 @@ class FHDLTestCase(unittest.TestCase):
mode=mode, mode=mode,
depth=depth, depth=depth,
script=script, script=script,
rtlil=rtlil.convert_fragment(Fragment.get(spec, platform="formal").prepare())[0] rtlil=rtlil.convert(spec, ports=ports, platform="formal"),
) )
with subprocess.Popen( with subprocess.Popen(
[require_tool("sby"), "-f", "-d", spec_name], [require_tool("sby"), "-f", "-d", spec_name],