Merge branch 'master' of ssh://libre-riscv.org:922/ieee754fpu
[ieee754fpu.git] / src / add / singlepipe.py
index 4b61c4c59bf4297f32a17433280404557cb70cf0..50eba9dc67167aae04355ebc78237c7275ef3706 100644 (file)
@@ -168,10 +168,13 @@ class PrevControl:
         * i_data : an input - added by the user of this class
     """
 
-    def __init__(self, i_width=1):
+    def __init__(self, i_width=1, stage_ctl=False):
+        self.stage_ctl = stage_ctl
         self.i_valid = Signal(i_width, name="p_i_valid") # prev   >>in  self
         self.o_ready = Signal(name="p_o_ready") # prev   <<out self
         self.i_data = None # XXX MUST BE ADDED BY USER
+        if stage_ctl:
+            self.s_o_ready = Signal(name="p_s_o_rdy") # prev   <<out self
 
     def _connect_in(self, prev):
         """ internal helper function to connect stage to an input source.
@@ -197,10 +200,13 @@ class NextControl:
         * 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):
+    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 stage_ctl:
+            self.s_o_valid = Signal(name="n_s_o_vld") # self out>>  next
 
     def connect_to_next(self, nxt):
         """ helper function to connect to the next stage data/valid/ready.
@@ -247,9 +253,15 @@ def eq(o, i):
         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]
-    res = []
     for (ao, ai) in zip(o, i):
         #print ("eq", ao, ai)
         if isinstance(ao, Record):
@@ -351,8 +363,9 @@ class StageChain(StageCls):
         * output of second goes into input into third (etc. etc.)
         * the output of this class will be the output of the last stage
     """
-    def __init__(self, chain):
+    def __init__(self, chain, specallocate=False):
         self.chain = chain
+        self.specallocate = specallocate
 
     def ispec(self):
         return self.chain[0].ispec()
@@ -364,12 +377,18 @@ class StageChain(StageCls):
         for (idx, c) in enumerate(self.chain):
             if hasattr(c, "setup"):
                 c.setup(m, i)               # stage may have some module stuff
-            o = self.chain[idx].ospec()     # only the last assignment survives
-            m.d.comb += eq(o, c.process(i)) # process input into "o"
+            if self.specallocate:
+                o = self.chain[idx].ospec()     # last assignment survives
+                m.d.comb += eq(o, c.process(i)) # process input into "o"
+            else:
+                o = c.process(i) # store input into "o"
             if idx != len(self.chain)-1:
-                ni = self.chain[idx+1].ispec() # becomes new input on next loop
-                m.d.comb += eq(ni, o)          # assign output to next input
-                i = ni
+                if self.specallocate:
+                    ni = self.chain[idx+1].ispec() # new input on next loop
+                    m.d.comb += eq(ni, o)          # assign to next input
+                    i = ni
+                else:
+                    i = o
         self.o = o                             # last loop is the output
 
     def process(self, i):
@@ -379,20 +398,19 @@ class StageChain(StageCls):
 class ControlBase:
     """ Common functions for Pipeline API
     """
-    def __init__(self, in_multi=None):
+    def __init__(self, in_multi=None, stage_ctl=False):
         """ Base class containing ready/valid/data to previous and next stages
 
             * p: contains ready/valid to the previous stage
             * n: contains ready/valid to the next stage
 
-            User must also:
+            Except when calling Controlbase.connect(), user must also:
             * add i_data member to PrevControl (p) and
             * add o_data member to NextControl (n)
         """
-
         # set up input and output IO ACK (prev/next ready/valid)
-        self.p = PrevControl(in_multi)
-        self.n = NextControl()
+        self.p = PrevControl(in_multi, stage_ctl)
+        self.n = NextControl(stage_ctl)
 
     def connect_to_next(self, nxt):
         """ helper function to connect to the next stage data/valid/ready.
@@ -411,7 +429,7 @@ class ControlBase:
         """
         return self.n._connect_out(nxt.n)
 
-    def connect(self, m, pipechain):
+    def connect(self, pipechain):
         """ connects a chain (list) of Pipeline instances together and
             links them to this ControlBase instance:
 
@@ -459,8 +477,7 @@ class ControlBase:
         self.n.o_data = end.stage.ospec()
         eqs += end._connect_out(self)
 
-        # activate the assignments
-        m.d.comb += eqs
+        return eqs
 
     def set_input(self, i):
         """ helper function to set the input data
@@ -468,10 +485,18 @@ class ControlBase:
         return eq(self.p.i_data, i)
 
     def ports(self):
