Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / cpus / vonk / n2 / lib / csr / src / N2_Csr.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: N2_Csr.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_Csr.h"
#include "N2_Model.h"
#include "N2_Strand.h"
using namespace std;
const int N2_Csr::PA_BITS = 40;
const uint64_t N2_Csr::IO_MASK = 0x1ULL << (PA_BITS - 1);
const int N2_Csr::IO_OFFSET = 32;
const uint64_t N2_Csr::IO_RANGE = 0xffULL << IO_OFFSET;
N2_Model* N2_Csr::model = NULL;
//=============================================================================
// return SS_Io::NOT_HANDLED will pass the operation to SS_Memory
//=============================================================================
int N2_Csr::access_io(void* obj, int sid, int access, SS_Paddr addr, uint32_t size, uint64_t* data, uint64_t bitmask)/*{{{*/
{
// filter out non-io address ---> this should never happen as only I/O addr
// get here
if ((addr & IO_MASK) == 0x0)
return SS_Io::NOT_HANDLED;
// CSR access should be 8-byte bound
if ((size % 8) != 0)
return SS_Io::NOT_HANDLED;
N2_Csr *csr = (N2_Csr*)obj;
csr->set_followme(false);
int ret = csr->address_map(addr, access, sid);
int prev_ret = -1;
if (ret == SS_Io::OK)
{
int count = size / 8;
for (int i = 0; i < count; i++)
{
if (access & MemoryTransaction::WRITE)
ret = csr->write64((addr+i*8), data[i], access, sid);
else
ret = csr->read64((addr+i*8), &data[i], access, sid);
// make sure the entire block has the same return state
if (prev_ret == -1)
prev_ret = ret;
else if (prev_ret != ret)
{
fprintf(stderr, "ERROR: N2_Csr::access_io(): addr=%#llx, i=%d, prev_ret=%d, ret=%d\n", addr, i, prev_ret, ret);
assert(0);
}
else if (ret == SS_Io::FOLLOWME)
csr->set_followme(true);
}
if (csr->get_followme())
return SS_Io::FOLLOWME;
else
return ret;
}
else
{
if (access & MemoryTransaction::READ)
{
// address not allowed for read
*data = 0xDEADDEADDEADDEADLLU;
// tell msync not to change this value
return SS_Io::FOLLOWME;
}
else
return ret;
}
}
/*}}}*/
//=============================================================================
//=============================================================================
SS_Io::access_io_status N2_Csr::address_map( SS_Paddr paddr, int access, int sid)/*{{{*/
{
// when IO address CSRs are added, make sure there is no conflict on
// address range, when there is one, CSR has the higher priority.
bool write = (access & MemoryTransaction::WRITE) ? true : false;
bool internal = (access & MemoryTransaction::INTERNAL) ? true : false;
bool mem_slam = (access & MemoryTransaction::MEM_SLAM) ? true : false;
uint64_t range = (paddr & IO_RANGE) >> IO_OFFSET;
// if it is an instruction fetch to I/O space other than 0xff,
// throw illegal instruction.
//TODO the current setup does not provide any information regarding whether
// the access is an instr fetch or not.
//TODO if (type == INSTR_ACCESS) && (range != 0xff) && !write && !internal
// ===> trap( illegal_instruction )
if (range == RANGE_RNG)
{
// 0x82: // RNG (Random Number Generator) IGNORE writes
if (write && !(internal || mem_slam))
{
// silently drop the write
return SS_Io::NOP;
}
else
{
return SS_Io::OK;
}
}
else if (((range == 0x87) ||
((range >= 0x8a) && (range <= 0x8f)) ||
((range >= 0x91) && (range <= 0x9f) && (paddr != 0x9a00000000)) ||
((range >= 0xd0) && (range <= 0xfe))))
{
// 0x87: // TAP to L2 CSR (not supported) ERROR on read
// 0x91-0x9F: // Reserved ERROR on read
// xA0-0xBF: // L2 CSR (never comes to NCU) ERROR on read
// ---> 0xA0-0xBF: (Jeff) This range is read/write from a Riesling
// perspective and a "should never happen"
// from the NCU perspective.
// 0xD0-0xFE: // Reserved ERROR on read
if (!write)
{
if (!internal)
{
//TODO throw data_access_error 0x32 (12.9) on read
// if (seter->getPSCCE() && cerer->getL2U_SOCU())
// ===> trap( data_access_error )
//TODO if seter & cerer are provided through ASI follow-me, then make
// sure we use those follow-me values
return SS_Io::NOP;
}
else
{
return SS_Io::FOLLOWME;
}
}
else
{
return SS_Io::OK;
}
}
else
{
return SS_Io::OK;
}
}
/*}}}*/
//=============================================================================
// return NOT_HANDLED means should re-try the operation in SS_Memory
//=============================================================================
int N2_Csr::read64( SS_Paddr paddr, uint64_t *value, int access, int sid)/*{{{*/
{
int range = (int)((paddr & IO_RANGE) >> IO_OFFSET);
switch (range)
{
case RANGE_NCU:
case RANGE_NCU_2:
// NCU R/W allowed
return ncu_.read64(paddr, value, access, sid);
break;
case RANGE_NIU:
// NIU R/W allowed
return niu_.read64(paddr, value, access, sid);
break;
case RANGE_MCU:
// MCUs R/W allowed
return mcu_.read64(paddr, value, access, sid);
break;
case RANGE_L2_TAP:
// TAP to L2 CSR (not supported) ERROR on read
// should be filtered out by addressMap()
if (internal)
return SS_Io::NOT_HANDLED;
else
{
fprintf(stderr, "ERROR: N2_Csr::read64( addr=%#llx ) not allowed\n", paddr);
return SS_Io::NOP;
}
break;
case RANGE_PIU:
// DMU CSR R/W allowed
return piu_.read64(paddr, value, access, sid);
break;
case RANGE_RST:
// RST R/W allowed
return rst_.read64(paddr, value, access, sid);
break;
case RANGE_SSI:
// SSI (boot ROM) IGNORE writes
return ssi_.read64(paddr, value, access, sid);
break;
default:
if (range >= RANGE_L2_LOW && range <= RANGE_L2_HIGH)
{
// L2 CSR
return l2_.read64(paddr, value, access, sid);
}
else if (range >= RANGE_PCIE_LOW && range <= RANGE_PCIE_HIGH)
{
// PCIE (64GB) / DMUPIO R/W allowed
return pcie_.read64(paddr, value, access, sid);
}
else
{
return SS_Io::NOT_HANDLED;
}
break;
}
return SS_Io::NOT_HANDLED;
}
/*}}}*/
//=============================================================================
// return NOT_HANDLED means should re-try the operation in SS_Memory
//=============================================================================
int N2_Csr::write64( SS_Paddr paddr, uint64_t value, int access, int sid)/*{{{*/
{
int range = (int)((paddr & IO_RANGE) >> IO_OFFSET);
switch (range)
{
case RANGE_NCU:
case RANGE_NCU_2:
// NCU R/W allowed
return ncu_.write64(paddr, value, access, sid);
break;
case RANGE_NIU:
// NIU R/W allowed
return niu_.write64(paddr, value, access, sid);
break;
case RANGE_RNG:
// RNG (Random Number Generator) IGNORE writes
// should be filtered out by addressMap()
// we can get here if msync does an memSlam write, have to take it.
if (access & MemoryTransaction::MEM_SLAM)
return SS_Io::NOT_HANDLED;
else
return SS_Io::NOP;
break;
case RANGE_MCU:
// MCUs R/W allowed
return mcu_.write64(paddr, value, access, sid);
break;
case RANGE_PIU:
// DMU CSR R/W allowed
return piu_.write64(paddr, value, access, sid);
break;
case RANGE_RST:
{
// RST R/W allowed
if (((paddr == 0x8900000808) || (paddr == 0x8900000810)) &&
(access & MemoryTransaction::MEM_SLAM))
{
access |= MemoryTransaction::INTERNAL;
}
int ret = rst_.write64(paddr, value, access, sid);
if (paddr == 0x8900000808)
{
uint64_t data;
int ret2 = rst_.read64(paddr, &data, (access|MemoryTransaction::READ|MemoryTransaction::INTERNAL|SS_BaseCsr::NO_FOLLOW_ME), sid);
// if reset_gen is set to non-zero, raise reset_gen trap if in
// standalone mode, if in cosim mode, wait for testbench INTP command
//TODO if (data > 0) && !cosim ===> raise reset_gen trap
if ((data > 0) && (sid >= 0))
{
if (!(model->strand_ptr(sid)->sim_state.cosim()))
{
fprintf(stderr, "ERROR: N2_Csr::write64(): addr=%#llx, sid=%d, should raise a reset_gen trap\n", paddr, sid);
assert(0);
}
}
}
return ret;
break;
}
case RANGE_SSI:
// SSI (boot ROM)
return ssi_.write64(paddr, value, access, sid);
break;
default:
if (range >= RANGE_L2_LOW && range <= RANGE_L2_HIGH)
{
// L2 CSR
return l2_.write64(paddr, value, access, sid);
}
else if (range >= RANGE_PCIE_LOW && range <= RANGE_PCIE_HIGH)
{
// PCIE (64GB) / DMUPIO R/W allowed
return pcie_.write64(paddr, value, access, sid);
}
else
{
return SS_Io::NOT_HANDLED;
}
break;
}
return SS_Io::NOT_HANDLED;
}
/*}}}*/