1 """ nmigen operator functions / utils
3 This work is funded through NLnet under Grant 2019-02-012
11 a strategically very important function that is identical in function
12 to nmigen's Signal.eq function, except it may take objects, or a list
13 of objects, or a tuple of objects, and where objects may also be
17 from nmigen
import Signal
, Cat
, Value
18 from nmigen
.hdl
.ast
import ArrayProxy
19 from nmigen
.hdl
.rec
import Record
, Layout
21 from abc
import ABCMeta
, abstractmethod
22 from collections
.abc
import Sequence
, Iterable
27 """ a helper class for iterating twin-argument compound data structures.
29 Record is a special (unusual, recursive) case, where the input may be
30 specified as a dictionary (which may contain further dictionaries,
31 recursively), where the field names of the dictionary must match
32 the Record's field spec. Alternatively, an object with the same
33 member names as the Record may be assigned: it does not have to
36 ArrayProxy is also special-cased, it's a bit messy: whilst ArrayProxy
37 has an eq function, the object being assigned to it (e.g. a python
38 object) might not. despite the *input* having an eq function,
39 that doesn't help us, because it's the *ArrayProxy* that's being
40 assigned to. so.... we cheat. use the ports() function of the
41 python object, enumerate them, find out the list of Signals that way,
44 def iterator2(self
, o
, i
):
45 if isinstance(o
, dict):
46 yield from self
.dict_iter2(o
, i
)
48 if not isinstance(o
, Sequence
):
50 for (ao
, ai
) in zip(o
, i
):
51 # print ("visit", ao, ai)
52 # print (" isinstance Record(ao)", isinstance(ao, Record))
53 # print (" isinstance ArrayProxy(ao)",
54 # isinstance(ao, ArrayProxy))
55 # print (" isinstance Value(ai)",
56 # isinstance(ai, Value))
57 if isinstance(ao
, Record
):
58 yield from self
.record_iter2(ao
, ai
)
59 elif isinstance(ao
, ArrayProxy
) and not isinstance(ai
, Value
):
60 yield from self
.arrayproxy_iter2(ao
, ai
)
61 elif isinstance(ai
, ArrayProxy
) and not isinstance(ao
, Value
):
62 assert False, "whoops, input ArrayProxy not supported yet"
63 yield from self
.arrayproxy_iter3(ao
, ai
)
67 def dict_iter2(self
, o
, i
):
68 for (k
, v
) in o
.items():
69 # print ("d-iter", v, i[k])
73 def _not_quite_working_with_all_unit_tests_record_iter2(self
, ao
, ai
):
74 # print ("record_iter2", ao, ai, type(ao), type(ai))
75 if isinstance(ai
, Value
):
76 if isinstance(ao
, Sequence
):
78 for o
, i
in zip(ao
, ai
):
81 for idx
, (field_name
, field_shape
, _
) in enumerate(ao
.layout
):
82 if isinstance(field_shape
, Layout
):
86 if hasattr(val
, field_name
): # check for attribute
87 val
= getattr(val
, field_name
)
89 val
= val
[field_name
] # dictionary-style specification
90 yield from self
.iterator2(ao
.fields
[field_name
], val
)
92 def record_iter2(self
, ao
, ai
):
93 for idx
, (field_name
, field_shape
, _
) in enumerate(ao
.layout
):
94 if isinstance(field_shape
, Layout
):
98 if hasattr(val
, field_name
): # check for attribute
99 val
= getattr(val
, field_name
)
101 val
= val
[field_name
] # dictionary-style specification
102 yield from self
.iterator2(ao
.fields
[field_name
], val
)
104 def arrayproxy_iter2(self
, ao
, ai
):
105 # print ("arrayproxy_iter2", ai.ports(), ai, ao)
107 # print ("arrayproxy - p", p, p.name, ao)
108 op
= getattr(ao
, p
.name
)
109 yield from self
.iterator2(op
, p
)
111 def arrayproxy_iter3(self
, ao
, ai
):
112 # print ("arrayproxy_iter3", ao.ports(), ai, ao)
114 # print ("arrayproxy - p", p, p.name, ao)
115 op
= getattr(ao
, p
.name
)
116 yield from self
.iterator2(op
, p
)
120 """ a helper class for iterating single-argument compound data structures.
123 def iterate(self
, i
):
124 """ iterate a compound structure recursively using yield
126 if not isinstance(i
, Sequence
):
129 #print ("iterate", ai)
130 if isinstance(ai
, Record
):
131 #print ("record", list(ai.layout))
132 yield from self
.record_iter(ai
)
133 elif isinstance(ai
, ArrayProxy
) and not isinstance(ai
, Value
):
134 yield from self
.array_iter(ai
)
138 def record_iter(self
, ai
):
139 for idx
, (field_name
, field_shape
, _
) in enumerate(ai
.layout
):
140 if isinstance(field_shape
, Layout
):
144 if hasattr(val
, field_name
): # check for attribute
145 val
= getattr(val
, field_name
)
147 val
= val
[field_name
] # dictionary-style specification
148 #print ("recidx", idx, field_name, field_shape, val)
149 yield from self
.iterate(val
)
151 def array_iter(self
, ai
):
153 yield from self
.iterate(p
)
157 """ makes signals equal: a helper routine which identifies if it is being
158 passed a list (or tuple) of objects, or signals, or Records, and calls
159 the objects' eq function.
162 for (ao
, ai
) in Visitor2().iterator2(o
, i
):
164 if not isinstance(rres
, Sequence
):
174 #print ("shape?", part)
181 """ flattens a compound structure recursively using Cat
183 from nmigen
._utils
import flatten
184 #res = list(flatten(i)) # works (as of nmigen commit f22106e5) HOWEVER...
185 res
= list(Visitor().iterate(i
)) # needed because input may be a sequence