[Libre-soc-bugs] [Bug 713] PartitionedSignal enhancement to add partition-context-aware lengths

bugzilla-daemon at libre-soc.org bugzilla-daemon at libre-soc.org
Wed Oct 6 13:01:05 BST 2021


--- Comment #14 from Luke Kenneth Casson Leighton <lkcl at lkcl.net> ---
(In reply to Jacob Lifshay from comment #10)
> (In reply to Luke Kenneth Casson Leighton from comment #9)

> > this suggests a dict as the spec.  mantissa:
> > 
> > { 0b00 : (64, 53),  # FP64
> >   0b01 : (32, 23),  # FP32x2
> >   0b10 : (16, 10),  # FP16
> >   0b11 : (16, 5),  # BF16
> > }
> > 
> > no types, no classes.
> Oh, what I have as types that can be SimdLayout.cast (like nmigen
> Shape.cast) are 1 of 3 options:

3 options when one will do and can be covered by a dict
is massive complication and overengineering.

the *actual* needs come from a 2-bit elwidth, where at each
elwidth i would have said even the power-2 is implicitly
understood, if it wasn't for the fact that in FP we specify
BF16 as one of the options.

int elwidths:

0b00 64
0b01 32
0b10 16
0b11 8


0b00 FP64
0b01 FP32
0b10 FP16
0b11 BF16 16 bit not 8

that puts the "aligned power 2" width (or the number of power2 partitions)
on the requirements, and it can be the first of the tuple under each key.

the second requirement is the *useful* width at each elwidth, nonpow2

there are no other requirements, because supporting different signed/unsigned
is out of the question.

it's very much simpler than SimdKind.

> 1. dict-like types that map lane-size-in-abstract-parts to Shape-castable
> values:
> This (after Shape.cast-ing all dict values) is the canonical internal
> representation of a layout (stored in self.lane_shapes).
> example:
> { # keys are always powers of 2
>     1: 5, # 1-part lanes are u5
>     2: unsigned(3), # 2-part lanes are u3
>     4: range(3, 25), # 4-part lanes are u5 since that fits 24
>     8: MyEnum, # 8-part lanes are u10, assuming MyEnum fits in u10
> }

we need neither Enums nor range, and *definitely* signed/unsigned is
out of the question.

even if using this type of specification, how does it relate to

> or:
> { # keys are always powers of 2
>     1: signed(1), # 1-part lanes are i1
>     2: signed(3), # 2-part lanes are i3
>     4: range(-30, 25), # 4-part lanes are i6 since that fits -30
>     8: MySignedBoolEnum, # 8-part lanes are i1
>     16: signed(0), # 16-part lanes are i0, zero-bit shapes are supported
> }

no.  range enum and signed...  arrgh, these are *subtypes*? 

no, absolutely not.  no way. this is far too advanced, far too complicated.

> 2. callables that take lane-size-in-abstract-parts and return Shape-castable
> values. Useful for making non-uniform layouts quickly.
> example:
> lambda lane_size: signed(lane_size * 8)
> dict equivalent (for part_count == 8):
> {
>     1: signed(8),
>     2: signed(16),
>     4: signed(32),
>     8: signed(64),
> }

signed is out of the question

> 3. a Shape-castable value that is used for all lane-sizes. Useful for making
> uniform layouts quickly.
> example:
> 5
> dict equivalent (for part_count == 8):
> {
>     1: unsigned(5),
>     2: unsigned(5),
>     4: unsigned(5),
>     8: unsigned(5),
> }

unsigned is out of the question.

> From that dict-equivalent (stored as self.lane_shapes), it computes the
> minimum number of bits needed for each abstract part required to pack those
> chosen lane shapes into their abstract parts -- the bits needed per abstract
> part is self.padded_bits_per_part (same value for all parts).
> From that, member functions can calculate the total required signal width in
> bits (self.width), and the unpadded/padded slices for each lane
> (self.lane_bits_slice(lane, include_padding=False/True)).
> > 
> > turning that into PartitionPoints, a simple
> > function creates a dict and a mask Signal.
> PartitionPoints needs to be re-vamped to carry all the necessary information
> -- that's what SimdLayout is.

above i have established that you are making fundamental assumptions
(not discussed until now and only just discovering what they are)
which was part of the silence and rushing ahead creating code without
first talking it through.

what you *believe* is necessary information is in fact unnecessary and
massive over-engineering, making for several days wasted coding time

*if* it was actually practical to support massive numbers of disparate data
types *then* SimdKind etc. would be necesssary.

but you didn't talk this through before going ahead so have gone down
a blind alley.

from the *much* more basic pragmatic requirements we can incrementally
leverage existing code.

if during the actual deployment we end up finding use-cases that are not
covered *then* we can stop and reevaluate.

however by keeping it SIMPLE from the very start and taking some short cuts
we can get the job done FAST, and optimise later.

practical pragmatic engineering.


with that in mind, can you please write a quick prototype pair of
functions which use a dict as input, as in comment #2, returning a
PartitionPoints as a result from the first, and the second function
to take a 2 bit Signal, the same dict spec, a PartitionPoint instance
and a module (m), where it uses the 2 bit Signal as input to a
Switch and the cases set the prerequisite mask bits embedded
as values in the PP instance?

each function should (he said) be self-contained, not require
calling any external functions, and be absolute maximum 35
lines (without comments)

*no types*, not even classes! throw them into the codebase near
make_partition2 (above PartitionPoints) with some really quick
demo tests, we can work out where to put them later.

this is an important and valuable lesson for you to learn
to do ultra-basic pragmatic programming that an HDL Engineer or
a 20 year old 2nd year undergraduate student can
look and and go, "oh, that's blindingly obvious".

You are receiving this mail because:
You are on the CC list for the bug.

More information about the libre-soc-bugs mailing list