add simulation-only division core using nmigen div and rem operators
authorJacob Lifshay <programmerjake@gmail.com>
Fri, 17 Jul 2020 04:26:07 +0000 (21:26 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Fri, 17 Jul 2020 04:26:07 +0000 (21:26 -0700)
src/soc/fu/div/core_stages.py
src/soc/fu/div/pipe_data.py
src/soc/fu/div/sim_only_core.py [new file with mode: 0644]
src/soc/fu/div/test/test_pipe_caller.py

index aa53845abc6941f03631502d45bf9cd38a43d63c..fc1d7520e0b094a0b8c32da2d026bd65c9a7cb15 100644 (file)
@@ -41,7 +41,8 @@ class DivCoreBaseStage(PipeModBase):
 
 class DivCoreSetupStage(DivCoreBaseStage):
     def __init__(self, pspec):
-        super().__init__(pspec, "core_setup_stage", DivPipeCoreSetupStage)
+        super().__init__(pspec, "core_setup_stage",
+                         pspec.div_pipe_kind.config.core_setup_stage_class)
 
     def ispec(self):
         return CoreInputData(self.pspec)
@@ -52,8 +53,9 @@ class DivCoreSetupStage(DivCoreBaseStage):
 
 class DivCoreCalculateStage(DivCoreBaseStage):
     def __init__(self, pspec, stage_index):
+        stage = pspec.div_pipe_kind.config.core_calculate_stage_class
         super().__init__(pspec, f"core_calculate_stage_{stage_index}",
-                         DivPipeCoreCalculateStage, stage_index)
+                         stage, stage_index)
 
     def ispec(self):
         return CoreInterstageData(self.pspec)
@@ -64,7 +66,8 @@ class DivCoreCalculateStage(DivCoreBaseStage):
 
 class DivCoreFinalStage(DivCoreBaseStage):
     def __init__(self, pspec):
-        super().__init__(pspec, "core_final_stage", DivPipeCoreFinalStage)
+        super().__init__(pspec, "core_final_stage",
+                         pspec.div_pipe_kind.config.core_final_stage_class)
 
     def ispec(self):
         return CoreInterstageData(self.pspec)
index d8dd73931e14bfb8841ab06d1ead1e3f05b5b89c..970ac7cfb5c476b6020fdeccadc7b9f4d4b3ff00 100644 (file)
@@ -1,10 +1,12 @@
+import enum
 from nmigen import Signal, Const
 from soc.fu.pipe_data import IntegerData
 from soc.fu.alu.pipe_data import CommonPipeSpec
 from soc.fu.logical.logical_input_record import CompLogicalOpSubset
 from ieee754.div_rem_sqrt_rsqrt.core import (
     DivPipeCoreConfig, DivPipeCoreInputData, DP,
-    DivPipeCoreInterstageData, DivPipeCoreOutputData)
+    DivPipeCoreInterstageData, DivPipeCoreOutputData,
+    DivPipeCoreSetupStage, DivPipeCoreCalculateStage, DivPipeCoreFinalStage)
 
 
 class DivInputData(IntegerData):
@@ -31,15 +33,79 @@ class DivMulOutputData(IntegerData):
         self.cr0 = self.cr_a
 
 
+class DivPipeKindConfig:
+    def __init__(self,
+                 core_config,
+                 core_input_data_class,
+                 core_interstage_data_class,
+                 core_output_data_class,
+                 core_setup_stage_class,
+                 core_calculate_stage_class,
+                 core_final_stage_class):
+        self.core_config = core_config
+        self.core_input_data_class = core_input_data_class
+        self.core_interstage_data_class = core_interstage_data_class
+        self.core_output_data_class = core_output_data_class
+        self.core_setup_stage_class = core_setup_stage_class
+        self.core_calculate_stage_class = core_calculate_stage_class
+        self.core_final_stage_class = core_final_stage_class
+
+
+class DivPipeKind(enum.Enum):
+    # use ieee754.div_rem_sqrt_rsqrt.core.DivPipeCore*
+    DivPipeCore = enum.auto()
+    # use nmigen's built-in div and rem operators -- only suitable for simulation
+    SimOnly = enum.auto()
+    # use a FSM-based div core
+    FSMCore = enum.auto()
+
+    @property
+    def config(self):
+        if self == DivPipeKind.DivPipeCore:
+            return DivPipeKindConfig(
+                core_config=DivPipeCoreConfig(
+                    bit_width=64,
+                    fract_width=64,
+                    log2_radix=1,
+                    supported=[DP.UDivRem]
+                ),
+                core_input_data_class=DivPipeCoreInputData,
+                core_interstage_data_class=DivPipeCoreInterstageData,
+                core_output_data_class=DivPipeCoreOutputData,
+                core_setup_stage_class=DivPipeCoreSetupStage,
+                core_calculate_stage_class=DivPipeCoreCalculateStage,
+                core_final_stage_class=DivPipeCoreFinalStage)
+        elif self == DivPipeKind.SimOnly:
+            # import here to avoid import loop
+            from soc.fu.div.sim_only_core import (
+                SimOnlyCoreConfig, SimOnlyCoreInputData,
+                SimOnlyCoreInterstageData, SimOnlyCoreOutputData,
+                SimOnlyCoreSetupStage, SimOnlyCoreCalculateStage,
+                SimOnlyCoreFinalStage)
+            return DivPipeKindConfig(
+                core_config=SimOnlyCoreConfig(),
+                core_input_data_class=SimOnlyCoreInputData,
+                core_interstage_data_class=SimOnlyCoreInterstageData,
+                core_output_data_class=SimOnlyCoreOutputData,
+                core_setup_stage_class=SimOnlyCoreSetupStage,
+                core_calculate_stage_class=SimOnlyCoreCalculateStage,
+                core_final_stage_class=SimOnlyCoreFinalStage)
+        else:
+            # ensure we didn't forget any cases
+            # -- I wish Python had a switch/match statement
+            assert self == DivPipeKind.FSMCore
+            # TODO(programmerjake): implement
+            raise NotImplementedError()
+
+
 class DivPipeSpec(CommonPipeSpec):
