gem5/arch/alpha/alpha_memory.cc
Steve Raasch a5366f9683 testing behavior of decoder.cc file
arch/alpha/alpha_memory.cc:
    append a blank line

--HG--
extra : convert_revision : 482388741a3000b0c745733c9882300ff9ecf529
2003-10-08 23:09:50 -04:00

663 lines
17 KiB
C++

/*
* Copyright (c) 2003 The Regents of The University of Michigan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sstream>
#include <string>
#include <vector>
#include "alpha_memory.hh"
#include "ev5.hh"
#include "exec_context.hh"
#include "trace.hh"
#include "inifile.hh"
#include "str.hh"
using namespace std;
///////////////////////////////////////////////////////////////////////
//
// Alpha TLB
//
AlphaTlb::AlphaTlb(const string &name, int s)
: SimObject(name), size(s), nlu(0)
{
table = new AlphaISA::PTE[size];
memset(table, 0, sizeof(AlphaISA::PTE[size]));
}
AlphaTlb::~AlphaTlb()
{
if (table)
delete [] table;
}
// look up an entry in the TLB
AlphaISA::PTE *
AlphaTlb::lookup(Addr vpn, uint8_t asn) const
{
DPRINTF(TLB, "lookup %#x\n", vpn);
PageTable::const_iterator i = lookupTable.find(vpn);
if (i == lookupTable.end())
return NULL;
while (i->first == vpn) {
int index = i->second;
AlphaISA::PTE *pte = &table[index];
assert(pte->valid);
if (vpn == pte->tag && (pte->asma || pte->asn == asn))
return pte;
++i;
}
// not found...
return NULL;
}
void
AlphaTlb::checkCacheability(MemReqPtr req)
{
// in Alpha, cacheability is controlled by upper-level bits of the
// physical address
if (req->paddr & PA_UNCACHED_BIT) {
if (PA_IPR_SPACE(req->paddr)) {
// IPR memory space not implemented
if (!req->xc->misspeculating())
panic("IPR memory space not implemented! PA=%x\n", req->paddr);
} else {
// mark request as uncacheable
req->flags |= UNCACHEABLE;
}
}
}
// insert a new TLB entry
void
AlphaTlb::insert(Addr vaddr, AlphaISA::PTE &pte)
{
if (table[nlu].valid) {
Addr oldvpn = table[nlu].tag;
PageTable::iterator i = lookupTable.find(oldvpn);
if (i == lookupTable.end())
panic("TLB entry not found in lookupTable");
int index;
while ((index = i->second) != nlu) {
if (table[index].tag != oldvpn)
panic("TLB entry not found in lookupTable");
++i;
}
DPRINTF(TLB, "remove @%d: %#x -> %#x\n", nlu, oldvpn, table[nlu].ppn);
lookupTable.erase(i);
}
Addr vpn = VA_VPN(vaddr);
DPRINTF(TLB, "insert @%d: %#x -> %#x\n", nlu, vpn, pte.ppn);
table[nlu] = pte;
table[nlu].tag = vpn;
table[nlu].valid = true;
lookupTable.insert(make_pair(vpn, nlu));
nextnlu();
}
void
AlphaTlb::flushAll()
{
memset(table, 0, sizeof(AlphaISA::PTE[size]));
lookupTable.clear();
nlu = 0;
}
void
AlphaTlb::flushProcesses()
{
PageTable::iterator i = lookupTable.begin();
PageTable::iterator end = lookupTable.end();
while (i != end) {
int index = i->second;
AlphaISA::PTE *pte = &table[index];
assert(pte->valid);
if (!pte->asma) {
DPRINTF(TLB, "flush @%d: %#x -> %#x\n", index, pte->tag, pte->ppn);
pte->valid = false;
lookupTable.erase(i);
}
++i;
}
}
void
AlphaTlb::flushAddr(Addr vaddr, uint8_t asn)
{
Addr vpn = VA_VPN(vaddr);
PageTable::iterator i = lookupTable.find(vpn);
if (i == lookupTable.end())
return;
while (i->first == vpn) {
int index = i->second;
AlphaISA::PTE *pte = &table[index];
assert(pte->valid);
if (vpn == pte->tag && (pte->asma || pte->asn == asn)) {
DPRINTF(TLB, "flushaddr @%d: %#x -> %#x\n", index, vpn, pte->ppn);
// invalidate this entry
pte->valid = false;
lookupTable.erase(i);
}
++i;
}
}
void
AlphaTlb::serialize()
{
nameOut();
paramOut("size", size);
paramOut("nlu", nlu);
stringstream buf;
for (int i = 0; i < size; i++) {
buf.str("");
ccprintf(buf, "pte%02d.valid", i);
paramOut(buf.str(), table[i].valid);
buf.str("");
ccprintf(buf, "pte%02d.tag", i);
paramOut(buf.str(), table[i].tag);
buf.str("");
ccprintf(buf, "pte%02d.ppn", i);
paramOut(buf.str(), table[i].ppn);
buf.str("");
ccprintf(buf, "pte%02d.xre", i);
paramOut(buf.str(), table[i].xre);
buf.str("");
ccprintf(buf, "pte%02d.xwe", i);
paramOut(buf.str(), table[i].xwe);
buf.str("");
ccprintf(buf, "pte%02d.fonr", i);
paramOut(buf.str(), table[i].fonr);
buf.str("");
ccprintf(buf, "pte%02d.fonw", i);
paramOut(buf.str(), table[i].fonw);
buf.str("");
ccprintf(buf, "pte%02d.asma", i);
paramOut(buf.str(), table[i].asma);
buf.str("");
ccprintf(buf, "pte%02d.asn", i);
paramOut(buf.str(), table[i].asn);
}
}
void
AlphaTlb::unserialize(IniFile &db, const string &category, ConfigNode *node)
{
string data;
stringstream buf;
db.findDefault(category,"size",data);
to_number(data,size);
db.findDefault(category,"nlu",data);
to_number(data,nlu);
for (int i = 0; i < size; i++) {
buf.str("");
ccprintf(buf, "pte%02d.valid", i);
db.findDefault(category, buf.str(), data);
to_number(data, table[i].valid);
buf.str("");
ccprintf(buf, "pte%02d.tag", i);
db.findDefault(category, buf.str(), data);
to_number(data, table[i].tag);
buf.str("");
ccprintf(buf, "pte%02d.ppn", i);
db.findDefault(category, buf.str(), data);
to_number(data, table[i].ppn);
buf.str("");
ccprintf(buf, "pte%02d.xre", i);
db.findDefault(category, buf.str(), data);
to_number(data, table[i].xre);
buf.str("");
ccprintf(buf, "pte%02d.xwe", i);
db.findDefault(category, buf.str(), data);
to_number(data, table[i].xwe);
buf.str("");
ccprintf(buf, "pte%02d.fonr", i);
db.findDefault(category, buf.str(), data);
to_number(data, table[i].fonr);
buf.str("");
ccprintf(buf, "pte%02d.fonw", i);
db.findDefault(category, buf.str(), data);
to_number(data, table[i].fonw);
buf.str("");
ccprintf(buf, "pte%02d.asma", i);
db.findDefault(category, buf.str(), data);
to_number(data, table[i].asma);
buf.str("");
ccprintf(buf, "pte%02d.asn", i);
db.findDefault(category, buf.str(), data);
to_number(data, table[i].asn);
}
}
///////////////////////////////////////////////////////////////////////
//
// Alpha ITB
//
AlphaItb::AlphaItb(const std::string &name, int size)
: AlphaTlb(name, size)
{}
void
AlphaItb::regStats()
{
hits
.name(name() + ".hits")
.desc("ITB hits");
misses
.name(name() + ".misses")
.desc("ITB misses");
acv
.name(name() + ".acv")
.desc("ITB acv");
accesses
.name(name() + ".accesses")
.desc("ITB accesses");
accesses = hits + misses;
}
void
AlphaItb::fault(Addr pc, ExecContext *xc) const
{
uint64_t *ipr = xc->regs.ipr;
if (!xc->misspeculating()) {
ipr[AlphaISA::IPR_ITB_TAG] = pc;
ipr[AlphaISA::IPR_IFAULT_VA_FORM] =
ipr[AlphaISA::IPR_IVPTBR] | (VA_VPN(pc) << 3);
}
}
Fault
AlphaItb::translate(MemReqPtr req) const
{
InternalProcReg *ipr = req->xc->regs.ipr;
if (PC_PAL(req->vaddr)) {
// strip off PAL PC marker (lsb is 1)
req->paddr = (req->vaddr & ~3) & PA_IMPL_MASK;
hits++;
return No_Fault;
}
// verify that this is a good virtual address
if (!validVirtualAddress(req->vaddr)) {
fault(req->vaddr, req->xc);
acv++;
return Itb_Acv_Fault;
}
// Check for "superpage" mapping: when SP<1> is set, and
// VA<42:41> == 2, VA<39:13> maps directly to PA<39:13>.
if ((MCSR_SP(ipr[AlphaISA::IPR_MCSR]) & 2) &&
VA_SPACE(req->vaddr) == 2) {
// only valid in kernel mode
if (ICM_CM(ipr[AlphaISA::IPR_ICM]) != AlphaISA::mode_kernel) {
fault(req->vaddr, req->xc);
acv++;
return Itb_Acv_Fault;
}
req->flags |= PHYSICAL;
}
if (req->flags & PHYSICAL) {
req->paddr = req->vaddr & PA_IMPL_MASK;
} else {
// not a physical address: need to look up pte
AlphaISA::PTE *pte = lookup(VA_VPN(req->vaddr),
DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN]));
if (!pte) {
fault(req->vaddr, req->xc);
misses++;
return Itb_Fault_Fault;
}
req->paddr = PA_PFN2PA(pte->ppn) + VA_POFS(req->vaddr & ~3);
// check permissions for this access
if (!(pte->xre & (1 << ICM_CM(ipr[AlphaISA::IPR_ICM])))) {
// instruction access fault
fault(req->vaddr, req->xc);
acv++;
return Itb_Acv_Fault;
}
}
checkCacheability(req);
hits++;
return No_Fault;
}
///////////////////////////////////////////////////////////////////////
//
// Alpha DTB
//
AlphaDtb::AlphaDtb(const std::string &name, int size)
: AlphaTlb(name, size)
{}
void
AlphaDtb::regStats()
{
read_hits
.name(name() + ".read_hits")
.desc("DTB read hits")
;
read_misses
.name(name() + ".read_misses")
.desc("DTB read misses")
;
read_acv
.name(name() + ".read_acv")
.desc("DTB read access violations")
;
read_accesses
.name(name() + ".read_accesses")
.desc("DTB read accesses")
;
write_hits
.name(name() + ".write_hits")
.desc("DTB write hits")
;
write_misses
.name(name() + ".write_misses")
.desc("DTB write misses")
;
write_acv
.name(name() + ".write_acv")
.desc("DTB write access violations")
;
write_accesses
.name(name() + ".write_accesses")
.desc("DTB write accesses")
;
hits
.name(name() + ".hits")
.desc("DTB hits")
;
misses
.name(name() + ".misses")
.desc("DTB misses")
;
acv
.name(name() + ".acv")
.desc("DTB access violations")
;
accesses
.name(name() + ".accesses")
.desc("DTB accesses")
;
hits = read_hits + write_hits;
misses = read_misses + write_misses;
acv = read_acv + write_acv;
accesses = read_accesses + write_accesses;
}
void
AlphaDtb::fault(Addr vaddr, uint64_t flags, ExecContext *xc) const
{
uint64_t *ipr = xc->regs.ipr;
// set fault address and flags
if (!xc->misspeculating() && !xc->regs.intrlock) {
// set VA register with faulting address
ipr[AlphaISA::IPR_VA] = vaddr;
// set MM_STAT register flags
ipr[AlphaISA::IPR_MM_STAT] = (((xc->regs.opcode & 0x3f) << 11)
| ((xc->regs.ra & 0x1f) << 6)
| (flags & 0x3f));
// set VA_FORM register with faulting formatted address
ipr[AlphaISA::IPR_VA_FORM] =
ipr[AlphaISA::IPR_MVPTBR] | (VA_VPN(vaddr) << 3);
// lock these registers until the VA register is read
xc->regs.intrlock = true;
}
}
Fault
AlphaDtb::translate(MemReqPtr req, bool write) const
{
RegFile *regs = &req->xc->regs;
Addr pc = regs->pc;
InternalProcReg *ipr = regs->ipr;
if (write)
write_accesses++;
else
read_accesses++;
AlphaISA::md_mode_type mode =
(AlphaISA::md_mode_type)DTB_CM_CM(ipr[AlphaISA::IPR_DTB_CM]);
if (PC_PAL(pc)) {
mode = (req->flags & ALTMODE) ? (AlphaISA::md_mode_type)
(ALT_MODE_AM(ipr[AlphaISA::IPR_ALT_MODE]))
: AlphaISA::mode_kernel;
}
// verify that this is a good virtual address
if (!validVirtualAddress(req->vaddr)) {
fault(req->vaddr,
((write ? MM_STAT_WR_MASK : 0) | MM_STAT_BAD_VA_MASK |
MM_STAT_ACV_MASK),
req->xc);
if (write) { write_acv++; } else { read_acv++; }
return Dtb_Fault_Fault;
}
// Check for "superpage" mapping: when SP<1> is set, and
// VA<42:41> == 2, VA<39:13> maps directly to PA<39:13>.
if ((MCSR_SP(ipr[AlphaISA::IPR_MCSR]) & 2) && VA_SPACE(req->vaddr) == 2) {
// only valid in kernel mode
if (DTB_CM_CM(ipr[AlphaISA::IPR_DTB_CM]) != AlphaISA::mode_kernel) {
fault(req->vaddr,
((write ? MM_STAT_WR_MASK : 0) | MM_STAT_ACV_MASK),
req->xc);
if (write) { write_acv++; } else { read_acv++; }
return Dtb_Acv_Fault;
}
req->flags |= PHYSICAL;
}
if (req->flags & PHYSICAL) {
req->paddr = req->vaddr & PA_IMPL_MASK;
} else {
// not a physical address: need to look up pte
AlphaISA::PTE *pte = lookup(VA_VPN(req->vaddr),
DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN]));
if (!pte) {
// page fault
fault(req->vaddr,
((write ? MM_STAT_WR_MASK : 0) | MM_STAT_DTB_MISS_MASK),
req->xc);
if (write) { write_misses++; } else { read_misses++; }
return (req->flags & VPTE) ? Pdtb_Miss_Fault : Ndtb_Miss_Fault;
}
req->paddr = PA_PFN2PA(pte->ppn) | VA_POFS(req->vaddr);
if (write) {
if (!(pte->xwe & MODE2MASK(mode))) {
// declare the instruction access fault
fault(req->vaddr, MM_STAT_WR_MASK | MM_STAT_ACV_MASK |
(pte->fonw ? MM_STAT_FONW_MASK : 0),
req->xc);
write_acv++;
return Dtb_Fault_Fault;
}
if (pte->fonw) {
fault(req->vaddr, MM_STAT_WR_MASK | MM_STAT_FONW_MASK,
req->xc);
write_acv++;
return Dtb_Fault_Fault;
}
} else {
if (!(pte->xre & MODE2MASK(mode))) {
fault(req->vaddr,
MM_STAT_ACV_MASK | (pte->fonr ? MM_STAT_FONR_MASK : 0),
req->xc);
read_acv++;
return Dtb_Acv_Fault;
}
if (pte->fonr) {
fault(req->vaddr, MM_STAT_FONR_MASK, req->xc);
read_acv++;
return Dtb_Fault_Fault;
}
}
}
checkCacheability(req);
if (write)
write_hits++;
else
read_hits++;
return No_Fault;
}
AlphaISA::PTE &
AlphaTlb::index()
{
AlphaISA::PTE *pte = &table[nlu];
nextnlu();
return *pte;
}
BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaItb)
Param<int> size;
END_DECLARE_SIM_OBJECT_PARAMS(AlphaItb)
BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaItb)
INIT_PARAM_DFLT(size, "TLB size", 48)
END_INIT_SIM_OBJECT_PARAMS(AlphaItb)
CREATE_SIM_OBJECT(AlphaItb)
{
return new AlphaItb(getInstanceName(), size);
}
REGISTER_SIM_OBJECT("AlphaITB", AlphaItb)
BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaDtb)
Param<int> size;
END_DECLARE_SIM_OBJECT_PARAMS(AlphaDtb)
BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaDtb)
INIT_PARAM_DFLT(size, "TLB size", 64)
END_INIT_SIM_OBJECT_PARAMS(AlphaDtb)
CREATE_SIM_OBJECT(AlphaDtb)
{
return new AlphaDtb(getInstanceName(), size);
}
REGISTER_SIM_OBJECT("AlphaDTB", AlphaDtb)