From 401bdf070c25cfca2e7907007df58020ef88faa8 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 16 Jul 2020 21:26:07 -0700 Subject: [PATCH] add simulation-only division core using nmigen div and rem operators --- src/soc/fu/div/core_stages.py | 9 +- src/soc/fu/div/pipe_data.py | 86 ++++++++++-- src/soc/fu/div/sim_only_core.py | 175 ++++++++++++++++++++++++ src/soc/fu/div/test/test_pipe_caller.py | 5 +- 4 files changed, 260 insertions(+), 15 deletions(-) create mode 100644 src/soc/fu/div/sim_only_core.py diff --git a/src/soc/fu/div/core_stages.py b/src/soc/fu/div/core_stages.py index aa53845a..fc1d7520 100644 --- a/src/soc/fu/div/core_stages.py +++ b/src/soc/fu/div/core_stages.py @@ -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) diff --git a/src/soc/fu/div/pipe_data.py b/src/soc/fu/div/pipe_data.py index d8dd7393..970ac7cf 100644 --- a/src/soc/fu/div/pipe_data.py +++ b/src/soc/fu/div/pipe_data.py @@ -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 index 00000000..d3bc8779 --- /dev/null +++ b/src/soc/fu/div/sim_only_core.py @@ -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 diff --git a/src/soc/fu/div/test/test_pipe_caller.py b/src/soc/fu/div/test/test_pipe_caller.py index e0746aef..851e015a 100644 --- a/src/soc/fu/div/test/test_pipe_caller.py +++ b/src/soc/fu/div/test/test_pipe_caller.py @@ -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) -- 2.30.2