- return i_valid
-
-
-class NextControl:
- """ contains the signals that go *to* the next stage (both in and out)
- * o_valid: output indicating to next stage that data is valid
- * i_ready: input from next stage indicating that it can accept data
- * o_data : an output - added by the user of this class
- """
- def __init__(self, stage_ctl=False):
- self.stage_ctl = stage_ctl
- self.o_valid = Signal(name="n_o_valid") # self out>> next
- self.i_ready = Signal(name="n_i_ready") # self <<in next
- self.o_data = None # XXX MUST BE ADDED BY USER
- #if self.stage_ctl:
- self.d_valid = Signal(reset=1) # INTERNAL (data valid)
-
- @property
- def i_ready_test(self):
- if self.stage_ctl:
- return self.i_ready & self.d_valid
- return self.i_ready
-
- def connect_to_next(self, nxt):
- """ helper function to connect to the next stage data/valid/ready.
- data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
- use this when connecting stage-to-stage
- """
- return [nxt.i_valid.eq(self.o_valid),
- self.i_ready.eq(nxt.o_ready),
- eq(nxt.i_data, self.o_data),
- ]
-
- def _connect_out(self, nxt, direct=False, fn=None):
- """ internal helper function to connect stage to an output source.
- do not use to connect stage-to-stage!
- """
- i_ready = nxt.i_ready if direct else nxt.i_ready_test
- o_data = fn(nxt.o_data) if fn is not None else nxt.o_data
- return [nxt.o_valid.eq(self.o_valid),
- self.i_ready.eq(i_ready),
- eq(o_data, self.o_data),
- ]
-
-
-class Visitor:
- """ a helper routine which identifies if it is being passed a list
- (or tuple) of objects, or signals, or Records, and calls
- a visitor function.
-
- the visiting fn is called when an object is identified.
-
- Record is a special (unusual, recursive) case, where the input may be
- specified as a dictionary (which may contain further dictionaries,
- recursively), where the field names of the dictionary must match
- the Record's field spec. Alternatively, an object with the same
- member names as the Record may be assigned: it does not have to
- *be* a Record.
-
- ArrayProxy is also special-cased, it's a bit messy: whilst ArrayProxy
- has an eq function, the object being assigned to it (e.g. a python
- object) might not. despite the *input* having an eq function,
- that doesn't help us, because it's the *ArrayProxy* that's being
- assigned to. so.... we cheat. use the ports() function of the
- python object, enumerate them, find out the list of Signals that way,
- and assign them.
- """
- def visit(self, o, i, act):
- if isinstance(o, dict):
- return self.dict_visit(o, i, act)
-
- res = act.prepare()
- if not isinstance(o, Sequence):
- o, i = [o], [i]
- for (ao, ai) in zip(o, i):
- #print ("visit", fn, ao, ai)
- if isinstance(ao, Record):
- rres = self.record_visit(ao, ai, act)
- elif isinstance(ao, ArrayProxy) and not isinstance(ai, Value):
- rres = self.arrayproxy_visit(ao, ai, act)
- else:
- rres = act.fn(ao, ai)
- res += rres
- return res
-
- def dict_visit(self, o, i, act):
- res = act.prepare()
- for (k, v) in o.items():
- print ("d-eq", v, i[k])
- res.append(act.fn(v, i[k]))
- return res
-
- def record_visit(self, ao, ai, act):
- res = act.prepare()
- for idx, (field_name, field_shape, _) in enumerate(ao.layout):
- if isinstance(field_shape, Layout):
- val = ai.fields
- else:
- val = ai
- if hasattr(val, field_name): # check for attribute
- val = getattr(val, field_name)
- else:
- val = val[field_name] # dictionary-style specification
- val = self.visit(ao.fields[field_name], val, act)
- if isinstance(val, Sequence):
- res += val
- else:
- res.append(val)
- return res
-
- def arrayproxy_visit(self, ao, ai, act):
- res = act.prepare()
- for p in ai.ports():
- op = getattr(ao, p.name)
- #print (op, p, p.name)
- res.append(fn(op, p))
- return res
-
-
-class Eq(Visitor):
- def __init__(self):
- self.res = []
- def prepare(self):
- return []
- def fn(self, o, i):
- rres = o.eq(i)
- if not isinstance(rres, Sequence):
- rres = [rres]
- return rres
- def __call__(self, o, i):
- return self.visit(o, i, self)
-
-
-def eq(o, i):
- """ makes signals equal: a helper routine which identifies if it is being
- passed a list (or tuple) of objects, or signals, or Records, and calls
- the objects' eq function.
- """
- return Eq()(o, i)
-
-
-def flatten(i):
- """ flattens a compound structure recursively using Cat
- """
- if not isinstance(i, Sequence):
- i = [i]
- res = []
- for ai in i:
- print ("flatten", ai)
- if isinstance(ai, Record):
- print ("record", list(ai.layout))
- rres = []
- for idx, (field_name, field_shape, _) in enumerate(ai.layout):
- if isinstance(field_shape, Layout):
- val = ai.fields
- else:
- val = ai
- if hasattr(val, field_name): # check for attribute
- val = getattr(val, field_name)
- else:
- val = val[field_name] # dictionary-style specification
- print ("recidx", idx, field_name, field_shape, val)
- val = flatten(val)
- print ("recidx flat", idx, val)
- if isinstance(val, Sequence):
- rres += val
- else:
- rres.append(val)
-
- elif isinstance(ai, ArrayProxy) and not isinstance(ai, Value):
- rres = []
- for p in ai.ports():
- op = getattr(ai, p.name)
- #print (op, p, p.name)
- rres.append(flatten(p))
- else:
- rres = ai
- if not isinstance(rres, Sequence):
- rres = [rres]
- res += rres
- print ("flatten res", res)
- return Cat(*res)
-
-
-
-class StageCls(metaclass=ABCMeta):
- """ Class-based "Stage" API. requires instantiation (after derivation)
-
- see "Stage API" above.. Note: python does *not* require derivation
- from this class. All that is required is that the pipelines *have*
- the functions listed in this class. Derivation from this class
- is therefore merely a "courtesy" to maintainers.
- """
- @abstractmethod
- def ispec(self): pass # REQUIRED
- @abstractmethod
- def ospec(self): pass # REQUIRED
- #@abstractmethod
- #def setup(self, m, i): pass # OPTIONAL
- @abstractmethod
- def process(self, i): pass # REQUIRED
-
-
-class Stage(metaclass=ABCMeta):
- """ Static "Stage" API. does not require instantiation (after derivation)
-
- see "Stage API" above. Note: python does *not* require derivation
- from this class. All that is required is that the pipelines *have*
- the functions listed in this class. Derivation from this class
- is therefore merely a "courtesy" to maintainers.
- """
- @staticmethod
- @abstractmethod
- def ispec(): pass
-
- @staticmethod
- @abstractmethod
- def ospec(): pass
-
- #@staticmethod
- #@abstractmethod
- #def setup(m, i): pass
-
- @staticmethod
- @abstractmethod
- def process(i): pass