From: Michael Nolan Date: Wed, 5 Feb 2020 15:54:24 +0000 (-0500) Subject: move unused partitioned comparison modules to experiments/ X-Git-Tag: ls180-24jan2020~262 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=3f794980c7786c65ffa0e8221709a4717d5fe384;p=ieee754fpu.git move unused partitioned comparison modules to experiments/ --- diff --git a/src/ieee754/part_cmp/equal.py b/src/ieee754/part_cmp/equal.py deleted file mode 100644 index 21bd0e21..00000000 --- a/src/ieee754/part_cmp/equal.py +++ /dev/null @@ -1,116 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -# See Notices.txt for copyright information - -""" -Copyright (C) 2020 Luke Kenneth Casson Leighton - -dynamically-partitionable "comparison" class, directly equivalent -to Signal.__eq__ except SIMD-partitionable - -See: - -* http://libre-riscv.org/3d_gpu/architecture/dynamic_simd/eq -* http://bugs.libre-riscv.org/show_bug.cgi?id=132 -""" - -from nmigen import Signal, Module, Elaboratable, Cat, C, Mux, Repl -from nmigen.cli import main - -from ieee754.part_mul_add.partpoints import PartitionPoints - -class PartitionedEq(Elaboratable): - - def __init__(self, width, partition_points): - """Create a ``PartitionedEq`` operator - """ - self.width = width - self.a = Signal(width, reset_less=True) - self.b = Signal(width, reset_less=True) - self.partition_points = PartitionPoints(partition_points) - self.mwidth = len(self.partition_points)+1 - self.output = Signal(self.mwidth, reset_less=True) - if not self.partition_points.fits_in_width(width): - raise ValueError("partition_points doesn't fit in width") - - def elaborate(self, platform): - m = Module() - comb = m.d.comb - - # ok first thing to note, before reading this (read the wiki page - # first), according to boolean algebra, these two are equivalent: - # (~[~eq0, ~eq1, ~eq2].bool()) is the same as (eq0 AND eq1 AND eq2) - # where bool() is the *OR* of all bits in the list. - # - # given that ~eqN is neN (not equal), we first create a series - # of != comparisons on the partitions, then chain the relevant - # ones together depending on partition points, BOOL those together - # and invert the result. - # - # the outer loop is on the partition value. the preparation phase - # (idx array) is to work out how and when the eqs (ne's) are to be - # chained together. finally an inner loop - one per bit - grabs - # each chain, on a per-output-bit basis. - # - # the result is that for each partition-point permutation you get - # a different set of output results for each bit. it's... messy - # but functional. - - # prepare the output bits (name them for convenience) - eqsigs = [] - for i in range(self.mwidth): - eqsig = Signal(name="eqsig%d"%i, reset_less=True) - eqsigs.append(eqsig) - - # make a series of "not-eqs", splitting a and b into partition chunks - nes = Signal(self.mwidth, reset_less=True) - nel = [] - keys = list(self.partition_points.keys()) + [self.width] - start = 0 - for i in range(len(keys)): - end = keys[i] - nel.append(self.a[start:end] != self.b[start:end]) # see bool below - start = end # for next time round loop - comb += nes.eq(Cat(*nel)) - - # now, based on the partition points, create the (multi-)boolean result - # this is a terrible way to do it, it's very laborious. however it - # will actually "work". optimisations come later - - # we want just the partition points, as a number - ppoints = Signal(self.mwidth-1) - comb += ppoints.eq(self.partition_points.as_sig()) - - with m.Switch(ppoints): - for pval in range(1<<(self.mwidth-1)): # for each partition point - # identify (find-first) transition points, and how - # long each partition is - start = 0 - count = 1 - idx = [0] * self.mwidth - for ipdx in range((self.mwidth-1)): - if (pval & (1< - -dynamically-partitionable "comparison" class, directly equivalent -to Signal.__eq__ except SIMD-partitionable - -See: - -* http://libre-riscv.org/3d_gpu/architecture/dynamic_simd/eq -* http://bugs.libre-riscv.org/show_bug.cgi?id=132 -""" - -from nmigen import Signal, Module, Elaboratable, Cat, C, Mux, Repl -from nmigen.cli import main - -from ieee754.part_mul_add.partpoints import PartitionPoints -from ieee754.part_cmp.experiments.eq_combiner import EQCombiner - - -class PartitionedEq(Elaboratable): - - def __init__(self, width, partition_points): - """Create a ``PartitionedEq`` operator - """ - self.width = width - self.a = Signal(width, reset_less=True) - self.b = Signal(width, reset_less=True) - self.partition_points = PartitionPoints(partition_points) - self.mwidth = len(self.partition_points)+1 - self.output = Signal(self.mwidth, reset_less=True) - if not self.partition_points.fits_in_width(width): - raise ValueError("partition_points doesn't fit in width") - - def elaborate(self, platform): - m = Module() - comb = m.d.comb - m.submodules.eqc = eqc = EQCombiner(self.mwidth) - - # make a series of "not-eqs", splitting a and b into partition chunks - nes = Signal(self.mwidth, reset_less=True) - nel = [] - keys = list(self.partition_points.keys()) + [self.width] - start = 0 - for i in range(len(keys)): - end = keys[i] - nel.append(self.a[start:end] != self.b[start:end]) - start = end # for next time round loop - comb += nes.eq(Cat(*nel)) - - comb += eqc.gates.eq(self.partition_points.as_sig()) - comb += eqc.neqs.eq(nes) - comb += self.output.eq(eqc.outputs) - - - return m diff --git a/src/ieee754/part_cmp/experiments/equal.py b/src/ieee754/part_cmp/experiments/equal.py new file mode 100644 index 00000000..21bd0e21 --- /dev/null +++ b/src/ieee754/part_cmp/experiments/equal.py @@ -0,0 +1,116 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# See Notices.txt for copyright information + +""" +Copyright (C) 2020 Luke Kenneth Casson Leighton + +dynamically-partitionable "comparison" class, directly equivalent +to Signal.__eq__ except SIMD-partitionable + +See: + +* http://libre-riscv.org/3d_gpu/architecture/dynamic_simd/eq +* http://bugs.libre-riscv.org/show_bug.cgi?id=132 +""" + +from nmigen import Signal, Module, Elaboratable, Cat, C, Mux, Repl +from nmigen.cli import main + +from ieee754.part_mul_add.partpoints import PartitionPoints + +class PartitionedEq(Elaboratable): + + def __init__(self, width, partition_points): + """Create a ``PartitionedEq`` operator + """ + self.width = width + self.a = Signal(width, reset_less=True) + self.b = Signal(width, reset_less=True) + self.partition_points = PartitionPoints(partition_points) + self.mwidth = len(self.partition_points)+1 + self.output = Signal(self.mwidth, reset_less=True) + if not self.partition_points.fits_in_width(width): + raise ValueError("partition_points doesn't fit in width") + + def elaborate(self, platform): + m = Module() + comb = m.d.comb + + # ok first thing to note, before reading this (read the wiki page + # first), according to boolean algebra, these two are equivalent: + # (~[~eq0, ~eq1, ~eq2].bool()) is the same as (eq0 AND eq1 AND eq2) + # where bool() is the *OR* of all bits in the list. + # + # given that ~eqN is neN (not equal), we first create a series + # of != comparisons on the partitions, then chain the relevant + # ones together depending on partition points, BOOL those together + # and invert the result. + # + # the outer loop is on the partition value. the preparation phase + # (idx array) is to work out how and when the eqs (ne's) are to be + # chained together. finally an inner loop - one per bit - grabs + # each chain, on a per-output-bit basis. + # + # the result is that for each partition-point permutation you get + # a different set of output results for each bit. it's... messy + # but functional. + + # prepare the output bits (name them for convenience) + eqsigs = [] + for i in range(self.mwidth): + eqsig = Signal(name="eqsig%d"%i, reset_less=True) + eqsigs.append(eqsig) + + # make a series of "not-eqs", splitting a and b into partition chunks + nes = Signal(self.mwidth, reset_less=True) + nel = [] + keys = list(self.partition_points.keys()) + [self.width] + start = 0 + for i in range(len(keys)): + end = keys[i] + nel.append(self.a[start:end] != self.b[start:end]) # see bool below + start = end # for next time round loop + comb += nes.eq(Cat(*nel)) + + # now, based on the partition points, create the (multi-)boolean result + # this is a terrible way to do it, it's very laborious. however it + # will actually "work". optimisations come later + + # we want just the partition points, as a number + ppoints = Signal(self.mwidth-1) + comb += ppoints.eq(self.partition_points.as_sig()) + + with m.Switch(ppoints): + for pval in range(1<<(self.mwidth-1)): # for each partition point + # identify (find-first) transition points, and how + # long each partition is + start = 0 + count = 1 + idx = [0] * self.mwidth + for ipdx in range((self.mwidth-1)): + if (pval & (1< + +dynamically-partitionable "comparison" class, directly equivalent +to Signal.__eq__ except SIMD-partitionable + +See: + +* http://libre-riscv.org/3d_gpu/architecture/dynamic_simd/eq +* http://bugs.libre-riscv.org/show_bug.cgi?id=132 +""" + +from nmigen import Signal, Module, Elaboratable, Cat, C, Mux, Repl +from nmigen.cli import main + +from ieee754.part_mul_add.partpoints import PartitionPoints +from ieee754.part_cmp.experiments.eq_combiner import EQCombiner + + +class PartitionedEq(Elaboratable): + + def __init__(self, width, partition_points): + """Create a ``PartitionedEq`` operator + """ + self.width = width + self.a = Signal(width, reset_less=True) + self.b = Signal(width, reset_less=True) + self.partition_points = PartitionPoints(partition_points) + self.mwidth = len(self.partition_points)+1 + self.output = Signal(self.mwidth, reset_less=True) + if not self.partition_points.fits_in_width(width): + raise ValueError("partition_points doesn't fit in width") + + def elaborate(self, platform): + m = Module() + comb = m.d.comb + m.submodules.eqc = eqc = EQCombiner(self.mwidth) + + # make a series of "not-eqs", splitting a and b into partition chunks + nes = Signal(self.mwidth, reset_less=True) + nel = [] + keys = list(self.partition_points.keys()) + [self.width] + start = 0 + for i in range(len(keys)): + end = keys[i] + nel.append(self.a[start:end] != self.b[start:end]) + start = end # for next time round loop + comb += nes.eq(Cat(*nel)) + + comb += eqc.gates.eq(self.partition_points.as_sig()) + comb += eqc.neqs.eq(nes) + comb += self.output.eq(eqc.outputs) + + + return m diff --git a/src/ieee754/part_cmp/experiments/formal/proof_equal.py b/src/ieee754/part_cmp/experiments/formal/proof_equal.py new file mode 100644 index 00000000..441a4c7b --- /dev/null +++ b/src/ieee754/part_cmp/experiments/formal/proof_equal.py @@ -0,0 +1,106 @@ +# Proof of correctness for partitioned equals module +# Copyright (C) 2020 Michael Nolan + +from nmigen import Module, Signal, Elaboratable, Mux +from nmigen.asserts import Assert, AnyConst, Assume +from nmigen.test.utils import FHDLTestCase +from nmigen.cli import rtlil + +from ieee754.part_mul_add.partpoints import PartitionPoints +from ieee754.part_cmp.experiments.equal_ortree import PartitionedEq +import unittest + + +# This defines a module to drive the device under test and assert +# properties about its outputs +class EqualsDriver(Elaboratable): + def __init__(self): + # inputs and outputs + pass + + def get_intervals(self, signal, points): + start = 0 + interval = [] + keys = list(points.keys()) + [signal.width] + for key in keys: + end = key + interval.append(signal[start:end]) + start = end + return interval + + def elaborate(self, platform): + m = Module() + comb = m.d.comb + width = 32 + mwidth = 4 + + # setup the inputs and outputs of the DUT as anyconst + a = Signal(width) + b = Signal(width) + points = PartitionPoints() + gates = Signal(mwidth-1) + for i in range(mwidth-1): + points[i*4+4] = gates[i] + out = Signal(mwidth) + + comb += [a.eq(AnyConst(width)), + b.eq(AnyConst(width)), + gates.eq(AnyConst(mwidth-1))] + + m.submodules.dut = dut = PartitionedEq(width, points) + + a_intervals = self.get_intervals(a, points) + b_intervals = self.get_intervals(b, points) + + with m.Switch(gates): + with m.Case(0b000): + comb += Assert(out == (a == b)) + with m.Case(0b001): + comb += Assert(out[1] == ((a_intervals[1] == b_intervals[1]) & + (a_intervals[2] == b_intervals[2]) & + (a_intervals[3] == b_intervals[3]))) + comb += Assert(out[0] == (a_intervals[0] == b_intervals[0])) + with m.Case(0b010): + comb += Assert(out[2] == ((a_intervals[2] == b_intervals[2]) & + (a_intervals[3] == b_intervals[3]))) + comb += Assert(out[0] == ((a_intervals[0] == b_intervals[0]) & + (a_intervals[1] == b_intervals[1]))) + with m.Case(0b011): + comb += Assert(out[2] == ((a_intervals[2] == b_intervals[2]) & + (a_intervals[3] == b_intervals[3]))) + comb += Assert(out[0] == (a_intervals[0] == b_intervals[0])) + comb += Assert(out[1] == (a_intervals[1] == b_intervals[1])) + with m.Case(0b100): + comb += Assert(out[0] == ((a_intervals[0] == b_intervals[0]) & + (a_intervals[1] == b_intervals[1]) & + (a_intervals[2] == b_intervals[2]))) + comb += Assert(out[3] == (a_intervals[3] == b_intervals[3])) + with m.Case(0b101): + comb += Assert(out[1] == ((a_intervals[1] == b_intervals[1]) & + (a_intervals[2] == b_intervals[2]))) + comb += Assert(out[3] == (a_intervals[3] == b_intervals[3])) + comb += Assert(out[0] == (a_intervals[0] == b_intervals[0])) + with m.Case(0b110): + comb += Assert(out[0] == ((a_intervals[0] == b_intervals[0]) & + (a_intervals[1] == b_intervals[1]))) + comb += Assert(out[3] == (a_intervals[3] == b_intervals[3])) + comb += Assert(out[2] == (a_intervals[2] == b_intervals[2])) + with m.Case(0b111): + for i in range(mwidth-1): + comb += Assert(out[i] == (a_intervals[i] == b_intervals[i])) + + + + comb += [dut.a.eq(a), + dut.b.eq(b), + out.eq(dut.output)] + return m + +class PartitionedEqTestCase(FHDLTestCase): + def test_eq(self): + module = EqualsDriver() + self.assertFormal(module, mode="bmc", depth=4) + +if __name__ == "__main__": + unittest.main() + diff --git a/src/ieee754/part_cmp/experiments/ge.py b/src/ieee754/part_cmp/experiments/ge.py new file mode 100644 index 00000000..8c3efb31 --- /dev/null +++ b/src/ieee754/part_cmp/experiments/ge.py @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# See Notices.txt for copyright information + +""" +Copyright (C) 2020 Luke Kenneth Casson Leighton + +dynamically-partitionable "comparison" class, directly equivalent +to Signal.__ge__ except SIMD-partitionable + +See: + +* http://libre-riscv.org/3d_gpu/architecture/dynamic_simd/ge +* http://bugs.libre-riscv.org/show_bug.cgi?id=132 +""" + +from nmigen import Signal, Module, Elaboratable, Cat, C, Mux, Repl +from nmigen.cli import main + +from ieee754.part_mul_add.partpoints import PartitionPoints + +def create_ge(nes, les, start, count): + """create a greater-than-or-equal from partitioned eqs and greaterthans + + this works by doing: lt3 | + (lt2 & eq3) | + (lt1 & eq3 & eq2) | + (lt0 & eq3 & eq2 & eq1) + """ + res = [] + for i in range(count-1): + ands = [les[start+count-1]] # always one, and it's the end one + res.append(les[start+i]) + + +class PartitionedGe(Elaboratable): + + def __init__(self, width, partition_points): + """Create a ``PartitionedGe`` operator + """ + self.width = width + self.a = Signal(width, reset_less=True) + self.b = Signal(width, reset_less=True) + self.partition_points = PartitionPoints(partition_points) + self.mwidth = len(self.partition_points)+1 + self.output = Signal(self.mwidth, reset_less=True) + if not self.partition_points.fits_in_width(width): + raise ValueError("partition_points doesn't fit in width") + + def elaborate(self, platform): + m = Module() + comb = m.d.comb + + # see equal.py notes + + # prepare the output bits (name them for convenience) + gtsigs = [] + for i in range(self.mwidth): + gtsig = Signal(name="gtsig%d"%i, reset_less=True) + gtsigs.append(gtsig) + + # make series of !eqs and !gts, splitting a and b into partition chunks + nes = Signal(self.mwidth, reset_less=True) + les = Signal(self.mwidth, reset_less=True) + nel = [] + lel = [] + keys = list(self.partition_points.keys()) + [self.width] + start = 0 + for i in range(len(keys)): + end = keys[i] + nel.append(self.a[start:end] != self.b[start:end]) # see bool below + lel.append(self.a[start:end] <= self.b[start:end]) # see bool below + start = end # for next time round loop + comb += nes.eq(Cat(*nel)) + comb += les.eq(Cat(*nel)) + + # now, based on the partition points, create the (multi-)boolean result + # this is a terrible way to do it, it's very laborious. however it + # will actually "work". optimisations come later + + # we want just the partition points, as a number + ppoints = Signal(self.mwidth-1) + comb += ppoints.eq(self.partition_points.as_sig()) + + with m.Switch(ppoints): + for pval in range(1<<(self.mwidth-1)): # for each partition point + # identify (find-first) transition points, and how + # long each partition is + start = 0 + count = 1 + idx = [0] * self.mwidth + for ipdx in range((self.mwidth-1)): + if (pval & (1< - -from nmigen import Module, Signal, Elaboratable, Mux -from nmigen.asserts import Assert, AnyConst, Assume -from nmigen.test.utils import FHDLTestCase -from nmigen.cli import rtlil - -from ieee754.part_mul_add.partpoints import PartitionPoints -from ieee754.part_cmp.equal_ortree import PartitionedEq -import unittest - - -# This defines a module to drive the device under test and assert -# properties about its outputs -class EqualsDriver(Elaboratable): - def __init__(self): - # inputs and outputs - pass - - def get_intervals(self, signal, points): - start = 0 - interval = [] - keys = list(points.keys()) + [signal.width] - for key in keys: - end = key - interval.append(signal[start:end]) - start = end - return interval - - def elaborate(self, platform): - m = Module() - comb = m.d.comb - width = 32 - mwidth = 4 - - # setup the inputs and outputs of the DUT as anyconst - a = Signal(width) - b = Signal(width) - points = PartitionPoints() - gates = Signal(mwidth-1) - for i in range(mwidth-1): - points[i*4+4] = gates[i] - out = Signal(mwidth) - - comb += [a.eq(AnyConst(width)), - b.eq(AnyConst(width)), - gates.eq(AnyConst(mwidth-1))] - - m.submodules.dut = dut = PartitionedEq(width, points) - - a_intervals = self.get_intervals(a, points) - b_intervals = self.get_intervals(b, points) - - with m.Switch(gates): - with m.Case(0b000): - comb += Assert(out == (a == b)) - with m.Case(0b001): - comb += Assert(out[1] == ((a_intervals[1] == b_intervals[1]) & - (a_intervals[2] == b_intervals[2]) & - (a_intervals[3] == b_intervals[3]))) - comb += Assert(out[0] == (a_intervals[0] == b_intervals[0])) - with m.Case(0b010): - comb += Assert(out[2] == ((a_intervals[2] == b_intervals[2]) & - (a_intervals[3] == b_intervals[3]))) - comb += Assert(out[0] == ((a_intervals[0] == b_intervals[0]) & - (a_intervals[1] == b_intervals[1]))) - with m.Case(0b011): - comb += Assert(out[2] == ((a_intervals[2] == b_intervals[2]) & - (a_intervals[3] == b_intervals[3]))) - comb += Assert(out[0] == (a_intervals[0] == b_intervals[0])) - comb += Assert(out[1] == (a_intervals[1] == b_intervals[1])) - with m.Case(0b100): - comb += Assert(out[0] == ((a_intervals[0] == b_intervals[0]) & - (a_intervals[1] == b_intervals[1]) & - (a_intervals[2] == b_intervals[2]))) - comb += Assert(out[3] == (a_intervals[3] == b_intervals[3])) - with m.Case(0b101): - comb += Assert(out[1] == ((a_intervals[1] == b_intervals[1]) & - (a_intervals[2] == b_intervals[2]))) - comb += Assert(out[3] == (a_intervals[3] == b_intervals[3])) - comb += Assert(out[0] == (a_intervals[0] == b_intervals[0])) - with m.Case(0b110): - comb += Assert(out[0] == ((a_intervals[0] == b_intervals[0]) & - (a_intervals[1] == b_intervals[1]))) - comb += Assert(out[3] == (a_intervals[3] == b_intervals[3])) - comb += Assert(out[2] == (a_intervals[2] == b_intervals[2])) - with m.Case(0b111): - for i in range(mwidth-1): - comb += Assert(out[i] == (a_intervals[i] == b_intervals[i])) - - - - comb += [dut.a.eq(a), - dut.b.eq(b), - out.eq(dut.output)] - return m - -class PartitionedEqTestCase(FHDLTestCase): - def test_eq(self): - module = EqualsDriver() - self.assertFormal(module, mode="bmc", depth=4) - -if __name__ == "__main__": - unittest.main() - diff --git a/src/ieee754/part_cmp/ge.py b/src/ieee754/part_cmp/ge.py deleted file mode 100644 index 8c3efb31..00000000 --- a/src/ieee754/part_cmp/ge.py +++ /dev/null @@ -1,117 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -# See Notices.txt for copyright information - -""" -Copyright (C) 2020 Luke Kenneth Casson Leighton - -dynamically-partitionable "comparison" class, directly equivalent -to Signal.__ge__ except SIMD-partitionable - -See: - -* http://libre-riscv.org/3d_gpu/architecture/dynamic_simd/ge -* http://bugs.libre-riscv.org/show_bug.cgi?id=132 -""" - -from nmigen import Signal, Module, Elaboratable, Cat, C, Mux, Repl -from nmigen.cli import main - -from ieee754.part_mul_add.partpoints import PartitionPoints - -def create_ge(nes, les, start, count): - """create a greater-than-or-equal from partitioned eqs and greaterthans - - this works by doing: lt3 | - (lt2 & eq3) | - (lt1 & eq3 & eq2) | - (lt0 & eq3 & eq2 & eq1) - """ - res = [] - for i in range(count-1): - ands = [les[start+count-1]] # always one, and it's the end one - res.append(les[start+i]) - - -class PartitionedGe(Elaboratable): - - def __init__(self, width, partition_points): - """Create a ``PartitionedGe`` operator - """ - self.width = width - self.a = Signal(width, reset_less=True) - self.b = Signal(width, reset_less=True) - self.partition_points = PartitionPoints(partition_points) - self.mwidth = len(self.partition_points)+1 - self.output = Signal(self.mwidth, reset_less=True) - if not self.partition_points.fits_in_width(width): - raise ValueError("partition_points doesn't fit in width") - - def elaborate(self, platform): - m = Module() - comb = m.d.comb - - # see equal.py notes - - # prepare the output bits (name them for convenience) - gtsigs = [] - for i in range(self.mwidth): - gtsig = Signal(name="gtsig%d"%i, reset_less=True) - gtsigs.append(gtsig) - - # make series of !eqs and !gts, splitting a and b into partition chunks - nes = Signal(self.mwidth, reset_less=True) - les = Signal(self.mwidth, reset_less=True) - nel = [] - lel = [] - keys = list(self.partition_points.keys()) + [self.width] - start = 0 - for i in range(len(keys)): - end = keys[i] - nel.append(self.a[start:end] != self.b[start:end]) # see bool below - lel.append(self.a[start:end] <= self.b[start:end]) # see bool below - start = end # for next time round loop - comb += nes.eq(Cat(*nel)) - comb += les.eq(Cat(*nel)) - - # now, based on the partition points, create the (multi-)boolean result - # this is a terrible way to do it, it's very laborious. however it - # will actually "work". optimisations come later - - # we want just the partition points, as a number - ppoints = Signal(self.mwidth-1) - comb += ppoints.eq(self.partition_points.as_sig()) - - with m.Switch(ppoints): - for pval in range(1<<(self.mwidth-1)): # for each partition point - # identify (find-first) transition points, and how - # long each partition is - start = 0 - count = 1 - idx = [0] * self.mwidth - for ipdx in range((self.mwidth-1)): - if (pval & (1<