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