#ifndef __CRAY_MAINFRAME_H__ #define __CRAY_MAINFRAME_H__ #include #include #include #include #include "utils.h" #include "cray_types.h" #include #include #include #include #include #include #include #include #include "sim_iop.h" #include "iop_iop2iop.h" #include "cray_channels.h" #include "debug_events.h" #include "config_file.h" #include "sys_task_req.h" #include "unicos_proc_list.h" #include "iop_cluster_if.h" // Cray Mainframe: this variant doesn't uses host-implemented IOPs instead of simulated ones // !!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!! // !!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!! // // To be true to the documentation, octal numbers are extensively used in this file. // Watch out for the leading 0-s! // // !!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!! // !!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!! class SimIopCluster_c; class Lock_c { public: Lock_c(std::atomic_flag &aFlag): mFlag(aFlag) { while (mFlag.test_and_set()); } ~Lock_c() { mFlag.clear(); } protected: std::atomic_flag &mFlag; }; class Terminate_x : public Generic_x { public: Terminate_x() { mErrorStrm << "*** TERMINATE ***"; } }; class InterCpuCluster_c { public: InterCpuCluster_c() : TestAndSetBlockedCnt(0), ClusterCpuCnt(0) { ClusterMutex.clear(); } void Dump(CLogger_c &aLogger, size_t aIdent=0) const { for(size_t Idx=0;Idx> Idx)) == 0) ? '0' : '1'); aLogger << setloglevel(LogLevel_Dump) << (SM[Idx] ? '0' : '1'); } aLogger << setloglevel(LogLevel_Dump) << ')' << std::endl; } CAddr_t SB[8]; CInt_t ST[8]; std::array SM; size_t TestAndSetBlockedCnt; size_t ClusterCpuCnt; //std::mutex ClusterMutex; std::atomic_flag ClusterMutex; }; class Cpu_c; class Mainframe_c; class Mainframe_c: public TimeStampGenerator_i { public: class BreakPoint_i { public: BreakPoint_i() {} virtual ~BreakPoint_i() {} virtual void Fire(Cpu_c &aCpu) = 0; virtual bool Test(const Cpu_c &aCpu, const Mainframe_c &aMainframe) = 0; }; class BreakPointBase_c: public BreakPoint_i { public: explicit BreakPointBase_c(const Configuration_c &aConfig) { /*** try { for(const auto &DataCondition: aConfig.get_child_safe("DataConditions")) { IopAddr_t Addr = FromString(DataCondition.first); IopInt_t Value = DataCondition.second.get_value(); mDataConditions.push_back(DataCondition_s(Addr,Value)); } } catch(exception &Ex) { mDataConditions.clear(); throw Ex; } ****/ mTriggerCnt = aConfig.get("TriggerCnt", 0); boost::optional AbsoluteAddr = aConfig.get_optional("AbsoluteAddr"); if (AbsoluteAddr.is_initialized()) { mAbsoluteAddr = FromString(AbsoluteAddr.get(), StringFormat_e::ProgramAddr); } mPassCnt = 0; }; virtual bool Test(const Cpu_c &aCpu, const Mainframe_c &aMainframe) override; protected: /*** struct DataCondition_s { IopAddr_t Addr; IopInt_t Value; DataCondition_s(IopAddr_t aAddr, IopInt_t aValue): Addr(aAddr), Value(aValue) {} }; vector mDataConditions; ****/ size_t mPassCnt; size_t mTriggerCnt; boost::optional mAbsoluteAddr; }; class EventFirePoint_c: public BreakPointBase_c { public: EventFirePoint_c(const Configuration_c &aConfig): BreakPointBase_c(aConfig), mEvent(aConfig.get("Event")) {} virtual void Fire(Cpu_c &aCpu) override; protected: DebugEvent_t mEvent; }; class TracePoint_c : public BreakPointBase_c { public: TracePoint_c(const Configuration_c &aConfig) : BreakPointBase_c(aConfig), mTrace(aConfig.get("Message")) {} virtual void Fire(Cpu_c &aCpu) override; protected: std::string mTrace; }; class DumpPoint_c : public BreakPointBase_c { public: DumpPoint_c(const Configuration_c &aConfig) : BreakPointBase_c(aConfig) {} virtual void Fire(Cpu_c &aCpu) override; protected: std::string mTrace; }; class CpuDumpPoint_c: public BreakPointBase_c { public: CpuDumpPoint_c(const Configuration_c &aConfig): BreakPointBase_c(aConfig) {} virtual void Fire(Cpu_c &aCpu) override; }; class TerminatePoint_c: public BreakPointBase_c { public: TerminatePoint_c(const Configuration_c &aConfig): BreakPointBase_c(aConfig), mMessage(aConfig.get("Message")) {} virtual void Fire(Cpu_c &aCpu) override; protected: std::string mMessage; }; class LogOn_c: public BreakPointBase_c { public: LogOn_c(const Configuration_c &aConfig): BreakPointBase_c(aConfig) {} virtual void Fire(Cpu_c &aCpu) override; }; class LogOff_c: public BreakPointBase_c { public: LogOff_c(const Configuration_c &aConfig): BreakPointBase_c(aConfig) {} virtual void Fire(Cpu_c &aCpu) override; }; class LogLevel_c: public BreakPointBase_c { public: LogLevel_c(const Configuration_c &aConfig): BreakPointBase_c(aConfig) { std::string Level = aConfig.get("Level"); mLogLevel = FromString_LogLevels_e(Level); } virtual void Fire(Cpu_c &aCpu) override; protected: LogLevels_e mLogLevel; }; class LogLevelPush_c: public BreakPointBase_c { public: LogLevelPush_c(const Configuration_c &aConfig): BreakPointBase_c(aConfig) { std::string Level = aConfig.get("Level"); mLogLevel = FromString_LogLevels_e(Level); } virtual void Fire(Cpu_c &aCpu) override; protected: LogLevels_e mLogLevel; }; class LogLevelPop_c: public BreakPointBase_c { public: LogLevelPop_c(const Configuration_c &aConfig): BreakPointBase_c(aConfig) {} virtual void Fire(Cpu_c &aCpu) override; }; class Dump_c : public BreakPointBase_c { public: Dump_c(const Configuration_c &aConfig) : BreakPointBase_c(aConfig) {} virtual void Fire(Cpu_c &aCpu) override; }; class History_c : public BreakPointBase_c { public: History_c(const Configuration_c &aConfig) : BreakPointBase_c(aConfig) {} virtual void Fire(Cpu_c &aCpu) override; }; typedef std::map>> BreakPoints_t; class ImageLoadFailure_x: public std::exception { public: ImageLoadFailure_x(const char *aFileName, const char *aReason) { std::stringstream Strm; Strm << "Image file: " << aFileName << " cannot be loaded due to: " << aReason; mErrorStr = Strm.str(); } ImageLoadFailure_x(const char *aFileName) { std::stringstream Strm; Strm << "Image file: " << aFileName << " cannot be loaded"; mErrorStr = Strm.str(); } virtual const char *what() const throw() override { return mErrorStr.c_str(); } protected: std::string mErrorStr; }; typedef std::unique_ptr (*CpuCreator_t)(Mainframe_c *aThis, const std::string aName, const std::string aType, const Configuration_c &aCpuConfig, const Configuration_c &aDefaultCpuConfig, BreakPoints_t &aBreakPoints, size_t aCpuId); typedef std::unique_ptr(*IopClusterCreator_t)(Mainframe_c *aThis, std::string aType, const Configuration_c &aConfig, size_t aIopId); explicit Mainframe_c(const Configuration_c &aConfig, CLogger_c &aLogger, bool aMultiThreaded, bool aDisableAutoTerminal, CpuCreator_t aCpuCreator = CreateCpu, IopClusterCreator_t aIopClusterCreator = CreateIopCluster); explicit Mainframe_c(size_t aMemSize, MachineTypes_e aMachineType); virtual ~Mainframe_c() override; size_t GetChannelCnt() const { return mChannels.size(); } typedef Channel_i *ChannelPtr_t; ChannelPtr_t &GetChannel(size_t aIdx) { CRAY_ASSERT(aIdx < mChannels.size()); return mChannels[aIdx]; } const ChannelPtr_t &GetChannel(size_t aIdx) const { CRAY_ASSERT(aIdx < mChannels.size()); return mChannels[aIdx]; } void SetChannel(size_t aIdx, Channel_i &aChannel) { CRAY_ASSERT(aIdx < mChannels.size()); if (mChannels[aIdx] != nullptr) { CRAY_ASSERT(mOwnedChannels[aIdx] != nullptr); mLogger << setloglevel(LogLevel_Warning) << "WARNING: replacing channel at channel number: " << DecPrinter(aIdx + 8) << std::endl; mOwnedChannels[aIdx] = nullptr; // Remove the possibly default channel } mChannels[aIdx] = &aChannel; RebuildTickedChannels(); } typedef IteratorWrapper_t ChannelIteratorWrapper_c; const ChannelIteratorWrapper_c GetChannels() { return ChannelIteratorWrapper_c(*this); } typedef ConstIteratorWrapper_t ConstChannelIteratorWrapper_c; const ConstChannelIteratorWrapper_c GetChannels() const { return ConstChannelIteratorWrapper_c(*this); } size_t GetCpuCnt() const { return mCpus.size(); } Cpu_c &GetCpu(size_t aIdx) { CRAY_ASSERT(aIdx < mCpus.size()); return *mCpus[aIdx]; } const Cpu_c &GetCpu(size_t aIdx) const { CRAY_ASSERT(aIdx < mCpus.size()); return *mCpus[aIdx]; } typedef IteratorWrapper_t CpuIteratorWrapper_c; const CpuIteratorWrapper_c GetCpus() { return CpuIteratorWrapper_c(*this); } typedef ConstIteratorWrapper_t ConstCpuIteratorWrapper_c; const ConstCpuIteratorWrapper_c GetCpus() const { return ConstCpuIteratorWrapper_c(*this); } size_t GetIopClusterCnt() const { return mIopClusters.size(); } IopClusterBase_i &GetIopCluster(size_t aIdx) { CRAY_ASSERT(aIdx < mIopClusters.size()); return *mIopClusters[aIdx]; } const IopClusterBase_i &GetIopCluster(size_t aIdx) const { CRAY_ASSERT(aIdx < mIopClusters.size()); return *mIopClusters[aIdx]; } typedef IteratorWrapper_t IopIteratorWrapper_c; const IopIteratorWrapper_c GetIopClusters() { return IopIteratorWrapper_c(*this); } typedef ConstIteratorWrapper_t ConstIopIteratorWrapper_c; const ConstIopIteratorWrapper_c GetIopClusters() const { return ConstIopIteratorWrapper_c(*this); } size_t GetClusterCnt() const { return mClusters.size(); } InterCpuCluster_c &GetCluster(size_t aIdx) { CRAY_ASSERT(aIdx < mClusters.size()); return *mClusters[aIdx]; } const InterCpuCluster_c &GetCluster(size_t aIdx) const { CRAY_ASSERT(aIdx < mClusters.size()); return *mClusters[aIdx]; } typedef IteratorWrapper_t ClusterIteratorWrapper_c; const ClusterIteratorWrapper_c GetClusters() { return ClusterIteratorWrapper_c(*this); } typedef ConstIteratorWrapper_t ConstClusterIteratorWrapper_c; ConstClusterIteratorWrapper_c GetClusters() const { return ConstClusterIteratorWrapper_c(*this); } std::vector &GetMemory() { return mMemory; } const std::vector &GetMemory() const { return mMemory; } size_t GetMemorySize() { return mMemory.size(); } size_t GetMemorySizeInWords() const { return mMemSizeInWords; } size_t GetMemorySizeInParcels() const { return mMemSizeInParcels; } size_t mMemSizeInWords; size_t mMemSizeInParcels; void SetLastCpuIssuedCI(size_t aCpuId) { mLastCpuIssuedCI = aCpuId; } void IoMasterClear(bool aMasterClear); void CpuMasterClear(bool aMasterClear); void DeadStart(); void ChannelTick(); virtual CInt_t GetRealTimeClock() const; void SetRealTimeClock(CInt_t aValue) { if (mUseHostRealTimeClock) { mRealTimeStart = mRealTimeTimer.elapsed().wall; mLastRealTimeReading = 0; } mRealTimeClock = aValue; } void UpdateRealTimeClock() { if (!mUseHostRealTimeClock) mRealTimeClock += mRealTimeClockIncrement; } void Dump(size_t aIdent=0) const; void DumpMemories() const; void DumpHistory(); CLogger_c &GetLogger() const { return mLogger; } virtual uint64_t GetTimeStamp() const override { return mEnableTimeStamp ? mTickCnt : 0ULL; } virtual uint64_t GetResolution() const override { return 105000000ULL; } // TODO: make this configurable const DebugEventDispatcher_c &GetEventDispatcher() const { return mEventDispatcher; } DebugEventDispatcher_c &GetEventDispatcher() { return mEventDispatcher; } void HandleDebugEvent(const DebugEvent_t &aEvent) { //std::cout << "EVENT POINTS LENGTH: " << mEventPoints.size() << std::endl; mEventPoints.Fire(aEvent); } bool IsMultiThreaded() const { return mMultiThreaded; } OsTypes_e GetOsType() const { return mOsType; } void LoadImageFile(const char *aFileName, CAddr_t aLoadAddr, size_t aPreamble = 0, size_t aImageSize = std::numeric_limits::max()); MachineTypes_e GetMachineType() const { return mMachineType; } void HandleDeadLock(uint16_t aCluster); void RouteChannelInterrupt(); // Route the channel interrupt to the appropriate CPU bool DoDisableAutoTerminal() const { return mDisableAutoTerminal; } CInt_t SimToHost(CInt_t aValue); CInt_t HostToSim(); void RegisterCommands(CommandHooks_t &aHooks); protected: MachineTypes_e mMachineType; bool mMultiThreaded; bool mDisableAutoTerminal; mutable CInt_t mLastRealTimeReading; CInt_t mDeltaClockIncrement; class MainFrameEventDispatcher_c: public DebugEventDispatcher_c { public: MainFrameEventDispatcher_c(Mainframe_c &aParent): mParent(aParent) {} virtual void Fire(const DebugEvent_t &aEvent) const override { mParent.GetLogger() << setloglevel(LogLevel_EventFire) << aEvent << std::endl; DebugEventDispatcher_c::Fire(aEvent); } protected: Mainframe_c &mParent; } mEventDispatcher; struct MemoryPoke_s { CAddr_t Addr; CInt_t Value; bool IsParcelPoke; }; std::vector mMemoryPokes; bool mMemoryPokesProcessed; std::vector> mCpus; std::vector> mIopClusters; std::vector> mIopIopChannels; size_t mLastCpuIssuedCI; // Index to the last CPU that issued a clear interrupt std::vector> mClusters; std::vector mChannels; std::vector mTickedChannels; std::vector> mOwnedChannels; std::vector mMemory; uint8_t *mFastMemory; uint8_t mStartupCpuIdx; void RebuildTickedChannels(); boost::timer::cpu_timer mRealTimeTimer; boost::timer::nanosecond_type mRealTimeStart; double mSystemClockPeriod; CInt_t mRealTimeClock; CInt_t mRealTimeClockIncrement; CInt_t mRealTimeClockChunkLimit; bool mUseHostRealTimeClock; struct WatchPoint_c { WatchPoint_c() {} explicit WatchPoint_c(const std::string &aMessage): mHitCnt(0), mMessage(aMessage) {} size_t mHitCnt; std::string mMessage; }; std::map mWatchPoints; static std::shared_ptr CreateBreakPoint(const std::string &aBreakPointType, const Configuration_c &aConfig); class EventPoints_c: public EventPoints_t { public: explicit EventPoints_c(Mainframe_c &aParent): mParent(aParent) {} protected: virtual void FireBreakPoint(BreakPoint_i *aBreakPoint) override { if (aBreakPoint->Test(mParent.GetCpu(0), mParent)) aBreakPoint->Fire(mParent.GetCpu(0)); } // TODO: which CPU to pass on to Fire??? virtual std::shared_ptr CreateBreakPoint(const std::string &aBreakPointType, const Configuration_c &aConfig) override { return Mainframe_c::CreateBreakPoint(aBreakPointType, aConfig); } Mainframe_c &mParent; }; EventPoints_c mEventPoints; bool mIoMasterClear; bool mCpuMasterClear; uint64_t mTickCnt; bool mEnableTimeStamp; OsTypes_e mOsType; void CheckWatchPointsInternal(CAddr_t aAddr, bool aWrite, CInt_t aData); void CheckWatchPoints(CAddr_t aAddr, CInt_t aData) { #ifndef CRAY_TURBO if (mProcListParser != nullptr) mProcListParser->TestMemAccess(aAddr, aData); #endif if (mWatchPoints.empty()) return; CheckWatchPointsInternal(aAddr, true, aData); } void CheckWatchPoints(CAddr_t aAddr) { if (mWatchPoints.empty()) return; CheckWatchPointsInternal(aAddr, false, 0); } std::string ReportHist() const; std::string ReportBBHist() const; public: struct ReadOutOfBoundsError_x : public Generic_x { ReadOutOfBoundsError_x() : Generic_x("Data read out of bounds") {} }; template aType MemReadNoWatchpoint(const CAddr_t aAddr) const { size_t Addr = size_t(aAddr) * 8; if (Addr >= mMemory.size()) throw ReadOutOfBoundsError_x(); return *(aType *)(&mMemory[Addr]); } // Default addresses are by QWORDs template aType MemReadNoWatchpointByType(const CAddr_t aAddr) const { size_t Addr = size_t(aAddr) * sizeof(aType); if (Addr >= mMemory.size()) throw ReadOutOfBoundsError_x(); return *(aType *)(&mMemory[Addr]); } template aType MemReadNoWatchpointByByte(const CAddr_t aAddr) const { size_t Addr = size_t(aAddr); if (Addr >= mMemory.size()) throw ReadOutOfBoundsError_x(); return *(aType *)(&mMemory[Addr]); } // template void MemWriteNoWatchpoint(const CAddr_t aAddr, aType aVal) { CheckWatchPoints(aAddr, aVal); *(aType *)(&mMemory[aAddr * 8]) = aVal; } // Default addresses are by QWORDs template void MemWriteNoWatchpointByType(const CAddr_t aAddr, aType aVal) { *(aType *)(&mMemory[aAddr * sizeof(aType)]) = aVal; } // template void MemWriteNoWatchpointByByte(const CAddr_t aAddr, aType aVal) { *(aType *)(&mMemory[size_t(aAddr)]) = aVal; } // template aType *MemAccess(const CAddr_t aAddr) { CheckWatchPoints(aAddr); return (aType *)(&mMemory[aAddr * 8]); } // Default addresses are by QWORDs template aType *MemAccessByType(const CAddr_t aAddr) { CheckWatchPoints(aAddr * sizeof(aType) / 8); return (aType *)(&mMemory[aAddr * sizeof(aType)]); } // template aType *MemAccessByByte(const CAddr_t aAddr) { CheckWatchPoints(aAddr / 8); return (aType *)(&mMemory[size_t(aAddr)]); } template aType MemRead(const CAddr_t aAddr) { CheckWatchPoints(aAddr); return *(aType *)(&mMemory[aAddr * 8]); } // Default addresses are by QWORDs template aType MemReadByType(const CAddr_t aAddr) { CheckWatchPoints(aAddr * sizeof(aType) / 8); return *(aType *)(&mMemory[aAddr * sizeof(aType)]); } template aType MemReadByByte(const CAddr_t aAddr) { CheckWatchPoints(aAddr / 8); return *(aType *)(&mMemory[size_t(aAddr)]); } // template void MemWrite(const CAddr_t aAddr, aType aVal) { CheckWatchPoints(aAddr); *(aType *)(&mMemory[aAddr * 8]) = aVal; } // Default addresses are by QWORDs // template void MemWriteByType(const CAddr_t aAddr, aType aVal) { CheckWatchPoints(aAddr * sizeof(aType) / 8); *(aType *)(&mMemory[aAddr * sizeof(aType)]) = aVal; } void MemWrite(const CAddr_t aAddr, uint8_t aVal) { CheckWatchPoints(aAddr / 8); mMemory[size_t(aAddr)] = aVal; } void MemWrite(const CAddr_t aAddr, CInt_t aVal) { CheckWatchPoints(aAddr, aVal); *(CInt_t *)(&mMemory[aAddr * 8]) = aVal; } // Default addresses are by QWORDs protected: boost::optional mMainMemoryDumpFileName; mutable CLogger_c mLogger; std::unique_ptr mProcListParser; // A function that needs to be implemented to create various CPU types inside the final applcation. // If not, then the application will not link. static std::unique_ptr CreateCpu(Mainframe_c *aThis, const std::string aName, const std::string aType, const Configuration_c &aCpuConfig, const Configuration_c &aDefaultCpuConfig, BreakPoints_t &aBreakPoints, size_t aCpuId); static std::unique_ptr CreateIopCluster(Mainframe_c *aThis, std::string aType, const Configuration_c &aConfig, size_t aIopId); }; #endif // __CRAY_MAINFRAME_H__