lib.coding: port from Migen.
authorwhitequark <whitequark@whitequark.org>
Wed, 26 Dec 2018 13:19:34 +0000 (13:19 +0000)
committerwhitequark <whitequark@whitequark.org>
Wed, 26 Dec 2018 13:19:34 +0000 (13:19 +0000)
doc/COMPAT_SUMMARY.md
nmigen/compat/genlib/coding.py [new file with mode: 0644]
nmigen/lib/coding.py [new file with mode: 0644]
nmigen/test/test_lib_coding.py [new file with mode: 0644]

index 096ce69fab89f06515c29bef346a8e542b151746..8cd07d5966333f157a02283c9bb56971ff82f105 100644 (file)
@@ -130,11 +130,11 @@ Compatibility summary
       - (−) `ElasticBuffer` ?
       - (−) `lcm` ?
       - (−) `Gearbox` ?
-    - (â\88\92) `coding` ?
-      - (â\88\92) `Encoder` ?
-      - (â\88\92) `PriorityEncoder` ?
-      - (â\88\92) `Decoder` ?
-      - (â\88\92) `PriorityDecoder` ?
+    - (â\8a\95) `coding` id
+      - (â\8a\95) `Encoder` id
+      - (â\8a\95) `PriorityEncoder` id
+      - (â\8a\95) `Decoder` id
+      - (â\8a\95) `PriorityDecoder` id
     - (−) `divider` ?
       - (−) `Divider` ?
     - (−) `fifo` ?
