Allow the formal engine to perform a same-cycle result in the ALU
[soc.git] / src / soc / regfile / virtual_port.py
1 """VirtualRegPort - terrible name for a complex register class
2
3 This Register file has a "virtual" port on it which is effectively
4 the ability to read and write to absolutely every bit in the regfile
5 at once. This is achieved by having N actual read and write ports
6 if there are N registers. That results in a staggeringly high gate count
7 with full crossbars, so attempting to do use this for anything other
8 than really small registers (XER, CR) is a seriously bad idea.
9 """
10
11 from nmigen.compat.sim import run_simulation
12 from nmigen.cli import verilog, rtlil
13
14 from nmigen import Cat, Const, Array, Signal, Elaboratable, Module
15 from nmutil.iocontrol import RecordObject
16
17 from soc.regfile.regfile import RegFileArray
18
19
20 class VirtualRegPort(RegFileArray):
21 def __init__(self, bitwidth, n_regs, rd2=False, wr2=False, synced=True):
22 self.bitwidth = bitwidth
23 self.nregs = n_regs
24 self.rd2 = rd2 # eurgh hack
25 self.wr2 = wr2 # eurgh hack
26 self.regwidth = regwidth = bitwidth // n_regs
27 super().__init__(self.regwidth, n_regs, synced=synced)
28
29 # "full" depth variant of the "external" port
30 self.full_wr = RecordObject([("wen", n_regs),
31 ("i_data", bitwidth)], # *full* wid
32 name="full_wr")
33 self.full_rd = RecordObject([("ren", n_regs),
34 ("o_data", bitwidth)], # *full* wid
35 name="full_rd")
36 if wr2:
37 self.full_wr2 = RecordObject([("wen", n_regs),
38 ("i_data", bitwidth)], # *full* wid
39 name="full_wr2")
40 if rd2:
41 self.full_rd2 = RecordObject([("ren", n_regs),
42 ("o_data", bitwidth)], # *full* wid
43 name="full_rd2")
44
45 def connect_full_wr(self, m, wfull, name):
46 comb = m.d.comb
47 wr_regs = self.write_reg_port(name)
48
49 # wire up the enable signals from the large (full) port
50 l = map(lambda port: port.i_data, wr_regs)
51 le = map(lambda port: port.wen, wr_regs) # get port wen(s)
52
53 # get list of all i_data (and wens) and assign to them via Cat
54 comb += Cat(*l).eq(wfull.i_data)
55 comb += Cat(*le).eq(wfull.wen)
56
57 def connect_full_rd(self, m, rfull, name):
58 comb = m.d.comb
59 rd_regs = self.read_reg_port(name)
60
61 # wire up the enable signals and chain-accumulate the data
62 l = map(lambda port: port.o_data, rd_regs) # get port data(s)
63 le = map(lambda port: port.ren, rd_regs) # get port ren(s)
64
65 comb += rfull.o_data.eq(Cat(*l)) # we like Cat on lists
66 comb += Cat(*le).eq(rfull.ren)
67
68 def elaborate(self, platform):
69 m = super().elaborate(platform)
70 comb = m.d.comb
71
72 # connect up full write port
73 self.connect_full_wr(m, self.full_wr, "w")
74 if self.wr2:
75 self.connect_full_wr(m, self.full_wr2, "w2")
76
77 # connect up full read port
78 self.connect_full_rd(m, self.full_rd, "r")
79 if self.rd2: # hack!
80 self.connect_full_rd(m, self.full_rd2, "r2")
81
82 return m
83
84 def __iter__(self):
85 yield from super().__iter__()
86 yield from self.full_wr
87 yield from self.full_rd
88
89
90 def regfile_array_sim(dut, rp1, rp2, rp3, wp):
91 # part-port write
92 yield wp.i_data.eq(2)
93 yield wp.wen.eq(1 << 1)
94 yield
95 yield wp.wen.eq(0)
96 # part-port read
97 yield rp1.ren.eq(1 << 1)
98 yield
99 data = yield rp1.o_data
100 print(data)
101 assert data == 2
102
103 # simultaneous read/write - should be pass-thru
104 yield rp1.ren.eq(1 << 5)
105 yield rp2.ren.eq(1 << 1)
106 yield wp.wen.eq(1 << 5)
107 yield wp.i_data.eq(6)
108 yield
109 yield wp.wen.eq(0)
110 yield rp1.ren.eq(0)
111 yield rp2.ren.eq(0)
112 data1 = yield rp1.o_data
113 print(data1)
114 assert data1 == 6, data1
115 data2 = yield rp2.o_data
116 print(data2)
117 assert data2 == 2, data2
118 yield
119 data = yield rp1.o_data
120 print(data)
121
122 # full port read (whole reg)
123 yield dut.full_rd.ren.eq(0xff)
124 yield
125 yield dut.full_rd.ren.eq(0)
126 data = yield dut.full_rd.o_data
127 print(hex(data))
128
129 # full port read (part reg)
130 yield dut.full_rd.ren.eq(0x1 << 5)
131 yield
132 yield dut.full_rd.ren.eq(0)
133 data = yield dut.full_rd.o_data
134 print(hex(data))
135
136 # full port part-write (part masked reg)
137 yield dut.full_wr.wen.eq(0x1 << 1)
138 yield dut.full_wr.i_data.eq(0xe0)
139 yield
140 yield dut.full_wr.wen.eq(0x0)
141
142 # full port read (whole reg)
143 yield dut.full_rd.ren.eq(0xff)
144 yield
145 yield dut.full_rd.ren.eq(0)
146 data = yield dut.full_rd.o_data
147 print(hex(data))
148
149 # full port write
150 yield dut.full_wr.wen.eq(0xff)
151 yield dut.full_wr.i_data.eq(0xcafeface)
152 yield
153 yield dut.full_wr.wen.eq(0x0)
154
155 # full port read (whole reg)
156 yield dut.full_rd.ren.eq(0xff)
157 yield
158 yield dut.full_rd.ren.eq(0)
159 data = yield dut.full_rd.o_data
160 print(hex(data))
161
162 # part write
163 yield wp.i_data.eq(2)
164 yield wp.wen.eq(1 << 1)
165 yield
166 yield wp.wen.eq(0)
167 yield rp1.ren.eq(1 << 1)
168 yield
169 data = yield rp1.o_data
170 print(hex(data))
171 assert data == 2
172
173 # full port read (whole reg)
174 yield dut.full_rd.ren.eq(0xff)
175 yield
176 yield dut.full_rd.ren.eq(0)
177 data = yield dut.full_rd.o_data
178 print(hex(data))
179
180 # simultaneous read/write: full-write, part-write, 3x part-read
181 yield rp1.ren.eq(1 << 5)
182 yield rp2.ren.eq(1 << 1)
183 yield rp3.ren.eq(1 << 3)
184 yield wp.wen.eq(1 << 3)
185 yield wp.i_data.eq(6)
186 yield dut.full_wr.wen.eq((1 << 1) | (1 << 5))
187 yield dut.full_wr.i_data.eq((0xa << (1*4)) | (0x3 << (5*4)))
188 yield
189 yield dut.full_wr.wen.eq(0)
190 yield wp.wen.eq(0)
191 yield rp1.ren.eq(0)
192 yield rp2.ren.eq(0)
193 yield rp3.ren.eq(0)
194 data1 = yield rp1.o_data
195 print(hex(data1))
196 assert data1 == 0x3
197 data2 = yield rp2.o_data
198 print(hex(data2))
199 assert data2 == 0xa
200 data3 = yield rp3.o_data
201 print(hex(data3))
202 assert data3 == 0x6
203
204
205 def test_regfile():
206 dut = VirtualRegPort(32, 8)
207 rp1 = dut.read_port("read1")
208 rp2 = dut.read_port("read2")
209 rp3 = dut.read_port("read3")
210 wp = dut.write_port("write")
211
212 ports = dut.ports()
213 print("ports", ports)
214 vl = rtlil.convert(dut, ports=ports)
215 with open("test_virtualregfile.il", "w") as f:
216 f.write(vl)
217
218 run_simulation(dut, regfile_array_sim(dut, rp1, rp2, rp3, wp),
219 vcd_name='test_regfile_array.vcd')
220
221
222 if __name__ == '__main__':
223 test_regfile()