From e152b71c27e1fa4f04da20fd85012c860f3bfcde Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Sat, 6 Apr 2019 04:56:00 +0100 Subject: [PATCH] add 2nd unbuffered pipeline class --- src/add/singlepipe.py | 68 ++++++++++++++++++++++++++++++++++++++++ src/add/test_buf_pipe.py | 9 ++---- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/add/singlepipe.py b/src/add/singlepipe.py index 42b20654..f9e5ec8f 100644 --- a/src/add/singlepipe.py +++ b/src/add/singlepipe.py @@ -699,6 +699,74 @@ class UnbufferedPipeline(ControlBase): return self.m +class UnbufferedPipeline2(ControlBase): + """ A simple pipeline stage with single-clock synchronisation + and two-way valid/ready synchronised signalling. + + Note that a stall in one stage will result in the entire pipeline + chain stalling. + + Also that unlike BufferedPipeline, the valid/ready signalling does NOT + travel synchronously with the data: the valid/ready signalling + combines in a *combinatorial* fashion. Therefore, a long pipeline + chain will lengthen propagation delays. + + Argument: stage. see Stage API, above + + stage-1 p.i_valid >>in stage n.o_valid out>> stage+1 + stage-1 p.o_ready <>in stage n.o_data out>> stage+1 + | | + r_data result + | | + +--process ->-+ + + Attributes: + ----------- + p.i_data : StageInput, shaped according to ispec + The pipeline input + p.o_data : StageOutput, shaped according to ospec + The pipeline output + buf : output_shape according to ospec + A temporary (buffered) copy of a valid output + This is HELD if the output is not ready. It is updated + SYNCHRONOUSLY. + """ + + def __init__(self, stage, stage_ctl=False): + ControlBase.__init__(self, stage_ctl=stage_ctl) + self.stage = stage + + # set up the input and output data + self.p.i_data = stage.ispec() # input type + self.n.o_data = stage.ospec() # output type + + def elaborate(self, platform): + self.m = ControlBase._elaborate(self, platform) + + buf_full = Signal() # is data valid or not + buf = self.stage.ospec() # output type + if hasattr(self.stage, "setup"): + self.stage.setup(self.m, self.p.i_data) + + # some temporaries + p_i_valid = Signal(reset_less=True) + self.m.d.comb += p_i_valid.eq(self.p.i_valid_test) + + self.m.d.comb += self.n.o_valid.eq(buf_full | p_i_valid) + self.m.d.comb += self.p._o_ready.eq(~buf_full) + self.m.d.sync += buf_full.eq(~self.n.i_ready_test & \ + (p_i_valid | buf_full)) + with self.m.If(buf_full): + self.m.d.comb += eq(self.n.o_data, buf) + with self.m.Else(): + self.m.d.comb += eq(self.n.o_data, + self.stage.process(self.p.i_data)) + self.m.d.sync += eq(buf, self.n.o_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. diff --git a/src/add/test_buf_pipe.py b/src/add/test_buf_pipe.py index 17caa176..2c3c430f 100644 --- a/src/add/test_buf_pipe.py +++ b/src/add/test_buf_pipe.py @@ -24,6 +24,7 @@ from example_buf_pipe import ExamplePipeline, UnbufferedPipeline from example_buf_pipe import ExampleStageCls from example_buf_pipe import PrevControl, NextControl, BufferedPipeline from example_buf_pipe import StageChain, ControlBase, StageCls +from singlepipe import UnbufferedPipeline2 from random import randint @@ -206,7 +207,6 @@ class Test5: send = True else: send = randint(0, send_range) != 0 - send = True o_p_ready = yield self.dut.p.o_ready if not o_p_ready: yield @@ -688,17 +688,14 @@ class ExampleBufAdd1Pipe(BufferedPipeline): BufferedPipeline.__init__(self, stage) -class ExampleUnBufAdd1Pipe(UnbufferedPipeline): +class ExampleUnBufAdd1Pipe(UnbufferedPipeline2): def __init__(self): stage = ExampleStageCls() - UnbufferedPipeline.__init__(self, stage) + UnbufferedPipeline2.__init__(self, stage) class ExampleBufUnBufPipe(ControlBase): - """ Example of how to do delayed pipeline, where the stage signals - whether it is ready. - """ def elaborate(self, platform): m = ControlBase._elaborate(self, platform) -- 2.30.2