From 53550db33a39199969480a17fd5336c02b5e5aa6 Mon Sep 17 00:00:00 2001 From: Hanchen Ye Date: Sun, 20 Dec 2020 17:52:56 -0600 Subject: [PATCH] [EmitHLSCpp] support to emit line number and schedule information; [HLSCpp] update all code related to storage_type attribute to align with Vivado 2019.1 settings --- README.md | 4 +- include/Dialect/HLSCpp/Attributes.td | 38 +++-- include/Dialect/HLSCpp/StructureOps.td | 6 +- lib/Analysis/QoREstimation.cpp | 45 +++--- lib/Conversion/ConvertToHLSCpp.cpp | 11 +- lib/EmitHLSCpp/EmitHLSCpp.cpp | 150 ++++++++++++------ .../ConvertToHLSCpp/test_conversion.mlir | 4 +- 7 files changed, 160 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 0e78308..732425c 100644 --- a/README.md +++ b/README.md @@ -40,12 +40,12 @@ $ # Benchmark generation, dataflow-level optimization, and bufferization. $ benchmark-gen -type "cnn" -config "config/cnn-config.ini" -number 1 \ | scalehls-opt -legalize-dataflow -split-function \ -hlskernel-bufferize -hlskernel-to-affine -func-bufferize -canonicalize -$ + $ # HLSKernel lowering, loop-level and pragma-level optimizations, and performance estimation. $ scalehls-opt test/Conversion/HLSKernelToAffine/test_gemm.mlir -hlskernel-to-affine \ -affine-loop-perfection -remove-var-loop-bound -partial-affine-loop-tile="tile-level=1 tile-size=4" \ -convert-to-hlscpp="top-function=test_gemm" -loop-pipelining="pipeline-level=1" \ - -store-op-forward -simplify-memref-access -array-partition -canonicalize \ + -store-op-forward -simplify-memref-access -array-partition -cse -canonicalize \ -qor-estimation="target-spec=config/target-spec.ini" $ # HLS C++ code generation. diff --git a/include/Dialect/HLSCpp/Attributes.td b/include/Dialect/HLSCpp/Attributes.td index dc1dac1..b87eebf 100644 --- a/include/Dialect/HLSCpp/Attributes.td +++ b/include/Dialect/HLSCpp/Attributes.td @@ -33,22 +33,32 @@ def PartitionTypeArrayAttr : TypedArrayAttrBase {} // Pragma bind_storage Attributes //===----------------------------------------------------------------------===// -def StorageImplAttr : StrEnumAttr<"StorageImpl", "", [ - StrEnumAttrCase<"bram", 0>, - StrEnumAttrCase<"lutram", 1>, - StrEnumAttrCase<"uram", 2>, - StrEnumAttrCase<"srl", 3> -]> { - let cppNamespace = "::mlir::scalehls::hlscpp"; -} +// def StorageImplAttr : StrEnumAttr<"StorageImpl", "", [ +// StrEnumAttrCase<"bram", 0>, +// StrEnumAttrCase<"lutram", 1>, +// StrEnumAttrCase<"uram", 2>, +// StrEnumAttrCase<"srl", 3> +// ]> { +// let cppNamespace = "::mlir::scalehls::hlscpp"; +// } +// def StorageTypeAttr : StrEnumAttr<"StorageType", "", [ +// StrEnumAttrCase<"fifo", 0>, +// StrEnumAttrCase<"ram_1p", 1>, +// StrEnumAttrCase<"ram_1wnr", 2>, +// StrEnumAttrCase<"ram_2p", 3>, +// StrEnumAttrCase<"ram_s2p", 4>, +// StrEnumAttrCase<"ram_t2p", 5> +// ]> { +// let cppNamespace = "::mlir::scalehls::hlscpp"; +// } + +// Currently we only support bram/lut based memory instance. def StorageTypeAttr : StrEnumAttr<"StorageType", "", [ - StrEnumAttrCase<"fifo", 0>, - StrEnumAttrCase<"ram_1p", 1>, - StrEnumAttrCase<"ram_1wnr", 2>, - StrEnumAttrCase<"ram_2p", 3>, - StrEnumAttrCase<"ram_s2p", 4>, - StrEnumAttrCase<"ram_t2p", 5> + StrEnumAttrCase<"ram_1p_bram", 0>, + StrEnumAttrCase<"ram_2p_bram", 1>, + StrEnumAttrCase<"ram_s2p_bram", 2>, + StrEnumAttrCase<"ram_t2p_bram", 3> ]> { let cppNamespace = "::mlir::scalehls::hlscpp"; } diff --git a/include/Dialect/HLSCpp/StructureOps.td b/include/Dialect/HLSCpp/StructureOps.td index 83ed55c..5e07991 100644 --- a/include/Dialect/HLSCpp/StructureOps.td +++ b/include/Dialect/HLSCpp/StructureOps.td @@ -33,15 +33,15 @@ def ArrayOp : HLSCppOp<"array", [SameOperandsAndResultType]> { // Interface-related attributes. DefaultValuedAttr : $interface, DefaultValuedAttr : $interface_mode, - DefaultValuedAttr : $interface_depth, + DefaultValuedAttr : $interface_depth, // BindStorage-related attributes. DefaultValuedAttr : $storage, - DefaultValuedAttr : $storage_type, - DefaultValuedAttr : $storage_impl, + DefaultValuedAttr : $storage_type, // ArrayPartition-related attributes. DefaultValuedAttr : $partition, + DefaultValuedAttr : $partition_num, DefaultValuedAttr : $partition_type, DefaultValuedAttr : $partition_factor ); diff --git a/lib/Analysis/QoREstimation.cpp b/lib/Analysis/QoREstimation.cpp index cc024a3..f2821f3 100644 --- a/lib/Analysis/QoREstimation.cpp +++ b/lib/Analysis/QoREstimation.cpp @@ -245,9 +245,8 @@ unsigned HLSCppEstimator::getLoadStoreSchedule(Operation *op, unsigned begin) { setAttrValue(op, "partition_index", partitionIdx); auto arrayOp = getArrayOp(op); - auto partitionNum = - max((unsigned)1, getUIntAttrValue(arrayOp, "partition_num")); - auto storageType = getStrAttrValue(arrayOp, "storage_type"); + auto partitionNum = arrayOp.partition_num(); + auto storageType = arrayOp.storage_type(); // Try to avoid memory port violation until a legal schedule is found. Since // an infinite length schedule cannot be generated, this while loop can be @@ -265,15 +264,16 @@ unsigned HLSCppEstimator::getLoadStoreSchedule(Operation *op, unsigned begin) { unsigned wrPort = 0; unsigned rdwrPort = 0; - if (storageType == "ram_s2p") - rdPort = 1, wrPort = 1; - else if (storageType == "ram_2p" || storageType == "ram_t2p") - rdwrPort = 2; - else if (storageType == "ram_1p") + if (storageType == "ram_1p_bram") rdwrPort = 1; - else { + else if (storageType == "ram_2p_bram") + rdPort = 1, rdwrPort = 1; + else if (storageType == "ram_s2p_bram") + rdPort = 1, wrPort = 1; + else if (storageType == "ram_t2p_bram") + rdwrPort = 2; + else rdwrPort = 2; - } memPort.push_back(PortInfo(rdPort, wrPort, rdwrPort)); } @@ -346,7 +346,6 @@ unsigned HLSCppEstimator::getLoadStoreSchedule(Operation *op, unsigned begin) { // else // minII = getUIntAttrValue(op, "schedule_end") - // getUIntAttrValue(op, "schedule_begin"); - // II = max(II, minII); // }); // return II; @@ -359,9 +358,8 @@ unsigned HLSCppEstimator::getResMinII(LoadStoresMap &map) { for (auto &pair : map) { auto arrayOp = getArrayOp(pair.first); // Partition number should at least be 1. - auto partitionNum = - max((unsigned)1, getUIntAttrValue(arrayOp, "partition_num")); - auto storageType = getStrAttrValue(arrayOp, "storage_type"); + auto partitionNum = arrayOp.partition_num(); + auto storageType = arrayOp.storage_type(); SmallVector readNum; SmallVector writeNum; @@ -376,12 +374,15 @@ unsigned HLSCppEstimator::getResMinII(LoadStoresMap &map) { auto partitionIdx = getIntAttrValue(op, "partition_index"); if (partitionIdx == -1) { unsigned accessNum = 2; - if (storageType == "ram_s2p") + if (storageType == "ram_1p_bram") accessNum = 1; - else if (storageType == "ram_1p") + else if (storageType == "ram_2p_bram") accessNum = 1; - else if (storageType == "ram_2p" || storageType == "ram_t2p" || - storageType == "") + else if (storageType == "ram_s2p_bram") + accessNum = 1; + else if (storageType == "ram_t2p_bram") + accessNum = 1; + else accessNum = 2; // The rationale here is an undetermined partition access will introduce @@ -401,15 +402,15 @@ unsigned HLSCppEstimator::getResMinII(LoadStoresMap &map) { } unsigned minII = 1; - if (storageType == "ram_s2p") + if (storageType == "ram_s2p_bram") minII = max({minII, *std::max_element(readNum.begin(), readNum.end()), *std::max_element(writeNum.begin(), writeNum.end())}); - else if (storageType == "ram_1p") + else if (storageType == "ram_1p_bram") for (unsigned i = 0, e = partitionNum; i < e; ++i) minII = max(minII, readNum[i] + writeNum[i]); - else if (storageType == "ram_2p" || storageType == "ram_t2p" || + else if (storageType == "ram_2p_bram" || storageType == "ram_t2p_bram" || storageType == "") for (unsigned i = 0, e = partitionNum; i < e; ++i) minII = max(minII, (readNum[i] + writeNum[i] + 1) / 2); @@ -672,7 +673,7 @@ void HLSCppEstimator::estimateFunc() { // setScheduleValue(op, latency - end, latency - begin); // }); } else - setAttrValue(func, "latency", -1); + setAttrValue(func, "latency", std::string("unknown")); } //===----------------------------------------------------------------------===// diff --git a/lib/Conversion/ConvertToHLSCpp.cpp b/lib/Conversion/ConvertToHLSCpp.cpp index f771bc3..db9004b 100644 --- a/lib/Conversion/ConvertToHLSCpp.cpp +++ b/lib/Conversion/ConvertToHLSCpp.cpp @@ -86,7 +86,7 @@ void ConvertToHLSCpp::runOnOperation() { // 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) { + if (!arrayOp.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 = @@ -99,10 +99,13 @@ void ConvertToHLSCpp::runOnOperation() { } else arrayOp.setAttr("interface", builder.getBoolAttr(false)); - if (!arrayOp.getAttr("storage")) - arrayOp.setAttr("storage", builder.getBoolAttr(false)); + if (!arrayOp.storage()) { + arrayOp.setAttr("storage", builder.getBoolAttr(true)); + arrayOp.setAttr("storage_type", + builder.getStringAttr("ram_1p_bram")); + } - if (!arrayOp.getAttr("partition")) + if (!arrayOp.partition()) arrayOp.setAttr("partition", builder.getBoolAttr(false)); } } diff --git a/lib/EmitHLSCpp/EmitHLSCpp.cpp b/lib/EmitHLSCpp/EmitHLSCpp.cpp index 6044b68..a819d9d 100644 --- a/lib/EmitHLSCpp/EmitHLSCpp.cpp +++ b/lib/EmitHLSCpp/EmitHLSCpp.cpp @@ -210,6 +210,7 @@ private: void emitArrayDecl(Value array); unsigned emitNestedLoopHead(Value val); void emitNestedLoopTail(unsigned rank); + void emitInfoAndNewLine(Operation *op); /// MLIR component emitters. void emitOperation(Operation *op); @@ -527,16 +528,16 @@ void ModuleEmitter::emitScfFor(scf::ForOp *op) { emitValue(iterVar); os << " += "; emitValue(op->step()); - os << ") {\n"; + os << ") {"; + emitInfoAndNewLine(*op); addIndent(); if (auto pipeline = op->getAttrOfType("pipeline")) { - indent(); - if (pipeline.getValue()) + if (pipeline.getValue()) { + indent(); os << "#pragma HLS pipeline\n"; - else - os << "#pragma HLS pipeline off\n"; + } } // if (auto flatten = op->getAttrOfType("flatten")) { @@ -578,7 +579,9 @@ void ModuleEmitter::emitScfIf(scf::IfOp *op) { indent(); os << "if ("; emitValue(op->condition()); - os << ") {\n"; + os << ") {"; + emitInfoAndNewLine(*op); + addIndent(); emitBlock(op->thenRegion().front()); reduceIndent(); @@ -609,7 +612,8 @@ void ModuleEmitter::emitScfYield(scf::YieldOp *op) { emitValue(result, rank); os << " = "; emitValue(op->getOperand(resultIdx++), rank); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); emitNestedLoopTail(rank); } } @@ -663,16 +667,16 @@ void ModuleEmitter::emitAffineFor(AffineForOp *op) { // Emit increase step. emitValue(iterVar); - os << " += " << op->getStep() << ") {\n"; + os << " += " << op->getStep() << ") {"; + emitInfoAndNewLine(*op); addIndent(); if (auto pipeline = op->getAttrOfType("pipeline")) { - indent(); - if (pipeline.getValue()) + if (pipeline.getValue()) { + indent(); os << "#pragma HLS pipeline\n"; - else - os << "#pragma HLS pipeline off\n"; + } } // if (auto flatten = op->getAttrOfType("flatten")) { @@ -729,7 +733,9 @@ void ModuleEmitter::emitAffineIf(AffineIfOp *op) { if (constrIdx++ != constrSet.getNumConstraints() - 1) os << " && "; } - os << ") {\n"; + os << ") {"; + emitInfoAndNewLine(*op); + addIndent(); emitBlock(*op->getThenBlock()); reduceIndent(); @@ -799,8 +805,12 @@ void ModuleEmitter::emitAffineParallel(AffineParallelOp *op) { reduceIndent(); indent(); - os << "}\n"; + if (i == e - 1) + os << "}"; + else + os << "}\n"; } + emitInfoAndNewLine(*op); } void ModuleEmitter::emitAffineApply(AffineApplyOp *op) { @@ -810,7 +820,8 @@ void ModuleEmitter::emitAffineApply(AffineApplyOp *op) { auto affineMap = op->getAffineMap(); AffineExprEmitter(state, affineMap.getNumDims(), op->getOperands()) .emitAffineExpr(affineMap.getResult(0)); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); } template @@ -829,7 +840,8 @@ void ModuleEmitter::emitAffineMaxMin(OpType *op, const char *syntax) { affineEmitter.emitAffineExpr(expr); os << ")"; } - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); } void ModuleEmitter::emitAffineLoad(AffineLoadOp *op) { @@ -845,7 +857,8 @@ void ModuleEmitter::emitAffineLoad(AffineLoadOp *op) { affineEmitter.emitAffineExpr(index); os << "]"; } - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); } void ModuleEmitter::emitAffineStore(AffineStoreOp *op) { @@ -861,7 +874,8 @@ void ModuleEmitter::emitAffineStore(AffineStoreOp *op) { } os << " = "; emitValue(op->getValueToStore()); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); } void ModuleEmitter::emitAffineVectorLoad(AffineVectorLoadOp *op) { @@ -890,7 +904,8 @@ void ModuleEmitter::emitAffineYield(AffineYieldOp *op) { emitValue(result, rank); os << " = "; emitValue(op->getOperand(resultIdx++), rank); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); emitNestedLoopTail(rank); } } else if (auto parentOp = @@ -916,7 +931,8 @@ void ModuleEmitter::emitAffineYield(AffineYieldOp *op) { emitValue(result, rank); os << " = "; emitValue(op->getOperand(resultIdx++), rank); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); emitNestedLoopTail(rank); } reduceIndent(); @@ -970,7 +986,8 @@ void ModuleEmitter::emitAffineYield(AffineYieldOp *op) { emitValue(op->getOperand(resultIdx++), rank); break; } - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); emitNestedLoopTail(rank); } reduceIndent(); @@ -993,7 +1010,8 @@ template void ModuleEmitter::emitAlloc(OpType *op) { indent(); emitArrayDecl(op->getResult()); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); } void ModuleEmitter::emitLoad(LoadOp *op) { @@ -1006,7 +1024,8 @@ void ModuleEmitter::emitLoad(LoadOp *op) { emitValue(index); os << "]"; } - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); } void ModuleEmitter::emitStore(StoreOp *op) { @@ -1019,7 +1038,8 @@ void ModuleEmitter::emitStore(StoreOp *op) { } os << " = "; emitValue(op->getValueToStore()); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); } /// Tensor-related statement emitters. @@ -1029,7 +1049,8 @@ void ModuleEmitter::emitTensorLoad(TensorLoadOp *op) { emitValue(op->getResult(), rank); os << " = "; emitValue(op->getOperand(), rank); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); emitNestedLoopTail(rank); } @@ -1039,7 +1060,8 @@ void ModuleEmitter::emitTensorStore(TensorStoreOp *op) { emitValue(op->getOperand(1), rank); os << " = "; emitValue(op->getOperand(0), rank); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); emitNestedLoopTail(rank); } @@ -1065,7 +1087,8 @@ void ModuleEmitter::emitDim(DimOp *op) { indent(); emitValue(op->getResult()); os << " = "; - os << type.getShape()[constVal] << ";\n"; + os << type.getShape()[constVal] << ";"; + emitInfoAndNewLine(*op); } else emitError(*op, "index is out of range."); } else @@ -1080,7 +1103,8 @@ void ModuleEmitter::emitRank(RankOp *op) { indent(); emitValue(op->getResult()); os << " = "; - os << type.getRank() << ";\n"; + os << type.getRank() << ";"; + emitInfoAndNewLine(*op); } else emitError(*op, "is unranked."); } @@ -1094,7 +1118,8 @@ void ModuleEmitter::emitBinary(Operation *op, const char *syntax) { emitValue(op->getOperand(0), rank); os << " " << syntax << " "; emitValue(op->getOperand(1), rank); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(op); emitNestedLoopTail(rank); } @@ -1104,7 +1129,8 @@ void ModuleEmitter::emitUnary(Operation *op, const char *syntax) { emitValue(op->getResult(0), rank); os << " = " << syntax << "("; emitValue(op->getOperand(0), rank); - os << ");\n"; + os << ");"; + emitInfoAndNewLine(op); emitNestedLoopTail(rank); } @@ -1123,7 +1149,8 @@ void ModuleEmitter::emitSelect(SelectOp *op) { emitValue(op->getTrueValue(), rank); os << " : "; emitValue(op->getFalseValue(), rank); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); emitNestedLoopTail(rank); } @@ -1154,7 +1181,8 @@ void ModuleEmitter::emitConstant(ConstantOp *op) { if (elementIdx++ != denseAttr.getNumElements() - 1) os << ", "; } - os << "};\n"; + os << "};"; + emitInfoAndNewLine(*op); } else emitError(*op, "has unsupported constant type."); } @@ -1164,7 +1192,8 @@ void ModuleEmitter::emitIndexCast(IndexCastOp *op) { emitValue(op->getResult()); os << " = "; emitValue(op->getOperand()); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); } void ModuleEmitter::emitCall(CallOp *op) { @@ -1204,7 +1233,8 @@ void ModuleEmitter::emitCall(CallOp *op) { emitValue(result); } - os << ");\n"; + os << ");"; + emitInfoAndNewLine(*op); } /// Structure operation emitters. @@ -1214,17 +1244,17 @@ void ModuleEmitter::emitAssign(AssignOp *op) { emitValue(op->getResult(), rank); os << " = "; emitValue(op->getOperand(), rank); - os << ";\n"; + os << ";"; + emitInfoAndNewLine(*op); emitNestedLoopTail(rank); } void ModuleEmitter::emitArray(ArrayOp *op) { bool emitPragmaFlag = false; + // Emit interface pragma. if (op->interface()) { emitPragmaFlag = true; - - // Emit interface pragma. indent(); os << "#pragma HLS interface"; os << " " << op->interface_mode(); @@ -1233,21 +1263,21 @@ void ModuleEmitter::emitArray(ArrayOp *op) { if (op->interface_mode() == "m_axi") { os << " depth=" << op->interface_depth(); os << " offset=slave"; - } else if (op->storage()) - os << " storage_type=" << op->storage_type(); - + } + os << "\n"; + } + + // Emit resource pragma. + if (op->storage()) { + emitPragmaFlag = true; + indent(); + os << "#pragma HLS resource"; + os << " variable="; + emitValue(op->getOperand()); + os << " core="; + os << 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"; - // os << " variable="; - // emitValue(op->getOperand()); - // os << " type=" << op->storage_type(); - // os << " impl=" << op->storage_impl() << "\n"; - //} auto type = op->getOperand().getType().cast(); if (op->partition() && type.hasStaticShape()) { @@ -1370,6 +1400,17 @@ void ModuleEmitter::emitNestedLoopTail(unsigned rank) { } } +void ModuleEmitter::emitInfoAndNewLine(Operation *op) { + os << "\t//"; + if (auto loc = op->getLoc().dyn_cast()) + os << " #L" << loc.getLine(); + if (auto begin = op->getAttrOfType("schedule_begin")) + os << ", [" << begin.getUInt(); + if (auto end = op->getAttrOfType("schedule_end")) + os << ", " << end.getUInt() << ")"; + os << "\n"; +} + /// MLIR component emitters. void ModuleEmitter::emitOperation(Operation *op) { if (ExprVisitor(*this).dispatchVisitor(op)) @@ -1393,6 +1434,12 @@ void ModuleEmitter::emitFunction(FuncOp func) { if (func.getBlocks().size() != 1) emitError(func, "has zero or more than one basic blocks."); + if (auto top = func.getAttrOfType("top_function")) + os << "/// This is top function.\n"; + + if (auto latency = func.getAttrOfType("latency")) + os << "/// Function latency is " << latency.getUInt() << ".\n"; + // Emit function signature. os << "void " << func.getName() << "(\n"; addIndent(); @@ -1434,7 +1481,8 @@ void ModuleEmitter::emitFunction(FuncOp func) { emitError(func, "doesn't have a return operation as terminator."); reduceIndent(); - os << "\n) {\n"; + os << "\n) {"; + emitInfoAndNewLine(func); // Emit function body. addIndent(); diff --git a/test/Conversion/ConvertToHLSCpp/test_conversion.mlir b/test/Conversion/ConvertToHLSCpp/test_conversion.mlir index 96d5ef6..0b8709b 100644 --- a/test/Conversion/ConvertToHLSCpp/test_conversion.mlir +++ b/test/Conversion/ConvertToHLSCpp/test_conversion.mlir @@ -3,11 +3,11 @@ // CHECK-LABEL: func @test_conversion( // 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 = true, partition = false, storage = false} : (memref<16xf32>) -> memref<16xf32> + // CHECK: %[[VAL_0:.*]] = "hlscpp.array"(%[[ARG_1:.*]]) {interface = true, partition = false, storage = true, storage_type = "ram_1p_bram"} : (memref<16xf32>) -> memref<16xf32> %c11_i32 = constant 11 : i32 %cst = constant dense<[[11, 0], [0, -42]]> : tensor<2x2xi32> - // CHECK: %[[VAL_1:.*]] = "hlscpp.array"(%cst) {interface = false, partition = false, storage = false} : (tensor<2x2xi32>) -> tensor<2x2xi32> + // CHECK: %[[VAL_1:.*]] = "hlscpp.array"(%cst) {interface = false, partition = false, storage = true, storage_type = "ram_1p_bram"} : (tensor<2x2xi32>) -> tensor<2x2xi32> // CHECK: %[[VAL_2:.*]] = "hlscpp.assign"(%arg0) : (f32) -> f32 // CHECK: %[[VAL_3:.*]] = "hlscpp.assign"(%[[VAL_0:.*]]) : (memref<16xf32>) -> memref<16xf32> // CHECK: %[[VAL_4:.*]] = "hlscpp.assign"(%c11_i32) : (i32) -> i32