X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fadd%2Fmultipipe.py;h=35da5c2ec741aafc66e97804e842be0395b8a863;hb=25a0ec563bd7837b43a1d04036b2a5945c97023b;hp=8dfc87aadf5a5c2d47c449eee6fa8e4588f53ed3;hpb=6f89c9b35dceb0fbe7f6ece11b259713a8c1789f;p=ieee754fpu.git diff --git a/src/add/multipipe.py b/src/add/multipipe.py index 8dfc87aa..35da5c2e 100644 --- a/src/add/multipipe.py +++ b/src/add/multipipe.py @@ -1,4 +1,13 @@ -""" Combinatorial Multi-input multiplexer block conforming to Pipeline API +""" Combinatorial Multi-input and Multi-output multiplexer blocks + conforming to Pipeline API + + Multi-input is complex because if any one input is ready, the output + can be ready, and the decision comes from a separate module. + + Multi-output is simple (pretty much identical to UnbufferedPipeline), + and the selection is just a mux. The only proviso (difference) being: + the outputs not being selected have to have their o_ready signals + DEASSERTED. """ from math import log @@ -12,11 +21,15 @@ from collections.abc import Sequence from example_buf_pipe import eq, NextControl, PrevControl, ExampleStage -class MultiInControl: +class MultiInControlBase: """ Common functions for Pipeline API """ def __init__(self, in_multi=None, p_len=1): - """ Multi-input Control class + """ Multi-input Control class. Conforms to same API as ControlBase... + mostly. has additional indices to the *multiple* input stages + + * p: contains ready/valid to the previous stages PLURAL + * n: contains ready/valid to the next stage User must also: * add i_data members to PrevControl and @@ -58,19 +71,37 @@ class MultiInControl: def ports(self): res = [] for i in range(len(self.p)): - res += [self.p[i].i_valid, self.p[i].o_ready, - self.p[i].i_data]# XXX need flattening!] - res += [self.n.i_ready, self.n.o_valid, - self.n.o_data] # XXX need flattening!] + p = self.p[i] + res += [p.i_valid, p.o_ready] + if hasattr(p.i_data, "ports"): + res += p.i_data.ports() + else: + rres = p.i_data + if not isinstance(rres, Sequence): + rres = [rres] + res += rres + n = self.n + res += [n.i_ready, n.o_valid] + if hasattr(n.o_data, "ports"): + res += n.o_data.ports() + else: + rres = n.o_data + if not isinstance(rres, Sequence): + rres = [rres] + res += rres return res - -class MultiOutControl: +class MultiOutControlBase: """ Common functions for Pipeline API """ - def __init__(self, n_len=1): - """ Multi-output Control class + def __init__(self, n_len=1, in_multi=None): + """ Multi-output Control class. Conforms to same API as ControlBase... + mostly. has additional indices to the multiple *output* stages + [MultiInControlBase has multiple *input* stages] + + * p: contains ready/valid to the previou stage + * n: contains ready/valid to the next stages PLURAL User must also: * add i_data member to PrevControl and @@ -79,6 +110,7 @@ class MultiOutControl: # set up input and output IO ACK (prev/next ready/valid) self.p = PrevControl(in_multi) + n = [] for i in range(n_len): n.append(NextControl()) self.n = Array(n) @@ -108,88 +140,77 @@ class MultiOutControl: return eq(self.p.i_data, i) def ports(self): - res = [] - res += [self.p.i_valid, self.p.o_ready, - self.p.i_data]# XXX need flattening!] + res = [self.p.i_valid, self.p.o_ready] + if hasattr(self.p.i_data, "ports"): + res += self.p.i_data.ports() + else: + res += self.p.i_data + for i in range(len(self.n)): - res += [self.n[i].i_ready, self.n[i].o_valid, - self.n[i].o_data] # XXX need flattening!] + n = self.n[i] + res += [n.i_ready, n.o_valid] + if hasattr(n.o_data, "ports"): + res += n.o_data.ports() + else: + res += n.o_data return res - -class CombMultiOutPipeline(MultiInControl): +class CombMultiOutPipeline(MultiOutControlBase): """ A multi-input Combinatorial block conforming to the Pipeline API Attributes: ----------- - p.i_data : StageInput, shaped according to ispec - The pipeline input - p.o_data : StageOutput, shaped according to ospec - The pipeline output - r_data : input_shape according to ispec - A temporary (buffered) copy of a prior (valid) input. - This is HELD if the output is not ready. It is updated - SYNCHRONOUSLY. + p.i_data : stage input data (non-array). shaped according to ispec + n.o_data : stage output data array. shaped according to ospec """ def __init__(self, stage, n_len, n_mux): - MultiInControl.__init__(self, n_len=n_len) + MultiOutControlBase.__init__(self, n_len=n_len) self.stage = stage - self.p_mux = p_mux + self.n_mux = n_mux # set up the input and output data - for i in range(p_len): - self.p[i].i_data = stage.ispec() # input type - self.n.o_data = stage.ospec() + self.p.i_data = stage.ispec() # input type + for i in range(n_len): + self.n[i].o_data = stage.ospec() # output type def elaborate(self, platform): m = Module() - m.submodules += self.p_mux + if hasattr(self.n_mux, "elaborate"): # TODO: identify submodule? + m.submodules += self.n_mux # need buffer register conforming to *input* spec r_data = self.stage.ispec() # input type if hasattr(self.stage, "setup"): self.stage.setup(m, r_data) - data_valid = [] - n_i_readyn = [] - n_len = len(self.n) - for i in range(n_len): - data_valid.append(Signal(name="data_valid", reset_less=True)) - n_i_readyn.append(Signal(name="n_i_readyn", reset_less=True)) - n_i_readyn = Array(n_i_readyn) - data_valid = Array(data_valid) + # multiplexer id taken from n_mux + mid = self.n_mux.m_id + # temporaries p_i_valid = Signal(reset_less=True) - m.d.comb += p_i_valid.eq(self.p.i_valid_logic()) - - mid = self.p_mux.m_id - - for i in range(p_len): - m.d.comb += data_valid[i].eq(0) - m.d.comb += n_i_readyn[i].eq(1) - m.d.comb += self.n[i].o_valid.eq(data_valid[i]) - m.d.comb += self.p[mid].o_ready.eq(~data_valid[mid] | self.n.i_ready) - m.d.comb += n_i_readyn[mid].eq(~self.n[mid].i_ready & data_valid[mid]) - anyvalid = Signal(i, reset_less=True) - av = [] - for i in range(p_len): - av.append(~data_valid[i] | self.n[i].i_ready) - anyvalid = Cat(*av) - m.d.comb += self.p.o_ready.eq(anyvalid.bool()) - m.d.comb += data_valid[mid].eq(p_i_valid | \ - (n_i_readyn[mid] & data_valid[mid])) + pv = Signal(reset_less=True) + m.d.comb += p_i_valid.eq(self.p.i_valid_test) + m.d.comb += pv.eq(self.p.i_valid & self.p.o_ready) - with m.If(self.p.i_valid & self.p.o_ready): + # all outputs to next stages first initialised to zero (invalid) + # the only output "active" is then selected by the muxid + for i in range(len(self.n)): + m.d.comb += self.n[i].o_valid.eq(0) + data_valid = self.n[mid].o_valid + m.d.comb += self.p.o_ready.eq(~data_valid | self.n[mid].i_ready) + m.d.comb += data_valid.eq(p_i_valid | \ + (~self.n[mid].i_ready & data_valid)) + with m.If(pv): m.d.comb += eq(r_data, self.p.i_data) m.d.comb += eq(self.n[mid].o_data, self.stage.process(r_data)) return m -class CombMultiInPipeline(MultiInControl): +class CombMultiInPipeline(MultiInControlBase): """ A multi-input Combinatorial block conforming to the Pipeline API Attributes: @@ -205,7 +226,7 @@ class CombMultiInPipeline(MultiInControl): """ def __init__(self, stage, p_len, p_mux): - MultiInControl.__init__(self, p_len=p_len) + MultiInControlBase.__init__(self, p_len=p_len) self.stage = stage self.p_mux = p_mux @@ -239,6 +260,8 @@ class CombMultiInPipeline(MultiInControl): n_i_readyn = Array(n_i_readyn) data_valid = Array(data_valid) + nirn = Signal(reset_less=True) + m.d.comb += nirn.eq(~self.n.i_ready) mid = self.p_mux.m_id for i in range(p_len): m.d.comb += data_valid[i].eq(0) @@ -247,7 +270,7 @@ class CombMultiInPipeline(MultiInControl): m.d.comb += self.p[i].o_ready.eq(0) m.d.comb += p_i_valid[mid].eq(self.p_mux.active) m.d.comb += self.p[mid].o_ready.eq(~data_valid[mid] | self.n.i_ready) - m.d.comb += n_i_readyn[mid].eq(~self.n.i_ready & data_valid[mid]) + m.d.comb += n_i_readyn[mid].eq(nirn & data_valid[mid]) anyvalid = Signal(i, reset_less=True) av = [] for i in range(p_len): @@ -268,7 +291,19 @@ class CombMultiInPipeline(MultiInControl): return m +class CombMuxOutPipe(CombMultiOutPipeline): + def __init__(self, stage, n_len): + # HACK: stage is also the n-way multiplexer + CombMultiOutPipeline.__init__(self, stage, n_len=n_len, n_mux=stage) + + # HACK: n-mux is also the stage... so set the muxid equal to input mid + stage.m_id = self.p.i_data.mid + + + class InputPriorityArbiter: + """ arbitration module for Input-Mux pipe, baed on PriorityEncoder + """ def __init__(self, pipe, num_rows): self.pipe = pipe self.num_rows = num_rows @@ -288,7 +323,7 @@ class InputPriorityArbiter: in_ready = [] for i in range(self.num_rows): p_i_valid = Signal(reset_less=True) - m.d.comb += p_i_valid.eq(self.pipe.p[i].i_valid_logic()) + m.d.comb += p_i_valid.eq(self.pipe.p[i].i_valid_test) in_ready.append(p_i_valid) m.d.comb += pe.i.eq(Cat(*in_ready)) # array of input "valids" m.d.comb += self.active.eq(~pe.n) # encoder active (one input valid) @@ -301,18 +336,18 @@ class InputPriorityArbiter: -class ExamplePipeline(CombMultiInPipeline): +class PriorityCombMuxInPipe(CombMultiInPipeline): """ an example of how to use the combinatorial pipeline. """ - def __init__(self, p_len=2): + def __init__(self, stage, p_len=2): p_mux = InputPriorityArbiter(self, p_len) - CombMultiInPipeline.__init__(self, ExampleStage, p_len, p_mux) + CombMultiInPipeline.__init__(self, stage, p_len, p_mux) if __name__ == '__main__': - dut = ExamplePipeline() + dut = PriorityCombMuxInPipe(ExampleStage) vl = rtlil.convert(dut, ports=dut.ports()) with open("test_combpipe.il", "w") as f: f.write(vl)