2 # Copyright 2018 ETH Zurich and University of Bologna.
3 # Copyright and related rights are licensed under the Solderpad Hardware
4 # License, Version 0.51 (the "License"); you may not use this file except in
5 # compliance with the License. You may obtain a copy of the License at
6 # http:#solderpad.org/licenses/SHL-0.51. Unless required by applicable law
7 # or agreed to in writing, software, hardware and materials distributed under
8 # this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
9 # CONDITIONS OF ANY KIND, either express or implied. See the License for the
10 # specific language governing permissions and limitations under the License.
12 # Author: David Schaffenrath, TU Graz
13 # Author: Florian Zaruba, ETH Zurich
15 # Description: Translation Lookaside Buffer, SV39
16 # fully set-associative
18 Implementation in c++:
19 https://raw.githubusercontent.com/Tony-Hu/TreePLRU/master/TreePLRU.cpp
22 https://people.cs.clemson.edu/~mark/464/p_lru.txt
25 from nmigen
import Signal
, Module
, Cat
, Const
, Array
26 from nmigen
.cli
import verilog
, rtlil
27 from nmigen
.lib
.coding
import Encoder
29 from ptw
import TLBUpdate
, PTE
, ASID_WIDTH
31 from tlb_content
import TLBContent
36 def __init__(self
, tlb_entries
=8, asid_width
=8):
37 self
.tlb_entries
= tlb_entries
38 self
.asid_width
= asid_width
40 self
.flush_i
= Signal() # Flush signal
42 self
.lu_access_i
= Signal()
43 self
.lu_asid_i
= Signal(self
.asid_width
)
44 self
.lu_vaddr_i
= Signal(64)
45 self
.lu_content_o
= PTE()
46 self
.lu_is_2M_o
= Signal()
47 self
.lu_is_1G_o
= Signal()
48 self
.lu_hit_o
= Signal()
50 self
.pte_width
= len(self
.lu_content_o
.flatten())
51 self
.update_i
= TLBUpdate(asid_width
)
53 def elaborate(self
, platform
):
64 # SV39 defines three levels of page tables
65 m
.d
.comb
+= [ vpn0
.eq(self
.lu_vaddr_i
[12:21]),
66 vpn1
.eq(self
.lu_vaddr_i
[21:30]),
67 vpn2
.eq(self
.lu_vaddr_i
[30:39]),
71 for i
in range(self
.tlb_entries
):
72 tlc
= TLBContent(self
.pte_width
, self
.asid_width
)
73 setattr(m
.submodules
, "tc%d" % i
, tlc
)
76 tlc
.update_i
= self
.update_i
# saves a lot of graphviz links
77 m
.d
.comb
+= [tlc
.vpn0
.eq(vpn0
),
80 tlc
.flush_i
.eq(self
.flush_i
),
81 #tlc.update_i.eq(self.update_i),
82 tlc
.lu_asid_i
.eq(self
.lu_asid_i
)]
89 # use Encoder to select hit index
90 # XXX TODO: assert that there's only one valid entry (one lu_hit)
91 hitsel
= Encoder(self
.tlb_entries
)
92 m
.submodules
.hitsel
= hitsel
95 for i
in range(self
.tlb_entries
):
96 hits
.append(tc
[i
].lu_hit_o
)
97 m
.d
.comb
+= hitsel
.i
.eq(Cat(*hits
)) # (goes into plru as well)
100 active
= Signal(reset_less
=True)
101 m
.d
.comb
+= active
.eq(~hitsel
.n
)
103 # active hit, send selected as output
104 m
.d
.comb
+= [ self
.lu_is_1G_o
.eq(tc
[idx
].lu_is_1G_o
),
105 self
.lu_is_2M_o
.eq(tc
[idx
].lu_is_2M_o
),
107 self
.lu_content_o
.flatten().eq(tc
[idx
].lu_content_o
),
114 p
= PLRU(self
.tlb_entries
)
115 plru_tree
= Signal(p
.TLBSZ
)
116 m
.submodules
.plru
= p
118 # connect PLRU inputs/outputs
119 # XXX TODO: assert that there's only one valid entry (one replace_en)
121 for i
in range(self
.tlb_entries
):
122 en
.append(tc
[i
].replace_en_i
)
123 m
.d
.comb
+= [Cat(*en
).eq(p
.replace_en_o
), # output from PLRU into tags
124 p
.lu_hit
.eq(hitsel
.i
),
125 p
.lu_access_i
.eq(self
.lu_access_i
),
126 p
.plru_tree
.eq(plru_tree
)]
127 m
.d
.sync
+= plru_tree
.eq(p
.plru_tree_o
)
133 assert (self
.tlb_entries
% 2 == 0) and (self
.tlb_entries
> 1), \
134 "TLB size must be a multiple of 2 and greater than 1"
135 assert (self
.asid_width
>= 1), \
136 "ASID width must be at least 1"
142 function int countSetBits(logic[self.tlb_entries-1:0] vector);
143 automatic int count = 0;
144 foreach (vector[idx]) begin
145 count += vector[idx];
150 assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
151 else $error("More then one hit in TLB!"); $stop(); end
152 assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
153 else $error("More then one TLB entry selected for next replace!");
157 return [self
.flush_i
, self
.lu_access_i
,
158 self
.lu_asid_i
, self
.lu_vaddr_i
,
159 self
.lu_is_2M_o
, self
.lu_is_1G_o
, self
.lu_hit_o
,
160 ] + self
.lu_content_o
.ports() + self
.update_i
.ports()
162 if __name__
== '__main__':
164 vl
= rtlil
.convert(tlb
, ports
=tlb
.ports())
165 with
open("test_tlb.il", "w") as f
: