add deduped
[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 ReservationStations(Elaboratable):
97 """ Reservation-Station pipeline
98
99 Input: num_rows - number of input and output Reservation Stations
100
101 Requires: the addition of an "alu" object, from which ispec and ospec
102 are taken, and inpipe and outpipe are connected to it
103
104 * fan-in on inputs (an array of BaseData: a,b,mid)
105 * ALU pipeline
106 * fan-out on outputs (an array of FPPackData: z,mid)
107
108 Fan-in and Fan-out are combinatorial.
109 """
110 def __init__(self, num_rows, maskwid=0, feedback_width=None):
111 self.num_rows = nr = num_rows
112 self.feedback_width = feedback_width
113 self.inpipe = InMuxPipe(nr, self.i_specfn, maskwid) # fan-in
114 self.outpipe = MuxOutPipe(nr, self.o_specfn, maskwid) # fan-out
115
116 self.p = self.inpipe.p # kinda annoying,
117 self.n = self.outpipe.n # use pipe in/out as this class in/out
118 self._ports = self.inpipe.ports() + self.outpipe.ports()
119
120 def elaborate(self, platform):
121 m = Module()
122 m.submodules.inpipe = self.inpipe
123 m.submodules.alu = self.alu
124 m.submodules.outpipe = self.outpipe
125
126 m.d.comb += self.inpipe.n.connect_to_next(self.alu.p)
127 m.d.comb += self.alu.connect_to_next(self.outpipe)
128
129 if self.feedback_width is None:
130 return m
131
132 # connect all outputs above the feedback width back to their inputs
133 # (hence, feedback). pipeline stages are then expected to *modify*
134 # the muxid (with care) in order to use the "upper numbered" RSes
135 # for storing partially-completed results. micro-coding, basically
136
137 for i in range(self.feedback_width, self.num_rows):
138 self.outpipe.n[i].connect_to_next(self.inpipe.p[i])
139
140 return m
141
142 def ports(self):
143 return self._ports
144
145 def i_specfn(self):
146 return self.alu.ispec()
147
148 def o_specfn(self):
149 return self.alu.ospec()