2019-09-08 06:10:31 -06:00
|
|
|
import warnings
|
2023-12-02 20:03:13 -07:00
|
|
|
from enum import Enum, EnumMeta
|
2019-07-02 13:02:16 -06:00
|
|
|
|
2024-01-30 09:43:33 -07:00
|
|
|
from amaranth.hdl._ast import *
|
2023-05-12 16:04:35 -06:00
|
|
|
from amaranth.lib.enum import Enum as AmaranthEnum
|
tests: move out of the main package.
Compared to tests in the repository root, tests in the package have
many downsides:
* Unless explicitly excluded in find_packages(), tests and their
support code effectively become a part of public API.
This, unfortunately, happened with FHDLTestCase, which was never
intended for downstream use.
* Even if explicitly excluded from the setuptools package, using
an editable install, or setting PYTHONPATH still allows accessing
the tests.
* Having a sub-package that is present in the source tree but not
exported (or, worse, exported only sometimes) is confusing.
* The name `nmigen.test` cannot be used for anything else, such as
testing utilities that *are* intended for downstream use.
2020-08-26 18:33:31 -06:00
|
|
|
|
2019-10-13 12:53:38 -06:00
|
|
|
from .utils import *
|
2023-02-28 07:28:41 -07:00
|
|
|
from amaranth._utils import _ignore_deprecated
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
|
2019-09-16 12:59:28 -06:00
|
|
|
class UnsignedEnum(Enum):
|
|
|
|
FOO = 1
|
|
|
|
BAR = 2
|
|
|
|
BAZ = 3
|
|
|
|
|
|
|
|
|
|
|
|
class SignedEnum(Enum):
|
|
|
|
FOO = -1
|
|
|
|
BAR = 0
|
|
|
|
BAZ = +1
|
|
|
|
|
|
|
|
|
|
|
|
class StringEnum(Enum):
|
|
|
|
FOO = "a"
|
|
|
|
BAR = "b"
|
|
|
|
|
|
|
|
|
2023-01-22 14:37:49 -07:00
|
|
|
class TypedEnum(int, Enum):
|
|
|
|
FOO = 1
|
|
|
|
BAR = 2
|
|
|
|
BAZ = 3
|
|
|
|
|
|
|
|
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
class ShapeTestCase(FHDLTestCase):
|
|
|
|
def test_make(self):
|
|
|
|
s1 = Shape()
|
|
|
|
self.assertEqual(s1.width, 1)
|
|
|
|
self.assertEqual(s1.signed, False)
|
|
|
|
s2 = Shape(signed=True)
|
|
|
|
self.assertEqual(s2.width, 1)
|
|
|
|
self.assertEqual(s2.signed, True)
|
|
|
|
s3 = Shape(3, True)
|
|
|
|
self.assertEqual(s3.width, 3)
|
|
|
|
self.assertEqual(s3.signed, True)
|
2023-09-02 22:27:17 -06:00
|
|
|
s4 = Shape(0)
|
|
|
|
self.assertEqual(s4.width, 0)
|
|
|
|
self.assertEqual(s4.signed, False)
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
|
|
|
|
def test_make_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2023-09-02 22:27:17 -06:00
|
|
|
r"^Width must be an integer, not 'a'$"):
|
|
|
|
Shape("a")
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Width of an unsigned value must be zero or a positive integer, not -1$"):
|
|
|
|
Shape(-1, signed=False)
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Width of a signed value must be a positive integer, not 0$"):
|
|
|
|
Shape(0, signed=True)
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
|
2023-02-28 08:52:50 -07:00
|
|
|
def test_compare_non_shape(self):
|
|
|
|
self.assertNotEqual(Shape(1, True), "hi")
|
2020-07-06 23:17:03 -06:00
|
|
|
|
2020-04-11 21:59:56 -06:00
|
|
|
def test_repr(self):
|
|
|
|
self.assertEqual(repr(Shape()), "unsigned(1)")
|
|
|
|
self.assertEqual(repr(Shape(2, True)), "signed(2)")
|
|
|
|
|
2023-01-31 05:24:56 -07:00
|
|
|
def test_convert_tuple_wrong(self):
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^cannot unpack non-iterable Shape object$"):
|
|
|
|
width, signed = Shape()
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
|
|
|
|
def test_unsigned(self):
|
|
|
|
s1 = unsigned(2)
|
|
|
|
self.assertIsInstance(s1, Shape)
|
|
|
|
self.assertEqual(s1.width, 2)
|
|
|
|
self.assertEqual(s1.signed, False)
|
|
|
|
|
|
|
|
def test_signed(self):
|
|
|
|
s1 = signed(2)
|
|
|
|
self.assertIsInstance(s1, Shape)
|
|
|
|
self.assertEqual(s1.width, 2)
|
|
|
|
self.assertEqual(s1.signed, True)
|
|
|
|
|
2019-10-11 07:22:08 -06:00
|
|
|
def test_cast_shape(self):
|
|
|
|
s1 = Shape.cast(unsigned(1))
|
|
|
|
self.assertEqual(s1.width, 1)
|
|
|
|
self.assertEqual(s1.signed, False)
|
|
|
|
s2 = Shape.cast(signed(3))
|
|
|
|
self.assertEqual(s2.width, 3)
|
|
|
|
self.assertEqual(s2.signed, True)
|
|
|
|
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
def test_cast_int(self):
|
|
|
|
s1 = Shape.cast(2)
|
|
|
|
self.assertEqual(s1.width, 2)
|
|
|
|
self.assertEqual(s1.signed, False)
|
|
|
|
|
|
|
|
def test_cast_int_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2023-09-02 22:27:17 -06:00
|
|
|
r"^Width of an unsigned value must be zero or a positive integer, not -1$"):
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
Shape.cast(-1)
|
|
|
|
|
|
|
|
def test_cast_tuple_wrong(self):
|
2023-01-31 05:24:56 -07:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Object \(1, True\) cannot be converted to an Amaranth shape$"):
|
|
|
|
Shape.cast((1, True))
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
|
|
|
|
def test_cast_range(self):
|
|
|
|
s1 = Shape.cast(range(0, 8))
|
|
|
|
self.assertEqual(s1.width, 3)
|
|
|
|
self.assertEqual(s1.signed, False)
|
|
|
|
s2 = Shape.cast(range(0, 9))
|
|
|
|
self.assertEqual(s2.width, 4)
|
|
|
|
self.assertEqual(s2.signed, False)
|
|
|
|
s3 = Shape.cast(range(-7, 8))
|
|
|
|
self.assertEqual(s3.width, 4)
|
|
|
|
self.assertEqual(s3.signed, True)
|
|
|
|
s4 = Shape.cast(range(0, 1))
|
2024-02-02 19:13:51 -07:00
|
|
|
self.assertEqual(s4.width, 0)
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
self.assertEqual(s4.signed, False)
|
|
|
|
s5 = Shape.cast(range(-1, 0))
|
|
|
|
self.assertEqual(s5.width, 1)
|
|
|
|
self.assertEqual(s5.signed, True)
|
|
|
|
s6 = Shape.cast(range(0, 0))
|
|
|
|
self.assertEqual(s6.width, 0)
|
|
|
|
self.assertEqual(s6.signed, False)
|
|
|
|
s7 = Shape.cast(range(-1, -1))
|
|
|
|
self.assertEqual(s7.width, 0)
|
2023-09-02 22:27:17 -06:00
|
|
|
self.assertEqual(s7.signed, False)
|
2023-06-07 03:45:56 -06:00
|
|
|
s8 = Shape.cast(range(0, 10, 3))
|
|
|
|
self.assertEqual(s8.width, 4)
|
|
|
|
self.assertEqual(s8.signed, False)
|
2024-02-02 19:13:51 -07:00
|
|
|
s9 = Shape.cast(range(0, 3, 3))
|
|
|
|
self.assertEqual(s9.width, 0)
|
|
|
|
self.assertEqual(s9.signed, False)
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
|
|
|
|
def test_cast_enum(self):
|
|
|
|
s1 = Shape.cast(UnsignedEnum)
|
|
|
|
self.assertEqual(s1.width, 2)
|
|
|
|
self.assertEqual(s1.signed, False)
|
|
|
|
s2 = Shape.cast(SignedEnum)
|
|
|
|
self.assertEqual(s2.width, 2)
|
|
|
|
self.assertEqual(s2.signed, True)
|
|
|
|
|
|
|
|
def test_cast_enum_bad(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2023-02-21 03:07:55 -07:00
|
|
|
r"^Only enumerations whose members have constant-castable values can be used "
|
|
|
|
r"in Amaranth code$"):
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
Shape.cast(StringEnum)
|
|
|
|
|
|
|
|
def test_cast_bad(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2022-04-05 15:55:50 -06:00
|
|
|
r"^Object 'foo' cannot be converted to an Amaranth shape$"):
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
Shape.cast("foo")
|
|
|
|
|
|
|
|
|
2022-04-05 15:55:50 -06:00
|
|
|
class MockShapeCastable(ShapeCastable):
|
|
|
|
def __init__(self, dest):
|
|
|
|
self.dest = dest
|
|
|
|
|
|
|
|
def as_shape(self):
|
|
|
|
return self.dest
|
|
|
|
|
2023-05-12 19:16:28 -06:00
|
|
|
def __call__(self, value):
|
|
|
|
return value
|
|
|
|
|
|
|
|
def const(self, init):
|
|
|
|
return Const(init, self.dest)
|
2023-05-12 16:04:35 -06:00
|
|
|
|
2022-04-05 15:55:50 -06:00
|
|
|
|
|
|
|
class ShapeCastableTestCase(FHDLTestCase):
|
|
|
|
def test_no_override(self):
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
2024-01-29 18:44:07 -07:00
|
|
|
r"^Class 'MockShapeCastableNoOverride' deriving from 'ShapeCastable' must "
|
|
|
|
r"override the 'as_shape' method$"):
|
2023-03-21 17:17:21 -06:00
|
|
|
class MockShapeCastableNoOverride(ShapeCastable):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
2022-04-05 15:55:50 -06:00
|
|
|
|
|
|
|
def test_cast(self):
|
|
|
|
sc = MockShapeCastable(unsigned(2))
|
|
|
|
self.assertEqual(Shape.cast(sc), unsigned(2))
|
|
|
|
|
|
|
|
def test_recurse_bad(self):
|
|
|
|
sc = MockShapeCastable(None)
|
|
|
|
sc.dest = sc
|
|
|
|
with self.assertRaisesRegex(RecursionError,
|
|
|
|
r"^Shape-castable object <.+> casts to itself$"):
|
|
|
|
Shape.cast(sc)
|
|
|
|
|
|
|
|
def test_recurse(self):
|
|
|
|
sc = MockShapeCastable(MockShapeCastable(unsigned(1)))
|
|
|
|
self.assertEqual(Shape.cast(sc), unsigned(1))
|
|
|
|
|
2024-02-08 19:09:42 -07:00
|
|
|
def test_abstract(self):
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Can't instantiate abstract class ShapeCastable$"):
|
|
|
|
ShapeCastable()
|
|
|
|
|
2022-04-05 15:55:50 -06:00
|
|
|
|
2023-12-02 20:03:13 -07:00
|
|
|
class ShapeLikeTestCase(FHDLTestCase):
|
|
|
|
def test_construct(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
ShapeLike()
|
|
|
|
|
|
|
|
def test_subclass(self):
|
|
|
|
self.assertTrue(issubclass(Shape, ShapeLike))
|
|
|
|
self.assertTrue(issubclass(MockShapeCastable, ShapeLike))
|
|
|
|
self.assertTrue(issubclass(int, ShapeLike))
|
|
|
|
self.assertTrue(issubclass(range, ShapeLike))
|
|
|
|
self.assertTrue(issubclass(EnumMeta, ShapeLike))
|
|
|
|
self.assertFalse(issubclass(Enum, ShapeLike))
|
|
|
|
self.assertFalse(issubclass(str, ShapeLike))
|
|
|
|
self.assertTrue(issubclass(ShapeLike, ShapeLike))
|
|
|
|
|
|
|
|
def test_isinstance(self):
|
|
|
|
self.assertTrue(isinstance(unsigned(2), ShapeLike))
|
|
|
|
self.assertTrue(isinstance(MockShapeCastable(unsigned(2)), ShapeLike))
|
|
|
|
self.assertTrue(isinstance(2, ShapeLike))
|
|
|
|
self.assertTrue(isinstance(0, ShapeLike))
|
|
|
|
self.assertFalse(isinstance(-1, ShapeLike))
|
|
|
|
self.assertTrue(isinstance(range(10), ShapeLike))
|
|
|
|
self.assertFalse(isinstance("abc", ShapeLike))
|
|
|
|
|
|
|
|
def test_isinstance_enum(self):
|
|
|
|
class EnumA(Enum):
|
|
|
|
A = 1
|
|
|
|
B = 2
|
|
|
|
class EnumB(Enum):
|
|
|
|
A = "a"
|
|
|
|
B = "b"
|
|
|
|
class EnumC(Enum):
|
|
|
|
A = Cat(Const(1, 2), Const(0, 2))
|
|
|
|
self.assertTrue(isinstance(EnumA, ShapeLike))
|
|
|
|
self.assertFalse(isinstance(EnumB, ShapeLike))
|
|
|
|
self.assertTrue(isinstance(EnumC, ShapeLike))
|
|
|
|
|
|
|
|
|
2018-12-13 02:19:16 -07:00
|
|
|
class ValueTestCase(FHDLTestCase):
|
2019-10-11 04:49:34 -06:00
|
|
|
def test_cast(self):
|
|
|
|
self.assertIsInstance(Value.cast(0), Const)
|
|
|
|
self.assertIsInstance(Value.cast(True), Const)
|
2018-12-12 19:04:44 -07:00
|
|
|
c = Const(0)
|
2019-10-11 04:49:34 -06:00
|
|
|
self.assertIs(Value.cast(c), c)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2021-12-09 22:39:50 -07:00
|
|
|
r"^Object 'str' cannot be converted to an Amaranth value$"):
|
2019-10-11 04:49:34 -06:00
|
|
|
Value.cast("str")
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2019-10-11 04:49:34 -06:00
|
|
|
def test_cast_enum(self):
|
|
|
|
e1 = Value.cast(UnsignedEnum.FOO)
|
2019-09-16 12:59:28 -06:00
|
|
|
self.assertIsInstance(e1, Const)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(e1.shape(), unsigned(2))
|
2019-10-11 04:49:34 -06:00
|
|
|
e2 = Value.cast(SignedEnum.FOO)
|
2019-09-16 12:59:28 -06:00
|
|
|
self.assertIsInstance(e2, Const)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(e2.shape(), signed(2))
|
2019-09-16 12:59:28 -06:00
|
|
|
|
2023-01-22 14:37:49 -07:00
|
|
|
def test_cast_typedenum(self):
|
|
|
|
e1 = Value.cast(TypedEnum.FOO)
|
|
|
|
self.assertIsInstance(e1, Const)
|
|
|
|
self.assertEqual(e1.shape(), unsigned(2))
|
|
|
|
|
2019-10-11 04:49:34 -06:00
|
|
|
def test_cast_enum_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2023-02-21 03:07:55 -07:00
|
|
|
r"^Only enumerations whose members have constant-castable values can be used "
|
|
|
|
r"in Amaranth code$"):
|
2019-10-11 04:49:34 -06:00
|
|
|
Value.cast(StringEnum.FOO)
|
2019-09-16 12:59:28 -06:00
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_bool(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2021-12-09 22:39:50 -07:00
|
|
|
r"^Attempted to convert Amaranth value to Python boolean$"):
|
2018-12-12 19:04:44 -07:00
|
|
|
if Const(0):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_len(self):
|
|
|
|
self.assertEqual(len(Const(10)), 4)
|
|
|
|
|
|
|
|
def test_getitem_int(self):
|
|
|
|
s1 = Const(10)[0]
|
|
|
|
self.assertIsInstance(s1, Slice)
|
|
|
|
self.assertEqual(s1.start, 0)
|
2019-10-12 16:40:30 -06:00
|
|
|
self.assertEqual(s1.stop, 1)
|
2018-12-12 19:04:44 -07:00
|
|
|
s2 = Const(10)[-1]
|
|
|
|
self.assertIsInstance(s2, Slice)
|
|
|
|
self.assertEqual(s2.start, 3)
|
2019-10-12 16:40:30 -06:00
|
|
|
self.assertEqual(s2.stop, 4)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(IndexError,
|
2020-08-26 19:14:05 -06:00
|
|
|
r"^Index 5 is out of bounds for a 4-bit value$"):
|
2018-12-12 19:04:44 -07:00
|
|
|
Const(10)[5]
|
|
|
|
|
|
|
|
def test_getitem_slice(self):
|
|
|
|
s1 = Const(10)[1:3]
|
|
|
|
self.assertIsInstance(s1, Slice)
|
|
|
|
self.assertEqual(s1.start, 1)
|
2019-10-12 16:40:30 -06:00
|
|
|
self.assertEqual(s1.stop, 3)
|
2018-12-12 19:04:44 -07:00
|
|
|
s2 = Const(10)[1:-2]
|
|
|
|
self.assertIsInstance(s2, Slice)
|
|
|
|
self.assertEqual(s2.start, 1)
|
2019-10-12 16:40:30 -06:00
|
|
|
self.assertEqual(s2.stop, 2)
|
2018-12-12 19:04:44 -07:00
|
|
|
s3 = Const(31)[::2]
|
|
|
|
self.assertIsInstance(s3, Cat)
|
2018-12-18 12:15:44 -07:00
|
|
|
self.assertIsInstance(s3.parts[0], Slice)
|
|
|
|
self.assertEqual(s3.parts[0].start, 0)
|
2019-10-12 16:40:30 -06:00
|
|
|
self.assertEqual(s3.parts[0].stop, 1)
|
2018-12-18 12:15:44 -07:00
|
|
|
self.assertIsInstance(s3.parts[1], Slice)
|
|
|
|
self.assertEqual(s3.parts[1].start, 2)
|
2019-10-12 16:40:30 -06:00
|
|
|
self.assertEqual(s3.parts[1].stop, 3)
|
2018-12-18 12:15:44 -07:00
|
|
|
self.assertIsInstance(s3.parts[2], Slice)
|
|
|
|
self.assertEqual(s3.parts[2].start, 4)
|
2019-10-12 16:40:30 -06:00
|
|
|
self.assertEqual(s3.parts[2].stop, 5)
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_getitem_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Cannot index value with 'str'$"):
|
2018-12-12 19:04:44 -07:00
|
|
|
Const(31)["str"]
|
2024-01-18 13:00:48 -07:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Cannot index value with a value; use Value.bit_select\(\) instead$"):
|
|
|
|
Const(31)[Signal(3)]
|
|
|
|
s = Signal(3)
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Cannot slice value with a value; use Value.bit_select\(\) or Value.word_select\(\) instead$"):
|
|
|
|
Const(31)[s:s+3]
|
|
|
|
|
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2020-05-19 21:18:33 -06:00
|
|
|
def test_shift_left(self):
|
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_left(0),
|
|
|
|
"(cat (const 0'd0) (const 9'd256))")
|
|
|
|
|
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_left(1),
|
|
|
|
"(cat (const 1'd0) (const 9'd256))")
|
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_left(5),
|
|
|
|
"(cat (const 5'd0) (const 9'd256))")
|
|
|
|
self.assertRepr(Const(256, signed(9)).shift_left(1),
|
|
|
|
"(s (cat (const 1'd0) (const 9'sd-256)))")
|
|
|
|
self.assertRepr(Const(256, signed(9)).shift_left(5),
|
|
|
|
"(s (cat (const 5'd0) (const 9'sd-256)))")
|
|
|
|
|
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_left(-1),
|
|
|
|
"(slice (const 9'd256) 1:9)")
|
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_left(-5),
|
|
|
|
"(slice (const 9'd256) 5:9)")
|
|
|
|
self.assertRepr(Const(256, signed(9)).shift_left(-1),
|
|
|
|
"(s (slice (const 9'sd-256) 1:9))")
|
|
|
|
self.assertRepr(Const(256, signed(9)).shift_left(-5),
|
|
|
|
"(s (slice (const 9'sd-256) 5:9))")
|
|
|
|
self.assertRepr(Const(256, signed(9)).shift_left(-15),
|
2024-02-12 21:31:51 -07:00
|
|
|
"(s (slice (const 9'sd-256) 8:9))")
|
2020-05-19 21:18:33 -06:00
|
|
|
|
|
|
|
def test_shift_left_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Shift amount must be an integer, not 'str'$"):
|
2020-05-19 21:18:33 -06:00
|
|
|
Const(31).shift_left("str")
|
|
|
|
|
|
|
|
def test_shift_right(self):
|
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_right(0),
|
|
|
|
"(slice (const 9'd256) 0:9)")
|
|
|
|
|
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_right(-1),
|
|
|
|
"(cat (const 1'd0) (const 9'd256))")
|
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_right(-5),
|
|
|
|
"(cat (const 5'd0) (const 9'd256))")
|
|
|
|
self.assertRepr(Const(256, signed(9)).shift_right(-1),
|
|
|
|
"(s (cat (const 1'd0) (const 9'sd-256)))")
|
|
|
|
self.assertRepr(Const(256, signed(9)).shift_right(-5),
|
|
|
|
"(s (cat (const 5'd0) (const 9'sd-256)))")
|
|
|
|
|
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_right(1),
|
|
|
|
"(slice (const 9'd256) 1:9)")
|
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_right(5),
|
|
|
|
"(slice (const 9'd256) 5:9)")
|
2024-02-12 21:31:51 -07:00
|
|
|
self.assertRepr(Const(256, unsigned(9)).shift_right(15),
|
|
|
|
"(slice (const 9'd256) 9:9)")
|
2020-05-19 21:18:33 -06:00
|
|
|
self.assertRepr(Const(256, signed(9)).shift_right(1),
|
|
|
|
"(s (slice (const 9'sd-256) 1:9))")
|
|
|
|
self.assertRepr(Const(256, signed(9)).shift_right(5),
|
|
|
|
"(s (slice (const 9'sd-256) 5:9))")
|
2024-02-12 21:31:51 -07:00
|
|
|
self.assertRepr(Const(256, signed(9)).shift_right(7),
|
|
|
|
"(s (slice (const 9'sd-256) 7:9))")
|
|
|
|
self.assertRepr(Const(256, signed(9)).shift_right(8),
|
|
|
|
"(s (slice (const 9'sd-256) 8:9))")
|
|
|
|
self.assertRepr(Const(256, signed(9)).shift_right(9),
|
|
|
|
"(s (slice (const 9'sd-256) 8:9))")
|
2020-05-19 21:18:33 -06:00
|
|
|
self.assertRepr(Const(256, signed(9)).shift_right(15),
|
2024-02-12 21:31:51 -07:00
|
|
|
"(s (slice (const 9'sd-256) 8:9))")
|
2020-05-19 21:18:33 -06:00
|
|
|
|
|
|
|
def test_shift_right_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Shift amount must be an integer, not 'str'$"):
|
2020-05-19 21:18:33 -06:00
|
|
|
Const(31).shift_left("str")
|
|
|
|
|
2020-04-13 07:40:39 -06:00
|
|
|
def test_rotate_left(self):
|
2020-05-19 21:18:33 -06:00
|
|
|
self.assertRepr(Const(256).rotate_left(1),
|
|
|
|
"(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
|
|
|
|
self.assertRepr(Const(256).rotate_left(7),
|
|
|
|
"(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
|
|
|
|
self.assertRepr(Const(256).rotate_left(-1),
|
|
|
|
"(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
|
|
|
|
self.assertRepr(Const(256).rotate_left(-7),
|
|
|
|
"(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
|
2023-06-07 04:49:41 -06:00
|
|
|
self.assertRepr(Const(0, 0).rotate_left(3),
|
|
|
|
"(cat (slice (const 0'd0) 0:0) (slice (const 0'd0) 0:0))")
|
|
|
|
self.assertRepr(Const(0, 0).rotate_left(-3),
|
|
|
|
"(cat (slice (const 0'd0) 0:0) (slice (const 0'd0) 0:0))")
|
2020-04-13 07:40:39 -06:00
|
|
|
|
|
|
|
def test_rotate_left_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Rotate amount must be an integer, not 'str'$"):
|
2020-04-13 07:40:39 -06:00
|
|
|
Const(31).rotate_left("str")
|
|
|
|
|
|
|
|
def test_rotate_right(self):
|
2020-05-19 21:18:33 -06:00
|
|
|
self.assertRepr(Const(256).rotate_right(1),
|
|
|
|
"(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
|
|
|
|
self.assertRepr(Const(256).rotate_right(7),
|
|
|
|
"(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
|
|
|
|
self.assertRepr(Const(256).rotate_right(-1),
|
|
|
|
"(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
|
|
|
|
self.assertRepr(Const(256).rotate_right(-7),
|
|
|
|
"(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
|
2023-06-07 04:49:41 -06:00
|
|
|
self.assertRepr(Const(0, 0).rotate_right(3),
|
|
|
|
"(cat (slice (const 0'd0) 0:0) (slice (const 0'd0) 0:0))")
|
|
|
|
self.assertRepr(Const(0, 0).rotate_right(-3),
|
|
|
|
"(cat (slice (const 0'd0) 0:0) (slice (const 0'd0) 0:0))")
|
2020-04-13 07:40:39 -06:00
|
|
|
|
|
|
|
def test_rotate_right_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Rotate amount must be an integer, not 'str'$"):
|
2020-04-13 07:40:39 -06:00
|
|
|
Const(31).rotate_right("str")
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2023-06-21 19:30:54 -06:00
|
|
|
def test_replicate_shape(self):
|
|
|
|
s1 = Const(10).replicate(3)
|
|
|
|
self.assertEqual(s1.shape(), unsigned(12))
|
|
|
|
self.assertIsInstance(s1.shape(), Shape)
|
|
|
|
s2 = Const(10).replicate(0)
|
|
|
|
self.assertEqual(s2.shape(), unsigned(0))
|
|
|
|
|
|
|
|
def test_replicate_count_wrong(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Const(10).replicate(-1)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
Const(10).replicate("str")
|
|
|
|
|
|
|
|
def test_replicate_repr(self):
|
|
|
|
s = Const(10).replicate(3)
|
|
|
|
self.assertEqual(repr(s), "(cat (const 4'd10) (const 4'd10) (const 4'd10))")
|
|
|
|
|
2020-05-19 21:18:33 -06:00
|
|
|
|
2018-12-13 02:19:16 -07:00
|
|
|
class ConstTestCase(FHDLTestCase):
|
2018-12-12 19:06:49 -07:00
|
|
|
def test_shape(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(Const(0).shape(), unsigned(1))
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
self.assertIsInstance(Const(0).shape(), Shape)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(Const(1).shape(), unsigned(1))
|
|
|
|
self.assertEqual(Const(10).shape(), unsigned(4))
|
|
|
|
self.assertEqual(Const(-10).shape(), signed(5))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(Const(1, 4).shape(), unsigned(4))
|
|
|
|
self.assertEqual(Const(-1, 4).shape(), signed(4))
|
|
|
|
self.assertEqual(Const(1, signed(4)).shape(), signed(4))
|
|
|
|
self.assertEqual(Const(0, unsigned(0)).shape(), unsigned(0))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2019-10-11 05:47:42 -06:00
|
|
|
def test_shape_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2023-09-02 22:27:17 -06:00
|
|
|
r"^Width of an unsigned value must be zero or a positive integer, not -1$"):
|
2018-12-12 19:04:44 -07:00
|
|
|
Const(1, -1)
|
|
|
|
|
2023-03-13 14:27:13 -06:00
|
|
|
def test_wrong_fencepost(self):
|
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
r"^Value 10 equals the non-inclusive end of the constant shape "
|
|
|
|
r"range\(0, 10\); this is likely an off-by-one error$"):
|
|
|
|
Const(10, range(10))
|
|
|
|
|
2018-12-13 11:00:05 -07:00
|
|
|
def test_normalization(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(Const(0b10110, signed(5)).value, -10)
|
2023-06-07 04:18:13 -06:00
|
|
|
self.assertEqual(Const(0b10000, signed(4)).value, 0)
|
|
|
|
self.assertEqual(Const(-16, 4).value, 0)
|
2018-12-13 11:00:05 -07:00
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_value(self):
|
|
|
|
self.assertEqual(Const(10).value, 10)
|
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
self.assertEqual(repr(Const(10)), "(const 4'd10)")
|
2018-12-13 11:00:05 -07:00
|
|
|
self.assertEqual(repr(Const(-10)), "(const 5'sd-10)")
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_hash(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
hash(Const(0))
|
|
|
|
|
2024-01-30 15:05:15 -07:00
|
|
|
def test_shape_castable(self):
|
|
|
|
class MockConstValue:
|
|
|
|
def __init__(self, value):
|
|
|
|
self.value = value
|
|
|
|
|
|
|
|
class MockConstShape(ShapeCastable):
|
|
|
|
def as_shape(self):
|
|
|
|
return unsigned(8)
|
|
|
|
|
|
|
|
def __call__(self, value):
|
|
|
|
return value
|
|
|
|
|
|
|
|
def const(self, init):
|
|
|
|
return MockConstValue(init)
|
|
|
|
|
|
|
|
s = Const(10, MockConstShape())
|
|
|
|
self.assertIsInstance(s, MockConstValue)
|
|
|
|
self.assertEqual(s.value, 10)
|
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2018-12-13 02:19:16 -07:00
|
|
|
class OperatorTestCase(FHDLTestCase):
|
2018-12-15 02:46:20 -07:00
|
|
|
def test_bool(self):
|
|
|
|
v = Const(0, 4).bool()
|
|
|
|
self.assertEqual(repr(v), "(b (const 4'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v.shape(), unsigned(1))
|
2018-12-15 02:46:20 -07:00
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_invert(self):
|
|
|
|
v = ~Const(0, 4)
|
|
|
|
self.assertEqual(repr(v), "(~ (const 4'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v.shape(), unsigned(4))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2020-02-06 11:27:55 -07:00
|
|
|
def test_as_unsigned(self):
|
|
|
|
v = Const(-1, signed(4)).as_unsigned()
|
|
|
|
self.assertEqual(repr(v), "(u (const 4'sd-1))")
|
|
|
|
self.assertEqual(v.shape(), unsigned(4))
|
|
|
|
|
|
|
|
def test_as_signed(self):
|
|
|
|
v = Const(1, unsigned(4)).as_signed()
|
|
|
|
self.assertEqual(repr(v), "(s (const 4'd1))")
|
|
|
|
self.assertEqual(v.shape(), signed(4))
|
|
|
|
|
2024-02-12 21:31:51 -07:00
|
|
|
def test_as_signed_wrong(self):
|
|
|
|
with self.assertRaisesRegex(ValueError,
|
|
|
|
r"^Cannot create a 0-width signed value$"):
|
|
|
|
Const(0, 0).as_signed()
|
|
|
|
|
2023-02-27 15:31:17 -07:00
|
|
|
def test_pos(self):
|
|
|
|
self.assertRepr(+Const(10), "(const 4'd10)")
|
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_neg(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
v1 = -Const(0, unsigned(4))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.assertEqual(repr(v1), "(- (const 4'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v1.shape(), signed(5))
|
|
|
|
v2 = -Const(0, signed(4))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.assertEqual(repr(v2), "(- (const 4'sd0))")
|
2019-12-03 10:33:26 -07:00
|
|
|
self.assertEqual(v2.shape(), signed(5))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_add(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
v1 = Const(0, unsigned(4)) + Const(0, unsigned(6))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.assertEqual(repr(v1), "(+ (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v1.shape(), unsigned(7))
|
|
|
|
v2 = Const(0, signed(4)) + Const(0, signed(6))
|
|
|
|
self.assertEqual(v2.shape(), signed(7))
|
|
|
|
v3 = Const(0, signed(4)) + Const(0, unsigned(4))
|
|
|
|
self.assertEqual(v3.shape(), signed(6))
|
|
|
|
v4 = Const(0, unsigned(4)) + Const(0, signed(4))
|
|
|
|
self.assertEqual(v4.shape(), signed(6))
|
2018-12-12 19:04:44 -07:00
|
|
|
v5 = 10 + Const(0, 4)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v5.shape(), unsigned(5))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_sub(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
v1 = Const(0, unsigned(4)) - Const(0, unsigned(6))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.assertEqual(repr(v1), "(- (const 4'd0) (const 6'd0))")
|
2023-06-07 06:29:33 -06:00
|
|
|
self.assertEqual(v1.shape(), signed(7))
|
2019-10-11 07:22:08 -06:00
|
|
|
v2 = Const(0, signed(4)) - Const(0, signed(6))
|
|
|
|
self.assertEqual(v2.shape(), signed(7))
|
|
|
|
v3 = Const(0, signed(4)) - Const(0, unsigned(4))
|
|
|
|
self.assertEqual(v3.shape(), signed(6))
|
|
|
|
v4 = Const(0, unsigned(4)) - Const(0, signed(4))
|
|
|
|
self.assertEqual(v4.shape(), signed(6))
|
2018-12-12 19:04:44 -07:00
|
|
|
v5 = 10 - Const(0, 4)
|
2023-06-07 06:29:33 -06:00
|
|
|
self.assertEqual(v5.shape(), signed(5))
|
|
|
|
v6 = 1 - Const(2)
|
|
|
|
self.assertEqual(v6.shape(), signed(3))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_mul(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
v1 = Const(0, unsigned(4)) * Const(0, unsigned(6))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.assertEqual(repr(v1), "(* (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v1.shape(), unsigned(10))
|
|
|
|
v2 = Const(0, signed(4)) * Const(0, signed(6))
|
|
|
|
self.assertEqual(v2.shape(), signed(10))
|
|
|
|
v3 = Const(0, signed(4)) * Const(0, unsigned(4))
|
|
|
|
self.assertEqual(v3.shape(), signed(8))
|
2018-12-12 19:04:44 -07:00
|
|
|
v5 = 10 * Const(0, 4)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v5.shape(), unsigned(8))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2019-10-04 01:49:24 -06:00
|
|
|
def test_mod(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
v1 = Const(0, unsigned(4)) % Const(0, unsigned(6))
|
2019-10-04 01:49:24 -06:00
|
|
|
self.assertEqual(repr(v1), "(% (const 4'd0) (const 6'd0))")
|
2021-12-11 01:52:14 -07:00
|
|
|
self.assertEqual(v1.shape(), unsigned(6))
|
2019-10-11 07:22:08 -06:00
|
|
|
v3 = Const(0, signed(4)) % Const(0, unsigned(4))
|
2021-12-11 01:52:14 -07:00
|
|
|
self.assertEqual(v3.shape(), unsigned(4))
|
|
|
|
v4 = Const(0, signed(4)) % Const(0, signed(6))
|
|
|
|
self.assertEqual(v4.shape(), signed(6))
|
2019-10-04 01:49:24 -06:00
|
|
|
v5 = 10 % Const(0, 4)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v5.shape(), unsigned(4))
|
2019-10-04 01:49:24 -06:00
|
|
|
|
2019-09-28 13:33:24 -06:00
|
|
|
def test_floordiv(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
v1 = Const(0, unsigned(4)) // Const(0, unsigned(6))
|
2019-09-28 13:33:24 -06:00
|
|
|
self.assertEqual(repr(v1), "(// (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v1.shape(), unsigned(4))
|
|
|
|
v3 = Const(0, signed(4)) // Const(0, unsigned(4))
|
|
|
|
self.assertEqual(v3.shape(), signed(4))
|
2021-12-11 01:52:14 -07:00
|
|
|
v4 = Const(0, signed(4)) // Const(0, signed(6))
|
|
|
|
self.assertEqual(v4.shape(), signed(5))
|
2019-09-28 13:33:24 -06:00
|
|
|
v5 = 10 // Const(0, 4)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v5.shape(), unsigned(4))
|
2019-09-28 13:33:24 -06:00
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_and(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
v1 = Const(0, unsigned(4)) & Const(0, unsigned(6))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.assertEqual(repr(v1), "(& (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v1.shape(), unsigned(6))
|
|
|
|
v2 = Const(0, signed(4)) & Const(0, signed(6))
|
|
|
|
self.assertEqual(v2.shape(), signed(6))
|
|
|
|
v3 = Const(0, signed(4)) & Const(0, unsigned(4))
|
|
|
|
self.assertEqual(v3.shape(), signed(5))
|
|
|
|
v4 = Const(0, unsigned(4)) & Const(0, signed(4))
|
|
|
|
self.assertEqual(v4.shape(), signed(5))
|
2018-12-12 19:04:44 -07:00
|
|
|
v5 = 10 & Const(0, 4)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v5.shape(), unsigned(4))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_or(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
v1 = Const(0, unsigned(4)) | Const(0, unsigned(6))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.assertEqual(repr(v1), "(| (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v1.shape(), unsigned(6))
|
|
|
|
v2 = Const(0, signed(4)) | Const(0, signed(6))
|
|
|
|
self.assertEqual(v2.shape(), signed(6))
|
|
|
|
v3 = Const(0, signed(4)) | Const(0, unsigned(4))
|
|
|
|
self.assertEqual(v3.shape(), signed(5))
|
|
|
|
v4 = Const(0, unsigned(4)) | Const(0, signed(4))
|
|
|
|
self.assertEqual(v4.shape(), signed(5))
|
2018-12-12 19:04:44 -07:00
|
|
|
v5 = 10 | Const(0, 4)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v5.shape(), unsigned(4))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_xor(self):
|
2019-10-11 07:22:08 -06:00
|
|
|
v1 = Const(0, unsigned(4)) ^ Const(0, unsigned(6))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.assertEqual(repr(v1), "(^ (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v1.shape(), unsigned(6))
|
|
|
|
v2 = Const(0, signed(4)) ^ Const(0, signed(6))
|
|
|
|
self.assertEqual(v2.shape(), signed(6))
|
|
|
|
v3 = Const(0, signed(4)) ^ Const(0, unsigned(4))
|
|
|
|
self.assertEqual(v3.shape(), signed(5))
|
|
|
|
v4 = Const(0, unsigned(4)) ^ Const(0, signed(4))
|
|
|
|
self.assertEqual(v4.shape(), signed(5))
|
2018-12-12 19:04:44 -07:00
|
|
|
v5 = 10 ^ Const(0, 4)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v5.shape(), unsigned(4))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2018-12-15 02:58:30 -07:00
|
|
|
def test_shl(self):
|
|
|
|
v1 = Const(1, 4) << Const(4)
|
|
|
|
self.assertEqual(repr(v1), "(<< (const 4'd1) (const 3'd4))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v1.shape(), unsigned(11))
|
2020-02-01 16:04:25 -07:00
|
|
|
|
|
|
|
def test_shl_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Shift amount must be unsigned$"):
|
2020-02-01 16:04:25 -07:00
|
|
|
1 << Const(0, signed(6))
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Shift amount must be unsigned$"):
|
2020-02-01 16:04:25 -07:00
|
|
|
Const(1, unsigned(4)) << -1
|
2018-12-15 02:58:30 -07:00
|
|
|
|
|
|
|
def test_shr(self):
|
|
|
|
v1 = Const(1, 4) >> Const(4)
|
|
|
|
self.assertEqual(repr(v1), "(>> (const 4'd1) (const 3'd4))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v1.shape(), unsigned(4))
|
2020-02-01 16:04:25 -07:00
|
|
|
|
|
|
|
def test_shr_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Shift amount must be unsigned$"):
|
2020-02-01 16:04:25 -07:00
|
|
|
1 << Const(0, signed(6))
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Shift amount must be unsigned$"):
|
2020-02-01 16:04:25 -07:00
|
|
|
Const(1, unsigned(4)) << -1
|
2018-12-15 02:58:30 -07:00
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_lt(self):
|
|
|
|
v = Const(0, 4) < Const(0, 6)
|
|
|
|
self.assertEqual(repr(v), "(< (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v.shape(), unsigned(1))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_le(self):
|
|
|
|
v = Const(0, 4) <= Const(0, 6)
|
|
|
|
self.assertEqual(repr(v), "(<= (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v.shape(), unsigned(1))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_gt(self):
|
|
|
|
v = Const(0, 4) > Const(0, 6)
|
|
|
|
self.assertEqual(repr(v), "(> (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v.shape(), unsigned(1))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_ge(self):
|
|
|
|
v = Const(0, 4) >= Const(0, 6)
|
|
|
|
self.assertEqual(repr(v), "(>= (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v.shape(), unsigned(1))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_eq(self):
|
|
|
|
v = Const(0, 4) == Const(0, 6)
|
|
|
|
self.assertEqual(repr(v), "(== (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v.shape(), unsigned(1))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_ne(self):
|
|
|
|
v = Const(0, 4) != Const(0, 6)
|
|
|
|
self.assertEqual(repr(v), "(!= (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v.shape(), unsigned(1))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_mux(self):
|
|
|
|
s = Const(0)
|
2019-10-11 07:22:08 -06:00
|
|
|
v1 = Mux(s, Const(0, unsigned(4)), Const(0, unsigned(6)))
|
2018-12-13 11:00:05 -07:00
|
|
|
self.assertEqual(repr(v1), "(m (const 1'd0) (const 4'd0) (const 6'd0))")
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v1.shape(), unsigned(6))
|
|
|
|
v2 = Mux(s, Const(0, signed(4)), Const(0, signed(6)))
|
|
|
|
self.assertEqual(v2.shape(), signed(6))
|
|
|
|
v3 = Mux(s, Const(0, signed(4)), Const(0, unsigned(4)))
|
|
|
|
self.assertEqual(v3.shape(), signed(5))
|
|
|
|
v4 = Mux(s, Const(0, unsigned(4)), Const(0, signed(4)))
|
|
|
|
self.assertEqual(v4.shape(), signed(5))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2019-09-23 07:39:31 -06:00
|
|
|
def test_mux_wide(self):
|
|
|
|
s = Const(0b100)
|
2019-10-11 07:22:08 -06:00
|
|
|
v = Mux(s, Const(0, unsigned(4)), Const(0, unsigned(6)))
|
2021-10-02 08:18:02 -06:00
|
|
|
self.assertEqual(repr(v), "(m (const 3'd4) (const 4'd0) (const 6'd0))")
|
2019-09-23 07:39:31 -06:00
|
|
|
|
2019-10-02 02:24:37 -06:00
|
|
|
def test_mux_bool(self):
|
|
|
|
v = Mux(True, Const(0), Const(0))
|
|
|
|
self.assertEqual(repr(v), "(m (const 1'd1) (const 1'd0) (const 1'd0))")
|
|
|
|
|
2019-09-13 07:14:52 -06:00
|
|
|
def test_any(self):
|
|
|
|
v = Const(0b101).any()
|
|
|
|
self.assertEqual(repr(v), "(r| (const 3'd5))")
|
|
|
|
|
|
|
|
def test_all(self):
|
|
|
|
v = Const(0b101).all()
|
|
|
|
self.assertEqual(repr(v), "(r& (const 3'd5))")
|
|
|
|
|
2021-05-18 13:18:14 -06:00
|
|
|
def test_xor_value(self):
|
2019-09-13 08:28:43 -06:00
|
|
|
v = Const(0b101).xor()
|
|
|
|
self.assertEqual(repr(v), "(r^ (const 3'd5))")
|
|
|
|
|
2019-09-14 15:06:12 -06:00
|
|
|
def test_matches(self):
|
|
|
|
s = Signal(4)
|
2024-01-13 04:35:52 -07:00
|
|
|
self.assertRepr(s.matches(), "(const 1'd0)")
|
2019-09-14 15:06:12 -06:00
|
|
|
self.assertRepr(s.matches(1), """
|
|
|
|
(== (sig s) (const 1'd1))
|
|
|
|
""")
|
|
|
|
self.assertRepr(s.matches(0, 1), """
|
|
|
|
(r| (cat (== (sig s) (const 1'd0)) (== (sig s) (const 1'd1))))
|
|
|
|
""")
|
|
|
|
self.assertRepr(s.matches("10--"), """
|
|
|
|
(== (& (sig s) (const 4'd12)) (const 4'd8))
|
|
|
|
""")
|
2020-02-04 00:54:54 -07:00
|
|
|
self.assertRepr(s.matches("1 0--"), """
|
|
|
|
(== (& (sig s) (const 4'd12)) (const 4'd8))
|
|
|
|
""")
|
2019-09-14 15:06:12 -06:00
|
|
|
|
2019-09-16 12:59:28 -06:00
|
|
|
def test_matches_enum(self):
|
2019-10-11 07:07:42 -06:00
|
|
|
s = Signal(SignedEnum)
|
2019-09-16 12:59:28 -06:00
|
|
|
self.assertRepr(s.matches(SignedEnum.FOO), """
|
2023-02-21 03:07:55 -07:00
|
|
|
(== (sig s) (const 2'sd-1))
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_matches_const_castable(self):
|
|
|
|
s = Signal(4)
|
|
|
|
self.assertRepr(s.matches(Cat(C(0b10, 2), C(0b11, 2))), """
|
|
|
|
(== (sig s) (const 4'd14))
|
2019-09-16 12:59:28 -06:00
|
|
|
""")
|
|
|
|
|
2019-09-14 15:06:12 -06:00
|
|
|
def test_matches_width_wrong(self):
|
|
|
|
s = Signal(4)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Match pattern '--' must have the same width as match value \(which is 4\)$"):
|
2019-09-14 15:06:12 -06:00
|
|
|
s.matches("--")
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
2023-02-21 03:07:55 -07:00
|
|
|
r"^Match pattern '22' \(5'10110\) is wider than match value \(which has "
|
|
|
|
r"width 4\); comparison will never be true$"):
|
2019-09-14 15:06:12 -06:00
|
|
|
s.matches(0b10110)
|
2023-02-21 03:07:55 -07:00
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
r"^Match pattern '\(cat \(const 1'd0\) \(const 4'd11\)\)' \(5'10110\) is wider "
|
|
|
|
r"than match value \(which has width 4\); comparison will never be true$"):
|
|
|
|
s.matches(Cat(0, C(0b1011, 4)))
|
2019-09-14 15:06:12 -06:00
|
|
|
|
|
|
|
def test_matches_bits_wrong(self):
|
|
|
|
s = Signal(4)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
2023-02-21 03:07:55 -07:00
|
|
|
r"^Match pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, "
|
|
|
|
r"and may include whitespace$"):
|
2019-09-14 15:06:12 -06:00
|
|
|
s.matches("abc")
|
|
|
|
|
|
|
|
def test_matches_pattern_wrong(self):
|
|
|
|
s = Signal(4)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
2023-02-21 03:07:55 -07:00
|
|
|
r"^Match pattern must be a string or a constant-castable expression, not 1\.0$"):
|
2019-09-14 15:06:12 -06:00
|
|
|
s.matches(1.0)
|
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_hash(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
hash(Const(0) + Const(0))
|
|
|
|
|
2023-06-07 09:50:01 -06:00
|
|
|
def test_abs(self):
|
|
|
|
s = Signal(4)
|
|
|
|
self.assertRepr(abs(s), """
|
|
|
|
(sig s)
|
|
|
|
""")
|
|
|
|
s = Signal(signed(4))
|
|
|
|
self.assertRepr(abs(s), """
|
|
|
|
(slice (m (>= (sig s) (const 1'd0)) (sig s) (- (sig s))) 0:4)
|
|
|
|
""")
|
|
|
|
self.assertEqual(abs(s).shape(), unsigned(4))
|
|
|
|
|
2024-01-13 05:39:02 -07:00
|
|
|
def test_contains(self):
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Cannot use 'in' with an Amaranth value$"):
|
|
|
|
1 in Signal(3)
|
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2018-12-13 02:19:16 -07:00
|
|
|
class SliceTestCase(FHDLTestCase):
|
2018-12-12 19:06:49 -07:00
|
|
|
def test_shape(self):
|
2018-12-12 19:04:44 -07:00
|
|
|
s1 = Const(10)[2]
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s1.shape(), unsigned(1))
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
self.assertIsInstance(s1.shape(), Shape)
|
2018-12-12 19:04:44 -07:00
|
|
|
s2 = Const(-10)[0:2]
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s2.shape(), unsigned(2))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2018-12-15 02:19:26 -07:00
|
|
|
def test_start_end_negative(self):
|
|
|
|
c = Const(0, 8)
|
|
|
|
s1 = Slice(c, 0, -1)
|
2019-10-12 16:40:30 -06:00
|
|
|
self.assertEqual((s1.start, s1.stop), (0, 7))
|
2018-12-15 02:19:26 -07:00
|
|
|
s1 = Slice(c, -4, -1)
|
2019-10-12 16:40:30 -06:00
|
|
|
self.assertEqual((s1.start, s1.stop), (4, 7))
|
2018-12-15 02:19:26 -07:00
|
|
|
|
2021-03-18 17:52:23 -06:00
|
|
|
def test_start_end_bool(self):
|
|
|
|
c = Const(0, 8)
|
|
|
|
s = Slice(c, False, True)
|
|
|
|
self.assertIs(type(s.start), int)
|
|
|
|
self.assertIs(type(s.stop), int)
|
|
|
|
|
2018-12-15 02:19:26 -07:00
|
|
|
def test_start_end_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Slice start must be an integer, not 'x'$"):
|
2018-12-15 02:19:26 -07:00
|
|
|
Slice(0, "x", 1)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Slice stop must be an integer, not 'x'$"):
|
2018-12-15 02:19:26 -07:00
|
|
|
Slice(0, 1, "x")
|
|
|
|
|
|
|
|
def test_start_end_out_of_range(self):
|
|
|
|
c = Const(0, 8)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(IndexError,
|
|
|
|
r"^Cannot start slice 10 bits into 8-bit value$"):
|
2018-12-15 02:19:26 -07:00
|
|
|
Slice(c, 10, 12)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(IndexError,
|
|
|
|
r"^Cannot stop slice 12 bits into 8-bit value$"):
|
2018-12-15 02:19:26 -07:00
|
|
|
Slice(c, 0, 12)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(IndexError,
|
|
|
|
r"^Slice start 4 must be less than slice stop 2$"):
|
2018-12-15 02:19:26 -07:00
|
|
|
Slice(c, 4, 2)
|
2023-06-07 06:09:19 -06:00
|
|
|
with self.assertRaisesRegex(IndexError,
|
|
|
|
r"^Cannot start slice -9 bits into 8-bit value$"):
|
|
|
|
Slice(c, -9, -5)
|
2018-12-15 02:19:26 -07:00
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_repr(self):
|
|
|
|
s1 = Const(10)[2]
|
|
|
|
self.assertEqual(repr(s1), "(slice (const 4'd10) 2:3)")
|
|
|
|
|
2023-12-30 04:10:29 -07:00
|
|
|
def test_const(self):
|
|
|
|
a = Const.cast(Const(0x1234, 16)[4:12])
|
|
|
|
self.assertEqual(a.value, 0x23)
|
|
|
|
self.assertEqual(a.width, 8)
|
|
|
|
self.assertEqual(a.signed, False)
|
|
|
|
a = Const.cast(Const(-4, signed(8))[1:6])
|
|
|
|
self.assertEqual(a.value, 0x1e)
|
|
|
|
self.assertEqual(a.width, 5)
|
|
|
|
self.assertEqual(a.signed, False)
|
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2019-08-03 07:05:41 -06:00
|
|
|
class BitSelectTestCase(FHDLTestCase):
|
2018-12-15 02:19:26 -07:00
|
|
|
def setUp(self):
|
|
|
|
self.c = Const(0, 8)
|
2019-10-11 07:07:42 -06:00
|
|
|
self.s = Signal(range(self.c.width))
|
2018-12-15 02:19:26 -07:00
|
|
|
|
|
|
|
def test_shape(self):
|
2019-08-03 07:05:41 -06:00
|
|
|
s1 = self.c.bit_select(self.s, 2)
|
2019-10-25 18:09:53 -06:00
|
|
|
self.assertIsInstance(s1, Part)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s1.shape(), unsigned(2))
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
self.assertIsInstance(s1.shape(), Shape)
|
2019-08-03 07:05:41 -06:00
|
|
|
s2 = self.c.bit_select(self.s, 0)
|
2019-10-25 18:09:53 -06:00
|
|
|
self.assertIsInstance(s2, Part)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s2.shape(), unsigned(0))
|
2018-12-15 02:19:26 -07:00
|
|
|
|
2019-08-03 07:05:41 -06:00
|
|
|
def test_stride(self):
|
|
|
|
s1 = self.c.bit_select(self.s, 2)
|
2019-10-25 18:09:53 -06:00
|
|
|
self.assertIsInstance(s1, Part)
|
2019-08-03 07:05:41 -06:00
|
|
|
self.assertEqual(s1.stride, 1)
|
|
|
|
|
2019-10-25 18:09:53 -06:00
|
|
|
def test_const(self):
|
|
|
|
s1 = self.c.bit_select(1, 2)
|
|
|
|
self.assertIsInstance(s1, Slice)
|
|
|
|
self.assertRepr(s1, """(slice (const 8'd0) 1:3)""")
|
|
|
|
|
2019-10-11 05:47:42 -06:00
|
|
|
def test_width_wrong(self):
|
2019-08-03 07:05:41 -06:00
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
self.c.bit_select(self.s, -1)
|
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
s = self.c.bit_select(self.s, 2)
|
|
|
|
self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2 1)")
|
|
|
|
|
2023-08-20 23:19:24 -06:00
|
|
|
def test_offset_wrong(self):
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Part offset must be unsigned$"):
|
|
|
|
self.c.bit_select(self.s.as_signed(), 1)
|
|
|
|
|
2019-08-03 07:05:41 -06:00
|
|
|
|
|
|
|
class WordSelectTestCase(FHDLTestCase):
|
|
|
|
def setUp(self):
|
|
|
|
self.c = Const(0, 8)
|
2019-10-11 07:07:42 -06:00
|
|
|
self.s = Signal(range(self.c.width))
|
2019-08-03 07:05:41 -06:00
|
|
|
|
|
|
|
def test_shape(self):
|
|
|
|
s1 = self.c.word_select(self.s, 2)
|
2019-10-25 18:09:53 -06:00
|
|
|
self.assertIsInstance(s1, Part)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s1.shape(), unsigned(2))
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
self.assertIsInstance(s1.shape(), Shape)
|
2019-08-03 07:05:41 -06:00
|
|
|
|
|
|
|
def test_stride(self):
|
|
|
|
s1 = self.c.word_select(self.s, 2)
|
2019-10-25 18:09:53 -06:00
|
|
|
self.assertIsInstance(s1, Part)
|
2019-08-03 07:05:41 -06:00
|
|
|
self.assertEqual(s1.stride, 2)
|
|
|
|
|
2019-10-25 18:09:53 -06:00
|
|
|
def test_const(self):
|
|
|
|
s1 = self.c.word_select(1, 2)
|
|
|
|
self.assertIsInstance(s1, Slice)
|
|
|
|
self.assertRepr(s1, """(slice (const 8'd0) 2:4)""")
|
|
|
|
|
2019-10-11 05:47:42 -06:00
|
|
|
def test_width_wrong(self):
|
2018-12-15 02:19:26 -07:00
|
|
|
with self.assertRaises(TypeError):
|
2019-08-03 07:05:41 -06:00
|
|
|
self.c.word_select(self.s, 0)
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
self.c.word_select(self.s, -1)
|
2018-12-15 02:19:26 -07:00
|
|
|
|
|
|
|
def test_repr(self):
|
2019-08-03 07:05:41 -06:00
|
|
|
s = self.c.word_select(self.s, 2)
|
|
|
|
self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2 2)")
|
2018-12-15 02:19:26 -07:00
|
|
|
|
2023-08-20 23:19:24 -06:00
|
|
|
def test_offset_wrong(self):
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Part offset must be unsigned$"):
|
|
|
|
self.c.word_select(self.s.as_signed(), 1)
|
|
|
|
|
2018-12-15 02:19:26 -07:00
|
|
|
|
2018-12-13 02:19:16 -07:00
|
|
|
class CatTestCase(FHDLTestCase):
|
2018-12-12 19:06:49 -07:00
|
|
|
def test_shape(self):
|
2019-06-04 04:19:54 -06:00
|
|
|
c0 = Cat()
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(c0.shape(), unsigned(0))
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
self.assertIsInstance(c0.shape(), Shape)
|
2018-12-12 19:04:44 -07:00
|
|
|
c1 = Cat(Const(10))
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(c1.shape(), unsigned(4))
|
2018-12-12 19:04:44 -07:00
|
|
|
c2 = Cat(Const(10), Const(1))
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(c2.shape(), unsigned(5))
|
2018-12-12 19:04:44 -07:00
|
|
|
c3 = Cat(Const(10), Const(1), Const(0))
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(c3.shape(), unsigned(6))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
c1 = Cat(Const(10), Const(1))
|
|
|
|
self.assertEqual(repr(c1), "(cat (const 4'd10) (const 1'd1))")
|
|
|
|
|
2021-10-02 07:18:11 -06:00
|
|
|
def test_cast(self):
|
|
|
|
c = Cat(1, 0)
|
|
|
|
self.assertEqual(repr(c), "(cat (const 1'd1) (const 1'd0))")
|
2023-11-24 17:38:33 -07:00
|
|
|
|
2021-12-11 00:39:35 -07:00
|
|
|
def test_str_wrong(self):
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Object 'foo' cannot be converted to an Amaranth value$"):
|
|
|
|
Cat("foo")
|
2021-10-02 07:18:11 -06:00
|
|
|
|
2021-12-11 01:18:33 -07:00
|
|
|
def test_int_01(self):
|
|
|
|
with warnings.catch_warnings():
|
|
|
|
warnings.filterwarnings(action="error", category=SyntaxWarning)
|
|
|
|
Cat(0, 1, 1, 0)
|
|
|
|
|
2023-02-20 15:58:38 -07:00
|
|
|
def test_enum_wrong(self):
|
2023-01-22 14:37:49 -07:00
|
|
|
class Color(Enum):
|
|
|
|
RED = 1
|
|
|
|
BLUE = 2
|
2023-02-20 15:58:38 -07:00
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
r"^Argument #1 of Cat\(\) is an enumerated value <Color\.RED: 1> without "
|
|
|
|
r"a defined shape used in bit vector context; define the enumeration by "
|
|
|
|
r"inheriting from the class in amaranth\.lib\.enum and specifying "
|
|
|
|
r"the 'shape=' keyword argument$"):
|
2023-01-22 14:37:49 -07:00
|
|
|
c = Cat(Color.RED, Color.BLUE)
|
2023-01-22 15:05:54 -07:00
|
|
|
self.assertEqual(repr(c), "(cat (const 2'd1) (const 2'd2))")
|
|
|
|
|
2023-02-20 15:58:38 -07:00
|
|
|
def test_intenum_wrong(self):
|
2023-01-22 15:05:54 -07:00
|
|
|
class Color(int, Enum):
|
|
|
|
RED = 1
|
|
|
|
BLUE = 2
|
2023-02-20 15:58:38 -07:00
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
r"^Argument #1 of Cat\(\) is an enumerated value <Color\.RED: 1> without "
|
|
|
|
r"a defined shape used in bit vector context; define the enumeration by "
|
|
|
|
r"inheriting from the class in amaranth\.lib\.enum and specifying "
|
|
|
|
r"the 'shape=' keyword argument$"):
|
2023-01-22 15:05:54 -07:00
|
|
|
c = Cat(Color.RED, Color.BLUE)
|
2023-01-22 14:37:49 -07:00
|
|
|
self.assertEqual(repr(c), "(cat (const 2'd1) (const 2'd2))")
|
|
|
|
|
2021-12-11 01:18:33 -07:00
|
|
|
def test_int_wrong(self):
|
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
r"^Argument #1 of Cat\(\) is a bare integer 2 used in bit vector context; "
|
2023-02-20 15:58:38 -07:00
|
|
|
r"specify the width explicitly using C\(2, 2\)$"):
|
2021-12-11 01:18:33 -07:00
|
|
|
Cat(2)
|
|
|
|
|
2023-12-30 04:09:28 -07:00
|
|
|
def test_const(self):
|
|
|
|
a = Const.cast(Cat(Const(1, 1), Const(0, 1), Const(3, 2), Const(2, 2)))
|
|
|
|
self.assertEqual(a.value, 0x2d)
|
|
|
|
self.assertEqual(a.width, 6)
|
|
|
|
self.assertEqual(a.signed, False)
|
|
|
|
a = Const.cast(Cat(Const(-4, 8), Const(-3, 8)))
|
|
|
|
self.assertEqual(a.value, 0xfdfc)
|
|
|
|
self.assertEqual(a.width, 16)
|
|
|
|
self.assertEqual(a.signed, False)
|
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2018-12-15 10:16:22 -07:00
|
|
|
class ArrayTestCase(FHDLTestCase):
|
|
|
|
def test_acts_like_array(self):
|
|
|
|
a = Array([1,2,3])
|
|
|
|
self.assertSequenceEqual(a, [1,2,3])
|
|
|
|
self.assertEqual(a[1], 2)
|
|
|
|
a[1] = 4
|
|
|
|
self.assertSequenceEqual(a, [1,4,3])
|
|
|
|
del a[1]
|
|
|
|
self.assertSequenceEqual(a, [1,3])
|
|
|
|
a.insert(1, 2)
|
|
|
|
self.assertSequenceEqual(a, [1,2,3])
|
|
|
|
|
|
|
|
def test_becomes_immutable(self):
|
|
|
|
a = Array([1,2,3])
|
2019-10-11 07:07:42 -06:00
|
|
|
s1 = Signal(range(len(a)))
|
|
|
|
s2 = Signal(range(len(a)))
|
2018-12-15 10:16:22 -07:00
|
|
|
v1 = a[s1]
|
|
|
|
v2 = a[s2]
|
|
|
|
with self.assertRaisesRegex(ValueError,
|
2020-07-02 16:49:04 -06:00
|
|
|
r"^Array can no longer be mutated after it was indexed with a value at "):
|
2018-12-15 10:16:22 -07:00
|
|
|
a[1] = 2
|
|
|
|
with self.assertRaisesRegex(ValueError,
|
2020-07-02 16:49:04 -06:00
|
|
|
r"^Array can no longer be mutated after it was indexed with a value at "):
|
2018-12-15 10:16:22 -07:00
|
|
|
del a[1]
|
|
|
|
with self.assertRaisesRegex(ValueError,
|
2020-07-02 16:49:04 -06:00
|
|
|
r"^Array can no longer be mutated after it was indexed with a value at "):
|
2018-12-15 10:16:22 -07:00
|
|
|
a.insert(1, 2)
|
|
|
|
|
2024-01-03 06:11:33 -07:00
|
|
|
def test_index_value_castable(self):
|
|
|
|
class MyValue(ValueCastable):
|
|
|
|
def as_value(self):
|
|
|
|
return Signal()
|
2024-01-30 09:43:33 -07:00
|
|
|
|
2024-01-03 06:11:33 -07:00
|
|
|
def shape():
|
|
|
|
return unsigned(1)
|
2024-01-30 09:43:33 -07:00
|
|
|
|
2024-01-03 06:11:33 -07:00
|
|
|
a = Array([1,2,3])
|
|
|
|
a[MyValue()]
|
|
|
|
|
2018-12-15 10:16:22 -07:00
|
|
|
def test_repr(self):
|
|
|
|
a = Array([1,2,3])
|
|
|
|
self.assertEqual(repr(a), "(array mutable [1, 2, 3])")
|
2019-10-11 07:07:42 -06:00
|
|
|
s = Signal(range(len(a)))
|
2018-12-15 10:16:22 -07:00
|
|
|
v = a[s]
|
|
|
|
self.assertEqual(repr(a), "(array [1, 2, 3])")
|
|
|
|
|
|
|
|
|
|
|
|
class ArrayProxyTestCase(FHDLTestCase):
|
|
|
|
def test_index_shape(self):
|
|
|
|
m = Array(Array(x * y for y in range(1, 4)) for x in range(1, 4))
|
2019-10-11 07:07:42 -06:00
|
|
|
a = Signal(range(3))
|
|
|
|
b = Signal(range(3))
|
2018-12-15 10:16:22 -07:00
|
|
|
v = m[a][b]
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v.shape(), unsigned(4))
|
2018-12-15 10:16:22 -07:00
|
|
|
|
|
|
|
def test_attr_shape(self):
|
|
|
|
from collections import namedtuple
|
|
|
|
pair = namedtuple("pair", ("p", "n"))
|
|
|
|
a = Array(pair(i, -i) for i in range(10))
|
2019-10-11 07:07:42 -06:00
|
|
|
s = Signal(range(len(a)))
|
2018-12-15 10:16:22 -07:00
|
|
|
v = a[s]
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(v.p.shape(), unsigned(4))
|
2020-08-26 00:58:22 -06:00
|
|
|
self.assertEqual(v.n.shape(), signed(5))
|
|
|
|
|
|
|
|
def test_attr_shape_signed(self):
|
|
|
|
# [unsigned(1), unsigned(1)] → unsigned(1)
|
|
|
|
a1 = Array([1, 1])
|
|
|
|
v1 = a1[Const(0)]
|
|
|
|
self.assertEqual(v1.shape(), unsigned(1))
|
|
|
|
# [signed(1), signed(1)] → signed(1)
|
|
|
|
a2 = Array([-1, -1])
|
|
|
|
v2 = a2[Const(0)]
|
|
|
|
self.assertEqual(v2.shape(), signed(1))
|
|
|
|
# [unsigned(1), signed(2)] → signed(2)
|
|
|
|
a3 = Array([1, -2])
|
|
|
|
v3 = a3[Const(0)]
|
|
|
|
self.assertEqual(v3.shape(), signed(2))
|
|
|
|
# [unsigned(1), signed(1)] → signed(2); 1st operand padded with sign bit!
|
|
|
|
a4 = Array([1, -1])
|
|
|
|
v4 = a4[Const(0)]
|
|
|
|
self.assertEqual(v4.shape(), signed(2))
|
|
|
|
# [unsigned(2), signed(1)] → signed(3); 1st operand padded with sign bit!
|
|
|
|
a5 = Array([1, -1])
|
|
|
|
v5 = a5[Const(0)]
|
|
|
|
self.assertEqual(v5.shape(), signed(2))
|
2018-12-15 10:16:22 -07:00
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
a = Array([1, 2, 3])
|
2019-10-11 07:07:42 -06:00
|
|
|
s = Signal(range(3))
|
2018-12-15 10:16:22 -07:00
|
|
|
v = a[s]
|
|
|
|
self.assertEqual(repr(v), "(proxy (array [1, 2, 3]) (sig s))")
|
|
|
|
|
|
|
|
|
2018-12-13 02:19:16 -07:00
|
|
|
class SignalTestCase(FHDLTestCase):
|
2018-12-12 19:06:49 -07:00
|
|
|
def test_shape(self):
|
2018-12-12 19:04:44 -07:00
|
|
|
s1 = Signal()
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s1.shape(), unsigned(1))
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
self.assertIsInstance(s1.shape(), Shape)
|
2018-12-12 19:04:44 -07:00
|
|
|
s2 = Signal(2)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s2.shape(), unsigned(2))
|
|
|
|
s3 = Signal(unsigned(2))
|
|
|
|
self.assertEqual(s3.shape(), unsigned(2))
|
|
|
|
s4 = Signal(signed(2))
|
|
|
|
self.assertEqual(s4.shape(), signed(2))
|
2019-09-08 06:10:31 -06:00
|
|
|
s5 = Signal(0)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s5.shape(), unsigned(0))
|
2019-10-11 07:07:42 -06:00
|
|
|
s6 = Signal(range(16))
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s6.shape(), unsigned(4))
|
2019-10-11 07:07:42 -06:00
|
|
|
s7 = Signal(range(4, 16))
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s7.shape(), unsigned(4))
|
2019-10-11 07:07:42 -06:00
|
|
|
s8 = Signal(range(-4, 16))
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s8.shape(), signed(5))
|
2019-10-11 07:07:42 -06:00
|
|
|
s9 = Signal(range(-20, 16))
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s9.shape(), signed(6))
|
2024-01-29 11:18:18 -07:00
|
|
|
s10 = Signal(range(0))
|
|
|
|
self.assertEqual(s10.shape(), unsigned(0))
|
2019-10-11 07:07:42 -06:00
|
|
|
s11 = Signal(range(1))
|
2024-02-02 19:13:51 -07:00
|
|
|
self.assertEqual(s11.shape(), unsigned(0))
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2019-10-11 05:47:42 -06:00
|
|
|
def test_shape_wrong(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2023-09-02 22:27:17 -06:00
|
|
|
r"^Width of an unsigned value must be zero or a positive integer, not -10$"):
|
2018-12-12 19:04:44 -07:00
|
|
|
Signal(-10)
|
|
|
|
|
|
|
|
def test_name(self):
|
|
|
|
s1 = Signal()
|
|
|
|
self.assertEqual(s1.name, "s1")
|
|
|
|
s2 = Signal(name="sig")
|
|
|
|
self.assertEqual(s2.name, "sig")
|
|
|
|
|
|
|
|
def test_reset(self):
|
|
|
|
s1 = Signal(4, reset=0b111, reset_less=True)
|
|
|
|
self.assertEqual(s1.reset, 0b111)
|
|
|
|
self.assertEqual(s1.reset_less, True)
|
|
|
|
|
2020-01-10 05:28:19 -07:00
|
|
|
def test_reset_enum(self):
|
|
|
|
s1 = Signal(2, reset=UnsignedEnum.BAR)
|
|
|
|
self.assertEqual(s1.reset, 2)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2023-03-02 23:20:34 -07:00
|
|
|
r"^Reset value must be a constant-castable expression, "
|
|
|
|
r"not <StringEnum\.FOO: 'a'>$"):
|
2020-01-10 05:28:19 -07:00
|
|
|
Signal(1, reset=StringEnum.FOO)
|
|
|
|
|
2023-09-23 12:29:22 -06:00
|
|
|
def test_reset_const_castable(self):
|
|
|
|
s1 = Signal(4, reset=Cat(Const(0, 1), Const(1, 1), Const(0, 2)))
|
|
|
|
self.assertEqual(s1.reset, 2)
|
|
|
|
|
2023-05-12 16:04:35 -06:00
|
|
|
def test_reset_shape_castable_const(self):
|
|
|
|
class CastableFromHex(ShapeCastable):
|
|
|
|
def as_shape(self):
|
|
|
|
return unsigned(8)
|
|
|
|
|
2023-05-12 19:16:28 -06:00
|
|
|
def __call__(self, value):
|
|
|
|
return value
|
|
|
|
|
2023-05-12 16:04:35 -06:00
|
|
|
def const(self, init):
|
|
|
|
return int(init, 16)
|
|
|
|
|
|
|
|
s1 = Signal(CastableFromHex(), reset="aa")
|
|
|
|
self.assertEqual(s1.reset, 0xaa)
|
|
|
|
|
|
|
|
with self.assertRaisesRegex(ValueError,
|
|
|
|
r"^Constant returned by <.+?CastableFromHex.+?>\.const\(\) must have the shape "
|
|
|
|
r"that it casts to, unsigned\(8\), and not unsigned\(1\)$"):
|
|
|
|
Signal(CastableFromHex(), reset="01")
|
|
|
|
|
|
|
|
def test_reset_shape_castable_enum_wrong(self):
|
2023-10-24 17:46:29 -06:00
|
|
|
class EnumA(AmaranthEnum, shape=1):
|
2023-05-12 16:04:35 -06:00
|
|
|
X = 1
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Reset value must be a constant initializer of <enum 'EnumA'>$"):
|
|
|
|
Signal(EnumA) # implied reset=0
|
|
|
|
|
2023-03-02 23:20:34 -07:00
|
|
|
def test_reset_signed_mismatch(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
2023-03-02 23:20:34 -07:00
|
|
|
r"^Reset value -2 is signed, but the signal shape is unsigned\(2\)$"):
|
|
|
|
Signal(unsigned(2), reset=-2)
|
|
|
|
|
|
|
|
def test_reset_wrong_too_wide(self):
|
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
r"^Reset value 2 will be truncated to the signal shape unsigned\(1\)$"):
|
|
|
|
Signal(unsigned(1), reset=2)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
2023-03-02 23:20:34 -07:00
|
|
|
r"^Reset value 1 will be truncated to the signal shape signed\(1\)$"):
|
|
|
|
Signal(signed(1), reset=1)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
2023-03-02 23:20:34 -07:00
|
|
|
r"^Reset value -2 will be truncated to the signal shape signed\(1\)$"):
|
|
|
|
Signal(signed(1), reset=-2)
|
2019-09-10 01:25:28 -06:00
|
|
|
|
2023-03-13 14:27:13 -06:00
|
|
|
def test_reset_wrong_fencepost(self):
|
2024-01-29 11:18:18 -07:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
2023-03-13 14:27:13 -06:00
|
|
|
r"^Reset value 10 equals the non-inclusive end of the signal shape "
|
|
|
|
r"range\(0, 10\); this is likely an off-by-one error$"):
|
|
|
|
Signal(range(0, 10), reset=10)
|
2024-01-29 11:18:18 -07:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Reset value 0 equals the non-inclusive end of the signal shape "
|
|
|
|
r"range\(0, 0\); this is likely an off-by-one error$"):
|
|
|
|
Signal(range(0), reset=0)
|
|
|
|
|
|
|
|
def test_reset_wrong_range(self):
|
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Reset value 11 is not within the signal shape range\(0, 10\)$"):
|
|
|
|
Signal(range(0, 10), reset=11)
|
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Reset value 0 is not within the signal shape range\(1, 10\)$"):
|
|
|
|
Signal(range(1, 10), reset=0)
|
2023-03-13 14:27:13 -06:00
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_attrs(self):
|
|
|
|
s1 = Signal()
|
|
|
|
self.assertEqual(s1.attrs, {})
|
|
|
|
s2 = Signal(attrs={"no_retiming": True})
|
|
|
|
self.assertEqual(s2.attrs, {"no_retiming": True})
|
|
|
|
|
|
|
|
def test_repr(self):
|
|
|
|
s1 = Signal()
|
|
|
|
self.assertEqual(repr(s1), "(sig s1)")
|
|
|
|
|
|
|
|
def test_like(self):
|
|
|
|
s1 = Signal.like(Signal(4))
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s1.shape(), unsigned(4))
|
2019-10-11 07:07:42 -06:00
|
|
|
s2 = Signal.like(Signal(range(-15, 1)))
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s2.shape(), signed(5))
|
2018-12-12 19:04:44 -07:00
|
|
|
s3 = Signal.like(Signal(4, reset=0b111, reset_less=True))
|
|
|
|
self.assertEqual(s3.reset, 0b111)
|
|
|
|
self.assertEqual(s3.reset_less, True)
|
|
|
|
s4 = Signal.like(Signal(attrs={"no_retiming": True}))
|
|
|
|
self.assertEqual(s4.attrs, {"no_retiming": True})
|
2018-12-15 02:19:26 -07:00
|
|
|
s5 = Signal.like(Signal(decoder=str))
|
|
|
|
self.assertEqual(s5.decoder, str)
|
|
|
|
s6 = Signal.like(10)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s6.shape(), unsigned(4))
|
2019-01-16 08:55:28 -07:00
|
|
|
s7 = [Signal.like(Signal(4))][0]
|
|
|
|
self.assertEqual(s7.name, "$like")
|
2019-06-12 16:21:23 -06:00
|
|
|
s8 = Signal.like(s1, name_suffix="_ff")
|
|
|
|
self.assertEqual(s8.name, "s1_ff")
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2019-07-02 13:02:16 -06:00
|
|
|
def test_decoder(self):
|
|
|
|
class Color(Enum):
|
|
|
|
RED = 1
|
|
|
|
BLUE = 2
|
|
|
|
s = Signal(decoder=Color)
|
|
|
|
self.assertEqual(s.decoder(1), "RED/1")
|
|
|
|
self.assertEqual(s.decoder(3), "3")
|
|
|
|
|
2019-09-16 12:59:28 -06:00
|
|
|
def test_enum(self):
|
2019-10-11 07:07:42 -06:00
|
|
|
s1 = Signal(UnsignedEnum)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s1.shape(), unsigned(2))
|
2019-10-11 07:07:42 -06:00
|
|
|
s2 = Signal(SignedEnum)
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s2.shape(), signed(2))
|
2019-09-16 12:59:28 -06:00
|
|
|
self.assertEqual(s2.decoder(SignedEnum.FOO), "FOO/-1")
|
|
|
|
|
2023-12-30 04:10:29 -07:00
|
|
|
def test_const_wrong(self):
|
|
|
|
s1 = Signal()
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Value \(sig s1\) cannot be converted to an Amaranth constant$"):
|
|
|
|
Const.cast(s1)
|
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2018-12-13 02:19:16 -07:00
|
|
|
class ClockSignalTestCase(FHDLTestCase):
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_domain(self):
|
|
|
|
s1 = ClockSignal()
|
|
|
|
self.assertEqual(s1.domain, "sync")
|
|
|
|
s2 = ClockSignal("pix")
|
|
|
|
self.assertEqual(s2.domain, "pix")
|
|
|
|
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Clock domain name must be a string, not 1$"):
|
2018-12-12 19:04:44 -07:00
|
|
|
ClockSignal(1)
|
|
|
|
|
2018-12-15 02:19:26 -07:00
|
|
|
def test_shape(self):
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
s1 = ClockSignal()
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s1.shape(), unsigned(1))
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
self.assertIsInstance(s1.shape(), Shape)
|
2018-12-15 02:19:26 -07:00
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_repr(self):
|
|
|
|
s1 = ClockSignal()
|
|
|
|
self.assertEqual(repr(s1), "(clk sync)")
|
|
|
|
|
2019-07-08 04:26:49 -06:00
|
|
|
def test_wrong_name_comb(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(ValueError,
|
|
|
|
r"^Domain 'comb' does not have a clock$"):
|
2019-07-08 04:26:49 -06:00
|
|
|
ClockSignal("comb")
|
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
|
2018-12-13 02:19:16 -07:00
|
|
|
class ResetSignalTestCase(FHDLTestCase):
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_domain(self):
|
|
|
|
s1 = ResetSignal()
|
|
|
|
self.assertEqual(s1.domain, "sync")
|
|
|
|
s2 = ResetSignal("pix")
|
|
|
|
self.assertEqual(s2.domain, "pix")
|
|
|
|
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Clock domain name must be a string, not 1$"):
|
2018-12-12 19:04:44 -07:00
|
|
|
ResetSignal(1)
|
|
|
|
|
2018-12-15 02:19:26 -07:00
|
|
|
def test_shape(self):
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
s1 = ResetSignal()
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(s1.shape(), unsigned(1))
|
hdl.ast: add an explicit Shape class, included in prelude.
Shapes have long been a part of nMigen, but represented using tuples.
This commit adds a Shape class (using namedtuple for backwards
compatibility), and accepts anything castable to Shape (including
enums, ranges, etc) anywhere a tuple was accepted previously.
In addition, `signed(n)` and `unsigned(n)` are added as aliases for
`Shape(n, signed=True)` and `Shape(n, signed=False)`, transforming
code such as `Signal((8, True))` to `Signal(signed(8))`.
These aliases are also included in prelude.
Preparation for #225.
2019-10-11 06:52:41 -06:00
|
|
|
self.assertIsInstance(s1.shape(), Shape)
|
2018-12-15 02:19:26 -07:00
|
|
|
|
2018-12-12 19:04:44 -07:00
|
|
|
def test_repr(self):
|
|
|
|
s1 = ResetSignal()
|
2018-12-13 01:57:14 -07:00
|
|
|
self.assertEqual(repr(s1), "(rst sync)")
|
2019-01-16 18:36:27 -07:00
|
|
|
|
2019-07-08 04:26:49 -06:00
|
|
|
def test_wrong_name_comb(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(ValueError,
|
|
|
|
r"^Domain 'comb' does not have a reset$"):
|
2019-07-08 04:26:49 -06:00
|
|
|
ResetSignal("comb")
|
|
|
|
|
2019-01-16 18:36:27 -07:00
|
|
|
|
2022-04-05 15:29:36 -06:00
|
|
|
class MockValueCastable(ValueCastable):
|
|
|
|
def __init__(self, dest):
|
|
|
|
self.dest = dest
|
|
|
|
|
2023-08-23 01:48:33 -06:00
|
|
|
def shape(self):
|
|
|
|
return Value.cast(self.dest).shape()
|
|
|
|
|
2022-04-05 15:29:36 -06:00
|
|
|
def as_value(self):
|
|
|
|
return self.dest
|
|
|
|
|
|
|
|
|
2020-11-05 17:20:54 -07:00
|
|
|
class MockValueCastableChanges(ValueCastable):
|
|
|
|
def __init__(self, width=0):
|
|
|
|
self.width = width
|
|
|
|
|
2023-08-23 01:48:33 -06:00
|
|
|
def shape(self):
|
|
|
|
return unsigned(self.width)
|
|
|
|
|
2024-02-12 21:21:39 -07:00
|
|
|
with _ignore_deprecated():
|
|
|
|
@ValueCastable.lowermethod
|
|
|
|
def as_value(self):
|
|
|
|
return Signal(self.width)
|
2020-11-05 17:20:54 -07:00
|
|
|
|
|
|
|
|
2021-10-03 14:28:07 -06:00
|
|
|
class MockValueCastableCustomGetattr(ValueCastable):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
|
2023-08-23 01:48:33 -06:00
|
|
|
def shape(self):
|
|
|
|
assert False
|
|
|
|
|
2024-02-12 21:21:39 -07:00
|
|
|
with _ignore_deprecated():
|
|
|
|
@ValueCastable.lowermethod
|
|
|
|
def as_value(self):
|
|
|
|
return Const(0)
|
2021-10-03 14:28:07 -06:00
|
|
|
|
|
|
|
def __getattr__(self, attr):
|
|
|
|
assert False
|
|
|
|
|
|
|
|
|
2020-11-05 17:20:54 -07:00
|
|
|
class ValueCastableTestCase(FHDLTestCase):
|
|
|
|
def test_no_override(self):
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
2024-02-12 19:09:52 -07:00
|
|
|
r"^Class 'MockValueCastableNoOverrideAsValue' deriving from 'ValueCastable' must "
|
|
|
|
r"override the 'as_value' method$"):
|
2023-08-23 01:48:33 -06:00
|
|
|
class MockValueCastableNoOverrideAsValue(ValueCastable):
|
2023-03-21 17:17:21 -06:00
|
|
|
def __init__(self):
|
|
|
|
pass
|
2020-11-05 17:20:54 -07:00
|
|
|
|
2023-08-23 01:48:33 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
2024-02-12 19:09:52 -07:00
|
|
|
r"^Class 'MockValueCastableNoOverrideShapec' deriving from 'ValueCastable' must "
|
|
|
|
r"override the 'shape' method$"):
|
2023-08-23 01:48:33 -06:00
|
|
|
class MockValueCastableNoOverrideShapec(ValueCastable):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def as_value(self):
|
|
|
|
return Signal()
|
|
|
|
|
2020-11-05 17:20:54 -07:00
|
|
|
def test_memoized(self):
|
|
|
|
vc = MockValueCastableChanges(1)
|
|
|
|
sig1 = vc.as_value()
|
|
|
|
vc.width = 2
|
|
|
|
sig2 = vc.as_value()
|
|
|
|
self.assertIs(sig1, sig2)
|
|
|
|
vc.width = 3
|
|
|
|
sig3 = Value.cast(vc)
|
|
|
|
self.assertIs(sig1, sig3)
|
|
|
|
|
2021-10-03 14:28:07 -06:00
|
|
|
def test_custom_getattr(self):
|
|
|
|
vc = MockValueCastableCustomGetattr()
|
|
|
|
vc.as_value() # shouldn't call __getattr__
|
|
|
|
|
2022-04-05 15:29:36 -06:00
|
|
|
def test_recurse_bad(self):
|
|
|
|
vc = MockValueCastable(None)
|
|
|
|
vc.dest = vc
|
|
|
|
with self.assertRaisesRegex(RecursionError,
|
|
|
|
r"^Value-castable object <.+> casts to itself$"):
|
|
|
|
Value.cast(vc)
|
|
|
|
|
|
|
|
def test_recurse(self):
|
|
|
|
vc = MockValueCastable(MockValueCastable(Signal()))
|
|
|
|
self.assertIsInstance(Value.cast(vc), Signal)
|
|
|
|
|
2024-02-08 19:09:42 -07:00
|
|
|
def test_abstract(self):
|
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Can't instantiate abstract class ValueCastable$"):
|
|
|
|
ValueCastable()
|
|
|
|
|
2020-11-05 17:20:54 -07:00
|
|
|
|
2023-12-02 20:03:13 -07:00
|
|
|
class ValueLikeTestCase(FHDLTestCase):
|
|
|
|
def test_construct(self):
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
ValueLike()
|
|
|
|
|
|
|
|
def test_subclass(self):
|
|
|
|
self.assertTrue(issubclass(Value, ValueLike))
|
|
|
|
self.assertTrue(issubclass(MockValueCastable, ValueLike))
|
|
|
|
self.assertTrue(issubclass(int, ValueLike))
|
|
|
|
self.assertFalse(issubclass(range, ValueLike))
|
|
|
|
self.assertFalse(issubclass(EnumMeta, ValueLike))
|
|
|
|
self.assertTrue(issubclass(Enum, ValueLike))
|
|
|
|
self.assertFalse(issubclass(str, ValueLike))
|
|
|
|
self.assertTrue(issubclass(ValueLike, ValueLike))
|
|
|
|
|
|
|
|
def test_isinstance(self):
|
|
|
|
self.assertTrue(isinstance(Const(0, 2), ValueLike))
|
|
|
|
self.assertTrue(isinstance(MockValueCastable(Const(0, 2)), ValueLike))
|
|
|
|
self.assertTrue(isinstance(2, ValueLike))
|
|
|
|
self.assertTrue(isinstance(-2, ValueLike))
|
|
|
|
self.assertFalse(isinstance(range(10), ValueLike))
|
|
|
|
|
|
|
|
def test_enum(self):
|
|
|
|
class EnumA(Enum):
|
|
|
|
A = 1
|
|
|
|
B = 2
|
|
|
|
class EnumB(Enum):
|
|
|
|
A = "a"
|
|
|
|
B = "b"
|
|
|
|
class EnumC(Enum):
|
|
|
|
A = Cat(Const(1, 2), Const(0, 2))
|
|
|
|
class EnumD(Enum):
|
|
|
|
A = 1
|
|
|
|
B = "a"
|
|
|
|
self.assertTrue(issubclass(EnumA, ValueLike))
|
|
|
|
self.assertFalse(issubclass(EnumB, ValueLike))
|
|
|
|
self.assertTrue(issubclass(EnumC, ValueLike))
|
|
|
|
self.assertFalse(issubclass(EnumD, ValueLike))
|
|
|
|
self.assertTrue(isinstance(EnumA.A, ValueLike))
|
|
|
|
self.assertFalse(isinstance(EnumB.A, ValueLike))
|
|
|
|
self.assertTrue(isinstance(EnumC.A, ValueLike))
|
|
|
|
self.assertFalse(isinstance(EnumD.A, ValueLike))
|
|
|
|
|
|
|
|
|
2019-08-14 20:53:07 -06:00
|
|
|
class InitialTestCase(FHDLTestCase):
|
|
|
|
def test_initial(self):
|
|
|
|
i = Initial()
|
2019-10-11 07:22:08 -06:00
|
|
|
self.assertEqual(i.shape(), unsigned(1))
|
2020-12-12 05:42:12 -07:00
|
|
|
|
|
|
|
|
|
|
|
class SwitchTestCase(FHDLTestCase):
|
|
|
|
def test_default_case(self):
|
|
|
|
s = Switch(Const(0), {None: []})
|
|
|
|
self.assertEqual(s.cases, {(): []})
|
|
|
|
|
|
|
|
def test_int_case(self):
|
|
|
|
s = Switch(Const(0, 8), {10: []})
|
|
|
|
self.assertEqual(s.cases, {("00001010",): []})
|
|
|
|
|
|
|
|
def test_int_neg_case(self):
|
|
|
|
s = Switch(Const(0, 8), {-10: []})
|
|
|
|
self.assertEqual(s.cases, {("11110110",): []})
|
|
|
|
|
2024-02-13 15:48:59 -07:00
|
|
|
def test_int_zero_width(self):
|
|
|
|
s = Switch(Const(0, 0), {0: []})
|
|
|
|
self.assertEqual(s.cases, {("",): []})
|
|
|
|
|
|
|
|
def test_int_zero_width_enum(self):
|
|
|
|
class ZeroEnum(Enum):
|
|
|
|
A = 0
|
|
|
|
s = Switch(Const(0, 0), {ZeroEnum.A: []})
|
|
|
|
self.assertEqual(s.cases, {("",): []})
|
|
|
|
|
2020-12-12 05:42:12 -07:00
|
|
|
def test_enum_case(self):
|
|
|
|
s = Switch(Const(0, UnsignedEnum), {UnsignedEnum.FOO: []})
|
|
|
|
self.assertEqual(s.cases, {("01",): []})
|
|
|
|
|
|
|
|
def test_str_case(self):
|
|
|
|
s = Switch(Const(0, 8), {"0000 11\t01": []})
|
|
|
|
self.assertEqual(s.cases, {("00001101",): []})
|
|
|
|
|
|
|
|
def test_two_cases(self):
|
|
|
|
s = Switch(Const(0, 8), {("00001111", 123): []})
|
|
|
|
self.assertEqual(s.cases, {("00001111", "01111011"): []})
|