f768571ed28377e3fad3f5c30615e3f1842e4035
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 http://www.ntu.edu.sg/home/smitha/ParaCache/Paracache/vm.html
28 from nmigen
import Signal
, Module
, Cat
, Const
, Array
29 from nmigen
.cli
import verilog
, rtlil
30 from nmigen
.lib
.coding
import Encoder
32 from ptw
import TLBUpdate
, PTE
, ASID_WIDTH
34 from tlb_content
import TLBContent
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
):
67 # SV39 defines three levels of page tables
68 m
.d
.comb
+= [ vpn0
.eq(self
.lu_vaddr_i
[12:21]),
69 vpn1
.eq(self
.lu_vaddr_i
[21:30]),
70 vpn2
.eq(self
.lu_vaddr_i
[30:39]),
74 for i
in range(self
.tlb_entries
):
75 tlc
= TLBContent(self
.pte_width
, self
.asid_width
)
76 setattr(m
.submodules
, "tc%d" % i
, tlc
)
79 tlc
.update_i
= self
.update_i
# saves a lot of graphviz links
80 m
.d
.comb
+= [tlc
.vpn0
.eq(vpn0
),
83 tlc
.flush_i
.eq(self
.flush_i
),
84 #tlc.update_i.eq(self.update_i),
85 tlc
.lu_asid_i
.eq(self
.lu_asid_i
)]
92 # use Encoder to select hit index
93 # XXX TODO: assert that there's only one valid entry (one lu_hit)
94 hitsel
= Encoder(self
.tlb_entries
)
95 m
.submodules
.hitsel
= hitsel
98 for i
in range(self
.tlb_entries
):
99 hits
.append(tc
[i
].lu_hit_o
)
100 m
.d
.comb
+= hitsel
.i
.eq(Cat(*hits
)) # (goes into plru as well)
103 active
= Signal(reset_less
=True)
104 m
.d
.comb
+= active
.eq(~hitsel
.n
)
106 # active hit, send selected as output
107 m
.d
.comb
+= [ self
.lu_is_1G_o
.eq(tc
[idx
].lu_is_1G_o
),
108 self
.lu_is_2M_o
.eq(tc
[idx
].lu_is_2M_o
),
110 self
.lu_content_o
.flatten().eq(tc
[idx
].lu_content_o
),
117 p
= PLRU(self
.tlb_entries
)
118 plru_tree
= Signal(p
.TLBSZ
)
119 m
.submodules
.plru
= p
121 # connect PLRU inputs/outputs
122 # XXX TODO: assert that there's only one valid entry (one replace_en)
124 for i
in range(self
.tlb_entries
):
125 en
.append(tc
[i
].replace_en_i
)
126 m
.d
.comb
+= [Cat(*en
).eq(p
.replace_en_o
), # output from PLRU into tags
127 p
.lu_hit
.eq(hitsel
.i
),
128 p
.lu_access_i
.eq(self
.lu_access_i
),
129 p
.plru_tree
.eq(plru_tree
)]
130 m
.d
.sync
+= plru_tree
.eq(p
.plru_tree_o
)
136 assert (self
.tlb_entries
% 2 == 0) and (self
.tlb_entries
> 1), \
137 "TLB size must be a multiple of 2 and greater than 1"
138 assert (self
.asid_width
>= 1), \
139 "ASID width must be at least 1"
145 function int countSetBits(logic[self.tlb_entries-1:0] vector);
146 automatic int count = 0;
147 foreach (vector[idx]) begin
148 count += vector[idx];
153 assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
154 else $error("More then one hit in TLB!"); $stop(); end
155 assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
156 else $error("More then one TLB entry selected for next replace!");
160 return [self
.flush_i
, self
.lu_access_i
,
161 self
.lu_asid_i
, self
.lu_vaddr_i
,
162 self
.lu_is_2M_o
, self
.lu_is_1G_o
, self
.lu_hit_o
,
163 ] + self
.lu_content_o
.ports() + self
.update_i
.ports()
165 if __name__
== '__main__':
167 vl
= rtlil
.convert(tlb
, ports
=tlb
.ports())
168 with
open("test_tlb.il", "w") as f
: