1 # SPDX-License-Identifier: LGPL-2.1-or-later
2 # See Notices.txt for copyright information
5 Copyright (C) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
7 dynamically-partitionable "assign" class, directly equivalent
12 * http://libre-riscv.org/3d_gpu/architecture/dynamic_simd/assign
13 * http://bugs.libre-riscv.org/show_bug.cgi?id=709
17 from nmigen
import Signal
, Module
, Elaboratable
, Cat
, Const
, signed
18 from nmigen
.back
.pysim
import Simulator
, Settle
19 from nmutil
.extend
import ext
21 from ieee754
.part_mul_add
.partpoints
import PartitionPoints
22 from ieee754
.part
.partsig
import PartitionedSignal
25 def get_runlengths(pbit
, size
):
28 # identify where the 1s are, which indicates "start of a new partition"
29 # we want a list of the lengths of all partitions
31 if pbit
& (1<<i
): # it's a 1: ends old partition, starts new
32 res
.append(count
) # add partition
33 count
= 1 # start again
36 # end reached, add whatever is left. could have done this by creating
37 # "fake" extra bit on the partitions, but hey
40 print ("get_runlengths", bin(pbit
), size
, res
)
45 class PartitionedAssign(Elaboratable
):
46 def __init__(self
, shape
, assign
, mask
):
47 """Create a ``PartitionedAssign`` operator
49 # work out the length (total of all PartitionedSignals)
51 if isinstance(mask
, dict):
52 mask
= list(mask
.values())
55 self
.output
= PartitionedSignal(mask
, self
.shape
, reset_less
=True)
56 self
.partition_points
= self
.output
.partpoints
57 self
.mwidth
= len(self
.partition_points
)+1
59 def get_chunk(self
, y
, numparts
):
61 if not isinstance(x
, PartitionedSignal
):
62 # assume Scalar. totally different rules
63 end
= numparts
* (len(x
) // self
.mwidth
)
65 # PartitionedSignal: start at partition point
66 keys
= [0] + list(x
.partpoints
.keys()) + [len(x
)]
67 # get current index and increment it (for next Assign chunk)
70 print ("getting", upto
, numparts
, keys
, len(x
))
71 # get the partition point as far as we are up to
73 end
= keys
[upto
+numparts
]
74 print ("start end", start
, end
, len(x
))
77 def elaborate(self
, platform
):
81 keys
= list(self
.partition_points
.keys())
82 print ("keys", keys
, "values", self
.partition_points
.values())
83 print ("mask", self
.mask
)
84 outpartsize
= len(self
.output
) // self
.mwidth
85 width
, signed
= self
.output
.shape()
86 print ("width, signed", width
, signed
)
88 with m
.Switch(Cat(self
.mask
)):
89 # for each partition possibility, create a Assign sequence
90 for pbit
in range(1<<len(keys
)):
91 # set up some indices pointing to where things have got
92 # then when called below in the inner nested loop they give
93 # the relevant sequential chunk
96 # get a list of the length of each partition run
97 runlengths
= get_runlengths(pbit
, len(keys
))
98 print ("pbit", bin(pbit
), "runs", runlengths
)
99 for i
in runlengths
: # for each partition
100 thing
= self
.get_chunk(y
, i
) # sequential chunks
101 # now check the length: truncate, extend or leave-alone
102 outlen
= i
* outpartsize
104 thing
= ext(thing
, (tlen
, signed
), outlen
)
107 # direct access to the underlying Signal
108 comb
+= self
.output
.sig
.eq(Cat(*output
))
113 if isinstance(self
.assign
, PartitionedSignal
):
114 return [self
.assign
.lower(), self
.output
.lower()]
115 return [self
.assign
, self
.output
.lower()]
118 if __name__
== "__main__":
119 from ieee754
.part
.test
.test_partsig
import create_simulator
122 a
= PartitionedSignal(mask
, 32)
123 m
.submodules
.ass
= ass
= PartitionedAssign(signed(48), a
, mask
)
124 omask
= (1<<len(ass
.output
))-1
127 sim
= create_simulator(m
, traces
, "partass")
131 yield a
.sig
.eq(0xa12345c7)
133 out
= yield ass
.output
.sig
134 print("out 000", bin(out
&omask
), hex(out
&omask
))
137 out
= yield ass
.output
.sig
138 print("out 010", bin(out
&omask
), hex(out
&omask
))
141 out
= yield ass
.output
.sig
142 print("out 110", bin(out
&omask
), hex(out
&omask
))
145 out
= yield ass
.output
.sig
146 print("out 111", bin(out
&omask
), hex(out
&omask
))
148 sim
.add_process(process
)
149 with sim
.write_vcd("partition_ass.vcd", "partition_ass.gtkw",
157 m
.submodules
.ass
= ass
= PartitionedAssign(signed(48), a
, mask
)
158 omask
= (1<<len(ass
.output
))-1
161 sim
= create_simulator(m
, traces
, "partass")
165 yield a
.eq(0xa12345c7)
167 out
= yield ass
.output
.sig
168 print("out 000", bin(out
&omask
), hex(out
&omask
))
171 out
= yield ass
.output
.sig
172 print("out 010", bin(out
&omask
), hex(out
&omask
))
175 out
= yield ass
.output
.sig
176 print("out 110", bin(out
&omask
), hex(out
&omask
))
179 out
= yield ass
.output
.sig
180 print("out 111", bin(out
&omask
), hex(out
&omask
))
182 sim
.add_process(process
)
183 with sim
.write_vcd("partition_ass.vcd", "partition_ass.gtkw",