1
0
mirror of synced 2026-01-24 19:11:21 +00:00
2020-09-09 15:11:45 -07:00

766 lines
30 KiB
C++

// !!!!!!!!!!!! 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 !!!!!!!!!!!!!!
#include <stdio.h>
#include <stdlib.h>
#include <limits>
#include <math.h>
#include <sstream>
#include <iostream>
#include <iomanip>
#include "utils.h"
#include "cray_types.h"
#include "cray_mainframe.h"
#include "cray_cpu.h"
#include "cray_softcpu.h"
#include "sys_task_req.h"
#include "iop_cluster.h"
bool Mainframe_c::BreakPointBase_c::Test(const Cpu_c &aCpu, const Mainframe_c &aMainframe) {
if (mAbsoluteAddr.is_initialized()) {
if (aCpu.GetAbsoluteProgramCounter() != mAbsoluteAddr.get()) return false;
}
/***
for(auto &DataCondition: mDataConditions) {
if (aMemory.ReadWord(DataCondition.Addr) != DataCondition.Value) {
//cout << "Breakpoint doesn't hit, memory at address " << HexPrinter(DataCondition.Addr) << " is " << HexPrinter(aMemory.ReadWord(DataCondition.Addr)) << " instead of " << HexPrinter(DataCondition.Value) << endl;
return false;
}
}
***/
++mPassCnt;
if (mTriggerCnt > mPassCnt) return false;
return true;
}
void Mainframe_c::EventFirePoint_c::Fire(Cpu_c &aCpu) {
std::stringstream CpuName;
CpuName << "CPU" << DecPrinter(aCpu.GetCpuId());
DebugEvent_t FiredEvent = Replace(mEvent, "{cpu}", CpuName.str());
aCpu.GetMainframe().GetEventDispatcher().Fire(FiredEvent);
}
void Mainframe_c::TracePoint_c::Fire(Cpu_c &aCpu) { aCpu.GetLogger() << setloglevel(LogLevel_Event) << mTrace << std::endl; }
void Mainframe_c::CpuDumpPoint_c::Fire(Cpu_c &aCpu) { aCpu.Dump(); }
void Mainframe_c::DumpPoint_c::Fire(Cpu_c &aCpu) { aCpu.GetMainframe().Dump(); aCpu.GetMainframe().DumpMemories(); }
void Mainframe_c::TerminatePoint_c::Fire(Cpu_c &aCpu) { throw Assert_x(mMessage); }
void Mainframe_c::LogOn_c::Fire(Cpu_c &aCpu) { aCpu.GetLogger().SetDisplayLogLevel(LogLevel_All); }
void Mainframe_c::LogOff_c::Fire(Cpu_c &aCpu) { aCpu.GetLogger().SetDisplayLogLevel(LogLevel_None); }
void Mainframe_c::LogLevel_c::Fire(Cpu_c &aCpu) { aCpu.GetLogger().SetDisplayLogLevel(mLogLevel); }
void Mainframe_c::LogLevelPush_c::Fire(Cpu_c &aCpu) { aCpu.GetLogger().PushDisplayLogLevel(mLogLevel); }
void Mainframe_c::LogLevelPop_c::Fire(Cpu_c &aCpu) { aCpu.GetLogger().PopDisplayLogLevel(); }
void Mainframe_c::Dump_c::Fire(Cpu_c &aCpu) { aCpu.Dump(1); }
void Mainframe_c::History_c::Fire(Cpu_c &aCpu) {
auto LogLine = aCpu.GetLogger() << setloglevel(LogLevel_Dump);
if (!LogLine.good()) {
std::cout << "History dump disabled" << std::endl;
return;
}
LogLine << "============================== HISTORY ==============================" << std::endl;
aCpu.DumpHistory();
aCpu.ClearHistory();
aCpu.GetLogger() << setloglevel(LogLevel_Dump) << "============================== END HISTORY ==========================" << std::endl;
}
// Mainframe_c
////////////////////////////////////
std::shared_ptr<Mainframe_c::BreakPoint_i> Mainframe_c::CreateBreakPoint(const std::string &aBreakPointType, const Configuration_c &aConfig) {
std::unique_ptr<BreakPoint_i> BreakPoint;
if (aBreakPointType == "LogOn") {
return std::make_shared<LogOn_c>(aConfig);
} else if (aBreakPointType == "Terminate") {
return std::make_shared<TerminatePoint_c>(aConfig);
} else if (aBreakPointType == "Dump") {
return std::make_shared<DumpPoint_c>(aConfig);
} else if (aBreakPointType == "LogOff") {
return std::make_shared<LogOff_c>(aConfig);
} else if (aBreakPointType == "LogLevel") {
return std::make_shared<LogLevel_c>(aConfig);
} else if (aBreakPointType == "LogLevelPush") {
return std::make_shared<LogLevelPush_c>(aConfig);
} else if (aBreakPointType == "LogLevelPop") {
return std::make_shared<LogLevelPop_c>(aConfig);
} else if (aBreakPointType == "Trace") {
return std::make_shared<TracePoint_c>(aConfig);
} else if (aBreakPointType == "CpuDump") {
return std::make_shared<CpuDumpPoint_c>(aConfig);
} else if (aBreakPointType == "Event") {
return std::make_shared<EventFirePoint_c>(aConfig);
} else if (aBreakPointType == "DumpCpu") {
return std::make_shared<Dump_c>(aConfig);
} else if (aBreakPointType == "History") {
return std::make_shared<History_c>(aConfig);
} else {
throw InvalidParameter_x(boost::format("Unkown channel breakpoint type:") % aBreakPointType);
}
}
std::unique_ptr<Cpu_c> Mainframe_c::CreateCpu(Mainframe_c *aThis, const std::string aName, std::string aType, const Configuration_c &aCpuConfig, const Configuration_c &aDefaultCpuConfig, BreakPoints_t &aBreakPoints, size_t aCpuId) {
if (aType == "Soft") {
return std::make_unique<SoftCpu_c>(aCpuConfig, aDefaultCpuConfig, *aThis, aBreakPoints, aCpuId);
} else {
throw InvalidParameter_x(boost::format("Invalid CPU Type specification for CPU %1%: %2%") % aName % aType);
}
}
std::unique_ptr<IopClusterBase_i> Mainframe_c::CreateIopCluster(Mainframe_c *aThis, std::string aType, const Configuration_c &aConfig, size_t aIopId) {
if (aType == "SimIopCluster") {
return std::make_unique<SimIopCluster_c>(
aConfig,
aIopId,
*aThis
);
} else if (aType == "IopDCluster") {
return std::make_unique<IopCluster_c>(
aConfig,
aThis->GetLogger(),
aIopId,
*aThis
);
} else {
throw Generic_x() << "Unkown IopCluster type: " << aType;
}
}
Mainframe_c::Mainframe_c(const Configuration_c &aConfig, CLogger_c &aLogger, bool aMultiThreaded, bool aDisableAutoTerminal, CpuCreator_t aCpuCreator, IopClusterCreator_t aIopClusterCreator) :
mLastCpuIssuedCI(0),
mRealTimeClock(0),
mTickCnt(0),
mIoMasterClear(false),
mCpuMasterClear(false),
mMemoryPokesProcessed(false),
mEventPoints(*this),
mEventDispatcher(*this),
mEnableTimeStamp(aConfig.get<bool>("EnableTimeStamp",true)),
mMultiThreaded(aMultiThreaded),
mLogger(aConfig),
mOsType(OsTypes_e::None),
mUseHostRealTimeClock(aConfig.get<bool>("UseHostRealTimeClock", true)),
mDisableAutoTerminal(aDisableAutoTerminal)
{
mLogger.SetParent(aLogger);
mChannels.resize(aConfig.get<size_t>("ChannelCount", 8));
mOwnedChannels.resize(mChannels.size());
try {
mRealTimeClockIncrement = aConfig.get<CInt_t>("RealTimeClockIncrement", 10);
std::string MachineType = aConfig.get<std::string>("MachineType", "J90");
if (MachineType == "J90") {
mMachineType = MachineTypes_e::J90;
mCpuClockPeriod = 10.0;
}
else if (MachineType == "YEL" || MachineType == "YMP-el") {
mMachineType = MachineTypes_e::YEL;
mCpuClockPeriod = 30.0;
}
else if (MachineType == "YMP") {
mMachineType = MachineTypes_e::YMP;
mCpuClockPeriod = 6.0;
}
else if (MachineType == "SV1") {
mMachineType = MachineTypes_e::SV1;
mCpuClockPeriod = 2.0;
}
else if (MachineType == "XMP1") {
mMachineType = MachineTypes_e::XMP1xx;
mCpuClockPeriod = 9.5;
}
else if (MachineType == "XMP2") {
mMachineType = MachineTypes_e::XMP2xx;
mCpuClockPeriod = 9.5;
}
else if (MachineType == "XMP4") {
mMachineType = MachineTypes_e::XMP4xx;
mCpuClockPeriod = 9.5;
}
else {
throw Generic_x() << "Unknown machine type: " << MachineType;
}
mCpuClockPeriod = aConfig.get<double>("CpuClockPeriod", mCpuClockPeriod);
if (mUseHostRealTimeClock) {
mRealTimeTimer.start();
mRealTimeStart = mRealTimeTimer.elapsed().wall;
}
// Create shared memories
mMemory.resize(aConfig.get("CpuMemorySize",8*1024*1024) * sizeof(CInt_t),0);
mMemSizeInWords = mMemory.size() / sizeof(CInt_t);
mMemSizeInParcels = mMemory.size() / sizeof(CParcel_t);
mFastMemory = &mMemory[0];
// Load all image files
for(const auto &Image : aConfig.get_child_safe("ImageFiles")) {
size_t LoadAddress = FromString<size_t>(Image.first);
std::string ImageFileName = Image.second.get_value<std::string>();
LoadImageFile(ImageFileName.c_str(),CAddr_t(LoadAddress));
}
// Create IOP Clusters
for(const auto &IopCluster : aConfig.get_child_safe("IopClusters")) {
const std::string &Type = IopCluster.first;
mIopClusters.push_back(aIopClusterCreator(this, Type, IopCluster.second, mIopClusters.size()));
}
BreakPoints_t BreakPoints;
// Load all break-points (need to happen before CPUs are created as CPUs create local copies of the BreakPoint map)
for (const auto &BreakPoint : aConfig.get_child_safe("BreakPoints")) {
std::string BreakPointType = BreakPoint.second.get<std::string>("Type");
CProgramAddr_t Addr = FromString(BreakPoint.first, StringFormat_e::ProgramAddr);
std::shared_ptr<BreakPoint_i> BreakPointObj(CreateBreakPoint(BreakPointType, BreakPoint.second));
CRAY_ASSERT(BreakPointObj != nullptr);
BreakPoints[Addr].push_back(BreakPointObj);
}
// Load all watch points
for (const auto &WatchPoint : aConfig.get_child_safe("WatchPoints")) {
std::string EventMessage = WatchPoint.second.data();
CAddr_t Addr = FromString(WatchPoint.first, StringFormat_e::DataAddr);
mWatchPoints[Addr] = WatchPoint_c(EventMessage);
}
// Load all event-points
if (aConfig.get_optional<std::string>("EventPoints").is_initialized()) {
mEventPoints.LoadEventPoints(aConfig.get_child("EventPoints"));
}
// Create CPUs
Configuration_c Cpus = aConfig.get_child_safe("Cpus");
Configuration_c DefaultCpuConfig = Cpus.get_child_safe("Default");
boost::optional<size_t> CpuCount = aConfig.get_optional<size_t>("CpuCount");
if (!CpuCount.is_initialized()) {
size_t MaxCpuId = 0;
for (const auto &Cpu : aConfig.get_child_safe("Cpus")) {
if (Cpu.first == "Default") continue;
size_t CurCpuId = FromString<size_t>(Cpu.first);
MaxCpuId = std::max(CurCpuId, MaxCpuId);
}
CpuCount = MaxCpuId;
}
for (size_t CpuIdx = 0; CpuIdx < CpuCount.get(); ++CpuIdx) {
std::stringstream CpuName;
CpuName << CpuIdx;
Configuration_c CpuConfig = Cpus.get_child_safe(CpuName.str().c_str());
std::string CpuType = CpuConfig.get<std::string>("Type", DefaultCpuConfig.get<std::string>("Type", "Soft"));
mCpus.push_back(aCpuCreator(this, CpuName.str(), CpuType, CpuConfig, DefaultCpuConfig, BreakPoints, mCpus.size()));
}
// Create clusters
size_t ClusterCnt = aConfig.get<size_t>("ClusterCount", mCpus.size() + 1);
for(size_t i=0;i<ClusterCnt;++i) mClusters.push_back(std::make_unique<InterCpuCluster_c>());
// Create channels
for(const auto &CpuChannel : aConfig.get_child_safe("CpuChannels")) {
int ChannelIdx = FromString<int>(CpuChannel.first) - 8;
if (size_t(ChannelIdx) >= mChannels.size() || ChannelIdx < 0) throw InvalidParameter_x(boost::format("Invalid cpu channel index: %1$03o") % (ChannelIdx+8));
if (mChannels[ChannelIdx] != nullptr) throw InvalidParameter_x(boost::format("Duplicate cpu channel index: %1$03o") % (ChannelIdx+8));
std::string ChannelType = CpuChannel.second.get<std::string>("Type", "Disconnected");
if (ChannelType == "Disconnected") {
mOwnedChannels[ChannelIdx] = std::make_unique<DisconnectedChannel_c>(CpuChannel.second, ChannelIdx + 8, *this, mLogger);
mChannels[ChannelIdx] = mOwnedChannels[ChannelIdx].get();
} else if (ChannelType == "Logger") {
std::string Direction = CpuChannel.second.get<std::string>("Direction", "Auto");
bool IsInput;
if (Direction == "Input") { IsInput = true; }
else if (Direction == "Output") { IsInput = false; }
else if (Direction == "Auto") { IsInput = (ChannelIdx % 2) == 0; }
else throw InvalidParameter_x(boost::format("Invalid direction for logger channel %1%: %2%") % (ChannelIdx + 8) % Direction);
mOwnedChannels[ChannelIdx] = std::make_unique<LoggerChannel_c>(CpuChannel.second, ChannelIdx + 8, *this, mLogger, IsInput);
mChannels[ChannelIdx] = mOwnedChannels[ChannelIdx].get();
}else if (ChannelType == "FileInput") {
mOwnedChannels[ChannelIdx] = std::make_unique<FileInputChannel_c>(CpuChannel.second, ChannelIdx + 8, *this, mLogger);
mChannels[ChannelIdx] = mOwnedChannels[ChannelIdx].get();
} else if (ChannelType == "FileOutput") {
mOwnedChannels[ChannelIdx] = std::make_unique<FileOutputChannel_c>(CpuChannel.second, ChannelIdx + 8, *this, mLogger);
mChannels[ChannelIdx] = mOwnedChannels[ChannelIdx].get();
} else {
throw InvalidParameter_x(boost::format("Invalid CPU channel type specification for channel %1%: %2%") % (ChannelIdx + 8) % ChannelType);
}
}
if (aConfig.get<bool>("LogUnusedChannels")) {
for (size_t Idx = 0; Idx < mOwnedChannels.size(); ++Idx) {
if (mChannels[Idx] == nullptr) {
CRAY_ASSERT(mOwnedChannels[Idx] == nullptr);
bool IsInput = (Idx % 2) == 0;
mOwnedChannels[Idx] = std::make_unique<LoggerChannel_c>(aConfig.get_child_safe("DefaultChannel"), Idx + 8, *this, mLogger, IsInput);
mChannels[Idx] = mOwnedChannels[Idx].get();
}
}
}
RebuildTickedChannels();
// Execute pokes to change memory image
for(const auto &CpuMemoryPoke: aConfig.get_child_safe("CpuMemoryPokes")) {
MemoryPoke_s Poke;
Poke.Value = FromString<CInt_t>(CpuMemoryPoke.second.data());
try {
Poke.Addr = FromString(CpuMemoryPoke.first, StringFormat_e::ProgramAddr);
if (size_t(Poke.Addr) * sizeof(CParcel_t) >= GetMemorySize()) throw InvalidParameter_x("Poke address outside of memory");
Poke.IsParcelPoke = true;
mMemoryPokes.push_back(Poke);
}
catch (StringFormatError_x &) {
Poke.Addr = FromString(CpuMemoryPoke.first, StringFormat_e::DataAddr);
if (size_t(Poke.Addr) * sizeof(CInt_t) >= GetMemorySize()) throw InvalidParameter_x("Poke address outside of memory");
Poke.IsParcelPoke = false;
mMemoryPokes.push_back(Poke);
}
}
// Set startup CPU
mStartupCpuIdx = aConfig.get<uint8_t>("StartupCpuIdx",0);
// Get memory dump file names
mMainMemoryDumpFileName = aConfig.get_optional<std::string>("MemoryDumpFile");
// Registering ourselves to receive events
GetEventDispatcher().AddHandler(*this);
// Get the OS type
std::string OsTypeString = aConfig.get<std::string>("OsType", "None");
if (OsTypeString == "None") {
mOsType = OsTypes_e::None;
} else if (OsTypeString == "COS") {
mOsType = OsTypes_e::COS;
} else if (OsTypeString == "UNICOS") {
mOsType = OsTypes_e::UNICOS;
} else {
throw InvalidParameter_x("Invalid OS type specified");
}
if (mOsType == OsTypes_e::UNICOS) {
SetupSystaskParser(aConfig);
try {
mProcListParser = std::make_unique<UnicosProcList_c>(*this, aConfig);
}
catch (std::exception &Ex) {
mLogger << setloglevel(LogLevel_Error) << "Can't create proc list parser with error: " << Ex.what() << std::endl;
} // Simply eat the exception and leave the proc-list unconstructed
}
}
catch (...) {
mCpus.clear();
mIopClusters.clear();
mIopIopChannels.clear();
mClusters.clear();
mChannels.clear();
mOwnedChannels.clear();
mMemory.clear();
throw;
}
}
Mainframe_c::Mainframe_c(size_t aMemSize, MachineTypes_e aMachineType) :
mLastCpuIssuedCI(0),
mRealTimeClock(0),
mTickCnt(0),
mIoMasterClear(false),
mCpuMasterClear(false),
mMemoryPokesProcessed(false),
mEventPoints(*this),
mEventDispatcher(*this),
mMultiThreaded(false),
mMachineType(aMachineType),
mLogger(Configuration_c::GetEmptyConfig()),
mDisableAutoTerminal(false)
{
try {
// Create shared memories
uint64_t MemorySize = aMemSize;
const size_t MaxMemSize = 128 * 1024 * 1024;
if (MemorySize > MaxMemSize) throw Generic_x("Can't allocate that much memory!");
mMemory.resize((size_t)MemorySize * sizeof(CInt_t), 0);
mMemSizeInWords = mMemory.size() / sizeof(CInt_t);
mMemSizeInParcels = mMemory.size() / sizeof(CParcel_t);
// Create a CPU
mCpus.push_back(std::make_unique<SoftCpu_c>(*this));
}
catch (...) {
mCpus.clear();
mIopClusters.clear();
mIopIopChannels.clear();
mClusters.clear();
mChannels.clear();
mOwnedChannels.clear();
mMemory.clear();
throw;
}
}
void Mainframe_c::LoadImageFile(const char *aFileName, CAddr_t aLoadAddr, size_t aPreamble, size_t aImageSize) {
CRAY_ASSERT(mMemory.size() % sizeof(CInt_t) == 0);
CAddr_t MemSize = CAddr_t(mMemory.size()/sizeof(CInt_t));
if (aLoadAddr > MemSize) throw ImageLoadFailure_x(aFileName,"load address is beyond memory size");
std::ifstream File(aFileName, std::ios_base::in | std::ios_base::binary);
if (File.fail()) throw ImageLoadFailure_x(aFileName, "can't open file");
File.seekg(aPreamble);
uintmax_t FileSize = std::min(boost::filesystem::file_size(aFileName) - aPreamble, uintmax_t(aImageSize));
//if (FileSize % sizeof(CInt_t) != 0) throw ImageLoadFailure_x(aFileName,"file size is not modulo 8");
if ((FileSize + sizeof(CInt_t) - 1) / sizeof(CInt_t) + size_t(aLoadAddr) > size_t(MemSize)) throw ImageLoadFailure_x(aFileName, "file doesn't fit in memory");
File.read((char*)(&mMemory[(size_t(aLoadAddr) * sizeof(CInt_t))]), FileSize);
size_t BytesRead = size_t(File.gcount());
if (BytesRead != FileSize || File.fail()) throw ImageLoadFailure_x(aFileName,"can't read file");
}
Mainframe_c::~Mainframe_c() {
}
void Mainframe_c::RebuildTickedChannels() {
mTickedChannels.clear();
for (auto &Channel : mChannels) {
if (Channel == nullptr) continue;
if (Channel->NeedsChannelTick()) mTickedChannels.push_back(Channel);
}
}
void Mainframe_c::IoMasterClear(bool aMasterClear) {
// Resetting the mainframe channels only
if (mIoMasterClear && !aMasterClear) {
// Resetting all IO channels
for (auto &Channel : GetChannels()) if (Channel != nullptr) Channel->MainframeSideMasterClear();
}
mIoMasterClear = aMasterClear;
}
void Mainframe_c::CpuMasterClear(bool aMasterClear) {
if (mCpuMasterClear != aMasterClear) {
if (aMasterClear) {
mLogger << setloglevel(LogLevel_IoActivity) << "setting CPU Master Clear ON" << std::endl;
for(size_t i=0;i<GetCpuCnt();++i) {
mCpus[i]->MasterClear();
}
}
else {
mLogger << setloglevel(LogLevel_IoActivity) << "setting CPU Master Clear OFF" << std::endl;
CRAY_ASSERT(mStartupCpuIdx < mCpus.size());
if (!mMemoryPokesProcessed) {
// Process memory pokes only once, when the first main CPU gets out of reset
for(size_t i=0;i<mMemoryPokes.size();++i) {
if (mMemoryPokes[i].IsParcelPoke) {
mLogger << setloglevel(LogLevel_Dump) << "Overriding address " << InstAddr(mMemoryPokes[i].Addr) << " with value " << HexPrinter(CParcel_t(mMemoryPokes[i].Value)) << std::endl;
MemWriteNoWatchpointByType<CParcel_t>(mMemoryPokes[i].Addr, SwapBytes(CParcel_t(mMemoryPokes[i].Value)));
} else {
mLogger << setloglevel(LogLevel_Dump) << "Overriding address " << Addr(mMemoryPokes[i].Addr) << " with value " << HexPrinter(mMemoryPokes[i].Value) << std::endl;
MemWriteNoWatchpointByType<CInt_t>(mMemoryPokes[i].Addr, SwapBytes(mMemoryPokes[i].Value));
}
}
mMemoryPokesProcessed = true;
}
mCpus[mStartupCpuIdx]->DeadStart();
}
}
mCpuMasterClear = aMasterClear;
}
void Mainframe_c::RegisterCommands(CommandHooks_t &aHooks) {
class ProcList_c : public CmdFactoryBase_i {
public:
explicit ProcList_c(Mainframe_c &aParent) : mParent(aParent) {}
virtual bool ParseAndExec(TokenStream_t::const_iterator aBegin, TokenStream_t::const_iterator aEnd) override {
bool DoHelp = false;
if (aBegin->mValue == "help") {
DoHelp = true;
++aBegin; if (aBegin == aEnd) return false;
}
if (aBegin->mValue != "ps") return false;
++aBegin; if (aBegin == aEnd) return false;
if (DoHelp) {
std::cout << "ps" << std::endl;
std::cout << " Lists running processes" << std::endl;
return true;
}
else {
++aBegin;
if (aBegin != aEnd && aBegin->mTokenId != TokenTypes_e::EndOfLine) return false;
// TODO: add warning about existing file
try {
if (mParent.mProcListParser != nullptr) {
std::cout << mParent.mProcListParser->Dump() << std::endl;
} else {
std::cout << "Can't do ps: did you forget to set ProcTableBase or ProcTableLength?" << std::endl;
}
}
catch (std::exception &Ex) {
std::cout << "ps failed" << std::endl;
std::cout << Ex.what() << std::endl;
}
return true;
}
}
virtual std::string GetCommandName() const override { return "ps"; }
virtual std::string GetDeviceName() const override { return "mainframe"; }
protected:
Mainframe_c &mParent;
};
class LoadUseHist_c : public CmdFactoryBase_i {
public:
explicit LoadUseHist_c(Mainframe_c &aParent) : mParent(aParent) {}
virtual bool ParseAndExec(TokenStream_t::const_iterator aBegin, TokenStream_t::const_iterator aEnd) override {
bool DoHelp = false;
if (aBegin->mValue == "help") {
DoHelp = true;
++aBegin; if (aBegin == aEnd) return false;
}
if (aBegin->mValue != "loadusehist") return false;
++aBegin; if (aBegin == aEnd) return false;
if (DoHelp) {
std::cout << "loadusehist" << std::endl;
std::cout << " Reports load-to-use histogram" << std::endl;
return true;
}
else {
++aBegin;
if (aBegin != aEnd && aBegin->mTokenId != TokenTypes_e::EndOfLine) return false;
try {
std::cout << mParent.ReportHist() << std::endl;
}
catch (std::exception &Ex) {
std::cout << "loadusehist failed" << std::endl;
std::cout << Ex.what() << std::endl;
}
return true;
}
}
virtual std::string GetCommandName() const override { return "loadusehist"; }
virtual std::string GetDeviceName() const override { return "mainframe"; }
protected:
Mainframe_c & mParent;
};
class BBHist_c : public CmdFactoryBase_i {
public:
explicit BBHist_c(Mainframe_c &aParent) : mParent(aParent) {}
virtual bool ParseAndExec(TokenStream_t::const_iterator aBegin, TokenStream_t::const_iterator aEnd) override {
bool DoHelp = false;
if (aBegin->mValue == "help") {
DoHelp = true;
++aBegin; if (aBegin == aEnd) return false;
}
if (aBegin->mValue != "bbhist") return false;
++aBegin; if (aBegin == aEnd) return false;
if (DoHelp) {
std::cout << "bbhist" << std::endl;
std::cout << " Reports basic-block histogram" << std::endl;
return true;
}
else {
++aBegin;
if (aBegin != aEnd && aBegin->mTokenId != TokenTypes_e::EndOfLine) return false;
try {
std::cout << mParent.ReportBBHist() << std::endl;
}
catch (std::exception &Ex) {
std::cout << "bbhist failed" << std::endl;
std::cout << Ex.what() << std::endl;
}
return true;
}
}
virtual std::string GetCommandName() const override { return "bbhist"; }
virtual std::string GetDeviceName() const override { return "mainframe"; }
protected:
Mainframe_c & mParent;
};
if (mOsType == OsTypes_e::UNICOS) {
aHooks.emplace_back(std::make_unique<ProcList_c>(*this));
#ifdef COLLECT_PERF
aHooks.emplace_back(std::make_unique<LoadUseHist_c>(*this));
aHooks.emplace_back(std::make_unique<BBHist_c>(*this));
#endif
}
}
std::string Mainframe_c::ReportHist() const {
std::stringstream Strm;
#ifdef COLLECT_PERF
std::array<uint64_t, Cpu_c::mHistSize> Hist;
std::fill(Hist.begin(), Hist.end(), 0);
uint64_t CycleCount = 0;
for (auto &Cpu : mCpus) {
if (!Cpu->IsInReset()) {
std::array<uint64_t, Cpu_c::mHistSize> CurrentHist = Cpu->GetLoadUseHist();
for (size_t i = 0; i < CurrentHist.size(); ++i) {
CycleCount += CurrentHist[i];
Hist[i] += CurrentHist[i];
}
}
}
for (size_t i = 0; i < Hist.size(); ++i) {
Strm << DecPrinter(i, 3) << ": " << DecPrinter(int(double(Hist[i]) / double(CycleCount) * 100.0), 3) << "%" << std::endl;
}
#else
Strm << "This build doesn't have histogram functions enabled." << std::endl;
#endif
return Strm.str();
}
std::string Mainframe_c::ReportBBHist() const {
std::stringstream Strm;
#ifdef COLLECT_PERF
std::array<uint64_t, Cpu_c::mHistSize> BBHist;
std::fill(BBHist.begin(), BBHist.end(), 0);
uint64_t JumpCount = 0;
uint64_t JumpsTaken = 0;
uint64_t JumpsNotTaken = 0;
for (auto &Cpu : mCpus) {
if (!Cpu->IsInReset()) {
std::array<uint64_t, Cpu_c::mHistSize> CurrentHist = Cpu->GetBBHist();
for (size_t i = 0; i < CurrentHist.size(); ++i) {
JumpCount += CurrentHist[i];
BBHist[i] += CurrentHist[i];
}
JumpsTaken += Cpu->GetJumpsTaken();
JumpsNotTaken += Cpu->GetJumpsNotTaken();
}
}
for (size_t i = 0; i < BBHist.size(); ++i) {
Strm << DecPrinter(i, 3) << ": " << DecPrinter(int(double(BBHist[i]) / double(JumpCount) * 100.0), 3) << "%" << std::endl;
}
Strm << std::endl;
Strm << "Jumps taken: " << DecPrinter(JumpsTaken) << " " << DecPrinter(int(double(JumpsTaken) / double(JumpCount) * 100.0), 3) << "%" << std::endl;
Strm << "Jumps not taken: " << DecPrinter(JumpsNotTaken) << " " << DecPrinter(int(double(JumpsNotTaken) / double(JumpCount) * 100.0), 3) << "%" << std::endl;
Strm << "Total jumps: " << DecPrinter(JumpsTaken + JumpsNotTaken) << " " << DecPrinter(JumpCount) << std::endl;
#else
Strm << "This build doesn't have histogram functions enabled." << std::endl;
#endif
return Strm.str();
}
void Mainframe_c::DeadStart() {
for (auto &Cpu : GetCpus()) Cpu.MasterClear();
for (auto &Channel : GetChannels()) if (Channel != nullptr) Channel->MainframeSideMasterClear();
for (auto &Iop: GetIopClusters()) Iop.MasterClear();
for (auto &Iop : GetIopClusters()) Iop.DeadStart();
}
void Mainframe_c::ChannelTick() {
++mTickCnt;
for (auto &Channel : mTickedChannels) { CRAY_ASSERT(Channel != nullptr); Channel->ChannelTick(); }
}
void Mainframe_c::RouteChannelInterrupt() {
// mLogger << setloglevel(LogLevel_IoActivity) << "**************** SETTING IOI INTERRUPT ***********" << std::endl;
// We have an external interrupt, route it to the appropriate CPU
// This procedure is detailed in HR-0097 2-15
// 1. Route the interrupt to the CPU who has the Select External Interrupt mode set
for (auto &Cpu : GetCpus()) {
if (Cpu.IsSelectedForExternalInterrupts()) {
Cpu.SetIoInterrupt();
//mLogger << setloglevel(LogLevel_Event) << "**************** SETTING IOI INTERRUPT ***********" << std::endl;
return;
}
}
// 2. If no CPU has it set, route it to the CPU stalling on a test-and-set
for (auto &Cpu : GetCpus()) {
if (Cpu.IsTsBlocked()) {
Cpu.SetIoInterrupt();
//mLogger << setloglevel(LogLevel_Event) << "**************** SETTING IOI INTERRUPT ***********" << std::endl;
return;
}
}
// 3. If not CPU is in a test-end-set route to the CPU that las issued a CI,Aj instruction
// (if none, than simply route to the first CPU)
mCpus[mLastCpuIssuedCI]->SetIoInterrupt();
//mLogger << setloglevel(LogLevel_Event) << "**************** SETTING IOI INTERRUPT ***********" << std::endl;
}
void Mainframe_c::HandleDeadLock(uint16_t aCluster) {
CRAY_ASSERT(aCluster != 0);
for(size_t Cpu=0;Cpu<GetCpuCnt();++Cpu) {
uint8_t Cluster = mCpus[Cpu]->GetCluster();
if (Cluster == aCluster) {
mCpus[Cpu]->SetDeadLockInterrupt();
}
}
}
void Mainframe_c::Dump(size_t aIdent) const {
for(size_t Cpu=0;Cpu<mCpus.size();++Cpu) {
if (mCpus[Cpu] == nullptr) continue;
mLogger << setloglevel(LogLevel_Dump) << "Cpu" << DecPrinter(Cpu,0) << std::endl;
mCpus[Cpu]->Dump(aIdent+1);
}
for(size_t Cluster=0;Cluster<mClusters.size();++Cluster) {
if (mClusters[Cluster] == nullptr) continue;
mLogger << setloglevel(LogLevel_Dump) << "Cluster" << DecPrinter(Cluster,0) << std::endl;
mClusters[Cluster]->Dump(mLogger,aIdent+1);
}
for(size_t Channel=0;Channel<mChannels.size();++Channel) {
if (mChannels[Channel] == nullptr) continue;
mLogger << setloglevel(LogLevel_Dump) << "Channel" << DecPrinter(Channel + 8, 0) << std::endl;
mChannels[Channel]->Dump(aIdent + 1);
}
for(size_t Iop=0;Iop<mIopClusters.size();++Iop) {
if (mIopClusters[Iop] == nullptr) continue;
mLogger << setloglevel(LogLevel_Dump) << "Iop" << DecPrinter(Iop,0) << std::endl;
mIopClusters[Iop]->Dump(aIdent+1);
}
std::stringstream Strm;
DumpSyscallStats(Strm, *this, mOsType);
mLogger << setloglevel(LogLevel_Dump) << Strm.str();
}
void Mainframe_c::DumpHistory() {
for (size_t Cpu = 0; Cpu<mCpus.size(); ++Cpu) {
if (mCpus[Cpu] == nullptr) continue;
auto LogLine = mCpus[Cpu]->GetLogger() << setloglevel(LogLevel_Dump);
if (!LogLine.good()) {
std::cout << "History dump disabled" << std::endl;
return;
}
LogLine << "============================== CPU " << DecPrinter(Cpu) << " HISTORY ==============================" << std::endl;
mCpus[Cpu]->DumpHistory();
mCpus[Cpu]->ClearHistory();
mCpus[Cpu]->GetLogger() << setloglevel(LogLevel_Dump) << "============================== END HISTORY ==========================" << std::endl;
}
}
void Mainframe_c::DumpMemories() const {
if (mMainMemoryDumpFileName.is_initialized()) {
std::ofstream MemDump(mMainMemoryDumpFileName.get(), std::ios::out | std::ios::trunc | std::ios::binary);
MemDump.write((char*)(&mMemory[0]),mMemory.size());
MemDump.close();
}
}
void Mainframe_c::CheckWatchPointsInternal(CAddr_t aAddr, bool aWrite, CInt_t aData) {
auto WatchPoint = mWatchPoints.find(aAddr);
if (WatchPoint != mWatchPoints.end()) {
std::stringstream EventStr;
if (aWrite) {
EventStr << "Watchpoint at address " << Addr(aAddr) << " hit #:" << WatchPoint->second.mHitCnt << " written with: " << HexPrinter(SwapBytes(aData)) << " " << WatchPoint->second.mMessage << std::endl;
} else {
EventStr << "Watchpoint at address " << Addr(aAddr) << " hit #:" << WatchPoint->second.mHitCnt << " read " << WatchPoint->second.mMessage << std::endl;
}
WatchPoint->second.mHitCnt++;
GetEventDispatcher().Fire(EventStr.str());
std::cout << EventStr.str();
}
}
// Host <---> Sim communication works on a high level, like a FIFO.
// The simulated target can send 32-bit values to the host, using SimToHost. On return, the low-order 32 bits are unchanged, and the MSB is set if the write DID NOT take place due to the FIFO being full.
CInt_t Mainframe_c::SimToHost(CInt_t aValue) {
// For now, the only communication that's implemented is to terminate the simulation.
if ((aValue & 0xffffffff) == 0x65786974) { // 'exit' in hex
throw Terminate_x() << " requested from SIM";
}
return (CInt_t(1) << 63) | aValue;
}
// The simulated target can read 32-bit values to the host, using HostToSim. On return, the low-order 32 bits are from the FIFO, and the MSB is set if the read DID NOT take place due to the FIFO being empty.
CInt_t Mainframe_c::HostToSim() {
return CInt_t(1) << 63;
}