2007-03-04 01:02:31 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2006 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.
|
|
|
|
*
|
|
|
|
* Authors: Ali Saidi
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** @file
|
|
|
|
* This device implemetns the niagara I/O bridge chip. It manages incomming
|
|
|
|
* interrupts and posts them to the CPU when needed. It holds mask registers and
|
|
|
|
* various status registers for CPUs to check what interrupts are pending as
|
|
|
|
* well as facilities to send IPIs to other cpus.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
#include "arch/sparc/isa_traits.hh"
|
2007-03-09 22:56:39 +01:00
|
|
|
#include "arch/sparc/faults.hh"
|
2007-03-04 01:02:31 +01:00
|
|
|
#include "base/trace.hh"
|
|
|
|
#include "cpu/intr_control.hh"
|
|
|
|
#include "dev/sparc/iob.hh"
|
|
|
|
#include "dev/platform.hh"
|
|
|
|
#include "mem/port.hh"
|
|
|
|
#include "mem/packet_access.hh"
|
|
|
|
#include "sim/builder.hh"
|
2007-03-09 22:56:39 +01:00
|
|
|
#include "sim/faults.hh"
|
2007-03-04 01:02:31 +01:00
|
|
|
#include "sim/system.hh"
|
|
|
|
|
|
|
|
Iob::Iob(Params *p)
|
|
|
|
: PioDevice(p), ic(p->platform->intrctrl)
|
|
|
|
{
|
|
|
|
iobManAddr = ULL(0x9800000000);
|
|
|
|
iobManSize = ULL(0x0100000000);
|
|
|
|
iobJBusAddr = ULL(0x9F00000000);
|
|
|
|
iobJBusSize = ULL(0x0100000000);
|
|
|
|
assert (params()->system->threadContexts.size() <= MaxNiagaraProcs);
|
|
|
|
// Get the interrupt controller from the platform
|
|
|
|
ic = platform->intrctrl;
|
|
|
|
|
|
|
|
for (int x = 0; x < NumDeviceIds; ++x) {
|
|
|
|
intMan[x].cpu = 0;
|
|
|
|
intMan[x].vector = 0;
|
|
|
|
intCtl[x].mask = true;
|
|
|
|
intCtl[x].pend = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Tick
|
|
|
|
Iob::read(PacketPtr pkt)
|
|
|
|
{
|
|
|
|
assert(pkt->result == Packet::Unknown);
|
|
|
|
|
|
|
|
if (pkt->getAddr() >= iobManAddr && pkt->getAddr() < iobManAddr + iobManSize)
|
|
|
|
readIob(pkt);
|
|
|
|
else if (pkt->getAddr() >= iobJBusAddr && pkt->getAddr() < iobJBusAddr+iobJBusSize)
|
|
|
|
readJBus(pkt);
|
|
|
|
else
|
|
|
|
panic("Invalid address reached Iob\n");
|
|
|
|
|
|
|
|
pkt->result = Packet::Success;
|
|
|
|
return pioDelay;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Iob::readIob(PacketPtr pkt)
|
|
|
|
{
|
|
|
|
Addr accessAddr = pkt->getAddr() - iobManAddr;
|
|
|
|
int index;
|
|
|
|
uint64_t data;
|
|
|
|
|
|
|
|
if (accessAddr >= IntManAddr && accessAddr < IntManAddr + IntManSize) {
|
|
|
|
index = (accessAddr - IntManAddr) >> 3;
|
|
|
|
data = intMan[index].cpu << 8 | intMan[index].vector << 0;
|
|
|
|
pkt->set(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (accessAddr >= IntCtlAddr && accessAddr < IntCtlAddr + IntCtlSize) {
|
|
|
|
index = (accessAddr - IntManAddr) >> 3;
|
|
|
|
data = intCtl[index].mask ? 1 << 2 : 0 |
|
|
|
|
intCtl[index].pend ? 1 << 0 : 0;
|
|
|
|
pkt->set(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (accessAddr == JIntVecAddr) {
|
|
|
|
pkt->set(jIntVec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
panic("Read to unknown IOB offset 0x%x\n", accessAddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Iob::readJBus(PacketPtr pkt)
|
|
|
|
{
|
|
|
|
Addr accessAddr = pkt->getAddr() - iobJBusAddr;
|
|
|
|
int cpuid = pkt->req->getCpuNum();
|
|
|
|
int index;
|
|
|
|
uint64_t data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (accessAddr >= JIntData0Addr && accessAddr < JIntData1Addr) {
|
|
|
|
index = (accessAddr - JIntData0Addr) >> 3;
|
|
|
|
pkt->set(jBusData0[index]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (accessAddr >= JIntData1Addr && accessAddr < JIntDataA0Addr) {
|
|
|
|
index = (accessAddr - JIntData1Addr) >> 3;
|
|
|
|
pkt->set(jBusData1[index]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (accessAddr == JIntDataA0Addr) {
|
|
|
|
pkt->set(jBusData0[cpuid]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (accessAddr == JIntDataA1Addr) {
|
|
|
|
pkt->set(jBusData1[cpuid]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (accessAddr >= JIntBusyAddr && accessAddr < JIntBusyAddr + JIntBusySize) {
|
|
|
|
index = (accessAddr - JIntBusyAddr) >> 3;
|
|
|
|
data = jIntBusy[index].busy ? 1 << 5 : 0 |
|
|
|
|
jIntBusy[index].source;
|
|
|
|
pkt->set(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (accessAddr == JIntABusyAddr) {
|
|
|
|
data = jIntBusy[cpuid].busy ? 1 << 5 : 0 |
|
|
|
|
jIntBusy[cpuid].source;
|
|
|
|
pkt->set(data);
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
panic("Read to unknown JBus offset 0x%x\n", accessAddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Tick
|
|
|
|
Iob::write(PacketPtr pkt)
|
|
|
|
{
|
|
|
|
if (pkt->getAddr() >= iobManAddr && pkt->getAddr() < iobManAddr + iobManSize)
|
|
|
|
writeIob(pkt);
|
|
|
|
else if (pkt->getAddr() >= iobJBusAddr && pkt->getAddr() < iobJBusAddr+iobJBusSize)
|
|
|
|
writeJBus(pkt);
|
|
|
|
else
|
|
|
|
panic("Invalid address reached Iob\n");
|
|
|
|
|
|
|
|
|
|
|
|
pkt->result = Packet::Success;
|
|
|
|
return pioDelay;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Iob::writeIob(PacketPtr pkt)
|
|
|
|
{
|
|
|
|
Addr accessAddr = pkt->getAddr() - iobManAddr;
|
|
|
|
int index;
|
|
|
|
uint64_t data;
|
|
|
|
|
|
|
|
if (accessAddr >= IntManAddr && accessAddr < IntManAddr + IntManSize) {
|
|
|
|
index = (accessAddr - IntManAddr) >> 3;
|
|
|
|
data = pkt->get<uint64_t>();
|
|
|
|
intMan[index].cpu = bits(data,12,8);
|
|
|
|
intMan[index].vector = bits(data,5,0);
|
2007-03-13 05:05:52 +01:00
|
|
|
DPRINTF(Iob, "Wrote IntMan %d cpu %d, vec %d\n", index,
|
|
|
|
intMan[index].cpu, intMan[index].vector);
|
2007-03-04 01:02:31 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (accessAddr >= IntCtlAddr && accessAddr < IntCtlAddr + IntCtlSize) {
|
|
|
|
index = (accessAddr - IntManAddr) >> 3;
|
|
|
|
data = pkt->get<uint64_t>();
|
|
|
|
intCtl[index].mask = bits(data,2,2);
|
|
|
|
if (bits(data,1,1))
|
|
|
|
intCtl[index].pend = false;
|
2007-03-13 05:05:52 +01:00
|
|
|
DPRINTF(Iob, "Wrote IntCtl %d pend %d cleared %d\n", index,
|
|
|
|
intCtl[index].pend, bits(data,2,2));
|
2007-03-04 01:02:31 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (accessAddr == JIntVecAddr) {
|
|
|
|
jIntVec = bits(pkt->get<uint64_t>(), 5,0);
|
2007-03-13 05:05:52 +01:00
|
|
|
DPRINTF(Iob, "Wrote jIntVec %d\n", jIntVec);
|
2007-03-04 01:02:31 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (accessAddr >= IntVecDisAddr && accessAddr < IntVecDisAddr + IntVecDisSize) {
|
|
|
|
Type type;
|
|
|
|
int cpu_id;
|
|
|
|
int vector;
|
|
|
|
index = (accessAddr - IntManAddr) >> 3;
|
|
|
|
data = pkt->get<uint64_t>();
|
|
|
|
type = (Type)bits(data,17,16);
|
|
|
|
cpu_id = bits(data, 12,8);
|
|
|
|
vector = bits(data,5,0);
|
|
|
|
generateIpi(type,cpu_id, vector);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
panic("Write to unknown IOB offset 0x%x\n", accessAddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Iob::writeJBus(PacketPtr pkt)
|
|
|
|
{
|
|
|
|
Addr accessAddr = pkt->getAddr() - iobJBusAddr;
|
|
|
|
int cpuid = pkt->req->getCpuNum();
|
|
|
|
int index;
|
|
|
|
uint64_t data;
|
|
|
|
|
|
|
|
if (accessAddr >= JIntBusyAddr && accessAddr < JIntBusyAddr + JIntBusySize) {
|
|
|
|
index = (accessAddr - JIntBusyAddr) >> 3;
|
|
|
|
data = pkt->get<uint64_t>();
|
|
|
|
jIntBusy[index].busy = bits(data,5,5);
|
2007-03-13 05:05:52 +01:00
|
|
|
DPRINTF(Iob, "Wrote jIntBusy index %d busy: %d\n", index,
|
|
|
|
jIntBusy[index].busy);
|
2007-03-04 01:02:31 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (accessAddr == JIntABusyAddr) {
|
|
|
|
data = pkt->get<uint64_t>();
|
|
|
|
jIntBusy[cpuid].busy = bits(data,5,5);
|
2007-03-13 05:05:52 +01:00
|
|
|
DPRINTF(Iob, "Wrote jIntBusy index %d busy: %d\n", cpuid,
|
|
|
|
jIntBusy[cpuid].busy);
|
2007-03-04 01:02:31 +01:00
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
panic("Write to unknown JBus offset 0x%x\n", accessAddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Iob::receiveDeviceInterrupt(DeviceId devid)
|
|
|
|
{
|
|
|
|
assert(devid < NumDeviceIds);
|
|
|
|
if (intCtl[devid].mask)
|
|
|
|
return;
|
|
|
|
intCtl[devid].mask = true;
|
|
|
|
intCtl[devid].pend = true;
|
2007-03-13 05:05:52 +01:00
|
|
|
DPRINTF(Iob, "Receiving Device interrupt: %d for cpu %d vec %d\n",
|
|
|
|
devid, intMan[devid].cpu, intMan[devid].vector);
|
2007-03-04 01:02:31 +01:00
|
|
|
ic->post(intMan[devid].cpu, SparcISA::IT_INT_VEC, intMan[devid].vector);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Iob::generateIpi(Type type, int cpu_id, int vector)
|
|
|
|
{
|
2007-03-09 22:56:39 +01:00
|
|
|
SparcISA::SparcFault<SparcISA::PowerOnReset> *por = new SparcISA::PowerOnReset();
|
|
|
|
if (cpu_id >= sys->getNumCPUs())
|
2007-03-04 01:02:31 +01:00
|
|
|
return;
|
2007-03-04 04:45:26 +01:00
|
|
|
|
2007-03-09 22:56:39 +01:00
|
|
|
switch (type) {
|
|
|
|
case 0: // interrupt
|
2007-03-13 05:05:52 +01:00
|
|
|
DPRINTF(Iob, "Generating interrupt because of I/O write to cpu: %d vec %d\n",
|
|
|
|
cpu_id, vector);
|
2007-03-09 22:56:39 +01:00
|
|
|
ic->post(cpu_id, SparcISA::IT_INT_VEC, vector);
|
|
|
|
break;
|
|
|
|
case 1: // reset
|
|
|
|
warn("Sending reset to CPU: %d\n", cpu_id);
|
|
|
|
if (vector != por->trapType())
|
|
|
|
panic("Don't know how to set non-POR reset to cpu\n");
|
|
|
|
por->invoke(sys->threadContexts[cpu_id]);
|
|
|
|
sys->threadContexts[cpu_id]->activate();
|
|
|
|
break;
|
|
|
|
case 2: // idle -- this means stop executing and don't wake on interrupts
|
2007-03-13 05:05:52 +01:00
|
|
|
DPRINTF(Iob, "Idling CPU because of I/O write cpu: %d\n", cpu_id);
|
2007-03-09 22:56:39 +01:00
|
|
|
sys->threadContexts[cpu_id]->halt();
|
|
|
|
break;
|
|
|
|
case 3: // resume
|
2007-03-13 05:05:52 +01:00
|
|
|
DPRINTF(Iob, "Resuming CPU because of I/O write cpu: %d\n", cpu_id);
|
2007-03-09 22:56:39 +01:00
|
|
|
sys->threadContexts[cpu_id]->activate();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("Invalid type to generate ipi\n");
|
|
|
|
}
|
2007-03-04 01:02:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
Iob::receiveJBusInterrupt(int cpu_id, int source, uint64_t d0, uint64_t d1)
|
|
|
|
{
|
|
|
|
// If we are already dealing with an interrupt for that cpu we can't deal
|
|
|
|
// with another one right now... come back later
|
|
|
|
if (jIntBusy[cpu_id].busy)
|
|
|
|
return false;
|
|
|
|
|
2007-03-13 05:05:52 +01:00
|
|
|
DPRINTF(Iob, "Receiving jBus interrupt: %d for cpu %d vec %d\n",
|
|
|
|
source, cpu_id, jIntVec);
|
|
|
|
|
2007-03-04 01:02:31 +01:00
|
|
|
jIntBusy[cpu_id].busy = true;
|
|
|
|
jIntBusy[cpu_id].source = source;
|
|
|
|
jBusData0[cpu_id] = d0;
|
|
|
|
jBusData1[cpu_id] = d1;
|
|
|
|
|
|
|
|
ic->post(cpu_id, SparcISA::IT_INT_VEC, jIntVec);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Iob::addressRanges(AddrRangeList &range_list)
|
|
|
|
{
|
|
|
|
range_list.clear();
|
|
|
|
range_list.push_back(RangeSize(iobManAddr, iobManSize));
|
|
|
|
range_list.push_back(RangeSize(iobJBusAddr, iobJBusSize));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
Iob::serialize(std::ostream &os)
|
|
|
|
{
|
|
|
|
|
|
|
|
SERIALIZE_SCALAR(jIntVec);
|
|
|
|
SERIALIZE_ARRAY(jBusData0, MaxNiagaraProcs);
|
|
|
|
SERIALIZE_ARRAY(jBusData1, MaxNiagaraProcs);
|
|
|
|
for (int x = 0; x < NumDeviceIds; x++) {
|
|
|
|
nameOut(os, csprintf("%s.Int%d", name(), x));
|
|
|
|
paramOut(os, "cpu", intMan[x].cpu);
|
|
|
|
paramOut(os, "vector", intMan[x].vector);
|
|
|
|
paramOut(os, "mask", intCtl[x].mask);
|
|
|
|
paramOut(os, "pend", intCtl[x].pend);
|
|
|
|
};
|
|
|
|
for (int x = 0; x < MaxNiagaraProcs; x++) {
|
|
|
|
nameOut(os, csprintf("%s.jIntBusy%d", name(), x));
|
|
|
|
paramOut(os, "busy", jIntBusy[x].busy);
|
|
|
|
paramOut(os, "source", jIntBusy[x].source);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Iob::unserialize(Checkpoint *cp, const std::string §ion)
|
|
|
|
{
|
|
|
|
UNSERIALIZE_SCALAR(jIntVec);
|
|
|
|
UNSERIALIZE_ARRAY(jBusData0, MaxNiagaraProcs);
|
|
|
|
UNSERIALIZE_ARRAY(jBusData1, MaxNiagaraProcs);
|
|
|
|
for (int x = 0; x < NumDeviceIds; x++) {
|
|
|
|
paramIn(cp, csprintf("%s.Int%d", name(), x), "cpu", intMan[x].cpu);
|
|
|
|
paramIn(cp, csprintf("%s.Int%d", name(), x), "vector", intMan[x].vector);
|
|
|
|
paramIn(cp, csprintf("%s.Int%d", name(), x), "mask", intCtl[x].mask);
|
|
|
|
paramIn(cp, csprintf("%s.Int%d", name(), x), "pend", intCtl[x].pend);
|
|
|
|
};
|
|
|
|
for (int x = 0; x < MaxNiagaraProcs; x++) {
|
|
|
|
paramIn(cp, csprintf("%s.jIntBusy%d", name(), x), "busy", jIntBusy[x].busy);
|
|
|
|
paramIn(cp, csprintf("%s.jIntBusy%d", name(), x), "source", jIntBusy[x].source);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BEGIN_DECLARE_SIM_OBJECT_PARAMS(Iob)
|
|
|
|
Param<Tick> pio_latency;
|
|
|
|
SimObjectParam<Platform *> platform;
|
|
|
|
SimObjectParam<System *> system;
|
|
|
|
END_DECLARE_SIM_OBJECT_PARAMS(Iob)
|
|
|
|
|
|
|
|
BEGIN_INIT_SIM_OBJECT_PARAMS(Iob)
|
|
|
|
|
|
|
|
INIT_PARAM(pio_latency, "Programmed IO latency"),
|
|
|
|
INIT_PARAM(platform, "platform"),
|
|
|
|
INIT_PARAM(system, "system object")
|
|
|
|
|
|
|
|
END_INIT_SIM_OBJECT_PARAMS(Iob)
|
|
|
|
|
|
|
|
CREATE_SIM_OBJECT(Iob)
|
|
|
|
{
|
|
|
|
Iob::Params *p = new Iob::Params;
|
|
|
|
p->name = getInstanceName();
|
|
|
|
p->pio_delay = pio_latency;
|
|
|
|
p->platform = platform;
|
|
|
|
p->system = system;
|
|
|
|
return new Iob(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
REGISTER_SIM_OBJECT("Iob", Iob)
|