Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / cpus / vonk / ss / lib / csr / src / SS_BaseCsr.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: SS_BaseCsr.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 <sstream>
#include <list>
#include "SS_BaseCsr.h"
#include "SS_Memory.h"
#include "SS_AddressMap.h"
using namespace std;
//======================================================================
//======================================================================
void
SS_BaseCsr::access( void* obj,
uint_t sid,
SS_Access::Type type,
SS_Paddr addr,
uint_t size,
uint64_t* data)
{
SS_BaseCsr* self = (SS_BaseCsr*)obj;
switch (type)
{
case SS_Access::LOAD:
self->read64(addr, data, MemoryTransaction::READ, sid);
break;
case SS_Access::STORE:
self->write64(addr, *data, MemoryTransaction::WRITE, sid);
break;
}
}
//======================================================================
//======================================================================
int64_t
SS_BaseCsr::findAttributeEntryNdx(SS_Paddr paddr,
const RegisterAttribute* attributeTable,
int entries)
{
for (int i = 0; i < entries; i++) {
if (attributeTable[i].startAddr <= paddr && paddr <= attributeTable[i].endAddr) {
if (((paddr - attributeTable[i].startAddr) & (attributeTable[i].stride - 1)) == 0) {
return i;
}
}
}
return ENTRY_NOT_FOUND;
}
//======================================================================
//======================================================================
const RegisterAttribute* const
SS_BaseCsr::findAttributeEntry(SS_Paddr paddr,
const RegisterAttribute* attributeTable,
int entries)
{
uint64_t ndx = findAttributeEntryNdx(paddr, attributeTable, entries);
return ndx == ENTRY_NOT_FOUND ? NULL: &attributeTable[ndx];
}
//======================================================================
//======================================================================
void
SS_BaseCsr::warmReset(vector<vector<RegisterValue>*>* values,
int nrAttributeEntries)
{
//TODO if a register has different por and warm-reset values, and
// the register is never referenced up to this point, such that it
// is not in the table yet, then we won't create a register with
// warm-reset value for it, the next access to the register will
// return its por value, which is wrong. 11/29/05
for (int i = 0; i < nrAttributeEntries; i++)
{
// CSR entries, has 1-to-1 mapping with RegisterAttribute table
vector<vector<RegisterValue>*> nodes = values[i];
for (int j = 0; j < nodes.size(); j++)
{
// each entry points to a vector of registers belong to a particulat node
if (nodes[j] != NULL)
{
vector<RegisterValue>& regs = *nodes[j];
for (int k = 0; k < regs.size(); k++)
{
// registers, of a particular node and a particular CSR entry
if (regs[k].valid())
{
if (regs[k].attribute->maskWarm == 0x0)
{
// if no warm-reset mask, just use warm-reset value
regs[k].set_data(regs[k].attribute->warmReset);
}
else
{
// otherwise, use warm-reset mask on current data to produce
// the new data.
regs[k].set_data(regs[k].data() & regs[k].attribute->maskWarm);
}
}
}
}
}
}
}
//=============================================================================
// this function assumes value is always 8-byte
//=============================================================================
uint64_t
SS_BaseCsr::reverseByteOrder(uint64_t value, bool littleEndian)
{
if (littleEndian)
{
int size = 8;
uint8_t *byteArr = (uint8_t*)&value;
uint64_t revValue = 0;
uint8_t *revByteArr = (uint8_t*)&revValue;
int revIdx = sizeof( value ) - size;
int topByte = sizeof( value ) - 1;
for ( int byte = topByte; byte > topByte - size; --byte )
{
revByteArr[revIdx] = byteArr[byte];
++revIdx;
}
return revValue;
}
else
{
return value;
}
}
//=============================================================================
// mainly used by follow-me, allow writing into any bits except reserved ones,
// ignore RW1C and RW1S masking. If the data is in littleEndian, then the
// reserved-mask must be reversed before doing the masking.
//=============================================================================
uint64_t
SS_BaseCsr::forceWrite(uint64_t inputData,
const RegisterAttribute* attribute,
int access)
{
bool littleEndian = (access & MemoryTransaction::LITTLE_ENDIAN) ? true : false;
return (inputData & ~(reverseByteOrder(attribute->maskRSVD, littleEndian)));
}
//=============================================================================
// normal write operation, take all masking into account.
//
// exclude_maskRW = maskRW & ~(maskRW1C | maskRW1S)
// new_data = (input_data & exclude_maskRW) |
// (input_data & maskRW1S) |
// (old_data & ~(exclde_maskRW | (input_data & maskRW1C)))
// newData = normalWrite(inputData, oldData, attribute, littleEndian);
//=============================================================================
uint64_t
SS_BaseCsr::normalWrite(uint64_t inputData,
uint64_t oldData,
const RegisterAttribute* attribute,
int access)
{
bool littleEndian = (access & MemoryTransaction::LITTLE_ENDIAN) ? true : false;
uint64_t maskRW_exc = attribute->maskRW & ~(attribute->maskRW1C | attribute->maskRW1S);
uint64_t newData = (inputData & reverseByteOrder(maskRW_exc, littleEndian)) |
(inputData & reverseByteOrder(attribute->maskRW1S, littleEndian)) |
(oldData & ~(reverseByteOrder(maskRW_exc, littleEndian) | (inputData & reverseByteOrder(attribute->maskRW1C, littleEndian))));
return newData;
}
//=============================================================================
//=============================================================================
void
SS_BaseCsr::registerAddressSpace(RegisterAttribute *attributeTable,
uint_t tableSize,
const string &description)
{
//TODO this routine does not take localvs global I/O address into account
uint64_t start = ULLONG_MAX;
uint64_t end = 0;
for (uint_t tableNdx = 0; tableNdx < tableSize; ++tableNdx) {
if (attributeTable[tableNdx].startAddr < start) {
start = attributeTable[tableNdx].startAddr;
}
if (attributeTable[tableNdx].endAddr > end) {
// Be sure to cover the entire address range
end = attributeTable[tableNdx].endAddr +
attributeTable[tableNdx].stride - 1;
// Die if the stride and the count don't match the
// endAddr
if (end + 1 < attributeTable[tableNdx].startAddr +
attributeTable[tableNdx].stride *
attributeTable[tableNdx].count) {
ostringstream os;
os << "SS_BaseCsr::registerAddressSpace: " <<
"bad RegisterAttribute ranges: endAddr 0x" <<
hex << end <<
" startAddr 0x" << attributeTable[tableNdx].startAddr <<
" stride " << dec << attributeTable[tableNdx].stride <<
" count\n" << dec << attributeTable[tableNdx].count <<
"\n";
fprintf(stderr, "ERROR: %s\n", os.str().c_str());
}
}
}
#ifndef COMPILE_FOR_COSIM
//TODO we don't have strand pointer here, cannot access sim_state.cosim()
//if (!s->sim_state.cosim())
SS_Io::io.add(start, end, this, SS_AddressMap::ABS, SS_BaseCsr::access);
#endif
}
//=============================================================================
// when mem==NULL, a paddr that does not match any supported CSR will
// return INVALID value, this is mainly used by callback corresponding
// to asi_read. when access.getInternal()==true, it means the
// read64() is invoked by callback// corresponding to asi_read, the
// read64() will return INVALID for non-matched paddr, and it will
// remove a matched entry after the value is returned.
//=============================================================================
//======================================================================
// THIS IS READ64
//======================================================================
int
SS_BaseCsr::read64( SS_Paddr paddr, uint64_t* data, int access, int sid )
{
// for N2, there is only one cpu (i.e., node), so local I/O address is
// equal to global I/O address.
// In general strands in the same cpu use the same global I/O address, but
// for CSR follow-me, each strand can have its own follow-me value, so we
// need strand-id in follow-me matching up.
SS_Paddr laddr;
SS_Paddr gaddr;
if (is_global_addr(paddr))
{
laddr = global2local(paddr);
gaddr = paddr;
}
else if (sid < 0)
{
// if it is not a global address, then we need a real strand-id to
// convert the address to global address, otherwise it is an error.
fprintf(stderr, "ERROR: SS_BaseCsr::read64( paddr=%#llx, sid=%d ): paddr not a global addr\n", paddr, sid);
*data = 0;
return SS_Io::NOP;
}
else
{
laddr = paddr;
gaddr = local2global(paddr, sid);
}
SS_Paddr baddr = addr_node0(paddr);
int nid = get_node_id(gaddr);
// for any read, look at follow-me list first, if a match can be found,
// use it (and then discard the entry), otherwise go to real register area.
if ((access & SS_BaseCsr::NO_FOLLOW_ME) == 0)
{
list<FollowMeData>::iterator iter = followme_.begin();
for (; iter != followme_.end(); iter++)
{
// if either side of sid is -1, then pick the first entry that has a
// matching addr
if (((*iter).addr == gaddr) &&
(((*iter).sid == sid) || ((*iter).sid < 0) || (sid < 0)))
{
*data = (*iter).data;
followme_.erase(iter);
return SS_Io::FOLLOWME;
}
}
}
// no matching follow-me, search real CSR
uint64_t attributeNdx =
findAttributeEntryNdx(baddr, attributeTable_, nrAttributeEntries_);
if (attributeNdx != ENTRY_NOT_FOUND)
{
// attribute table uses node0 addr as keyword, 'value' vector has an
// 1-on-1 mapping with attribute table, so node0 addr has to be used.
const RegisterAttribute* attribute = &attributeTable_[attributeNdx];
RegisterValue *value = attribute->find(baddr, values_[attributeNdx], nid);
if (value == NULL)
{
// this is an error
fprintf(stderr, "ERROR: SS_BaseCsr::read64( paddr=%#llx, sid=%d ): null RegisterValue pointer\n", paddr, sid);
*data = 0;
return SS_Io::NOP;
}
else
{
if (!value->valid())
{
// no valid data for this entry yet, generate it
uint64_t init_data;
// first, see if the addr has a valid data in mem.image (sometimes
// uses this approach to provide a csr's init value other then its
// pre-defined por value).
int state = read_memimage(gaddr, laddr, &init_data);
init_data &= attribute->maskRSVD;
if (init_data == 0x0)
{
// when read_memimage() returns 0, we consider that means no data
// for this addr is available in mem.image, so we use the por value
// in CSR attribute table.
//TODO this can be troublesome, if a real 0x0 value is set for the
// addr in mem.image, then we will mistake it as not available,
// and use por value instead (wrongly), do we have a way of
// knowing whether a valid data is available in mem.image?
init_data = attribute->por;
}
RegisterValue newValue(baddr, init_data, attribute, sid);
*value = newValue;
}
*data = value->data();
}
}
else
{
// not in csr attribute list, use global addr for non-csr
return read64_notCsr( gaddr, laddr, data );
}
return SS_Io::OK;
}
//=============================================================================
// read64_notCsr() reads any non-CSR address
//=============================================================================
int
SS_BaseCsr::read64_notCsr( SS_Paddr gaddr, SS_Paddr laddr, uint64_t* data )
{
// for addr not in csr list, just read it from memory
return read_memimage(gaddr, laddr, data);
}
//=============================================================================
// when mem==NULL, a paddr that does not match any supported CSR will
// be silently dropped, this is mainly used by asi_read function.
// when access.getInternal()==true, it means allwoing write to RO
// non-reserved field(s) of a register, this is to mimic hardware
// writing to RO non-reserved field(s).
//=============================================================================
int
SS_BaseCsr::write64( SS_Paddr paddr, uint64_t data, int access, int sid )
{
SS_Paddr laddr;
SS_Paddr gaddr;
if (is_global_addr(paddr))
{
laddr = global2local(paddr);
gaddr = paddr;
}
else if (sid < 0)
{
// if it is not a global address, then we need a real strand-id to
// convert the address to global address, otherwise it is an error.
fprintf(stderr, "ERROR: SS_BaseCsr::write64( paddr=%#llx, sid=%d ): paddr not a global addr\n", paddr, sid);
return SS_Io::NOP;
}
else
{
laddr = paddr;
gaddr = local2global(paddr, sid);
}
SS_Paddr baddr = addr_node0(paddr);
int nid = get_node_id(gaddr);
if (access & MemoryTransaction::FOLLOW_ME)
{
// this is a follow-me write, keep the value in followme_ list
FollowMeData fm(gaddr, data, sid);
followme_.push_back(fm);
return SS_Io::OK;
}
else
{
uint64_t attributeNdx =
findAttributeEntryNdx(baddr, attributeTable_, nrAttributeEntries_);
if (attributeNdx != ENTRY_NOT_FOUND)
{
const RegisterAttribute* attribute = &attributeTable_[attributeNdx];
RegisterValue *value = attribute->find(baddr, values_[attributeNdx], nid);
if (value == NULL)
{
// this is an error
fprintf(stderr, "ERROR: SS_BaseCsr::write64( paddr=%#llx, sid=%d ): null RegisterValue pointer\n", paddr, sid);
return SS_Io::NOP;
}
else
{
uint64_t new_data;
uint64_t old_data = attribute->por;
if (value->valid())
{
old_data = value->data();
}
if ((access & MemoryTransaction::INTERNAL) ||
(access & MemoryTransaction::MEM_SLAM))
{
// allow writing into any non-reserved bits
new_data = forceWrite(data, attribute, access);
}
else
{
if (attribute->protect != RegisterAttribute::RO)
{
// can only write into W bits (e.g., WO, RW, RW1C, RW1S, etc)
new_data = normalWrite(data, old_data, attribute, access);
}
else
{
fprintf(stderr, "WARNING: %s::write64(): paddr=%#llx (gaddr=%#llx) is read-only, data=%#llx is NOT committed\n", className_.c_str(), paddr, gaddr, data);
return SS_Io::NOP;
}
}
if (!value->valid())
{
// the entry is not available yet, create it
RegisterValue newValue(baddr, new_data, attribute, sid);
*value = newValue;
}
else
{
value->set_data(new_data);
}
}
}
else
{
// paddr not in csr list
return write64_notCsr( gaddr, data );
}
}
return SS_Io::OK;
}
//=============================================================================
// write64_notCsr() reads any non-CSR address
//=============================================================================
int
SS_BaseCsr::write64_notCsr( SS_Paddr gaddr, uint64_t data )
{
// for addr not in csr list, just write it to memory using global-addr
#ifndef COMPILE_FOR_SAM
SS_Memory::memory.poke64(gaddr, data);
return SS_Io::OK;
#else
extern SS_Memory *mm1;
mm1->st64(gaddr, data);
return SS_Io::OK;
#endif
}
//=============================================================================
// even if an addr in question is in CSR list, a diag may have the addr's
// initial value in mem.image, it can use either local or global addr, so
// look for those first. Always start with global addr, as that is specific
// to each node, local addr is shared among nodes. If none is found, then use
// the pre-defined por value (if one is available). (yes, it is messy, but
// verification diag wants the flexibility).
//=============================================================================
int
SS_BaseCsr::read_memimage( SS_Paddr gaddr, SS_Paddr laddr, uint64_t* data )
{
#ifndef COMPILE_FOR_SAM
*data = SS_Memory::memory.peek64(gaddr);
#else
extern SS_Memory *mm1;
*data = mm1->ld64(gaddr);
#endif
if ((*data == 0x0) && (gaddr != laddr))
{
// if not match with global addr, try local addr
//TODO this is not perfect, returned value of 0x0 can be
// (1) the addr is not in mem.image, (2) the addr is in
// mem.image with value 0x0. In here we assume 0x0 means
// not in mem.image.
#ifndef COMPILE_FOR_SAM
*data = SS_Memory::memory.peek64(laddr);
#else
extern SS_Memory *mm1;
*data = mm1->ld64(laddr);
#endif
}
return SS_Io::OK;
}