X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fadd%2Fstageapi.py;h=9651bf79fe778f1b26aa510d8a0f5d6c0800778d;hb=6bff1a997f3846872cf489c24b5c01426c4dc97c;hp=9217c1fee22445a44b71dd648884c9ad99eaaea4;hpb=489eedc41010016a8e1a01c3baf75d333fc6cd1c;p=ieee754fpu.git diff --git a/src/add/stageapi.py b/src/add/stageapi.py index 9217c1fe..9651bf79 100644 --- a/src/add/stageapi.py +++ b/src/add/stageapi.py @@ -15,12 +15,16 @@ of the incoming and outgoing data, and they provide a means to PROCESS that data (from incoming format to outgoing format). - Stage Blocks really must be combinatorial blocks. It would be ok - to have input come in from sync'd sources (clock-driven) however by - doing so they would no longer be deterministic, and chaining such - blocks with such side-effects together could result in unexpected, - unpredictable, unreproduceable behaviour. + Stage Blocks really should be combinatorial blocks (Moore FSMs). + It would be ok to have input come in from sync'd sources + (clock-driven, Mealy FSMs) however by doing so they would no longer + be deterministic, and chaining such blocks with such side-effects + together could result in unexpected, unpredictable, unreproduceable + behaviour. + So generally to be avoided, then unless you know what you are doing. + https://en.wikipedia.org/wiki/Moore_machine + https://en.wikipedia.org/wiki/Mealy_machine the methods of a stage instance must be as follows: @@ -73,20 +77,20 @@ all the optional bits. """ -from nmigen import Signal, Cat, Const, Mux, Module, Value, Elaboratable -from nmigen.cli import verilog, rtlil -from nmigen.hdl.rec import Record - from abc import ABCMeta, abstractmethod -from collections.abc import Sequence, Iterable -from collections import OrderedDict import inspect -from iocontrol import PrevControl, NextControl import nmoperator def _spec(fn, name=None): + """ useful function that determines if "fn" has an argument "name". + if so, fn(name) is called otherwise fn() is called. + + means that ispec and ospec can be declared with *or without* + a name argument. normally it would be necessary to have + "ispec(name=None)" to achieve the same effect. + """ if name is None: return fn() varnames = dict(inspect.getmembers(fn.__code__))['co_varnames'] @@ -138,7 +142,62 @@ class Stage(metaclass=ABCMeta): #def process(i): pass -class StageChain(StageCls): +class StageHelper(Stage): + """ a convenience wrapper around something that is Stage-API-compliant. + (that "something" may be a static class, for example). + + StageHelper happens to also be compliant with the Stage API, + it differs from the stage that it wraps in that all the "optional" + functions are provided (hence the designation "convenience wrapper") + """ + def __init__(self, stage): + self.stage = stage + self._ispecfn = None + self._ospecfn = None + if stage is not None: + self.set_specs(self, self) + + def ospec(self, name): + assert self._ospecfn is not None + return _spec(self._ospecfn, name) + + def ispec(self, name): + assert self._ispecfn is not None + return _spec(self._ispecfn, name) + + def set_specs(self, p, n): + """ sets up the ispecfn and ospecfn for getting input and output data + """ + if hasattr(p, "stage"): + p = p.stage + if hasattr(n, "stage"): + n = n.stage + self._ispecfn = p.ispec + self._ospecfn = n.ospec + + def new_specs(self, name): + """ allocates new ispec and ospec pair + """ + return (_spec(self.ispec, "%s_i" % name), + _spec(self.ospec, "%s_o" % name)) + + def process(self, i): + if self.stage and hasattr(self.stage, "process"): + return self.stage.process(i) + return i + + def setup(self, m, i): + if self.stage is not None and hasattr(self.stage, "setup"): + self.stage.setup(m, i) + + def _postprocess(self, i): # XXX DISABLED + return i # RETURNS INPUT + if hasattr(self.stage, "postprocess"): + return self.stage.postprocess(i) + return i + + +class StageChain(StageHelper): """ pass in a list of stages, and they will automatically be chained together via their input and output specs into a combinatorial chain, to create one giant combinatorial block. @@ -179,19 +238,11 @@ class StageChain(StageCls): def __init__(self, chain, specallocate=False): assert len(chain) > 0, "stage chain must be non-zero length" self.chain = chain - self.specallocate = specallocate - - def ispec(self): - """ returns the ispec of the first of the chain - """ - return _spec(self.chain[0].ispec, "chainin") - - def ospec(self): - """ returns the ospec of the last of the chain - """ - return _spec(self.chain[-1].ospec, "chainout") + StageHelper.__init__(self, None) + self.setup = self._sa_setup if specallocate else self._na_setup + self.set_specs(self.chain[0], self.chain[-1]) - def _specallocate_setup(self, m, i): + def _sa_setup(self, m, i): for (idx, c) in enumerate(self.chain): if hasattr(c, "setup"): c.setup(m, i) # stage may have some module stuff @@ -203,56 +254,18 @@ class StageChain(StageCls): ifn = self.chain[idx+1].ispec # new input on next loop i = _spec(ifn, 'chainin%d' % (idx+1)) m.d.comb += nmoperator.eq(i, o) # assign to next input - return o # last loop is the output + self.o = o + return self.o # last loop is the output - def _noallocate_setup(self, m, i): + def _na_setup(self, m, i): for (idx, c) in enumerate(self.chain): if hasattr(c, "setup"): c.setup(m, i) # stage may have some module stuff i = o = c.process(i) # store input into "o" - return o # last loop is the output - - def setup(self, m, i): - if self.specallocate: - self.o = self._specallocate_setup(m, i) - else: - self.o = self._noallocate_setup(m, i) + self.o = o + return self.o # last loop is the output def process(self, i): return self.o # conform to Stage API: return last-loop output -class StageHelper(Stage): - """ a convenience wrapper around something that is Stage-API-compliant. - (that "something" may be a static class, for example). - - StageHelper happens to also be compliant with the Stage API, - except that all the "optional" functions are provided - (hence the designation "convenience wrapper") - """ - def __init__(self, stage): - self.stage = stage - - def ospec(self, name): - assert self.stage is not None - return _spec(self.stage.ospec, name) - - def ispec(self, name): - assert self.stage is not None - return _spec(self.stage.ispec, name) - - def process(self, i): - if self.stage and hasattr(self.stage, "process"): - return self.stage.process(i) - return i - - def setup(self, m, i): - if self.stage is not None and hasattr(self.stage, "setup"): - self.stage.setup(m, i) - - def _postprocess(self, i): # XXX DISABLED - return i # RETURNS INPUT - if hasattr(self.stage, "postprocess"): - return self.stage.postprocess(i) - return i -