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
27 from nmigen
.utils
import log2_int
28 from nmigen
import Memory
34 class Register(Elaboratable
):
35 def __init__(self
, width
, writethru
=True, synced
=True, resetval
=0):
38 self
.writethru
= writethru
43 def read_port(self
, name
=None):
44 port
= RecordObject([("ren", 1),
45 ("o_data", self
.width
)],
47 self
._rdports
.append(port
)
50 def write_port(self
, name
=None):
51 port
= RecordObject([("wen", 1),
52 ("i_data", self
.width
)],
54 self
._wrports
.append(port
)
57 def elaborate(self
, platform
):
59 self
.reg
= reg
= Signal(self
.width
, name
="reg", reset
=self
.reset
)
66 # read ports. has write-through detection (returns data written)
67 for rp
in self
._rdports
:
68 domain
+= rp
.o_data
.eq(0)
71 wr_detect
= Signal(reset_less
=False)
72 m
.d
.comb
+= wr_detect
.eq(0)
73 for wp
in self
._wrports
:
75 domain
+= rp
.o_data
.eq(wp
.i_data
)
76 m
.d
.comb
+= wr_detect
.eq(1)
77 with m
.If(~wr_detect
):
78 domain
+= rp
.o_data
.eq(reg
)
80 domain
+= rp
.o_data
.eq(reg
)
82 # write ports, delayed by 1 cycle
83 for wp
in self
._wrports
:
85 m
.d
.sync
+= reg
.eq(wp
.i_data
)
90 for p
in self
._rdports
:
92 for p
in self
._wrports
:
99 def ortreereduce(tree
, attr
="o_data"):
100 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
103 class RegFileArray(Elaboratable
):
105 """ an array-based register file (register having write-through capability)
106 that has no "address" decoder, instead it has individual write-en
107 and read-en signals (per port).
110 def __init__(self
, width
, depth
, synced
=True, fwd_bus_mode
=True,
117 self
.regs
= Array(Register(width
, synced
=synced
,
118 writethru
=fwd_bus_mode
,
124 def read_reg_port(self
, name
=None):
126 for i
in range(self
.depth
):
127 port
= self
.regs
[i
].read_port("%s%d" % (name
, i
))
131 def write_reg_port(self
, name
=None):
133 for i
in range(self
.depth
):
134 port
= self
.regs
[i
].write_port("%s%d" % (name
, i
))
138 def read_port(self
, name
=None):
139 regs
= self
.read_reg_port(name
)
141 port
= RecordObject([("ren", self
.depth
),
142 ("o_data", self
.width
)], name
)
143 self
._rdports
.append((regs
, port
))
146 def write_port(self
, name
=None):
147 regs
= self
.write_reg_port(name
)
149 port
= RecordObject([("wen", self
.depth
),
150 ("i_data", self
.width
)])
151 self
._wrports
.append((regs
, port
))
154 def _get_en_sig(self
, port
, typ
):
160 def elaborate(self
, platform
):
162 for i
, reg
in enumerate(self
.regs
):
163 setattr(m
.submodules
, "reg_%d" % i
, reg
)
170 for (regs
, p
) in self
._rdports
:
172 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'ren').eq(p
.ren
)
173 ror
= ortreereduce(list(regs
))
175 ren_delay
= Signal
.like(p
.ren
)
176 m
.d
.sync
+= ren_delay
.eq(p
.ren
)
177 with m
.If(ren_delay
):
178 m
.d
.comb
+= p
.o_data
.eq(ror
)
180 m
.d
.comb
+= p
.o_data
.eq(ror
)
181 for (regs
, p
) in self
._wrports
:
182 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'wen').eq(p
.wen
)
184 m
.d
.comb
+= r
.i_data
.eq(p
.i_data
)
196 class RegFileMem(Elaboratable
):
198 def __init__(self
, width
, depth
, fwd_bus_mode
=False, synced
=True):
199 self
.fwd_bus_mode
= fwd_bus_mode
201 self
.width
, self
.depth
= width
, depth
202 self
.memory
= Memory(width
=width
, depth
=depth
)
206 def read_port(self
, name
=None):
207 bsz
= log2_int(self
.depth
, False)
208 port
= RecordObject([("addr", bsz
),
210 ("o_data", self
.width
)], name
=name
)
215 self
._rdports
[name
] = (port
, self
.memory
.read_port(domain
=domain
))
218 def write_port(self
, name
=None):
219 bsz
= log2_int(self
.depth
, False)
220 port
= RecordObject([("addr", bsz
),
222 ("i_data", self
.width
)], name
=name
)
223 self
._wrports
[name
] = (port
, self
.memory
.write_port())
226 def elaborate(self
, platform
):
230 # read ports. has write-through detection (returns data written)
231 for name
, (rp
, rport
) in self
._rdports
.items():
232 setattr(m
.submodules
, "rp_"+name
, rport
)
233 wr_detect
= Signal(reset_less
=False)
234 comb
+= rport
.addr
.eq(rp
.addr
)
235 if self
.fwd_bus_mode
:
237 m
.d
.comb
+= wr_detect
.eq(0)
238 for _
, (wp
, wport
) in self
._wrports
.items():
239 addrmatch
= Signal(reset_less
=False)
240 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
241 with m
.If(wp
.wen
& addrmatch
):
242 m
.d
.comb
+= rp
.o_data
.eq(wp
.i_data
)
243 m
.d
.comb
+= wr_detect
.eq(1)
244 with m
.If(~wr_detect
):
245 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
248 ren_delay
= Signal
.like(rp
.ren
)
249 m
.d
.sync
+= ren_delay
.eq(rp
.ren
)
250 with m
.If(ren_delay
):
251 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
253 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
255 # write ports, delayed by one cycle (in the memory itself)
256 for name
, (port
, wp
) in self
._wrports
.items():
257 setattr(m
.submodules
, "wp_"+name
, wp
)
258 comb
+= wp
.addr
.eq(port
.addr
)
259 comb
+= wp
.en
.eq(port
.wen
)
260 comb
+= wp
.data
.eq(port
.i_data
)
265 class RegFile(Elaboratable
):
267 def __init__(self
, width
, depth
):
273 def read_port(self
, name
=None):
274 bsz
= int(log(self
.width
) / log(2))
275 port
= RecordObject([("addr", bsz
),
277 ("o_data", self
.width
)], name
=name
)
278 self
._rdports
.append(port
)
281 def write_port(self
, name
=None):
282 bsz
= int(log(self
.width
) / log(2))
283 port
= RecordObject([("addr", bsz
),
285 ("i_data", self
.width
)], name
=name
)
286 self
._wrports
.append(port
)
289 def elaborate(self
, platform
):
291 bsz
= int(log(self
.width
) / log(2))
292 regs
= Array(Signal(self
.width
, name
="reg") for _
in range(self
.depth
))
294 # read ports. has write-through detection (returns data written)
295 for rp
in self
._rdports
:
296 wr_detect
= Signal(reset_less
=False)
298 m
.d
.comb
+= wr_detect
.eq(0)
299 for wp
in self
._wrports
:
300 addrmatch
= Signal(reset_less
=False)
301 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
302 with m
.If(wp
.wen
& addrmatch
):
303 m
.d
.comb
+= rp
.o_data
.eq(wp
.i_data
)
304 m
.d
.comb
+= wr_detect
.eq(1)
305 with m
.If(~wr_detect
):
306 m
.d
.comb
+= rp
.o_data
.eq(regs
[rp
.addr
])
308 # write ports, delayed by one cycle
309 for wp
in self
._wrports
:
311 m
.d
.sync
+= regs
[wp
.addr
].eq(wp
.i_data
)
316 yield from self
._rdports
317 yield from self
._wrports
322 if isinstance(r
, RecordObject
):
328 def regfile_sim(dut
, rp
, wp
):
330 yield wp
.i_data
.eq(2)
340 data
= yield rp
.o_data
343 data
= yield rp
.o_data
346 data2
= yield rp
.o_data
355 yield wp
.i_data
.eq(6)
357 data
= yield rp
.o_data
364 data
= yield rp
.o_data
368 data
= yield rp
.o_data
372 def regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
):
373 print("regfile_array_sim")
374 yield wp
.i_data
.eq(2)
375 yield wp
.wen
.eq(1 << 1)
378 yield rp1
.ren
.eq(1 << 1)
380 data
= yield rp1
.o_data
385 yield rp1
.ren
.eq(1 << 5)
386 yield rp2
.ren
.eq(1 << 1)
387 yield wp
.wen
.eq(1 << 5)
388 yield wp
.i_data
.eq(6)
390 data
= yield rp1
.o_data
398 data1
= yield rp1
.o_data
401 data2
= yield rp2
.o_data
406 data
= yield rp1
.o_data
414 wp
= dut
.write_port()
415 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
416 with
open("test_regfile.il", "w") as f
:
419 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
421 dut
= RegFileMem(32, 8, True, False)
422 rp
= dut
.read_port("rp1")
423 wp
= dut
.write_port("wp1")
424 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
425 with
open("test_regmem.il", "w") as f
:
428 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regmem.vcd')
430 dut
= RegFileArray(32, 8, False)
431 rp1
= dut
.read_port("read1")
432 rp2
= dut
.read_port("read2")
433 wp
= dut
.write_port("write")
434 wp2
= dut
.write_port("write2")
436 print("ports", ports
)
437 vl
= rtlil
.convert(dut
, ports
=ports
)
438 with
open("test_regfile_array.il", "w") as f
:
441 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
),
442 vcd_name
='test_regfile_array.vcd')
445 if __name__
== '__main__':