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 ("data_o", self
.width
)],
47 self
._rdports
.append(port
)
50 def write_port(self
, name
=None):
51 port
= RecordObject([("wen", 1),
52 ("data_i", 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
.data_o
.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
.data_o
.eq(wp
.data_i
)
76 m
.d
.comb
+= wr_detect
.eq(1)
77 with m
.If(~wr_detect
):
78 domain
+= rp
.data_o
.eq(reg
)
80 domain
+= rp
.data_o
.eq(reg
)
82 # write ports, delayed by 1 cycle
83 for wp
in self
._wrports
:
85 m
.d
.sync
+= reg
.eq(wp
.data_i
)
90 for p
in self
._rdports
:
92 for p
in self
._wrports
:
99 def ortreereduce(tree
, attr
="data_o"):
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):
114 self
.regs
= Array(Register(width
, synced
=synced
) \
115 for _
in range(self
.depth
))
119 def read_reg_port(self
, name
=None):
121 for i
in range(self
.depth
):
122 port
= self
.regs
[i
].read_port("%s%d" % (name
, i
))
126 def write_reg_port(self
, name
=None):
128 for i
in range(self
.depth
):
129 port
= self
.regs
[i
].write_port("%s%d" % (name
, i
))
133 def read_port(self
, name
=None):
134 regs
= self
.read_reg_port(name
)
136 port
= RecordObject([("ren", self
.depth
),
137 ("data_o", self
.width
)], name
)
138 self
._rdports
.append((regs
, port
))
141 def write_port(self
, name
=None):
142 regs
= self
.write_reg_port(name
)
144 port
= RecordObject([("wen", self
.depth
),
145 ("data_i", self
.width
)])
146 self
._wrports
.append((regs
, port
))
149 def _get_en_sig(self
, port
, typ
):
155 def elaborate(self
, platform
):
157 for i
, reg
in enumerate(self
.regs
):
158 setattr(m
.submodules
, "reg_%d" % i
, reg
)
165 for (regs
, p
) in self
._rdports
:
167 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'ren').eq(p
.ren
)
168 ror
= ortreereduce(list(regs
))
170 ren_delay
= Signal
.like(p
.ren
)
171 m
.d
.sync
+= ren_delay
.eq(p
.ren
)
172 with m
.If(ren_delay
):
173 m
.d
.comb
+= p
.data_o
.eq(ror
)
175 m
.d
.comb
+= p
.data_o
.eq(ror
)
176 for (regs
, p
) in self
._wrports
:
177 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'wen').eq(p
.wen
)
179 m
.d
.comb
+= r
.data_i
.eq(p
.data_i
)
191 class RegFileMem(Elaboratable
):
193 def __init__(self
, width
, depth
, fwd_bus_mode
=False, synced
=True):
194 self
.fwd_bus_mode
= fwd_bus_mode
196 self
.width
, self
.depth
= width
, depth
197 self
.memory
= Memory(width
=width
, depth
=depth
)
201 def read_port(self
, name
=None):
202 bsz
= log2_int(self
.depth
, False)
203 port
= RecordObject([("addr", bsz
),
205 ("data_o", self
.width
)], name
=name
)
210 self
._rdports
[name
] = (port
, self
.memory
.read_port(domain
=domain
))
213 def write_port(self
, name
=None):
214 bsz
= log2_int(self
.depth
, False)
215 port
= RecordObject([("addr", bsz
),
217 ("data_i", self
.width
)], name
=name
)
218 self
._wrports
[name
] = (port
, self
.memory
.write_port())
221 def elaborate(self
, platform
):
225 # read ports. has write-through detection (returns data written)
226 for name
, (rp
, rport
) in self
._rdports
.items():
227 setattr(m
.submodules
, "rp_"+name
, rport
)
228 wr_detect
= Signal(reset_less
=False)
229 comb
+= rport
.addr
.eq(rp
.addr
)
230 if self
.fwd_bus_mode
:
232 m
.d
.comb
+= wr_detect
.eq(0)
233 for _
, (wp
, wport
) in self
._wrports
.items():
234 addrmatch
= Signal(reset_less
=False)
235 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
236 with m
.If(wp
.wen
& addrmatch
):
237 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
238 m
.d
.comb
+= wr_detect
.eq(1)
239 with m
.If(~wr_detect
):
240 m
.d
.comb
+= rp
.data_o
.eq(rport
.data
)
243 ren_delay
= Signal
.like(rp
.ren
)
244 m
.d
.sync
+= ren_delay
.eq(rp
.ren
)
245 with m
.If(ren_delay
):
246 m
.d
.comb
+= rp
.data_o
.eq(rport
.data
)
248 m
.d
.comb
+= rp
.data_o
.eq(rport
.data
)
250 # write ports, delayed by one cycle (in the memory itself)
251 for name
, (port
, wp
) in self
._wrports
.items():
252 setattr(m
.submodules
, "wp_"+name
, wp
)
253 comb
+= wp
.addr
.eq(port
.addr
)
254 comb
+= wp
.en
.eq(port
.wen
)
255 comb
+= wp
.data
.eq(port
.data_i
)
260 class RegFile(Elaboratable
):
262 def __init__(self
, width
, depth
):
268 def read_port(self
, name
=None):
269 bsz
= int(log(self
.width
) / log(2))
270 port
= RecordObject([("addr", bsz
),
272 ("data_o", self
.width
)], name
=name
)
273 self
._rdports
.append(port
)
276 def write_port(self
, name
=None):
277 bsz
= int(log(self
.width
) / log(2))
278 port
= RecordObject([("addr", bsz
),
280 ("data_i", self
.width
)], name
=name
)
281 self
._wrports
.append(port
)
284 def elaborate(self
, platform
):
286 bsz
= int(log(self
.width
) / log(2))
287 regs
= Array(Signal(self
.width
, name
="reg") for _
in range(self
.depth
))
289 # read ports. has write-through detection (returns data written)
290 for rp
in self
._rdports
:
291 wr_detect
= Signal(reset_less
=False)
293 m
.d
.comb
+= wr_detect
.eq(0)
294 for wp
in self
._wrports
:
295 addrmatch
= Signal(reset_less
=False)
296 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
297 with m
.If(wp
.wen
& addrmatch
):
298 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
299 m
.d
.comb
+= wr_detect
.eq(1)
300 with m
.If(~wr_detect
):
301 m
.d
.comb
+= rp
.data_o
.eq(regs
[rp
.addr
])
303 # write ports, delayed by one cycle
304 for wp
in self
._wrports
:
306 m
.d
.sync
+= regs
[wp
.addr
].eq(wp
.data_i
)
311 yield from self
._rdports
312 yield from self
._wrports
317 if isinstance(r
, RecordObject
):
323 def regfile_sim(dut
, rp
, wp
):
325 yield wp
.data_i
.eq(2)
335 data
= yield rp
.data_o
338 data
= yield rp
.data_o
341 data2
= yield rp
.data_o
350 yield wp
.data_i
.eq(6)
352 data
= yield rp
.data_o
359 data
= yield rp
.data_o
363 data
= yield rp
.data_o
367 def regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
):
368 print("regfile_array_sim")
369 yield wp
.data_i
.eq(2)
370 yield wp
.wen
.eq(1 << 1)
373 yield rp1
.ren
.eq(1 << 1)
375 data
= yield rp1
.data_o
380 yield rp1
.ren
.eq(1 << 5)
381 yield rp2
.ren
.eq(1 << 1)
382 yield wp
.wen
.eq(1 << 5)
383 yield wp
.data_i
.eq(6)
385 data
= yield rp1
.data_o
393 data1
= yield rp1
.data_o
396 data2
= yield rp2
.data_o
401 data
= yield rp1
.data_o
409 wp
= dut
.write_port()
410 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
411 with
open("test_regfile.il", "w") as f
:
414 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
416 dut
= RegFileMem(32, 8, True, False)
417 rp
= dut
.read_port("rp1")
418 wp
= dut
.write_port("wp1")
419 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
420 with
open("test_regmem.il", "w") as f
:
423 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regmem.vcd')
425 dut
= RegFileArray(32, 8, False)
426 rp1
= dut
.read_port("read1")
427 rp2
= dut
.read_port("read2")
428 wp
= dut
.write_port("write")
429 wp2
= dut
.write_port("write2")
431 print("ports", ports
)
432 vl
= rtlil
.convert(dut
, ports
=ports
)
433 with
open("test_regfile_array.il", "w") as f
:
436 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
),
437 vcd_name
='test_regfile_array.vcd')
440 if __name__
== '__main__':