Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / cpus / vonk / n2 / lib / cpu / src / N2_Core.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: N2_Core.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 ============================================
#include <string.h>
#include <new>
#include "N2_Strand.h"
#include "N2_Core.h"
#include "N2_Cpu.h"
#include "BL_BitUtility.h"
#include "N2_IrfEcc.h"
#include "N2_FrfEcc.h"
N2_Core::N2_Core( N2_Cpu& _cpu, const char* _name, uint_t strand_id_base )/*{{{*/
:
SS_Node(_cpu,_name),
cpu(_cpu),
inst_tlb(SS_Tlb::INST_TLB,N2_Tlb::ITLB_SIZE),
data_tlb(SS_Tlb::DATA_TLB,N2_Tlb::DTLB_SIZE),
icache_empty(true),
dcache_empty(true)
{
for (uint_t i=0; i < N2_Model::NO_STRANDS_PER_CORE; i++)
{
char num[8];
sprintf(num,"s%i",i);
void* s = ss_memalign(64,sizeof(N2_Strand));
new(s) N2_Strand(*this,num,strand_id_base + i);
strand[i] = (N2_Strand*)s;
}
asi_map[0x42].add(0x08,0x08,this,0,
inst_iw_ld64,inst_iw_st64,
inst_iw_ld64,inst_iw_st64);
asi_map[0x42].add(0x10,this,lsu_diag);
asi_map[0x43].add(0x00,this,&error_inject,
SS_SharedAsiCtrReg::ld64,error_inject_st64,
SS_SharedAsiCtrReg::rd64,error_inject_st64);
asi_map[0x45].add(0x08,this,decr);
asi_map[0x46].set_mask(N2_DcacheDataStReg::DCACHE_DATA_SIZE_MASK);
asi_map[0x46].add(SS_VADDR_MIN,SS_VADDR_MAX,this,0,
dcache_data_ld64,dcache_data_st64,
dcache_data_ld64,dcache_data_st64);
asi_map[0x47].set_mask(N2_DcacheTagLdReg::DCACHE_TAG_SIZE_MASK);
asi_map[0x47].add(0x0,(N2_DcacheTagLdReg::DCACHE_TAG_SIZE_MASK -1),this,0,
dcache_tag_ld64,dcache_tag_st64,
dcache_tag_ld64,dcache_tag_st64);
asi_map[0x4c].add(0x08,this,&dfesr,
SS_SharedAsiCtrReg::ld64,0,
SS_SharedAsiCtrReg::rd64,SS_SharedAsiCtrReg::wr64);
asi_map[0x4c].add(0x10,this,cerer);
asi_map[0x4c].add(0x20,this,&clesr,
SS_SharedAsiCtrReg::ld64,0,
SS_SharedAsiCtrReg::rd64,0);
asi_map[0x4c].add(0x28,this,&clfesr,
SS_SharedAsiCtrReg::ld64,0,
SS_SharedAsiCtrReg::rd64,0);
asi_map[0x4e].add(0x00,this,power_mgmt);
asi_map[0x50].add(0x38,0x38,this,0,
inst_wp_ld64,inst_wp_st64,
inst_wp_ld64,inst_wp_st64);
asi_map[0x54].add(0x98,this,&tw_status,
tw_status_ld64,0,
tw_status_ld64,SS_SharedAsiCtrReg::wr64);
asi_map[0x66].set_mask(N2_IcacheInstrStReg::ICACHE_DATA_SIZE_MASK);
asi_map[0x66].add(0x0,(N2_IcacheInstrStReg::ICACHE_DATA_SIZE_MASK -1),this,0,
icache_data_ld64,icache_data_st64,
icache_data_ld64,icache_data_st64);
asi_map[0x67].set_mask(N2_IcacheTagLdReg::ICACHE_TAG_SIZE_MASK);
asi_map[0x67].add(0x0,(N2_IcacheTagLdReg::ICACHE_TAG_SIZE_MASK -1),this,0,
icache_tag_ld64,icache_tag_st64,
icache_tag_ld64,icache_tag_st64);
}
/*}}}*/
N2_Core::~N2_Core()/*{{{*/
{
}
/*}}}*/
void N2_Core::hard_reset()/*{{{*/
{
for (uint s=0; s < N2_Model::NO_STRANDS_PER_CORE; s++)
((N2_Strand*)strand[s])->hard_reset();
}
/*}}}*/
void N2_Core::warm_reset(bool intp)/*{{{*/
{
lsu_diag = 0;
error_inject = 0;
decr = 0;
cerer = 0;
power_mgmt = 0;
inst_iw[0] = 0;
inst_iw[1] = 0;
for (uint s=0; s < N2_Model::NO_STRANDS_PER_CORE; s++)
((N2_Strand*)strand[s])->warm_reset(intp);
}
/*}}}*/
void N2_Core::snapshot( SS_SnapShot& ss )/*{{{*/
{
char prefix[32];
get_name(prefix);
power_mgmt.snapshot(ss,prefix);
error_inject.snapshot(ss,prefix);
decr.snapshot(ss,prefix);
dfesr.snapshot(ss,prefix);
cerer.snapshot(ss,prefix);
clesr.snapshot(ss,prefix);
clfesr.snapshot(ss,prefix);
SS_SharedAsiCtrReg::snapshot(ss,inst_iw,2,prefix);
SS_SharedAsiCtrReg::snapshot(ss,inst_wp,2,prefix);
lsu_diag.snapshot(ss,prefix);
for (int s=0; s < N2_Model::NO_STRANDS_PER_CORE; s++)
strand[s]->snapshot(ss);
inst_tlb.snapshot(ss,prefix,"inst");
data_tlb.snapshot(ss,prefix,"data");
// Deal with loaded values that need to propagate through the
// model into the strands etc.
if (ss.do_load())
{
// Deal with values that have sife effecrts when written to.
// This to ensure everything is updated. We reuse thet asi st64
// functions for this and pass sensible values for unused parameters.
inst_iw_st64(this,0,strand[0],0,inst_iw[0]());
inst_iw_st64(this,0,strand[2],0,inst_iw[1]());
inst_wp_st64(this,0,strand[0],0,inst_wp[0]());
inst_wp_st64(this,0,strand[2],0,inst_wp[1]());
}
}
/*}}}*/
void N2_Core::ras_enable(char*)/*{{{*/
{
for (int s=0; s < N2_Model::NO_STRANDS_PER_CORE; s++)
strand[s]->ras_enable(strand[s], NULL);
}
/*}}}*/
SS_AsiSpace::Error N2_Core::inst_iw_ld64( SS_Node* _core, void*, SS_Strand* s, SS_Vaddr va , uint64_t* data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
N2_InstMask* r = &core->inst_iw[(s->strand_id() >> 2) & 1];
*data = r->get();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::inst_iw_st64( SS_Node* _core, void*, SS_Strand* s, SS_Vaddr va, uint64_t data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
uint_t i = s->strand_id() & 4;
N2_InstMask* r = &core->inst_iw[i >> 2];
r->lock();
r->set(data);
uint32_t iw_mask = 0;
if (r->en_rs2()) iw_mask |= 0x1f;
if (r->en_asi()) iw_mask |= 0xff << 5;
if (r->en_i()) iw_mask |= 0x01 << 13;
if (r->en_rs1()) iw_mask |= 0x1f << 14;
if (r->en_op3()) iw_mask |= 0x3f << 19;
if (r->en_rd()) iw_mask |= 0x1f << 25;
if (r->en_op()) iw_mask |= 0x03 << 30;
uint32_t iw_data = r->iw() & iw_mask;
core->strand[i ]->inst_breakpoint_set(0,iw_mask,iw_data);
core->strand[i + 1]->inst_breakpoint_set(0,iw_mask,iw_data);
core->strand[i + 2]->inst_breakpoint_set(0,iw_mask,iw_data);
core->strand[i + 3]->inst_breakpoint_set(0,iw_mask,iw_data);
r->unlock();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::inst_wp_ld64( SS_Node* _core, void*, SS_Strand* s, SS_Vaddr va , uint64_t* data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
uint_t i = s->strand_id() & 4;
N2_InstWp* r = &core->inst_wp[i >> 2];
*data = r->get(); // Signextended on store
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::inst_wp_st64( SS_Node* _core, void*, SS_Strand* s, SS_Vaddr va, uint64_t data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
uint_t i = s->strand_id() & 4;
N2_InstWp* r = &core->inst_wp[i >> 2];
r->lock();
uint64_t old_enabled = r->enabled();
// Sign extend from bit47 up now which saves a shift later in use :-)
// but first clip of the bits that are RO.
r->set(data);
r->set_unmasked((SS_Vaddr(r->get()) << (64 - s->va_bits())) >> (64 - s->va_bits()));
// Bit0 of the address is used to indicate enabled (0) or disabled (1)
// This saves an extra compare, eg. bit 0 of the PC is always 0
// so a disabled watchpoint has the address bit0 set, so addr won't
// compare equal to the PC when disabled - ever.
SS_Vaddr addr = (r->va() << 2) + !r->enabled();
SS_Vaddr mask = (SS_Vaddr(1) << 48) - 4;
core->strand[i ]->inst_watchpoint_va_set(mask,addr);
core->strand[i + 1]->inst_watchpoint_va_set(mask,addr);
core->strand[i + 2]->inst_watchpoint_va_set(mask,addr);
core->strand[i + 3]->inst_watchpoint_va_set(mask,addr);
// On every watchpoint change we signal flush decode caches to
// all strands ... as we use the decode cache to store special
// decoders that have knowledge about the watchpoints.
SS_Signal* sgn;
sgn = s->msg.make_signal(SS_Signal::FLUSH_VA);
sgn->flush_va = addr;
core->strand[i]->post_signal(sgn);
sgn = s->msg.make_signal(SS_Signal::FLUSH_VA);
sgn->flush_va = addr;
core->strand[i + 1]->post_signal(sgn);
sgn = s->msg.make_signal(SS_Signal::FLUSH_VA);
sgn->flush_va = addr;
core->strand[i + 2]->post_signal(sgn);
sgn = s->msg.make_signal(SS_Signal::FLUSH_VA);
sgn->flush_va = addr;
core->strand[i + 3]->post_signal(sgn);
r->unlock();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::icache_tag_ld64( SS_Node* _core, void*, SS_Strand* s, SS_Vaddr va , uint64_t* data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
// convert ASI_VA to hardware index/way
N2_IcacheDiagTagAddrFields diagTagAddr;
diagTagAddr.set(va);
diagTagAddr.perren(0);// clear the err inject and valid bits
diagTagAddr.vb_err_en(0); // before accessing the ASI map
*data = (core->icacheTag.get(diagTagAddr))();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::icache_tag_st64( SS_Node* _core, void*, SS_Strand*, SS_Vaddr va, uint64_t data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
// ASI VA for tag contains, way/index,
// parity error enable and vb_err_enable
N2_IcacheDiagTagAddrFields vaddr;
vaddr.set(va);
N2_IcacheDiagTagAddrFields indexVa = vaddr;
indexVa.perren(0); // clear the err inject and valid bits
indexVa.vb_err_en(0); // before accessing the ASI map
N2_IcacheTagLdReg icacheTagLdReg;
icacheTagLdReg.set(data);
// calculate parity over tag
uint64_t t = BL_BitUtility::calc_parity(icacheTagLdReg.tag());
t ^= vaddr.perren(); // invert parity if perren is one
// merge tag and parity
icacheTagLdReg.tag_parity(t);
// enforce valid bit error injection enable
icacheTagLdReg.valid0(icacheTagLdReg.valid1() ^ vaddr.vb_err_en());
// write the tag back into the cache
core->icacheTag.set(indexVa, icacheTagLdReg);
/* Flush all the decode caches associated with this core */
core->flush_tte_all();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::icache_data_ld64( SS_Node* _core, void*, SS_Strand* s, SS_Vaddr va , uint64_t* data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
// convert ASI_VA to hardware index/way
N2_IcacheDiagInstrAddrFields diagInstrAddr;
diagInstrAddr.set(va);
uint64_t index = diagInstrAddr();
*data = core->icacheInstr[index]();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::icache_data_st64( SS_Node* _core, void*, SS_Strand*, SS_Vaddr va, uint64_t data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
// extract icache perrinj, way, and index from ASI_VA
N2_IcacheDiagInstrAddrFields vaddr;
vaddr.set(va);
uint64_t index = vaddr();
N2_IcacheInstrStReg icacheInstrStReg;
icacheInstrStReg.set(data);
// enforce perrinj
uint32_t t = BL_BitUtility::calc_parity(icacheInstrStReg.instr());
t ^= icacheInstrStReg.perrinj();
// merge parity into data
icacheInstrStReg.perrinj(t);
// write the data (instr) into the I-cache
core->icacheInstr[index] = icacheInstrStReg;
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::dcache_tag_ld64( SS_Node* _core, void*, SS_Strand* s, SS_Vaddr va , uint64_t* data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
N2_DcacheDiagTagAddrFields dcacheDiagTagAddr;
dcacheDiagTagAddr.set(va);
*data = (core->dcacheTag[dcacheDiagTagAddr()])();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::dcache_tag_st64( SS_Node* _core, void*, SS_Strand*, SS_Vaddr va, uint64_t data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
// ASI_VA contains valid bit error enable (vb_err_en)
// parity error enable (perren)
// way and index (way_index)
N2_DcacheDiagTagAddrFields vaddr;
vaddr.set(va);
uint64_t vb_err_en = vaddr.vb_err_en();
uint64_t perren = vaddr.perren();
uint64_t parity;
// clear out reserved bits from the data
N2_DcacheTagLdReg dcacheTagLdReg;
dcacheTagLdReg.set(data);
// calculate parity over tag and tag valid
parity = BL_BitUtility::calc_parity(dcacheTagLdReg.tag());
parity ^= perren;
// merge parity and valid bits
dcacheTagLdReg.tag_parity(parity);
dcacheTagLdReg.valid0(dcacheTagLdReg.valid1() ^ vb_err_en);
vaddr.vb_err_en(0);
vaddr.perren(0);
// write it into the D$ tag
core->dcacheTag[vaddr()].set(dcacheTagLdReg());
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::dcache_data_ld64( SS_Node* _core, void*, SS_Strand* s, SS_Vaddr va , uint64_t* data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
N2_DcacheDiagDataLdAddrFields dcacheDiagDataLdAddr;
dcacheDiagDataLdAddr.set(va);
*data = (core->dcacheData[dcacheDiagDataLdAddr()])();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::dcache_data_st64( SS_Node* _core, void*, SS_Strand*, SS_Vaddr va, uint64_t data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
// ASI VA contains D$ index, and parity error injection mask
N2_DcacheDiagDataStAddrFields vaddr;
vaddr.set(va);
uint64_t index = vaddr.index();
uint64_t perrmask = vaddr.perrmask();
uint64_t parity;
// record the parity
parity = BL_BitUtility::calc_byte_parities(data);
parity ^= perrmask;
// This ends up being a write to data *and* parity
N2_DcacheDiagDataLdAddrFields ldVa;
ldVa.index(index);
ldVa.way(vaddr.way());
// Write the parity into the cache
core->dcacheData[ldVa()].set(parity);
ldVa.data_notparity(1);
core->dcacheData[ldVa()].set(data);
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::tw_status_ld64( SS_Node* _core, void*, SS_Strand* s, SS_Vaddr va , uint64_t* data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
uint_t tws = 0;
for (uint_t i=0; i < N2_Model::NO_STRANDS_PER_CORE; i++)
tws |= core->strand[i]->tw_status << i;
core->tw_status.lock();
core->tw_status.htp(tws);
*data = core->tw_status();
core->tw_status.unlock();
return SS_AsiSpace::OK;
}
/*}}}*/
SS_AsiSpace::Error N2_Core::error_inject_st64( SS_Node* _core, void* _reg, SS_Strand* , SS_Vaddr, uint64_t data )/*{{{*/
{
N2_Core* core = (N2_Core*)_core;
N2_ErrorInject* reg = (N2_ErrorInject*) _reg;
reg->set(data);
/* If IRF/FRF injection is set , then enable the inject/detect in RAS mode */
/* for performance reasons these are not enabled immediately while transitioning to RAS mode */
if(reg->ircu())
{
for (uint_t ndx = 0; ndx < N2_Model::NO_STRANDS_PER_CORE; ++ndx)
{
N2_Strand* s = core->strand[ndx];
if(s->sim_state.ras_enabled())
{
s->ras_rs1 = n2_irf_ras_detect;
s->ras_rs2 = n2_irf_ras_detect;
s->ras_rs3 = n2_irf_ras_detect;
s->ras_rd = n2_irf_ras_inject;
}
}
}
if(reg->frcu())
{
for (uint_t ndx = 0; ndx < N2_Model::NO_STRANDS_PER_CORE; ++ndx)
{
N2_Strand* s = core->strand[ndx];
if(s->sim_state.ras_enabled())
{
s->ras_frs1 = n2_frf_ras_detect;
s->ras_frs2 = n2_frf_ras_detect;
s->ras_frs3 = n2_frf_ras_detect;
s->ras_frd = n2_frf_ras_inject;
s->ras_drs1 = n2_frf_dp_ras_detect;
s->ras_drs2 = n2_frf_dp_ras_detect;
s->ras_drs3 = n2_frf_dp_ras_detect;
s->ras_drd = n2_frf_dp_ras_inject;
}
}
}
return SS_AsiSpace::OK;
}
/*}}}*/
/* RAS Routines */
void N2_Core::update_clesr(int sid, int isDesr, uint64_t value)/*{{{*/
{
// 'value' is either desr or dfesr's F field, record it in clesr's
// corresponding bit location
int shift = 48 + (sid * 2) + isDesr;
uint64_t mask = 1 << shift;
uint64_t data = value << shift;
uint64_t origData = clesr.get();
uint64_t newData = (origData & ~mask) | data;
clesr.set(newData);
if (value != 0)
{
if ((clfesr.get() & mask) == 0)
{
// the corresponding bit in clfesr is not set, record the new value
origData = clfesr.get();
newData = (origData & ~mask) | data;
clfesr.set(newData);
}
}
else if (clesr.get() == 0)
{
// clesr is clear, so go ahead and clear clfesr
clfesr.set(0);
}
}
/*}}}*/
SS_Trap::Type N2_Core::icache_ifetch(
const MemoryTransaction &memXact,
bool trap_enabled,
uint32_t strand_id,
N2_MemErrDetector *mem_err_detector)/*{{{*/
{
/***************************************************
* Adding I-cache RAS error check here inline, because
* I'm not quite sure where else to add it
*
* The map icacheTag_ contains all the tags for this
* core.
* The map icacheData_ contains all the data for this
* core
*
* Algorithm:
* 1. Search for tag valid errors
* DESR = ICVP
* trap_type = HW_CORRECTED_ERROR
* 2. Search for tag parity errors
* DESR = ICTP
* trap_type = HW_CORRECTED_ERROR
* 3. Search for hit and multi-hit errors
* DESR = ICTM
* trap_type = HW_CORRECTED_ERROR
* 4. If line in cache
* if instruction parity error
* DESR = ICTM
* trap_type = HW_CORRECTED_ERROR
* 5. if line not in cache
* pick and allocate L1 way
* for each instr in line
* set good instr parity
* set good tag parity/valid
*
***************************************************/
// Split physical address into icache index and tag
N2_IcacheAddressingFields pc_pa;
pc_pa.set(memXact.getPaddr());
uint64_t index = pc_pa.sets(); // Tbl B-2 refers to SET
// Tbl 28-8 and -10 to index
uint64_t tag = pc_pa.tag();
N2_Desr &desr = strand[strand_id]->desr;
icache_empty = false; // remember the I$ has been used
int32_t hit_way = -1;
uint32_t errortype = search_icache_tags(pc_pa, hit_way);
// if there were no errors and we got a hit, then lets check the
// addressed instruction for parity error
if (errortype == 0 && hit_way != -1)
{
uint32_t way = hit_way;
N2_IcacheDiagInstrAddrFields diagInstrAddr;
diagInstrAddr.index(index);
diagInstrAddr.way(way);
diagInstrAddr.word(pc_pa.instr()); // WORD == INSTR in PRM
N2_IcacheInstrStReg icacheInstrStReg = icacheInstr[diagInstrAddr()];
uint32_t pe = icacheInstrStReg.instr(); // extract instruction
// calculate parity over instruction
pe = BL_BitUtility::calc_parity(pe);
pe ^= icacheInstrStReg.perrinj(); // xor in parity bit
// if this comes up one then we have a parity error
if (pe && cerer.icdp())
{
errortype = 4;
}
}
// all done checking for errors
if (errortype == 0)
{
// no errors. If we got a miss (no tag match), then create a new
// line in the cache (possibly overwriting a currently valid entry)
if (hit_way == -1)
{
// we didn't get a tag match
//TODO: Handle NotData poison here.
SS_Trap::Type trapNumber = SS_Trap::NO_TRAP;
if(mem_err_detector)
{
trapNumber = mem_err_detector->L2CacheFill(memXact);
}
// For now, just create a clean line with valid tag
uint32_t way = (icache_lru++ % N2_IcacheAddressingFields::ICACHE_WAYS);
N2_IcacheDiagInstrAddrFields diagInstrAddr;
diagInstrAddr.index(index);
diagInstrAddr.way(way);
// Clean out any old errors
for (int word = 0; word < 8; word++)
{
diagInstrAddr.word(word);
icacheInstr.erase(diagInstrAddr());
if (trapNumber != SS_Trap::NO_TRAP)
{
N2_IcacheInstrStReg &icacheInstrStReg = icacheInstr[diagInstrAddr()];
icacheInstrStReg.set(0);
icacheInstrStReg.perrinj(1);
}
}
// calculate tag parity
int64_t tag_pe = BL_BitUtility::calc_parity(tag);
// format tag entry in ASI format
N2_IcacheTagLdReg obj;
obj.tag(tag);
obj.tag_parity(tag_pe);
obj.valid1(1);
obj.valid0(1);
N2_IcacheDiagTagAddrFields diagTagAddr;
diagTagAddr.index(index);
diagTagAddr.way(way);
// write the tag into the cache
icacheTag.set(diagTagAddr, obj);
if (trapNumber != SS_Trap::NO_TRAP)
{
return trapNumber;
}
// Now poison all matching decode cache entries
strand[strand_id]->flush(pc_pa(), true);
}
} else
{
// errortype != 0
// we detected some kind of error (recorded in errortype)
// we try to record into Disrupting Error Status Register (DESR)
if (desr.f() == 0)
{ // record the error address if not full
desr.erraddr((hit_way << 6) | (pc_pa.sets()));
}
// hardware automatically recovers from this error by erasing
// all ways in the set
for (uint32_t way = 0; way < N2_IcacheAddressingFields::ICACHE_WAYS; way++)
{
N2_IcacheDiagTagAddrFields diagTagAddr;
diagTagAddr.index(index);
diagTagAddr.way(way);
N2_IcacheTagLdReg tagReg = icacheTag.get(diagTagAddr);
tagReg.valid0(0);
tagReg.valid1(0);
icacheTag.set(diagTagAddr, tagReg);
N2_IcacheDiagInstrAddrFields diagInstrAddr;
diagInstrAddr.index(index);
diagInstrAddr.way(way);
for (int word = 0; word < 8; word++)
{
diagInstrAddr.word(word);
icacheInstr.erase(diagInstrAddr());
}
}
// if DESR not full, record error type, and mark it full
if (desr.f() == 0)
{
// FIXME: set the CLESR.T field for this strand
desr.f(1);
desr.s(0);
desr.errtype(errortype);
} else
{
// if DESR was already full, indicate multiple error
desr.me(1);
}
update_clesr(strand_id, 1, desr.f());
if (trap_enabled)
{
return SS_Trap::HW_CORRECTED_ERROR;
}
}
return SS_Trap::NO_TRAP;
}
/*}}}*/
// flushIcache() flushes the I-cache line at paddr.
//
// No RAS correction or detection occurs.
void N2_Core::flush_icache(uint64_t paddr)/*{{{*/
{
if (icache_empty) // avoid searching I$ if empty
return;
N2_IcacheAddressingFields pc_pa;
pc_pa.set(paddr);
// Verify correct alignment
if (paddr % (1 <<
(N2_IcacheAddressingFields::WIDTH_RSVD0 +
N2_IcacheAddressingFields::WIDTH_INSTR))) {
fprintf(stderr, "Internal error: N2_core::flushIcache(): "
"misaligned paddr 0x%ullx", paddr);
exit(1);
}
// Look for a matching line in the cache and invalidate it
int32_t hit_way;
search_icache_tags(pc_pa, hit_way);
if (hit_way != -1)
{
N2_IcacheTagLdReg icacheTagLdReg;
icacheTagLdReg.valid1(0);
icacheTagLdReg.valid0(0);
N2_IcacheDiagTagAddrFields diagTagAddr;
diagTagAddr.index(pc_pa.sets());
diagTagAddr.way(hit_way);
// write the tag into the cache
icacheTag.set(diagTagAddr, icacheTagLdReg);
}
}
/*}}}*/
// searchIcacheTags() returns the RAS error type found in Tbl. 12-13,
// N2 1.1 PRM. It is passed the physical address to be looked up and
// a reference to the matching way if the address hits in the I-cache.
// If there is no hit, the reference is set to -1.
//
// A subset of the physical address, called index, gets us down to a
// set of 8 places where our instructions might be. A given entry in
// our set of 8 is called a way.
// A given way contains:
// room for 8 instructions (32 bytes aligned on 32 byte boundary)
// valid bit(s) so we know if the instructions are meaningful
// tag - the bits of the physical address not used as index or to
// address individual instructions
// RAS elements
// each instruction has a parity bit (even parity)
// tag valid is duplicated (parity)
// tag has parity (even parity)
uint32_t N2_Core::search_icache_tags(N2_IcacheAddressingFields paddr,int32_t &hit_way)/*{{{*/
{
hit_way = -1;
uint64_t index = paddr.sets(); // Tbl B-2 refers to SET
// Tbl 28-8 and -10 to index
uint64_t tag = paddr.tag();
uint32_t way;
char valids[N2_IcacheAddressingFields::ICACHE_WAYS];
char perrs[N2_IcacheAddressingFields::ICACHE_WAYS];
uint64_t tags[N2_IcacheAddressingFields::ICACHE_WAYS];
// walk through the tag array finding out which tags are valid
// and checking for tag valid errors
// eight ways
for (way = 0; way < N2_IcacheAddressingFields::ICACHE_WAYS; way++)
{
N2_IcacheDiagTagAddrFields diagTagAddr;
diagTagAddr.index(index);
diagTagAddr.way(way);
// use way and index to look up tags in map
N2_IcacheTagLdReg icacheTagLdReg = icacheTag.get(diagTagAddr);
// tag found in map (may have valids == 0)
valids[way] = (icacheTagLdReg.valid1() << 1) | icacheTagLdReg.valid0();
// check valids (Should be the same, so 0 and 3 are good, 1 and 2 bad)
if (valids[way] == 1 || valids[way] == 2)
{
if (cerer.icvp()) { // I-cache Valid Parity Error
hit_way = way;
return 1;
}
}
// extract the tag (address bits)
tags[way] = icacheTagLdReg.tag();
// calculate tag parity using log2 N based XOR trick
uint64_t pe;
pe = BL_BitUtility::calc_parity(tags[way]);
pe ^= icacheTagLdReg.tag_parity();
perrs[way] = pe;
}
// if there were no tag valid errors, check for tag parity errors
for (way = 0; way < N2_IcacheAddressingFields::ICACHE_WAYS; way++)
{
if (valids[way] && perrs[way] && cerer.ictp())
{
// we got valid tag, with errors and we're enabled for
// reporting in CERER
hit_way = way;
return 2;
}
}
// if there were no tag parity errors, look for tag matching the
// requesting physical address. If there is more than one tag
// match try to report tag multi-hit error (if CERER allows)
bool hit = false;
for (way = 0; way < N2_IcacheAddressingFields::ICACHE_WAYS; way++)
{
if (valids[way] && tag == tags[way])
{
// tag match
if (hit && cerer.ictm())
{
// if we already had tag hit, and CERER allows report
// tag multi-hit
hit_way = way;
return 3;
}
// remember that we got a tag match, and remember the way
hit = true;
hit_way = way;
}
}
return 0;
}
/*}}}*/
SS_Trap::Type N2_Core::dcache_trans( const MemoryTransaction &memXact,
bool trap_enabled,
uint32_t strand_id,
bool store_id,
N2_MemErrDetector *mem_err_detector)/*{{{*/
{
/***************************************************
* The map dcacheTag_ contains all the tags for this
* core.
* The map dcacheData_ contains all the data and
* parity for this core
*
* Algorithm:
* 1. Search for tag valid errors
* DESR = DCVP
* trap_type = HW_CORRECTED_ERROR
* 2. Search for tag parity errors
* DESR = DCTP
* trap_type = HW_CORRECTED_ERROR
* 3. Search for hit and multi-hit errors
* DESR = DCTM
* trap_type = HW_CORRECTED_ERROR
* 4. If line in cache
* if instruction parity error
* DESR = DCTM
* trap_type = HW_CORRECTED_ERROR
* 5. if line not in cache
* pick and allocate L1 way
* for each instr in line
* set good instr parity
* set good tag parity/valid
*
***************************************************/
N2_Desr &desr = strand[strand_id]->desr;
N2_DcacheAddressingFields paddr;
paddr.set(memXact.getPaddr());
uint64_t index = paddr.sets();
uint64_t tag = paddr.tag();
uint64_t dw = paddr.data();
dcache_empty = false; // remember the D$ has been used
int32_t hit_way;
uint32_t errortype = search_dcache_tags(paddr, hit_way);
if (errortype == 0)
{
// no errors so far
if (hit_way != -1)
{
// we found a tag match (hit) so check data parity
uint32_t way = hit_way;
N2_DcacheDiagDataLdAddrFields dcacheDiagDataLdAddr;
dcacheDiagDataLdAddr.way(way);
dcacheDiagDataLdAddr.index(index);
dcacheDiagDataLdAddr.doubleword(dw);
N2_DcacheDataStReg dcacheDataStReg = dcacheData[dcacheDiagDataLdAddr()];
uint64_t parity = dcacheDataStReg.data();
if (calc_dcache_parity(dcacheDiagDataLdAddr) != parity && memXact.readXact())
{
if (cerer.dcdp())
{
errortype = 8;
}
}
// writes update the dirty bits in the L2$
if (memXact.writeXact()/* || memXact.rdwrXact()*/)
{
SS_Trap::Type tt = strand[strand_id]->fill_store_buffer_mem(memXact);
if(tt != SS_Trap::NO_TRAP)
return tt;
if(mem_err_detector){
SS_Trap::Type tt = mem_err_detector->L2CacheFill(memXact);
if(tt != SS_Trap::NO_TRAP)
return tt;
}
}
} else
{
// Create a clean line with valid tag
uint32_t way = (dcache_lru++ % N2_DcacheAddressingFields::DCACHE_WAYS);
N2_DcacheDiagDataLdAddrFields dcacheDiagDataLdAddr;
dcacheDiagDataLdAddr.way(way);
dcacheDiagDataLdAddr.index(index);
for (int dword = 0; dword < (1<<N2_DcacheDiagDataLdAddrFields::bit_size_doubleword); dword++)
{
dcacheDiagDataLdAddr.doubleword(dw);
dcacheData.erase(dcacheDiagDataLdAddr());
dcacheDiagDataLdAddr.data_notparity(1);
dcacheData.erase(dcacheDiagDataLdAddr());
dcacheDiagDataLdAddr.data_notparity(0);
}
int64_t tag_pe = BL_BitUtility::calc_parity(tag);
N2_DcacheDiagTagAddrFields dcacheDiagTagAddr;
dcacheDiagTagAddr.way(way);
dcacheDiagTagAddr.index(index);
N2_DcacheTagLdReg dcacheTagLdReg;
dcacheTagLdReg.tag(tag);
dcacheTagLdReg.tag_parity(tag_pe);
dcacheTagLdReg.valid1(1);
dcacheTagLdReg.valid0(1);
uint64_t data = dcacheTagLdReg();
dcacheTag[dcacheDiagTagAddr()].set(data);
if (memXact.writeXact())
{
SS_Trap::Type tt = strand[strand_id]->fill_store_buffer_mem(memXact);
if(tt != SS_Trap::NO_TRAP)
return tt;
}
// Get line from L2 cache
SS_Trap::Type trapNumber = SS_Trap::NO_TRAP;
if(mem_err_detector)
trapNumber = mem_err_detector->L2CacheFill(memXact);
// If NotData
if (trapNumber != SS_Trap::NO_TRAP)
{
// flip all the parity bits
uint32_t parity = calc_dcache_parity(dcacheDiagDataLdAddr);
parity ^= 0xff;
dcacheDiagDataLdAddr.data_notparity(0);
N2_DcacheDataStReg dcacheDataStReg;
dcacheDataStReg.set(parity);
dcacheData[dcacheDiagDataLdAddr()] = dcacheDataStReg;
if (errortype == 0)
{
return trapNumber;
}
}
}
}
if (errortype)
{ // errtype != 0
if (desr.f() == 0)
{
uint32_t errorAddr = index;
if (hit_way != -1) {
errorAddr |= hit_way << N2_DcacheAddressingFields::bit_size_sets;
}
desr.erraddr(errorAddr);
}
// invalidate all ways
for (uint32_t way = 0; way < N2_DcacheAddressingFields::DCACHE_WAYS; ++way)
{
N2_DcacheDiagTagAddrFields dcacheDiagTagAddr;
dcacheDiagTagAddr.way(way);
dcacheDiagTagAddr.index(index);
dcacheTag.erase(dcacheDiagTagAddr());
for (int dword = 0; dword < (1<<N2_DcacheDiagDataLdAddrFields::bit_size_doubleword); dword++)
{
N2_DcacheDiagDataLdAddrFields dcacheDiagDataLdAddr;
dcacheDiagDataLdAddr.way(way);
dcacheDiagDataLdAddr.index(index);
dcacheDiagDataLdAddr.doubleword(dword);
dcacheData.erase(dcacheDiagDataLdAddr());
dcacheDiagDataLdAddr.data_notparity(1);
dcacheData.erase(dcacheDiagDataLdAddr());
dcacheDiagDataLdAddr.data_notparity(0);
}
}
if (desr.f() == 0)
{
desr.f(1);
desr.s(0);
desr.errtype(errortype);
update_clesr(strand_id, 1, desr.f());
} else
{
desr.me(1);
}
if (trap_enabled)
{
return SS_Trap::HW_CORRECTED_ERROR;
}
}
return SS_Trap::NO_TRAP;
}
/*}}}*/
// flushDcache() flushes the D-cache line at paddr.
//
// No RAS correction or detection occurs.
void N2_Core::flush_dcache(uint64_t paddr)/*{{{*/
{
if (dcache_empty) // avoid searching #$ if empty
return;
N2_DcacheAddressingFields pc_pa;
pc_pa.set(paddr);
// Verify correct alignment
/*if (paddr % (1 << N2_DcacheAddressingFields::bitSizeDATA)) {
RS_DOMAIN_ERR("N2_core::flushDcache(): misaligned paddr 0x%ullx",
paddr);
}*/
// Look for a matching line in the cache and invalidate it
int32_t hit_way;
search_dcache_tags(pc_pa, hit_way);
if (hit_way != -1)
{
N2_DcacheTagLdReg dcacheTagLdReg;
dcacheTagLdReg.valid1(0);
dcacheTagLdReg.valid0(0);
N2_DcacheDiagTagAddrFields diagTagAddr;
diagTagAddr.index(pc_pa.sets());
diagTagAddr.way(hit_way);
// write the tag into the cache
dcacheTag[diagTagAddr()] = dcacheTagLdReg;
}
}
/*}}}*/
// searchDcacheTags() returns the RAS error type found in Tbl. 12-13,
// N2 1.1 PRM. It is passed the physical address to be looked up and
// a reference to the matching way if the address hits in the D-cache.
// If there is no hit, the reference is set to -1.
uint32_t N2_Core::search_dcache_tags(N2_DcacheAddressingFields pa,int32_t &hit_way)/*{{{*/
{
// Dcache has 16 byte lines, with four ways
uint64_t index = pa.sets();
uint64_t tag = pa.tag();
uint64_t dw = pa.data();
uint64_t way;
char valids[N2_DcacheAddressingFields::DCACHE_WAYS];
char perrs[N2_DcacheAddressingFields::DCACHE_WAYS];
uint64_t tags[N2_DcacheAddressingFields::DCACHE_WAYS];
hit_way = -1;
/* Search all the ways for tag valids and tag valid errors */
for (way = 0; way < N2_DcacheAddressingFields::DCACHE_WAYS; way++)
{
N2_DcacheDiagTagAddrFields dcacheDiagTagAddr;
dcacheDiagTagAddr.way(way);
dcacheDiagTagAddr.index(index);
N2_DcacheTagLdReg dcacheTagLdReg = dcacheTag[dcacheDiagTagAddr()];
// check valids
if (dcacheTagLdReg.valid1() != dcacheTagLdReg.valid0())
{
if (cerer.dcvp())
{
hit_way = way;
return 5;
}
}
valids[way] = dcacheTagLdReg.valid0();
tags[way] = dcacheTagLdReg.tag();
uint64_t pe;
pe = BL_BitUtility::calc_parity(tags[way]);
pe ^= dcacheTagLdReg.tag_parity();
perrs[way] = pe;
}
// next highest priority is D-cache Tag parity
for (way = 0; way < N2_DcacheAddressingFields::DCACHE_WAYS; way++)
{
if (valids[way] && perrs[way])
{
if (cerer.dctp())
{
hit_way = way;
return 6;
}
}
}
// next highest priority is d-cache tag multihit
for (way = 0; way < N2_DcacheAddressingFields::DCACHE_WAYS; way++)
{
if (valids[way] && tag == tags[way])
{
if (hit_way != -1 && cerer.dctm())
{
return 7;
}
hit_way = way;
}
}
return 0;
}
/*}}}*/
// Calculates the eight parity bits for the Dcache line at
// dcacheDiagDataLdAddr located with diagnostic access
uint32_t N2_Core::calc_dcache_parity(N2_DcacheDiagDataLdAddrFields dcacheDiagDataLdAddr)/*{{{*/
{
// just to make sure
dcacheDiagDataLdAddr.data_notparity(1);
N2_DcacheDataStReg dcacheDataStReg = dcacheData[dcacheDiagDataLdAddr()];
uint64_t data = dcacheDataStReg.data();
return BL_BitUtility::calc_byte_parities(data);
}
/*}}}*/
// Flush all the decode caches associated with this core.
void N2_Core::flush_tte_all()/*{{{*/
{
for (uint_t strand_ndx = 0; strand_ndx < N2_Model::NO_STRANDS_PER_CORE;
++strand_ndx)
strand[strand_ndx]->flush_tte_all();
}
/*}}}*/