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: 9010805040ee
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: 3a8685c3523c
Choose a head ref
  • 3 commits
  • 5 files changed
  • 1 contributor

Commits on Dec 15, 2018

  1. fhdl.ir: fix incorrect uses of positive to say non-negative.

    Also test Part and Slice properly.
    whitequark committed Dec 15, 2018
    Copy the full SHA
    f5e8c90 View commit details
  2. Copy the full SHA
    f9f7921 View commit details
  3. Copy the full SHA
    3a8685c View commit details
Showing with 112 additions and 28 deletions.
  1. +1 −1 nmigen/back/pysim.py
  2. +18 −17 nmigen/fhdl/ast.py
  3. +2 −2 nmigen/fhdl/xfrm.py
  4. +65 −8 nmigen/test/{test_fhdl_value.py → test_fhdl_ast.py}
  5. +26 −0 nmigen/test/test_fhdl_ir.py
2 changes: 1 addition & 1 deletion nmigen/back/pysim.py
Original file line number Diff line number Diff line change
@@ -100,7 +100,7 @@ def on_Operator(self, value):
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 '{!r}' not implemented".format(value.op)) # :nocov:
raise NotImplementedError("Operator '{}' not implemented".format(value.op)) # :nocov:

def on_Slice(self, value):
shape = value.shape()
35 changes: 18 additions & 17 deletions nmigen/fhdl/ast.py
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ def wrap(obj):
elif isinstance(obj, (bool, int)):
return Const(obj)
else:
raise TypeError("Object {!r} is not a Migen value".format(repr(obj)))
raise TypeError("Object '{!r}' is not a Migen value".format(obj))

def __init__(self, src_loc_at=0):
super().__init__()
@@ -236,7 +236,7 @@ def __init__(self, value, shape=None):
shape = shape, self.value < 0
self.nbits, self.signed = shape
if not isinstance(self.nbits, int) or self.nbits < 0:
raise TypeError("Width must be a positive integer")
raise TypeError("Width must be a non-negative integer")
self.value = self.normalize(self.value, shape)

def shape(self):
@@ -313,7 +313,7 @@ def shape(self):
return obs[0]
elif self.op == "m":
return self._bitwise_binary_shape(obs[1], obs[2])
raise NotImplementedError("Operator '{!r}' not implemented".format(self.op)) # :nocov:
raise NotImplementedError("Operator '{}' not implemented".format(self.op)) # :nocov:

def _rhs_signals(self):
return union(op._rhs_signals() for op in self.operands)
@@ -344,9 +344,9 @@ def Mux(sel, val1, val0):
class Slice(Value):
def __init__(self, value, start, end):
if not isinstance(start, int):
raise TypeError("Slice start must be integer, not {!r}".format(start))
raise TypeError("Slice start must be an integer, not '{!r}'".format(start))
if not isinstance(end, int):
raise TypeError("Slice end must be integer, not {!r}".format(end))
raise TypeError("Slice end must be an integer, not '{!r}'".format(end))

n = len(value)
if start not in range(-n, n):
@@ -381,7 +381,7 @@ def __repr__(self):
class Part(Value):
def __init__(self, value, offset, width):
if not isinstance(width, int) or width < 0:
raise TypeError("Part width must be a positive integer, not {!r}".format(width))
raise TypeError("Part width must be a non-negative integer, not '{!r}'".format(width))

super().__init__()
self.value = value
@@ -398,7 +398,7 @@ def _rhs_signals(self):
return self.value._rhs_signals()

def __repr__(self):
return "(part {} {})".format(repr(self.value), repr(self.offset), self.width)
return "(part {} {} {})".format(repr(self.value), repr(self.offset), self.width)


