51c920e2622446c03440e7d49a443b938dbd236c
[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 Const, Array, Signal, Elaboratable, Module
5 from nmutil.iocontrol import RecordObject
6
7 from math import log
8
9
10 class Register(Elaboratable):
11 def __init__(self, width):
12 self.width = width
13 self._rdports = []
14 self._wrports = []
15
16 def read_port(self, name=None):
17 port = RecordObject([("ren", 1),
18 ("data_o", self.width)],
19 name=name)
20 self._rdports.append(port)
21 return port
22
23 def write_port(self, name=None):
24 port = RecordObject([("wen", 1),
25 ("data_i", self.width)],
26 name=name)
27 self._wrports.append(port)
28 return port
29
30 def elaborate(self, platform):
31 m = Module()
32 reg = Signal(self.width, name="reg")
33
34 # read ports. has write-through detection (returns data written)
35 for rp in self._rdports:
36 wr_detect = Signal(reset_less=False)
37 with m.If(rp.ren):
38 m.d.comb += wr_detect.eq(0)
39 for wp in self._wrports:
40 with m.If(wp.wen):
41 m.d.comb += rp.data_o.eq(wp.data_i)
42 m.d.comb += wr_detect.eq(1)
43 with m.If(~wr_detect):
44 m.d.comb += rp.data_o.eq(reg)
45
46 # write ports, don't allow write to address 0 (ignore it)
47 for wp in self._wrports:
48 with m.If(wp.wen):
49 m.d.sync += reg.eq(wp.data_i)
50
51 return m
52
53 def __iter__(self):
54 for p in self._rdports:
55 yield from p
56 for p in self._wrports:
57 yield from p
58
59 def ports(self):
60 res = list(self)
61
62
63 class RegFileArray(Elaboratable):
64 """ an array-based register file (register having write-through capability)
65 that has no "address" decoder, instead it has individual write-en
66 and read-en signals (per port).
67 """
68 def __init__(self, width, depth):
69 self.width = width
70 self.depth = depth
71 self.regs = Array(Register(width) for _ in range(self.depth))
72 self._rdports = []
73 self._wrports = []
74
75 def read_port(self, name=None):
76 regs = []
77 for i in range(self.depth):
78 port = self.regs[i].read_port(name)
79 regs.append(port)
80 regs = Array(regs)
81 self._rdports.append(regs)
82 return regs
83
84 def write_port(self, name=None):
85 regs = []
86 for i in range(self.depth):
87 port = self.regs[i].write_port(name)
88 regs.append(port)
89 regs = Array(regs)
90 self._wrports.append(regs)
91 return regs
92
93 def elaborate(self, platform):
94 m = Module()
95 for i, reg in enumerate(self.regs):
96 setattr(m.submodules, "reg_%d" % i, reg)
97 return m
98
99 def __iter__(self):
100 for r in self.regs:
101 yield from r
102
103 def ports(self):
104 return list(self)
105
106
107 class RegFile(Elaboratable):
108 def __init__(self, width, depth):
109 self.width = width
110 self.depth = depth
111 self._rdports = []
112 self._wrports = []
113
114 def read_port(self):
115 bsz = int(log(self.width) / log(2))
116 port = RecordObject([("raddr", bsz),
117 ("ren", 1),
118 ("data_o", self.width)])
119 self._rdports.append(port)
120 return port
121
122 def write_port(self):
123 bsz = int(log(self.width) / log(2))
124 port = RecordObject([("waddr", bsz),
125 ("wen", 1),
126 ("data_i", self.width)])
127 self._wrports.append(port)
128 return port
129
130 def elaborate(self, platform):
131 m = Module()
132 bsz = int(log(self.width) / log(2))
133 regs = Array(Signal(self.width, name="reg") for _ in range(self.depth))
134
135 # read ports. has write-through detection (returns data written)
136 for rp in self._rdports:
137 wr_detect = Signal(reset_less=False)
138 with m.If(rp.ren):
139 m.d.comb += wr_detect.eq(0)
140 for wp in self._wrports:
141 addrmatch = Signal(reset_less=False)
142 m.d.comb += addrmatch.eq(wp.waddr == rp.raddr)
143 with m.If(wp.wen & addrmatch):
144 m.d.comb += rp.data_o.eq(wp.data_i)
145 m.d.comb += wr_detect.eq(1)
146 with m.If(~wr_detect):
147 m.d.comb += rp.data_o.eq(regs[rp.raddr])
148
149 # write ports, don't allow write to address 0 (ignore it)
150 for wp in self._wrports:
151 with m.If(wp.wen & (wp.waddr != Const(0, bsz))):
152 m.d.sync += regs[wp.waddr].eq(wp.data_i)
153
154 return m
155
156 def __iter__(self):
157 yield from self._rdports
158 yield from self._wrports
159
160 def ports(self):
161 res = list(self)
162 for r in res:
163 if isinstance(r, RecordObject):
164 yield from r
165 else:
166 yield r
167
168 def regfile_sim(dut, rp, wp):
169 yield wp.waddr.eq(1)
170 yield wp.data_i.eq(2)
171 yield wp.wen.eq(1)
172 yield
173 yield wp.wen.eq(0)
174 yield rp.ren.eq(1)
175 yield rp.raddr.eq(1)
176 yield
177 data = yield rp.data_o
178 print (data)
179 assert data == 2
180
181 yield wp.waddr.eq(5)
182 yield rp.raddr.eq(5)
183 yield rp.ren.eq(1)
184 yield wp.wen.eq(1)
185 yield wp.data_i.eq(6)
186 data = yield rp.data_o
187 print (data)
188 yield
189 yield wp.wen.eq(0)
190 yield rp.ren.eq(0)
191 data = yield rp.data_o
192 print (data)
193 assert data == 6
194 yield
195 data = yield rp.data_o
196 print (data)
197
198 def test_regfile():
199 dut = RegFile(32, 8)
200 rp = dut.read_port()
201 wp = dut.write_port()
202 vl = rtlil.convert(dut, ports=dut.ports())
203 with open("test_regfile.il", "w") as f:
204 f.write(vl)
205
206 run_simulation(dut, regfile_sim(dut, rp, wp), vcd_name='test_regfile.vcd')
207
208 dut = RegFileArray(32, 8)
209 rp = dut.read_port()
210 wp = dut.write_port()
211 ports=dut.ports()
212 print ("ports", ports)
213 vl = rtlil.convert(dut, ports=ports)
214 with open("test_regfile_array.il", "w") as f:
215 f.write(vl)
216
217 #run_simulation(dut, regfile_sim(dut, rp, wp), vcd_name='test_regfile.vcd')
218
219 if __name__ == '__main__':
220 test_regfile()