+ assert self.n_inputs % FULL_ADDER_INPUT_COUNT == 0
+
+ return terms, adders
+
+ def elaborate(self, platform):
+ """Elaborate this module."""
+ m = Module()
+
+ terms, adders = self.create_next_terms()
+
+ # copy the intermediate terms to the output
+ for i, value in enumerate(terms):
+ m.d.comb += self.o.terms[i].eq(value)
+
+ # copy reg part points and part ops to output
+ m.d.comb += self.o.part_pts.eq(self.i.part_pts)
+ m.d.comb += [self.o.part_ops[i].eq(self.i.part_ops[i])
+ for i in range(len(self.i.part_ops))]
+
+ # set up the partition mask (for the adders)
+ part_mask = Signal(self.output_width, reset_less=True)
+
+ # get partition points as a mask
+ mask = self.i.part_pts.as_mask(self.output_width,
+ mul=self.partition_step)
+ m.d.comb += part_mask.eq(mask)
+
+ # add and link the intermediate term modules
+ for i, (iidx, adder_i) in enumerate(adders):
+ setattr(m.submodules, f"adder_{i}", adder_i)
+
+ m.d.comb += adder_i.in0.eq(self.i.terms[iidx])
+ m.d.comb += adder_i.in1.eq(self.i.terms[iidx + 1])
+ m.d.comb += adder_i.in2.eq(self.i.terms[iidx + 2])
+ m.d.comb += adder_i.mask.eq(part_mask)
+
+ return m
+
+
+class AddReduceInternal:
+ """Iteratively Add list of numbers together.
+
+ :attribute inputs: input ``Signal``s to be summed. Modification not
+ supported, except for by ``Signal.eq``.
+ :attribute register_levels: List of nesting levels that should have
+ pipeline registers.
+ :attribute output: output sum.
+ :attribute partition_points: the input partition points. Modification not
+ supported, except for by ``Signal.eq``.
+ """
+
+ def __init__(self, pspec, n_inputs, part_pts, partition_step=1):
+ """Create an ``AddReduce``.
+
+ :param inputs: input ``Signal``s to be summed.
+ :param output_width: bit-width of ``output``.
+ :param partition_points: the input partition points.
+ """
+ self.pspec = pspec
+ self.n_inputs = n_inputs
+ self.output_width = pspec.width * 2
+ self.partition_points = part_pts
+ self.partition_step = partition_step
+
+ self.create_levels()
+
+ def create_levels(self):
+ """creates reduction levels"""
+
+ mods = []
+ partition_points = self.partition_points
+ ilen = self.n_inputs
+ while True:
+ groups = AddReduceSingle.full_adder_groups(ilen)
+ if len(groups) == 0:
+ break
+ lidx = len(mods)
+ next_level = AddReduceSingle(self.pspec, lidx, ilen,
+ partition_points,
+ self.partition_step)
+ mods.append(next_level)
+ partition_points = next_level.i.part_pts
+ ilen = len(next_level.o.terms)
+
+ lidx = len(mods)
+ next_level = FinalAdd(self.pspec, lidx, ilen,
+ partition_points, self.partition_step)
+ mods.append(next_level)
+
+ self.levels = mods
+
+
+class AddReduce(AddReduceInternal, Elaboratable):
+ """Recursively Add list of numbers together.
+
+ :attribute inputs: input ``Signal``s to be summed. Modification not
+ supported, except for by ``Signal.eq``.
+ :attribute register_levels: List of nesting levels that should have
+ pipeline registers.
+ :attribute output: output sum.
+ :attribute partition_points: the input partition points. Modification not
+ supported, except for by ``Signal.eq``.
+ """
+
+ def __init__(self, inputs, output_width, register_levels, part_pts,
+ part_ops, partition_step=1):
+ """Create an ``AddReduce``.
+
+ :param inputs: input ``Signal``s to be summed.
+ :param output_width: bit-width of ``output``.
+ :param register_levels: List of nesting levels that should have
+ pipeline registers.
+ :param partition_points: the input partition points.
+ """
+ self._inputs = inputs
+ self._part_pts = part_pts
+ self._part_ops = part_ops
+ n_parts = len(part_ops)
+ self.i = AddReduceData(part_pts, len(inputs),
+ output_width, n_parts)
+ AddReduceInternal.__init__(self, pspec, n_inputs, part_pts,
+ partition_step)
+ self.o = FinalReduceData(part_pts, output_width, n_parts)
+ self.register_levels = register_levels
+
+ @staticmethod
+ def get_max_level(input_count):
+ return AddReduceSingle.get_max_level(input_count)
+
+ @staticmethod
+ def next_register_levels(register_levels):
+ """``Iterable`` of ``register_levels`` for next recursive level."""
+ for level in register_levels:
+ if level > 0:
+ yield level - 1
+
+ def elaborate(self, platform):
+ """Elaborate this module."""
+ m = Module()
+
+ m.d.comb += self.i.eq_from(self._part_pts, self._inputs, self._part_ops)
+
+ for i, next_level in enumerate(self.levels):
+ setattr(m.submodules, "next_level%d" % i, next_level)
+
+ i = self.i
+ for idx in range(len(self.levels)):
+ mcur = self.levels[idx]
+ if idx in self.register_levels:
+ m.d.sync += mcur.i.eq(i)
+ else:
+ m.d.comb += mcur.i.eq(i)
+ i = mcur.o # for next loop
+
+ # output comes from last module
+ m.d.comb += self.o.eq(i)
+