From 837eb17472ba4f9cc43f47d0939415a6b9ad61ab Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Tue, 19 Mar 2019 04:01:01 +0000 Subject: [PATCH] rename / reorder to have prev and next control classes input i_ and output o_ secondary prefixes are kept --- src/add/example_buf_pipe.py | 138 ++++++++++++++++++------------------ src/add/test_buf_pipe.py | 116 +++++++++++++++--------------- 2 files changed, 127 insertions(+), 127 deletions(-) diff --git a/src/add/example_buf_pipe.py b/src/add/example_buf_pipe.py index 24e83034..51c95520 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_valid) is HIGH - * outgoing previous-stage ready (o.p_ready) 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_valid) is HIGH - * outgoing next-stage ready (i.n_ready) 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 @@ -48,18 +48,18 @@ from nmigen.cli import verilog, rtlil from collections.abc import Sequence -class IOAckIn: +class PrevControl: def __init__(self): - self.p_valid = Signal() # >>in - comes in from PREVIOUS stage - self.n_ready = Signal() # in<< - comes in from the NEXT stage + self.i_valid = Signal(name="p_i_valid") # >>in + self.o_ready = Signal(name="p_o_ready") # <> - goes out to the NEXT stage - self.p_ready = Signal() # <> + self.i_ready = Signal(name="n_i_ready") # <>in stage o.n_valid out>> stage+1 - stage-1 o.p_ready <>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.data out>> stage+1 | | process --->----^ | | +-- r_data ->-+ - input data i_data is read (only), is processed and goes into an + input data p.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 @@ -105,7 +105,7 @@ class BufferedPipeline: * ispec: returns output signals to the output specification * process: takes an input instance and returns processed data - i_data -> process() -> result --> o.data + p.data -> process() -> result --> n.data | ^ | | +-> r_data -+ @@ -113,46 +113,46 @@ class BufferedPipeline: self.stage = stage # set up input and output IO ACK (prev/next ready/valid) - self.i = IOAckIn() - self.o = IOAckOut() + self.p = PrevControl() + self.n = NextControl() # set up the input and output data - self.i.data = stage.ispec() # input type + self.p.data = stage.ispec() # input type self.r_data = stage.ospec() # all these are output type self.result = stage.ospec() - self.o.data = stage.ospec() + self.n.data = stage.ospec() def connect_next(self, nxt): """ helper function to connect to the next stage data/valid/ready. data/valid is passed *TO* nxt, and ready comes *IN* from nxt. """ - return [nxt.i.p_valid.eq(self.o.n_valid), - self.i.n_ready.eq(nxt.o.p_ready), - eq(nxt.i.data, self.o.data), + return [nxt.p.i_valid.eq(self.n.o_valid), + self.n.i_ready.eq(nxt.p.o_ready), + eq(nxt.p.data, self.n.data), ] def connect_in(self, prev): """ helper function to connect stage to an input source. do not use to connect stage-to-stage! """ - return [self.i.p_valid.eq(prev.i.p_valid), - prev.o.p_ready.eq(self.o.p_ready), - eq(self.i.data, prev.i.data), + return [self.p.i_valid.eq(prev.p.i_valid), + prev.p.o_ready.eq(self.p.o_ready), + eq(self.p.data, prev.p.data), ] def connect_out(self, nxt): """ helper function to connect stage to an output source. do not use to connect stage-to-stage! """ - return [nxt.o.n_valid.eq(self.o.n_valid), - self.i.n_ready.eq(nxt.i.n_ready), - eq(nxt.o.data, self.o.data), + return [nxt.n.o_valid.eq(self.n.o_valid), + self.n.i_ready.eq(nxt.n.i_ready), + eq(nxt.n.data, self.n.data), ] def set_input(self, i): """ helper function to set the input data """ - return eq(self.i.data, i) + return eq(self.p.data, i) def update_buffer(self): """ copies the result into the intermediate register r_data, @@ -164,70 +164,70 @@ class BufferedPipeline: def update_output(self): """ copies the (combinatorial) result into the output """ - return eq(self.o.data, self.result) + return eq(self.n.data, self.result) def flush_buffer(self): """ copies the *intermediate* register r_data into the output """ - return eq(self.o.data, self.r_data) + return eq(self.n.data, self.r_data) def ports(self): - return [self.i.data, self.o.data] + return [self.p.data, self.n.data] def elaborate(self, platform): m = Module() if hasattr(self.stage, "setup"): - self.stage.setup(m, self.i.data) + self.stage.setup(m, self.p.data) # establish some combinatorial temporaries o_n_validn = Signal(reset_less=True) i_p_valid_o_p_ready = Signal(reset_less=True) - m.d.comb += [o_n_validn.eq(~self.o.n_valid), - i_p_valid_o_p_ready.eq(self.i.p_valid & self.o.p_ready), + m.d.comb += [o_n_validn.eq(~self.n.o_valid), + i_p_valid_o_p_ready.eq(self.p.i_valid & self.p.o_ready), ] # store result of processing in combinatorial temporary - with m.If(self.i.p_valid): # input is valid: process it - m.d.comb += eq(self.result, self.stage.process(self.i.data)) + with m.If(self.p.i_valid): # input is valid: process it + m.d.comb += eq(self.result, self.stage.process(self.p.data)) # if not in stall condition, update the temporary register - with m.If(self.o.p_ready): # not stalled + with m.If(self.p.o_ready): # not stalled m.d.sync += self.update_buffer() - #with m.If(self.i.p_rst): # reset - # m.d.sync += self.o.n_valid.eq(0) - # m.d.sync += self.o.p_ready.eq(0) - with m.If(self.i.n_ready): # next stage is ready - with m.If(self.o.p_ready): # not stalled + #with m.If(self.p.i_rst): # reset + # m.d.sync += self.n.o_valid.eq(0) + # m.d.sync += self.p.o_ready.eq(0) + with m.If(self.n.i_ready): # next stage is ready + with m.If(self.p.o_ready): # not stalled # nothing in buffer: send (processed) input direct to output - m.d.sync += [self.o.n_valid.eq(self.i.p_valid), + m.d.sync += [self.n.o_valid.eq(self.p.i_valid), self.update_output(), ] - with m.Else(): # o.p_ready is false, and something is in buffer. + with m.Else(): # p.o_ready is false, and something is in buffer. # Flush the [already processed] buffer to the output port. - m.d.sync += [self.o.n_valid.eq(1), + m.d.sync += [self.n.o_valid.eq(1), self.flush_buffer(), # clear stall condition, declare register empty. - self.o.p_ready.eq(1), + self.p.o_ready.eq(1), ] - # ignore input, since o.p_ready is also false. + # ignore input, since p.o_ready is also false. - # (i.n_ready) is false here: next stage is ready + # (n.i_ready) is false here: next stage is ready with m.Elif(o_n_validn): # next stage being told "ready" - m.d.sync += [self.o.n_valid.eq(self.i.p_valid), - self.o.p_ready.eq(1), # Keep the buffer empty + m.d.sync += [self.n.o_valid.eq(self.p.i_valid), + self.p.o_ready.eq(1), # Keep the buffer empty # set the output data (from comb result) self.update_output(), ] - # (i.n_ready) false and (o.n_valid) true: + # (n.i_ready) false and (n.o_valid) true: with m.Elif(i_p_valid_o_p_ready): # If next stage *is* ready, and not stalled yet, accept input - m.d.sync += self.o.p_ready.eq(~(self.i.p_valid & self.o.n_valid)) + m.d.sync += self.p.o_ready.eq(~(self.p.i_valid & self.n.o_valid)) return m def ports(self): - return [self.i.p_valid, self.i.n_ready, - self.o.n_valid, self.o.p_ready, + return [self.p.i_valid, self.n.i_ready, + self.n.o_valid, self.p.o_ready, ] @@ -315,37 +315,37 @@ class CombPipe: self.stage = stage self._data_valid = Signal() # set up input and output IO ACK (prev/next ready/valid) - self.i = IOAckIn() - self.o = IOAckOut() + self.p = PrevControl() + self.n = NextControl() # set up the input and output data - self.i.data = stage.ispec() # input type + self.p.data = stage.ispec() # input type self.r_data = stage.ispec() # input type self.result = stage.ospec() # output data - self.o.data = stage.ospec() # output type - self.o.data.name = "outdata" + self.n.data = stage.ospec() # output type + self.n.data.name = "outdata" def set_input(self, i): """ helper function to set the input data """ - return eq(self.i.data, i) + return eq(self.p.data, i) def elaborate(self, platform): m = Module() if hasattr(self.stage, "setup"): self.stage.setup(m, self.r_data) m.d.comb += eq(self.result, self.stage.process(self.r_data)) - m.d.comb += self.o.n_valid.eq(self._data_valid) - m.d.comb += self.o.p_ready.eq(~self._data_valid | self.i.n_ready) - m.d.sync += self._data_valid.eq(self.i.p_valid | \ - (~self.i.n_ready & self._data_valid)) - with m.If(self.i.p_valid & self.o.p_ready): - m.d.sync += eq(self.r_data, self.i.data) - m.d.comb += eq(self.o.data, self.result) + m.d.comb += self.n.o_valid.eq(self._data_valid) + m.d.comb += self.p.o_ready.eq(~self._data_valid | self.n.i_ready) + m.d.sync += self._data_valid.eq(self.p.i_valid | \ + (~self.n.i_ready & self._data_valid)) + with m.If(self.p.i_valid & self.p.o_ready): + m.d.sync += eq(self.r_data, self.p.data) + m.d.comb += eq(self.n.data, self.result) return m def ports(self): - return [self.i.data, self.o.data] + return [self.p.data, self.n.data] class ExampleCombPipe(CombPipe): diff --git a/src/add/test_buf_pipe.py b/src/add/test_buf_pipe.py index 9ba8449c..76c71a83 100644 --- a/src/add/test_buf_pipe.py +++ b/src/add/test_buf_pipe.py @@ -2,42 +2,42 @@ from nmigen import Module, Signal, Mux from nmigen.compat.sim import run_simulation from example_buf_pipe import ExampleBufPipe, ExampleBufPipeAdd from example_buf_pipe import ExampleCombPipe, CombPipe -from example_buf_pipe import IOAckIn, IOAckOut +from example_buf_pipe import PrevControl, NextControl from random import randint def check_o_n_valid(dut, val): - o_n_valid = yield dut.o.n_valid + o_n_valid = yield dut.n.o_valid assert o_n_valid == val def testbench(dut): #yield dut.i_p_rst.eq(1) - yield dut.i.n_ready.eq(0) - yield dut.o.p_ready.eq(0) + yield dut.n.i_ready.eq(0) + yield dut.p.o_ready.eq(0) yield yield #yield dut.i_p_rst.eq(0) - yield dut.i.n_ready.eq(1) - yield dut.i.data.eq(5) - yield dut.i.p_valid.eq(1) + yield dut.n.i_ready.eq(1) + yield dut.p.data.eq(5) + yield dut.p.i_valid.eq(1) yield - yield dut.i.data.eq(7) + yield dut.p.data.eq(7) yield from check_o_n_valid(dut, 0) # effects of i_p_valid delayed yield yield from check_o_n_valid(dut, 1) # ok *now* i_p_valid effect is felt - yield dut.i.data.eq(2) + yield dut.p.data.eq(2) yield - yield dut.i.n_ready.eq(0) # begin going into "stall" (next stage says ready) - yield dut.i.data.eq(9) + yield dut.n.i_ready.eq(0) # begin going into "stall" (next stage says ready) + yield dut.p.data.eq(9) yield - yield dut.i.p_valid.eq(0) - yield dut.i.data.eq(12) + yield dut.p.i_valid.eq(0) + yield dut.p.data.eq(12) yield - yield dut.i.data.eq(32) - yield dut.i.n_ready.eq(1) + yield dut.p.data.eq(32) + yield dut.n.i_ready.eq(1) yield yield from check_o_n_valid(dut, 1) # buffer still needs to output yield @@ -48,33 +48,33 @@ def testbench(dut): def testbench2(dut): - #yield dut.i.p_rst.eq(1) - yield dut.i.n_ready.eq(0) - #yield dut.o.p_ready.eq(0) + #yield dut.p.i_rst.eq(1) + yield dut.n.i_ready.eq(0) + #yield dut.p.o_ready.eq(0) yield yield - #yield dut.i.p_rst.eq(0) - yield dut.i.n_ready.eq(1) - yield dut.i.data.eq(5) - yield dut.i.p_valid.eq(1) + #yield dut.p.i_rst.eq(0) + yield dut.n.i_ready.eq(1) + yield dut.p.data.eq(5) + yield dut.p.i_valid.eq(1) yield - yield dut.i.data.eq(7) + yield dut.p.data.eq(7) yield from check_o_n_valid(dut, 0) # effects of i_p_valid delayed 2 clocks yield yield from check_o_n_valid(dut, 0) # effects of i_p_valid delayed 2 clocks - yield dut.i.data.eq(2) + yield dut.p.data.eq(2) yield yield from check_o_n_valid(dut, 1) # ok *now* i_p_valid effect is felt - yield dut.i.n_ready.eq(0) # begin going into "stall" (next stage says ready) - yield dut.i.data.eq(9) + yield dut.n.i_ready.eq(0) # begin going into "stall" (next stage says ready) + yield dut.p.data.eq(9) yield - yield dut.i.p_valid.eq(0) - yield dut.i.data.eq(12) + yield dut.p.i_valid.eq(0) + yield dut.p.data.eq(12) yield - yield dut.i.data.eq(32) - yield dut.i.n_ready.eq(1) + yield dut.p.data.eq(32) + yield dut.n.i_ready.eq(1) yield yield from check_o_n_valid(dut, 1) # buffer still needs to output yield @@ -107,16 +107,16 @@ class Test3: send = True else: send = randint(0, send_range) != 0 - o_p_ready = yield self.dut.o.p_ready + o_p_ready = yield self.dut.p.o_ready if not o_p_ready: yield continue if send and self.i != len(self.data): - yield self.dut.i.p_valid.eq(1) - yield self.dut.i.data.eq(self.data[self.i]) + yield self.dut.p.i_valid.eq(1) + yield self.dut.p.data.eq(self.data[self.i]) self.i += 1 else: - yield self.dut.i.p_valid.eq(0) + yield self.dut.p.i_valid.eq(0) yield def rcv(self): @@ -124,13 +124,13 @@ class Test3: stall_range = randint(0, 3) for j in range(randint(1,10)): stall = randint(0, stall_range) != 0 - yield self.dut.i.n_ready.eq(stall) + yield self.dut.n.i_ready.eq(stall) yield - o_n_valid = yield self.dut.o.n_valid - i_n_ready = yield self.dut.i.n_ready + o_n_valid = yield self.dut.n.o_valid + i_n_ready = yield self.dut.n.i_ready if not o_n_valid or not i_n_ready: continue - o_data = yield self.dut.o.data + o_data = yield self.dut.n.data self.resultfn(o_data, self.data[self.o], self.i, self.o) self.o += 1 if self.o == len(self.data): @@ -159,17 +159,17 @@ class Test5: send = True else: send = randint(0, send_range) != 0 - o_p_ready = yield self.dut.o.p_ready + o_p_ready = yield self.dut.p.o_ready if not o_p_ready: yield continue if send and self.i != len(self.data): - yield self.dut.i.p_valid.eq(1) + yield self.dut.p.i_valid.eq(1) for v in self.dut.set_input(self.data[self.i]): yield v self.i += 1 else: - yield self.dut.i.p_valid.eq(0) + yield self.dut.p.i_valid.eq(0) yield def rcv(self): @@ -177,13 +177,13 @@ class Test5: stall_range = randint(0, 3) for j in range(randint(1,10)): stall = randint(0, stall_range) != 0 - yield self.dut.i.n_ready.eq(stall) + yield self.dut.n.i_ready.eq(stall) yield - o_n_valid = yield self.dut.o.n_valid - i_n_ready = yield self.dut.i.n_ready + o_n_valid = yield self.dut.n.o_valid + i_n_ready = yield self.dut.n.i_ready if not o_n_valid or not i_n_ready: continue - o_data = yield self.dut.o.data + o_data = yield self.dut.n.data self.resultfn(o_data, self.data[self.o], self.i, self.o) self.o += 1 if self.o == len(self.data): @@ -205,20 +205,20 @@ def testbench4(dut): while True: stall = randint(0, 3) != 0 send = randint(0, 5) != 0 - yield dut.i.n_ready.eq(stall) - o_p_ready = yield dut.o.p_ready + yield dut.n.i_ready.eq(stall) + o_p_ready = yield dut.p.o_ready if o_p_ready: if send and i != len(data): - yield dut.i.p_valid.eq(1) - yield dut.i.data.eq(data[i]) + yield dut.p.i_valid.eq(1) + yield dut.p.data.eq(data[i]) i += 1 else: - yield dut.i.p_valid.eq(0) + yield dut.p.i_valid.eq(0) yield - o_n_valid = yield dut.o.n_valid - i_n_ready = yield dut.i.n_ready + o_n_valid = yield dut.n.o_valid + i_n_ready = yield dut.n.i_ready if o_n_valid and i_n_ready: - o_data = yield dut.o.data + o_data = yield dut.n.data assert o_data == data[o] + 2, "%d-%d data %x not match %x\n" \ % (i, o, o_data, data[o]) o += 1 @@ -232,19 +232,19 @@ class ExampleBufPipe2: v v i_p_valid >>in pipe1 o_n_valid out>> i_p_valid >>in pipe2 o_p_ready <>in pipe1 o_data out>> i_data >>in pipe2 + p_data >>in pipe1 o_data out>> p_data >>in pipe2 """ def __init__(self): self.pipe1 = ExampleBufPipe() self.pipe2 = ExampleBufPipe() # input - self.i = IOAckIn() - self.i.data = Signal(32) # >>in - comes in from the PREVIOUS stage + self.p = PrevControl() + self.p.data = Signal(32) # >>in - comes in from the PREVIOUS stage # output - self.o = IOAckOut() - self.o.data = Signal(32) # out>> - goes out to the NEXT stage + self.n = NextControl() + self.n.data = Signal(32) # out>> - goes out to the NEXT stage def elaborate(self, platform): m = Module() -- 2.30.2