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_is_512G_o
= Signal()
52 self
.lu_hit_o
= Signal()
54 self
.pte_width
= len(self
.lu_content_o
.flatten())
55 self
.update_i
= TLBUpdate(asid_width
)
57 def elaborate(self
, platform
):
60 vpn3
= Signal(9) #FIXME unused signal
69 # SV48 defines four levels of page tables
70 m
.d
.comb
+= [ vpn0
.eq(self
.lu_vaddr_i
[12:21]),
71 vpn1
.eq(self
.lu_vaddr_i
[21:30]),
72 vpn2
.eq(self
.lu_vaddr_i
[30:39]),
73 vpn3
.eq(self
.lu_vaddr_i
[39:48]), ### FIXME
77 for i
in range(self
.tlb_entries
):
78 tlc
= TLBContent(self
.pte_width
, self
.asid_width
)
79 setattr(m
.submodules
, "tc%d" % i
, tlc
)
82 tlc
.update_i
= self
.update_i
# saves a lot of graphviz links
83 m
.d
.comb
+= [tlc
.vpn0
.eq(vpn0
),
87 tlc
.flush_i
.eq(self
.flush_i
),
88 #tlc.update_i.eq(self.update_i),
89 tlc
.lu_asid_i
.eq(self
.lu_asid_i
)]
96 # use Encoder to select hit index
97 # XXX TODO: assert that there's only one valid entry (one lu_hit)
98 hitsel
= Encoder(self
.tlb_entries
)
99 m
.submodules
.hitsel
= hitsel
102 for i
in range(self
.tlb_entries
):
103 hits
.append(tc
[i
].lu_hit_o
)
104 m
.d
.comb
+= hitsel
.i
.eq(Cat(*hits
)) # (goes into plru as well)
107 active
= Signal(reset_less
=True)
108 m
.d
.comb
+= active
.eq(~hitsel
.n
)
110 # active hit, send selected as output
111 m
.d
.comb
+= [ self
.lu_is_512G_o
.eq(tc
[idx
].lu_is_512G_o
),
112 self
.lu_is_1G_o
.eq(tc
[idx
].lu_is_1G_o
),
113 self
.lu_is_2M_o
.eq(tc
[idx
].lu_is_2M_o
),
115 self
.lu_content_o
.flatten().eq(tc
[idx
].lu_content_o
),
122 p
= PLRU(self
.tlb_entries
)
123 plru_tree
= Signal(p
.TLBSZ
)
124 m
.submodules
.plru
= p
126 # connect PLRU inputs/outputs
127 # XXX TODO: assert that there's only one valid entry (one replace_en)
129 for i
in range(self
.tlb_entries
):
130 en
.append(tc
[i
].replace_en_i
)
131 m
.d
.comb
+= [Cat(*en
).eq(p
.replace_en_o
), # output from PLRU into tags
132 p
.lu_hit
.eq(hitsel
.i
),
133 p
.lu_access_i
.eq(self
.lu_access_i
),
134 p
.plru_tree
.eq(plru_tree
)]
135 m
.d
.sync
+= plru_tree
.eq(p
.plru_tree_o
)
141 assert (self
.tlb_entries
% 2 == 0) and (self
.tlb_entries
> 1), \
142 "TLB size must be a multiple of 2 and greater than 1"
143 assert (self
.asid_width
>= 1), \
144 "ASID width must be at least 1"
150 function int countSetBits(logic[self.tlb_entries-1:0] vector);
151 automatic int count = 0;
152 foreach (vector[idx]) begin
153 count += vector[idx];
158 assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
159 else $error("More then one hit in TLB!"); $stop(); end
160 assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
161 else $error("More then one TLB entry selected for next replace!");
165 return [self
.flush_i
, self
.lu_access_i
,
166 self
.lu_asid_i
, self
.lu_vaddr_i
,
167 self
.lu_is_2M_o
, self
.lu_1G_o
, self
.lu_is_512G_o
, self
.lu_hit_o
168 ] + self
.lu_content_o
.ports() + self
.update_i
.ports()
170 if __name__
== '__main__':
172 vl
= rtlil
.convert(tlb
, ports
=tlb
.ports())
173 with
open("test_tlb.il", "w") as f
: