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