X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fadd%2Fexample_buf_pipe.py;h=4bb7cdf1e87fe6f929c7b17342df71924e467af8;hb=6bff1a997f3846872cf489c24b5c01426c4dc97c;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..4bb7cdf1 100644 --- a/src/add/example_buf_pipe.py +++ b/src/add/example_buf_pipe.py @@ -1,198 +1,103 @@ -""" nmigen implementation of buffered pipeline stage, based on zipcpu: - https://zipcpu.com/blog/2017/08/14/strategies-for-pipelining.html - - this module requires quite a bit of thought to understand how it works - (and why it is needed in the first place). reading the above is - *strongly* recommended. - - unlike john dawson's IEEE754 FPU STB/ACK signalling, which requires - the STB / ACK signals to raise and lower (on separate clocks) before - data may proceeed (thus only allowing one piece of data to proceed - on *ALTERNATE* cycles), the signalling here is a true pipeline - 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 - - output transmission conditions are when: - * outgoing next-stage strobe (o_n_stb) is HIGH - * outgoing next-stage busy (i_n_busy) 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 - would be possible to tell the input "hey don't send that data, we're - not ready". unfortunately, it's not possible to "change the past": - the previous stage *has no choice* but to pass on its data. - - therefore, the incoming data *must* be accepted - and stored: that - is the responsibility / contract that this stage *must* accept. - on the same clock, it's possible to tell the input that it must - not send any more data. this is the "stall" condition. - - 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 - the buffer if a stall had previously occurred, otherwise it comes - direct from processing the input. - - this allows us to respect a synchronous "travelling STB" with what - dan calls a "buffered handshake". - - it's quite a complex state machine! +""" Pipeline and BufferedHandshake examples """ -from nmigen import Signal, Cat, Const, Mux, Module +from nmoperator import eq +from iocontrol import (PrevControl, NextControl) +from singlepipe import (PrevControl, NextControl, ControlBase, + StageCls, Stage, StageChain, + BufferedHandshake, UnbufferedPipeline) + +from nmigen import Signal, Module from nmigen.cli import verilog, rtlil -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 ExampleAddStage(StageCls): + """ an example of how to use the buffered pipeline, as a class instance + """ - input data i_data is read (only), is processed and goes into an - intermediate result store [process()]. this is updated combinatorially. + def ispec(self): + """ returns a tuple of input signals which will be the incoming data + """ + return (Signal(16), Signal(16)) - 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 ospec(self): + """ returns an output signal which will happen to contain the sum + of the two inputs + """ + return Signal(16) + + def process(self, i): + """ process the input data (sums the values in the tuple) and returns it + """ + return i[0] + i[1] - 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. +class ExampleBufPipeAdd(BufferedHandshake): + """ an example of how to use the buffered pipeline, using a class instance """ 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) + addstage = ExampleAddStage() + BufferedHandshake.__init__(self, addstage) - 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) +class ExampleStage(Stage): + """ an example of how to use the buffered pipeline, in a static class + fashion + """ + + def ispec(): + return Signal(16, name="example_input_signal") + + def ospec(): + return Signal(16, name="example_output_signal") - def flush_buffer(self): - """ copies the *intermediate* register r_data into the output + def process(i): + """ process the input data and returns it (adds 1) """ - return self.o_data.eq(self.r_data) + return i + 1 - def ports(self): - return [self.i_data, self.o_data] +class ExampleStageCls(StageCls): + """ an example of how to use the buffered pipeline, in a static class + fashion + """ + + def ispec(self): + return Signal(16, name="example_input_signal") -class BufferedPipeline: - """ buffered pipeline stage + def ospec(self): + return Signal(16, name="example_output_signal") + + def process(self, i): + """ process the input data and returns it (adds 1) + """ + return i + 1 - 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 ---+ + +class ExampleBufPipe(BufferedHandshake): + """ an example of how to use the buffered pipeline. """ - 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() # <