blob: af29a428bc69cdff2d4c315022d2a09592c7272a [file] [log] [blame]
/*
* 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 <gtest/gtest.h>
#include <tvm/runtime/logging.h>
#include "../src/runtime/hexagon/hexagon_device_api.h"
#include "../src/runtime/hexagon/hexagon_thread_manager.h"
using namespace tvm::runtime;
using namespace tvm::runtime::hexagon;
class HexagonThreadManagerTest : public ::testing::Test {
protected:
void SetUp() override {
// Create with no hardware resources so we don't conflict with session HexagonThreadManager
htm = new HexagonThreadManager(threads, stack_size, pipe_size);
streams = htm->GetStreamHandles();
}
void TearDown() override { delete htm; }
HexagonThreadManager* htm{nullptr};
std::vector<TVMStreamHandle> streams;
int answer{0};
const unsigned threads{6};
const unsigned pipe_size{100};
const unsigned stack_size{0x4000}; // 16KB
};
TEST_F(HexagonThreadManagerTest, ctor_edge_cases) {
// zero threads
ASSERT_THROW(HexagonThreadManager(0, stack_size, pipe_size), InternalError);
// too many threads
ASSERT_THROW(HexagonThreadManager(0x10000000, stack_size, pipe_size), InternalError);
// stack too small
ASSERT_THROW(HexagonThreadManager(6, 0, pipe_size), InternalError);
// stack too big
ASSERT_THROW(HexagonThreadManager(6, 0x10000000, pipe_size), InternalError);
// pipe too small
ASSERT_THROW(HexagonThreadManager(6, stack_size, 9), InternalError);
// pipe too big
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);
// 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);
// 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) {
CHECK(htm != nullptr);
CHECK_EQ(streams.size(), threads);
}
void get_the_answer(void* answer) { *reinterpret_cast<int*>(answer) = 42; }
TEST_F(HexagonThreadManagerTest, dispatch) {
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->Start();
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, dispatch_wait) {
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, wait_signal) {
htm->Wait(streams[0], 0);
htm->Signal(streams[1], 0);
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, re_signal) {
htm->Wait(streams[0], 0);
htm->Signal(streams[1], 0);
htm->Signal(streams[1], 0);
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, re_wait) {
htm->Wait(streams[0], 0);
htm->Signal(streams[1], 0);
htm->Wait(streams[0], 0);
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, wait_signal_x2) {
htm->Wait(streams[0], 0);
htm->Signal(streams[1], 0);
htm->Wait(streams[0], 1);
htm->Signal(streams[1], 1);
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, signal_wait) {
htm->Signal(streams[1], 0);
htm->Wait(streams[0], 0);
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, sync_from_to) {
htm->SyncFromTo(streams[1], streams[0]);
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, sync_from_to_self) {
htm->SyncFromTo(streams[0], streams[0]);
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, sync_from_to_x2) {
htm->SyncFromTo(streams[0], streams[1]);
htm->SyncFromTo(streams[1], streams[0]);
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, sync_from_to_all) {
htm->SyncFromTo(streams[5], streams[4]);
htm->SyncFromTo(streams[4], streams[3]);
htm->SyncFromTo(streams[3], streams[2]);
htm->SyncFromTo(streams[2], streams[1]);
htm->SyncFromTo(streams[1], streams[0]);
htm->Dispatch(streams[0], get_the_answer, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
TEST_F(HexagonThreadManagerTest, pipe_fill) {
// fill the pipe
for (int i = 0; i < pipe_size; ++i) {
htm->Dispatch(streams[0], get_the_answer, &answer);
}
htm->WaitOnThreads();
CHECK_EQ(answer, 42);
}
// TODO(HWE): Create a temporary thread manager with a smaller pipe for this test
TEST_F(HexagonThreadManagerTest, pipe_overflow) {
// fill the pipe
for (int i = 0; i < pipe_size; ++i) {
htm->Dispatch(streams[0], get_the_answer, &answer);
}
// overflow the pipe
bool space = htm->Dispatch(streams[0], get_the_answer, &answer);
CHECK_EQ(space, false);
}
void increment(void* voidptr) {
int* intptr = reinterpret_cast<int*>(voidptr);
*intptr = *intptr + 1;
}
TEST_F(HexagonThreadManagerTest, producer_consumer) {
htm->Dispatch(streams[5], increment, &answer);
htm->SyncFromTo(streams[5], streams[4]);
htm->Dispatch(streams[4], increment, &answer);
htm->SyncFromTo(streams[4], streams[3]);
htm->Dispatch(streams[3], increment, &answer);
htm->SyncFromTo(streams[3], streams[2]);
htm->Dispatch(streams[2], increment, &answer);
htm->SyncFromTo(streams[2], streams[1]);
htm->Dispatch(streams[1], increment, &answer);
htm->SyncFromTo(streams[1], streams[0]);
htm->Dispatch(streams[0], increment, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 6);
}
TEST_F(HexagonThreadManagerTest, producer_consumer_signal_wait) {
htm->Wait(streams[0], 0);
htm->Wait(streams[1], 1);
htm->Wait(streams[2], 2);
htm->Wait(streams[3], 3);
htm->Wait(streams[4], 4);
htm->Dispatch(streams[5], increment, &answer);
htm->Signal(streams[5], 4);
htm->Dispatch(streams[4], increment, &answer);
htm->Signal(streams[4], 3);
htm->Dispatch(streams[3], increment, &answer);
htm->Signal(streams[3], 2);
htm->Dispatch(streams[2], increment, &answer);
htm->Signal(streams[2], 1);
htm->Dispatch(streams[1], increment, &answer);
htm->Signal(streams[1], 0);
htm->Dispatch(streams[0], increment, &answer);
htm->WaitOnThreads();
CHECK_EQ(answer, 6);
}
struct ToAppend {
std::vector<int>* arr;
int value;
ToAppend(std::vector<int>* addr, int value) : arr(addr), value(value){};
};
void append(void* toappend) {
ToAppend* cmd = reinterpret_cast<ToAppend*>(toappend);
cmd->arr->push_back(cmd->value);
}
TEST_F(HexagonThreadManagerTest, thread_order) {
std::vector<int> arr;
ToAppend cmd0(&arr, 0);
htm->Dispatch(streams[0], append, &cmd0);
htm->SyncFromTo(streams[0], streams[1]);
ToAppend cmd1(&arr, 1);
htm->Dispatch(streams[1], append, &cmd1);
htm->SyncFromTo(streams[1], streams[2]);
ToAppend cmd2(&arr, 2);
htm->Dispatch(streams[2], append, &cmd2);
htm->SyncFromTo(streams[2], streams[3]);
ToAppend cmd3(&arr, 3);
htm->Dispatch(streams[3], append, &cmd3);
htm->SyncFromTo(streams[3], streams[4]);
ToAppend cmd4(&arr, 4);
htm->Dispatch(streams[4], append, &cmd4);
htm->SyncFromTo(streams[4], streams[5]);
ToAppend cmd5(&arr, 5);
htm->Dispatch(streams[5], append, &cmd5);
htm->WaitOnThreads();
for (int i = 0; i < threads; ++i) {
CHECK_EQ(arr[i], i);
}
}
TEST_F(HexagonThreadManagerTest, thread_order_signal_wait) {
std::vector<int> arr;
htm->Wait(streams[1], 1);
htm->Wait(streams[2], 2);
htm->Wait(streams[3], 3);
htm->Wait(streams[4], 4);
htm->Wait(streams[5], 5);
ToAppend cmd0(&arr, 0);
htm->Dispatch(streams[0], append, &cmd0);
htm->Signal(streams[0], 1);
ToAppend cmd1(&arr, 1);
htm->Dispatch(streams[1], append, &cmd1);
htm->Signal(streams[1], 2);
ToAppend cmd2(&arr, 2);
htm->Dispatch(streams[2], append, &cmd2);
htm->Signal(streams[2], 3);
ToAppend cmd3(&arr, 3);
htm->Dispatch(streams[3], append, &cmd3);
htm->Signal(streams[3], 4);
ToAppend cmd4(&arr, 4);
htm->Dispatch(streams[4], append, &cmd4);
htm->Signal(streams[4], 5);
ToAppend cmd5(&arr, 5);
htm->Dispatch(streams[5], append, &cmd5);
htm->WaitOnThreads();
for (int i = 0; i < threads; ++i) {
CHECK_EQ(arr[i], i);
}
}
struct ToWrite {
int* addr;
int value;
ToWrite(int* addr, int value) : addr(addr), value(value){};
};
void thread_write_val(void* towrite) {
ToWrite* cmd = reinterpret_cast<ToWrite*>(towrite);
*(cmd->addr) = cmd->value;
delete cmd;
}
TEST_F(HexagonThreadManagerTest, dispatch_writes) {
std::vector<int> array;
std::vector<int> truth;
array.resize(streams.size());
truth.resize(streams.size());
for (int i = 0; i < streams.size(); i++) {
int val = i * 2;
ToWrite* cmd = new ToWrite(&array[i], val);
htm->Dispatch(streams[i], thread_write_val, cmd);
truth[i] = val;
}
htm->Start();
htm->WaitOnThreads();
for (int i = 0; i < streams.size(); i++) {
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);
}