get score6600_multi.py working again
[soc.git] / src / soc / regfile / regfile.py
1 """Specialist Regfiles
2
3 These are not "normal" binary-indexed regfiles (although that is included).
4 They include *unary* indexed regfiles as well as Dependency-tracked ones
5 (SPR files with 1024 registers, only around 4-5 of which need to be active)
6 and special "split" regfiles that have 8R8W for 8 4-bit quantities and a
7 1R1W to read/write *all* 8 4-bit registers in a single one-off 32-bit way.
8
9 Due to the way that the Dependency Matrices are set up (bit-vectors), the
10 primary focus here is on *unary* indexing.
11
12 Links:
13
14 * https://libre-soc.org/3d_gpu/architecture/regfile
15 * https://bugs.libre-soc.org/show_bug.cgi?id=345
16 * https://bugs.libre-soc.org/show_bug.cgi?id=351
17 * https://bugs.libre-soc.org/show_bug.cgi?id=352
18 """
19
20 from nmigen.compat.sim import run_simulation
21 from nmigen.cli import verilog, rtlil
22
23 from nmigen import Cat, Const, Array, Signal, Elaboratable, Module
24 from nmutil.iocontrol import RecordObject
25 from nmutil.util import treereduce
26
27 from math import log
28 import operator
29
30
31 class Register(Elaboratable):
32 def __init__(self, width, writethru=True):
33 self.width = width
34 self.writethru = writethru
35 self._rdports = []
36 self._wrports = []
37
38 def read_port(self, name=None):
39 port = RecordObject([("ren", 1),
40 ("data_o", self.width)],
41 name=name)
42 self._rdports.append(port)
43 return port
44
45 def write_port(self, name=None):
46 port = RecordObject([("wen", 1),
47 ("data_i", self.width)],
48 name=name)
49 self._wrports.append(port)
50 return port
51
52 def elaborate(self, platform):
53 m = Module()
54 self.reg = reg = Signal(self.width, name="reg")
55
56 # read ports. has write-through detection (returns data written)
57 for rp in self._rdports:
58 with m.If(rp.ren):
59 if self.writethru:
60 wr_detect = Signal(reset_less=False)
61 m.d.comb += wr_detect.eq(0)
62 for wp in self._wrports:
63 with m.If(wp.wen):
64 m.d.comb += rp.data_o.eq(wp.data_i)
65 m.d.comb += wr_detect.eq(1)
66 with m.If(~wr_detect):
67 m.d.comb += rp.data_o.eq(reg)
68 else:
69 m.d.comb += rp.data_o.eq(reg)
70
71 # write ports, don't allow write to address 0 (ignore it)
72 for wp in self._wrports:
73 with m.If(wp.wen):
74 m.d.sync += reg.eq(wp.data_i)
75
76 return m
77
78 def __iter__(self):
79 for p in self._rdports:
80 yield from p
81 for p in self._wrports:
82 yield from p
83
84 def ports(self):
85 res = list(self)
86
87 def ortreereduce(tree, attr="data_o"):
88 return treereduce(tree, operator.or_, lambda x: getattr(x, attr))
89
90
91 class RegFileArray(Elaboratable):
92 """ an array-based register file (register having write-through capability)
93 that has no "address" decoder, instead it has individual write-en
94 and read-en signals (per port).
95 """
96 def __init__(self, width, depth):
97 self.width = width
98 self.depth = depth
99 self.regs = Array(Register(width) for _ in range(self.depth))
100 self._rdports = []
101 self._wrports = []
102
103 def read_reg_port(self, name=None):
104 regs = []
105 for i in range(self.depth):
106 port = self.regs[i].read_port("%s%d" % (name, i))
107 regs.append(port)
108 return regs
109
110 def write_reg_port(self, name=None):
111 regs = []
112 for i in range(self.depth):
113 port = self.regs[i].write_port("%s%d" % (name, i))
114 regs.append(port)
115 return regs
116
117 def read_port(self, name=None):
118 regs = self.read_reg_port(name)
119 regs = Array(regs)
120 port = RecordObject([("ren", self.depth),
121 ("data_o", self.width)], name)
122 self._rdports.append((regs, port))
123 return port
124
125 def write_port(self, name=None):
126 regs = self.write_reg_port(name)
127 regs = Array(regs)
128 port = RecordObject([("wen", self.depth),
129 ("data_i", self.width)])
130 self._wrports.append((regs, port))
131 return port
132
133 def _get_en_sig(self, port, typ):
134 wen = []
135 for p in port:
136 wen.append(p[typ])
137 return Cat(*wen)
138
139 def elaborate(self, platform):
140 m = Module()
141 for i, reg in enumerate(self.regs):
142 setattr(m.submodules, "reg_%d" % i, reg)
143
144 for (regs, p) in self._rdports:
145 #print (p)
146 m.d.comb += self._get_en_sig(regs, 'ren').eq(p.ren)
147 ror = ortreereduce(list(regs))
148 m.d.comb += p.data_o.eq(ror)
149 for (regs, p) in self._wrports:
150 m.d.comb += self._get_en_sig(regs, 'wen').eq(p.wen)
151 for r in regs:
152 m.d.comb += r.data_i.eq(p.data_i)
153
154 return m
155
156 def __iter__(self):
157 for r in self.regs:
158 yield from r
159
160 def ports(self):
161 return list(self)
162
163
164 class RegFile(Elaboratable):
165 def __init__(self, width, depth):
166 self.width = width
167 self.depth = depth
168 self._rdports = []
169 self._wrports = []
170
171 def read_port(self):
172 bsz = int(log(self.width) / log(2))
173 port = RecordObject([("raddr", bsz),
174 ("ren", 1),
175 ("data_o", self.width)])
176 self._rdports.append(port)
177 return port
178
179 def write_port(self):
180 bsz = int(log(self.width) / log(2))
181 port = RecordObject([("waddr", bsz),
182 ("wen", 1),
183 ("data_i", self.width)])
184 self._wrports.append(port)
185 return port
186
187 def elaborate(self, platform):
188 m = Module()
189 bsz = int(log(self.width) / log(2))
190 regs = Array(Signal(self.width, name="reg") for _ in range(self.depth))
191
192 # read ports. has write-through detection (returns data written)
193 for rp in self._rdports:
194 wr_detect = Signal(reset_less=False)
195 with m.If(rp.ren):
196 m.d.comb += wr_detect.eq(0)
197 for wp in self._wrports:
198 addrmatch = Signal(reset_less=False)
199 m.d.comb += addrmatch.eq(wp.waddr == rp.raddr)
200 with m.If(wp.wen & addrmatch):
201 m.d.comb += rp.data_o.eq(wp.data_i)
202 m.d.comb += wr_detect.eq(1)
203 with m.If(~wr_detect):
204 m.d.comb += rp.data_o.eq(regs[rp.raddr])
205
206 # write ports, don't allow write to address 0 (ignore it)
207 for wp in self._wrports:
208 with m.If(wp.wen & (wp.waddr != Const(0, bsz))):
209 m.d.sync += regs[wp.waddr].eq(wp.data_i)
210
211 return m
212
213 def __iter__(self):
214 yield from self._rdports
215 yield from self._wrports
216
217 def ports(self):
218 res = list(self)
219 for r in res:
220 if isinstance(r, RecordObject):
221 yield from r
222 else:
223 yield r
224
225 def regfile_sim(dut, rp, wp):
226 yield wp.waddr.eq(1)
227 yield wp.data_i.eq(2)
228 yield wp.wen.eq(1)
229 yield
230 yield wp.wen.eq(0)
231 yield rp.ren.eq(1)
232 yield rp.raddr.eq(1)
233 yield
234 data = yield rp.data_o
235 print (data)
236 assert data == 2
237
238 yield wp.waddr.eq(5)
239 yield rp.raddr.eq(5)
240 yield rp.ren.eq(1)
241 yield wp.wen.eq(1)
242 yield wp.data_i.eq(6)
243 data = yield rp.data_o
244 print (data)
245 yield
246 yield wp.wen.eq(0)
247 yield rp.ren.eq(0)
248 data = yield rp.data_o
249 print (data)
250 assert data == 6
251 yield
252 data = yield rp.data_o
253 print (data)
254
255 def regfile_array_sim(dut, rp1, rp2, wp):
256 yield wp.data_i.eq(2)
257 yield wp.wen.eq(1<<1)
258 yield
259 yield wp.wen.eq(0)
260 yield rp1.ren.eq(1<<1)
261 yield
262 data = yield rp1.data_o
263 print (data)
264 assert data == 2
265
266 yield rp1.ren.eq(1<<5)
267 yield rp2.ren.eq(1<<1)
268 yield wp.wen.eq(1<<5)
269 yield wp.data_i.eq(6)
270 data = yield rp1.data_o
271 print (data)
272 yield
273 yield wp.wen.eq(0)
274 yield rp1.ren.eq(0)
275 yield rp2.ren.eq(0)
276 data1 = yield rp1.data_o
277 print (data1)
278 data2 = yield rp2.data_o
279 print (data2)
280 assert data1 == 6
281 yield
282 data = yield rp1.data_o
283 print (data)
284
285 def test_regfile():
286 dut = RegFile(32, 8)
287 rp = dut.read_port()
288 wp = dut.write_port()
289 vl = rtlil.convert(dut, ports=dut.ports())
290 with open("test_regfile.il", "w") as f:
291 f.write(vl)
292
293 run_simulation(dut, regfile_sim(dut, rp, wp), vcd_name='test_regfile.vcd')
294
295 dut = RegFileArray(32, 8)
296 rp1 = dut.read_port("read1")
297 rp2 = dut.read_port("read2")
298 wp = dut.write_port("write")
299 ports=dut.ports()
300 print ("ports", ports)
301 vl = rtlil.convert(dut, ports=ports)
302 with open("test_regfile_array.il", "w") as f:
303 f.write(vl)
304
305 run_simulation(dut, regfile_array_sim(dut, rp1, rp2, wp),
306 vcd_name='test_regfile_array.vcd')
307
308 if __name__ == '__main__':
309 test_regfile()