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):
114 self
.regs
= Array(Register(width
, synced
=synced
,
115 writethru
=fwd_bus_mode
) \
116 for _
in range(self
.depth
))
120 def read_reg_port(self
, name
=None):
122 for i
in range(self
.depth
):
123 port
= self
.regs
[i
].read_port("%s%d" % (name
, i
))
127 def write_reg_port(self
, name
=None):
129 for i
in range(self
.depth
):
130 port
= self
.regs
[i
].write_port("%s%d" % (name
, i
))
134 def read_port(self
, name
=None):
135 regs
= self
.read_reg_port(name
)
137 port
= RecordObject([("ren", self
.depth
),
138 ("o_data", self
.width
)], name
)
139 self
._rdports
.append((regs
, port
))
142 def write_port(self
, name
=None):
143 regs
= self
.write_reg_port(name
)
145 port
= RecordObject([("wen", self
.depth
),
146 ("i_data", self
.width
)])
147 self
._wrports
.append((regs
, port
))
150 def _get_en_sig(self
, port
, typ
):
156 def elaborate(self
, platform
):
158 for i
, reg
in enumerate(self
.regs
):
159 setattr(m
.submodules
, "reg_%d" % i
, reg
)
166 for (regs
, p
) in self
._rdports
:
168 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'ren').eq(p
.ren
)
169 ror
= ortreereduce(list(regs
))
171 ren_delay
= Signal
.like(p
.ren
)
172 m
.d
.sync
+= ren_delay
.eq(p
.ren
)
173 with m
.If(ren_delay
):
174 m
.d
.comb
+= p
.o_data
.eq(ror
)
176 m
.d
.comb
+= p
.o_data
.eq(ror
)
177 for (regs
, p
) in self
._wrports
:
178 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'wen').eq(p
.wen
)
180 m
.d
.comb
+= r
.i_data
.eq(p
.i_data
)
192 class RegFileMem(Elaboratable
):
194 def __init__(self
, width
, depth
, fwd_bus_mode
=False, synced
=True):
195 self
.fwd_bus_mode
= fwd_bus_mode
197 self
.width
, self
.depth
= width
, depth
198 self
.memory
= Memory(width
=width
, depth
=depth
)
202 def read_port(self
, name
=None):
203 bsz
= log2_int(self
.depth
, False)
204 port
= RecordObject([("addr", bsz
),
206 ("o_data", self
.width
)], name
=name
)
211 self
._rdports
[name
] = (port
, self
.memory
.read_port(domain
=domain
))
214 def write_port(self
, name
=None):
215 bsz
= log2_int(self
.depth
, False)
216 port
= RecordObject([("addr", bsz
),
218 ("i_data", self
.width
)], name
=name
)
219 self
._wrports
[name
] = (port
, self
.memory
.write_port())
222 def elaborate(self
, platform
):
226 # read ports. has write-through detection (returns data written)
227 for name
, (rp
, rport
) in self
._rdports
.items():
228 setattr(m
.submodules
, "rp_"+name
, rport
)
229 wr_detect
= Signal(reset_less
=False)
230 comb
+= rport
.addr
.eq(rp
.addr
)
231 if self
.fwd_bus_mode
:
233 m
.d
.comb
+= wr_detect
.eq(0)
234 for _
, (wp
, wport
) in self
._wrports
.items():
235 addrmatch
= Signal(reset_less
=False)
236 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
237 with m
.If(wp
.wen
& addrmatch
):
238 m
.d
.comb
+= rp
.o_data
.eq(wp
.i_data
)
239 m
.d
.comb
+= wr_detect
.eq(1)
240 with m
.If(~wr_detect
):
241 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
244 ren_delay
= Signal
.like(rp
.ren
)
245 m
.d
.sync
+= ren_delay
.eq(rp
.ren
)
246 with m
.If(ren_delay
):
247 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
249 m
.d
.comb
+= rp
.o_data
.eq(rport
.data
)
251 # write ports, delayed by one cycle (in the memory itself)
252 for name
, (port
, wp
) in self
._wrports
.items():
253 setattr(m
.submodules
, "wp_"+name
, wp
)
254 comb
+= wp
.addr
.eq(port
.addr
)
255 comb
+= wp
.en
.eq(port
.wen
)
256 comb
+= wp
.data
.eq(port
.i_data
)
261 class RegFile(Elaboratable
):
263 def __init__(self
, width
, depth
):
269 def read_port(self
, name
=None):
270 bsz
= int(log(self
.width
) / log(2))
271 port
= RecordObject([("addr", bsz
),
273 ("o_data", self
.width
)], name
=name
)
274 self
._rdports
.append(port
)
277 def write_port(self
, name
=None):
278 bsz
= int(log(self
.width
) / log(2))
279 port
= RecordObject([("addr", bsz
),
281 ("i_data", self
.width
)], name
=name
)
282 self
._wrports
.append(port
)
285 def elaborate(self
, platform
):
287 bsz
= int(log(self
.width
) / log(2))
288 regs
= Array(Signal(self
.width
, name
="reg") for _
in range(self
.depth
))
290 # read ports. has write-through detection (returns data written)
291 for rp
in self
._rdports
:
292 wr_detect
= Signal(reset_less
=False)
294 m
.d
.comb
+= wr_detect
.eq(0)
295 for wp
in self
._wrports
:
296 addrmatch
= Signal(reset_less
=False)
297 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
298 with m
.If(wp
.wen
& addrmatch
):
299 m
.d
.comb
+= rp
.o_data
.eq(wp
.i_data
)
300 m
.d
.comb
+= wr_detect
.eq(1)
301 with m
.If(~wr_detect
):
302 m
.d
.comb
+= rp
.o_data
.eq(regs
[rp
.addr
])
304 # write ports, delayed by one cycle
305 for wp
in self
._wrports
:
307 m
.d
.sync
+= regs
[wp
.addr
].eq(wp
.i_data
)
312 yield from self
._rdports
313 yield from self
._wrports
318 if isinstance(r
, RecordObject
):
324 def regfile_sim(dut
, rp
, wp
):
326 yield wp
.i_data
.eq(2)
336 data
= yield rp
.o_data
339 data
= yield rp
.o_data
342 data2
= yield rp
.o_data
351 yield wp
.i_data
.eq(6)
353 data
= yield rp
.o_data
360 data
= yield rp
.o_data
364 data
= yield rp
.o_data
368 def regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
):
369 print("regfile_array_sim")
370 yield wp
.i_data
.eq(2)
371 yield wp
.wen
.eq(1 << 1)
374 yield rp1
.ren
.eq(1 << 1)
376 data
= yield rp1
.o_data
381 yield rp1
.ren
.eq(1 << 5)
382 yield rp2
.ren
.eq(1 << 1)
383 yield wp
.wen
.eq(1 << 5)
384 yield wp
.i_data
.eq(6)
386 data
= yield rp1
.o_data
394 data1
= yield rp1
.o_data
397 data2
= yield rp2
.o_data
402 data
= yield rp1
.o_data
410 wp
= dut
.write_port()
411 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
412 with
open("test_regfile.il", "w") as f
:
415 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
417 dut
= RegFileMem(32, 8, True, False)
418 rp
= dut
.read_port("rp1")
419 wp
= dut
.write_port("wp1")
420 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
421 with
open("test_regmem.il", "w") as f
:
424 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regmem.vcd')
426 dut
= RegFileArray(32, 8, False)
427 rp1
= dut
.read_port("read1")
428 rp2
= dut
.read_port("read2")
429 wp
= dut
.write_port("write")
430 wp2
= dut
.write_port("write2")
432 print("ports", ports
)
433 vl
= rtlil
.convert(dut
, ports
=ports
)
434 with
open("test_regfile_array.il", "w") as f
:
437 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
),
438 vcd_name
='test_regfile_array.vcd')
441 if __name__
== '__main__':