where data will flow on *every* clock when the conditions are right.
input acceptance conditions are when:
- * incoming previous-stage strobe (i_p_stb) is HIGH
- * outgoing previous-stage busy (o_p_busy) is LOW
+ * incoming previous-stage strobe (i.p_valid) is HIGH
+ * outgoing previous-stage ready (o.p_ready) is LOW
output transmission conditions are when:
- * outgoing next-stage strobe (o_n_stb) is HIGH
- * outgoing next-stage busy (i_n_busy) is LOW
+ * outgoing next-stage strobe (o.n_valid) is HIGH
+ * outgoing next-stage ready (i.n_ready) is LOW
the tricky bit is when the input has valid data and the output is not
ready to accept it. if it wasn't for the clock synchronisation, it
we now effectively have *two* possible pieces of data to "choose" from:
the buffered data, and the incoming data. the decision as to which
to process and output is based on whether we are in "stall" or not.
- i.e. when the next stage is no longer busy, the output comes from
+ i.e. when the next stage is no longer ready, the output comes from
the buffer if a stall had previously occurred, otherwise it comes
direct from processing the input.
def __init__(self):
""" i_data can be a DIFFERENT type from everything else
- o_data, r_data and result must be of the same type
+ o_data, r_data and result are best of the same type.
+ however this is not strictly necessary. an intermediate
+ transformation process could hypothetically be applied, however
+ it is result and r_data that definitively need to be of the same
+ (intermediary) type, as it is both result and r_data that
+ are transferred into o_data:
+
+ i_data -> process() -> result --> o_data
+ | ^
+ | |
+ +-> r_data -+
"""
self.i_data = Signal(16)
self.r_data = Signal(16)
return self.result.eq(self.i_data + 1)
def update_buffer(self):
- """ copies the result into the intermediate register r_data
+ """ copies the result into the intermediate register r_data,
+ which will need to be outputted on a subsequent cycle
+ prior to allowing "normal" operation.
"""
return self.r_data.eq(self.result)
def ports(self):
return [self.i_data, self.o_data]
+class IOAckIn:
+
+ def __init__(self):
+ self.p_valid = Signal() # >>in - comes in from PREVIOUS stage
+ self.n_ready = Signal() # in<< - comes in from the NEXT stage
+
+
+class IOAckOut:
+
+ def __init__(self):
+ self.n_valid = Signal() # out>> - goes out to the NEXT stage
+ self.p_ready = Signal() # <<out - goes out to the PREVIOUS stage
+
class BufferedPipeline:
""" buffered pipeline stage
- stage-1 i_p_stb >>in stage o_n_stb out>> stage+1
- stage-1 o_p_busy <<out stage i_n_busy <<in stage+1
- stage-1 i_data >>in stage o_data out>> stage+1
+ stage-1 i.p_valid >>in stage o.n_valid out>> stage+1
+ stage-1 o.p_ready <<out stage i.n_ready <<in stage+1
+ stage-1 i_data >>in stage o_data out>> stage+1
| |
+-------> process
| |
+-- r_data ---+
"""
def __init__(self):
- # input: strobe comes in from previous stage, busy comes in from next
- #self.i_p_rst = Signal() # >>in - comes in from PREVIOUS stage
- self.i_p_stb = Signal() # >>in - comes in from PREVIOUS stage
- self.i_n_busy = Signal() # in<< - comes in from the NEXT stage
+ # input: strobe comes in from previous stage, ready comes in from next
+ self.i = IOAckIn()
+ #self.i.p_valid = Signal() # >>in - comes in from PREVIOUS stage
+ #self.i.n_ready = Signal() # in<< - comes in from the NEXT stage
- # output: strobe goes out to next stage, busy comes in from previous
- self.o_n_stb = Signal() # out>> - goes out to the NEXT stage
- self.o_p_busy = Signal() # <<out - goes out to the PREVIOUS stage
+ # output: strobe goes out to next stage, ready comes in from previous
+ self.o = IOAckOut()
+ #self.o.n_valid = Signal() # out>> - goes out to the NEXT stage
+ #self.o.p_ready = Signal() # <<out - goes out to the PREVIOUS stage
def elaborate(self, platform):
m = Module()
# establish some combinatorial temporaries
- o_p_busyn = Signal(reset_less=True)
- o_n_stbn = Signal(reset_less=True)
- i_n_busyn = Signal(reset_less=True)
- i_p_stb_o_p_busyn = Signal(reset_less=True)
- m.d.comb += [i_n_busyn.eq(~self.i_n_busy),
- o_n_stbn.eq(~self.o_n_stb),
- o_p_busyn.eq(~self.o_p_busy),
- i_p_stb_o_p_busyn.eq(self.i_p_stb & o_p_busyn),
+ o_n_validn = Signal(reset_less=True)
+ i_p_valid_o_p_ready = Signal(reset_less=True)
+ m.d.comb += [o_n_validn.eq(~self.o.n_valid),
+ i_p_valid_o_p_ready.eq(self.i.p_valid & self.o.p_ready),
]
# store result of processing in combinatorial temporary
- with m.If(self.i_p_stb): # input is valid: process it
+ with m.If(self.i.p_valid): # input is valid: process it
m.d.comb += self.stage.process()
# if not in stall condition, update the temporary register
- with m.If(o_p_busyn): # not stalled
+ with m.If(self.o.p_ready): # not stalled
m.d.sync += self.stage.update_buffer()
- #with m.If(self.i_p_rst): # reset
- # m.d.sync += self.o_n_stb.eq(0)
- # m.d.sync += self.o_p_busy.eq(0)
- with m.If(i_n_busyn): # next stage is not busy
- with m.If(o_p_busyn): # not stalled
+ #with m.If(self.i.p_rst): # reset
+ # m.d.sync += self.o.n_valid.eq(0)
+ # m.d.sync += self.o.p_ready.eq(0)
+ with m.If(self.i.n_ready): # next stage is ready
+ with m.If(self.o.p_ready): # not stalled
# nothing in buffer: send (processed) input direct to output
- m.d.sync += [self.o_n_stb.eq(self.i_p_stb),
+ m.d.sync += [self.o.n_valid.eq(self.i.p_valid),
self.stage.update_output(),
]
- with m.Else(): # o_p_busy is true, and something is in our buffer.
+ with m.Else(): # o.p_ready is false, and something is in buffer.
# Flush the [already processed] buffer to the output port.
- m.d.sync += [self.o_n_stb.eq(1),
+ m.d.sync += [self.o.n_valid.eq(1),
self.stage.flush_buffer(),
# clear stall condition, declare register empty.
- self.o_p_busy.eq(0),
+ self.o.p_ready.eq(1),
]
- # ignore input, since o_p_busy is also true.
+ # ignore input, since o.p_ready is also false.
- # (i_n_busy) is true here: next stage is busy
- with m.Elif(o_n_stbn): # next stage being told "not busy"
- m.d.sync += [self.o_n_stb.eq(self.i_p_stb),
- self.o_p_busy.eq(0), # Keep the buffer empty
+ # (i.n_ready) is false here: next stage is ready
+ with m.Elif(o_n_validn): # next stage being told "ready"
+ m.d.sync += [self.o.n_valid.eq(self.i.p_valid),
+ self.o.p_ready.eq(1), # Keep the buffer empty
# set the output data (from comb result)
self.stage.update_output(),
]
- # (i_n_busy) and (o_n_stb) both true:
- with m.Elif(i_p_stb_o_p_busyn):
- # If next stage *is* busy, and not stalled yet, accept input
- m.d.sync += self.o_p_busy.eq(self.i_p_stb & self.o_n_stb)
+ # (i.n_ready) false and (o.n_valid) true:
+ with m.Elif(i_p_valid_o_p_ready):
+ # If next stage *is* ready, and not stalled yet, accept input
+ m.d.sync += self.o.p_ready.eq(~(self.i.p_valid & self.o.n_valid))
return m
def ports(self):
- return [self.i_p_stb, self.i_n_busy,
- self.o_n_stb, self.o_p_busy,
+ return [self.i.p_valid, self.i.n_ready,
+ self.o.n_valid, self.o.p_ready,
]
-class BufPipe(BufferedPipeline):
+class ExampleBufPipe(BufferedPipeline):
def __init__(self):
BufferedPipeline.__init__(self)