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