+    def __init__(self, id_wid, div_pipe_kind):
+        super().__init__(id_wid=id_wid)
+        self.div_pipe_kind = div_pipe_kind
+        self.core_config = div_pipe_kind.config.core_config
+
     regspec = (DivInputData.regspec, DivMulOutputData.regspec)
     opsubsetkls = CompLogicalOpSubset
-    core_config = DivPipeCoreConfig(
-        bit_width=64,
-        fract_width=64,
-        log2_radix=1,
-        supported=[DP.UDivRem]
-    )
 
 
 class CoreBaseData(DivInputData):
@@ -76,14 +142,14 @@ class CoreBaseData(DivInputData):
 
 class CoreInputData(CoreBaseData):
     def __init__(self, pspec):
-        super().__init__(pspec, DivPipeCoreInputData)
+        super().__init__(pspec, pspec.div_pipe_kind.config.core_input_data_class)
 
 
 class CoreInterstageData(CoreBaseData):
     def __init__(self, pspec):
-        super().__init__(pspec, DivPipeCoreInterstageData)
+        super().__init__(pspec, pspec.div_pipe_kind.config.core_interstage_data_class)
 
 
 class CoreOutputData(CoreBaseData):
     def __init__(self, pspec):
-        super().__init__(pspec, DivPipeCoreOutputData)
+        super().__init__(pspec, pspec.div_pipe_kind.config.core_output_data_class)
diff --git a/src/soc/fu/div/sim_only_core.py b/src/soc/fu/div/sim_only_core.py
new file mode 100644 (file)
index 0000000..d3bc877
--- /dev/null
@@ -0,0 +1,175 @@
+from nmigen import Signal, Elaboratable, Module
+from ieee754.div_rem_sqrt_rsqrt.core import DivPipeCoreOperation
+
+
+class SimOnlyCoreConfig:
+    n_stages = 1
+    bit_width = 64
+    fract_width = 64
+
+
+class SimOnlyCoreInputData:
+    def __init__(self, core_config, reset_less=True):
+        self.core_config = core_config
+        self.dividend = Signal(128, reset_less=reset_less)
+        self.divisor_radicand = Signal(64, reset_less=reset_less)
+        self.operation = DivPipeCoreOperation.create_signal(
+            reset_less=reset_less)
+
+    def __iter__(self):
+        """ Get member signals. """
+        yield self.dividend
+        yield self.divisor_radicand
+        yield self.operation
+
+    def eq(self, rhs):
+        """ Assign member signals. """
+        return [self.dividend.eq(rhs.dividend),
+                self.divisor_radicand.eq(rhs.divisor_radicand),
+                self.operation.eq(rhs.operation),
+                ]
+
+
+class SimOnlyCoreInterstageData:
+    def __init__(self, core_config, reset_less=True):
+        self.core_config = core_config
+        self.dividend = Signal(128, reset_less=reset_less)
+        self.divisor = Signal(64, reset_less=reset_less)
+
+    def __iter__(self):
+        """ Get member signals. """
+        yield self.dividend
+        yield self.divisor
+
+    def eq(self, rhs):
+        """ Assign member signals. """
+        return [self.dividend.eq(rhs.dividend),
+                self.divisor.eq(rhs.divisor)]
+
+
+class SimOnlyCoreOutputData:
+    def __init__(self, core_config, reset_less=True):
+        self.core_config = core_config
+        self.quotient_root = Signal(64, reset_less=reset_less)
+        self.remainder = Signal(3 * 64, reset_less=reset_less)
+
+    def __iter__(self):
+        """ Get member signals. """
+        yield self.quotient_root
+        yield self.remainder
+        return
+
+    def eq(self, rhs):
+        """ Assign member signals. """
+        return [self.quotient_root.eq(rhs.quotient_root),
+                self.remainder.eq(rhs.remainder)]
+
+
+class SimOnlyCoreSetupStage(Elaboratable):
+    def __init__(self, core_config):
+        self.core_config = core_config
+        self.i = self.ispec()
+        self.o = self.ospec()
+
+    def ispec(self):
+        """ Get the input spec for this pipeline stage."""
+        return SimOnlyCoreInputData(self.core_config)
+
+    def ospec(self):
+        """ Get the output spec for this pipeline stage."""
+        return SimOnlyCoreInterstageData(self.core_config)
+
+    def setup(self, m, i):
+        """ Pipeline stage setup. """
+        m.submodules.sim_only_core_setup = self
+        m.d.comb += self.i.eq(i)
+
+    def process(self, i):
+        """ Pipeline stage process. """
+        return self.o  # return processed data (ignore i)
+
+    def elaborate(self, platform):
+        """ Elaborate into ``Module``. """
+        m = Module()
+        comb = m.d.comb
+
+        comb += self.o.divisor.eq(self.i.divisor_radicand)
+        comb += self.o.dividend.eq(self.i.dividend)
+
+        return m
+
+
+class SimOnlyCoreCalculateStage(Elaboratable):
+    def __init__(self, core_config, stage_index):
+        assert stage_index == 0
+        self.core_config = core_config
+        self.stage_index = stage_index
+        self.i = self.ispec()
+        self.o = self.ospec()
+
+    def ispec(self):
+        """ Get the input spec for this pipeline stage. """
+        return SimOnlyCoreInterstageData(self.core_config)
+
+    def ospec(self):
+        """ Get the output spec for this pipeline stage. """
+        return SimOnlyCoreInterstageData(self.core_config)
+
+    def setup(self, m, i):
+        """ Pipeline stage setup. """
+        setattr(m.submodules,
+                f"sim_only_core_calculate_{self.stage_index}",
+                self)
+        m.d.comb += self.i.eq(i)
+
+    def process(self, i):
+        """ Pipeline stage process. """
+        return self.o
+
+    def elaborate(self, platform):
+        """ Elaborate into ``Module``. """
+        m = Module()
+        m.d.comb += self.o.eq(self.i)
+        return m
+
+
+class SimOnlyCoreFinalStage(Elaboratable):
+    """ Final Stage of the core of the div/rem/sqrt/rsqrt pipeline. """
+
+    def __init__(self, core_config):
+        """ Create a ``SimOnlyCoreFinalStage`` instance."""
+        self.core_config = core_config
+        self.i = self.ispec()
+        self.o = self.ospec()
+
+    def ispec(self):
+        """ Get the input spec for this pipeline stage."""
+        return SimOnlyCoreInterstageData(self.core_config)
+
+    def ospec(self):
+        """ Get the output spec for this pipeline stage."""
+        return SimOnlyCoreOutputData(self.core_config)
+
+    def setup(self, m, i):
+        """ Pipeline stage setup. """
+        m.submodules.sim_only_core_final = self
+        m.d.comb += self.i.eq(i)
+
+    def process(self, i):
+        """ Pipeline stage process. """
+        return self.o  # return processed data (ignore i)
+
+    def elaborate(self, platform):
+        """ Elaborate into ``Module``. """
+        m = Module()
+        remainder_shift = self.core_config.fract_width
+        with m.If(self.i.divisor != 0):
+            quotient = self.i.dividend // self.i.divisor
+            remainder = self.i.dividend % self.i.divisor
+            m.d.comb += self.o.quotient_root.eq(quotient)
+            m.d.comb += self.o.remainder.eq(remainder << remainder_shift)
+        with m.Else():
+            m.d.comb += self.o.quotient_root.eq(-1)
+            m.d.comb += self.o.remainder.eq(self.i.dividend << remainder_shift)
+
+        return m
index e0746aef2f29af565df2f9897974cf8b55f0ba69..851e015aa9df7ac2f88afdf4d7e1c02dc0e2ab20 100644 (file)
@@ -14,7 +14,7 @@ from soc.config.endian import bigendian
 
 from soc.fu.test.common import (TestCase, ALUHelpers)
 from soc.fu.div.pipeline import DivBasePipe
-from soc.fu.div.pipe_data import DivPipeSpec
+from soc.fu.div.pipe_data import DivPipeSpec, DivPipeKind
 import random
 
 
@@ -221,7 +221,8 @@ class TestRunner(FHDLTestCase):
 
         m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
 
-        pspec = DivPipeSpec(id_wid=2)
+        # TODO(programmerjake): thread div_pipe_kind through somehow to allow testing other cases
+        pspec = DivPipeSpec(id_wid=2, div_pipe_kind=DivPipeKind.SimOnly)
         m.submodules.alu = alu = DivBasePipe(pspec)
 
         comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)