X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fadd%2Fexample_buf_pipe.py;h=b83d5035cc01c7ec338c1589359323b213e3099f;hb=f3945d1317aa236b752c1e286801caa2c3a07703;hp=6678a6713f05280828d7b3a5ef4fd4f3adffaa96;hpb=af3ae7902ba4e5a26556eb4442c8351c95b267a4;p=ieee754fpu.git diff --git a/src/add/example_buf_pipe.py b/src/add/example_buf_pipe.py index 6678a671..b83d5035 100644 --- a/src/add/example_buf_pipe.py +++ b/src/add/example_buf_pipe.py @@ -12,12 +12,12 @@ 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 (p.i_valid) is HIGH + * outgoing previous-stage ready (p.o_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 (n.o_valid) is HIGH + * outgoing next-stage ready (n.i_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 @@ -33,7 +33,7 @@ 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. @@ -45,154 +45,414 @@ from nmigen import Signal, Cat, Const, Mux, Module from nmigen.cli import verilog, rtlil +from nmigen.hdl.rec import Record, Layout +from collections.abc import Sequence -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. +class PrevControl: + """ contains signals that come *from* the previous stage (both in and out) + * i_valid: previous stage indicating all incoming data is valid. + may be a multi-bit signal, where all bits are required + to be asserted to indicate "valid". + * o_ready: output to next stage indicating readiness to accept data + * i_data : an input - added by the user of this class + """ - 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()]. + def __init__(self, i_width=1): + self.i_valid = Signal(i_width, name="p_i_valid") # prev >>in self + self.o_ready = Signal(name="p_o_ready") # prev < 1: # multi-bit case: valid only when i_valid is all 1s + all1s = Const(-1, (len(self.i_valid), False)) + return self.i_valid == all1s + # single-bit i_valid case + return self.i_valid - on the next cycle (as long as stall is not raised again) the - input may begin to be processed and transferred directly to output. - """ +class NextControl: + """ contains the signals that go *to* the next stage (both in and out) + * o_valid: output indicating to next stage that data is valid + * i_ready: input from next stage indicating that it can accept data + * o_data : an output - added by the user of this class + """ 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.o_valid = Signal(name="n_o_valid") # self out>> next + self.i_ready = Signal(name="n_i_ready") # self <>in stage o_n_stb out>> stage+1 - stage-1 o_p_busy <>in stage o_data out>> stage+1 + 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 | | - +-------> process + process --->----^ | | - +-- r_data ---+ + +-- r_data ->-+ + + input data p.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): - # 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 + def __init__(self, stage): + PipelineBase.__init__(self, 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() # <