class Cat(Value):
@@ -464,7 +464,8 @@ class Repl(Value):
"""
def __init__(self, value, count):
if not isinstance(count, int) or count < 0:
raise TypeError("Replication count must be a positive integer, not {!r}".format(count))
raise TypeError("Replication count must be a non-negative integer, not '{!r}'"
.format(count))

super().__init__()
self.value = Value.wrap(value)
@@ -543,7 +544,7 @@ def __init__(self, shape=None, name=None, reset=0, reset_less=False, min=None, m
max = 2
max -= 1 # make both bounds inclusive
if not min < max:
raise ValueError("Lower bound {!r} should be less than higher bound {!r}"
raise ValueError("Lower bound {} should be less than higher bound {}"
.format(min, max))
self.signed = min < 0 or max < 0
self.nbits = builtins.max(bits_for(min, self.signed), bits_for(max, self.signed))
@@ -557,7 +558,7 @@ def __init__(self, shape=None, name=None, reset=0, reset_less=False, min=None, m
self.nbits, self.signed = shape

if not isinstance(self.nbits, int) or self.nbits < 0:
raise TypeError("Width must be a positive integer, not {!r}".format(self.nbits))
raise TypeError("Width must be a non-negative integer, not '{!r}'".format(self.nbits))
self.reset = int(reset)
self.reset_less = bool(reset_less)

@@ -608,7 +609,7 @@ class ClockSignal(Value):
def __init__(self, domain="sync"):
super().__init__()
if not isinstance(domain, str):
raise TypeError("Clock domain name must be a string, not {!r}".format(domain))
raise TypeError("Clock domain name must be a string, not '{!r}'".format(domain))
self.domain = domain

def shape(self):
@@ -637,7 +638,7 @@ class ResetSignal(Value):
def __init__(self, domain="sync", allow_reset_less=False):
super().__init__()
if not isinstance(domain, str):
raise TypeError("Clock domain name must be a string, not {!r}".format(domain))
raise TypeError("Clock domain name must be a string, not '{!r}'".format(domain))
self.domain = domain
self.allow_reset_less = allow_reset_less

@@ -665,7 +666,7 @@ def wrap(obj):
if isinstance(obj, Statement):
return _StatementList([obj])
else:
raise TypeError("Object {!r} is not a Migen statement".format(obj))
raise TypeError("Object '{!r}' is not a Migen statement".format(obj))


class Assign(Statement):
@@ -693,7 +694,7 @@ def __init__(self, test, cases):
elif isinstance(key, str):
assert len(key) == len(self.test)
else:
raise TypeError("Object {!r} cannot be used as a switch key"
raise TypeError("Object '{!r}' cannot be used as a switch key"
.format(key))
if not isinstance(stmts, Iterable):
stmts = [stmts]
@@ -758,7 +759,7 @@ def __hash__(self):
elif isinstance(self.value, Slice):
return hash((ValueKey(self.value.value), self.value.start, self.value.end))
else: # :nocov:
raise TypeError("Object {!r} cannot be used as a key in value collections")
raise TypeError("Object '{!r}' cannot be used as a key in value collections")

def __eq__(self, other):
if not isinstance(other, ValueKey):
@@ -775,7 +776,7 @@ def __eq__(self, other):
self.value.start == other.value.start and
self.value.end == other.value.end)
else: # :nocov:
raise TypeError("Object {!r} cannot be used as a key in value collections")
raise TypeError("Object '{!r}' cannot be used as a key in value collections")

def __lt__(self, other):
if not isinstance(other, ValueKey):
@@ -792,7 +793,7 @@ def __lt__(self, other):
self.value.start < other.value.start and
self.value.end < other.value.end)
else: # :nocov:
raise TypeError("Object {!r} cannot be used as a key in value collections")
raise TypeError("Object '{!r}' cannot be used as a key in value collections")

def __repr__(self):
return "<{}.ValueKey {!r}>".format(__name__, self.value)
4 changes: 2 additions & 2 deletions nmigen/fhdl/xfrm.py
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ def on_Repl(self, value):
return Repl(self.on_value(value.value), value.count)

def on_unknown_value(self, value):
raise TypeError("Cannot transform value {!r}".format(value)) # :nocov:
raise TypeError("Cannot transform value '{!r}'".format(value)) # :nocov:

def on_value(self, value):
if isinstance(value, Const):
@@ -87,7 +87,7 @@ def on_statements(self, stmt):
return _StatementList(flatten(self.on_statement(stmt) for stmt in stmt))

def on_unknown_statement(self, stmt):
raise TypeError("Cannot transform statement {!r}".format(stmt)) # :nocov:
raise TypeError("Cannot transform statement '{!r}'".format(stmt)) # :nocov:

def on_statement(self, stmt):
if isinstance(stmt, Assign):
73 changes: 65 additions & 8 deletions nmigen/test/test_fhdl_value.py → nmigen/test/test_fhdl_ast.py
Original file line number Diff line number Diff line change
@@ -64,9 +64,11 @@ def test_shape(self):
self.assertEqual(Const(10).shape(), (4, False))
self.assertEqual(Const(-10).shape(), (5, True))

self.assertEqual(Const(1, 4).shape(), (4, False))
self.assertEqual(Const(1, (4, True)).shape(), (4, True))
self.assertEqual(Const(1, 4).shape(), (4, False))
self.assertEqual(Const(1, (4, True)).shape(), (4, True))
self.assertEqual(Const(0, (0, False)).shape(), (0, False))

def test_shape_bad(self):
with self.assertRaises(TypeError):
Const(1, -1)

@@ -234,11 +236,53 @@ def test_shape(self):
s2 = Const(-10)[0:2]
self.assertEqual(s2.shape(), (2, False))

def test_start_end_negative(self):
c = Const(0, 8)
s1 = Slice(c, 0, -1)
self.assertEqual((s1.start, s1.end), (0, 7))
s1 = Slice(c, -4, -1)
self.assertEqual((s1.start, s1.end), (4, 7))

def test_start_end_wrong(self):
with self.assertRaises(TypeError):
Slice(0, "x", 1)
with self.assertRaises(TypeError):
Slice(0, 1, "x")

def test_start_end_out_of_range(self):
c = Const(0, 8)
with self.assertRaises(IndexError):
Slice(c, 10, 12)
with self.assertRaises(IndexError):
Slice(c, 0, 12)
with self.assertRaises(IndexError):
Slice(c, 4, 2)

def test_repr(self):
s1 = Const(10)[2]
self.assertEqual(repr(s1), "(slice (const 4'd10) 2:3)")


class PartTestCase(FHDLTestCase):
def setUp(self):
self.c = Const(0, 8)
self.s = Signal(max=self.c.nbits)

def test_shape(self):
s1 = self.c.part(self.s, 2)
self.assertEqual(s1.shape(), (2, False))
s2 = self.c.part(self.s, 0)
self.assertEqual(s2.shape(), (0, False))

def test_width_bad(self):
with self.assertRaises(TypeError):
self.c.part(self.s, -1)

def test_repr(self):
s = self.c.part(self.s, 2)
self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2)")


class CatTestCase(FHDLTestCase):
def test_shape(self):
c1 = Cat(Const(10))
@@ -255,8 +299,10 @@ def test_repr(self):

class ReplTestCase(FHDLTestCase):
def test_shape(self):
r1 = Repl(Const(10), 3)
self.assertEqual(r1.shape(), (12, False))
s1 = Repl(Const(10), 3)
self.assertEqual(s1.shape(), (12, False))
s2 = Repl(Const(10), 0)
self.assertEqual(s2.shape(), (0, False))

def test_count_wrong(self):
with self.assertRaises(TypeError):
@@ -265,8 +311,8 @@ def test_count_wrong(self):
Repl(Const(10), "str")

def test_repr(self):
r1 = Repl(Const(10), 3)
self.assertEqual(repr(r1), "(repl (const 4'd10) 3)")
s = Repl(Const(10), 3)
self.assertEqual(repr(s), "(repl (const 4'd10) 3)")


class SignalTestCase(FHDLTestCase):
@@ -287,7 +333,10 @@ def test_shape(self):
self.assertEqual(s7.shape(), (5, True))
s8 = Signal(min=-20, max=16)
self.assertEqual(s8.shape(), (6, True))
s9 = Signal(0)
self.assertEqual(s9.shape(), (0, False))

def test_shape_bad(self):
with self.assertRaises(ValueError):
Signal(min=10, max=4)
with self.assertRaises(ValueError):
@@ -326,8 +375,10 @@ def test_like(self):
self.assertEqual(s3.reset_less, True)
s4 = Signal.like(Signal(attrs={"no_retiming": True}))
self.assertEqual(s4.attrs, {"no_retiming": True})
s5 = Signal.like(10)
self.assertEqual(s5.shape(), (4, False))
s5 = Signal.like(Signal(decoder=str))
self.assertEqual(s5.decoder, str)
s6 = Signal.like(10)
self.assertEqual(s6.shape(), (4, False))


class ClockSignalTestCase(FHDLTestCase):
@@ -340,6 +391,9 @@ def test_domain(self):
with self.assertRaises(TypeError):
ClockSignal(1)

def test_shape(self):
self.assertEqual(ClockSignal().shape(), (1, False))

def test_repr(self):
s1 = ClockSignal()
self.assertEqual(repr(s1), "(clk sync)")
@@ -355,6 +409,9 @@ def test_domain(self):
with self.assertRaises(TypeError):
ResetSignal(1)

def test_shape(self):
self.assertEqual(ResetSignal().shape(), (1, False))

def test_repr(self):
s1 = ResetSignal()
self.assertEqual(repr(s1), "(rst sync)")
26 changes: 26 additions & 0 deletions nmigen/test/test_fhdl_ir.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,13 @@
from .tools import *


class FragmentDriversTestCase(FHDLTestCase):
def test_empty(self):
f = Fragment()
self.assertEqual(list(f.iter_comb()), [])
self.assertEqual(list(f.iter_sync()), [])


class FragmentPortsTestCase(FHDLTestCase):
def setUp(self):
self.s1 = Signal()
@@ -15,10 +22,16 @@ def setUp(self):

def test_empty(self):
f = Fragment()
self.assertEqual(list(f.iter_ports()), [])

f._propagate_ports(ports=())
self.assertEqual(f.ports, ValueDict([]))

def test_iter_signals(self):
f = Fragment()
f.add_ports(self.s1, self.s2, kind="io")
self.assertEqual(ValueSet((self.s1, self.s2)), f.iter_signals())

def test_self_contained(self):
f = Fragment()
f.add_statements(
@@ -135,6 +148,19 @@ def test_input_cd_reset_less(self):


class FragmentDomainsTestCase(FHDLTestCase):
def test_iter_signals(self):
cd1 = ClockDomain()
cd2 = ClockDomain(reset_less=True)
s1 = Signal()
s2 = Signal()

f = Fragment()
f.add_domains(cd1, cd2)
f.add_driver(s1, "cd1")
self.assertEqual(ValueSet((cd1.clk, cd1.rst, s1)), f.iter_signals())
f.add_driver(s2, "cd2")
self.assertEqual(ValueSet((cd1.clk, cd1.rst, cd2.clk, s1, s2)), f.iter_signals())

def test_propagate_up(self):
cd = ClockDomain()