[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:
Hanchen Ye 2020-12-09 18:59:00 -06:00
parent a17a1abeda
commit 6b5d682df1
6 changed files with 118 additions and 79 deletions

View File

@ -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"> {

View File

@ -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"))

View File

@ -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,13 +1245,14 @@ 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();
os << "\n";
}
// This branch is prepared for 2020.1 or higher version.
// else {
// Emit bind_storage pragma.
// indent();
// os << "#pragma HLS bind_storage";
@ -1257,17 +1260,18 @@ void ModuleEmitter::emitArray(ArrayOp *op) {
// 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)
if (emitPragmaFlag)
os << "\n";
}
}
/// Pragma operation emitters. (deprecated)
@ -1533,8 +1536,9 @@ void ModuleEmitter::emitFunction(FuncOp func) {
}
}
// TODO: only top function should emit these pragmas.
if (true) {
// 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 port=return bundle=ctrl\n";
@ -1554,6 +1558,7 @@ void ModuleEmitter::emitFunction(FuncOp func) {
}
os << "\n";
}
}
emitBlock(func.front());
reduceIndent();

View File

@ -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

View File

@ -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>

View File

@ -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];