[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
Fri Oct 8 01:36:29 BST 2021


https://bugs.libre-soc.org/show_bug.cgi?id=713

--- Comment #36 from Jacob Lifshay <programmerjake at gmail.com> ---
(In reply to Luke Kenneth Casson Leighton from comment #34)
> (In reply to Luke Kenneth Casson Leighton from comment #32)
> 
> > what are the implications of that being a nmigen AST
> > code-fragment rather than a plain slice on a Signal...
> > i'll have to think that through.
> 
> got it.
> 
> it wasn't actually whether it's a code-fragment or not at all
> (which comes with inherent risks of AST Expression duplication
> which yosys can *in no way* properly handle and optimise away)

only if you never run `opt`...expression deduplication is a pretty trivial
compiler optimization. In my experience, yosys deduplicates built-in
expressions just fine.
> 
> it was the submodules.

we can deduplicate expressions ourselves pretty easily:
def deduped(f):
    map = {}
    def wrapper(*args, **kwargs):
        key0 = []
        for arg in args:
            key0.append(id(arg))
        key1 = []
        for k, v in kwargs.items():
            key1.append((id(k), id(v)))
        key = tuple(key0), tuple(key1)
        if key in map:
            return map[key][0]
        retval = f(*args, **kwargs)
        # keep reference to args and kwargs to avoid ids
        # getting reused for something else. we can't use weakref,
        # too many things don't work with it such as `list` and `dict`
        map[key] = retval, args, kwargs
        return retval
    return wrapper

class PartitionedSignal:
    ...
    @deduped
    def __add__(self, other):
        ...

    @deduped
    def __sub__(self, other):
        ...

    ...

> 
> example:
> 
> https://git.libre-soc.org/?p=ieee754fpu.git;a=blob;f=src/ieee754/part_ass/
> assign.py;h=00cebc6dab33a25f93d4e790dd7883fc859bae9e;hb=HEAD#l88
> 
> that Switch, to support elwidths, would become Switch(self.elwidth)
> 
> the next problem is: how do you now reduce the number of Case() statements
> needed to four, or, more to the point, on doing so which bits of data do
> you copy from which partition, without the prerequisite information?

easy, each `part_wid` wide bit slice in any PartitionedSignal can be
independently muxed -- as long as the condition is first converted to the
layout:
{
    ElWid.I8: 1,
    ElWid.I16: 1,
    ElWid.I32: 1,
    ElWid.I64: 1,
}
with padding being a sign-extended version of each lane... Aka. the result of
PartitionedSignal.bool(). (or what *should* be the result if design were left
to me).

lanes for elwid=16
  0    1    2    3    4    5    6    7
[-u1-pppp][-u1-pppp][-u1-pppp][-u1-pppp]
example value for that layout for elwid==16 and lanes of False, True, False,
True:
0 1 2 3 4 5 6 7
0 0 1 1 0 0 1 1

m.Case simply is an If/Elif that uses the results of
PartitionedSignal.matches() instead of .bool().

a = PartitionedSignal(XLEN - 2)
b = PartitionedSignal(3)
with m.If(a):
    with m.Switch(b):
        with m.Case('1-0'):
            m.d.comb += c.eq(d)
with m.Elif(b == 1):
    m.d.sync += e.eq(f)

compiles to (full trace, with unnecessary assignments omitted):
a = PartitionedSignal(XLEN - 2)
b = PartitionedSignal(3)
# If.enter
if_cond = cond_stack[-1] & a.bool()
cond_stack.append(if_cond)
# If body
# Switch.enter
switch_value_stack.append(b)
# Switch body
# Case.enter
case_cond = cond_stack[-1] & switch_value_stack[-1].matches('1-0')
cond_stack.append(case_cond)
# Case body
# assign c.eq(d)
rhs = d.cast_to(c.layout)
for i in range(part_count):
    with m.If(cond_stack[-1].sig[i]): # cond_stack all have part_wid == 1
        # slice computation should just be c.layout.part_slice(i)
        start = c.layout.part_wid * i
        s = slice(start, start + c.layout.part_wid)
        m.d.comb += c.sig[s].eq(rhs.sig[s])
# Case.exit
cond_stack.pop()
# Switch.exit
switch_value_stack.pop()
# If.exit
else_cond = ~cond_stack.pop()
else_cond &= cond_stack[-1]
# Elif.enter
if_cond = else_cond & (b == 1).bool()
cond_stack.append(if_cond)
# Elif body
# assign e.eq(f)
rhs = f.cast_to(e.layout)
for i in range(part_count):
    with m.If(cond_stack[-1].sig[i]): # cond_stack all have part_wid == 1
        # slice computation should just be e.layout.part_slice(i)
        start = e.layout.part_wid * i
        s = slice(start, start + e.layout.part_wid)
        m.d.sync += e.sig[s].eq(rhs.sig[s])
# Elif.exit

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


More information about the libre-soc-bugs mailing list