input acceptance conditions are when:
* incoming previous-stage strobe (i.p_valid) is HIGH
- * outgoing previous-stage busy (o.p_busy) is LOW
+ * outgoing previous-stage ready (o.p_ready) is LOW
output transmission conditions are when:
* outgoing next-stage strobe (o.n_valid) is HIGH
- * outgoing next-stage busy (i.n_busy) is LOW
+ * 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):
self.p_valid = Signal() # >>in - comes in from PREVIOUS stage
- self.n_busy = Signal() # in<< - comes in from the NEXT 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_busy = Signal() # <<out - goes out to the PREVIOUS stage
+ self.p_ready = Signal() # <<out - goes out to the PREVIOUS stage
class BufferedPipeline:
""" buffered pipeline stage
stage-1 i.p_valid >>in stage o.n_valid out>> stage+1
- stage-1 o.p_busy <<out stage i.n_busy <<in 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
+ # 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_busy = Signal() # in<< - comes in from the NEXT 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
+ # 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_busy = Signal() # <<out - goes out to the PREVIOUS 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_p_readyn = Signal(reset_less=True)
o_n_validn = Signal(reset_less=True)
- i_n_busyn = Signal(reset_less=True)
- i_p_valid_o_p_busyn = Signal(reset_less=True)
- m.d.comb += [i_n_busyn.eq(~self.i.n_busy),
+ i_n_readyn = Signal(reset_less=True)
+ i_p_valid_o_p_ready = Signal(reset_less=True)
+ m.d.comb += [i_n_readyn.eq(~self.i.n_ready),
o_n_validn.eq(~self.o.n_valid),
- o_p_busyn.eq(~self.o.p_busy),
- i_p_valid_o_p_busyn.eq(self.i.p_valid & o_p_busyn),
+ o_p_readyn.eq(~self.o.p_ready),
+ 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_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_valid.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
+ # 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_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_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_validn): # next stage being told "not busy"
+ # (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_busy.eq(0), # Keep the buffer empty
+ 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_valid) both true:
- with m.Elif(i_p_valid_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_valid & self.o.n_valid)
+ # (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_valid, self.i.n_busy,
- self.o.n_valid, self.o.p_busy,
+ return [self.i.p_valid, self.i.n_ready,
+ self.o.n_valid, self.o.p_ready,
]
def testbench(dut):
#yield dut.i_p_rst.eq(1)
- yield dut.i.n_busy.eq(1)
- yield dut.o.p_busy.eq(1)
+ yield dut.i.n_ready.eq(0)
+ yield dut.o.p_ready.eq(0)
yield
yield
#yield dut.i_p_rst.eq(0)
- yield dut.i.n_busy.eq(0)
+ yield dut.i.n_ready.eq(1)
yield dut.stage.i_data.eq(5)
yield dut.i.p_valid.eq(1)
yield
yield dut.stage.i_data.eq(2)
yield
- yield dut.i.n_busy.eq(1) # begin going into "stall" (next stage says busy)
+ yield dut.i.n_ready.eq(0) # begin going into "stall" (next stage says ready)
yield dut.stage.i_data.eq(9)
yield
yield dut.i.p_valid.eq(0)
yield dut.stage.i_data.eq(12)
yield
yield dut.stage.i_data.eq(32)
- yield dut.i.n_busy.eq(0)
+ yield dut.i.n_ready.eq(1)
yield
yield from check_o_n_valid(dut, 1) # buffer still needs to output
yield
def testbench2(dut):
#yield dut.i.p_rst.eq(1)
- yield dut.i_n_busy.eq(1)
- #yield dut.o.p_busy.eq(1)
+ yield dut.i_n_ready.eq(0)
+ #yield dut.o.p_ready.eq(0)
yield
yield
#yield dut.i.p_rst.eq(0)
- yield dut.i_n_busy.eq(0)
+ yield dut.i_n_ready.eq(1)
yield dut.i_data.eq(5)
yield dut.i_p_valid.eq(1)
yield
yield dut.i_data.eq(2)
yield
yield from check_o_n_valid2(dut, 1) # ok *now* i_p_valid effect is felt
- yield dut.i_n_busy.eq(1) # begin going into "stall" (next stage says busy)
+ yield dut.i_n_ready.eq(0) # begin going into "stall" (next stage says ready)
yield dut.i_data.eq(9)
yield
yield dut.i_p_valid.eq(0)
yield dut.i_data.eq(12)
yield
yield dut.i_data.eq(32)
- yield dut.i_n_busy.eq(0)
+ yield dut.i_n_ready.eq(1)
yield
yield from check_o_n_valid2(dut, 1) # buffer still needs to output
yield
send = True
else:
send = randint(0, send_range) != 0
- o_p_busy = yield self.dut.o.p_busy
- if o_p_busy:
+ o_p_ready = yield self.dut.o.p_ready
+ if not o_p_ready:
yield
continue
if send and self.i != len(self.data):
while self.o != len(self.data):
stall_range = randint(0, 3)
for j in range(randint(1,10)):
- stall = randint(0, stall_range) == 0
- yield self.dut.i.n_busy.eq(stall)
+ stall = randint(0, stall_range) != 0
+ yield self.dut.i.n_ready.eq(stall)
yield
o_n_valid = yield self.dut.o.n_valid
- i_n_busy = yield self.dut.i.n_busy
- if not o_n_valid or i_n_busy:
+ i_n_ready = yield self.dut.i.n_ready
+ if not o_n_valid or not i_n_ready:
continue
o_data = yield self.dut.stage.o_data
assert o_data == self.data[self.o] + 1, \
i = 0
o = 0
while True:
- stall = randint(0, 3) == 0
+ stall = randint(0, 3) != 0
send = randint(0, 5) != 0
- yield dut.i_n_busy.eq(stall)
- o_p_busy = yield dut.o_p_busy
- if not o_p_busy:
+ yield dut.i_n_ready.eq(stall)
+ o_p_ready = yield dut.o_p_ready
+ if o_p_ready:
if send and i != len(data):
yield dut.i_p_valid.eq(1)
yield dut.i_data.eq(data[i])
yield dut.i_p_valid.eq(0)
yield
o_n_valid = yield dut.o_n_valid
- i_n_busy = yield dut.i_n_busy
- if o_n_valid and not i_n_busy:
+ i_n_ready = yield dut.i_n_ready
+ if o_n_valid and i_n_ready:
o_data = yield dut.o_data
assert o_data == data[o] + 2, "%d-%d data %x not match %x\n" \
% (i, o, o_data, data[o])
connect these: ------|---------------|
v v
i_p_valid >>in pipe1 o_n_valid out>> i_p_valid >>in pipe2
- o_p_busy <<out pipe1 i_n_busy <<in o_p_busy <<out pipe2
+ o_p_ready <<out pipe1 i_n_ready <<in o_p_ready <<out pipe2
stage.i_data >>in pipe1 o_data out>> stage.i_data >>in pipe2
"""
def __init__(self):
# input
self.i_p_valid = Signal() # >>in - comes in from PREVIOUS stage
- self.i_n_busy = Signal() # in<< - comes in from the NEXT stage
+ self.i_n_ready = Signal() # in<< - comes in from the NEXT stage
self.i_data = Signal(32) # >>in - comes in from the PREVIOUS stage
# output
self.o_n_valid = Signal() # out>> - goes out to the NEXT stage
- self.o_p_busy = Signal() # <<out - goes out to the PREVIOUS stage
+ self.o_p_ready = Signal() # <<out - goes out to the PREVIOUS stage
self.o_data = Signal(32) # out>> - goes out to the NEXT stage
def elaborate(self, platform):
m.submodules.pipe1 = self.pipe1
m.submodules.pipe2 = self.pipe2
- # connect inter-pipe input/output valid/busy/data
+ # connect inter-pipe input/output valid/ready/data
m.d.comb += self.pipe2.i.p_valid.eq(self.pipe1.o.n_valid)
- m.d.comb += self.pipe1.i.n_busy.eq(self.pipe2.o.p_busy)
+ m.d.comb += self.pipe1.i.n_ready.eq(self.pipe2.o.p_ready)
m.d.comb += self.pipe2.stage.i_data.eq(self.pipe1.stage.o_data)
# inputs/outputs to the module: pipe1 connections here (LHS)
m.d.comb += self.pipe1.i.p_valid.eq(self.i_p_valid)
- m.d.comb += self.o_p_busy.eq(self.pipe1.o.p_busy)
+ m.d.comb += self.o_p_ready.eq(self.pipe1.o.p_ready)
m.d.comb += self.pipe1.stage.i_data.eq(self.i_data)
# now pipe2 connections (RHS)
m.d.comb += self.o_n_valid.eq(self.pipe2.o.n_valid)
- m.d.comb += self.pipe2.i.n_busy.eq(self.i_n_busy)
+ m.d.comb += self.pipe2.i.n_ready.eq(self.i_n_ready)
m.d.comb += self.o_data.eq(self.pipe2.stage.o_data)
return m