diff --git a/include/RevCPU.h b/include/RevCPU.h index b906d13c4..8e4c1060b 100644 --- a/include/RevCPU.h +++ b/include/RevCPU.h @@ -116,6 +116,7 @@ class RevCPU : public SST::Component { { "trcStartCycle", "Starting tracer cycle (disables trcOp)", "0" }, { "splash", "Display the splash logo", "0" }, { "independentCoprocClock", "Enables each coprocessor to register its own clock handler", "0" }, + { "randomizeCosts", "Randomizes the cost of each instruction", "0" }, ) // ------------------------------------------------------- @@ -233,7 +234,7 @@ class RevCPU : public SST::Component { // Adds Thread with ThreadID to AssignedThreads vector for ProcID // - Handles updating LSQueue & MarkLoadComplete function pointers - void AssignThread( std::unique_ptr&& ThreadToAssign, unsigned ProcID ); + void SetThread( std::unique_ptr&& ThreadToAssign, unsigned ProcID ); // Checks the status of ALL threads that are currently blocked. void CheckBlockedThreads(); diff --git a/include/RevCore.h b/include/RevCore.h index ad4d3bd24..28c023bb2 100644 --- a/include/RevCore.h +++ b/include/RevCore.h @@ -68,7 +68,8 @@ class RevCore { RevMem* mem, RevLoader* loader, std::function GetNewThreadID, - SST::Output* output + SST::Output* output, + bool randomizeCosts ); /// RevCore: standard destructor @@ -81,6 +82,15 @@ class RevCore { /// RevCore: per-processor clock function bool ClockTick( SST::Cycle_t currentCycle ); + /// RevCore: per-processor clock frontend function + bool FrontEnd(); + + /// RevCore: per-processor clock backend function + bool BackEnd(); + + /// RevCore: Backend execute instruction function + bool RevCore::Backend_Execute( RevInst& Inst ); + /// RevCore: Called by RevCPU when there is no more work to do (ie. All RevThreads are ThreadState::DONE ) void PrintStatSummary(); @@ -93,9 +103,6 @@ class RevCore { /// RevCore: execute a single step bool SingleStepHart(); - /// RevCore: retrieve the local PC for the correct feature set - uint64_t GetPC() const { return RegFile->GetPC(); } - /// RevCore: set time converter for RTC void SetTimeConverter( TimeConverter* tc ) { timeConverter = tc; } @@ -169,7 +176,7 @@ class RevCore { void CreateThread( uint32_t NewTid, uint64_t fn, void* arg ); ///< RevCore: Returns the current HartToExecID active pid - uint32_t GetActiveThreadID() { return Harts.at( HartToDecodeID )->GetAssignedThreadID(); } + uint32_t GetThreadID( unsigned HartID ) { return Harts.at( HartID )->GetThreadID(); } ///< RevCore: Get this Core's feature const RevFeature* GetRevFeature() const { return feature; } @@ -183,62 +190,56 @@ class RevCore { ///< RevCore: Add a co-processor to the RevCore void SetCoProc( RevCoProc* coproc ); - /// GetHartToExecID: Retrieve the current executing Hart - unsigned GetHartToExecID() const { return HartToExecID; } - - /// SetHartToExecID: Set the current executing Hart - void SetHartToExecID( unsigned hart ) { HartToExecID = hart; } - //--------------- External Interface for use with Co-Processor ------------------------- ///< RevCore: Allow a co-processor to query the bits in scoreboard. Note the RevCorePassKey may only /// be created by a RevCoProc (or a class derived from RevCoProc) so this function may not be called from even within /// RevCore - [[deprecated( "RevRegClass regClass is used instead of bool isFloat" )]] bool + [[deprecated( "RevRegClass RegClass is used instead of bool isFloat" )]] bool ExternalDepCheck( RevCorePasskey, uint16_t HartID, uint16_t reg, bool IsFloat ) { - RevRegClass regClass = IsFloat ? RevRegClass::RegFLOAT : RevRegClass::RegGPR; - const RevRegFile* regFile = GetRegFile( HartID ); - return LSQCheck( HartID, regFile, reg, regClass ) || ScoreboardCheck( regFile, reg, regClass ); + RevRegClass RegClass = IsFloat ? RevRegClass::RegFLOAT : RevRegClass::RegGPR; + const RevRegFile* RegFile = GetRegFile( HartID ); + return LSQCheck( HartID, RegFile, reg, RegClass ) || ScoreboardCheck( RegFile, reg, RegClass ); } ///< RevCore: Allow a co-processor to manipulate the scoreboard by setting a bit. Note the RevCorePassKey may only /// be created by a RevCoProc (or a class derived from RevCoProc) so this funciton may not be called from even within /// RevCore - [[deprecated( "RevRegClass regClass is used instead of bool isFloat" )]] void + [[deprecated( "RevRegClass RegClass is used instead of bool isFloat" )]] void ExternalDepSet( RevCorePasskey, uint16_t HartID, uint16_t RegNum, bool isFloat, bool value = true ) { - RevRegClass regClass = isFloat ? RevRegClass::RegFLOAT : RevRegClass::RegGPR; - DependencySet( HartID, RegNum, regClass, value ); + RevRegClass RegClass = isFloat ? RevRegClass::RegFLOAT : RevRegClass::RegGPR; + DependencySet( HartID, RegNum, RegClass, value ); } ///< RevCore: Allow a co-processor to manipulate the scoreboard by clearing a bit. Note the RevCorePassKey may only /// be created by a RevCoProc (or a class derived from RevCoProc) so this funciton may not be called from even within /// RevCore - [[deprecated( "RevRegClass regClass is used instead of bool isFloat" )]] void + [[deprecated( "RevRegClass RegClass is used instead of bool isFloat" )]] void ExternalDepClear( RevCorePasskey, uint16_t HartID, uint16_t RegNum, bool isFloat ) { - RevRegClass regClass = isFloat ? RevRegClass::RegFLOAT : RevRegClass::RegGPR; - DependencyClear( HartID, RegNum, regClass ); + RevRegClass RegClass = isFloat ? RevRegClass::RegFLOAT : RevRegClass::RegGPR; + DependencyClear( HartID, RegNum, RegClass ); } //--------------- External Interface for use with Co-Processor ------------------------- ///< RevCore: Allow a co-processor to query the bits in scoreboard. Note the RevCorePassKey may only /// be created by a RevCoProc (or a class derived from RevCoProc) so this function may not be called from even within /// RevCore - bool ExternalDepCheck( RevCorePasskey, uint16_t HartID, uint16_t reg, RevRegClass regClass ) { - const RevRegFile* regFile = GetRegFile( HartID ); - return LSQCheck( HartID, regFile, reg, regClass ) || ScoreboardCheck( regFile, reg, regClass ); + bool ExternalDepCheck( RevCorePasskey, uint16_t HartID, uint16_t reg, RevRegClass RegClass ) { + const RevRegFile* RegFile = GetRegFile( HartID ); + return LSQCheck( HartID, RegFile, reg, RegClass ) || ScoreboardCheck( RegFile, reg, RegClass ); } ///< RevCore: Allow a co-processor to manipulate the scoreboard by setting a bit. Note the RevCorePassKey may only /// be created by a RevCoProc (or a class derived from RevCoProc) so this funciton may not be called from even within /// RevCore - void ExternalDepSet( RevCorePasskey, uint16_t HartID, uint16_t RegNum, RevRegClass regClass, bool value = true ) { - DependencySet( HartID, RegNum, regClass, value ); + void ExternalDepSet( RevCorePasskey, uint16_t HartID, uint16_t RegNum, RevRegClass RegClass, bool value = true ) { + DependencySet( HartID, RegNum, RegClass, value ); } ///< RevCore: Allow a co-processor to manipulate the scoreboard by clearing a bit. Note the RevCorePassKey may only /// be created by a RevCoProc (or a class derived from RevCoProc) so this funciton may not be called from even within /// RevCore - void ExternalDepClear( RevCorePasskey, uint16_t HartID, uint16_t RegNum, RevRegClass regClass ) { - DependencyClear( HartID, RegNum, regClass ); + void ExternalDepClear( RevCorePasskey, uint16_t HartID, uint16_t RegNum, RevRegClass RegClass ) { + DependencyClear( HartID, RegNum, RegClass ); } ///< RevCore: Allow a co-processor to stall the pipeline of this proc and hold it in a stall condition @@ -249,46 +250,65 @@ class RevCore { ///< RevCore: Allow a co-processor to release the pipeline of this proc and allow a hart to continue /// execution (this un-does the ExternalStallHart() function ). Note the RevCorePassKey may only - /// be created by a RevCoProc (or a class derived from RevCoProc) so this funciton may not be called from even within + /// be created by a RevCoProc (or a class derived from RevCoProc) so this function may not be called from even within /// RevCore void ExternalReleaseHart( RevCorePasskey, uint16_t HartID ); //------------- END External Interface ------------------------------- ///< RevCore: Used for loading a software thread into a RevHart - void AssignThread( std::unique_ptr ThreadToAssign ); - - ///< RevCore: - void UpdateStatusOfHarts(); + void SetThread( std::unique_ptr ThreadToAssign ); - ///< RevCore: Returns the id of an idle hart (or _INVALID_HART_ID_ if none are idle) - unsigned FindIdleHartID() const; + ///< RevCore: Returns the id of an idle hart (or _REV_INVALID_HART_ID_ if none are idle) + unsigned FindIdleHartID() const { + for( auto& hart : Harts ) { + if( hart->GetState() == RevHartState::Idle ) + return &hart - &Harts[0]; + } + output->fatal( CALL_INFO, -1, "Attempted to find an idle hart but none were found. This is a bug\n" ); + return _REV_INVALID_HART_ID_; + } ///< RevCore: Returns true if all harts are available (ie. There is nothing executing on this Core) - bool HasNoBusyHarts() const { return IdleHarts == ValidHarts; } + bool HasNoBusyHarts() const { + for( auto& hart : Harts ) { + if( hart->GetState() != RevHartState::Idle ) + return false; + } + return true; + } + + ///< RevCore: Returns true if there are any IdleHarts + bool HasIdleHart() const { + for( auto& hart : Harts ) { + if( hart->GetState() == RevHartState::Idle ) + return true; + } + return false; + } ///< RevCore: Used by RevCPU to determine if it can disable this proc /// based on the criteria there are no threads assigned to it and the /// CoProc is done bool HasNoWork() const; - ///< RevCore: Returns true if there are any IdleHarts - bool HasIdleHart() const { return IdleHarts.any(); } - ///< RevCore: Returns the number of cycles executed so far uint64_t GetCycles() const { return cycles; } private: - bool Halted = false; ///< RevCore: determines if the core is halted - bool Stalled = false; ///< RevCore: determines if the core is stalled on instruction fetch - bool SingleStep = false; ///< RevCore: determines if we are in a single step - bool CrackFault = false; ///< RevCore: determines if we need to handle a crack fault - bool ALUFault = false; ///< RevCore: determines if we need to handle an ALU fault - unsigned fault_width = 0; ///< RevCore: the width of the target fault - unsigned const id; ///< RevCore: processor id - uint64_t ExecPC = 0; ///< RevCore: executing PC - unsigned HartToDecodeID = 0; ///< RevCore: Current executing ThreadID - unsigned HartToExecID = 0; ///< RevCore: Thread to dispatch instruction - uint64_t currentSimCycle = 0; ///< RevCore: Current simulation cycle + bool Halted = false; ///< RevCore: determines if the core is halted + bool Stalled = false; ///< RevCore: determines if the core is stalled on instruction fetch + bool SingleStep = false; ///< RevCore: determines if we are in a single step + bool CrackFault = false; ///< RevCore: determines if we need to handle a crack fault + bool ALUFault = false; ///< RevCore: determines if we need to handle an ALU fault + bool RandomizeCosts = false; ///< RevCore: whether to randomize costs of instructions + unsigned fault_width = 0; ///< RevCore: the width of the target fault + unsigned const id; ///< RevCore: ID + // uint64_t ExecPC = 0; ///< RevCore: executing PC + // unsigned HartToDecodeID = 0; ///< RevCore: Current executing ThreadID + // unsigned HartToExecID = 0; ///< RevCore: Thread to dispatch instruction + unsigned HartToExecID = 0; ///< RevCore: Thread to dispatch instruction + uint64_t currentSimCycle = 0; ///< RevCore: Current simulation cycle + unsigned CurrentHartID = 0; ///< RevCore: Current executing ThreadID std::vector> Harts{}; ///< RevCore: vector of Harts without a thread assigned to them std::bitset<_MAX_HARTS_> IdleHarts{}; ///< RevCore: bitset of Harts with no thread assigned @@ -324,9 +344,8 @@ class RevCore { LSQueue{}; ///< RevCore: Load / Store queue used to track memory operations. Currently only tracks outstanding loads. TimeConverter* timeConverter{}; ///< RevCore: Time converter for RTC - RevRegFile* RegFile = nullptr; ///< RevCore: Initial pointer to HartToDecodeID RegFile - uint32_t ActiveThreadID = _INVALID_TID_; ///< Software ThreadID (Not the Hart) that belongs to the Hart currently decoding - RevTracer* Tracer = nullptr; ///< RevCore: Tracer object + uint32_t ActiveThreadID = _INVALID_TID_; ///< Software ThreadID (Not the Hart) that belongs to the Hart currently decoding + RevTracer* Tracer = nullptr; ///< RevCore: Tracer object std::bitset<_MAX_HARTS_> CoProcStallReq{}; @@ -687,12 +706,14 @@ class RevCore { bool ExecEcall(); /// RevCore: Get a pointer to the register file loaded into Hart w/ HartID - RevRegFile* GetRegFile( unsigned HartID ) const; + RevRegFile* GetRegFile( unsigned HartID ) const { return Harts.at( HartID )->GetRegFile(); } + + /// RevCore: Get ECALL state + EcallState& GetEcallState( unsigned HartID ) const { return Harts.at( HartID )->GetEcallState(); } - std::vector InstTable{}; ///< RevCore: target instruction table - std::vector> Extensions{}; ///< RevCore: vector of enabled extensions - //std::vector> Pipeline; ///< RevCore: pipeline of instructions - std::deque> Pipeline{}; ///< RevCore: pipeline of instructions + std::vector InstTable{}; ///< RevCore: target instruction table + std::vector> Extensions{}; ///< RevCore: vector of enabled extensions + std::deque> Pipeline{}; ///< RevCore: pipeline of instructions std::unordered_map NameToEntry{}; ///< RevCore: instruction mnemonic to table entry mapping std::unordered_multimap EncToEntry{}; ///< RevCore: instruction encoding to table entry mapping std::unordered_multimap CEncToEntry{}; ///< RevCore: compressed instruction encoding to table entry mapping @@ -738,9 +759,6 @@ class RevCore { /// RevCore: reset the core and its associated features bool Reset(); - /// RevCore: set the PC - void SetPC( uint64_t PC ) { RegFile->SetPC( PC ); } - /// RevCore: prefetch the next instruction bool PrefetchInst(); @@ -805,36 +823,36 @@ class RevCore { unsigned GetNextHartToDecodeID() const; /// RevCore: Whether any scoreboard bits are set - bool AnyDependency( unsigned HartID, RevRegClass regClass = RevRegClass::RegUNKNOWN ) const { - const RevRegFile* regFile = GetRegFile( HartID ); - switch( regClass ) { - case RevRegClass::RegGPR: return regFile->RV_Scoreboard.any(); - case RevRegClass::RegFLOAT: return regFile->FP_Scoreboard.any(); - case RevRegClass::RegUNKNOWN: return regFile->RV_Scoreboard.any() || regFile->FP_Scoreboard.any(); + bool AnyDependency( unsigned HartID, RevRegClass RegClass = RevRegClass::RegUNKNOWN ) const { + const RevRegFile* RegFile = GetRegFile( HartID ); + if( !RegFile ) + return false; + switch( RegClass ) { + case RevRegClass::RegGPR: return RegFile->RV_Scoreboard.any(); + case RevRegClass::RegFLOAT: return RegFile->FP_Scoreboard.any(); + case RevRegClass::RegUNKNOWN: return RegFile->RV_Scoreboard.any() || RegFile->FP_Scoreboard.any(); default: return false; } } /// RevCore: Check LS queue for outstanding load - ignore x0 - bool LSQCheck( unsigned HartID, const RevRegFile* regFile, uint16_t reg, RevRegClass regClass ) const { - if( reg == 0 && regClass == RevRegClass::RegGPR ) { + bool LSQCheck( unsigned HartID, const RevRegFile* RegFile, uint16_t reg, RevRegClass RegClass ) const { + if( reg == 0 && RegClass == RevRegClass::RegGPR ) { return false; // GPR x0 is not considered } else { - return regFile->GetLSQueue()->count( LSQHash( reg, regClass, HartID ) ) > 0; + return RegFile->GetLSQueue()->count( LSQHash( reg, RegClass, HartID ) ) > 0; } } /// RevCore: Check scoreboard for a source register dependency - bool ScoreboardCheck( const RevRegFile* regFile, uint16_t reg, RevRegClass regClass ) const { - switch( regClass ) { - case RevRegClass::RegGPR: return reg != 0 && regFile->RV_Scoreboard[reg]; - case RevRegClass::RegFLOAT: return regFile->FP_Scoreboard[reg]; + bool ScoreboardCheck( const RevRegFile* RegFile, uint16_t reg, RevRegClass RegClass ) const { + switch( RegClass ) { + case RevRegClass::RegGPR: return reg != 0 && RegFile->RV_Scoreboard[reg]; + case RevRegClass::RegFLOAT: return RegFile->FP_Scoreboard[reg]; default: return false; } } - bool HartHasNoDependencies( unsigned HartID ) const { return !AnyDependency( HartID ); } - ///< Removes thread from Hart and returns it std::unique_ptr PopThreadFromHart( unsigned HartID ); @@ -851,24 +869,28 @@ class RevCore { /// RevCore: Set or clear scoreboard based on register number and floating point. template - void DependencySet( unsigned HartID, T RegNum, RevRegClass regClass, bool value = true ) { + void DependencySet( unsigned HartID, T RegNum, RevRegClass RegClass, bool value = true ) { if( size_t( RegNum ) < _REV_NUM_REGS_ ) { - RevRegFile* regFile = GetRegFile( HartID ); - switch( regClass ) { + RevRegFile* RegFile = GetRegFile( HartID ); + // clang-format off + switch( RegClass ) { case RevRegClass::RegGPR: if( size_t( RegNum ) != 0 ) - regFile->RV_Scoreboard[size_t( RegNum )] = value; + RegFile->RV_Scoreboard[size_t( RegNum )] = value; + break; + case RevRegClass::RegFLOAT: + RegFile->FP_Scoreboard[size_t( RegNum )] = value; break; - case RevRegClass::RegFLOAT: regFile->FP_Scoreboard[size_t( RegNum )] = value; break; default: break; } + // clang-format on } } /// RevCore: Clear scoreboard on instruction retirement template - void DependencyClear( unsigned HartID, T RegNum, RevRegClass regClass ) { - DependencySet( HartID, RegNum, regClass, false ); + void DependencyClear( unsigned HartID, T RegNum, RevRegClass RegClass ) { + DependencySet( HartID, RegNum, RegClass, false ); } }; // class RevCore diff --git a/include/RevExt.h b/include/RevExt.h index 6c2e97bdd..8bc65a0fe 100644 --- a/include/RevExt.h +++ b/include/RevExt.h @@ -27,6 +27,9 @@ #include "RevInstTable.h" #include "RevMem.h" +// Maximum cost when randomizing costs of instructions +#define MAX_COST 2 + namespace SST::RevCPU { struct RevExt { @@ -46,16 +49,16 @@ struct RevExt { /// RevExt: sets the internal instruction table // Note: && means the argument should be an rvalue or std::move(lvalue) // This avoids deep std::vector copies and uses only one std::vector move. - void SetTable( std::vector&& InstVect ) { table = std::move( InstVect ); } + void SetTable( std::vector&& InstVect ) { RandomizeCosts( table = std::move( InstVect ) ); } /// RevExt: sets the internal compressed instruction table - void SetCTable( std::vector&& InstVect ) { ctable = std::move( InstVect ); } + void SetCTable( std::vector&& InstVect ) { RandomizeCosts( ctable = std::move( InstVect ) ); } /// RevExt: retrieve the extension name std::string_view GetName() const { return name; } /// RevExt: baseline execution function - bool Execute( unsigned Inst, const RevInst& Payload, uint16_t HartID, RevRegFile* regFile ) const; + bool Execute( unsigned Inst, RevInst& Payload, uint16_t HartID, RevRegFile* regFile ) const; /// RevExt: retrieves the extension's instruction table const std::vector& GetTable() const { return table; } @@ -64,6 +67,17 @@ struct RevExt { const std::vector& GetCTable() const { return ctable; } private: + // RevExt: Randomize instruction costs if randomizeCosts == true + void RandomizeCosts( std::vector& table ) const { + if( feature->GetRandomizeCosts() ) { + for( auto& entry : table ) { + if( entry.cost == 1 ) { + entry.cost = RevRand( 1, MAX_COST ); + } + } + } + } + std::string_view const name; ///< RevExt: extension name const RevFeature* const feature; ///< RevExt: feature object RevMem* const mem; ///< RevExt: memory object @@ -71,6 +85,8 @@ struct RevExt { std::vector table{}; ///< RevExt: instruction table std::vector ctable{}; ///< RevExt: compressed instruction table +public: + const bool isFloat = name == "RV32F" || name == "RV32D" || name == "RV64F" || name == "RV64D"; }; // class RevExt } // namespace SST::RevCPU diff --git a/include/RevFeature.h b/include/RevFeature.h index c24d9d51f..06cd31ca2 100644 --- a/include/RevFeature.h +++ b/include/RevFeature.h @@ -49,7 +49,7 @@ enum RevFeatureType : uint32_t { struct RevFeature { /// RevFeature: standard constructor - RevFeature( std::string Machine, SST::Output* Output, unsigned Min, unsigned Max, unsigned Id ); + RevFeature( std::string Machine, SST::Output* Output, unsigned Min, unsigned Max, unsigned Id, bool randomizeCosts ); /// RevFeature: standard destructor ~RevFeature() = default; @@ -99,6 +99,9 @@ struct RevFeature { /// SetHartToExecID: Set the current executing Hart void SetHartToExecID( unsigned hart ) { HartToExecID = hart; } + /// GetRandomizeCosts: Return whether to randomize costs + bool GetRandomizeCosts() const { return randomizeCosts; } + private: const std::string machine; ///< RevFeature: feature string SST::Output* const output; ///< RevFeature: output handler @@ -108,9 +111,9 @@ struct RevFeature { unsigned HartToExecID{}; ///< RevFeature: The current executing Hart on RevCore RevFeatureType features{ RV_UNKNOWN }; ///< RevFeature: feature elements unsigned xlen{}; ///< RevFeature: RISC-V Xlen + const bool randomizeCosts; ///< RevFeature: Whether to randomize costs + bool ParseMachineModel(); ///< RevFeature: Parse the machine model string - /// ParseMachineModel: parse the machine model string - bool ParseMachineModel(); }; // class RevFeature } // namespace SST::RevCPU diff --git a/include/RevHart.h b/include/RevHart.h index 811a4cdcc..9761a3041 100644 --- a/include/RevHart.h +++ b/include/RevHart.h @@ -18,9 +18,18 @@ namespace SST::RevCPU { +enum class RevHartState { + Idle, + Fetching, + Decoding, + Halted, + Executing, + Retired, +}; + class RevHart { ///< RevHart: Id for the Hart (0,1,2,3,etc) - unsigned ID{}; + unsigned const ID; ///< RevHart: State management object when a Hart is executing a system call EcallState Ecall{}; @@ -29,14 +38,16 @@ class RevHart { const std::shared_ptr>& LSQueue; ///< RevHart: Pointer to the Proc's MarkLoadCompleteFunc - std::function MarkLoadCompleteFunc{}; + std::function const MarkLoadCompleteFunc; ///< RevHart: Thread currently executing on this Hart - std::unique_ptr Thread = nullptr; - std::unique_ptr RegFile = nullptr; + std::unique_ptr Thread = nullptr; + + ///< RevHart: State of hart + RevHartState State = RevHartState::Idle; - ///< RevHart: Make RevCore a friend of this - friend class RevCore; + ///GetRegFile(); } + + ///< RevHart: Add new file descriptor to this hart's thread (ie. rev_open) + void AddFD( int fd ) { Thread->AddFD( fd ); } + + ///< RevHart: Remove file descriptor from this hart's thread (ie. rev_close) + void RemoveFD( int fd ) { Thread->RemoveFD( fd ); } + + ///< See if file descriptor exists/is owned by this hart's thread + bool FindFD( int fd ) const { return Thread->FindFD( fd ); } ///< RevHart: Get Hart's ID uint16_t GetID() const { return ID; } ///< RevHart: Returns the ID of the assigned thread - uint32_t GetAssignedThreadID() const { return Thread ? Thread->GetID() : _INVALID_TID_; } + uint32_t GetThreadID() const { return Thread ? Thread->GetID() : _INVALID_TID_; } - ///< RevHart: Load the register file from the RevThread - void LoadRegFile( std::unique_ptr regFile ) { - RegFile = std::move( regFile ); + ///< RevHart: Assigns a RevThread to this Hart + void SetThread( std::unique_ptr ThreadToAssign ) { + Thread = std::move( ThreadToAssign ); + auto RegFile = Thread->GetRegFile(); RegFile->SetMarkLoadComplete( MarkLoadCompleteFunc ); RegFile->SetLSQueue( LSQueue ); - } - - ///< RevHart: Assigns a RevThread to this Hart - void AssignThread( std::unique_ptr ThreadToAssign ) { - Thread = std::move( ThreadToAssign ); Thread->SetState( ThreadState::RUNNING ); - LoadRegFile( Thread->TransferVirtRegState() ); } - ///< RevHart: Removed a RevThread from this Hart - std::unique_ptr PopThread() { - // return the register file to the thread - Thread->UpdateVirtRegState( std::move( RegFile ) ); - // return the thread - return std::move( Thread ); - } + ///< RevHart: Remove a RevThread from this Hart + std::unique_ptr PopThread() { return std::move( Thread ); } }; // class RevHart } // namespace SST::RevCPU diff --git a/include/RevInstHelpers.h b/include/RevInstHelpers.h index a4af0f813..425300f58 100644 --- a/include/RevInstHelpers.h +++ b/include/RevInstHelpers.h @@ -66,7 +66,7 @@ inline constexpr double fpmin = 0x0p+0; /// FP values outside the range of the target integer type are clipped /// at the integer type's numerical limits, whether signed or unsigned. template -bool fcvtif( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool fcvtif( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { // Read the FP register. Round to integer according to current rounding mode. FP fp = std::rint( R->GetFP( Inst.rs1 ) ); @@ -132,7 +132,7 @@ uint32_t fclass( T val ) { /// Load template template -bool load( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool load( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { if( sizeof( T ) < sizeof( int64_t ) && !F->IsRV64() ) { static constexpr RevFlag flags = sizeof( T ) < sizeof( int32_t ) ? std::is_signed_v ? RevFlag::F_SEXT32 : RevFlag::F_ZEXT32 : RevFlag::F_NONE; @@ -168,14 +168,14 @@ bool load( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) } // update the cost - R->cost += M->RandCost( F->GetMinCost(), F->GetMaxCost() ); + Inst.cost += M->RandCost( F->GetMinCost(), F->GetMaxCost() ); R->AdvancePC( Inst ); return true; } /// Store template template -bool store( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool store( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { M->Write( F->GetHartToExecID(), R->GetX( Inst.rs1 ) + Inst.ImmSignExt( 12 ), R->GetX( Inst.rs2 ) ); R->AdvancePC( Inst ); return true; @@ -183,7 +183,7 @@ bool store( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) /// Floating-point load template template -bool fload( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool fload( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { if( std::is_same_v || F->HasD() ) { static constexpr RevFlag flags = sizeof( T ) < sizeof( double ) ? RevFlag::F_BOXNAN : RevFlag::F_NONE; uint64_t rs1 = R->GetX( Inst.rs1 ); @@ -212,15 +212,16 @@ bool fload( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) R->LSQueue->insert( req.LSQHashPair() ); M->ReadVal( F->GetHartToExecID(), rs1 + Inst.ImmSignExt( 12 ), &R->SPF[Inst.rd], std::move( req ), RevFlag::F_NONE ); } + // update the cost - R->cost += M->RandCost( F->GetMinCost(), F->GetMaxCost() ); + Inst.cost += M->RandCost( F->GetMinCost(), F->GetMaxCost() ); R->AdvancePC( Inst ); return true; } /// Floating-point store template template -bool fstore( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool fstore( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { T val = R->GetFP( Inst.rs2 ); M->Write( F->GetHartToExecID(), R->GetX( Inst.rs1 ) + Inst.ImmSignExt( 12 ), val ); R->AdvancePC( Inst ); @@ -229,7 +230,7 @@ bool fstore( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst /// Floating-point operation template template class OP> -bool foper( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool foper( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, OP()( R->GetFP( Inst.rs1 ), R->GetFP( Inst.rs2 ) ) ); R->AdvancePC( Inst ); return true; @@ -267,7 +268,7 @@ struct FMax { /// Floating-point conditional operation template template class OP> -bool fcondop( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool fcondop( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { bool res = OP()( R->GetFP( Inst.rs1 ), R->GetFP( Inst.rs2 ) ); R->SetX( Inst.rd, res ); R->AdvancePC( Inst ); @@ -283,7 +284,7 @@ enum class OpKind { Imm, Reg }; // The third parameter is std::make_unsigned_t or std::make_signed_t (default) // The optional fourth parameter indicates W mode (32-bit on XLEN == 64) template class OP, OpKind KIND, template class SIGN = std::make_signed_t, bool W_MODE = false> -bool oper( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool oper( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { if( !W_MODE && !F->IsRV64() ) { using T = SIGN; T rs1 = R->GetX( Inst.rs1 ); @@ -322,7 +323,7 @@ struct ShiftRight { // Computes the UPPER half of multiplication, based on signedness template -bool uppermul( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool uppermul( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { if( F->IsRV64() ) { uint64_t rs1 = R->GetX( Inst.rs1 ); uint64_t rs2 = R->GetX( Inst.rs2 ); @@ -353,7 +354,7 @@ enum class DivRem { Div, Rem }; // The second parameter is std::make_signed_t or std::make_unsigned_t // The optional third parameter indicates W mode (32-bit on XLEN == 64) template class SIGN, bool W_MODE = false> -bool divrem( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool divrem( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { if( !W_MODE && !F->IsRV64() ) { using T = SIGN; T rs1 = R->GetX( Inst.rs1 ); @@ -386,7 +387,7 @@ bool divrem( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst // The first template parameter is the comparison functor // The second template parameter is std::make_signed_t or std::make_unsigned_t template class OP, template class SIGN = std::make_unsigned_t> -bool bcond( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool bcond( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { bool cond; if( F->IsRV64() ) { cond = OP()( R->GetX>( Inst.rs1 ), R->GetX>( Inst.rs2 ) ); @@ -419,7 +420,7 @@ inline auto revFMA( T x, T y, T z ) { /// Fused Multiply+Add template -bool fmadd( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool fmadd( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, revFMA( R->GetFP( Inst.rs1 ), R->GetFP( Inst.rs2 ), R->GetFP( Inst.rs3 ) ) ); R->AdvancePC( Inst ); return true; @@ -427,7 +428,7 @@ bool fmadd( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) /// Fused Multiply-Subtract template -bool fmsub( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool fmsub( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, revFMA( R->GetFP( Inst.rs1 ), R->GetFP( Inst.rs2 ), negate( R->GetFP( Inst.rs3 ) ) ) ); R->AdvancePC( Inst ); return true; @@ -435,7 +436,7 @@ bool fmsub( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) /// Fused Negated (Multiply-Subtract) template -bool fnmsub( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool fnmsub( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, revFMA( negate( R->GetFP( Inst.rs1 ) ), R->GetFP( Inst.rs2 ), R->GetFP( Inst.rs3 ) ) ); R->AdvancePC( Inst ); return true; @@ -443,7 +444,7 @@ bool fnmsub( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst /// Fused Negated (Multiply+Add) template -bool fnmadd( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +bool fnmadd( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, negate( revFMA( R->GetFP( Inst.rs1 ), R->GetFP( Inst.rs2 ), R->GetFP( Inst.rs3 ) ) ) ); R->AdvancePC( Inst ); return true; @@ -451,7 +452,7 @@ bool fnmadd( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst // Square root template -static bool fsqrt( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +static bool fsqrt( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, std::sqrt( R->GetFP( Inst.rs1 ) ) ); R->AdvancePC( Inst ); return true; @@ -459,7 +460,7 @@ static bool fsqrt( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& // Transfer sign bit template -static bool fsgnj( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +static bool fsgnj( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, std::copysign( R->GetFP( Inst.rs1 ), R->GetFP( Inst.rs2 ) ) ); R->AdvancePC( Inst ); return true; @@ -467,7 +468,7 @@ static bool fsgnj( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& // Negated transfer sign bit template -static bool fsgnjn( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +static bool fsgnjn( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, std::copysign( R->GetFP( Inst.rs1 ), negate( R->GetFP( Inst.rs2 ) ) ) ); R->AdvancePC( Inst ); return true; @@ -475,7 +476,7 @@ static bool fsgnjn( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst // Xor transfer sign bit template -static bool fsgnjx( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +static bool fsgnjx( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { T rs1 = R->GetFP( Inst.rs1 ), rs2 = R->GetFP( Inst.rs2 ); R->SetFP( Inst.rd, std::copysign( rs1, std::signbit( rs1 ) ? negate( rs2 ) : rs2 ) ); R->AdvancePC( Inst ); @@ -484,7 +485,7 @@ static bool fsgnjx( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst // Move floating-point register to integer register template -static bool fmvif( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +static bool fmvif( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { std::make_signed_t> i; T fp = R->GetFP( Inst.rs1 ); // The FP value static_assert( sizeof( i ) == sizeof( fp ) ); @@ -496,7 +497,7 @@ static bool fmvif( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& // Move integer register to floating-point register template -static bool fmvfi( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +static bool fmvfi( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { T fp; auto i = R->GetX>( Inst.rs1 ); // The X register static_assert( sizeof( i ) == sizeof( fp ) ); @@ -508,7 +509,7 @@ static bool fmvfi( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& // Floating-point classify template -static bool fclassify( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +static bool fclassify( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetX( Inst.rd, fclass( R->GetFP( Inst.rs1 ) ) ); R->AdvancePC( Inst ); return true; @@ -516,7 +517,7 @@ static bool fclassify( const RevFeature* F, RevRegFile* R, RevMem* M, const RevI // Convert integer to floating point template -static bool fcvtfi( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +static bool fcvtfi( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, static_cast( R->GetX( Inst.rs1 ) ) ); R->AdvancePC( Inst ); return true; @@ -524,7 +525,7 @@ static bool fcvtfi( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst // Convert floating point to floating point template -static bool fcvtff( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { +static bool fcvtff( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, static_cast( R->GetFP( Inst.rs1 ) ) ); R->AdvancePC( Inst ); return true; diff --git a/include/RevInstTable.h b/include/RevInstTable.h index c71b59b62..3190c9745 100644 --- a/include/RevInstTable.h +++ b/include/RevInstTable.h @@ -153,7 +153,7 @@ struct RevInstEntry { bool raisefpe = false; ///compressed = c; return *this; } auto& Setrs2fcvtOp(uint8_t op) { this->rs2fcvtOp = op; return *this; } auto& SetRaiseFPE(bool c) { this->raisefpe = c; return *this; } - auto& SetImplFunc( bool func( const RevFeature *, RevRegFile *, RevMem *, const RevInst& ) ) + auto& SetImplFunc( bool func( const RevFeature *, RevRegFile *, RevMem *, RevInst& ) ) { this->func = func; return *this; } auto& SetPredicate( bool pred( uint32_t ) ) { this->predicate = pred; return *this; } diff --git a/include/RevRegFile.h b/include/RevRegFile.h index 9bf2f267d..cdafba105 100644 --- a/include/RevRegFile.h +++ b/include/RevRegFile.h @@ -131,13 +131,10 @@ class RevCore; class RevTracer; class RevRegFile : public RevCSR { - RevCore* const Core; ///< RevRegFile: Owning core of this register file's hart - const bool IsRV64_v; ///< RevRegFile: Cached copy of Features->IsRV64() - const bool HasD_v; ///< RevRegFile: Cached copy of Features->HasD() - bool trigger{}; ///< RevRegFile: Has the instruction been triggered? - unsigned Entry{}; ///< RevRegFile: Instruction entry - uint32_t cost{}; ///< RevRegFile: Cost of the instruction - RevTracer* Tracer{}; ///< RegRegFile: Tracer object + RevCore* const Core; ///< RevRegFile: Owning core of this register file's hart + const bool IsRV64_v; ///< RevRegFile: Cached copy of Features->IsRV64() + const bool HasD_v; ///< RevRegFile: Cached copy of Features->HasD() + RevTracer* Tracer{}; ///< RegRegFile: Tracer object union { // Anonymous union. We zero-initialize the largest member uint32_t RV32_PC; ///< RevRegFile: RV32 PC @@ -205,26 +202,6 @@ class RevRegFile : public RevCSR { bool HasD() const { return HasD_v; } // Getters/Setters - /// Get cost of the instruction - const uint32_t& GetCost() const { return cost; } - - uint32_t& GetCost() { return cost; } - - /// Set cost of the instruction - void SetCost( uint32_t c ) { cost = c; } - - /// Get whether the instruction has been triggered - bool GetTrigger() const { return trigger; } - - /// Set whether the instruction has been triggered - void SetTrigger( bool t ) { trigger = t; } - - /// Get the instruction entry - unsigned GetEntry() const { return Entry; } - - /// Set the instruction entry - void SetEntry( unsigned e ) { Entry = e; } - /// Get the Load/Store Queue const auto& GetLSQueue() const { return LSQueue; } @@ -299,13 +276,7 @@ class RevRegFile : public RevCSR { } /// GetPC: Get the Program Counter - uint64_t GetPC() const { - if( IsRV64() ) { - return RV64_PC; - } else { - return RV32_PC; - } - } + uint64_t GetPC() const { return IsRV64() ? RV64_PC : RV32_PC; } /// SetPC: Set the Program Counter to a specific value template @@ -358,25 +329,25 @@ class RevRegFile : public RevCSR { // Friend functions and classes to access internal register state template - friend bool fcvtif( const class RevFeature* F, RevRegFile* R, class RevMem* M, const class RevInst& Inst ); + friend bool fcvtif( const class RevFeature* F, RevRegFile* R, class RevMem* M, class RevInst& Inst ); template - friend bool load( const class RevFeature* F, RevRegFile* R, class RevMem* M, const class RevInst& Inst ); + friend bool load( const class RevFeature* F, RevRegFile* R, class RevMem* M, class RevInst& Inst ); template - friend bool store( const class RevFeature* F, RevRegFile* R, class RevMem* M, const class RevInst& Inst ); + friend bool store( const class RevFeature* F, RevRegFile* R, class RevMem* M, class RevInst& Inst ); template - friend bool fload( const class RevFeature* F, RevRegFile* R, class RevMem* M, const class RevInst& Inst ); + friend bool fload( const class RevFeature* F, RevRegFile* R, class RevMem* M, class RevInst& Inst ); template - friend bool fstore( const class RevFeature* F, RevRegFile* R, class RevMem* M, const class RevInst& Inst ); + friend bool fstore( const class RevFeature* F, RevRegFile* R, class RevMem* M, class RevInst& Inst ); template class OP> - friend bool foper( const class RevFeature* F, RevRegFile* R, class RevMem* M, const class RevInst& Inst ); + friend bool foper( const class RevFeature* F, RevRegFile* R, class RevMem* M, class RevInst& Inst ); template class OP> - friend bool fcondop( const class RevFeature* F, RevRegFile* R, class RevMem* M, const class RevInst& Inst ); + friend bool fcondop( const class RevFeature* F, RevRegFile* R, class RevMem* M, class RevInst& Inst ); friend std::ostream& operator<<( std::ostream& os, const RevRegFile& regFile ); diff --git a/include/RevSysCalls.h b/include/RevSysCalls.h index 8167d6cce..23bdd8444 100644 --- a/include/RevSysCalls.h +++ b/include/RevSysCalls.h @@ -43,7 +43,9 @@ struct EcallState { bytesRead = 0; } - explicit EcallState() = default; + explicit EcallState() = default; + EcallState( const EcallState& ) = delete; + EcallState& operator=( const EcallState& ) = delete; }; // struct EcallState } // namespace SST::RevCPU #endif diff --git a/include/RevThread.h b/include/RevThread.h index 69c584cbe..75d3b2585 100644 --- a/include/RevThread.h +++ b/include/RevThread.h @@ -27,13 +27,9 @@ namespace SST::RevCPU { class RevThread { public: - using RevVirtRegState = RevRegFile; - ///< RevThread: Constructor - RevThread( - uint32_t ID, uint32_t ParentID, std::shared_ptr& ThreadMem, std::unique_ptr VirtRegState - ) - : ID( ID ), ParentID( ParentID ), ThreadMem( ThreadMem ), VirtRegState( std::move( VirtRegState ) ) {} + RevThread( uint32_t ID, uint32_t ParentID, std::shared_ptr ThreadMem, std::unique_ptr RegFile ) + : ID( ID ), ParentID( ParentID ), ThreadMem( std::move( ThreadMem ) ), RegFile( std::move( RegFile ) ) {} ///< RevThread: Destructor ~RevThread() { @@ -53,20 +49,14 @@ class RevThread { ///< RevThread: Get this thread's ID uint32_t GetID() const { return ID; } - ///< RevThread: Set this thread's ID - void SetID( const uint32_t NewID ) { ID = NewID; } - ///< RevThread: Get the parent of this thread's TID uint32_t GetParentID() const { return ParentID; } - ///< RevThread: Set the parent of this thread's TID - void SetParentID( const uint32_t NewParentID ) { ParentID = NewParentID; } - ///< RevThread: Get the TID of the thread that this thread is waiting to join uint32_t GetWaitingToJoinTID() const { return WaitingToJoinTID; } ///< RevThread: Set the TID of the thread that this thread is waiting to join - void SetWaitingToJoinTID( const uint32_t ThreadToWaitOn ) { WaitingToJoinTID = ThreadToWaitOn; } + void SetWaitingToJoinTID( uint32_t ThreadToWaitOn ) { WaitingToJoinTID = ThreadToWaitOn; } ///< RevThread: Add new file descriptor to this thread (ie. rev_open) void AddFD( int fd ) { fildes.insert( fd ); } @@ -75,26 +65,16 @@ class RevThread { void RemoveFD( int fd ) { fildes.erase( fd ); } ///< See if file descriptor exists/is owned by this thread - bool FindFD( int fd ) { return fildes.count( fd ); } - - ///< RevThread: Get the fildes valid for this thread - const std::unordered_set& GetFildes() { return fildes; } - - ///< RevThread: Update this thread's virtual register state - void UpdateVirtRegState( std::unique_ptr vRegState ) { VirtRegState = std::move( vRegState ); } - - ///< RevThread: Transfers the pointer of this Thread's register state - /// (Used for loading reg state into a Hart's RegFile) - std::unique_ptr TransferVirtRegState() { return std::move( VirtRegState ); } + bool FindFD( int fd ) const { return fildes.count( fd ); } - ///< RevThread: Get the RevFeature this thread was created with - RevFeature* GetFeature() const { return Feature; } + ///< RevThread: Returns this Thread's register state + RevRegFile* GetRegFile() const { return RegFile.get(); } ///< RevThread: Get this thread's current ThreadState ThreadState GetState() const { return State; } ///< RevThread: Set this thread's ThreadState - void SetState( ThreadState newState ) { State = std::move( newState ); } + void SetState( ThreadState newState ) { State = newState; } ///< RevThread: Add a child to this thread id void AddChildID( uint32_t tid ) { ChildrenIDs.insert( tid ); } @@ -113,17 +93,13 @@ class RevThread { } private: - uint32_t ID{}; // Thread ID - uint32_t ParentID{}; // Parent ID - // uint64_t StackPtr{}; // Initial stack pointer - // uint64_t FirstPC{}; // Initial PC - std::shared_ptr ThreadMem{}; // TLS and Stack memory - RevFeature* Feature{}; // Feature set for this thread - // uint64_t ThreadPtr{}; // Thread pointer - ThreadState State{ ThreadState::START }; // Thread state - std::unique_ptr VirtRegState{}; // Register file - std::unordered_set ChildrenIDs{}; // Child thread IDs - std::unordered_set fildes{ 0, 1, 2 }; // Default file descriptors + uint32_t const ID; // Thread ID + uint32_t const ParentID; // Parent ID + std::shared_ptr ThreadMem{}; // TLS and Stack memory + ThreadState State{ ThreadState::START }; // Thread state + std::unique_ptr const RegFile; // Register file + std::unordered_set ChildrenIDs{}; // Child thread IDs + std::unordered_set fildes{ 0, 1, 2 }; // Default file descriptors ///< RevThread: ID of the thread this thread is waiting to join uint32_t WaitingToJoinTID = _INVALID_TID_; diff --git a/include/insns/RV32I.h b/include/insns/RV32I.h index 5cf20bc08..31c25b05e 100644 --- a/include/insns/RV32I.h +++ b/include/insns/RV32I.h @@ -22,31 +22,31 @@ namespace SST::RevCPU { class RV32I : public RevExt { // Standard instructions - static bool nop( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool nop( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->AdvancePC( Inst ); return true; } - static bool lui( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool lui( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetX( Inst.rd, static_cast( Inst.imm << 12 ) ); R->AdvancePC( Inst ); return true; } - static bool auipc( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool auipc( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { auto ui = static_cast( Inst.imm << 12 ); R->SetX( Inst.rd, ui + R->GetPC() ); R->AdvancePC( Inst ); return true; } - static bool jal( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool jal( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetX( Inst.rd, R->GetPC() + Inst.instSize ); R->SetPC( R->GetPC() + Inst.ImmSignExt( 21 ) ); return true; } - static bool jalr( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool jalr( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { auto ret = R->GetPC() + Inst.instSize; R->SetPC( ( R->GetX( Inst.rs1 ) + Inst.ImmSignExt( 12 ) ) & -2 ); R->SetX( Inst.rd, ret ); @@ -98,13 +98,13 @@ class RV32I : public RevExt { static constexpr auto& srl = oper; static constexpr auto& sra = oper; - static bool fence( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool fence( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { M->FenceMem( F->GetHartToExecID() ); R->AdvancePC( Inst ); return true; // temporarily disabled } - static bool ecall( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool ecall( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { /* * In reality this should be getting/setting a LOT of bits inside the * CSRs however because we are only concerned with ecall right now it's diff --git a/include/insns/RV64P.h b/include/insns/RV64P.h index e1d5c690b..42e4d796b 100644 --- a/include/insns/RV64P.h +++ b/include/insns/RV64P.h @@ -19,19 +19,19 @@ namespace SST::RevCPU { class RV64P : public RevExt { - static bool future( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool future( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetX( Inst.rd, !!M->SetFuture( R->GetX( Inst.rs1 ) + Inst.ImmSignExt( 12 ) ) ); // R->AdvancePC(Inst); return true; } - static bool rfuture( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool rfuture( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetX( Inst.rd, !!M->RevokeFuture( R->GetX( Inst.rs1 ) + Inst.ImmSignExt( 12 ) ) ); // R->AdvancePC(Inst); return true; } - static bool sfuture( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool sfuture( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetX( Inst.rd, !!M->StatusFuture( R->GetX( Inst.rs1 ) + Inst.ImmSignExt( 12 ) ) ); // R->AdvancePC(Inst); return true; diff --git a/include/insns/Zaamo.h b/include/insns/Zaamo.h index b25427cef..80fc726ce 100644 --- a/include/insns/Zaamo.h +++ b/include/insns/Zaamo.h @@ -18,7 +18,7 @@ namespace SST::RevCPU { class Zaamo : public RevExt { template - static bool amooper( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool amooper( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { static_assert( std::is_unsigned_v, "XLEN must be unsigned integral type" ); RevFlag flags{ F_AMO }; @@ -52,7 +52,7 @@ class Zaamo : public RevExt { ); } // update the cost - R->cost += M->RandCost( F->GetMinCost(), F->GetMaxCost() ); + Inst.cost += M->RandCost( F->GetMinCost(), F->GetMaxCost() ); R->AdvancePC( Inst ); return true; } diff --git a/include/insns/Zalrsc.h b/include/insns/Zalrsc.h index a91162176..b919d4cc0 100644 --- a/include/insns/Zalrsc.h +++ b/include/insns/Zalrsc.h @@ -20,7 +20,7 @@ class Zalrsc : public RevExt { // Load Reserved instruction template - static bool lr( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool lr( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { static_assert( std::is_unsigned_v, "TYPE must be unsigned integral type" ); auto addr = R->GetX( Inst.rs1 ); @@ -47,14 +47,14 @@ class Zalrsc : public RevExt { // Create a reservation and load the data M->LR( F->GetHartToExecID(), addr, sizeof( TYPE ), target, req, flags ); - R->cost += M->RandCost( F->GetMinCost(), F->GetMaxCost() ); + Inst.cost += M->RandCost( F->GetMinCost(), F->GetMaxCost() ); R->AdvancePC( Inst ); return true; } // Store Conditional instruction template - static bool sc( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool sc( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { static_assert( std::is_unsigned_v, "TYPE must be unsigned integral type" ); auto addr = R->GetX( Inst.rs1 ); auto val = R->GetX( Inst.rs2 ); diff --git a/include/insns/Zfa.h b/include/insns/Zfa.h index 0e26f0c3d..c300f36b5 100644 --- a/include/insns/Zfa.h +++ b/include/insns/Zfa.h @@ -20,7 +20,7 @@ class Zfa : public RevExt { // Round to integer without inexact exceptions template - static bool fround( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool fround( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, std::nearbyint( R->GetFP( Inst.rs1 ) ) ); R->AdvancePC( Inst ); return true; @@ -28,7 +28,7 @@ class Zfa : public RevExt { // Round to integer with inexact exceptions template - static bool froundnx( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool froundnx( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { R->SetFP( Inst.rd, std::rint( R->GetFP( Inst.rs1 ) ) ); R->AdvancePC( Inst ); return true; @@ -57,7 +57,7 @@ class Zfa : public RevExt { /// Floating-point minimum/maximum /// If either argument is NaN, the result is NaN template class MINMAX> - static bool fminmaxm( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool fminmaxm( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { T x = R->GetFP( Inst.rs1 ); T y = R->GetFP( Inst.rs2 ); T res = MINMAX()( x, y ); @@ -70,7 +70,7 @@ class Zfa : public RevExt { } // Move the high 32 bits of floating-point double to a XLEN==32 register - static bool fmvhxd( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool fmvhxd( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { double fp = R->GetFP( Inst.rs1 ); uint64_t ifp; memcpy( &ifp, &fp, sizeof( ifp ) ); @@ -81,7 +81,7 @@ class Zfa : public RevExt { } // Move a pair of XLEN==32 registers to a floating-point double - static bool fmvpdx( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool fmvpdx( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { uint64_t ifp = R->GetX( Inst.rs1 ) | R->GetX( Inst.rs2 ) << 32; double fp; memcpy( &fp, &ifp, sizeof( fp ) ); @@ -94,7 +94,7 @@ class Zfa : public RevExt { // Convert double precision floating point to 32-bit signed integer modulo 2^32 // Always truncates (rounds toward 0) regardless of rounding mode // Raises the same exceptions as fcvt.w.d but the result is always modulo 2^32 - static bool fcvtmodwd( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool fcvtmodwd( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { double fp = R->GetFP( Inst.rs1 ); uint64_t ifp; diff --git a/include/insns/Zicbom.h b/include/insns/Zicbom.h index 3a4b07033..e4c537d95 100644 --- a/include/insns/Zicbom.h +++ b/include/insns/Zicbom.h @@ -28,7 +28,7 @@ class Zicbom : public RevExt { }; template - static bool cmo( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool cmo( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { switch( cbo ) { case CBO::INVAL: M->InvLine( F->GetHartToExecID(), R->GetX( Inst.rs1 ) ); break; case CBO::CLEAN: M->CleanLine( F->GetHartToExecID(), R->GetX( Inst.rs1 ) ); break; diff --git a/include/insns/Zicsr.h b/include/insns/Zicsr.h index 46fe819ba..c7b5deaf9 100644 --- a/include/insns/Zicsr.h +++ b/include/insns/Zicsr.h @@ -44,7 +44,7 @@ class Zicsr : public RevExt { /// Modify a CSR Register according to CSRRW, CSRRS, or CSRRC // Because CSR has a 32/64-bit width, this function is templatized template - static bool ModCSRImpl( RevRegFile* R, const RevInst& Inst ) { + static bool ModCSRImpl( RevRegFile* R, RevInst& Inst ) { static_assert( std::is_unsigned_v, "XLEN must be an unsigned type" ); XLEN old = 0; @@ -86,7 +86,7 @@ class Zicsr : public RevExt { /// Modify a CSR Register according to CSRRW, CSRRS, or CSRRC // This calls the 32/64-bit ModCSR depending on the current XLEN template - static bool ModCSR( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool ModCSR( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { return F->IsRV64() ? ModCSRImpl( R, Inst ) : ModCSRImpl( R, Inst ); } diff --git a/include/insns/Zifencei.h b/include/insns/Zifencei.h index 79b3743e7..42802ebb2 100644 --- a/include/insns/Zifencei.h +++ b/include/insns/Zifencei.h @@ -18,7 +18,7 @@ namespace SST::RevCPU { class Zifencei : public RevExt { - static bool fencei( const RevFeature* F, RevRegFile* R, RevMem* M, const RevInst& Inst ) { + static bool fencei( const RevFeature* F, RevRegFile* R, RevMem* M, RevInst& Inst ) { M->FenceMem( F->GetHartToExecID() ); R->AdvancePC( Inst ); return true; diff --git a/src/RevCPU.cc b/src/RevCPU.cc index d6e3da12a..db9f1e5be 100644 --- a/src/RevCPU.cc +++ b/src/RevCPU.cc @@ -101,8 +101,11 @@ RevCPU::RevCPU( SST::ComponentId_t id, const SST::Params& params ) : SST::Compon msgPerCycle = params.find( "msgPerCycle", 1 ); } + // Whether to randomize the costs of instructions + bool randomizeCosts = params.find( "randomizeCosts", 0 ); + // Look for the fault injection logic - EnableFaults = params.find( "enable_faults", 0 ); + EnableFaults = params.find( "enable_faults", 0 ); if( EnableFaults ) { std::vector faults; params.find_array( "faults", faults ); @@ -144,7 +147,9 @@ RevCPU::RevCPU( SST::ComponentId_t id, const SST::Params& params ) : SST::Compon // Create the processor objects Procs.reserve( Procs.size() + numCores ); for( unsigned i = 0; i < numCores; i++ ) { - Procs.push_back( std::make_unique( i, Opts.get(), numHarts, Mem.get(), Loader.get(), this->GetNewTID(), &output ) ); + Procs.push_back( + std::make_unique( i, Opts.get(), numHarts, Mem.get(), Loader.get(), this->GetNewTID(), &output, randomizeCosts ) + ); } EnableCoProc = params.find( "enableCoProc", 0 ); @@ -645,12 +650,12 @@ bool RevCPU::clockTick( SST::Cycle_t currentCycle ) { // - Adds it's ThreadID to the ReadyThreads to be scheduled void RevCPU::InitThread( std::unique_ptr&& ThreadToInit ) { // Get a pointer to the register state for this thread - std::unique_ptr RegState = ThreadToInit->TransferVirtRegState(); + RevRegFile* RegFile = ThreadToInit->GetRegFile(); // Set the global pointer register and the frame pointer register - auto gp = Loader->GetSymbolAddr( "__global_pointer$" ); - RegState->SetX( RevReg::gp, gp ); - RegState->SetX( RevReg::fp, gp ); + auto gp = Loader->GetSymbolAddr( "__global_pointer$" ); + RegFile->SetX( RevReg::gp, gp ); + RegFile->SetX( RevReg::fp, gp ); // Set state to Ready ThreadToInit->SetState( ThreadState::READY ); @@ -659,11 +664,11 @@ void RevCPU::InitThread( std::unique_ptr&& ThreadToInit ) { ReadyThreads.emplace_back( std::move( ThreadToInit ) ); } -// Assigns a RevThred to a specific Proc which then loads it into a RevHart +// Assigns a RevThread to a specific Proc which then loads it into a RevHart // This should not be called without first checking if the Proc has an IdleHart -void RevCPU::AssignThread( std::unique_ptr&& ThreadToAssign, unsigned ProcID ) { +void RevCPU::SetThread( std::unique_ptr&& ThreadToAssign, unsigned ProcID ) { output.verbose( CALL_INFO, 4, 0, "Assigning Thread %" PRIu32 " to Processor %" PRIu32 "\n", ThreadToAssign->GetID(), ProcID ); - Procs[ProcID]->AssignThread( std::move( ThreadToAssign ) ); + Procs.at( ProcID )->SetThread( std::move( ThreadToAssign ) ); return; } @@ -721,7 +726,7 @@ void RevCPU::UpdateThreadAssignments( uint32_t ProcID ) { if( Procs[ProcID]->HasIdleHart() ) { // There is room, assign a thread // Get the next thread to assign - Procs[ProcID]->AssignThread( std::move( ReadyThreads.front() ) ); + Procs[ProcID]->SetThread( std::move( ReadyThreads.front() ) ); // Remove thread from ready threads vector ReadyThreads.erase( ReadyThreads.begin() ); @@ -802,10 +807,10 @@ void RevCPU::HandleThreadStateChangesForProc( uint32_t ProcID ) { } void RevCPU::InitMainThread( uint32_t MainThreadID, const uint64_t StartAddr ) { - auto MainThreadRegState = std::make_unique( Procs[0].get() ); + auto RegFile = std::make_unique( Procs[0].get() ); // The Program Counter gets set to the start address - MainThreadRegState->SetPC( StartAddr ); + RegFile->SetPC( StartAddr ); // We need to initialize the a0 register to the value of ARGC. // This is >= 1 (the executable name is always included). @@ -813,8 +818,8 @@ void RevCPU::InitMainThread( uint32_t MainThreadID, const uint64_t StartAddr ) { // of the ARGV base pointer in memory which is currently set to // the top of stack. uint64_t stackTop = Mem->GetStackTop(); - MainThreadRegState->SetX( RevReg::a0, Opts->GetArgv().size() + 1 ); - MainThreadRegState->SetX( RevReg::a1, stackTop ); + RegFile->SetX( RevReg::a0, Opts->GetArgv().size() + 1 ); + RegFile->SetX( RevReg::a1, stackTop ); // We subtract Mem->GetTLSSize() bytes from the current stack top, and // round down to a multiple of 16 bytes. We set it as the new top of stack. @@ -824,18 +829,18 @@ void RevCPU::InitMainThread( uint32_t MainThreadID, const uint64_t StartAddr ) { // We set the stack pointer and the thread pointer to the new stack top // The thread local storage is accessed with a nonnegative offset from tp, // and the stack grows down with sp being subtracted from before storing. - MainThreadRegState->SetX( RevReg::sp, stackTop ); - MainThreadRegState->SetX( RevReg::tp, stackTop ); + RegFile->SetX( RevReg::sp, stackTop ); + RegFile->SetX( RevReg::tp, stackTop ); auto gp = Loader->GetSymbolAddr( "__global_pointer$" ); - MainThreadRegState->SetX( RevReg::gp, gp ); - MainThreadRegState->SetX( RevReg::fp, gp ); + RegFile->SetX( RevReg::gp, gp ); + RegFile->SetX( RevReg::fp, gp ); std::unique_ptr MainThread = std::make_unique( MainThreadID, _INVALID_TID_, // No Parent Thread ID Mem->GetThreadMemSegs().front(), - std::move( MainThreadRegState ) + std::move( RegFile ) ); MainThread->SetState( ThreadState::READY ); diff --git a/src/RevCore.cc b/src/RevCore.cc index 56b89960f..6fbbc866a 100644 --- a/src/RevCore.cc +++ b/src/RevCore.cc @@ -27,7 +27,7 @@ std::unique_ptr RevCore::CreateFeature() { opts->GetMemCost( id, MinCost, MaxCost ); - return std::make_unique( std::move( Machine ), output, MinCost, MaxCost, id ); + return std::make_unique( std::move( Machine ), output, MinCost, MaxCost, id, RandomizeCosts ); } RevCore::RevCore( @@ -37,10 +37,11 @@ RevCore::RevCore( RevMem* mem, RevLoader* loader, std::function GetNewTID, - SST::Output* output + SST::Output* output, + bool RandomizeCosts ) - : id( id ), numHarts( numHarts ), opts( opts ), mem( mem ), loader( loader ), GetNewThreadID( std::move( GetNewTID ) ), - output( output ), featureUP( CreateFeature() ) { + : RandomizeCosts( RandomizeCosts ), id( id ), numHarts( numHarts ), opts( opts ), mem( mem ), loader( loader ), + GetNewThreadID( std::move( GetNewTID ) ), output( output ) { LSQueue = std::make_shared>(); LSQueue->clear(); @@ -845,7 +846,10 @@ RevInst RevCore::DecodeCompressed( uint32_t Inst ) const { if( !feature->HasCompressed() ) { output->fatal( - CALL_INFO, -1, "Error: failed to decode instruction at PC=0x%" PRIx64 "; Compressed instructions not enabled!\n", GetPC() + CALL_INFO, + -1, + "Error: failed to decode instruction at PC=0x%" PRIx64 "; Compressed instructions not enabled!\n", + GetRegFile( HartToDecodeID )->GetPC() ); } @@ -903,7 +907,7 @@ RevInst RevCore::DecodeCompressed( uint32_t Inst ) const { bool isCoProcInst = false; auto it = matchInst( CEncToEntry, Enc, InstTable, Inst ); if( it == CEncToEntry.end() ) { - if( coProc && coProc->IssueInst( feature, RegFile, mem, Inst ) ) { + if( coProc && coProc->IssueInst( feature, GetRegFile( HartToDecodeID ), mem, Inst ) ) { isCoProcInst = true; //Create NOP - ADDI x0, x0, 0 uint8_t caddi_op = 0b01; @@ -920,7 +924,7 @@ RevInst RevCore::DecodeCompressed( uint32_t Inst ) const { -1, "Error: failed to decode instruction at PC=0x%" PRIx64 "; Enc=%" PRIu32 "\n opc=%x; funct2=%x, funct3=%x, funct4=%x, funct6=%x\n", - GetPC(), + GetRegFile( HartToDecodeID )->GetPC(), Enc, opc, funct2, @@ -937,7 +941,7 @@ RevInst RevCore::DecodeCompressed( uint32_t Inst ) const { -1, "Error: no entry in table for instruction at PC=0x%" PRIx64 " Opcode = %x Funct2 = %x Funct3 = %x Funct4 = %x Funct6 = " "%x Enc = %x \n", - GetPC(), + GetRegFile( HartToDecodeID )->GetPC(), opc, funct2, funct3, @@ -959,7 +963,10 @@ RevInst RevCore::DecodeCompressed( uint32_t Inst ) const { case RVCTypeCA: ret = DecodeCAInst( Inst, Entry ); break; case RVCTypeCB: ret = DecodeCBInst( Inst, Entry ); break; case RVCTypeCJ: ret = DecodeCJInst( Inst, Entry ); break; - default: output->fatal( CALL_INFO, -1, "Error: failed to decode instruction format at PC=%" PRIx64 ".", GetPC() ); + default: + output->fatal( + CALL_INFO, -1, "Error: failed to decode instruction format at PC=%" PRIx64 ".", GetRegFile( HartToDecodeID )->GetPC() + ); } ret.entry = Entry; @@ -1246,22 +1253,11 @@ bool RevCore::DebugWriteReg( unsigned Idx, uint64_t Value ) const { return true; } -bool RevCore::PrefetchInst() { - uint64_t PC = Harts[HartToDecodeID]->RegFile->GetPC(); - - // These are addresses that we can't decode - // Return false back to the main program loop - if( PC == 0x00ull ) { - return false; - } - - return sfetch->IsAvail( PC ); -} - RevInst RevCore::FetchAndDecodeInst() { - uint32_t Inst = 0x00ul; - uint64_t PC = GetPC(); - bool Fetched = false; + uint32_t Inst = 0; + RevRegFile* RegFile = GetRegFile( HartToDecodeID ); + uint64_t PC = RegFile->GetPC(); + bool Fetched = false; // Stage 1: Retrieve the instruction if( !sfetch->InstFetch( PC, Fetched, Inst ) ) { @@ -1300,16 +1296,8 @@ RevInst RevCore::FetchAndDecodeInst() { CrackFault = false; } - // Decode the instruction - RevInst DInst = DecodeInst( Inst ); - - // Set RegFile Entry and cost, and clear trigger - RegFile->SetEntry( DInst.entry ); - RegFile->SetCost( DInst.cost ); - RegFile->SetTrigger( false ); - // Return decoded instruction - return DInst; + return DecodeInst( Inst ); } // Decode the instruction @@ -1317,6 +1305,8 @@ RevInst RevCore::FetchAndDecodeInst() { // on non-constant outside variables. This make it memoizable, // but right now, there isn't enough benefit for memoization. RevInst RevCore::DecodeInst( uint32_t Inst ) const { + RevRegFile* RegFile = GetRegFile( HartToDecodeID ); + if( ~Inst & 0b11 ) { // this is a compressed instruction return DecodeCompressed( Inst ); @@ -1433,7 +1423,13 @@ RevInst RevCore::DecodeInst( uint32_t Inst ) const { if( it == EncToEntry.end() ) { // failed to decode the instruction - output->fatal( CALL_INFO, -1, "Error: failed to decode instruction at PC=0x%" PRIx64 "; Enc=%" PRIu64 "\n", GetPC(), Enc ); + output->fatal( + CALL_INFO, + -1, + "Error: failed to decode instruction at PC=0x%" PRIx64 "; Enc=%" PRIu64 "\n", + GetRegFile( HartToDecodeID )->GetPC(), + Enc + ); } unsigned Entry = it->second; @@ -1454,7 +1450,7 @@ RevInst RevCore::DecodeInst( uint32_t Inst ) const { -1, "Error: no entry in table for instruction at PC=0x%" PRIx64 " Opcode = %x Funct3 = %x Funct2or7 = %x Imm12 = %x Enc = %" PRIx64 "\n", - GetPC(), + GetRegFile( HartToDecodeID )->GetPC(), Opcode, Funct3, Funct2or7, @@ -1473,7 +1469,10 @@ RevInst RevCore::DecodeInst( uint32_t Inst ) const { case RVTypeB: ret = DecodeBInst( Inst, Entry ); break; case RVTypeJ: ret = DecodeJInst( Inst, Entry ); break; case RVTypeR4: ret = DecodeR4Inst( Inst, Entry ); break; - default: output->fatal( CALL_INFO, -1, "Error: failed to decode instruction format at PC=%" PRIx64 ".", GetPC() ); + default: + output->fatal( + CALL_INFO, -1, "Error: failed to decode instruction format at PC=%" PRIx64 ".", GetRegFile( HartToDecodeID )->GetPC() + ); } ret.entry = Entry; @@ -1530,7 +1529,7 @@ void RevCore::HandleALUFault( unsigned width ) { } bool RevCore::DependencyCheck( unsigned HartID, const RevInst* I ) const { - const RevRegFile* regFile = GetRegFile( HartID ); + const RevRegFile* RegFile = GetRegFile( HartID ); const RevInstEntry* E = &InstTable[I->entry]; // For ECALL, check for any outstanding dependencies on a0-a7 @@ -1545,13 +1544,13 @@ bool RevCore::DependencyCheck( unsigned HartID, const RevInst* I ) const { return // check LS queue for outstanding load - LSQCheck( HartID, regFile, I->rs1, E->rs1Class ) || LSQCheck( HartID, regFile, I->rs2, E->rs2Class ) || - LSQCheck( HartID, regFile, I->rs3, E->rs3Class ) || LSQCheck( HartID, regFile, I->rd, E->rdClass ) || + LSQCheck( HartID, RegFile, I->rs1, E->rs1Class ) || LSQCheck( HartID, RegFile, I->rs2, E->rs2Class ) || + LSQCheck( HartID, RegFile, I->rs3, E->rs3Class ) || LSQCheck( HartID, RegFile, I->rd, E->rdClass ) || // Iterate through the source registers rs1, rs2, rs3 and find any dependency // based on the class of the source register and the associated scoreboard - ScoreboardCheck( regFile, I->rs1, E->rs1Class ) || ScoreboardCheck( regFile, I->rs2, E->rs2Class ) || - ScoreboardCheck( regFile, I->rs3, E->rs3Class ); + ScoreboardCheck( RegFile, I->rs1, E->rs1Class ) || ScoreboardCheck( RegFile, I->rs2, E->rs2Class ) || + ScoreboardCheck( RegFile, I->rs3, E->rs3Class ); } void RevCore::ExternalStallHart( RevCorePasskey, uint16_t HartID ) { @@ -1628,66 +1627,91 @@ void RevCore::MarkLoadComplete( const MemReq& req ) { } bool RevCore::ClockTick( SST::Cycle_t currentCycle ) { - RevInst Inst; - bool rtn = false; ++Stats.totalCycles; ++cycles; currentSimCycle = currentCycle; - // -- MAIN PROGRAM LOOP -- - // - // If the clock is down to zero, then fetch the next instruction - // else if the the instruction has not yet been triggered, execute it - // else, wait until the counter is decremented to zero to retire the instruction + if( !FrontEnd() ) + return false; + + if( !BackEnd() ) + return false; + + return true; +} + +///< RevCore: Update status of harts +// A Hart is Cleared To Decode if: +// 1. It has a thread assigned to it (ie. NOT Idle) +// 2. Its last instruction is done executing (ie. cost is set to 0) +// Returns the ID of the hart to decode +unsigned RevCore::GetNextHartToDecodeID() const() { + unsigned nextID = _REV_INVALID_HART_ID_; + if( Harts.size() ) { + usigned ID = CurrentHartID; + do { + if( ++ID >= Harts.size() ) + ID = 0; + if( Harts[ID]->GetState() != RevHartState::Idle && Harts[ID]->GetCost() == 0 ) { + Harts[ID]->SetState( RevHartState::Decoding ); + if( nextID == _REV_INVALID_HART_ID_ ) + nextID = ID; + } + } while( ID != CurrentHartID ); + } + return nextID; +} - // This function updates the bitset of Harts that are - // ready to decode - UpdateStatusOfHarts(); +///< RevCore: Fetch and pipeline instruction +bool RevCore::FrontEnd() { + bool rtn = false; - if( HartsClearToDecode.any() && ( !Halted ) ) { - // Determine what hart is ready to decode - HartToDecodeID = GetNextHartToDecodeID(); - ActiveThreadID = Harts.at( HartToDecodeID )->GetAssignedThreadID(); - RegFile = Harts[HartToDecodeID]->RegFile.get(); + // Determine which hart is ready to decode + unsigned HartToDecodeID = GetNextHartToDecodeID(); - feature->SetHartToExecID( HartToDecodeID ); + if( HartToDecodeID != _REV_INVALID_HART_ID_ && !Halted ) { + ActiveThreadID = Harts.at( HartToDecodeID )->GetThreadID(); + RegFile = GetRegFile( HartToDecodeID ); // fetch the next instruction - if( !PrefetchInst() ) { - Stalled = true; - Stats.cyclesStalled++; - } else { - Stalled = false; - } + uint64_t PC = RegFile->GetPC(); - if( !Stalled && !CoProcStallReq[HartToDecodeID] ) { - Inst = FetchAndDecodeInst(); - Inst.entry = RegFile->GetEntry(); - } + // These are addresses that we can't decode + // Return false back to the main program loop + bool Stalled = !PC || !sfetch->IsAvail( PC ); - // Now that we have decoded the instruction, check for pipeline hazards - if( ExecEcall() || Stalled || DependencyCheck( HartToDecodeID, &Inst ) || CoProcStallReq[HartToDecodeID] ) { - RegFile->SetCost( 0 ); // We failed dependency check, so set cost to 0 - this will - Stats.cyclesIdle_Pipeline++; // prevent the instruction from advancing to the next stage - HartsClearToExecute[HartToDecodeID] = false; - HartToExecID = _REV_INVALID_HART_ID_; - } else { - Stats.cyclesBusy++; - HartsClearToExecute[HartToDecodeID] = true; - HartToExecID = HartToDecodeID; + if( Stalled ) { + Stats.cyclesStalled++; + Stats.cyclesIdle_Pipeline++; + } else if( !CoProcStallReq[HartToDecodeID] ) { + // Fetch and Decode Instruction + RevInst Inst = FetchAndDecodeInst(); + + // Now that we have decoded the instruction, check for pipeline hazards + if( ExecEcall() || DependencyCheck( HartToDecodeID, &Inst ) || CoProcStallReq[HartToDecodeID] ) { + Stats.cyclesIdle_Pipeline++; + } else { + Stats.cyclesBusy++; + + // Pineline the instruction + Pipeline.emplace_front( HartToDecodeID, Inst ); + } } - Inst.cost = RegFile->GetCost(); - Inst.entry = RegFile->GetEntry(); - rtn = true; - ExecPC = RegFile->GetPC(); } - if( ( ( HartToExecID != _REV_INVALID_HART_ID_ ) && !RegFile->GetTrigger() ) && !Halted && HartsClearToExecute[HartToExecID] ) { - // trigger the next instruction - // HartToExecID = HartToDecodeID; - RegFile->SetTrigger( true ); + return rtn; +} + +bool RevCore::Backend_Execute( RevInst& Inst ) { + bool rtn = false; + RevRegFile* RegFile = Harts[HartToExecID]->GetRegFile(); + uint32_t ThreadID = Harts[HartToExecID]->GetThreadID(); + uint64_t ExecPC = RegFile->GetPC(); + + if( Inst.cost ) { + --Inst.cost; -#ifdef NO_REV_TRACER +#ifndef NO_REV_TRACER // pull the PC output->verbose( CALL_INFO, @@ -1696,13 +1720,13 @@ bool RevCore::ClockTick( SST::Cycle_t currentCycle ) { "Core %" PRIu32 "; Hart %" PRIu32 "; Thread %" PRIu32 "; Executing PC= 0x%" PRIx64 "\n", id, HartToExecID, - ActiveThreadID, + ThreadID, ExecPC ); #endif // Find the instruction extension - auto it = EntryToExt.find( RegFile->GetEntry() ); + auto it = EntryToExt.find( Inst.entry ); if( it == EntryToExt.end() ) { // failed to find the extension output->fatal( CALL_INFO, -1, "Error: failed to find the instruction extension at PC=%" PRIx64 ".", ExecPC ); @@ -1712,16 +1736,11 @@ bool RevCore::ClockTick( SST::Cycle_t currentCycle ) { std::pair EToE = it->second; RevExt* Ext = Extensions[EToE.first].get(); - // -- BEGIN new pipelining implementation - Pipeline.emplace_back( std::make_pair( HartToExecID, Inst ) ); - - if( ( Ext->GetName() == "RV32F" ) || ( Ext->GetName() == "RV32D" ) || ( Ext->GetName() == "RV64F" ) || ( Ext->GetName() == "RV64D" ) ) { + if( Ext->isFloat ) Stats.floatsExec++; - } // set the hazarding - DependencySet( HartToExecID, &( Pipeline.back().second ) ); - // -- END new pipelining implementation + DependencySet( HartToExecID, &Inst ); #ifndef NO_REV_TRACER // Tracer context @@ -1730,7 +1749,7 @@ bool RevCore::ClockTick( SST::Cycle_t currentCycle ) { #endif // execute the instruction - if( !Ext->Execute( EToE.second, Pipeline.back().second, HartToExecID, RegFile ) ) { + if( !Ext->Execute( EToE.second, Inst, HartToExecID, RegFile ) ) { output->fatal( CALL_INFO, -1, "Error: failed to execute instruction at PC=%" PRIx64 ".", ExecPC ); } @@ -1770,303 +1789,271 @@ bool RevCore::ClockTick( SST::Cycle_t currentCycle ) { } rtn = true; - } else { - // wait until the counter has been decremented - // note that this will continue to occur until the counter is drained - // and the HART is halted - output->verbose( CALL_INFO, 9, 0, "Core %" PRIu32 " ; No available thread to exec PC= 0x%" PRIx64 "\n", id, ExecPC ); - rtn = true; - Stats.cyclesIdle_Total++; - if( HartsClearToExecute.any() ) { - Stats.cyclesIdle_MemoryFetch++; - } } + return rtn; +} + +///< RevCore: Execute instruction in the pipeline +bool RevCore::BackEnd() { + if( !Pipeline.empty() && !Halted ) { + auto& [HartToExecID, Inst] = Pipeline.back(); + this->HartToExecID = HartToExecID; + + if( !AnyDependency( HartToExecID ) ) { + BackEnd_Execute( Inst ); + } - // Check for pipeline hazards - if( !Pipeline.empty() && ( Pipeline.front().second.cost > 0 ) ) { - Pipeline.front().second.cost--; - if( Pipeline.front().second.cost == 0 ) { // && + if( !Inst.cost ) { // Ready to retire this instruction - uint16_t HartID = Pipeline.front().first; -#ifdef NO_REV_TRACER + ++Stats.retired; + RegFile->IncrementInstRet(); + +#ifndef NO_REV_TRACER output->verbose( CALL_INFO, 6, 0, "Core %" PRIu32 "; Hart %" PRIu32 "; ThreadID %" PRIu32 "; Retiring PC= 0x%" PRIx64 "\n", id, - HartID, - ActiveThreadID, + ExecID, + ThreadID, ExecPC ); #endif - ++Stats.retired; - RegFile->IncrementInstRet(); - // Only clear the dependency if there is no outstanding load - if( ( RegFile->GetLSQueue()->count( LSQHash( Pipeline.front().second.rd, InstTable[Pipeline.front().second.entry].rdClass, HartID ) ) ) == 0 ) { - DependencyClear( HartID, &( Pipeline.front().second ) ); - } - Pipeline.pop_front(); - RegFile->SetCost( 0 ); - } else { - // could not retire the instruction, bump the cost - Pipeline.front().second.cost++; +#if 0 + else { + // wait until the counter has been decremented + // note that this will continue to occur until the counter is drained + // and the HART is halted + output->verbose( CALL_INFO, 9, 0, "Core %" PRIu32 " ; No available thread to exec PC= 0x%" PRIx64 "\n", id, ExecPC ); + rtn = true; + Stats.cyclesIdle_Total++; + if( HartsClearToExecute.any() ) { + Stats.cyclesIdle_MemoryFetch++; } } - // Check for completion states and new tasks - if( RegFile->GetPC() == 0x00ull ) { - // look for more work on the execution queue - // if no work is found, don't update the PC - // just wait and spin - if( HartHasNoDependencies( HartToDecodeID ) ) { - std::unique_ptr ActiveThread = PopThreadFromHart( HartToDecodeID ); - ActiveThread->SetState( ThreadState::DONE ); - HartsClearToExecute[HartToDecodeID] = false; - HartsClearToDecode[HartToDecodeID] = false; - IdleHarts.set( HartToDecodeID ); - AddThreadsThatChangedState( std::move( ActiveThread ) ); - } +#endif + + // Only clear the dependency if there is no outstanding load + if( RegFile->GetLSQueue()->count( LSQHash( Inst.rd, InstTable[Inst.entry].rdClass, ExecID ) ) == 0 ) { + DependencyClear( HartID, &Inst ); + } + + // Check for completion states and new tasks + if( !GetRegFile->GetPC() ) { + + // look for more work on the execution queue + // if no work is found, don't update the PC + // just wait and spin - if( HartToExecID != _REV_INVALID_HART_ID_ && !IdleHarts[HartToExecID] && HartHasNoDependencies( HartToExecID ) ) { - std::unique_ptr ActiveThread = PopThreadFromHart( HartToDecodeID ); - ActiveThread->SetState( ThreadState::DONE ); - HartsClearToExecute[HartToExecID] = false; - HartsClearToDecode[HartToExecID] = false; - IdleHarts[HartToExecID] = true; - AddThreadsThatChangedState( std::move( ActiveThread ) ); + if( !IdleHarts[HartToExecID] && HartHasNoDependencies( HartToExecID ) ) { + std::unique_ptr ActiveThread = PopThreadFromHart( HartToExecID ); + ThreadId->SetState( ThreadState::DONE ); + Harts[HartToExecID]->SetState( RevHartState::Idle ); + AddThreadsThatChangedState( std::move( ActiveThread ) ); + } + } + + Pipeline.pop_back(); } - } #ifndef NO_REV_TRACER - // Dump trace state - if( Tracer ) - Tracer->Render( currentCycle ); + // Dump trace state + if( Tracer ) + Tracer->Render( GetCurrentSimCycle() ); #endif - return rtn; -} - -std::unique_ptr RevCore::PopThreadFromHart( unsigned HartID ) { - if( HartID >= numHarts ) { - output->fatal( - CALL_INFO, -1, "Error: tried to pop thread from hart %" PRIu32 " but there are only %" PRIu32 " hart(s)\n", HartID, numHarts - ); + return rtn; } - IdleHarts[HartID] = true; - return Harts.at( HartID )->PopThread(); -} -void RevCore::PrintStatSummary() { - auto memStatsTotal = mem->GetMemStatsTotal(); + std::unique_ptr RevCore::PopThreadFromHart( unsigned HartID ) { + if( HartID >= numHarts ) { + output->fatal( + CALL_INFO, -1, "Error: tried to pop thread from hart %" PRIu32 " but there are only %" PRIu32 " hart(s)\n", HartID, numHarts + ); + } + IdleHarts[HartID] = true; + return Harts.at( HartID )->PopThread(); + } - double eff = StatsTotal.totalCycles ? double( StatsTotal.cyclesBusy ) / StatsTotal.totalCycles : 0; - output->verbose( - CALL_INFO, - 2, - 0, - "Program execution complete\n" - "Core %u Program Stats: Total Cycles: %" PRIu64 " Busy Cycles: %" PRIu64 " Idle Cycles: %" PRIu64 " Eff: %f\n", - id, - StatsTotal.totalCycles, - StatsTotal.cyclesBusy, - StatsTotal.cyclesIdle_Total, - eff - ); + void RevCore::PrintStatSummary() { + auto memStatsTotal = mem->GetMemStatsTotal(); - output->verbose( - CALL_INFO, - 3, - 0, - "\t Bytes Read: %" PRIu64 " Bytes Written: %" PRIu64 " Floats Read: %" PRIu64 " Doubles Read %" PRIu64 " Floats Exec: %" PRIu64 - " TLB Hits: %" PRIu64 " TLB Misses: %" PRIu64 " Inst Retired: %" PRIu64 "\n\n", - memStatsTotal.bytesRead, - memStatsTotal.bytesWritten, - memStatsTotal.floatsRead, - memStatsTotal.doublesRead, - StatsTotal.floatsExec, - memStatsTotal.TLBHits, - memStatsTotal.TLBMisses, - StatsTotal.retired - ); -} + double eff = StatsTotal.totalCycles ? double( StatsTotal.cyclesBusy ) / StatsTotal.totalCycles : 0; + output->verbose( + CALL_INFO, + 2, + 0, + "Program execution complete\n" + "Core %u Program Stats: Total Cycles: %" PRIu64 " Busy Cycles: %" PRIu64 " Idle Cycles: %" PRIu64 " Eff: %f\n", + id, + StatsTotal.totalCycles, + StatsTotal.cyclesBusy, + StatsTotal.cyclesIdle_Total, + eff + ); -RevRegFile* RevCore::GetRegFile( unsigned HartID ) const { - if( HartID >= Harts.size() ) { - output->fatal( - CALL_INFO, -1, "Error: tried to get RegFile for Hart %" PRIu32 " but there are only %" PRIu32 " hart(s)\n", HartID, numHarts + output->verbose( + CALL_INFO, + 3, + 0, + "\t Bytes Read: %" PRIu64 " Bytes Written: %" PRIu64 " Floats Read: %" PRIu64 " Doubles Read %" PRIu64 + " Floats Exec: %" PRIu64 " TLB Hits: %" PRIu64 " TLB Misses: %" PRIu64 " Inst Retired: %" PRIu64 "\n\n", + memStatsTotal.bytesRead, + memStatsTotal.bytesWritten, + memStatsTotal.floatsRead, + memStatsTotal.doublesRead, + StatsTotal.floatsExec, + memStatsTotal.TLBHits, + memStatsTotal.TLBMisses, + StatsTotal.retired ); } - return Harts.at( HartID )->RegFile.get(); -} - -void RevCore::CreateThread( uint32_t NewTID, uint64_t firstPC, void* arg ) { - // tidAddr is the address we have to write the new thread's id to - output->verbose( CALL_INFO, 2, 0, "Creating new thread with PC = 0x%" PRIx64 "\n", firstPC ); - uint32_t ParentThreadID = Harts.at( HartToExecID )->GetAssignedThreadID(); - // Create the new thread's memory - std::shared_ptr NewThreadMem = mem->AddThreadMem(); + void RevCore::CreateThread( uint32_t NewTID, uint64_t firstPC, void* arg ) { + // tidAddr is the address we have to write the new thread's id to + output->verbose( CALL_INFO, 2, 0, "Creating new thread with PC = 0x%" PRIx64 "\n", firstPC ); + uint32_t ParentThreadID = Harts.at( HartToExecID )->GetThreadID(); - // TODO: Copy TLS into new memory + // Create the new thread's memory + std::shared_ptr NewThreadMem = mem->AddThreadMem(); - // Create new register file - auto NewThreadRegFile = std::make_unique( this ); + // TODO: Copy TLS into new memory - // Copy the arg to the new threads a0 register - NewThreadRegFile->SetX( RevReg::a0, reinterpret_cast( arg ) ); + // Create new register file + auto NewThreadRegFile = std::make_unique( this ); - // Set the stack and thread pointer - // The thread local storage is accessed with a nonnegative offset from tp, - // and the stack grows down with sp being subtracted from before storing. - uint64_t sp = ( NewThreadMem->getTopAddr() - mem->GetTLSSize() ) & ~uint64_t{ 15 }; - NewThreadRegFile->SetX( RevReg::tp, sp ); - NewThreadRegFile->SetX( RevReg::sp, sp ); + // Copy the arg to the new threads a0 register + NewThreadRegFile->SetX( RevReg::a0, reinterpret_cast( arg ) ); - // Set the global pointer - auto gp = loader->GetSymbolAddr( "__global_pointer$" ); - NewThreadRegFile->SetX( RevReg::gp, gp ); - NewThreadRegFile->SetX( RevReg::fp, gp ); // frame pointer register - NewThreadRegFile->SetPC( firstPC ); + // Set the stack and thread pointer + // The thread local storage is accessed with a nonnegative offset from tp, + // and the stack grows down with sp being subtracted from before storing. + uint64_t sp = ( NewThreadMem->getTopAddr() - mem->GetTLSSize() ) & ~uint64_t{ 15 }; + NewThreadRegFile->SetX( RevReg::tp, sp ); + NewThreadRegFile->SetX( RevReg::sp, sp ); - // Create a new RevThread Object - std::unique_ptr NewThread = - std::make_unique( NewTID, ParentThreadID, NewThreadMem, std::move( NewThreadRegFile ) ); + // Set the global pointer + auto gp = loader->GetSymbolAddr( "__global_pointer$" ); + NewThreadRegFile->SetX( RevReg::gp, gp ); + NewThreadRegFile->SetX( RevReg::fp, gp ); // frame pointer register + NewThreadRegFile->SetPC( firstPC ); - // Add new thread to this vector so the RevCPU will add and schedule it - AddThreadsThatChangedState( std::move( NewThread ) ); -} + // Create a new RevThread Object + std::unique_ptr NewThread = + std::make_unique( NewTID, ParentThreadID, NewThreadMem, std::move( NewThreadRegFile ) ); -// -// This is the function that is called when an ECALL exception is detected inside ClockTick -// - Currently the only way to set this exception is by Ext->Execute(....) an ECALL instruction -// -// Eventually this will be integrated into a TrapHandler however since ECALLs are the only -// supported exceptions at this point there is no need just yet. -// -// Returns true if an ECALL is in progress -bool RevCore::ExecEcall() { - if( RegFile->GetSCAUSE() != RevExceptionCause::ECALL_USER_MODE ) - return false; - - // ECALL in progress - uint32_t EcallCode = RegFile->GetX( RevReg::a7 ); - output->verbose( - CALL_INFO, - 6, - 0, - "Core %" PRIu32 "; Hart %" PRIu32 "; Thread %" PRIu32 " - Exception Raised: ECALL with code = %" PRIu32 "\n", - id, - HartToExecID, - ActiveThreadID, - EcallCode - ); - - // TODO: Cache handler function during ECALL instruction execution - auto it = Ecalls.find( EcallCode ); - if( it == Ecalls.end() ) { - output->fatal( CALL_INFO, -1, "Ecall Code = %" PRIu32 " not found", EcallCode ); + // Add new thread to this vector so the RevCPU will add and schedule it + AddThreadsThatChangedState( std::move( NewThread ) ); } - // Execute the Ecall handler - HartToExecID = HartToDecodeID; - bool completed = ( this->*it->second )() != EcallStatus::CONTINUE; + // + // This is the function that is called when an ECALL exception is detected inside ClockTick + // - Currently the only way to set this exception is by Ext->Execute(....) an ECALL instruction + // + // Eventually this will be integrated into a TrapHandler however since ECALLs are the only + // supported exceptions at this point there is no need just yet. + // + // Returns true if an ECALL is in progress + bool RevCore::ExecEcall() { + RevRegFile* RegFile = GetRegFile( HartToDecodeID ); + if( RegFile->GetSCAUSE() != RevExceptionCause::ECALL_USER_MODE ) + return false; + + // ECALL in progress + uint32_t EcallCode = RegFile->GetX( RevReg::a7 ); + output->verbose( + CALL_INFO, + 6, + 0, + "Core %" PRIu32 "; Hart %" PRIu32 "; Thread %" PRIu32 " - Exception Raised: ECALL with code = %" PRIu32 "\n", + id, + HartToExecID, + ActiveThreadID, + EcallCode + ); - // If we have completed, reset the EcallState and SCAUSE - if( completed ) { - Harts[HartToDecodeID]->GetEcallState().clear(); - RegFile->SetSCAUSE( RevExceptionCause::NONE ); - } + // TODO: Cache handler function during ECALL instruction execution + auto it = Ecalls.find( EcallCode ); + if( it == Ecalls.end() ) { + output->fatal( CALL_INFO, -1, "Ecall Code = %" PRIu32 " not found", EcallCode ); + } - return true; -} + // Execute the Ecall handler + HartToExecID = HartToDecodeID; + bool completed = ( this->*it->second )() != EcallStatus::CONTINUE; -// Looks for a hart without a thread assigned to it and then assigns it. -// This function should never be called if there are no available harts -// so if for some reason we can't find a hart without a thread assigned -// to it then we have a bug. -void RevCore::AssignThread( std::unique_ptr Thread ) { - unsigned HartToAssign = FindIdleHartID(); + // If we have completed, reset the EcallState and SCAUSE + if( completed ) { + GetEcallState( HartToDecodeID ).clear(); + RegFile->SetSCAUSE( RevExceptionCause::NONE ); + } - if( HartToAssign == _REV_INVALID_HART_ID_ ) { - output->fatal( - CALL_INFO, - 1, - "Attempted to assign a thread to a hart but no available harts were " - "found.\n" - "We should never have tried to assign a thread to this Core if it had no " - "harts available (ie. Core->NumIdleHarts() == 0 ).\n" - "This is a bug\n" - ); + return true; } - // Assign the thread to the hart - Harts.at( HartToAssign )->AssignThread( std::move( Thread ) ); - - IdleHarts[HartToAssign] = false; - - return; -} + // Looks for a hart without a thread assigned to it and then assigns it. + // This function should never be called if there are no available harts + // so if for some reason we can't find a hart without a thread assigned + // to it then we have a bug. + void RevCore::SetThread( std::unique_ptr Thread ) { + unsigned HartToAssign = FindIdleHartID(); -unsigned RevCore::FindIdleHartID() const { - unsigned IdleHartID = _REV_INVALID_HART_ID_; - // Iterate over IdleHarts to find the first idle hart - for( size_t i = 0; i < Harts.size(); i++ ) { - if( IdleHarts[i] ) { - IdleHartID = i; - break; + if( HartToAssign == _REV_INVALID_HART_ID_ ) { + output->fatal( + CALL_INFO, + 1, + "Attempted to assign a thread to a hart but no available harts were " + "found.\n" + "We should never have tried to assign a thread to this Core if it had no " + "harts available (ie. Core->NumIdleHarts() == 0 ).\n" + "This is a bug\n" + ); } - } - if( IdleHartID == _REV_INVALID_HART_ID_ ) { - output->fatal( CALL_INFO, -1, "Attempted to find an idle hart but none were found. This is a bug\n" ); - } - return IdleHartID; -} + // Assign the thread to the hart + Harts.at( HartToAssign )->SetThread( std::move( Thread ) ); -void RevCore::InjectALUFault( std::pair EToE, RevInst& Inst ) { - // inject ALU fault - RevExt* Ext = Extensions[EToE.first].get(); - if( ( Ext->GetName() == "RV64F" ) || ( Ext->GetName() == "RV64D" ) ) { - // write an rv64 float rd - uint64_t tmp; - static_assert( sizeof( tmp ) == sizeof( RegFile->DPF[Inst.rd] ) ); - memcpy( &tmp, &RegFile->DPF[Inst.rd], sizeof( tmp ) ); - tmp |= RevRand( 0, ~( ~uint64_t{ 0 } << fault_width ) ); - memcpy( &RegFile->DPF[Inst.rd], &tmp, sizeof( tmp ) ); - } else if( ( Ext->GetName() == "RV32F" ) || ( Ext->GetName() == "RV32D" ) ) { - // write an rv32 float rd - uint32_t tmp; - static_assert( sizeof( tmp ) == sizeof( RegFile->SPF[Inst.rd] ) ); - memcpy( &tmp, &RegFile->SPF[Inst.rd], sizeof( tmp ) ); - tmp |= RevRand( 0, ~( uint32_t{ 0 } << fault_width ) ); - memcpy( &RegFile->SPF[Inst.rd], &tmp, sizeof( tmp ) ); - } else { - // write an X register - uint64_t rval = RevRand( 0, ~( ~uint64_t{ 0 } << fault_width ) ); - RegFile->SetX( Inst.rd, rval | RegFile->GetX( Inst.rd ) ); + IdleHarts[HartToAssign] = false; + + return; } - // clear the fault - ALUFault = false; -} + void RevCore::InjectALUFault( std::pair EToE, RevInst & Inst ) { + // inject ALU fault + RevExt* Ext = Extensions[EToE.first].get(); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + if( ( Ext->GetName() == "RV64F" ) || ( Ext->GetName() == "RV64D" ) ) { + // write an rv64 float rd + uint64_t tmp; + static_assert( sizeof( tmp ) == sizeof( RegFile->DPF[Inst.rd] ) ); + memcpy( &tmp, &RegFile->DPF[Inst.rd], sizeof( tmp ) ); + tmp |= RevRand( 0, ~( ~uint64_t{ 0 } << fault_width ) ); + memcpy( &RegFile->DPF[Inst.rd], &tmp, sizeof( tmp ) ); + } else if( ( Ext->GetName() == "RV32F" ) || ( Ext->GetName() == "RV32D" ) ) { + // write an rv32 float rd + uint32_t tmp; + static_assert( sizeof( tmp ) == sizeof( RegFile->SPF[Inst.rd] ) ); + memcpy( &tmp, &RegFile->SPF[Inst.rd], sizeof( tmp ) ); + tmp |= RevRand( 0, ~( uint32_t{ 0 } << fault_width ) ); + memcpy( &RegFile->SPF[Inst.rd], &tmp, sizeof( tmp ) ); + } else { + // write an X register + uint64_t rval = RevRand( 0, ~( ~uint64_t{ 0 } << fault_width ) ); + RegFile->SetX( Inst.rd, rval | RegFile->GetX( Inst.rd ) ); + } -///< RevCore: Used by RevCPU to determine if it can disable this proc -/// based on the criteria there are no threads assigned to it and the -/// CoProc is done -bool RevCore::HasNoWork() const { - return HasNoBusyHarts() && ( !coProc || coProc->IsDone() ); -} + // clear the fault + ALUFault = false; + } -void RevCore::UpdateStatusOfHarts() { - // A Hart is ClearToDecode if: - // 1. It has a thread assigned to it (ie. NOT Idle) - // 2. It's last instruction is done executing (ie. cost is set to 0) - for( size_t i = 0; i < Harts.size(); i++ ) { - HartsClearToDecode[i] = !IdleHarts[i] && Harts[i]->RegFile->cost == 0; + ///< RevCore: Used by RevCPU to determine if it can disable this proc + /// based on the criteria there are no threads assigned to it and the + /// CoProc is done + bool RevCore::HasNoWork() const { + return HasNoBusyHarts() && ( !coProc || coProc->IsDone() ); } - return; -} } // namespace SST::RevCPU diff --git a/src/RevExt.cc b/src/RevExt.cc index 438eeb7b5..1dc1fdd0a 100644 --- a/src/RevExt.cc +++ b/src/RevExt.cc @@ -13,8 +13,8 @@ namespace SST::RevCPU { /// Execute an instruction -bool RevExt::Execute( unsigned Inst, const RevInst& payload, uint16_t HartID, RevRegFile* regFile ) const { - bool ( *func )( const RevFeature*, RevRegFile*, RevMem*, const RevInst& ); +bool RevExt::Execute( unsigned Inst, RevInst& payload, uint16_t HartID, RevRegFile* regFile ) const { + bool ( *func )( const RevFeature*, RevRegFile*, RevMem*, RevInst& ); if( payload.compressed ) { // this is a compressed instruction, grab the compressed trampoline function diff --git a/src/RevFeature.cc b/src/RevFeature.cc index f336515c2..9cc9689d7 100644 --- a/src/RevFeature.cc +++ b/src/RevFeature.cc @@ -16,8 +16,9 @@ namespace SST::RevCPU { -RevFeature::RevFeature( std::string Machine, SST::Output* Output, unsigned Min, unsigned Max, unsigned Id ) - : machine( std::move( Machine ) ), output( Output ), MinCost( Min ), MaxCost( Max ), ProcID( Id ) { +RevFeature::RevFeature( std::string Machine, SST::Output* Output, unsigned Min, unsigned Max, unsigned Id, bool randomizeCosts ) + : machine( std::move( Machine ) ), output( Output ), MinCost( Min ), MaxCost( Max ), ProcID( Id ), + randomizeCosts( randomizeCosts ) { output->verbose( CALL_INFO, 6, 0, "Core %u ; Initializing feature set from machine string=%s\n", ProcID, machine.c_str() ); if( !ParseMachineModel() ) output->fatal( CALL_INFO, -1, "Error: failed to parse the machine model: %s\n", machine.c_str() ); diff --git a/src/RevOpts.cc b/src/RevOpts.cc index 137b18a6b..fbdaa158b 100644 --- a/src/RevOpts.cc +++ b/src/RevOpts.cc @@ -43,7 +43,7 @@ void RevOpts::SetArgs( const SST::Params& params ) { std::string args = params.find( "args" ); auto nonspace = args.find_first_not_of( delim ); if( nonspace == args.npos || args[nonspace] != '[' ) { - RevOpts::splitStr( args, delim, Argv ); + splitStr( args, delim, Argv ); } else { params.find_array( "args", Argv ); } @@ -51,8 +51,8 @@ void RevOpts::SetArgs( const SST::Params& params ) { bool RevOpts::InitPrefetchDepth( const std::vector& Depths ) { std::vector vstr; - for( unsigned i = 0; i < Depths.size(); i++ ) { - std::string s = Depths[i]; + for( size_t i = 0; i < Depths.size(); i++ ) { + const std::string& s = Depths[i]; splitStr( s, ":", vstr ); if( vstr.size() != 2 ) return false; @@ -75,7 +75,7 @@ bool RevOpts::InitStartAddrs( const std::vector& StartAddrs ) { // check to see if we expand into multiple cores if( StartAddrs.size() == 1 ) { - std::string s = StartAddrs[0]; + const std::string& s = StartAddrs[0]; splitStr( s, ":", vstr ); if( vstr.size() != 2 ) return false; @@ -91,8 +91,8 @@ bool RevOpts::InitStartAddrs( const std::vector& StartAddrs ) { } } - for( unsigned i = 0; i < StartAddrs.size(); i++ ) { - std::string s = StartAddrs[i]; + for( size_t i = 0; i < StartAddrs.size(); i++ ) { + const std::string& s = StartAddrs[i]; splitStr( s, ":", vstr ); if( vstr.size() != 2 ) return false; @@ -112,8 +112,8 @@ bool RevOpts::InitStartAddrs( const std::vector& StartAddrs ) { bool RevOpts::InitStartSymbols( const std::vector& StartSymbols ) { std::vector vstr; - for( unsigned i = 0; i < StartSymbols.size(); i++ ) { - std::string s = StartSymbols[i]; + for( size_t i = 0; i < StartSymbols.size(); i++ ) { + const std::string& s = StartSymbols[i]; splitStr( s, ":", vstr ); if( vstr.size() != 2 ) return false; @@ -133,7 +133,7 @@ bool RevOpts::InitMachineModels( const std::vector& Machines ) { // check to see if we expand into multiple cores if( Machines.size() == 1 ) { - std::string s = Machines[0]; + const std::string& s = Machines[0]; splitStr( s, ":", vstr ); if( vstr.size() != 2 ) return false; @@ -148,8 +148,8 @@ bool RevOpts::InitMachineModels( const std::vector& Machines ) { } // parse individual core configs - for( unsigned i = 0; i < Machines.size(); i++ ) { - std::string s = Machines[i]; + for( size_t i = 0; i < Machines.size(); i++ ) { + const std::string& s = Machines[i]; splitStr( s, ":", vstr ); if( vstr.size() != 2 ) return false; @@ -166,8 +166,8 @@ bool RevOpts::InitMachineModels( const std::vector& Machines ) { bool RevOpts::InitInstTables( const std::vector& InstTables ) { std::vector vstr; - for( unsigned i = 0; i < InstTables.size(); i++ ) { - std::string s = InstTables[i]; + for( size_t i = 0; i < InstTables.size(); i++ ) { + const std::string& s = InstTables[i]; splitStr( s, ":", vstr ); if( vstr.size() != 2 ) return false; @@ -185,8 +185,8 @@ bool RevOpts::InitInstTables( const std::vector& InstTables ) { bool RevOpts::InitMemCosts( const std::vector& MemCosts ) { std::vector vstr; - for( unsigned i = 0; i < MemCosts.size(); i++ ) { - std::string s = MemCosts[i]; + for( size_t i = 0; i < MemCosts.size(); i++ ) { + const std::string& s = MemCosts[i]; splitStr( s, ":", vstr ); if( vstr.size() != 3 ) return false; diff --git a/src/RevSysCalls.cc b/src/RevSysCalls.cc index 937f23d14..9fa4d97b6 100644 --- a/src/RevSysCalls.cc +++ b/src/RevSysCalls.cc @@ -11,8 +11,9 @@ namespace SST::RevCPU { /// Parse a string for an ECALL starting at address straddr, updating the state /// as characters are read, and call action() when the end of string is reached. EcallStatus RevCore::EcallLoadAndParseString( uint64_t straddr, std::function action ) { - auto rtval = EcallStatus::ERROR; - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); + auto rtval = EcallStatus::ERROR; + auto& EcallState = GetEcallState( HartToExecID ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); if( RegFile->GetLSQueue()->count( LSQHash( RevReg::a0, RevRegClass::RegGPR, HartToExecID ) ) > 0 ) { rtval = EcallStatus::CONTINUE; @@ -100,7 +101,7 @@ EcallStatus RevCore::ECALL_setxattr() { #if 0 // TODO: Need to load the data from (value, size bytes) into // hostValue vector before it can be passed to setxattr() on host. - + RevRegFile* RegFile = GetRegFile( HartToExecID ); auto path = RegFile->GetX(RevReg::a0); auto name = RegFile->GetX(RevReg::a1); auto value = RegFile->GetX(RevReg::a2); @@ -245,9 +246,10 @@ EcallStatus RevCore::ECALL_fremovexattr() { // 17, rev_getcwd(char *buf, unsigned long size) EcallStatus RevCore::ECALL_getcwd() { - auto BufAddr = RegFile->GetX( RevReg::a0 ); - auto size = RegFile->GetX( RevReg::a1 ); - auto CWD = std::filesystem::current_path(); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto BufAddr = RegFile->GetX( RevReg::a0 ); + auto size = RegFile->GetX( RevReg::a1 ); + auto CWD = std::filesystem::current_path(); mem->WriteMem( HartToExecID, BufAddr, size, CWD.c_str() ); // Returns null-terminated string in buf @@ -386,12 +388,13 @@ EcallStatus RevCore::ECALL_mknodat() { // TODO: 34, rev_mkdirat(int dfd, const char * pathname, umode_t mode) EcallStatus RevCore::ECALL_mkdirat() { output->verbose( CALL_INFO, 2, 0, "ECALL: mkdirat called" ); - EcallState& ECALL = Harts.at( HartToExecID )->GetEcallState(); - auto dirfd = RegFile->GetX( RevReg::a0 ); - auto path = RegFile->GetX( RevReg::a1 ); - auto mode = RegFile->GetX( RevReg::a2 ); + EcallState& ECALL = GetEcallState( HartToExecID ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto dirfd = RegFile->GetX( RevReg::a0 ); + auto path = RegFile->GetX( RevReg::a1 ); + auto mode = RegFile->GetX( RevReg::a2 ); - auto action = [&] { + auto action = [&] { // Do the mkdirat on the host int rc = mkdirat( dirfd, ECALL.string.c_str(), mode ); RegFile->SetX( RevReg::a0, rc ); @@ -520,9 +523,10 @@ EcallStatus RevCore::ECALL_faccessat() { // 49, rev_chdir(const char *filename) EcallStatus RevCore::ECALL_chdir() { output->verbose( CALL_INFO, 2, 0, "ECALL: chdir called\n" ); - auto path = RegFile->GetX( RevReg::a0 ); - auto action = [&] { - int rc = chdir( Harts.at( HartToExecID )->GetEcallState().string.c_str() ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto path = RegFile->GetX( RevReg::a0 ); + auto action = [&] { + int rc = chdir( GetEcallState( HartToExecID ).string.c_str() ); RegFile->SetX( RevReg::a0, rc ); }; return EcallLoadAndParseString( path, action ); @@ -584,18 +588,19 @@ EcallStatus RevCore::ECALL_fchown() { // 56, rev_openat(int dfd, const char *filename, int flags, umode_t mode) EcallStatus RevCore::ECALL_openat() { - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); + auto& EcallState = GetEcallState( HartToExecID ); if( EcallState.bytesRead == 0 ) { output->verbose( CALL_INFO, 2, 0, "ECALL: openat called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); } - auto dirfd = RegFile->GetX( RevReg::a0 ); - auto pathname = RegFile->GetX( RevReg::a1 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto dirfd = RegFile->GetX( RevReg::a0 ); + auto pathname = RegFile->GetX( RevReg::a1 ); // commented out to remove warnings // auto flags = RegFile->GetX(RevReg::a2); - auto mode = RegFile->GetX( RevReg::a3 ); + auto mode = RegFile->GetX( RevReg::a3 ); /* * NOTE: this is currently only opening files in the current directory @@ -605,16 +610,16 @@ EcallStatus RevCore::ECALL_openat() { /* Read the filename from memory one character at a time until we find '\0' */ - auto action = [&] { + auto action = [&] { // Do the openat on the host dirfd = open( std::filesystem::current_path().c_str(), mode ); int fd = openat( dirfd, EcallState.string.c_str(), mode ); // Add the file descriptor to this thread - Harts.at( HartToExecID )->Thread->AddFD( fd ); + Harts.at( HartToExecID )->AddFD( fd ); // openat returns the file descriptor of the opened file - Harts.at( HartToExecID )->RegFile->SetX( RevReg::a0, fd ); + RegFile->SetX( RevReg::a0, fd ); }; return EcallLoadAndParseString( pathname, action ); @@ -625,11 +630,12 @@ EcallStatus RevCore::ECALL_close() { output->verbose( CALL_INFO, 2, 0, "ECALL: close called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); - auto fd = RegFile->GetX( RevReg::a0 ); - auto& ActiveThread = Harts.at( HartToExecID )->Thread; + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto fd = RegFile->GetX( RevReg::a0 ); + auto& ActiveHart = Harts.at( HartToExecID ); // Check if CurrCtx has fd in fildes vector - if( !ActiveThread->FindFD( fd ) ) { + if( !ActiveHart->FindFD( fd ) ) { output->fatal( CALL_INFO, -1, @@ -646,7 +652,7 @@ EcallStatus RevCore::ECALL_close() { int rc = close( fd ); // Remove from Ctx's fildes - ActiveThread->RemoveFD( fd ); + ActiveHart->RemoveFD( fd ); // rc is propogated to rev from host RegFile->SetX( RevReg::a0, rc ); @@ -699,14 +705,15 @@ EcallStatus RevCore::ECALL_read() { output->verbose( CALL_INFO, 2, 0, "ECALL: read called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); - auto fd = RegFile->GetX( RevReg::a0 ); - auto BufAddr = RegFile->GetX( RevReg::a1 ); - auto BufSize = RegFile->GetX( RevReg::a2 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto fd = RegFile->GetX( RevReg::a0 ); + auto BufAddr = RegFile->GetX( RevReg::a1 ); + auto BufSize = RegFile->GetX( RevReg::a2 ); // Check if Current Ctx has access to the fd - auto& ActiveThread = Harts.at( HartToExecID )->Thread; + auto& ActiveHart = Harts.at( HartToExecID ); - if( !ActiveThread->FindFD( fd ) ) { + if( !ActiveHart->FindFD( fd ) ) { output->fatal( CALL_INFO, -1, @@ -735,17 +742,18 @@ EcallStatus RevCore::ECALL_read() { } EcallStatus RevCore::ECALL_write() { - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); + auto& EcallState = GetEcallState( HartToExecID ); if( EcallState.bytesRead == 0 ) { output->verbose( CALL_INFO, 2, 0, "ECALL: write called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); } - auto fd = RegFile->GetX( RevReg::a0 ); - auto addr = RegFile->GetX( RevReg::a1 ); - auto nbytes = RegFile->GetX( RevReg::a2 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto fd = RegFile->GetX( RevReg::a0 ); + auto addr = RegFile->GetX( RevReg::a1 ); + auto nbytes = RegFile->GetX( RevReg::a2 ); - auto lsq_hash = LSQHash( RevReg::a0, RevRegClass::RegGPR, HartToExecID ); // Cached hash value + auto lsq_hash = LSQHash( RevReg::a0, RevRegClass::RegGPR, HartToExecID ); // Cached hash value if( EcallState.bytesRead && LSQueue->count( lsq_hash ) == 0 ) { EcallState.string += std::string_view( EcallState.buf.data(), EcallState.bytesRead ); @@ -1036,7 +1044,8 @@ EcallStatus RevCore::ECALL_exit() { output->verbose( CALL_INFO, 2, 0, "ECALL: exit called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); - auto status = RegFile->GetX( RevReg::a0 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto status = RegFile->GetX( RevReg::a0 ); output->verbose( CALL_INFO, @@ -1209,6 +1218,7 @@ EcallStatus RevCore::ECALL_clock_gettime() { output->verbose( CALL_INFO, 2, 0, "ECALL: clock_gettime called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); struct timespec src, *tp = (struct timespec*) RegFile->GetX( RevReg::a1 ); if( timeConverter == nullptr ) { @@ -1757,6 +1767,7 @@ EcallStatus RevCore::ECALL_gettid() { CALL_INFO, 2, 0, "ECALL: gettid called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); /* rc = Currently Executing Hart */ RegFile->SetX( RevReg::a0, ActiveThreadID ); return EcallStatus::SUCCESS; @@ -2044,7 +2055,8 @@ EcallStatus RevCore::ECALL_readahead() { // 214, rev_brk(unsigned long brk) EcallStatus RevCore::ECALL_brk() { - auto Addr = RegFile->GetX( RevReg::a0 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto Addr = RegFile->GetX( RevReg::a0 ); const uint64_t heapend = mem->GetHeapEnd(); if( Addr > 0 && Addr > heapend ) { @@ -2065,10 +2077,11 @@ EcallStatus RevCore::ECALL_brk() { // 215, rev_munmap(unsigned long addr, size_t len) EcallStatus RevCore::ECALL_munmap() { output->verbose( CALL_INFO, 2, 0, "ECALL: munmap called\n" ); - auto Addr = RegFile->GetX( RevReg::a0 ); - auto Size = RegFile->GetX( RevReg::a1 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto Addr = RegFile->GetX( RevReg::a0 ); + auto Size = RegFile->GetX( RevReg::a1 ); - int rc = mem->DeallocMem( Addr, Size ) == uint64_t( -1 ); + int rc = mem->DeallocMem( Addr, Size ) == uint64_t( -1 ); if( rc == -1 ) { output->fatal( CALL_INFO, @@ -2258,7 +2271,7 @@ EcallStatus RevCore::ECALL_clone() { // RegFile->SetX(RevReg::a0, ChildPID); // // Child's return value is 0 - // ChildCtx->GetRegFile()->SetX(RevReg::a0, 0); + // ChildCtx->GetRegFile( HartToExecID )->SetX(RevReg::a0, 0); // // clean up ecall state // rtval = EcallStatus::SUCCESS; @@ -2278,9 +2291,10 @@ EcallStatus RevCore::ECALL_execve() { // 222, rev_old_mmap(struct mmap_arg_struct *arg) EcallStatus RevCore::ECALL_mmap() { output->verbose( CALL_INFO, 2, 0, "ECALL: mmap called\n" ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); - auto addr = RegFile->GetX( RevReg::a0 ); - auto size = RegFile->GetX( RevReg::a1 ); + auto addr = RegFile->GetX( RevReg::a0 ); + auto size = RegFile->GetX( RevReg::a1 ); // auto prot = RegFile->GetX(RevReg::a2); // auto Flags = RegFile->GetX(RevReg::a3); // auto fd = RegFile->GetX(RevReg::a4); @@ -3109,7 +3123,7 @@ EcallStatus RevCore::ECALL_clone3() { // RegFile->SetX(RevReg::a0, ChildPID); // // Child's return value is 0 - // ChildCtx->GetRegFile()->SetX(RevReg::a0, 0); + // ChildCtx->GetRegFile( HartToExecID )->SetX(RevReg::a0, 0); // // clean up ecall state // rtval = EcallStatus::SUCCESS; @@ -3162,9 +3176,10 @@ EcallStatus RevCore::ECALL_process_madvise() { EcallStatus RevCore::ECALL_cpuinfo() { output->verbose( CALL_INFO, 2, 0, "ECALL: cpuinfoc called by thread %" PRIu32 "\n", ActiveThreadID ); struct rev_cpuinfo info; - auto addr = RegFile->GetX( RevReg::a0 ); - info.cores = opts->GetNumCores(); - info.harts_per_core = opts->GetNumHarts(); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto addr = RegFile->GetX( RevReg::a0 ); + info.cores = opts->GetNumCores(); + info.harts_per_core = opts->GetNumHarts(); mem->WriteMem( HartToExecID, addr, sizeof( info ), &info ); RegFile->SetX( RevReg::a0, 0 ); return EcallStatus::SUCCESS; @@ -3173,7 +3188,8 @@ EcallStatus RevCore::ECALL_cpuinfo() { // 501, rev_perf_stats(struct rev_perf_stats *stats) EcallStatus RevCore::ECALL_perf_stats() { output->verbose( CALL_INFO, 2, 0, "ECALL: perf_stats called by thread %" PRIu32 "\n", GetActiveThreadID() ); - rev_stats rs, *dest = reinterpret_cast( RegFile->GetX( RevReg::a0 ) ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + rev_stats rs, *dest = reinterpret_cast( RegFile->GetX( RevReg::a0 ) ); rs.cycles = Stats.totalCycles; rs.instructions = Stats.retired; @@ -3190,7 +3206,8 @@ EcallStatus RevCore::ECALL_pthread_create() { output->verbose( CALL_INFO, 2, 0, "ECALL: pthread_create called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); - uint64_t tidAddr = RegFile->GetX( RevReg::a0 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + uint64_t tidAddr = RegFile->GetX( RevReg::a0 ); //uint64_t AttrPtr = RegFile->GetX(RevReg::a1); uint64_t NewThreadPC = RegFile->GetX( RevReg::a2 ); uint64_t ArgPtr = RegFile->GetX( RevReg::a3 ); @@ -3214,6 +3231,7 @@ EcallStatus RevCore::ECALL_pthread_join() { // Set current thread to blocked std::unique_ptr BlockedThread = PopThreadFromHart( HartToExecID ); BlockedThread->SetState( ThreadState::BLOCKED ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); BlockedThread->SetWaitingToJoinTID( RegFile->GetX( RevReg::a0 ) ); // Signal to RevCPU this thread is has changed state @@ -3226,26 +3244,21 @@ EcallStatus RevCore::ECALL_pthread_join() { // // if retval is not null, // //store the return value of the thread in retval - // void **retval = (void **)RegFile->RV64[11]; - // if( retval != NULL ){ - // *retval = (void *) - // Harts.at(HartToExecID)->Thread(HartToDecodeID)->GetRegFile()->RV64[10]; - // } - // } return rtval; } // 9000, rev_dump_mem_range(uint64_t addr, uint64_t size) EcallStatus RevCore::ECALL_dump_mem_range() { - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); + auto& EcallState = GetEcallState( HartToExecID ); if( EcallState.bytesRead == 0 ) { output->verbose( CALL_INFO, 2, 0, "ECALL: openat called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); } - auto addr = RegFile->GetX( RevReg::a1 ); - auto size = RegFile->GetX( RevReg::a2 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto addr = RegFile->GetX( RevReg::a1 ); + auto size = RegFile->GetX( RevReg::a2 ); // TODO: Add error handling if memh is enabled mem->DumpMem( addr, size, 16 ); @@ -3255,18 +3268,19 @@ EcallStatus RevCore::ECALL_dump_mem_range() { // 9001, rev_dump_mem_range(const char* outputFile, uint64_t addr, uint64_t size) EcallStatus RevCore::ECALL_dump_mem_range_to_file() { - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); + auto& EcallState = GetEcallState( HartToExecID ); if( EcallState.bytesRead == 0 ) { output->verbose( CALL_INFO, 2, 0, "ECALL: dump_mem_range called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); } - auto pathname = RegFile->GetX( RevReg::a0 ); - auto addr = RegFile->GetX( RevReg::a1 ); - auto size = RegFile->GetX( RevReg::a2 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto pathname = RegFile->GetX( RevReg::a0 ); + auto addr = RegFile->GetX( RevReg::a1 ); + auto size = RegFile->GetX( RevReg::a2 ); /* Read the filename from memory one character at a time until we find '\0' */ - auto action = [&] { + auto action = [&] { // open the current directory and the file // open the file in write mode std::ofstream outputFile( EcallState.string, std::ios::out | std::ios::binary ); @@ -3279,6 +3293,7 @@ EcallStatus RevCore::ECALL_dump_mem_range_to_file() { // 9002, rev_mem_dump_stack() EcallStatus RevCore::ECALL_dump_stack() { output->verbose( CALL_INFO, 2, 0, "ECALL: dump_stack called" ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); // TODO: Factor in TLS // Check if sp + _STACK_SIZE_ is in the valid memory range // if not, dump the memory that is valid @@ -3290,16 +3305,17 @@ EcallStatus RevCore::ECALL_dump_stack() { // 9003, rev_dump_stck_to_file(const char* outputFile) EcallStatus RevCore::ECALL_dump_stack_to_file() { - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); + auto& EcallState = GetEcallState( HartToExecID ); if( EcallState.bytesRead == 0 ) { output->verbose( CALL_INFO, 2, 0, "ECALL: dump_stack_to_file called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID ); } - auto pathname = RegFile->GetX( RevReg::a0 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto pathname = RegFile->GetX( RevReg::a0 ); /* Read the filename from memory one character at a time until we find '\0' */ - auto action = [&] { + auto action = [&] { // open the current directory and the file // open the file in write mode std::ofstream outputFile( EcallState.string, std::ios::out | std::ios::binary ); @@ -3316,7 +3332,7 @@ EcallStatus RevCore::ECALL_dump_stack_to_file() { } EcallStatus RevCore::ECALL_dump_valid_mem() { - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); + auto& EcallState = GetEcallState( HartToExecID ); if( EcallState.bytesRead == 0 ) { output->verbose( CALL_INFO, 2, 0, "ECALL: dump_valid_mem called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID @@ -3328,7 +3344,7 @@ EcallStatus RevCore::ECALL_dump_valid_mem() { } EcallStatus RevCore::ECALL_dump_valid_mem_to_file() { - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); + auto& EcallState = GetEcallState( HartToExecID ); if( EcallState.bytesRead == 0 ) { output->verbose( CALL_INFO, @@ -3339,10 +3355,11 @@ EcallStatus RevCore::ECALL_dump_valid_mem_to_file() { HartToExecID ); } - auto pathname = RegFile->GetX( RevReg::a0 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto pathname = RegFile->GetX( RevReg::a0 ); /* Read the filename from memory one character at a time until we find '\0' */ - auto action = [&] { + auto action = [&] { // open the current directory and the file // open the file in write mode std::ofstream outputFile( EcallState.string, std::ios::out | std::ios::binary ); @@ -3353,7 +3370,7 @@ EcallStatus RevCore::ECALL_dump_valid_mem_to_file() { } EcallStatus RevCore::ECALL_dump_thread_mem() { - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); + auto& EcallState = GetEcallState( HartToExecID ); if( EcallState.bytesRead == 0 ) { output->verbose( CALL_INFO, 2, 0, "ECALL: dump_thread_mem called by thread %" PRIu32 " on hart %" PRIu32 "\n", ActiveThreadID, HartToExecID @@ -3365,7 +3382,7 @@ EcallStatus RevCore::ECALL_dump_thread_mem() { } EcallStatus RevCore::ECALL_dump_thread_mem_to_file() { - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); + auto& EcallState = GetEcallState( HartToExecID ); if( EcallState.bytesRead == 0 ) { output->verbose( CALL_INFO, @@ -3376,10 +3393,11 @@ EcallStatus RevCore::ECALL_dump_thread_mem_to_file() { HartToExecID ); } - auto pathname = RegFile->GetX( RevReg::a0 ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + auto pathname = RegFile->GetX( RevReg::a0 ); /* Read the filename from memory one character at a time until we find '\0' */ - auto action = [&] { + auto action = [&] { // open the current directory and the file // open the file in write mode std::ofstream outputFile( EcallState.string, std::ios::out | std::ios::binary ); @@ -3395,9 +3413,10 @@ EcallStatus RevCore::ECALL_dump_thread_mem_to_file() { // Restrictions: // - 6 data, all XLEN in size EcallStatus RevCore::ECALL_fast_printf() { - auto& EcallState = Harts.at( HartToExecID )->GetEcallState(); - uint64_t pFormat = RegFile->GetX( RevReg::a0 ); - auto action = [&] { + auto& EcallState = GetEcallState( HartToExecID ); + RevRegFile* RegFile = GetRegFile( HartToExecID ); + uint64_t pFormat = RegFile->GetX( RevReg::a0 ); + auto action = [&] { const char* format = EcallState.string.c_str(); char buffer[1024]; // This is sort of a hack -- we pass XLEN-sized values from a1-a6 which go into va_args slots diff --git a/src/RevThread.cc b/src/RevThread.cc index 87d35b035..bdfda2b75 100644 --- a/src/RevThread.cc +++ b/src/RevThread.cc @@ -48,7 +48,7 @@ std::ostream& operator<<( std::ostream& os, const RevThread& Thread ) { } os << "\n"; - os << *Thread.VirtRegState.get(); + os << *Thread.GetRegFile(); return os; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d43620e81..9c37eedb8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,13 +9,21 @@ if(NOT DEFINED TEST_LEVEL) set(TEST_LEVEL "$ENV{TEST_LEVEL}") endif() - if(NOT TEST_LEVEL OR TEST_LEVEL GREATER 3 OR TEST_LEVEL LESS 1) set(TEST_LEVEL 3) endif() - message("TEST_LEVEL=${TEST_LEVEL}") +if(NOT DEFINED RANDOMIZE_COSTS) + set(RANDOMIZE_COSTS "$ENV{RANDOMIZE_COSTS}") +endif() +if(NOT RANDOMIZE_COSTS) + set(RANDOMIZE_COSTS "") +else() + set(RANDOMIZE_COSTS --randomizeCosts) +endif() +message("RANDOMIZE_COSTS=\"${RANDOMIZE_COSTS}\"") + set(RISCV_ENV "$ENV{RISCV}") if(RISCV_ENV) message(STATUS "RISCV environment set to ${RISCV_ENV}") @@ -65,8 +73,8 @@ set (passRegex "Simulation is complete") #------- TESTS --------------- message(STATUS "CTest Setup") -# Macro to build and configure tests in rev based on the labels the test was specified with -macro(add_rev_test test_name test_dir timeout labels) +# Function to build and configure tests in rev based on the labels the test was specified with +function(add_rev_test test_name test_dir timeout labels) string(TOLOWER ${test_dir} test_dir_lower) string(TOLOWER ${test_name} test_name_lower) @@ -140,13 +148,22 @@ macro(add_rev_test test_name test_dir timeout labels) # set(startSymbol "\"[0:_start]\"") endif() + string(FIND "${labels}" "cost1" cost1_index) + if(NOT ${cost1_index} EQUAL -1) + set(RANDOMIZE_COSTS "") + endif() + # increase timeout if costs randomized + if(NOT RANDOMIZE_COSTS STREQUAL "") + math(EXPR timeout "${timeout} * 3 /2") + endif() + if(NOT optional_script) foreach(numHarts IN LISTS numHartsList) foreach(numCores IN LISTS numCoresList) # Define revmem target with the new naming convention add_custom_target(run_${test_name_lower}_revmem_${numHarts}_harts_${numCores}_cores - COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}/${test_name_lower} && sst --add-lib-path=${CMAKE_SOURCE_DIR}/build/src/ ${CMAKE_SOURCE_DIR}/test/rev-model-options-config.py -- --program="${test_name_lower}.exe" --numHarts=${numHarts} --numCores=${numCores} --machine "${machine_args}" --startSymbol "${startSymbol}" + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}/${test_name_lower} && sst --add-lib-path=${CMAKE_SOURCE_DIR}/build/src/ ${CMAKE_SOURCE_DIR}/test/rev-model-options-config.py -- --program="${test_name_lower}.exe" --numHarts=${numHarts} --numCores=${numCores} --machine "${machine_args}" --startSymbol "${startSymbol}" ${RANDOMIZE_COSTS} DEPENDS build_${test_name_lower} COMMENT "Running ${test_name_lower} test with revmem, numHarts=${numHarts}, numCores=${numCores}" ) @@ -159,7 +176,7 @@ macro(add_rev_test test_name test_dir timeout labels) # If 'memh' label found, add a memHierarchy test if(add_memh_test) add_custom_target(run_${test_name_lower}_memh_${numHarts}_harts_${numCores}_cores - COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}/${test_name_lower} && sst --add-lib-path=${CMAKE_SOURCE_DIR}/build/src/ ${CMAKE_SOURCE_DIR}/test/rev-model-options-config.py -- --program="${test_name_lower}.exe" --numHarts=${numHarts} --numCores=${numCores} --machine "${machine_args}" --enableMemH=1 --startSymbol "${startSymbol}" + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}/${test_name_lower} && sst --add-lib-path=${CMAKE_SOURCE_DIR}/build/src/ ${CMAKE_SOURCE_DIR}/test/rev-model-options-config.py -- --program="${test_name_lower}.exe" --numHarts=${numHarts} --numCores=${numCores} --machine "${machine_args}" --enableMemH=1 --startSymbol "${startSymbol}" ${RANDOMIZE_COSTS} DEPENDS build_${test_name_lower} COMMENT "Running ${test_name_lower} test with memHierarchy enabled, numHarts=${numHarts}, numCores=${numCores}" ) @@ -212,7 +229,7 @@ macro(add_rev_test test_name test_dir timeout labels) set_tests_properties(${test_name_lower}_script PROPERTIES DISABLED TRUE) endif() endif() -endmacro() +endfunction() # Not all toolchains have support RV32, so we test and skip RV32 tests with a warning execute_process(COMMAND ${RVCC} -march=rv32i -mabi=ilp32 -o /dev/null ex1.c WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/ex1 ERROR_VARIABLE RV32_Missing) @@ -257,8 +274,8 @@ add_rev_test(BACKINGSTORE backingstore 100 "test_level=2;rv64" SCRIPT "run_backi add_rev_test(MEM_DUMP mem_dump 10 "rv64" SCRIPT "run_mem_dump.sh") add_rev_test(LWSP lwsp 30 "test_level=2;memh;rv64") add_rev_test(CSR csr 30 "rv64") -add_rev_test(RDCYCLE rdcycle 5 "test_level=2;rv64;zicntr") -add_rev_test(RDTIME rdtime 5 "test_level=2;rv64;zicntr") +add_rev_test(RDCYCLE rdcycle 5 "test_level=2;rv64;zicntr;cost1") +add_rev_test(RDTIME rdtime 5 "test_level=2;rv64;zicntr;cost1") add_rev_test(RDINSTRET rdinstret 5 "test_level=2;memh;rv64;zicntr") add_rev_test(Zfa zfa 30 "rv64" SCRIPT "run_zfa_tests.sh") diff --git a/test/rev-model-options-config.py b/test/rev-model-options-config.py index b3055084e..f6826ddeb 100644 --- a/test/rev-model-options-config.py +++ b/test/rev-model-options-config.py @@ -10,6 +10,7 @@ import argparse import sst +import ast DEBUG_L1 = 0 DEBUG_MEM = 0 @@ -22,15 +23,19 @@ parser.add_argument("--numCores", type=int, help="Number of Rev Cores per RevCPU", default=1) parser.add_argument("--numHarts", type=int, help="Number of HARTs per Rev Core", default=1) parser.add_argument("--program", help="The program executable to run in the simulation", default="a.out") -parser.add_argument("--enableMemH", type=int, choices=[0, 1], help="Enable (1) or disable (0) memHierarchy backend", default=0) +parser.add_argument("--enableMemH", nargs='?', help="Enable memHierarchy backend", default="False") parser.add_argument("--verbose", type=int, help="Verbosity level", default=2) parser.add_argument("--machine", help="Machine type/configuration", default="[CORES:RV64GC]") parser.add_argument("--args", help="Command line arguments to pass to the target executable", default="") parser.add_argument("--startSymbol", help="ELF Symbol Rev should begin execution at", default="[0:main]") +parser.add_argument("--randomizeCosts", nargs='?', help="Randomize costs of instructions", default="False") # Parse arguments args = parser.parse_args() +args.enableMemH = True if args.enableMemH is None or ast.literal_eval(args.enableMemH) else False +args.randomizeCosts = True if args.randomizeCosts is None or ast.literal_eval(args.randomizeCosts) else False + # Print arguments nicely print("Rev SST Simulation Configuration:") for arg in vars(args): @@ -53,6 +58,7 @@ "startAddr": "[0:0x00000000]", "startSymbol": args.startSymbol, "enableMemH": args.enableMemH, + "randomizeCosts": args.randomizeCosts, "args": args.args, "splash": 1 })