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
14 from nmigen
import Signal
, Module
, Elaboratable
, Mux
, Cat
, Shape
, Repl
15 from nmigen
.back
.pysim
import Simulator
, Delay
, Settle
16 from nmigen
.cli
import rtlil
18 from collections
.abc
import Mapping
19 from functools
import reduce
21 from collections
import defaultdict
22 from pprint
import pprint
24 from ieee754
.part_mul_add
.partpoints
import PartitionPoints
27 # main fn, which started out here in the bugtracker:
28 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c20
29 def layout(elwid
, signed
, part_counts
, lane_shapes
, fixed_width
=None):
30 # identify if the lane_shapes is a mapping (dict, etc.)
31 # if not, then assume that it is an integer (width) that
32 # needs to be requested across all partitions
33 if not isinstance(lane_shapes
, Mapping
):
34 lane_shapes
= {i
: lane_shapes
for i
in part_counts
}
35 # compute a set of partition widths
36 cpart_wid
= -min(-lane_shapes
[i
] // c
for i
, c
in part_counts
.items())
37 part_count
= max(part_counts
.values())
38 # calculate the minumum width required
39 width
= cpart_wid
* part_count
40 if fixed_width
is not None: # override the width and part_wid
41 assert width
< fixed_width
, "not enough space to fit partitions"
42 part_wid
= fixed_width
// part_count
43 assert part_wid
* part_count
== fixed_width
, \
44 "calculated width not aligned multiples"
46 print ("part_wid", part_wid
, "count", part_count
)
48 # go with computed width
50 # create the breakpoints dictionary.
51 # do multi-stage version https://bugs.libre-soc.org/show_bug.cgi?id=713#c34
52 # https://stackoverflow.com/questions/26367812/
53 dpoints
= defaultdict(list) # if empty key, create a (empty) list
54 for i
, c
in part_counts
.items():
56 dpoints
[p
].append(i
) # auto-creates list if key non-existent
57 for start
in range(0, part_count
, c
):
58 add_p(start
* part_wid
) # start of lane
59 add_p(start
* part_wid
+ lane_shapes
[i
]) # start of padding
60 # do not need the breakpoints at the very start or the very end
62 dpoints
.pop(width
, None)
63 plist
= list(dpoints
.keys())
67 # second stage, add (map to) the elwidth==i expressions.
68 # TODO: use nmutil.treereduce?
71 points
[p
] = map(lambda i
: elwid
== i
, dpoints
[p
])
72 points
[p
] = reduce(operator
.or_
, points
[p
])
73 # third stage, create the binary values which *if* elwidth is set to i
74 # *would* result in the mask at that elwidth being set to this value
75 # these can easily be double-checked through Assertion
77 for i
in part_counts
.keys():
79 for p
, elwidths
in dpoints
.items():
81 bitpos
= plist
.index(p
)
83 # fourth stage: determine which partitions are 100% unused.
84 # these can then be "blanked out"
85 bmask
= (1<<len(plist
))-1
86 for p
in bitp
.values():
88 return (PartitionPoints(points
), bitp
, bmask
, width
, lane_shapes
,
92 if __name__
== '__main__':
94 # for each element-width (elwidth 0-3) the number of partitions is given
95 # elwidth=0b00 QTY 1 partitions: | ? |
96 # elwidth=0b01 QTY 1 partitions: | ? |
97 # elwidth=0b10 QTY 2 partitions: | ? | ? |
98 # elwidth=0b11 QTY 4 partitions: | ? | ? | ? | ? |
99 # actual widths of Signals *within* those partitions is given separately
107 # width=3 indicates "we want the same width (3) at all elwidths"
108 # elwidth=0b00 1x 5-bit | ..3 |
109 # elwidth=0b01 1x 6-bit | ..3 |
110 # elwidth=0b10 2x 12-bit | ..3 | ..3 |
111 # elwidth=0b11 3x 24-bit | ..3| ..3 | ..3 |..3 |
112 width_in_all_parts
= 3
115 pprint((i
, layout(i
, True, part_counts
, width_in_all_parts
)))
117 # specify that the length is to be *different* at each of the elwidths.
118 # combined with part_counts we have:
119 # elwidth=0b00 1x 5-bit | ....5 |
120 # elwidth=0b01 1x 6-bit | .....6 |
121 # elwidth=0b10 2x 12-bit | ....12 | .....12 |
122 # elwidth=0b11 3x 24-bit | 24 | 24 | 24 | 24 |
123 widths_at_elwidth
= {
131 pprint((i
, layout(i
, False, part_counts
, widths_at_elwidth
)))
133 # this tests elwidth as an actual Signal. layout is allowed to
134 # determine arbitrarily the overall length
135 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c30
138 pp
,bitp
,bm
,b
,c
,d
,e
= layout(elwid
, False, part_counts
, widths_at_elwidth
)
139 pprint ((pp
,b
,c
,d
,e
))
140 for k
, v
in bitp
.items():
141 print ("bitp elwidth=%d" % k
, bin(v
))
142 print ("bmask", bin(bm
))
150 for pval
in list(pp
.values()):
151 val
= yield pval
# get nmigen to evaluate pp
153 pprint((i
, (ppt
,b
,c
,d
,e
)))
154 # check the results against bitp static-expected partition points
155 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c47
156 # https://stackoverflow.com/a/27165694
157 ival
= int(''.join(map(str, ppt
[::-1])), 2)
158 assert ival
== bitp
[i
]
161 sim
.add_process(process
)
164 # this tests elwidth as an actual Signal. layout is *not* allowed to
165 # determine arbitrarily the overall length, it is fixed to 64
166 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c22
169 pp
,bitp
,bm
,b
,c
,d
,e
= layout(elwid
, False, part_counts
, widths_at_elwidth
,
171 pprint ((pp
,b
,c
,d
,e
))
172 for k
, v
in bitp
.items():
173 print ("bitp elwidth=%d" % k
, bin(v
))
174 print ("bmask", bin(bm
))
182 for pval
in list(pp
.values()):
183 val
= yield pval
# get nmigen to evaluate pp
185 print ("test elwidth=%d" % i
)
186 pprint((i
, (ppt
,b
,c
,d
,e
)))
187 # check the results against bitp static-expected partition points
188 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c47
189 # https://stackoverflow.com/a/27165694
190 ival
= int(''.join(map(str, ppt
[::-1])), 2)
191 assert ival
== bitp
[i
], "ival %s actual %s" % (bin(ival
),
195 sim
.add_process(process
)