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