* ========== Copyright Header Begin ==========================================
* OpenSPARC T2 Processor File: SS_Tlb.h
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
* The above named program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
* ========== Copyright Header End ============================================
friend Type
operator|( Type a
, Type b
) { return Type(int(a
)|int(b
)); }
SS_Tlb( Type type
, uint_t _size
);
uint_t
tlb_id() { return tlb_ident
; }
// is_xxxx_tlb() returns true when the TLB is of type xxxx
bool is_inst_tlb() { return tlb_type
& INST_TLB
; }
bool is_data_tlb() { return tlb_type
& DATA_TLB
; }
bool is_a_clone() { return !not_a_clone
; }
// clone() can be called to make a copy of the tlb. The method function
// is provided by this class or derived classes. clone() is primarily used in
SS_Tlb
* (*clone
)( SS_Tlb
* );
// add_strand() is called by a strand to register itself with the TLB to
// receive tte flushes for decode cache updates. (strand unparks)
void add_strand( SS_Strand
* s
);
// rem_strand() is called by a strand to unregister itself with the TLB to
// stop receivingtte flushes for decode cache updates. (strand parks)
void rem_strand( SS_Strand
* s
);
// reuse_tte() is called by the strand after strand->flush_tte is done and
// the usage_count count is decremented to zero (the atomic add returns the
// previous value of the atomic count). Note in cosim mode with tlb syncing
// we should not call this as we keep all tte's ever created.
void reuse_tte( SS_Tte
* tte
)
assert(tte
->usage_count
);
if (bl_atomic_add32(&tte
->usage_count
,-1) == 1)
// lock_reuse_tte() is used in SS_Strand::trc_step to hold on to the current
// tte used for the instruction trace. SS_Strand::trc_step will call reuse_tte
// to unlock the TTE. Locking is done by bumping the flosh_pending count by one
// which prevents the recycling of the TTE.
void lock_reuse_tte( SS_Tte
* tte
)
bl_atomic_add32(&tte
->usage_count
,1);
// size() returns the size of the tlb in number of TTEs
// get() return the TTE as index.
SS_Tte
* get( uint_t index
)
// set() inserts tte into the TLb at index: it copies the TTE contents
void set( uint_t index
, SS_Tte
* tte
)
SS_Tte
* old_tte
= ptr_table
[index
];
assert(old_tte
->index
== index
);
SS_Tte
* new_tte
= alloc_tte(index
);
ptr_table
[index
] = new_tte
;
// flush() is for frontends that play with tte fields we need a way to flush
// the modified tte from the decode caches.
void flush( SS_Tte
* tte
)
int next_valid_index( int i
)
if (ptr_table
[i
]->valid())
void dump(FILE *fp
=stdout
);
// invalidate_tte() is for demap operations to invalidate a TTE.
// It replaces the TTE at index i with a new TTE and calls flush_tte()
// to evict the TTE from the decode caches.
void invalidate_tte( SS_Strand
* strand
, uint i
)
SS_Tte
* tte
= ptr_table
[i
];
ptr_table
[i
] = alloc_tte(i
);
void invalidate_tte( SS_Strand
* strand
, SS_Tte
* tte
)
assert(tte
== ptr_table
[tte
->index
]);
ptr_table
[tte
->index
] = alloc_tte(tte
->index
);
BL_Mutex tlb_access
; // The lock used for gaining write access to the TLB
// tlb_access_lock() and tlb_access_unlock() are used for TLB insert and
// demap. The tlb_access_lock bumps the generation count by one after
// locking the tlb access mutex. A tlb lookup routine that
// does not need the lock and unlock routines can use tlb_access_enter()
// and tlb_access_leave() to ensure that at most one TLB insert or remove
// happened. If the results of these routines are different then one or
// more tlb modifications happened. If they are the same then zero or
// maximum one tlb modification is happening.
// When c and C are tlb_access_enter and leave respectively, and m and M
// are tlb_access_lock and unlock respectively, then we can have the
// following situaltions.
// cCmM ok, generation count equal. all fine.
// cmCM oops, generation count different
// cmMC oops, generation count different
// mcCM fail, generation count equal but mutexed operation ongoing
// mcMC oops, generation count different
// mMcC ok, generation count equal. all fine.
// The mcCM case is painfull, but at least it allows us to deal
// with recycling memory in not to difficult fashion and have mutex
// free TLB lookup which is crucial for MP performance (scalability).
uint64_t tlb_access_enter()
uint64_t tlb_access_leave()
static uint_t tlb_id_no
; // The next tlb_id number, bumped on creation
uint_t tlb_ident
; // The id of this TLB
Type tlb_type
; // Inst and or data tlb
uint_t tlb_size
; // The size of the TLB in no TTEs
SS_Tte
** ptr_table
; // The PTR table points to the tte_table entries
// flush_tte() is called to signal the strands that a tte should be flushed
// from the decode cache. usage_count is set to the number of flushes send
// out. Each strand decrements usage_count by one. The tte will not be reused
// while usage_count >= 0. The last strand to perform an atomic decrement by -1
// on the usage_count during reuse_tte will cause the tte to become available again.
void flush_tte( SS_Strand
* caller
, SS_Tte
* tte
)
if (caller
&& caller
->trc_hook
)
caller
->trc_hook
->tlb_update(false,this,tte
->index
,tte
);
bl_atomic_add32(&tte
->usage_count
,no_strands
- 1); // Initial usage_count is 1
for (Node
* strand
=strand_list
; strand
; strand
= strand
->next
)
sgn
= caller
->msg
.make_signal(SS_Signal::FLUSH_TTE
);
sgn
= SS_Signal::alloc(SS_Signal::FLUSH_TTE
);
strand
->strand
->post_signal(sgn
);
else if ((caller
&& !caller
->sim_state
.cosim()) || (caller
== 0))
// alloc_tte() gets a new tte from either the tte_list or mallocs it.
// We initialise the usage_count of the TTE to 1 and we set the index
// of the TTE to the index of the ptr_table.
SS_Tte
* alloc_tte_block();
SS_Tte
* alloc_tte( uint_t index
)
help
= alloc_tte_block();
volatile uint64_t generation_count
; // Each modifications of the TLB bumps the count by 1
static SS_Tlb
* ss_clone( SS_Tlb
* );
Node( SS_Strand
* s
, Node
* _next
) : strand(s
), next(_next
) {}
bool not_a_clone
; // True when the TLB is the original TLB (false when clone()d)
Node
* strand_list
; // Linked list of all strands that share this TLB
uint_t no_strands
; // No strands in the strands list
BL_Mutex tte_list_mutex
; // Mutex lock for updating tte_list
SS_Tte
* tte_list
; // List of free tte's that can be used for tlb updates.