diff options
Diffstat (limited to 'src')
79 files changed, 2838 insertions, 1962 deletions
diff --git a/src/SConscript b/src/SConscript index 260aca25c..2722444a3 100644 --- a/src/SConscript +++ b/src/SConscript @@ -47,6 +47,7 @@ Import('env') # Base sources used by all configurations. base_sources = Split(''' + base/annotate.cc base/circlebuf.cc base/cprintf.cc base/fast_alloc.cc diff --git a/src/arch/alpha/isa/decoder.isa b/src/arch/alpha/isa/decoder.isa index f449d2d69..30959c72e 100644 --- a/src/arch/alpha/isa/decoder.isa +++ b/src/arch/alpha/isa/decoder.isa @@ -823,7 +823,12 @@ decode OPCODE default Unknown::unknown() { 0x54: m5panic({{ panic("M5 panic instruction called at pc=%#x.", xc->readPC()); }}, IsNonSpeculative); - + 0x55: m5anBegin({{ + AlphaPseudo::anBegin(xc->tcBase(), R16); + }}, IsNonSpeculative); + 0x56: m5anWait({{ + AlphaPseudo::anWait(xc->tcBase(), R16, R17); + }}, IsNonSpeculative); } } #endif diff --git a/src/arch/alpha/isa_traits.hh b/src/arch/alpha/isa_traits.hh index 72e38ae3e..4f439b8df 100644 --- a/src/arch/alpha/isa_traits.hh +++ b/src/arch/alpha/isa_traits.hh @@ -42,7 +42,6 @@ class StaticInstPtr; namespace AlphaISA { - using namespace LittleEndianGuest; // These enumerate all the registers for dependence tracking. @@ -60,12 +59,14 @@ namespace AlphaISA StaticInstPtr decodeInst(ExtMachInst); + // Alpha Does NOT have a delay slot + #define ISA_HAS_DELAY_SLOT 0 + const Addr PageShift = 13; const Addr PageBytes = ULL(1) << PageShift; const Addr PageMask = ~(PageBytes - 1); const Addr PageOffset = PageBytes - 1; - #if FULL_SYSTEM //////////////////////////////////////////////////////////////////////// diff --git a/src/arch/alpha/linux/process.cc b/src/arch/alpha/linux/process.cc index 997c78ac9..357ebdada 100644 --- a/src/arch/alpha/linux/process.cc +++ b/src/arch/alpha/linux/process.cc @@ -162,7 +162,7 @@ SyscallDesc AlphaLinuxProcess::syscallDescs[] = { /* 38 */ SyscallDesc("osf_old_stat", unimplementedFunc), /* 39 */ SyscallDesc("setpgid", unimplementedFunc), /* 40 */ SyscallDesc("osf_old_lstat", unimplementedFunc), - /* 41 */ SyscallDesc("dup", unimplementedFunc), + /* 41 */ SyscallDesc("dup", dupFunc), /* 42 */ SyscallDesc("pipe", pipePseudoFunc), /* 43 */ SyscallDesc("osf_set_program_attributes", unimplementedFunc), /* 44 */ SyscallDesc("osf_profil", unimplementedFunc), diff --git a/src/arch/mips/isa_traits.hh b/src/arch/mips/isa_traits.hh index fd484e315..f85fc5bea 100644 --- a/src/arch/mips/isa_traits.hh +++ b/src/arch/mips/isa_traits.hh @@ -47,6 +47,9 @@ namespace MipsISA StaticInstPtr decodeInst(ExtMachInst); + // MIPS DOES a delay slot + #define ISA_HAS_DELAY_SLOT 1 + const Addr PageShift = 13; const Addr PageBytes = ULL(1) << PageShift; const Addr PageMask = ~(PageBytes - 1); diff --git a/src/arch/sparc/isa_traits.hh b/src/arch/sparc/isa_traits.hh index 7f830eb28..6d5aa4251 100644 --- a/src/arch/sparc/isa_traits.hh +++ b/src/arch/sparc/isa_traits.hh @@ -57,6 +57,9 @@ namespace SparcISA //This makes sure the big endian versions of certain functions are used. using namespace BigEndianGuest; + // Alpha Does NOT have a delay slot + #define ISA_HAS_DELAY_SLOT 1 + //TODO this needs to be a SPARC Noop // Alpha UNOP (ldq_u r31,0(r0)) const MachInst NoopMachInst = 0x2ffe0000; diff --git a/src/base/annotate.cc b/src/base/annotate.cc new file mode 100644 index 000000000..ba2fb1788 --- /dev/null +++ b/src/base/annotate.cc @@ -0,0 +1,122 @@ +/* + * 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 + */ + +#include "base/annotate.hh" +#include "base/callback.hh" +#include "base/output.hh" +#include "base/trace.hh" +#include "sim/root.hh" +#include "sim/sim_exit.hh" +#include "sim/system.hh" + + + +class AnnotateDumpCallback : public Callback +{ + public: + virtual void process(); +}; + +void +AnnotateDumpCallback::process() +{ + Annotate::annotations.dump(); +} + +namespace Annotate { + + +Annotate annotations; + +Annotate::Annotate() +{ + registerExitCallback(new AnnotateDumpCallback); +} + +void +Annotate::add(System *sys, Addr stack, uint32_t sm, uint32_t st, + uint32_t wm, uint32_t ws) +{ + AnnotateData *an; + + an = new AnnotateData; + an->time = curTick; + + std::map<System*, std::string>::iterator i = nameCache.find(sys); + if (i == nameCache.end()) { + nameCache[sys] = sys->name(); + } + + an->system = nameCache[sys]; + an->stack = stack; + an->stateMachine = sm; + an->curState = st; + an->waitMachine = wm; + an->waitState = ws; + + data.push_back(an); + if (an->waitMachine) + DPRINTF(Annotate, "Annotating: %s(%#llX) %d:%d waiting on %d:%d\n", + an->system, an->stack, an->stateMachine, an->curState, + an->waitMachine, an->waitState); + else + DPRINTF(Annotate, "Annotating: %s(%#llX) %d:%d beginning\n", an->system, + an->stack, an->stateMachine, an->curState); + + DPRINTF(Annotate, "Now %d events on list\n", data.size()); + +} + +void +Annotate::dump() +{ + + std::list<AnnotateData*>::iterator i; + + i = data.begin(); + + if (i == data.end()) + return; + + std::ostream *os = simout.create("annotate.dat"); + + AnnotateData *an; + + while (i != data.end()) { + DPRINTF(Annotate, "Writing\n", data.size()); + an = *i; + ccprintf(*os, "%d %s(%#llX) %d %d %d %d\n", an->time, an->system, + an->stack, an->stateMachine, an->curState, an->waitMachine, + an->waitState); + i++; + } +} + +} //namespace Annotate diff --git a/src/base/annotate.hh b/src/base/annotate.hh new file mode 100644 index 000000000..36607bf90 --- /dev/null +++ b/src/base/annotate.hh @@ -0,0 +1,73 @@ +/* + * 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 + */ + +#ifndef __BASE__ANNOTATE_HH__ +#define __BASE__ANNOTATE_HH__ + +#include "sim/host.hh" + +#include <string> +#include <list> +#include <map> + + +class System; + +namespace Annotate { + + +class Annotate { + + protected: + struct AnnotateData { + Tick time; + std::string system; + Addr stack; + uint32_t stateMachine; + uint32_t curState; + uint32_t waitMachine; + uint32_t waitState; + }; + + std::list<AnnotateData*> data; + std::map<System*, std::string> nameCache; + + public: + Annotate(); + void add(System *sys, Addr stack, uint32_t sm, uint32_t st, uint32_t + wm, uint32_t ws); + void dump(); +}; + +extern Annotate annotations; +} //namespace Annotate + +#endif //__BASE__ANNOTATE_HH__ + diff --git a/src/base/remote_gdb.cc b/src/base/remote_gdb.cc index 0d3b73b1e..e4efa31e3 100644 --- a/src/base/remote_gdb.cc +++ b/src/base/remote_gdb.cc @@ -796,7 +796,6 @@ RemoteGDB::trap(int type) size_t datalen, len; char data[KGDB_BUFLEN + 1]; char buffer[sizeof(gdbregs) * 2 + 256]; - char temp[KGDB_BUFLEN]; const char *p; char command, subcmd; string var; @@ -904,10 +903,14 @@ RemoteGDB::trap(int type) } if (read(val, (size_t)len, (char *)buffer)) { - mem2hex(temp, buffer, len); - send(temp); + // variable length array would be nice, but C++ doesn't + // officially support those... + char *temp = new char[2*len+1]; + mem2hex(temp, buffer, len); + send(temp); + delete [] temp; } else { - send("E05"); + send("E05"); } continue; diff --git a/src/base/traceflags.py b/src/base/traceflags.py index 27c24107c..8e8153b68 100644 --- a/src/base/traceflags.py +++ b/src/base/traceflags.py @@ -50,6 +50,7 @@ ccfilename = sys.argv[1] + '.cc' baseFlags = [ 'Activity', 'AlphaConsole', + 'Annotate', 'BADADDR', 'BE', 'BPredRAS', diff --git a/src/cpu/base_dyn_inst.hh b/src/cpu/base_dyn_inst.hh index 40611abe6..3158aa9cf 100644 --- a/src/cpu/base_dyn_inst.hh +++ b/src/cpu/base_dyn_inst.hh @@ -291,18 +291,18 @@ class BaseDynInst : public FastAlloc, public RefCounted /** Returns whether the instruction was predicted taken or not. */ bool predTaken() -#if THE_ISA == ALPHA_ISA - { return predPC != (PC + sizeof(MachInst)); } -#else +#if ISA_HAS_DELAY_SLOT { return predPC != (nextPC + sizeof(MachInst)); } +#else + { return predPC != (PC + sizeof(MachInst)); } #endif /** Returns whether the instruction mispredicted. */ bool mispredicted() -#if THE_ISA == ALPHA_ISA - { return predPC != nextPC; } -#else +#if ISA_HAS_DELAY_SLOT { return predPC != nextNPC; } +#else + { return predPC != nextPC; } #endif // // Instruction types. Forward checks to StaticInst object. diff --git a/src/cpu/o3/bpred_unit_impl.hh b/src/cpu/o3/bpred_unit_impl.hh index e4e656632..477c8e4cb 100644 --- a/src/cpu/o3/bpred_unit_impl.hh +++ b/src/cpu/o3/bpred_unit_impl.hh @@ -29,6 +29,7 @@ */ #include "arch/types.hh" +#include "arch/isa_traits.hh" #include "base/trace.hh" #include "base/traceflags.hh" #include "cpu/o3/bpred_unit.hh" @@ -197,10 +198,10 @@ BPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC, unsigned tid) ++BTBLookups; if (inst->isCall()) { -#if THE_ISA == ALPHA_ISA - Addr ras_pc = PC + sizeof(MachInst); // Next PC -#else +#if ISA_HAS_DELAY_SLOT Addr ras_pc = PC + (2 * sizeof(MachInst)); // Next Next PC +#else + Addr ras_pc = PC + sizeof(MachInst); // Next PC #endif RAS[tid].push(ras_pc); @@ -209,8 +210,8 @@ BPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC, unsigned tid) predict_record.wasCall = true; DPRINTF(Fetch, "BranchPred: [tid:%i]: Instruction %#x was a call" - ", adding %#x to the RAS.\n", - tid, inst->readPC(), ras_pc); + ", adding %#x to the RAS index: %i.\n", + tid, inst->readPC(), ras_pc, RAS[tid].topIdx()); } if (BTB.valid(PC, tid)) { @@ -283,7 +284,6 @@ BPredUnit<Impl>::squash(const InstSeqNum &squashed_sn, unsigned tid) RAS[tid].restore(pred_hist.front().RASIndex, pred_hist.front().RASTarget); - } else if (pred_hist.front().wasCall) { DPRINTF(Fetch, "BranchPred: [tid:%i]: Removing speculative entry " "added to the RAS.\n",tid); diff --git a/src/cpu/o3/commit_impl.hh b/src/cpu/o3/commit_impl.hh index f200f5f18..34f487e2c 100644 --- a/src/cpu/o3/commit_impl.hh +++ b/src/cpu/o3/commit_impl.hh @@ -722,7 +722,7 @@ DefaultCommit<Impl>::commit() // then use one older sequence number. InstSeqNum squashed_inst = fromIEW->squashedSeqNum[tid]; -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT InstSeqNum bdelay_done_seq_num; bool squash_bdelay_slot; @@ -748,7 +748,7 @@ DefaultCommit<Impl>::commit() if (fromIEW->includeSquashInst[tid] == true) { squashed_inst--; -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT bdelay_done_seq_num--; #endif } @@ -756,13 +756,13 @@ DefaultCommit<Impl>::commit() // number as the youngest instruction in the ROB. youngestSeqNum[tid] = squashed_inst; -#if THE_ISA == ALPHA_ISA - rob->squash(squashed_inst, tid); - toIEW->commitInfo[tid].squashDelaySlot = true; -#else +#if ISA_HAS_DELAY_SLOT rob->squash(bdelay_done_seq_num, tid); toIEW->commitInfo[tid].squashDelaySlot = squash_bdelay_slot; toIEW->commitInfo[tid].bdelayDoneSeqNum = bdelay_done_seq_num; +#else + rob->squash(squashed_inst, tid); + toIEW->commitInfo[tid].squashDelaySlot = true; #endif changedROBNumEntries[tid] = true; @@ -800,7 +800,7 @@ DefaultCommit<Impl>::commit() // Try to commit any instructions. commitInsts(); } else { -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT skidInsert(); #endif } @@ -906,11 +906,11 @@ DefaultCommit<Impl>::commitInsts() } PC[tid] = nextPC[tid]; -#if THE_ISA == ALPHA_ISA - nextPC[tid] = nextPC[tid] + sizeof(TheISA::MachInst); -#else +#if ISA_HAS_DELAY_SLOT nextPC[tid] = nextNPC[tid]; nextNPC[tid] = nextNPC[tid] + sizeof(TheISA::MachInst); +#else + nextPC[tid] = nextPC[tid] + sizeof(TheISA::MachInst); #endif #if FULL_SYSTEM @@ -1115,10 +1115,7 @@ DefaultCommit<Impl>::getInsts() { DPRINTF(Commit, "Getting instructions from Rename stage.\n"); -#if THE_ISA == ALPHA_ISA - // Read any renamed instructions and place them into the ROB. - int insts_to_process = std::min((int)renameWidth, fromRename->size); -#else +#if ISA_HAS_DELAY_SLOT // Read any renamed instructions and place them into the ROB. int insts_to_process = std::min((int)renameWidth, (int)(fromRename->size + skidBuffer.size())); @@ -1127,15 +1124,16 @@ DefaultCommit<Impl>::getInsts() DPRINTF(Commit, "%i insts available to process. Rename Insts:%i " "SkidBuffer Insts:%i\n", insts_to_process, fromRename->size, skidBuffer.size()); +#else + // Read any renamed instructions and place them into the ROB. + int insts_to_process = std::min((int)renameWidth, fromRename->size); #endif for (int inst_num = 0; inst_num < insts_to_process; ++inst_num) { DynInstPtr inst; -#if THE_ISA == ALPHA_ISA - inst = fromRename->insts[inst_num]; -#else +#if ISA_HAS_DELAY_SLOT // Get insts from skidBuffer or from Rename if (skidBuffer.size() > 0) { DPRINTF(Commit, "Grabbing skidbuffer inst.\n"); @@ -1145,6 +1143,8 @@ DefaultCommit<Impl>::getInsts() DPRINTF(Commit, "Grabbing rename inst.\n"); inst = fromRename->insts[rename_idx++]; } +#else + inst = fromRename->insts[inst_num]; #endif int tid = inst->threadNumber; @@ -1167,7 +1167,7 @@ DefaultCommit<Impl>::getInsts() } } -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT if (rename_idx < fromRename->size) { DPRINTF(Commit,"Placing Rename Insts into skidBuffer.\n"); diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc index af032132e..19ab7f4c5 100644 --- a/src/cpu/o3/cpu.cc +++ b/src/cpu/o3/cpu.cc @@ -181,7 +181,6 @@ FullO3CPU<Impl>::FullO3CPU(Params *params) params->activity), globalSeqNum(1), - #if FULL_SYSTEM system(params->system), physmem(system->physmem), @@ -322,6 +321,11 @@ FullO3CPU<Impl>::FullO3CPU(Params *params) lastActivatedCycle = -1; + // Give renameMap & rename stage access to the freeList; + //for (int i=0; i < numThreads; i++) { + //globalSeqNum[i] = 1; + //} + contextSwitch = false; } @@ -627,7 +631,7 @@ FullO3CPU<Impl>::insertThread(unsigned tid) //Set PC/NPC/NNPC setPC(src_tc->readPC(), tid); setNextPC(src_tc->readNextPC(), tid); -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT setNextNPC(src_tc->readNextNPC(), tid); #endif @@ -1197,7 +1201,7 @@ FullO3CPU<Impl>::removeInstsNotInROB(unsigned tid, while (inst_it != end_it) { assert(!instList.empty()); -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT if(!squash_delay_slot && delay_slot_seq_num >= (*inst_it)->seqNum) { break; diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh index 7e18571f1..dcdcd1fe6 100644 --- a/src/cpu/o3/cpu.hh +++ b/src/cpu/o3/cpu.hh @@ -598,7 +598,7 @@ class FullO3CPU : public BaseO3CPU } /** The global sequence number counter. */ - InstSeqNum globalSeqNum; + InstSeqNum globalSeqNum;//[Impl::MaxThreads]; /** Pointer to the checker, which can dynamically verify * instruction results at run time. This can be set to NULL if it diff --git a/src/cpu/o3/decode_impl.hh b/src/cpu/o3/decode_impl.hh index 160845378..80b6cc4c9 100644 --- a/src/cpu/o3/decode_impl.hh +++ b/src/cpu/o3/decode_impl.hh @@ -282,12 +282,7 @@ DefaultDecode<Impl>::squash(DynInstPtr &inst, unsigned tid) toFetch->decodeInfo[tid].doneSeqNum = inst->seqNum; toFetch->decodeInfo[tid].squash = true; toFetch->decodeInfo[tid].nextPC = inst->branchTarget(); -#if THE_ISA == ALPHA_ISA - toFetch->decodeInfo[tid].branchTaken = - inst->readNextPC() != (inst->readPC() + sizeof(TheISA::MachInst)); - - InstSeqNum squash_seq_num = inst->seqNum; -#else +#if ISA_HAS_DELAY_SLOT toFetch->decodeInfo[tid].branchTaken = inst->readNextNPC() != (inst->readNextPC() + sizeof(TheISA::MachInst)); @@ -295,6 +290,11 @@ DefaultDecode<Impl>::squash(DynInstPtr &inst, unsigned tid) squashAfterDelaySlot[tid] = false; InstSeqNum squash_seq_num = bdelayDoneSeqNum[tid]; +#else + toFetch->decodeInfo[tid].branchTaken = + inst->readNextPC() != (inst->readPC() + sizeof(TheISA::MachInst)); + + InstSeqNum squash_seq_num = inst->seqNum; #endif // Might have to tell fetch to unblock. @@ -317,7 +317,7 @@ DefaultDecode<Impl>::squash(DynInstPtr &inst, unsigned tid) // insts in them. while (!insts[tid].empty()) { -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT if (insts[tid].front()->seqNum <= squash_seq_num) { DPRINTF(Decode, "[tid:%i]: Cannot remove incoming decode " "instructions before delay slot [sn:%i]. %i insts" @@ -331,7 +331,7 @@ DefaultDecode<Impl>::squash(DynInstPtr &inst, unsigned tid) while (!skidBuffer[tid].empty()) { -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT if (skidBuffer[tid].front()->seqNum <= squash_seq_num) { DPRINTF(Decode, "[tid:%i]: Cannot remove skidBuffer " "instructions before delay slot [sn:%i]. %i insts" @@ -765,7 +765,7 @@ DefaultDecode<Impl>::decodeInsts(unsigned tid) // Might want to set some sort of boolean and just do // a check at the end -#if THE_ISA == ALPHA_ISA +#if !ISA_HAS_DELAY_SLOT squash(inst, inst->threadNumber); inst->setPredTarg(inst->branchTarget()); break; diff --git a/src/cpu/o3/fetch_impl.hh b/src/cpu/o3/fetch_impl.hh index 1a31f58e4..1e080181c 100644 --- a/src/cpu/o3/fetch_impl.hh +++ b/src/cpu/o3/fetch_impl.hh @@ -339,7 +339,7 @@ DefaultFetch<Impl>::initStage() for (int tid = 0; tid < numThreads; tid++) { PC[tid] = cpu->readPC(tid); nextPC[tid] = cpu->readNextPC(tid); -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT nextNPC[tid] = cpu->readNextNPC(tid); #endif } @@ -429,7 +429,7 @@ DefaultFetch<Impl>::takeOverFrom() stalls[i].commit = 0; PC[i] = cpu->readPC(i); nextPC[i] = cpu->readNextPC(i); -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT nextNPC[i] = cpu->readNextNPC(i); delaySlotInfo[i].branchSeqNum = -1; delaySlotInfo[i].numInsts = 0; @@ -492,22 +492,20 @@ DefaultFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC, bool predict_taken; if (!inst->isControl()) { -#if THE_ISA == ALPHA_ISA - next_PC = next_PC + instSize; - inst->setPredTarg(next_PC); -#else +#if ISA_HAS_DELAY_SLOT Addr cur_PC = next_PC; next_PC = cur_PC + instSize; //next_NPC; next_NPC = cur_PC + (2 * instSize);//next_NPC + instSize; inst->setPredTarg(next_NPC); +#else + next_PC = next_PC + instSize; + inst->setPredTarg(next_PC); #endif return false; } int tid = inst->threadNumber; -#if THE_ISA == ALPHA_ISA - predict_taken = branchPred.predict(inst, next_PC, tid); -#else +#if ISA_HAS_DELAY_SLOT Addr pred_PC = next_PC; predict_taken = branchPred.predict(inst, pred_PC, tid); @@ -539,6 +537,8 @@ DefaultFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC, next_NPC = next_NPC + instSize; } +#else + predict_taken = branchPred.predict(inst, next_PC, tid); #endif ++fetchedBranches; @@ -692,7 +692,7 @@ DefaultFetch<Impl>::squashFromDecode(const Addr &new_PC, doSquash(new_PC, tid); -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT if (seq_num <= delaySlotInfo[tid].branchSeqNum) { delaySlotInfo[tid].numInsts = 0; delaySlotInfo[tid].targetAddr = 0; @@ -780,10 +780,7 @@ DefaultFetch<Impl>::squash(const Addr &new_PC, const InstSeqNum &seq_num, doSquash(new_PC, tid); -#if THE_ISA == ALPHA_ISA - // Tell the CPU to remove any instructions that are not in the ROB. - cpu->removeInstsNotInROB(tid, true, 0); -#else +#if ISA_HAS_DELAY_SLOT if (seq_num <= delaySlotInfo[tid].branchSeqNum) { delaySlotInfo[tid].numInsts = 0; delaySlotInfo[tid].targetAddr = 0; @@ -792,6 +789,9 @@ DefaultFetch<Impl>::squash(const Addr &new_PC, const InstSeqNum &seq_num, // Tell the CPU to remove any instructions that are not in the ROB. cpu->removeInstsNotInROB(tid, squash_delay_slot, seq_num); +#else + // Tell the CPU to remove any instructions that are not in the ROB. + cpu->removeInstsNotInROB(tid, true, 0); #endif } @@ -901,10 +901,10 @@ DefaultFetch<Impl>::checkSignalsAndUpdate(unsigned tid) DPRINTF(Fetch, "[tid:%u]: Squashing instructions due to squash " "from commit.\n",tid); -#if THE_ISA == ALPHA_ISA - InstSeqNum doneSeqNum = fromCommit->commitInfo[tid].doneSeqNum; +#if ISA_HAS_DELAY_SLOT + InstSeqNum doneSeqNum = fromCommit->commitInfo[tid].bdelayDoneSeqNum; #else - InstSeqNum doneSeqNum = fromCommit->commitInfo[tid].bdelayDoneSeqNum; + InstSeqNum doneSeqNum = fromCommit->commitInfo[tid].doneSeqNum; #endif // In any case, squash. squash(fromCommit->commitInfo[tid].nextPC, @@ -958,10 +958,10 @@ DefaultFetch<Impl>::checkSignalsAndUpdate(unsigned tid) if (fetchStatus[tid] != Squashing) { -#if THE_ISA == ALPHA_ISA - InstSeqNum doneSeqNum = fromDecode->decodeInfo[tid].doneSeqNum; -#else +#if ISA_HAS_DELAY_SLOT InstSeqNum doneSeqNum = fromDecode->decodeInfo[tid].bdelayDoneSeqNum; +#else + InstSeqNum doneSeqNum = fromDecode->decodeInfo[tid].doneSeqNum; #endif // Squash unless we're already squashing squashFromDecode(fromDecode->decodeInfo[tid].nextPC, @@ -1162,7 +1162,7 @@ DefaultFetch<Impl>::fetch(bool &status_change) offset += instSize; -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT if (predicted_branch) { delaySlotInfo[tid].branchSeqNum = inst_seq; @@ -1205,11 +1205,7 @@ DefaultFetch<Impl>::fetch(bool &status_change) // Now that fetching is completed, update the PC to signify what the next // cycle will be. if (fault == NoFault) { -#if THE_ISA == ALPHA_ISA - DPRINTF(Fetch, "[tid:%i]: Setting PC to %08p.\n",tid, next_PC); - PC[tid] = next_PC; - nextPC[tid] = next_PC + instSize; -#else +#if ISA_HAS_DELAY_SLOT if (delaySlotInfo[tid].targetReady && delaySlotInfo[tid].numInsts == 0) { // Set PC to target @@ -1225,6 +1221,10 @@ DefaultFetch<Impl>::fetch(bool &status_change) } DPRINTF(Fetch, "[tid:%i]: Setting PC to %08p.\n", tid, PC[tid]); +#else + DPRINTF(Fetch, "[tid:%i]: Setting PC to %08p.\n",tid, next_PC); + PC[tid] = next_PC; + nextPC[tid] = next_PC + instSize; #endif } else { // We shouldn't be in an icache miss and also have a fault (an ITB diff --git a/src/cpu/o3/iew_impl.hh b/src/cpu/o3/iew_impl.hh index cdc36c6c3..e9b24a6d4 100644 --- a/src/cpu/o3/iew_impl.hh +++ b/src/cpu/o3/iew_impl.hh @@ -427,10 +427,10 @@ DefaultIEW<Impl>::squash(unsigned tid) instQueue.squash(tid); // Tell the LDSTQ to start squashing. -#if THE_ISA == ALPHA_ISA - ldstQueue.squash(fromCommit->commitInfo[tid].doneSeqNum, tid); -#else +#if ISA_HAS_DELAY_SLOT ldstQueue.squash(fromCommit->commitInfo[tid].bdelayDoneSeqNum, tid); +#else + ldstQueue.squash(fromCommit->commitInfo[tid].doneSeqNum, tid); #endif updatedQueues = true; @@ -439,7 +439,7 @@ DefaultIEW<Impl>::squash(unsigned tid) tid, fromCommit->commitInfo[tid].bdelayDoneSeqNum); while (!skidBuffer[tid].empty()) { -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT if (skidBuffer[tid].front()->seqNum <= fromCommit->commitInfo[tid].bdelayDoneSeqNum) { DPRINTF(IEW, "[tid:%i]: Cannot remove skidbuffer instructions " @@ -479,11 +479,7 @@ DefaultIEW<Impl>::squashDueToBranch(DynInstPtr &inst, unsigned tid) toCommit->mispredPC[tid] = inst->readPC(); toCommit->branchMispredict[tid] = true; -#if THE_ISA == ALPHA_ISA - toCommit->branchTaken[tid] = inst->readNextPC() != - (inst->readPC() + sizeof(TheISA::MachInst)); - toCommit->nextPC[tid] = inst->readNextPC(); -#else +#if ISA_HAS_DELAY_SLOT bool branch_taken = inst->readNextNPC() != (inst->readNextPC() + sizeof(TheISA::MachInst)); @@ -496,6 +492,10 @@ DefaultIEW<Impl>::squashDueToBranch(DynInstPtr &inst, unsigned tid) } else { toCommit->nextPC[tid] = inst->readNextNPC(); } +#else + toCommit->branchTaken[tid] = inst->readNextPC() != + (inst->readPC() + sizeof(TheISA::MachInst)); + toCommit->nextPC[tid] = inst->readNextPC(); #endif toCommit->includeSquashInst[tid] = false; @@ -860,7 +860,7 @@ DefaultIEW<Impl>::sortInsts() { int insts_from_rename = fromRename->size; #ifdef DEBUG -#if THE_ISA == ALPHA_ISA +#if !ISA_HAS_DELAY_SLOT for (int i = 0; i < numThreads; i++) assert(insts[i].empty()); #endif @@ -878,8 +878,7 @@ DefaultIEW<Impl>::emptyRenameInsts(unsigned tid) "[sn:%i].\n", tid, bdelayDoneSeqNum[tid]); while (!insts[tid].empty()) { - -#if THE_ISA != ALPHA_ISA +#if ISA_HAS_DELAY_SLOT if (insts[tid].front()->seqNum <= bdelayDoneSeqNum[tid]) { DPRINTF(IEW, "[tid:%i]: Done removing, cannot remove instruction" " that occurs at or before delay slot [sn:%i].\n", @@ -1316,12 +1315,12 @@ DefaultIEW<Impl>::executeInsts() fetchRedirect[tid] = true; DPRINTF(IEW, "Execute: Branch mispredict detected.\n"); -#if THE_ISA == ALPHA_ISA +#if ISA_HAS_DELAY_SLOT DPRINTF(IEW, "Execute: Redirecting fetch to PC: %#x.\n", - inst->nextPC); + inst->nextNPC); #else DPRINTF(IEW, "Execute: Redirecting fetch to PC: %#x.\n", - inst->nextNPC); + inst->nextPC); #endif // If incorrect, then signal the ROB that it must be squashed. squashDueToBranch(inst, tid); diff --git a/src/cpu/o3/inst_queue_impl.hh b/src/cpu/o3/inst_queue_impl.hh index e7991662b..47634f645 100644 --- a/src/cpu/o3/inst_queue_impl.hh +++ b/src/cpu/o3/inst_queue_impl.hh @@ -991,10 +991,10 @@ InstructionQueue<Impl>::squash(unsigned tid) // Read instruction sequence number of last instruction out of the // time buffer. -#if THE_ISA == ALPHA_ISA - squashedSeqNum[tid] = fromCommit->commitInfo[tid].doneSeqNum; -#else +#if ISA_HAS_DELAY_SLOT squashedSeqNum[tid] = fromCommit->commitInfo[tid].bdelayDoneSeqNum; +#else + squashedSeqNum[tid] = fromCommit->commitInfo[tid].doneSeqNum; #endif // Call doSquash if there are insts in the IQ diff --git a/src/cpu/o3/rename_impl.hh b/src/cpu/o3/rename_impl.hh index 892eb12cf..782c0fe5f 100644 --- a/src/cpu/o3/rename_impl.hh +++ b/src/cpu/o3/rename_impl.hh @@ -355,9 +355,7 @@ DefaultRename<Impl>::squash(const InstSeqNum &squash_seq_num, unsigned tid) // "insts[tid].clear();" or "skidBuffer[tid].clear()" since there is // a possible delay slot inst for different architectures // insts[tid].clear(); -#if THE_ISA == ALPHA_ISA - insts[tid].clear(); -#else +#if ISA_HAS_DELAY_SLOT DPRINTF(Rename, "[tid:%i] Squashing incoming decode instructions until " "[sn:%i].\n",tid, squash_seq_num); ListIt ilist_it = insts[tid].begin(); @@ -369,14 +367,14 @@ DefaultRename<Impl>::squash(const InstSeqNum &squash_seq_num, unsigned tid) } ilist_it++; } +#else + insts[tid].clear(); #endif // Clear the skid buffer in case it has any data in it. // See comments above. // skidBuffer[tid].clear(); -#if THE_ISA == ALPHA_ISA - skidBuffer[tid].clear(); -#else +#if ISA_HAS_DELAY_SLOT DPRINTF(Rename, "[tid:%i] Squashing incoming skidbuffer instructions " "until [sn:%i].\n", tid, squash_seq_num); ListIt slist_it = skidBuffer[tid].begin(); @@ -388,6 +386,8 @@ DefaultRename<Impl>::squash(const InstSeqNum &squash_seq_num, unsigned tid) } slist_it++; } +#else + skidBuffer[tid].clear(); #endif doSquash(squash_seq_num, tid); } @@ -743,7 +743,7 @@ DefaultRename<Impl>::sortInsts() { int insts_from_decode = fromDecode->size; #ifdef DEBUG -#if THE_ISA == ALPHA_ISA +#if !ISA_HAS_DELAY_SLOT for (int i=0; i < numThreads; i++) assert(insts[i].empty()); #endif @@ -1182,10 +1182,10 @@ DefaultRename<Impl>::checkSignalsAndUpdate(unsigned tid) DPRINTF(Rename, "[tid:%u]: Squashing instructions due to squash from " "commit.\n", tid); -#if THE_ISA == ALPHA_ISA - InstSeqNum squashed_seq_num = fromCommit->commitInfo[tid].doneSeqNum; -#else +#if ISA_HAS_DELAY_SLOT InstSeqNum squashed_seq_num = fromCommit->commitInfo[tid].bdelayDoneSeqNum; +#else + InstSeqNum squashed_seq_num = fromCommit->commitInfo[tid].doneSeqNum; #endif squash(squashed_seq_num, tid); diff --git a/src/cpu/simple/base.cc b/src/cpu/simple/base.cc index b7cfddcb8..f801b93fa 100644 --- a/src/cpu/simple/base.cc +++ b/src/cpu/simple/base.cc @@ -358,12 +358,12 @@ Fault BaseSimpleCPU::setupFetchRequest(Request *req) { // set up memory request for instruction fetch -#if THE_ISA == ALPHA_ISA - DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p",thread->readPC(), - thread->readNextPC()); -#else +#if ISA_HAS_DELAY_SLOT DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",thread->readPC(), thread->readNextPC(),thread->readNextNPC()); +#else + DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p",thread->readPC(), + thread->readNextPC()); #endif req->setVirt(0, thread->readPC() & ~3, sizeof(MachInst), @@ -450,12 +450,12 @@ BaseSimpleCPU::advancePC(Fault fault) else { // go to the next instruction thread->setPC(thread->readNextPC()); -#if THE_ISA == ALPHA_ISA - thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); -#else +#if ISA_HAS_DELAY_SLOT thread->setNextPC(thread->readNextNPC()); thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst)); assert(thread->readNextPC() != thread->readNextNPC()); +#else + thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); #endif } diff --git a/src/dev/ide_disk.hh b/src/dev/ide_disk.hh index fb0614d4d..0bc0b73ab 100644 --- a/src/dev/ide_disk.hh +++ b/src/dev/ide_disk.hh @@ -43,6 +43,8 @@ #include "dev/io_device.hh" #include "sim/eventq.hh" +class ChunkGenerator; + #define DMA_BACKOFF_PERIOD 200 #define MAX_DMA_SIZE (131072) // 128K diff --git a/src/dev/io_device.cc b/src/dev/io_device.cc index b51a93190..408d8de3e 100644 --- a/src/dev/io_device.cc +++ b/src/dev/io_device.cc @@ -29,6 +29,7 @@ * Nathan Binkert */ +#include "base/chunk_generator.hh" #include "base/trace.hh" #include "dev/io_device.hh" #include "sim/builder.hh" @@ -36,20 +37,14 @@ PioPort::PioPort(PioDevice *dev, System *s, std::string pname) - : SimpleTimingPort(dev->name() + pname), device(dev), sys(s) + : SimpleTimingPort(dev->name() + pname), device(dev) { } Tick PioPort::recvAtomic(Packet *pkt) { - return device->recvAtomic(pkt); -} - -void -PioPort::recvFunctional(Packet *pkt) -{ - device->recvAtomic(pkt); + return pkt->isRead() ? device->read(pkt) : device->write(pkt); } void @@ -60,20 +55,6 @@ PioPort::getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) } -bool -PioPort::recvTiming(Packet *pkt) -{ - if (pkt->result == Packet::Nacked) { - resendNacked(pkt); - } else { - Tick latency = device->recvAtomic(pkt); - // turn packet around to go back to requester - pkt->makeTimingResponse(); - sendTiming(pkt, latency); - } - return true; -} - PioDevice::~PioDevice() { if (pioPort) diff --git a/src/dev/io_device.hh b/src/dev/io_device.hh index 710b22b2c..df4f494cb 100644 --- a/src/dev/io_device.hh +++ b/src/dev/io_device.hh @@ -32,13 +32,12 @@ #ifndef __DEV_IO_DEVICE_HH__ #define __DEV_IO_DEVICE_HH__ -#include "base/chunk_generator.hh" #include "mem/mem_object.hh" #include "mem/packet_impl.hh" -#include "sim/eventq.hh" #include "sim/sim_object.hh" #include "mem/tport.hh" +class Event; class Platform; class PioDevice; class DmaDevice; @@ -49,35 +48,22 @@ class System; * sensitive to an address range use. The port takes all the memory * access types and roles them into one read() and write() call that the device * must respond to. The device must also provide the addressRanges() function - * with which it returns the address ranges it is interested in. */ - + * with which it returns the address ranges it is interested in. + */ class PioPort : public SimpleTimingPort { protected: /** The device that this port serves. */ PioDevice *device; - /** The system that device/port are in. This is used to select which mode - * we are currently operating in. */ - System *sys; - - /** The current status of the peer(bus) that we are connected to. */ - Status peerStatus; - - virtual bool recvTiming(Packet *pkt); - virtual Tick recvAtomic(Packet *pkt); - virtual void recvFunctional(Packet *pkt) ; - - virtual void recvStatusChange(Status status) - { peerStatus = status; } - - virtual void getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop); + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop); public: - PioPort(PioDevice *dev, System *s, std::string pname = "-pioport"); + PioPort(PioDevice *dev, System *s, std::string pname = "-pioport"); }; @@ -132,7 +118,8 @@ class DmaPort : public Port virtual void recvRetry() ; - virtual void getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) { resp.clear(); snoop.clear(); } void sendDma(Packet *pkt, bool front = false); @@ -155,7 +142,6 @@ class DmaPort : public Port * mode we are in, etc is handled by the PioPort so the device doesn't have to * bother. */ - class PioDevice : public MemObject { protected: @@ -172,13 +158,8 @@ class PioDevice : public MemObject virtual void addressRanges(AddrRangeList &range_list) = 0; - /** As far as the devices are concerned they only accept atomic transactions - * which are converted to either a write or a read. */ - Tick recvAtomic(Packet *pkt) - { return pkt->isRead() ? this->read(pkt) : this->write(pkt); } - - /** Pure virtual function that the device must implement. Called when a read - * command is recieved by the port. + /** Pure virtual function that the device must implement. Called + * when a read command is recieved by the port. * @param pkt Packet describing this request * @return number of ticks it took to complete */ @@ -192,10 +173,9 @@ class PioDevice : public MemObject virtual Tick write(Packet *pkt) = 0; public: - /** Params struct which is extended through each device based on the - * parameters it needs. Since we are re-writing everything, we might as well - * start from the bottom this time. */ - + /** Params struct which is extended through each device based on + * the parameters it needs. Since we are re-writing everything, we + * might as well start from the bottom this time. */ struct Params { std::string name; @@ -255,7 +235,8 @@ class BasicPioDevice : public PioDevice public: BasicPioDevice(Params *p) - : PioDevice(p), pioAddr(p->pio_addr), pioSize(0), pioDelay(p->pio_delay) + : PioDevice(p), pioAddr(p->pio_addr), pioSize(0), + pioDelay(p->pio_delay) {} /** return the address ranges that this device responds to. diff --git a/src/dev/pcidev.cc b/src/dev/pcidev.cc index e81e0d1ee..8ea22cb24 100644 --- a/src/dev/pcidev.cc +++ b/src/dev/pcidev.cc @@ -39,6 +39,7 @@ #include <vector> #include "base/inifile.hh" +#include "base/intmath.hh" // for isPowerOf2( #include "base/misc.hh" #include "base/str.hh" // for to_number #include "base/trace.hh" @@ -56,8 +57,8 @@ using namespace std; PciDev::PciConfigPort::PciConfigPort(PciDev *dev, int busid, int devid, int funcid, Platform *p) - : PioPort(dev,p->system,"-pciconf"), device(dev), platform(p), - busId(busid), deviceId(devid), functionId(funcid) + : SimpleTimingPort(dev->name() + "-pciconf"), device(dev), platform(p), + busId(busid), deviceId(devid), functionId(funcid) { configAddr = platform->calcConfigAddr(busId, deviceId, functionId); } @@ -67,45 +68,20 @@ Tick PciDev::PciConfigPort::recvAtomic(Packet *pkt) { assert(pkt->result == Packet::Unknown); - assert(pkt->getAddr() >= configAddr && pkt->getAddr() < configAddr + - PCI_CONFIG_SIZE); - return device->recvConfig(pkt); + assert(pkt->getAddr() >= configAddr && + pkt->getAddr() < configAddr + PCI_CONFIG_SIZE); + return pkt->isRead() ? device->readConfig(pkt) : device->writeConfig(pkt); } void -PciDev::PciConfigPort::recvFunctional(Packet *pkt) -{ - assert(pkt->result == Packet::Unknown); - assert(pkt->getAddr() >= configAddr && pkt->getAddr() < configAddr + - PCI_CONFIG_SIZE); - device->recvConfig(pkt); -} - -void -PciDev::PciConfigPort::getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) +PciDev::PciConfigPort::getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) { snoop.clear(); resp.push_back(RangeSize(configAddr, PCI_CONFIG_SIZE+1)); } -bool -PciDev::PciConfigPort::recvTiming(Packet *pkt) -{ - if (pkt->result == Packet::Nacked) { - resendNacked(pkt); - } else { - assert(pkt->result == Packet::Unknown); - assert(pkt->getAddr() >= configAddr && pkt->getAddr() < configAddr + - PCI_CONFIG_SIZE); - Tick latency = device->recvConfig(pkt); - // turn packet around to go back to requester - pkt->makeTimingResponse(); - sendTiming(pkt, latency); - } - return true; -} - PciDev::PciDev(Params *p) : DmaDevice(p), plat(p->platform), configData(p->configData), pioDelay(p->pio_delay), configDelay(p->config_delay), @@ -115,10 +91,11 @@ PciDev::PciDev(Params *p) if (configData) { memcpy(config.data, configData->config.data, sizeof(config.data)); memcpy(BARSize, configData->BARSize, sizeof(BARSize)); - memcpy(BARAddrs, configData->BARAddrs, sizeof(BARAddrs)); } else panic("NULL pointer to configuration data"); + memset(BARAddrs, 0, sizeof(BARAddrs)); + plat->registerPciDevice(0, p->deviceNum, p->functionNum, letoh(configData->config.interruptLine)); } @@ -157,21 +134,21 @@ PciDev::readConfig(Packet *pkt) case sizeof(uint8_t): pkt->set<uint8_t>(config.data[offset]); DPRINTF(PCIDEV, - "read device: %#x function: %#x register: %#x 1 bytes: data: %#x\n", + "readConfig: dev %#x func %#x reg %#x 1 bytes: data = %#x\n", params()->deviceNum, params()->functionNum, offset, (uint32_t)pkt->get<uint8_t>()); break; case sizeof(uint16_t): pkt->set<uint16_t>(*(uint16_t*)&config.data[offset]); DPRINTF(PCIDEV, - "read device: %#x function: %#x register: %#x 2 bytes: data: %#x\n", + "readConfig: dev %#x func %#x reg %#x 2 bytes: data = %#x\n", params()->deviceNum, params()->functionNum, offset, (uint32_t)pkt->get<uint16_t>()); break; case sizeof(uint32_t): pkt->set<uint32_t>(*(uint32_t*)&config.data[offset]); DPRINTF(PCIDEV, - "read device: %#x function: %#x register: %#x 4 bytes: data: %#x\n", + "readConfig: dev %#x func %#x reg %#x 4 bytes: data = %#x\n", params()->deviceNum, params()->functionNum, offset, (uint32_t)pkt->get<uint32_t>()); break; @@ -221,7 +198,7 @@ PciDev::writeConfig(Packet *pkt) panic("writing to a read only register"); } DPRINTF(PCIDEV, - "write device: %#x function: %#x register: %#x 1 bytes: data: %#x\n", + "writeConfig: dev %#x func %#x reg %#x 1 bytes: data = %#x\n", params()->deviceNum, params()->functionNum, offset, (uint32_t)pkt->get<uint8_t>()); break; @@ -238,7 +215,7 @@ PciDev::writeConfig(Packet *pkt) panic("writing to a read only register"); } DPRINTF(PCIDEV, - "write device: %#x function: %#x register: %#x 2 bytes: data: %#x\n", + "writeConfig: dev %#x func %#x reg %#x 2 bytes: data = %#x\n", params()->deviceNum, params()->functionNum, offset, (uint32_t)pkt->get<uint16_t>()); break; @@ -250,40 +227,33 @@ PciDev::writeConfig(Packet *pkt) case PCI0_BASE_ADDR3: case PCI0_BASE_ADDR4: case PCI0_BASE_ADDR5: - - uint32_t barnum, bar_mask; - Addr base_addr, base_size, space_base; - - barnum = BAR_NUMBER(offset); - - if (BAR_IO_SPACE(letoh(config.baseAddr[barnum]))) { - bar_mask = BAR_IO_MASK; - space_base = TSUNAMI_PCI0_IO; - } else { - bar_mask = BAR_MEM_MASK; - space_base = TSUNAMI_PCI0_MEMORY; - } - - // Writing 0xffffffff to a BAR tells the card to set the - // value of the bar to size of memory it needs - if (letoh(pkt->get<uint32_t>()) == 0xffffffff) { - // This is I/O Space, bottom two bits are read only - - config.baseAddr[barnum] = letoh( - (~(BARSize[barnum] - 1) & ~bar_mask) | - (letoh(config.baseAddr[barnum]) & bar_mask)); - } else { - config.baseAddr[barnum] = letoh( - (letoh(pkt->get<uint32_t>()) & ~bar_mask) | - (letoh(config.baseAddr[barnum]) & bar_mask)); - - if (letoh(config.baseAddr[barnum]) & ~bar_mask) { - base_addr = (letoh(pkt->get<uint32_t>()) & ~bar_mask) + space_base; - base_size = BARSize[barnum]; - BARAddrs[barnum] = base_addr; - - pioPort->sendStatusChange(Port::RangeChange); + { + int barnum = BAR_NUMBER(offset); + + // convert BAR values to host endianness + uint32_t he_old_bar = letoh(config.baseAddr[barnum]); + uint32_t he_new_bar = letoh(pkt->get<uint32_t>()); + + uint32_t bar_mask = + BAR_IO_SPACE(he_old_bar) ? BAR_IO_MASK : BAR_MEM_MASK; + + // Writing 0xffffffff to a BAR tells the card to set the + // value of the bar to a bitmask indicating the size of + // memory it needs + if (he_new_bar == 0xffffffff) { + he_new_bar = ~(BARSize[barnum] - 1); + } else { + // does it mean something special to write 0 to a BAR? + he_new_bar &= ~bar_mask; + if (he_new_bar) { + Addr space_base = BAR_IO_SPACE(he_old_bar) ? + TSUNAMI_PCI0_IO : TSUNAMI_PCI0_MEMORY; + BARAddrs[barnum] = he_new_bar + space_base; + pioPort->sendStatusChange(Port::RangeChange); + } } + config.baseAddr[barnum] = htole((he_new_bar & ~bar_mask) | + (he_old_bar & bar_mask)); } break; @@ -305,7 +275,7 @@ PciDev::writeConfig(Packet *pkt) DPRINTF(PCIDEV, "Writing to a read only register"); } DPRINTF(PCIDEV, - "write device: %#x function: %#x register: %#x 4 bytes: data: %#x\n", + "writeConfig: dev %#x func %#x reg %#x 4 bytes: data = %#x\n", params()->deviceNum, params()->functionNum, offset, (uint32_t)pkt->get<uint32_t>()); break; @@ -427,12 +397,12 @@ CREATE_SIM_OBJECT(PciConfigData) data->config.headerType = htole(HeaderType); data->config.bist = htole(BIST); - data->config.baseAddr0 = htole(BAR0); - data->config.baseAddr1 = htole(BAR1); - data->config.baseAddr2 = htole(BAR2); - data->config.baseAddr3 = htole(BAR3); - data->config.baseAddr4 = htole(BAR4); - data->config.baseAddr5 = htole(BAR5); + data->config.baseAddr[0] = htole(BAR0); + data->config.baseAddr[1] = htole(BAR1); + data->config.baseAddr[2] = htole(BAR2); + data->config.baseAddr[3] = htole(BAR3); + data->config.baseAddr[4] = htole(BAR4); + data->config.baseAddr[5] = htole(BAR5); data->config.cardbusCIS = htole(CardbusCIS); data->config.subsystemVendorID = htole(SubsystemVendorID); data->config.subsystemID = htole(SubsystemVendorID); @@ -449,6 +419,14 @@ CREATE_SIM_OBJECT(PciConfigData) data->BARSize[4] = BAR4Size; data->BARSize[5] = BAR5Size; + for (int i = 0; i < 6; ++i) { + uint32_t barsize = data->BARSize[i]; + if (barsize != 0 && !isPowerOf2(barsize)) { + fatal("%s: BAR %d size %d is not a power of 2\n", + getInstanceName(), i, data->BARSize[i]); + } + } + return data; } diff --git a/src/dev/pcidev.hh b/src/dev/pcidev.hh index 847fb07d0..22dd6296e 100644 --- a/src/dev/pcidev.hh +++ b/src/dev/pcidev.hh @@ -62,7 +62,6 @@ class PciConfigData : public SimObject : SimObject(name) { memset(config.data, 0, sizeof(config.data)); - memset(BARAddrs, 0, sizeof(BARAddrs)); memset(BARSize, 0, sizeof(BARSize)); } @@ -71,29 +70,23 @@ class PciConfigData : public SimObject /** The size of the BARs */ uint32_t BARSize[6]; - - /** The addresses of the BARs */ - Addr BARAddrs[6]; }; /** - * PCI device, base implemnation is only config space. + * PCI device, base implementation is only config space. */ class PciDev : public DmaDevice { - class PciConfigPort : public PioPort + class PciConfigPort : public SimpleTimingPort { protected: PciDev *device; - virtual bool recvTiming(Packet *pkt); - virtual Tick recvAtomic(Packet *pkt); - virtual void recvFunctional(Packet *pkt) ; - - virtual void getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop); + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop); Platform *platform; @@ -105,9 +98,7 @@ class PciDev : public DmaDevice public: PciConfigPort(PciDev *dev, int busid, int devid, int funcid, - Platform *p); - - friend class PioPort::SendEvent; + Platform *p); }; public: @@ -151,6 +142,10 @@ class PciDev : public DmaDevice /** The current address mapping of the BARs */ Addr BARAddrs[6]; + /** + * Does the given address lie within the space mapped by the given + * base address register? + */ bool isBAR(Addr addr, int bar) const { @@ -158,6 +153,10 @@ class PciDev : public DmaDevice return BARAddrs[bar] <= addr && addr < BARAddrs[bar] + BARSize[bar]; } + /** + * Which base address register (if any) maps the given address? + * @return The BAR number (0-5 inclusive), or -1 if none. + */ int getBAR(Addr addr) { @@ -168,14 +167,23 @@ class PciDev : public DmaDevice return -1; } + /** + * Which base address register (if any) maps the given address? + * @param addr The address to check. + * @retval bar The BAR number (0-5 inclusive), + * only valid if return value is true. + * @retval offs The offset from the base address, + * only valid if return value is true. + * @return True iff address maps to a base address register's region. + */ bool - getBAR(Addr paddr, Addr &daddr, int &bar) + getBAR(Addr addr, int &bar, Addr &offs) { - int b = getBAR(paddr); + int b = getBAR(addr); if (b < 0) return false; - daddr = paddr - BARAddrs[b]; + offs = addr - BARAddrs[b]; bar = b; return true; } @@ -225,10 +233,6 @@ class PciDev : public DmaDevice */ void addressRanges(AddrRangeList &range_list); - /** Do a PCI Configspace memory access. */ - Tick recvConfig(Packet *pkt) - { return pkt->isRead() ? readConfig(pkt) : writeConfig(pkt); } - /** * Constructor for PCI Dev. This function copies data from the * config file object PCIConfigData and registers the device with diff --git a/src/dev/pcireg.h b/src/dev/pcireg.h index a48abd4fa..df57acdb0 100644 --- a/src/dev/pcireg.h +++ b/src/dev/pcireg.h @@ -54,18 +54,7 @@ union PCIConfig { uint8_t latencyTimer; uint8_t headerType; uint8_t bist; - union { - uint32_t baseAddr[6]; - - struct { - uint32_t baseAddr0; - uint32_t baseAddr1; - uint32_t baseAddr2; - uint32_t baseAddr3; - uint32_t baseAddr4; - uint32_t baseAddr5; - }; - }; + uint32_t baseAddr[6]; uint32_t cardbusCIS; uint16_t subsystemVendorID; uint16_t subsystemID; diff --git a/src/mem/bus.cc b/src/mem/bus.cc index b945f93b3..cf9e54e62 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -79,7 +79,17 @@ Bus::recvTiming(Packet *pkt) short dest = pkt->getDest(); if (dest == Packet::Broadcast) { - port = findPort(pkt->getAddr(), pkt->getSrc()); + if ( timingSnoopPhase1(pkt) ) + { + timingSnoopPhase2(pkt); + port = findPort(pkt->getAddr(), pkt->getSrc()); + } + else + { + //Snoop didn't succeed + retryList.push_back(interfaces[pkt->getSrc()]); + return false; + } } else { assert(dest >= 0 && dest < interfaces.size()); assert(dest != pkt->getSrc()); // catch infinite loops @@ -128,7 +138,7 @@ Bus::findPort(Addr addr, int id) if (portList[i].range == addr) { dest_id = portList[i].portId; found = true; - DPRINTF(Bus, " found addr 0x%llx on device %d\n", addr, dest_id); + DPRINTF(Bus, " found addr %#llx on device %d\n", addr, dest_id); } i++; } @@ -137,11 +147,11 @@ Bus::findPort(Addr addr, int id) if (dest_id == -1) { for (iter = defaultRange.begin(); iter != defaultRange.end(); iter++) { if (*iter == addr) { - DPRINTF(Bus, " found addr 0x%llx on default\n", addr); + DPRINTF(Bus, " found addr %#llx on default\n", addr); return defaultPort; } } - panic("Unable to find destination for addr: %llx", addr); + panic("Unable to find destination for addr: %#llx", addr); } @@ -151,6 +161,77 @@ Bus::findPort(Addr addr, int id) return interfaces[dest_id]; } +std::vector<int> +Bus::findSnoopPorts(Addr addr, int id) +{ + int i = 0; + AddrRangeIter iter; + std::vector<int> ports; + + while (i < portSnoopList.size()) + { + if (portSnoopList[i].range == addr && portSnoopList[i].portId != id) { + //Careful to not overlap ranges + //or snoop will be called more than once on the port + ports.push_back(portSnoopList[i].portId); + DPRINTF(Bus, " found snoop addr %#llx on device%d\n", addr, + portSnoopList[i].portId); + } + i++; + } + return ports; +} + +void +Bus::atomicSnoop(Packet *pkt) +{ + std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc()); + + while (!ports.empty()) + { + interfaces[ports.back()]->sendAtomic(pkt); + ports.pop_back(); + } +} + +bool +Bus::timingSnoopPhase1(Packet *pkt) +{ + std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc()); + bool success = true; + + while (!ports.empty() && success) + { + snoopCallbacks.push_back(ports.back()); + success = interfaces[ports.back()]->sendTiming(pkt); + ports.pop_back(); + } + if (!success) + { + while (!snoopCallbacks.empty()) + { + interfaces[snoopCallbacks.back()]->sendStatusChange(Port::SnoopSquash); + snoopCallbacks.pop_back(); + } + return false; + } + return true; +} + +void +Bus::timingSnoopPhase2(Packet *pkt) +{ + bool success; + pkt->flags |= SNOOP_COMMIT; + while (!snoopCallbacks.empty()) + { + success = interfaces[snoopCallbacks.back()]->sendTiming(pkt); + //We should not fail on snoop callbacks + assert(success); + snoopCallbacks.pop_back(); + } +} + /** Function called by the port when the bus is receiving a Atomic * transaction.*/ Tick @@ -159,6 +240,7 @@ Bus::recvAtomic(Packet *pkt) DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n", pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); assert(pkt->getDest() == Packet::Broadcast); + atomicSnoop(pkt); return findPort(pkt->getAddr(), pkt->getSrc())->sendAtomic(pkt); } @@ -193,7 +275,7 @@ Bus::recvStatusChange(Port::Status status, int id) assert(snoops.size() == 0); for(iter = ranges.begin(); iter != ranges.end(); iter++) { defaultRange.push_back(*iter); - DPRINTF(BusAddrRanges, "Adding range %llx - %llx for default\n", + DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", iter->start, iter->end); } } else { @@ -201,6 +283,7 @@ Bus::recvStatusChange(Port::Status status, int id) assert((id < interfaces.size() && id >= 0) || id == -1); Port *port = interfaces[id]; std::vector<DevMap>::iterator portIter; + std::vector<DevMap>::iterator snoopIter; // Clean out any previously existent ids for (portIter = portList.begin(); portIter != portList.end(); ) { @@ -210,16 +293,31 @@ Bus::recvStatusChange(Port::Status status, int id) portIter++; } + for (snoopIter = portSnoopList.begin(); snoopIter != portSnoopList.end(); ) { + if (snoopIter->portId == id) + snoopIter = portSnoopList.erase(snoopIter); + else + snoopIter++; + } + port->getPeerAddressRanges(ranges, snoops); - // not dealing with snooping yet either - assert(snoops.size() == 0); + for(iter = snoops.begin(); iter != snoops.end(); iter++) { + DevMap dm; + dm.portId = id; + dm.range = *iter; + + DPRINTF(BusAddrRanges, "Adding snoop range %#llx - %#llx for id %d\n", + dm.range.start, dm.range.end, id); + portSnoopList.push_back(dm); + } + for(iter = ranges.begin(); iter != ranges.end(); iter++) { DevMap dm; dm.portId = id; dm.range = *iter; - DPRINTF(BusAddrRanges, "Adding range %llx - %llx for id %d\n", + DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", dm.range.start, dm.range.end, id); portList.push_back(dm); } @@ -251,7 +349,7 @@ Bus::addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id) for (dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end(); dflt_iter++) { resp.push_back(*dflt_iter); - DPRINTF(BusAddrRanges, " -- %#llX : %#llX\n",dflt_iter->start, + DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n",dflt_iter->start, dflt_iter->end); } for (portIter = portList.begin(); portIter != portList.end(); portIter++) { @@ -267,13 +365,13 @@ Bus::addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id) if (portIter->range.start >= dflt_iter->start && portIter->range.end <= dflt_iter->end) { subset = true; - DPRINTF(BusAddrRanges, " -- %#llX : %#llX is a SUBSET\n", + DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n", portIter->range.start, portIter->range.end); } } if (portIter->portId != id && !subset) { resp.push_back(portIter->range); - DPRINTF(BusAddrRanges, " -- %#llX : %#llX\n", + DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", portIter->range.start, portIter->range.end); } } diff --git a/src/mem/bus.hh b/src/mem/bus.hh index cd25fab2c..941389296 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -60,6 +60,9 @@ class Bus : public MemObject }; std::vector<DevMap> portList; AddrRangeList defaultRange; + std::vector<DevMap> portSnoopList; + + std::vector<int> snoopCallbacks; /** Function called by the port when the bus is recieving a Timing @@ -90,6 +93,29 @@ class Bus : public MemObject */ Port *findPort(Addr addr, int id); + /** Find all ports with a matching snoop range, except src port. Keep in mind + * that the ranges shouldn't overlap or you will get a double snoop to the same + * interface.and the cache will assert out. + * @param addr Address to find snoop prts for. + * @param id Id of the src port of the request to avoid calling snoop on src + * @return vector of IDs to snoop on + */ + std::vector<int> findSnoopPorts(Addr addr, int id); + + /** Snoop all relevant ports atomicly. */ + void atomicSnoop(Packet *pkt); + + /** Snoop for NACK and Blocked in phase 1 + * @return True if succeds. + */ + bool timingSnoopPhase1(Packet *pkt); + + /** @todo Don't need to commit all snoops just those that need it + *(register somehow). */ + /** Commit all snoops now that we know if any of them would have blocked. + */ + void timingSnoopPhase2(Packet *pkt); + /** Process address range request. * @param resp addresses that we can respond to * @param snoop addresses that we would like to snoop diff --git a/src/mem/cache/cache_impl.hh b/src/mem/cache/cache_impl.hh index 2f9415852..11cd84e88 100644 --- a/src/mem/cache/cache_impl.hh +++ b/src/mem/cache/cache_impl.hh @@ -60,6 +60,9 @@ doTimingAccess(Packet *pkt, CachePort *cachePort, bool isCpuSide) { if (isCpuSide) { + if (pkt->isWrite() && (pkt->req->getFlags() & LOCKED)) { + pkt->req->setScResult(1); + } access(pkt); } else @@ -79,6 +82,11 @@ doAtomicAccess(Packet *pkt, bool isCpuSide) { if (isCpuSide) { + //Temporary solution to LL/SC + if (pkt->isWrite() && (pkt->req->getFlags() & LOCKED)) { + pkt->req->setScResult(1); + } + probe(pkt, true); //TEMP ALWAYS SUCCES FOR NOW pkt->result = Packet::Success; @@ -103,6 +111,12 @@ doFunctionalAccess(Packet *pkt, bool isCpuSide) { //TEMP USE CPU?THREAD 0 0 pkt->req->setThreadContext(0,0); + + //Temporary solution to LL/SC + if (pkt->isWrite() && (pkt->req->getFlags() & LOCKED)) { + assert("Can't handle LL/SC on functional path\n"); + } + probe(pkt, true); //TEMP ALWAYS SUCCESFUL FOR NOW pkt->result = Packet::Success; diff --git a/src/mem/packet.hh b/src/mem/packet.hh index 5d8308df7..c7d28010c 100644 --- a/src/mem/packet.hh +++ b/src/mem/packet.hh @@ -56,6 +56,7 @@ typedef std::list<PacketPtr> PacketList; #define CACHE_LINE_FILL 1 << 3 #define COMPRESSED 1 << 4 #define NO_ALLOCATE 1 << 5 +#define SNOOP_COMMIT 1 << 6 //For statistics we need max number of commands, hard code it at //20 for now. @todo fix later diff --git a/src/mem/physical.cc b/src/mem/physical.cc index f4fbd2fb1..8fea733ec 100644 --- a/src/mem/physical.cc +++ b/src/mem/physical.cc @@ -182,7 +182,8 @@ PhysicalMemory::getAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) { snoop.clear(); resp.clear(); - resp.push_back(RangeSize(params()->addrRange.start, params()->addrRange.size())); + resp.push_back(RangeSize(params()->addrRange.start, + params()->addrRange.size())); } int @@ -191,21 +192,6 @@ PhysicalMemory::MemoryPort::deviceBlockSize() return memory->deviceBlockSize(); } -bool -PhysicalMemory::MemoryPort::recvTiming(Packet *pkt) -{ - assert(pkt->result != Packet::Nacked); - - Tick latency = memory->calculateLatency(pkt); - - memory->doFunctionalAccess(pkt); - - pkt->makeTimingResponse(); - sendTiming(pkt, latency); - - return true; -} - Tick PhysicalMemory::MemoryPort::recvAtomic(Packet *pkt) { @@ -216,6 +202,9 @@ PhysicalMemory::MemoryPort::recvAtomic(Packet *pkt) void PhysicalMemory::MemoryPort::recvFunctional(Packet *pkt) { + // Default implementation of SimpleTimingPort::recvFunctional() + // calls recvAtomic() and throws away the latency; we can save a + // little here by just not calculating the latency. memory->doFunctionalAccess(pkt); } diff --git a/src/mem/physical.hh b/src/mem/physical.hh index 1489e6700..02308b2ef 100644 --- a/src/mem/physical.hh +++ b/src/mem/physical.hh @@ -57,8 +57,6 @@ class PhysicalMemory : public MemObject protected: - virtual bool recvTiming(Packet *pkt); - virtual Tick recvAtomic(Packet *pkt); virtual void recvFunctional(Packet *pkt); diff --git a/src/mem/port.hh b/src/mem/port.hh index 42e369205..6b4184043 100644 --- a/src/mem/port.hh +++ b/src/mem/port.hh @@ -106,7 +106,8 @@ class Port /** Holds the ports status. Currently just that a range recomputation needs * to be done. */ enum Status { - RangeChange + RangeChange, + SnoopSquash }; void setName(const std::string &name) @@ -251,11 +252,13 @@ class FunctionalPort : public Port : Port(_name) {} + protected: virtual bool recvTiming(Packet *pkt) { panic("FuncPort is UniDir"); } virtual Tick recvAtomic(Packet *pkt) { panic("FuncPort is UniDir"); } virtual void recvFunctional(Packet *pkt) { panic("FuncPort is UniDir"); } virtual void recvStatusChange(Status status) {} + public: /** a write function that also does an endian conversion. */ template <typename T> inline void writeHtoG(Addr addr, T d); diff --git a/src/mem/tport.cc b/src/mem/tport.cc index 90cf68f02..55c301c87 100644 --- a/src/mem/tport.cc +++ b/src/mem/tport.cc @@ -31,18 +31,41 @@ #include "mem/tport.hh" void +SimpleTimingPort::recvFunctional(Packet *pkt) +{ + // just do an atomic access and throw away the returned latency + recvAtomic(pkt); +} + +bool +SimpleTimingPort::recvTiming(Packet *pkt) +{ + // If the device is only a slave, it should only be sending + // responses, which should never get nacked. There used to be + // code to hanldle nacks here, but I'm pretty sure it didn't work + // correctly with the drain code, so that would need to be fixed + // if we ever added it back. + assert(pkt->result != Packet::Nacked); + Tick latency = recvAtomic(pkt); + // turn packet around to go back to requester + pkt->makeTimingResponse(); + sendTimingLater(pkt, latency); + return true; +} + +void SimpleTimingPort::recvRetry() { bool result = true; while (result && transmitList.size()) { - result = Port::sendTiming(transmitList.front()); + result = sendTiming(transmitList.front()); if (result) transmitList.pop_front(); } - if (transmitList.size() == 0 && drainEvent) { - drainEvent->process(); - drainEvent = NULL; - } + if (transmitList.size() == 0 && drainEvent) { + drainEvent->process(); + drainEvent = NULL; + } } void @@ -50,26 +73,18 @@ SimpleTimingPort::SendEvent::process() { port->outTiming--; assert(port->outTiming >= 0); - if (port->Port::sendTiming(packet)) - if (port->transmitList.size() == 0 && port->drainEvent) { - port->drainEvent->process(); - port->drainEvent = NULL; - } - return; - - port->transmitList.push_back(packet); -} - -void -SimpleTimingPort::resendNacked(Packet *pkt) { - pkt->reinitNacked(); - if (transmitList.size()) { - transmitList.push_front(pkt); + if (port->sendTiming(packet)) { + // send successfule + if (port->transmitList.size() == 0 && port->drainEvent) { + port->drainEvent->process(); + port->drainEvent = NULL; + } } else { - if (!Port::sendTiming(pkt)) - transmitList.push_front(pkt); + // send unsuccessful (due to flow control). Will get retry + // callback later; save for then. + port->transmitList.push_back(packet); } -}; +} unsigned int diff --git a/src/mem/tport.hh b/src/mem/tport.hh index 5473e945e..df6d48196 100644 --- a/src/mem/tport.hh +++ b/src/mem/tport.hh @@ -28,67 +28,52 @@ * Authors: Ali Saidi */ +#ifndef __MEM_TPORT_HH__ +#define __MEM_TPORT_HH__ + /** * @file - * Implement a port which adds simple support of a sendTiming() function that - * takes a delay. In this way the * device can immediatly call - * sendTiming(pkt, time) after processing a request and the request will be - * handled by the port even if the port bus the device connects to is blocked. + * + * Declaration of SimpleTimingPort. */ -/** recvTiming and drain should be implemented something like this when this - * class is used. - -bool -PioPort::recvTiming(Packet *pkt) -{ - if (pkt->result == Packet::Nacked) { - resendNacked(pkt); - } else { - Tick latency = device->recvAtomic(pkt); - // turn packet around to go back to requester - pkt->makeTimingResponse(); - sendTiming(pkt, latency); - } - return true; -} - -PioDevice::drain(Event *de) -{ - unsigned int count; - count = SimpleTimingPort->drain(de); - if (count) - changeState(Draining); - else - changeState(Drained); - return count; -} -*/ - -#ifndef __MEM_TPORT_HH__ -#define __MEM_TPORT_HH__ - #include "mem/port.hh" #include "sim/eventq.hh" #include <list> #include <string> +/** + * A simple port for interfacing objects that basically have only + * functional memory behavior (e.g. I/O devices) to the memory system. + * Both timing and functional accesses are implemented in terms of + * atomic accesses. A derived port class thus only needs to provide + * recvAtomic() to support all memory access modes. + * + * The tricky part is handling recvTiming(), where the response must + * be scheduled separately via a later call to sendTiming(). This + * feature is handled by scheduling an internal event that calls + * sendTiming() after a delay, and optionally rescheduling the + * response if it is nacked. + */ class SimpleTimingPort : public Port { protected: - /** A list of outgoing timing response packets that haven't been serviced - * yet. */ + /** A list of outgoing timing response packets that haven't been + * serviced yet. */ std::list<Packet*> transmitList; + /** - * This class is used to implemented sendTiming() with a delay. When a delay - * is requested a new event is created. When the event time expires it - * attempts to send the packet. If it cannot, the packet is pushed onto the - * transmit list to be sent when recvRetry() is called. */ + * This class is used to implemented sendTiming() with a delay. When + * a delay is requested a new event is created. When the event time + * expires it attempts to send the packet. If it cannot, the packet + * is pushed onto the transmit list to be sent when recvRetry() is + * called. */ class SendEvent : public Event { SimpleTimingPort *port; Packet *packet; + public: SendEvent(SimpleTimingPort *p, Packet *pkt, Tick t) : Event(&mainEventQueue), port(p), packet(pkt) { setFlags(AutoDelete); schedule(curTick + t); } @@ -97,8 +82,6 @@ class SimpleTimingPort : public Port virtual const char *description() { return "Future scheduled sendTiming event"; } - - friend class SimpleTimingPort; }; @@ -112,23 +95,49 @@ class SimpleTimingPort : public Port Event *drainEvent; /** Schedule a sendTiming() event to be called in the future. */ - void sendTiming(Packet *pkt, Tick time) - { outTiming++; new SimpleTimingPort::SendEvent(this, pkt, time); } + void sendTimingLater(Packet *pkt, Tick time) + { outTiming++; new SendEvent(this, pkt, time); } /** This function is notification that the device should attempt to send a * packet again. */ virtual void recvRetry(); - void resendNacked(Packet *pkt); + /** Implemented using recvAtomic(). */ + void recvFunctional(Packet *pkt); + + /** Implemented using recvAtomic(). */ + bool recvTiming(Packet *pkt); + + /** + * Simple ports generally don't care about any status + * changes... can always override this in cases where that's not + * true. */ + virtual void recvStatusChange(Status status) { } + + public: SimpleTimingPort(std::string pname) : Port(pname), outTiming(0), drainEvent(NULL) {} + /** Hook for draining timing accesses from the system. The + * associated SimObject's drain() functions should be implemented + * something like this when this class is used: + \code + PioDevice::drain(Event *de) + { + unsigned int count; + count = SimpleTimingPort->drain(de); + if (count) + changeState(Draining); + else + changeState(Drained); + return count; + } + \endcode + */ unsigned int drain(Event *de); - - friend class SimpleTimingPort::SendEvent; }; #endif // __MEM_TPORT_HH__ diff --git a/src/python/m5/SimObject.py b/src/python/m5/SimObject.py new file mode 100644 index 000000000..a0d66e643 --- /dev/null +++ b/src/python/m5/SimObject.py @@ -0,0 +1,798 @@ +# Copyright (c) 2004-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: Steve Reinhardt +# Nathan Binkert + +import sys, types + +from util import * +from multidict import multidict + +# These utility functions have to come first because they're +# referenced in params.py... otherwise they won't be defined when we +# import params below, and the recursive import of this file from +# params.py will not find these names. +def isSimObject(value): + return isinstance(value, SimObject) + +def isSimObjectClass(value): + return issubclass(value, SimObject) + +def isSimObjectSequence(value): + if not isinstance(value, (list, tuple)) or len(value) == 0: + return False + + for val in value: + if not isNullPointer(val) and not isSimObject(val): + return False + + return True + +def isSimObjectOrSequence(value): + return isSimObject(value) or isSimObjectSequence(value) + +# Have to import params up top since Param is referenced on initial +# load (when SimObject class references Param to create a class +# variable, the 'name' param)... +from params import * +# There are a few things we need that aren't in params.__all__ since +# normal users don't need them +from params import ParamDesc, isNullPointer, SimObjVector + +noDot = False +try: + import pydot +except: + noDot = True + +##################################################################### +# +# M5 Python Configuration Utility +# +# The basic idea is to write simple Python programs that build Python +# objects corresponding to M5 SimObjects for the desired simulation +# configuration. For now, the Python emits a .ini file that can be +# parsed by M5. In the future, some tighter integration between M5 +# and the Python interpreter may allow bypassing the .ini file. +# +# Each SimObject class in M5 is represented by a Python class with the +# same name. The Python inheritance tree mirrors the M5 C++ tree +# (e.g., SimpleCPU derives from BaseCPU in both cases, and all +# SimObjects inherit from a single SimObject base class). To specify +# an instance of an M5 SimObject in a configuration, the user simply +# instantiates the corresponding Python object. The parameters for +# that SimObject are given by assigning to attributes of the Python +# object, either using keyword assignment in the constructor or in +# separate assignment statements. For example: +# +# cache = BaseCache(size='64KB') +# cache.hit_latency = 3 +# cache.assoc = 8 +# +# The magic lies in the mapping of the Python attributes for SimObject +# classes to the actual SimObject parameter specifications. This +# allows parameter validity checking in the Python code. Continuing +# the example above, the statements "cache.blurfl=3" or +# "cache.assoc='hello'" would both result in runtime errors in Python, +# since the BaseCache object has no 'blurfl' parameter and the 'assoc' +# parameter requires an integer, respectively. This magic is done +# primarily by overriding the special __setattr__ method that controls +# assignment to object attributes. +# +# Once a set of Python objects have been instantiated in a hierarchy, +# calling 'instantiate(obj)' (where obj is the root of the hierarchy) +# will generate a .ini file. +# +##################################################################### + +# dict to look up SimObjects based on path +instanceDict = {} + +# The metaclass for SimObject. This class controls how new classes +# that derive from SimObject are instantiated, and provides inherited +# class behavior (just like a class controls how instances of that +# class are instantiated, and provides inherited instance behavior). +class MetaSimObject(type): + # Attributes that can be set only at initialization time + init_keywords = { 'abstract' : types.BooleanType, + 'type' : types.StringType } + # Attributes that can be set any time + keywords = { 'check' : types.FunctionType, + 'cxx_type' : types.StringType, + 'cxx_predecls' : types.ListType, + 'swig_predecls' : types.ListType } + + # __new__ is called before __init__, and is where the statements + # in the body of the class definition get loaded into the class's + # __dict__. We intercept this to filter out parameter & port assignments + # and only allow "private" attributes to be passed to the base + # __new__ (starting with underscore). + def __new__(mcls, name, bases, dict): + # Copy "private" attributes, functions, and classes to the + # official dict. Everything else goes in _init_dict to be + # filtered in __init__. + cls_dict = {} + value_dict = {} + for key,val in dict.items(): + if key.startswith('_') or isinstance(val, (types.FunctionType, + types.TypeType)): + cls_dict[key] = val + else: + # must be a param/port setting + value_dict[key] = val + cls_dict['_value_dict'] = value_dict + return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict) + + # subclass initialization + def __init__(cls, name, bases, dict): + # calls type.__init__()... I think that's a no-op, but leave + # it here just in case it's not. + super(MetaSimObject, cls).__init__(name, bases, dict) + + # initialize required attributes + + # class-only attributes + cls._params = multidict() # param descriptions + cls._ports = multidict() # port descriptions + + # class or instance attributes + cls._values = multidict() # param values + cls._port_refs = multidict() # port ref objects + cls._instantiated = False # really instantiated, cloned, or subclassed + + # We don't support multiple inheritance. If you want to, you + # must fix multidict to deal with it properly. + if len(bases) > 1: + raise TypeError, "SimObjects do not support multiple inheritance" + + base = bases[0] + + # Set up general inheritance via multidicts. A subclass will + # inherit all its settings from the base class. The only time + # the following is not true is when we define the SimObject + # class itself (in which case the multidicts have no parent). + if isinstance(base, MetaSimObject): + cls._params.parent = base._params + cls._ports.parent = base._ports + cls._values.parent = base._values + cls._port_refs.parent = base._port_refs + # mark base as having been subclassed + base._instantiated = True + + # Now process the _value_dict items. They could be defining + # new (or overriding existing) parameters or ports, setting + # class keywords (e.g., 'abstract'), or setting parameter + # values or port bindings. The first 3 can only be set when + # the class is defined, so we handle them here. The others + # can be set later too, so just emulate that by calling + # setattr(). + for key,val in cls._value_dict.items(): + # param descriptions + if isinstance(val, ParamDesc): + cls._new_param(key, val) + + # port objects + elif isinstance(val, Port): + cls._new_port(key, val) + + # init-time-only keywords + elif cls.init_keywords.has_key(key): + cls._set_keyword(key, val, cls.init_keywords[key]) + + # default: use normal path (ends up in __setattr__) + else: + setattr(cls, key, val) + + cls.cxx_type = cls.type + '*' + # A forward class declaration is sufficient since we are just + # declaring a pointer. + cls.cxx_predecls = ['class %s;' % cls.type] + cls.swig_predecls = cls.cxx_predecls + + def _set_keyword(cls, keyword, val, kwtype): + if not isinstance(val, kwtype): + raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \ + (keyword, type(val), kwtype) + if isinstance(val, types.FunctionType): + val = classmethod(val) + type.__setattr__(cls, keyword, val) + + def _new_param(cls, name, pdesc): + # each param desc should be uniquely assigned to one variable + assert(not hasattr(pdesc, 'name')) + pdesc.name = name + cls._params[name] = pdesc + if hasattr(pdesc, 'default'): + cls._set_param(name, pdesc.default, pdesc) + + def _set_param(cls, name, value, param): + assert(param.name == name) + try: + cls._values[name] = param.convert(value) + except Exception, e: + msg = "%s\nError setting param %s.%s to %s\n" % \ + (e, cls.__name__, name, value) + e.args = (msg, ) + raise + + def _new_port(cls, name, port): + # each port should be uniquely assigned to one variable + assert(not hasattr(port, 'name')) + port.name = name + cls._ports[name] = port + if hasattr(port, 'default'): + cls._cls_get_port_ref(name).connect(port.default) + + # same as _get_port_ref, effectively, but for classes + def _cls_get_port_ref(cls, attr): + # Return reference that can be assigned to another port + # via __setattr__. There is only ever one reference + # object per port, but we create them lazily here. + ref = cls._port_refs.get(attr) + if not ref: + ref = cls._ports[attr].makeRef(cls) + cls._port_refs[attr] = ref + return ref + + # Set attribute (called on foo.attr = value when foo is an + # instance of class cls). + def __setattr__(cls, attr, value): + # normal processing for private attributes + if attr.startswith('_'): + type.__setattr__(cls, attr, value) + return + + if cls.keywords.has_key(attr): + cls._set_keyword(attr, value, cls.keywords[attr]) + return + + if cls._ports.has_key(attr): + cls._cls_get_port_ref(attr).connect(value) + return + + if isSimObjectOrSequence(value) and cls._instantiated: + raise RuntimeError, \ + "cannot set SimObject parameter '%s' after\n" \ + " class %s has been instantiated or subclassed" \ + % (attr, cls.__name__) + + # check for param + param = cls._params.get(attr) + if param: + cls._set_param(attr, value, param) + return + + if isSimObjectOrSequence(value): + # If RHS is a SimObject, it's an implicit child assignment. + # Classes don't have children, so we just put this object + # in _values; later, each instance will do a 'setattr(self, + # attr, _values[attr])' in SimObject.__init__ which will + # add this object as a child. + cls._values[attr] = value + return + + # no valid assignment... raise exception + raise AttributeError, \ + "Class %s has no parameter \'%s\'" % (cls.__name__, attr) + + def __getattr__(cls, attr): + if cls._values.has_key(attr): + return cls._values[attr] + + raise AttributeError, \ + "object '%s' has no attribute '%s'" % (cls.__name__, attr) + + def __str__(cls): + return cls.__name__ + + def cxx_decl(cls): + code = "#ifndef __PARAMS__%s\n#define __PARAMS__%s\n\n" % (cls, cls) + + if str(cls) != 'SimObject': + base = cls.__bases__[0].type + else: + base = None + + # The 'dict' attribute restricts us to the params declared in + # the object itself, not including inherited params (which + # will also be inherited from the base class's param struct + # here). + params = cls._params.dict.values() + try: + ptypes = [p.ptype for p in params] + except: + print cls, p, p.ptype_str + print params + raise + + # get a list of lists of predeclaration lines + predecls = [p.cxx_predecls() for p in params] + # flatten + predecls = reduce(lambda x,y:x+y, predecls, []) + # remove redundant lines + predecls2 = [] + for pd in predecls: + if pd not in predecls2: + predecls2.append(pd) + predecls2.sort() + code += "\n".join(predecls2) + code += "\n\n"; + + if base: + code += '#include "params/%s.hh"\n\n' % base + + # Generate declarations for locally defined enumerations. + enum_ptypes = [t for t in ptypes if issubclass(t, Enum)] + if enum_ptypes: + code += "\n".join([t.cxx_decl() for t in enum_ptypes]) + code += "\n\n" + + # now generate the actual param struct + code += "struct %sParams" % cls + if base: + code += " : public %sParams" % base + code += " {\n" + decls = [p.cxx_decl() for p in params] + decls.sort() + code += "".join([" %s\n" % d for d in decls]) + code += "};\n" + + # close #ifndef __PARAMS__* guard + code += "\n#endif\n" + return code + + def swig_decl(cls): + + code = '%%module %sParams\n' % cls + + if str(cls) != 'SimObject': + base = cls.__bases__[0].type + else: + base = None + + # The 'dict' attribute restricts us to the params declared in + # the object itself, not including inherited params (which + # will also be inherited from the base class's param struct + # here). + params = cls._params.dict.values() + ptypes = [p.ptype for p in params] + + # get a list of lists of predeclaration lines + predecls = [p.swig_predecls() for p in params] + # flatten + predecls = reduce(lambda x,y:x+y, predecls, []) + # remove redundant lines + predecls2 = [] + for pd in predecls: + if pd not in predecls2: + predecls2.append(pd) + predecls2.sort() + code += "\n".join(predecls2) + code += "\n\n"; + + if base: + code += '%%import "python/m5/swig/%sParams.i"\n\n' % base + + code += '%{\n' + code += '#include "params/%s.hh"\n' % cls + code += '%}\n\n' + code += '%%include "params/%s.hh"\n\n' % cls + + return code + +# The SimObject class is the root of the special hierarchy. Most of +# the code in this class deals with the configuration hierarchy itself +# (parent/child node relationships). +class SimObject(object): + # Specify metaclass. Any class inheriting from SimObject will + # get this metaclass. + __metaclass__ = MetaSimObject + type = 'SimObject' + + name = Param.String("Object name") + + # Initialize new instance. For objects with SimObject-valued + # children, we need to recursively clone the classes represented + # by those param values as well in a consistent "deep copy"-style + # fashion. That is, we want to make sure that each instance is + # cloned only once, and that if there are multiple references to + # the same original object, we end up with the corresponding + # cloned references all pointing to the same cloned instance. + def __init__(self, **kwargs): + ancestor = kwargs.get('_ancestor') + memo_dict = kwargs.get('_memo') + if memo_dict is None: + # prepare to memoize any recursively instantiated objects + memo_dict = {} + elif ancestor: + # memoize me now to avoid problems with recursive calls + memo_dict[ancestor] = self + + if not ancestor: + ancestor = self.__class__ + ancestor._instantiated = True + + # initialize required attributes + self._parent = None + self._children = {} + self._ccObject = None # pointer to C++ object + self._instantiated = False # really "cloned" + + # Inherit parameter values from class using multidict so + # individual value settings can be overridden. + self._values = multidict(ancestor._values) + # clone SimObject-valued parameters + for key,val in ancestor._values.iteritems(): + if isSimObject(val): + setattr(self, key, val(_memo=memo_dict)) + elif isSimObjectSequence(val) and len(val): + setattr(self, key, [ v(_memo=memo_dict) for v in val ]) + # clone port references. no need to use a multidict here + # since we will be creating new references for all ports. + self._port_refs = {} + for key,val in ancestor._port_refs.iteritems(): + self._port_refs[key] = val.clone(self, memo_dict) + # apply attribute assignments from keyword args, if any + for key,val in kwargs.iteritems(): + setattr(self, key, val) + + # "Clone" the current instance by creating another instance of + # this instance's class, but that inherits its parameter values + # and port mappings from the current instance. If we're in a + # "deep copy" recursive clone, check the _memo dict to see if + # we've already cloned this instance. + def __call__(self, **kwargs): + memo_dict = kwargs.get('_memo') + if memo_dict is None: + # no memo_dict: must be top-level clone operation. + # this is only allowed at the root of a hierarchy + if self._parent: + raise RuntimeError, "attempt to clone object %s " \ + "not at the root of a tree (parent = %s)" \ + % (self, self._parent) + # create a new dict and use that. + memo_dict = {} + kwargs['_memo'] = memo_dict + elif memo_dict.has_key(self): + # clone already done & memoized + return memo_dict[self] + return self.__class__(_ancestor = self, **kwargs) + + def _get_port_ref(self, attr): + # Return reference that can be assigned to another port + # via __setattr__. There is only ever one reference + # object per port, but we create them lazily here. + ref = self._port_refs.get(attr) + if not ref: + ref = self._ports[attr].makeRef(self) + self._port_refs[attr] = ref + return ref + + def __getattr__(self, attr): + if self._ports.has_key(attr): + return self._get_port_ref(attr) + + if self._values.has_key(attr): + return self._values[attr] + + raise AttributeError, "object '%s' has no attribute '%s'" \ + % (self.__class__.__name__, attr) + + # Set attribute (called on foo.attr = value when foo is an + # instance of class cls). + def __setattr__(self, attr, value): + # normal processing for private attributes + if attr.startswith('_'): + object.__setattr__(self, attr, value) + return + + if self._ports.has_key(attr): + # set up port connection + self._get_port_ref(attr).connect(value) + return + + if isSimObjectOrSequence(value) and self._instantiated: + raise RuntimeError, \ + "cannot set SimObject parameter '%s' after\n" \ + " instance been cloned %s" % (attr, `self`) + + # must be SimObject param + param = self._params.get(attr) + if param: + try: + value = param.convert(value) + except Exception, e: + msg = "%s\nError setting param %s.%s to %s\n" % \ + (e, self.__class__.__name__, attr, value) + e.args = (msg, ) + raise + self._set_child(attr, value) + return + + if isSimObjectOrSequence(value): + self._set_child(attr, value) + return + + # no valid assignment... raise exception + raise AttributeError, "Class %s has no parameter %s" \ + % (self.__class__.__name__, attr) + + + # this hack allows tacking a '[0]' onto parameters that may or may + # not be vectors, and always getting the first element (e.g. cpus) + def __getitem__(self, key): + if key == 0: + return self + raise TypeError, "Non-zero index '%s' to SimObject" % key + + # clear out children with given name, even if it's a vector + def clear_child(self, name): + if not self._children.has_key(name): + return + child = self._children[name] + if isinstance(child, SimObjVector): + for i in xrange(len(child)): + del self._children["s%d" % (name, i)] + del self._children[name] + + def add_child(self, name, value): + self._children[name] = value + + def _maybe_set_parent(self, parent, name): + if not self._parent: + self._parent = parent + self._name = name + parent.add_child(name, self) + + def _set_child(self, attr, value): + # if RHS is a SimObject, it's an implicit child assignment + # clear out old child with this name, if any + self.clear_child(attr) + + if isSimObject(value): + value._maybe_set_parent(self, attr) + elif isSimObjectSequence(value): + value = SimObjVector(value) + [v._maybe_set_parent(self, "%s%d" % (attr, i)) + for i,v in enumerate(value)] + + self._values[attr] = value + + def path(self): + if not self._parent: + return 'root' + ppath = self._parent.path() + if ppath == 'root': + return self._name + return ppath + "." + self._name + + def __str__(self): + return self.path() + + def ini_str(self): + return self.path() + + def find_any(self, ptype): + if isinstance(self, ptype): + return self, True + + found_obj = None + for child in self._children.itervalues(): + if isinstance(child, ptype): + if found_obj != None and child != found_obj: + raise AttributeError, \ + 'parent.any matched more than one: %s %s' % \ + (found_obj.path, child.path) + found_obj = child + # search param space + for pname,pdesc in self._params.iteritems(): + if issubclass(pdesc.ptype, ptype): + match_obj = self._values[pname] + if found_obj != None and found_obj != match_obj: + raise AttributeError, \ + 'parent.any matched more than one: %s' % obj.path + found_obj = match_obj + return found_obj, found_obj != None + + def unproxy(self, base): + return self + + def unproxy_all(self): + for param in self._params.iterkeys(): + value = self._values.get(param) + if value != None and proxy.isproxy(value): + try: + value = value.unproxy(self) + except: + print "Error in unproxying param '%s' of %s" % \ + (param, self.path()) + raise + setattr(self, param, value) + + # Unproxy ports in sorted order so that 'append' operations on + # vector ports are done in a deterministic fashion. + port_names = self._ports.keys() + port_names.sort() + for port_name in port_names: + port = self._port_refs.get(port_name) + if port != None: + port.unproxy(self) + + # Unproxy children in sorted order for determinism also. + child_names = self._children.keys() + child_names.sort() + for child in child_names: + self._children[child].unproxy_all() + + def print_ini(self): + print '[' + self.path() + ']' # .ini section header + + instanceDict[self.path()] = self + + if hasattr(self, 'type') and not isinstance(self, ParamContext): + print 'type=%s' % self.type + + child_names = self._children.keys() + child_names.sort() + np_child_names = [c for c in child_names \ + if not isinstance(self._children[c], ParamContext)] + if len(np_child_names): + print 'children=%s' % ' '.join(np_child_names) + + param_names = self._params.keys() + param_names.sort() + for param in param_names: + value = self._values.get(param) + if value != None: + print '%s=%s' % (param, self._values[param].ini_str()) + + port_names = self._ports.keys() + port_names.sort() + for port_name in port_names: + port = self._port_refs.get(port_name, None) + if port != None: + print '%s=%s' % (port_name, port.ini_str()) + + print # blank line between objects + + for child in child_names: + self._children[child].print_ini() + + # Call C++ to create C++ object corresponding to this object and + # (recursively) all its children + def createCCObject(self): + self.getCCObject() # force creation + for child in self._children.itervalues(): + child.createCCObject() + + # Get C++ object corresponding to this object, calling C++ if + # necessary to construct it. Does *not* recursively create + # children. + def getCCObject(self): + if not self._ccObject: + self._ccObject = -1 # flag to catch cycles in recursion + self._ccObject = cc_main.createSimObject(self.path()) + elif self._ccObject == -1: + raise RuntimeError, "%s: recursive call to getCCObject()" \ + % self.path() + return self._ccObject + + # Create C++ port connections corresponding to the connections in + # _port_refs (& recursively for all children) + def connectPorts(self): + for portRef in self._port_refs.itervalues(): + portRef.ccConnect() + for child in self._children.itervalues(): + child.connectPorts() + + def startDrain(self, drain_event, recursive): + count = 0 + # ParamContexts don't serialize + if isinstance(self, SimObject) and not isinstance(self, ParamContext): + count += self._ccObject.drain(drain_event) + if recursive: + for child in self._children.itervalues(): + count += child.startDrain(drain_event, True) + return count + + def resume(self): + if isinstance(self, SimObject) and not isinstance(self, ParamContext): + self._ccObject.resume() + for child in self._children.itervalues(): + child.resume() + + def changeTiming(self, mode): + if isinstance(self, System): + self._ccObject.setMemoryMode(mode) + for child in self._children.itervalues(): + child.changeTiming(mode) + + def takeOverFrom(self, old_cpu): + cpu_ptr = cc_main.convertToBaseCPUPtr(old_cpu._ccObject) + self._ccObject.takeOverFrom(cpu_ptr) + + # generate output file for 'dot' to display as a pretty graph. + # this code is currently broken. + def outputDot(self, dot): + label = "{%s|" % self.path + if isSimObject(self.realtype): + label += '%s|' % self.type + + if self.children: + # instantiate children in same order they were added for + # backward compatibility (else we can end up with cpu1 + # before cpu0). + for c in self.children: + dot.add_edge(pydot.Edge(self.path,c.path, style="bold")) + + simobjs = [] + for param in self.params: + try: + if param.value is None: + raise AttributeError, 'Parameter with no value' + + value = param.value + string = param.string(value) + except Exception, e: + msg = 'exception in %s:%s\n%s' % (self.name, param.name, e) + e.args = (msg, ) + raise + + if isSimObject(param.ptype) and string != "Null": + simobjs.append(string) + else: + label += '%s = %s\\n' % (param.name, string) + + for so in simobjs: + label += "|<%s> %s" % (so, so) + dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so, + tailport="w")) + label += '}' + dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label)) + + # recursively dump out children + for c in self.children: + c.outputDot(dot) + +class ParamContext(SimObject): + pass + +# Function to provide to C++ so it can look up instances based on paths +def resolveSimObject(name): + obj = instanceDict[name] + return obj.getCCObject() + +# __all__ defines the list of symbols that get exported when +# 'from config import *' is invoked. Try to keep this reasonably +# short to avoid polluting other namespaces. +__all__ = ['SimObject', 'ParamContext'] + + +# see comment on imports at end of __init__.py. +import proxy +import cc_main +import m5 diff --git a/src/python/m5/__init__.py b/src/python/m5/__init__.py index 950d605df..5717b49b6 100644 --- a/src/python/m5/__init__.py +++ b/src/python/m5/__init__.py @@ -44,6 +44,7 @@ def panic(string): print >>sys.stderr, 'panic:', string sys.exit(1) +# force scalars to one-element lists for uniformity def makeList(objOrList): if isinstance(objOrList, list): return objOrList @@ -71,17 +72,11 @@ build_env.update(defines.m5_build_env) env = smartdict.SmartDict() env.update(os.environ) -# Function to provide to C++ so it can look up instances based on paths -def resolveSimObject(name): - obj = config.instanceDict[name] - return obj.getCCObject() - -from main import options, arguments, main - # The final hook to generate .ini files. Called from the user script # once the config is built. def instantiate(root): - config.ticks_per_sec = float(root.clock.frequency) + params.ticks_per_sec = float(root.clock.frequency) + root.unproxy_all() # ugly temporary hack to get output to config.ini sys.stdout = file(os.path.join(options.outdir, 'config.ini'), 'w') root.print_ini() @@ -109,11 +104,6 @@ def curTick(): # register our C++ exit callback function with Python atexit.register(cc_main.doExitCleanup) -# This import allows user scripts to reference 'm5.objects.Foo' after -# just doing an 'import m5' (without an 'import m5.objects'). May not -# matter since most scripts will probably 'from m5.objects import *'. -import objects - # This loops until all objects have been fully drained. def doDrain(root): all_drained = drain(root) @@ -206,3 +196,17 @@ def switchCpus(cpuList): new_cpu.takeOverFrom(old_cpus[index]) new_cpu._ccObject.resume() index += 1 + +# Since we have so many mutual imports in this package, we should: +# 1. Put all intra-package imports at the *bottom* of the file, unless +# they're absolutely needed before that (for top-level statements +# or class attributes). Imports of "trivial" packages that don't +# import other packages (e.g., 'smartdict') can be at the top. +# 2. Never use 'from foo import *' on an intra-package import since +# you can get the wrong result if foo is only partially imported +# at the point you do that (i.e., because foo is in the middle of +# importing *you*). +from main import options +import objects +import params +from SimObject import resolveSimObject diff --git a/src/python/m5/config.py b/src/python/m5/config.py deleted file mode 100644 index df4b74cbd..000000000 --- a/src/python/m5/config.py +++ /dev/null @@ -1,1527 +0,0 @@ -# Copyright (c) 2004-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: Steve Reinhardt -# Nathan Binkert - -import os, re, sys, types, inspect, copy - -import m5 -from m5 import panic, cc_main -from convert import * -from multidict import multidict - -noDot = False -try: - import pydot -except: - noDot = True - -class Singleton(type): - def __call__(cls, *args, **kwargs): - if hasattr(cls, '_instance'): - return cls._instance - - cls._instance = super(Singleton, cls).__call__(*args, **kwargs) - return cls._instance - -##################################################################### -# -# M5 Python Configuration Utility -# -# The basic idea is to write simple Python programs that build Python -# objects corresponding to M5 SimObjects for the desired simulation -# configuration. For now, the Python emits a .ini file that can be -# parsed by M5. In the future, some tighter integration between M5 -# and the Python interpreter may allow bypassing the .ini file. -# -# Each SimObject class in M5 is represented by a Python class with the -# same name. The Python inheritance tree mirrors the M5 C++ tree -# (e.g., SimpleCPU derives from BaseCPU in both cases, and all -# SimObjects inherit from a single SimObject base class). To specify -# an instance of an M5 SimObject in a configuration, the user simply -# instantiates the corresponding Python object. The parameters for -# that SimObject are given by assigning to attributes of the Python -# object, either using keyword assignment in the constructor or in -# separate assignment statements. For example: -# -# cache = BaseCache(size='64KB') -# cache.hit_latency = 3 -# cache.assoc = 8 -# -# The magic lies in the mapping of the Python attributes for SimObject -# classes to the actual SimObject parameter specifications. This -# allows parameter validity checking in the Python code. Continuing -# the example above, the statements "cache.blurfl=3" or -# "cache.assoc='hello'" would both result in runtime errors in Python, -# since the BaseCache object has no 'blurfl' parameter and the 'assoc' -# parameter requires an integer, respectively. This magic is done -# primarily by overriding the special __setattr__ method that controls -# assignment to object attributes. -# -# Once a set of Python objects have been instantiated in a hierarchy, -# calling 'instantiate(obj)' (where obj is the root of the hierarchy) -# will generate a .ini file. -# -##################################################################### - -# dict to look up SimObjects based on path -instanceDict = {} - -############################# -# -# Utility methods -# -############################# - -def isSimObject(value): - return isinstance(value, SimObject) - -def isSimObjectSequence(value): - if not isinstance(value, (list, tuple)) or len(value) == 0: - return False - - for val in value: - if not isNullPointer(val) and not isSimObject(val): - return False - - return True - -def isSimObjectOrSequence(value): - return isSimObject(value) or isSimObjectSequence(value) - -def isNullPointer(value): - return isinstance(value, NullSimObject) - -# Apply method to object. -# applyMethod(obj, 'meth', <args>) is equivalent to obj.meth(<args>) -def applyMethod(obj, meth, *args, **kwargs): - return getattr(obj, meth)(*args, **kwargs) - -# If the first argument is an (non-sequence) object, apply the named -# method with the given arguments. If the first argument is a -# sequence, apply the method to each element of the sequence (a la -# 'map'). -def applyOrMap(objOrSeq, meth, *args, **kwargs): - if not isinstance(objOrSeq, (list, tuple)): - return applyMethod(objOrSeq, meth, *args, **kwargs) - else: - return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq] - - -# The metaclass for SimObject. This class controls how new classes -# that derive from SimObject are instantiated, and provides inherited -# class behavior (just like a class controls how instances of that -# class are instantiated, and provides inherited instance behavior). -class MetaSimObject(type): - # Attributes that can be set only at initialization time - init_keywords = { 'abstract' : types.BooleanType, - 'type' : types.StringType } - # Attributes that can be set any time - keywords = { 'check' : types.FunctionType } - - # __new__ is called before __init__, and is where the statements - # in the body of the class definition get loaded into the class's - # __dict__. We intercept this to filter out parameter & port assignments - # and only allow "private" attributes to be passed to the base - # __new__ (starting with underscore). - def __new__(mcls, name, bases, dict): - # Copy "private" attributes, functions, and classes to the - # official dict. Everything else goes in _init_dict to be - # filtered in __init__. - cls_dict = {} - value_dict = {} - for key,val in dict.items(): - if key.startswith('_') or isinstance(val, (types.FunctionType, - types.TypeType)): - cls_dict[key] = val - else: - # must be a param/port setting - value_dict[key] = val - cls_dict['_value_dict'] = value_dict - return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict) - - # subclass initialization - def __init__(cls, name, bases, dict): - # calls type.__init__()... I think that's a no-op, but leave - # it here just in case it's not. - super(MetaSimObject, cls).__init__(name, bases, dict) - - # initialize required attributes - - # class-only attributes - cls._params = multidict() # param descriptions - cls._ports = multidict() # port descriptions - - # class or instance attributes - cls._values = multidict() # param values - cls._port_map = multidict() # port bindings - cls._instantiated = False # really instantiated, cloned, or subclassed - - # We don't support multiple inheritance. If you want to, you - # must fix multidict to deal with it properly. - if len(bases) > 1: - raise TypeError, "SimObjects do not support multiple inheritance" - - base = bases[0] - - # Set up general inheritance via multidicts. A subclass will - # inherit all its settings from the base class. The only time - # the following is not true is when we define the SimObject - # class itself (in which case the multidicts have no parent). - if isinstance(base, MetaSimObject): - cls._params.parent = base._params - cls._ports.parent = base._ports - cls._values.parent = base._values - cls._port_map.parent = base._port_map - # mark base as having been subclassed - base._instantiated = True - - # Now process the _value_dict items. They could be defining - # new (or overriding existing) parameters or ports, setting - # class keywords (e.g., 'abstract'), or setting parameter - # values or port bindings. The first 3 can only be set when - # the class is defined, so we handle them here. The others - # can be set later too, so just emulate that by calling - # setattr(). - for key,val in cls._value_dict.items(): - # param descriptions - if isinstance(val, ParamDesc): - cls._new_param(key, val) - - # port objects - elif isinstance(val, Port): - cls._ports[key] = val - - # init-time-only keywords - elif cls.init_keywords.has_key(key): - cls._set_keyword(key, val, cls.init_keywords[key]) - - # default: use normal path (ends up in __setattr__) - else: - setattr(cls, key, val) - - def _set_keyword(cls, keyword, val, kwtype): - if not isinstance(val, kwtype): - raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \ - (keyword, type(val), kwtype) - if isinstance(val, types.FunctionType): - val = classmethod(val) - type.__setattr__(cls, keyword, val) - - def _new_param(cls, name, value): - cls._params[name] = value - if hasattr(value, 'default'): - setattr(cls, name, value.default) - - # Set attribute (called on foo.attr = value when foo is an - # instance of class cls). - def __setattr__(cls, attr, value): - # normal processing for private attributes - if attr.startswith('_'): - type.__setattr__(cls, attr, value) - return - - if cls.keywords.has_key(attr): - cls._set_keyword(attr, value, cls.keywords[attr]) - return - - if cls._ports.has_key(attr): - self._ports[attr].connect(self, attr, value) - return - - if isSimObjectOrSequence(value) and cls._instantiated: - raise RuntimeError, \ - "cannot set SimObject parameter '%s' after\n" \ - " class %s has been instantiated or subclassed" \ - % (attr, cls.__name__) - - # check for param - param = cls._params.get(attr, None) - if param: - try: - cls._values[attr] = param.convert(value) - except Exception, e: - msg = "%s\nError setting param %s.%s to %s\n" % \ - (e, cls.__name__, attr, value) - e.args = (msg, ) - raise - elif isSimObjectOrSequence(value): - # if RHS is a SimObject, it's an implicit child assignment - cls._values[attr] = value - else: - raise AttributeError, \ - "Class %s has no parameter \'%s\'" % (cls.__name__, attr) - - def __getattr__(cls, attr): - if cls._values.has_key(attr): - return cls._values[attr] - - raise AttributeError, \ - "object '%s' has no attribute '%s'" % (cls.__name__, attr) - -# The SimObject class is the root of the special hierarchy. Most of -# the code in this class deals with the configuration hierarchy itself -# (parent/child node relationships). -class SimObject(object): - # Specify metaclass. Any class inheriting from SimObject will - # get this metaclass. - __metaclass__ = MetaSimObject - - # Initialize new instance. For objects with SimObject-valued - # children, we need to recursively clone the classes represented - # by those param values as well in a consistent "deep copy"-style - # fashion. That is, we want to make sure that each instance is - # cloned only once, and that if there are multiple references to - # the same original object, we end up with the corresponding - # cloned references all pointing to the same cloned instance. - def __init__(self, **kwargs): - ancestor = kwargs.get('_ancestor') - memo_dict = kwargs.get('_memo') - if memo_dict is None: - # prepare to memoize any recursively instantiated objects - memo_dict = {} - elif ancestor: - # memoize me now to avoid problems with recursive calls - memo_dict[ancestor] = self - - if not ancestor: - ancestor = self.__class__ - ancestor._instantiated = True - - # initialize required attributes - self._parent = None - self._children = {} - self._ccObject = None # pointer to C++ object - self._instantiated = False # really "cloned" - - # Inherit parameter values from class using multidict so - # individual value settings can be overridden. - self._values = multidict(ancestor._values) - # clone SimObject-valued parameters - for key,val in ancestor._values.iteritems(): - if isSimObject(val): - setattr(self, key, val(_memo=memo_dict)) - elif isSimObjectSequence(val) and len(val): - setattr(self, key, [ v(_memo=memo_dict) for v in val ]) - # clone port references. no need to use a multidict here - # since we will be creating new references for all ports. - self._port_map = {} - for key,val in ancestor._port_map.iteritems(): - self._port_map[key] = applyOrMap(val, 'clone', memo_dict) - # apply attribute assignments from keyword args, if any - for key,val in kwargs.iteritems(): - setattr(self, key, val) - - # "Clone" the current instance by creating another instance of - # this instance's class, but that inherits its parameter values - # and port mappings from the current instance. If we're in a - # "deep copy" recursive clone, check the _memo dict to see if - # we've already cloned this instance. - def __call__(self, **kwargs): - memo_dict = kwargs.get('_memo') - if memo_dict is None: - # no memo_dict: must be top-level clone operation. - # this is only allowed at the root of a hierarchy - if self._parent: - raise RuntimeError, "attempt to clone object %s " \ - "not at the root of a tree (parent = %s)" \ - % (self, self._parent) - # create a new dict and use that. - memo_dict = {} - kwargs['_memo'] = memo_dict - elif memo_dict.has_key(self): - # clone already done & memoized - return memo_dict[self] - return self.__class__(_ancestor = self, **kwargs) - - def __getattr__(self, attr): - if self._ports.has_key(attr): - # return reference that can be assigned to another port - # via __setattr__ - return self._ports[attr].makeRef(self, attr) - - if self._values.has_key(attr): - return self._values[attr] - - raise AttributeError, "object '%s' has no attribute '%s'" \ - % (self.__class__.__name__, attr) - - # Set attribute (called on foo.attr = value when foo is an - # instance of class cls). - def __setattr__(self, attr, value): - # normal processing for private attributes - if attr.startswith('_'): - object.__setattr__(self, attr, value) - return - - if self._ports.has_key(attr): - # set up port connection - self._ports[attr].connect(self, attr, value) - return - - if isSimObjectOrSequence(value) and self._instantiated: - raise RuntimeError, \ - "cannot set SimObject parameter '%s' after\n" \ - " instance been cloned %s" % (attr, `self`) - - # must be SimObject param - param = self._params.get(attr, None) - if param: - try: - value = param.convert(value) - except Exception, e: - msg = "%s\nError setting param %s.%s to %s\n" % \ - (e, self.__class__.__name__, attr, value) - e.args = (msg, ) - raise - elif isSimObjectOrSequence(value): - pass - else: - raise AttributeError, "Class %s has no parameter %s" \ - % (self.__class__.__name__, attr) - - # clear out old child with this name, if any - self.clear_child(attr) - - if isSimObject(value): - value.set_path(self, attr) - elif isSimObjectSequence(value): - value = SimObjVector(value) - [v.set_path(self, "%s%d" % (attr, i)) for i,v in enumerate(value)] - - self._values[attr] = value - - # this hack allows tacking a '[0]' onto parameters that may or may - # not be vectors, and always getting the first element (e.g. cpus) - def __getitem__(self, key): - if key == 0: - return self - raise TypeError, "Non-zero index '%s' to SimObject" % key - - # clear out children with given name, even if it's a vector - def clear_child(self, name): - if not self._children.has_key(name): - return - child = self._children[name] - if isinstance(child, SimObjVector): - for i in xrange(len(child)): - del self._children["s%d" % (name, i)] - del self._children[name] - - def add_child(self, name, value): - self._children[name] = value - - def set_path(self, parent, name): - if not self._parent: - self._parent = parent - self._name = name - parent.add_child(name, self) - - def path(self): - if not self._parent: - return 'root' - ppath = self._parent.path() - if ppath == 'root': - return self._name - return ppath + "." + self._name - - def __str__(self): - return self.path() - - def ini_str(self): - return self.path() - - def find_any(self, ptype): - if isinstance(self, ptype): - return self, True - - found_obj = None - for child in self._children.itervalues(): - if isinstance(child, ptype): - if found_obj != None and child != found_obj: - raise AttributeError, \ - 'parent.any matched more than one: %s %s' % \ - (found_obj.path, child.path) - found_obj = child - # search param space - for pname,pdesc in self._params.iteritems(): - if issubclass(pdesc.ptype, ptype): - match_obj = self._values[pname] - if found_obj != None and found_obj != match_obj: - raise AttributeError, \ - 'parent.any matched more than one: %s' % obj.path - found_obj = match_obj - return found_obj, found_obj != None - - def unproxy(self, base): - return self - - def print_ini(self): - print '[' + self.path() + ']' # .ini section header - - instanceDict[self.path()] = self - - if hasattr(self, 'type') and not isinstance(self, ParamContext): - print 'type=%s' % self.type - - child_names = self._children.keys() - child_names.sort() - np_child_names = [c for c in child_names \ - if not isinstance(self._children[c], ParamContext)] - if len(np_child_names): - print 'children=%s' % ' '.join(np_child_names) - - param_names = self._params.keys() - param_names.sort() - for param in param_names: - value = self._values.get(param, None) - if value != None: - if isproxy(value): - try: - value = value.unproxy(self) - except: - print >> sys.stderr, \ - "Error in unproxying param '%s' of %s" % \ - (param, self.path()) - raise - setattr(self, param, value) - print '%s=%s' % (param, self._values[param].ini_str()) - - print # blank line between objects - - for child in child_names: - self._children[child].print_ini() - - # Call C++ to create C++ object corresponding to this object and - # (recursively) all its children - def createCCObject(self): - self.getCCObject() # force creation - for child in self._children.itervalues(): - child.createCCObject() - - # Get C++ object corresponding to this object, calling C++ if - # necessary to construct it. Does *not* recursively create - # children. - def getCCObject(self): - if not self._ccObject: - self._ccObject = -1 # flag to catch cycles in recursion - self._ccObject = cc_main.createSimObject(self.path()) - elif self._ccObject == -1: - raise RuntimeError, "%s: recursive call to getCCObject()" \ - % self.path() - return self._ccObject - - # Create C++ port connections corresponding to the connections in - # _port_map (& recursively for all children) - def connectPorts(self): - for portRef in self._port_map.itervalues(): - applyOrMap(portRef, 'ccConnect') - for child in self._children.itervalues(): - child.connectPorts() - - def startDrain(self, drain_event, recursive): - count = 0 - # ParamContexts don't serialize - if isinstance(self, SimObject) and not isinstance(self, ParamContext): - count += self._ccObject.drain(drain_event) - if recursive: - for child in self._children.itervalues(): - count += child.startDrain(drain_event, True) - return count - - def resume(self): - if isinstance(self, SimObject) and not isinstance(self, ParamContext): - self._ccObject.resume() - for child in self._children.itervalues(): - child.resume() - - def changeTiming(self, mode): - if isinstance(self, System): - self._ccObject.setMemoryMode(mode) - for child in self._children.itervalues(): - child.changeTiming(mode) - - def takeOverFrom(self, old_cpu): - cpu_ptr = cc_main.convertToBaseCPUPtr(old_cpu._ccObject) - self._ccObject.takeOverFrom(cpu_ptr) - - # generate output file for 'dot' to display as a pretty graph. - # this code is currently broken. - def outputDot(self, dot): - label = "{%s|" % self.path - if isSimObject(self.realtype): - label += '%s|' % self.type - - if self.children: - # instantiate children in same order they were added for - # backward compatibility (else we can end up with cpu1 - # before cpu0). - for c in self.children: - dot.add_edge(pydot.Edge(self.path,c.path, style="bold")) - - simobjs = [] - for param in self.params: - try: - if param.value is None: - raise AttributeError, 'Parameter with no value' - - value = param.value - string = param.string(value) - except Exception, e: - msg = 'exception in %s:%s\n%s' % (self.name, param.name, e) - e.args = (msg, ) - raise - - if isSimObject(param.ptype) and string != "Null": - simobjs.append(string) - else: - label += '%s = %s\\n' % (param.name, string) - - for so in simobjs: - label += "|<%s> %s" % (so, so) - dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so, - tailport="w")) - label += '}' - dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label)) - - # recursively dump out children - for c in self.children: - c.outputDot(dot) - -class ParamContext(SimObject): - pass - -##################################################################### -# -# Proxy object support. -# -##################################################################### - -class BaseProxy(object): - def __init__(self, search_self, search_up): - self._search_self = search_self - self._search_up = search_up - self._multiplier = None - - def __setattr__(self, attr, value): - if not attr.startswith('_'): - raise AttributeError, 'cannot set attribute on proxy object' - super(BaseProxy, self).__setattr__(attr, value) - - # support multiplying proxies by constants - def __mul__(self, other): - if not isinstance(other, (int, long, float)): - raise TypeError, "Proxy multiplier must be integer" - if self._multiplier == None: - self._multiplier = other - else: - # support chained multipliers - self._multiplier *= other - return self - - __rmul__ = __mul__ - - def _mulcheck(self, result): - if self._multiplier == None: - return result - return result * self._multiplier - - def unproxy(self, base): - obj = base - done = False - - if self._search_self: - result, done = self.find(obj) - - if self._search_up: - while not done: - obj = obj._parent - if not obj: - break - result, done = self.find(obj) - - if not done: - raise AttributeError, \ - "Can't resolve proxy '%s' from '%s'" % \ - (self.path(), base.path()) - - if isinstance(result, BaseProxy): - if result == self: - raise RuntimeError, "Cycle in unproxy" - result = result.unproxy(obj) - - return self._mulcheck(result) - - def getindex(obj, index): - if index == None: - return obj - try: - obj = obj[index] - except TypeError: - if index != 0: - raise - # if index is 0 and item is not subscriptable, just - # use item itself (so cpu[0] works on uniprocessors) - return obj - getindex = staticmethod(getindex) - - def set_param_desc(self, pdesc): - self._pdesc = pdesc - -class AttrProxy(BaseProxy): - def __init__(self, search_self, search_up, attr): - super(AttrProxy, self).__init__(search_self, search_up) - self._attr = attr - self._modifiers = [] - - def __getattr__(self, attr): - # python uses __bases__ internally for inheritance - if attr.startswith('_'): - return super(AttrProxy, self).__getattr__(self, attr) - if hasattr(self, '_pdesc'): - raise AttributeError, "Attribute reference on bound proxy" - self._modifiers.append(attr) - return self - - # support indexing on proxies (e.g., Self.cpu[0]) - def __getitem__(self, key): - if not isinstance(key, int): - raise TypeError, "Proxy object requires integer index" - self._modifiers.append(key) - return self - - def find(self, obj): - try: - val = getattr(obj, self._attr) - except: - return None, False - while isproxy(val): - val = val.unproxy(obj) - for m in self._modifiers: - if isinstance(m, str): - val = getattr(val, m) - elif isinstance(m, int): - val = val[m] - else: - assert("Item must be string or integer") - while isproxy(val): - val = val.unproxy(obj) - return val, True - - def path(self): - p = self._attr - for m in self._modifiers: - if isinstance(m, str): - p += '.%s' % m - elif isinstance(m, int): - p += '[%d]' % m - else: - assert("Item must be string or integer") - return p - -class AnyProxy(BaseProxy): - def find(self, obj): - return obj.find_any(self._pdesc.ptype) - - def path(self): - return 'any' - -def isproxy(obj): - if isinstance(obj, (BaseProxy, EthernetAddr)): - return True - elif isinstance(obj, (list, tuple)): - for v in obj: - if isproxy(v): - return True - return False - -class ProxyFactory(object): - def __init__(self, search_self, search_up): - self.search_self = search_self - self.search_up = search_up - - def __getattr__(self, attr): - if attr == 'any': - return AnyProxy(self.search_self, self.search_up) - else: - return AttrProxy(self.search_self, self.search_up, attr) - -# global objects for handling proxies -Parent = ProxyFactory(search_self = False, search_up = True) -Self = ProxyFactory(search_self = True, search_up = False) - -##################################################################### -# -# Parameter description classes -# -# The _params dictionary in each class maps parameter names to either -# a Param or a VectorParam object. These objects contain the -# parameter description string, the parameter type, and the default -# value (if any). The convert() method on these objects is used to -# force whatever value is assigned to the parameter to the appropriate -# type. -# -# Note that the default values are loaded into the class's attribute -# space when the parameter dictionary is initialized (in -# MetaSimObject._new_param()); after that point they aren't used. -# -##################################################################### - -# Dummy base class to identify types that are legitimate for SimObject -# parameters. -class ParamValue(object): - - # default for printing to .ini file is regular string conversion. - # will be overridden in some cases - def ini_str(self): - return str(self) - - # allows us to blithely call unproxy() on things without checking - # if they're really proxies or not - def unproxy(self, base): - return self - -# Regular parameter description. -class ParamDesc(object): - def __init__(self, ptype_str, ptype, *args, **kwargs): - self.ptype_str = ptype_str - # remember ptype only if it is provided - if ptype != None: - self.ptype = ptype - - if args: - if len(args) == 1: - self.desc = args[0] - elif len(args) == 2: - self.default = args[0] - self.desc = args[1] - else: - raise TypeError, 'too many arguments' - - if kwargs.has_key('desc'): - assert(not hasattr(self, 'desc')) - self.desc = kwargs['desc'] - del kwargs['desc'] - - if kwargs.has_key('default'): - assert(not hasattr(self, 'default')) - self.default = kwargs['default'] - del kwargs['default'] - - if kwargs: - raise TypeError, 'extra unknown kwargs %s' % kwargs - - if not hasattr(self, 'desc'): - raise TypeError, 'desc attribute missing' - - def __getattr__(self, attr): - if attr == 'ptype': - try: - ptype = eval(self.ptype_str, m5.objects.__dict__) - if not isinstance(ptype, type): - panic("Param qualifier is not a type: %s" % self.ptype) - self.ptype = ptype - return ptype - except NameError: - pass - raise AttributeError, "'%s' object has no attribute '%s'" % \ - (type(self).__name__, attr) - - def convert(self, value): - if isinstance(value, BaseProxy): - value.set_param_desc(self) - return value - if not hasattr(self, 'ptype') and isNullPointer(value): - # deferred evaluation of SimObject; continue to defer if - # we're just assigning a null pointer - return value - if isinstance(value, self.ptype): - return value - if isNullPointer(value) and issubclass(self.ptype, SimObject): - return value - return self.ptype(value) - -# Vector-valued parameter description. Just like ParamDesc, except -# that the value is a vector (list) of the specified type instead of a -# single value. - -class VectorParamValue(list): - def ini_str(self): - return ' '.join([v.ini_str() for v in self]) - - def unproxy(self, base): - return [v.unproxy(base) for v in self] - -class SimObjVector(VectorParamValue): - def print_ini(self): - for v in self: - v.print_ini() - -class VectorParamDesc(ParamDesc): - # Convert assigned value to appropriate type. If the RHS is not a - # list or tuple, it generates a single-element list. - def convert(self, value): - if isinstance(value, (list, tuple)): - # list: coerce each element into new list - tmp_list = [ ParamDesc.convert(self, v) for v in value ] - if isSimObjectSequence(tmp_list): - return SimObjVector(tmp_list) - else: - return VectorParamValue(tmp_list) - else: - # singleton: leave it be (could coerce to a single-element - # list here, but for some historical reason we don't... - return ParamDesc.convert(self, value) - - -class ParamFactory(object): - def __init__(self, param_desc_class, ptype_str = None): - self.param_desc_class = param_desc_class - self.ptype_str = ptype_str - - def __getattr__(self, attr): - if self.ptype_str: - attr = self.ptype_str + '.' + attr - return ParamFactory(self.param_desc_class, attr) - - # E.g., Param.Int(5, "number of widgets") - def __call__(self, *args, **kwargs): - caller_frame = inspect.currentframe().f_back - ptype = None - try: - ptype = eval(self.ptype_str, - caller_frame.f_globals, caller_frame.f_locals) - if not isinstance(ptype, type): - raise TypeError, \ - "Param qualifier is not a type: %s" % ptype - except NameError: - # if name isn't defined yet, assume it's a SimObject, and - # try to resolve it later - pass - return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) - -Param = ParamFactory(ParamDesc) -VectorParam = ParamFactory(VectorParamDesc) - -##################################################################### -# -# Parameter Types -# -# Though native Python types could be used to specify parameter types -# (the 'ptype' field of the Param and VectorParam classes), it's more -# flexible to define our own set of types. This gives us more control -# over how Python expressions are converted to values (via the -# __init__() constructor) and how these values are printed out (via -# the __str__() conversion method). Eventually we'll need these types -# to correspond to distinct C++ types as well. -# -##################################################################### - -# superclass for "numeric" parameter values, to emulate math -# operations in a type-safe way. e.g., a Latency times an int returns -# a new Latency object. -class NumericParamValue(ParamValue): - def __str__(self): - return str(self.value) - - def __float__(self): - return float(self.value) - - # hook for bounds checking - def _check(self): - return - - def __mul__(self, other): - newobj = self.__class__(self) - newobj.value *= other - newobj._check() - return newobj - - __rmul__ = __mul__ - - def __div__(self, other): - newobj = self.__class__(self) - newobj.value /= other - newobj._check() - return newobj - - def __sub__(self, other): - newobj = self.__class__(self) - newobj.value -= other - newobj._check() - return newobj - -class Range(ParamValue): - type = int # default; can be overridden in subclasses - def __init__(self, *args, **kwargs): - - def handle_kwargs(self, kwargs): - if 'end' in kwargs: - self.second = self.type(kwargs.pop('end')) - elif 'size' in kwargs: - self.second = self.first + self.type(kwargs.pop('size')) - 1 - else: - raise TypeError, "Either end or size must be specified" - - if len(args) == 0: - self.first = self.type(kwargs.pop('start')) - handle_kwargs(self, kwargs) - - elif len(args) == 1: - if kwargs: - self.first = self.type(args[0]) - handle_kwargs(self, kwargs) - elif isinstance(args[0], Range): - self.first = self.type(args[0].first) - self.second = self.type(args[0].second) - else: - self.first = self.type(0) - self.second = self.type(args[0]) - 1 - - elif len(args) == 2: - self.first = self.type(args[0]) - self.second = self.type(args[1]) - else: - raise TypeError, "Too many arguments specified" - - if kwargs: - raise TypeError, "too many keywords: %s" % kwargs.keys() - - def __str__(self): - return '%s:%s' % (self.first, self.second) - -# Metaclass for bounds-checked integer parameters. See CheckedInt. -class CheckedIntType(type): - def __init__(cls, name, bases, dict): - super(CheckedIntType, cls).__init__(name, bases, dict) - - # CheckedInt is an abstract base class, so we actually don't - # want to do any processing on it... the rest of this code is - # just for classes that derive from CheckedInt. - if name == 'CheckedInt': - return - - if not (hasattr(cls, 'min') and hasattr(cls, 'max')): - if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): - panic("CheckedInt subclass %s must define either\n" \ - " 'min' and 'max' or 'size' and 'unsigned'\n" \ - % name); - if cls.unsigned: - cls.min = 0 - cls.max = 2 ** cls.size - 1 - else: - cls.min = -(2 ** (cls.size - 1)) - cls.max = (2 ** (cls.size - 1)) - 1 - -# Abstract superclass for bounds-checked integer parameters. This -# class is subclassed to generate parameter classes with specific -# bounds. Initialization of the min and max bounds is done in the -# metaclass CheckedIntType.__init__. -class CheckedInt(NumericParamValue): - __metaclass__ = CheckedIntType - - def _check(self): - if not self.min <= self.value <= self.max: - raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ - (self.min, self.value, self.max) - - def __init__(self, value): - if isinstance(value, str): - self.value = toInteger(value) - elif isinstance(value, (int, long, float)): - self.value = long(value) - self._check() - -class Int(CheckedInt): size = 32; unsigned = False -class Unsigned(CheckedInt): size = 32; unsigned = True - -class Int8(CheckedInt): size = 8; unsigned = False -class UInt8(CheckedInt): size = 8; unsigned = True -class Int16(CheckedInt): size = 16; unsigned = False -class UInt16(CheckedInt): size = 16; unsigned = True -class Int32(CheckedInt): size = 32; unsigned = False -class UInt32(CheckedInt): size = 32; unsigned = True -class Int64(CheckedInt): size = 64; unsigned = False -class UInt64(CheckedInt): size = 64; unsigned = True - -class Counter(CheckedInt): size = 64; unsigned = True -class Tick(CheckedInt): size = 64; unsigned = True -class TcpPort(CheckedInt): size = 16; unsigned = True -class UdpPort(CheckedInt): size = 16; unsigned = True - -class Percent(CheckedInt): min = 0; max = 100 - -class Float(ParamValue, float): - pass - -class MemorySize(CheckedInt): - size = 64 - unsigned = True - def __init__(self, value): - if isinstance(value, MemorySize): - self.value = value.value - else: - self.value = toMemorySize(value) - self._check() - -class MemorySize32(CheckedInt): - size = 32 - unsigned = True - def __init__(self, value): - if isinstance(value, MemorySize): - self.value = value.value - else: - self.value = toMemorySize(value) - self._check() - -class Addr(CheckedInt): - size = 64 - unsigned = True - def __init__(self, value): - if isinstance(value, Addr): - self.value = value.value - else: - try: - self.value = toMemorySize(value) - except TypeError: - self.value = long(value) - self._check() - -class AddrRange(Range): - type = Addr - -# String-valued parameter. Just mixin the ParamValue class -# with the built-in str class. -class String(ParamValue,str): - pass - -# Boolean parameter type. Python doesn't let you subclass bool, since -# it doesn't want to let you create multiple instances of True and -# False. Thus this is a little more complicated than String. -class Bool(ParamValue): - def __init__(self, value): - try: - self.value = toBool(value) - except TypeError: - self.value = bool(value) - - def __str__(self): - return str(self.value) - - def ini_str(self): - if self.value: - return 'true' - return 'false' - -def IncEthernetAddr(addr, val = 1): - bytes = map(lambda x: int(x, 16), addr.split(':')) - bytes[5] += val - for i in (5, 4, 3, 2, 1): - val,rem = divmod(bytes[i], 256) - bytes[i] = rem - if val == 0: - break - bytes[i - 1] += val - assert(bytes[0] <= 255) - return ':'.join(map(lambda x: '%02x' % x, bytes)) - -class NextEthernetAddr(object): - addr = "00:90:00:00:00:01" - - def __init__(self, inc = 1): - self.value = NextEthernetAddr.addr - NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc) - -class EthernetAddr(ParamValue): - def __init__(self, value): - if value == NextEthernetAddr: - self.value = value - return - - if not isinstance(value, str): - raise TypeError, "expected an ethernet address and didn't get one" - - bytes = value.split(':') - if len(bytes) != 6: - raise TypeError, 'invalid ethernet address %s' % value - - for byte in bytes: - if not 0 <= int(byte) <= 256: - raise TypeError, 'invalid ethernet address %s' % value - - self.value = value - - def unproxy(self, base): - if self.value == NextEthernetAddr: - self.addr = self.value().value - return self - - def __str__(self): - if self.value == NextEthernetAddr: - if hasattr(self, 'addr'): - return self.addr - else: - return "NextEthernetAddr (unresolved)" - else: - return self.value - -# Special class for NULL pointers. Note the special check in -# make_param_value() above that lets these be assigned where a -# SimObject is required. -# only one copy of a particular node -class NullSimObject(object): - __metaclass__ = Singleton - - def __call__(cls): - return cls - - def _instantiate(self, parent = None, path = ''): - pass - - def ini_str(self): - return 'Null' - - def unproxy(self, base): - return self - - def set_path(self, parent, name): - pass - def __str__(self): - return 'Null' - -# The only instance you'll ever need... -Null = NULL = NullSimObject() - -# Enumerated types are a little more complex. The user specifies the -# type as Enum(foo) where foo is either a list or dictionary of -# alternatives (typically strings, but not necessarily so). (In the -# long run, the integer value of the parameter will be the list index -# or the corresponding dictionary value. For now, since we only check -# that the alternative is valid and then spit it into a .ini file, -# there's not much point in using the dictionary.) - -# What Enum() must do is generate a new type encapsulating the -# provided list/dictionary so that specific values of the parameter -# can be instances of that type. We define two hidden internal -# classes (_ListEnum and _DictEnum) to serve as base classes, then -# derive the new type from the appropriate base class on the fly. - - -# Metaclass for Enum types -class MetaEnum(type): - def __init__(cls, name, bases, init_dict): - if init_dict.has_key('map'): - if not isinstance(cls.map, dict): - raise TypeError, "Enum-derived class attribute 'map' " \ - "must be of type dict" - # build list of value strings from map - cls.vals = cls.map.keys() - cls.vals.sort() - elif init_dict.has_key('vals'): - if not isinstance(cls.vals, list): - raise TypeError, "Enum-derived class attribute 'vals' " \ - "must be of type list" - # build string->value map from vals sequence - cls.map = {} - for idx,val in enumerate(cls.vals): - cls.map[val] = idx - else: - raise TypeError, "Enum-derived class must define "\ - "attribute 'map' or 'vals'" - - super(MetaEnum, cls).__init__(name, bases, init_dict) - - def cpp_declare(cls): - s = 'enum %s {\n ' % cls.__name__ - s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) - s += '\n};\n' - return s - -# Base class for enum types. -class Enum(ParamValue): - __metaclass__ = MetaEnum - vals = [] - - def __init__(self, value): - if value not in self.map: - raise TypeError, "Enum param got bad value '%s' (not in %s)" \ - % (value, self.vals) - self.value = value - - def __str__(self): - return self.value - -ticks_per_sec = None - -# how big does a rounding error need to be before we warn about it? -frequency_tolerance = 0.001 # 0.1% - -# convert a floting-point # of ticks to integer, and warn if rounding -# discards too much precision -def tick_check(float_ticks): - if float_ticks == 0: - return 0 - int_ticks = int(round(float_ticks)) - err = (float_ticks - int_ticks) / float_ticks - if err > frequency_tolerance: - print >> sys.stderr, "Warning: rounding error > tolerance" - print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks) - #raise ValueError - return int_ticks - -def getLatency(value): - if isinstance(value, Latency) or isinstance(value, Clock): - return value.value - elif isinstance(value, Frequency) or isinstance(value, RootClock): - return 1 / value.value - elif isinstance(value, str): - try: - return toLatency(value) - except ValueError: - try: - return 1 / toFrequency(value) - except ValueError: - pass # fall through - raise ValueError, "Invalid Frequency/Latency value '%s'" % value - - -class Latency(NumericParamValue): - def __init__(self, value): - self.value = getLatency(value) - - def __getattr__(self, attr): - if attr in ('latency', 'period'): - return self - if attr == 'frequency': - return Frequency(self) - raise AttributeError, "Latency object has no attribute '%s'" % attr - - # convert latency to ticks - def ini_str(self): - return str(tick_check(self.value * ticks_per_sec)) - -class Frequency(NumericParamValue): - def __init__(self, value): - self.value = 1 / getLatency(value) - - def __getattr__(self, attr): - if attr == 'frequency': - return self - if attr in ('latency', 'period'): - return Latency(self) - raise AttributeError, "Frequency object has no attribute '%s'" % attr - - # convert frequency to ticks per period - def ini_str(self): - return self.period.ini_str() - -# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz). -# We can't inherit from Frequency because we don't want it to be directly -# assignable to a regular Frequency parameter. -class RootClock(ParamValue): - def __init__(self, value): - self.value = 1 / getLatency(value) - - def __getattr__(self, attr): - if attr == 'frequency': - return Frequency(self) - if attr in ('latency', 'period'): - return Latency(self) - raise AttributeError, "Frequency object has no attribute '%s'" % attr - - def ini_str(self): - return str(tick_check(self.value)) - -# A generic frequency and/or Latency value. Value is stored as a latency, -# but to avoid ambiguity this object does not support numeric ops (* or /). -# An explicit conversion to a Latency or Frequency must be made first. -class Clock(ParamValue): - def __init__(self, value): - self.value = getLatency(value) - - def __getattr__(self, attr): - if attr == 'frequency': - return Frequency(self) - if attr in ('latency', 'period'): - return Latency(self) - raise AttributeError, "Frequency object has no attribute '%s'" % attr - - def ini_str(self): - return self.period.ini_str() - -class NetworkBandwidth(float,ParamValue): - def __new__(cls, value): - val = toNetworkBandwidth(value) / 8.0 - return super(cls, NetworkBandwidth).__new__(cls, val) - - def __str__(self): - return str(self.val) - - def ini_str(self): - return '%f' % (ticks_per_sec / float(self)) - -class MemoryBandwidth(float,ParamValue): - def __new__(self, value): - val = toMemoryBandwidth(value) - return super(cls, MemoryBandwidth).__new__(cls, val) - - def __str__(self): - return str(self.val) - - def ini_str(self): - return '%f' % (ticks_per_sec / float(self)) - -# -# "Constants"... handy aliases for various values. -# - -# Some memory range specifications use this as a default upper bound. -MaxAddr = Addr.max -MaxTick = Tick.max -AllMemory = AddrRange(0, MaxAddr) - - -##################################################################### -# -# Port objects -# -# Ports are used to interconnect objects in the memory system. -# -##################################################################### - -# Port reference: encapsulates a reference to a particular port on a -# particular SimObject. -class PortRef(object): - def __init__(self, simobj, name, isVec): - assert(isSimObject(simobj)) - self.simobj = simobj - self.name = name - self.index = -1 - self.isVec = isVec # is this a vector port? - self.peer = None # not associated with another port yet - self.ccConnected = False # C++ port connection done? - - # Set peer port reference. Called via __setattr__ as a result of - # a port assignment, e.g., "obj1.port1 = obj2.port2". - def setPeer(self, other): - if self.isVec: - curMap = self.simobj._port_map.get(self.name, []) - self.index = len(curMap) - curMap.append(other) - else: - curMap = self.simobj._port_map.get(self.name) - if curMap and not self.isVec: - print "warning: overwriting port", self.simobj, self.name - curMap = other - self.simobj._port_map[self.name] = curMap - self.peer = other - - def clone(self, memo): - newRef = copy.copy(self) - assert(isSimObject(newRef.simobj)) - newRef.simobj = newRef.simobj(_memo=memo) - # Tricky: if I'm the *second* PortRef in the pair to be - # cloned, then my peer is still in the middle of its clone - # method, and thus hasn't returned to its owner's - # SimObject.__init__ to get installed in _port_map. As a - # result I have no way of finding the *new* peer object. So I - # mark myself as "waiting" for my peer, and I let the *first* - # PortRef clone call set up both peer pointers after I return. - newPeer = newRef.simobj._port_map.get(self.name) - if newPeer: - if self.isVec: - assert(self.index != -1) - newPeer = newPeer[self.index] - # other guy is all set up except for his peer pointer - assert(newPeer.peer == -1) # peer must be waiting for handshake - newPeer.peer = newRef - newRef.peer = newPeer - else: - # other guy is in clone; just wait for him to do the work - newRef.peer = -1 # mark as waiting for handshake - return newRef - - # Call C++ to create corresponding port connection between C++ objects - def ccConnect(self): - if self.ccConnected: # already done this - return - peer = self.peer - cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index, - peer.simobj.getCCObject(), peer.name, peer.index) - self.ccConnected = True - peer.ccConnected = True - -# Port description object. Like a ParamDesc object, this represents a -# logical port in the SimObject class, not a particular port on a -# SimObject instance. The latter are represented by PortRef objects. -class Port(object): - def __init__(self, desc): - self.desc = desc - self.isVec = False - - # Generate a PortRef for this port on the given SimObject with the - # given name - def makeRef(self, simobj, name): - return PortRef(simobj, name, self.isVec) - - # Connect an instance of this port (on the given SimObject with - # the given name) with the port described by the supplied PortRef - def connect(self, simobj, name, ref): - if not isinstance(ref, PortRef): - raise TypeError, \ - "assigning non-port reference port '%s'" % name - myRef = self.makeRef(simobj, name) - myRef.setPeer(ref) - ref.setPeer(myRef) - -# VectorPort description object. Like Port, but represents a vector -# of connections (e.g., as on a Bus). -class VectorPort(Port): - def __init__(self, desc): - Port.__init__(self, desc) - self.isVec = True - -##################################################################### - -# __all__ defines the list of symbols that get exported when -# 'from config import *' is invoked. Try to keep this reasonably -# short to avoid polluting other namespaces. -__all__ = ['SimObject', 'ParamContext', 'Param', 'VectorParam', - 'Parent', 'Self', - 'Enum', 'Bool', 'String', 'Float', - 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', - 'Int32', 'UInt32', 'Int64', 'UInt64', - 'Counter', 'Addr', 'Tick', 'Percent', - 'TcpPort', 'UdpPort', 'EthernetAddr', - 'MemorySize', 'MemorySize32', - 'Latency', 'Frequency', 'RootClock', 'Clock', - 'NetworkBandwidth', 'MemoryBandwidth', - 'Range', 'AddrRange', 'MaxAddr', 'MaxTick', 'AllMemory', - 'Null', 'NULL', - 'NextEthernetAddr', - 'Port', 'VectorPort'] - diff --git a/src/python/m5/multidict.py b/src/python/m5/multidict.py index 34fc3139b..b5cd700ef 100644 --- a/src/python/m5/multidict.py +++ b/src/python/m5/multidict.py @@ -29,7 +29,6 @@ __all__ = [ 'multidict' ] class multidict(object): - __nodefault = object() def __init__(self, parent = {}, **kwargs): self.local = dict(**kwargs) self.parent = parent @@ -102,14 +101,11 @@ class multidict(object): def values(self): return [ value for key,value in self.next() ] - def get(self, key, default=__nodefault): + def get(self, key, default=None): try: return self[key] except KeyError, e: - if default != self.__nodefault: - return default - else: - raise KeyError, e + return default def setdefault(self, key, default): try: diff --git a/src/python/m5/objects/AlphaConsole.py b/src/python/m5/objects/AlphaConsole.py index 329b8c5bd..1c71493b1 100644 --- a/src/python/m5/objects/AlphaConsole.py +++ b/src/python/m5/objects/AlphaConsole.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.params import * +from m5.proxy import * from Device import BasicPioDevice class AlphaConsole(BasicPioDevice): diff --git a/src/python/m5/objects/AlphaTLB.py b/src/python/m5/objects/AlphaTLB.py index 11c1792ee..af7c04a84 100644 --- a/src/python/m5/objects/AlphaTLB.py +++ b/src/python/m5/objects/AlphaTLB.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * class AlphaTLB(SimObject): type = 'AlphaTLB' abstract = True diff --git a/src/python/m5/objects/BadDevice.py b/src/python/m5/objects/BadDevice.py index 186b733fa..919623887 100644 --- a/src/python/m5/objects/BadDevice.py +++ b/src/python/m5/objects/BadDevice.py @@ -1,4 +1,4 @@ -from m5.config import * +from m5.params import * from Device import BasicPioDevice class BadDevice(BasicPioDevice): diff --git a/src/python/m5/objects/BaseCPU.py b/src/python/m5/objects/BaseCPU.py index 41e90b12b..3dd0bda01 100644 --- a/src/python/m5/objects/BaseCPU.py +++ b/src/python/m5/objects/BaseCPU.py @@ -1,5 +1,7 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * from m5 import build_env -from m5.config import * from AlphaTLB import AlphaDTB, AlphaITB from Bus import Bus diff --git a/src/python/m5/objects/BaseCache.py b/src/python/m5/objects/BaseCache.py index 497b2b038..db58a177f 100644 --- a/src/python/m5/objects/BaseCache.py +++ b/src/python/m5/objects/BaseCache.py @@ -1,4 +1,4 @@ -from m5.config import * +from m5.params import * from MemObject import MemObject class Prefetch(Enum): vals = ['none', 'tagged', 'stride', 'ghb'] diff --git a/src/python/m5/objects/Bridge.py b/src/python/m5/objects/Bridge.py index c9e673afb..ee8e76bff 100644 --- a/src/python/m5/objects/Bridge.py +++ b/src/python/m5/objects/Bridge.py @@ -1,4 +1,4 @@ -from m5.config import * +from m5.params import * from MemObject import MemObject class Bridge(MemObject): diff --git a/src/python/m5/objects/Bus.py b/src/python/m5/objects/Bus.py index e0278e6c3..f6828a0d5 100644 --- a/src/python/m5/objects/Bus.py +++ b/src/python/m5/objects/Bus.py @@ -1,4 +1,4 @@ -from m5.config import * +from m5.params import * from MemObject import MemObject class Bus(MemObject): diff --git a/src/python/m5/objects/CoherenceProtocol.py b/src/python/m5/objects/CoherenceProtocol.py index 64b6cbacf..82adb6862 100644 --- a/src/python/m5/objects/CoherenceProtocol.py +++ b/src/python/m5/objects/CoherenceProtocol.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * class Coherence(Enum): vals = ['uni', 'msi', 'mesi', 'mosi', 'moesi'] class CoherenceProtocol(SimObject): diff --git a/src/python/m5/objects/Device.py b/src/python/m5/objects/Device.py index f72c8e73f..4672d1065 100644 --- a/src/python/m5/objects/Device.py +++ b/src/python/m5/objects/Device.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.params import * +from m5.proxy import * from MemObject import MemObject class PioDevice(MemObject): @@ -17,4 +18,4 @@ class BasicPioDevice(PioDevice): class DmaDevice(PioDevice): type = 'DmaDevice' abstract = True - dma = Port("DMA port") + dma = Port(Self.pio.peerObj.port, "DMA port") diff --git a/src/python/m5/objects/DiskImage.py b/src/python/m5/objects/DiskImage.py index a98b35a4f..d0ada7ee1 100644 --- a/src/python/m5/objects/DiskImage.py +++ b/src/python/m5/objects/DiskImage.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * class DiskImage(SimObject): type = 'DiskImage' abstract = True diff --git a/src/python/m5/objects/Ethernet.py b/src/python/m5/objects/Ethernet.py index fb641bf80..609a3dd6f 100644 --- a/src/python/m5/objects/Ethernet.py +++ b/src/python/m5/objects/Ethernet.py @@ -1,5 +1,7 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * from m5 import build_env -from m5.config import * from Device import DmaDevice from Pci import PciDevice, PciConfigData diff --git a/src/python/m5/objects/FUPool.py b/src/python/m5/objects/FUPool.py index cbf1089cf..4b4be79a6 100644 --- a/src/python/m5/objects/FUPool.py +++ b/src/python/m5/objects/FUPool.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * class FUPool(SimObject): type = 'FUPool' diff --git a/src/python/m5/objects/FuncUnit.py b/src/python/m5/objects/FuncUnit.py index f61590ae9..f0ad55f7a 100644 --- a/src/python/m5/objects/FuncUnit.py +++ b/src/python/m5/objects/FuncUnit.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * class OpType(Enum): vals = ['(null)', 'IntAlu', 'IntMult', 'IntDiv', 'FloatAdd', diff --git a/src/python/m5/objects/Ide.py b/src/python/m5/objects/Ide.py index a8bd4ac5a..69681bdbd 100644 --- a/src/python/m5/objects/Ide.py +++ b/src/python/m5/objects/Ide.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * from Pci import PciDevice, PciConfigData class IdeID(Enum): vals = ['master', 'slave'] diff --git a/src/python/m5/objects/IntrControl.py b/src/python/m5/objects/IntrControl.py index 514c3fc62..95be0f4df 100644 --- a/src/python/m5/objects/IntrControl.py +++ b/src/python/m5/objects/IntrControl.py @@ -1,4 +1,6 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * class IntrControl(SimObject): type = 'IntrControl' cpu = Param.BaseCPU(Parent.any, "the cpu") diff --git a/src/python/m5/objects/MemObject.py b/src/python/m5/objects/MemObject.py index d957dae17..8982d553d 100644 --- a/src/python/m5/objects/MemObject.py +++ b/src/python/m5/objects/MemObject.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.SimObject import SimObject class MemObject(SimObject): type = 'MemObject' diff --git a/src/python/m5/objects/MemTest.py b/src/python/m5/objects/MemTest.py index 9916d7cb4..97600768f 100644 --- a/src/python/m5/objects/MemTest.py +++ b/src/python/m5/objects/MemTest.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * class MemTest(SimObject): type = 'MemTest' cache = Param.BaseCache("L1 cache") diff --git a/src/python/m5/objects/O3CPU.py b/src/python/m5/objects/O3CPU.py index 900bbf28c..5100c7ccb 100644 --- a/src/python/m5/objects/O3CPU.py +++ b/src/python/m5/objects/O3CPU.py @@ -1,5 +1,6 @@ +from m5.params import * +from m5.proxy import * from m5 import build_env -from m5.config import * from BaseCPU import BaseCPU from Checker import O3Checker diff --git a/src/python/m5/objects/OzoneCPU.py b/src/python/m5/objects/OzoneCPU.py index 88fb63c74..8f25d77ed 100644 --- a/src/python/m5/objects/OzoneCPU.py +++ b/src/python/m5/objects/OzoneCPU.py @@ -1,5 +1,5 @@ +from m5.params import * from m5 import build_env -from m5.config import * from BaseCPU import BaseCPU class DerivOzoneCPU(BaseCPU): diff --git a/src/python/m5/objects/Pci.py b/src/python/m5/objects/Pci.py index cc0d1cf4a..9872532ab 100644 --- a/src/python/m5/objects/Pci.py +++ b/src/python/m5/objects/Pci.py @@ -1,4 +1,6 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * from Device import BasicPioDevice, DmaDevice, PioDevice class PciConfigData(SimObject): @@ -48,7 +50,7 @@ class PciConfigAll(PioDevice): class PciDevice(DmaDevice): type = 'PciDevice' abstract = True - config = Port("PCI configuration space port") + config = Port(Self.pio.peerObj.port, "PCI configuration space port") pci_bus = Param.Int("PCI bus") pci_dev = Param.Int("PCI device number") pci_func = Param.Int("PCI function code") diff --git a/src/python/m5/objects/PhysicalMemory.py b/src/python/m5/objects/PhysicalMemory.py index bc427aa88..dd3ffd651 100644 --- a/src/python/m5/objects/PhysicalMemory.py +++ b/src/python/m5/objects/PhysicalMemory.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.params import * +from m5.proxy import * from MemObject import * class PhysicalMemory(MemObject): diff --git a/src/python/m5/objects/Platform.py b/src/python/m5/objects/Platform.py index 89fee9991..ab2083eea 100644 --- a/src/python/m5/objects/Platform.py +++ b/src/python/m5/objects/Platform.py @@ -1,4 +1,6 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * class Platform(SimObject): type = 'Platform' abstract = True diff --git a/src/python/m5/objects/Process.py b/src/python/m5/objects/Process.py index 0b44aac8b..771ad4101 100644 --- a/src/python/m5/objects/Process.py +++ b/src/python/m5/objects/Process.py @@ -1,4 +1,6 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * class Process(SimObject): type = 'Process' abstract = True diff --git a/src/python/m5/objects/Repl.py b/src/python/m5/objects/Repl.py index 8e9f1094f..10892cf6f 100644 --- a/src/python/m5/objects/Repl.py +++ b/src/python/m5/objects/Repl.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * class Repl(SimObject): type = 'Repl' abstract = True diff --git a/src/python/m5/objects/Root.py b/src/python/m5/objects/Root.py index 33dd22620..f01fc06c4 100644 --- a/src/python/m5/objects/Root.py +++ b/src/python/m5/objects/Root.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * from Serialize import Serialize from Statistics import Statistics from Trace import Trace diff --git a/src/python/m5/objects/SimConsole.py b/src/python/m5/objects/SimConsole.py index 9e1452c6d..bdd7f246d 100644 --- a/src/python/m5/objects/SimConsole.py +++ b/src/python/m5/objects/SimConsole.py @@ -1,4 +1,6 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * class ConsoleListener(SimObject): type = 'ConsoleListener' port = Param.TcpPort(3456, "listen port") diff --git a/src/python/m5/objects/SimpleDisk.py b/src/python/m5/objects/SimpleDisk.py index 44ef709af..099a77dbb 100644 --- a/src/python/m5/objects/SimpleDisk.py +++ b/src/python/m5/objects/SimpleDisk.py @@ -1,4 +1,6 @@ -from m5.config import * +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * class SimpleDisk(SimObject): type = 'SimpleDisk' disk = Param.DiskImage("Disk Image") diff --git a/src/python/m5/objects/SimpleOzoneCPU.py b/src/python/m5/objects/SimpleOzoneCPU.py index 5d968cab0..193f31b0f 100644 --- a/src/python/m5/objects/SimpleOzoneCPU.py +++ b/src/python/m5/objects/SimpleOzoneCPU.py @@ -1,5 +1,5 @@ +from m5.params import * from m5 import build_env -from m5.config import * from BaseCPU import BaseCPU class SimpleOzoneCPU(BaseCPU): diff --git a/src/python/m5/objects/System.py b/src/python/m5/objects/System.py index 386f39277..bc2a002cb 100644 --- a/src/python/m5/objects/System.py +++ b/src/python/m5/objects/System.py @@ -1,5 +1,7 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * from m5 import build_env -from m5.config import * class MemoryMode(Enum): vals = ['invalid', 'atomic', 'timing'] diff --git a/src/python/m5/objects/Tsunami.py b/src/python/m5/objects/Tsunami.py index 0b5ff9e7d..0b53153a0 100644 --- a/src/python/m5/objects/Tsunami.py +++ b/src/python/m5/objects/Tsunami.py @@ -1,4 +1,5 @@ -from m5.config import * +from m5.params import * +from m5.proxy import * from Device import BasicPioDevice from Platform import Platform from AlphaConsole import AlphaConsole diff --git a/src/python/m5/objects/Uart.py b/src/python/m5/objects/Uart.py index 8e1fd1a37..62062c6b1 100644 --- a/src/python/m5/objects/Uart.py +++ b/src/python/m5/objects/Uart.py @@ -1,5 +1,6 @@ +from m5.params import * +from m5.proxy import * from m5 import build_env -from m5.config import * from Device import BasicPioDevice class Uart(BasicPioDevice): diff --git a/src/python/m5/params.py b/src/python/m5/params.py new file mode 100644 index 000000000..cbbd23004 --- /dev/null +++ b/src/python/m5/params.py @@ -0,0 +1,968 @@ +# Copyright (c) 2004-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: Steve Reinhardt +# Nathan Binkert + +##################################################################### +# +# Parameter description classes +# +# The _params dictionary in each class maps parameter names to either +# a Param or a VectorParam object. These objects contain the +# parameter description string, the parameter type, and the default +# value (if any). The convert() method on these objects is used to +# force whatever value is assigned to the parameter to the appropriate +# type. +# +# Note that the default values are loaded into the class's attribute +# space when the parameter dictionary is initialized (in +# MetaSimObject._new_param()); after that point they aren't used. +# +##################################################################### + +import sys, inspect, copy +import convert +from util import * + +# Dummy base class to identify types that are legitimate for SimObject +# parameters. +class ParamValue(object): + + cxx_predecls = [] + swig_predecls = [] + + # default for printing to .ini file is regular string conversion. + # will be overridden in some cases + def ini_str(self): + return str(self) + + # allows us to blithely call unproxy() on things without checking + # if they're really proxies or not + def unproxy(self, base): + return self + +# Regular parameter description. +class ParamDesc(object): + def __init__(self, ptype_str, ptype, *args, **kwargs): + self.ptype_str = ptype_str + # remember ptype only if it is provided + if ptype != None: + self.ptype = ptype + + if args: + if len(args) == 1: + self.desc = args[0] + elif len(args) == 2: + self.default = args[0] + self.desc = args[1] + else: + raise TypeError, 'too many arguments' + + if kwargs.has_key('desc'): + assert(not hasattr(self, 'desc')) + self.desc = kwargs['desc'] + del kwargs['desc'] + + if kwargs.has_key('default'): + assert(not hasattr(self, 'default')) + self.default = kwargs['default'] + del kwargs['default'] + + if kwargs: + raise TypeError, 'extra unknown kwargs %s' % kwargs + + if not hasattr(self, 'desc'): + raise TypeError, 'desc attribute missing' + + def __getattr__(self, attr): + if attr == 'ptype': + try: + ptype = eval(self.ptype_str, objects.__dict__) + if not isinstance(ptype, type): + raise NameError + self.ptype = ptype + return ptype + except NameError: + raise TypeError, \ + "Param qualifier '%s' is not a type" % self.ptype_str + raise AttributeError, "'%s' object has no attribute '%s'" % \ + (type(self).__name__, attr) + + def convert(self, value): + if isinstance(value, proxy.BaseProxy): + value.set_param_desc(self) + return value + if not hasattr(self, 'ptype') and isNullPointer(value): + # deferred evaluation of SimObject; continue to defer if + # we're just assigning a null pointer + return value + if isinstance(value, self.ptype): + return value + if isNullPointer(value) and isSimObjectClass(self.ptype): + return value + return self.ptype(value) + + def cxx_predecls(self): + return self.ptype.cxx_predecls + + def swig_predecls(self): + return self.ptype.swig_predecls + + def cxx_decl(self): + return '%s %s;' % (self.ptype.cxx_type, self.name) + +# Vector-valued parameter description. Just like ParamDesc, except +# that the value is a vector (list) of the specified type instead of a +# single value. + +class VectorParamValue(list): + def ini_str(self): + return ' '.join([v.ini_str() for v in self]) + + def unproxy(self, base): + return [v.unproxy(base) for v in self] + +class SimObjVector(VectorParamValue): + def print_ini(self): + for v in self: + v.print_ini() + +class VectorParamDesc(ParamDesc): + # Convert assigned value to appropriate type. If the RHS is not a + # list or tuple, it generates a single-element list. + def convert(self, value): + if isinstance(value, (list, tuple)): + # list: coerce each element into new list + tmp_list = [ ParamDesc.convert(self, v) for v in value ] + if isSimObjectSequence(tmp_list): + return SimObjVector(tmp_list) + else: + return VectorParamValue(tmp_list) + else: + # singleton: leave it be (could coerce to a single-element + # list here, but for some historical reason we don't... + return ParamDesc.convert(self, value) + + def cxx_predecls(self): + return ['#include <vector>'] + self.ptype.cxx_predecls + + def swig_predecls(self): + return ['%include "std_vector.i"'] + self.ptype.swig_predecls + + def cxx_decl(self): + return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) + +class ParamFactory(object): + def __init__(self, param_desc_class, ptype_str = None): + self.param_desc_class = param_desc_class + self.ptype_str = ptype_str + + def __getattr__(self, attr): + if self.ptype_str: + attr = self.ptype_str + '.' + attr + return ParamFactory(self.param_desc_class, attr) + + # E.g., Param.Int(5, "number of widgets") + def __call__(self, *args, **kwargs): + caller_frame = inspect.currentframe().f_back + ptype = None + try: + ptype = eval(self.ptype_str, + caller_frame.f_globals, caller_frame.f_locals) + if not isinstance(ptype, type): + raise TypeError, \ + "Param qualifier is not a type: %s" % ptype + except NameError: + # if name isn't defined yet, assume it's a SimObject, and + # try to resolve it later + pass + return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) + +Param = ParamFactory(ParamDesc) +VectorParam = ParamFactory(VectorParamDesc) + +##################################################################### +# +# Parameter Types +# +# Though native Python types could be used to specify parameter types +# (the 'ptype' field of the Param and VectorParam classes), it's more +# flexible to define our own set of types. This gives us more control +# over how Python expressions are converted to values (via the +# __init__() constructor) and how these values are printed out (via +# the __str__() conversion method). +# +##################################################################### + +# String-valued parameter. Just mixin the ParamValue class with the +# built-in str class. +class String(ParamValue,str): + cxx_type = 'std::string' + cxx_predecls = ['#include <string>'] + swig_predecls = ['%include "std_string.i"\n' + + '%apply const std::string& {std::string *};'] + pass + +# superclass for "numeric" parameter values, to emulate math +# operations in a type-safe way. e.g., a Latency times an int returns +# a new Latency object. +class NumericParamValue(ParamValue): + def __str__(self): + return str(self.value) + + def __float__(self): + return float(self.value) + + # hook for bounds checking + def _check(self): + return + + def __mul__(self, other): + newobj = self.__class__(self) + newobj.value *= other + newobj._check() + return newobj + + __rmul__ = __mul__ + + def __div__(self, other): + newobj = self.__class__(self) + newobj.value /= other + newobj._check() + return newobj + + def __sub__(self, other): + newobj = self.__class__(self) + newobj.value -= other + newobj._check() + return newobj + +# Metaclass for bounds-checked integer parameters. See CheckedInt. +class CheckedIntType(type): + def __init__(cls, name, bases, dict): + super(CheckedIntType, cls).__init__(name, bases, dict) + + # CheckedInt is an abstract base class, so we actually don't + # want to do any processing on it... the rest of this code is + # just for classes that derive from CheckedInt. + if name == 'CheckedInt': + return + + if not cls.cxx_predecls: + # most derived types require this, so we just do it here once + cls.cxx_predecls = ['#include "sim/host.hh"'] + + if not cls.swig_predecls: + # most derived types require this, so we just do it here once + cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + '%import "sim/host.hh"'] + + if not (hasattr(cls, 'min') and hasattr(cls, 'max')): + if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): + panic("CheckedInt subclass %s must define either\n" \ + " 'min' and 'max' or 'size' and 'unsigned'\n" \ + % name); + if cls.unsigned: + cls.min = 0 + cls.max = 2 ** cls.size - 1 + else: + cls.min = -(2 ** (cls.size - 1)) + cls.max = (2 ** (cls.size - 1)) - 1 + +# Abstract superclass for bounds-checked integer parameters. This +# class is subclassed to generate parameter classes with specific +# bounds. Initialization of the min and max bounds is done in the +# metaclass CheckedIntType.__init__. +class CheckedInt(NumericParamValue): + __metaclass__ = CheckedIntType + + def _check(self): + if not self.min <= self.value <= self.max: + raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ + (self.min, self.value, self.max) + + def __init__(self, value): + if isinstance(value, str): + self.value = convert.toInteger(value) + elif isinstance(value, (int, long, float)): + self.value = long(value) + self._check() + +class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False +class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True + +class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False +class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True +class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False +class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True +class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False +class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True +class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False +class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True + +class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True +class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True +class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True +class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True + +class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 + +class Float(ParamValue, float): + pass + +class MemorySize(CheckedInt): + cxx_type = 'uint64_t' + size = 64 + unsigned = True + def __init__(self, value): + if isinstance(value, MemorySize): + self.value = value.value + else: + self.value = convert.toMemorySize(value) + self._check() + +class MemorySize32(CheckedInt): + size = 32 + unsigned = True + def __init__(self, value): + if isinstance(value, MemorySize): + self.value = value.value + else: + self.value = convert.toMemorySize(value) + self._check() + +class Addr(CheckedInt): + cxx_type = 'Addr' + cxx_predecls = ['#include "targetarch/isa_traits.hh"'] + size = 64 + unsigned = True + def __init__(self, value): + if isinstance(value, Addr): + self.value = value.value + else: + try: + self.value = convert.toMemorySize(value) + except TypeError: + self.value = long(value) + self._check() + + +class MetaRange(type): + def __init__(cls, name, bases, dict): + super(MetaRange, cls).__init__(name, bases, dict) + if name == 'Range': + return + cls.cxx_type = 'Range< %s >' % cls.type.cxx_type + cls.cxx_predecls = \ + ['#include "base/range.hh"'] + cls.type.cxx_predecls + +class Range(ParamValue): + __metaclass__ = MetaRange + type = Int # default; can be overridden in subclasses + def __init__(self, *args, **kwargs): + def handle_kwargs(self, kwargs): + if 'end' in kwargs: + self.second = self.type(kwargs.pop('end')) + elif 'size' in kwargs: + self.second = self.first + self.type(kwargs.pop('size')) - 1 + else: + raise TypeError, "Either end or size must be specified" + + if len(args) == 0: + self.first = self.type(kwargs.pop('start')) + handle_kwargs(self, kwargs) + + elif len(args) == 1: + if kwargs: + self.first = self.type(args[0]) + handle_kwargs(self, kwargs) + elif isinstance(args[0], Range): + self.first = self.type(args[0].first) + self.second = self.type(args[0].second) + else: + self.first = self.type(0) + self.second = self.type(args[0]) - 1 + + elif len(args) == 2: + self.first = self.type(args[0]) + self.second = self.type(args[1]) + else: + raise TypeError, "Too many arguments specified" + + if kwargs: + raise TypeError, "too many keywords: %s" % kwargs.keys() + + def __str__(self): + return '%s:%s' % (self.first, self.second) + +class AddrRange(Range): + type = Addr + +class TickRange(Range): + type = Tick + +# Boolean parameter type. Python doesn't let you subclass bool, since +# it doesn't want to let you create multiple instances of True and +# False. Thus this is a little more complicated than String. +class Bool(ParamValue): + cxx_type = 'bool' + def __init__(self, value): + try: + self.value = convert.toBool(value) + except TypeError: + self.value = bool(value) + + def __str__(self): + return str(self.value) + + def ini_str(self): + if self.value: + return 'true' + return 'false' + +def IncEthernetAddr(addr, val = 1): + bytes = map(lambda x: int(x, 16), addr.split(':')) + bytes[5] += val + for i in (5, 4, 3, 2, 1): + val,rem = divmod(bytes[i], 256) + bytes[i] = rem + if val == 0: + break + bytes[i - 1] += val + assert(bytes[0] <= 255) + return ':'.join(map(lambda x: '%02x' % x, bytes)) + +class NextEthernetAddr(object): + addr = "00:90:00:00:00:01" + + def __init__(self, inc = 1): + self.value = NextEthernetAddr.addr + NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc) + +class EthernetAddr(ParamValue): + cxx_type = 'Net::EthAddr' + cxx_predecls = ['#include "base/inet.hh"'] + swig_predecls = ['class Net::EthAddr;'] + def __init__(self, value): + if value == NextEthernetAddr: + self.value = value + return + + if not isinstance(value, str): + raise TypeError, "expected an ethernet address and didn't get one" + + bytes = value.split(':') + if len(bytes) != 6: + raise TypeError, 'invalid ethernet address %s' % value + + for byte in bytes: + if not 0 <= int(byte) <= 256: + raise TypeError, 'invalid ethernet address %s' % value + + self.value = value + + def unproxy(self, base): + if self.value == NextEthernetAddr: + self.addr = self.value().value + return self + + def __str__(self): + if self.value == NextEthernetAddr: + if hasattr(self, 'addr'): + return self.addr + else: + return "NextEthernetAddr (unresolved)" + else: + return self.value + +# Enumerated types are a little more complex. The user specifies the +# type as Enum(foo) where foo is either a list or dictionary of +# alternatives (typically strings, but not necessarily so). (In the +# long run, the integer value of the parameter will be the list index +# or the corresponding dictionary value. For now, since we only check +# that the alternative is valid and then spit it into a .ini file, +# there's not much point in using the dictionary.) + +# What Enum() must do is generate a new type encapsulating the +# provided list/dictionary so that specific values of the parameter +# can be instances of that type. We define two hidden internal +# classes (_ListEnum and _DictEnum) to serve as base classes, then +# derive the new type from the appropriate base class on the fly. + + +# Metaclass for Enum types +class MetaEnum(type): + def __init__(cls, name, bases, init_dict): + if init_dict.has_key('map'): + if not isinstance(cls.map, dict): + raise TypeError, "Enum-derived class attribute 'map' " \ + "must be of type dict" + # build list of value strings from map + cls.vals = cls.map.keys() + cls.vals.sort() + elif init_dict.has_key('vals'): + if not isinstance(cls.vals, list): + raise TypeError, "Enum-derived class attribute 'vals' " \ + "must be of type list" + # build string->value map from vals sequence + cls.map = {} + for idx,val in enumerate(cls.vals): + cls.map[val] = idx + else: + raise TypeError, "Enum-derived class must define "\ + "attribute 'map' or 'vals'" + + cls.cxx_type = name + '::Enum' + + super(MetaEnum, cls).__init__(name, bases, init_dict) + + # Generate C++ class declaration for this enum type. + # Note that we wrap the enum in a class/struct to act as a namespace, + # so that the enum strings can be brief w/o worrying about collisions. + def cxx_decl(cls): + s = 'struct %s {\n enum Enum {\n ' % cls.__name__ + s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) + s += '\n };\n};\n' + return s + +# Base class for enum types. +class Enum(ParamValue): + __metaclass__ = MetaEnum + vals = [] + + def __init__(self, value): + if value not in self.map: + raise TypeError, "Enum param got bad value '%s' (not in %s)" \ + % (value, self.vals) + self.value = value + + def __str__(self): + return self.value + +ticks_per_sec = None + +# how big does a rounding error need to be before we warn about it? +frequency_tolerance = 0.001 # 0.1% + +# convert a floting-point # of ticks to integer, and warn if rounding +# discards too much precision +def tick_check(float_ticks): + if float_ticks == 0: + return 0 + int_ticks = int(round(float_ticks)) + err = (float_ticks - int_ticks) / float_ticks + if err > frequency_tolerance: + print >> sys.stderr, "Warning: rounding error > tolerance" + print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks) + #raise ValueError + return int_ticks + +def getLatency(value): + if isinstance(value, Latency) or isinstance(value, Clock): + return value.value + elif isinstance(value, Frequency) or isinstance(value, RootClock): + return 1 / value.value + elif isinstance(value, str): + try: + return convert.toLatency(value) + except ValueError: + try: + return 1 / convert.toFrequency(value) + except ValueError: + pass # fall through + raise ValueError, "Invalid Frequency/Latency value '%s'" % value + + +class Latency(NumericParamValue): + cxx_type = 'Tick' + cxx_predecls = ['#include "sim/host.hh"'] + swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + '%import "sim/host.hh"'] + def __init__(self, value): + self.value = getLatency(value) + + def __getattr__(self, attr): + if attr in ('latency', 'period'): + return self + if attr == 'frequency': + return Frequency(self) + raise AttributeError, "Latency object has no attribute '%s'" % attr + + # convert latency to ticks + def ini_str(self): + return str(tick_check(self.value * ticks_per_sec)) + +class Frequency(NumericParamValue): + cxx_type = 'Tick' + cxx_predecls = ['#include "sim/host.hh"'] + swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + '%import "sim/host.hh"'] + def __init__(self, value): + self.value = 1 / getLatency(value) + + def __getattr__(self, attr): + if attr == 'frequency': + return self + if attr in ('latency', 'period'): + return Latency(self) + raise AttributeError, "Frequency object has no attribute '%s'" % attr + + # convert frequency to ticks per period + def ini_str(self): + return self.period.ini_str() + +# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz). +# We can't inherit from Frequency because we don't want it to be directly +# assignable to a regular Frequency parameter. +class RootClock(ParamValue): + cxx_type = 'Tick' + cxx_predecls = ['#include "sim/host.hh"'] + swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + '%import "sim/host.hh"'] + def __init__(self, value): + self.value = 1 / getLatency(value) + + def __getattr__(self, attr): + if attr == 'frequency': + return Frequency(self) + if attr in ('latency', 'period'): + return Latency(self) + raise AttributeError, "Frequency object has no attribute '%s'" % attr + + def ini_str(self): + return str(tick_check(self.value)) + +# A generic frequency and/or Latency value. Value is stored as a latency, +# but to avoid ambiguity this object does not support numeric ops (* or /). +# An explicit conversion to a Latency or Frequency must be made first. +class Clock(ParamValue): + cxx_type = 'Tick' + cxx_predecls = ['#include "sim/host.hh"'] + swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + '%import "sim/host.hh"'] + def __init__(self, value): + self.value = getLatency(value) + + def __getattr__(self, attr): + if attr == 'frequency': + return Frequency(self) + if attr in ('latency', 'period'): + return Latency(self) + raise AttributeError, "Frequency object has no attribute '%s'" % attr + + def ini_str(self): + return self.period.ini_str() + +class NetworkBandwidth(float,ParamValue): + cxx_type = 'float' + def __new__(cls, value): + val = convert.toNetworkBandwidth(value) / 8.0 + return super(cls, NetworkBandwidth).__new__(cls, val) + + def __str__(self): + return str(self.val) + + def ini_str(self): + return '%f' % (ticks_per_sec / float(self)) + +class MemoryBandwidth(float,ParamValue): + cxx_type = 'float' + def __new__(self, value): + val = convert.toMemoryBandwidth(value) + return super(cls, MemoryBandwidth).__new__(cls, val) + + def __str__(self): + return str(self.val) + + def ini_str(self): + return '%f' % (ticks_per_sec / float(self)) + +# +# "Constants"... handy aliases for various values. +# + +# Special class for NULL pointers. Note the special check in +# make_param_value() above that lets these be assigned where a +# SimObject is required. +# only one copy of a particular node +class NullSimObject(object): + __metaclass__ = Singleton + + def __call__(cls): + return cls + + def _instantiate(self, parent = None, path = ''): + pass + + def ini_str(self): + return 'Null' + + def unproxy(self, base): + return self + + def set_path(self, parent, name): + pass + def __str__(self): + return 'Null' + +# The only instance you'll ever need... +NULL = NullSimObject() + +def isNullPointer(value): + return isinstance(value, NullSimObject) + +# Some memory range specifications use this as a default upper bound. +MaxAddr = Addr.max +MaxTick = Tick.max +AllMemory = AddrRange(0, MaxAddr) + + +##################################################################### +# +# Port objects +# +# Ports are used to interconnect objects in the memory system. +# +##################################################################### + +# Port reference: encapsulates a reference to a particular port on a +# particular SimObject. +class PortRef(object): + def __init__(self, simobj, name): + assert(isSimObject(simobj) or isSimObjectClass(simobj)) + self.simobj = simobj + self.name = name + self.peer = None # not associated with another port yet + self.ccConnected = False # C++ port connection done? + self.index = -1 # always -1 for non-vector ports + + def __str__(self): + return '%s.%s' % (self.simobj, self.name) + + # for config.ini, print peer's name (not ours) + def ini_str(self): + return str(self.peer) + + def __getattr__(self, attr): + if attr == 'peerObj': + # shorthand for proxies + return self.peer.simobj + raise AttributeError, "'%s' object has no attribute '%s'" % \ + (self.__class__.__name__, attr) + + # Full connection is symmetric (both ways). Called via + # SimObject.__setattr__ as a result of a port assignment, e.g., + # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, + # e.g., "obj1.portA[3] = obj2.portB". + def connect(self, other): + if isinstance(other, VectorPortRef): + # reference to plain VectorPort is implicit append + other = other._get_next() + if self.peer and not proxy.isproxy(self.peer): + print "warning: overwriting port", self, \ + "value", self.peer, "with", other + self.peer = other + if proxy.isproxy(other): + other.set_param_desc(PortParamDesc()) + elif isinstance(other, PortRef): + if other.peer is not self: + other.connect(self) + else: + raise TypeError, \ + "assigning non-port reference '%s' to port '%s'" \ + % (other, self) + + def clone(self, simobj, memo): + if memo.has_key(self): + return memo[self] + newRef = copy.copy(self) + memo[self] = newRef + newRef.simobj = simobj + assert(isSimObject(newRef.simobj)) + if self.peer and not proxy.isproxy(self.peer): + peerObj = memo[self.peer.simobj] + newRef.peer = self.peer.clone(peerObj, memo) + assert(not isinstance(newRef.peer, VectorPortRef)) + return newRef + + def unproxy(self, simobj): + assert(simobj is self.simobj) + if proxy.isproxy(self.peer): + try: + realPeer = self.peer.unproxy(self.simobj) + except: + print "Error in unproxying port '%s' of %s" % \ + (self.name, self.simobj.path()) + raise + self.connect(realPeer) + + # Call C++ to create corresponding port connection between C++ objects + def ccConnect(self): + if self.ccConnected: # already done this + return + peer = self.peer + cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index, + peer.simobj.getCCObject(), peer.name, peer.index) + self.ccConnected = True + peer.ccConnected = True + +# A reference to an individual element of a VectorPort... much like a +# PortRef, but has an index. +class VectorPortElementRef(PortRef): + def __init__(self, simobj, name, index): + PortRef.__init__(self, simobj, name) + self.index = index + + def __str__(self): + return '%s.%s[%d]' % (self.simobj, self.name, self.index) + +# A reference to a complete vector-valued port (not just a single element). +# Can be indexed to retrieve individual VectorPortElementRef instances. +class VectorPortRef(object): + def __init__(self, simobj, name): + assert(isSimObject(simobj) or isSimObjectClass(simobj)) + self.simobj = simobj + self.name = name + self.elements = [] + + def __str__(self): + return '%s.%s[:]' % (self.simobj, self.name) + + # for config.ini, print peer's name (not ours) + def ini_str(self): + return ' '.join([el.ini_str() for el in self.elements]) + + def __getitem__(self, key): + if not isinstance(key, int): + raise TypeError, "VectorPort index must be integer" + if key >= len(self.elements): + # need to extend list + ext = [VectorPortElementRef(self.simobj, self.name, i) + for i in range(len(self.elements), key+1)] + self.elements.extend(ext) + return self.elements[key] + + def _get_next(self): + return self[len(self.elements)] + + def __setitem__(self, key, value): + if not isinstance(key, int): + raise TypeError, "VectorPort index must be integer" + self[key].connect(value) + + def connect(self, other): + if isinstance(other, (list, tuple)): + # Assign list of port refs to vector port. + # For now, append them... not sure if that's the right semantics + # or if it should replace the current vector. + for ref in other: + self._get_next().connect(ref) + else: + # scalar assignment to plain VectorPort is implicit append + self._get_next().connect(other) + + def clone(self, simobj, memo): + if memo.has_key(self): + return memo[self] + newRef = copy.copy(self) + memo[self] = newRef + newRef.simobj = simobj + assert(isSimObject(newRef.simobj)) + newRef.elements = [el.clone(simobj, memo) for el in self.elements] + return newRef + + def unproxy(self, simobj): + [el.unproxy(simobj) for el in self.elements] + + def ccConnect(self): + [el.ccConnect() for el in self.elements] + +# Port description object. Like a ParamDesc object, this represents a +# logical port in the SimObject class, not a particular port on a +# SimObject instance. The latter are represented by PortRef objects. +class Port(object): + # Port("description") or Port(default, "description") + def __init__(self, *args): + if len(args) == 1: + self.desc = args[0] + elif len(args) == 2: + self.default = args[0] + self.desc = args[1] + else: + raise TypeError, 'wrong number of arguments' + # self.name is set by SimObject class on assignment + # e.g., pio_port = Port("blah") sets self.name to 'pio_port' + + # Generate a PortRef for this port on the given SimObject with the + # given name + def makeRef(self, simobj): + return PortRef(simobj, self.name) + + # Connect an instance of this port (on the given SimObject with + # the given name) with the port described by the supplied PortRef + def connect(self, simobj, ref): + self.makeRef(simobj).connect(ref) + +# VectorPort description object. Like Port, but represents a vector +# of connections (e.g., as on a Bus). +class VectorPort(Port): + def __init__(self, *args): + Port.__init__(self, *args) + self.isVec = True + + def makeRef(self, simobj): + return VectorPortRef(simobj, self.name) + +# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of +# proxy objects (via set_param_desc()) so that proxy error messages +# make sense. +class PortParamDesc(object): + __metaclass__ = Singleton + + ptype_str = 'Port' + ptype = Port + + +__all__ = ['Param', 'VectorParam', + 'Enum', 'Bool', 'String', 'Float', + 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', + 'Int32', 'UInt32', 'Int64', 'UInt64', + 'Counter', 'Addr', 'Tick', 'Percent', + 'TcpPort', 'UdpPort', 'EthernetAddr', + 'MemorySize', 'MemorySize32', + 'Latency', 'Frequency', 'RootClock', 'Clock', + 'NetworkBandwidth', 'MemoryBandwidth', + 'Range', 'AddrRange', 'TickRange', + 'MaxAddr', 'MaxTick', 'AllMemory', + 'NextEthernetAddr', 'NULL', + 'Port', 'VectorPort'] + +# see comment on imports at end of __init__.py. +from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass +import proxy +import objects +import cc_main diff --git a/src/python/m5/proxy.py b/src/python/m5/proxy.py new file mode 100644 index 000000000..7ebc0ae19 --- /dev/null +++ b/src/python/m5/proxy.py @@ -0,0 +1,206 @@ +# Copyright (c) 2004-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: Steve Reinhardt +# Nathan Binkert + +##################################################################### +# +# Proxy object support. +# +##################################################################### + +class BaseProxy(object): + def __init__(self, search_self, search_up): + self._search_self = search_self + self._search_up = search_up + self._multiplier = None + + def __str__(self): + if self._search_self and not self._search_up: + s = 'Self' + elif not self._search_self and self._search_up: + s = 'Parent' + else: + s = 'ConfusedProxy' + return s + '.' + self.path() + + def __setattr__(self, attr, value): + if not attr.startswith('_'): + raise AttributeError, \ + "cannot set attribute '%s' on proxy object" % attr + super(BaseProxy, self).__setattr__(attr, value) + + # support multiplying proxies by constants + def __mul__(self, other): + if not isinstance(other, (int, long, float)): + raise TypeError, "Proxy multiplier must be integer" + if self._multiplier == None: + self._multiplier = other + else: + # support chained multipliers + self._multiplier *= other + return self + + __rmul__ = __mul__ + + def _mulcheck(self, result): + if self._multiplier == None: + return result + return result * self._multiplier + + def unproxy(self, base): + obj = base + done = False + + if self._search_self: + result, done = self.find(obj) + + if self._search_up: + while not done: + obj = obj._parent + if not obj: + break + result, done = self.find(obj) + + if not done: + raise AttributeError, \ + "Can't resolve proxy '%s' of type '%s' from '%s'" % \ + (self.path(), self._pdesc.ptype_str, base.path()) + + if isinstance(result, BaseProxy): + if result == self: + raise RuntimeError, "Cycle in unproxy" + result = result.unproxy(obj) + + return self._mulcheck(result) + + def getindex(obj, index): + if index == None: + return obj + try: + obj = obj[index] + except TypeError: + if index != 0: + raise + # if index is 0 and item is not subscriptable, just + # use item itself (so cpu[0] works on uniprocessors) + return obj + getindex = staticmethod(getindex) + + # This method should be called once the proxy is assigned to a + # particular parameter or port to set the expected type of the + # resolved proxy + def set_param_desc(self, pdesc): + self._pdesc = pdesc + +class AttrProxy(BaseProxy): + def __init__(self, search_self, search_up, attr): + super(AttrProxy, self).__init__(search_self, search_up) + self._attr = attr + self._modifiers = [] + + def __getattr__(self, attr): + # python uses __bases__ internally for inheritance + if attr.startswith('_'): + return super(AttrProxy, self).__getattr__(self, attr) + if hasattr(self, '_pdesc'): + raise AttributeError, "Attribute reference on bound proxy" + self._modifiers.append(attr) + return self + + # support indexing on proxies (e.g., Self.cpu[0]) + def __getitem__(self, key): + if not isinstance(key, int): + raise TypeError, "Proxy object requires integer index" + self._modifiers.append(key) + return self + + def find(self, obj): + try: + val = getattr(obj, self._attr) + except: + return None, False + while isproxy(val): + val = val.unproxy(obj) + for m in self._modifiers: + if isinstance(m, str): + val = getattr(val, m) + elif isinstance(m, int): + val = val[m] + else: + assert("Item must be string or integer") + while isproxy(val): + val = val.unproxy(obj) + return val, True + + def path(self): + p = self._attr + for m in self._modifiers: + if isinstance(m, str): + p += '.%s' % m + elif isinstance(m, int): + p += '[%d]' % m + else: + assert("Item must be string or integer") + return p + +class AnyProxy(BaseProxy): + def find(self, obj): + return obj.find_any(self._pdesc.ptype) + + def path(self): + return 'any' + +def isproxy(obj): + if isinstance(obj, (BaseProxy, params.EthernetAddr)): + return True + elif isinstance(obj, (list, tuple)): + for v in obj: + if isproxy(v): + return True + return False + +class ProxyFactory(object): + def __init__(self, search_self, search_up): + self.search_self = search_self + self.search_up = search_up + + def __getattr__(self, attr): + if attr == 'any': + return AnyProxy(self.search_self, self.search_up) + else: + return AttrProxy(self.search_self, self.search_up, attr) + +# global objects for handling proxies +Parent = ProxyFactory(search_self = False, search_up = True) +Self = ProxyFactory(search_self = True, search_up = False) + +# limit exports on 'from proxy import *' +__all__ = ['Parent', 'Self'] + +# see comment on imports at end of __init__.py. +import params # for EthernetAddr diff --git a/src/python/m5/util.py b/src/python/m5/util.py new file mode 100644 index 000000000..28b8b1b94 --- /dev/null +++ b/src/python/m5/util.py @@ -0,0 +1,59 @@ +# Copyright (c) 2004-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: Steve Reinhardt +# Nathan Binkert + +############################# +# +# Utility classes & methods +# +############################# + +class Singleton(type): + def __call__(cls, *args, **kwargs): + if hasattr(cls, '_instance'): + return cls._instance + + cls._instance = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instance + +# Apply method to object. +# applyMethod(obj, 'meth', <args>) is equivalent to obj.meth(<args>) +def applyMethod(obj, meth, *args, **kwargs): + return getattr(obj, meth)(*args, **kwargs) + +# If the first argument is an (non-sequence) object, apply the named +# method with the given arguments. If the first argument is a +# sequence, apply the method to each element of the sequence (a la +# 'map'). +def applyOrMap(objOrSeq, meth, *args, **kwargs): + if not isinstance(objOrSeq, (list, tuple)): + return applyMethod(objOrSeq, meth, *args, **kwargs) + else: + return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq] + + diff --git a/src/sim/main.cc b/src/sim/main.cc index 4ea8c4138..5725897f8 100644 --- a/src/sim/main.cc +++ b/src/sim/main.cc @@ -151,8 +151,8 @@ main(int argc, char **argv) // initialize SWIG 'cc_main' module init_cc_main(); - PyRun_SimpleString("import m5"); - PyRun_SimpleString("m5.main()"); + PyRun_SimpleString("import m5.main"); + PyRun_SimpleString("m5.main.main()"); // clean up Python intepreter. Py_Finalize(); diff --git a/src/sim/pseudo_inst.cc b/src/sim/pseudo_inst.cc index fcf0b957a..bd26e9dc5 100644 --- a/src/sim/pseudo_inst.cc +++ b/src/sim/pseudo_inst.cc @@ -36,6 +36,7 @@ #include "sim/pseudo_inst.hh" #include "arch/vtophys.hh" +#include "base/annotate.hh" #include "cpu/base.hh" #include "cpu/thread_context.hh" #include "cpu/quiesce_event.hh" @@ -188,6 +189,21 @@ namespace AlphaPseudo } void + anBegin(ThreadContext *tc, uint64_t cur) + { + Annotate::annotations.add(tc->getSystemPtr(), 0, cur >> 32, cur & + 0xFFFFFFFF, 0,0); + } + + void + anWait(ThreadContext *tc, uint64_t cur, uint64_t wait) + { + Annotate::annotations.add(tc->getSystemPtr(), 0, cur >> 32, cur & + 0xFFFFFFFF, wait >> 32, wait & 0xFFFFFFFF); + } + + + void dumpresetstats(ThreadContext *tc, Tick delay, Tick period) { if (!doStatisticsInsts) diff --git a/src/sim/pseudo_inst.hh b/src/sim/pseudo_inst.hh index 4a83b93e0..da2fb4ee3 100644 --- a/src/sim/pseudo_inst.hh +++ b/src/sim/pseudo_inst.hh @@ -59,4 +59,6 @@ namespace AlphaPseudo void debugbreak(ThreadContext *tc); void switchcpu(ThreadContext *tc); void addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr); + void anBegin(ThreadContext *tc, uint64_t cur); + void anWait(ThreadContext *tc, uint64_t cur, uint64_t wait); } diff --git a/src/sim/syscall_emul.cc b/src/sim/syscall_emul.cc index 6620d95e3..fe0260223 100644 --- a/src/sim/syscall_emul.cc +++ b/src/sim/syscall_emul.cc @@ -301,6 +301,19 @@ fchownFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc) SyscallReturn +dupFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc) +{ + int fd = process->sim_fd(tc->getSyscallArg(0)); + + if (fd < 0) + return -EBADF; + + int result = dup(fd); + return (result == -1) ? -errno : process->alloc_fd(result); +} + + +SyscallReturn fcntlFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc) { diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh index a3ff006ef..69ef31421 100644 --- a/src/sim/syscall_emul.hh +++ b/src/sim/syscall_emul.hh @@ -245,6 +245,10 @@ SyscallReturn chownFunc(SyscallDesc *desc, int num, SyscallReturn fchownFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc); +/// Target dup() handler. +SyscallReturn dupFunc(SyscallDesc *desc, int num, + Process *process, ThreadContext *tc); + /// Target fnctl() handler. SyscallReturn fcntlFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc); |