Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/runtime/hexagon/hexagon_htp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/hexagon/hexagon_htp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
27 changes: 15 additions & 12 deletions src/runtime/hexagon/hexagon_hvx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions src/runtime/hexagon/hexagon_hvx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
84 changes: 69 additions & 15 deletions src/runtime/hexagon/hexagon_thread_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<HexagonHtp>();
hvx_ = std::make_unique<HexagonHvx>();
}
Expand All @@ -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<TVMStreamHandle>(i), thread_exit, nullptr);
bool success = Dispatch(reinterpret_cast<TVMStreamHandle>(i), thread_exit, contexts_[i]);
while (!success) {
success = Dispatch(reinterpret_cast<TVMStreamHandle>(i), thread_exit, nullptr);
success = Dispatch(reinterpret_cast<TVMStreamHandle>(i), thread_exit, contexts_[i]);
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand All @@ -185,6 +201,21 @@ const std::vector<TVMStreamHandle> 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<TVMStreamHandle>(i);
}
}
CHECK(false) << "Thread for resource type " << type << " not found";
}

HardwareResourceType HexagonThreadManager::GetResourceTypeForStreamHandle(TVMStreamHandle thread) {
CHECK(hw_resources_.size() > reinterpret_cast<int>(thread))
<< "No thread for handle id exists " << thread;
return hw_resources_[reinterpret_cast<int>(thread)];
}

bool HexagonThreadManager::Dispatch(TVMStreamHandle stream, voidfunc f, void* args) {
unsigned thread = reinterpret_cast<unsigned>(stream);
DLOG(INFO) << "Dispatching to stream " << thread;
Expand Down Expand Up @@ -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<ThreadContext*>(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<ThreadContext*>(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
Expand Down
32 changes: 30 additions & 2 deletions src/runtime/hexagon/hexagon_thread_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ class HexagonThreadManager {
*/
const std::vector<TVMStreamHandle> 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.
Expand Down Expand Up @@ -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);

Expand All @@ -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);
Expand Down Expand Up @@ -203,6 +228,9 @@ class HexagonThreadManager {
//! \brief List of hardware resources
std::vector<HardwareResourceType> 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<HexagonHtp> htp_;
Expand Down
35 changes: 30 additions & 5 deletions tests/cpp-runtime/hexagon/hexagon_thread_manager_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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<TVMStreamHandle>(6);
EXPECT_THROW(thread_manager->GetResourceTypeForStreamHandle(thread), InternalError);
}