1b2f6f0aca22001451b8f3864984f3c0dc0cda37
[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 """
10
11 from nmigen import Signal, Module, Elaboratable, Mux, Cat, Shape, Repl
12 from nmigen.back.pysim import Simulator, Delay, Settle
13 from nmigen.cli import rtlil
14
15 from collections.abc import Mapping
16 from functools import reduce
17 import operator
18 from collections import defaultdict
19 from pprint import pprint
20
21 from ieee754.part_mul_add.partpoints import PartitionPoints
22
23
24 # main fn
25 def layout(elwid, signed, part_counts, lane_shapes):
26 # identify if the lane_shapes is a mapping (dict, etc.)
27 # if not, then assume that it is an integer (width) that
28 # needs to be requested across all partitions
29 if not isinstance(lane_shapes, Mapping):
30 lane_shapes = {i: lane_shapes for i in part_counts}
31 # compute a set of partition widths
32 part_wid = -min(-lane_shapes[i] // c for i, c in part_counts.items())
33 part_count = max(part_counts.values())
34 # calculate the minumum width required
35 width = part_wid * part_count
36 # create the breakpoints dictionary.
37 # do multi-stage version https://bugs.libre-soc.org/show_bug.cgi?id=713#c34
38 # https://stackoverflow.com/questions/26367812/
39 dpoints = defaultdict(list) # if empty key, create a (empty) list
40 for i, c in part_counts.items():
41 def add_p(p):
42 dpoints[p].append(i) # auto-creates list if key non-existent
43 for start in range(0, part_count, c):
44 add_p(start * part_wid) # start of lane
45 add_p(start * part_wid + lane_shapes[i]) # start of padding
46 # do not need the breakpoints at the very start or the very end
47 dpoints.pop(0, None)
48 dpoints.pop(width, None)
49 # second stage, add (map to) the elwidth==i expressions
50 points = {}
51 for p in dpoints.keys():
52 points[p] = map(lambda i: elwid == i, dpoints[p])
53 points[p] = reduce(operator.or_, points[p])
54 # third stage, create the binary values which *if* elwidth is set to i
55 # *would* result in the mask at that elwidth being set to this value
56 # these can easily be double-checked through Assertion
57 plist = list(points.keys())
58 plist.sort()
59 bitp = {}
60 for i in part_counts.keys():
61 bitp[i] = 0
62 for p, elwidths in dpoints.items():
63 if i in elwidths:
64 bitpos = plist.index(p)
65 bitp[i] |= 1<< bitpos
66 return (PartitionPoints(points), bitp, width, lane_shapes,
67 part_wid, part_count)
68
69 if __name__ == '__main__':
70 part_counts = {
71 0: 1,
72 1: 1,
73 2: 2,
74 3: 4,
75 }
76
77 for i in range(4):
78 pprint((i, layout(i, True, part_counts, 3)))
79
80 l = {0: 5, 1: 6, 2: 12, 3: 24}
81 for i in range(4):
82 pprint((i, layout(i, False, part_counts, l)))
83
84 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c30
85 elwid = Signal(2)
86 pp,bitp,b,c,d,e = layout(elwid, False, part_counts, l)
87 pprint ((pp,b,c,d,e))
88 for k, v in bitp.items():
89 print ("bitp elwidth=%d" % k, bin(v))
90
91 m = Module()
92 def process():
93 for i in range(4):
94 yield elwid.eq(i)
95 yield Settle()
96 ppt = []
97 for pval in list(pp.values()):
98 val = yield pval # get nmigen to evaluate pp
99 ppt.append(val)
100 pprint((i, (ppt,b,c,d,e)))
101 # check the results against bitp static-expected partition points
102 # https://bugs.libre-soc.org/show_bug.cgi?id=713#c47
103 # https://stackoverflow.com/a/27165694
104 ival = int(''.join(map(str, map(int, ppt))), 2)
105 assert ival == bitp[i]
106
107 sim = Simulator(m)
108 sim.add_process(process)
109 sim.run()