Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / cpus / vonk / ss / lib / cpu / src / SS_Tlb.h
/*
* ========== 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 ============================================
*/
#ifndef __SS_Tlb_h__
#define __SS_Tlb_h__
#include "SS_Tte.h"
#include "SS_Node.h"
#include "SS_Strand.h"
#include "BL_Mutex.h"
#include "BL_Atomic.h"
#include <synch.h>
class SS_Tlb
{
public:
enum Type
{
INST_TLB = 0x1,
DATA_TLB = 0x2
};
friend Type operator|( Type a, Type b ) { return Type(int(a)|int(b)); }
SS_Tlb( Type type, uint_t _size );
SS_Tlb( SS_Tlb& tlb );
~SS_Tlb();
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
// the SS_TlbSync.
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)
{
tte_list_mutex.lock();
tte->next = tte_list;
tte->valid_bit(false);
tte_list = tte;
tte_list_mutex.unlock();
}
}
// 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
uint_t size()
{
return tlb_size;
}
// get() return the TTE as index.
SS_Tte* get( uint_t index )
{
assert(index < size());
return ptr_table[index];
}
// set() inserts tte into the TLb at index: it copies the TTE contents
void set( uint_t index, SS_Tte* tte )
{
assert(index < size());
tlb_access.lock();
SS_Tte* old_tte = ptr_table[index];
assert(old_tte->index == index);
SS_Tte* new_tte = alloc_tte(index);
*new_tte = *tte;
new_tte->index = index;
ptr_table[index] = new_tte;
flush_tte(0,old_tte);
tlb_access.unlock();
}
// 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 )
{
assert(tte);
flush_tte(0,tte);
}
int next_valid_index( int i )
{
if (0 <= i)
for (; i < size(); i++)
if (ptr_table[i]->valid())
return i;
return -1;
}
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];
assert(tte->index == i);
ptr_table[i] = alloc_tte(i);
flush_tte(strand,tte);
}
void invalidate_tte( SS_Strand* strand, SS_Tte* tte )
{
assert(tte == ptr_table[tte->index]);
ptr_table[tte->index] = alloc_tte(tte->index);
flush_tte(strand,tte);
}
protected:
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).
void tlb_access_lock()
{
tlb_access.lock();
++generation_count;
}
void tlb_access_unlock()
{
tlb_access.unlock();
}
uint64_t tlb_access_enter()
{
return generation_count;
}
uint64_t tlb_access_leave()
{
return generation_count;
}
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 (tte->valid_bit())
{
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)
{
SS_Signal* sgn;
if (caller)
sgn = caller->msg.make_signal(SS_Signal::FLUSH_TTE);
else
sgn = SS_Signal::alloc(SS_Signal::FLUSH_TTE);
sgn->tte = tte;
sgn->tlb = this;
strand->strand->post_signal(sgn);
}
}
else if ((caller && !caller->sim_state.cosim()) || (caller == 0))
{
tte_list_mutex.lock();
tte->next = tte_list;
tte_list = tte;
tte_list_mutex.unlock();
}
}
// 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 )
{
tte_list_mutex.lock();
SS_Tte* help = tte_list;
if (help == 0)
help = alloc_tte_block();
else
tte_list = help->next;
help->index = index;
help->usage_count = 1;
tte_list_mutex.unlock();
return help;
}
private:
volatile uint64_t generation_count; // Each modifications of the TLB bumps the count by 1
static SS_Tlb* ss_clone( SS_Tlb* );
class Node
{
public:
Node( SS_Strand* s, Node* _next ) : strand(s), next(_next) {}
SS_Strand* strand;
Node* 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.
};
#endif