-        return [self.p.i_valid, self.n.i_ready,
+        res = [self.p.i_valid, self.n.i_ready,
                 self.n.o_valid, self.p.o_ready,
-                self.p.i_data, self.n.o_data   # XXX need flattening!
                ]
+        if hasattr(self.p.i_data, "ports"):
+            res += self.p.i_data.ports()
+        else:
+            res += self.p.i_data
+        if hasattr(self.n.o_data, "ports"):
+            res += self.n.o_data.ports()
+        else:
+            res += self.n.o_data
+        return res
 
 
 class BufferedPipeline(ControlBase):
@@ -513,56 +538,57 @@ class BufferedPipeline(ControlBase):
         self.n.o_data = stage.ospec()
 
     def elaborate(self, platform):
-        m = Module()
+
+        self.m = Module()
 
         result = self.stage.ospec()
         r_data = self.stage.ospec()
         if hasattr(self.stage, "setup"):
-            self.stage.setup(m, self.p.i_data)
+            self.stage.setup(self.m, self.p.i_data)
 
         # establish some combinatorial temporaries
         o_n_validn = Signal(reset_less=True)
         i_p_valid_o_p_ready = Signal(reset_less=True)
         p_i_valid = Signal(reset_less=True)
-        m.d.comb += [p_i_valid.eq(self.p.i_valid_logic()),
+        self.m.d.comb += [p_i_valid.eq(self.p.i_valid_logic()),
                      o_n_validn.eq(~self.n.o_valid),
                      i_p_valid_o_p_ready.eq(p_i_valid & self.p.o_ready),
         ]
 
         # store result of processing in combinatorial temporary
-        m.d.comb += eq(result, self.stage.process(self.p.i_data))
+        self.m.d.comb += eq(result, self.stage.process(self.p.i_data))
 
         # if not in stall condition, update the temporary register
-        with m.If(self.p.o_ready): # not stalled
-            m.d.sync += eq(r_data, result) # update buffer
+        with self.m.If(self.p.o_ready): # not stalled
+            self.m.d.sync += eq(r_data, result) # update buffer
 
-        with m.If(self.n.i_ready): # next stage is ready
-            with m.If(self.p.o_ready): # not stalled
+        with self.m.If(self.n.i_ready): # next stage is ready
+            with self.m.If(self.p.o_ready): # not stalled
                 # nothing in buffer: send (processed) input direct to output
-                m.d.sync += [self.n.o_valid.eq(p_i_valid),
-                             eq(self.n.o_data, result), # update output
+                self.m.d.sync += [self.n.o_valid.eq(p_i_valid),
+                                  eq(self.n.o_data, result), # update output
                             ]
-            with m.Else(): # p.o_ready is false, and something is in buffer.
+            with self.m.Else(): # p.o_ready is false, and something in buffer
                 # Flush the [already processed] buffer to the output port.
-                m.d.sync += [self.n.o_valid.eq(1),      # declare reg empty
-                             eq(self.n.o_data, r_data), # flush buffer
-                             self.p.o_ready.eq(1),      # clear stall condition
+                self.m.d.sync += [self.n.o_valid.eq(1),      # declare reg empty
+                                  eq(self.n.o_data, r_data), # flush buffer
+                                  self.p.o_ready.eq(1),      # clear stall
                             ]
                 # ignore input, since p.o_ready is also false.
 
         # (n.i_ready) is false here: next stage is ready
-        with m.Elif(o_n_validn): # next stage being told "ready"
-            m.d.sync += [self.n.o_valid.eq(p_i_valid),
-                         self.p.o_ready.eq(1), # Keep the buffer empty
-                         eq(self.n.o_data, result), # set output data
+        with self.m.Elif(o_n_validn): # next stage being told "ready"
+            self.m.d.sync += [self.n.o_valid.eq(p_i_valid),
+                              self.p.o_ready.eq(1), # Keep the buffer empty
+                              eq(self.n.o_data, result), # set output data
                         ]
 
         # (n.i_ready) false and (n.o_valid) true:
-        with m.Elif(i_p_valid_o_p_ready):
+        with self.m.Elif(i_p_valid_o_p_ready):
             # If next stage *is* ready, and not stalled yet, accept input
-            m.d.sync += self.p.o_ready.eq(~(p_i_valid & self.n.o_valid))
+            self.m.d.sync += self.p.o_ready.eq(~(p_i_valid & self.n.o_valid))
 
-        return m
+        return self.m
 
 
 class UnbufferedPipeline(ControlBase):
@@ -611,30 +637,34 @@ class UnbufferedPipeline(ControlBase):
         self.n.o_data = stage.ospec() # output type
 
     def elaborate(self, platform):
-        m = Module()
+        self.m = Module()
 
         data_valid = Signal() # is data valid or not
         r_data = self.stage.ispec() # input type
         if hasattr(self.stage, "setup"):
-            self.stage.setup(m, r_data)
+            self.stage.setup(self.m, r_data)
 
+        # some temporarie
         p_i_valid = Signal(reset_less=True)
-        m.d.comb += p_i_valid.eq(self.p.i_valid_logic())
-        m.d.comb += self.n.o_valid.eq(data_valid)
-        m.d.comb += self.p.o_ready.eq(~data_valid | self.n.i_ready)
-        m.d.sync += data_valid.eq(p_i_valid | \
+        pv = Signal(reset_less=True)
+        self.m.d.comb += p_i_valid.eq(self.p.i_valid_logic())
+        self.m.d.comb += pv.eq(self.p.i_valid & self.p.o_ready)
+
+        self.m.d.comb += self.n.o_valid.eq(data_valid)
+        self.m.d.comb += self.p.o_ready.eq(~data_valid | self.n.i_ready)
+        self.m.d.sync += data_valid.eq(p_i_valid | \
                                         (~self.n.i_ready & data_valid))
-        with m.If(self.p.i_valid & self.p.o_ready):
-            m.d.sync += eq(r_data, self.p.i_data)
-        m.d.comb += eq(self.n.o_data, self.stage.process(r_data))
-        return m
+        with self.m.If(pv):
+            self.m.d.sync += eq(r_data, self.p.i_data)
+        self.m.d.comb += eq(self.n.o_data, self.stage.process(r_data))
+        return self.m
 
 
 class PassThroughStage(StageCls):
     """ a pass-through stage which has its input data spec equal to its output,
         and "passes through" its data from input to output.
     """
-    def __init__(self, iospec):
+    def __init__(self, iospecfn):
         self.iospecfn = iospecfn
     def ispec(self): return self.iospecfn()
     def ospec(self): return self.iospecfn()