make read/write regs properly internal
[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 def elaborate(self, platform):
35 m = super().elaborate(platform)
36 comb = m.d.comb
37
38 # for internal use only.
39 wr_regs = self.write_reg_port(f"w")
40 rd_regs = self.read_reg_port(f"r")
41
42 # connect up full read port
43 rfull = self.full_rd
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 # connect up full write port
53 wfull = self.full_wr
54
55 # wire up the enable signals from the large (full) port
56 l = map(lambda port: port.data_i, wr_regs)
57 le = map(lambda port: port.wen, wr_regs) # get port wen(s)
58
59 # get list of all data_i (and wens) and assign to them via Cat
60 comb += Cat(*l).eq(wfull.data_i)
61 comb += Cat(*le).eq(wfull.wen)
62
63 return m
64
65 def __iter__(self):
66 yield from super().__iter__()
67 yield from self.full_wr
68 yield from self.full_rd
69
70
71 def regfile_array_sim(dut, rp1, rp2, rp3, wp):
72 # part-port write
73 yield wp.data_i.eq(2)
74 yield wp.wen.eq(1<<1)
75 yield
76 yield wp.wen.eq(0)
77 # part-port read
78 yield rp1.ren.eq(1<<1)
79 yield
80 data = yield rp1.data_o
81 print (data)
82 assert data == 2
83
84 # simultaneous read/write - should be pass-thru
85 yield rp1.ren.eq(1<<5)
86 yield rp2.ren.eq(1<<1)
87 yield wp.wen.eq(1<<5)
88 yield wp.data_i.eq(6)
89 yield
90 yield wp.wen.eq(0)
91 yield rp1.ren.eq(0)
92 yield rp2.ren.eq(0)
93 data1 = yield rp1.data_o
94 print (data1)
95 data2 = yield rp2.data_o
96 print (data2)
97 assert data1 == 6
98 yield
99 data = yield rp1.data_o
100 print (data)
101
102 # full port read (whole reg)
103 yield dut.full_rd.ren.eq(0xff)
104 yield
105 yield dut.full_rd.ren.eq(0)
106 data = yield dut.full_rd.data_o
107 print (hex(data))
108
109 # full port read (part reg)
110 yield dut.full_rd.ren.eq(0x1<<5)
111 yield
112 yield dut.full_rd.ren.eq(0)
113 data = yield dut.full_rd.data_o
114 print (hex(data))
115
116 # full port part-write (part masked reg)
117 yield dut.full_wr.wen.eq(0x1<<1)
118 yield dut.full_wr.data_i.eq(0xe0)
119 yield
120 yield dut.full_wr.wen.eq(0x0)
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.data_o
127 print (hex(data))
128
129 # full port write
130 yield dut.full_wr.wen.eq(0xff)
131 yield dut.full_wr.data_i.eq(0xcafeface)
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 # part write
143 yield wp.data_i.eq(2)
144 yield wp.wen.eq(1<<1)
145 yield
146 yield wp.wen.eq(0)
147 yield rp1.ren.eq(1<<1)
148 yield
149 data = yield rp1.data_o
150 print (hex(data))
151 assert data == 2
152
153 # full port read (whole reg)
154 yield dut.full_rd.ren.eq(0xff)
155 yield
156 yield dut.full_rd.ren.eq(0)
157 data = yield dut.full_rd.data_o
158 print (hex(data))
159
160 # simultaneous read/write: full-write, part-write, 3x part-read
161 yield rp1.ren.eq(1<<5)
162 yield rp2.ren.eq(1<<1)
163 yield rp3.ren.eq(1<<3)
164 yield wp.wen.eq(1<<3)
165 yield wp.data_i.eq(6)
166 yield dut.full_wr.wen.eq((1<<1) | (1<<5))
167 yield dut.full_wr.data_i.eq((0xa<<(1*4)) | (0x3<<(5*4)))
168 yield
169 yield dut.full_wr.wen.eq(0)
170 yield wp.wen.eq(0)
171 yield rp1.ren.eq(0)
172 yield rp2.ren.eq(0)
173 yield rp3.ren.eq(0)
174 data1 = yield rp1.data_o
175 print (hex(data1))
176 assert data1 == 0x3
177 data2 = yield rp2.data_o
178 print (hex(data2))
179 assert data2 == 0xa
180 data3 = yield rp3.data_o
181 print (hex(data3))
182 assert data3 == 0x6
183
184
185 def test_regfile():
186 dut = VirtualRegPort(32, 8)
187 rp1 = dut.read_port("read1")
188 rp2 = dut.read_port("read2")
189 rp3 = dut.read_port("read3")
190 wp = dut.write_port("write")
191
192 ports=dut.ports()
193 print ("ports", ports)
194 vl = rtlil.convert(dut, ports=ports)
195 with open("test_virtualregfile.il", "w") as f:
196 f.write(vl)
197
198 run_simulation(dut, regfile_array_sim(dut, rp1, rp2, rp3, wp),
199 vcd_name='test_regfile_array.vcd')
200
201 if __name__ == '__main__':
202 test_regfile()
203
204