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
):
22 self
.bitwidth
= bitwidth
24 self
.regwidth
= regwidth
= bitwidth
// n_regs
25 super().__init
__(self
.regwidth
, n_regs
)
27 # create suite of 8 write and 8 read ports for external use
30 for i
in range(n_regs
):
31 self
.wr_ports
.append(RecordObject([("wen", n_regs
),
32 ("data_i", regwidth
)],
34 self
.rd_ports
.append(RecordObject([("ren", n_regs
),
35 ("data_o", regwidth
)],
37 # and append the "full" depth variant to the "external" ports
38 self
.wr_ports
.append(RecordObject([("wen", n_regs
),
39 ("data_i", bitwidth
)], # *full* wid
41 self
.rd_ports
.append(RecordObject([("ren", n_regs
),
42 ("data_o", bitwidth
)], # *full* wid
44 # now for internal use
45 self
._wr
_regs
= self
.write_reg_port(f
"intw")
46 self
._rd
_regs
= self
.read_reg_port(f
"intr")
48 def elaborate(self
, platform
):
49 m
= super().elaborate(platform
)
50 comb
, sync
= m
.d
.comb
, m
.d
.sync
52 # connect up: detect if read is requested on large (full) port
53 # nothing fancy needed because reads are same-cycle
54 rlast
= self
.rd_ports
[-1]
55 ren_sig
= Signal(reset_less
=True)
56 comb
+= ren_sig
.eq(rlast
.ren
.bool())
59 # wire up the enable signals and chain-accumulate the data
61 l
= map(lambda port
: port
.data_o
, self
._rd
_regs
) # get port data(s)
62 le
= map(lambda port
: port
.ren
, self
._rd
_regs
) # get port ren(s)
63 comb
+= rlast
.data_o
.eq(Cat(*l
)) # we like Cat on lists
64 comb
+= Cat(*le
).eq(rlast
.ren
)
66 # allow request through the corresponding lower indexed ports
67 # TODO: make data_o and ren fields "directional" then simply
68 # use record "connect"
69 for i
, port
in enumerate(self
._rd
_regs
):
70 comb
+= port
.ren
.eq(self
.rd_ports
[i
].ren
)
71 comb
+= self
.rd_ports
[i
].data_o
.eq(port
.data_o
)
73 # connect up: detect if write is requested on large (full) port
74 # however due to the delay (1 clock) on write, we also need to
75 # delay the test. enable is not-delayed, but data is.
76 en_sig
= Signal(reset_less
=True) # combinatorial
77 data_sig
= Signal(reset_less
=True) # sync (one clock delay)
79 wlast
= self
.wr_ports
[-1]
80 comb
+= en_sig
.eq(wlast
.wen
.bool())
81 sync
+= data_sig
.eq(en_sig
)
84 # wire up the enable signals from the large (full) port
85 le
= map(lambda port
: port
.wen
, self
._wr
_regs
) # get port wen(s)
86 comb
+= Cat(*le
).eq(wlast
.wen
)
88 # allow request through the corresponding lower indexed ports
89 for i
, port
in enumerate(self
._wr
_regs
):
90 comb
+= port
.wen
.eq(self
.wr_ports
[i
].wen
)
92 # and (sigh) data is on one clock-delay, connect that too
94 # get list of all data_i (and wens) and assign to them via Cat
95 l
= map(lambda port
: port
.data_i
, self
._wr
_regs
)
96 comb
+= Cat(*l
).eq(wlast
.data_i
)
98 # allow data through the corresponding lower indexed ports
99 for i
, port
in enumerate(self
._wr
_regs
):
100 comb
+= port
.data_i
.eq(self
.wr_ports
[i
].data_i
)
105 yield from super().__iter
__()
106 for p
in self
.wr_ports
:
108 for p
in self
.rd_ports
:
113 dut
= VirtualRegPort(32, 8)
115 print ("ports", ports
)
116 vl
= rtlil
.convert(dut
, ports
=ports
)
117 with
open("test_virtualregfile.il", "w") as f
:
120 if __name__
== '__main__':