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
,
60 attrs
={'syn_ramstyle': "block_ram"})
67 # read ports. has write-through detection (returns data written)
68 for rp
in self
._rdports
:
69 domain
+= rp
.o_data
.eq(0)
72 wr_detect
= Signal(reset_less
=False)
73 m
.d
.comb
+= wr_detect
.eq(0)
74 for wp
in self
._wrports
:
76 domain
+= rp
.o_data
.eq(wp
.i_data
)
77 m
.d
.comb
+= wr_detect
.eq(1)
78 with m
.If(~wr_detect
):
79 domain
+= rp
.o_data
.eq(reg
)
81 domain
+= rp
.o_data
.eq(reg
)
83 # write ports, delayed by 1 cycle
84 for wp
in self
._wrports
:
86 m
.d
.sync
+= reg
.eq(wp
.i_data
)
91 for p
in self
._rdports
:
93 for p
in self
._wrports
:
100 def ortreereduce(tree
, attr
="o_data"):
101 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
104 class RegFileArray(Elaboratable
):
106 """ an array-based register file (register having write-through capability)
107 that has no "address" decoder, instead it has individual write-en
108 and read-en signals (per port).
111 def __init__(self
, width
, depth
, synced
=True, fwd_bus_mode
=True,
118 self
.regs
= Array(Register(width
, synced
=synced
,
119 writethru
=fwd_bus_mode
,
125 def read_reg_port(self
, name
=None):
127 for i
in range(self
.depth
):
128 port
= self
.regs
[i
].read_port("%s%d" % (name
, i
))
132 def write_reg_port(self
, name
=None):
134 for i
in range(self
.depth
):
135 port
= self
.regs
[i
].write_port("%s%d" % (name
, i
))
139 def read_port(self
, name
=None):
140 regs
= self
.read_reg_port(name
)
142 port
= RecordObject([("ren", self
.depth
),
143 ("o_data", self
.width
)], name
)
144 self
._rdports
.append((regs
, port
))
147 def write_port(self
, name
=None):
148 regs
= self
.write_reg_port(name
)
150 port
= RecordObject([("wen", self
.depth
),
151 ("i_data", self
.width
)])
152 self
._wrports
.append((regs
, port
))
155 def _get_en_sig(self
, port
, typ
):
161 def elaborate(self
, platform
):
163 for i
, reg
in enumerate(self
.regs
):
164 setattr(m
.submodules
, "reg_%d" % i
, reg
)
171 for (regs
, p
) in self
._rdports
:
173 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'ren').eq(p
.ren
)
174 ror
= ortreereduce(list(regs
))
176 ren_delay
= Signal
.like(p
.ren
)
177 m
.d
.sync
+= ren_delay
.eq(p
.ren
)
178 with m
.If(ren_delay
):
179 m
.d
.comb
+= p
.o_data
.eq(ror
)
181 m
.d
.comb
+= p
.o_data
.eq(ror
)
182 for (regs
, p
) in self
._wrports
:
183 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'wen').eq(p
.wen
)
185 m
.d
.comb
+= r
.i_data
.eq(p
.i_data
)
197 class RegFileMem(Elaboratable
):
199 def __init__(self
, width
, depth
, fwd_bus_mode
=False, synced
=True):
200 self
.fwd_bus_mode
= fwd_bus_mode
202 self
.width
, self
.depth
= width
, depth
203 self
.memory
= Memory(width
=width
, depth
=depth
,
204 attrs
={'syn_ramstyle': "block_ram"})
208 def read_port(self
, name
=None):
209 bsz
= log2_int(self
.depth
, False)
210 port
= RecordObject([("addr", bsz
),
212 ("o_data", self
.width
)], name
=name
)
217 self
._rdports
[name
] = (port
, self
.memory
.read_port(domain
=domain
))
220 def write_port(self
, name
=None):
221 bsz
= log2_int(self
.depth
, False)
222 port
= RecordObject([("addr", bsz
),
224 ("i_data", self
.width
)], name
=name
)
225 self
._wrports
[name
] = (port
, self
.memory
.write_port())
228 def elaborate(self
, platform
):
232 # read ports. has write-through detection (returns data written)
233 for name
, (rp
, rport
) in self
._rdports
.items():
234 setattr(m
.submodules
, "rp_"+name
, rport
)
235 wr_detect
= Signal(reset_less
=False)
236 comb
+= rport
.addr
.eq(rp
.addr
)
237 if self
.fwd_bus_mode
:
239 m
.d
.comb
+= wr_detect
.eq(0)
240 for _
, (wp
, wport
) in self
._wrports
.items():
241 addrmatch
= Signal(reset_less
=False)
242 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
243 with m
.If(wp
.wen
& addrmatch
):
244 m
.d
.comb
+= rp
.o_data
.eq(wp
.i_data
)
245 m
.d
.comb
+= wr_detect
.eq(1)
246 with m
.If(~wr_detect
):
247 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
250 ren_delay
= Signal
.like(rp
.ren
)
251 m
.d
.sync
+= ren_delay
.eq(rp
.ren
)
252 with m
.If(ren_delay
):
253 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
255 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
257 # write ports, delayed by one cycle (in the memory itself)
258 for name
, (port
, wp
) in self
._wrports
.items():
259 setattr(m
.submodules
, "wp_"+name
, wp
)
260 comb
+= wp
.addr
.eq(port
.addr
)
261 comb
+= wp
.en
.eq(port
.wen
)
262 comb
+= wp
.data
.eq(port
.i_data
)
267 class RegFile(Elaboratable
):
269 def __init__(self
, width
, depth
):
275 def read_port(self
, name
=None):
276 bsz
= int(log(self
.width
) / log(2))
277 port
= RecordObject([("addr", bsz
),
279 ("o_data", self
.width
)], name
=name
)
280 self
._rdports
.append(port
)
283 def write_port(self
, name
=None):
284 bsz
= int(log(self
.width
) / log(2))
285 port
= RecordObject([("addr", bsz
),
287 ("i_data", self
.width
)], name
=name
)
288 self
._wrports
.append(port
)
291 def elaborate(self
, platform
):
293 bsz
= int(log(self
.width
) / log(2))
294 regs
= Array(Signal(self
.width
, name
="reg",
295 attrs
={'syn_ramstyle': "block_ram"}) \
296 for _
in range(self
.depth
))
298 # read ports. has write-through detection (returns data written)
299 for rp
in self
._rdports
:
300 wr_detect
= Signal(reset_less
=False)
302 m
.d
.comb
+= wr_detect
.eq(0)
303 for wp
in self
._wrports
:
304 addrmatch
= Signal(reset_less
=False)
305 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
306 with m
.If(wp
.wen
& addrmatch
):
307 m
.d
.comb
+= rp
.o_data
.eq(wp
.i_data
)
308 m
.d
.comb
+= wr_detect
.eq(1)
309 with m
.If(~wr_detect
):
310 m
.d
.comb
+= rp
.o_data
.eq(regs
[rp
.addr
])
312 # write ports, delayed by one cycle
313 for wp
in self
._wrports
:
315 m
.d
.sync
+= regs
[wp
.addr
].eq(wp
.i_data
)
320 yield from self
._rdports
321 yield from self
._wrports
326 if isinstance(r
, RecordObject
):
332 def regfile_sim(dut
, rp
, wp
):
334 yield wp
.i_data
.eq(2)
344 data
= yield rp
.o_data
347 data
= yield rp
.o_data
350 data2
= yield rp
.o_data
359 yield wp
.i_data
.eq(6)
361 data
= yield rp
.o_data
368 data
= yield rp
.o_data
372 data
= yield rp
.o_data
376 def regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
):
377 print("regfile_array_sim")
378 yield wp
.i_data
.eq(2)
379 yield wp
.wen
.eq(1 << 1)
382 yield rp1
.ren
.eq(1 << 1)
384 data
= yield rp1
.o_data
389 yield rp1
.ren
.eq(1 << 5)
390 yield rp2
.ren
.eq(1 << 1)
391 yield wp
.wen
.eq(1 << 5)
392 yield wp
.i_data
.eq(6)
394 data
= yield rp1
.o_data
402 data1
= yield rp1
.o_data
405 data2
= yield rp2
.o_data
410 data
= yield rp1
.o_data
418 wp
= dut
.write_port()
419 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
420 with
open("test_regfile.il", "w") as f
:
423 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
425 dut
= RegFileMem(32, 8, True, False)
426 rp
= dut
.read_port("rp1")
427 wp
= dut
.write_port("wp1")
428 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
429 with
open("test_regmem.il", "w") as f
:
432 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regmem.vcd')
434 dut
= RegFileArray(32, 8, False)
435 rp1
= dut
.read_port("read1")
436 rp2
= dut
.read_port("read2")
437 wp
= dut
.write_port("write")
438 wp2
= dut
.write_port("write2")
440 print("ports", ports
)
441 vl
= rtlil
.convert(dut
, ports
=ports
)
442 with
open("test_regfile_array.il", "w") as f
:
445 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
),
446 vcd_name
='test_regfile_array.vcd')
449 if __name__
== '__main__':