connect read-enable and src_i to regfile ports
[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 def regfile_sim(dut, rp, wp):
229 yield wp.waddr.eq(1)
230 yield wp.data_i.eq(2)
231 yield wp.wen.eq(1)
232 yield
233 yield wp.wen.eq(0)
234 yield rp.ren.eq(1)
235 yield rp.raddr.eq(1)
236 yield Settle()
237 data = yield rp.data_o
238 print (data)
239 assert data == 2
240 yield
241
242 yield wp.waddr.eq(5)
243 yield rp.raddr.eq(5)
244 yield rp.ren.eq(1)
245 yield wp.wen.eq(1)
246 yield wp.data_i.eq(6)
247 yield Settle()
248 data = yield rp.data_o
249 print (data)
250 assert data == 6
251 yield
252 yield wp.wen.eq(0)
253 yield rp.ren.eq(0)
254 yield Settle()
255 data = yield rp.data_o
256 print (data)
257 assert data == 0
258 yield
259 data = yield rp.data_o
260 print (data)
261
262 def regfile_array_sim(dut, rp1, rp2, wp):
263 yield wp.data_i.eq(2)
264 yield wp.wen.eq(1<<1)
265 yield
266 yield wp.wen.eq(0)
267 yield rp1.ren.eq(1<<1)
268 yield Settle()
269 data = yield rp1.data_o
270 print (data)
271 assert data == 2
272 yield
273
274 yield rp1.ren.eq(1<<5)
275 yield rp2.ren.eq(1<<1)
276 yield wp.wen.eq(1<<5)
277 yield wp.data_i.eq(6)
278 yield Settle()
279 data = yield rp1.data_o
280 assert data == 6
281 print (data)
282 yield
283 yield wp.wen.eq(0)
284 yield rp1.ren.eq(0)
285 yield rp2.ren.eq(0)
286 yield Settle()
287 data1 = yield rp1.data_o
288 print (data1)
289 assert data1 == 0
290 data2 = yield rp2.data_o
291 print (data2)
292 assert data2 == 0
293
294 yield
295 data = yield rp1.data_o
296 print (data)
297 assert data == 0
298
299 def test_regfile():
300 dut = RegFile(32, 8)
301 rp = dut.read_port()
302 wp = dut.write_port()
303 vl = rtlil.convert(dut, ports=dut.ports())
304 with open("test_regfile.il", "w") as f:
305 f.write(vl)
306
307 run_simulation(dut, regfile_sim(dut, rp, wp), vcd_name='test_regfile.vcd')
308
309 dut = RegFileArray(32, 8)
310 rp1 = dut.read_port("read1")
311 rp2 = dut.read_port("read2")
312 wp = dut.write_port("write")
313 ports=dut.ports()
314 print ("ports", ports)
315 vl = rtlil.convert(dut, ports=ports)
316 with open("test_regfile_array.il", "w") as f:
317 f.write(vl)
318
319 run_simulation(dut, regfile_array_sim(dut, rp1, rp2, wp),
320 vcd_name='test_regfile_array.vcd')
321
322 if __name__ == '__main__':
323 test_regfile()