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