Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / cpus / vonk / n2 / lib / ras / src / N2_StoreBuffer.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: N2_StoreBuffer.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 "N2_StoreBuffer.h"
#include "BL_BaseEcc.h"
#include "BL_Hamming_32_7_Synd.h"
#include "N2_Core.h"
#include "N2_Strand.h"
//=============================================================================
//=============================================================================
// fillStoreBuffer() adds an entry to the strand's store buffer. As
// arguments, it takes whether or not the store is an ASI, the
// store's address (physical for memory, virtual for ASI), either the
// "byte marks" for memory or the ASI number, and the data.
//
// The store buffer, which contains 8 entries, is filled in a
// round-robin fashion by incr_stb_pointer() . It calculates ECC for
// both the upper and lower 32-bits of the data, injecting an error if
// indicates by the ASI_ERROR_INJECT_REG. It saves the address in the
// STB's CAM, the byte-marks-or-ASI, the privilege level, etc. in the
// store buffer's entry, and the parity of the CAM (again, possibly
// injecting an error).
//
// When a store buffer entry is purged, its CAM parity and data ECC
// are checked for errors. If the correct state information is set,
// then the appropriate trap is thrown.
static uint64_t max( uint64_t l, uint64_t r ) { return l<r?r:l; }
SS_Trap::Type N2_StoreBuffer::fill_store_buffer(bool is_asi,uint64_t addr, uint8_t asi,uint64_t data)/*{{{*/
{
uint_t stb_ptr_ndx = get_stb_pointer();
if (!stb_entry_is_asi[stb_ptr_ndx] && stb_entry_valid(stb_ptr_ndx))
{
SS_Trap::Type tt = purge_store_buffer(stb_ptr_ndx);
if(tt != SS_Trap::NO_TRAP)
return tt;
}
N2_ErrorInject *error_inject_reg = &(strand.core.error_inject);
uint_t stb_pointer = incr_stb_pointer();
N2_StbAccessDaReg stb_access_da_reg;
stb_access_da_reg.data(data);
set_stb_data(stb_pointer, stb_access_da_reg);
N2_StbAccessEccReg stb_access_ecc_reg;
BL_EccBits even_ecc = BL_Hamming_32_7_Synd::calc_check_bits(data & 0xffffffff);
if (error_inject_reg->ene() == 1 && error_inject_reg->stdu())
{
even_ecc.set(even_ecc.get() ^ error_inject_reg->eccmask());
}
stb_access_ecc_reg.ecc_even(even_ecc.get());
BL_EccBits odd_ecc = BL_Hamming_32_7_Synd::calc_check_bits(data >> 32);
if (error_inject_reg->ene() == 1 && error_inject_reg->stdu())
{
odd_ecc.set(odd_ecc.get() ^ error_inject_reg->eccmask());
}
stb_access_ecc_reg.ecc_odd(odd_ecc.get());
set_stb_ecc(stb_pointer, stb_access_ecc_reg);
N2_StbAccessCamReg stb_access_cam_reg;
stb_access_cam_reg.set(addr); // set address
stb_access_cam_reg.bm_asi(asi); // override bm_asi field
set_stb_cam(stb_pointer, stb_access_cam_reg);
uint_t cam_parity = BL_BitUtility::calc_parity(stb_access_cam_reg());
if (error_inject_reg->ene() == 1 && error_inject_reg->stau())
{
cam_parity = ~cam_parity;;
}
N2_StbAccessCtlReg stb_access_ctl_reg;
stb_access_ctl_reg.c_p(cam_parity);
if (strand.hpstate.hpriv())
{
stb_access_ctl_reg.control(N2_StbAccessCtlReg::CTL_HPRIV);
} else if (strand.pstate.priv())
{
stb_access_ctl_reg.control(N2_StbAccessCtlReg::CTL_PRIV);
} else
{
stb_access_ctl_reg.control(N2_StbAccessCtlReg::CTL_USER);
}
set_stb_ctl(stb_pointer, stb_access_ctl_reg);
stb_entry_is_asi[stb_pointer] = is_asi;
return SS_Trap::NO_TRAP;
}
/*}}}*/
SS_Trap::Type N2_StoreBuffer::check_store_buffer_RAWtrap(const MemoryTransaction &memXact)/*{{{*/
{
// search the entire Store Buffer
for (uint_t ndx = 0;ndx < (1<<N2_StbAccessAddrFields::bit_size_entry);++ndx)
{
if (!stb_entry_valid(ndx) || stb_entry_is_asi[ndx])
{
continue;
}
N2_StbAccessCamReg stb_access_cam_reg = get_stb_cam(ndx);
// NB: Range check is broken because BM_ASI() should be
// log2(BM_ASI()) + 1.
uint64_t cam_pa = stb_access_cam_reg.pa() << N2_StbAccessCamReg::bit_size_rsvd0;
uint64_t cam_len = 0;
for (int shift_ndx = 0; shift_ndx < N2_StbAccessCamReg::bit_size_bm_asi; ++shift_ndx)
{
if (stb_access_cam_reg.bm_asi() & (1 << shift_ndx))
{
cam_len = shift_ndx + 1;
}
}
if (memXact.paddr() <= cam_pa + cam_len &&
memXact.paddr() + memXact.size() >= cam_pa)
{
SS_Trap::Type tt = check_store_buffer_entry(ndx);
if(tt != SS_Trap::NO_TRAP)
return tt;
}
}
return SS_Trap::NO_TRAP;
}
/*}}}*/
//flush_store_buffer is called at every step interval or any other regular
//interval to periodically purge the stb contents
SS_Trap::Type N2_StoreBuffer::flush_store_buffer()
{
if(++stb_flush_count == STB_FLUSH_RATE)
{
stb_flush_count = 0;
return purge_store_buffer(incr_stb_pointer());
}
else
return SS_Trap::NO_TRAP;
}
// purge_store_buffer() is passed the index of store buffer entry and
// purges the associated entry from the buffer. It checks for un- and
// correctabled data RAS errors (SBDPU and SBDPC) and CAM address
// parity RAS errors (SBAPP). If any of these errors are detected, it
// sets the necessary error status registers and throws or posts the
// correct trap.
//
// In any case, the store buffer entry is invalidated to avoid
// multiple redundant error hits and mimic the hardware's actual
// behavior.
SS_Trap::Type N2_StoreBuffer::purge_store_buffer(uint_t ndx)/*{{{*/
{
if (!stb_entry_valid(ndx) || stb_entry_is_asi[ndx])
{
return SS_Trap::NO_TRAP;
}
N2_StbAccessAddrFields addr;
addr.entry(ndx);
addr.field(N2_StbAccessAddrFields::CAM_FIELD);
N2_Cerer *cerer = &(strand.core.cerer);
N2_StbAccessDaReg stb_access_da_reg = get_stb_data(ndx);
N2_StbAccessEccReg stb_access_ecc_reg = get_stb_ecc(ndx);
uint32_t even_data = stb_access_da_reg() & 0xffffffff;
BL_EccBits even_ecc(stb_access_ecc_reg.ecc_even());
BL_Hamming_32_7_Synd even_syndrome(even_data,even_ecc);
uint32_t odd_data = stb_access_da_reg() >> 32;
BL_EccBits odd_ecc(stb_access_ecc_reg.ecc_odd());
BL_Hamming_32_7_Synd odd_syndrome(odd_data,odd_ecc);
if (even_syndrome.isUncorrectableError() ||
odd_syndrome.isUncorrectableError())
{
if (cerer->sbdpu_sbiou() && strand.seter.de())
{
set_desr(true, N2_Desr::RE_SBDPU, ndx);
strand.irq.raise(&strand,SS_Interrupt::BIT_SW_RECOVERABLE_ERROR);
}
}
else if (even_syndrome.isSingleBitError() ||
odd_syndrome.isSingleBitError())
{
if (cerer->sbdpc() && strand.seter.dhcce())
{
set_desr(false, N2_Desr::CE_SBDPC, ndx);
strand.irq.raise(&strand,SS_Interrupt::BIT_HW_CORRECTED_ERROR);
}
}
// Check CAM parity
if (cerer->sbapp())
{
if (BL_BitUtility::calc_parity(get_stb_cam(ndx)()) !=
get_stb_ctl(ndx).c_p())
{
strand.core.dfesr.type(1);
strand.core.dfesr.stbindex(ndx);
// scan Store Buffer for entry with highest privledge
uint_t max_priv = N2_Dfesr::USER_PRIV;
for (int priv_ndx = 0;
priv_ndx < (1<<N2_StbAccessAddrFields::bit_size_entry);
++priv_ndx)
{
if (stb_entry_valid(priv_ndx))
{
N2_StbAccessCtlReg stb_access_ctl_reg = get_stb_ctl(priv_ndx);
uint_t entry_control = stb_access_ctl_reg.control();
switch (entry_control)
{
case N2_StbAccessCtlReg::CTL_USER:
break;
case N2_StbAccessCtlReg::CTL_PRIV:
max_priv = max(max_priv, N2_Dfesr::PRIV_PRIV);
break;
case N2_StbAccessCtlReg::CTL_HPRIV:
max_priv = max(max_priv, N2_Dfesr::HPRIV_PRIV);
break;
default: // parity error in entry_control
max_priv = N2_Dfesr::UNKNOWN_PRIV;
break;
}
}
}
invalidate_stb_entry(ndx);
strand.core.dfesr.priv(max_priv);
return SS_Trap::STORE_ERROR;
}
}
invalidate_stb_entry(ndx);
return SS_Trap::NO_TRAP;
}
/*}}}*/
// checkStoreBufferEntry() checks to see if a store buffer entry
// contains a RAS data error (either SBDLC or SBDLU). If so, it sets
// the correct status registers and throws a internal_processor_error
// trap.
SS_Trap::Type N2_StoreBuffer::check_store_buffer_entry(uint_t ndx)/*{{{*/
{
N2_StbAccessDaReg stb_access_da_reg = get_stb_data(ndx);
N2_StbAccessEccReg stb_access_ecc_reg = get_stb_ecc(ndx);
uint32_t even_data = stb_access_da_reg() & 0xffffffff;
BL_EccBits even_ecc(stb_access_ecc_reg.ecc_even());
BL_Hamming_32_7_Synd even_syndrome(even_data,even_ecc);
uint32_t odd_data = stb_access_da_reg() >> 32;
BL_EccBits odd_ecc(stb_access_ecc_reg.ecc_odd());
BL_Hamming_32_7_Synd odd_syndrome(odd_data,odd_ecc);
if (even_syndrome.isUncorrectableError() ||
odd_syndrome.isUncorrectableError())
{
N2_Cerer *cerer = &(strand.core.cerer);
if (cerer->sbdlu() && strand.seter.pscce())
{
// Set error in DSFSR
strand.data_sfsr.error_type(N2_DataSfsr::SBDLU);
// Set stb index in DSFAR
strand.data_sfar.error_addr(ndx);
//purge_store_buffer(ndx);
return SS_Trap::INTERNAL_PROCESSOR_ERROR;
}
} else if (even_syndrome.isSingleBitError() ||
odd_syndrome.isSingleBitError())
{
N2_Cerer *cerer = &(strand.core.cerer);
if(cerer->sbdlc() && strand.seter.pscce())
{
// Set error in DSFSR
strand.data_sfsr.error_type(N2_DataSfsr::SBDLC);
// Set stb index in DSFAR
strand.data_sfar.error_addr(ndx);
//purge_store_buffer(ndx);
return SS_Trap::INTERNAL_PROCESSOR_ERROR;
}
}
return SS_Trap::NO_TRAP;
}
/*}}}*/
void N2_StoreBuffer::set_desr(bool sw_recoverable_trap, uint32_t error_type,
uint32_t error_addr)/*{{{*/
{
N2_Desr *desr = &(strand.desr);
if (!desr->f() )
{
// sets the Strand's desr; not a copy
desr->f(1); // full bit
// S <- 1 for SW_recoverable_trap
// S <- 0 for HW_corrected_error
desr->s(sw_recoverable_trap);
desr->errtype(error_type); // error code
desr->erraddr(error_addr); // error address
}
else
{ // set multiple error bit
desr->me(1);
}
}
/*}}}*/
//=============================================================================
//=============================================================================
//
// STORE BUFFER ACCESS ROUTINES
//
//=============================================================================
//=============================================================================
// Return store buffer content at addr
uint64_t N2_StoreBuffer::get_stb_value(uint64_t addr)/*{{{*/
{
uint_t ptr = (stb_access[addr])();
return ptr;
}
/*}}}*/
// Return store buffer current pointer
uint_t N2_StoreBuffer::get_stb_pointer()/*{{{*/
{
N2_StbAccessAddrFields addr;
addr.field(N2_StbAccessAddrFields::STB_POINTER_FIELD);
addr.entry(0);
uint_t ptr = (stb_access[addr()])();
return ptr & ((1<<N2_StbAccessAddrFields::bit_size_entry) - 1);
}
/*}}}*/
// Increment store buffer current pointer
uint_t N2_StoreBuffer::incr_stb_pointer()/*{{{*/
{
N2_StbAccessAddrFields addr;
addr.field(N2_StbAccessAddrFields::STB_POINTER_FIELD);
addr.entry(0);
uint_t old_ptr = stb_access[addr()]();
uint_t ptr = old_ptr + 1;
ptr &= ((1<<N2_StbAccessAddrFields::bit_size_entry) - 1);
stb_access[addr()].set(ptr);
return old_ptr;
}
/*}}}*/
// Gets the data associated with a store buffer entry
N2_StbAccessDaReg N2_StoreBuffer::get_stb_data(uint_t entry)/*{{{*/
{
N2_StbAccessAddrFields addr;
addr.field(N2_StbAccessAddrFields::DATA_FIELD);
addr.entry(entry);
return stb_access[addr()];
}
/*}}}*/
// Sets the data associated with a store buffer entry
void N2_StoreBuffer::set_stb_data(uint_t entry, N2_StbAccessDaReg &stb_access_da_reg)/*{{{*/
{
N2_StbAccessAddrFields addr;
addr.field(N2_StbAccessAddrFields::DATA_FIELD);
addr.entry(entry);
stb_access[addr()] = stb_access_da_reg;
}
/*}}}*/
// Gets the ECC associated with a store buffer entry
N2_StbAccessEccReg N2_StoreBuffer::get_stb_ecc(uint_t entry)/*{{{*/
{
N2_StbAccessAddrFields addr;
addr.field(N2_StbAccessAddrFields::ECC_FIELD);
addr.entry(entry);
N2_StbAccessEccReg stb_access_ecc_reg;
stb_access_ecc_reg.set(stb_access[addr()]());
return stb_access_ecc_reg;
}
/*}}}*/
// Sets the ECC associated with a store buffer entry
void
N2_StoreBuffer::set_stb_ecc(uint_t entry, N2_StbAccessEccReg &stb_access_ecc_reg)/*{{{*/
{
N2_StbAccessAddrFields addr;
addr.field(N2_StbAccessAddrFields::ECC_FIELD);
addr.entry(entry);
N2_StbAccessDaReg stb_access_da_reg;
stb_access_da_reg.set(stb_access_ecc_reg());
stb_access[addr()] = stb_access_da_reg;
}
/*}}}*/
// Gets the Control/Parity associated with a store buffer entry
N2_StbAccessCtlReg N2_StoreBuffer::get_stb_ctl(uint_t entry) /*{{{*/
{
N2_StbAccessAddrFields addr;
addr.field(N2_StbAccessAddrFields::CNTRL_PARITY_FIELD);
addr.entry(entry);
N2_StbAccessCtlReg stb_access_ctl_reg;
stb_access_ctl_reg.set(stb_access[addr()]());
return stb_access_ctl_reg;
}
/*}}}*/
// Sets the Control/Parity associated with a store buffer entry
void N2_StoreBuffer::set_stb_ctl(uint_t entry, N2_StbAccessCtlReg &stb_access_ctl_reg) /*{{{*/
{
N2_StbAccessAddrFields addr;
addr.field(N2_StbAccessAddrFields::CNTRL_PARITY_FIELD);
addr.entry(entry);
N2_StbAccessDaReg stb_access_da_reg;
stb_access_da_reg.set(stb_access_ctl_reg());
stb_access[addr()] = stb_access_da_reg;
}
/*}}}*/
// Gets the CAM address associated with a store buffer entry
N2_StbAccessCamReg N2_StoreBuffer::get_stb_cam(uint_t entry) {
N2_StbAccessAddrFields addr;
addr.field(N2_StbAccessAddrFields::CAM_FIELD);
addr.entry(entry);
N2_StbAccessCamReg stb_access_cam_reg;
stb_access_cam_reg.set(stb_access[addr()]());
return stb_access_cam_reg;
}
/*}}}*/
// Sets the CAM address associated with a store buffer entry
void N2_StoreBuffer::set_stb_cam(uint_t entry, N2_StbAccessCamReg &stb_access_cam_reg)/*{{{*/
{
N2_StbAccessAddrFields addr;
addr.field(N2_StbAccessAddrFields::CAM_FIELD);
addr.entry(entry);
N2_StbAccessDaReg stb_access_da_reg;
stb_access_da_reg.set(stb_access_cam_reg());
stb_access[addr()] = stb_access_da_reg;
}
/*}}}*/
bool N2_StoreBuffer::stb_entry_valid(uint ndx)/*{{{*/
{
N2_StbAccessAddrFields addr;
addr.entry(ndx);
addr.field(N2_StbAccessAddrFields::CAM_FIELD);
std::map<uint64_t,N2_StbAccessDaReg>::iterator stb_access_da_regNdx = stb_access.find(addr());
return stb_access_da_regNdx != stb_access.end();
}
/*}}}*/
void N2_StoreBuffer::invalidate_stb_entry(uint ndx)/*{{{*/
{
N2_StbAccessAddrFields addr;
addr.entry(ndx);
addr.field(N2_StbAccessAddrFields::CAM_FIELD);
stb_access.erase(addr());
}
/*}}}*/