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.
7 from nmigen
.hdl
.ir
import Elaboratable
, Display
, Signal
, Array
, Const
, Value
8 from nmigen
.hdl
.dsl
import Module
9 from nmigen
.cli
import rtlil
10 from nmigen
.lib
.coding
import Decoder
13 class PLRU(Elaboratable
):
14 r
""" PLRU - Pseudo Least Recently Used Replacement
28 def __init__(self
, log2_num_ways
, debug
=False):
29 # type: (int, bool) -> None
33 the log-base-2 of the number of cache ways -- BITS in plru.vhdl
35 true if this should print debugging messages at simulation time.
37 assert log2_num_ways
> 0
38 self
.log2_num_ways
= log2_num_ways
40 self
.acc_i
= Signal(log2_num_ways
)
41 self
.acc_en_i
= Signal()
42 self
.lru_o
= Signal(log2_num_ways
)
45 return Signal(name
=f
"tree_{i}", reset
=0)
47 # original vhdl has array 1 too big, last entry is never used,
48 # subtract 1 to compensate
49 self
._tree
= Array(mk_tree(i
) for i
in range(self
.num_ways
- 1))
50 """ exposed only for testing """
52 def mk_node(i
, prefix
):
53 return Signal(range(self
.num_ways
), name
=f
"{prefix}_node_{i}",
56 nodes_range
= range(self
.log2_num_ways
)
58 self
._get
_lru
_nodes
= [mk_node(i
, "get_lru") for i
in nodes_range
]
59 """ exposed only for testing """
61 self
._upd
_lru
_nodes
= [mk_node(i
, "upd_lru") for i
in nodes_range
]
62 """ exposed only for testing """
66 return 1 << self
.log2_num_ways
68 def _display(self
, msg
, *args
):
71 # work around not yet having
72 # https://gitlab.com/nmigen/nmigen/-/merge_requests/10
73 # by sending through Value.cast()
74 return [Display(msg
, *map(Value
.cast
, args
))]
76 def _get_lru(self
, m
):
77 """ get_lru process in plru.vhdl """
78 # XXX Check if we can turn that into a little ROM instead that
79 # takes the tree bit vector and returns the LRU. See if it's better
80 # in term of FPGA resource usage...
81 m
.d
.comb
+= self
._get
_lru
_nodes
[0].eq(0)
82 for i
in range(self
.log2_num_ways
):
83 node
= self
._get
_lru
_nodes
[i
]
84 val
= self
._tree
[node
]
85 m
.d
.comb
+= self
._display
("GET: i:%i node:%#x val:%i",
87 m
.d
.comb
+= self
.lru_o
[self
.log2_num_ways
- 1 - i
].eq(val
)
88 if i
!= self
.log2_num_ways
- 1:
89 # modified from microwatt version, it uses `node * 2` value
90 # to index into tree, rather than using node like is used
91 # earlier in this loop iteration
94 m
.d
.comb
+= self
._get
_lru
_nodes
[i
+ 1].eq(node
+ 2)
96 m
.d
.comb
+= self
._get
_lru
_nodes
[i
+ 1].eq(node
+ 1)
98 def _update_lru(self
, m
):
99 """ update_lru process in plru.vhdl """
100 with m
.If(self
.acc_en_i
):
101 m
.d
.comb
+= self
._upd
_lru
_nodes
[0].eq(0)
102 for i
in range(self
.log2_num_ways
):
103 node
= self
._upd
_lru
_nodes
[i
]
104 abit
= self
.acc_i
[self
.log2_num_ways
- 1 - i
]
106 self
._tree
[node
].eq(~abit
),
107 self
._display
("UPD: i:%i node:%#x val:%i",
110 if i
!= self
.log2_num_ways
- 1:
113 m
.d
.comb
+= self
._upd
_lru
_nodes
[i
+ 1].eq(node
+ 2)
115 m
.d
.comb
+= self
._upd
_lru
_nodes
[i
+ 1].eq(node
+ 1)
117 def elaborate(self
, platform
=None):
132 # FIXME: convert PLRUs to new API
133 # class PLRUs(Elaboratable):
134 # def __init__(self, n_plrus, n_bits):
135 # self.n_plrus = n_plrus
136 # self.n_bits = n_bits
137 # self.valid = Signal()
138 # self.way = Signal(n_bits)
139 # self.index = Signal(n_plrus.bit_length())
140 # self.isel = Signal(n_plrus.bit_length())
141 # self.o_index = Signal(n_bits)
143 # def elaborate(self, platform):
144 # """Generate TLB PLRUs
149 # if self.n_plrus == 0:
152 # # Binary-to-Unary one-hot, enabled by valid
153 # m.submodules.te = te = Decoder(self.n_plrus)
154 # comb += te.n.eq(~self.valid)
155 # comb += te.i.eq(self.index)
157 # out = Array(Signal(self.n_bits, name="plru_out%d" % x)
158 # for x in range(self.n_plrus))
160 # for i in range(self.n_plrus):
162 # m.submodules["plru_%d" % i] = plru = PLRU(self.n_bits)
164 # comb += plru.acc_en.eq(te.o[i])
165 # comb += plru.acc_i.eq(self.way)
166 # comb += out[i].eq(plru.lru_o)
168 # # select output based on index
169 # comb += self.o_index.eq(out[self.isel])
174 # return [self.valid, self.way, self.index, self.isel, self.o_index]
177 if __name__
== '__main__':
179 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
180 with
open("test_plru.il", "w") as f
:
184 # vl = rtlil.convert(dut, ports=dut.ports())
185 # with open("test_plrus.il", "w") as f: