From: Luke Kenneth Casson Leighton Date: Mon, 18 Mar 2019 11:31:40 +0000 (+0000) Subject: refactor the buffered pipeline to a cleaner API with better separation X-Git-Tag: ls180-24jan2020~1649 X-Git-Url: https://git.libre-soc.org/?p=ieee754fpu.git;a=commitdiff_plain;h=6ba0c1a42bfc8362af281aaae60858f05acc991b refactor the buffered pipeline to a cleaner API with better separation --- diff --git a/src/add/example_buf_pipe.py b/src/add/example_buf_pipe.py index 00eecc3e..45bed193 100644 --- a/src/add/example_buf_pipe.py +++ b/src/add/example_buf_pipe.py @@ -45,12 +45,44 @@ 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 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: + + def __init__(self): + self.n_valid = Signal() # out>> - goes out to the NEXT stage + self.p_ready = Signal() # <>in stage o.n_valid out>> stage+1 + stage-1 o.p_ready <>in stage o.data out>> stage+1 + | | + process --->----^ + | | + +-- r_data ->-+ input data i_data is read (only), is processed and goes into an intermediate result store [process()]. this is updated combinatorially. @@ -66,87 +98,57 @@ 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): - """ 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 + 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 + + 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) + # input: strobe comes in from previous stage, ready comes in from next + self.i = IOAckIn() + #self.i.p_valid = Signal() # >>in - comes in from PREVIOUS stage + #self.i.n_ready = Signal() # in<< - comes in from the NEXT stage + + # output: strobe goes out to next stage, ready comes in from previous + self.o = IOAckOut() + #self.o.n_valid = Signal() # out>> - goes out to the NEXT stage + #self.o.p_ready = Signal() # <>in - comes in from PREVIOUS stage - self.n_ready = Signal() # in<< - comes in from the NEXT stage - - -class IOAckOut: - - def __init__(self): - self.n_valid = Signal() # out>> - goes out to the NEXT stage - self.p_ready = Signal() # <>in stage o.n_valid out>> stage+1 - stage-1 o.p_ready <>in stage o_data out>> stage+1 - | | - +-------> process - | | - +-- r_data ---+ - """ - def __init__(self): - # input: strobe comes in from previous stage, ready comes in from next - self.i = IOAckIn() - #self.i.p_valid = Signal() # >>in - comes in from PREVIOUS stage - #self.i.n_ready = Signal() # in<< - comes in from the NEXT stage - - # output: strobe goes out to next stage, ready comes in from previous - self.o = IOAckOut() - #self.o.n_valid = Signal() # out>> - goes out to the NEXT stage - #self.o.p_ready = Signal() # <>in pipe1 o_n_valid out>> i_p_valid >>in pipe2 o_p_ready <>in pipe1 o_data out>> stage.i_data >>in pipe2 + i_data >>in pipe1 o_data out>> i_data >>in pipe2 """ def __init__(self): self.pipe1 = ExampleBufPipe() @@ -201,20 +252,22 @@ class ExampleBufPipe2: # connect inter-pipe input/output valid/ready/data m.d.comb += self.pipe2.i.p_valid.eq(self.pipe1.o.n_valid) m.d.comb += self.pipe1.i.n_ready.eq(self.pipe2.o.p_ready) - m.d.comb += self.pipe2.stage.i_data.eq(self.pipe1.stage.o_data) + m.d.comb += self.pipe2.i.data.eq(self.pipe1.o.data) # inputs/outputs to the module: pipe1 connections here (LHS) m.d.comb += self.pipe1.i.p_valid.eq(self.i_p_valid) m.d.comb += self.o_p_ready.eq(self.pipe1.o.p_ready) - m.d.comb += self.pipe1.stage.i_data.eq(self.i_data) + m.d.comb += self.pipe1.i.data.eq(self.i_data) # now pipe2 connections (RHS) m.d.comb += self.o_n_valid.eq(self.pipe2.o.n_valid) m.d.comb += self.pipe2.i.n_ready.eq(self.i_n_ready) - m.d.comb += self.o_data.eq(self.pipe2.stage.o_data) + m.d.comb += self.o_data.eq(self.pipe2.o.data) return m +num_tests = 10000 + if __name__ == '__main__': print ("test 1") dut = ExampleBufPipe() @@ -232,3 +285,9 @@ if __name__ == '__main__': print ("test 4") dut = ExampleBufPipe2() run_simulation(dut, testbench4(dut), vcd_name="test_bufpipe4.vcd") + + print ("test 5") + dut = ExampleBufPipeAdd() + test = Test5(dut) + run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe5.vcd") +