Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / cpus / vonk / n2 / lib / cpu / src / N2_Cpu.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: N2_Cpu.cc
// 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 ============================================
/************************************************************************
**
** Copyright (C) 2006, Sun Microsystems, Inc.
**
** Sun considers its source code as an unpublished, proprietary
** trade secret and it is available only under strict license provisions.
** This copyright notice is placed here only to protect Sun in the event
** the source is deemed a published work. Disassembly, decompilation,
** or other means of reducing the object code to human readable form
** is prohibited by the license agreement under which this code is
** provided to the user or company in possession of this copy.
**
*************************************************************************/
#include "N2_Cpu.h"
#include "N2_Core.h"
#include "N2_Strand.h"
#include "SS_Signal.h"
N2_Cpu::N2_Cpu( SS_Model& _model, const char* _name, uint_t strand_id_base )/*{{{*/
:
SS_Cpu(_model,_name),
mem_err_detector(&_model)
{
uint_t i = 0;
for (uint_t c=0; c < N2_Model::NO_CORES_PER_CPU; c++)
{
char n[3] = "c%";
n[1] = '0' + c;
core[c] = new N2_Core(*this,n,strand_id_base + 8 * c);
if (core[c] == 0)
{
perror("malloc(new)");
exit(-1);
}
for (uint_t s=0; s < N2_Model::NO_STRANDS_PER_CORE; s++)
strand[i++] = core[c]->strand[s];
}
strand_count = N2_Model::NO_STRANDS_PER_CPU;
asi_map[0x41].add(0x00,this,&strand_available,
SS_SharedAsiCtrReg::ld64,0,
SS_SharedAsiCtrReg::rd64,set_strand_available);
asi_map[0x41].add(0x10,this,&strand_enable_status,
SS_SharedAsiCtrReg::ld64,0,
SS_SharedAsiCtrReg::rd64,0);
asi_map[0x41].add(0x20,this,&strand_enable,
SS_SharedAsiCtrReg::ld64,set_strand_enable,
SS_SharedAsiCtrReg::rd64,set_strand_enable);
asi_map[0x41].add(0x30,this,&xir_steering,
SS_SharedAsiCtrReg::ld64,set_xir_steering,
SS_SharedAsiCtrReg::rd64,set_xir_steering);
asi_map[0x41].add(0x38,this,&tick_enable,
SS_SharedAsiCtrReg::ld64,set_tick_enable,
SS_SharedAsiCtrReg::rd64,set_tick_enable);
asi_map[0x41].add(0x50,this,&strand_running,
SS_SharedAsiCtrReg::ld64,set_strand_running,
SS_SharedAsiCtrReg::rd64,set_strand_running);
asi_map[0x41].add(0x58,this,&strand_running_status,
SS_SharedAsiCtrReg::ld64,0,
SS_SharedAsiCtrReg::rd64,0);
asi_map[0x41].add(0x60,this,&strand_running,
0,set_strand_running_w1s,
0,set_strand_running_w1s);
asi_map[0x41].add(0x68,this,&strand_running,
0,set_strand_running_w1c,
0,set_strand_running_w1c);
asi_map[0x45].add(0x10,this,&overlap_mode,
0,SS_SharedAsiCtrReg::st64,
0,SS_SharedAsiCtrReg::wr64);
asi_map[0x45].add(0x18,this,&rst_vec_mask,
SS_SharedAsiCtrReg::ld64,rst_vec_st64,
SS_SharedAsiCtrReg::rd64,rst_vec_st64);
asi_map[0x73].add(0x0,this,0,
0,intr_w_st64,
0,intr_w_st64);
strand_enable_status.set_unmasked(~uint64_t(0));
strand_running_status.set_unmasked(1);
strand_available = ~uint64_t(0);
strand_enable = ~uint64_t(0);
strand_running = 1;
xir_steering = ~uint64_t(0);
change_running(strand_running_status());
for (uint_t s=0; s < N2_Model::NO_STRANDS_PER_CPU; s++)
strand[s]-> merge_asi_map();
hard_reset();
}
/*}}}*/
void N2_Cpu::hard_reset()/*{{{*/
{
for (uint_t c=0; c<N2_Model::NO_CORES_PER_CPU; c++)
core[c]->hard_reset();
}
/*}}}*/
void N2_Cpu::warm_reset(bool intp)/*{{{*/
{
for (uint_t c=0; c<N2_Model::NO_CORES_PER_CPU; c++)
core[c]->warm_reset(intp);
}
/*}}}*/
void N2_Cpu::xtrn_reset()/*{{{*/
{
xir_steering.lock();
for (uint_t s=0; s<N2_Model::NO_STRANDS_PER_CPU; s++)
if (xir_steering() & (uint64_t(1) << s))
strand[s]->xtrn_reset();
xir_steering.unlock();
}
/*}}}*/
void N2_Cpu::snapshot( SS_SnapShot& ss )/*{{{*/
{
char prefix[32];
get_name(prefix);
// First deal with the common base for snaphot.
SS_Cpu::snapshot(ss);
// Now handle the N2 specifics like CMP. Note we do not snapshot
// the strand_running_status, as it is restored by the call to
// strand_running_update on ss.do_load() below
strand_available.snapshot(ss,prefix);
strand_enable.snapshot(ss,prefix);
strand_enable_status.snapshot(ss,prefix);
strand_running.snapshot(ss,prefix);
// SS_Strand saves the running flags so we don;t have to call
// strand_running_update() here to propagate the running flags
xir_steering.snapshot(ss,prefix);
tick_enable.snapshot(ss,prefix);
tw_status.snapshot(ss,prefix);
overlap_mode.snapshot(ss,prefix);
intr_w.snapshot(ss,prefix);
rst_vec_mask.snapshot(ss,prefix);
for (int c=0; c<N2_Model::NO_CORES_PER_CPU; c++)
core[c]->snapshot(ss);
// Make sure that on snapshot load we propagate the strand running
// flags properly. E.g. the strands shoudl registers themselves
// with the TLB etc.
if (ss.do_load())
strand_running_update(0);
}
/*}}}*/
void N2_Cpu::ras_enable(char*)/*{{{*/
{
for (int c=0; c<N2_Model::NO_CORES_PER_CPU; c++)
core[c]->ras_enable(NULL);
}
/*}}}*/
void N2_Cpu::strand_running_update( uint_t strand_id, int cosim, uint64_t data )/*{{{*/
{
uint64_t chg, run;
strand_enable_status.lock();
strand_running.lock();
strand_running_status.lock();
chg = strand_running_status();
// In non cosim mode (0) update the running status directly.
// In cosim mode (1) we ignore the running status update and
// when we get a status update from rtl (2) we update it with data.
// This is done through the pli CMP_WRITE command.
if (cosim == 0)
strand_running_status.set_unmasked(strand_running() & strand_enable_status());
else if (cosim == 2)
strand_running_status.set_unmasked(data);
run = strand_running_status();
// The strand that wants to park every strand including itself will remain
// running. This is done to prevent the chip from stalling.
if (run == 0)
{
run = uint64_t(1) << (strand_id % BITS_PER_CMP_REGISTER);
strand_running_status.set_unmasked(run);
}
change_running(strand_running_status());
strand_running_status.unlock();
strand_running.unlock();
strand_enable_status.unlock();
// Find out which strands switched from running to parked or visa versa.
// Notify those strands through our signalling mechanism.
chg = chg ^ run;
for (int s = 0; s < 64; s++)
{
if ((chg >> s) & 1)
{
SS_Signal* sgn = SS_Signal::alloc(SS_Signal::RUNNING);
sgn->running = (run >> s) & 1;
strand[s]->post_signal(sgn);
}
}
}
/*}}}*/
SS_AsiSpace::Error N2_Cpu::set_strand_available( SS_Node* _cpu, void*, SS_Strand* s, SS_Vaddr, uint64_t data )/*{{{*/
{
N2_Cpu* cpu = (N2_Cpu*)_cpu;
cpu->strand_available.lock();
cpu->strand_enable.lock();
cpu->strand_enable_status.lock();
cpu->xir_steering.lock();
uint64_t byte = 0xff;
for (int i=0; i < 8; i++)
{
if ((data & byte) != byte)
data &= ~byte;
byte <<= 8;
}
cpu->strand_available.set_unmasked(data);
cpu->strand_enable_status.set_unmasked(cpu->strand_enable() & data);
cpu->xir_steering = data;
cpu->xir_steering.unlock();
cpu->strand_enable_status.unlock();
cpu->strand_enable.unlock();
cpu->strand_available.unlock();
cpu->strand_running_update(s->strand_id());
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Cpu::set_strand_enable( SS_Node* _cpu, void*, SS_Strand* s, SS_Vaddr, uint64_t data )/*{{{*/
{
N2_Cpu* cpu = (N2_Cpu*)_cpu;
cpu->strand_available.lock();
cpu->strand_enable.lock();
cpu->strand_enable_status.lock();
uint64_t byte = 0xff;
for (int i=0; i < 8; i++)
{
if ((data & byte) != byte)
data &= ~byte;
byte <<= 8;
}
// Core 0 is the default enabled core if all are being disabled
if (data == 0)
data = 0xff;
cpu->strand_enable = data;
cpu->strand_enable_status.set_unmasked(cpu->strand_available() & data);
cpu->strand_enable.unlock();
cpu->strand_enable_status.unlock();
cpu->strand_available.unlock();
cpu->strand_running_update(s->strand_id());
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Cpu::set_strand_running( SS_Node* _cpu, void*, SS_Strand* s, SS_Vaddr, uint64_t data )/*{{{*/
{
N2_Cpu* cpu = (N2_Cpu*)_cpu;
// In cosim we get CMP_WRITE pli messages to take care of
// timing issues, so just ignore the data here, it gets updated
// by the pli CMP_WRITE command.
if (!s->sim_state.cosim())
{
cpu->strand_running.lock();
cpu->strand_running = data;
cpu->strand_running.unlock();
cpu->strand_running_update(s->strand_id());
}
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Cpu::set_strand_running_w1s( SS_Node* _cpu, void*, SS_Strand* s, SS_Vaddr, uint64_t data )/*{{{*/
{
N2_Cpu* cpu = (N2_Cpu*)_cpu;
// In cosim we get CMP_WRITE pli messages to take care of
// timing issues, so just ignore the data here, it gets updated
// by the pli CMP_WRITE command.
if (!s->sim_state.cosim())
{
cpu->strand_running.lock();
cpu->strand_running = cpu->strand_running() | data;
cpu->strand_running.unlock();
cpu->strand_running_update(s->strand_id());
}
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Cpu::set_strand_running_w1c( SS_Node* _cpu, void*, SS_Strand* s, SS_Vaddr, uint64_t data )/*{{{*/
{
N2_Cpu* cpu = (N2_Cpu*)_cpu;
// In cosim we get CMP_WRITE pli messages to take care of
// timing issues, so just ignore the data here, it gets updated
// by the pli CMP_WRITE command.
if (!s->sim_state.cosim())
{
cpu->strand_running.lock();
cpu->strand_running = cpu->strand_running() &~ data;
cpu->strand_running.unlock();
cpu->strand_running_update(s->strand_id());
}
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Cpu::set_xir_steering( SS_Node* _cpu, void*, SS_Strand* s, SS_Vaddr, uint64_t data )/*{{{*/
{
N2_Cpu* cpu = (N2_Cpu*)_cpu;
cpu->xir_steering.lock();
cpu->xir_steering = data & cpu->strand_enable_status();
cpu->xir_steering.unlock();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Cpu::set_tick_enable( SS_Node* _cpu, void*, SS_Strand* s, SS_Vaddr, uint64_t data )/*{{{*/
{
N2_Cpu* cpu = (N2_Cpu*)_cpu;
cpu->tick_enable.lock();
cpu->tick_enable = data;
cpu->tick_enable.unlock();
return SS_AsiSpace::OK;
}
/*}}}*/
void N2_Cpu::set_stepping( uint_t strand_id )/*{{{*/
{
// set_strand_stepping() is called when a virtual cpu
// instance is created. This is so when Vonk is used in SAM.
// In that environment we adjust the strand_available flag
// accordingly; mimicing what the service processor should do.
SS_Cpu::set_stepping(strand_id);
strand_available.lock();
strand_enable_status.lock();
// Note we don't round strand_available of to the nearest core set of bits.
strand_available = strand_stepping[0];
strand_enable_status.set_unmasked(strand_enable() & strand_available());
strand_available.unlock();
strand_enable_status.unlock();
// Set the first strand_id from sam.rc as running; order of sysconf cpu
// lines in the sam.rc file does matter.
strand_running_update(strand_id);
}
/*}}}*/
SS_AsiSpace::Error N2_Cpu::intr_w_st64( SS_Node* _cpu, void*, SS_Strand* s, SS_Vaddr, uint64_t data )/*{{{*/
{
N2_Cpu* cpu = (N2_Cpu*)_cpu;
cpu->intr_w.lock();
cpu->intr_w = data;
uint_t strand_id = cpu->intr_w.strand();
// If the strand that is addressed by the cross call is
// enabled it will receive the cross calls. Otherwise
// the crosscall is dropped. In case the strand that is
// called is parked, we unpark it first.
//
// In cosim mode we drop the crosscall. For timing reasons
// we wait for the DUT to send the cross call INTP packet.
if (!s->sim_state.cosim() && (cpu->strand_enable_status() & (1 << (strand_id % BITS_PER_CMP_REGISTER))))
{
cpu->strand_running_status.lock();
if ((cpu->strand_running_status() & (1 << (strand_id % BITS_PER_CMP_REGISTER))) == 0)
{
cpu->strand_running_status.unlock();
cpu->strand_running.lock();
cpu->strand_running = cpu->strand_running() | (1 << (strand_id % BITS_PER_CMP_REGISTER));
cpu->strand_running.unlock();
cpu->strand_running_update(s->strand_id());
}
else
cpu->strand_running_status.unlock();
SS_Signal* sgn = s->msg.make_signal(SS_Signal::EXTERNAL_INTERRUPT);
sgn->irq_type = cpu->intr_w.vector();
sgn->irq_raise = true;
cpu->strand[strand_id]->post_signal(sgn);
}
cpu->intr_w.unlock();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Cpu::rst_vec_st64( SS_Node* _cpu, void*, SS_Strand*, SS_Vaddr va, uint64_t data )/*{{{*/
{
N2_Cpu* cpu = (N2_Cpu*)_cpu;
cpu->rst_vec_mask.lock();
cpu->rst_vec_mask.set(data);
cpu->rst_vec_mask.unlock();
// Now set the reset vectors of all the strands ... do we need to place
// locks around this or can we get away with this?
for (int s =0; s < N2_Model::NO_STRANDS_PER_CPU; s++)
cpu->strand[s]->rstv_addr = data ? 0x0 : 0xfffffffff0000000;
return SS_AsiSpace::OK;
}
/*}}}*/