| /* |
| * 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. |
| */ |
| |
| #ifndef __BASE__CP_ANNOTATE_HH__ |
| #define __BASE__CP_ANNOTATE_HH__ |
| |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <unordered_map> |
| #include <vector> |
| |
| #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, Addr sm_string, |
| int32_t sm_id, int32_t flags) { return; } |
| void swSmEnd(ThreadContext *tc, Addr sm_string) { return; } |
| void swExplictBegin(ThreadContext *tc, int32_t flags, |
| Addr st_string) { return; } |
| void swAutoBegin(ThreadContext *tc, Addr next_pc) { return; } |
| void swEnd(ThreadContext *tc) { return; } |
| void swQ(ThreadContext *tc, Addr id, Addr q_string, |
| int32_t count) { return; } |
| void swDq(ThreadContext *tc, Addr id, Addr q_string, |
| int32_t count) { return; } |
| void swPq(ThreadContext *tc, Addr id, Addr q_string, |
| int32_t count) { return; } |
| void swRq(ThreadContext *tc, Addr id, Addr q_string, |
| int32_t count) { return; } |
| void swWf(ThreadContext *tc, Addr id, Addr q_string, |
| Addr sm_string, int32_t count) { return; } |
| void swWe(ThreadContext *tc, Addr id, Addr q_string, |
| Addr sm_string, int32_t count) { return; } |
| void swSq(ThreadContext *tc, Addr id, Addr q_string, |
| int32_t size, int32_t flags) { return; } |
| void swAq(ThreadContext *tc, Addr id, Addr q_string, |
| int32_t size) { return; } |
| void swLink(ThreadContext *tc, Addr lsm_string, |
| Addr lsm_id, Addr sm_string) { return; } |
| void swIdentify(ThreadContext *tc, Addr smi_string) { return; } |
| uint64_t swGetId(ThreadContext *tc) { return 0; } |
| void swSyscallLink(ThreadContext *tc, Addr lsm_string, |
| Addr sm_string) { 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<std::pair<std::string, uint64_t> > |
| { |
| |
| size_t |
| operator()(const std::pair<std::string, uint64_t>& x) const |
| { |
| return hash<std::string>()(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<const Params *>(_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<AnnotateData> 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<uint64_t> 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<int> 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<std::string, int> SCache; |
| typedef std::vector<SCache> StCache; |
| |
| // Convert sm and queue name,id into queue id |
| typedef std::pair<std::string, uint64_t> Id; |
| typedef std::unordered_map<Id, int> IdHCache; |
| typedef std::vector<IdHCache> IdCache; |
| |
| // Hold mapping of sm and queues to output python |
| typedef std::vector<std::pair<int, Id> > IdMap; |
| |
| // System pointer to name,id |
| typedef std::map<System*, std::pair<std::string, int> > NameCache; |
| |
| // array of systems each of which is a stack of running sm |
| typedef std::pair<int, uint64_t> StackId; |
| typedef std::map<StackId, std::vector<int> > 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<StackId, bool> SwExpl; |
| |
| typedef std::map<int,int> IMap; |
| // List of annotate records have not been written/completed yet |
| typedef std::list<AnnDataPtr> AnnotateList; |
| |
| // Maintain link state information |
| typedef std::map<int, int> LinkMap; |
| |
| // SC Links |
| typedef std::unordered_map<Id, AnnDataPtr> ScHCache; |
| typedef std::vector<ScHCache> ScCache; |
| |
| |
| AnnotateList data; |
| |
| // vector indexed by queueid to find current number of elements and bytes |
| std::vector<int> qSize; |
| std::vector<int32_t> 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<AnnotateList> 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<int>()); |
| //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<std::string, Loader::SymbolTable*> 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__ |
| |