Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / cpus / vonk / ss / lib / cpu / src / SS_Message.h
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: SS_Message.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_Message_h__
#define __SS_Message_h__
#include "SS_Signal.h"
#include "BL_Atomic.h"
#include "SS_SnapShot.h"
// SS_Message is the container class that holds messages for
// a strand that need to be dealth with on the next step.
// The class is the crucial element in the run loop of the
// strand, and part of it's implementation directly impacts
// performance in a big way. The SS_Strand::get_signal is
// the only consumer of this class.
class SS_Message
{
public:
SS_Message();
// is_pending() returns true if one or more messages have been
// send to the strand. This routines is the key test in the
// tight runloop of the strand and should not be changed.
uint64_t is_pending()
{
return pending;
}
// set_reenter_loop() is used to send a message to the strand
// that special state such as pstate and hpstate of the strand
// changed and w\it needs to get out of the loop to update things
// like global variables held in registers. Currently only
// SS_Strand::ss_sim_update calls this function.
void set_reenter_loop() { flag.reenterloop = 1; }
// clr_reenter_loop() is used to notify that the reenter loop
// message is handled.
void clr_reenter_loop() { flag.reenterloop = 0; }
// set_handle_trap() is used to message othe strand that the
// next step should launch the trap handler. Only SS_Interrupt
// calls this method as it controls which trap is launched.
void set_handle_trap( SS_Trap::Type tt )
{
assert(tt < SS_Trap::TCC_INSTRUCTION);
assert(SS_Trap::Type(flag.trap_type) == SS_Trap::RESERVED);
flag.trap_type = tt;
}
// clr_handle_trap() is used to notify that the trap has been
// handled: the strand is now set to step into the trap code.
void clr_handle_trap()
{
flag.trap_type = SS_Trap::RESERVED;
}
// get_handle_trap() is used to find out if a trap is to be
// handled. A return value SS_Trap:RESERVED means no trap.
SS_Trap::Type get_handle_trap()
{
return SS_Trap::Type(flag.trap_type);
}
// test_signal() returns true while there are SS_Signal type
// messages pending. get_signal() is used to consume those
// messages.
bool test_signal() { return head->next != 0; }
// post_signal() is used to send a SS_Signal based message
// to the strand. The routine is MP safe; it uses a mutex for enqueue.
void post_signal( SS_Signal* node )
{
node->next = 0;
mutex.lock();
tail = tail->next = node;
mutex.unlock();
bl_atomic_add32((int32_t*)&flag.signal,1);
}
// get_signal() is used by the strand to consume a posted SS_Signal
// message. This routine can only be used when test_signal() returns
// true. Note each strand has one SS_Message instance so there is
// only one user of get_signal(), hence dequeue does not have to
// use MP safe constructs.
SS_Signal* get_signal()
{
// There is always one SS_Signal on an empty queue. On a non empty queue
// the SS_Signal after the head is the one we are interested in.
SS_Signal* temp = head;
SS_Signal* help = temp->next;
head = help;
assert(help);
bl_atomic_add32((int32_t*)&flag.signal,-1);
temp->next = free_list;
temp->type = SS_Signal::FREE;
free_list = temp;
++free_count;
if (free_count >= free_count_max)
{
SS_Signal* h = free_list;
for (temp = free_list->next;
free_count > free_count_max/2;
free_list = temp)
{
assert(free_list->type == SS_Signal::FREE);
assert(free_count > 0);
--free_count;
temp = free_list->next;
}
SS_Signal* t = free_list;
free_list = t->next;
assert(free_count > 0);
--free_count;
SS_Signal::list_free(h, t);
}
return help;
}
// make_signal() creates a new signal that can be posted to a strand.
// This class maintains it's own pool of signals for MP scalability
// reasons.
SS_Signal* make_signal( SS_Signal::Type type )
{
SS_Signal* help = free_list;
if (help == 0)
{
assert(free_count == 0);
return SS_Signal::alloc(type);
}
else
{
assert(free_count > 0);
--free_count;
}
free_list = help->next;
help->init(type);
return help;
}
void snapshot( SS_SnapShot& ss, const char* prefix );
private:
BL_Mutex mutex;
// The Flags structure holds all fast but not MP safe flags. These
// can only be set by the strand itself. The signal flag is used
// for the MP safe SS_Signal based list.
struct Flags
{
uint8_t reenterloop; // Get out of tight loop and reenter
uint8_t trap_type; // Handle (disrupting) trap on th next step
uint8_t spare[2]; // Reserved for future fast lock free flags
int32_t signal; // Used to count number SS_Signals pending
};
union
{
uint64_t pending; // One 8 byte load to test all the flags
Flags flag; // The flags for fast messages
};
// The signal queue always has a valid head and tail pointer.
SS_Signal* head; // Head of the message queue
SS_Signal* tail; // Tail of the message queue
SS_Signal* free_list; // Free list of signals
uint_t free_count; // Nr signals on free list
static const uint_t free_count_max = 1024;
};
#endif