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: 1655b59d1bd0
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: 474d46ced8f7
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Dec 14, 2018

  1. Copy the full SHA
    d9aaf01 View commit details
  2. back.pysim: implement most operators and add tests.

    whitequark committed Dec 14, 2018
    Copy the full SHA
    474d46c View commit details
Showing with 136 additions and 15 deletions.
  1. +2 −0 .coveragerc
  2. +20 −14 nmigen/back/pysim.py
  3. +1 −1 nmigen/fhdl/ast.py
  4. +113 −0 nmigen/test/test_sim.py
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[run]
branch = True
include =
nmigen/*
omit =
nmigen/test/*
*/__init__.py
34 changes: 20 additions & 14 deletions nmigen/back/pysim.py
Original file line number Diff line number Diff line change
@@ -27,13 +27,7 @@ def __init__(self):
def get(self, signal):
return self.curr[signal]

def set_curr(self, signal, value):
assert isinstance(value, int)
if self.curr[signal] != value:
self.curr_dirty.add(signal)
self.curr[signal] = value

def set_next(self, signal, value):
def set(self, signal, value):
assert isinstance(value, int)
if self.next[signal] != value:
self.next_dirty.add(signal)
@@ -48,11 +42,6 @@ def commit(self, signal):
new_value = self.curr[signal]
return old_value, new_value

def iter_dirty(self):
dirty, self.dirty = self.dirty, ValueSet()
for signal in dirty:
yield signal, self.curr[signal], self.next[signal]


normalize = Const.normalize

@@ -82,6 +71,8 @@ def on_Operator(self, value):
return lambda state: normalize(~arg(state), shape)
if value.op == "-":
return lambda state: normalize(-arg(state), shape)
if value.op == "b":
return lambda state: normalize(bool(arg(state)), shape)
elif len(value.operands) == 2:
lhs, rhs = map(self, value.operands)
if value.op == "+":
@@ -96,11 +87,21 @@ def on_Operator(self, value):
return lambda state: normalize(lhs(state) ^ rhs(state), shape)
if value.op == "==":
return lambda state: normalize(lhs(state) == rhs(state), shape)
if value.op == "!=":
return lambda state: normalize(lhs(state) != rhs(state), shape)
if value.op == "<":
return lambda state: normalize(lhs(state) < rhs(state), shape)
if value.op == "<=":
return lambda state: normalize(lhs(state) <= rhs(state), shape)
if value.op == ">":
return lambda state: normalize(lhs(state) > rhs(state), shape)
if value.op == ">=":
return lambda state: normalize(lhs(state) >= rhs(state), shape)
elif len(value.operands) == 3:
if value.op == "m":
sel, val1, val0 = map(self, value.operands)
return lambda state: val1(state) if sel(state) else val0(state)
raise NotImplementedError("Operator '{}' not implemented".format(value.op))
raise NotImplementedError("Operator '{!r}' not implemented".format(value.op)) # :nocov:

def on_Slice(self, value):
shape = value.shape()
@@ -148,7 +149,7 @@ def __init__(self):

def lhs_compiler(self, value):
# TODO
return lambda state, arg: state.set_next(value, arg)
return lambda state, arg: state.set(value, arg)

def on_Assign(self, stmt):
assert isinstance(stmt.lhs, Signal)
@@ -579,3 +580,8 @@ def add_trace(signal, **kwargs):

for signal in self._gtkw_signals:
add_trace(signal)

if self._vcd_file:
self._vcd_file.close()
if self._gtkw_file:
self._gtkw_file.close()
2 changes: 1 addition & 1 deletion nmigen/fhdl/ast.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@


