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, SV48
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 http://www.ntu.edu.sg/home/smitha/ParaCache/Paracache/vm.html
28 from nmigen
import Signal
, Module
, Cat
, Const
, Array
, Elaboratable
29 from nmigen
.cli
import verilog
, rtlil
30 from nmigen
.lib
.coding
import Encoder
32 from TLB
.ariane
.ptw
import TLBUpdate
, PTE
, ASID_WIDTH
33 from TLB
.ariane
.plru
import PLRU
34 from TLB
.ariane
.tlb_content
import TLBContent
38 class TLB(Elaboratable
):
39 def __init__(self
, tlb_entries
=8, asid_width
=8):
40 self
.tlb_entries
= tlb_entries
41 self
.asid_width
= asid_width
43 self
.flush_i
= Signal() # Flush signal
45 self
.lu_access_i
= Signal()
46 self
.lu_asid_i
= Signal(self
.asid_width
)
47 self
.lu_vaddr_i
= Signal(64)
48 self
.lu_content_o
= PTE()
49 self
.lu_is_2M_o
= Signal()
50 self
.lu_is_1G_o
= Signal()
51 self
.lu_hit_o
= Signal()
53 self
.pte_width
= len(self
.lu_content_o
.flatten())
54 self
.update_i
= TLBUpdate(asid_width
)
56 def elaborate(self
, platform
):
59 vpn3
= Signal(9) #FIXME unused signal
68 # SV48 defines four levels of page tables
69 m
.d
.comb
+= [ vpn0
.eq(self
.lu_vaddr_i
[12:21]),
70 vpn1
.eq(self
.lu_vaddr_i
[21:30]),
71 vpn2
.eq(self
.lu_vaddr_i
[30:39]), ### PLATEN ### OK
72 vpn3
.eq(self
.lu_vaddr_i
[39:48]), ### PLATEN ### now using SV48
76 for i
in range(self
.tlb_entries
):
77 tlc
= TLBContent(self
.pte_width
, self
.asid_width
)
78 setattr(m
.submodules
, "tc%d" % i
, tlc
)
81 tlc
.update_i
= self
.update_i
# saves a lot of graphviz links
82 m
.d
.comb
+= [tlc
.vpn0
.eq(vpn0
),
86 tlc
.flush_i
.eq(self
.flush_i
),
87 #tlc.update_i.eq(self.update_i),
88 tlc
.lu_asid_i
.eq(self
.lu_asid_i
)]
95 # use Encoder to select hit index
96 # XXX TODO: assert that there's only one valid entry (one lu_hit)
97 hitsel
= Encoder(self
.tlb_entries
)
98 m
.submodules
.hitsel
= hitsel
101 for i
in range(self
.tlb_entries
):
102 hits
.append(tc
[i
].lu_hit_o
)
103 m
.d
.comb
+= hitsel
.i
.eq(Cat(*hits
)) # (goes into plru as well)
106 active
= Signal(reset_less
=True)
107 m
.d
.comb
+= active
.eq(~hitsel
.n
)
109 # active hit, send selected as output
110 m
.d
.comb
+= [ self
.lu_is_1G_o
.eq(tc
[idx
].lu_is_1G_o
),
111 self
.lu_is_2M_o
.eq(tc
[idx
].lu_is_2M_o
),
113 self
.lu_content_o
.flatten().eq(tc
[idx
].lu_content_o
),
120 p
= PLRU(self
.tlb_entries
)
121 plru_tree
= Signal(p
.TLBSZ
)
122 m
.submodules
.plru
= p
124 # connect PLRU inputs/outputs
125 # XXX TODO: assert that there's only one valid entry (one replace_en)
127 for i
in range(self
.tlb_entries
):
128 en
.append(tc
[i
].replace_en_i
)
129 m
.d
.comb
+= [Cat(*en
).eq(p
.replace_en_o
), # output from PLRU into tags
130 p
.lu_hit
.eq(hitsel
.i
),
131 p
.lu_access_i
.eq(self
.lu_access_i
),
132 p
.plru_tree
.eq(plru_tree
)]
133 m
.d
.sync
+= plru_tree
.eq(p
.plru_tree_o
)
139 assert (self
.tlb_entries
% 2 == 0) and (self
.tlb_entries
> 1), \
140 "TLB size must be a multiple of 2 and greater than 1"
141 assert (self
.asid_width
>= 1), \
142 "ASID width must be at least 1"
148 function int countSetBits(logic[self.tlb_entries-1:0] vector);
149 automatic int count = 0;
150 foreach (vector[idx]) begin
151 count += vector[idx];
156 assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
157 else $error("More then one hit in TLB!"); $stop(); end
158 assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
159 else $error("More then one TLB entry selected for next replace!");
163 return [self
.flush_i
, self
.lu_access_i
,
164 self
.lu_asid_i
, self
.lu_vaddr_i
,
165 self
.lu_is_2M_o
, self
.lu_is_1G_o
, self
.lu_hit_o
,
166 ] + self
.lu_content_o
.ports() + self
.update_i
.ports()
168 if __name__
== '__main__':
170 vl
= rtlil
.convert(tlb
, ports
=tlb
.ports())
171 with
open("test_tlb.il", "w") as f
: