503ce8ee289b920f4d34d6f05bc82190733c0977
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):
37 self
.writethru
= writethru
41 def read_port(self
, name
=None):
42 port
= RecordObject([("ren", 1),
43 ("data_o", self
.width
)],
45 self
._rdports
.append(port
)
48 def write_port(self
, name
=None):
49 port
= RecordObject([("wen", 1),
50 ("data_i", self
.width
)],
52 self
._wrports
.append(port
)
55 def elaborate(self
, platform
):
57 self
.reg
= reg
= Signal(self
.width
, name
="reg")
59 # read ports. has write-through detection (returns data written)
60 for rp
in self
._rdports
:
61 with m
.If(rp
.ren
== 1):
63 wr_detect
= Signal(reset_less
=False)
64 m
.d
.comb
+= wr_detect
.eq(0)
65 for wp
in self
._wrports
:
67 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
68 m
.d
.comb
+= wr_detect
.eq(1)
69 with m
.If(~wr_detect
):
70 m
.d
.comb
+= rp
.data_o
.eq(reg
)
72 m
.d
.comb
+= rp
.data_o
.eq(reg
)
74 m
.d
.comb
+= rp
.data_o
.eq(0)
76 # write ports, delayed by 1 cycle
77 for wp
in self
._wrports
:
79 m
.d
.sync
+= reg
.eq(wp
.data_i
)
84 for p
in self
._rdports
:
86 for p
in self
._wrports
:
93 def ortreereduce(tree
, attr
="data_o"):
94 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
97 class RegFileArray(Elaboratable
):
99 """ an array-based register file (register having write-through capability)
100 that has no "address" decoder, instead it has individual write-en
101 and read-en signals (per port).
104 def __init__(self
, width
, depth
):
107 self
.regs
= Array(Register(width
) for _
in range(self
.depth
))
111 def read_reg_port(self
, name
=None):
113 for i
in range(self
.depth
):
114 port
= self
.regs
[i
].read_port("%s%d" % (name
, i
))
118 def write_reg_port(self
, name
=None):
120 for i
in range(self
.depth
):
121 port
= self
.regs
[i
].write_port("%s%d" % (name
, i
))
125 def read_port(self
, name
=None):
126 regs
= self
.read_reg_port(name
)
128 port
= RecordObject([("ren", self
.depth
),
129 ("data_o", self
.width
)], name
)
130 self
._rdports
.append((regs
, port
))
133 def write_port(self
, name
=None):
134 regs
= self
.write_reg_port(name
)
136 port
= RecordObject([("wen", self
.depth
),
137 ("data_i", self
.width
)])
138 self
._wrports
.append((regs
, port
))
141 def _get_en_sig(self
, port
, typ
):
147 def elaborate(self
, platform
):
149 for i
, reg
in enumerate(self
.regs
):
150 setattr(m
.submodules
, "reg_%d" % i
, reg
)
152 for (regs
, p
) in self
._rdports
:
154 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'ren').eq(p
.ren
)
155 ror
= ortreereduce(list(regs
))
156 m
.d
.comb
+= p
.data_o
.eq(ror
)
157 for (regs
, p
) in self
._wrports
:
158 m
.d
.comb
+= self
._get
_en
_sig
(regs
, 'wen').eq(p
.wen
)
160 m
.d
.comb
+= r
.data_i
.eq(p
.data_i
)
172 class RegFileMem(Elaboratable
):
174 def __init__(self
, width
, depth
):
175 self
.width
, self
.depth
= width
, depth
176 self
.memory
= Memory(width
=width
, depth
=depth
)
180 def read_port(self
, name
=None):
181 bsz
= log2_int(self
.depth
, False)
182 port
= RecordObject([("addr", bsz
),
184 ("data_o", self
.width
)], name
=name
)
185 self
._rdports
[name
] = (port
, self
.memory
.read_port(domain
="comb"))
188 def write_port(self
, name
=None):
189 bsz
= log2_int(self
.depth
, False)
190 port
= RecordObject([("addr", bsz
),
192 ("data_i", self
.width
)], name
=name
)
193 self
._wrports
[name
] = (port
, self
.memory
.write_port())
196 def elaborate(self
, platform
):
200 # read ports. has write-through detection (returns data written)
201 for name
, (rp
, rport
) in self
._rdports
.items():
202 setattr(m
.submodules
, "rp_"+name
, rport
)
203 wr_detect
= Signal(reset_less
=False)
204 comb
+= rport
.addr
.eq(rp
.addr
)
206 m
.d
.comb
+= wr_detect
.eq(0)
207 for _
, (wp
, wport
) in self
._wrports
.items():
208 addrmatch
= Signal(reset_less
=False)
209 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
210 with m
.If(wp
.wen
& addrmatch
):
211 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
212 m
.d
.comb
+= wr_detect
.eq(1)
213 with m
.If(~wr_detect
):
214 m
.d
.comb
+= rp
.data_o
.eq(rport
.data
)
216 # write ports, delayed by one cycle (in the memory itself)
217 for name
, (port
, wp
) in self
._wrports
.items():
218 setattr(m
.submodules
, "wp_"+name
, wp
)
219 comb
+= wp
.addr
.eq(port
.addr
)
220 comb
+= wp
.en
.eq(port
.wen
)
221 comb
+= wp
.data
.eq(port
.data_i
)
226 class RegFile(Elaboratable
):
228 def __init__(self
, width
, depth
):
234 def read_port(self
, name
=None):
235 bsz
= int(log(self
.width
) / log(2))
236 port
= RecordObject([("addr", bsz
),
238 ("data_o", self
.width
)], name
=name
)
239 self
._rdports
.append(port
)
242 def write_port(self
, name
=None):
243 bsz
= int(log(self
.width
) / log(2))
244 port
= RecordObject([("addr", bsz
),
246 ("data_i", self
.width
)], name
=name
)
247 self
._wrports
.append(port
)
250 def elaborate(self
, platform
):
252 bsz
= int(log(self
.width
) / log(2))
253 regs
= Array(Signal(self
.width
, name
="reg") for _
in range(self
.depth
))
255 # read ports. has write-through detection (returns data written)
256 for rp
in self
._rdports
:
257 wr_detect
= Signal(reset_less
=False)
259 m
.d
.comb
+= wr_detect
.eq(0)
260 for wp
in self
._wrports
:
261 addrmatch
= Signal(reset_less
=False)
262 m
.d
.comb
+= addrmatch
.eq(wp
.addr
== rp
.addr
)
263 with m
.If(wp
.wen
& addrmatch
):
264 m
.d
.comb
+= rp
.data_o
.eq(wp
.data_i
)
265 m
.d
.comb
+= wr_detect
.eq(1)
266 with m
.If(~wr_detect
):
267 m
.d
.comb
+= rp
.data_o
.eq(regs
[rp
.addr
])
269 # write ports, delayed by one cycle
270 for wp
in self
._wrports
:
272 m
.d
.sync
+= regs
[wp
.addr
].eq(wp
.data_i
)
277 yield from self
._rdports
278 yield from self
._wrports
283 if isinstance(r
, RecordObject
):
289 def regfile_sim(dut
, rp
, wp
):
291 yield wp
.data_i
.eq(2)
301 data
= yield rp
.data_o
304 data
= yield rp
.data_o
307 data2
= yield rp
.data_o
316 yield wp
.data_i
.eq(6)
318 data
= yield rp
.data_o
325 data
= yield rp
.data_o
329 data
= yield rp
.data_o
333 def regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
):
334 print("regfile_array_sim")
335 yield wp
.data_i
.eq(2)
336 yield wp
.wen
.eq(1 << 1)
339 yield rp1
.ren
.eq(1 << 1)
341 data
= yield rp1
.data_o
346 yield rp1
.ren
.eq(1 << 5)
347 yield rp2
.ren
.eq(1 << 1)
348 yield wp
.wen
.eq(1 << 5)
349 yield wp
.data_i
.eq(6)
351 data
= yield rp1
.data_o
359 data1
= yield rp1
.data_o
362 data2
= yield rp2
.data_o
367 data
= yield rp1
.data_o
375 wp
= dut
.write_port()
376 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
377 with
open("test_regfile.il", "w") as f
:
380 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regfile.vcd')
382 dut
= RegFileMem(32, 8)
383 rp
= dut
.read_port("rp1")
384 wp
= dut
.write_port("wp1")
385 vl
= rtlil
.convert(dut
)#, ports=dut.ports())
386 with
open("test_regmem.il", "w") as f
:
389 run_simulation(dut
, regfile_sim(dut
, rp
, wp
), vcd_name
='test_regmem.vcd')
391 dut
= RegFileArray(32, 8)
392 rp1
= dut
.read_port("read1")
393 rp2
= dut
.read_port("read2")
394 wp
= dut
.write_port("write")
395 wp2
= dut
.write_port("write2")
397 print("ports", ports
)
398 vl
= rtlil
.convert(dut
, ports
=ports
)
399 with
open("test_regfile_array.il", "w") as f
:
402 run_simulation(dut
, regfile_array_sim(dut
, rp1
, rp2
, wp
, wp2
),
403 vcd_name
='test_regfile_array.vcd')
406 if __name__
== '__main__':