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