Skip to content

Commit beda7f5

Browse files
authored
Merge pull request #238 from tactcomplabs/fenv
Add support for CSR instructions, FP exception flags, FP rounding modes, Performance Counters
2 parents aa36b3a + 7ddce64 commit beda7f5

40 files changed

+2017
-261
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
*.csv
55
.cache/clangd/index
66
.vscode
7-
*.dylib
7+
*.o
88
.#*
99
*~
1010
.gdb_history
1111
*.out
12-
build/*
1312
*.asm

CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,13 @@ endif()
7777
# Compiler Options
7878
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
7979
set(WERROR_FLAG "")
80-
set(FP_MODE_FLAG "-ffp-model=strict")
80+
set(FP_MODE_FLAG "-ffp-model=strict -frounding-math -ftrapping-math")
8181
else()
8282
set(WERROR_FLAG "-Werror")
83-
set(FP_MODE_FLAG "-frounding-math")
83+
set(FP_MODE_FLAG "-frounding-math -ftrapping-math")
8484
endif()
8585

86-
set(CMAKE_CXX_FLAGS "-std=c++17 ${FP_MODE_FLAG} -O2 -Wall -Wextra ${WERROR_FLAG} -Wvla -Wuninitialized -Wfloat-conversion -Wdouble-promotion -Wno-unused-parameter -Wno-deprecated-declarations ${CMAKE_CXX_FLAGS} -I./ ${LDFLAGS} ${REVCPU_COMPILER_MACROS}")
86+
set(CMAKE_CXX_FLAGS "-std=c++17 ${FP_MODE_FLAG} -O2 -Wall -Wextra ${WERROR_FLAG} -Wvla -Wuninitialized -Wdouble-promotion -Wno-unused-parameter -Wno-deprecated-declarations ${CMAKE_CXX_FLAGS} -I./ ${LDFLAGS} ${REVCPU_COMPILER_MACROS}")
8787
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -Wall ${REVCPU_COMPILER_MACROS}")
8888
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -Wall ${REVCPU_COMPILER_MACROS}")
8989

include/AllRevInstTables.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "insns/RV64M.h"
3232
#include "insns/RV64P.h"
3333
#include "insns/Zicbom.h"
34+
#include "insns/Zicsr.h"
3435
#include "insns/Zifencei.h"
3536

3637
#endif

include/RevCPU.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -212,17 +212,17 @@ class RevCPU : public SST::Component {
212212
}
213213

214214
private:
215-
unsigned numCores{}; ///< RevCPU: number of RISC-V cores
216-
unsigned numHarts{}; ///< RevCPU: number of RISC-V cores
217-
unsigned msgPerCycle{}; ///< RevCPU: number of messages to send per cycle
218-
unsigned RDMAPerCycle{}; ///< RevCPU: number of RDMA messages per cycle to inject into PAN network
219-
unsigned testStage{}; ///< RevCPU: controls the PAN Test harness staging
220-
unsigned testIters{}; ///< RevCPU: the number of message iters for each PAN Test
221-
RevOpts* Opts{}; ///< RevCPU: Simulation options object
222-
RevMem* Mem{}; ///< RevCPU: RISC-V main memory object
223-
RevLoader* Loader{}; ///< RevCPU: RISC-V loader
224-
std::vector<RevCore*> Procs{}; ///< RevCPU: RISC-V processor objects
225-
bool* Enabled{}; ///< RevCPU: Completion structure
215+
unsigned numCores{}; ///< RevCPU: number of RISC-V cores
216+
unsigned numHarts{}; ///< RevCPU: number of RISC-V cores
217+
unsigned msgPerCycle{}; ///< RevCPU: number of messages to send per cycle
218+
// unsigned RDMAPerCycle{}; ///< RevCPU: number of RDMA messages per cycle to inject into PAN network
219+
// unsigned testStage{}; ///< RevCPU: controls the PAN Test harness staging
220+
// unsigned testIters{}; ///< RevCPU: the number of message iters for each PAN Test
221+
RevOpts* Opts{}; ///< RevCPU: Simulation options object
222+
RevMem* Mem{}; ///< RevCPU: RISC-V main memory object
223+
RevLoader* Loader{}; ///< RevCPU: RISC-V loader
224+
std::vector<RevCore*> Procs{}; ///< RevCPU: RISC-V processor objects
225+
bool* Enabled{}; ///< RevCPU: Completion structure
226226

227227
// Initializes a RevThread object.
228228
// - Adds it's ThreadID to the ThreadQueue to be scheduled
@@ -266,16 +266,16 @@ class RevCPU : public SST::Component {
266266
// Generates a new Thread ID using the RNG.
267267
uint32_t GetNewThreadID() { return RevRand( 0, UINT32_MAX ); }
268268

269-
uint8_t PrivTag{}; ///< RevCPU: private tag locator
270-
uint32_t LToken{}; ///< RevCPU: token identifier for PAN Test
269+
uint8_t PrivTag{}; ///< RevCPU: private tag locator
270+
// uint32_t LToken{}; ///< RevCPU: token identifier for PAN Test
271271

272272
int address{}; ///< RevCPU: local network address
273273

274274
unsigned fault_width{}; ///< RevCPU: the width (in bits) for target faults
275-
int64_t fault_range{}; ///< RevCPU: the range of cycles to inject the fault
276-
int64_t FaultCntr{}; ///< RevCPU: the fault counter
275+
// int64_t fault_range{}; ///< RevCPU: the range of cycles to inject the fault
276+
int64_t FaultCntr{}; ///< RevCPU: the fault counter
277277

278-
uint64_t PrevAddr{}; ///< RevCPU: previous address for handling PAN messages
278+
// uint64_t PrevAddr{}; ///< RevCPU: previous address for handling PAN messages
279279

280280
bool EnableNIC{}; ///< RevCPU: Flag for enabling the NIC
281281
bool EnableMemH{}; ///< RevCPU: Enable memHierarchy

include/RevCore.h

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
#include <vector>
3838

3939
// -- RevCPU Headers
40-
#include "AllRevInstTables.h"
4140
#include "RevCoProc.h"
4241
#include "RevCorePasskey.h"
4342
#include "RevFeature.h"
@@ -54,20 +53,22 @@
5453
#include "../common/include/RevCommon.h"
5554
#include "../common/syscalls/syscalls.h"
5655

56+
#include "AllRevInstTables.h"
57+
5758
namespace SST::RevCPU {
5859
class RevCoProc;
5960

6061
class RevCore {
6162
public:
6263
/// RevCore: standard constructor
6364
RevCore(
64-
unsigned Id,
65-
RevOpts* Opts,
66-
unsigned NumHarts,
67-
RevMem* Mem,
68-
RevLoader* Loader,
65+
unsigned id,
66+
RevOpts* opts,
67+
unsigned numHarts,
68+
RevMem* mem,
69+
RevLoader* loader,
6970
std::function<uint32_t()> GetNewThreadID,
70-
SST::Output* Output
71+
SST::Output* output
7172
);
7273

7374
/// RevCore: standard destructor
@@ -157,7 +158,9 @@ class RevCore {
157158

158159
RevMem& GetMem() const { return *mem; }
159160

160-
///< RevCore: Called by RevCPU to handle the state changes threads may have happened during this Proc's ClockTick
161+
uint64_t GetCurrentSimCycle() const { return currentSimCycle; }
162+
163+
///< RevCore: Called by RevCPU to handle the state changes threads may have happened during this Core's ClockTick
161164
auto TransferThreadsThatChangedState() { return std::move( ThreadsThatChangedState ); }
162165

163166
///< RevCore: Add
@@ -171,7 +174,7 @@ class RevCore {
171174
///< RevCore: Returns the current HartToExecID active pid
172175
uint32_t GetActiveThreadID() { return Harts.at( HartToDecodeID )->GetAssignedThreadID(); }
173176

174-
///< RevCore: Get this Proc's feature
177+
///< RevCore: Get this Core's feature
175178
RevFeature* GetRevFeature() const { return feature; }
176179

177180
///< RevCore: Mark a current request as complete
@@ -257,7 +260,7 @@ class RevCore {
257260
///< RevCore: Returns the id of an idle hart (or _INVALID_HART_ID_ if none are idle)
258261
unsigned FindIdleHartID() const;
259262

260-
///< RevCore: Returns true if all harts are available (ie. There is nothing executing on this Proc)
263+
///< RevCore: Returns true if all harts are available (ie. There is nothing executing on this Core)
261264
bool HasNoBusyHarts() const { return IdleHarts == ValidHarts; }
262265

263266
///< RevCore: Used by RevCPU to determine if it can disable this proc
@@ -268,6 +271,9 @@ class RevCore {
268271
///< RevCore: Returns true if there are any IdleHarts
269272
bool HasIdleHart() const { return IdleHarts.any(); }
270273

274+
///< RevCore: Returns the number of cycles executed so far
275+
uint64_t GetCycles() const { return cycles; }
276+
271277
private:
272278
bool Halted = false; ///< RevCore: determines if the core is halted
273279
bool Stalled = false; ///< RevCore: determines if the core is stalled on instruction fetch
@@ -276,9 +282,10 @@ class RevCore {
276282
bool ALUFault = false; ///< RevCore: determines if we need to handle an ALU fault
277283
unsigned fault_width = 0; ///< RevCore: the width of the target fault
278284
unsigned const id; ///< RevCore: processor id
279-
uint64_t ExecPC = 0; ///< RevCore: executing PC
280-
unsigned HartToDecodeID = 0; ///< RevCore: Current executing ThreadID
281-
unsigned HartToExecID = 0; ///< RevCore: Thread to dispatch instruction
285+
uint64_t ExecPC = 0; ///< RevCore: executing PC
286+
unsigned HartToDecodeID = 0; ///< RevCore: Current executing ThreadID
287+
unsigned HartToExecID = 0; ///< RevCore: Thread to dispatch instruction
288+
uint64_t currentSimCycle = 0; ///< RevCore: Current simulation cycle
282289

283290
std::vector<std::shared_ptr<RevHart>> Harts{}; ///< RevCore: vector of Harts without a thread assigned to them
284291
std::bitset<_MAX_HARTS_> IdleHarts{}; ///< RevCore: bitset of Harts with no thread assigned
@@ -293,7 +300,7 @@ class RevCore {
293300
RevLoader* loader{}; ///< RevCore: loader object
294301

295302
// Function pointer to the GetNewThreadID function in RevCPU (monotonically increasing thread ID counter)
296-
std::function<uint32_t()> GetNewThreadID;
303+
std::function<uint32_t()> const GetNewThreadID;
297304

298305
// If a given assigned thread experiences a change of state, it sets the corresponding bit
299306
std::vector<std::unique_ptr<RevThread>>
@@ -316,6 +323,8 @@ class RevCore {
316323

317324
std::bitset<_MAX_HARTS_> CoProcStallReq{};
318325

326+
uint64_t cycles{}; ///< RevCore: The number of cycles executed
327+
319328
///< RevCore: Utility function for system calls that involve reading a string from memory
320329
EcallStatus EcallLoadAndParseString( uint64_t straddr, std::function<void()> );
321330

include/RevExt.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// -- Standard Headers
1818
#include <cmath>
1919
#include <string>
20+
#include <type_traits>
2021
#include <utility>
2122
#include <vector>
2223

@@ -34,7 +35,13 @@ struct RevExt {
3435
: name( name ), feature( feature ), mem( mem ), output( output ) {}
3536

3637
/// RevExt: standard destructor. virtual so that Extensions[i] can be deleted
37-
virtual ~RevExt() = default;
38+
virtual ~RevExt() = default;
39+
40+
// We do not allow copying, moving or assigning
41+
RevExt( const RevExt& ) = delete;
42+
RevExt( RevExt&& ) = delete;
43+
RevExt& operator=( const RevExt& ) = delete;
44+
RevExt& operator=( RevExt&& ) = delete;
3845

3946
/// RevExt: sets the internal instruction table
4047
// Note: && means the argument should be an rvalue or std::move(lvalue)
@@ -72,8 +79,8 @@ struct RevExt {
7279
std::vector<RevInstEntry> ctable{}; ///< RevExt: compressed instruction table
7380
std::vector<RevInstEntry> otable{}; ///< RevExt: optional compressed instruction table
7481

75-
auto SetFPEnv( unsigned Inst, const RevInst& Payload, uint16_t threadID, RevRegFile* regFile );
7682
}; // class RevExt
83+
7784
} // namespace SST::RevCPU
7885

7986
#endif

include/RevFenv.h

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,69 +11,103 @@
1111
#ifndef _REV_FENV_H_
1212
#define _REV_FENV_H_
1313

14+
#include "RevInstTable.h"
1415
#include <cfenv>
15-
#include <memory>
16+
#include <cmath>
17+
#include <stdexcept>
1618

1719
// GCC and Clang do not fully support FENV_ACCESS now. See:
1820
//
21+
// https://bugs.llvm.org/show_bug.cgi?id=8100
1922
// https://gcc.gnu.org/legacy-ml/gcc-patches/2003-09/msg00104.html
2023
// https://gcc.gcc.gnu.narkive.com/hDi1eOot/setting-frounding-math-by-default
21-
// https://bugs.llvm.org/show_bug.cgi?id=8100
22-
// https://github.com/llvm/llvm-project/issues/8472
2324
// https://discourse.llvm.org/t/why-is-pragma-stdc-fenv-access-not-supported/46128/25
2425
//
2526
// Currently FP_MODE_FLAG in CMakeLists.txt is used as a workaround
2627
//#pragma STDC FENV_ACCESS ON
2728

2829
namespace SST::RevCPU {
2930

30-
// TODO: Right now we only need to save/restore rounding mode.
31-
// Later, we may need to save and restore the entire fenv_t state
3231
class RevFenv {
33-
int saved_round; ///< Saved Floating-Point Rounding Mode
34-
35-
// We use Meyers singleton to avoid initialization order fiasco
36-
static int& round() {
37-
thread_local int round = fegetround();
38-
return round;
39-
}
32+
FCSR& fcsr; // Reference to this register file's FCSR
33+
std::fenv_t saved_env{}; // The saved FP environment which is restored
4034

4135
public:
42-
/// Constructor saves Fenv state
43-
RevFenv() : saved_round( round() ) {
44-
if( saved_round < 0 ) {
45-
throw std::runtime_error( "Getting floating-point rounding mode with "
46-
"fegetround() is not working." );
36+
/// Constructor saves Fenv state to be restored at destruction
37+
RevFenv( RevRegFile* R, FRMode rm, SST::Output* output ) : fcsr( R->GetFCSR() ) {
38+
39+
// Save host's FP environment and set flags to default, non-trapping
40+
if( std::feholdexcept( &saved_env ) ) {
41+
throw std::runtime_error( "Getting floating-point environment with feholdexcept() is not working." );
42+
}
43+
44+
// If the encoded rounding mode is dynamic, load the frm register
45+
if( rm == FRMode::DYN ) {
46+
rm = R->GetFRM();
47+
}
48+
49+
// Set the Floating-Point Rounding Mode on the host
50+
int ret = 0;
51+
switch( rm ) {
52+
case FRMode::RNE: // Round to Nearest, ties to Even
53+
ret = std::fesetround( FE_TONEAREST );
54+
break;
55+
56+
case FRMode::RTZ: // Round towards Zero
57+
ret = std::fesetround( FE_TOWARDZERO );
58+
break;
59+
60+
case FRMode::RDN: // Round Down (towards -Inf)
61+
ret = std::fesetround( FE_DOWNWARD );
62+
break;
63+
64+
case FRMode::RUP: // Round Up (towards +Inf)
65+
ret = std::fesetround( FE_UPWARD );
66+
break;
67+
68+
case FRMode::RMM: // Round to Nearest, ties to Max Magnitude
69+
#ifdef FE_TONEARESTFROMZERO
70+
ret = std::fesetround( FE_TONEARESTFROMZERO );
71+
#else
72+
output->fatal( CALL_INFO, -1, "Error: Round to nearest Max Magnitude not implemented at PC = 0x%" PRIx64 "\n", R->GetPC() );
73+
#endif
74+
break;
75+
76+
case FRMode::DYN:
77+
output->fatal( CALL_INFO, -1, "Illegal FCSR Rounding Mode of DYN at PC = 0x%" PRIx64 "\n", R->GetPC() );
78+
break;
79+
80+
default: output->fatal( CALL_INFO, -1, "Unknown Rounding Mode at PC = 0x%" PRIx64 "\n", R->GetPC() ); break;
81+
}
82+
if( ret != 0 ) {
83+
output->fatal( CALL_INFO, -1, "Could not set FP rounding mode at PC = 0x%" PRIx64 "\n", R->GetPC() );
4784
}
4885
}
4986

50-
/// Destructor restores Fenv state
51-
~RevFenv() { SetRound( saved_round ); }
87+
/// Destructor sets FP exception flags and restores host FP Environment
88+
~RevFenv() {
89+
// RISC-V does not support FP traps
90+
// Set the accumulated fflags based on exceptions
91+
int except = std::fetestexcept( FE_ALL_EXCEPT );
92+
if( except & FE_DIVBYZERO )
93+
fcsr = FCSR{ static_cast<uint32_t>( fcsr ) | static_cast<uint32_t>( FCSR::DZ ) };
94+
if( except & FE_INEXACT )
95+
fcsr = FCSR{ static_cast<uint32_t>( fcsr ) | static_cast<uint32_t>( FCSR::NX ) };
96+
if( except & FE_INVALID )
97+
fcsr = FCSR{ static_cast<uint32_t>( fcsr ) | static_cast<uint32_t>( FCSR::NV ) };
98+
if( except & FE_OVERFLOW )
99+
fcsr = FCSR{ static_cast<uint32_t>( fcsr ) | static_cast<uint32_t>( FCSR::OF ) };
100+
if( except & FE_UNDERFLOW )
101+
fcsr = FCSR{ static_cast<uint32_t>( fcsr ) | static_cast<uint32_t>( FCSR::UF ) };
52102

53-
// We allow moving, but not copying RevFenv
54-
// This is to ensure that there is only a single copy
55-
// of the saved state at a time, similar to std::unique_ptr
103+
// Restore the host's saved FP Environment
104+
std::fesetenv( &saved_env );
105+
}
106+
107+
// We allow moving, but not copying or assigning RevFenv.
56108
RevFenv( RevFenv&& ) = default;
57109
RevFenv( const RevFenv& ) = delete;
58-
59-
// We disallow assigning
60110
RevFenv& operator=( const RevFenv& ) = delete;
61-
RevFenv& operator=( RevFenv&& ) = delete;
62-
63-
// Get the current FP rounding state
64-
static int GetRound() { return round(); }
65-
66-
// Set the FP rounding state if it differs from current
67-
static int SetRound( int mode ) {
68-
int rc = 0;
69-
if( mode != round() ) {
70-
rc = fesetround( mode );
71-
if( rc == 0 ) {
72-
round() = mode;
73-
}
74-
}
75-
return rc;
76-
}
77111
}; // RevFenv
78112

79113
} // namespace SST::RevCPU

include/RevHart.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class RevHart {
5959
uint16_t GetID() const { return ID; }
6060

6161
///< RevHart: Returns the ID of the assigned thread
62-
uint32_t GetAssignedThreadID() const { return ( Thread != nullptr ) ? Thread->GetID() : _INVALID_TID_; }
62+
uint32_t GetAssignedThreadID() const { return Thread ? Thread->GetID() : _INVALID_TID_; }
6363

6464
///< RevHart: Load the register file from the RevThread
6565
void LoadRegFile( std::unique_ptr<RevRegFile> regFile ) {

0 commit comments

Comments
 (0)