convert ram tp modules
[soc.git] / src / regfile / regfile.py
1 from nmigen.compat.sim import run_simulation
2 from nmigen.cli import verilog, rtlil
3
4 from nmigen import Cat, Const, Array, Signal, Elaboratable, Module
5 from nmutil.iocontrol import RecordObject
6
7 from math import log
8 from functools import reduce
9 import operator
10
11
12 class Register(Elaboratable):
13 def __init__(self, width, writethru=True):
14 self.width = width
15 self.writethru = writethru
16 self._rdports = []
17 self._wrports = []
18
19 def read_port(self, name=None):
20 port = RecordObject([("ren", 1),
21 ("data_o", self.width)],
22 name=name)
23 self._rdports.append(port)
24 return port
25
26 def write_port(self, name=None):
27 port = RecordObject([("wen", 1),
28 ("data_i", self.width)],
29 name=name)
30 self._wrports.append(port)
31 return port
32
33 def elaborate(self, platform):
34 m = Module()
35 self.reg = reg = Signal(self.width, name="reg")
36
37 # read ports. has write-through detection (returns data written)
38 for rp in self._rdports:
39 with m.If(rp.ren):
40 if self.writethru:
41 wr_detect = Signal(reset_less=False)
42 m.d.comb += wr_detect.eq(0)
43 for wp in self._wrports:
44 with m.If(wp.wen):
45 m.d.comb += rp.data_o.eq(wp.data_i)
46 m.d.comb += wr_detect.eq(1)
47 with m.If(~wr_detect):
48 m.d.comb += rp.data_o.eq(reg)
49 else:
50 m.d.comb += rp.data_o.eq(reg)
51
52 # write ports, don't allow write to address 0 (ignore it)
53 for wp in self._wrports:
54 with m.If(wp.wen):
55 m.d.sync += reg.eq(wp.data_i)
56
57 return m
58
59 def __iter__(self):
60 for p in self._rdports:
61 yield from p
62 for p in self._wrports:
63 yield from p
64
65 def ports(self):
66 res = list(self)
67
68 def treereduce(tree, attr="data_o"):
69 #print ("treereduce", tree)
70 if not isinstance(tree, list):
71 return tree
72 if len(tree) == 1:
73 return getattr(tree[0], attr)
74 if len(tree) == 2:
75 return getattr(tree[0], attr) | getattr(tree[1], attr)
76 split = len(tree) // 2
77 return treereduce(tree[:split], attr) | treereduce(tree[split:], attr)
78
79
80 class RegFileArray(Elaboratable):
81 """ an array-based register file (register having write-through capability)
82 that has no "address" decoder, instead it has individual write-en
83 and read-en signals (per port).
84 """
85 def __init__(self, width, depth):
86 self.width = width
87 self.depth = depth
88 self.regs = Array(Register(width) for _ in range(self.depth))
89 self._rdports = []
90 self._wrports = []
91
92 def read_port(self, name=None):
93 regs = []
94 for i in range(self.depth):
95 port = self.regs[i].read_port(name)
96 regs.append(port)
97 regs = Array(regs)
98 port = RecordObject([("ren", self.depth),
99 ("data_o", self.width)], name)
100 self._rdports.append((regs, port))
101 return port
102
103 def write_port(self, name=None):
104 regs = []
105 for i in range(self.depth):
106 port = self.regs[i].write_port(name)
107 regs.append(port)
108 regs = Array(regs)
109 port = RecordObject([("wen", self.depth),
110 ("data_i", self.width)])
111 self._wrports.append((regs, port))
112 return port
113
114 def _get_en_sig(self, port, typ):
115 wen = []
116 for p in port:
117 wen.append(p[typ])
118 return Cat(*wen)
119
120 def elaborate(self, platform):
121 m = Module()
122 for i, reg in enumerate(self.regs):
123 setattr(m.submodules, "reg_%d" % i, reg)
124
125 for (regs, p) in self._rdports:
126 #print (p)
127 m.d.comb += self._get_en_sig(regs, 'ren').eq(p.ren)
128 ror = treereduce(list(regs))
129 m.d.comb += p.data_o.eq(ror)
130 for (regs, p) in self._wrports:
131 m.d.comb += self._get_en_sig(regs, 'wen').eq(p.wen)
132 for r in regs:
133 m.d.comb += r.data_i.eq(p.data_i)
134
135 return m
136
137 def __iter__(self):
138 for r in self.regs:
139 yield from r
140
141 def ports(self):
142 return list(self)
143
144
145 class RegFile(Elaboratable):
146 def __init__(self, width, depth):
147 self.width = width
148 self.depth = depth
149 self._rdports = []
150 self._wrports = []
151
152 def read_port(self):
153 bsz = int(log(self.width) / log(2))
154 port = RecordObject([("raddr", bsz),
155 ("ren", 1),
156 ("data_o", self.width)])
157 self._rdports.append(port)
158 return port
159
160 def write_port(self):
161 bsz = int(log(self.width) / log(2))
162 port = RecordObject([("waddr", bsz),
163 ("wen", 1),
164 ("data_i", self.width)])
165 self._wrports.append(port)
166 return port
167
168 def elaborate(self, platform):
169 m = Module()
170 bsz = int(log(self.width) / log(2))
171 regs = Array(Signal(self.width, name="reg") for _ in range(self.depth))
172
173 # read ports. has write-through detection (returns data written)
174 for rp in self._rdports:
175 wr_detect = Signal(reset_less=False)
176 with m.If(rp.ren):
177 m.d.comb += wr_detect.eq(0)
178 for wp in self._wrports:
179 addrmatch = Signal(reset_less=False)
180 m.d.comb += addrmatch.eq(wp.waddr == rp.raddr)
181 with m.If(wp.wen & addrmatch):
182 m.d.comb += rp.data_o.eq(wp.data_i)
183 m.d.comb += wr_detect.eq(1)
184 with m.If(~wr_detect):
185 m.d.comb += rp.data_o.eq(regs[rp.raddr])
186
187 # write ports, don't allow write to address 0 (ignore it)
188 for wp in self._wrports:
189 with m.If(wp.wen & (wp.waddr != Const(0, bsz))):
190 m.d.sync += regs[wp.waddr].eq(wp.data_i)
191
192 return m
193
194 def __iter__(self):
195 yield from self._rdports
196 yield from self._wrports
197
198 def ports(self):
199 res = list(self)
200 for r in res:
201 if isinstance(r, RecordObject):
202 yield from r
203 else:
204 yield r
205
206 def regfile_sim(dut, rp, wp):
207 yield wp.waddr.eq(1)
208 yield wp.data_i.eq(2)
209 yield wp.wen.eq(1)
210 yield
211 yield wp.wen.eq(0)
212 yield rp.ren.eq(1)
213 yield rp.raddr.eq(1)
214 yield
215 data = yield rp.data_o
216 print (data)
217 assert data == 2
218
219 yield wp.waddr.eq(5)
220 yield rp.raddr.eq(5)
221 yield rp.ren.eq(1)
222 yield wp.wen.eq(1)
223 yield wp.data_i.eq(6)
224 data = yield rp.data_o
225 print (data)
226 yield
227 yield wp.wen.eq(0)
228 yield rp.ren.eq(0)
229 data = yield rp.data_o
230 print (data)
231 assert data == 6
232 yield
233 data = yield rp.data_o
234 print (data)
235
236 def regfile_array_sim(dut, rp1, rp2, wp):
237 yield wp.data_i.eq(2)
238 yield wp.wen.eq(1<<1)
239 yield
240 yield wp.wen.eq(0)
241 yield rp1.ren.eq(1<<1)
242 yield
243 data = yield rp1.data_o
244 print (data)
245 assert data == 2
246
247 yield rp1.ren.eq(1<<5)
248 yield rp2.ren.eq(1<<1)
249 yield wp.wen.eq(1<<5)
250 yield wp.data_i.eq(6)
251 data = yield rp1.data_o
252 print (data)
253 yield
254 yield wp.wen.eq(0)
255 yield rp1.ren.eq(0)
256 yield rp2.ren.eq(0)
257 data1 = yield rp1.data_o
258 print (data1)
259 data2 = yield rp2.data_o
260 print (data2)
261 assert data1 == 6
262 yield
263 data = yield rp1.data_o
264 print (data)
265
266 def test_regfile():
267 dut = RegFile(32, 8)
268 rp = dut.read_port()
269 wp = dut.write_port()
270 vl = rtlil.convert(dut, ports=dut.ports())
271 with open("test_regfile.il", "w") as f:
272 f.write(vl)
273
274 run_simulation(dut, regfile_sim(dut, rp, wp), vcd_name='test_regfile.vcd')
275
276 dut = RegFileArray(32, 8)
277 rp1 = dut.read_port("read1")
278 rp2 = dut.read_port("read2")
279 wp = dut.write_port("write")
280 ports=dut.ports()
281 print ("ports", ports)
282 vl = rtlil.convert(dut, ports=ports)
283 with open("test_regfile_array.il", "w") as f:
284 f.write(vl)
285
286 run_simulation(dut, regfile_array_sim(dut, rp1, rp2, wp),
287 vcd_name='test_regfile_array.vcd')
288
289 if __name__ == '__main__':
290 test_regfile()