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