__all__ = [
"Value", "Const", "Operator", "Mux", "Part", "Slice", "Cat", "Repl",
"Value", "Const", "C", "Operator", "Mux", "Part", "Slice", "Cat", "Repl",
"Signal", "ClockSignal", "ResetSignal",
"Statement", "Assign", "Switch", "Delay", "Tick", "Passive",
"ValueKey", "ValueDict", "ValueSet",
113 changes: 113 additions & 0 deletions nmigen/test/test_sim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from .tools import *
from ..fhdl.ast import *
from ..fhdl.ir import *
from ..back.pysim import *


class SimulatorUnitTestCase(FHDLTestCase):
def assertOperator(self, stmt, inputs, output):
inputs = [Value.wrap(i) for i in inputs]
output = Value.wrap(output)

isigs = [Signal(i.shape(), name=n) for i, n in zip(inputs, "abcd")]
osig = Signal(output.shape(), name="y")

frag = Fragment()
frag.add_statements(osig.eq(stmt(*isigs)))
frag.drive(osig)

with Simulator(frag,
vcd_file =open("test.vcd", "w"),
gtkw_file=open("test.gtkw", "w"),
gtkw_signals=[*isigs, osig]) as sim:
def process():
for isig, input in zip(isigs, inputs):
yield isig.eq(input)
yield Delay()
self.assertEqual((yield osig), output.value)
sim.add_process(process)
sim.run()

def test_invert(self):
stmt = lambda a: ~a
self.assertOperator(stmt, [C(0b0000, 4)], C(0b1111, 4))
self.assertOperator(stmt, [C(0b1010, 4)], C(0b0101, 4))
self.assertOperator(stmt, [C(0, 4)], C(-1, 4))

def test_neg(self):
stmt = lambda a: -a
self.assertOperator(stmt, [C(0b0000, 4)], C(0b0000, 4))
self.assertOperator(stmt, [C(0b0001, 4)], C(0b1111, 4))
self.assertOperator(stmt, [C(0b1010, 4)], C(0b0110, 4))
self.assertOperator(stmt, [C(1, 4)], C(-1, 4))
self.assertOperator(stmt, [C(5, 4)], C(-5, 4))

def test_bool(self):
stmt = lambda a: a.bool()
self.assertOperator(stmt, [C(0, 4)], C(0))
self.assertOperator(stmt, [C(1, 4)], C(1))
self.assertOperator(stmt, [C(2, 4)], C(1))

def test_add(self):
stmt = lambda a, b: a + b
self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(1, 4))
self.assertOperator(stmt, [C(-5, 4), C(-5, 4)], C(-10, 5))

def test_sub(self):
stmt = lambda a, b: a - b
self.assertOperator(stmt, [C(2, 4), C(1, 4)], C(1, 4))
self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(-1, 4))
self.assertOperator(stmt, [C(0, 4), C(10, 4)], C(-10, 5))

def test_and(self):
stmt = lambda a, b: a & b
self.assertOperator(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b1000, 4))

def test_or(self):
stmt = lambda a, b: a | b
self.assertOperator(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b1110, 4))

def test_xor(self):
stmt = lambda a, b: a ^ b
self.assertOperator(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b0110, 4))

def test_eq(self):
stmt = lambda a, b: a == b
self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(1))
self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(0))
self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(0))

def test_ne(self):
stmt = lambda a, b: a != b
self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(0))
self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(1))
self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(1))

def test_lt(self):
stmt = lambda a, b: a < b
self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(0))
self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(1))
self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(0))

def test_ge(self):
stmt = lambda a, b: a >= b
self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(1))
self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(0))
self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(1))

def test_gt(self):
stmt = lambda a, b: a > b
self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(0))
self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(0))
self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(1))

def test_le(self):
stmt = lambda a, b: a <= b
self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(1))
self.assertOperator(stmt, [C(0, 4), C(1, 4)], C(1))
self.assertOperator(stmt, [C(1, 4), C(0, 4)], C(0))

def test_mux(self):
stmt = lambda a, b, c: Mux(c, a, b)
self.assertOperator(stmt, [C(2, 4), C(3, 4), C(0)], C(3, 4))
self.assertOperator(stmt, [C(2, 4), C(3, 4), C(1)], C(2, 4))