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