blob: c394984ded8fcf762716a21f98f7cc4fa24d46e3 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/drivers/gpu/msd-vsl-gc/src/instructions.h"
#include "garnet/drivers/gpu/msd-vsl-gc/src/msd_vsl_device.h"
#include "gtest/gtest.h"
#include "helper/platform_device_helper.h"
class TestEvents : public ::testing::Test {
public:
static constexpr uint32_t kAddressSpaceIndex = 1;
void SetUp() override {
device_ = MsdVslDevice::Create(GetTestDeviceHandle());
EXPECT_NE(device_, nullptr);
address_space_owner_ =
std::make_unique<AddressSpaceOwner>(device_->GetBusMapper());
address_space_ = AddressSpace::Create(address_space_owner_.get());
EXPECT_NE(address_space_, nullptr);
device_->page_table_arrays()->AssignAddressSpace(kAddressSpaceIndex,
address_space_.get());
}
protected:
class AddressSpaceOwner : public AddressSpace::Owner {
public:
AddressSpaceOwner(magma::PlatformBusMapper* bus_mapper) : bus_mapper_(bus_mapper) {}
virtual ~AddressSpaceOwner() = default;
magma::PlatformBusMapper* GetBusMapper() override { return bus_mapper_; }
private:
magma::PlatformBusMapper* bus_mapper_;
};
void StopRingbuffer() {
device_->StopRingbuffer();
auto start = std::chrono::high_resolution_clock::now();
while (!device_->IsIdle() &&
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
.count() < 1000) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
auto reg = registers::IdleState::Get().ReadFrom(device_->register_io());
EXPECT_EQ(0x7FFFFFFFu, reg.reg_value());
}
std::unique_ptr<AddressSpaceOwner> address_space_owner_;
std::shared_ptr<AddressSpace> address_space_;
std::unique_ptr<MsdVslDevice> device_;
};
TEST_F(TestEvents, AllocAndFree) {
for (unsigned int i = 0; i < 2; i++) {
uint32_t event_ids[MsdVslDevice::kNumEvents] = {};
for (unsigned int j = 0; j < MsdVslDevice::kNumEvents; j++) {
ASSERT_TRUE(device_->AllocInterruptEvent(&event_ids[j]));
}
// We should have no events left.
uint32_t id;
ASSERT_FALSE(device_->AllocInterruptEvent(&id));
ASSERT_FALSE(device_->CompleteInterruptEvent(0)); // Not yet submitted.
for (unsigned int j = 0; j < MsdVslDevice::kNumEvents; j++) {
ASSERT_TRUE(device_->FreeInterruptEvent(event_ids[j]));
}
ASSERT_FALSE(device_->FreeInterruptEvent(0)); // Already freed.
ASSERT_FALSE(device_->FreeInterruptEvent(100)); // Out of bounds.
}
ASSERT_FALSE(device_->CompleteInterruptEvent(0)); // Not yet allocated.
}
TEST_F(TestEvents, Write) {
// We need to load the address space as we are writing to the ringbuffer directly,
// rather than via SubmitCommandBuffer.
ASSERT_TRUE(device_->LoadInitialAddressSpace(address_space_, kAddressSpaceIndex));
ASSERT_TRUE(device_->StartRingbuffer(address_space_));
auto& ringbuffer = device_->ringbuffer_;
uint64_t rb_gpu_addr;
ASSERT_TRUE(ringbuffer->GetGpuAddress(&rb_gpu_addr));
// Allocate the maximum number of interrupt events, and corresponding semaphores.
uint32_t event_ids[MsdVslDevice::kNumEvents] = {};
std::unique_ptr<magma::PlatformSemaphore> semaphores[MsdVslDevice::kNumEvents] = {};
for (unsigned int i = 0; i < MsdVslDevice::kNumEvents; i++) {
ASSERT_TRUE(device_->AllocInterruptEvent(&event_ids[i]));
auto semaphore = magma::PlatformSemaphore::Create();
ASSERT_NE(semaphore, nullptr);
semaphores[i] = std::move(semaphore);
}
for (unsigned int i = 0; i < 2; i++) {
uint32_t prev_wait_link = ringbuffer->SubtractOffset(kWaitLinkDwords * sizeof(uint32_t));
// We will link to the end of the ringbuffer, where we are adding new events.
uint32_t rb_link_addr = rb_gpu_addr + ringbuffer->tail();
for (unsigned int j = 0; j < MsdVslDevice::kNumEvents; j++) {
auto copy = semaphores[j]->Clone();
ASSERT_NE(copy, nullptr);
ASSERT_TRUE(device_->WriteInterruptEvent(event_ids[j], std::move(copy)));
// Should not be able to submit the same event while it is still pending.
ASSERT_FALSE(device_->WriteInterruptEvent(event_ids[j], nullptr));
}
ASSERT_TRUE(device_->AddRingbufferWaitLink());
// Link the ringbuffer to the newly written events.
uint32_t num_new_rb_instructions = MsdVslDevice::kNumEvents + 2; // Add 2 for WAIT-LINK.
device_->LinkRingbuffer(prev_wait_link, rb_link_addr,
num_new_rb_instructions /* prefetch */);
constexpr uint64_t kTimeoutMs = 5000;
for (unsigned int j = 0; j < MsdVslDevice::kNumEvents; j++) {
ASSERT_EQ(MAGMA_STATUS_OK, semaphores[j]->Wait(kTimeoutMs).get());
}
}
for (unsigned int i = 0; i < MsdVslDevice::kNumEvents; i++) {
ASSERT_TRUE(device_->FreeInterruptEvent(event_ids[i]));
}
StopRingbuffer();
}
TEST_F(TestEvents, Submit) {
for (int i = 0; i < 10; i++) {
uint32_t event_id;
ASSERT_TRUE(device_->AllocInterruptEvent(&event_id));
auto semaphore = magma::PlatformSemaphore::Create();
ASSERT_NE(semaphore, nullptr);
uint16_t prefetch_out;
ASSERT_TRUE(device_->SubmitCommandBuffer(address_space_, kAddressSpaceIndex,
nullptr /* buf */, 0 /* gpu_addr */, 0 /* length */,
event_id, semaphore->Clone(), &prefetch_out));
constexpr uint64_t kTimeoutMs = 1000;
ASSERT_EQ(MAGMA_STATUS_OK, semaphore->Wait(kTimeoutMs).get());
ASSERT_TRUE(device_->FreeInterruptEvent(event_id));
}
{
// The ringbuffer should be in WAIT-LINK until we explicitly stop it.
auto reg = registers::IdleState::Get().ReadFrom(device_->register_io());
EXPECT_NE(0x7FFFFFFFu, reg.reg_value());
}
StopRingbuffer();
}