test.compat: import tests from Migen as appropriate.
test_signed and test_coding are adjusted slightly to account for differences in comb propagation between the simulators; we might want to revert that eventually.
This commit is contained in:
parent
f71e0fffbb
commit
4922a73c5d
|
@ -185,8 +185,8 @@ Compatibility summary
|
||||||
- (⊙) `core` **brk**
|
- (⊙) `core` **brk**
|
||||||
- (⊙) `vcd` **brk** → `vcd`
|
- (⊙) `vcd` **brk** → `vcd`
|
||||||
- (⊙) `Simulator` **brk**
|
- (⊙) `Simulator` **brk**
|
||||||
- (+) `run_simulation` **obs** → `.back.pysim.Simulator`
|
- (⊕) `run_simulation` **obs** → `.back.pysim.Simulator`
|
||||||
- (−) `passive` **obs** → `.hdl.ast.Passive`
|
- (⊕) `passive` **obs** → `.hdl.ast.Passive`
|
||||||
- (−) `build` ?
|
- (−) `build` ?
|
||||||
- (+) `util` **obs**
|
- (+) `util` **obs**
|
||||||
- (+) `misc` ⇒ `.tools`
|
- (+) `misc` ⇒ `.tools`
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import functools
|
||||||
|
import collections
|
||||||
|
import inspect
|
||||||
from ...back.pysim import *
|
from ...back.pysim import *
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["run_simulation"]
|
__all__ = ["run_simulation", "passive"]
|
||||||
|
|
||||||
|
|
||||||
def run_simulation(fragment_or_module, generators, clocks={"sync": 10}, vcd_name=None,
|
def run_simulation(fragment_or_module, generators, clocks={"sync": 10}, vcd_name=None,
|
||||||
|
@ -19,6 +22,18 @@ def run_simulation(fragment_or_module, generators, clocks={"sync": 10}, vcd_name
|
||||||
with Simulator(fragment, vcd_file=open(vcd_name, "w") if vcd_name else None) as sim:
|
with Simulator(fragment, vcd_file=open(vcd_name, "w") if vcd_name else None) as sim:
|
||||||
for domain, period in clocks.items():
|
for domain, period in clocks.items():
|
||||||
sim.add_clock(period / 1e9, domain=domain)
|
sim.add_clock(period / 1e9, domain=domain)
|
||||||
for domain, process in generators.items():
|
for domain, processes in generators.items():
|
||||||
sim.add_sync_process(process, domain=domain)
|
if isinstance(processes, collections.Iterable) and not inspect.isgenerator(processes):
|
||||||
|
for process in processes:
|
||||||
|
sim.add_sync_process(process, domain=domain)
|
||||||
|
else:
|
||||||
|
sim.add_sync_process(processes, domain=domain)
|
||||||
sim.run()
|
sim.run()
|
||||||
|
|
||||||
|
|
||||||
|
def passive(generator):
|
||||||
|
@functools.wraps(generator)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
yield Passive()
|
||||||
|
yield from generator(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
0
nmigen/test/compat/__init__.py
Normal file
0
nmigen/test/compat/__init__.py
Normal file
13
nmigen/test/compat/support.py
Normal file
13
nmigen/test/compat/support.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
from ...compat import *
|
||||||
|
# from ...compat.fhdl import verilog
|
||||||
|
|
||||||
|
|
||||||
|
class SimCase:
|
||||||
|
def setUp(self, *args, **kwargs):
|
||||||
|
self.tb = self.TestBench(*args, **kwargs)
|
||||||
|
|
||||||
|
# def test_to_verilog(self):
|
||||||
|
# verilog.convert(self.tb)
|
||||||
|
|
||||||
|
def run_with(self, generator):
|
||||||
|
run_simulation(self.tb, generator)
|
114
nmigen/test/compat/test_coding.py
Normal file
114
nmigen/test/compat/test_coding.py
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from ...compat import *
|
||||||
|
from ...compat.genlib.coding import *
|
||||||
|
|
||||||
|
from .support import SimCase
|
||||||
|
|
||||||
|
|
||||||
|
class EncCase(SimCase, unittest.TestCase):
|
||||||
|
class TestBench(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.submodules.dut = Encoder(8)
|
||||||
|
|
||||||
|
def test_sizes(self):
|
||||||
|
self.assertEqual(len(self.tb.dut.i), 8)
|
||||||
|
self.assertEqual(len(self.tb.dut.o), 3)
|
||||||
|
self.assertEqual(len(self.tb.dut.n), 1)
|
||||||
|
|
||||||
|
def test_run_sequence(self):
|
||||||
|
seq = list(range(1<<8))
|
||||||
|
def gen():
|
||||||
|
for _ in range(256):
|
||||||
|
if seq:
|
||||||
|
yield self.tb.dut.i.eq(seq.pop(0))
|
||||||
|
yield
|
||||||
|
if (yield self.tb.dut.n):
|
||||||
|
self.assertNotIn((yield self.tb.dut.i), [1<<i for i in range(8)])
|
||||||
|
else:
|
||||||
|
self.assertEqual((yield self.tb.dut.i), 1<<(yield self.tb.dut.o))
|
||||||
|
self.run_with(gen())
|
||||||
|
|
||||||
|
|
||||||
|
class PrioEncCase(SimCase, unittest.TestCase):
|
||||||
|
class TestBench(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.submodules.dut = PriorityEncoder(8)
|
||||||
|
|
||||||
|
def test_sizes(self):
|
||||||
|
self.assertEqual(len(self.tb.dut.i), 8)
|
||||||
|
self.assertEqual(len(self.tb.dut.o), 3)
|
||||||
|
self.assertEqual(len(self.tb.dut.n), 1)
|
||||||
|
|
||||||
|
def test_run_sequence(self):
|
||||||
|
seq = list(range(1<<8))
|
||||||
|
def gen():
|
||||||
|
for _ in range(256):
|
||||||
|
if seq:
|
||||||
|
yield self.tb.dut.i.eq(seq.pop(0))
|
||||||
|
yield
|
||||||
|
i = yield self.tb.dut.i
|
||||||
|
if (yield self.tb.dut.n):
|
||||||
|
self.assertEqual(i, 0)
|
||||||
|
else:
|
||||||
|
o = yield self.tb.dut.o
|
||||||
|
if o > 0:
|
||||||
|
self.assertEqual(i & 1<<(o - 1), 0)
|
||||||
|
self.assertGreaterEqual(i, 1<<o)
|
||||||
|
self.run_with(gen())
|
||||||
|
|
||||||
|
|
||||||
|
class DecCase(SimCase, unittest.TestCase):
|
||||||
|
class TestBench(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.submodules.dut = Decoder(8)
|
||||||
|
|
||||||
|
def test_sizes(self):
|
||||||
|
self.assertEqual(len(self.tb.dut.i), 3)
|
||||||
|
self.assertEqual(len(self.tb.dut.o), 8)
|
||||||
|
self.assertEqual(len(self.tb.dut.n), 1)
|
||||||
|
|
||||||
|
def test_run_sequence(self):
|
||||||
|
seq = list(range(8*2))
|
||||||
|
def gen():
|
||||||
|
for _ in range(256):
|
||||||
|
if seq:
|
||||||
|
i = seq.pop()
|
||||||
|
yield self.tb.dut.i.eq(i//2)
|
||||||
|
yield self.tb.dut.n.eq(i%2)
|
||||||
|
yield
|
||||||
|
i = yield self.tb.dut.i
|
||||||
|
o = yield self.tb.dut.o
|
||||||
|
if (yield self.tb.dut.n):
|
||||||
|
self.assertEqual(o, 0)
|
||||||
|
else:
|
||||||
|
self.assertEqual(o, 1<<i)
|
||||||
|
self.run_with(gen())
|
||||||
|
|
||||||
|
|
||||||
|
class SmallPrioEncCase(SimCase, unittest.TestCase):
|
||||||
|
class TestBench(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.submodules.dut = PriorityEncoder(1)
|
||||||
|
|
||||||
|
def test_sizes(self):
|
||||||
|
self.assertEqual(len(self.tb.dut.i), 1)
|
||||||
|
self.assertEqual(len(self.tb.dut.o), 1)
|
||||||
|
self.assertEqual(len(self.tb.dut.n), 1)
|
||||||
|
|
||||||
|
def test_run_sequence(self):
|
||||||
|
seq = list(range(1))
|
||||||
|
def gen():
|
||||||
|
for _ in range(5):
|
||||||
|
if seq:
|
||||||
|
yield self.tb.dut.i.eq(seq.pop(0))
|
||||||
|
yield
|
||||||
|
i = yield self.tb.dut.i
|
||||||
|
if (yield self.tb.dut.n):
|
||||||
|
self.assertEqual(i, 0)
|
||||||
|
else:
|
||||||
|
o = yield self.tb.dut.o
|
||||||
|
if o > 0:
|
||||||
|
self.assertEqual(i & 1<<(o - 1), 0)
|
||||||
|
self.assertGreaterEqual(i, 1<<o)
|
||||||
|
self.run_with(gen())
|
29
nmigen/test/compat/test_constant.py
Normal file
29
nmigen/test/compat/test_constant.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from ...compat import *
|
||||||
|
from .support import SimCase
|
||||||
|
|
||||||
|
|
||||||
|
class ConstantCase(SimCase, unittest.TestCase):
|
||||||
|
class TestBench(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.sigs = [
|
||||||
|
(Signal(3), Constant(0), 0),
|
||||||
|
(Signal(3), Constant(5), 5),
|
||||||
|
(Signal(3), Constant(1, 2), 1),
|
||||||
|
(Signal(3), Constant(-1, 7), 7),
|
||||||
|
(Signal(3), Constant(0b10101)[:3], 0b101),
|
||||||
|
(Signal(3), Constant(0b10101)[1:4], 0b10),
|
||||||
|
(Signal(4), Constant(0b1100)[::-1], 0b0011),
|
||||||
|
]
|
||||||
|
self.comb += [a.eq(b) for a, b, c in self.sigs]
|
||||||
|
|
||||||
|
def test_comparisons(self):
|
||||||
|
def gen():
|
||||||
|
for s, l, v in self.tb.sigs:
|
||||||
|
s = yield s
|
||||||
|
self.assertEqual(
|
||||||
|
s, int(v),
|
||||||
|
"got {}, want {} from literal {}".format(
|
||||||
|
s, v, l))
|
||||||
|
self.run_with(gen())
|
56
nmigen/test/compat/test_fifo.py
Normal file
56
nmigen/test/compat/test_fifo.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import unittest
|
||||||
|
from itertools import count
|
||||||
|
|
||||||
|
from ...compat import *
|
||||||
|
from ...compat.genlib.fifo import SyncFIFO
|
||||||
|
|
||||||
|
from .support import SimCase
|
||||||
|
|
||||||
|
|
||||||
|
class SyncFIFOCase(SimCase, unittest.TestCase):
|
||||||
|
class TestBench(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.submodules.dut = SyncFIFO(64, 2)
|
||||||
|
|
||||||
|
self.sync += [
|
||||||
|
If(self.dut.we & self.dut.writable,
|
||||||
|
self.dut.din[:32].eq(self.dut.din[:32] + 1),
|
||||||
|
self.dut.din[32:].eq(self.dut.din[32:] + 2)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_run_sequence(self):
|
||||||
|
seq = list(range(20))
|
||||||
|
def gen():
|
||||||
|
for cycle in count():
|
||||||
|
# fire re and we at "random"
|
||||||
|
yield self.tb.dut.we.eq(cycle % 2 == 0)
|
||||||
|
yield self.tb.dut.re.eq(cycle % 3 == 0)
|
||||||
|
# the output if valid must be correct
|
||||||
|
if (yield self.tb.dut.readable) and (yield self.tb.dut.re):
|
||||||
|
try:
|
||||||
|
i = seq.pop(0)
|
||||||
|
except IndexError:
|
||||||
|
break
|
||||||
|
self.assertEqual((yield self.tb.dut.dout[:32]), i)
|
||||||
|
self.assertEqual((yield self.tb.dut.dout[32:]), i*2)
|
||||||
|
yield
|
||||||
|
self.run_with(gen())
|
||||||
|
|
||||||
|
def test_replace(self):
|
||||||
|
seq = [x for x in range(20) if x % 5]
|
||||||
|
def gen():
|
||||||
|
for cycle in count():
|
||||||
|
yield self.tb.dut.we.eq(cycle % 2 == 0)
|
||||||
|
yield self.tb.dut.re.eq(cycle % 7 == 0)
|
||||||
|
yield self.tb.dut.replace.eq(
|
||||||
|
(yield self.tb.dut.din[:32]) % 5 == 1)
|
||||||
|
if (yield self.tb.dut.readable) and (yield self.tb.dut.re):
|
||||||
|
try:
|
||||||
|
i = seq.pop(0)
|
||||||
|
except IndexError:
|
||||||
|
break
|
||||||
|
self.assertEqual((yield self.tb.dut.dout[:32]), i)
|
||||||
|
self.assertEqual((yield self.tb.dut.dout[32:]), i*2)
|
||||||
|
yield
|
||||||
|
self.run_with(gen())
|
87
nmigen/test/compat/test_fsm.py
Normal file
87
nmigen/test/compat/test_fsm.py
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import unittest
|
||||||
|
from itertools import count
|
||||||
|
|
||||||
|
from ...compat import *
|
||||||
|
from ...compat.genlib.fsm import FSM
|
||||||
|
|
||||||
|
from .support import SimCase
|
||||||
|
|
||||||
|
|
||||||
|
class FSMCase(SimCase, unittest.TestCase):
|
||||||
|
class TestBench(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.ctrl = Signal()
|
||||||
|
self.data = Signal()
|
||||||
|
self.status = Signal(8)
|
||||||
|
|
||||||
|
self.submodules.dut = FSM()
|
||||||
|
self.dut.act("IDLE",
|
||||||
|
If(self.ctrl,
|
||||||
|
NextState("START")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.dut.act("START",
|
||||||
|
If(self.data,
|
||||||
|
NextState("SET-STATUS-LOW")
|
||||||
|
).Else(
|
||||||
|
NextState("SET-STATUS")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.dut.act("SET-STATUS",
|
||||||
|
NextValue(self.status, 0xaa),
|
||||||
|
NextState("IDLE")
|
||||||
|
)
|
||||||
|
self.dut.act("SET-STATUS-LOW",
|
||||||
|
NextValue(self.status[:4], 0xb),
|
||||||
|
NextState("IDLE")
|
||||||
|
)
|
||||||
|
|
||||||
|
def assertState(self, fsm, state):
|
||||||
|
self.assertEqual(fsm.decoding[(yield fsm.state)], state)
|
||||||
|
|
||||||
|
def test_next_state(self):
|
||||||
|
def gen():
|
||||||
|
yield from self.assertState(self.tb.dut, "IDLE")
|
||||||
|
yield
|
||||||
|
yield from self.assertState(self.tb.dut, "IDLE")
|
||||||
|
yield self.tb.ctrl.eq(1)
|
||||||
|
yield
|
||||||
|
yield from self.assertState(self.tb.dut, "IDLE")
|
||||||
|
yield self.tb.ctrl.eq(0)
|
||||||
|
yield
|
||||||
|
yield from self.assertState(self.tb.dut, "START")
|
||||||
|
yield
|
||||||
|
yield from self.assertState(self.tb.dut, "SET-STATUS")
|
||||||
|
yield self.tb.ctrl.eq(1)
|
||||||
|
yield
|
||||||
|
yield from self.assertState(self.tb.dut, "IDLE")
|
||||||
|
yield self.tb.ctrl.eq(0)
|
||||||
|
yield self.tb.data.eq(1)
|
||||||
|
yield
|
||||||
|
yield from self.assertState(self.tb.dut, "START")
|
||||||
|
yield self.tb.data.eq(0)
|
||||||
|
yield
|
||||||
|
yield from self.assertState(self.tb.dut, "SET-STATUS-LOW")
|
||||||
|
self.run_with(gen())
|
||||||
|
|
||||||
|
def test_next_value(self):
|
||||||
|
def gen():
|
||||||
|
self.assertEqual((yield self.tb.status), 0x00)
|
||||||
|
yield self.tb.ctrl.eq(1)
|
||||||
|
yield
|
||||||
|
yield self.tb.ctrl.eq(0)
|
||||||
|
yield
|
||||||
|
yield
|
||||||
|
yield from self.assertState(self.tb.dut, "SET-STATUS")
|
||||||
|
yield self.tb.ctrl.eq(1)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield self.tb.status), 0xaa)
|
||||||
|
yield self.tb.ctrl.eq(0)
|
||||||
|
yield self.tb.data.eq(1)
|
||||||
|
yield
|
||||||
|
yield self.tb.data.eq(0)
|
||||||
|
yield
|
||||||
|
yield from self.assertState(self.tb.dut, "SET-STATUS-LOW")
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield self.tb.status), 0xab)
|
||||||
|
self.run_with(gen())
|
23
nmigen/test/compat/test_passive.py
Normal file
23
nmigen/test/compat/test_passive.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from ...compat import *
|
||||||
|
|
||||||
|
|
||||||
|
class PassiveCase(unittest.TestCase):
|
||||||
|
def test_terminates_correctly(self):
|
||||||
|
n = 5
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
@passive
|
||||||
|
def counter():
|
||||||
|
nonlocal count
|
||||||
|
while True:
|
||||||
|
yield
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
def terminator():
|
||||||
|
for i in range(n):
|
||||||
|
yield
|
||||||
|
|
||||||
|
run_simulation(Module(), [counter(), terminator()])
|
||||||
|
self.assertEqual(count, n)
|
42
nmigen/test/compat/test_signed.py
Normal file
42
nmigen/test/compat/test_signed.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from ...compat import *
|
||||||
|
from .support import SimCase
|
||||||
|
|
||||||
|
|
||||||
|
class SignedCase(SimCase, unittest.TestCase):
|
||||||
|
class TestBench(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.a = Signal((3, True))
|
||||||
|
self.b = Signal((4, True))
|
||||||
|
comps = [
|
||||||
|
lambda p, q: p > q,
|
||||||
|
lambda p, q: p >= q,
|
||||||
|
lambda p, q: p < q,
|
||||||
|
lambda p, q: p <= q,
|
||||||
|
lambda p, q: p == q,
|
||||||
|
lambda p, q: p != q,
|
||||||
|
]
|
||||||
|
self.vals = []
|
||||||
|
for asign in 1, -1:
|
||||||
|
for bsign in 1, -1:
|
||||||
|
for f in comps:
|
||||||
|
r = Signal()
|
||||||
|
r0 = f(asign*self.a, bsign*self.b)
|
||||||
|
self.comb += r.eq(r0)
|
||||||
|
self.vals.append((asign, bsign, f, r, r0.op))
|
||||||
|
|
||||||
|
def test_comparisons(self):
|
||||||
|
def gen():
|
||||||
|
for i in range(-4, 4):
|
||||||
|
yield self.tb.a.eq(i)
|
||||||
|
yield self.tb.b.eq(i)
|
||||||
|
yield
|
||||||
|
a = yield self.tb.a
|
||||||
|
b = yield self.tb.b
|
||||||
|
for asign, bsign, f, r, op in self.tb.vals:
|
||||||
|
r, r0 = (yield r), f(asign*a, bsign*b)
|
||||||
|
self.assertEqual(r, int(r0),
|
||||||
|
"got {}, want {}*{} {} {}*{} = {}".format(
|
||||||
|
r, asign, a, op, bsign, b, r0))
|
||||||
|
self.run_with(gen())
|
19
nmigen/test/compat/test_size.py
Normal file
19
nmigen/test/compat/test_size.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from ...compat import *
|
||||||
|
|
||||||
|
|
||||||
|
def _same_slices(a, b):
|
||||||
|
return a.value is b.value and a.start == b.start and a.stop == b.stop
|
||||||
|
|
||||||
|
|
||||||
|
class SignalSizeCase(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.i = C(0xaa)
|
||||||
|
self.j = C(-127)
|
||||||
|
self.s = Signal((13, True))
|
||||||
|
|
||||||
|
def test_len(self):
|
||||||
|
self.assertEqual(len(self.s), 13)
|
||||||
|
self.assertEqual(len(self.i), 8)
|
||||||
|
self.assertEqual(len(self.j), 8)
|
Loading…
Reference in a new issue