PartitionedSignal is a dynamic SIMD version of Signal. it needs to work with the following constructs:
* m.If / Elif / Else
* m.Switch / Case
without PartitionedSignal, SIMD has to be done as follows:
with m.If(partition == 64):
operations treating Signals at full 64 bit
with m.Elif(partition == 2x32bit):
for i in range(2):
exactly the same code as 64 bit
except now it is repeated twice,
first on the low 32 bits and then
on the hi 32
and that repetition continues right the way down to 8 bit, complicating design massively.
PartitionedSignal hides that entirely, as far as arithmetic and logic operations are concerned (__lt__, __or__, see bug #132)
where things break down is this:
ppts = PartitionPoints(64, 8)
x = PartitionedSignal(64, partition=ppts)
with m.If(x == 5):
... do something.
the reason it breaks down is because PartitionedSignal.__eq__ does *not* return a single bit object, it returns a *dynamic multi-bit* object that dynamically reflects the current state of the partitioning.
* partition points is set to 8 breaks
* this subdivides the 64 bit signal into 8 separate 8 bit values
* comparison against a constant (5) creates EIGHT separate 8-bit comparisons
* those 8 comparisons are bundled together
when the partitions are cleared to indicate that the 64 bits are to be treated as a single 64 bit value, the comparison "5" is done against the entire 64 bit, however the answer still goes into the same 8 bit object used when the partition was set to 8x8bit.
the m.If construct cannot cope with this.
the current workaround is to completely avoid using m.If entirely and to use a special PartitionedMux construct, PMux, instead.
this however is less than ideal.
similar logic applies to Switch/Case. Arrays need some discussion.
this is the location where the code-fragments for m.If/Elif/Else are
generated, and it should be really pretty straightforward to drop the appropriate replacement (parallel, SIMD) statements in at this location, with a replacement (override) of Module._pop_ctrl
relevant page for describing the dynamic partitioned signal operators
taking a look at this more closely, i realised that Value (and UserValue) will need some tweaks.
at present, the code does this:
return Part(self, ...)
where Part is a global function. in order to support override capability this needs to change to:
return ValuePart(self, ...)
where Part is defined as:
def Part(lhs, ....):
and the *current* Part function is *renamed* to ValuePart.
UserValue then does:
return UserValuePart(self, ...)
or more likely also calls ValuePart but it is clearly documented that user-derived overrides are perfectly well permitted.
PartitionedSignal would do *exactly that*.
then, a crucial addition to Value and UserValue would be:
def mux(self, choice1, choice2):
return ValueMux(self, choice1, choice2)
and PartitionedSignal could override it to provide the correct dynamic SIMD functionality.