diff --git a/src/runtime/hexagon/hexagon_htp.cc b/src/runtime/hexagon/hexagon_htp.cc index 01344ccf4a79..f6c1d2f01ffb 100644 --- a/src/runtime/hexagon/hexagon_htp.cc +++ b/src/runtime/hexagon/hexagon_htp.cc @@ -54,15 +54,19 @@ void HexagonHtp::Acquire() { if (!context_id_) { LOG(FATAL) << "InternalError: HAP_compute_res_acquire failed\n"; } +} + +void HexagonHtp::Release() { HAP_compute_res_release((unsigned int)context_id_); } + +void HexagonHtp::Lock() { + int nErr; + if ((nErr = HAP_compute_res_hmx_lock(context_id_))) { LOG(FATAL) << "InternalError: Unable to lock HTP!"; } } -void HexagonHtp::Release() { - HAP_compute_res_hmx_unlock((unsigned int)context_id_); - HAP_compute_res_release((unsigned int)context_id_); -} +void HexagonHtp::Unlock() { HAP_compute_res_hmx_unlock((unsigned int)context_id_); } } // namespace hexagon } // namespace runtime diff --git a/src/runtime/hexagon/hexagon_htp.h b/src/runtime/hexagon/hexagon_htp.h index b3f0c0b5f71f..928936133dd8 100644 --- a/src/runtime/hexagon/hexagon_htp.h +++ b/src/runtime/hexagon/hexagon_htp.h @@ -44,6 +44,9 @@ class HexagonHtp { //! \brief Prevent move assignment. HexagonHtp& operator=(HexagonHtp&&) = delete; + void Lock(); + void Unlock(); + private: //! \brief Acquisition context ID unsigned int context_id_; diff --git a/src/runtime/hexagon/hexagon_hvx.cc b/src/runtime/hexagon/hexagon_hvx.cc index 0c3160a7d89b..4fc97bf95475 100644 --- a/src/runtime/hexagon/hexagon_hvx.cc +++ b/src/runtime/hexagon/hexagon_hvx.cc @@ -31,25 +31,28 @@ namespace tvm { namespace runtime { namespace hexagon { -HexagonHvx::HexagonHvx() { - // Reserve HVX. - int res = qurt_hvx_reserve(QURT_HVX_RESERVE_ALL_AVAILABLE); - CHECK((res != QURT_HVX_RESERVE_NOT_SUPPORTED) && (res != QURT_HVX_RESERVE_NOT_SUCCESSFUL)) - << "error reserving HVX: " << res; +HexagonHvx::HexagonHvx() { Acquire(); } - // Lock HVX. +HexagonHvx::~HexagonHvx() { Release(); } + +void HexagonHvx::Acquire() { + reserved_count_ = qurt_hvx_reserve(QURT_HVX_RESERVE_ALL); + CHECK(reserved_count_ == QURT_HVX_RESERVE_ALL) << "error reserving HVX: " << reserved_count_; +} + +void HexagonHvx::Release() { + int rel = qurt_hvx_cancel_reserve(); + CHECK(rel == 0) << "error releasing HVX: " << rel; +} + +void HexagonHvx::Lock() { int lck = qurt_hvx_lock(QURT_HVX_MODE_128B); CHECK(lck == 0) << "error locking HVX: " << lck; } -HexagonHvx::~HexagonHvx() { - // Unlock HVX. +void HexagonHvx::Unlock() { int unl = qurt_hvx_unlock(); CHECK(unl == 0) << "error unlocking HVX: " << unl; - - // Release HVX. - int rel = qurt_hvx_cancel_reserve(); - CHECK(rel == 0) << "error releasing HVX: " << rel; } } // namespace hexagon diff --git a/src/runtime/hexagon/hexagon_hvx.h b/src/runtime/hexagon/hexagon_hvx.h index 042977981c99..06394d7c5c7d 100644 --- a/src/runtime/hexagon/hexagon_hvx.h +++ b/src/runtime/hexagon/hexagon_hvx.h @@ -45,7 +45,20 @@ class HexagonHvx { //! \brief Prevent move assignment. HexagonHvx& operator=(HexagonHvx&&) = delete; + //! \brief Lock one HVX to the calling thread. + void Lock(); + + //! \brief Unlock the HVX for the calling thread. + void Unlock(); + + //! \brief Number of HVX units reserved. + int ReservedCount() { return reserved_count_; } + private: + int reserved_count_; + + void Acquire(); + void Release(); }; } // namespace hexagon diff --git a/src/runtime/hexagon/hexagon_thread_manager.cc b/src/runtime/hexagon/hexagon_thread_manager.cc index cf64cdc8b2d0..2fbc231e5781 100644 --- a/src/runtime/hexagon/hexagon_thread_manager.cc +++ b/src/runtime/hexagon/hexagon_thread_manager.cc @@ -38,18 +38,15 @@ HexagonThreadManager::HexagonThreadManager(unsigned num_threads, unsigned thread CHECK_GE(thread_pipe_size_words, MIN_PIPE_SIZE_WORDS); CHECK_LE(thread_pipe_size_words, MAX_PIPE_SIZE_WORDS); - // Support either no resources or a specific set of hardware resources for now. - if (!hw_resources.empty()) { - CHECK((hw_resources.size() == nthreads_) && (nthreads_ == 6) && (hw_resources[0] == DMA_0) && - (hw_resources[1] == HTP_0) && (hw_resources[2] == HVX_0) && (hw_resources[3] == HVX_1) && - (hw_resources[4] == HVX_2) && (hw_resources[5] == HVX_3)) - << "Unsupported hardware resource set"; - } hw_resources_ = hw_resources; + CheckResources(); - if (!hw_resources_.empty()) { + if (create_resource_managers_) { DLOG(INFO) << "Initialize hardware resource managers"; - // Acquisition/locks will be performed on specific threads + // This creates the manager objects, which reserves (acquires) the resources. + // Calls to lock/unlock will be performed on threads dedicated to instances. + // This must be done before spawning threads so we can pass pointers to the + // objects in the thread context. htp_ = std::make_unique(); hvx_ = std::make_unique(); } @@ -74,9 +71,9 @@ HexagonThreadManager::~HexagonThreadManager() { // dispatch a command to each thread to exit with status 0 for (unsigned i = 0; i < nthreads_; i++) { - bool success = Dispatch(reinterpret_cast(i), thread_exit, nullptr); + bool success = Dispatch(reinterpret_cast(i), thread_exit, contexts_[i]); while (!success) { - success = Dispatch(reinterpret_cast(i), thread_exit, nullptr); + success = Dispatch(reinterpret_cast(i), thread_exit, contexts_[i]); } } @@ -121,6 +118,24 @@ HexagonThreadManager::~HexagonThreadManager() { DLOG(INFO) << "Hardware resources released"; } +void HexagonThreadManager::CheckResources() { + create_resource_managers_ = false; + CHECK(hw_resources_.empty() || hw_resources_.size() == nthreads_) + << "Thread count must match resource count"; + if (!hw_resources_.empty()) { + // Ensure that no more than one of each hardware resource is specified + for (int i = 0; i < hw_resources_.size(); i++) { + if (hw_resources_[i] != NONE) { + create_resource_managers_ = true; + for (int j = i + 1; j < hw_resources_.size(); j++) { + CHECK(hw_resources_[i] != hw_resources_[j]) + << "No more than one of each resource type may be specified " << hw_resources_[i]; + } + } + } + } +} + void HexagonThreadManager::SpawnThreads(unsigned thread_stack_size_bytes, unsigned thread_pipe_size_words) { // allocate all stack space for threads @@ -168,7 +183,8 @@ void HexagonThreadManager::SpawnThreads(unsigned thread_stack_size_bytes, next_stack_start += thread_stack_size_bytes; // create the thread - contexts_[i] = new ThreadContext(&pipes_[i], i); + contexts_[i] = new ThreadContext(&pipes_[i], i, hw_resources_.empty() ? NONE : hw_resources_[i], + hvx_.get(), htp_.get()); int rc = qurt_thread_create(&threads_[i], &thread_attr, thread_main, contexts_[i]); CHECK_EQ(rc, QURT_EOK); } @@ -185,6 +201,21 @@ const std::vector HexagonThreadManager::GetStreamHandles() { return out; } +TVMStreamHandle HexagonThreadManager::GetStreamHandleByResourceType(HardwareResourceType type) { + for (unsigned i = 0; i < hw_resources_.size(); i++) { + if (hw_resources_[i] == type) { + return reinterpret_cast(i); + } + } + CHECK(false) << "Thread for resource type " << type << " not found"; +} + +HardwareResourceType HexagonThreadManager::GetResourceTypeForStreamHandle(TVMStreamHandle thread) { + CHECK(hw_resources_.size() > reinterpret_cast(thread)) + << "No thread for handle id exists " << thread; + return hw_resources_[reinterpret_cast(thread)]; +} + bool HexagonThreadManager::Dispatch(TVMStreamHandle stream, voidfunc f, void* args) { unsigned thread = reinterpret_cast(stream); DLOG(INFO) << "Dispatching to stream " << thread; @@ -284,18 +315,41 @@ void HexagonThreadManager::thread_wait_free(void* semaphore) { free(semaphore); } -void HexagonThreadManager::thread_exit(void* status) { - DLOG(INFO) << "thread exiting"; - qurt_thread_exit((uint64_t)status); +void HexagonThreadManager::thread_exit(void* context) { + ThreadContext* tc = static_cast(context); + unsigned index = tc->index; + HardwareResourceType resource_type = tc->resource_type; + + if ((resource_type == HVX_0) || (resource_type == HVX_1) || (resource_type == HVX_2) || + (resource_type == HVX_3)) { + tc->hvx->Unlock(); + DLOG(INFO) << "Thread " << index << " unlocked an HVX instance"; + } else if (resource_type == HTP_0) { + tc->htp->Unlock(); + DLOG(INFO) << "Thread " << index << " unlocked the HTP"; + } + + DLOG(INFO) << "Thread " << index << " exiting"; + qurt_thread_exit((uint64_t)tc->status); } void HexagonThreadManager::thread_main(void* context) { ThreadContext* tc = static_cast(context); unsigned index = tc->index; qurt_pipe_t* mypipe = tc->pipe; + HardwareResourceType resource_type = tc->resource_type; DLOG(INFO) << "Thread " << index << " spawned"; + if ((resource_type == HVX_0) || (resource_type == HVX_1) || (resource_type == HVX_2) || + (resource_type == HVX_3)) { + tc->hvx->Lock(); + DLOG(INFO) << "Thread " << index << " locked an HVX instance"; + } else if (resource_type == HTP_0) { + tc->htp->Lock(); + DLOG(INFO) << "Thread " << index << " locked the HTP"; + } + while (true) { // loop, executing commands from pipe DLOG(INFO) << "Thread " << index << " receiving command"; qurt_pipe_data_t msg = qurt_pipe_receive(mypipe); // blocks if empty diff --git a/src/runtime/hexagon/hexagon_thread_manager.h b/src/runtime/hexagon/hexagon_thread_manager.h index a263cf42dc58..c911d1326a39 100644 --- a/src/runtime/hexagon/hexagon_thread_manager.h +++ b/src/runtime/hexagon/hexagon_thread_manager.h @@ -87,6 +87,18 @@ class HexagonThreadManager { */ const std::vector GetStreamHandles(); + /*! + * \brief Get the spawned threads as stream handles for a resource type. + * \returns stream handle. + */ + TVMStreamHandle GetStreamHandleByResourceType(HardwareResourceType type); + + /*! + * \brief Get the resource type for a stream handle + * \returns stream handle. + */ + HardwareResourceType GetResourceTypeForStreamHandle(TVMStreamHandle thread); + /*! * \brief Non-blocking dispatch of a void function and args on a given thread. * \param thread Stream handle of the thread on which to dispatch the void function. @@ -137,9 +149,22 @@ class HexagonThreadManager { struct ThreadContext { qurt_pipe_t* pipe; unsigned index; - ThreadContext(qurt_pipe_t* pipe, unsigned index) : pipe(pipe), index(index) {} + HardwareResourceType resource_type; + HexagonHvx* hvx; + HexagonHtp* htp; + uint64_t status; + ThreadContext(qurt_pipe_t* pipe, unsigned index, HardwareResourceType resource_type, + HexagonHvx* hvx, HexagonHtp* htp) + : pipe(pipe), index(index), resource_type(resource_type), hvx(hvx), htp(htp), status(0) { + CHECK(resource_type == NONE || (hvx && htp)) + << "Missing resource manager pointer, type: " << resource_type << " hvx: " << hvx + << " htp: " << htp; + } }; + //! \brief Helper function to ensure the set of requested resources is valid. + void CheckResources(); + //! \brief Helper function for the constructor to spawn threads. void SpawnThreads(unsigned thread_stack_size_bytes, unsigned thread_pipe_size_words); @@ -157,7 +182,7 @@ class HexagonThreadManager { static void thread_wait_free(void* semaphore); //! \brief Void function executed by a thread to exit at time of destruction. - static void thread_exit(void* status); + static void thread_exit(void* context); //! \brief Void function executed by each thread as `main`. static void thread_main(void* context); @@ -203,6 +228,9 @@ class HexagonThreadManager { //! \brief List of hardware resources std::vector hw_resources_; + //! \brief Whether or not resource managers should be created + bool create_resource_managers_{false}; + //! \brief HTP hardware resource. // TODO(HWE): Move binding of HTP to a specific thread std::unique_ptr htp_; diff --git a/tests/cpp-runtime/hexagon/hexagon_thread_manager_tests.cc b/tests/cpp-runtime/hexagon/hexagon_thread_manager_tests.cc index d7bf0afed906..af29a428bc69 100644 --- a/tests/cpp-runtime/hexagon/hexagon_thread_manager_tests.cc +++ b/tests/cpp-runtime/hexagon/hexagon_thread_manager_tests.cc @@ -42,7 +42,7 @@ class HexagonThreadManagerTest : public ::testing::Test { const unsigned stack_size{0x4000}; // 16KB }; -TEST_F(HexagonThreadManagerTest, ctor_errors) { +TEST_F(HexagonThreadManagerTest, ctor_edge_cases) { // zero threads ASSERT_THROW(HexagonThreadManager(0, stack_size, pipe_size), InternalError); // too many threads @@ -57,13 +57,16 @@ TEST_F(HexagonThreadManagerTest, ctor_errors) { ASSERT_THROW(HexagonThreadManager(6, stack_size, 0x10000000), InternalError); // hw resources count doesn't match thread count ASSERT_THROW(HexagonThreadManager(6, stack_size, pipe_size, {DMA_0}), InternalError); - // hw resources doesn't match specific supported configuration + // no more than one of each hw resource may be specified + ASSERT_THROW(HexagonThreadManager(4, stack_size, pipe_size, {DMA_0, HTP_0, HVX_0, HVX_0}), + InternalError); + // no more than one of each hw resource may be specified ASSERT_THROW( HexagonThreadManager(6, stack_size, pipe_size, {DMA_0, HTP_0, HVX_0, HVX_1, HVX_2, DMA_0}), InternalError); - // hw resources doesn't match specific supported configuration - ASSERT_THROW(HexagonThreadManager(5, stack_size, pipe_size, {DMA_0, HTP_0, HVX_0, HVX_1, HVX_2}), - InternalError); + // multiple entries for no resource is allowed. + HexagonThreadManager* htm_none = new HexagonThreadManager(2, stack_size, pipe_size, {NONE, NONE}); + delete htm_none; } TEST_F(HexagonThreadManagerTest, init) { @@ -334,3 +337,25 @@ TEST_F(HexagonThreadManagerTest, dispatch_writes) { CHECK_EQ(array[i], truth[i]); } } + +// Validate threads created for hw resources on global manager +TEST_F(HexagonThreadManagerTest, threads_for_resource_types) { + HexagonThreadManager* thread_manager = HexagonDeviceAPI::Global()->ThreadManager(); + TVMStreamHandle thread; + + thread = thread_manager->GetStreamHandleByResourceType(DMA_0); + CHECK(thread_manager->GetResourceTypeForStreamHandle(thread) == DMA_0); + thread = thread_manager->GetStreamHandleByResourceType(HTP_0); + CHECK(thread_manager->GetResourceTypeForStreamHandle(thread) == HTP_0); + thread = thread_manager->GetStreamHandleByResourceType(HVX_0); + CHECK(thread_manager->GetResourceTypeForStreamHandle(thread) == HVX_0); + thread = thread_manager->GetStreamHandleByResourceType(HVX_1); + CHECK(thread_manager->GetResourceTypeForStreamHandle(thread) == HVX_1); + thread = thread_manager->GetStreamHandleByResourceType(HVX_2); + CHECK(thread_manager->GetResourceTypeForStreamHandle(thread) == HVX_2); + thread = thread_manager->GetStreamHandleByResourceType(HVX_3); + CHECK(thread_manager->GetResourceTypeForStreamHandle(thread) == HVX_3); + EXPECT_THROW(thread_manager->GetStreamHandleByResourceType(NONE), InternalError); + thread = reinterpret_cast(6); + EXPECT_THROW(thread_manager->GetResourceTypeForStreamHandle(thread), InternalError); +}