improve code-comments some more
[ieee754fpu.git] / src / ieee754 / part / layout_experiment.py
1 #!/usr/bin/env python3
2 # SPDX-License-Identifier: LGPL-3-or-later
3 # See Notices.txt for copyright information
4 """
5 Links:
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 """
13
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
17
18 from collections.abc import Mapping
19 from functools import reduce
20 import operator
21 from collections import defaultdict
22 from pprint import pprint
23
24 from ieee754.part_mul_add.partpoints import PartitionPoints
25
26
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"
45 width = fixed_width
46 print ("part_wid", part_wid, "count", part_count)
47 else:
48 # go with computed width
49 part_wid = cpart_wid
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():
55 def add_p(p):
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
61 dpoints.pop(0, None)
62 dpoints.pop(width, None)
63 plist = list(dpoints.keys())
64 plist.sort()
65 print ("dpoints")
66 pprint(dict(dpoints))
67 # second stage, add (map to) the elwidth==i expressions.
68 # TODO: use nmutil.treereduce?
69 points = {}
70 for p in plist:
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
76 bitp = {}
77 for i in part_counts.keys():
78 bitp[i] = 0
79 for p, elwidths in dpoints.items():
80 if i in elwidths:
81 bitpos = plist.index(p)
82 bitp[i] |= 1<< bitpos
83 return (PartitionPoints(points), bitp, width, lane_shapes,
84 part_wid, part_count)
85
86
87 if __name__ == '__main__':
88
89 # for each element-width (elwidth 0-3) the number of partitions is given
90 # elwidth=0b00 QTY 1 partitions: | ? |
91 # elwidth=0b01 QTY 1 partitions: | ? |
92 # elwidth=0b10 QTY 2 partitions: | ? | ? |
93 # elwidth=0b11 QTY 4 partitions: | ? | ? | ? | ? |
94 # actual widths of Signals *within* those partitions is given separately
95 part_counts = {
96 0: 1,
97 1: 1,
98 2: 2,
99 3: 4,
100 }
101
102 # width=3 indicates "we want the same width (3) at all elwidths"
103 # elwidth=0b00 1x 5-bit | ..3 |
104 # elwidth=0b01 1x 6-bit | ..3 |
105 # elwidth=0b10 2x 12-bit | ..3 | ..3 |
106 # elwidth=0b11 3x 24-bit | ..3| ..3 | ..3 |..3 |
107 width_in_all_parts = 3
108
109 for i in range(4):
110 pprint((i, layout(i, True, part_counts, width_in_all_parts)))
111
112 # specify that the length is to be *different* at each of the elwidths.
113 # combined with part_counts we have:
114 # elwidth=0b00 1x 5-bit | ....5 |
115 # elwidth=0b01 1x 6-bit | .....6 |
116 # elwidth=0b10 2x 12-bit | ....12 | .....12 |
117 # elwidth=0b11 3x 24-bit | 24 | 24 | 24 | 24 |
118 widths_at_elwidth = {
119 0: 5,
120 1: 6,
121 2: 12,
122 3: 24
123 }
124
125 for i in range(4):
126 pprint((i, layout(i, False, part_counts, widths_at_elwidth)))
127
128 # this tests elwidth as an actual Signal. layout is allowed to
129 # determine arbitrarily the overall length
130 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c30
131
132 elwid = Signal(2)
133 pp,bitp,b,c,d,e = layout(elwid, False, part_counts, widths_at_elwidth)
134 pprint ((pp,b,c,d,e))
135 for k, v in bitp.items():
136 print ("bitp elwidth=%d" % k, bin(v))
137
138 m = Module()
139 def process():
140 for i in range(4):
141 yield elwid.eq(i)
142 yield Settle()
143 ppt = []
144 for pval in list(pp.values()):
145 val = yield pval # get nmigen to evaluate pp
146 ppt.append(val)
147 pprint((i, (ppt,b,c,d,e)))
148 # check the results against bitp static-expected partition points
149 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c47
150 # https://stackoverflow.com/a/27165694
151 ival = int(''.join(map(str, ppt[::-1])), 2)
152 assert ival == bitp[i]
153
154 sim = Simulator(m)
155 sim.add_process(process)
156 sim.run()
157
158 # this tests elwidth as an actual Signal. layout is *not* allowed to
159 # determine arbitrarily the overall length, it is fixed to 64
160 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c22
161
162 elwid = Signal(2)
163 pp,bitp,b,c,d,e = layout(elwid, False, part_counts, widths_at_elwidth,
164 fixed_width=64)
165 pprint ((pp,b,c,d,e))
166 for k, v in bitp.items():
167 print ("bitp elwidth=%d" % k, bin(v))
168
169 m = Module()
170 def process():
171 for i in range(4):
172 yield elwid.eq(i)
173 yield Settle()
174 ppt = []
175 for pval in list(pp.values()):
176 val = yield pval # get nmigen to evaluate pp
177 ppt.append(val)
178 print ("test elwidth=%d" % i)
179 pprint((i, (ppt,b,c,d,e)))
180 # check the results against bitp static-expected partition points
181 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c47
182 # https://stackoverflow.com/a/27165694
183 ival = int(''.join(map(str, ppt[::-1])), 2)
184 assert ival == bitp[i], "ival %s actual %s" % (bin(ival),
185 bin(bitp[i]))
186
187 sim = Simulator(m)
188 sim.add_process(process)
189 sim.run()