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):
37 self
.writethru
= writethru
42 def read_port(self
, name
=None):
43 port
= RecordObject([("ren", 1),
44 ("data_o", self
.width
)],
46 self
._rdports
.append(port
)
49 def write_port(self
, name
=None):
50 port
= RecordObject([("wen", 1),
51 ("data_i", self
.width
)],
53 self
._wrports
.append(port
)
56 def elaborate(self
, platform
):
58 self
.reg
= reg
= Signal(self
.width
, name
="reg")
65 # read ports. has write-through detection (returns data written)
66 for rp
in self
._rdports
:
67 domain
+= rp
.data_o
.eq(0)
70 wr_detect
= Signal(reset_less
=False)
71 m
.d
.comb
+= wr_detect
.eq(0)
72 for wp
in self
._wrports
:
74 domain
+= rp
.data_o
.eq(wp
.data_i
)
75 m
.d
.comb
+= wr_detect
.eq(1)
76 with m
.If(~wr_detect
):
77 domain
+= rp
.data_o
.eq(reg
)
79 domain
+= rp
.data_o
.eq(reg
)
81 # write ports, delayed by 1 cycle
82 for wp
in self
._wrports
:
84 m
.d
.sync
+= reg
.eq(wp
.data_i
)
89 for p
in self
._rdports
:
91 for p
in self
._wrports
:
98 def ortreereduce(tree
, attr
="data_o"):
99 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
102 class RegFileArray(Elaboratable
):
104 """ an array-based register file (register having write-through capability)
105 that has no "address" decoder, instead it has individual write-en
106 and read-en signals (per port).
109 def __init__(self
, width
, depth
, synced
=True):
113 self
.regs
= Array(Register(width
, synced
=synced
) \
114 for _
in range(self
.depth
))
118 def read_reg_port(self
, name
=None):
120 for i
in range(self
.depth
):
121 port
= self
.regs
[i
].read_port("%s%d" % (name
, i
))
125 def write_reg_port(self
, name
=None):
127 for i
in range(self
.depth
):
128 port
= self
.regs
[i
].write_port("%s%d" % (name
, i
))
132 def read_port(self
, name
=None):
133 regs
= self
.read_reg_port(name
)
135 port
= RecordObject([("ren", self
.depth
),
136 ("data_o", self
.width
)], name
)
137 self
._rdports
.append((regs
, port
))
140 def write_port(self
, name
=None):
141 regs
= self
.write_reg_port(name
)
143 port
= RecordObject([("wen", self
.depth
),
144 ("data_i", self
.width
)])
145 self
._wrports
.append((regs
, port
))
148 def _get_en_sig(self
, port
, typ
):
154 def elaborate(self
, platform
):
156 for i
, reg
in enumerate(self
.regs
):
157 setattr(m
.submodules
, "reg_%d" % i
, reg
)
164 for (regs
, p
) in self
._rdports
:
166 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'ren').eq(p
.ren
)
167 ror
= ortreereduce(list(regs
))
169 ren_delay
= Signal
.like(p
.ren
)
170 m
.d
.sync
+= ren_delay
.eq(p
.ren
)
171 with m
.If(ren_delay
):
172 m
.d
.comb
+= p
.data_o
.eq(ror
)
174 m
.d
.comb
+= p
.data_o
.eq(ror
)
175 for (regs
, p
) in self
._wrports
:
176 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'wen').eq(p
.wen
)
178 m
.d
.comb
+= r
.data_i
.eq(p
.data_i
)
190 class RegFileMem(Elaboratable
):
192 def __init__(self
, width
, depth
, fwd_bus_mode
=False, synced
=True):
193 self
.fwd_bus_mode
= fwd_bus_mode
195 self
.width
, self
.depth
= width
, depth
196 self
.memory
= Memory(width
=width
, depth
=depth
)
200 def read_port(self
, name
=None):
201 bsz
= log2_int(self
.depth
, False)
202 port
= RecordObject([("addr", bsz
),
204 ("data_o", self
.width
)], name
=name
)
209 self
._rdports
[name
] = (port
, self
.memory
.read_port(domain
=domain
))
212 def write_port(self
, name
=None):
213 bsz
= log2_int(self
.depth
, False)
214 port
= RecordObject([("addr", bsz
),
216 ("data_i", self
.width
)], name
=name
)
217 self
._wrports
[name
] = (port
, self
.memory
.write_port())
220 def elaborate(self
, platform
):
224 # read ports. has write-through detection (returns data written)
225 for name
, (rp
, rport
) in self
._rdports
.items():
226 setattr(m
.submodules
, "rp_"+name
, rport
)
227 wr_detect
= Signal(reset_less
=False)
228 comb
+= rport
.addr
.eq(rp
.addr
)
229 if self
.fwd_bus_mode
:
231 m
.d
.comb
+= wr_detect
.eq(0)
232 for _
, (wp
, wport
) in self
._wrports
.items():
233 addrmatch
= Signal(reset_less
=False)
234 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
235 with m
.If(wp
.wen
& addrmatch
):
236 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
237 m
.d
.comb
+= wr_detect
.eq(1)
238 with m
.If(~wr_detect
):
239 m
.d
.comb
+= rp
.data_o
.eq(rport
.data
)
242 ren_delay
= Signal
.like(rp
.ren
)
243 m
.d
.sync
+= ren_delay
.eq(rp
.ren
)
244 with m
.If(ren_delay
):
245 m
.d
.comb
+= rp
.data_o
.eq(rport
.data
)
247 m
.d
.comb
+= rp
.data_o
.eq(rport
.data
)
249 # write ports, delayed by one cycle (in the memory itself)
250 for name
, (port
, wp
) in self
._wrports
.items():
251 setattr(m
.submodules
, "wp_"+name
, wp
)
252 comb
+= wp
.addr
.eq(port
.addr
)
253 comb
+= wp
.en
.eq(port
.wen
)
254 comb
+= wp
.data
.eq(port
.data_i
)
259 class RegFile(Elaboratable
):
261 def __init__(self
, width
, depth
):
267 def read_port(self
, name
=None):
268 bsz
= int(log(self
.width
) / log(2))
269 port
= RecordObject([("addr", bsz
),
271 ("data_o", self
.width
)], name
=name
)
272 self
._rdports
.append(port
)
275 def write_port(self
, name
=None):
276 bsz
= int(log(self
.width
) / log(2))
277 port
= RecordObject([("addr", bsz
),
279 ("data_i", self
.width
)], name
=name
)
280 self
._wrports
.append(port
)
283 def elaborate(self
, platform
):
285 bsz
= int(log(self
.width
) / log(2))
286 regs
= Array(Signal(self
.width
, name
="reg") for _
in range(self
.depth
))
288 # read ports. has write-through detection (returns data written)
289 for rp
in self
._rdports
:
290 wr_detect
= Signal(reset_less
=False)
292 m
.d
.comb
+= wr_detect
.eq(0)
293 for wp
in self
._wrports
:
294 addrmatch
= Signal(reset_less
=False)
295 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
296 with m
.If(wp
.wen
& addrmatch
):
297 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
298 m
.d
.comb
+= wr_detect
.eq(1)
299 with m
.If(~wr_detect
):
300 m
.d
.comb
+= rp
.data_o
.eq(regs
[rp
.addr
])
302 # write ports, delayed by one cycle
303 for wp
in self
._wrports
:
305 m
.d
.sync
+= regs
[wp
.addr
].eq(wp
.data_i
)
310 yield from self
._rdports
311 yield from self
._wrports
316 if isinstance(r
, RecordObject
):
322 def regfile_sim(dut
, rp
, wp
):
324 yield wp
.data_i
.eq(2)
334 data
= yield rp
.data_o
337 data
= yield rp
.data_o
340 data2
= yield rp
.data_o
349 yield wp
.data_i
.eq(6)
351 data
= yield rp
.data_o
358 data
= yield rp
.data_o
362 data
= yield rp
.data_o
366 def regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
):
367 print("regfile_array_sim")
368 yield wp
.data_i
.eq(2)
369 yield wp
.wen
.eq(1 << 1)
372 yield rp1
.ren
.eq(1 << 1)
374 data
= yield rp1
.data_o
379 yield rp1
.ren
.eq(1 << 5)
380 yield rp2
.ren
.eq(1 << 1)
381 yield wp
.wen
.eq(1 << 5)
382 yield wp
.data_i
.eq(6)
384 data
= yield rp1
.data_o
392 data1
= yield rp1
.data_o
395 data2
= yield rp2
.data_o
400 data
= yield rp1
.data_o
408 wp
= dut
.write_port()
409 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
410 with
open("test_regfile.il", "w") as f
:
413 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
415 dut
= RegFileMem(32, 8, True, False)
416 rp
= dut
.read_port("rp1")
417 wp
= dut
.write_port("wp1")
418 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
419 with
open("test_regmem.il", "w") as f
:
422 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regmem.vcd')
424 dut
= RegFileArray(32, 8, False)
425 rp1
= dut
.read_port("read1")
426 rp2
= dut
.read_port("read2")
427 wp
= dut
.write_port("write")
428 wp2
= dut
.write_port("write2")
430 print("ports", ports
)
431 vl
= rtlil
.convert(dut
, ports
=ports
)
432 with
open("test_regfile_array.il", "w") as f
:
435 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
),
436 vcd_name
='test_regfile_array.vcd')
439 if __name__
== '__main__':