[ConvertToHLSCpp] support defining top function; [EmitHLSCpp] support to emit interface pragmas according to whether the current function is top function or not
This commit is contained in:
parent
a17a1abeda
commit
6b5d682df1
|
@ -15,6 +15,11 @@ def ConvertToHLSCpp : Pass<"convert-to-hlscpp", "ModuleOp"> {
|
|||
}];
|
||||
|
||||
let constructor = "mlir::scalehls::createConvertToHLSCppPass()";
|
||||
|
||||
let options = [
|
||||
Option<"TopFunction", "top-function", "std::string", /*default=*/"\"main\"",
|
||||
"The top function for HLS synthesis">,
|
||||
];
|
||||
}
|
||||
|
||||
def HLSKernelToAffine : Pass<"hlskernel-to-affine", "ModuleOp"> {
|
||||
|
|
|
@ -27,8 +27,13 @@ void ConvertToHLSCpp::runOnOperation() {
|
|||
|
||||
// Set function pragma attributes.
|
||||
func.setAttr("dataflow", b.getBoolAttr(false));
|
||||
if (func.getName() == TopFunction)
|
||||
func.setAttr("top_function", b.getBoolAttr(true));
|
||||
else
|
||||
func.setAttr("top_function", b.getBoolAttr(false));
|
||||
|
||||
// Insert AssignOp.
|
||||
// Insert AssignOp when an arguments or result of ConstantOp are directly
|
||||
// connected to ReturnOp.
|
||||
if (auto returnOp = dyn_cast<ReturnOp>(func.front().getTerminator())) {
|
||||
b.setInsertionPoint(returnOp);
|
||||
unsigned idx = 0;
|
||||
|
@ -77,10 +82,23 @@ void ConvertToHLSCpp::runOnOperation() {
|
|||
operand.replaceAllUsesExcept(arrayOp.getResult(),
|
||||
SmallPtrSet<Operation *, 1>{arrayOp});
|
||||
|
||||
// Set array pragma attributes, default array instance is ram_1p
|
||||
// bram. Other attributes are not set here since they requires more
|
||||
// analysis to be determined.
|
||||
if (!arrayOp.getAttr("interface"))
|
||||
// Set array pragma attributes.
|
||||
// TODO: A known bug is if ArrayOp is connected to ReturnOp through
|
||||
// an AssignOp, it will always not be annotated as interface. This
|
||||
// is acceptable because AssignOp is only used to handle some weird
|
||||
// corner cases that rarely happen.
|
||||
if (!arrayOp.getAttr("interface") &&
|
||||
func.getName() == TopFunction) {
|
||||
// Only if when the array is an block arguments or a returned
|
||||
// value, it will be annotated as interface.
|
||||
bool interfaceFlag =
|
||||
operand.getKind() == Value::Kind::BlockArgument;
|
||||
for (auto &use : arrayOp.getResult().getUses())
|
||||
if (isa<mlir::ReturnOp>(use.getOwner()))
|
||||
interfaceFlag = true;
|
||||
|
||||
arrayOp.setAttr("interface", builder.getBoolAttr(interfaceFlag));
|
||||
} else
|
||||
arrayOp.setAttr("interface", builder.getBoolAttr(false));
|
||||
|
||||
if (!arrayOp.getAttr("storage"))
|
||||
|
|
|
@ -1232,8 +1232,10 @@ void ModuleEmitter::emitAssign(AssignOp *op) {
|
|||
|
||||
void ModuleEmitter::emitArray(ArrayOp *op) {
|
||||
addAlias(op->getOperand(), op->getResult());
|
||||
bool emitPragmaFlag = false;
|
||||
|
||||
if (op->interface()) {
|
||||
emitPragmaFlag = true;
|
||||
|
||||
// Emit interface pragma.
|
||||
indent();
|
||||
|
@ -1243,31 +1245,33 @@ void ModuleEmitter::emitArray(ArrayOp *op) {
|
|||
emitValue(op->getOperand());
|
||||
if (op->interface_mode() == "m_axi") {
|
||||
os << " depth=" << op->interface_depth();
|
||||
os << " offset=slave\n";
|
||||
} else {
|
||||
os << "\n";
|
||||
// os << " storage_type=" << op->storage_type() << "\n";
|
||||
}
|
||||
} else {
|
||||
os << " offset=slave";
|
||||
} else if (op->storage())
|
||||
os << " storage_type=" << op->storage_type();
|
||||
|
||||
// Emit bind_storage pragma.
|
||||
// indent();
|
||||
// os << "#pragma HLS bind_storage";
|
||||
// os << " variable=";
|
||||
// emitValue(op->getOperand());
|
||||
// os << " type=" << op->storage_type();
|
||||
// os << " impl=" << op->storage_impl() << "\n";
|
||||
os << "\n";
|
||||
}
|
||||
// This branch is prepared for 2020.1 or higher version.
|
||||
// else {
|
||||
// Emit bind_storage pragma.
|
||||
// indent();
|
||||
// os << "#pragma HLS bind_storage";
|
||||
// os << " variable=";
|
||||
// emitValue(op->getOperand());
|
||||
// os << " type=" << op->storage_type();
|
||||
// os << " impl=" << op->storage_impl() << "\n";
|
||||
//}
|
||||
|
||||
auto type = op->getOperand().getType().cast<ShapedType>();
|
||||
if (op->partition() && type.hasStaticShape()) {
|
||||
|
||||
// Emit array_partition pragma(s).
|
||||
bool emitFlag = false;
|
||||
for (unsigned dim = 0; dim < type.getRank(); ++dim) {
|
||||
auto partitionType =
|
||||
op->partition_type()[dim].cast<StringAttr>().getValue();
|
||||
if (partitionType != "none") {
|
||||
emitPragmaFlag = true;
|
||||
|
||||
indent();
|
||||
os << "#pragma HLS array_partition";
|
||||
os << " variable=";
|
||||
|
@ -1277,14 +1281,13 @@ void ModuleEmitter::emitArray(ArrayOp *op) {
|
|||
os << " factor="
|
||||
<< op->partition_factor()[dim].cast<IntegerAttr>().getUInt();
|
||||
os << " dim=" << dim + 1 << "\n";
|
||||
emitFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Emit an empty line.
|
||||
if (emitFlag)
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
// Emit an empty line.
|
||||
if (emitPragmaFlag)
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
/// Pragma operation emitters. (deprecated)
|
||||
|
@ -1533,26 +1536,28 @@ void ModuleEmitter::emitFunction(FuncOp func) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: only top function should emit these pragmas.
|
||||
if (true) {
|
||||
indent();
|
||||
os << "#pragma HLS interface s_axilite port=return bundle=ctrl\n";
|
||||
|
||||
// Interface pragma for array ports will be seperately handled since
|
||||
// they are much more complicated.
|
||||
for (auto &port : portList) {
|
||||
// Only top function should emit these pragmas.
|
||||
if (auto topFlag = func.getAttrOfType<BoolAttr>("top_function")) {
|
||||
if (topFlag.getValue()) {
|
||||
indent();
|
||||
os << "#pragma HLS interface s_axilite";
|
||||
os << " port=";
|
||||
os << "#pragma HLS interface s_axilite port=return bundle=ctrl\n";
|
||||
|
||||
// TODO: This is a temporary solution.
|
||||
auto name = getName(port);
|
||||
if (name.front() == "*"[0])
|
||||
name.erase(name.begin());
|
||||
os << name;
|
||||
os << " bundle=ctrl\n";
|
||||
// Interface pragma for array ports will be seperately handled since
|
||||
// they are much more complicated.
|
||||
for (auto &port : portList) {
|
||||
indent();
|
||||
os << "#pragma HLS interface s_axilite";
|
||||
os << " port=";
|
||||
|
||||
// TODO: This is a temporary solution.
|
||||
auto name = getName(port);
|
||||
if (name.front() == "*"[0])
|
||||
name.erase(name.begin());
|
||||
os << name;
|
||||
os << " bundle=ctrl\n";
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
emitBlock(func.front());
|
||||
|
|
|
@ -64,61 +64,64 @@ do
|
|||
# Generate HLS C++ files.
|
||||
for file in ../test/Conversion/HLSKernelToAffine/*
|
||||
do
|
||||
name=${file##*Affine/}
|
||||
name=top-function=${name%.mlir*}
|
||||
output="cpp_src/${file##*Affine/}.cpp"
|
||||
|
||||
case $n in
|
||||
0) scalehls-opt $hta $cth $can $file | scalehls-translate $emit -o $output ;;
|
||||
0) scalehls-opt $hta $cth="$name" $can $file | scalehls-translate $emit -o $output ;;
|
||||
|
||||
# Apply pipeline.
|
||||
1) scalehls-opt $hta $cth "$p0" $can $file | scalehls-translate $emit -o $output ;;
|
||||
2) scalehls-opt $hta $cth "$p1" $can $file | scalehls-translate $emit -o $output ;;
|
||||
3) scalehls-opt $hta $cth "$p2" $can $file | scalehls-translate $emit -o $output ;;
|
||||
4) scalehls-opt $hta $cth "$p3" $can $file | scalehls-translate $emit -o $output ;;
|
||||
1) scalehls-opt $hta $cth="$name" "$p0" $can $file | scalehls-translate $emit -o $output ;;
|
||||
2) scalehls-opt $hta $cth="$name" "$p1" $can $file | scalehls-translate $emit -o $output ;;
|
||||
3) scalehls-opt $hta $cth="$name" "$p2" $can $file | scalehls-translate $emit -o $output ;;
|
||||
4) scalehls-opt $hta $cth="$name" "$p3" $can $file | scalehls-translate $emit -o $output ;;
|
||||
|
||||
# Apply pipeline + array partition.
|
||||
5) scalehls-opt $hta $cth "$p0" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
6) scalehls-opt $hta $cth "$p1" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
7) scalehls-opt $hta $cth "$p2" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
8) scalehls-opt $hta $cth "$p3" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
5) scalehls-opt $hta $cth="$name" "$p0" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
6) scalehls-opt $hta $cth="$name" "$p1" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
7) scalehls-opt $hta $cth="$name" "$p2" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
8) scalehls-opt $hta $cth="$name" "$p3" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
|
||||
# Apply loop perfection + remove variable bound + pipeline.
|
||||
9) scalehls-opt $hta $alp $rvb $cth "$p0" $can $file | scalehls-translate $emit -o $output ;;
|
||||
10) scalehls-opt $hta $alp $rvb $cth "$p1" $can $file | scalehls-translate $emit -o $output ;;
|
||||
11) scalehls-opt $hta $alp $rvb $cth "$p2" $can $file | scalehls-translate $emit -o $output ;;
|
||||
12) scalehls-opt $hta $alp $rvb $cth "$p3" $can $file | scalehls-translate $emit -o $output ;;
|
||||
9) scalehls-opt $hta $alp $rvb $cth="$name" "$p0" $can $file | scalehls-translate $emit -o $output ;;
|
||||
10) scalehls-opt $hta $alp $rvb $cth="$name" "$p1" $can $file | scalehls-translate $emit -o $output ;;
|
||||
11) scalehls-opt $hta $alp $rvb $cth="$name" "$p2" $can $file | scalehls-translate $emit -o $output ;;
|
||||
12) scalehls-opt $hta $alp $rvb $cth="$name" "$p3" $can $file | scalehls-translate $emit -o $output ;;
|
||||
|
||||
# Apply loop perfection + remove variable bound + pipeline + array partition.
|
||||
13) scalehls-opt $hta $alp $rvb $cth "$p0" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
14) scalehls-opt $hta $alp $rvb $cth "$p1" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
15) scalehls-opt $hta $alp $rvb $cth "$p2" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
16) scalehls-opt $hta $alp $rvb $cth "$p3" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
13) scalehls-opt $hta $alp $rvb $cth="$name" "$p0" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
14) scalehls-opt $hta $alp $rvb $cth="$name" "$p1" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
15) scalehls-opt $hta $alp $rvb $cth="$name" "$p2" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
16) scalehls-opt $hta $alp $rvb $cth="$name" "$p3" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
|
||||
# Apply ... + 1st-level loop tiling + pipeline.
|
||||
17) scalehls-opt $hta $alp $rvb "$t1s2" $cth "$p1" "$u1" $can $file | scalehls-translate $emit -o $output ;;
|
||||
18) scalehls-opt $hta $alp $rvb "$t1s4" $cth "$p1" "$u1" $can $file | scalehls-translate $emit -o $output ;;
|
||||
19) scalehls-opt $hta $alp $rvb "$t1s8" $cth "$p1" "$u1" $can $file | scalehls-translate $emit -o $output ;;
|
||||
17) scalehls-opt $hta $alp $rvb "$t1s2" $cth="$name" "$p1" "$u1" $can $file | scalehls-translate $emit -o $output ;;
|
||||
18) scalehls-opt $hta $alp $rvb "$t1s4" $cth="$name" "$p1" "$u1" $can $file | scalehls-translate $emit -o $output ;;
|
||||
19) scalehls-opt $hta $alp $rvb "$t1s8" $cth="$name" "$p1" "$u1" $can $file | scalehls-translate $emit -o $output ;;
|
||||
|
||||
# Apply ... + 1st-level loop tiling + pipeline + array partition.
|
||||
20) scalehls-opt $hta $alp $rvb "$t1s2" $cth "$p1" "$u1" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
21) scalehls-opt $hta $alp $rvb "$t1s4" $cth "$p1" "$u1" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
22) scalehls-opt $hta $alp $rvb "$t1s8" $cth "$p1" "$u1" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
20) scalehls-opt $hta $alp $rvb "$t1s2" $cth="$name" "$p1" "$u1" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
21) scalehls-opt $hta $alp $rvb "$t1s4" $cth="$name" "$p1" "$u1" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
22) scalehls-opt $hta $alp $rvb "$t1s8" $cth="$name" "$p1" "$u1" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
|
||||
# Apply ... + 2nd-level loop tiling + pipeline.
|
||||
23) scalehls-opt $hta $alp $rvb "$t2s2" $cth "$p2" "$u2" $can $file | scalehls-translate $emit -o $output ;;
|
||||
24) scalehls-opt $hta $alp $rvb "$t2s4" $cth "$p2" "$u2" $can $file | scalehls-translate $emit -o $output ;;
|
||||
25) scalehls-opt $hta $alp $rvb "$t2s8" $cth "$p2" "$u2" $can $file | scalehls-translate $emit -o $output ;;
|
||||
23) scalehls-opt $hta $alp $rvb "$t2s2" $cth="$name" "$p2" "$u2" $can $file | scalehls-translate $emit -o $output ;;
|
||||
24) scalehls-opt $hta $alp $rvb "$t2s4" $cth="$name" "$p2" "$u2" $can $file | scalehls-translate $emit -o $output ;;
|
||||
25) scalehls-opt $hta $alp $rvb "$t2s8" $cth="$name" "$p2" "$u2" $can $file | scalehls-translate $emit -o $output ;;
|
||||
|
||||
# Apply ... + 2nd-level loop tiling + pipeline + array partition.
|
||||
26) scalehls-opt $hta $alp $rvb "$t2s2" $cth "$p2" "$u2" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
27) scalehls-opt $hta $alp $rvb "$t2s4" $cth "$p2" "$u2" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
28) scalehls-opt $hta $alp $rvb "$t2s8" $cth "$p2" "$u2" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
26) scalehls-opt $hta $alp $rvb "$t2s2" $cth="$name" "$p2" "$u2" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
27) scalehls-opt $hta $alp $rvb "$t2s4" $cth="$name" "$p2" "$u2" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
28) scalehls-opt $hta $alp $rvb "$t2s8" $cth="$name" "$p2" "$u2" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
|
||||
# Apply ... + 3rd-level loop tiling + pipeline.
|
||||
29) scalehls-opt $hta $alp $rvb "$t3s2" $cth "$p3" "$u3" $can $file | scalehls-translate $emit -o $output ;;
|
||||
30) scalehls-opt $hta $alp $rvb "$t3s4" $cth "$p3" "$u3" $can $file | scalehls-translate $emit -o $output ;;
|
||||
29) scalehls-opt $hta $alp $rvb "$t3s2" $cth="$name" "$p3" "$u3" $can $file | scalehls-translate $emit -o $output ;;
|
||||
30) scalehls-opt $hta $alp $rvb "$t3s4" $cth="$name" "$p3" "$u3" $can $file | scalehls-translate $emit -o $output ;;
|
||||
|
||||
# Apply ... + 3rd-level loop tiling + pipeline + array partition.
|
||||
31) scalehls-opt $hta $cth $alp $rvb "$t3s2" "$p3" "$u3" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
32) scalehls-opt $hta $cth $alp $rvb "$t3s4" "$p3" "$u3" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
31) scalehls-opt $hta $alp $rvb "$t3s2" $cth="$name" "$p3" "$u3" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
32) scalehls-opt $hta $alp $rvb "$t3s4" $cth="$name" "$p3" "$u3" $par $can $file | scalehls-translate $emit -o $output ;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// RUN: scalehls-opt -convert-to-hlscpp %s | FileCheck %s
|
||||
// RUN: scalehls-opt -convert-to-hlscpp="top-function=test_conversion" %s | FileCheck %s
|
||||
|
||||
// CHECK-LABEL: func @test_conversion(
|
||||
// CHECK-SAME: %arg0: f32, %arg1: memref<16xf32>) -> (f32, memref<16xf32>, i32, tensor<2x2xi32>) attributes {dataflow = false} {
|
||||
// CHECK-SAME: %arg0: f32, %arg1: memref<16xf32>) -> (f32, memref<16xf32>, i32, tensor<2x2xi32>) attributes {dataflow = false, top_function = true} {
|
||||
func @test_conversion(%arg0: f32, %arg1: memref<16xf32>) -> (f32, memref<16xf32>, i32, tensor<2x2xi32>) {
|
||||
// CHECK: %[[VAL_0:.*]] = "hlscpp.array"(%[[ARG_1:.*]]) {interface = false, partition = false, storage = false} : (memref<16xf32>) -> memref<16xf32>
|
||||
// CHECK: %[[VAL_0:.*]] = "hlscpp.array"(%[[ARG_1:.*]]) {interface = true, partition = false, storage = false} : (memref<16xf32>) -> memref<16xf32>
|
||||
%c11_i32 = constant 11 : i32
|
||||
%cst = constant dense<[[11, 0], [0, -42]]> : tensor<2x2xi32>
|
||||
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
// RUN: scalehls-translate -emit-hlscpp %s | FileCheck %s
|
||||
|
||||
func @callee(%arg0: index, %arg1: memref<16xindex>) -> (index, index, memref<16xindex>, memref<16xindex>) {
|
||||
func @callee(%arg0: index, %arg1: memref<16xindex>) -> (index, index, memref<16xindex>, memref<16xindex>) attributes {top_function = false} {
|
||||
// CHECK-NOT: #pragma HLS interface s_axilite port=return bundle=ctrl
|
||||
// CHECK-NOT: #pragma HLS interface s_axilite port=val0 bundle=ctrl
|
||||
// CHECK-NOT: #pragma HLS interface s_axilite port=val2 bundle=ctrl
|
||||
// CHECK-NOT: #pragma HLS interface s_axilite port=val3 bundle=ctrl
|
||||
|
||||
%0 = affine.load %arg1[%arg0] : memref<16xindex>
|
||||
%1 = affine.load %arg1[%arg0 + 1] : memref<16xindex>
|
||||
%2 = alloc() : memref<16xindex>
|
||||
|
@ -8,7 +13,10 @@ func @callee(%arg0: index, %arg1: memref<16xindex>) -> (index, index, memref<16x
|
|||
return %0, %1, %2, %3 : index, index, memref<16xindex>, memref<16xindex>
|
||||
}
|
||||
|
||||
func @test_call(%arg0: index, %arg1: memref<16xindex>) -> (index, memref<16xindex>) {
|
||||
func @test_call(%arg0: index, %arg1: memref<16xindex>) -> (index, memref<16xindex>) attributes {top_function = true} {
|
||||
// CHECK: #pragma HLS interface s_axilite port=return bundle=ctrl
|
||||
// CHECK: #pragma HLS interface s_axilite port=val6 bundle=ctrl
|
||||
// CHECK: #pragma HLS interface s_axilite port=val8 bundle=ctrl
|
||||
|
||||
// CHECK: int val10;
|
||||
// CHECK: int val11[16];
|
||||
|
|
Loading…
Reference in New Issue