1 """VirtualRegPort - terrible name for a complex register class
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.
11 from nmigen
.compat
.sim
import run_simulation
12 from nmigen
.cli
import verilog
, rtlil
14 from nmigen
import Cat
, Const
, Array
, Signal
, Elaboratable
, Module
15 from nmutil
.iocontrol
import RecordObject
17 from soc
.regfile
.regfile
import RegFileArray
20 class VirtualRegPort(RegFileArray
):
21 def __init__(self
, bitwidth
, n_regs
, rd2
=False, wr2
=False, synced
=True):
22 self
.bitwidth
= bitwidth
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
)
29 # "full" depth variant of the "external" port
30 self
.full_wr
= RecordObject([("wen", n_regs
),
31 ("i_data", bitwidth
)], # *full* wid
33 self
.full_rd
= RecordObject([("ren", n_regs
),
34 ("o_data", bitwidth
)], # *full* wid
37 self
.full_wr2
= RecordObject([("wen", n_regs
),
38 ("i_data", bitwidth
)], # *full* wid
41 self
.full_rd2
= RecordObject([("ren", n_regs
),
42 ("o_data", bitwidth
)], # *full* wid
45 def connect_full_wr(self
, m
, wfull
, name
):
47 wr_regs
= self
.write_reg_port(name
)
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)
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
)
57 def connect_full_rd(self
, m
, rfull
, name
):
59 rd_regs
= self
.read_reg_port(name
)
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)
65 comb
+= rfull
.o_data
.eq(Cat(*l
)) # we like Cat on lists
66 comb
+= Cat(*le
).eq(rfull
.ren
)
68 def elaborate(self
, platform
):
69 m
= super().elaborate(platform
)
72 # connect up full write port
73 self
.connect_full_wr(m
, self
.full_wr
, "w")
75 self
.connect_full_wr(m
, self
.full_wr2
, "w2")
77 # connect up full read port
78 self
.connect_full_rd(m
, self
.full_rd
, "r")
80 self
.connect_full_rd(m
, self
.full_rd2
, "r2")
85 yield from super().__iter
__()
86 yield from self
.full_wr
87 yield from self
.full_rd
90 def regfile_array_sim(dut
, rp1
, rp2
, rp3
, wp
):
93 yield wp
.wen
.eq(1 << 1)
97 yield rp1
.ren
.eq(1 << 1)
99 data
= yield rp1
.o_data
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)
112 data1
= yield rp1
.o_data
114 assert data1
== 6, data1
115 data2
= yield rp2
.o_data
117 assert data2
== 2, data2
119 data
= yield rp1
.o_data
122 # full port read (whole reg)
123 yield dut
.full_rd
.ren
.eq(0xff)
125 yield dut
.full_rd
.ren
.eq(0)
126 data
= yield dut
.full_rd
.o_data
129 # full port read (part reg)
130 yield dut
.full_rd
.ren
.eq(0x1 << 5)
132 yield dut
.full_rd
.ren
.eq(0)
133 data
= yield dut
.full_rd
.o_data
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)
140 yield dut
.full_wr
.wen
.eq(0x0)
142 # full port read (whole reg)
143 yield dut
.full_rd
.ren
.eq(0xff)
145 yield dut
.full_rd
.ren
.eq(0)
146 data
= yield dut
.full_rd
.o_data
150 yield dut
.full_wr
.wen
.eq(0xff)
151 yield dut
.full_wr
.i_data
.eq(0xcafeface)
153 yield dut
.full_wr
.wen
.eq(0x0)
155 # full port read (whole reg)
156 yield dut
.full_rd
.ren
.eq(0xff)
158 yield dut
.full_rd
.ren
.eq(0)
159 data
= yield dut
.full_rd
.o_data
163 yield wp
.i_data
.eq(2)
164 yield wp
.wen
.eq(1 << 1)
167 yield rp1
.ren
.eq(1 << 1)
169 data
= yield rp1
.o_data
173 # full port read (whole reg)
174 yield dut
.full_rd
.ren
.eq(0xff)
176 yield dut
.full_rd
.ren
.eq(0)
177 data
= yield dut
.full_rd
.o_data
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)))
189 yield dut
.full_wr
.wen
.eq(0)
194 data1
= yield rp1
.o_data
197 data2
= yield rp2
.o_data
200 data3
= yield rp3
.o_data
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")
213 print("ports", ports
)
214 vl
= rtlil
.convert(dut
, ports
=ports
)
215 with
open("test_virtualregfile.il", "w") as f
:
218 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, rp3
, wp
),
219 vcd_name
='test_regfile_array.vcd')
222 if __name__
== '__main__':