1 # based on microwatt plru.vhdl
2 # https://github.com/antonblanchard/microwatt/blob/f67b1431655c291fc1c99857a5c1ef624d5b264c/plru.vhdl
4 # new PLRU API, once all users have migrated to new API in plru2.py, then
5 # plru2.py will be renamed to plru.py.
6 # IMPORTANT: since the API will change more, migration should be blocked on:
7 # https://bugs.libre-soc.org/show_bug.cgi?id=913
9 from nmigen
.hdl
.ir
import Elaboratable
, Display
, Signal
, Array
, Const
, Value
10 from nmigen
.hdl
.dsl
import Module
11 from nmigen
.cli
import rtlil
12 from nmigen
.lib
.coding
import Decoder
15 class PLRU(Elaboratable
):
16 r
""" PLRU - Pseudo Least Recently Used Replacement
18 IMPORTANT: since the API will change more, migration should be blocked on:
19 https://bugs.libre-soc.org/show_bug.cgi?id=913
33 def __init__(self
, log2_num_ways
, debug
=False):
34 # type: (int, bool) -> None
36 IMPORTANT: since the API will change more, migration should be blocked on:
37 https://bugs.libre-soc.org/show_bug.cgi?id=913
41 the log-base-2 of the number of cache ways -- BITS in plru.vhdl
43 true if this should print debugging messages at simulation time.
45 assert log2_num_ways
> 0
46 self
.log2_num_ways
= log2_num_ways
48 self
.acc_i
= Signal(log2_num_ways
)
49 self
.acc_en_i
= Signal()
50 self
.lru_o
= Signal(log2_num_ways
)
53 return Signal(name
=f
"tree_{i}", reset
=0)
55 # original vhdl has array 1 too big, last entry is never used,
56 # subtract 1 to compensate
57 self
._tree
= Array(mk_tree(i
) for i
in range(self
.num_ways
- 1))
58 """ exposed only for testing """
60 def mk_node(i
, prefix
):
61 return Signal(range(self
.num_ways
), name
=f
"{prefix}_node_{i}",
64 nodes_range
= range(self
.log2_num_ways
)
66 self
._get
_lru
_nodes
= [mk_node(i
, "get_lru") for i
in nodes_range
]
67 """ exposed only for testing """
69 self
._upd
_lru
_nodes
= [mk_node(i
, "upd_lru") for i
in nodes_range
]
70 """ exposed only for testing """
74 return 1 << self
.log2_num_ways
76 def _display(self
, msg
, *args
):
79 # work around not yet having
80 # https://gitlab.com/nmigen/nmigen/-/merge_requests/10
81 # by sending through Value.cast()
82 return [Display(msg
, *map(Value
.cast
, args
))]
84 def _get_lru(self
, m
):
85 """ get_lru process in plru.vhdl """
86 # XXX Check if we can turn that into a little ROM instead that
87 # takes the tree bit vector and returns the LRU. See if it's better
88 # in term of FPGA resource usage...
89 m
.d
.comb
+= self
._get
_lru
_nodes
[0].eq(0)
90 for i
in range(self
.log2_num_ways
):
91 node
= self
._get
_lru
_nodes
[i
]
92 val
= self
._tree
[node
]
93 m
.d
.comb
+= self
._display
("GET: i:%i node:%#x val:%i",
95 m
.d
.comb
+= self
.lru_o
[self
.log2_num_ways
- 1 - i
].eq(val
)
96 if i
!= self
.log2_num_ways
- 1:
97 # modified from microwatt version, it uses `node * 2` value
98 # to index into tree, rather than using node like is used
99 # earlier in this loop iteration
102 m
.d
.comb
+= self
._get
_lru
_nodes
[i
+ 1].eq(node
+ 2)
104 m
.d
.comb
+= self
._get
_lru
_nodes
[i
+ 1].eq(node
+ 1)
106 def _update_lru(self
, m
):
107 """ update_lru process in plru.vhdl """
108 with m
.If(self
.acc_en_i
):
109 m
.d
.comb
+= self
._upd
_lru
_nodes
[0].eq(0)
110 for i
in range(self
.log2_num_ways
):
111 node
= self
._upd
_lru
_nodes
[i
]
112 abit
= self
.acc_i
[self
.log2_num_ways
- 1 - i
]
114 self
._tree
[node
].eq(~abit
),
115 self
._display
("UPD: i:%i node:%#x val:%i",
118 if i
!= self
.log2_num_ways
- 1:
121 m
.d
.comb
+= self
._upd
_lru
_nodes
[i
+ 1].eq(node
+ 2)
123 m
.d
.comb
+= self
._upd
_lru
_nodes
[i
+ 1].eq(node
+ 1)
125 def elaborate(self
, platform
=None):
140 # FIXME: convert PLRUs to new API
141 # class PLRUs(Elaboratable):
142 # def __init__(self, n_plrus, n_bits):
143 # self.n_plrus = n_plrus
144 # self.n_bits = n_bits
145 # self.valid = Signal()
146 # self.way = Signal(n_bits)
147 # self.index = Signal(n_plrus.bit_length())
148 # self.isel = Signal(n_plrus.bit_length())
149 # self.o_index = Signal(n_bits)
151 # def elaborate(self, platform):
152 # """Generate TLB PLRUs
157 # if self.n_plrus == 0:
160 # # Binary-to-Unary one-hot, enabled by valid
161 # m.submodules.te = te = Decoder(self.n_plrus)
162 # comb += te.n.eq(~self.valid)
163 # comb += te.i.eq(self.index)
165 # out = Array(Signal(self.n_bits, name="plru_out%d" % x)
166 # for x in range(self.n_plrus))
168 # for i in range(self.n_plrus):
170 # m.submodules["plru_%d" % i] = plru = PLRU(self.n_bits)
172 # comb += plru.acc_en.eq(te.o[i])
173 # comb += plru.acc_i.eq(self.way)
174 # comb += out[i].eq(plru.lru_o)
176 # # select output based on index
177 # comb += self.o_index.eq(out[self.isel])
182 # return [self.valid, self.way, self.index, self.isel, self.o_index]
185 if __name__
== '__main__':
187 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
188 with
open("test_plru.il", "w") as f
:
192 # vl = rtlil.convert(dut, ports=dut.ports())
193 # with open("test_plrus.il", "w") as f: