X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fadd%2Fmultipipe.py;h=35da5c2ec741aafc66e97804e842be0395b8a863;hb=25a0ec563bd7837b43a1d04036b2a5945c97023b;hp=84f52f3ed401dadf3b9c745b8c1fdb3a8e2b52fe;hpb=cf79aee21fb4a377d9a6572b666deb58ca09defb;p=ieee754fpu.git diff --git a/src/add/multipipe.py b/src/add/multipipe.py index 84f52f3e..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 @@ -62,18 +71,31 @@ class MultiInControlBase: 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 MultiOutControlBase: """ Common functions for Pipeline API """ - def __init__(self, n_len=1): + 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] @@ -88,6 +110,7 @@ class MultiOutControlBase: # 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) @@ -117,81 +140,70 @@ class MultiOutControlBase: 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(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): 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()) + 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) - 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])) - - 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)) @@ -248,6 +260,8 @@ class CombMultiInPipeline(MultiInControlBase): 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) @@ -256,7 +270,7 @@ class CombMultiInPipeline(MultiInControlBase): 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): @@ -277,7 +291,19 @@ class CombMultiInPipeline(MultiInControlBase): 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 @@ -297,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) @@ -310,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)