rather big change to interaction between regfile and compunits on read
[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.back.pysim import Settle
22 from nmigen.cli import verilog, rtlil
23
24 from nmigen import Cat, Const, Array, Signal, Elaboratable, Module
25 from nmutil.iocontrol import RecordObject
26 from nmutil.util import treereduce
27 from nmigen.utils import log2_int
28 from nmigen import Memory
29
30 from math import log
31 import operator
32
33
34 class Register(Elaboratable):
35 def __init__(self, width, writethru=True, synced=True):
36 self.width = width
37 self.writethru = writethru
38 self.synced = synced
39 self._rdports = []
40 self._wrports = []
41
42 def read_port(self, name=None):
43 port = RecordObject([("ren", 1),
44 ("data_o", self.width)],
45 name=name)
46 self._rdports.append(port)
47 return port
48
49 def write_port(self, name=None):
50 port = RecordObject([("wen", 1),
51 ("data_i", self.width)],
52 name=name)
53 self._wrports.append(port)
54 return port
55
56 def elaborate(self, platform):
57 m = Module()
58 self.reg = reg = Signal(self.width, name="reg")
59
60 if self.synced:
61 domain = m.d.sync
62 else:
63 domain = m.d.comb
64
65 # read ports. has write-through detection (returns data written)
66 for rp in self._rdports:
67 domain += rp.data_o.eq(0)
68 with m.If(rp.ren):
69 if self.writethru:
70 wr_detect = Signal(reset_less=False)
71 m.d.comb += wr_detect.eq(0)
72 for wp in self._wrports:
73 with m.If(wp.wen):
74 domain += rp.data_o.eq(wp.data_i)
75 m.d.comb += wr_detect.eq(1)
76 with m.If(~wr_detect):
77 domain += rp.data_o.eq(reg)
78 else:
79 domain += rp.data_o.eq(reg)
80
81 # write ports, delayed by 1 cycle
82 for wp in self._wrports:
83 with m.If(wp.wen):
84 m.d.sync += reg.eq(wp.data_i)
85
86 return m
87
88 def __iter__(self):
89 for p in self._rdports:
90 yield from p
91 for p in self._wrports:
92 yield from p
93
94 def ports(self):
95 res = list(self)
96
97
98 def ortreereduce(tree, attr="data_o"):
99 return treereduce(tree, operator.or_, lambda x: getattr(x, attr))
100
101
102 class RegFileArray(Elaboratable):
103 unary = True
104 """ an array-based register file (register having write-through capability)
105 that has no "address" decoder, instead it has individual write-en
106 and read-en signals (per port).
107 """
108
109 def __init__(self, width, depth, synced=True):
110 self.synced = synced
111 self.width = width
112 self.depth = depth
113 self.regs = Array(Register(width, synced=synced) \
114 for _ in range(self.depth))
115 self._rdports = []
116 self._wrports = []
117
118 def read_reg_port(self, name=None):
119 regs = []
120 for i in range(self.depth):
121 port = self.regs[i].read_port("%s%d" % (name, i))
122 regs.append(port)
123 return regs
124
125 def write_reg_port(self, name=None):
126 regs = []
127 for i in range(self.depth):
128 port = self.regs[i].write_port("%s%d" % (name, i))
129 regs.append(port)
130 return regs
131
132 def read_port(self, name=None):
133 regs = self.read_reg_port(name)
134 regs = Array(regs)
135 port = RecordObject([("ren", self.depth),
136 ("data_o", self.width)], name)
137 self._rdports.append((regs, port))
138 return port
139
140 def write_port(self, name=None):
141 regs = self.write_reg_port(name)
142 regs = Array(regs)
143 port = RecordObject([("wen", self.depth),
144 ("data_i", self.width)])
145 self._wrports.append((regs, port))
146 return port
147
148 def _get_en_sig(self, port, typ):
149 wen = []
150 for p in port:
151 wen.append(p[typ])
152 return Cat(*wen)
153
154 def elaborate(self, platform):
155 m = Module()
156 for i, reg in enumerate(self.regs):
157 setattr(m.submodules, "reg_%d" % i, reg)
158
159 if self.synced:
160 domain = m.d.sync
161 else:
162 domain = m.d.comb
163
164 for (regs, p) in self._rdports:
165 #print (p)
166 m.d.comb += self._get_en_sig(regs, 'ren').eq(p.ren)
167 ror = ortreereduce(list(regs))
168 if self.synced:
169 ren_delay = Signal.like(p.ren)
170 m.d.sync += ren_delay.eq(p.ren)
171 with m.If(ren_delay):
172 m.d.comb += p.data_o.eq(ror)
173 else:
174 m.d.comb += p.data_o.eq(ror)
175 for (regs, p) in self._wrports:
176 m.d.comb += self._get_en_sig(regs, 'wen').eq(p.wen)
177 for r in regs:
178 m.d.comb += r.data_i.eq(p.data_i)
179
180 return m
181
182 def __iter__(self):
183 for r in self.regs:
184 yield from r
185
186 def ports(self):
187 return list(self)
188
189
190 class RegFileMem(Elaboratable):
191 unary = False
192 def __init__(self, width, depth, fwd_bus_mode=False, synced=True):
193 self.fwd_bus_mode = fwd_bus_mode
194 self.synced = synced
195 self.width, self.depth = width, depth
196 self.memory = Memory(width=width, depth=depth)
197 self._rdports = {}
198 self._wrports = {}
199
200 def read_port(self, name=None):
201 bsz = log2_int(self.depth, False)
202 port = RecordObject([("addr", bsz),
203 ("ren", 1),
204 ("data_o", self.width)], name=name)
205 if self.synced:
206 domain = "sync"
207 else:
208 domain = "comb"
209 self._rdports[name] = (port, self.memory.read_port(domain=domain))
210 return port
211
212 def write_port(self, name=None):
213 bsz = log2_int(self.depth, False)
214 port = RecordObject([("addr", bsz),
215 ("wen", 1),
216 ("data_i", self.width)], name=name)
217 self._wrports[name] = (port, self.memory.write_port())
218 return port
219
220 def elaborate(self, platform):
221 m = Module()
222 comb = m.d.comb
223
224 # read ports. has write-through detection (returns data written)
225 for name, (rp, rport) in self._rdports.items():
226 setattr(m.submodules, "rp_"+name, rport)
227 wr_detect = Signal(reset_less=False)
228 comb += rport.addr.eq(rp.addr)
229 if self.fwd_bus_mode:
230 with m.If(rp.ren):
231 m.d.comb += wr_detect.eq(0)
232 for _, (wp, wport) in self._wrports.items():
233 addrmatch = Signal(reset_less=False)
234 m.d.comb += addrmatch.eq(wp.addr == rp.addr)
235 with m.If(wp.wen & addrmatch):
236 m.d.comb += rp.data_o.eq(wp.data_i)
237 m.d.comb += wr_detect.eq(1)
238 with m.If(~wr_detect):
239 m.d.comb += rp.data_o.eq(rport.data)
240 else:
241 if self.synced:
242 ren_delay = Signal.like(rp.ren)
243 m.d.sync += ren_delay.eq(rp.ren)
244 with m.If(ren_delay):
245 m.d.comb += rp.data_o.eq(rport.data)
246 else:
247 m.d.comb += rp.data_o.eq(rport.data)
248
249 # write ports, delayed by one cycle (in the memory itself)
250 for name, (port, wp) in self._wrports.items():
251 setattr(m.submodules, "wp_"+name, wp)
252 comb += wp.addr.eq(port.addr)
253 comb += wp.en.eq(port.wen)
254 comb += wp.data.eq(port.data_i)
255
256 return m
257
258
259 class RegFile(Elaboratable):
260 unary = False
261 def __init__(self, width, depth):
262 self.width = width
263 self.depth = depth
264 self._rdports = []
265 self._wrports = []
266
267 def read_port(self, name=None):
268 bsz = int(log(self.width) / log(2))
269 port = RecordObject([("addr", bsz),
270 ("ren", 1),
271 ("data_o", self.width)], name=name)
272 self._rdports.append(port)
273 return port
274
275 def write_port(self, name=None):
276 bsz = int(log(self.width) / log(2))
277 port = RecordObject([("addr", bsz),
278 ("wen", 1),
279 ("data_i", self.width)], name=name)
280 self._wrports.append(port)
281 return port
282
283 def elaborate(self, platform):
284 m = Module()
285 bsz = int(log(self.width) / log(2))
286 regs = Array(Signal(self.width, name="reg") for _ in range(self.depth))
287
288 # read ports. has write-through detection (returns data written)
289 for rp in self._rdports:
290 wr_detect = Signal(reset_less=False)
291 with m.If(rp.ren):
292 m.d.comb += wr_detect.eq(0)
293 for wp in self._wrports:
294 addrmatch = Signal(reset_less=False)
295 m.d.comb += addrmatch.eq(wp.addr == rp.addr)
296 with m.If(wp.wen & addrmatch):
297 m.d.comb += rp.data_o.eq(wp.data_i)
298 m.d.comb += wr_detect.eq(1)
299 with m.If(~wr_detect):
300 m.d.comb += rp.data_o.eq(regs[rp.addr])
301
302 # write ports, delayed by one cycle
303 for wp in self._wrports:
304 with m.If(wp.wen):
305 m.d.sync += regs[wp.addr].eq(wp.data_i)
306
307 return m
308
309 def __iter__(self):
310 yield from self._rdports
311 yield from self._wrports
312
313 def ports(self):
314 res = list(self)
315 for r in res:
316 if isinstance(r, RecordObject):
317 yield from r
318 else:
319 yield r
320
321
322 def regfile_sim(dut, rp, wp):
323 yield wp.addr.eq(1)
324 yield wp.data_i.eq(2)
325 yield wp.wen.eq(1)
326 yield
327 yield wp.wen.eq(0)
328 yield wp.addr.eq(0)
329 yield
330 yield
331 yield rp.ren.eq(1)
332 yield rp.addr.eq(1)
333 yield Settle()
334 data = yield rp.data_o
335 print(data)
336 yield
337 data = yield rp.data_o
338 print(data)
339 yield
340 data2 = yield rp.data_o
341 print(data2)
342 assert data == 2
343 yield
344
345 yield wp.addr.eq(5)
346 yield rp.addr.eq(5)
347 yield rp.ren.eq(1)
348 yield wp.wen.eq(1)
349 yield wp.data_i.eq(6)
350 yield
351 data = yield rp.data_o
352 print(data)
353 assert data == 6
354 yield
355 yield wp.wen.eq(0)
356 yield rp.ren.eq(0)
357 yield
358 data = yield rp.data_o
359 print(data)
360 assert data == 0
361 yield
362 data = yield rp.data_o
363 print(data)
364
365
366 def regfile_array_sim(dut, rp1, rp2, wp, wp2):
367 print("regfile_array_sim")
368 yield wp.data_i.eq(2)
369 yield wp.wen.eq(1 << 1)
370 yield
371 yield wp.wen.eq(0)
372 yield rp1.ren.eq(1 << 1)
373 yield Settle()
374 data = yield rp1.data_o
375 print(data)
376 assert data == 2
377 yield
378
379 yield rp1.ren.eq(1 << 5)
380 yield rp2.ren.eq(1 << 1)
381 yield wp.wen.eq(1 << 5)
382 yield wp.data_i.eq(6)
383 yield Settle()
384 data = yield rp1.data_o
385 assert data == 6
386 print(data)
387 yield
388 yield wp.wen.eq(0)
389 yield rp1.ren.eq(0)
390 yield rp2.ren.eq(0)
391 yield Settle()
392 data1 = yield rp1.data_o
393 print(data1)
394 assert data1 == 0
395 data2 = yield rp2.data_o
396 print(data2)
397 assert data2 == 0
398
399 yield
400 data = yield rp1.data_o
401 print(data)
402 assert data == 0
403
404
405 def test_regfile():
406 dut = RegFile(32, 8)
407 rp = dut.read_port()
408 wp = dut.write_port()
409 vl = rtlil.convert(dut)#, ports=dut.ports())
410 with open("test_regfile.il", "w") as f:
411 f.write(vl)
412
413 run_simulation(dut, regfile_sim(dut, rp, wp), vcd_name='test_regfile.vcd')
414
415 dut = RegFileMem(32, 8, True, False)
416 rp = dut.read_port("rp1")
417 wp = dut.write_port("wp1")
418 vl = rtlil.convert(dut)#, ports=dut.ports())
419 with open("test_regmem.il", "w") as f:
420 f.write(vl)
421
422 run_simulation(dut, regfile_sim(dut, rp, wp), vcd_name='test_regmem.vcd')
423
424 dut = RegFileArray(32, 8, False)
425 rp1 = dut.read_port("read1")
426 rp2 = dut.read_port("read2")
427 wp = dut.write_port("write")
428 wp2 = dut.write_port("write2")
429 ports = dut.ports()
430 print("ports", ports)
431 vl = rtlil.convert(dut, ports=ports)
432 with open("test_regfile_array.il", "w") as f:
433 f.write(vl)
434
435 run_simulation(dut, regfile_array_sim(dut, rp1, rp2, wp, wp2),
436 vcd_name='test_regfile_array.vcd')
437
438
439 if __name__ == '__main__':
440 test_regfile()