- @property
- def o_ready(self):
- """ public-facing API: indicates (externally) that stage is ready
- """
- if self.stage_ctl:
- return self.s_o_ready # set dynamically by stage
- return self._o_ready # return this when not under dynamic control
-
- def _connect_in(self, prev):
- """ internal helper function to connect stage to an input source.
- do not use to connect stage-to-stage!
- """
- return [self.i_valid.eq(prev.i_valid_test),
- prev.o_ready.eq(self.o_ready),
- eq(self.i_data, prev.i_data),
- ]
-
- @property
- def i_valid_test(self):
- vlen = len(self.i_valid)
- if vlen > 1:
- # multi-bit case: valid only when i_valid is all 1s
- all1s = Const(-1, (len(self.i_valid), False))
- i_valid = (self.i_valid == all1s)
- else:
- # single-bit i_valid case
- i_valid = self.i_valid
-
- # when stage indicates not ready, incoming data
- # must "appear" to be not ready too
- if self.stage_ctl:
- i_valid = i_valid & self.s_o_ready
-
- 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):
- """ internal helper function to connect stage to an output source.
- do not use to connect stage-to-stage!
- """
- return [nxt.o_valid.eq(self.o_valid),
- self.i_ready.eq(nxt.i_ready_test),
- eq(nxt.o_data, self.o_data),
- ]
-
-
-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.
-
- complex objects (classes) can be used: they must follow the
- convention of having an eq member function, which takes the
- responsibility of further calling eq and returning a list of
- eq assignments
-
- 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.
- """
- res = []
- if isinstance(o, dict):
- for (k, v) in o.items():
- print ("d-eq", v, i[k])
- res.append(v.eq(i[k]))
- return res
-
- if not isinstance(o, Sequence):
- o, i = [o], [i]
- for (ao, ai) in zip(o, i):
- #print ("eq", ao, ai)
- if isinstance(ao, Record):
- rres = []
- 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
- rres += eq(ao.fields[field_name], val)
- elif isinstance(ao, ArrayProxy) and not isinstance(ai, Value):
- rres = []
- for p in ai.ports():
- op = getattr(ao, p.name)
- #print (op, p, p.name)
- rres.append(op.eq(p))
- else:
- rres = ao.eq(ai)
- if not isinstance(rres, Sequence):
- rres = [rres]
- res += rres
- return 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