// ========== 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 "SS_AddressMap.h"
//======================================================================
//======================================================================
SS_BaseCsr::access( void* obj
,
SS_BaseCsr
* self
= (SS_BaseCsr
*)obj
;
self
->read64(addr
, data
, MemoryTransaction::READ
, sid
);
self
->write64(addr
, *data
, MemoryTransaction::WRITE
, sid
);
//======================================================================
//======================================================================
SS_BaseCsr::findAttributeEntryNdx(SS_Paddr paddr
,
const RegisterAttribute
* attributeTable
,
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) {
//======================================================================
//======================================================================
const RegisterAttribute
* const
SS_BaseCsr::findAttributeEntry(SS_Paddr paddr
,
const RegisterAttribute
* attributeTable
,
uint64_t ndx
= findAttributeEntryNdx(paddr
, attributeTable
, entries
);
return ndx
== ENTRY_NOT_FOUND
? NULL
: &attributeTable
[ndx
];
//======================================================================
//======================================================================
SS_BaseCsr::warmReset(vector
<vector
<RegisterValue
>*>* values
,
//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
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
].attribute
->maskWarm
== 0x0)
// if no warm-reset mask, just use warm-reset value
regs
[k
].set_data(regs
[k
].attribute
->warmReset
);
// otherwise, use warm-reset mask on current data to produce
regs
[k
].set_data(regs
[k
].data() & regs
[k
].attribute
->maskWarm
);
//=============================================================================
// this function assumes value is always 8-byte
//=============================================================================
SS_BaseCsr::reverseByteOrder(uint64_t value
, bool littleEndian
)
uint8_t *byteArr
= (uint8_t*)&value
;
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
];
//=============================================================================
// 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.
//=============================================================================
SS_BaseCsr::forceWrite(uint64_t inputData
,
const RegisterAttribute
* attribute
,
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);
//=============================================================================
SS_BaseCsr::normalWrite(uint64_t inputData
,
const RegisterAttribute
* attribute
,
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
))));
//=============================================================================
//=============================================================================
SS_BaseCsr::registerAddressSpace(RegisterAttribute
*attributeTable
,
const string
&description
)
//TODO this routine does not take localvs global I/O address into account
uint64_t start
= ULLONG_MAX
;
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
if (end
+ 1 < attributeTable
[tableNdx
].startAddr
+
attributeTable
[tableNdx
].stride
*
attributeTable
[tableNdx
].count
) {
os
<< "SS_BaseCsr::registerAddressSpace: " <<
"bad RegisterAttribute ranges: endAddr 0x" <<
" startAddr 0x" << attributeTable
[tableNdx
].startAddr
<<
" stride " << dec
<< attributeTable
[tableNdx
].stride
<<
" count\n" << dec
<< attributeTable
[tableNdx
].count
<<
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
);
//=============================================================================
// 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.
//=============================================================================
//======================================================================
//======================================================================
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.
if (is_global_addr(paddr
))
laddr
= global2local(paddr
);
// 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
);
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
if (((*iter
).addr
== gaddr
) &&
(((*iter
).sid
== sid
) || ((*iter
).sid
< 0) || (sid
< 0)))
// no matching follow-me, search real CSR
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
);
fprintf(stderr
, "ERROR: SS_BaseCsr::read64( paddr=%#llx, sid=%d ): null RegisterValue pointer\n", paddr
, sid
);
// no valid data for this entry yet, generate it
// 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
;
// 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
);
// not in csr attribute list, use global addr for non-csr
return read64_notCsr( gaddr
, laddr
, data
);
//=============================================================================
// read64_notCsr() reads any non-CSR address
//=============================================================================
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).
//=============================================================================
SS_BaseCsr::write64( SS_Paddr paddr
, uint64_t data
, int access
, int sid
)
if (is_global_addr(paddr
))
laddr
= global2local(paddr
);
// 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
);
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
);
findAttributeEntryNdx(baddr
, attributeTable_
, nrAttributeEntries_
);
if (attributeNdx
!= ENTRY_NOT_FOUND
)
const RegisterAttribute
* attribute
= &attributeTable_
[attributeNdx
];
RegisterValue
*value
= attribute
->find(baddr
, values_
[attributeNdx
], nid
);
fprintf(stderr
, "ERROR: SS_BaseCsr::write64( paddr=%#llx, sid=%d ): null RegisterValue pointer\n", paddr
, sid
);
uint64_t old_data
= attribute
->por
;
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
);
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
);
fprintf(stderr
, "WARNING: %s::write64(): paddr=%#llx (gaddr=%#llx) is read-only, data=%#llx is NOT committed\n", className_
.c_str(), paddr
, gaddr
, data
);
// the entry is not available yet, create it
RegisterValue
newValue(baddr
, new_data
, attribute
, sid
);
value
->set_data(new_data
);
return write64_notCsr( gaddr
, data
);
//=============================================================================
// write64_notCsr() reads any non-CSR address
//=============================================================================
SS_BaseCsr::write64_notCsr( SS_Paddr gaddr
, uint64_t data
)
// for addr not in csr list, just write it to memory using global-addr
SS_Memory::memory
.poke64(gaddr
, data
);
//=============================================================================
// 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).
//=============================================================================
SS_BaseCsr::read_memimage( SS_Paddr gaddr
, SS_Paddr laddr
, uint64_t* data
)
*data
= SS_Memory::memory
.peek64(gaddr
);
*data
= mm1
->ld64(gaddr
);
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
*data
= SS_Memory::memory
.peek64(laddr
);
*data
= mm1
->ld64(laddr
);