Skip to content

[SYCL][FPGA] Add compute units library. #2395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
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
51 changes: 51 additions & 0 deletions sycl/include/CL/sycl/INTEL/compute_units.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//==--------- compute_units.hpp - SYCL Compute Units -------*- C++ -*-------==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#pragma once

#include <CL/sycl.hpp>
#include <CL/sycl/detail/defines.hpp>
#include <utility>

__SYCL_INLINE_NAMESPACE(cl) {
namespace sycl {
namespace INTEL {

namespace {
template <class Func, template <std::size_t> class Name, std::size_t index>
class SubmitOneComputeUnit {
public:
SubmitOneComputeUnit(Func &&f, sycl::queue &q) {

q.submit([&](sycl::handler &h) {
h.single_task<Name<index>>([=]() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
h.single_task<Name<index>>([=]() {
h.single_task<Name<index>>([=] {

// verifies that f only takes a single argument
f(std::integral_constant<std::size_t, index>());
});
});
}
};

template <template <std::size_t> class Name, class Func, std::size_t... index>
inline constexpr void ComputeUnitUnroller(sycl::queue &q, Func &&f,
std::index_sequence<index...>) {
(SubmitOneComputeUnit<Func, Name, index>(f, q), ...); // fold expression
}
} // namespace

// N is the number of compute units
// Name is the kernel's name
template <std::size_t N, template <std::size_t ID> class Name, class Func>
constexpr void submit_compute_units(sycl::queue &q, Func &&f) {
std::make_index_sequence<N> indices;
ComputeUnitUnroller<Name>(q, f, indices);
}

} // namespace INTEL
} // namespace sycl
} // __SYCL_INLINE_NAMESPACE(cl)
2 changes: 2 additions & 0 deletions sycl/include/CL/sycl/INTEL/fpga_extensions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
//===----------------------------------------------------------------------===//

#pragma once
#include <CL/sycl/INTEL/compute_units.hpp>
#include <CL/sycl/INTEL/fpga_device_selector.hpp>
#include <CL/sycl/INTEL/fpga_lsu.hpp>
#include <CL/sycl/INTEL/fpga_reg.hpp>
#include <CL/sycl/INTEL/pipe_array.hpp>
#include <CL/sycl/INTEL/pipes.hpp>
57 changes: 57 additions & 0 deletions sycl/include/CL/sycl/INTEL/pipe_array.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//==--------------- pipe_array.hpp - SYCL pipe array --------*- C++ -*------==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// ===--------------------------------------------------------------------=== //

#pragma once

#include <CL/sycl/INTEL/pipes.hpp>
#include <utility>

__SYCL_INLINE_NAMESPACE(cl) {
namespace sycl {
namespace INTEL {

namespace {
template <size_t dim1, size_t... dims> struct VerifierDimLayer {
template <size_t idx1, size_t... idxs> struct VerifierIdxLayer {
static constexpr bool IsValid() {
return idx1 < dim1 &&
(VerifierDimLayer<dims...>::template VerifierIdxLayer<
idxs...>::IsValid());
}
};
};
template <size_t dim> struct VerifierDimLayer<dim> {
template <size_t idx> struct VerifierIdxLayer {
static constexpr bool IsValid() { return idx < dim; }
};
};
} // namespace

template <class Id, typename BaseTy, size_t depth, size_t... dims>
struct PipeArray {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not really an array... But I cannot find a better name.
pipe_array looks more similar to SYCL coding style, otherwise.

PipeArray() = delete;

template <size_t... idxs> struct StructId;

template <size_t... idxs> struct VerifyIndices {
static_assert(sizeof...(idxs) == sizeof...(dims),
"Indexing into a PipeArray requires as many indices as "
"dimensions of the PipeArray.");
static_assert(VerifierDimLayer<dims...>::template VerifierIdxLayer<
idxs...>::IsValid(),
"Index out of bounds");
using VerifiedPipe = pipe<StructId<idxs...>, BaseTy, depth>;
};

template <size_t... idxs>
using PipeAt = typename VerifyIndices<idxs...>::VerifiedPipe;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps get to be closer to std::tuple and even std::array through the tuple interface?
Or just at?
I am not convinced you need to repeat pipe in the name.

};

} // namespace INTEL
} // namespace sycl
} // __SYCL_INLINE_NAMESPACE(cl)
81 changes: 81 additions & 0 deletions sycl/test/fpga_tests/compute_units.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// RUN: %clangxx -fsycl %s -o %t.out
// RUN: %ACC_RUN_PLACEHOLDER %t.out

//==------------ compute_units.cpp - SYCL FPGA compute units test ----------==//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to add compile and run string for the test. You can take one from pipes.cpp

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <CL/sycl/INTEL/fpga_extensions.hpp>

using namespace sycl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad coding style for a tutorial but probably OK in a unit test...


constexpr float kTestData = 555;

constexpr size_t kComputeUnits = 5;
using Pipes = INTEL::PipeArray<class MyPipe, float, 4, kComputeUnits + 1>;

class source_kernel;
class sink_kernel;

template <std::size_t ID> class chain_kernel;

// Write the first piece of data to the pipeline
void SourceKernel(queue &q, float data) {

q.submit([&](handler &h) {
h.single_task<source_kernel>([=]() { Pipes::PipeAt<0>::write(data); });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
h.single_task<source_kernel>([=]() { Pipes::PipeAt<0>::write(data); });
h.single_task<source_kernel>([=] { Pipes::PipeAt<0>::write(data); });

Otherwise, I was thinking to this alternative API:

    h.single_task<source_kernel>([=] { Pipes::write<0>(data); });

});
}

// Grab the data out of the pipeline and return to host in out_data array
void SinkKernel(queue &q, std::array<float, 1> &out_data) {

buffer<float, 1> out_buf(out_data.data(), 1);

q.submit([&](handler &h) {
auto out_accessor = out_buf.get_access<access::mode::write>(h);
h.single_task<sink_kernel>(
[=]() { out_accessor[0] = Pipes::PipeAt<kComputeUnits>::read(); });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[=]() { out_accessor[0] = Pipes::PipeAt<kComputeUnits>::read(); });
[=] { out_accessor[0] = Pipes::PipeAt<kComputeUnits>::read(); });

Otherwise, I was thinking to this alternative API:

        [=] { out_accessor[0] = Pipes::read<kComputeUnits>(); });

});
}

template <int TestNumber> int test_compute_units(queue q) {

std::array<float, 1> out_data = {0};

SourceKernel(q, kTestData);

INTEL::submit_compute_units<kComputeUnits, chain_kernel>(q, [=](auto ID) {
// read from id, not id-1 because the index_sequence starts from 0
float f = Pipes::PipeAt<ID>::read();
Pipes::PipeAt<ID + 1>::write(f);
});

SinkKernel(q, out_data);

if (out_data[0] != kTestData) {
std::cout << "Test: " << TestNumber << "\nResult mismatches " << out_data[0]
<< " Vs expected " << kTestData << std::endl;
return -1;
}

return 0;
}

int main() {
cl::sycl::queue Queue;

if (!Queue.get_device()
.get_info<cl::sycl::info::device::kernel_kernel_pipe_support>()) {
std::cout << "SYCL_INTEL_data_flow_pipes not supported, skipping"
<< std::endl;
return 0;
}

int Result = test_compute_units</*test number*/ 1>(Queue);
return Result;
}