| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 <HexagonWrapper.h> |
| #include <tvm/ffi/function.h> |
| #include <tvm/ffi/reflection/registry.h> |
| // POSIX includes |
| #include <dirent.h> |
| #include <unistd.h> |
| |
| #include <algorithm> |
| #include <cstdlib> |
| #include <deque> |
| #include <iterator> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <sstream> |
| #include <string> |
| #include <utility> |
| |
| #include "../../../rpc/rpc_channel.h" |
| #include "../../../rpc/rpc_endpoint.h" |
| #include "../../../rpc/rpc_session.h" |
| #include "hexagon_sim_proto.h" |
| |
| #define CHECKED_CALL(func, ...) \ |
| do { \ |
| HEXAPI_Status s = sim_->func(__VA_ARGS__); \ |
| TVM_FFI_ICHECK_EQ(s, HEX_STAT_SUCCESS) \ |
| << self_name_ << ": " #func " failed with code " << Status_{s}.str(); \ |
| } while (false) |
| |
| namespace tvm { |
| namespace runtime { |
| namespace hexagon { |
| |
| using string_list = std::deque<std::string>; |
| |
| namespace detail { |
| |
| // Replacement for llvm::StringSwitch. |
| template <typename T> |
| class StringSwitch { |
| public: |
| explicit StringSwitch(const std::string& key) : key(key) {} |
| operator T() const { |
| auto f = map.find(key); |
| if (f != map.end()) { |
| return f->second; |
| } |
| TVM_FFI_ICHECK(static_cast<bool>(def_val)) << "default value not set"; |
| return *def_val; |
| } |
| StringSwitch& Case(const std::string& key, T val) { |
| map.insert(std::make_pair(key, val)); |
| return *this; |
| } |
| StringSwitch& Default(T val) { |
| TVM_FFI_ICHECK(!static_cast<bool>(def_val)) << "default value already set"; |
| def_val = val; |
| return *this; |
| } |
| |
| private: |
| const std::string key; |
| std::map<std::string, T> map; |
| std::optional<T> def_val; |
| }; |
| |
| using MaybeString = std::optional<std::string>; |
| |
| MaybeString front(const string_list& deq) { |
| return !deq.empty() ? MaybeString(deq.front()) : MaybeString(); |
| } |
| |
| MaybeString pop_front(string_list& deq) { // NOLINT(*) |
| if (deq.empty()) return MaybeString(); |
| std::string f = deq.front(); |
| deq.pop_front(); |
| return MaybeString(f); |
| } |
| |
| // Functions used when parsing the argument string. |
| |
| std::optional<int64_t> to_int(const MaybeString& str) { |
| if (str.has_value()) { |
| try { |
| size_t pos; |
| int64_t val = std::stoll(*str, &pos, 0); |
| return pos == str->size() ? std::optional<int64_t>(val) : std::nullopt; |
| } catch (std::invalid_argument&) { |
| } |
| } |
| return std::nullopt; |
| } |
| |
| std::optional<uint64_t> to_uint(const MaybeString& str) { |
| if (str.has_value()) { |
| try { |
| size_t pos; |
| uint64_t val = std::stoull(*str, &pos, 0); |
| return pos == str->size() ? std::optional<uint64_t>(val) : std::nullopt; |
| } catch (std::invalid_argument&) { |
| } |
| } |
| return std::nullopt; |
| } |
| |
| std::optional<float> to_float(const MaybeString& str) { |
| if (str.has_value()) { |
| try { |
| size_t pos; |
| float val = std::stof(*str, &pos); |
| return pos == str->size() ? std::optional<float>(val) : std::nullopt; |
| } catch (std::invalid_argument&) { |
| } |
| } |
| return std::nullopt; |
| } |
| |
| std::optional<bool> to_bool(const MaybeString& str) { |
| if (auto num = to_int(str)) { |
| if (*num == 0) return false; |
| if (*num == 1) return true; |
| return std::nullopt; |
| } |
| if (str) { |
| if (*str == "true" || *str == "TRUE") return true; |
| if (*str == "false" || *str == "FALSE") return false; |
| } |
| return std::nullopt; |
| } |
| |
| template <typename T> |
| using MaybeRange = std::optional<std::pair<T, T>>; |
| |
| template <typename T, std::optional<T> Parse(const MaybeString&)> |
| MaybeRange<T> to_range(const MaybeString& str) { |
| if (str && !str->empty()) { |
| auto n = str->find('-', 1); |
| if (n != std::string::npos) { |
| auto begin = Parse(str->substr(0, n)); |
| auto end = Parse(str->substr(n + 1, str->size() - n - 1)); |
| if (begin && end) { |
| return std::make_pair(*begin, *end); |
| } |
| } |
| } |
| return std::nullopt; |
| } |
| |
| } // namespace detail |
| |
| class SimulatorRPCChannel final : public RPCChannel { |
| public: |
| SimulatorRPCChannel(int stack_size, std::string args); |
| ~SimulatorRPCChannel() final; |
| size_t Send(const void* data, size_t size) final; |
| size_t Recv(void* data, size_t size) final; |
| |
| private: |
| struct Status_ { |
| HEXAPI_Status s; |
| std::string str() const; |
| }; |
| struct Core_ { |
| HEXAPI_CoreState s; |
| std::string str() const; |
| }; |
| struct Cpu_ { |
| HEXAPI_Cpu c; |
| std::string str() const; |
| }; |
| struct SDKInfo_ { |
| SDKInfo_(const std::string& sdk_root, const std::string& cpu); |
| std::string root; |
| std::string qurt_root; // sdk_root/rtos/qurt/computevNN. |
| std::string runelf; // Path to runelf.pbn. |
| std::string runmain; // Path to run_main_on_hexagon. |
| }; |
| |
| struct Message_ { |
| Message msg; |
| std::string str() const; |
| }; |
| |
| Message SendMsg(Message msg); |
| Message SendMsg(uint32_t code, uint32_t len, uint32_t va); |
| void ReadFromProcess(void* host_dst, HEX_VA_t src, size_t len); |
| void WriteToProcess(HEX_VA_t dst, const void* host_src, size_t len); |
| |
| static HEX_8u_t PassVirtAddrCallback(void* handle, int threadno, HEX_8u_t RssV, HEX_8u_t RttV, |
| HEX_8u_t RxxV, HEX_1u_t imm); |
| |
| std::optional<HEXAPI_Cpu> GetCPU(const detail::MaybeString& cpu_str); |
| |
| // File name templates for mkstemps. |
| #define SUFFIX ".cfg" |
| static constexpr int template_length_ = sizeof("temp-xxxx-XXXXXX" SUFFIX); // == strlen() + 1 |
| char osam_file_[template_length_] = "temp-osam-XXXXXX" SUFFIX; |
| char cosim_file_[template_length_] = "temp-q6ss-XXXXXX" SUFFIX; |
| const int suffix_len_ = strlen(SUFFIX); |
| #undef SUFFIX |
| |
| static const constexpr char* self_name_ = "SimulatorRPCChannel"; |
| static const constexpr char* default_cpu_ = "v68"; |
| std::string cpu_; |
| |
| HEX_VA_t dispatch_v_, message_buffer_v_; |
| std::unique_ptr<HexagonWrapper> sim_; |
| |
| // Sim configuration routines. |
| bool Configure(string_list& opts); // NOLINT(*) |
| |
| bool HandleAHBBusPenalty(string_list& rest); // NOLINT(*) |
| bool HandleAHBBusRatio(string_list& rest); // NOLINT(*) |
| bool HandleAHBHighAddr(string_list& rest); // NOLINT(*) |
| bool HandleAHBLowAddr(string_list& rest); // NOLINT(*) |
| bool HandleAXI2BusPenalty(string_list& rest); // NOLINT(*) |
| bool HandleAXI2BusRatio(string_list& rest); // NOLINT(*) |
| bool HandleAXI2HighAddr(string_list& rest); // NOLINT(*) |
| bool HandleAXI2LowAddr(string_list& rest); // NOLINT(*) |
| bool HandleBuildTag(string_list& rest); // NOLINT(*) |
| bool HandleBusPenalty(string_list& rest); // NOLINT(*) |
| bool HandleBusRatio(string_list& rest); // NOLINT(*) |
| bool HandleBusTrace(string_list& rest); // NOLINT(*) |
| bool HandleBypassIdle(string_list& rest); // NOLINT(*) |
| bool HandleConnectionTimeout(string_list& rest); // NOLINT(*) |
| bool HandleCoprocTrace(string_list& rest); // NOLINT(*) |
| bool HandleCoreDump(string_list& rest); // NOLINT(*) |
| bool HandleCosimFile(string_list& rest); // NOLINT(*) |
| bool HandleDCacheTrace(string_list& rest); // NOLINT(*) |
| bool HandleDSPClock(string_list& rest); // NOLINT(*) |
| bool HandleETMCFGBase(string_list& rest); // NOLINT(*) |
| bool HandleGDBServ(string_list& rest); // NOLINT(*) |
| bool HandleHVXLength(string_list& rest); // NOLINT(*) |
| bool HandleICacheTrace(string_list& rest); // NOLINT(*) |
| bool HandleL2CacheTrace(string_list& rest); // NOLINT(*) |
| bool HandleL2CFGBase(string_list& rest); // NOLINT(*) |
| bool HandleL2TCMBase(string_list& rest); // NOLINT(*) |
| bool HandleMemFillRand(string_list& rest); // NOLINT(*) |
| bool HandleMemFill(string_list& rest); // NOLINT(*) |
| bool HandleMemTrace(string_list& rest); // NOLINT(*) |
| bool HandleNullPtr(string_list& rest); // NOLINT(*) |
| bool HandlePacketAnalyze(string_list& rest); // NOLINT(*) |
| bool HandlePCFilter(string_list& rest); // NOLINT(*) |
| bool HandlePCTraceMin(string_list& rest); // NOLINT(*) |
| bool HandlePCTraceNano(string_list& rest); // NOLINT(*) |
| bool HandlePCTrace(string_list& rest); // NOLINT(*) |
| bool HandlePMUStatsFile(string_list& rest); // NOLINT(*) |
| bool HandleProfile(string_list& rest); // NOLINT(*) |
| bool HandleProfileTimeZero(string_list& rest); // NOLINT(*) |
| bool HandleQuiet(string_list& rest); // NOLINT(*) |
| bool HandleReconnect(string_list& rest); // NOLINT(*) |
| bool HandleRTOS(string_list& rest); // NOLINT(*) |
| bool HandleSimErr(string_list& rest); // NOLINT(*) |
| bool HandleSimIn(string_list& rest); // NOLINT(*) |
| bool HandleSimOut(string_list& rest); // NOLINT(*) |
| bool HandleStackStart(string_list& rest); // NOLINT(*) |
| bool HandleStallTrace(string_list& rest); // NOLINT(*) |
| bool HandleStatsFile(string_list& rest); // NOLINT(*) |
| bool HandleSubsystemBase(string_list& rest); // NOLINT(*) |
| bool HandleSymFile(string_list& rest); // NOLINT(*) |
| bool HandleTCM(string_list& rest); // NOLINT(*) |
| bool HandleTCMHighAddr(string_list& rest); // NOLINT(*) |
| bool HandleTCMLowAddr(string_list& rest); // NOLINT(*) |
| bool HandleTimeFilterNS(string_list& rest); // NOLINT(*) |
| bool HandleTiming(string_list& rest); // NOLINT(*) |
| bool HandleUArchTrace(string_list& rest); // NOLINT(*) |
| bool HandleUseFS(string_list& rest); // NOLINT(*) |
| bool HandleV2PTranslation(string_list& rest); // NOLINT(*) |
| bool HandleVerbose(string_list& rest); // NOLINT(*) |
| |
| using MaybeUInt64 = std::optional<uint64_t>; |
| using MaybeUIntRange = std::pair<MaybeUInt64, MaybeUInt64>; |
| |
| bool should_parse_next(const string_list& rest); |
| std::optional<HEXAPI_Interval> to_interval(const detail::MaybeString& str); |
| std::optional<HEXAPI_TimingMode> to_timingmode(const detail::MaybeString& str); |
| std::optional<HEXAPI_VerboseMode> to_verbosemode(const detail::MaybeString& str); |
| std::optional<HEXAPI_Nullptr> to_nullptr(const detail::MaybeString& str); |
| |
| MaybeUIntRange ahb_, axi2_; |
| std::optional<uint32_t> debug_port_; |
| |
| using OptionHandler = bool (SimulatorRPCChannel::*)(string_list&); |
| static std::map<std::string, OptionHandler> opt_map_; |
| }; |
| |
| const constexpr char* SimulatorRPCChannel::self_name_; |
| const constexpr char* SimulatorRPCChannel::default_cpu_; |
| |
| decltype(SimulatorRPCChannel::opt_map_) SimulatorRPCChannel::opt_map_ = { |
| {"--ahbbuspenalty", &SimulatorRPCChannel::HandleAHBBusPenalty}, |
| {"--ahbbusratio", &SimulatorRPCChannel::HandleAHBBusRatio}, |
| {"--ahb:highaddr", &SimulatorRPCChannel::HandleAHBHighAddr}, |
| {"--ahb:lowaddr", &SimulatorRPCChannel::HandleAHBLowAddr}, |
| {"--axi2buspenalty", &SimulatorRPCChannel::HandleAXI2BusPenalty}, |
| {"--axi2busratio", &SimulatorRPCChannel::HandleAXI2BusRatio}, |
| {"--axi2:highaddr", &SimulatorRPCChannel::HandleAXI2HighAddr}, |
| {"--axi2:lowaddr", &SimulatorRPCChannel::HandleAXI2LowAddr}, |
| {"-b", &SimulatorRPCChannel::HandleBusTrace}, |
| {"--build_tag", &SimulatorRPCChannel::HandleBuildTag}, |
| {"--buspenalty", &SimulatorRPCChannel::HandleBusPenalty}, |
| {"--busratio", &SimulatorRPCChannel::HandleBusRatio}, |
| {"--bustrace", &SimulatorRPCChannel::HandleBusTrace}, |
| {"--bypass_idle", &SimulatorRPCChannel::HandleBypassIdle}, |
| {"--connection_timeout", &SimulatorRPCChannel::HandleConnectionTimeout}, |
| {"--coproctrace", &SimulatorRPCChannel::HandleCoprocTrace}, |
| {"--coredump", &SimulatorRPCChannel::HandleCoreDump}, |
| {"--cosim_file", &SimulatorRPCChannel::HandleCosimFile}, |
| {"--dcachetrace", &SimulatorRPCChannel::HandleDCacheTrace}, |
| {"--dsp_clock", &SimulatorRPCChannel::HandleDSPClock}, |
| {"-E", &SimulatorRPCChannel::HandleSimErr}, |
| {"--etm_base", &SimulatorRPCChannel::HandleETMCFGBase}, |
| {"--etmcfg_base", &SimulatorRPCChannel::HandleETMCFGBase}, |
| {"--gdbserv", &SimulatorRPCChannel::HandleGDBServ}, |
| {"-G", &SimulatorRPCChannel::HandleGDBServ}, |
| {"--hvx_length", &SimulatorRPCChannel::HandleHVXLength}, |
| {"--icachetrace", &SimulatorRPCChannel::HandleICacheTrace}, |
| {"-I", &SimulatorRPCChannel::HandleSimIn}, |
| {"--l2cachetrace", &SimulatorRPCChannel::HandleL2CacheTrace}, |
| {"--l2cfg_base", &SimulatorRPCChannel::HandleL2CFGBase}, |
| {"--l2tcm_base", &SimulatorRPCChannel::HandleL2TCMBase}, |
| {"--memfill", &SimulatorRPCChannel::HandleMemFill}, |
| {"--memfill_rand", &SimulatorRPCChannel::HandleMemFillRand}, |
| {"--memtrace", &SimulatorRPCChannel::HandleMemTrace}, |
| {"-m", &SimulatorRPCChannel::HandleMemTrace}, |
| {"--nullptr", &SimulatorRPCChannel::HandleNullPtr}, |
| {"-O", &SimulatorRPCChannel::HandleSimOut}, |
| {"--packet_analyze", &SimulatorRPCChannel::HandlePacketAnalyze}, |
| {"--pcfilter", &SimulatorRPCChannel::HandlePCFilter}, |
| {"--pctrace", &SimulatorRPCChannel::HandlePCTrace}, |
| {"--pctrace_min", &SimulatorRPCChannel::HandlePCTraceMin}, |
| {"--pctrace_nano", &SimulatorRPCChannel::HandlePCTraceNano}, |
| {"-p", &SimulatorRPCChannel::HandleProfile}, |
| {"--pmu_statsfile", &SimulatorRPCChannel::HandlePMUStatsFile}, |
| {"--profile", &SimulatorRPCChannel::HandleProfile}, |
| {"--profile_timezero", &SimulatorRPCChannel::HandleProfileTimeZero}, |
| {"-q", &SimulatorRPCChannel::HandleQuiet}, |
| {"--quiet", &SimulatorRPCChannel::HandleQuiet}, |
| {"--reconnect", &SimulatorRPCChannel::HandleReconnect}, |
| {"--rtos", &SimulatorRPCChannel::HandleRTOS}, |
| {"-S", &SimulatorRPCChannel::HandleStatsFile}, |
| {"--sim_err", &SimulatorRPCChannel::HandleSimErr}, |
| {"--sim_in", &SimulatorRPCChannel::HandleSimIn}, |
| {"--sim_out", &SimulatorRPCChannel::HandleSimOut}, |
| {"--stackstart", &SimulatorRPCChannel::HandleStackStart}, |
| {"--stalltrace", &SimulatorRPCChannel::HandleStallTrace}, |
| {"--statsfile", &SimulatorRPCChannel::HandleStatsFile}, |
| {"--subsystem_base", &SimulatorRPCChannel::HandleSubsystemBase}, |
| {"--symfile", &SimulatorRPCChannel::HandleSymFile}, |
| {"--tcm", &SimulatorRPCChannel::HandleTCM}, |
| {"--tcm:highaddr", &SimulatorRPCChannel::HandleTCMHighAddr}, |
| {"--tcm:lowaddr", &SimulatorRPCChannel::HandleTCMLowAddr}, |
| {"-t", &SimulatorRPCChannel::HandlePCTrace}, |
| {"--timefilter_ns", &SimulatorRPCChannel::HandleTimeFilterNS}, |
| {"--timing", &SimulatorRPCChannel::HandleTiming}, |
| {"--uarchtrace", &SimulatorRPCChannel::HandleUArchTrace}, |
| {"-u", &SimulatorRPCChannel::HandlePCTraceMin}, |
| {"--usefs", &SimulatorRPCChannel::HandleUseFS}, |
| {"--v2p_translation", &SimulatorRPCChannel::HandleV2PTranslation}, |
| {"--verbose", &SimulatorRPCChannel::HandleVerbose}, |
| }; |
| |
| std::string SimulatorRPCChannel::Status_::str() const { |
| switch (s) { |
| case HEX_STAT_ERROR: |
| return "HEX_STAT_ERROR"; |
| case HEX_STAT_SUCCESS: |
| return "HEX_STAT_SUCCESS"; |
| case HEX_STAT_CANNOT_CONFIG: |
| return "HEX_STAT_CANNOT_CONFIG"; |
| case HEX_STAT_INVALID_ARGS: |
| return "HEX_STAT_INVALID_ARGS"; |
| case HEX_STAT_RANGE_ERROR: |
| return "HEX_STAT_RANGE_ERROR"; |
| case HEX_STAT_FILE_ACCESS_ERROR: |
| return "HEX_STAT_FILE_ACCESS_ERROR"; |
| case HEX_STAT_DEVICE_NOT_FOUND: |
| return "HEX_STAT_DEVICE_NOT_FOUND"; |
| case HEX_STAT_MEM_ACCESS_ERROR: |
| return "HEX_STAT_MEM_ACCESS_ERROR"; |
| case HEX_STAT_CANNOT_TRANSLATE: |
| return "HEX_STAT_CANNOT_TRANSLATE"; |
| case HEX_STAT_NO_ACTIVE_THREADS: |
| return "HEX_STAT_NO_ACTIVE_THREADS"; |
| case HEX_STAT_LOAD_ELF_ERROR: |
| return "HEX_STAT_LOAD_ELF_ERROR"; |
| case HEX_STAT_CORE_RESET: |
| return "HEX_STAT_CORE_RESET"; |
| default: |
| break; |
| } |
| return std::to_string(static_cast<int>(s)); |
| } |
| |
| std::string SimulatorRPCChannel::Core_::str() const { |
| switch (s) { |
| case HEX_CORE_SUCCESS: |
| return "HEX_CORE_SUCCESS"; |
| case HEX_CORE_FINISHED: |
| return "HEX_CORE_FINISHED"; |
| case HEX_CORE_RESET: |
| return "HEX_CORE_RESET"; |
| case HEX_CORE_BREAKPOINT: |
| return "HEX_CORE_BREAKPOINT"; |
| case HEX_CORE_ASYNCHRONOUS_BREAK: |
| return "HEX_CORE_ASYNCHRONOUS_BREAK"; |
| case HEX_CORE_ERROR: |
| return "HEX_CORE_ERROR"; |
| default: |
| break; |
| } |
| return std::to_string(static_cast<int>(s)); |
| } |
| |
| std::string SimulatorRPCChannel::Cpu_::str() const { |
| switch (c) { |
| #ifdef HEX_CPU_ID_V65A_512 |
| case HEX_CPU_V65: |
| return "v65"; |
| #endif |
| #ifdef HEX_CPU_ID_V66A_512 |
| case HEX_CPU_V66: |
| return "v66"; |
| #endif |
| #ifdef HEX_CPU_ID_V68N_1024 |
| case HEX_CPU_V68: |
| return "v68"; |
| #endif |
| #ifdef HEX_CPU_ID_V69NA |
| case HEX_CPU_V69: |
| return "v69"; |
| #endif |
| #ifdef HEX_CPU_ID_V71H_MDM_SCA |
| case HEX_CPU_V71: |
| return "v71"; |
| #endif |
| #ifdef HEX_CPU_ID_V73NA_1 |
| case HEX_CPU_V73: |
| return "v73"; |
| #endif |
| #ifdef HEX_CPU_ID_V75NA_1 |
| case HEX_CPU_V75: |
| return "v75"; |
| #endif |
| default: |
| break; |
| } |
| return default_cpu_; |
| } |
| |
| // TVM_FFI_THROW(InternalError) always throws an exception or terminates the |
| // process, but the compiler doesn't know that. |
| #if (__GNUC__) |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wreturn-type" |
| #endif |
| |
| #if (__clang__) |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wreturn-type" |
| #endif |
| |
| std::string SimulatorRPCChannel::Message_::str() const { |
| switch (msg.code) { |
| case Message::kNone: |
| return "kNone"; |
| case Message::kAck: |
| return "kAck"; |
| case Message::kTerminate: |
| return "kTerminate"; |
| case Message::kReceiveStart: |
| return "kReceiveStart"; |
| case Message::kReceiveEnd: |
| return "kReceiveEnd"; |
| case Message::kSendStart: |
| return "kSendStart"; |
| case Message::kSendEnd: |
| return "kSendEnd"; |
| default: |
| TVM_FFI_THROW(InternalError) << "Internal error: Unrecognized code value: " << msg.code; |
| break; |
| } |
| } |
| |
| #if (__GNUC__) |
| #pragma GCC diagnostic pop |
| #endif |
| |
| #if (__clang__) |
| #pragma GCC diagnostic pop |
| #endif |
| |
| SimulatorRPCChannel::SDKInfo_::SDKInfo_(const std::string& sdk_root, const std::string& cpu) |
| : root(sdk_root) { |
| // For v69 chips, still look for v68 in the directory names. |
| std::string check_cpu = cpu == "v69" ? "v68" : cpu; |
| |
| qurt_root = root + "/rtos/qurt/compute" + check_cpu; |
| runelf = qurt_root + "/sdksim_bin/runelf.pbn"; |
| |
| // The "run_main_on_hexagon_sim" binary lives in a subdirectory that looks |
| // like "[...]on_hexagon/ship/hexagon_toolv84_v68/run_main_on_hexagon_sim". |
| // We need to get the right "hexagon_toolv..." component, based mostly on |
| // the cpu version. |
| std::vector<std::string> dir_names; |
| |
| DIR* dir = opendir((root + "/libs/run_main_on_hexagon/ship").c_str()); |
| TVM_FFI_ICHECK(dir != nullptr) << "Cannot read directory " |
| << root + "/libs/run_main_on_hexagon/ship"; |
| while (dirent* d = readdir(dir)) { |
| if (d->d_type != DT_DIR) continue; |
| |
| std::string name = d->d_name; |
| // Note: The first substr is always safe, and the second only executes |
| // when "name" is at least 13 characters long. |
| if (name.substr(0, 13) == "hexagon_toolv" && name.substr(name.size() - 3, 3) == check_cpu) { |
| dir_names.push_back(name); |
| } |
| } |
| closedir(dir); |
| TVM_FFI_ICHECK(!dir_names.empty()); |
| |
| auto max_it = std::max_element(dir_names.begin(), dir_names.end()); |
| runmain = root + "/libs/run_main_on_hexagon/ship/" + *max_it + "/run_main_on_hexagon_sim"; |
| } |
| |
| HEX_8u_t SimulatorRPCChannel::PassVirtAddrCallback(void* handle, int threadno, HEX_8u_t RssV, |
| HEX_8u_t RttV, HEX_8u_t RxxV, HEX_1u_t imm) { |
| // Rssv = combine(&message_buffer, &dispatch) |
| auto* rpc = reinterpret_cast<SimulatorRPCChannel*>(handle); |
| rpc->dispatch_v_ = RssV & ~0u; // ~0u is uint32_t |
| rpc->message_buffer_v_ = RssV >> 32; |
| |
| LOG(INFO) << "dispatch:" << reinterpret_cast<void*>(rpc->dispatch_v_) |
| << ", message buffer:" << reinterpret_cast<void*>(rpc->message_buffer_v_); |
| HEXAPI_Status s = rpc->sim_->SetBreakpoint(rpc->dispatch_v_); |
| TVM_FFI_ICHECK_EQ(s, HEX_STAT_SUCCESS) |
| << self_name_ << ": SetBreakpoint failed with code " << Status_{s}.str(); |
| return RssV; |
| } |
| |
| std::optional<HEXAPI_Cpu> SimulatorRPCChannel::GetCPU(const detail::MaybeString& cpu_str) { |
| if (!cpu_str) return std::nullopt; |
| return detail::StringSwitch<std::optional<HEXAPI_Cpu>>(*cpu_str) |
| #ifdef HEX_CPU_ID_V65A_512 |
| .Case("v65", HEX_CPU_V65) |
| #endif |
| #ifdef HEX_CPU_ID_V66A_512 |
| .Case("v66", HEX_CPU_V66) |
| #endif |
| #ifdef HEX_CPU_ID_V68N_1024 |
| .Case("v68", HEX_CPU_V68) |
| #endif |
| #ifdef HEX_CPU_ID_V69NA |
| .Case("v69", HEX_CPU_V69) |
| #endif |
| #ifdef HEX_CPU_ID_V71H_MDM_SCA |
| .Case("v71", HEX_CPU_V71) |
| #endif |
| #ifdef HEX_CPU_ID_V73NA_1 |
| .Case("v73", HEX_CPU_V73) |
| #endif |
| #ifdef HEX_CPU_ID_V75NA_1 |
| .Case("v75", HEX_CPU_V75) |
| #endif |
| .Default(std::nullopt); |
| } |
| |
| SimulatorRPCChannel::SimulatorRPCChannel(int stack_size, std::string args) { |
| const char* sdk_root_env = std::getenv("HEXAGON_SDK_ROOT"); |
| TVM_FFI_ICHECK(sdk_root_env != nullptr) << "Please set HEXAGON_SDK_ROOT"; |
| const char* toolchain_env = std::getenv("HEXAGON_TOOLCHAIN"); |
| TVM_FFI_ICHECK(toolchain_env != nullptr) << "Please set HEXAGON_TOOLCHAIN"; |
| |
| std::string sdk_root(sdk_root_env); |
| std::string toolchain(toolchain_env); |
| |
| auto sim_args_iss = std::istringstream(args); |
| using iterator = std::istream_iterator<std::string>; |
| auto sim_args = string_list(iterator(sim_args_iss), iterator()); |
| |
| detail::MaybeString target_str = detail::pop_front(sim_args); |
| auto maybe_cpu = GetCPU(target_str); |
| if (!maybe_cpu) { |
| if (!target_str || target_str->empty()) { |
| LOG(INFO) << "CPU not given, defaulting to " << default_cpu_; |
| maybe_cpu = GetCPU(std::string(default_cpu_)); |
| } else { |
| TVM_FFI_THROW(InternalError) << "Invalid CPU name " << *target_str; |
| } |
| } |
| cpu_ = Cpu_{*maybe_cpu}.str(); |
| sim_ = std::make_unique<HexagonWrapper>(*maybe_cpu, (toolchain + "/lib/iss").c_str()); |
| SDKInfo_ sdk(sdk_root, cpu_); |
| |
| // Prepare the osam.cfg file. |
| int fd_osam = mkstemps(osam_file_, suffix_len_); |
| TVM_FFI_ICHECK_GE(fd_osam, 0); |
| std::string osam_str = sdk.qurt_root + "/debugger/lnx64/qurt_model.so"; |
| TVM_FFI_ICHECK_EQ(write(fd_osam, osam_str.c_str(), osam_str.size()), osam_str.size()); |
| close(fd_osam); |
| // Prepare the q6ss.cfg file. |
| int fd_cosim = mkstemps(cosim_file_, suffix_len_); |
| TVM_FFI_ICHECK_GE(fd_cosim, 0); |
| std::string cosim_str = |
| toolchain + |
| "/lib/iss/qtimer.so --csr_base=0xFC900000 --irq_p=1 --freq=19200000 --cnttid=1\n" + |
| toolchain + "/lib/iss/l2vic.so 32 0xFC910000"; |
| TVM_FFI_ICHECK_EQ(write(fd_cosim, cosim_str.c_str(), cosim_str.size()), cosim_str.size()); |
| close(fd_cosim); |
| |
| CHECKED_CALL(ConfigureL2tcmBase, 0xD800); |
| CHECKED_CALL(ConfigureARFilesystem, &std::string(".")[0]); |
| CHECKED_CALL(ConfigureOSAwareness, osam_file_); |
| CHECKED_CALL(ConfigureCosim, cosim_file_); |
| CHECKED_CALL(ConfigureExecutableBinary, sdk.runelf.c_str()); |
| |
| std::string stack_arg = |
| stack_size > 0 ? std::string(" -stack_size=") + std::to_string(stack_size) : ""; |
| std::string cmdline = sdk.runelf + " " + sdk.runmain + stack_arg + " -- libhexagon_rpc_sim.so"; |
| char* parg = &cmdline[0]; |
| CHECKED_CALL(ConfigureAppCommandLine, 1, &parg); |
| |
| // Configure the simulator. |
| Configure(sim_args); |
| |
| CHECKED_CALL(EndOfConfiguration); |
| CHECKED_CALL(AddUserDefinedInstCallback, this, &PassVirtAddrCallback); |
| |
| // Start the initial run, until the callback is executed. |
| HEX_4u_t result; |
| HEXAPI_CoreState core = sim_->Run(&result); |
| if (core != HEX_CORE_BREAKPOINT) { |
| TVM_FFI_THROW(InternalError) << self_name_ |
| << ": Run not stopped on breakpoint, code=" << Core_{core}.str(); |
| } |
| |
| // At this point the simulator has executed the executable's initialization |
| // code that could have written to the SSR register. |
| // Enable UPCYCLE register. |
| HEX_4u_t thread_num; |
| CHECKED_CALL(GetCurrentHWThreadNum, &thread_num); |
| HEX_4u_t thread_ssr; |
| CHECKED_CALL(ReadThreadRegister, thread_num, TH_REG_SSR, &thread_ssr); |
| thread_ssr |= (1 << 23); |
| CHECKED_CALL(WriteThreadRegister, thread_num, TH_REG_SSR, thread_ssr); |
| } |
| |
| SimulatorRPCChannel::~SimulatorRPCChannel() { |
| SendMsg(Message::kTerminate, 0, Message::null_va); |
| |
| HEX_4u_t result; |
| HEXAPI_CoreState core = sim_->Run(&result); |
| TVM_FFI_ICHECK_EQ(core, HEX_CORE_FINISHED); |
| |
| unlink(osam_file_); |
| unlink(cosim_file_); |
| } |
| |
| size_t SimulatorRPCChannel::Send(const void* data, size_t size) { |
| TVM_FFI_ICHECK(size <= std::numeric_limits<uint32_t>::max()); |
| |
| Message reply_start = |
| SendMsg(Message::kReceiveStart, static_cast<uint32_t>(size), Message::null_va); |
| TVM_FFI_ICHECK_EQ(reply_start.code, Message::kAck); |
| TVM_FFI_ICHECK_GE(reply_start.len, size); |
| TVM_FFI_ICHECK_NE(reply_start.va, Message::null_va); |
| |
| WriteToProcess(reply_start.va, data, size); |
| |
| Message reply_end = SendMsg(Message::kReceiveEnd, static_cast<uint32_t>(size), reply_start.va); |
| TVM_FFI_ICHECK_EQ(reply_end.code, Message::kAck); |
| return size; |
| } |
| |
| size_t SimulatorRPCChannel::Recv(void* data, size_t size) { |
| TVM_FFI_ICHECK(size <= std::numeric_limits<uint32_t>::max()); |
| |
| Message reply_start = SendMsg(Message::kSendStart, static_cast<uint32_t>(size), Message::null_va); |
| TVM_FFI_ICHECK_EQ(reply_start.code, Message::kAck); |
| TVM_FFI_ICHECK_GE(reply_start.len, size); |
| TVM_FFI_ICHECK_NE(reply_start.va, Message::null_va); |
| |
| ReadFromProcess(data, reply_start.va, size); |
| |
| Message reply_end = SendMsg(Message::kSendEnd, static_cast<uint32_t>(size), reply_start.va); |
| TVM_FFI_ICHECK_EQ(reply_end.code, Message::kAck); |
| return size; |
| } |
| |
| Message SimulatorRPCChannel::SendMsg(Message msg) { |
| auto run = [this]() { |
| HEXAPI_CoreState core = HEX_CORE_RESET; |
| HEX_4u_t result; |
| |
| core = sim_->Run(&result); |
| Core_ core_ = {core}; |
| TVM_FFI_ICHECK_EQ(core, HEX_CORE_BREAKPOINT) |
| << "Expecting HEX_CORE_BREAKPOINT, received: " << core_.str(); |
| }; |
| |
| WriteToProcess(message_buffer_v_, &msg, sizeof msg); |
| run(); |
| |
| Message ret = {0}; |
| ReadFromProcess(&ret, message_buffer_v_, sizeof ret); |
| return ret; |
| } |
| |
| Message SimulatorRPCChannel::SendMsg(uint32_t code, uint32_t len, uint32_t va) { |
| Message m; |
| m.code = code; |
| m.len = len; |
| m.va = va; |
| return SendMsg(m); |
| } |
| |
| void SimulatorRPCChannel::ReadFromProcess(void* host_dst, HEX_VA_t src, size_t len) { |
| if (len == 0) return; |
| |
| auto* dst = reinterpret_cast<char*>(host_dst); |
| |
| while (len > 0) { |
| uint32_t src_align = 1u << __builtin_ctz(src); // Max pow-of-2 dividing src. |
| uint32_t len_align = 1u << __builtin_ctz(len); // Max pow-of-2 dividing len. |
| // The transfer behaves as a bus transaction, so it has to be of a size |
| // that is a power of 2 not exceeding 8, and the remote address has to |
| // be aligned to at least the transfer size. |
| auto read_size = std::min<size_t>({src_align, len_align, 8}); |
| CHECKED_CALL(ReadVirtual, src, /*asid*/ -1u, read_size, dst); |
| |
| src += read_size; |
| dst += read_size; |
| len -= read_size; |
| } |
| } |
| |
| void SimulatorRPCChannel::WriteToProcess(HEX_VA_t dst, const void* host_src, size_t len) { |
| if (len == 0) return; |
| |
| auto* src = reinterpret_cast<const char*>(host_src); |
| |
| while (len > 0) { |
| uint32_t dst_align = 1u << __builtin_ctz(dst); // Max pow-of-2 dividing dst. |
| uint32_t len_align = 1u << __builtin_ctz(len); // Max pow-of-2 dividing len. |
| // The transfer behaves as a bus transaction, so it has to be of a size |
| // that is a power of 2 not exceeding 8, and the remote address has to |
| // be aligned to at least the transfer size. |
| auto write_size = std::min<size_t>({dst_align, len_align, 8}); |
| HEX_8u_t val = 0; |
| memcpy(&val, src, write_size); |
| CHECKED_CALL(WriteVirtual, dst, /*asid*/ -1u, write_size, val); |
| |
| src += write_size; |
| dst += write_size; |
| len -= write_size; |
| } |
| } |
| |
| // Configuration functions |
| |
| bool SimulatorRPCChannel::Configure(string_list& opts) { |
| while (!opts.empty()) { |
| std::string key = *detail::pop_front(opts); |
| auto f = opt_map_.find(key); |
| if (f == opt_map_.end()) { |
| TVM_FFI_THROW(InternalError) << "Unrecognized simulator option: " << key; |
| // unreachable |
| } |
| TVM_FFI_ICHECK((this->*f->second)(opts)) << "error handling option: " << key; |
| } |
| |
| // Check AHB. |
| if (ahb_.first.has_value() && ahb_.second.has_value()) { |
| CHECKED_CALL(ConfigureAHB, *ahb_.first, *ahb_.second); |
| } else { |
| TVM_FFI_ICHECK(!ahb_.first.has_value() && !ahb_.second.has_value()) |
| << self_name_ << ": please specify both low and high addresses for AHB"; |
| } |
| |
| // Check AXI2. |
| if (axi2_.first.has_value() && axi2_.second.has_value()) { |
| CHECKED_CALL(ConfigureAXI2, *axi2_.first, *axi2_.second); |
| } else { |
| TVM_FFI_ICHECK(!axi2_.first.has_value() && !axi2_.second.has_value()) |
| << self_name_ << ": please specify both low and high addresses for AXI2"; |
| } |
| |
| return true; |
| } |
| |
| bool SimulatorRPCChannel::HandleAHBBusPenalty(string_list& rest) { |
| auto penalty = detail::to_uint(detail::pop_front(rest)); |
| auto interval = to_interval(detail::pop_front(rest)); |
| if (penalty && interval) { |
| CHECKED_CALL(ConfigureAHBBusPenalty, *penalty, *interval); |
| } |
| return static_cast<bool>(penalty) && static_cast<bool>(interval); |
| } |
| |
| bool SimulatorRPCChannel::HandleAHBBusRatio(string_list& rest) { |
| auto ratio = detail::to_float(detail::pop_front(rest)); |
| if (ratio) { |
| CHECKED_CALL(ConfigureAHBBusRatio, *ratio); |
| } |
| return static_cast<bool>(ratio); |
| } |
| |
| bool SimulatorRPCChannel::HandleAHBHighAddr(string_list& rest) { |
| auto addr = detail::to_uint(detail::pop_front(rest)); |
| TVM_FFI_ICHECK(addr) << self_name_ << ": invalid value for AHB high adddress"; |
| if (addr) { |
| ahb_.second = *addr; |
| } |
| return static_cast<bool>(addr); |
| } |
| |
| bool SimulatorRPCChannel::HandleAHBLowAddr(string_list& rest) { |
| auto addr = detail::to_uint(detail::pop_front(rest)); |
| TVM_FFI_ICHECK(addr) << self_name_ << ": invalid value for AHB low adddress"; |
| if (addr) { |
| ahb_.first = *addr; |
| } |
| return static_cast<bool>(addr); |
| } |
| |
| bool SimulatorRPCChannel::HandleAXI2BusPenalty(string_list& rest) { |
| auto penalty = detail::to_uint(detail::pop_front(rest)); |
| auto interval = to_interval(detail::pop_front(rest)); |
| if (penalty && interval) { |
| CHECKED_CALL(ConfigureAXI2BusPenalty, *penalty, *interval); |
| } |
| return static_cast<bool>(penalty) && static_cast<bool>(interval); |
| } |
| |
| bool SimulatorRPCChannel::HandleAXI2BusRatio(string_list& rest) { |
| auto ratio = detail::to_float(detail::pop_front(rest)); |
| if (ratio) { |
| CHECKED_CALL(ConfigureAXI2BusRatio, *ratio); |
| } |
| return static_cast<bool>(ratio); |
| } |
| |
| bool SimulatorRPCChannel::HandleAXI2HighAddr(string_list& rest) { |
| auto addr = detail::to_uint(detail::pop_front(rest)); |
| TVM_FFI_ICHECK(addr) << self_name_ << ": invalid value for AXI2 high adddress"; |
| if (addr) { |
| axi2_.second = *addr; |
| } |
| return static_cast<bool>(addr); |
| } |
| |
| bool SimulatorRPCChannel::HandleAXI2LowAddr(string_list& rest) { |
| auto addr = detail::to_uint(detail::pop_front(rest)); |
| TVM_FFI_ICHECK(addr) << self_name_ << ": invalid value for AXI2 low adddress"; |
| if (addr) { |
| axi2_.first = *addr; |
| } |
| return static_cast<bool>(addr); |
| } |
| |
| bool SimulatorRPCChannel::HandleBuildTag(string_list& rest) { |
| sim_->PrintBuildTag(); |
| return true; |
| } |
| |
| bool SimulatorRPCChannel::HandleBusPenalty(string_list& rest) { |
| auto penalty = detail::to_uint(detail::pop_front(rest)); |
| auto interval = to_interval(detail::pop_front(rest)); |
| if (penalty && interval) { |
| CHECKED_CALL(ConfigureBusPenalty, *penalty, *interval); |
| } |
| return static_cast<bool>(penalty) && static_cast<bool>(interval); |
| } |
| |
| bool SimulatorRPCChannel::HandleBusRatio(string_list& rest) { |
| auto ratio = detail::to_float(detail::pop_front(rest)); |
| if (ratio) { |
| CHECKED_CALL(ConfigureBusRatio, *ratio); |
| } |
| return static_cast<bool>(ratio); |
| } |
| |
| bool SimulatorRPCChannel::HandleBusTrace(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_BUS, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleBypassIdle(string_list& rest) { |
| CHECKED_CALL(ConfigureBypassIdle, true); |
| return true; |
| } |
| |
| bool SimulatorRPCChannel::HandleConnectionTimeout(string_list& rest) { |
| auto time = detail::to_int(detail::pop_front(rest)); |
| if (time) { |
| CHECKED_CALL(ConfigureConnectionTimeout, *time); |
| } |
| return static_cast<bool>(time); |
| } |
| |
| bool SimulatorRPCChannel::HandleCoprocTrace(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_COPROC, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleCoreDump(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(ConfigureCoreDump, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleCosimFile(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(ConfigureCosim, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleDCacheTrace(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_DCACHE, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleDSPClock(string_list& rest) { |
| auto freq = detail::to_uint(detail::pop_front(rest)); |
| if (freq) { |
| CHECKED_CALL(ConfigureCoreFrequency, *freq); |
| } |
| return static_cast<bool>(freq); |
| } |
| |
| bool SimulatorRPCChannel::HandleETMCFGBase(string_list& rest) { |
| auto base = detail::to_uint(detail::pop_front(rest)); |
| if (base) { |
| CHECKED_CALL(ConfigureEtmcfgBase, *base); |
| } |
| return static_cast<bool>(base); |
| } |
| |
| bool SimulatorRPCChannel::HandleGDBServ(string_list& rest) { |
| auto port = detail::to_uint(detail::pop_front(rest)); |
| if (port) { |
| CHECKED_CALL(ConfigureRemoteDebug, *port); |
| debug_port_ = *port; |
| } |
| return static_cast<bool>(port); |
| } |
| |
| bool SimulatorRPCChannel::HandleHVXLength(string_list& rest) { |
| auto len = detail::to_int(detail::pop_front(rest)); |
| if (len) { |
| CHECKED_CALL(ConfigureHVXLength, *len); |
| } |
| return static_cast<bool>(len); |
| } |
| |
| bool SimulatorRPCChannel::HandleICacheTrace(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_ICACHE, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleL2CacheTrace(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_L2CACHE, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleL2CFGBase(string_list& rest) { |
| auto base = detail::to_uint(detail::pop_front(rest)); |
| if (base) { |
| CHECKED_CALL(ConfigureL2cfgBase, *base); |
| } |
| return static_cast<bool>(base); |
| } |
| |
| bool SimulatorRPCChannel::HandleL2TCMBase(string_list& rest) { |
| auto base = detail::to_uint(detail::pop_front(rest)); |
| if (base) { |
| CHECKED_CALL(ConfigureL2tcmBase, *base); |
| } |
| return static_cast<bool>(base); |
| } |
| |
| bool SimulatorRPCChannel::HandleMemFillRand(string_list& rest) { |
| auto seed = detail::to_uint(detail::pop_front(rest)); |
| if (seed) { |
| CHECKED_CALL(ConfigureMemFillRandom, *seed); |
| } |
| return static_cast<bool>(seed); |
| } |
| |
| bool SimulatorRPCChannel::HandleMemFill(string_list& rest) { |
| auto val = detail::to_uint(detail::pop_front(rest)); |
| if (val) { |
| CHECKED_CALL(ConfigureMemFill, *val); |
| } |
| return static_cast<bool>(val); |
| } |
| |
| bool SimulatorRPCChannel::HandleMemTrace(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_MEM, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleNullPtr(string_list& rest) { |
| auto behavior = to_nullptr(detail::pop_front(rest)); |
| if (behavior) { |
| CHECKED_CALL(ConfigureNULLPointerBehavior, *behavior); |
| } |
| return static_cast<bool>(behavior); |
| } |
| |
| bool SimulatorRPCChannel::HandlePacketAnalyze(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(ConfigurePacketAnalysis, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandlePCFilter(string_list& rest) { |
| auto range = detail::to_range<uint64_t, detail::to_uint>(detail::pop_front(rest)); |
| if (range) { |
| CHECKED_CALL(ConfigurePCRangeFilter, range->first, range->second); |
| } |
| return static_cast<bool>(range); |
| } |
| |
| bool SimulatorRPCChannel::HandlePCTraceMin(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_PC_MIN, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandlePCTraceNano(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_PC_NANO, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandlePCTrace(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_PC, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandlePMUStatsFile(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(ConfigurePmuStatisticsFile, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleProfile(string_list& rest) { |
| auto path = detail::pop_front(rest); |
| if (path) { |
| CHECKED_CALL(ConfigureGProf, path->c_str()); |
| } |
| return static_cast<bool>(path); |
| } |
| |
| bool SimulatorRPCChannel::HandleProfileTimeZero(string_list& rest) { |
| auto timezero = detail::to_bool(detail::pop_front(rest)); |
| if (timezero) { |
| CHECKED_CALL(ConfigureProfileMode, *timezero); |
| } |
| return static_cast<bool>(timezero); |
| } |
| |
| bool SimulatorRPCChannel::HandleQuiet(string_list& rest) { |
| sim_->VerboseMode(HEX_QUIET); |
| return true; |
| } |
| |
| bool SimulatorRPCChannel::HandleReconnect(string_list& rest) { |
| if (!debug_port_) { |
| TVM_FFI_THROW(InternalError) << "Reconnect error: --reconnect must be specified " |
| "AFTER --gdbserv <port_num>"; |
| } |
| CHECKED_CALL(ConfigureRemoteDebug, *debug_port_, true); |
| return true; |
| } |
| |
| bool SimulatorRPCChannel::HandleRTOS(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(ConfigureOSAwareness, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleSimErr(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(ConfigureSimStderr, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleSimIn(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(ConfigureSimStdin, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleSimOut(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(ConfigureSimStdout, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleStackStart(string_list& rest) { |
| auto base = detail::to_uint(detail::pop_front(rest)); |
| auto size = detail::to_uint(detail::pop_front(rest)); |
| if (base && size) { |
| CHECKED_CALL(ConfigureStackInfo, *base, *size); |
| } |
| return static_cast<bool>(base) && static_cast<bool>(size); |
| } |
| |
| bool SimulatorRPCChannel::HandleStallTrace(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_STALL, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleStatsFile(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(ConfigureStatisticsFile, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleSubsystemBase(string_list& rest) { |
| auto base = detail::to_uint(detail::pop_front(rest)); |
| if (base) { |
| CHECKED_CALL(ConfigureSubsystemBase, *base); |
| } |
| return static_cast<bool>(base); |
| } |
| |
| bool SimulatorRPCChannel::HandleSymFile(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(AddSymbolFile, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleTCM(string_list& rest) { |
| CHECKED_CALL(ConfigureTimingMode, HEX_TIMING); |
| return true; |
| } |
| |
| bool SimulatorRPCChannel::HandleTCMHighAddr(string_list& rest) { |
| // This option takes an argument, but (the option) is ignored. |
| auto addr = detail::to_uint(detail::pop_front(rest)); |
| return static_cast<bool>(addr); |
| } |
| |
| bool SimulatorRPCChannel::HandleTCMLowAddr(string_list& rest) { |
| auto addr = detail::to_uint(detail::pop_front(rest)); |
| if (addr) { |
| CHECKED_CALL(ConfigureTCM, *addr); |
| } |
| return static_cast<bool>(addr); |
| } |
| |
| bool SimulatorRPCChannel::HandleTimeFilterNS(string_list& rest) { |
| auto range = detail::to_range<uint64_t, detail::to_uint>(detail::pop_front(rest)); |
| if (range) { |
| CHECKED_CALL(ConfigureTimeRangeFilter, range->first, HEX_NANOSEC, range->second, HEX_NANOSEC); |
| } |
| return static_cast<bool>(range); |
| } |
| |
| bool SimulatorRPCChannel::HandleTiming(string_list& rest) { |
| HEXAPI_TimingMode timing_mode = HEX_TIMING; |
| // The argument to --timing is optional. |
| if (should_parse_next(rest)) { |
| if (auto mode = to_timingmode(detail::pop_front(rest))) { |
| timing_mode = *mode; |
| } else { |
| return false; |
| } |
| } |
| CHECKED_CALL(ConfigureTimingMode, timing_mode); |
| return true; |
| } |
| |
| bool SimulatorRPCChannel::HandleUArchTrace(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(SetTracing, HEX_TRACE_UARCH, file->c_str()); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleUseFS(string_list& rest) { |
| auto file = detail::pop_front(rest); |
| if (file) { |
| CHECKED_CALL(ConfigureARFilesystem, &(*file)[0]); |
| } |
| return static_cast<bool>(file); |
| } |
| |
| bool SimulatorRPCChannel::HandleV2PTranslation(string_list& rest) { |
| auto enable = detail::to_bool(detail::pop_front(rest)); |
| if (enable) { |
| CHECKED_CALL(EnableVirtualToPhysicalTranslation, *enable); |
| } |
| return static_cast<bool>(enable); |
| } |
| |
| bool SimulatorRPCChannel::HandleVerbose(string_list& rest) { |
| auto mode = to_verbosemode(detail::pop_front(rest)); |
| if (mode) { |
| sim_->VerboseMode(*mode); |
| } |
| return static_cast<bool>(mode); |
| } |
| |
| bool SimulatorRPCChannel::should_parse_next(const string_list& rest) { |
| if (auto str = detail::front(rest)) { |
| return str->empty() || str->front() != '-'; |
| } |
| return false; |
| } |
| |
| std::optional<HEXAPI_Interval> SimulatorRPCChannel::to_interval(const detail::MaybeString& str) { |
| if (!str) return std::nullopt; |
| |
| if (auto val = detail::to_int(*str)) { |
| switch (*val) { |
| case HEX_MILLISEC: |
| case HEX_MICROSEC: |
| case HEX_NANOSEC: |
| case HEX_PICOSEC: |
| case HEX_PCYCLE: |
| return static_cast<HEXAPI_Interval>(*val); |
| } |
| } |
| |
| return detail::StringSwitch<std::optional<HEXAPI_Interval>>(*str) |
| .Case("MILLISEC", HEX_MILLISEC) |
| .Case("MICROSEC", HEX_MICROSEC) |
| .Case("NANOSEC", HEX_NANOSEC) |
| .Case("PICOSEC", HEX_PICOSEC) |
| .Case("PCYCLE", HEX_PCYCLE) |
| .Default(std::nullopt); |
| } |
| |
| std::optional<HEXAPI_TimingMode> SimulatorRPCChannel::to_timingmode( |
| const detail::MaybeString& str) { |
| if (!str) return std::nullopt; |
| |
| if (auto val = detail::to_int(*str)) { |
| switch (*val) { |
| case HEX_NOTIMING: |
| case HEX_TIMING_NODBC: |
| case HEX_TIMING: |
| case HEX_TIMING_COHERENCY: |
| return static_cast<HEXAPI_TimingMode>(*val); |
| } |
| } |
| |
| return detail::StringSwitch<std::optional<HEXAPI_TimingMode>>(*str) |
| .Case("NOTIMING", HEX_NOTIMING) |
| .Case("TIMING_NODBC", HEX_TIMING_NODBC) |
| .Case("TIMING", HEX_TIMING) |
| .Case("TIMING_COHERENCY", HEX_TIMING_COHERENCY) |
| .Default(std::nullopt); |
| } |
| |
| std::optional<HEXAPI_VerboseMode> SimulatorRPCChannel::to_verbosemode( |
| const detail::MaybeString& str) { |
| if (!str) return std::nullopt; |
| |
| if (auto val = detail::to_int(*str)) { |
| switch (*val) { |
| case HEX_SILENT: |
| case HEX_QUIET: |
| case HEX_NORMAL: |
| case HEX_VERBOSE: |
| case HEX_REALLY_VERBOSE: |
| return static_cast<HEXAPI_VerboseMode>(*val); |
| } |
| } |
| |
| return detail::StringSwitch<std::optional<HEXAPI_VerboseMode>>(*str) |
| .Case("SILENT", HEX_SILENT) |
| .Case("QUIET", HEX_QUIET) |
| .Case("NORMAL", HEX_NORMAL) |
| .Case("VERBOSE", HEX_VERBOSE) |
| .Case("REALLY_VERBOSE", HEX_REALLY_VERBOSE) |
| .Default(std::nullopt); |
| } |
| |
| std::optional<HEXAPI_Nullptr> SimulatorRPCChannel::to_nullptr(const detail::MaybeString& str) { |
| if (!str) return std::nullopt; |
| |
| if (auto val = detail::to_int(*str)) { |
| switch (*val) { |
| case HEX_NULLPTR_IGNORE: |
| case HEX_NULLPTR_WARN: |
| case HEX_NULLPTR_FATAL: |
| case HEX_NULLPTR_PCZERO: |
| return static_cast<HEXAPI_Nullptr>(*val); |
| } |
| } |
| |
| return detail::StringSwitch<std::optional<HEXAPI_Nullptr>>(*str) |
| .Case("IGNORE", HEX_NULLPTR_IGNORE) |
| .Case("WARN", HEX_NULLPTR_WARN) |
| .Case("FATAL", HEX_NULLPTR_FATAL) |
| .Case("PCZERO", HEX_NULLPTR_PCZERO) |
| .Default(std::nullopt); |
| } |
| |
| TVM_FFI_STATIC_INIT_BLOCK() { |
| namespace refl = tvm::ffi::reflection; |
| refl::GlobalDef().def_packed( |
| "tvm.contrib.hexagon.create_hexagon_session", [](ffi::PackedArgs args, ffi::Any* rv) { |
| TVM_FFI_ICHECK(args.size() >= 4) << args.size() << " is less than 4"; |
| |
| auto session_name = args[0].cast<std::string>(); |
| int stack_size = args[1].cast<int>(); |
| auto sim_args = args[2].cast<std::string>(); |
| auto channel = std::make_unique<SimulatorRPCChannel>(stack_size, sim_args); |
| std::shared_ptr<RPCEndpoint> endpoint = |
| RPCEndpoint::Create(std::move(channel), session_name, "", nullptr); |
| std::shared_ptr<RPCSession> session = CreateClientSession(endpoint); |
| *rv = CreateRPCSessionModule(session); |
| }); |
| } |
| |
| } // namespace hexagon |
| } // namespace runtime |
| } // namespace tvm |