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
:
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 # write ports, delayed by 1 cycle
73 for wp
in self
._wrports
:
75 m
.d
.sync
+= reg
.eq(wp
.data_i
)
80 for p
in self
._rdports
:
82 for p
in self
._wrports
:
88 def ortreereduce(tree
, attr
="data_o"):
89 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
92 class RegFileArray(Elaboratable
):
93 """ an array-based register file (register having write-through capability)
94 that has no "address" decoder, instead it has individual write-en
95 and read-en signals (per port).
97 def __init__(self
, width
, depth
):
100 self
.regs
= Array(Register(width
) for _
in range(self
.depth
))
104 def read_reg_port(self
, name
=None):
106 for i
in range(self
.depth
):
107 port
= self
.regs
[i
].read_port("%s%d" % (name
, i
))
111 def write_reg_port(self
, name
=None):
113 for i
in range(self
.depth
):
114 port
= self
.regs
[i
].write_port("%s%d" % (name
, i
))
118 def read_port(self
, name
=None):
119 regs
= self
.read_reg_port(name
)
121 port
= RecordObject([("ren", self
.depth
),
122 ("data_o", self
.width
)], name
)
123 self
._rdports
.append((regs
, port
))
126 def write_port(self
, name
=None):
127 regs
= self
.write_reg_port(name
)
129 port
= RecordObject([("wen", self
.depth
),
130 ("data_i", self
.width
)])
131 self
._wrports
.append((regs
, port
))
134 def _get_en_sig(self
, port
, typ
):
140 def elaborate(self
, platform
):
142 for i
, reg
in enumerate(self
.regs
):
143 setattr(m
.submodules
, "reg_%d" % i
, reg
)
145 for (regs
, p
) in self
._rdports
:
147 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'ren').eq(p
.ren
)
148 ror
= ortreereduce(list(regs
))
149 m
.d
.comb
+= p
.data_o
.eq(ror
)
150 for (regs
, p
) in self
._wrports
:
151 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'wen').eq(p
.wen
)
153 m
.d
.comb
+= r
.data_i
.eq(p
.data_i
)
165 class RegFile(Elaboratable
):
166 def __init__(self
, width
, depth
):
173 bsz
= int(log(self
.width
) / log(2))
174 port
= RecordObject([("raddr", bsz
),
176 ("data_o", self
.width
)])
177 self
._rdports
.append(port
)
180 def write_port(self
):
181 bsz
= int(log(self
.width
) / log(2))
182 port
= RecordObject([("waddr", bsz
),
184 ("data_i", self
.width
)])
185 self
._wrports
.append(port
)
188 def elaborate(self
, platform
):
190 bsz
= int(log(self
.width
) / log(2))
191 regs
= Array(Signal(self
.width
, name
="reg") for _
in range(self
.depth
))
193 # read ports. has write-through detection (returns data written)
194 for rp
in self
._rdports
:
195 wr_detect
= Signal(reset_less
=False)
197 m
.d
.comb
+= wr_detect
.eq(0)
198 for wp
in self
._wrports
:
199 addrmatch
= Signal(reset_less
=False)
200 m
.d
.comb
+= addrmatch
.eq(wp
.waddr
== rp
.raddr
)
201 with m
.If(wp
.wen
& addrmatch
):
202 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
203 m
.d
.comb
+= wr_detect
.eq(1)
204 with m
.If(~wr_detect
):
205 m
.d
.comb
+= rp
.data_o
.eq(regs
[rp
.raddr
])
207 # write ports, delayed by one cycle
208 for wp
in self
._wrports
:
210 m
.d
.sync
+= regs
[wp
.waddr
].eq(wp
.data_i
)
215 yield from self
._rdports
216 yield from self
._wrports
221 if isinstance(r
, RecordObject
):
226 def regfile_sim(dut
, rp
, wp
):
228 yield wp
.data_i
.eq(2)
235 data
= yield rp
.data_o
244 yield wp
.data_i
.eq(6)
246 data
= yield rp
.data_o
253 data
= yield rp
.data_o
257 data
= yield rp
.data_o
260 def regfile_array_sim(dut
, rp1
, rp2
, wp
):
261 yield wp
.data_i
.eq(2)
262 yield wp
.wen
.eq(1<<1)
265 yield rp1
.ren
.eq(1<<1)
267 data
= yield rp1
.data_o
272 yield rp1
.ren
.eq(1<<5)
273 yield rp2
.ren
.eq(1<<1)
274 yield wp
.wen
.eq(1<<5)
275 yield wp
.data_i
.eq(6)
277 data
= yield rp1
.data_o
285 data1
= yield rp1
.data_o
288 data2
= yield rp2
.data_o
293 data
= yield rp1
.data_o
300 wp
= dut
.write_port()
301 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
302 with
open("test_regfile.il", "w") as f
:
305 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
307 dut
= RegFileArray(32, 8)
308 rp1
= dut
.read_port("read1")
309 rp2
= dut
.read_port("read2")
310 wp
= dut
.write_port("write")
312 print ("ports", ports
)
313 vl
= rtlil
.convert(dut
, ports
=ports
)
314 with
open("test_regfile_array.il", "w") as f
:
317 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
),
318 vcd_name
='test_regfile_array.vcd')
320 if __name__
== '__main__':