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
.cli
import verilog
, rtlil
23 from nmigen
import Cat
, Const
, Array
, Signal
, Elaboratable
, Module
24 from nmutil
.iocontrol
import RecordObject
25 from nmutil
.util
import treereduce
31 class Register(Elaboratable
):
32 def __init__(self
, width
, writethru
=True):
34 self
.writethru
= writethru
38 def read_port(self
, name
=None):
39 port
= RecordObject([("ren", 1),
40 ("data_o", self
.width
)],
42 self
._rdports
.append(port
)
45 def write_port(self
, name
=None):
46 port
= RecordObject([("wen", 1),
47 ("data_i", self
.width
)],
49 self
._wrports
.append(port
)
52 def elaborate(self
, platform
):
54 self
.reg
= reg
= Signal(self
.width
, name
="reg")
56 # read ports. has write-through detection (returns data written)
57 for rp
in self
._rdports
:
60 wr_detect
= Signal(reset_less
=False)
61 m
.d
.comb
+= wr_detect
.eq(0)
62 for wp
in self
._wrports
:
64 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
65 m
.d
.comb
+= wr_detect
.eq(1)
66 with m
.If(~wr_detect
):
67 m
.d
.comb
+= rp
.data_o
.eq(reg
)
69 m
.d
.comb
+= rp
.data_o
.eq(reg
)
71 # write ports, don't allow write to address 0 (ignore it)
72 for wp
in self
._wrports
:
74 m
.d
.sync
+= reg
.eq(wp
.data_i
)
79 for p
in self
._rdports
:
81 for p
in self
._wrports
:
87 def ortreereduce(tree
, attr
="data_o"):
88 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
91 class RegFileArray(Elaboratable
):
92 """ an array-based register file (register having write-through capability)
93 that has no "address" decoder, instead it has individual write-en
94 and read-en signals (per port).
96 def __init__(self
, width
, depth
):
99 self
.regs
= Array(Register(width
) for _
in range(self
.depth
))
103 def read_reg_port(self
, name
=None):
105 for i
in range(self
.depth
):
106 port
= self
.regs
[i
].read_port("%s%d" % (name
, i
))
110 def write_reg_port(self
, name
=None):
112 for i
in range(self
.depth
):
113 port
= self
.regs
[i
].write_port("%s%d" % (name
, i
))
117 def read_port(self
, name
=None):
118 regs
= self
.read_reg_port(name
)
120 port
= RecordObject([("ren", self
.depth
),
121 ("data_o", self
.width
)], name
)
122 self
._rdports
.append((regs
, port
))
125 def write_port(self
, name
=None):
126 regs
= self
.write_reg_port(name
)
128 port
= RecordObject([("wen", self
.depth
),
129 ("data_i", self
.width
)])
130 self
._wrports
.append((regs
, port
))
133 def _get_en_sig(self
, port
, typ
):
139 def elaborate(self
, platform
):
141 for i
, reg
in enumerate(self
.regs
):
142 setattr(m
.submodules
, "reg_%d" % i
, reg
)
144 for (regs
, p
) in self
._rdports
:
146 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'ren').eq(p
.ren
)
147 ror
= ortreereduce(list(regs
))
148 m
.d
.comb
+= p
.data_o
.eq(ror
)
149 for (regs
, p
) in self
._wrports
:
150 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'wen').eq(p
.wen
)
152 m
.d
.comb
+= r
.data_i
.eq(p
.data_i
)
164 class RegFile(Elaboratable
):
165 def __init__(self
, width
, depth
):
172 bsz
= int(log(self
.width
) / log(2))
173 port
= RecordObject([("raddr", bsz
),
175 ("data_o", self
.width
)])
176 self
._rdports
.append(port
)
179 def write_port(self
):
180 bsz
= int(log(self
.width
) / log(2))
181 port
= RecordObject([("waddr", bsz
),
183 ("data_i", self
.width
)])
184 self
._wrports
.append(port
)
187 def elaborate(self
, platform
):
189 bsz
= int(log(self
.width
) / log(2))
190 regs
= Array(Signal(self
.width
, name
="reg") for _
in range(self
.depth
))
192 # read ports. has write-through detection (returns data written)
193 for rp
in self
._rdports
:
194 wr_detect
= Signal(reset_less
=False)
196 m
.d
.comb
+= wr_detect
.eq(0)
197 for wp
in self
._wrports
:
198 addrmatch
= Signal(reset_less
=False)
199 m
.d
.comb
+= addrmatch
.eq(wp
.waddr
== rp
.raddr
)
200 with m
.If(wp
.wen
& addrmatch
):
201 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
202 m
.d
.comb
+= wr_detect
.eq(1)
203 with m
.If(~wr_detect
):
204 m
.d
.comb
+= rp
.data_o
.eq(regs
[rp
.raddr
])
206 # write ports, don't allow write to address 0 (ignore it)
207 for wp
in self
._wrports
:
208 with m
.If(wp
.wen
& (wp
.waddr
!= Const(0, bsz
))):
209 m
.d
.sync
+= regs
[wp
.waddr
].eq(wp
.data_i
)
214 yield from self
._rdports
215 yield from self
._wrports
220 if isinstance(r
, RecordObject
):
225 def regfile_sim(dut
, rp
, wp
):
227 yield wp
.data_i
.eq(2)
234 data
= yield rp
.data_o
242 yield wp
.data_i
.eq(6)
243 data
= yield rp
.data_o
248 data
= yield rp
.data_o
252 data
= yield rp
.data_o
255 def regfile_array_sim(dut
, rp1
, rp2
, wp
):
256 yield wp
.data_i
.eq(2)
257 yield wp
.wen
.eq(1<<1)
260 yield rp1
.ren
.eq(1<<1)
262 data
= yield rp1
.data_o
266 yield rp1
.ren
.eq(1<<5)
267 yield rp2
.ren
.eq(1<<1)
268 yield wp
.wen
.eq(1<<5)
269 yield wp
.data_i
.eq(6)
270 data
= yield rp1
.data_o
276 data1
= yield rp1
.data_o
278 data2
= yield rp2
.data_o
282 data
= yield rp1
.data_o
288 wp
= dut
.write_port()
289 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
290 with
open("test_regfile.il", "w") as f
:
293 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
295 dut
= RegFileArray(32, 8)
296 rp1
= dut
.read_port("read1")
297 rp2
= dut
.read_port("read2")
298 wp
= dut
.write_port("write")
300 print ("ports", ports
)
301 vl
= rtlil
.convert(dut
, ports
=ports
)
302 with
open("test_regfile_array.il", "w") as f
:
305 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
),
306 vcd_name
='test_regfile_array.vcd')
308 if __name__
== '__main__':