Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * OpenSPARC T2 Processor File: SS_Tlb.h | |
5 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
6 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
7 | * | |
8 | * The above named program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public | |
10 | * License version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * The above named program is distributed in the hope that it will be | |
13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public | |
18 | * License along with this work; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
20 | * | |
21 | * ========== Copyright Header End ============================================ | |
22 | */ | |
23 | #ifndef __SS_Tlb_h__ | |
24 | #define __SS_Tlb_h__ | |
25 | ||
26 | #include "SS_Tte.h" | |
27 | #include "SS_Node.h" | |
28 | #include "SS_Strand.h" | |
29 | #include "BL_Mutex.h" | |
30 | #include "BL_Atomic.h" | |
31 | #include <synch.h> | |
32 | ||
33 | class SS_Tlb | |
34 | { | |
35 | public: | |
36 | enum Type | |
37 | { | |
38 | INST_TLB = 0x1, | |
39 | DATA_TLB = 0x2 | |
40 | }; | |
41 | ||
42 | friend Type operator|( Type a, Type b ) { return Type(int(a)|int(b)); } | |
43 | ||
44 | ||
45 | SS_Tlb( Type type, uint_t _size ); | |
46 | SS_Tlb( SS_Tlb& tlb ); | |
47 | ~SS_Tlb(); | |
48 | ||
49 | uint_t tlb_id() { return tlb_ident; } | |
50 | ||
51 | // is_xxxx_tlb() returns true when the TLB is of type xxxx | |
52 | ||
53 | bool is_inst_tlb() { return tlb_type & INST_TLB; } | |
54 | bool is_data_tlb() { return tlb_type & DATA_TLB; } | |
55 | ||
56 | bool is_a_clone() { return !not_a_clone; } | |
57 | ||
58 | // clone() can be called to make a copy of the tlb. The method function | |
59 | // is provided by this class or derived classes. clone() is primarily used in | |
60 | // the SS_TlbSync. | |
61 | SS_Tlb* (*clone)( SS_Tlb* ); | |
62 | ||
63 | // add_strand() is called by a strand to register itself with the TLB to | |
64 | // receive tte flushes for decode cache updates. (strand unparks) | |
65 | void add_strand( SS_Strand* s ); | |
66 | ||
67 | // rem_strand() is called by a strand to unregister itself with the TLB to | |
68 | // stop receivingtte flushes for decode cache updates. (strand parks) | |
69 | void rem_strand( SS_Strand* s ); | |
70 | ||
71 | // reuse_tte() is called by the strand after strand->flush_tte is done and | |
72 | // the usage_count count is decremented to zero (the atomic add returns the | |
73 | // previous value of the atomic count). Note in cosim mode with tlb syncing | |
74 | // we should not call this as we keep all tte's ever created. | |
75 | void reuse_tte( SS_Tte* tte ) | |
76 | { | |
77 | assert(tte->usage_count); | |
78 | ||
79 | if (bl_atomic_add32(&tte->usage_count,-1) == 1) | |
80 | { | |
81 | tte_list_mutex.lock(); | |
82 | tte->next = tte_list; | |
83 | tte->valid_bit(false); | |
84 | tte_list = tte; | |
85 | tte_list_mutex.unlock(); | |
86 | } | |
87 | } | |
88 | ||
89 | // lock_reuse_tte() is used in SS_Strand::trc_step to hold on to the current | |
90 | // tte used for the instruction trace. SS_Strand::trc_step will call reuse_tte | |
91 | // to unlock the TTE. Locking is done by bumping the flosh_pending count by one | |
92 | // which prevents the recycling of the TTE. | |
93 | ||
94 | void lock_reuse_tte( SS_Tte* tte ) | |
95 | { | |
96 | bl_atomic_add32(&tte->usage_count,1); | |
97 | } | |
98 | ||
99 | // size() returns the size of the tlb in number of TTEs | |
100 | uint_t size() | |
101 | { | |
102 | return tlb_size; | |
103 | } | |
104 | ||
105 | // get() return the TTE as index. | |
106 | SS_Tte* get( uint_t index ) | |
107 | { | |
108 | assert(index < size()); | |
109 | return ptr_table[index]; | |
110 | } | |
111 | ||
112 | // set() inserts tte into the TLb at index: it copies the TTE contents | |
113 | void set( uint_t index, SS_Tte* tte ) | |
114 | { | |
115 | assert(index < size()); | |
116 | tlb_access.lock(); | |
117 | SS_Tte* old_tte = ptr_table[index]; | |
118 | assert(old_tte->index == index); | |
119 | SS_Tte* new_tte = alloc_tte(index); | |
120 | *new_tte = *tte; | |
121 | new_tte->index = index; | |
122 | ptr_table[index] = new_tte; | |
123 | flush_tte(0,old_tte); | |
124 | tlb_access.unlock(); | |
125 | } | |
126 | ||
127 | // flush() is for frontends that play with tte fields we need a way to flush | |
128 | // the modified tte from the decode caches. | |
129 | ||
130 | void flush( SS_Tte* tte ) | |
131 | { | |
132 | assert(tte); | |
133 | flush_tte(0,tte); | |
134 | } | |
135 | ||
136 | int next_valid_index( int i ) | |
137 | { | |
138 | if (0 <= i) | |
139 | for (; i < size(); i++) | |
140 | if (ptr_table[i]->valid()) | |
141 | return i; | |
142 | return -1; | |
143 | } | |
144 | ||
145 | void dump(FILE *fp=stdout); | |
146 | ||
147 | // invalidate_tte() is for demap operations to invalidate a TTE. | |
148 | // It replaces the TTE at index i with a new TTE and calls flush_tte() | |
149 | // to evict the TTE from the decode caches. | |
150 | ||
151 | void invalidate_tte( SS_Strand* strand, uint i ) | |
152 | { | |
153 | SS_Tte* tte = ptr_table[i]; | |
154 | assert(tte->index == i); | |
155 | ptr_table[i] = alloc_tte(i); | |
156 | flush_tte(strand,tte); | |
157 | } | |
158 | ||
159 | void invalidate_tte( SS_Strand* strand, SS_Tte* tte ) | |
160 | { | |
161 | assert(tte == ptr_table[tte->index]); | |
162 | ptr_table[tte->index] = alloc_tte(tte->index); | |
163 | flush_tte(strand,tte); | |
164 | } | |
165 | ||
166 | protected: | |
167 | BL_Mutex tlb_access; // The lock used for gaining write access to the TLB | |
168 | ||
169 | // tlb_access_lock() and tlb_access_unlock() are used for TLB insert and | |
170 | // demap. The tlb_access_lock bumps the generation count by one after | |
171 | // locking the tlb access mutex. A tlb lookup routine that | |
172 | // does not need the lock and unlock routines can use tlb_access_enter() | |
173 | // and tlb_access_leave() to ensure that at most one TLB insert or remove | |
174 | // happened. If the results of these routines are different then one or | |
175 | // more tlb modifications happened. If they are the same then zero or | |
176 | // maximum one tlb modification is happening. | |
177 | // | |
178 | // When c and C are tlb_access_enter and leave respectively, and m and M | |
179 | // are tlb_access_lock and unlock respectively, then we can have the | |
180 | // following situaltions. | |
181 | // | |
182 | // cCmM ok, generation count equal. all fine. | |
183 | // cmCM oops, generation count different | |
184 | // cmMC oops, generation count different | |
185 | // mcCM fail, generation count equal but mutexed operation ongoing | |
186 | // mcMC oops, generation count different | |
187 | // mMcC ok, generation count equal. all fine. | |
188 | // | |
189 | // The mcCM case is painfull, but at least it allows us to deal | |
190 | // with recycling memory in not to difficult fashion and have mutex | |
191 | // free TLB lookup which is crucial for MP performance (scalability). | |
192 | ||
193 | void tlb_access_lock() | |
194 | { | |
195 | tlb_access.lock(); | |
196 | ++generation_count; | |
197 | } | |
198 | ||
199 | void tlb_access_unlock() | |
200 | { | |
201 | tlb_access.unlock(); | |
202 | } | |
203 | ||
204 | uint64_t tlb_access_enter() | |
205 | { | |
206 | return generation_count; | |
207 | } | |
208 | ||
209 | uint64_t tlb_access_leave() | |
210 | { | |
211 | return generation_count; | |
212 | } | |
213 | ||
214 | static uint_t tlb_id_no; // The next tlb_id number, bumped on creation | |
215 | ||
216 | uint_t tlb_ident; // The id of this TLB | |
217 | Type tlb_type; // Inst and or data tlb | |
218 | uint_t tlb_size; // The size of the TLB in no TTEs | |
219 | SS_Tte** ptr_table; // The PTR table points to the tte_table entries | |
220 | ||
221 | // flush_tte() is called to signal the strands that a tte should be flushed | |
222 | // from the decode cache. usage_count is set to the number of flushes send | |
223 | // out. Each strand decrements usage_count by one. The tte will not be reused | |
224 | // while usage_count >= 0. The last strand to perform an atomic decrement by -1 | |
225 | // on the usage_count during reuse_tte will cause the tte to become available again. | |
226 | ||
227 | void flush_tte( SS_Strand* caller, SS_Tte* tte ) | |
228 | { | |
229 | if (tte->valid_bit()) | |
230 | { | |
231 | if (caller && caller->trc_hook) | |
232 | caller->trc_hook->tlb_update(false,this,tte->index,tte); | |
233 | ||
234 | bl_atomic_add32(&tte->usage_count,no_strands - 1); // Initial usage_count is 1 | |
235 | ||
236 | for (Node* strand=strand_list; strand; strand = strand->next) | |
237 | { | |
238 | SS_Signal* sgn; | |
239 | if (caller) | |
240 | sgn = caller->msg.make_signal(SS_Signal::FLUSH_TTE); | |
241 | else | |
242 | sgn = SS_Signal::alloc(SS_Signal::FLUSH_TTE); | |
243 | sgn->tte = tte; | |
244 | sgn->tlb = this; | |
245 | strand->strand->post_signal(sgn); | |
246 | } | |
247 | } | |
248 | else if ((caller && !caller->sim_state.cosim()) || (caller == 0)) | |
249 | { | |
250 | tte_list_mutex.lock(); | |
251 | tte->next = tte_list; | |
252 | tte_list = tte; | |
253 | tte_list_mutex.unlock(); | |
254 | } | |
255 | } | |
256 | ||
257 | // alloc_tte() gets a new tte from either the tte_list or mallocs it. | |
258 | // We initialise the usage_count of the TTE to 1 and we set the index | |
259 | // of the TTE to the index of the ptr_table. | |
260 | ||
261 | SS_Tte* alloc_tte_block(); | |
262 | ||
263 | SS_Tte* alloc_tte( uint_t index ) | |
264 | { | |
265 | tte_list_mutex.lock(); | |
266 | SS_Tte* help = tte_list; | |
267 | if (help == 0) | |
268 | help = alloc_tte_block(); | |
269 | else | |
270 | tte_list = help->next; | |
271 | help->index = index; | |
272 | help->usage_count = 1; | |
273 | tte_list_mutex.unlock(); | |
274 | return help; | |
275 | } | |
276 | ||
277 | private: | |
278 | volatile uint64_t generation_count; // Each modifications of the TLB bumps the count by 1 | |
279 | ||
280 | static SS_Tlb* ss_clone( SS_Tlb* ); | |
281 | ||
282 | class Node | |
283 | { | |
284 | public: | |
285 | Node( SS_Strand* s, Node* _next ) : strand(s), next(_next) {} | |
286 | ||
287 | SS_Strand* strand; | |
288 | Node* next; | |
289 | }; | |
290 | ||
291 | bool not_a_clone; // True when the TLB is the original TLB (false when clone()d) | |
292 | Node* strand_list; // Linked list of all strands that share this TLB | |
293 | uint_t no_strands; // No strands in the strands list | |
294 | ||
295 | BL_Mutex tte_list_mutex; // Mutex lock for updating tte_list | |
296 | SS_Tte* tte_list; // List of free tte's that can be used for tlb updates. | |
297 | }; | |
298 | ||
299 | #endif |