Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/nmigen
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 7b25665fde81
Choose a base ref
...
head repository: m-labs/nmigen
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4922a73c5d27
Choose a head ref
  • 2 commits
  • 13 files changed
  • 1 contributor

Commits on Jan 26, 2019

  1. hdl.ast: fix shape calculation for *.

    This was carried over from Migen, and is wrong there too.
    Counterexample: 1'sd-1 * 4'sd-4 = 4'sd-4 (but should be 5'sd4).
    whitequark committed Jan 26, 2019
    Copy the full SHA
    f71e0ff View commit details
  2. 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.
    whitequark committed Jan 26, 2019
    Copy the full SHA
    4922a73 View commit details
4 changes: 2 additions & 2 deletions doc/COMPAT_SUMMARY.md
Original file line number Diff line number Diff line change
@@ -185,8 +185,8 @@ Compatibility summary
- (⊙) `core` **brk**
- (⊙) `vcd` **brk**`vcd`
- (⊙) `Simulator` **brk**
- (+) `run_simulation` **obs**`.back.pysim.Simulator`
- () `passive` **obs**`.hdl.ast.Passive`
- () `run_simulation` **obs**`.back.pysim.Simulator`
- () `passive` **obs**`.hdl.ast.Passive`
- (−) `build` ?
- (+) `util` **obs**
- (+) `misc``.tools`
21 changes: 18 additions & 3 deletions nmigen/compat/sim/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import functools
import collections
import inspect
from ...back.pysim import *


__all__ = ["run_simulation"]
__all__ = ["run_simulation", "passive"]


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:
for domain, period in clocks.items():
sim.add_clock(period / 1e9, domain=domain)
for domain, process in generators.items():
sim.add_sync_process(process, domain=domain)
for domain, processes in generators.items():
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()


def passive(generator):
@functools.wraps(generator)
def wrapper(*args, **kwargs):
yield Passive()
yield from generator(*args, **kwargs)
return wrapper
9 changes: 1 addition & 8 deletions nmigen/hdl/ast.py
Original file line number Diff line number Diff line change
@@ -333,14 +333,7 @@ def shape(self):
bits, sign = self._bitwise_binary_shape(*op_shapes)
return bits + 1, sign
if self.op == "*":
if not a_sign and not b_sign:
# both operands unsigned
return a_bits + b_bits, False
if a_sign and b_sign:
# both operands signed
return a_bits + b_bits - 1, True
# one operand signed, the other unsigned (add sign bit)
return a_bits + b_bits + 1 - 1, True
return a_bits + b_bits, a_sign or b_sign
if self.op == "%":
return a_bits, a_sign
if self.op in ("<", "<=", "==", "!=", ">", ">=", "b"):
Empty file added nmigen/test/compat/__init__.py
Empty file.
13 changes: 13 additions & 0 deletions nmigen/test/compat/support.py
Original file line number Diff line number Diff line change
@@ -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 changes: 114 additions & 0 deletions nmigen/test/compat/test_coding.py
Original file line number Diff line number Diff line change
@@ -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 changes: 29 additions & 0 deletions nmigen/test/compat/test_constant.py
Original file line number Diff line number Diff line change
@@ -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 changes: 56 additions & 0 deletions nmigen/test/compat/test_fifo.py
Original file line number Diff line number Diff line change
@@ -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 changes: 87 additions & 0 deletions nmigen/test/compat/test_fsm.py
Original file line number Diff line number Diff line change
@@ -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 changes: 23 additions & 0 deletions nmigen/test/compat/test_passive.py
Original file line number Diff line number Diff line change
@@ -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)
Loading