Changed interface to riscv_top/cheriot_top to make it easier to reuse the class. PiperOrigin-RevId: 643133613 Change-Id: I8a6e201483394e112e85cc9ff7e2361a81d183bc
diff --git a/cheriot/BUILD b/cheriot/BUILD index 17501f1..e2750df 100644 --- a/cheriot/BUILD +++ b/cheriot/BUILD
@@ -220,6 +220,7 @@ ":debug_command_shell", ":instrumentation", ":riscv_cheriot", + ":riscv_cheriot_decoder", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", "@com_google_absl//absl/flags:usage", @@ -337,6 +338,7 @@ ":debug_command_shell", ":instrumentation", ":riscv_cheriot", + ":riscv_cheriot_decoder", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/functional:bind_front", "@com_google_absl//absl/log",
diff --git a/cheriot/cheriot_decoder.cc b/cheriot/cheriot_decoder.cc index ba5e4d7..63e6103 100644 --- a/cheriot/cheriot_decoder.cc +++ b/cheriot/cheriot_decoder.cc
@@ -44,7 +44,7 @@ // Need a data buffer to load instructions from memory. Allocate a single // buffer that can be reused for each instruction word. - inst_db_ = state_->db_factory()->Allocate<uint32_t>(1); + inst_db_ = db_factory_.Allocate<uint32_t>(1); // Allocate the isa factory class, the top level isa decoder instance, and // the encoding parser. cheriot_isa_factory_ = new CheriotIsaFactory();
diff --git a/cheriot/cheriot_decoder.h b/cheriot/cheriot_decoder.h index 93442f3..7912734 100644 --- a/cheriot/cheriot_decoder.h +++ b/cheriot/cheriot_decoder.h
@@ -64,6 +64,15 @@ // instance will raise an internal simulator error when executed. generic::Instruction *DecodeInstruction(uint64_t address) override; + // Return the number of opcodes supported by this decoder. + int GetNumOpcodes() const override { + return static_cast<int>(OpcodeEnum::kPastMaxValue); + } + // Return the name of the opcode at the given index. + const char *GetOpcodeName(int index) const override { + return isa32::kOpcodeNames[index]; + } + // Getter. isa32::RiscVCheriotEncoding *cheriot_encoding() const { return cheriot_encoding_; @@ -73,6 +82,7 @@ CheriotState *state_; util::MemoryInterface *memory_; std::unique_ptr<generic::ProgramError> decode_error_; + generic::DataBufferFactory db_factory_; generic::DataBuffer *inst_db_; isa32::RiscVCheriotEncoding *cheriot_encoding_; isa32::RiscVCheriotInstructionSetFactory *cheriot_isa_factory_;
diff --git a/cheriot/cheriot_renode.cc b/cheriot/cheriot_renode.cc index aad6ac0..e308fa9 100644 --- a/cheriot/cheriot_renode.cc +++ b/cheriot/cheriot_renode.cc
@@ -35,6 +35,7 @@ #include "cheriot/cheriot_cli_forwarder.h" #include "cheriot/cheriot_debug_info.h" #include "cheriot/cheriot_debug_interface.h" +#include "cheriot/cheriot_decoder.h" #include "cheriot/cheriot_instrumentation_control.h" #include "cheriot/cheriot_renode_cli_top.h" #include "cheriot/cheriot_renode_register_info.h" @@ -104,10 +105,13 @@ mem_profiler_ = new TaggedMemoryUseProfiler(data_memory); data_memory = mem_profiler_; mem_profiler_->set_is_enabled(false); + cheriot_state_ = new CheriotState( + "CherIoT", data_memory, static_cast<AtomicMemoryOpInterface *>(router_)); + cheriot_decoder_ = new CheriotDecoder( + cheriot_state_, static_cast<MemoryInterface *>(router_)); + // Instantiate cheriot_top. - cheriot_top_ = - new CheriotTop(name, static_cast<MemoryInterface *>(router_), data_memory, - static_cast<MemoryInterface *>(router_)); + cheriot_top_ = new CheriotTop("Cheriot", cheriot_state_, cheriot_decoder_); // Initialize minstret/minstreth. Bind the instruction counter to those // registers. auto minstret_res = cheriot_top_->state()->csr_set()->GetCsr("minstret"); @@ -142,9 +146,10 @@ CHECK_OK(renode_router_->AddDefaultTarget<MemoryInterface>(tagged_memory_)); // Set up semihosting. - semihost_ = new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord32, - cheriot_top_->inst_memory(), - cheriot_top_->data_memory()); + semihost_ = + new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord32, + static_cast<MemoryInterface *>(router_), + static_cast<MemoryInterface *>(renode_router_)); // Set up special handlers (ebreak, wfi, ecall). cheriot_top_->state()->AddEbreakHandler([this](const Instruction *inst) { if (this->semihost_->IsSemihostingCall(inst)) { @@ -217,6 +222,8 @@ delete socket_cli_; delete cheriot_renode_cli_top_; delete cheriot_cli_forwarder_; + delete cheriot_decoder_; + delete cheriot_state_; delete cheriot_top_; delete semihost_; delete router_;
diff --git a/cheriot/cheriot_renode.h b/cheriot/cheriot_renode.h index 377c01f..0f2717c 100644 --- a/cheriot/cheriot_renode.h +++ b/cheriot/cheriot_renode.h
@@ -24,8 +24,10 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "cheriot/cheriot_cli_forwarder.h" +#include "cheriot/cheriot_decoder.h" #include "cheriot/cheriot_instrumentation_control.h" #include "cheriot/cheriot_renode_cli_top.h" +#include "cheriot/cheriot_state.h" #include "cheriot/cheriot_top.h" #include "cheriot/debug_command_shell.h" #include "mpact/sim/generic/core_debug_interface.h" @@ -138,6 +140,8 @@ std::string name_; MemoryInterface *renode_sysbus_ = nullptr; TaggedMemoryInterface *tagged_sysbus_ = nullptr; + CheriotState *cheriot_state_ = nullptr; + CheriotDecoder *cheriot_decoder_ = nullptr; CheriotTop *cheriot_top_ = nullptr; RiscVArmSemihost *semihost_ = nullptr; SingleInitiatorRouter *router_ = nullptr;
diff --git a/cheriot/cheriot_state.cc b/cheriot/cheriot_state.cc index 525c5c8..1842f29 100644 --- a/cheriot/cheriot_state.cc +++ b/cheriot/cheriot_state.cc
@@ -289,11 +289,6 @@ // Create the other CSRs. csr_set_ = new RiscVCsrSet(); CreateCsrs<uint32_t>(this, csr_vec_); - if (tagged_memory_ == nullptr) { - owned_tagged_memory_ = new util::TaggedFlatDemandMemory( - CheriotRegister::kCapabilitySizeInBytes); - tagged_memory_ = owned_tagged_memory_; - } pc_src_operand_ = new RiscVCheri32PcSourceOperand(this); set_pc_operand(pc_src_operand_); // Create the revocation data buffer. @@ -308,7 +303,6 @@ delete executable_root_; delete sealing_root_; delete memory_root_; - delete owned_tagged_memory_; delete pc_src_operand_; for (auto *csr : csr_vec_) delete csr; delete csr_set_;
diff --git a/cheriot/cheriot_state.h b/cheriot/cheriot_state.h index 7df9bba..c14c73e 100644 --- a/cheriot/cheriot_state.h +++ b/cheriot/cheriot_state.h
@@ -168,8 +168,6 @@ util::AtomicMemoryOpInterface *atomic_memory); CheriotState(std::string_view id, util::TaggedMemoryInterface *memory) : CheriotState(id, memory, nullptr) {} - explicit CheriotState(std::string_view id) - : CheriotState(id, nullptr, nullptr) {} ~CheriotState() override; // Deleted constructors and operators. @@ -283,7 +281,7 @@ inline void reset_is_interrupt_available() { is_interrupt_available_ = false; } - void set_memory(util::TaggedMemoryInterface *tagged_memory) { + void set_tagged_memory(util::TaggedMemoryInterface *tagged_memory) { tagged_memory_ = tagged_memory; } util::TaggedMemoryInterface *tagged_memory() const { return tagged_memory_; } @@ -387,7 +385,6 @@ uint64_t max_physical_address_; uint64_t min_physical_address_ = 0; int num_tags_per_load_; - util::TaggedMemoryInterface *owned_tagged_memory_ = nullptr; util::TaggedMemoryInterface *tagged_memory_; util::AtomicMemoryOpInterface *atomic_tagged_memory_; RiscVCsrSet *csr_set_;
diff --git a/cheriot/cheriot_top.cc b/cheriot/cheriot_top.cc index a8b7221..5321f0d 100644 --- a/cheriot/cheriot_top.cc +++ b/cheriot/cheriot_top.cc
@@ -38,6 +38,7 @@ #include "cheriot/riscv_cheriot_register_aliases.h" #include "mpact/sim/generic/component.h" #include "mpact/sim/generic/core_debug_interface.h" +#include "mpact/sim/generic/counters.h" #include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/decode_cache.h" #include "mpact/sim/generic/type_helpers.h" @@ -62,71 +63,11 @@ using EC = ::mpact::sim::cheriot::ExceptionCode; using PB = ::mpact::sim::cheriot::CheriotRegister::PermissionBits; -CheriotTop::CheriotTop(std::string name) +CheriotTop::CheriotTop(std::string name, CheriotState *state, + CheriotDecoder *decoder) : Component(name), - owns_memory_(true), - counter_num_instructions_("num_instructions", 0), - counter_num_cycles_("num_cycles", 0), - counter_pc_("pc", 0), - cap_reg_re_{ - R"((\w+)\.(top|base|length|tag|permissions|object_type|reserved))"} { - data_memory_ = new util::TaggedFlatDemandMemory(8); - inst_memory_ = data_memory_; - Initialize(); -} - -CheriotTop::CheriotTop(std::string name, util::TaggedMemoryInterface *memory) - : Component(name), - inst_memory_(memory), - data_memory_(memory), - atomic_memory_(nullptr), - owns_memory_(false), - counter_num_instructions_("num_instructions", 0), - counter_num_cycles_("num_cycles", 0), - counter_pc_("pc", 0), - cap_reg_re_{ - R"((\w+)\.(top|base|length|tag|permissions|object_type|reserved))"} { - Initialize(); -} - -CheriotTop::CheriotTop(std::string name, util::MemoryInterface *inst_memory, - util::TaggedMemoryInterface *data_memory) - : Component(name), - inst_memory_(inst_memory), - data_memory_(data_memory), - atomic_memory_(nullptr), - owns_memory_(false), - counter_num_instructions_("num_instructions", 0), - counter_num_cycles_("num_cycles", 0), - counter_pc_("pc", 0), - cap_reg_re_{ - R"((\w+)\.(top|base|length|tag|permissions|object_type|reserved))"} { - Initialize(); -} - -CheriotTop::CheriotTop(std::string name, util::TaggedMemoryInterface *memory, - util::MemoryInterface *atomic_memory_if) - : Component(name), - inst_memory_(memory), - data_memory_(memory), - atomic_memory_if_(atomic_memory_if), - owns_memory_(false), - counter_num_instructions_("num_instructions", 0), - counter_num_cycles_("num_cycles", 0), - counter_pc_("pc", 0), - cap_reg_re_{ - R"((\w+)\.(top|base|length|tag|permissions|object_type|reserved))"} { - Initialize(); -} - -CheriotTop::CheriotTop(std::string name, util::MemoryInterface *inst_memory, - util::TaggedMemoryInterface *data_memory, - util::MemoryInterface *atomic_memory_if) - : Component(name), - inst_memory_(inst_memory), - data_memory_(data_memory), - atomic_memory_if_(atomic_memory_if), - owns_memory_(false), + state_(state), + cheriot_decoder_(decoder), counter_num_instructions_("num_instructions", 0), counter_num_cycles_("num_cycles", 0), counter_pc_("pc", 0), @@ -148,32 +89,35 @@ delete rv_bp_manager_; delete cheriot_decode_cache_; - delete cheriot_decoder_; - delete state_; - delete tagged_watcher_; - delete atomic_watcher_; delete atomic_memory_; - if (owns_memory_) delete inst_memory_; + delete tagged_watcher_; + delete memory_watcher_; } void CheriotTop::Initialize() { - // Create the simulation state. - tagged_watcher_ = new util::TaggedMemoryWatcher(data_memory_); - atomic_watcher_ = new util::MemoryWatcher(atomic_memory_if_); - atomic_memory_ = new util::AtomicMemory(atomic_watcher_); - state_ = new CheriotState(kCheriotName, tagged_watcher_, atomic_memory_); + // Create the watchers. + auto *memory = static_cast<util::MemoryInterface *>(state_->tagged_memory()); + tagged_watcher_ = new util::TaggedMemoryWatcher(state_->tagged_memory()); + memory_watcher_ = new util::MemoryWatcher(memory); + + atomic_memory_ = new util::AtomicMemory(memory_watcher_); + state_->set_tagged_memory(tagged_watcher_); + state_->set_atomic_tagged_memory(atomic_memory_); + pcc_ = static_cast<CheriotRegister *>( state_->registers()->at(CheriotState::kPcName)); - // Set up the decoder and decode cache. - cheriot_decoder_ = new CheriotDecoder(state_, inst_memory_); - // Register instruction opcode counters. - for (int i = 0; i < static_cast<int>(isa32::OpcodeEnum::kPastMaxValue); i++) { - counter_opcode_[i].Initialize(absl::StrCat("num_", isa32::kOpcodeNames[i]), - 0); + + // Register opcode counters. + int num_opcodes = cheriot_decoder_->GetNumOpcodes(); + counter_opcode_.resize(num_opcodes); + for (int i = 0; i < num_opcodes; i++) { + counter_opcode_.push_back(generic::SimpleCounter<uint64_t>()); + counter_opcode_[i].Initialize( + absl::StrCat("num_", cheriot_decoder_->GetOpcodeName(i)), 0); CHECK_OK(AddCounter(&counter_opcode_[i])) - << absl::StrCat("Failed to register opcode counter for :'", - isa32::kOpcodeNames[i], "'"); + << "Failed to register opcode counter"; } + cheriot_decode_cache_ = generic::DecodeCache::Create({16 * 1024, 2}, cheriot_decoder_); // Register instruction counter. @@ -184,8 +128,8 @@ // Breakpoints. rv_action_point_manager_ = new riscv::RiscVActionPointManager( - inst_memory_, absl::bind_front(&generic::DecodeCache::Invalidate, - cheriot_decode_cache_)); + memory, absl::bind_front(&generic::DecodeCache::Invalidate, + cheriot_decode_cache_)); rv_bp_manager_ = new riscv::RiscVBreakpointManager( rv_action_point_manager_, [this](HaltReason halt_reason) { RequestHalt(halt_reason, nullptr); }); @@ -231,21 +175,25 @@ } bool CheriotTop::ExecuteInstruction(Instruction *inst) { + // Check that pcc has tag set. if (!pcc_->tag()) { state_->HandleCheriRegException(inst, inst->address(), EC::kCapExTagViolation, pcc_); return true; } + // Check that pcc has execute permission. if (!pcc_->HasPermission(PB::kPermitExecute)) { state_->HandleCheriRegException(inst, inst->address(), EC::kCapExPermitExecuteViolation, pcc_); return true; } + // Check that pcc is within bounds. if (!pcc_->IsInBounds(inst->address(), inst->size())) { state_->HandleCheriRegException(inst, inst->address(), EC::kCapExBoundsViolation, pcc_); return true; } + // Execute the instruction. inst->Execute(nullptr); counter_pc_.SetValue(inst->address()); // Comment out instruction logging during execution. @@ -859,7 +807,7 @@ }); if (!rd_tagged_status.ok()) return rd_tagged_status; - auto rd_atomic_status = atomic_watcher_->SetLoadWatchCallback( + auto rd_atomic_status = memory_watcher_->SetLoadWatchCallback( util::MemoryWatcher::AddressRange(address, address + length - 1), [this](uint64_t address, int size) { set_halt_string(absl::StrFormat( @@ -885,12 +833,12 @@ if (access_type == AccessType::kLoadStore) { // Error recovery - ignore return value. (void)tagged_watcher_->ClearLoadWatchCallback(address); - (void)atomic_watcher_->ClearLoadWatchCallback(address); + (void)memory_watcher_->ClearLoadWatchCallback(address); } return wr_tagged_status; } - auto wr_atomic_status = atomic_watcher_->SetStoreWatchCallback( + auto wr_atomic_status = memory_watcher_->SetStoreWatchCallback( util::MemoryWatcher::AddressRange(address, address + length - 1), [this](uint64_t address, int size) { set_halt_string(absl::StrFormat( @@ -902,7 +850,7 @@ (void)tagged_watcher_->ClearStoreWatchCallback(address); if (access_type == AccessType::kLoadStore) { (void)tagged_watcher_->ClearLoadWatchCallback(address); - (void)atomic_watcher_->ClearLoadWatchCallback(address); + (void)memory_watcher_->ClearLoadWatchCallback(address); } return wr_atomic_status; } @@ -917,7 +865,7 @@ auto rd_tagged_status = tagged_watcher_->ClearLoadWatchCallback(address); if (!rd_tagged_status.ok()) return rd_tagged_status; - auto rd_atomic_status = atomic_watcher_->ClearLoadWatchCallback(address); + auto rd_atomic_status = memory_watcher_->ClearLoadWatchCallback(address); if (!rd_atomic_status.ok()) return rd_atomic_status; } if ((access_type == AccessType::kStore) || @@ -925,7 +873,7 @@ auto wr_tagged_status = tagged_watcher_->ClearStoreWatchCallback(address); if (!wr_tagged_status.ok()) return wr_tagged_status; - auto wr_atomic_status = atomic_watcher_->ClearStoreWatchCallback(address); + auto wr_atomic_status = memory_watcher_->ClearStoreWatchCallback(address); if (!wr_atomic_status.ok()) return wr_atomic_status; } return absl::OkStatus();
diff --git a/cheriot/cheriot_top.h b/cheriot/cheriot_top.h index 98d0421..4fc8d17 100644 --- a/cheriot/cheriot_top.h +++ b/cheriot/cheriot_top.h
@@ -20,6 +20,7 @@ #include <cstddef> #include <cstdint> #include <string> +#include <vector> #include "absl/container/flat_hash_map.h" #include "absl/functional/any_invocable.h" @@ -27,6 +28,7 @@ #include "absl/status/statusor.h" #include "absl/synchronization/notification.h" #include "cheriot/cheriot_debug_interface.h" +#include "cheriot/cheriot_decoder.h" #include "cheriot/cheriot_register.h" #include "cheriot/cheriot_state.h" #include "cheriot/riscv_cheriot_enums.h" @@ -63,22 +65,7 @@ using RunStatus = generic::CoreDebugInterface::RunStatus; using HaltReason = generic::CoreDebugInterface::HaltReason; - // Simple constructor, the memories are created and owned by the CheriotTop - // instance. - explicit CheriotTop(std::string name); - // Constructors without the atomic memory interface. Either one (unified), - // or two (inst and data) interfaces are passed in. - CheriotTop(std::string name, util::TaggedMemoryInterface *memory); - CheriotTop(std::string name, util::MemoryInterface *inst_memory, - util::TaggedMemoryInterface *data_memory); - // Constructors with the memory interface to be used with atomic memory - // operations. Either one (unified), or two (inst and data) interfaces are - // passed in. - CheriotTop(std::string name, util::TaggedMemoryInterface *memory, - util::MemoryInterface *atomic_memory_if); - CheriotTop(std::string name, util::MemoryInterface *inst_memory, - util::TaggedMemoryInterface *data_memory, - util::MemoryInterface *atomic_memory_if); + CheriotTop(std::string name, CheriotState *state, CheriotDecoder *decoder); ~CheriotTop() override; // Methods inherited from CoreDebugInterface. @@ -138,8 +125,6 @@ // Accessors. CheriotState *state() const { return state_; } - util::TaggedMemoryInterface *data_memory() const { return data_memory_; } - util::MemoryInterface *inst_memory() const { return inst_memory_; } // The following are not const as callers may need to call non-const methods // of the counter. generic::SimpleCounter<uint64_t> *counter_num_instructions() { @@ -151,7 +136,7 @@ generic::SimpleCounter<uint64_t> *counter_pc() { return &counter_pc_; } // Memory watchers used for data watch points. util::TaggedMemoryWatcher *tagged_watcher() { return tagged_watcher_; } - util::MemoryWatcher *atomic_watcher() { return atomic_watcher_; } + util::MemoryWatcher *memory_watcher() { return memory_watcher_; } const std::string &halt_string() const { return halt_string_; } void set_halt_string(std::string halt_string) { halt_string_ = halt_string; } @@ -192,13 +177,9 @@ generic::DecoderInterface *cheriot_decoder_ = nullptr; // Decode cache, memory and memory watcher. generic::DecodeCache *cheriot_decode_cache_ = nullptr; - util::MemoryInterface *inst_memory_ = nullptr; - util::TaggedMemoryInterface *data_memory_ = nullptr; - util::MemoryInterface *atomic_memory_if_ = nullptr; util::AtomicMemoryOpInterface *atomic_memory_ = nullptr; - bool owns_memory_ = false; util::TaggedMemoryWatcher *tagged_watcher_ = nullptr; - util::MemoryWatcher *atomic_watcher_ = nullptr; + util::MemoryWatcher *memory_watcher_ = nullptr; // Branch trace info - uses a circular buffer. The size is defined by the // constant kBranchTraceSize in the .cc file. BranchTraceEntry *branch_trace_; @@ -211,8 +192,7 @@ int branch_trace_mask_ = kBranchTraceSize - 1; int branch_trace_size_ = kBranchTraceSize; // Counter for the number of instructions simulated. - generic::SimpleCounter<uint64_t> - counter_opcode_[*isa32::OpcodeEnum::kPastMaxValue]; + std::vector<generic::SimpleCounter<uint64_t>> counter_opcode_; generic::SimpleCounter<uint64_t> counter_num_instructions_; generic::SimpleCounter<uint64_t> counter_num_cycles_; // Counter used for profiling by connecting it to a profiler. This allows
diff --git a/cheriot/mpact_cheriot.cc b/cheriot/mpact_cheriot.cc index 65dce13..9174c63 100644 --- a/cheriot/mpact_cheriot.cc +++ b/cheriot/mpact_cheriot.cc
@@ -39,6 +39,7 @@ #include "absl/strings/string_view.h" #include "absl/time/clock.h" #include "absl/time/time.h" +#include "cheriot/cheriot_decoder.h" #include "cheriot/cheriot_instrumentation_control.h" #include "cheriot/cheriot_top.h" #include "cheriot/debug_command_shell.h" @@ -63,9 +64,11 @@ #include "riscv//riscv_clint.h" #include "src/google/protobuf/text_format.h" -using ::mpact::sim::proto::ComponentData; using AddressRange = mpact::sim::util::MemoryWatcher::AddressRange; +using ::mpact::sim::cheriot::CheriotDecoder; using ::mpact::sim::cheriot::CheriotInstrumentationControl; +using ::mpact::sim::cheriot::CheriotState; +using ::mpact::sim::proto::ComponentData; using ::mpact::sim::util::InstructionProfiler; using ::mpact::sim::util::TaggedMemoryUseProfiler; @@ -251,8 +254,12 @@ memory_use_profiler->set_is_enabled(false); data_memory = memory_use_profiler; } - CheriotTop cheriot_top("Cheriot", static_cast<MemoryInterface *>(router), - data_memory, static_cast<MemoryInterface *>(router)); + CheriotState cheriot_state("CherIoT", data_memory, + static_cast<AtomicMemoryOpInterface *>(router)); + CheriotDecoder cheriot_decoder(&cheriot_state, + static_cast<MemoryInterface *>(router)); + + CheriotTop cheriot_top("Cheriot", &cheriot_state, &cheriot_decoder); // Enable instruction profiling if the flag is set. InstructionProfiler *inst_profiler = nullptr; @@ -343,9 +350,10 @@ } // Set up semihosting. - auto *semihost = new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord32, - cheriot_top.inst_memory(), - cheriot_top.data_memory()); + auto *memory = static_cast<MemoryInterface *>(router); + auto *semihost = + new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord32, memory, memory); + cheriot_top.state()->AddEbreakHandler([semihost](const Instruction *inst) { if (semihost->IsSemihostingCall(inst)) { semihost->OnEBreak(inst);
diff --git a/cheriot/test/BUILD b/cheriot/test/BUILD index bcac027..c4920c9 100644 --- a/cheriot/test/BUILD +++ b/cheriot/test/BUILD
@@ -16,27 +16,6 @@ package(default_applicable_licenses = ["//:license"]) -cc_library( - name = "riscv_cheriot_fp_test_base", - testonly = True, - hdrs = ["riscv_cheriot_fp_test_base.h"], - copts = [ - "-ffp-model=strict", - "-fprotect-parens", - ], - tags = ["not_run:arm"], - deps = [ - "//cheriot:riscv_cheriot", - "@com_google_absl//absl/random", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:span", - "@com_google_googletest//:gtest_for_library_testonly", - "@com_google_mpact-riscv//riscv:riscv_state", - "@com_google_mpact-sim//mpact/sim/generic:instruction", - "@com_google_mpact-sim//mpact/sim/util/memory", - ], -) - cc_test( name = "cheriot_register_test", size = "small", @@ -102,6 +81,7 @@ "@com_google_mpact-sim//mpact/sim/generic:core", "@com_google_mpact-sim//mpact/sim/generic:instruction", "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/memory", ], ) @@ -118,6 +98,7 @@ "@com_google_mpact-sim//mpact/sim/generic:core", "@com_google_mpact-sim//mpact/sim/generic:instruction", "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/memory", ], ) @@ -137,6 +118,7 @@ "@com_google_mpact-riscv//riscv:riscv_state", "@com_google_mpact-sim//mpact/sim/generic:core", "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/util/memory", ], ) @@ -152,6 +134,7 @@ "//cheriot:riscv_cheriot_isa", "@com_google_googletest//:gtest_main", "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/memory", ], )
diff --git a/cheriot/test/cheriot_state_test.cc b/cheriot/test/cheriot_state_test.cc index 28c0c04..85c728e 100644 --- a/cheriot/test/cheriot_state_test.cc +++ b/cheriot/test/cheriot_state_test.cc
@@ -30,6 +30,7 @@ using ::mpact::sim::cheriot::CheriotRegister; using ::mpact::sim::cheriot::CheriotState; +using ::mpact::sim::util::TaggedFlatDemandMemory; using RVEC = ::mpact::sim::riscv::ExceptionCode; constexpr int kPcValue = 0x1000; @@ -40,7 +41,9 @@ // additional functionality over the ArchState class. TEST(CheriotStateTest, Basic) { - auto *state = new CheriotState("test"); + TaggedFlatDemandMemory mem(8); + + auto *state = new CheriotState("test", &mem, nullptr); // Make sure pc has been created. auto iter = state->registers()->find("pcc"); auto *ptr = (iter != state->registers()->end()) ? iter->second : nullptr; @@ -56,7 +59,9 @@ } TEST(CheriotStateTest, Memory) { - auto *state = new CheriotState("test"); + TaggedFlatDemandMemory mem(8); + + auto *state = new CheriotState("test", &mem, nullptr); auto *db = state->db_factory()->Allocate<uint32_t>(1); state->LoadMemory(nullptr, kMemAddr, db, nullptr, nullptr); EXPECT_EQ(db->Get<uint32_t>(0), 0); @@ -70,7 +75,9 @@ } TEST(CheriotStateTest, OutOfBoundLoad) { - auto *state = new CheriotState("test"); + TaggedFlatDemandMemory mem(8); + + auto *state = new CheriotState("test", &mem, nullptr); state->set_max_physical_address(kMemAddr - 4); state->set_on_trap([](bool is_interrupt, uint64_t trap_value, uint64_t exception_code, uint64_t epc,
diff --git a/cheriot/test/riscv_cheriot_encoding_test.cc b/cheriot/test/riscv_cheriot_encoding_test.cc index 2f941e7..31604ba 100644 --- a/cheriot/test/riscv_cheriot_encoding_test.cc +++ b/cheriot/test/riscv_cheriot_encoding_test.cc
@@ -20,15 +20,17 @@ #include "cheriot/riscv_cheriot_enums.h" #include "googlemock/include/gmock/gmock.h" #include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/memory/tagged_flat_demand_memory.h" namespace { using ::mpact::sim::cheriot::CheriotState; using ::mpact::sim::cheriot::isa32::kOpcodeNames; using ::mpact::sim::cheriot::isa32::RiscVCheriotEncoding; +using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). +using ::mpact::sim::util::TaggedFlatDemandMemory; using SlotEnum = mpact::sim::cheriot::isa32::SlotEnum; using OpcodeEnum = mpact::sim::cheriot::isa32::OpcodeEnum; -using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). // Constexpr for opcodes for RV32 CHERIoT instructions grouped by isa group. @@ -186,14 +188,17 @@ class RiscVCheriotEncodingTest : public testing::Test { protected: RiscVCheriotEncodingTest() { - state_ = new CheriotState("test"); + mem_ = new TaggedFlatDemandMemory(8); + state_ = new CheriotState("test", mem_, nullptr); enc_ = new RiscVCheriotEncoding(state_); } ~RiscVCheriotEncodingTest() override { delete enc_; + delete mem_; delete state_; } + TaggedFlatDemandMemory *mem_; CheriotState *state_; RiscVCheriotEncoding *enc_; };
diff --git a/cheriot/test/riscv_cheriot_fp_test_base.h b/cheriot/test/riscv_cheriot_fp_test_base.h deleted file mode 100644 index b47923d..0000000 --- a/cheriot/test/riscv_cheriot_fp_test_base.h +++ /dev/null
@@ -1,781 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MPACT_CHERIOT___TEST_RISCV_CHERIOT_FP_TEST_BASE_H_ -#define MPACT_CHERIOT___TEST_RISCV_CHERIOT_FP_TEST_BASE_H_ - -#include <cmath> -#include <cstdint> -#include <functional> -#include <ios> -#include <limits> -#include <string> -#include <tuple> -#include <type_traits> -#include <vector> - -#include "absl/random/random.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "absl/types/span.h" -#include "cheriot/cheriot_register.h" -#include "cheriot/cheriot_state.h" -#include "cheriot/riscv_cheriot_fp_state.h" -#include "googlemock/include/gmock/gmock.h" -#include "mpact/sim/generic/instruction.h" -#include "mpact/sim/util/memory/tagged_flat_demand_memory.h" -#include "riscv//riscv_fp_host.h" -#include "riscv//riscv_fp_info.h" -#include "riscv//riscv_register.h" - -namespace mpact { -namespace sim { -namespace cheriot { -namespace test { - -using ::mpact::sim::cheriot::CheriotRegister; -using ::mpact::sim::cheriot::CheriotState; -using ::mpact::sim::cheriot::RiscVCheriotFPState; -using ::mpact::sim::generic::Instruction; -using ::mpact::sim::riscv::FPRoundingMode; -using ::mpact::sim::riscv::RV64Register; -using ::mpact::sim::riscv::RVFpRegister; -using ::mpact::sim::riscv::ScopedFPRoundingMode; -using ::mpact::sim::riscv::ScopedFPStatus; -using ::mpact::sim::util::TaggedFlatDemandMemory; - -constexpr int kTestValueLength = 256; - -constexpr uint32_t kInstAddress = 0x1000; -constexpr uint32_t kDataLoadAddress = 0x1'0000; -constexpr uint32_t kDataStoreAddress = 0x2'0000; - -// Templated helper structs to provide information about floating point types. -template <typename T> -struct FPTypeInfo { - using IntType = T; - static const int kBitSize = 8 * sizeof(T); - static const int kExpSize = 0; - static const int kSigSize = 0; - static bool IsNaN(T value) { return false; } - static constexpr IntType kQNaN = 0; - static constexpr IntType kSNaN = 0; - static constexpr IntType kPosInf = std::numeric_limits<T>::max(); - static constexpr IntType kNegInf = std::numeric_limits<T>::min(); - static constexpr IntType kPosZero = 0; - static constexpr IntType kNegZero = 0; - static constexpr IntType kPosDenorm = 0; - static constexpr IntType kNegDenorm = 0; -}; - -template <> -struct FPTypeInfo<float> { - using T = float; - using IntType = uint32_t; - static const int kExpBias = 127; - static const int kBitSize = sizeof(float) << 3; - static const int kExpSize = 8; - static const int kSigSize = kBitSize - kExpSize - 1; - static const IntType kExpMask = ((1ULL << kExpSize) - 1) << kSigSize; - static const IntType kSigMask = (1ULL << kSigSize) - 1; - static const IntType kQNaN = kExpMask | (1ULL << (kSigSize - 1)) | 1; - static const IntType kSNaN = kExpMask | 1; - static const IntType kPosInf = kExpMask; - static const IntType kNegInf = kExpMask | (1ULL << (kBitSize - 1)); - static const IntType kPosZero = 0; - static const IntType kNegZero = 1ULL << (kBitSize - 1); - static const IntType kPosDenorm = 1ULL << (kSigSize - 2); - static const IntType kNegDenorm = - (1ULL << (kBitSize - 1)) | (1ULL << (kSigSize - 2)); - static const IntType kCanonicalNaN = 0x7fc0'0000ULL; - static bool IsNaN(T value) { return std::isnan(value); } -}; - -template <> -struct FPTypeInfo<double> { - using T = double; - using IntType = uint64_t; - static const int kExpBias = 1023; - static const int kBitSize = sizeof(double) << 3; - static const int kExpSize = 11; - static const int kSigSize = kBitSize - kExpSize - 1; - static const IntType kExpMask = ((1ULL << kExpSize) - 1) << kSigSize; - static const IntType kSigMask = (1ULL << kSigSize) - 1; - static const IntType kQNaN = kExpMask | (1ULL << (kSigSize - 1)) | 1; - static const IntType kSNaN = kExpMask | 1; - static const IntType kPosInf = kExpMask; - static const IntType kNegInf = kExpMask | (1ULL << (kBitSize - 1)); - static const IntType kPosZero = 0; - static const IntType kNegZero = 1ULL << (kBitSize - 1); - static const IntType kPosDenorm = 1ULL << (kSigSize - 2); - static const IntType kNegDenorm = - (1ULL << (kBitSize - 1)) | (1ULL << (kSigSize - 2)); - static const IntType kCanonicalNaN = 0x7ff8'0000'0000'0000ULL; - static bool IsNaN(T value) { return std::isnan(value); } -}; - -// Templated helper function for classifying fp numbers. -template <typename T> -typename FPTypeInfo<T>::IntType VfclassVHelper(T val) { - auto fp_class = fpclassify(val); - switch (fp_class) { - case FP_INFINITE: - return std::signbit(val) ? 1 : 1 << 7; - case FP_NAN: { - auto uint_val = - *reinterpret_cast<typename FPTypeInfo<T>::IntType *>(&val); - bool quiet_nan = (uint_val >> (FPTypeInfo<T>::kSigSize - 1)) & 1; - return quiet_nan ? 1 << 9 : 1 << 8; - } - case FP_ZERO: - return std::signbit(val) ? 1 << 3 : 1 << 4; - case FP_SUBNORMAL: - return std::signbit(val) ? 1 << 2 : 1 << 5; - case FP_NORMAL: - return std::signbit(val) ? 1 << 1 : 1 << 6; - } - return 0; -} - -// These templated functions allow for comparison of values with a tolerance -// given for floating point types. The tolerance is stated as the bit position -// in the mantissa of the op, with 0 being the msb of the mantissa. If the -// bit position is beyond the mantissa, a comparison of equal is performed. -template <typename T> -inline void FPCompare(T op, T reg, int, absl::string_view str) { - EXPECT_EQ(reg, op) << str; -} - -template <> -inline void FPCompare<float>(float op, float reg, int delta_position, - absl::string_view str) { - using T = float; - using UInt = typename FPTypeInfo<T>::IntType; - UInt u_op = *reinterpret_cast<UInt *>(&op); - UInt u_reg = *reinterpret_cast<UInt *>(®); - if (!std::isnan(op) && !std::isinf(op) && - delta_position < FPTypeInfo<T>::kSigSize) { - T delta; - UInt exp = FPTypeInfo<T>::kExpMask >> FPTypeInfo<T>::kSigSize; - if (exp > delta_position) { - exp -= delta_position; - UInt udelta = exp << FPTypeInfo<T>::kSigSize; - delta = *reinterpret_cast<T *>(&udelta); - } else { - // Becomes a denormal - int diff = delta_position - exp; - UInt udelta = 1ULL << (FPTypeInfo<T>::kSigSize - 1 - diff); - delta = *reinterpret_cast<T *>(&udelta); - } - EXPECT_THAT(reg, testing::NanSensitiveFloatNear(op, delta)) - << str << " op: " << std::hex << u_op << " reg: " << std::hex - << u_reg; - } else { - EXPECT_THAT(reg, testing::NanSensitiveFloatEq(op)) - << str << " op: " << std::hex << u_op << " reg: " << std::hex - << u_reg; - } -} - -template <> -inline void FPCompare<double>(double op, double reg, int delta_position, - absl::string_view str) { - using T = double; - using UInt = typename FPTypeInfo<T>::IntType; - UInt u_op = *reinterpret_cast<UInt *>(&op); - UInt u_reg = *reinterpret_cast<UInt *>(®); - if (!std::isnan(op) && !std::isinf(op) && - delta_position < FPTypeInfo<T>::kSigSize) { - T delta; - UInt exp = FPTypeInfo<T>::kExpMask >> FPTypeInfo<T>::kSigSize; - if (exp > delta_position) { - exp -= delta_position; - UInt udelta = exp << FPTypeInfo<T>::kSigSize; - delta = *reinterpret_cast<T *>(&udelta); - } else { - // Becomes a denormal - int diff = delta_position - exp; - UInt udelta = 1ULL << (FPTypeInfo<T>::kSigSize - 1 - diff); - delta = *reinterpret_cast<T *>(&udelta); - } - EXPECT_THAT(reg, testing::NanSensitiveDoubleNear(op, delta)) - << str << " op: " << std::hex << u_op << " reg: " << std::hex - << u_reg; - } else { - EXPECT_THAT(reg, testing::NanSensitiveDoubleEq(op)) - << str << " op: " << std::hex << u_op << " reg: " << std::hex - << u_reg; - } -} - -template <typename FP> -FP OptimizationBarrier(FP op) { - asm volatile("" : "+X"(op)); - return op; -} - -namespace internal { - -// These are predicates used in the following NaNBox function definitions, as -// part of the enable_if construct. -template <typename S, typename D> -struct EqualSize { - static const bool value = sizeof(S) == sizeof(D) && - std::is_floating_point<S>::value && - std::is_integral<D>::value; -}; - -template <typename S, typename D> -struct GreaterSize { - static const bool value = - sizeof(S) > sizeof(D) && - std::is_floating_point<S>::value &&std::is_integral<D>::value; -}; - -template <typename S, typename D> -struct LessSize { - static const bool value = sizeof(S) < sizeof(D) && - std::is_floating_point<S>::value && - std::is_integral<D>::value; -}; - -} // namespace internal - -// Template functions to NaN box a floating point value when being assigned -// to a wider register. The first version places a smaller floating point value -// in a NaN box (all upper bits in the word are set to 1). - -// Enable_if is used to select the proper implementation for different S and D -// type combinations. It uses the SFINAE (substitution failure is not an error) -// "feature" of C++ to hide the implementation that don't match the predicate -// from being resolved. - -template <typename S, typename D> -inline typename std::enable_if<internal::LessSize<S, D>::value, D>::type NaNBox( - S value) { - using SInt = typename FPTypeInfo<S>::IntType; - SInt sval = *reinterpret_cast<SInt *>(&value); - D dval = (~static_cast<D>(0) << (sizeof(S) * 8)) | sval; - return *reinterpret_cast<D *>(&dval); -} - -// This version does a straight copy - as the data types are the same size. -template <typename S, typename D> -inline typename std::enable_if<internal::EqualSize<S, D>::value, D>::type -NaNBox(S value) { - return *reinterpret_cast<D *>(&value); -} - -// Signal error if the register is smaller than the floating point value. -template <typename S, typename D> -inline typename std::enable_if<internal::GreaterSize<S, D>::value, D>::type -NaNBox(S value) { - // No return statement, so error will be reported. -} - -class RiscVFPInstructionTestBase : public testing::Test { - public: - RiscVFPInstructionTestBase() { - memory_ = new TaggedFlatDemandMemory(8); - state_ = new CheriotState("test", memory_); - rv_fp_ = new RiscVCheriotFPState(state_); - state_->set_rv_fp(rv_fp_); - instruction_ = new Instruction(kInstAddress, state_); - instruction_->set_size(4); - child_instruction_ = new Instruction(kInstAddress, state_); - child_instruction_->set_size(4); - // Initialize a portion of memory with a known pattern. - auto *db = state_->db_factory()->Allocate(8192); - auto span = db->Get<uint8_t>(); - for (int i = 0; i < 8192; i++) { - span[i] = i & 0xff; - } - memory_->Store(kDataLoadAddress - 4096, db); - db->DecRef(); - for (int i = 1; i < 32; i++) { - creg_[i] = - state_->GetRegister<CheriotRegister>(absl::StrCat("c", i)).first; - } - for (int i = 1; i < 32; i++) { - freg_[i] = state_->GetRegister<RVFpRegister>(absl::StrCat("f", i)).first; - } - for (int i = 1; i < 32; i++) { - dreg_[i] = state_->GetRegister<RVFpRegister>(absl::StrCat("d", i)).first; - } - } - ~RiscVFPInstructionTestBase() override { - delete rv_fp_; - rv_fp_ = nullptr; - state_->set_rv_fp(nullptr); - delete state_; - state_ = nullptr; - if (instruction_ != nullptr) instruction_->DecRef(); - instruction_ = nullptr; - if (child_instruction_ != nullptr) child_instruction_->DecRef(); - child_instruction_ = nullptr; - delete memory_; - memory_ = nullptr; - } - - // Clear the instruction instance and allocate a new one. - void ResetInstruction() { - instruction_->DecRef(); - instruction_ = new Instruction(kInstAddress, state_); - instruction_->set_size(4); - } - - // Creates source and destination scalar register operands for the registers - // named in the two vectors and append them to the given instruction. - void AppendRegisterOperands(Instruction *inst, - const std::vector<std::string> &sources, - const std::vector<std::string> &destinations) { - for (auto ®_name : sources) { - auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first; - inst->AppendSource(reg->CreateSourceOperand()); - } - for (auto ®_name : destinations) { - auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first; - inst->AppendDestination(reg->CreateDestinationOperand(0)); - } - } - - // Creates source and destination scalar register operands for the registers - // named in the two vectors and append them to the default instruction. - void AppendRegisterOperands(const std::vector<std::string> &sources, - const std::vector<std::string> &destinations) { - AppendRegisterOperands(instruction_, sources, destinations); - } - - // named register and sets it to the corresponding value. - template <typename T, typename RegisterType = CheriotRegister> - void SetRegisterValues( - const std::vector<std::tuple<std::string, const T>> &values) { - for (auto &[reg_name, value] : values) { - auto *reg = state_->GetRegister<RegisterType>(reg_name).first; - auto *db = - state_->db_factory()->Allocate<typename RegisterType::ValueType>(1); - db->template Set<T>(0, value); - reg->SetDataBuffer(db); - db->DecRef(); - } - } - - // Initializes the semantic function of the instruction object. - void SetSemanticFunction(Instruction *inst, - Instruction::SemanticFunction fcn) { - inst->set_semantic_function(fcn); - } - - // Initializes the semantic function for the default instruction. - void SetSemanticFunction(Instruction::SemanticFunction fcn) { - instruction_->set_semantic_function(fcn); - } - - // Sets the default child instruction as the child of the default instruction. - void SetChildInstruction() { instruction_->AppendChild(child_instruction_); } - - // Initializes the semantic function for the default child instruction. - void SetChildSemanticFunction(Instruction::SemanticFunction fcn) { - child_instruction_->set_semantic_function(fcn); - } - - // Construct a random FP value by separately generating integer values for - // sign, exponent and mantissa. - template <typename T> - T RandomFPValue() { - using UInt = typename FPTypeInfo<T>::IntType; - UInt sign = absl::Uniform(absl::IntervalClosed, bitgen_, 0ULL, 1ULL); - UInt exp = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0ULL, - 1ULL << FPTypeInfo<T>::kExpSize); - UInt sig = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0ULL, - 1ULL << FPTypeInfo<T>::kSigSize); - UInt value = (sign & 1) << (FPTypeInfo<T>::kBitSize - 1) | - (exp << FPTypeInfo<T>::kSigSize) | sig; - T val = *reinterpret_cast<T *>(&value); - return val; - } - - // This method uses random values for each field in the fp number. - template <typename T> - void FillArrayWithRandomFPValues(absl::Span<T> span) { - for (auto &val : span) { - val = RandomFPValue<T>(); - } - } - - template <typename R, typename LHS> - void UnaryOpFPTestHelper(absl::string_view name, Instruction *inst, - absl::Span<const absl::string_view> reg_prefixes, - int delta_position, - std::function<R(LHS)> operation) { - using LhsRegisterType = RVFpRegister; - using DestRegisterType = RVFpRegister; - LHS lhs_values[kTestValueLength]; - auto lhs_span = absl::Span<LHS>(lhs_values); - const std::string kR1Name = absl::StrCat(reg_prefixes[0], 1); - const std::string kRdName = absl::StrCat(reg_prefixes[1], 5); - // This is used for the rounding mode operand. - const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kRmName}, {kRdName}); - FillArrayWithRandomFPValues<LHS>(lhs_span); - using LhsInt = typename FPTypeInfo<LHS>::IntType; - *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN; - *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN; - *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf; - *reinterpret_cast<LhsInt *>(&lhs_span[3]) = FPTypeInfo<LHS>::kNegInf; - *reinterpret_cast<LhsInt *>(&lhs_span[4]) = FPTypeInfo<LHS>::kPosZero; - *reinterpret_cast<LhsInt *>(&lhs_span[5]) = FPTypeInfo<LHS>::kNegZero; - *reinterpret_cast<LhsInt *>(&lhs_span[6]) = FPTypeInfo<LHS>::kPosDenorm; - *reinterpret_cast<LhsInt *>(&lhs_span[7]) = FPTypeInfo<LHS>::kNegDenorm; - for (int i = 0; i < kTestValueLength; i++) { - SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}}); - - for (int rm : {0, 1, 2, 3, 4}) { - rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm)); - SetRegisterValues<int, CheriotRegister>({{kRmName, rm}}); - SetRegisterValues<R, DestRegisterType>({{kRdName, 0}}); - - inst->Execute(nullptr); - - R op_val; - { - ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface()); - op_val = operation(lhs_span[i]); - } - auto reg_val = state_->GetRegister<DestRegisterType>(kRdName) - .first->data_buffer() - ->template Get<R>(0); - FPCompare<R>(op_val, reg_val, delta_position, - absl::StrCat(name, " ", i, ": ", lhs_span[i])); - } - } - } - - // Tester for unary instructions that produce an exception flag value. - template <typename R, typename LHS> - void UnaryOpWithFflagsFPTestHelper( - absl::string_view name, Instruction *inst, - absl::Span<const absl::string_view> reg_prefixes, int delta_position, - std::function<std::tuple<R, uint32_t>(LHS)> operation) { - using LhsRegisterType = RVFpRegister; - using DestRegisterType = RVFpRegister; - using LhsInt = typename FPTypeInfo<LHS>::IntType; - using RInt = typename FPTypeInfo<R>::IntType; - LHS lhs_values[kTestValueLength]; - auto lhs_span = absl::Span<LHS>(lhs_values); - const std::string kR1Name = absl::StrCat(reg_prefixes[0], 1); - const std::string kRdName = absl::StrCat(reg_prefixes[1], 5); - // This is used for the rounding mode operand. - const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kRmName}, {kRdName}); - auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags"); - instruction_->AppendDestination(flag_op); - FillArrayWithRandomFPValues<LHS>(lhs_span); - *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN; - *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN; - *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf; - *reinterpret_cast<LhsInt *>(&lhs_span[3]) = FPTypeInfo<LHS>::kNegInf; - *reinterpret_cast<LhsInt *>(&lhs_span[4]) = FPTypeInfo<LHS>::kPosZero; - *reinterpret_cast<LhsInt *>(&lhs_span[5]) = FPTypeInfo<LHS>::kNegZero; - *reinterpret_cast<LhsInt *>(&lhs_span[6]) = FPTypeInfo<LHS>::kPosDenorm; - *reinterpret_cast<LhsInt *>(&lhs_span[7]) = FPTypeInfo<LHS>::kNegDenorm; - for (int i = 0; i < kTestValueLength; i++) { - SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}}); - - for (int rm : {0, 1, 2, 3, 4}) { - rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm)); - rv_fp_->fflags()->Write(static_cast<uint32_t>(0)); - SetRegisterValues<int, CheriotRegister>({{kRmName, rm}, {}}); - SetRegisterValues<R, DestRegisterType>({{kRdName, 0}}); - - inst->Execute(nullptr); - auto fflags = rv_fp_->fflags()->GetUint32(); - - R op_val; - uint32_t flag; - { - ScopedFPRoundingMode scoped_rm(rv_fp_->host_fp_interface(), rm); - std::tie(op_val, flag) = operation(lhs_span[i]); - } - - auto reg_val = state_->GetRegister<DestRegisterType>(kRdName) - .first->data_buffer() - ->template Get<R>(0); - FPCompare<R>( - op_val, reg_val, delta_position, - absl::StrCat(name, " ", i, ": ", lhs_span[i], " rm: ", rm)); - auto lhs_uint = *reinterpret_cast<LhsInt *>(&lhs_span[i]); - auto op_val_uint = *reinterpret_cast<RInt *>(&op_val); - EXPECT_EQ(flag, fflags) - << name << "(" << lhs_span[i] << ") " << std::hex << name << "(0x" - << lhs_uint << ") == " << op_val << std::hex << " 0x" - << op_val_uint << " rm: " << rm; - } - } - } - - // Test helper for binary fp instructions. - template <typename R, typename LHS, typename RHS> - void BinaryOpFPTestHelper(absl::string_view name, Instruction *inst, - absl::Span<const absl::string_view> reg_prefixes, - int delta_position, - std::function<R(LHS, RHS)> operation) { - using LhsRegisterType = RVFpRegister; - using RhsRegisterType = RVFpRegister; - using DestRegisterType = RVFpRegister; - LHS lhs_values[kTestValueLength]; - RHS rhs_values[kTestValueLength]; - auto lhs_span = absl::Span<LHS>(lhs_values); - auto rhs_span = absl::Span<RHS>(rhs_values); - const std::string kR1Name = absl::StrCat(reg_prefixes[0], 1); - const std::string kR2Name = absl::StrCat(reg_prefixes[1], 2); - const std::string kRdName = absl::StrCat(reg_prefixes[2], 5); - // This is used for the rounding mode operand. - const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kR2Name, kRmName}, {kRdName}); - auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags"); - instruction_->AppendDestination(flag_op); - FillArrayWithRandomFPValues<LHS>(lhs_span); - FillArrayWithRandomFPValues<RHS>(rhs_span); - using LhsInt = typename FPTypeInfo<LHS>::IntType; - *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN; - *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN; - *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf; - *reinterpret_cast<LhsInt *>(&lhs_span[3]) = FPTypeInfo<LHS>::kNegInf; - *reinterpret_cast<LhsInt *>(&lhs_span[4]) = FPTypeInfo<LHS>::kPosZero; - *reinterpret_cast<LhsInt *>(&lhs_span[5]) = FPTypeInfo<LHS>::kNegZero; - *reinterpret_cast<LhsInt *>(&lhs_span[6]) = FPTypeInfo<LHS>::kPosDenorm; - *reinterpret_cast<LhsInt *>(&lhs_span[7]) = FPTypeInfo<LHS>::kNegDenorm; - for (int i = 0; i < kTestValueLength; i++) { - SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}}); - SetRegisterValues<RHS, RhsRegisterType>({{kR2Name, rhs_span[i]}}); - - for (int rm : {0, 1, 2, 3, 4}) { - rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm)); - SetRegisterValues<int, CheriotRegister>({{kRmName, rm}}); - SetRegisterValues<R, DestRegisterType>({{kRdName, 0}}); - - inst->Execute(nullptr); - - R op_val; - { - ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface()); - op_val = operation(lhs_span[i], rhs_span[i]); - } - auto reg_val = state_->GetRegister<DestRegisterType>(kRdName) - .first->data_buffer() - ->template Get<R>(0); - FPCompare<R>(op_val, reg_val, delta_position, - absl::StrCat(name, " ", i, ": ", lhs_span[i], " ", - rhs_span[i], " rm: ", rm)); - } - if (HasFailure()) return; - } - } - - // Test helper for binary instructions that also produce an exception flag - // value. - template <typename R, typename LHS, typename RHS> - void BinaryOpWithFflagsFPTestHelper( - absl::string_view name, Instruction *inst, - absl::Span<const absl::string_view> reg_prefixes, int delta_position, - std::function<std::tuple<R, uint32_t>(LHS, RHS)> operation) { - using LhsRegisterType = RVFpRegister; - using RhsRegisterType = RVFpRegister; - using DestRegisterType = RVFpRegister; - using LhsUInt = typename FPTypeInfo<LHS>::IntType; - using RhsUInt = typename FPTypeInfo<RHS>::IntType; - LHS lhs_values[kTestValueLength]; - RHS rhs_values[kTestValueLength]; - auto lhs_span = absl::Span<LHS>(lhs_values); - auto rhs_span = absl::Span<RHS>(rhs_values); - const std::string kR1Name = absl::StrCat(reg_prefixes[0], 1); - const std::string kR2Name = absl::StrCat(reg_prefixes[1], 2); - const std::string kRdName = absl::StrCat(reg_prefixes[2], 5); - // This is used for the rounding mode operand. - const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kR2Name, kRmName}, {kRdName}); - auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags"); - instruction_->AppendDestination(flag_op); - FillArrayWithRandomFPValues<LHS>(lhs_span); - FillArrayWithRandomFPValues<RHS>(rhs_span); - using LhsInt = typename FPTypeInfo<LHS>::IntType; - *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN; - *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN; - *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf; - *reinterpret_cast<LhsInt *>(&lhs_span[3]) = FPTypeInfo<LHS>::kNegInf; - *reinterpret_cast<LhsInt *>(&lhs_span[4]) = FPTypeInfo<LHS>::kPosZero; - *reinterpret_cast<LhsInt *>(&lhs_span[5]) = FPTypeInfo<LHS>::kNegZero; - *reinterpret_cast<LhsInt *>(&lhs_span[6]) = FPTypeInfo<LHS>::kPosDenorm; - *reinterpret_cast<LhsInt *>(&lhs_span[7]) = FPTypeInfo<LHS>::kNegDenorm; - for (int i = 0; i < kTestValueLength; i++) { - SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}}); - SetRegisterValues<RHS, RhsRegisterType>({{kR2Name, rhs_span[i]}}); - - for (int rm : {0, 1, 2, 3, 4}) { - rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm)); - rv_fp_->fflags()->Write(static_cast<uint32_t>(0)); - SetRegisterValues<int, CheriotRegister>({{kRmName, rm}, {}}); - SetRegisterValues<R, DestRegisterType>({{kRdName, 0}}); - - inst->Execute(nullptr); - - R op_val; - uint32_t flag; - { - ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface()); - std::tie(op_val, flag) = operation(lhs_span[i], rhs_span[i]); - } - auto reg_val = state_->GetRegister<DestRegisterType>(kRdName) - .first->data_buffer() - ->template Get<R>(0); - FPCompare<R>( - op_val, reg_val, delta_position, - absl::StrCat(name, " ", i, ": ", lhs_span[i], " ", rhs_span[i])); - auto lhs_uint = *reinterpret_cast<LhsUInt *>(&lhs_span[i]); - auto rhs_uint = *reinterpret_cast<RhsUInt *>(&rhs_span[i]); - auto fflags = rv_fp_->fflags()->GetUint32(); - EXPECT_EQ(flag, fflags) - << std::hex << name << "(" << lhs_uint << ", " << rhs_uint << ")"; - } - } - } - - template <typename R, typename LHS, typename MHS, typename RHS> - void TernaryOpFPTestHelper(absl::string_view name, Instruction *inst, - absl::Span<const absl::string_view> reg_prefixes, - int delta_position, - std::function<R(LHS, MHS, RHS)> operation) { - using LhsRegisterType = RVFpRegister; - using MhsRegisterType = RVFpRegister; - using RhsRegisterType = RVFpRegister; - using DestRegisterType = RVFpRegister; - LHS lhs_values[kTestValueLength]; - MHS mhs_values[kTestValueLength]; - RHS rhs_values[kTestValueLength]; - auto lhs_span = absl::Span<LHS>(lhs_values); - auto mhs_span = absl::Span<MHS>(mhs_values); - auto rhs_span = absl::Span<RHS>(rhs_values); - const std::string kR1Name = absl::StrCat(reg_prefixes[0], 1); - const std::string kR2Name = absl::StrCat(reg_prefixes[1], 2); - const std::string kR3Name = absl::StrCat(reg_prefixes[2], 3); - const std::string kRdName = absl::StrCat(reg_prefixes[3], 5); - // This is used for the rounding mode operand. - const std::string kRmName = absl::StrCat("x", 10); - AppendRegisterOperands({kR1Name, kR2Name, kR3Name, kRmName}, {kRdName}); - FillArrayWithRandomFPValues<LHS>(lhs_span); - FillArrayWithRandomFPValues<MHS>(mhs_span); - FillArrayWithRandomFPValues<RHS>(rhs_span); - using LhsInt = typename FPTypeInfo<LHS>::IntType; - *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN; - *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN; - *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf; - *reinterpret_cast<LhsInt *>(&lhs_span[3]) = FPTypeInfo<LHS>::kNegInf; - *reinterpret_cast<LhsInt *>(&lhs_span[4]) = FPTypeInfo<LHS>::kPosZero; - *reinterpret_cast<LhsInt *>(&lhs_span[5]) = FPTypeInfo<LHS>::kNegZero; - *reinterpret_cast<LhsInt *>(&lhs_span[6]) = FPTypeInfo<LHS>::kPosDenorm; - *reinterpret_cast<LhsInt *>(&lhs_span[7]) = FPTypeInfo<LHS>::kNegDenorm; - for (int i = 0; i < kTestValueLength; i++) { - SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}}); - SetRegisterValues<MHS, MhsRegisterType>({{kR2Name, mhs_span[i]}}); - SetRegisterValues<RHS, RhsRegisterType>({{kR3Name, rhs_span[i]}}); - - for (int rm : {0, 1, 2, 3, 4}) { - rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm)); - SetRegisterValues<int, CheriotRegister>({{kRmName, rm}}); - SetRegisterValues<R, DestRegisterType>({{kRdName, 0}}); - - inst->Execute(nullptr); - - R op_val; - { - ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface()); - op_val = operation(lhs_span[i], mhs_span[i], rhs_span[i]); - } - auto reg_val = state_->GetRegister<DestRegisterType>(kRdName) - .first->data_buffer() - ->template Get<R>(0); - FPCompare<R>( - op_val, reg_val, delta_position, - absl::StrCat(name, " ", i, ": ", lhs_span[i], " ", rhs_span[i])); - } - } - } - - absl::Span<CheriotRegister *> creg() { - return absl::Span<CheriotRegister *>(creg_); - } - - absl::Span<RVFpRegister *> freg() { - return absl::Span<RVFpRegister *>(freg_); - } - - absl::Span<RV64Register *> dreg() { - return absl::Span<RV64Register *>(dreg_); - } - absl::BitGen &bitgen() { return bitgen_; } - Instruction *instruction() { return instruction_; } - - template <typename T> - T RoundToInteger(T val) { - using FromUint = typename FPTypeInfo<T>::IntType; - auto constexpr kBias = FPTypeInfo<T>::kExpBias; - auto constexpr kExpMask = FPTypeInfo<T>::kExpMask; - auto constexpr kSigSize = FPTypeInfo<T>::kSigSize; - auto constexpr kSigMask = FPTypeInfo<T>::kSigMask; - FromUint val_u = *reinterpret_cast<FromUint *>(&val); - FromUint exp = kExpMask & val_u; - FromUint sign = val_u & (1ULL << (FPTypeInfo<T>::kBitSize - 1)); - int exp_value = exp >> kSigSize; - FromUint sig = kSigMask & val_u; - // Turn the value into a denormal. - constexpr FromUint hidden = 1ULL << (kSigSize - 1); - FromUint tmp_u = sign | ((exp != 0) ? hidden : 0ULL) | (sig >> 1); - T tmp = *reinterpret_cast<T *>(&tmp_u); - if ((exp_value >= kBias) && (exp_value - kBias + 1 < kSigSize)) { - // Divide so that only the bits we care about are left in the significand. - int shift = kBias + kSigSize - exp_value - 1; - FromUint div_exp = shift + kBias; - FromUint div_u = div_exp << kSigSize; - T div = *reinterpret_cast<T *>(&div_u); - tmp /= div; - // Convert back to normalized number, by using the original sign - // and exponent, and the normalized and significand from the division. - tmp_u = *reinterpret_cast<FromUint *>(&tmp); - val_u = sign | exp | ((tmp_u << (shift + 1)) & kSigMask); - val = *reinterpret_cast<T *>(&val_u); - } - return val; - } - - protected: - CheriotRegister *creg_[32]; - RV64Register *dreg_[32]; - RVFpRegister *freg_[32]; - CheriotState *state_; - Instruction *instruction_; - Instruction *child_instruction_; - TaggedFlatDemandMemory *memory_; - RiscVCheriotFPState *rv_fp_; - absl::BitGen bitgen_; -}; - -} // namespace test -} // namespace cheriot -} // namespace sim -} // namespace mpact - -#endif // MPACT_CHERIOT___TEST_RISCV_CHERIOT_FP_TEST_BASE_H_
diff --git a/cheriot/test/riscv_cheriot_i_instructions_test.cc b/cheriot/test/riscv_cheriot_i_instructions_test.cc index 93dbd41..d5ab010 100644 --- a/cheriot/test/riscv_cheriot_i_instructions_test.cc +++ b/cheriot/test/riscv_cheriot_i_instructions_test.cc
@@ -28,6 +28,7 @@ #include "mpact/sim/generic/immediate_operand.h" #include "mpact/sim/generic/instruction.h" #include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/memory/tagged_flat_demand_memory.h" // This file contains tests for individual RiscV32I instructions. @@ -38,6 +39,7 @@ using ::mpact::sim::cheriot::CheriotState; using ::mpact::sim::generic::ImmediateOperand; using ::mpact::sim::generic::Instruction; +using ::mpact::sim::util::TaggedFlatDemandMemory; using CH_EC = ::mpact::sim::cheriot::ExceptionCode; using PB = CheriotRegister::PermissionBits; @@ -62,7 +64,8 @@ class RVCheriotIInstructionTest : public testing::Test { public: RVCheriotIInstructionTest() { - state_ = new CheriotState("test"); + mem_ = new TaggedFlatDemandMemory(8); + state_ = new CheriotState("test", mem_, nullptr); instruction_ = new Instruction(kInstAddress, state_); instruction_->set_size(4); creg_1_ = state_->GetRegister<CheriotRegister>(kC1).first; @@ -76,6 +79,7 @@ } ~RVCheriotIInstructionTest() override { + delete mem_; delete state_; delete instruction_; } @@ -135,6 +139,7 @@ uint64_t exception_code, uint64_t epc, const Instruction *inst); + TaggedFlatDemandMemory *mem_; CheriotState *state_; Instruction *instruction_; CheriotRegister *creg_1_;
diff --git a/cheriot/test/riscv_cheriot_m_instructions_test.cc b/cheriot/test/riscv_cheriot_m_instructions_test.cc index 4874485..ec23951 100644 --- a/cheriot/test/riscv_cheriot_m_instructions_test.cc +++ b/cheriot/test/riscv_cheriot_m_instructions_test.cc
@@ -28,6 +28,7 @@ #include "mpact/sim/generic/immediate_operand.h" #include "mpact/sim/generic/instruction.h" #include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/memory/tagged_flat_demand_memory.h" // This file contains tests for individual CHERIoT RiscV32M instructions. @@ -38,6 +39,7 @@ using ::mpact::sim::cheriot::CheriotState; using ::mpact::sim::generic::ImmediateOperand; using ::mpact::sim::generic::Instruction; +using ::mpact::sim::util::TaggedFlatDemandMemory; using CH_EC = ::mpact::sim::cheriot::ExceptionCode; using PB = CheriotRegister::PermissionBits; @@ -64,7 +66,8 @@ class RVCheriotMInstructionTest : public testing::Test { public: RVCheriotMInstructionTest() { - state_ = new CheriotState("test"); + mem_ = new TaggedFlatDemandMemory(8); + state_ = new CheriotState("test", mem_, nullptr); instruction_ = new Instruction(kInstAddress, state_); instruction_->set_size(4); creg_1_ = state_->GetRegister<CheriotRegister>(kC1).first; @@ -73,6 +76,7 @@ } ~RVCheriotMInstructionTest() override { + delete mem_; delete state_; delete instruction_; } @@ -128,6 +132,7 @@ return reg->address(); } + TaggedFlatDemandMemory *mem_; CheriotState *state_; Instruction *instruction_; CheriotRegister *creg_1_;
diff --git a/cheriot/test/riscv_cheriot_zicsr_instructions_test.cc b/cheriot/test/riscv_cheriot_zicsr_instructions_test.cc index a1792af..6d75d87 100644 --- a/cheriot/test/riscv_cheriot_zicsr_instructions_test.cc +++ b/cheriot/test/riscv_cheriot_zicsr_instructions_test.cc
@@ -28,6 +28,7 @@ #include "googlemock/include/gmock/gmock.h" #include "mpact/sim/generic/immediate_operand.h" #include "mpact/sim/generic/instruction.h" +#include "mpact/sim/util/memory/tagged_flat_demand_memory.h" #include "riscv//riscv_csr.h" // This file contains tests for individual Zicsr instructions. @@ -42,6 +43,7 @@ using ::mpact::sim::generic::ImmediateOperand; using ::mpact::sim::generic::Instruction; using ::mpact::sim::riscv::RiscV32SimpleCsr; +using ::mpact::sim::util::TaggedFlatDemandMemory; constexpr uint32_t kInstAddress = 0x2468; @@ -59,7 +61,8 @@ class ZicsrInstructionsTest : public testing::Test { protected: ZicsrInstructionsTest() { - state_ = new CheriotState("test"); + mem_ = new TaggedFlatDemandMemory(8); + state_ = new CheriotState("test", mem_, nullptr); instruction_ = new Instruction(kInstAddress, state_); instruction_->set_size(4); state_->set_on_trap([this](bool is_interrupt, uint64_t trap_value, @@ -71,6 +74,7 @@ ~ZicsrInstructionsTest() override { delete instruction_; + delete mem_; delete state_; } @@ -130,6 +134,7 @@ uint64_t exception_code, uint64_t epc, const Instruction *inst); + TaggedFlatDemandMemory *mem_; RiscV32SimpleCsr *csr_; CheriotState *state_; Instruction *instruction_;