07cee2dd6773eb0644da0f9b5216d4f0a1dde7f6
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
,
203 attrs
={'syn_ramstyle': "block_ram"})
207 def read_port(self
, name
=None):
208 bsz
= log2_int(self
.depth
, False)
209 port
= RecordObject([("addr", bsz
),
211 ("o_data", self
.width
)], name
=name
)
216 self
._rdports
[name
] = (port
, self
.memory
.read_port(domain
=domain
))
219 def write_port(self
, name
=None):
220 bsz
= log2_int(self
.depth
, False)
221 port
= RecordObject([("addr", bsz
),
223 ("i_data", self
.width
)], name
=name
)
224 self
._wrports
[name
] = (port
, self
.memory
.write_port())
227 def elaborate(self
, platform
):
231 # read ports. has write-through detection (returns data written)
232 for name
, (rp
, rport
) in self
._rdports
.items():
233 setattr(m
.submodules
, "rp_"+name
, rport
)
234 wr_detect
= Signal(reset_less
=False)
235 comb
+= rport
.addr
.eq(rp
.addr
)
236 if self
.fwd_bus_mode
:
238 m
.d
.comb
+= wr_detect
.eq(0)
239 for _
, (wp
, wport
) in self
._wrports
.items():
240 addrmatch
= Signal(reset_less
=False)
241 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
242 with m
.If(wp
.wen
& addrmatch
):
243 m
.d
.comb
+= rp
.o_data
.eq(wp
.i_data
)
244 m
.d
.comb
+= wr_detect
.eq(1)
245 with m
.If(~wr_detect
):
246 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
249 ren_delay
= Signal
.like(rp
.ren
)
250 m
.d
.sync
+= ren_delay
.eq(rp
.ren
)
251 with m
.If(ren_delay
):
252 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
254 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
256 # write ports, delayed by one cycle (in the memory itself)
257 for name
, (port
, wp
) in self
._wrports
.items():
258 setattr(m
.submodules
, "wp_"+name
, wp
)
259 comb
+= wp
.addr
.eq(port
.addr
)
260 comb
+= wp
.en
.eq(port
.wen
)
261 comb
+= wp
.data
.eq(port
.i_data
)
266 class RegFile(Elaboratable
):
268 def __init__(self
, width
, depth
):
274 def read_port(self
, name
=None):
275 bsz
= int(log(self
.width
) / log(2))
276 port
= RecordObject([("addr", bsz
),
278 ("o_data", self
.width
)], name
=name
)
279 self
._rdports
.append(port
)
282 def write_port(self
, name
=None):
283 bsz
= int(log(self
.width
) / log(2))
284 port
= RecordObject([("addr", bsz
),
286 ("i_data", self
.width
)], name
=name
)
287 self
._wrports
.append(port
)
290 def elaborate(self
, platform
):
292 bsz
= int(log(self
.width
) / log(2))
293 regs
= Array(Signal(self
.width
, name
="reg") for _
in range(self
.depth
))
295 # read ports. has write-through detection (returns data written)
296 for rp
in self
._rdports
:
297 wr_detect
= Signal(reset_less
=False)
299 m
.d
.comb
+= wr_detect
.eq(0)
300 for wp
in self
._wrports
:
301 addrmatch
= Signal(reset_less
=False)
302 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
303 with m
.If(wp
.wen
& addrmatch
):
304 m
.d
.comb
+= rp
.o_data
.eq(wp
.i_data
)
305 m
.d
.comb
+= wr_detect
.eq(1)
306 with m
.If(~wr_detect
):
307 m
.d
.comb
+= rp
.o_data
.eq(regs
[rp
.addr
])
309 # write ports, delayed by one cycle
310 for wp
in self
._wrports
:
312 m
.d
.sync
+= regs
[wp
.addr
].eq(wp
.i_data
)
317 yield from self
._rdports
318 yield from self
._wrports
323 if isinstance(r
, RecordObject
):
329 def regfile_sim(dut
, rp
, wp
):
331 yield wp
.i_data
.eq(2)
341 data
= yield rp
.o_data
344 data
= yield rp
.o_data
347 data2
= yield rp
.o_data
356 yield wp
.i_data
.eq(6)
358 data
= yield rp
.o_data
365 data
= yield rp
.o_data
369 data
= yield rp
.o_data
373 def regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
):
374 print("regfile_array_sim")
375 yield wp
.i_data
.eq(2)
376 yield wp
.wen
.eq(1 << 1)
379 yield rp1
.ren
.eq(1 << 1)
381 data
= yield rp1
.o_data
386 yield rp1
.ren
.eq(1 << 5)
387 yield rp2
.ren
.eq(1 << 1)
388 yield wp
.wen
.eq(1 << 5)
389 yield wp
.i_data
.eq(6)
391 data
= yield rp1
.o_data
399 data1
= yield rp1
.o_data
402 data2
= yield rp2
.o_data
407 data
= yield rp1
.o_data
415 wp
= dut
.write_port()
416 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
417 with
open("test_regfile.il", "w") as f
:
420 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
422 dut
= RegFileMem(32, 8, True, False)
423 rp
= dut
.read_port("rp1")
424 wp
= dut
.write_port("wp1")
425 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
426 with
open("test_regmem.il", "w") as f
:
429 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regmem.vcd')
431 dut
= RegFileArray(32, 8, False)
432 rp1
= dut
.read_port("read1")
433 rp2
= dut
.read_port("read2")
434 wp
= dut
.write_port("write")
435 wp2
= dut
.write_port("write2")
437 print("ports", ports
)
438 vl
= rtlil
.convert(dut
, ports
=ports
)
439 with
open("test_regfile_array.il", "w") as f
:
442 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
),
443 vcd_name
='test_regfile_array.vcd')
446 if __name__
== '__main__':