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", fn, ao, ai)
52 if isinstance(ao
, Record
):
53 yield from self
.record_iter2(ao
, ai
)
54 elif isinstance(ao
, ArrayProxy
) and not isinstance(ai
, Value
):
55 yield from self
.arrayproxy_iter2(ao
, ai
)
59 def dict_iter2(self
, o
, i
):
60 for (k
, v
) in o
.items():
61 print ("d-iter", v
, i
[k
])
65 def _not_quite_working_with_all_unit_tests_record_iter2(self
, ao
, ai
):
66 print ("record_iter2", ao
, ai
, type(ao
), type(ai
))
67 if isinstance(ai
, Value
):
68 if isinstance(ao
, Sequence
):
70 for o
, i
in zip(ao
, ai
):
73 for idx
, (field_name
, field_shape
, _
) in enumerate(ao
.layout
):
74 if isinstance(field_shape
, Layout
):
78 if hasattr(val
, field_name
): # check for attribute
79 val
= getattr(val
, field_name
)
81 val
= val
[field_name
] # dictionary-style specification
82 yield from self
.iterator2(ao
.fields
[field_name
], val
)
84 def record_iter2(self
, ao
, ai
):
85 for idx
, (field_name
, field_shape
, _
) in enumerate(ao
.layout
):
86 if isinstance(field_shape
, Layout
):
90 if hasattr(val
, field_name
): # check for attribute
91 val
= getattr(val
, field_name
)
93 val
= val
[field_name
] # dictionary-style specification
94 yield from self
.iterator2(ao
.fields
[field_name
], val
)
96 def arrayproxy_iter2(self
, ao
, ai
):
97 #print ("arrayproxy_iter2", ai.ports(), ai, ao)
99 #print ("arrayproxy - p", p, p.name, ao)
100 op
= getattr(ao
, p
.name
)
101 yield from self
.iterator2(op
, p
)
105 """ a helper class for iterating single-argument compound data structures.
108 def iterate(self
, i
):
109 """ iterate a compound structure recursively using yield
111 if not isinstance(i
, Sequence
):
114 #print ("iterate", ai)
115 if isinstance(ai
, Record
):
116 #print ("record", list(ai.layout))
117 yield from self
.record_iter(ai
)
118 elif isinstance(ai
, ArrayProxy
) and not isinstance(ai
, Value
):
119 yield from self
.array_iter(ai
)
123 def record_iter(self
, ai
):
124 for idx
, (field_name
, field_shape
, _
) in enumerate(ai
.layout
):
125 if isinstance(field_shape
, Layout
):
129 if hasattr(val
, field_name
): # check for attribute
130 val
= getattr(val
, field_name
)
132 val
= val
[field_name
] # dictionary-style specification
133 #print ("recidx", idx, field_name, field_shape, val)
134 yield from self
.iterate(val
)
136 def array_iter(self
, ai
):
138 yield from self
.iterate(p
)
142 """ makes signals equal: a helper routine which identifies if it is being
143 passed a list (or tuple) of objects, or signals, or Records, and calls
144 the objects' eq function.
147 for (ao
, ai
) in Visitor2().iterator2(o
, i
):
149 if not isinstance(rres
, Sequence
):
159 #print ("shape?", part)
166 """ flattens a compound structure recursively using Cat
168 from nmigen
._utils
import flatten
169 #res = list(flatten(i)) # works (as of nmigen commit f22106e5) HOWEVER...
170 res
= list(Visitor().iterate(i
)) # needed because input may be a sequence