+
+class ExampleStage:
+ """ an example of how to use the buffered pipeline. actual names of
+ variables (i_data, r_data, o_data, result) below do not matter:
+ the functions however do.
+
+ input data i_data is read (only), is processed and goes into an
+ intermediate result store [process()]. this is updated combinatorially.
+
+ in a non-stall condition, the intermediate result will go into the
+ output (update_output). however if ever there is a stall, it goes
+ into r_data instead [update_buffer()].
+
+ when the non-stall condition is released, r_data is the first
+ to be transferred to the output [flush_buffer()], and the stall
+ condition cleared.
+
+ on the next cycle (as long as stall is not raised again) the
+ input may begin to be processed and transferred directly to output.
+ """
+
+ def __init__(self):
+ """ i_data can be a DIFFERENT type from everything else
+ 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)
+ self.o_data = Signal(16)
+ self.result = Signal(16)
+
+ def process(self):
+ """ process the input data and store it in result.
+ (not needed to be known: result is combinatorial)
+ """
+ return self.result.eq(self.i_data + 1)
+
+ def update_buffer(self):
+ """ 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 update_output(self):
+ """ copies the (combinatorial) result into the output
+ """
+ return self.o_data.eq(self.result)
+
+ def flush_buffer(self):
+ """ copies the *intermediate* register r_data into the output
+ """
+ return self.o_data.eq(self.r_data)
+
+ 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:
+