2 # SPDX-License-Identifier: LGPL-3-or-later
3 # See Notices.txt for copyright information
6 * https://libre-soc.org/3d_gpu/architecture/dynamic_simd/shape/
7 * https://bugs.libre-soc.org/show_bug.cgi?id=713#c20
8 * https://bugs.libre-soc.org/show_bug.cgi?id=713#c30
9 * https://bugs.libre-soc.org/show_bug.cgi?id=713#c34
10 * https://bugs.libre-soc.org/show_bug.cgi?id=713#c47
11 * https://bugs.libre-soc.org/show_bug.cgi?id=713#c22
12 * https://bugs.libre-soc.org/show_bug.cgi?id=713#c67
15 from nmigen
import Signal
, Module
, Elaboratable
, Mux
, Cat
, Shape
, Repl
16 from nmigen
.back
.pysim
import Simulator
, Delay
, Settle
17 from nmigen
.cli
import rtlil
19 from collections
.abc
import Mapping
20 from functools
import reduce
22 from collections
import defaultdict
23 from pprint
import pprint
25 from ieee754
.part_mul_add
.partpoints
import PartitionPoints
28 # main fn, which started out here in the bugtracker:
29 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c20
30 def layout(elwid
, signed
, part_counts
, lane_shapes
=None, fixed_width
=None):
31 # when there are no lane_shapes specified, this indicates a
32 # desire to use the maximum available space based on the fixed width
33 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c67
34 if lane_shapes
is None:
35 assert fixed_width
is not None, \
36 "both fixed_width and lane_shapes cannot be None"
37 lane_shapes
= {i
: fixed_width
// part_counts
[i
] for i
in part_counts
}
38 print("lane_shapes", fixed_width
, lane_shapes
)
39 # identify if the lane_shapes is a mapping (dict, etc.)
40 # if not, then assume that it is an integer (width) that
41 # needs to be requested across all partitions
42 if not isinstance(lane_shapes
, Mapping
):
43 lane_shapes
= {i
: lane_shapes
for i
in part_counts
}
44 # compute a set of partition widths
45 cpart_wid
= [-lane_shapes
[i
] // c
for i
, c
in part_counts
.items()]
46 print("cpart_wid", cpart_wid
, "part_counts", part_counts
)
47 cpart_wid
= -min(cpart_wid
)
48 part_count
= max(part_counts
.values())
49 # calculate the minumum width required
50 width
= cpart_wid
* part_count
51 print("width", width
, cpart_wid
, part_count
)
52 if fixed_width
is not None: # override the width and part_wid
53 assert width
< fixed_width
, "not enough space to fit partitions"
54 part_wid
= fixed_width
// part_count
55 assert part_wid
* part_count
== fixed_width
, \
56 "calculated width not aligned multiples"
58 print("part_wid", part_wid
, "count", part_count
)
60 # go with computed width
62 # create the breakpoints dictionary.
63 # do multi-stage version https://bugs.libre-soc.org/show_bug.cgi?id=713#c34
64 # https://stackoverflow.com/questions/26367812/
65 dpoints
= defaultdict(list) # if empty key, create a (empty) list
66 for i
, c
in part_counts
.items():
68 dpoints
[p
].append(i
) # auto-creates list if key non-existent
69 for start
in range(0, part_count
, c
):
70 add_p(start
* part_wid
) # start of lane
71 add_p(start
* part_wid
+ lane_shapes
[i
]) # start of padding
72 # do not need the breakpoints at the very start or the very end
74 dpoints
.pop(width
, None)
75 plist
= list(dpoints
.keys())
79 # second stage, add (map to) the elwidth==i expressions.
80 # TODO: use nmutil.treereduce?
83 points
[p
] = map(lambda i
: elwid
== i
, dpoints
[p
])
84 points
[p
] = reduce(operator
.or_
, points
[p
])
85 # third stage, create the binary values which *if* elwidth is set to i
86 # *would* result in the mask at that elwidth being set to this value
87 # these can easily be double-checked through Assertion
89 for i
in part_counts
.keys():
91 for p
, elwidths
in dpoints
.items():
93 bitpos
= plist
.index(p
)
94 bitp
[i
] |
= 1 << bitpos
95 # fourth stage: determine which partitions are 100% unused.
96 # these can then be "blanked out"
97 bmask
= (1 << len(plist
))-1
98 for p
in bitp
.values():
100 return (PartitionPoints(points
), bitp
, bmask
, width
, lane_shapes
,
101 part_wid
, part_count
)
104 if __name__
== '__main__':
106 # for each element-width (elwidth 0-3) the number of partitions is given
107 # elwidth=0b00 QTY 1 partitions: | ? |
108 # elwidth=0b01 QTY 1 partitions: | ? |
109 # elwidth=0b10 QTY 2 partitions: | ? | ? |
110 # elwidth=0b11 QTY 4 partitions: | ? | ? | ? | ? |
111 # actual widths of Signals *within* those partitions is given separately
119 # width=3 indicates "we want the same width (3) at all elwidths"
120 # elwidth=0b00 1x 5-bit | ..3 |
121 # elwidth=0b01 1x 6-bit | ..3 |
122 # elwidth=0b10 2x 12-bit | ..3 | ..3 |
123 # elwidth=0b11 3x 24-bit | ..3| ..3 | ..3 |..3 |
124 width_in_all_parts
= 3
127 pprint((i
, layout(i
, True, part_counts
, width_in_all_parts
)))
129 # fixed_width=32 and no lane_widths says "allocate maximum"
130 # elwidth=0b00 1x 32-bit | .................32 |
131 # elwidth=0b01 1x 32-bit | .................32 |
132 # elwidth=0b10 2x 12-bit | ......16 | ......16 |
133 # elwidth=0b11 3x 24-bit | ..8| ..8 | ..8 |..8 |
135 #print ("maximum allocation from fixed_width=32")
137 # pprint((i, layout(i, True, part_counts, fixed_width=32)))
139 # specify that the length is to be *different* at each of the elwidths.
140 # combined with part_counts we have:
141 # elwidth=0b00 1x 5-bit | ....5 |
142 # elwidth=0b01 1x 6-bit | .....6 |
143 # elwidth=0b10 2x 12-bit | ....12 | .....12 |
144 # elwidth=0b11 3x 24-bit | 24 | 24 | 24 | 24 |
145 widths_at_elwidth
= {
153 pprint((i
, layout(i
, False, part_counts
, widths_at_elwidth
)))
155 # this tests elwidth as an actual Signal. layout is allowed to
156 # determine arbitrarily the overall length
157 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c30
160 pp
, bitp
, bm
, b
, c
, d
, e
= layout(
161 elwid
, False, part_counts
, widths_at_elwidth
)
162 pprint((pp
, b
, c
, d
, e
))
163 for k
, v
in bitp
.items():
164 print("bitp elwidth=%d" % k
, bin(v
))
165 print("bmask", bin(bm
))
174 for pval
in list(pp
.values()):
175 val
= yield pval
# get nmigen to evaluate pp
177 pprint((i
, (ppt
, b
, c
, d
, e
)))
178 # check the results against bitp static-expected partition points
179 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c47
180 # https://stackoverflow.com/a/27165694
181 ival
= int(''.join(map(str, ppt
[::-1])), 2)
182 assert ival
== bitp
[i
]
185 sim
.add_process(process
)
188 # this tests elwidth as an actual Signal. layout is *not* allowed to
189 # determine arbitrarily the overall length, it is fixed to 64
190 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c22
193 pp
, bitp
, bm
, b
, c
, d
, e
= layout(elwid
, False, part_counts
, widths_at_elwidth
,
195 pprint((pp
, b
, c
, d
, e
))
196 for k
, v
in bitp
.items():
197 print("bitp elwidth=%d" % k
, bin(v
))
198 print("bmask", bin(bm
))
207 for pval
in list(pp
.values()):
208 val
= yield pval
# get nmigen to evaluate pp
210 print("test elwidth=%d" % i
)
211 pprint((i
, (ppt
, b
, c
, d
, e
)))
212 # check the results against bitp static-expected partition points
213 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c47
214 # https://stackoverflow.com/a/27165694
215 ival
= int(''.join(map(str, ppt
[::-1])), 2)
216 assert ival
== bitp
[i
], "ival %s actual %s" % (bin(ival
),
220 sim
.add_process(process
)