update comments
[ieee754fpu.git] / src / add / nmoperator.py
1 """ nmigen operator functions / utils
2
3 eq:
4 --
5
6 a strategically very important function that is identical in function
7 to nmigen's Signal.eq function, except it may take objects, or a list
8 of objects, or a tuple of objects, and where objects may also be
9 Records.
10 """
11
12 from nmigen import Signal, Cat, Const, Mux, Module, Value, Elaboratable
13 from nmigen.cli import verilog, rtlil
14 from nmigen.lib.fifo import SyncFIFO, SyncFIFOBuffered
15 from nmigen.hdl.ast import ArrayProxy
16 from nmigen.hdl.rec import Record, Layout
17
18 from abc import ABCMeta, abstractmethod
19 from collections.abc import Sequence, Iterable
20 from collections import OrderedDict
21 from queue import Queue
22 import inspect
23
24
25 class Visitor2:
26 """ a helper class for iterating twin-argument compound data structures.
27
28 Record is a special (unusual, recursive) case, where the input may be
29 specified as a dictionary (which may contain further dictionaries,
30 recursively), where the field names of the dictionary must match
31 the Record's field spec. Alternatively, an object with the same
32 member names as the Record may be assigned: it does not have to
33 *be* a Record.
34
35 ArrayProxy is also special-cased, it's a bit messy: whilst ArrayProxy
36 has an eq function, the object being assigned to it (e.g. a python
37 object) might not. despite the *input* having an eq function,
38 that doesn't help us, because it's the *ArrayProxy* that's being
39 assigned to. so.... we cheat. use the ports() function of the
40 python object, enumerate them, find out the list of Signals that way,
41 and assign them.
42 """
43 def iterator2(self, o, i):
44 if isinstance(o, dict):
45 yield from self.dict_iter2(o, i)
46
47 if not isinstance(o, Sequence):
48 o, i = [o], [i]
49 for (ao, ai) in zip(o, i):
50 #print ("visit", fn, ao, ai)
51 if isinstance(ao, Record):
52 yield from self.record_iter2(ao, ai)
53 elif isinstance(ao, ArrayProxy) and not isinstance(ai, Value):
54 yield from self.arrayproxy_iter2(ao, ai)
55 else:
56 yield (ao, ai)
57
58 def dict_iter2(self, o, i):
59 for (k, v) in o.items():
60 print ("d-iter", v, i[k])
61 yield (v, i[k])
62 return res
63
64 def _not_quite_working_with_all_unit_tests_record_iter2(self, ao, ai):
65 print ("record_iter2", ao, ai, type(ao), type(ai))
66 if isinstance(ai, Value):
67 if isinstance(ao, Sequence):
68 ao, ai = [ao], [ai]
69 for o, i in zip(ao, ai):
70 yield (o, i)
71 return
72 for idx, (field_name, field_shape, _) in enumerate(ao.layout):
73 if isinstance(field_shape, Layout):
74 val = ai.fields
75 else:
76 val = ai
77 if hasattr(val, field_name): # check for attribute
78 val = getattr(val, field_name)
79 else:
80 val = val[field_name] # dictionary-style specification
81 yield from self.iterator2(ao.fields[field_name], val)
82
83 def record_iter2(self, ao, ai):
84 for idx, (field_name, field_shape, _) in enumerate(ao.layout):
85 if isinstance(field_shape, Layout):
86 val = ai.fields
87 else:
88 val = ai
89 if hasattr(val, field_name): # check for attribute
90 val = getattr(val, field_name)
91 else:
92 val = val[field_name] # dictionary-style specification
93 yield from self.iterator2(ao.fields[field_name], val)
94
95 def arrayproxy_iter2(self, ao, ai):
96 for p in ai.ports():
97 op = getattr(ao, p.name)
98 print ("arrayproxy - p", p, p.name)
99 yield from self.iterator2(op, p)
100
101
102 class Visitor:
103 """ a helper class for iterating single-argument compound data structures.
104 similar to Visitor2.
105 """
106 def iterate(self, i):
107 """ iterate a compound structure recursively using yield
108 """
109 if not isinstance(i, Sequence):
110 i = [i]
111 for ai in i:
112 #print ("iterate", ai)
113 if isinstance(ai, Record):
114 #print ("record", list(ai.layout))
115 yield from self.record_iter(ai)
116 elif isinstance(ai, ArrayProxy) and not isinstance(ai, Value):
117 yield from self.array_iter(ai)
118 else:
119 yield ai
120
121 def record_iter(self, ai):
122 for idx, (field_name, field_shape, _) in enumerate(ai.layout):
123 if isinstance(field_shape, Layout):
124 val = ai.fields
125 else:
126 val = ai
127 if hasattr(val, field_name): # check for attribute
128 val = getattr(val, field_name)
129 else:
130 val = val[field_name] # dictionary-style specification
131 #print ("recidx", idx, field_name, field_shape, val)
132 yield from self.iterate(val)
133
134 def array_iter(self, ai):
135 for p in ai.ports():
136 yield from self.iterate(p)
137
138
139 def eq(o, i):
140 """ makes signals equal: a helper routine which identifies if it is being
141 passed a list (or tuple) of objects, or signals, or Records, and calls
142 the objects' eq function.
143 """
144 res = []
145 for (ao, ai) in Visitor2().iterator2(o, i):
146 rres = ao.eq(ai)
147 if not isinstance(rres, Sequence):
148 rres = [rres]
149 res += rres
150 return res
151
152
153 def shape(i):
154 #print ("shape", i)
155 r = 0
156 for part in list(i):
157 #print ("shape?", part)
158 s, _ = part.shape()
159 r += s
160 return r, False
161
162
163 def cat(i):
164 """ flattens a compound structure recursively using Cat
165 """
166 from nmigen.tools import flatten
167 #res = list(flatten(i)) # works (as of nmigen commit f22106e5) HOWEVER...
168 res = list(Visitor().iterate(i)) # needed because input may be a sequence
169 return Cat(*res)
170
171