/* * Copyright (c) 2014 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall * not be construed as granting a license to any other intellectual * property including but not limited to intellectual property relating * to a hardware implementation of the functionality of the software * licensed hereunder. You may use the software subject to the license * terms below provided that you ensure that this notice is replicated * unmodified and in its entirety in all distributions of the software, * modified or unmodified, in source code or in binary form. * * Copyright (c) 2006-2009 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__CP_ANNOTATE_HH__ #define __BASE__CP_ANNOTATE_HH__ #include #include #include #include #include #include #include "base/loader/symtab.hh" #include "base/trace.hh" #include "base/types.hh" #include "debug/AnnotateQ.hh" #include "config/cp_annotate.hh" #include "config/the_isa.hh" #include "sim/serialize.hh" #include "sim/system.hh" #if CP_ANNOTATE #include "params/CPA.hh" #endif class System; class ThreadContext; #if !CP_ANNOTATE class CPA { public: enum flags { FL_NONE = 0x00, FL_HW = 0x01, FL_BAD = 0x02, FL_QOPP = 0x04, FL_WAIT = 0x08, FL_LINK = 0x10, FL_RESET = 0x20 }; static CPA *cpa() { return NULL; } static bool available() { return false; } bool enabled() { return false; } void swSmBegin(ThreadContext *tc) { return; } void swSmEnd(ThreadContext *tc) { return; } void swExplictBegin(ThreadContext *tc) { return; } void swAutoBegin(ThreadContext *tc, Addr next_pc) { return; } void swEnd(ThreadContext *tc) { return; } void swQ(ThreadContext *tc) { return; } void swDq(ThreadContext *tc) { return; } void swPq(ThreadContext *tc) { return; } void swRq(ThreadContext *tc) { return; } void swWf(ThreadContext *tc) { return; } void swWe(ThreadContext *tc) { return; } void swSq(ThreadContext *tc) { return; } void swAq(ThreadContext *tc) { return; } void swLink(ThreadContext *tc) { return; } void swIdentify(ThreadContext *tc) { return; } uint64_t swGetId(ThreadContext *tc) { return 0; } void swSyscallLink(ThreadContext *tc) { return; } void hwBegin(flags f, System *sys, uint64_t frame, std::string sm, std::string st) { return; } void hwQ(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { return; } void hwDq(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { return; } void hwPq(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { return; } void hwRq(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { return; } void hwWf(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { return; } void hwWe(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { return; } }; #else /** * Provide a hash function for the CPI Id type */ namespace std { template <> struct hash > { size_t operator()(const std::pair& x) const { return hash()(x.first); } }; } class CPA : SimObject { public: typedef CPAParams Params; /** The known operations that are written to the annotation output file. */ enum ops { OP_BEGIN = 0x01, OP_WAIT_EMPTY = 0x02, OP_WAIT_FULL = 0x03, OP_QUEUE = 0x04, OP_DEQUEUE = 0x05, OP_SIZE_QUEUE = 0x08, OP_PEEK = 0x09, OP_LINK = 0x0A, OP_IDENT = 0x0B, OP_RESERVE = 0x0C }; /** Flags for the various options.*/ enum flags { /* no flags */ FL_NONE = 0x00, /* operation was done on hardware */ FL_HW = 0x01, /* operation should cause a warning when encountered */ FL_BAD = 0x02, /* Queue like a stack, not a queue */ FL_QOPP = 0x04, /* Mark HW state as waiting for some non-resource constraint * (e.g. wait because SM only starts after 10 items are queued) */ FL_WAIT = 0x08, /* operation is linking to another state machine */ FL_LINK = 0x10, /* queue should be completely cleared/reset before executing this * operation */ FL_RESET = 0x20 }; protected: const Params * params() const { return dynamic_cast(_params); } /* struct that is written to the annotation output file */ struct AnnotateData : public Serializable { Tick time; uint32_t data; uint32_t orig_data; uint16_t sm; uint16_t stq; uint8_t op; uint8_t flag; uint8_t cpu; bool dump; void serialize(CheckpointOut &cp) const override; void unserialize(CheckpointIn &cp) override; }; typedef std::shared_ptr AnnDataPtr; /* header for the annotation file */ struct AnnotateHeader { uint64_t version; uint64_t num_recs; uint64_t key_off; uint64_t idx_off; uint32_t key_len; uint32_t idx_len; }; AnnotateHeader ah; std::vector annotateIdx; // number of state machines encountered in the simulation int numSm; // number of states encountered in the simulation int numSmt; // number of states/queues for a given state machine/system respectively std::vector numSt, numQ; // number of systems in the simulation int numSys; // number of queues in the state machine int numQs; // maximum connection id assigned so far uint64_t conId; // Convert state strings into state ids typedef std::unordered_map SCache; typedef std::vector StCache; // Convert sm and queue name,id into queue id typedef std::pair Id; typedef std::unordered_map IdHCache; typedef std::vector IdCache; // Hold mapping of sm and queues to output python typedef std::vector > IdMap; // System pointer to name,id typedef std::map > NameCache; // array of systems each of which is a stack of running sm typedef std::pair StackId; typedef std::map > SmStack; // map of each context and if it's currently in explict state mode // states are not automatically updated until it leaves typedef std::map SwExpl; typedef std::map IMap; // List of annotate records have not been written/completed yet typedef std::list AnnotateList; // Maintain link state information typedef std::map LinkMap; // SC Links typedef std::unordered_map ScHCache; typedef std::vector ScCache; AnnotateList data; // vector indexed by queueid to find current number of elements and bytes std::vector qSize; std::vector qBytes; // Turn state machine string into state machine id (small int) // Used for outputting key to convert id back into string SCache smtCache; // Turn state machine id, state name into state id (small int) StCache stCache; // turn system, queue, and queue identify into qid (small int) // turn system, state, and context into state machine id (small int) IdCache qCache, smCache; //Link state machines accross system calls ScCache scLinks; // System pointer to name,id NameCache nameCache; // Stack of state machines currently nested (should unwind correctly) SmStack smStack; // Map of currently outstanding links LinkMap lnMap; // If the state machine is currently exculding automatic changes SwExpl swExpl; // Last state that a given state machine was in IMap lastState; // Hold mapping of sm and queues to output python IdMap smMap, qMap; // Items still in queue, used for sanity checking std::vector qData; void doDq(System *sys, int flags, int cpu, int sm, std::string q, int qi, int count); void doQ(System *sys, int flags, int cpu, int sm, std::string q, int qi, int count); void doSwSmEnd(System *sys, int cpuid, std::string sm, uint64_t frame); // Turn a system id, state machine string, state machine id into a small int // for annotation output int getSm(int sysi, std::string si, uint64_t id) { int smi; Id smid = Id(si, id); smi = smCache[sysi-1][smid]; if (smi == 0) { smCache[sysi-1][smid] = smi = ++numSm; assert(smi < 65535); smMap.push_back(std::make_pair(sysi, smid)); } return smi; } // Turn a state machine string, state string into a small int // for annotation output int getSt(std::string sm, std::string s) { int sti, smi; smi = smtCache[sm]; if (smi == 0) smi = smtCache[sm] = ++numSmt; while (stCache.size() < smi) { //stCache.resize(sm); stCache.push_back(SCache()); numSt.push_back(0); } //assert(stCache.size() == sm); //assert(numSt.size() == sm); sti = stCache[smi-1][s]; if (sti == 0) stCache[smi-1][s] = sti = ++numSt[smi-1]; return sti; } // Turn state machine pointer into a smal int for annotation output int getSys(System *s) { NameCache::iterator i = nameCache.find(s); if (i == nameCache.end()) { nameCache[s] = std::make_pair(s->name(), ++numSys); i = nameCache.find(s); // might need to put smstackid into map here, but perhaps not //smStack.push_back(std::vector()); //swExpl.push_back(false); numQ.push_back(0); qCache.push_back(IdHCache()); smCache.push_back(IdHCache()); scLinks.push_back(ScHCache()); } return i->second.second; } // Turn queue name, and queue context into small int for // annotation output int getQ(int sys, std::string q, uint64_t id) { int qi; Id qid = Id(q, id); qi = qCache[sys-1][qid]; if (qi == 0) { qi = qCache[sys-1][qid] = ++numQs; assert(qi < 65535); qSize.push_back(0); qBytes.push_back(0); qData.push_back(AnnotateList()); numQ[sys-1]++; qMap.push_back(std::make_pair(sys, qid)); } return qi; } void swBegin(System *sys, int cpuid, std::string st, uint64_t frame, bool expl = false, int flags = FL_NONE); AnnDataPtr add(int t, int f, int c, int sm, int stq, int32_t data=0); std::ostream *osbin; bool _enabled; /** Only allow one CPA object in a system. It doesn't make sense to have * more that one per simulation because if a part of the system was * important it would have annotations and queues, and with more than one * object none of the sanity checking for queues will work. */ static bool exists; static CPA *_cpa; std::map userApp; public: static CPA *cpa() { return _cpa; } void swSmBegin(ThreadContext *tc); void swSmEnd(ThreadContext *tc); void swExplictBegin(ThreadContext *tc); void swAutoBegin(ThreadContext *tc, Addr next_pc); void swEnd(ThreadContext *tc); void swQ(ThreadContext *tc); void swDq(ThreadContext *tc); void swPq(ThreadContext *tc); void swRq(ThreadContext *tc); void swWf(ThreadContext *tc); void swWe(ThreadContext *tc); void swSq(ThreadContext *tc); void swAq(ThreadContext *tc); void swLink(ThreadContext *tc); void swIdentify(ThreadContext *tc); uint64_t swGetId(ThreadContext *tc); void swSyscallLink(ThreadContext *tc); inline void hwBegin(flags f, System *sys, uint64_t frame, std::string sm, std::string st) { if (!enabled()) return; int sysi = getSys(sys); int smi = getSm(sysi, sm, frame); add(OP_BEGIN, FL_HW | f, 0, smi, getSt(sm, st)); if (f & FL_BAD) warn("BAD state encountered: at cycle %d: %s\n", curTick(), st); } inline void hwQ(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { if (!enabled()) return; int sysi = getSys(sys); int qi = getQ(q_sys ? getSys(q_sys) : sysi, q, qid); DPRINTFS(AnnotateQ, sys, "hwQ: %s[%#x] cur size %d %d bytes: %d adding: %d\n", q, qid, qSize[qi-1], qData[qi-1].size(), qBytes[qi-1], count); doQ(sys, FL_HW | f, 0, getSm(sysi, sm, frame), q, qi, count); } inline void hwDq(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { if (!enabled()) return; int sysi = getSys(sys); int qi = getQ(q_sys ? getSys(q_sys) : sysi, q, qid); DPRINTFS(AnnotateQ, sys, "hwDQ: %s[%#x] cur size %d %d bytes: %d removing: %d\n", q, qid, qSize[qi-1], qData[qi-1].size(), qBytes[qi-1], count); doDq(sys, FL_HW | f, 0, getSm(sysi,sm, frame), q, qi, count); } inline void hwPq(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { if (!enabled()) return; int sysi = getSys(sys); int qi = getQ(q_sys ? getSys(q_sys) : sysi, q, qid); DPRINTFS(AnnotateQ, sys, "hwPQ: %s[%#x] cur size %d %d bytes: %d peeking: %d\n", q, qid, qSize[qi-1], qData[qi-1].size(), qBytes[qi-1], count); add(OP_PEEK, FL_HW | f, 0, getSm(sysi, sm, frame), qi, count); } inline void hwRq(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { if (!enabled()) return; int sysi = getSys(sys); int qi = getQ(q_sys ? getSys(q_sys) : sysi, q, qid); DPRINTFS(AnnotateQ, sys, "hwRQ: %s[%#x] cur size %d %d bytes: %d reserving: %d\n", q, qid, qSize[qi-1], qData[qi-1].size(), qBytes[qi-1], count); add(OP_RESERVE, FL_HW | f, 0, getSm(sysi, sm, frame), qi, count); } inline void hwWf(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { if (!enabled()) return; int sysi = getSys(sys); int qi = getQ(q_sys ? getSys(q_sys) : sysi, q, qid); add(OP_WAIT_FULL, FL_HW | f, 0, getSm(sysi, sm, frame), qi, count); } inline void hwWe(flags f, System *sys, uint64_t frame, std::string sm, std::string q, uint64_t qid, System *q_sys = NULL, int32_t count = 1) { if (!enabled()) return; int sysi = getSys(sys); int qi = getQ(q_sys ? getSys(q_sys) : sysi, q, qid); add(OP_WAIT_EMPTY, FL_HW | f, 0, getSm(sysi, sm, frame), qi, count); } public: CPA(Params *p); void startup(); uint64_t getFrame(ThreadContext *tc); static bool available() { return true; } bool enabled() { if (!this) return false; return _enabled; } void dump(bool all); void dumpKey(); void serialize(CheckpointOut &cp) const override; void unserialize(CheckpointIn &cp) override; }; #endif // !CP_ANNOTATE #endif //__BASE__CP_ANNOTATE_HH__