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