add srcstep and correct PC-advancing during Sub-PC looping in ISACaller
[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):
22 self.bitwidth = bitwidth
23 self.nregs = n_regs
24 self.rd2 = rd2 # eurgh hack
25 self.regwidth = regwidth = bitwidth // n_regs
26 super().__init__(self.regwidth, n_regs)
27
28 # "full" depth variant of the "external" port
29 self.full_wr = RecordObject([("wen", n_regs),
30 ("data_i", bitwidth)], # *full* wid
31 name="full_wr")
32 self.full_rd = RecordObject([("ren", n_regs),
33 ("data_o", bitwidth)], # *full* wid
34 name="full_rd")
35 if not rd2:
36 return
37 self.full_rd2 = RecordObject([("ren", n_regs),
38 ("data_o", bitwidth)], # *full* wid
39 name="full_rd2")
40
41 def connect_full_rd(self, m, rfull, name):
42 comb = m.d.comb
43 rd_regs = self.read_reg_port(name)
44
45 # wire up the enable signals and chain-accumulate the data
46 l = map(lambda port: port.data_o, rd_regs) # get port data(s)
47 le = map(lambda port: port.ren, rd_regs) # get port ren(s)
48
49 comb += rfull.data_o.eq(Cat(*l)) # we like Cat on lists
50 comb += Cat(*le).eq(rfull.ren)
51
52 def elaborate(self, platform):
53 m = super().elaborate(platform)
54 comb = m.d.comb
55
56 # for internal use only.
57 wr_regs = self.write_reg_port(f"w")
58
59 # connect up full read port
60 self.connect_full_rd(m, self.full_rd, "r")
61 if self.rd2: # hack!
62 self.connect_full_rd(m, self.full_rd2, "r2")
63
64 # connect up full write port
65 wfull = self.full_wr
66
67 # wire up the enable signals from the large (full) port
68 l = map(lambda port: port.data_i, wr_regs)
69 le = map(lambda port: port.wen, wr_regs) # get port wen(s)
70
71 # get list of all data_i (and wens) and assign to them via Cat
72 comb += Cat(*l).eq(wfull.data_i)
73 comb += Cat(*le).eq(wfull.wen)
74
75 return m
76
77 def __iter__(self):
78 yield from super().__iter__()
79 yield from self.full_wr
80 yield from self.full_rd
81
82
83 def regfile_array_sim(dut, rp1, rp2, rp3, wp):
84 # part-port write
85 yield wp.data_i.eq(2)
86 yield wp.wen.eq(1 << 1)
87 yield
88 yield wp.wen.eq(0)
89 # part-port read
90 yield rp1.ren.eq(1 << 1)
91 yield
92 data = yield rp1.data_o
93 print(data)
94 assert data == 2
95
96 # simultaneous read/write - should be pass-thru
97 yield rp1.ren.eq(1 << 5)
98 yield rp2.ren.eq(1 << 1)
99 yield wp.wen.eq(1 << 5)
100 yield wp.data_i.eq(6)
101 yield
102 yield wp.wen.eq(0)
103 yield rp1.ren.eq(0)
104 yield rp2.ren.eq(0)
105 data1 = yield rp1.data_o
106 print(data1)
107 assert data1 == 6, data1
108 data2 = yield rp2.data_o
109 print(data2)
110 assert data2 == 2, data2
111 yield
112 data = yield rp1.data_o
113 print(data)
114
115 # full port read (whole reg)
116 yield dut.full_rd.ren.eq(0xff)
117 yield
118 yield dut.full_rd.ren.eq(0)
119 data = yield dut.full_rd.data_o
120 print(hex(data))
121
122 # full port read (part reg)
123 yield dut.full_rd.ren.eq(0x1 << 5)
124 yield
125 yield dut.full_rd.ren.eq(0)
126 data = yield dut.full_rd.data_o
127 print(hex(data))
128
129 # full port part-write (part masked reg)
130 yield dut.full_wr.wen.eq(0x1 << 1)
131 yield dut.full_wr.data_i.eq(0xe0)
132 yield
133 yield dut.full_wr.wen.eq(0x0)
134
135 # full port read (whole reg)
136 yield dut.full_rd.ren.eq(0xff)
137 yield
138 yield dut.full_rd.ren.eq(0)
139 data = yield dut.full_rd.data_o
140 print(hex(data))
141
142 # full port write
143 yield dut.full_wr.wen.eq(0xff)
144 yield dut.full_wr.data_i.eq(0xcafeface)
145 yield
146 yield dut.full_wr.wen.eq(0x0)
147
148 # full port read (whole reg)
149 yield dut.full_rd.ren.eq(0xff)
150 yield
151 yield dut.full_rd.ren.eq(0)
152 data = yield dut.full_rd.data_o
153 print(hex(data))
154
155 # part write
156 yield wp.data_i.eq(2)
157 yield wp.wen.eq(1 << 1)
158 yield
159 yield wp.wen.eq(0)
160 yield rp1.ren.eq(1 << 1)
161 yield
162 data = yield rp1.data_o
163 print(hex(data))
164 assert data == 2
165
166 # full port read (whole reg)
167 yield dut.full_rd.ren.eq(0xff)
168 yield
169 yield dut.full_rd.ren.eq(0)
170 data = yield dut.full_rd.data_o
171 print(hex(data))
172
173 # simultaneous read/write: full-write, part-write, 3x part-read
174 yield rp1.ren.eq(1 << 5)
175 yield rp2.ren.eq(1 << 1)
176 yield rp3.ren.eq(1 << 3)
177 yield wp.wen.eq(1 << 3)
178 yield wp.data_i.eq(6)
179 yield dut.full_wr.wen.eq((1 << 1) | (1 << 5))
180 yield dut.full_wr.data_i.eq((0xa << (1*4)) | (0x3 << (5*4)))
181 yield
182 yield dut.full_wr.wen.eq(0)
183 yield wp.wen.eq(0)
184 yield rp1.ren.eq(0)
185 yield rp2.ren.eq(0)
186 yield rp3.ren.eq(0)
187 data1 = yield rp1.data_o
188 print(hex(data1))
189 assert data1 == 0x3
190 data2 = yield rp2.data_o
191 print(hex(data2))
192 assert data2 == 0xa
193 data3 = yield rp3.data_o
194 print(hex(data3))
195 assert data3 == 0x6
196
197
198 def test_regfile():
199 dut = VirtualRegPort(32, 8)
200 rp1 = dut.read_port("read1")
201 rp2 = dut.read_port("read2")
202 rp3 = dut.read_port("read3")
203 wp = dut.write_port("write")
204
205 ports = dut.ports()
206 print("ports", ports)
207 vl = rtlil.convert(dut, ports=ports)
208 with open("test_virtualregfile.il", "w") as f:
209 f.write(vl)
210
211 run_simulation(dut, regfile_array_sim(dut, rp1, rp2, rp3, wp),
212 vcd_name='test_regfile_array.vcd')
213
214
215 if __name__ == '__main__':
216 test_regfile()