From 528747703d8fe260f1279e17a31b2626ebae040c Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 26 Dec 2018 13:19:34 +0000 Subject: [PATCH] lib.coding: port from Migen. --- doc/COMPAT_SUMMARY.md | 10 +-- nmigen/compat/genlib/coding.py | 4 ++ nmigen/lib/coding.py | 118 +++++++++++++++++++++++++++++++++ nmigen/test/test_lib_coding.py | 78 ++++++++++++++++++++++ 4 files changed, 205 insertions(+), 5 deletions(-) create mode 100644 nmigen/compat/genlib/coding.py create mode 100644 nmigen/lib/coding.py create mode 100644 nmigen/test/test_lib_coding.py diff --git a/doc/COMPAT_SUMMARY.md b/doc/COMPAT_SUMMARY.md index 096ce69..8cd07d5 100644 --- a/doc/COMPAT_SUMMARY.md +++ b/doc/COMPAT_SUMMARY.md @@ -130,11 +130,11 @@ Compatibility summary - (−) `ElasticBuffer` ? - (−) `lcm` ? - (−) `Gearbox` ? - - (−) `coding` ? - - (−) `Encoder` ? - - (−) `PriorityEncoder` ? - - (−) `Decoder` ? - - (−) `PriorityDecoder` ? + - (⊕) `coding` id + - (⊕) `Encoder` id + - (⊕) `PriorityEncoder` id + - (⊕) `Decoder` id + - (⊕) `PriorityDecoder` id - (−) `divider` ? - (−) `Divider` ? - (−) `fifo` ? diff --git a/nmigen/compat/genlib/coding.py b/nmigen/compat/genlib/coding.py new file mode 100644 index 0000000..cb32f87 --- /dev/null +++ b/nmigen/compat/genlib/coding.py @@ -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 index 0000000..6509077 --- /dev/null +++ b/nmigen/lib/coding.py @@ -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 index 0000000..5569e76 --- /dev/null +++ b/nmigen/test/test_lib_coding.py @@ -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) -- 2.30.2