reorg add0 to use Mux instead of m.If/Else
[ieee754fpu.git] / src / nmutil / 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 nmutil.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 #print ("arrayproxy_iter2", ai.ports(), ai, ao)
97 for p in ai.ports():
98 #print ("arrayproxy - p", p, p.name, ao)
99 op = getattr(ao, p.name)
100 yield from self.iterator2(op, p)
101
102
103 class Visitor:
104 """ a helper class for iterating single-argument compound data structures.
105 similar to Visitor2.
106 """
107 def iterate(self, i):
108 """ iterate a compound structure recursively using yield
109 """
110 if not isinstance(i, Sequence):
111 i = [i]
112 for ai in i:
113 #print ("iterate", ai)
114 if isinstance(ai, Record):
115 #print ("record", list(ai.layout))
116 yield from self.record_iter(ai)
117 elif isinstance(ai, ArrayProxy) and not isinstance(ai, Value):
118 yield from self.array_iter(ai)
119 else:
120 yield ai
121
122 def record_iter(self, ai):
123 for idx, (field_name, field_shape, _) in enumerate(ai.layout):
124 if isinstance(field_shape, Layout):
125 val = ai.fields
126 else:
127 val = ai
128 if hasattr(val, field_name): # check for attribute
129 val = getattr(val, field_name)
130 else:
131 val = val[field_name] # dictionary-style specification
132 #print ("recidx", idx, field_name, field_shape, val)
133 yield from self.iterate(val)
134
135 def array_iter(self, ai):
136 for p in ai.ports():
137 yield from self.iterate(p)
138
139
140 def eq(o, i):
141 """ makes signals equal: a helper routine which identifies if it is being
142 passed a list (or tuple) of objects, or signals, or Records, and calls
143 the objects' eq function.
144 """
145 res = []
146 for (ao, ai) in Visitor2().iterator2(o, i):
147 rres = ao.eq(ai)
148 if not isinstance(rres, Sequence):
149 rres = [rres]
150 res += rres
151 return res
152
153
154 def shape(i):
155 #print ("shape", i)
156 r = 0
157 for part in list(i):
158 #print ("shape?", part)
159 s, _ = part.shape()
160 r += s
161 return r, False
162
163
164 def cat(i):
165 """ flattens a compound structure recursively using Cat
166 """
167 from nmigen.tools import flatten
168 #res = list(flatten(i)) # works (as of nmigen commit f22106e5) HOWEVER...
169 res = list(Visitor().iterate(i)) # needed because input may be a sequence
170 return Cat(*res)
171
172