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
37 self
.flush_i
= Signal() # Flush signal
39 self
.lu_access_i
= Signal()
40 self
.lu_asid_i
= Signal(ASID_WIDTH
)
41 self
.lu_vaddr_i
= Signal(64)
42 self
.lu_content_o
= PTE()
43 self
.lu_is_2M_o
= Signal()
44 self
.lu_is_1G_o
= Signal()
45 self
.lu_hit_o
= Signal()
47 self
.pte_width
= len(self
.lu_content_o
.flatten())
48 self
.update_i
= TLBUpdate()
50 def elaborate(self
, platform
):
61 # SV39 defines three levels of page tables
62 m
.d
.comb
+= [ vpn0
.eq(self
.lu_vaddr_i
[12:21]),
63 vpn1
.eq(self
.lu_vaddr_i
[21:30]),
64 vpn2
.eq(self
.lu_vaddr_i
[30:39]),
68 for i
in range(TLB_ENTRIES
):
69 tlc
= TLBContent(self
.pte_width
, ASID_WIDTH
)
70 setattr(m
.submodules
, "tc%d" % i
, tlc
)
73 tlc
.update_i
= self
.update_i
# saves a lot of graphviz links
74 m
.d
.comb
+= [tlc
.vpn0
.eq(vpn0
),
77 tlc
.flush_i
.eq(self
.flush_i
),
78 #tlc.update_i.eq(self.update_i),
79 tlc
.lu_asid_i
.eq(self
.lu_asid_i
)]
86 # use Encoder to select hit index
87 # XXX TODO: assert that there's only one valid entry (one lu_hit)
88 hitsel
= Encoder(TLB_ENTRIES
)
89 m
.submodules
.hitsel
= hitsel
92 for i
in range(TLB_ENTRIES
):
93 hits
.append(tc
[i
].lu_hit_o
)
94 m
.d
.comb
+= hitsel
.i
.eq(Cat(*hits
)) # (goes into plru as well)
97 active
= Signal(reset_less
=True)
98 m
.d
.comb
+= active
.eq(~hitsel
.n
)
100 # active hit, send selected as output
101 m
.d
.comb
+= [ self
.lu_is_1G_o
.eq(tc
[idx
].lu_is_1G_o
),
102 self
.lu_is_2M_o
.eq(tc
[idx
].lu_is_2M_o
),
104 self
.lu_content_o
.flatten().eq(tc
[idx
].lu_content_o
),
111 p
= PLRU(TLB_ENTRIES
)
112 m
.submodules
.plru
= p
114 # connect PLRU inputs/outputs
115 # XXX TODO: assert that there's only one valid entry (one replace_en)
117 for i
in range(TLB_ENTRIES
):
118 en
.append(tc
[i
].replace_en_i
)
119 m
.d
.comb
+= [Cat(*en
).eq(p
.replace_en_o
), # output from PLRU into tags
120 p
.lu_hit
.eq(hitsel
.i
),
121 p
.lu_access_i
.eq(self
.lu_access_i
)]
127 assert (TLB_ENTRIES
% 2 == 0) and (TLB_ENTRIES
> 1), \
128 "TLB size must be a multiple of 2 and greater than 1"
129 assert (ASID_WIDTH
>= 1), \
130 "ASID width must be at least 1"
136 function int countSetBits(logic[TLB_ENTRIES-1:0] vector);
137 automatic int count = 0;
138 foreach (vector[idx]) begin
139 count += vector[idx];
144 assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
145 else $error("More then one hit in TLB!"); $stop(); end
146 assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
147 else $error("More then one TLB entry selected for next replace!");
151 return [self
.flush_i
, self
.lu_access_i
,
152 self
.lu_asid_i
, self
.lu_vaddr_i
,
153 self
.lu_is_2M_o
, self
.lu_is_1G_o
, self
.lu_hit_o
,
154 ] + self
.lu_content_o
.ports() + self
.update_i
.ports()
156 if __name__
== '__main__':
158 vl
= rtlil
.convert(tlb
, ports
=tlb
.ports())
159 with
open("test_tlb.il", "w") as f
: