+
+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 must be of the same type
+ """
+ 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
+ """
+ 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 BufferedPipeline: