X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fadd%2Fexample_buf_pipe.py;h=8b9c74f78f8abc4ef1be278e374eb6e024cfa55f;hb=25357c032b55274ce620a331ecc1dc0874f5fdac;hp=6bd4d062e9fbddc32582792d970407fc5aa4d3b7;hpb=1abb4da885f1e66f800c310766924918a3b1474c;p=ieee754fpu.git diff --git a/src/add/example_buf_pipe.py b/src/add/example_buf_pipe.py index 6bd4d062..8b9c74f7 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,14 +45,82 @@ from nmigen import Signal, Cat, Const, Mux, Module from nmigen.cli import verilog, rtlil +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. +class PrevControl: + """ contains signals that come *from* the previous stage (both in and out) + * i_valid: input from previous stage indicating incoming data is valid + * o_ready: output to next stage indicating readiness to accept data + * i_data : an input - added by the user of this class + """ + + def __init__(self): + self.i_valid = Signal(name="p_i_valid") # >>in + self.o_ready = Signal(name="p_o_ready") # <> + self.i_ready = Signal(name="n_i_ready") # <>in stage n.o_valid out>> stage+1 + stage-1 p.o_ready <>in stage n.o_data out>> stage+1 + | | + process --->----^ + | | + +-- 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 @@ -66,133 +134,261 @@ class ExampleStage: 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, stage): + """ pass in a "stage" which may be either a static class or a class + instance, which has three functions: + * ispec: returns input signals according to the input specification + * ispec: returns output signals to the output specification + * process: takes an input instance and returns processed data + + p.i_data -> process() -> result --> n.o_data + | ^ + | | + +-> r_data -+ + """ + self.stage = stage - 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 + # set up input and output IO ACK (prev/next ready/valid) + self.p = PrevControl() + self.n = NextControl() + + # set up the input and output data + self.p.i_data = stage.ispec() # input type + self.r_data = stage.ospec() # all these are output type + self.result = stage.ospec() + self.n.o_data = stage.ospec() + + def connect_to_next(self, nxt): + """ helper function to connect to the next stage data/valid/ready. + """ + return self.n.connect_to_next(nxt.p) + + def connect_in(self, prev): + """ helper function to connect stage to an input source. do not + use to connect stage-to-stage! + """ + return self.p.connect_in(prev.p) + + def connect_out(self, nxt): + """ helper function to connect stage to an output source. do not + use to connect stage-to-stage! """ - self.i_data = Signal(16) - self.r_data = Signal(16) - self.o_data = Signal(16) - self.result = Signal(16) + return self.n.connect_out(nxt.n) - def process(self): - """ process the input data and store it in result. - (not needed to be known: result is combinatorial) + def set_input(self, i): + """ helper function to set the input data """ - return self.result.eq(self.i_data + 1) + return eq(self.p.i_data, i) def update_buffer(self): - """ copies the result into the intermediate register r_data + """ 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) + return eq(self.r_data, self.result) def update_output(self): """ copies the (combinatorial) result into the output """ - return self.o_data.eq(self.result) + return eq(self.n.o_data, self.result) def flush_buffer(self): """ copies the *intermediate* register r_data into the output """ - return self.o_data.eq(self.r_data) + return eq(self.n.o_data, self.r_data) def ports(self): - return [self.i_data, self.o_data] - - -class BufferedPipeline: - """ buffered pipeline stage - - stage-1 i_p_stb >>in stage o_n_stb out>> stage+1 - stage-1 o_p_busy <>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 - #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 - - # 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() # <