3 These are not "normal" binary-indexed regfiles (although that is included).
4 They include *unary* indexed regfiles as well as Dependency-tracked ones
5 (SPR files with 1024 registers, only around 4-5 of which need to be active)
6 and special "split" regfiles that have 8R8W for 8 4-bit quantities and a
7 1R1W to read/write *all* 8 4-bit registers in a single one-off 32-bit way.
9 Due to the way that the Dependency Matrices are set up (bit-vectors), the
10 primary focus here is on *unary* indexing.
14 * https://libre-soc.org/3d_gpu/architecture/regfile
15 * https://bugs.libre-soc.org/show_bug.cgi?id=345
16 * https://bugs.libre-soc.org/show_bug.cgi?id=351
17 * https://bugs.libre-soc.org/show_bug.cgi?id=352
20 from nmigen
.compat
.sim
import run_simulation
21 from nmigen
.back
.pysim
import Settle
22 from nmigen
.cli
import verilog
, rtlil
24 from nmigen
import Cat
, Const
, Array
, Signal
, Elaboratable
, Module
25 from nmutil
.iocontrol
import RecordObject
26 from nmutil
.util
import treereduce
32 class Register(Elaboratable
):
33 def __init__(self
, width
, writethru
=True):
35 self
.writethru
= writethru
39 def read_port(self
, name
=None):
40 port
= RecordObject([("ren", 1),
41 ("data_o", self
.width
)],
43 self
._rdports
.append(port
)
46 def write_port(self
, name
=None):
47 port
= RecordObject([("wen", 1),
48 ("data_i", self
.width
)],
50 self
._wrports
.append(port
)
53 def elaborate(self
, platform
):
55 self
.reg
= reg
= Signal(self
.width
, name
="reg")
57 # read ports. has write-through detection (returns data written)
58 for rp
in self
._rdports
:
59 with m
.If(rp
.ren
== 1):
61 wr_detect
= Signal(reset_less
=False)
62 m
.d
.comb
+= wr_detect
.eq(0)
63 for wp
in self
._wrports
:
65 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
66 m
.d
.comb
+= wr_detect
.eq(1)
67 with m
.If(~wr_detect
):
68 m
.d
.comb
+= rp
.data_o
.eq(reg
)
70 m
.d
.comb
+= rp
.data_o
.eq(reg
)
72 m
.d
.comb
+= rp
.data_o
.eq(0)
74 # write ports, delayed by 1 cycle
75 for wp
in self
._wrports
:
77 m
.d
.sync
+= reg
.eq(wp
.data_i
)
82 for p
in self
._rdports
:
84 for p
in self
._wrports
:
91 def ortreereduce(tree
, attr
="data_o"):
92 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
95 class RegFileArray(Elaboratable
):
96 """ an array-based register file (register having write-through capability)
97 that has no "address" decoder, instead it has individual write-en
98 and read-en signals (per port).
101 def __init__(self
, width
, depth
):
104 self
.regs
= Array(Register(width
) for _
in range(self
.depth
))
108 def read_reg_port(self
, name
=None):
110 for i
in range(self
.depth
):
111 port
= self
.regs
[i
].read_port("%s%d" % (name
, i
))
115 def write_reg_port(self
, name
=None):
117 for i
in range(self
.depth
):
118 port
= self
.regs
[i
].write_port("%s%d" % (name
, i
))
122 def read_port(self
, name
=None):
123 regs
= self
.read_reg_port(name
)
125 port
= RecordObject([("ren", self
.depth
),
126 ("data_o", self
.width
)], name
)
127 self
._rdports
.append((regs
, port
))
130 def write_port(self
, name
=None):
131 regs
= self
.write_reg_port(name
)
133 port
= RecordObject([("wen", self
.depth
),
134 ("data_i", self
.width
)])
135 self
._wrports
.append((regs
, port
))
138 def _get_en_sig(self
, port
, typ
):
144 def elaborate(self
, platform
):
146 for i
, reg
in enumerate(self
.regs
):
147 setattr(m
.submodules
, "reg_%d" % i
, reg
)
149 for (regs
, p
) in self
._rdports
:
151 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'ren').eq(p
.ren
)
152 ror
= ortreereduce(list(regs
))
153 m
.d
.comb
+= p
.data_o
.eq(ror
)
154 for (regs
, p
) in self
._wrports
:
155 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'wen').eq(p
.wen
)
157 m
.d
.comb
+= r
.data_i
.eq(p
.data_i
)
169 class RegFile(Elaboratable
):
170 def __init__(self
, width
, depth
):
176 def read_port(self
, name
=None):
177 bsz
= int(log(self
.width
) / log(2))
178 port
= RecordObject([("raddr", bsz
),
180 ("data_o", self
.width
)], name
=name
)
181 self
._rdports
.append(port
)
184 def write_port(self
, name
=None):
185 bsz
= int(log(self
.width
) / log(2))
186 port
= RecordObject([("waddr", bsz
),
188 ("data_i", self
.width
)], name
=name
)
189 self
._wrports
.append(port
)
192 def elaborate(self
, platform
):
194 bsz
= int(log(self
.width
) / log(2))
195 regs
= Array(Signal(self
.width
, name
="reg") for _
in range(self
.depth
))
197 # read ports. has write-through detection (returns data written)
198 for rp
in self
._rdports
:
199 wr_detect
= Signal(reset_less
=False)
201 m
.d
.comb
+= wr_detect
.eq(0)
202 for wp
in self
._wrports
:
203 addrmatch
= Signal(reset_less
=False)
204 m
.d
.comb
+= addrmatch
.eq(wp
.waddr
== rp
.raddr
)
205 with m
.If(wp
.wen
& addrmatch
):
206 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
207 m
.d
.comb
+= wr_detect
.eq(1)
208 with m
.If(~wr_detect
):
209 m
.d
.comb
+= rp
.data_o
.eq(regs
[rp
.raddr
])
211 # write ports, delayed by one cycle
212 for wp
in self
._wrports
:
214 m
.d
.sync
+= regs
[wp
.waddr
].eq(wp
.data_i
)
219 yield from self
._rdports
220 yield from self
._wrports
225 if isinstance(r
, RecordObject
):
231 def regfile_sim(dut
, rp
, wp
):
233 yield wp
.data_i
.eq(2)
240 data
= yield rp
.data_o
249 yield wp
.data_i
.eq(6)
251 data
= yield rp
.data_o
258 data
= yield rp
.data_o
262 data
= yield rp
.data_o
266 def regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
):
267 print("regfile_array_sim")
268 yield wp
.data_i
.eq(2)
269 yield wp
.wen
.eq(1 << 1)
272 yield rp1
.ren
.eq(1 << 1)
274 data
= yield rp1
.data_o
279 yield rp1
.ren
.eq(1 << 5)
280 yield rp2
.ren
.eq(1 << 1)
281 yield wp
.wen
.eq(1 << 5)
282 yield wp
.data_i
.eq(6)
284 data
= yield rp1
.data_o
292 data1
= yield rp1
.data_o
295 data2
= yield rp2
.data_o
300 data
= yield rp1
.data_o
308 wp
= dut
.write_port()
309 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
310 with
open("test_regfile.il", "w") as f
:
313 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
315 dut
= RegFileArray(32, 8)
316 rp1
= dut
.read_port("read1")
317 rp2
= dut
.read_port("read2")
318 wp
= dut
.write_port("write")
319 wp2
= dut
.write_port("write2")
321 print("ports", ports
)
322 vl
= rtlil
.convert(dut
, ports
=ports
)
323 with
open("test_regfile_array.il", "w") as f
:
326 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
),
327 vcd_name
='test_regfile_array.vcd')
330 if __name__
== '__main__':