sort-of (maybe) implemented a virtual port on top of RegFileArray.
[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 # create suite of 8 write and 8 read ports for external use
28 self.wr_ports = []
29 self.rd_ports = []
30 for i in range(n_regs):
31 self.wr_ports.append(RecordObject([("wen", n_regs),
32 ("data_i", regwidth)],
33 name="w%d" % i))
34 self.rd_ports.append(RecordObject([("ren", n_regs),
35 ("data_o", regwidth)],
36 name="r%d" % i))
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
40 name="full_wr"))
41 self.rd_ports.append(RecordObject([("ren", n_regs),
42 ("data_o", bitwidth)], # *full* wid
43 name="full_rd"))
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")
47
48 def elaborate(self, platform):
49 m = super().elaborate(platform)
50 comb, sync = m.d.comb, m.d.sync
51
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())
57 print (rlast)
58 with m.If(ren_sig):
59 # wire up the enable signals and chain-accumulate the data
60 print (self._rd_regs)
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)
65 with m.Else():
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)
72
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)
78
79 wlast = self.wr_ports[-1]
80 comb += en_sig.eq(wlast.wen.bool())
81 sync += data_sig.eq(en_sig)
82
83 with m.If(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)
87 with m.Else():
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)
91
92 # and (sigh) data is on one clock-delay, connect that too
93 with m.If(data_sig):
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)
97 with m.Else():
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)
101
102 return m
103
104 def __iter__(self):
105 yield from super().__iter__()
106 for p in self.wr_ports:
107 yield from p
108 for p in self.rd_ports:
109 yield from p
110
111
112 def test_regfile():
113 dut = VirtualRegPort(32, 8)
114 ports=dut.ports()
115 print ("ports", ports)
116 vl = rtlil.convert(dut, ports=ports)
117 with open("test_virtualregfile.il", "w") as f:
118 f.write(vl)
119
120 if __name__ == '__main__':
121 test_regfile()
122