diff --git a/nmigen/compat/genlib/coding.py b/nmigen/compat/genlib/coding.py
new file mode 100644 (file)
index 0000000..cb32f87
--- /dev/null
@@ -0,0 +1,4 @@
+from ...lib.cdc import *
+
+
+__all__ = ["Encoder", "PriorityEncoder", "Decoder", "PriorityDecoder"]
diff --git a/nmigen/lib/coding.py b/nmigen/lib/coding.py
new file mode 100644 (file)
index 0000000..6509077
--- /dev/null
@@ -0,0 +1,118 @@
+"""Encoders and decoders between binary and one-hot representation."""
+
+from .. import *
+
+
+class Encoder:
+    """Encode one-hot to binary.
+
+    If one bit in ``i`` is asserted, ``n`` is low and ``o`` indicates the asserted bit.
+    Otherwise, ``n`` is high and ``o`` is ``0``.
+
+    Parameters
+    ----------
+    width : int
+        Bit width of the input
+
+    Attributes
+    ----------
+    i : Signal(width), in
+        One-hot input.
+    o : Signal(max=width), out
+        Encoded binary.
+    n : Signal, out
+        Invalid: either none or multiple input bits are asserted.
+    """
+    def __init__(self, width):
+        self.i = Signal(width)
+        self.o = Signal(max=max(2, width))
+        self.n = Signal()
+
+    def get_fragment(self, platform):
+        m = Module()
+        with m.Switch(self.i):
+            for j in range(len(self.i)):
+                with m.Case(1 << j):
+                    m.d.comb += self.o.eq(j)
+            with m.Case():
+                m.d.comb += self.n.eq(1)
+        return m.lower(platform)
+
+
+class PriorityEncoder:
+    """Priority encode requests to binary.
+
+    If any bit in ``i`` is asserted, ``n`` is low and ``o`` indicates the least significant
+    asserted bit.
+    Otherwise, ``n`` is high and ``o`` is ``0``.
+
+    Parameters
+    ----------
+    width : int
+        Bit width of the input.
+
+    Attributes
+    ----------
+    i : Signal(width), in
+        Input requests.
+    o : Signal(max=width), out
+        Encoded binary.
+    n : Signal, out
+        Invalid: no input bits are asserted.
+    """
+    def __init__(self, width):
+        self.i = Signal(width)
+        self.o = Signal(max=max(2, width))
+        self.n = Signal()
+
+    def get_fragment(self, platform):
+        m = Module()
+        for j, b in enumerate(reversed(self.i)):
+            with m.If(b):
+                m.d.comb += self.o.eq(j)
+        m.d.comb += self.n.eq(self.i == 0)
+        return m.lower(platform)
+
+
+class Decoder:
+    """Decode binary to one-hot.
+
+    If ``n`` is low, only the ``i``th bit in ``o`` is asserted.
+    If ``n`` is high, ``o`` is ``0``.
+
+    Parameters
+    ----------
+    width : int
+        Bit width of the output.
+
+    Attributes
+    ----------
+    i : Signal(max=width), in
+        Input binary.
+    o : Signal(width), out
+        Decoded one-hot.
+    n : Signal, in
+        Invalid, no output bits are to be asserted.
+    """
+    def __init__(self, width):
+        self.i = Signal(max=max(2, width))
+        self.n = Signal()
+        self.o = Signal(width)
+
+    def get_fragment(self, platform):
+        m = Module()
+        with m.Switch(self.i):
+            for j in range(len(self.o)):
+                with m.Case(j):
+                    m.d.comb += self.o.eq(1 << j)
+            with m.Case():
+                with m.If(self.n):
+                    m.d.comb += self.o.eq(0)
+        return m.lower(platform)
+
+
+class PriorityDecoder(Decoder):
+    """Decode binary to priority request.
+
+    Identical to :class:`Decoder`.
+    """
diff --git a/nmigen/test/test_lib_coding.py b/nmigen/test/test_lib_coding.py
new file mode 100644 (file)
index 0000000..5569e76
--- /dev/null
@@ -0,0 +1,78 @@
+from .tools import *
+from ..hdl.ast import *
+from ..back.pysim import *
+from ..lib.coding import *
+
+
+class EncoderTestCase(FHDLTestCase):
+    def test_basic(self):
+        enc = Encoder(4)
+        with Simulator(enc) as sim:
+            def process():
+                self.assertEqual((yield enc.n), 1)
+                self.assertEqual((yield enc.o), 0)
+
+                yield enc.i.eq(0b0001)
+                yield Delay()
+                self.assertEqual((yield enc.n), 0)
+                self.assertEqual((yield enc.o), 0)
+
+                yield enc.i.eq(0b0100)
+                yield Delay()
+                self.assertEqual((yield enc.n), 0)
+                self.assertEqual((yield enc.o), 2)
+
+                yield enc.i.eq(0b0110)
+                yield Delay()
+                self.assertEqual((yield enc.n), 1)
+                self.assertEqual((yield enc.o), 0)
+
+            sim.add_process(process)
+
+
+class PriorityEncoderTestCase(FHDLTestCase):
+    def test_basic(self):
+        enc = PriorityEncoder(4)
+        with Simulator(enc) as sim:
+            def process():
+                self.assertEqual((yield enc.n), 1)
+                self.assertEqual((yield enc.o), 0)
+
+                yield enc.i.eq(0b0001)
+                yield Delay()
+                self.assertEqual((yield enc.n), 0)
+                self.assertEqual((yield enc.o), 0)
+
+                yield enc.i.eq(0b0100)
+                yield Delay()
+                self.assertEqual((yield enc.n), 0)
+                self.assertEqual((yield enc.o), 2)
+
+                yield enc.i.eq(0b0110)
+                yield Delay()
+                self.assertEqual((yield enc.n), 0)
+                self.assertEqual((yield enc.o), 1)
+
+            sim.add_process(process)
+
+
+class DecoderTestCase(FHDLTestCase):
+    def test_basic(self):
+        dec = Decoder(4)
+        with Simulator(dec) as sim:
+            def process():
+                self.assertEqual((yield enc.o), 0b0001)
+
+                yield enc.i.eq(1)
+                yield Delay()
+                self.assertEqual((yield enc.o), 0b0010)
+
+                yield enc.i.eq(3)
+                yield Delay()
+                self.assertEqual((yield enc.o), 0b1000)
+
+                yield enc.n.eq(1)
+                yield Delay()
+                self.assertEqual((yield enc.o), 0b0000)
+
+            sim.add_process(process)