redo ReservationStations setup of pseudoalus
[nmutil.git] / src / nmutil / concurrentunit.py
1 """ concurrent unit from mitch alsup augmentations to 6600 scoreboard
2
3 This work is funded through NLnet under Grant 2019-02-012
4
5 License: LGPLv3+
6
7
8 * data fans in
9 * data goes through a pipeline
10 * results fan back out.
11
12 the output data format has to have a member "muxid", which is used
13 as the array index on fan-out
14
15 Associated bugreports:
16
17 * https://bugs.libre-soc.org/show_bug.cgi?id=538
18 """
19
20 from math import log
21 from nmigen import Module, Elaboratable, Signal
22 from nmigen.asserts import Assert
23 from nmigen.cli import main, verilog
24
25 from nmutil.singlepipe import PassThroughStage
26 from nmutil.multipipe import CombMuxOutPipe
27 from nmutil.multipipe import PriorityCombMuxInPipe
28
29
30 def num_bits(n):
31 return int(log(n) / log(2))
32
33
34 class PipeContext:
35
36 def __init__(self, pspec):
37 """ creates a pipeline context. currently: operator (op) and muxid
38
39 opkls (within pspec) - the class to create that will be the
40 "operator". instance must have an "eq"
41 function.
42 """
43 self.id_wid = pspec.id_wid
44 self.op_wid = pspec.op_wid
45 self.muxid = Signal(self.id_wid, reset_less=True) # RS multiplex ID
46 opkls = pspec.opkls
47 if opkls is None:
48 self.op = Signal(self.op_wid, reset_less=True)
49 else:
50 self.op = opkls(pspec)
51
52 def eq(self, i):
53 ret = [self.muxid.eq(i.muxid)]
54 ret.append(self.op.eq(i.op))
55 # don't forget to update matches if you add fields later.
56 return ret
57
58 def matches(self, another):
59 """
60 Returns a list of Assert()s validating that this context
61 matches the other context.
62 """
63 # I couldn't figure a clean way of overloading the == operator.
64 return [
65 Assert(self.muxid == another.muxid),
66 Assert(self.op == another.op),
67 ]
68
69 def __iter__(self):
70 yield self.muxid
71 yield self.op
72
73 def ports(self):
74 if hasattr(self.op, "ports"):
75 return [self.muxid] + self.op.ports()
76 else:
77 return list(self)
78
79
80 class InMuxPipe(PriorityCombMuxInPipe):
81 def __init__(self, num_rows, iospecfn, maskwid=0):
82 self.num_rows = num_rows
83 stage = PassThroughStage(iospecfn)
84 PriorityCombMuxInPipe.__init__(self, stage, p_len=self.num_rows,
85 maskwid=maskwid)
86
87
88 class MuxOutPipe(CombMuxOutPipe):
89 def __init__(self, num_rows, iospecfn, maskwid=0):
90 self.num_rows = num_rows
91 stage = PassThroughStage(iospecfn)
92 CombMuxOutPipe.__init__(self, stage, n_len=self.num_rows,
93 maskwid=maskwid)
94
95
96 class ALUProxy:
97 """ALUProxy: create a series of ALUs that look like the ALU being
98 sandwiched in between the fan-in and fan-out. One ALU looks like
99 it is multiple concurrent ALUs
100 """
101 def __init__(self, alu, p, n):
102 self.alu = alu
103 self.p = p
104 self.n = n
105
106
107 class ReservationStations(Elaboratable):
108 """ Reservation-Station pipeline
109
110 Input: num_rows - number of input and output Reservation Stations
111
112 Requires: the addition of an "alu" object, from which ispec and ospec
113 are taken, and inpipe and outpipe are connected to it
114
115 * fan-in on inputs (an array of BaseData: a,b,mid)
116 * ALU pipeline
117 * fan-out on outputs (an array of FPPackData: z,mid)
118
119 Fan-in and Fan-out are combinatorial.
120 """
121 def __init__(self, num_rows, maskwid=0, feedback_width=None):
122 self.num_rows = nr = num_rows
123 self.feedback_width = feedback_width
124 self.inpipe = InMuxPipe(nr, self.i_specfn, maskwid) # fan-in
125 self.outpipe = MuxOutPipe(nr, self.o_specfn, maskwid) # fan-out
126
127 self.p = self.inpipe.p # kinda annoying,
128 self.n = self.outpipe.n # use pipe in/out as this class in/out
129 self._ports = self.inpipe.ports() + self.outpipe.ports()
130
131 def setup_pseudoalus(self):
132 """setup_pseudoalus: establishes a suite of pseudo-alus
133 that look to all pipeline-intents-and-purposes just like the original
134 """
135 self.pseudoalus = []
136 for i in range(self.num_rows):
137 self.pseudoalus.append(ALUProxy(self.alu, self.p[i], self.n[i]))
138
139 def elaborate(self, platform):
140 m = Module()
141 m.submodules.inpipe = self.inpipe
142 m.submodules.alu = self.alu
143 m.submodules.outpipe = self.outpipe
144
145 m.d.comb += self.inpipe.n.connect_to_next(self.alu.p)
146 m.d.comb += self.alu.connect_to_next(self.outpipe)
147
148 if self.feedback_width is None:
149 return m
150
151 # connect all outputs above the feedback width back to their inputs
152 # (hence, feedback). pipeline stages are then expected to *modify*
153 # the muxid (with care) in order to use the "upper numbered" RSes
154 # for storing partially-completed results. micro-coding, basically
155
156 for i in range(self.feedback_width, self.num_rows):
157 self.outpipe.n[i].connect_to_next(self.inpipe.p[i])
158
159 return m
160
161 def ports(self):
162 return self._ports
163
164 def i_specfn(self):
165 return self.alu.ispec()
166
167 def o_specfn(self):
168 return self.alu.ospec()