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
:
90 def ortreereduce(tree
, attr
="data_o"):
91 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
94 class RegFileArray(Elaboratable
):
95 """ an array-based register file (register having write-through capability)
96 that has no "address" decoder, instead it has individual write-en
97 and read-en signals (per port).
99 def __init__(self
, width
, depth
):
102 self
.regs
= Array(Register(width
) for _
in range(self
.depth
))
106 def read_reg_port(self
, name
=None):
108 for i
in range(self
.depth
):
109 port
= self
.regs
[i
].read_port("%s%d" % (name
, i
))
113 def write_reg_port(self
, name
=None):
115 for i
in range(self
.depth
):
116 port
= self
.regs
[i
].write_port("%s%d" % (name
, i
))
120 def read_port(self
, name
=None):
121 regs
= self
.read_reg_port(name
)
123 port
= RecordObject([("ren", self
.depth
),
124 ("data_o", self
.width
)], name
)
125 self
._rdports
.append((regs
, port
))
128 def write_port(self
, name
=None):
129 regs
= self
.write_reg_port(name
)
131 port
= RecordObject([("wen", self
.depth
),
132 ("data_i", self
.width
)])
133 self
._wrports
.append((regs
, port
))
136 def _get_en_sig(self
, port
, typ
):
142 def elaborate(self
, platform
):
144 for i
, reg
in enumerate(self
.regs
):
145 setattr(m
.submodules
, "reg_%d" % i
, reg
)
147 for (regs
, p
) in self
._rdports
:
149 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'ren').eq(p
.ren
)
150 ror
= ortreereduce(list(regs
))
151 m
.d
.comb
+= p
.data_o
.eq(ror
)
152 for (regs
, p
) in self
._wrports
:
153 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'wen').eq(p
.wen
)
155 m
.d
.comb
+= r
.data_i
.eq(p
.data_i
)
167 class RegFile(Elaboratable
):
168 def __init__(self
, width
, depth
):
174 def read_port(self
, name
=None):
175 bsz
= int(log(self
.width
) / log(2))
176 port
= RecordObject([("raddr", bsz
),
178 ("data_o", self
.width
)], name
=name
)
179 self
._rdports
.append(port
)
182 def write_port(self
, name
=None):
183 bsz
= int(log(self
.width
) / log(2))
184 port
= RecordObject([("waddr", bsz
),
186 ("data_i", self
.width
)], name
=name
)
187 self
._wrports
.append(port
)
190 def elaborate(self
, platform
):
192 bsz
= int(log(self
.width
) / log(2))
193 regs
= Array(Signal(self
.width
, name
="reg") for _
in range(self
.depth
))
195 # read ports. has write-through detection (returns data written)
196 for rp
in self
._rdports
:
197 wr_detect
= Signal(reset_less
=False)
199 m
.d
.comb
+= wr_detect
.eq(0)
200 for wp
in self
._wrports
:
201 addrmatch
= Signal(reset_less
=False)
202 m
.d
.comb
+= addrmatch
.eq(wp
.waddr
== rp
.raddr
)
203 with m
.If(wp
.wen
& addrmatch
):
204 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
205 m
.d
.comb
+= wr_detect
.eq(1)
206 with m
.If(~wr_detect
):
207 m
.d
.comb
+= rp
.data_o
.eq(regs
[rp
.raddr
])
209 # write ports, delayed by one cycle
210 for wp
in self
._wrports
:
212 m
.d
.sync
+= regs
[wp
.waddr
].eq(wp
.data_i
)
217 yield from self
._rdports
218 yield from self
._wrports
223 if isinstance(r
, RecordObject
):
229 def regfile_sim(dut
, rp
, wp
):
231 yield wp
.data_i
.eq(2)
238 data
= yield rp
.data_o
247 yield wp
.data_i
.eq(6)
249 data
= yield rp
.data_o
256 data
= yield rp
.data_o
260 data
= yield rp
.data_o
263 def regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
):
264 print ("regfile_array_sim")
265 yield wp
.data_i
.eq(2)
266 yield wp
.wen
.eq(1<<1)
269 yield rp1
.ren
.eq(1<<1)
271 data
= yield rp1
.data_o
276 yield rp1
.ren
.eq(1<<5)
277 yield rp2
.ren
.eq(1<<1)
278 yield wp
.wen
.eq(1<<5)
279 yield wp
.data_i
.eq(6)
281 data
= yield rp1
.data_o
289 data1
= yield rp1
.data_o
292 data2
= yield rp2
.data_o
297 data
= yield rp1
.data_o
304 wp
= dut
.write_port()
305 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
306 with
open("test_regfile.il", "w") as f
:
309 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
311 dut
= RegFileArray(32, 8)
312 rp1
= dut
.read_port("read1")
313 rp2
= dut
.read_port("read2")
314 wp
= dut
.write_port("write")
315 wp2
= dut
.write_port("write2")
317 print ("ports", ports
)
318 vl
= rtlil
.convert(dut
, ports
=ports
)
319 with
open("test_regfile_array.il", "w") as f
:
322 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
),
323 vcd_name
='test_regfile_array.vcd')
325 if __name__
== '__main__':