Fixed errors in fmin and clmulh. Updated simulator targets to have the same set of options. PiperOrigin-RevId: 694530007 Change-Id: Ib2c9952db83c4343606e7abe199b40eba8885fdb
diff --git a/riscv/BUILD b/riscv/BUILD index da0d359..d2b0aa6 100644 --- a/riscv/BUILD +++ b/riscv/BUILD
@@ -313,11 +313,14 @@ src = "riscv32v.isa", includes = [ "riscv32g.isa", + "riscv32gzb.isa", + "riscv32zb.isa", "riscv_vector.isa", ], isa_name = "RiscV32GV", prefix = "riscv32gv", deps = [ + ":riscv_bitmanip_instructions", ":riscv_g", ":riscv_v", "@com_google_absl//absl/functional:bind_front", @@ -330,6 +333,7 @@ decoder_name = "RiscV32GV", includes = [ "riscv32g.bin_fmt", + "riscv32zb.bin_fmt", "riscv_vector.bin_fmt", ], prefix = "riscv32gv", @@ -370,6 +374,40 @@ ) mpact_isa_decoder( + name = "riscv32gvzb_isa", + src = "riscv32v.isa", + includes = [ + "riscv32g.isa", + "riscv32gzb.isa", + "riscv32zb.isa", + "riscv_vector.isa", + ], + isa_name = "RiscV32GVZB", + prefix = "riscv32gvzb", + deps = [ + ":riscv_bitmanip_instructions", + ":riscv_g", + ":riscv_v", + "@com_google_absl//absl/functional:bind_front", + ], +) + +mpact_bin_fmt_decoder( + name = "riscv32gvzb_bin_fmt", + src = "riscv32v.bin_fmt", + decoder_name = "RiscV32GVZB", + includes = [ + "riscv32g.bin_fmt", + "riscv32zb.bin_fmt", + "riscv_vector.bin_fmt", + ], + prefix = "riscv32gvzb", + deps = [ + ":riscv32gvzb_isa", + ], +) + +mpact_isa_decoder( name = "rvm23_isa", src = "rvm23.isa", includes = [ @@ -686,6 +724,40 @@ ) cc_library( + name = "riscv32gzb_vec_decoder", + srcs = [ + "riscv32gzb_vec_decoder.cc", + "riscv32gzb_vec_encoding.cc", + ], + hdrs = [ + "riscv32gzb_vec_decoder.h", + "riscv32gzb_vec_encoding.h", + ], + copts = ["-O3"], + deps = [ + ":riscv32gvzb_bin_fmt", + ":riscv32gvzb_isa", + ":riscv_encoding_common", + ":riscv_getters", + ":riscv_state", + "@com_google_absl//absl/base", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/functional:bind_front", + "@com_google_absl//absl/log", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:span", + "@com_google_mpact-sim//mpact/sim/generic:arch_state", + "@com_google_mpact-sim//mpact/sim/generic:core", + "@com_google_mpact-sim//mpact/sim/generic:instruction", + "@com_google_mpact-sim//mpact/sim/generic:program_error", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/memory", + ], +) + +cc_library( name = "riscv64g_decoder", srcs = [ "riscv64_decoder.cc", @@ -794,6 +866,7 @@ "@com_google_absl//absl/functional:bind_front", "@com_google_absl//absl/log", "@com_google_absl//absl/log:check", + "@com_google_absl//absl/numeric:bits", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", @@ -894,6 +967,7 @@ copts = ["-O3"], deps = [ ":riscv_debug_interface", + ":riscv_top", ":stoull_wrapper", "@com_google_absl//absl/container:btree", "@com_google_absl//absl/container:flat_hash_set", @@ -954,10 +1028,12 @@ ":riscv_fp_state", ":riscv_state", ":riscv_top", + "@com_google_absl//absl/base:log_severity", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", "@com_google_absl//absl/log", "@com_google_absl//absl/log:check", + "@com_google_absl//absl/log:globals", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", @@ -983,17 +1059,21 @@ ":debug_command_shell", ":riscv32_htif_semihost", ":riscv32g_vec_decoder", + ":riscv32gzb_vec_decoder", ":riscv_arm_semihost", ":riscv_fp_state", ":riscv_state", ":riscv_top", + "@com_google_absl//absl/base:log_severity", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", "@com_google_absl//absl/log", "@com_google_absl//absl/log:check", + "@com_google_absl//absl/log:globals", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", + "@com_google_mpact-sim//mpact/sim/generic:core", "@com_google_mpact-sim//mpact/sim/generic:core_debug_interface", "@com_google_mpact-sim//mpact/sim/generic:counters", "@com_google_mpact-sim//mpact/sim/generic:instruction", @@ -1018,10 +1098,12 @@ ":riscv_fp_state", ":riscv_state", ":riscv_top", + "@com_google_absl//absl/base:log_severity", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", "@com_google_absl//absl/log", "@com_google_absl//absl/log:check", + "@com_google_absl//absl/log:globals", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", @@ -1050,10 +1132,12 @@ ":riscv_fp_state", ":riscv_state", ":riscv_top", + "@com_google_absl//absl/base:log_severity", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", "@com_google_absl//absl/log", "@com_google_absl//absl/log:check", + "@com_google_absl//absl/log:globals", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time",
diff --git a/riscv/debug_command_shell.cc b/riscv/debug_command_shell.cc index 4147d99..6afea13 100644 --- a/riscv/debug_command_shell.cc +++ b/riscv/debug_command_shell.cc
@@ -35,6 +35,7 @@ #include "mpact/sim/generic/type_helpers.h" #include "re2/re2.h" #include "riscv/riscv_debug_interface.h" +#include "riscv/riscv_top.h" #include "riscv/stoull_wrapper.h" namespace mpact { @@ -83,6 +84,7 @@ disable_action_n_re_{R"(\s*action\s+disable\s+\*(\d+)\s*)"}, clear_action_n_re_{R"(\s*action\s+clear\s+\*(\d+)\s*)"}, clear_all_action_re_{R"(\s*action\s+clear-all\s*)"}, + branch_trace_re_{R"(\s*branch-trace\s*)"}, exec_re_{R"(\s*exec\s+(.+)\s*)"}, empty_re_{R"(\s*(?:\#.*)?)"}, help_re_{R"(\s*help\s*)"} { @@ -151,6 +153,8 @@ action clear #<N> - clear action point with index N. action clear-all - clear all action points. action - list action points. + branch-trace - list the control flow change (includes + interrupts) w/out repetitions due to loops. exec NAME - load commands from file 'NAME' and execute each line as a command. Lines starting with a '#' are treated as comments. @@ -1000,6 +1004,52 @@ continue; } + // branch-trace. + // This prints out a list of the last "branch trace size" pairs of <from, + // to> control flow changes (including interrupts), with no repetitions for + // loops. + if (RE2::FullMatch(line_view, *branch_trace_re_)) { + // Get the index of the head of the queue. + auto head_result = + core_access_[current_core_].debug_interface->ReadRegister( + "$branch_trace_head"); + if (!head_result.ok()) { + os << "Error: " << head_result.status().message() << "\n"; + continue; + } + // Adjust by one, as the head points to the most recent valid entry. + auto head = head_result.value() + 1; + // Get the branch trace data buffer. + auto result = + core_access_[current_core_].debug_interface->GetRegisterDataBuffer( + "$branch_trace"); + if (!result.ok()) { + os << "Error: " << result.status().message() << "\n"; + continue; + } + auto *db = result.value(); + // Check for null data buffer. + if (db == nullptr) { + os << "Error: register '$branch_trace' has no data buffer\n"; + os.flush(); + continue; + } + // Get a span for the branch trace. + auto trace_span = db->Get<BranchTraceEntry>(); + auto size = trace_span.size(); + os << absl::StrFormat(" %-8s %-8s %8s\n", "From", "To", + "Count"); + for (int i = 0; i < size; ++i) { + auto index = (head + i) % size; + auto [from, to, count] = trace_span[index]; + // Ignore 0 -> 0 entries. Those are the initial values. + if (count == 0) continue; + os << absl::StrFormat(" 0x%08x -> 0x%08x %8u\n", from, to, count); + } + os.flush(); + continue; + } + if (std::string file_name; RE2::FullMatch(line_view, *exec_re_, &file_name)) { auto *ifile = new std::ifstream(file_name);
diff --git a/riscv/riscv32gzb_vec_decoder.cc b/riscv/riscv32gzb_vec_decoder.cc new file mode 100644 index 0000000..cdef792 --- /dev/null +++ b/riscv/riscv32gzb_vec_decoder.cc
@@ -0,0 +1,104 @@ +// Copyright 2023 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 +// +// https://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. + +#include "riscv/riscv32gzb_vec_decoder.h" + +#include <cstdint> +#include <memory> +#include <new> +#include <string> + +#include "mpact/sim/generic/instruction.h" +#include "mpact/sim/generic/program_error.h" +#include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/memory/memory_interface.h" +#include "riscv/riscv32gvzb_decoder.h" +#include "riscv/riscv32gvzb_enums.h" +#include "riscv/riscv32gzb_vec_encoding.h" +#include "riscv/riscv_state.h" + +namespace mpact { +namespace sim { +namespace riscv { + +using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). + +RiscV32GZBVecDecoder::RiscV32GZBVecDecoder(RiscVState *state, + util::MemoryInterface *memory) + : state_(state), + memory_(memory), + inst_db_(state_->db_factory()->Allocate<uint32_t>(1)) { + // Get a handle to the internal error in the program error controller. + decode_error_ = state->program_error_controller()->GetProgramError( + generic::ProgramErrorController::kInternalErrorName); + + // Allocate the isa factory class, the top level isa decoder instance, and + // the encoding parser. + riscv_isa_factory_ = std::make_unique<RV32GVZBIsaFactory>(); + riscv_isa_ = std::make_unique<isa32gvzb::RiscV32GVZBInstructionSet>( + state, riscv_isa_factory_.get()); + riscv_encoding_ = std::make_unique<isa32gvzb::RiscV32GZBVecEncoding>(state); + decode_error_ = state->program_error_controller()->GetProgramError( + generic::ProgramErrorController::kInternalErrorName); +} + +RiscV32GZBVecDecoder::~RiscV32GZBVecDecoder() { inst_db_->DecRef(); } + +generic::Instruction *RiscV32GZBVecDecoder::DecodeInstruction( + uint64_t address) { + // First check that the address is aligned properly. If not, create and return + // an instruction object that will raise an exception. + if (address & 0x1) { + auto *inst = new generic::Instruction(address, state_); + inst->set_size(1); + inst->SetDisassemblyString("Misaligned instruction address"); + inst->set_opcode(*isa32gvzb::OpcodeEnum::kNone); + inst->set_address(address); + inst->set_semantic_function([this, address](generic::Instruction *inst) { + state_->Trap(/*is_interrupt*/ false, address, + *ExceptionCode::kInstructionAddressMisaligned, address ^ 0x1, + inst); + }); + return inst; + } + + // If the address is greater than the max address, return an instruction + // object that will raise an exception. + if (address > state_->max_physical_address()) { + auto *inst = new generic::Instruction(address, state_); + inst->set_size(0); + inst->SetDisassemblyString("Instruction access fault"); + inst->set_opcode(*isa32gvzb::OpcodeEnum::kNone); + inst->set_address(address); + inst->set_semantic_function([this, address](generic::Instruction *inst) { + state_->Trap(/*is_interrupt*/ false, address, + *ExceptionCode::kInstructionAccessFault, address, nullptr); + }); + return inst; + } + + // Read the instruction word from memory and parse it in the encoding parser. + memory_->Load(address, inst_db_, nullptr, nullptr); + const uint32_t iword = inst_db_->Get<uint32_t>(0); + riscv_encoding_->ParseInstruction(iword); + + // Call the isa decoder to obtain a new instruction object for the instruction + // word that was parsed above. + auto *instruction = riscv_isa_->Decode(address, riscv_encoding_.get()); + return instruction; +} + +} // namespace riscv +} // namespace sim +} // namespace mpact
diff --git a/riscv/riscv32gzb_vec_decoder.h b/riscv/riscv32gzb_vec_decoder.h new file mode 100644 index 0000000..4ecb035 --- /dev/null +++ b/riscv/riscv32gzb_vec_decoder.h
@@ -0,0 +1,90 @@ +// Copyright 2023 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 +// +// https://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 THIRD_PARTY_MPACT_RISCV_RISCV32GZB_VEC_DECODER_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV32GZB_VEC_DECODER_H_ + +#include <cstdint> +#include <memory> + +#include "mpact/sim/generic/data_buffer.h" +#include "mpact/sim/generic/decoder_interface.h" +#include "mpact/sim/generic/instruction.h" +#include "mpact/sim/generic/program_error.h" +#include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/memory/memory_interface.h" +#include "riscv/riscv32gvzb_decoder.h" +#include "riscv/riscv32gvzb_enums.h" +#include "riscv/riscv32gzb_vec_encoding.h" +#include "riscv/riscv_state.h" + +namespace mpact { +namespace sim { +namespace riscv { + +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. + +// This is the factory class needed by the generated decoder. It is responsible +// for creating the decoder for each slot instance. Since the riscv architecture +// only has a single slot, it's a pretty simple class. +class RV32GVZBIsaFactory : public isa32gvzb::RiscV32GVZBInstructionSetFactory { + public: + std::unique_ptr<isa32gvzb::Riscv32gvzbSlot> CreateRiscv32gvzbSlot( + ArchState *state) override { + return std::make_unique<isa32gvzb::Riscv32gvzbSlot>(state); + } +}; + +// This class implements the generic DecoderInterface and provides a bridge +// to the (isa specific) generated decoder classes. It implements a decoder that +// includes the RV32GZB + vector extensions. +class RiscV32GZBVecDecoder : public generic::DecoderInterface { + public: + using SlotEnum = isa32gvzb::SlotEnum; + using OpcodeEnum = isa32gvzb::OpcodeEnum; + + RiscV32GZBVecDecoder(RiscVState *state, util::MemoryInterface *memory); + RiscV32GZBVecDecoder() = delete; + ~RiscV32GZBVecDecoder() override; + + // This will always return a valid instruction that can be executed. In the + // case of a decode error, the semantic function in the instruction object + // 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 *OpcodeEnum::kPastMaxValue; } + // Return the name of the opcode at the given index. + const char *GetOpcodeName(int index) const override { + return isa32gvzb::kOpcodeNames[index]; + } + + private: + RiscVState *const state_; + util::MemoryInterface *const memory_; + + // Buffer used to load instructions from memory. Re-used for each instruction + // word. + generic::DataBuffer *const inst_db_; + + std::unique_ptr<generic::ProgramError> decode_error_; + std::unique_ptr<isa32gvzb::RiscV32GZBVecEncoding> riscv_encoding_; + std::unique_ptr<RV32GVZBIsaFactory> riscv_isa_factory_; + std::unique_ptr<isa32gvzb::RiscV32GVZBInstructionSet> riscv_isa_; +}; + +} // namespace riscv +} // namespace sim +} // namespace mpact + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV32GZB_VEC_DECODER_H_
diff --git a/riscv/riscv32gzb_vec_encoding.cc b/riscv/riscv32gzb_vec_encoding.cc new file mode 100644 index 0000000..ea62791 --- /dev/null +++ b/riscv/riscv32gzb_vec_encoding.cc
@@ -0,0 +1,181 @@ +// 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. + +#include "riscv/riscv32gzb_vec_encoding.h" + +#include <cstdint> + +#include "absl/log/log.h" +#include "absl/strings/str_cat.h" +#include "mpact/sim/generic/simple_resource.h" +#include "mpact/sim/generic/simple_resource_operand.h" +#include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv32gvzb_bin_decoder.h" +#include "riscv/riscv32gvzb_decoder.h" +#include "riscv/riscv32gvzb_enums.h" +#include "riscv/riscv_encoding_common.h" +#include "riscv/riscv_getters.h" +#include "riscv/riscv_getters_rv32.h" +#include "riscv/riscv_getters_vector.h" +#include "riscv/riscv_getters_zba.h" +#include "riscv/riscv_getters_zbb32.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_state.h" + +namespace mpact::sim::riscv::isa32gvzb { + +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. + +RiscV32GZBVecEncoding::RiscV32GZBVecEncoding(RiscVState *state) + : state_(state), + inst_word_(0), + opcode_(OpcodeEnum::kNone), + format_(FormatEnum::kNone) { + resource_pool_ = new generic::SimpleResourcePool("RiscV32GVZB", 128); + resource_delay_line_ = + state_->CreateAndAddDelayLine<generic::SimpleResourceDelayLine>(8); + // Initialize getters. + source_op_getters_.emplace(*SourceOpEnum::kNone, []() { return nullptr; }); + dest_op_getters_.emplace(*DestOpEnum::kNone, + [](int latency) { return nullptr; }); + simple_resource_getters_.emplace(*SimpleResourceEnum::kNone, + []() { return nullptr; }); + complex_resource_getters_.emplace( + *ComplexResourceEnum::kNone, + [](int latency, int end) { return nullptr; }); + // Add the operand getters for the base instruction set. + AddRiscVSourceGetters<SourceOpEnum, Extractors, RV32Register, RVFpRegister>( + source_op_getters_, this); + AddRiscVDestGetters<DestOpEnum, Extractors, RV32Register, RVFpRegister>( + dest_op_getters_, this); + AddRiscVSimpleResourceGetters<SimpleResourceEnum, Extractors>( + simple_resource_getters_, this); + // Add operand getters for 32 bit version not in the base instruction set. + AddRiscV32SourceGetters<SourceOpEnum, Extractors, RV32Register, RVFpRegister>( + source_op_getters_, this); + AddRiscV32DestGetters<DestOpEnum, Extractors, RV32Register, RVFpRegister>( + dest_op_getters_, this); + AddRiscV32SimpleResourceGetters<SimpleResourceEnum, Extractors>( + simple_resource_getters_, this); + // Add the common Zba operand getters. + AddRiscVZbaSourceGetters<SourceOpEnum, Extractors, RV32Register, + RVFpRegister>(source_op_getters_, this); + // Add operand getters for the 32 bit Zbb instructions. + AddRiscVZbb32SourceGetters<SourceOpEnum, Extractors, RV32Register, + RVFpRegister>(source_op_getters_, this); + // Add vector operand getters. + AddRiscVVectorSourceGetters<SourceOpEnum, Extractors, RVVectorRegister>( + source_op_getters_, this); + AddRiscVVectorDestGetters<DestOpEnum, Extractors, RVVectorRegister>( + dest_op_getters_, this); + // Verify that there are getters for each enum value. + for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; ++i) { + if (source_op_getters_.find(i) == source_op_getters_.end()) { + LOG(ERROR) << "No getter for source op enum value " << i; + } + } + for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; ++i) { + if (dest_op_getters_.find(i) == dest_op_getters_.end()) { + LOG(ERROR) << "No getter for destination op enum value " << i; + } + } + for (int i = *SimpleResourceEnum::kNone; + i < *SimpleResourceEnum::kPastMaxValue; ++i) { + if (simple_resource_getters_.find(i) == simple_resource_getters_.end()) { + LOG(ERROR) << "No getter for simple resource enum value " << i; + } + } +} + +RiscV32GZBVecEncoding::~RiscV32GZBVecEncoding() { delete resource_pool_; } + +void RiscV32GZBVecEncoding::ParseInstruction(uint32_t inst_word) { + inst_word_ = inst_word; + if ((inst_word_ & 0x3) == 3) { + auto [opcode, format] = DecodeRiscV32GVZBWithFormat(inst_word_); + opcode_ = opcode; + format_ = format; + return; + } + auto [opcode, format] = + DecodeRiscVCInst16WithFormat(static_cast<uint16_t>(inst_word & 0xffff)); + opcode_ = opcode; + format_ = format; +} + +ResourceOperandInterface *RiscV32GZBVecEncoding::GetComplexResourceOperand( + SlotEnum, int, OpcodeEnum, ComplexResourceEnum resource, int begin, + int end) { + int index = static_cast<int>(resource); + auto iter = complex_resource_getters_.find(index); + if (iter == complex_resource_getters_.end()) { + LOG(WARNING) << "No complex resource getter found for resource: " << index; + return nullptr; + } + return (iter->second)(begin, end); +} + +ResourceOperandInterface *RiscV32GZBVecEncoding::GetSimpleResourceOperand( + SlotEnum, int, OpcodeEnum, SimpleResourceVector &resource_vec, int end) { + if (resource_vec.empty()) return nullptr; + auto *resource_set = resource_pool_->CreateResourceSet(); + for (auto resource_enum : resource_vec) { + int index = static_cast<int>(resource_enum); + auto iter = simple_resource_getters_.find(index); + if (iter == simple_resource_getters_.end()) { + LOG(WARNING) << "No getter for simple resource " << index; + continue; + } + auto *resource = (iter->second)(); + auto status = resource_set->AddResource(resource); + if (!status.ok()) { + LOG(ERROR) << "Unable to add resource to resource set (" + << static_cast<int>(resource_enum) << ")"; + } + } + auto *op = new generic::SimpleResourceOperand(resource_set, end, + resource_delay_line_); + return op; +} + +DestinationOperandInterface *RiscV32GZBVecEncoding::GetDestination( + SlotEnum, int, OpcodeEnum opcode, DestOpEnum dest_op, int dest_no, + int latency) { + int index = static_cast<int>(dest_op); + auto iter = dest_op_getters_.find(index); + if (iter == dest_op_getters_.end()) { + LOG(ERROR) << absl::StrCat("No getter for destination op enum value ", + index, "for instruction ", + kOpcodeNames[static_cast<int>(opcode)]); + return nullptr; + } + return (iter->second)(latency); +} + +SourceOperandInterface *RiscV32GZBVecEncoding::GetSource(SlotEnum, int, + OpcodeEnum opcode, + SourceOpEnum source_op, + int source_no) { + int index = static_cast<int>(source_op); + auto iter = source_op_getters_.find(index); + if (iter == source_op_getters_.end()) { + LOG(ERROR) << absl::StrCat("No getter for source op enum value ", index, + " for instruction ", + kOpcodeNames[static_cast<int>(opcode)]); + return nullptr; + } + return (iter->second)(); +} + +} // namespace mpact::sim::riscv::isa32gvzb
diff --git a/riscv/riscv32gzb_vec_encoding.h b/riscv/riscv32gzb_vec_encoding.h new file mode 100644 index 0000000..7b2448d --- /dev/null +++ b/riscv/riscv32gzb_vec_encoding.h
@@ -0,0 +1,95 @@ +// 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 THIRD_PARTY_MPACT_RISCV_RISCV32GZB_VEC_ENCODING_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV32GZB_VEC_ENCODING_H_ + +#include <cstdint> +#include <string> + +#include "mpact/sim/generic/simple_resource.h" +#include "mpact/sim/generic/simple_resource_operand.h" +#include "riscv/riscv32gvzb_bin_decoder.h" +#include "riscv/riscv32gvzb_decoder.h" +#include "riscv/riscv32gvzb_enums.h" +#include "riscv/riscv_encoding_common.h" +#include "riscv/riscv_getter_helpers.h" +#include "riscv/riscv_state.h" + +namespace mpact::sim::riscv::isa32gvzb { + +// This class provides the interface between the generated instruction decoder +// framework (which is agnostic of the actual bit representation of +// instructions) and the instruction representation. This class provides methods +// to return the opcode, source operands, and destination operands for +// instructions according to the operand fields in the encoding. +class RiscV32GZBVecEncoding : public RiscV32GVZBEncodingBase, + public RiscVEncodingCommon { + public: + explicit RiscV32GZBVecEncoding(RiscVState *state); + ~RiscV32GZBVecEncoding() override; + + void ParseInstruction(uint32_t inst_word); + OpcodeEnum GetOpcode(SlotEnum, int) override { return opcode_; } + FormatEnum GetFormat(SlotEnum, int) { return format_; } + + PredicateOperandInterface *GetPredicate(SlotEnum, int, OpcodeEnum, + PredOpEnum) override { + return nullptr; + } + + ResourceOperandInterface *GetSimpleResourceOperand( + SlotEnum, int, OpcodeEnum, SimpleResourceVector &resource_vec, + int end) override; + + ResourceOperandInterface *GetComplexResourceOperand( + SlotEnum, int, OpcodeEnum, ComplexResourceEnum resource, int begin, + int end) override; + + SourceOperandInterface *GetSource(SlotEnum, int, OpcodeEnum, SourceOpEnum op, + int source_no) override; + + DestinationOperandInterface *GetDestination(SlotEnum, int, OpcodeEnum, + DestOpEnum op, int dest_no, + int latency) override; + + int GetLatency(SlotEnum, int, OpcodeEnum, DestOpEnum, int) override { + return 0; + } + + // Methods inherited from RiscVEncodingCommon. + RiscVState *state() const override { return state_; } + generic::SimpleResourcePool *resource_pool() override { + return resource_pool_; + } + uint32_t inst_word() const override { return inst_word_; } + + private: + std::string GetSimpleResourceName(SimpleResourceEnum resource_enum); + + RiscVState *state_; + uint32_t inst_word_; + OpcodeEnum opcode_; + FormatEnum format_; + SourceOpGetterMap source_op_getters_; + DestOpGetterMap dest_op_getters_; + SimpleResourceGetterMap simple_resource_getters_; + ComplexResourceGetterMap complex_resource_getters_; + generic::SimpleResourceDelayLine *resource_delay_line_ = nullptr; + generic::SimpleResourcePool *resource_pool_ = nullptr; +}; + +} // namespace mpact::sim::riscv::isa32gvzb + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV32GZB_VEC_ENCODING_H_
diff --git a/riscv/riscv32v.bin_fmt b/riscv/riscv32v.bin_fmt index 8684406..856c33c 100644 --- a/riscv/riscv32v.bin_fmt +++ b/riscv/riscv32v.bin_fmt
@@ -25,5 +25,18 @@ RiscVCInst16; }; +decoder RiscV32GVZB { + namespace mpact::sim::riscv::isa32gvzb; + opcode_enum = "isa32gvzb::OpcodeEnum"; + includes { + #include "riscv/riscv32gvzb_decoder.h" + } + // Group these instruction groups in the same decoder function. + RiscV32GVZB = {RiscVGInst32, RiscVVInst32, RiscVZbaInst32, RiscVZbbInst32, + RiscVZbcInst32, RiscVZbsInst32}; + // Keep this separate (different base format). + RiscVCInst16; +} +#include "riscv/riscv32zb.bin_fmt" #include "riscv/riscv32g.bin_fmt" #include "riscv/riscv_vector.bin_fmt"
diff --git a/riscv/riscv32v.isa b/riscv/riscv32v.isa index 27563e4..60ab378 100644 --- a/riscv/riscv32v.isa +++ b/riscv/riscv32v.isa
@@ -24,7 +24,12 @@ slots { riscv32gv; } } -#include "riscv/riscv32g.isa" +isa RiscV32GVZB { + namespace mpact::sim::riscv::isa32gvzb; + slots { riscv32gvzb; } +} + +#include "riscv/riscv32gzb.isa" #include "riscv/riscv_vector.isa" slot riscv32gv : riscv32g, riscv_vector { @@ -32,4 +37,11 @@ default opcode = disasm: "Illegal instruction at 0x%(@:08x)", semfunc: "&RiscVIllegalInstruction"; +} + +slot riscv32gvzb : riscv32gzb, riscv_vector { + default size = 4; + default opcode = + disasm: "Illegal instruction at 0x%(@:08x)", + semfunc: "&RiscVIllegalInstruction"; } \ No newline at end of file
diff --git a/riscv/riscv64gzb_vec_encoding.cc b/riscv/riscv64gzb_vec_encoding.cc index b0c4c8b..43e913a 100644 --- a/riscv/riscv64gzb_vec_encoding.cc +++ b/riscv/riscv64gzb_vec_encoding.cc
@@ -15,7 +15,6 @@ #include "riscv/riscv64gzb_vec_encoding.h" #include <cstdint> -#include <new> #include "absl/log/log.h" #include "absl/strings/str_cat.h"
diff --git a/riscv/riscv_bitmanip_instructions.cc b/riscv/riscv_bitmanip_instructions.cc index 0084402..dda17c2 100644 --- a/riscv/riscv_bitmanip_instructions.cc +++ b/riscv/riscv_bitmanip_instructions.cc
@@ -491,7 +491,7 @@ RiscVBinaryOp<RegisterType, UIntReg, UIntReg>( instruction, [](UIntReg a, UIntReg b) { UIntReg result = 0; - for (int i = 0; i < kXlen - 1; i++) { + for (int i = 0; i < kXlen; i++) { result = ((b >> i) & 1) ? result ^ (a >> (kXlen - i - 1)) : result; } return result;
diff --git a/riscv/riscv_csr.h b/riscv/riscv_csr.h index 54d5207..6765b2c 100644 --- a/riscv/riscv_csr.h +++ b/riscv/riscv_csr.h
@@ -83,7 +83,8 @@ kSIDeleg = 0x103, kSIe = 0x104, kSTvec = 0x105, - kSCounteren = 0x106, + kSCounteren = 0x106, // Supervisor counter enable. + kSenvcfg = 0x10a, // Supervisor environment configuration register. // Supervisor trap handling. kSScratch = 0x140, // Scratch register for supervisor trap handlers. kSEpc = 0x141, // Supervisor exception program counter. @@ -113,6 +114,7 @@ kMCause = 0x342, // Machine trap cause. kMTval = 0x343, // Machine bad address or instruction. kMIp = 0x344, // Machine interrupt pending. + kMenvcfg = 0x30A, // Machine environment configuration register. // PMP registers. kPmpCfg0 = 0x3a0, // PMP configuration register 0. @@ -136,6 +138,9 @@ kPmpAddr14 = 0x3be, // PMP address register 14. kPmpAddr15 = 0x3bf, // PMP address register 15. + // Hypervisor level CSRs. + kHenvcfg = 0x60A, // Hypervisor environment configuration register. + // Ignoring machine memory protection for now. kMCycle = 0xb00, // Machine cycle counter.
diff --git a/riscv/riscv_f_instructions.cc b/riscv/riscv_f_instructions.cc index 1864bac..34b572a 100644 --- a/riscv/riscv_f_instructions.cc +++ b/riscv/riscv_f_instructions.cc
@@ -221,7 +221,7 @@ // If either operand is NaN return the other. void RiscVFMin(const Instruction *instruction) { - RiscVBinaryOp<FPRegister, float, float>( + RiscVBinaryNaNBoxOp<FPRegister::ValueType, float, float>( instruction, [instruction](float a, float b) -> float { if (FPTypeInfo<float>::IsSNaN(a) || FPTypeInfo<float>::IsSNaN(b)) { auto *db = instruction->Destination(1)->AllocateDataBuffer(); @@ -236,7 +236,9 @@ } return b; } - if (FPTypeInfo<float>::IsNaN(b)) return a; + if (FPTypeInfo<float>::IsNaN(b)) { + return a; + } // If both are zero, return the negative zero if there is one. if ((a == 0.0) && (b == 0.0)) return (std::signbit(a)) ? a : b; return (a > b) ? b : a; @@ -245,7 +247,7 @@ // If either operand is NaN return the other. void RiscVFMax(const Instruction *instruction) { - RiscVBinaryOp<FPRegister, float, float>( + RiscVBinaryNaNBoxOp<FPRegister::ValueType, float, float>( instruction, [instruction](float a, float b) -> float { if (FPTypeInfo<float>::IsSNaN(a) || FPTypeInfo<float>::IsSNaN(b)) { auto *db = instruction->Destination(1)->AllocateDataBuffer();
diff --git a/riscv/riscv_getters.h b/riscv/riscv_getters.h index 5230372..4aee8bb 100644 --- a/riscv/riscv_getters.h +++ b/riscv/riscv_getters.h
@@ -76,7 +76,7 @@ auto num = Extractors::CS::ExtractCsRs2(common->inst_word()); return GetRegisterSourceOp<FpRegister>( common->state(), absl::StrCat(RiscVState::kFregPrefix, num), - kXRegisterAliases[num]); + kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kC3rs1, [common]() { auto num = Extractors::CS::ExtractCsRs1(common->inst_word()); @@ -98,7 +98,7 @@ auto num = Extractors::CR::ExtractRs2(common->inst_word()); return GetRegisterSourceOp<FpRegister>( common->state(), absl::StrCat(RiscVState::kFregPrefix, num), - kXRegisterAliases[num]); + kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kCrs1, [common]() { auto num = Extractors::CR::ExtractRs1(common->inst_word()); @@ -125,37 +125,37 @@ int num = Extractors::RType::ExtractRs1(common->inst_word()); return GetRegisterSourceOp<IntRegister>( common->state(), absl::StrCat(RiscVState::kFregPrefix, num), - kXRegisterAliases[num]); + kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kDrs2, [common]() -> SourceOperandInterface * { int num = Extractors::RType::ExtractRs2(common->inst_word()); return GetRegisterSourceOp<IntRegister>( common->state(), absl::StrCat(RiscVState::kFregPrefix, num), - kXRegisterAliases[num]); + kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kDrs3, [common]() -> SourceOperandInterface * { int num = Extractors::R4Type::ExtractRs3(common->inst_word()); return GetRegisterSourceOp<IntRegister>( common->state(), absl::StrCat(RiscVState::kFregPrefix, num), - kXRegisterAliases[num]); + kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kFrs1, [common]() -> SourceOperandInterface * { int num = Extractors::RType::ExtractRs1(common->inst_word()); return GetRegisterSourceOp<IntRegister>( common->state(), absl::StrCat(RiscVState::kFregPrefix, num), - kXRegisterAliases[num]); + kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kFrs2, [common]() -> SourceOperandInterface * { int num = Extractors::RType::ExtractRs2(common->inst_word()); return GetRegisterSourceOp<IntRegister>( common->state(), absl::StrCat(RiscVState::kFregPrefix, num), - kXRegisterAliases[num]); + kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kFrs3, [common]() -> SourceOperandInterface * { int num = Extractors::R4Type::ExtractRs3(common->inst_word()); return GetRegisterSourceOp<IntRegister>( common->state(), absl::StrCat(RiscVState::kFregPrefix, num), - kXRegisterAliases[num]); + kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kICbImm8, [common]() { return new ImmediateOperand<int32_t>( @@ -307,7 +307,8 @@ Insert(getter_map, *Enum::kC3drd, [common](int latency) { int num = Extractors::CL::ExtractClRd(common->inst_word()); return GetRegisterDestinationOp<IntRegister>( - common->state(), absl::StrCat(RiscVState::kFregPrefix, num), latency); + common->state(), absl::StrCat(RiscVState::kFregPrefix, num), latency, + kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kC3rd, [common](int latency) { int num = Extractors::CL::ExtractClRd(common->inst_word()); @@ -330,14 +331,14 @@ int num = Extractors::RType::ExtractRd(common->inst_word()); return GetRegisterDestinationOp<IntRegister>( common->state(), absl::StrCat(RiscVState::kFregPrefix, num), - latency); + latency, kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kFrd, [common](int latency) -> DestinationOperandInterface * { int num = Extractors::RType::ExtractRd(common->inst_word()); return GetRegisterDestinationOp<IntRegister>( common->state(), absl::StrCat(RiscVState::kFregPrefix, num), - latency); + latency, kFRegisterAliases[num]); }); Insert(getter_map, *Enum::kNextPc, [common](int latency) -> DestinationOperandInterface * {
diff --git a/riscv/riscv_i_instructions.cc b/riscv/riscv_i_instructions.cc index a72fe68..eb17c54 100644 --- a/riscv/riscv_i_instructions.cc +++ b/riscv/riscv_i_instructions.cc
@@ -128,6 +128,8 @@ UIntReg return_address = instruction->address() + instruction->size(); auto *db = instruction->Destination(0)->AllocateDataBuffer(); db->SetSubmit<UIntReg>(0, target); + auto *state = static_cast<RiscVState *>(instruction->state()); + state->set_branch(true); auto *reg = static_cast<generic::RegisterDestinationOperand<UIntReg> *>( instruction->Destination(1)) ->GetRegister(); @@ -141,6 +143,8 @@ UIntReg return_address = instruction->address() + instruction->size(); auto *db = instruction->Destination(0)->AllocateDataBuffer(); db->SetSubmit<UIntReg>(0, target); + auto *state = static_cast<RiscVState *>(instruction->state()); + state->set_branch(true); auto *reg = static_cast<generic::RegisterDestinationOperand<UIntReg> *>( instruction->Destination(1)) ->GetRegister(); @@ -277,6 +281,8 @@ UIntReg return_address = instruction->address() + instruction->size(); auto *db = instruction->Destination(0)->AllocateDataBuffer(); db->SetSubmit<UIntReg>(0, target); + auto *state = static_cast<RiscVState *>(instruction->state()); + state->set_branch(true); auto *reg = static_cast<generic::RegisterDestinationOperand<UIntReg> *>( instruction->Destination(1)) ->GetRegister(); @@ -291,6 +297,8 @@ UIntReg return_address = instruction->address() + instruction->size(); auto *db = instruction->Destination(0)->AllocateDataBuffer(); db->SetSubmit<UIntReg>(0, target); + auto *state = static_cast<RiscVState *>(instruction->state()); + state->set_branch(true); auto *reg = static_cast<generic::RegisterDestinationOperand<UIntReg> *>( instruction->Destination(1)) ->GetRegister();
diff --git a/riscv/riscv_instruction_helpers.h b/riscv/riscv_instruction_helpers.h index db40dc9..09a4e03 100644 --- a/riscv/riscv_instruction_helpers.h +++ b/riscv/riscv_instruction_helpers.h
@@ -281,10 +281,8 @@ if (sizeof(RegValue) <= sizeof(Argument)) { return generic::GetInstructionSource<Argument>(instruction, arg); } else { - using SInt = typename std::make_signed<RegValue>::type; using UInt = typename std::make_unsigned<RegValue>::type; - SInt val = generic::GetInstructionSource<SInt>(instruction, arg); - UInt uval = static_cast<UInt>(val); + UInt uval = generic::GetInstructionSource<UInt>(instruction, arg); UInt mask = std::numeric_limits<UInt>::max() << (sizeof(Argument) * 8); if (((mask & uval) != mask)) { return *reinterpret_cast<const Argument *>( @@ -346,6 +344,8 @@ UIntType target = offset + instruction->address(); auto *db = instruction->Destination(0)->AllocateDataBuffer(); db->SetSubmit<UIntType>(0, target); + auto state = static_cast<RiscVState *>(instruction->state()); + state->set_branch(true); } } @@ -583,8 +583,6 @@ std::function<Result(Argument, Argument)> operation) { Argument lhs = GetNaNBoxedSource<Register, Argument>(instruction, 0); Argument rhs = GetNaNBoxedSource<Register, Argument>(instruction, 1); - // Argument lhs = generic::GetInstructionSource<Argument>(instruction, 0); - // Argument rhs = generic::GetInstructionSource<Argument>(instruction, 1); // Get the rounding mode. int rm_value = generic::GetInstructionSource<int>(instruction, 2);
diff --git a/riscv/riscv_priv_instructions.cc b/riscv/riscv_priv_instructions.cc index 3bd7658..fd23133 100644 --- a/riscv/riscv_priv_instructions.cc +++ b/riscv/riscv_priv_instructions.cc
@@ -65,6 +65,7 @@ auto *db = inst->Destination(0)->AllocateDataBuffer(); // Write the contents of mepc to the pc. db->SetSubmit<UIntReg>(0, sepc->AsUint32()); + state->set_branch(true); // Need to update new privilege level. res = state->csr_set()->GetCsr(*RiscVCsrEnum::kMStatus); if (!res.ok()) { @@ -114,6 +115,7 @@ auto *db = inst->Destination(0)->AllocateDataBuffer(); // Write the contents of mepc to the pc. db->SetSubmit<UIntReg>(0, mepc->AsUint32()); + state->set_branch(true); // Need to update new privilege level. res = state->csr_set()->GetCsr(*RiscVCsrEnum::kMStatus); if (!res.ok()) { @@ -164,8 +166,10 @@ void RiscVPrivSRet(const Instruction *inst) { RiscVState *state = static_cast<RiscVState *>(inst->state()); - if (state->privilege_mode() != PrivilegeMode::kSupervisor) { - LOG(ERROR) << "sret executed when not in Supervisor mode"; + if (*state->privilege_mode() < *PrivilegeMode::kSupervisor) { + LOG(ERROR) << absl::StrCat( + "sret executed when not in Supervisor mode at pc = 0x", + absl::Hex(inst->address())); state->Trap(/*is_interrupt*/ false, /*trap_value*/ 0, *ExceptionCode::kIllegalInstruction, inst->address(), inst); return; @@ -186,6 +190,7 @@ auto *db = inst->Destination(0)->AllocateDataBuffer(); // Write the contents of mepc to the pc. db->SetSubmit<UIntReg>(0, sepc->AsUint64()); + state->set_branch(true); // Need to update new privilege level. res = state->csr_set()->GetCsr(*RiscVCsrEnum::kMStatus); if (!res.ok()) { @@ -211,7 +216,19 @@ mstatus->set_sie(mstatus->spie()); // Set mstatus:mpie to 1. mstatus->set_spie(1); - mstatus->set_spp(*PrivilegeMode::kUser & 0b1); + // Get misa too. + res = state->csr_set()->GetCsr(*RiscVCsrEnum::kMIsa); + if (!res.ok()) { + LOG(ERROR) << absl::StrCat("At PC=", absl::Hex(inst->address()), + " mret: cannot access isa"); + return; + } + auto *misa = static_cast<RiscVMIsa *>(*res); + if (misa->HasUserMode()) { + mstatus->set_spp(*PrivilegeMode::kUser); + } else { + mstatus->set_spp(*PrivilegeMode::kMachine); + } state->set_privilege_mode(static_cast<PrivilegeMode>(target_mode)); state->SignalReturnFromInterrupt(); mstatus->Submit(); @@ -235,6 +252,7 @@ auto *db = inst->Destination(0)->AllocateDataBuffer(); // Write the contents of mepc to the pc. db->SetSubmit<UIntReg>(0, mepc->AsUint64()); + state->set_branch(true); // Need to update new privilege level. res = state->csr_set()->GetCsr(*RiscVCsrEnum::kMStatus); if (!res.ok()) {
diff --git a/riscv/riscv_state.cc b/riscv/riscv_state.cc index 7eebd76..8d71bad 100644 --- a/riscv/riscv_state.cc +++ b/riscv/riscv_state.cc
@@ -162,6 +162,12 @@ void CreateCsrs(RiscVState *state, std::vector<RiscVCsrInterface *> &csr_vec) { absl::Status result; // Create CSRs. + + // menvcfg + CHECK_NE(CreateCsr<RiscVSimpleCsr<T>>(state, csr_vec, "menvcfg", + RiscVCsrEnum::kMenvcfg, 0, state), + nullptr); + // misa auto *misa = CreateCsr(state, state->misa_, csr_vec, CsrInfo<T>::kMisaInitialValue, state); @@ -247,8 +253,25 @@ nullptr); } + // Hypervisor level CSRs + + // henvcfg + CHECK_NE(CreateCsr<RiscVSimpleCsr<T>>(state, csr_vec, "henvcfg", + RiscVCsrEnum::kHenvcfg, 0, state), + nullptr); + // Supervisor level CSRs + // senvcfg + CHECK_NE(CreateCsr<RiscVSimpleCsr<T>>(state, csr_vec, "senvcfg", + RiscVCsrEnum::kSenvcfg, 0, state), + nullptr); + + // scounteren + CHECK_NE(CreateCsr<RiscVSimpleCsr<T>>(state, csr_vec, "scounteren", + RiscVCsrEnum::kSCounteren, 0, state), + nullptr); + // sstatus CHECK_NE(CreateCsr<RiscVSStatus>(state, csr_vec, mstatus, state), nullptr); @@ -530,13 +553,15 @@ if (res) return; } - // If no handler is specidied, then CEASE is treated as an infinite loop. + // If no handler is specified, then CEASE is treated as an infinite loop. auto current_xlen = xlen(); auto *db = pc_dst_operand_->AllocateDataBuffer(); if (current_xlen == RiscVXlen::RV32) { db->SetSubmit<uint32_t>(0, static_cast<uint32_t>(inst->address())); + set_branch(true); } else if (current_xlen == RiscVXlen::RV64) { db->SetSubmit<uint64_t>(0, inst->address()); + set_branch(true); } else { LOG(ERROR) << "Unknown xlen"; } @@ -581,7 +606,6 @@ } } } else { - // Exceptions are not delegated to a lower privilege level. if ((privilege_mode() != PrivilegeMode::kMachine) && (medeleg_->AsUint64() & (1ULL << exception_code))) { destination_mode = PrivilegeMode::kSupervisor; @@ -656,8 +680,10 @@ auto *db = pc_dst_operand_->AllocateDataBuffer(); if (current_xlen == RiscVXlen::RV32) { db->SetSubmit<uint32_t>(0, static_cast<uint32_t>(trap_target)); + set_branch(true); } else if (current_xlen == RiscVXlen::RV64) { db->SetSubmit<uint64_t>(0, trap_target); + set_branch(true); } else { LOG(ERROR) << "Unknown xlen"; }
diff --git a/riscv/riscv_state.h b/riscv/riscv_state.h index 97aa718..3c4bbd2 100644 --- a/riscv/riscv_state.h +++ b/riscv/riscv_state.h
@@ -372,6 +372,9 @@ is_interrupt_available_ = false; } + void set_branch(bool value) { branch_ = value; } + bool branch() const { return branch_; } + // Getters for select CSRs. RiscVMStatus *mstatus() const { return mstatus_; } RiscVMIsa *misa() const { return misa_; } @@ -421,6 +424,8 @@ InterruptCode available_interrupt_code_ = InterruptCode::kNone; // By default, execute in machine mode. PrivilegeMode privilege_mode_ = PrivilegeMode::kMachine; + // Flag set on branch instructions. + bool branch_ = false; // Handles to frequently used CSRs. RiscVMStatus *mstatus_ = nullptr; RiscVMIsa *misa_ = nullptr;
diff --git a/riscv/riscv_top.cc b/riscv/riscv_top.cc index d0e0b41..17885c7 100644 --- a/riscv/riscv_top.cc +++ b/riscv/riscv_top.cc
@@ -25,6 +25,7 @@ #include "absl/functional/bind_front.h" #include "absl/log/check.h" #include "absl/log/log.h" +#include "absl/numeric/bits.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" @@ -107,6 +108,8 @@ run_halted_ = nullptr; } + if (branch_trace_db_ != nullptr) branch_trace_db_->DecRef(); + delete icache_; delete dcache_; if (inst_db_) inst_db_->DecRef(); @@ -199,6 +202,13 @@ return false; }); inst_db_ = db_factory_.Allocate<uint32_t>(1); + // Branch trace. + branch_trace_db_ = db_factory_.Allocate<BranchTraceEntry>(kBranchTraceSize); + branch_trace_ = + reinterpret_cast<BranchTraceEntry *>(branch_trace_db_->raw_ptr()); + for (int i = 0; i < kBranchTraceSize; i++) { + branch_trace_[i] = {0, 0, 0}; + } } void RiscVTop::ConfigureCache(Cache *&cache, Config<std::string> &config) { @@ -250,8 +260,7 @@ // Execute the real instruction. auto real_inst = rv_decode_cache_->GetDecodedInstruction(pc); real_inst->IncRef(); - uint64_t next_seq_pc = pc + real_inst->size(); - SetPc(next_seq_pc); + uint64_t next_pc = pc + real_inst->size(); bool executed = false; if (icache_) ICacheFetch(pc); do { @@ -266,6 +275,13 @@ // Re-enable the breakpoint. (void)rv_action_point_manager_->ap_memory_interface() ->WriteBreakpointInstruction(bpt_pc); + if (state_->branch()) { + state_->set_branch(false); + auto new_pc = state_->pc_operand()->AsUint64(0); + AddToBranchTrace(pc, bpt_pc); + next_pc = new_pc; + } + SetPc(next_pc); return absl::OkStatus(); } @@ -303,13 +319,12 @@ // executed next. Post-loop it holds the address of the next instruction to // be executed. uint64_t next_pc = pc_operand->AsUint64(0); - while (count < num) { - pc = next_pc; + pc = next_pc; + while (!halted_ && (count < num)) { SetPc(pc); auto *inst = rv_decode_cache_->GetDecodedInstruction(pc); - // Set the PC destination operand to the next sequential address. Any branch - // that is executed will overwrite this. - SetPc(pc + inst->size()); + // Set the next_pc to the next sequential instruction. + next_pc = pc + inst->size(); bool executed = false; if (icache_) ICacheFetch(pc); do { @@ -318,7 +333,7 @@ state_->AdvanceDelayLines(); // Check for interrupt. if (state_->is_interrupt_available()) { - uint64_t epc = (executed ? state_->pc_operand()->AsUint64(0) : pc); + uint64_t epc = (executed ? state_->pc_operand()->AsUint64(0) : next_pc); state_->TakeAvailableInterrupt(epc); } } while (!executed); @@ -327,8 +342,16 @@ counter_opcode_[inst->opcode()].Increment(1); counter_num_instructions_.Increment(1); // Get the next pc value. - next_pc = state_->pc_operand()->AsUint64(0); - if (!halted_) continue; + auto pc_val = state_->pc_operand()->AsUint64(0); + if (state_->branch()) { + state_->set_branch(false); + AddToBranchTrace(pc, pc_val); + next_pc = pc_val; + } + if (!halted_) { + pc = next_pc; + continue; + } // If it's an action point, just step over and continue. if (halt_reason_ == *HaltReason::kActionPoint) { auto status = StepPastBreakpoint(); @@ -373,30 +396,29 @@ auto status = StepPastBreakpoint(); if (!status.ok()) return status; } - run_status_ = RunStatus::kRunning; - halted_ = false; - halt_reason_ = *HaltReason::kNone; - // The simulator is now run in a separate thread so as to allow a user // interface to continue operating. Allocate a new run_halted_ Notification // object, as they are single use only. run_halted_ = new absl::Notification(); + run_started_ = new absl::Notification(); // The thread is detached so it executes without having to be joined. std::thread([this]() { + run_status_ = RunStatus::kRunning; + halted_ = false; + halt_reason_ = *HaltReason::kNone; + run_started_->Notify(); auto pc_operand = state_->pc_operand(); - // This holds the value of the current pc, and post-loop, the address of - // the most recently executed instruction. - uint64_t pc; // At the top of the loop this holds the address of the instruction to be // executed next. Post-loop it holds the address of the next instruction to // be executed. uint64_t next_pc = pc_operand->AsUint64(0); - while (true) { - pc = next_pc; + // This holds the value of the current pc, and post-loop, the address of + // the most recently executed instruction. + uint64_t pc = next_pc; + while (!halted_) { auto *inst = rv_decode_cache_->GetDecodedInstruction(pc); - // Set the PC destination operand to next_seq_pc. Any branch that is - // executed will overwrite this. - SetPc(pc + inst->size()); + SetPc(pc); + next_pc = pc + inst->size(); bool executed = false; if (icache_) ICacheFetch(pc); do { @@ -415,8 +437,16 @@ counter_opcode_[inst->opcode()].Increment(1); counter_num_instructions_.Increment(1); // Get the next pc value. - next_pc = pc_operand->AsUint64(0); - if (!halted_) continue; + uint64_t pc_val = pc_operand->AsUint64(0); + if (state_->branch()) { + state_->set_branch(false); + AddToBranchTrace(pc, pc_val); + next_pc = pc_val; + } + if (!halted_) { + pc = next_pc; + continue; + } // If it's an action point, just step over and continue executing, as // this is not a full breakpoint. if (halt_reason_ == *HaltReason::kActionPoint) { @@ -445,6 +475,9 @@ // Notify that the run has completed. run_halted_->Notify(); }).detach(); + run_started_->WaitForNotification(); + delete run_started_; + run_started_ = nullptr; return absl::OkStatus(); } @@ -480,6 +513,9 @@ if (iter == state_->registers()->end()) { auto result = state_->csr_set()->GetCsr(name); if (!result.ok()) { + // See if it is $branch_trace_head. + if (name == "$branch_trace_head") return branch_trace_head_; + if (name == "$branch_trace_size") return branch_trace_size_; return absl::NotFoundError( absl::StrCat("Register '", name, "' not found")); } @@ -527,6 +563,9 @@ if (iter == state_->registers()->end()) { auto result = state_->csr_set()->GetCsr(name); if (!result.ok()) { + if (name == "$branch_trace_size") { + return ResizeBranchTrace(value); + } return absl::NotFoundError( absl::StrCat("Register '", name, "' not found")); } @@ -578,6 +617,7 @@ return absl::FailedPreconditionError( "GetRegisterDataBuffer: Core must be halted"); } + if (name == "$branch_trace") return branch_trace_db_; auto iter = state_->registers()->find(name); if (iter == state_->registers()->end()) { return absl::NotFoundError(absl::StrCat("Register '", name, "' not found")); @@ -803,6 +843,57 @@ } } +absl::Status RiscVTop::ResizeBranchTrace(size_t size) { + if (absl::popcount(size) != 1) { + return absl::InvalidArgumentError("Invalid size - must be a power of 2"); + } + auto *new_db = db_factory_.Allocate<BranchTraceEntry>(size); + auto *new_trace = reinterpret_cast<BranchTraceEntry *>(new_db->raw_ptr()); + if (new_db == nullptr) { + return absl::InternalError("Failed to allocate new branch trace buffer"); + } + // Copy entries from the old buffer to the new buffer, but do it so that + // the most recent entry of the old buffer is at the end of the newly + // allocated buffer. That way, if the new buffer is smaller, we don't have to + // do too much special handling. + int new_index = size - 1; + int old_index = branch_trace_head_; + while ((new_index >= 0) && (branch_trace_[old_index].count > 0)) { + new_trace[new_index] = branch_trace_[old_index]; + new_index--; + old_index--; + if (old_index < 0) { + old_index = branch_trace_size_ - 1; + } + // Stop if we get to the beginning of the old trace. + if (old_index == branch_trace_head_) break; + } + while (new_index >= 0) { + new_trace[new_index] = {0, 0, 0}; + new_index--; + } + branch_trace_db_->DecRef(); + branch_trace_db_ = new_db; + branch_trace_ = new_trace; + branch_trace_size_ = size; + branch_trace_mask_ = branch_trace_size_ - 1; + branch_trace_head_ = branch_trace_mask_; + return absl::OkStatus(); +} + +void RiscVTop::AddToBranchTrace(uint64_t from, uint64_t to) { + // Get the most recent entry. + auto &entry = branch_trace_[branch_trace_head_]; + // If the branch is the same as the previous, just increment its count. + if ((from == entry.from) && (to == entry.to)) { + entry.count++; + return; + } + branch_trace_head_ = (branch_trace_head_ + 1) & branch_trace_mask_; + branch_trace_[branch_trace_head_] = {static_cast<uint32_t>(from), + static_cast<uint32_t>(to), 1}; +} + void RiscVTop::EnableStatistics() { for (auto &[unused, counter_ptr] : counter_map()) { if (counter_ptr->GetName() == "pc") continue;
diff --git a/riscv/riscv_top.h b/riscv/riscv_top.h index 3d97f7b..7786ff2 100644 --- a/riscv/riscv_top.h +++ b/riscv/riscv_top.h
@@ -54,11 +54,18 @@ using ::mpact::sim::generic::Config; using ::mpact::sim::util::Cache; -// Top level class for the RiscV32G simulator. This is the main interface for +struct BranchTraceEntry { + uint32_t from; + uint32_t to; + uint32_t count; +}; + +// Top level class for the RiscV simulator. This is the main interface for // interacting and controlling execution of programs running on the simulator. // This class brings together the decoder, the architecture state, and control. class RiscVTop : public generic::Component, public RiscVDebugInterface { public: + static constexpr int kBranchTraceSize = 16; using RunStatus = generic::CoreDebugInterface::RunStatus; using HaltReason = generic::CoreDebugInterface::HaltReason; @@ -157,6 +164,8 @@ // Set the pc value. void SetPc(uint64_t value); void ICacheFetch(uint64_t address); + // Branch tracing. + void AddToBranchTrace(uint64_t from, uint64_t to); // The DB factory is used to manage data buffers for memory read/writes. generic::DataBufferFactory db_factory_; @@ -168,6 +177,7 @@ // Set to true if the next instruction requires a step-over. bool need_to_step_over_ = false; absl::Notification *run_halted_ = nullptr; + absl::Notification *run_started_ = nullptr; // The local RiscV32 state. RiscVState *state_; // Memory interface used by action point manager. @@ -185,6 +195,17 @@ // Decode cache, memory and memory watcher. generic::DecodeCache *rv_decode_cache_ = 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_; + // Data buffer used to hold the branch trace info. This is used so that it + // can be returned to the debug command shell using the GetRegisterDataBuffer + // call. + DataBuffer *branch_trace_db_ = nullptr; + // Points to the most recently written entry in the circular buffer. + int branch_trace_head_ = 0; + int branch_trace_mask_ = kBranchTraceSize - 1; + int branch_trace_size_ = kBranchTraceSize; // Counter for the number of instructions simulated. std::vector<generic::SimpleCounter<uint64_t>> counter_opcode_; generic::SimpleCounter<uint64_t> counter_num_instructions_;
diff --git a/riscv/riscv_zc_instructions.cc b/riscv/riscv_zc_instructions.cc index f7643cb..02ce209 100644 --- a/riscv/riscv_zc_instructions.cc +++ b/riscv/riscv_zc_instructions.cc
@@ -107,6 +107,8 @@ UIntReg target = generic::GetInstructionSource<UIntReg>(inst, 3); auto *db = inst->Destination(size + 1)->AllocateDataBuffer(); db->SetSubmit<UIntReg>(0, target); + auto *state = static_cast<RiscVState *>(inst->state()); + state->set_branch(true); } void RiscVZCmpPopRetz(const Instruction *inst) { @@ -119,6 +121,8 @@ UIntReg target = generic::GetInstructionSource<UIntReg>(inst, 3); auto *db = inst->Destination(size + 2)->AllocateDataBuffer(); db->SetSubmit<UIntReg>(0, target); + auto *state = static_cast<RiscVState *>(inst->state()); + state->set_branch(true); } void RiscVZCmpMvTwoRegs(const Instruction *inst) { @@ -150,6 +154,7 @@ // Write the target address to the next pc operand. auto *target_db = inst->Destination(0)->AllocateDataBuffer(); target_db->SetSubmit<UIntReg>(0, target_address); + state->set_branch(true); } } // namespace @@ -243,6 +248,8 @@ UIntReg target = generic::GetInstructionSource<UIntReg>(inst, 3); auto *db = inst->Destination(size + 1)->AllocateDataBuffer(); db->SetSubmit<UIntReg>(0, target); + auto *state = static_cast<RiscVState *>(inst->state()); + state->set_branch(true); } void RiscVZCmpPopRetz(const Instruction *inst) { @@ -255,6 +262,8 @@ UIntReg target = generic::GetInstructionSource<UIntReg>(inst, 3); auto *db = inst->Destination(size + 2)->AllocateDataBuffer(); db->SetSubmit<UIntReg>(0, target); + auto *state = static_cast<RiscVState *>(inst->state()); + state->set_branch(true); } void RiscVZCmpMvTwoRegs(const Instruction *inst) { @@ -286,6 +295,7 @@ // Write the target address to the next pc operand. auto *target_db = inst->Destination(0)->AllocateDataBuffer(); target_db->SetSubmit<UIntReg>(0, target_address); + state->set_branch(true); } } // namespace
diff --git a/riscv/rv32g_sim.cc b/riscv/rv32g_sim.cc index 481fe3f..c46e944 100644 --- a/riscv/rv32g_sim.cc +++ b/riscv/rv32g_sim.cc
@@ -26,9 +26,11 @@ #include <string> #include <vector> +#include "absl/base/log_severity.h" #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/log/check.h" +#include "absl/log/globals.h" #include "absl/log/log.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" @@ -42,6 +44,7 @@ #include "mpact/sim/proto/component_data.pb.h" #include "mpact/sim/util/memory/atomic_memory.h" #include "mpact/sim/util/memory/flat_demand_memory.h" +#include "mpact/sim/util/memory/memory_interface.h" #include "mpact/sim/util/memory/memory_watcher.h" #include "mpact/sim/util/program_loader/elf_program_loader.h" #include "re2/re2.h" @@ -143,6 +146,12 @@ // Enable bit manipulation instructions. ABSL_FLAG(bool, bitmanip, false, "Enable bit manipulation instructions"); +// Exit on write to 'tohost' +ABSL_FLAG(bool, exit_on_tohost, false, "Exit on write to 'tohost'"); + +// Quiet mode. Suppress informational and warning messages. +ABSL_FLAG(bool, quiet, false, "Suppress informational and warning messages"); + // Flag to enable and configure the instruction and data caches. ABSL_FLAG(std::string, icache, "", "Instruction cache configuration"); ABSL_FLAG(std::string, dcache, "", "Data cache configuration"); @@ -214,6 +223,7 @@ } int main(int argc, char **argv) { + int return_code = 0; auto arg_vec = absl::ParseCommandLine(argc, argv); if (absl::GetFlag(FLAGS_semihost_htif) && absl::GetFlag(FLAGS_semihost_arm)) { @@ -225,13 +235,26 @@ std::cerr << "Only a single input file allowed" << std::endl; return -1; } + + bool quiet = absl::GetFlag(FLAGS_quiet); + if (quiet) { + absl::SetMinLogLevel(absl::LogSeverityAtLeast::kError); + } + std::string full_file_name = arg_vec[1]; std::string file_name = full_file_name.substr(full_file_name.find_last_of('/') + 1); std::string file_basename = file_name.substr(0, file_name.find_first_of('.')); auto *memory = new mpact::sim::util::FlatDemandMemory(); - auto *atomic_memory = new mpact::sim::util::AtomicMemory(memory); + mpact::sim::util::MemoryWatcher *memory_watcher = nullptr; + mpact::sim::util::AtomicMemory *atomic_memory = nullptr; + if (absl::GetFlag(FLAGS_exit_on_tohost)) { + memory_watcher = new mpact::sim::util::MemoryWatcher(memory); + atomic_memory = new mpact::sim::util::AtomicMemory(memory_watcher); + } else { + atomic_memory = new mpact::sim::util::AtomicMemory(memory); + } // Load the elf segments into memory. mpact::sim::util::ElfProgramLoader elf_loader(memory); auto load_result = elf_loader.LoadProgram(full_file_name); @@ -241,8 +264,13 @@ return -1; } + mpact::sim::util::MemoryInterface *memory_interface = memory; + if (memory_watcher != nullptr) { + memory_interface = memory_watcher; + } // Set up architectural state and decoder. - RiscVState rv_state("RiscV32", RiscVXlen::RV32, memory, atomic_memory); + RiscVState rv_state("RiscV32", RiscVXlen::RV32, memory_interface, + atomic_memory); // For floating point support add the fp state. RiscVFPState rv_fp_state(rv_state.csr_set(), &rv_state); rv_state.set_rv_fp(&rv_fp_state); @@ -299,6 +327,35 @@ return true; }); } + if (absl::GetFlag(FLAGS_exit_on_tohost)) { + auto res = elf_loader.GetSymbol("tohost"); + if (res.ok()) { + auto tohost_addr = res.value().first; + auto status = memory_watcher->SetStoreWatchCallback( + AddressRange(tohost_addr), + [&riscv_top, tohost_addr, memory, &rv_state, &return_code, quiet]( + uint64_t, int) -> void { + riscv_top.RequestHalt(RiscVTop::HaltReason::kProgramDone, nullptr); + auto *db = rv_state.db_factory()->Allocate<uint32_t>(1); + memory->Load(tohost_addr, db, nullptr, nullptr); + auto word = db->Get<uint32_t>(0); + db->DecRef(); + return_code = word >> 1; + if (return_code == 0) { + if (!quiet) std::cerr << "PASS\n"; + } else { + } + }); + if (!status.ok()) { + std::cerr << "Error setting store watch callback for 'tohost': " + << status.message(); + return -1; + } + } else { + std::cerr << "Error: no symbol 'tohost' found"; + return -1; + } + } // Initialize the PC to the entry point. uint32_t entry_point = load_result.value(); @@ -358,15 +415,16 @@ } } - mpact::sim::util::MemoryWatcher *watcher = nullptr; RiscV32HtifSemiHost *htif_semihost = nullptr; if (absl::GetFlag(FLAGS_semihost_htif)) { // Add htif semihosting. RiscV32HtifSemiHost::SemiHostAddresses magic_addresses; if (GetMagicAddresses(&elf_loader, &magic_addresses)) { - watcher = new mpact::sim::util::MemoryWatcher(memory); + if (memory_watcher == nullptr) { + memory_watcher = new mpact::sim::util::MemoryWatcher(memory); + } htif_semihost = new RiscV32HtifSemiHost( - watcher, memory, magic_addresses, + memory_watcher, memory, magic_addresses, [&riscv_top]() { riscv_top.RequestHalt(RiscVTop::HaltReason::kSemihostHaltRequest, nullptr); @@ -375,7 +433,7 @@ riscv_top.RequestHalt(RiscVTop::HaltReason::kSemihostHaltRequest, nullptr); }); - riscv_top.state()->set_memory(watcher); + riscv_top.state()->set_memory(memory_watcher); } } @@ -422,7 +480,7 @@ PrintRegisters); cmd_shell.Run(std::cin, std::cout); } else { - std::cerr << "Starting simulation\n"; + if (!quiet) std::cerr << "Starting simulation\n"; auto t0 = absl::Now(); @@ -441,8 +499,9 @@ double sec = static_cast<double>(duration / absl::Milliseconds(100)) / 10; counter_sec.SetValue(sec); - std::cerr << absl::StrFormat("Simulation done: %0.1f sec\n", sec) - << std::endl; + if (!quiet) + std::cerr << absl::StrFormat("Simulation done: %0.1f sec\n", sec) + << std::endl; } // Export counters. @@ -472,8 +531,9 @@ } delete atomic_memory; delete memory; - delete watcher; - delete arm_semihost; delete htif_semihost; + delete memory_watcher; + delete arm_semihost; delete rv_decoder; + return return_code; }
diff --git a/riscv/rv32gv_sim.cc b/riscv/rv32gv_sim.cc index 262a1b9..94d1e65 100644 --- a/riscv/rv32gv_sim.cc +++ b/riscv/rv32gv_sim.cc
@@ -26,9 +26,11 @@ #include <string> #include <vector> +#include "absl/base/log_severity.h" #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/log/check.h" +#include "absl/log/globals.h" #include "absl/log/log.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" @@ -37,16 +39,19 @@ #include "absl/time/time.h" #include "mpact/sim/generic/core_debug_interface.h" #include "mpact/sim/generic/counters.h" +#include "mpact/sim/generic/decoder_interface.h" #include "mpact/sim/generic/instruction.h" #include "mpact/sim/proto/component_data.pb.h" #include "mpact/sim/util/memory/atomic_memory.h" #include "mpact/sim/util/memory/flat_demand_memory.h" +#include "mpact/sim/util/memory/memory_interface.h" #include "mpact/sim/util/memory/memory_watcher.h" #include "mpact/sim/util/program_loader/elf_program_loader.h" #include "re2/re2.h" #include "riscv/debug_command_shell.h" #include "riscv/riscv32_htif_semihost.h" #include "riscv/riscv32g_vec_decoder.h" +#include "riscv/riscv32gzb_vec_decoder.h" #include "riscv/riscv_arm_semihost.h" #include "riscv/riscv_fp_state.h" #include "riscv/riscv_register.h" @@ -59,6 +64,7 @@ using ::mpact::sim::generic::Instruction; using ::mpact::sim::proto::ComponentData; using ::mpact::sim::riscv::RiscV32GVecDecoder; +using ::mpact::sim::riscv::RiscV32GZBVecDecoder; using ::mpact::sim::riscv::RiscV32HtifSemiHost; using ::mpact::sim::riscv::RiscVArmSemihost; using ::mpact::sim::riscv::RiscVFPState; @@ -138,6 +144,19 @@ // Exit on execution of ecall instruction, default false. ABSL_FLAG(bool, exit_on_ecall, false, "Exit on ecall - false by default"); +// Enable bit manipulation instructions. +ABSL_FLAG(bool, bitmanip, false, "Enable bit manipulation instructions"); + +// Exit on write to 'tohost' +ABSL_FLAG(bool, exit_on_tohost, false, "Exit on write to 'tohost'"); + +// Quiet mode. Suppress informational and warning messages. +ABSL_FLAG(bool, quiet, false, "Suppress informational and warning messages"); + +// Flag to enable and configure the instruction and data caches. +ABSL_FLAG(std::string, icache, "", "Instruction cache configuration"); +ABSL_FLAG(std::string, dcache, "", "Data cache configuration"); + constexpr char kStackEndSymbolName[] = "__stack_end"; constexpr char kStackSizeSymbolName[] = "__stack_size"; @@ -205,6 +224,7 @@ } int main(int argc, char **argv) { + int return_code = 0; auto arg_vec = absl::ParseCommandLine(argc, argv); if (absl::GetFlag(FLAGS_semihost_htif) && absl::GetFlag(FLAGS_semihost_arm)) { @@ -216,13 +236,26 @@ std::cerr << "Only a single input file allowed" << std::endl; return -1; } + + bool quiet = absl::GetFlag(FLAGS_quiet); + if (quiet) { + absl::SetMinLogLevel(absl::LogSeverityAtLeast::kError); + } + std::string full_file_name = arg_vec[1]; std::string file_name = full_file_name.substr(full_file_name.find_last_of('/') + 1); std::string file_basename = file_name.substr(0, file_name.find_first_of('.')); auto *memory = new mpact::sim::util::FlatDemandMemory(); - auto *atomic_memory = new mpact::sim::util::AtomicMemory(memory); + mpact::sim::util::MemoryWatcher *memory_watcher = nullptr; + mpact::sim::util::AtomicMemory *atomic_memory = nullptr; + if (absl::GetFlag(FLAGS_exit_on_tohost)) { + memory_watcher = new mpact::sim::util::MemoryWatcher(memory); + atomic_memory = new mpact::sim::util::AtomicMemory(memory_watcher); + } else { + atomic_memory = new mpact::sim::util::AtomicMemory(memory); + } // Load the elf segments into memory. mpact::sim::util::ElfProgramLoader elf_loader(memory); auto load_result = elf_loader.LoadProgram(full_file_name); @@ -232,6 +265,10 @@ return -1; } + mpact::sim::util::MemoryInterface *memory_interface = memory; + if (memory_watcher != nullptr) { + memory_interface = memory_watcher; + } // Set up architectural state and decoder. RiscVState rv_state("RiscV32GV", RiscVXlen::RV32, memory, atomic_memory); // For floating point support add the fp state. @@ -239,7 +276,12 @@ RiscVVectorState rvv_state(&rv_state, 16 /*vector byte length*/); rv_state.set_rv_fp(&rv_fp_state); // Create the instruction decoder. - RiscV32GVecDecoder rv_decoder(&rv_state, memory); + mpact::sim::generic::DecoderInterface *rv_decoder = nullptr; + if (absl::GetFlag(FLAGS_bitmanip)) { + rv_decoder = new RiscV32GZBVecDecoder(&rv_state, memory); + } else { + rv_decoder = new RiscV32GVecDecoder(&rv_state, memory); + } // Make sure the architectural and abi register aliases are added. std::string reg_name; @@ -256,7 +298,7 @@ reg_name, ::mpact::sim::riscv::kFRegisterAliases[i]); } - RiscVTop riscv_top("RiscV32GVSim", &rv_state, &rv_decoder); + RiscVTop riscv_top("RiscV32GVSim", &rv_state, rv_decoder); if (absl::GetFlag(FLAGS_exit_on_ecall)) { rv_state.set_on_ecall([&riscv_top](const Instruction *inst) -> bool { @@ -265,6 +307,37 @@ }); } + if (absl::GetFlag(FLAGS_exit_on_tohost)) { + auto res = elf_loader.GetSymbol("tohost"); + if (res.ok()) { + auto tohost_addr = res.value().first; + auto status = memory_watcher->SetStoreWatchCallback( + AddressRange(tohost_addr), + [&riscv_top, tohost_addr, memory, &rv_state, &return_code, quiet]( + uint64_t, int) -> void { + riscv_top.RequestHalt(RiscVTop::HaltReason::kProgramDone, nullptr); + auto *db = rv_state.db_factory()->Allocate<uint32_t>(1); + memory->Load(tohost_addr, db, nullptr, nullptr); + auto word = db->Get<uint32_t>(0); + db->DecRef(); + return_code = word >> 1; + if (return_code == 0) { + if (!quiet) std::cerr << "PASS\n"; + } else { + std::cerr << "** FAIL **\n"; + } + }); + if (!status.ok()) { + std::cerr << "Error setting store watch callback for 'tohost': " + << status.message(); + return -1; + } + } else { + std::cerr << "Error: no symbol 'tohost' found"; + return -1; + } + } + // Initialize the PC to the entry point. uint32_t entry_point = load_result.value(); auto pc_write = riscv_top.WriteRegister("pc", entry_point); @@ -323,15 +396,16 @@ } } - mpact::sim::util::MemoryWatcher *watcher = nullptr; RiscV32HtifSemiHost *htif_semihost = nullptr; if (absl::GetFlag(FLAGS_semihost_htif)) { // Add htif semihosting. RiscV32HtifSemiHost::SemiHostAddresses magic_addresses; if (GetMagicAddresses(&elf_loader, &magic_addresses)) { - watcher = new mpact::sim::util::MemoryWatcher(memory); + if (memory_watcher == nullptr) { + memory_watcher = new mpact::sim::util::MemoryWatcher(memory); + } htif_semihost = new RiscV32HtifSemiHost( - watcher, memory, magic_addresses, + memory_watcher, memory, magic_addresses, [&riscv_top]() { riscv_top.RequestHalt(RiscVTop::HaltReason::kSemihostHaltRequest, nullptr); @@ -340,7 +414,7 @@ riscv_top.RequestHalt(RiscVTop::HaltReason::kSemihostHaltRequest, nullptr); }); - riscv_top.state()->set_memory(watcher); + riscv_top.state()->set_memory(memory_watcher); } } @@ -387,7 +461,7 @@ PrintRegisters); cmd_shell.Run(std::cin, std::cout); } else { - std::cerr << "Starting simulation\n"; + if (!quiet) std::cerr << "Starting simulation\n"; auto t0 = absl::Now(); @@ -406,8 +480,9 @@ double sec = static_cast<double>(duration / absl::Milliseconds(100)) / 10; counter_sec.SetValue(sec); - std::cerr << absl::StrFormat("Simulation done: %0.1f sec\n", sec) - << std::endl; + if (!quiet) + std::cerr << absl::StrFormat("Simulation done: %0.1f sec\n", sec) + << std::endl; } // Export counters. @@ -437,7 +512,8 @@ } delete atomic_memory; delete memory; - delete watcher; - delete arm_semihost; delete htif_semihost; + delete memory_watcher; + delete arm_semihost; + return return_code; }
diff --git a/riscv/rv64g_sim.cc b/riscv/rv64g_sim.cc index e27ba69..71181b8 100644 --- a/riscv/rv64g_sim.cc +++ b/riscv/rv64g_sim.cc
@@ -26,9 +26,11 @@ #include <string> #include <vector> +#include "absl/base/log_severity.h" #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/log/check.h" +#include "absl/log/globals.h" #include "absl/log/log.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" @@ -41,6 +43,7 @@ #include "mpact/sim/proto/component_data.pb.h" #include "mpact/sim/util/memory/atomic_memory.h" #include "mpact/sim/util/memory/flat_demand_memory.h" +#include "mpact/sim/util/memory/memory_interface.h" #include "mpact/sim/util/memory/memory_watcher.h" #include "mpact/sim/util/program_loader/elf_program_loader.h" #include "re2/re2.h" @@ -134,6 +137,15 @@ // Exit on execution of ecall instruction, default false. ABSL_FLAG(bool, exit_on_ecall, false, "Exit on ecall - false by default"); +// Enable bit manipulation instructions. +ABSL_FLAG(bool, bitmanip, false, "Enable bit manipulation instructions"); + +// Exit on write to 'tohost' +ABSL_FLAG(bool, exit_on_tohost, false, "Exit on write to 'tohost'"); + +// Quiet mode. Suppress informational and warning messages. +ABSL_FLAG(bool, quiet, false, "Suppress informational and warning messages"); + constexpr char kStackEndSymbolName[] = "__stack_end"; constexpr char kStackSizeSymbolName[] = "__stack_size"; @@ -179,6 +191,7 @@ } int main(int argc, char **argv) { + int return_code = 0; auto arg_vec = absl::ParseCommandLine(argc, argv); if (absl::GetFlag(FLAGS_semihost_htif) && absl::GetFlag(FLAGS_semihost_arm)) { @@ -190,13 +203,27 @@ std::cerr << "Only a single input file allowed" << std::endl; return -1; } + + bool quiet = absl::GetFlag(FLAGS_quiet); + if (quiet) { + absl::SetMinLogLevel(absl::LogSeverityAtLeast::kError); + } + std::string full_file_name = arg_vec[1]; std::string file_name = full_file_name.substr(full_file_name.find_last_of('/') + 1); std::string file_basename = file_name.substr(0, file_name.find_first_of('.')); auto *memory = new mpact::sim::util::FlatDemandMemory(); - auto *atomic_memory = new mpact::sim::util::AtomicMemory(memory); + mpact::sim::util::MemoryWatcher *memory_watcher = nullptr; + mpact::sim::util::AtomicMemory *atomic_memory = nullptr; + if (absl::GetFlag(FLAGS_exit_on_tohost)) { + memory_watcher = new mpact::sim::util::MemoryWatcher(memory); + atomic_memory = new mpact::sim::util::AtomicMemory(memory_watcher); + } else { + atomic_memory = new mpact::sim::util::AtomicMemory(memory); + } + // Load the elf segments into memory. mpact::sim::util::ElfProgramLoader elf_loader(memory); auto load_result = elf_loader.LoadProgram(full_file_name); @@ -206,8 +233,14 @@ return -1; } + mpact::sim::util::MemoryInterface *memory_interface = memory; + if (memory_watcher != nullptr) { + memory_interface = memory_watcher; + } + // Set up architectural state and decoder. - RiscVState rv_state("RiscV64", RiscVXlen::RV64, memory, atomic_memory); + RiscVState rv_state("RiscV64", RiscVXlen::RV64, memory_interface, + atomic_memory); // For floating point support add the fp state. RiscVFPState rv_fp_state(rv_state.csr_set(), &rv_state); rv_state.set_rv_fp(&rv_fp_state); @@ -238,6 +271,37 @@ }); } + if (absl::GetFlag(FLAGS_exit_on_tohost)) { + auto res = elf_loader.GetSymbol("tohost"); + if (res.ok()) { + auto tohost_addr = res.value().first; + auto status = memory_watcher->SetStoreWatchCallback( + AddressRange(tohost_addr), + [&riscv_top, tohost_addr, memory, &rv_state, &return_code, quiet]( + uint64_t, int) -> void { + riscv_top.RequestHalt(RiscVTop::HaltReason::kProgramDone, nullptr); + auto *db = rv_state.db_factory()->Allocate<uint32_t>(1); + memory->Load(tohost_addr, db, nullptr, nullptr); + auto word = db->Get<uint32_t>(0); + db->DecRef(); + return_code = word >> 1; + if (return_code == 0) { + if (!quiet) std::cerr << "PASS\n"; + } else { + std::cerr << "** FAIL **\n"; + } + }); + if (!status.ok()) { + std::cerr << "Error setting store watch callback for 'tohost': " + << status.message(); + return -1; + } + } else { + std::cerr << "Error: no symbol 'tohost' found"; + return -1; + } + } + // Initialize the PC to the entry point. uint64_t entry_point = load_result.value(); auto pc_write = riscv_top.WriteRegister("pc", entry_point); @@ -340,7 +404,7 @@ PrintRegisters); cmd_shell.Run(std::cin, std::cout); } else { - std::cerr << "Starting simulation\n"; + if (!quiet) std::cerr << "Starting simulation\n"; auto t0 = absl::Now(); @@ -359,8 +423,9 @@ double sec = static_cast<double>(duration / absl::Milliseconds(100)) / 10; counter_sec.SetValue(sec); - std::cerr << absl::StrFormat("Simulation done: %0.1f sec\n", sec) - << std::endl; + if (!quiet) + std::cerr << absl::StrFormat("Simulation done: %0.1f sec\n", sec) + << std::endl; } // Export counters. @@ -390,5 +455,7 @@ } delete atomic_memory; delete memory; + delete memory_watcher; delete arm_semihost; + return return_code; }
diff --git a/riscv/rv64gv_sim.cc b/riscv/rv64gv_sim.cc index 4ea7313..113fa6f 100644 --- a/riscv/rv64gv_sim.cc +++ b/riscv/rv64gv_sim.cc
@@ -21,15 +21,16 @@ #include <ios> #include <iostream> #include <memory> -#include <new> #include <optional> #include <ostream> #include <string> #include <vector> +#include "absl/base/log_severity.h" #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/log/check.h" +#include "absl/log/globals.h" #include "absl/log/log.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" @@ -43,6 +44,7 @@ #include "mpact/sim/proto/component_data.pb.h" #include "mpact/sim/util/memory/atomic_memory.h" #include "mpact/sim/util/memory/flat_demand_memory.h" +#include "mpact/sim/util/memory/memory_interface.h" #include "mpact/sim/util/memory/memory_watcher.h" #include "mpact/sim/util/program_loader/elf_program_loader.h" #include "re2/re2.h" @@ -143,6 +145,12 @@ // Enable bit manipulation instructions. ABSL_FLAG(bool, bitmanip, false, "Enable bit manipulation instructions"); +// Exit on write to 'tohost' +ABSL_FLAG(bool, exit_on_tohost, false, "Exit on write to 'tohost'"); + +// Quiet mode. Suppress informational and warning messages. +ABSL_FLAG(bool, quiet, false, "Suppress informational and warning messages"); + constexpr char kStackEndSymbolName[] = "__stack_end"; constexpr char kStackSizeSymbolName[] = "__stack_size"; @@ -188,6 +196,7 @@ } int main(int argc, char **argv) { + int return_code = 0; auto arg_vec = absl::ParseCommandLine(argc, argv); if (absl::GetFlag(FLAGS_semihost_htif) && absl::GetFlag(FLAGS_semihost_arm)) { @@ -199,13 +208,27 @@ std::cerr << "Only a single input file allowed" << std::endl; return -1; } + + bool quiet = absl::GetFlag(FLAGS_quiet); + if (quiet) { + absl::SetMinLogLevel(absl::LogSeverityAtLeast::kError); + } + std::string full_file_name = arg_vec[1]; std::string file_name = full_file_name.substr(full_file_name.find_last_of('/') + 1); std::string file_basename = file_name.substr(0, file_name.find_first_of('.')); auto *memory = new mpact::sim::util::FlatDemandMemory(); - auto *atomic_memory = new mpact::sim::util::AtomicMemory(memory); + mpact::sim::util::MemoryWatcher *memory_watcher = nullptr; + mpact::sim::util::AtomicMemory *atomic_memory = nullptr; + if (absl::GetFlag(FLAGS_exit_on_tohost)) { + memory_watcher = new mpact::sim::util::MemoryWatcher(memory); + atomic_memory = new mpact::sim::util::AtomicMemory(memory_watcher); + } else { + atomic_memory = new mpact::sim::util::AtomicMemory(memory); + } + // Load the elf segments into memory. mpact::sim::util::ElfProgramLoader elf_loader(memory); auto load_result = elf_loader.LoadProgram(full_file_name); @@ -215,8 +238,13 @@ return -1; } + mpact::sim::util::MemoryInterface *memory_interface = memory; + if (memory_watcher != nullptr) { + memory_interface = memory_watcher; + } // Set up architectural state and decoder. - RiscVState rv_state("RiscV64", RiscVXlen::RV64, memory, atomic_memory); + RiscVState rv_state("RiscV64", RiscVXlen::RV64, memory_interface, + atomic_memory); // For floating point support add the fp state. RiscVFPState rv_fp_state(rv_state.csr_set(), &rv_state); rv_state.set_rv_fp(&rv_fp_state); @@ -255,6 +283,37 @@ }); } + if (absl::GetFlag(FLAGS_exit_on_tohost)) { + auto res = elf_loader.GetSymbol("tohost"); + if (res.ok()) { + auto tohost_addr = res.value().first; + auto status = memory_watcher->SetStoreWatchCallback( + AddressRange(tohost_addr), + [&riscv_top, tohost_addr, memory, &rv_state, &return_code, quiet]( + uint64_t, int) -> void { + riscv_top.RequestHalt(RiscVTop::HaltReason::kProgramDone, nullptr); + auto *db = rv_state.db_factory()->Allocate<uint32_t>(1); + memory->Load(tohost_addr, db, nullptr, nullptr); + auto word = db->Get<uint32_t>(0); + db->DecRef(); + return_code = word >> 1; + if (return_code == 0) { + if (!quiet) std::cerr << "PASS\n"; + } else { + std::cerr << "** FAIL **\n"; + } + }); + if (!status.ok()) { + std::cerr << "Error setting store watch callback for 'tohost': " + << status.message(); + return -1; + } + } else { + std::cerr << "Error: no symbol 'tohost' found"; + return -1; + } + } + // Initialize the PC to the entry point. uint64_t entry_point = load_result.value(); auto pc_write = riscv_top.WriteRegister("pc", entry_point); @@ -357,7 +416,7 @@ PrintRegisters); cmd_shell.Run(std::cin, std::cout); } else { - std::cerr << "Starting simulation\n"; + if (!quiet) std::cerr << "Starting simulation\n"; auto t0 = absl::Now(); @@ -376,8 +435,9 @@ double sec = static_cast<double>(duration / absl::Milliseconds(100)) / 10; counter_sec.SetValue(sec); - std::cerr << absl::StrFormat("Simulation done: %0.1f sec\n", sec) - << std::endl; + if (!quiet) + std::cerr << absl::StrFormat("Simulation done: %0.1f sec\n", sec) + << std::endl; } // Export counters. @@ -407,6 +467,8 @@ } delete atomic_memory; delete memory; + delete memory_watcher; delete arm_semihost; delete rv_decoder; + return return_code; }
diff --git a/riscv/test/BUILD b/riscv/test/BUILD index 2bc43a7..16e6c12 100644 --- a/riscv/test/BUILD +++ b/riscv/test/BUILD
@@ -199,6 +199,7 @@ tags = ["not_run:arm"], deps = [ ":riscv_fp_test_base", + "//riscv:riscv_fp_state", "//riscv:riscv_g", "//riscv:riscv_state", "@com_google_googletest//:gtest_main",
diff --git a/riscv/test/riscv64_bitmanip_instructions_test.cc b/riscv/test/riscv64_bitmanip_instructions_test.cc index e3ecefb..ecb82e9 100644 --- a/riscv/test/riscv64_bitmanip_instructions_test.cc +++ b/riscv/test/riscv64_bitmanip_instructions_test.cc
@@ -606,7 +606,7 @@ SetRegisterValues<uint64_t>({{kX1, val1}, {kX2, val2}}); instruction_->Execute(nullptr); absl::uint128 result = 0; - for (int i = 0; i < sizeof(T) * 8; ++i) { + for (int i = 1; i < sizeof(T) * 8; ++i) { if (val2 & (1ULL << i)) result ^= (absl::MakeUint128(0, val1) << i); } EXPECT_EQ(GetRegisterValue<uint64_t>(kX3), absl::Uint128High64(result)); @@ -627,7 +627,7 @@ SetRegisterValues<uint64_t>({{kX1, val1}, {kX2, val2}}); instruction_->Execute(nullptr); uint64_t result = 0; - for (int i = 0; i < sizeof(T) * 8 - 1; ++i) { + for (int i = 0; i < sizeof(T) * 8; ++i) { if ((val2 >> i) & 1) result ^= (val1 >> (sizeof(T) * 8 - 1 - i)); } EXPECT_EQ(GetRegisterValue<uint64_t>(kX3), result);
diff --git a/riscv/test/riscv_f_instructions_test.cc b/riscv/test/riscv_f_instructions_test.cc index aa771ba..4beb278 100644 --- a/riscv/test/riscv_f_instructions_test.cc +++ b/riscv/test/riscv_f_instructions_test.cc
@@ -21,6 +21,7 @@ #include "googlemock/include/gmock/gmock.h" #include "mpact/sim/generic/instruction.h" #include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv_fp_info.h" #include "riscv/test/riscv_fp_test_base.h" namespace {
diff --git a/riscv/test/riscv_fp_test_base.h b/riscv/test/riscv_fp_test_base.h index cce8523..caec811 100644 --- a/riscv/test/riscv_fp_test_base.h +++ b/riscv/test/riscv_fp_test_base.h
@@ -266,7 +266,7 @@ 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); + SInt sval = absl::bit_cast<SInt>(value); D dval = (~static_cast<D>(0) << (sizeof(S) * 8)) | sval; return *reinterpret_cast<D *>(&dval); } @@ -275,7 +275,7 @@ 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); + return absl::bit_cast<D>(value); } // Signal error if the register is smaller than the floating point value. @@ -366,6 +366,23 @@ } } + template <typename T, typename RegisterType = RV32Register> + void SetNaNBoxedRegisterValues( + const std::vector<std::tuple<std::string, const T>> &values) { + for (auto &[reg_name, value] : values) { + typename RegisterType::ValueType reg_value = + NaNBox<T, typename RegisterType::ValueType>(value); + LOG(INFO) << "Setting " << reg_name << " to " << std::hex << reg_value + << " from: " << value; + auto *reg = state_->GetRegister<RegisterType>(reg_name).first; + auto *db = + state_->db_factory()->Allocate<typename RegisterType::ValueType>(1); + db->template Set<typename RegisterType::ValueType>(0, reg_value); + reg->SetDataBuffer(db); + db->DecRef(); + } + } + // Initializes the semantic function of the instruction object. void SetSemanticFunction(Instruction *inst, Instruction::SemanticFunction fcn) { @@ -486,7 +503,7 @@ *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]}}); + SetNaNBoxedRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}}); for (int rm : {0, 1, 2, 3, 4}) { rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm)); @@ -553,8 +570,8 @@ *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]}}); + SetNaNBoxedRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}}); + SetNaNBoxedRegisterValues<RHS, RhsRegisterType>({{kR2Name, rhs_span[i]}}); for (int rm : {0, 1, 2, 3, 4}) { rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm)); @@ -615,8 +632,8 @@ *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]}}); + SetNaNBoxedRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}}); + SetNaNBoxedRegisterValues<RHS, RhsRegisterType>({{kR2Name, rhs_span[i]}}); for (int rm : {0, 1, 2, 3, 4}) { rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm)); @@ -626,12 +643,24 @@ inst->Execute(nullptr); + auto fflags = rv_fp_->fflags()->GetUint32(); + 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]); } + if (name == "fmin") { + LOG(INFO) << "L: " << lhs_span[i] << " R: " << rhs_span[i]; + LHS lhs_val = state_->GetRegister<LhsRegisterType>(kR1Name) + .first->data_buffer() + ->template Get<LHS>(0); + RHS rhs_val = state_->GetRegister<RhsRegisterType>(kR2Name) + .first->data_buffer() + ->template Get<RHS>(0); + LOG(INFO) << "Lreg: " << lhs_val << " Rreg: " << rhs_val; + } auto reg_val = state_->GetRegister<DestRegisterType>(kRdName) .first->data_buffer() ->template Get<R>(0); @@ -640,7 +669,6 @@ 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 << ")"; } @@ -682,9 +710,9 @@ *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]}}); + SetNaNBoxedRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}}); + SetNaNBoxedRegisterValues<MHS, MhsRegisterType>({{kR2Name, mhs_span[i]}}); + SetNaNBoxedRegisterValues<RHS, RhsRegisterType>({{kR3Name, rhs_span[i]}}); for (int rm : {0, 1, 2, 3, 4}) { rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm)); @@ -745,9 +773,9 @@ *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]}}); + SetNaNBoxedRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}}); + SetNaNBoxedRegisterValues<MHS, MhsRegisterType>({{kR2Name, mhs_span[i]}}); + SetNaNBoxedRegisterValues<RHS, RhsRegisterType>({{kR3Name, rhs_span[i]}}); for (int rm : {0, 1, 2, 3, 4}) { rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
diff --git a/riscv/test/riscv_top_test.cc b/riscv/test/riscv_top_test.cc index fc23d65..0176c02 100644 --- a/riscv/test/riscv_top_test.cc +++ b/riscv/test/riscv_top_test.cc
@@ -114,9 +114,9 @@ } ~HtifSemihostSetup() { + state_->set_memory(memory_); delete semihost_; delete watcher_; - state_->set_memory(memory_); } private: