[LegalizeHLSCpp] Support to create AXI interfaces as an optional transform; [QoREstimation][EmitHLSCpp] Support memref CopyOp; [DSE] Move applyLegalizeToHLSCpp to the begining of the pass
This commit is contained in:
parent
ad2bf8b7c6
commit
626cfea264
|
@ -47,10 +47,10 @@ public:
|
||||||
|
|
||||||
// Memref-related statements.
|
// Memref-related statements.
|
||||||
memref::AllocOp, memref::AllocaOp, memref::LoadOp, memref::StoreOp,
|
memref::AllocOp, memref::AllocaOp, memref::LoadOp, memref::StoreOp,
|
||||||
memref::DeallocOp, memref::TensorStoreOp, tensor::ReshapeOp,
|
memref::DeallocOp, memref::CopyOp, memref::TensorStoreOp,
|
||||||
memref::ReshapeOp, memref::CollapseShapeOp, memref::ExpandShapeOp,
|
tensor::ReshapeOp, memref::ReshapeOp, memref::CollapseShapeOp,
|
||||||
memref::ReinterpretCastOp, bufferization::ToMemrefOp,
|
memref::ExpandShapeOp, memref::ReinterpretCastOp,
|
||||||
bufferization::ToTensorOp,
|
bufferization::ToMemrefOp, bufferization::ToTensorOp,
|
||||||
|
|
||||||
// HLSCpp primitive operations.
|
// HLSCpp primitive operations.
|
||||||
MulPrimOp, CastPrimOp, AssignOp,
|
MulPrimOp, CastPrimOp, AssignOp,
|
||||||
|
@ -135,6 +135,7 @@ public:
|
||||||
HANDLE(memref::LoadOp);
|
HANDLE(memref::LoadOp);
|
||||||
HANDLE(memref::StoreOp);
|
HANDLE(memref::StoreOp);
|
||||||
HANDLE(memref::DeallocOp);
|
HANDLE(memref::DeallocOp);
|
||||||
|
HANDLE(memref::CopyOp);
|
||||||
HANDLE(memref::TensorStoreOp);
|
HANDLE(memref::TensorStoreOp);
|
||||||
HANDLE(tensor::ReshapeOp);
|
HANDLE(tensor::ReshapeOp);
|
||||||
HANDLE(memref::ReshapeOp);
|
HANDLE(memref::ReshapeOp);
|
||||||
|
|
|
@ -46,6 +46,9 @@ def MultipleLevelDSE : Pass<"dse", "ModuleOp"> {
|
||||||
let options = [
|
let options = [
|
||||||
Option<"topFunc", "top-func", "std::string", /*default=*/"\"main\"",
|
Option<"topFunc", "top-func", "std::string", /*default=*/"\"main\"",
|
||||||
"The top function for HLS synthesis">,
|
"The top function for HLS synthesis">,
|
||||||
|
Option<"axiInterf", "axi-interf", "bool", /*default=*/"false",
|
||||||
|
"Whether to create AXI interfaces for the top function">,
|
||||||
|
|
||||||
Option<"outputPath", "output-path", "std::string",
|
Option<"outputPath", "output-path", "std::string",
|
||||||
/*default=*/"\"./\"",
|
/*default=*/"\"./\"",
|
||||||
"File path: the path for dumping the MLIR of pareto design points">,
|
"File path: the path for dumping the MLIR of pareto design points">,
|
||||||
|
@ -155,7 +158,9 @@ def LegalizeToHLSCpp : Pass<"legalize-to-hlscpp", "FuncOp"> {
|
||||||
|
|
||||||
let options = [
|
let options = [
|
||||||
Option<"topFunc", "top-func", "std::string", /*default=*/"\"main\"",
|
Option<"topFunc", "top-func", "std::string", /*default=*/"\"main\"",
|
||||||
"The top function for HLS synthesis">
|
"The top function for HLS synthesis">,
|
||||||
|
Option<"axiInterf", "axi-interf", "bool", /*default=*/"false",
|
||||||
|
"Whether to create AXI interfaces for the top function">
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,10 @@ public:
|
||||||
bool visitOp(memref::StoreOp op, int64_t begin) {
|
bool visitOp(memref::StoreOp op, int64_t begin) {
|
||||||
return setTiming(op, begin, begin + 1, 1, 1), true;
|
return setTiming(op, begin, begin + 1, 1, 1), true;
|
||||||
}
|
}
|
||||||
|
bool visitOp(memref::CopyOp op, int64_t begin) {
|
||||||
|
auto type = op.target().getType().cast<MemRefType>();
|
||||||
|
return setTiming(op, begin, begin + type.getNumElements(), 1, 1), true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle operations with profiled latency.
|
/// Handle operations with profiled latency.
|
||||||
#define HANDLE(OPTYPE, KEYNAME) \
|
#define HANDLE(OPTYPE, KEYNAME) \
|
||||||
|
|
|
@ -69,7 +69,7 @@ bool applyRemoveVariableBound(AffineLoopBand &band);
|
||||||
/// the innermost loop with the original loop order.
|
/// the innermost loop with the original loop order.
|
||||||
bool applyLoopTiling(AffineLoopBand &band, TileList tileList);
|
bool applyLoopTiling(AffineLoopBand &band, TileList tileList);
|
||||||
|
|
||||||
bool applyLegalizeToHLSCpp(FuncOp func, bool topFunc);
|
bool applyLegalizeToHLSCpp(FuncOp func, bool topFunc, bool axiInterf = false);
|
||||||
|
|
||||||
/// Apply loop pipelining to the pipelineLoc of the input loop band, all inner
|
/// Apply loop pipelining to the pipelineLoc of the input loop band, all inner
|
||||||
/// loops are automatically fully unrolled.
|
/// loops are automatically fully unrolled.
|
||||||
|
|
|
@ -54,7 +54,8 @@ struct MemrefStoreRewritePattern : public OpRewritePattern<memref::StoreOp> {
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool scalehls::applyLegalizeToHLSCpp(FuncOp func, bool isTopFunc) {
|
bool scalehls::applyLegalizeToHLSCpp(FuncOp func, bool isTopFunc,
|
||||||
|
bool axiInterf) {
|
||||||
auto builder = OpBuilder(func);
|
auto builder = OpBuilder(func);
|
||||||
|
|
||||||
// We constrain functions to only contain one block.
|
// We constrain functions to only contain one block.
|
||||||
|
@ -72,6 +73,38 @@ bool scalehls::applyLegalizeToHLSCpp(FuncOp func, bool isTopFunc) {
|
||||||
setParallel(loop);
|
setParallel(loop);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (axiInterf) {
|
||||||
|
auto loc = builder.getUnknownLoc();
|
||||||
|
|
||||||
|
// Convert each argument memory kind to DRAM and buffer each of them.
|
||||||
|
for (auto arg : func.getArguments()) {
|
||||||
|
if (auto type = arg.getType().dyn_cast<MemRefType>()) {
|
||||||
|
arg.setType(MemRefType::get(type.getShape(), type.getElementType(),
|
||||||
|
type.getLayout().getAffineMap(),
|
||||||
|
(unsigned)MemoryKind::DRAM));
|
||||||
|
|
||||||
|
// Allocate an on-chip buffer and create a copy from DRAM to the buffer.
|
||||||
|
builder.setInsertionPointToStart(&func.front());
|
||||||
|
auto buf = builder.create<memref::AllocOp>(loc, type);
|
||||||
|
arg.replaceAllUsesWith(buf);
|
||||||
|
builder.create<memref::CopyOp>(loc, arg, buf);
|
||||||
|
|
||||||
|
// If the buffer's state is written in the function, create a copy from
|
||||||
|
// the buffer to DRAM.
|
||||||
|
if (llvm::any_of(buf->getUsers(), [](Operation *op) {
|
||||||
|
return isa<AffineWriteOpInterface, memref::StoreOp>(op);
|
||||||
|
})) {
|
||||||
|
builder.setInsertionPoint(func.back().getTerminator());
|
||||||
|
builder.create<memref::CopyOp>(loc, buf, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, update the type of the function.
|
||||||
|
func.setType(builder.getFunctionType(func.front().getArgumentTypes(),
|
||||||
|
func.getResultTypes()));
|
||||||
|
}
|
||||||
|
|
||||||
// Insert AssignOp when an arguments or result of ConstantOp are directly
|
// Insert AssignOp when an arguments or result of ConstantOp are directly
|
||||||
// connected to ReturnOp.
|
// connected to ReturnOp.
|
||||||
auto returnOp = func.front().getTerminator();
|
auto returnOp = func.front().getTerminator();
|
||||||
|
@ -108,7 +141,8 @@ struct LegalizeToHLSCpp : public LegalizeToHLSCppBase<LegalizeToHLSCpp> {
|
||||||
|
|
||||||
void runOnOperation() override {
|
void runOnOperation() override {
|
||||||
auto func = getOperation();
|
auto func = getOperation();
|
||||||
applyLegalizeToHLSCpp(func, func.getName() == topFunc);
|
auto isTop = func.getName() == topFunc;
|
||||||
|
applyLegalizeToHLSCpp(func, isTop, isTop && axiInterf);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -889,7 +889,9 @@ struct MultipleLevelDSE : public MultipleLevelDSEBase<MultipleLevelDSE> {
|
||||||
// Optimize the top function.
|
// Optimize the top function.
|
||||||
// TODO: Handle sub-functions and dataflowed or pipelined functions.
|
// TODO: Handle sub-functions and dataflowed or pipelined functions.
|
||||||
for (auto func : module.getOps<FuncOp>()) {
|
for (auto func : module.getOps<FuncOp>()) {
|
||||||
if (func.getName() == topFunc)
|
auto isTop = func.getName() == topFunc;
|
||||||
|
applyLegalizeToHLSCpp(func, isTop, isTop && axiInterf);
|
||||||
|
if (isTop)
|
||||||
optimizer.applyMultipleLevelDSE(func, directiveOnly, outputPath,
|
optimizer.applyMultipleLevelDSE(func, directiveOnly, outputPath,
|
||||||
csvPath);
|
csvPath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -668,7 +668,8 @@ TimingAttr ScaleHLSEstimator::estimateBlock(Block &block, int64_t begin) {
|
||||||
|
|
||||||
// If either the depOp or the current operation is a function call,
|
// If either the depOp or the current operation is a function call,
|
||||||
// dependency exists and the schedule level should be updated.
|
// dependency exists and the schedule level should be updated.
|
||||||
if (isa<CallOp>(op) || isa<CallOp>(depOp)) {
|
if (isa<CallOp, memref::CopyOp>(op) ||
|
||||||
|
isa<CallOp, memref::CopyOp>(depOp)) {
|
||||||
opBegin = max(opBegin, depOpEnd);
|
opBegin = max(opBegin, depOpEnd);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,9 +146,6 @@ bool scalehls::applyOptStrategy(AffineLoopBand &band, FuncOp func,
|
||||||
if (!func->isProperAncestor(band.front()))
|
if (!func->isProperAncestor(band.front()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Apply legalization. TODO: Only support top-function for now.
|
|
||||||
applyLegalizeToHLSCpp(func, /*isTopFunc=*/true);
|
|
||||||
|
|
||||||
// Apply loop tiling.
|
// Apply loop tiling.
|
||||||
if (!applyLoopTiling(band, tileList))
|
if (!applyLoopTiling(band, tileList))
|
||||||
return false;
|
return false;
|
||||||
|
@ -172,9 +169,6 @@ bool scalehls::applyOptStrategy(FuncOp func, ArrayRef<TileList> tileLists,
|
||||||
assert(bands.size() == tileLists.size() && bands.size() == targetIIs.size() &&
|
assert(bands.size() == tileLists.size() && bands.size() == targetIIs.size() &&
|
||||||
"unexpected size of tile lists or target IIs");
|
"unexpected size of tile lists or target IIs");
|
||||||
|
|
||||||
// Apply legalization. TODO: Only support top-function for now.
|
|
||||||
applyLegalizeToHLSCpp(func, /*isTopFunc=*/true);
|
|
||||||
|
|
||||||
// Apply loop tiling to all loop bands.
|
// Apply loop tiling to all loop bands.
|
||||||
for (unsigned i = 0, e = bands.size(); i < e; ++i)
|
for (unsigned i = 0, e = bands.size(); i < e; ++i)
|
||||||
if (!applyLoopTiling(bands[i], tileLists[i]))
|
if (!applyLoopTiling(bands[i], tileLists[i]))
|
||||||
|
|
|
@ -218,6 +218,7 @@ public:
|
||||||
template <typename OpType> void emitAlloc(OpType op);
|
template <typename OpType> void emitAlloc(OpType op);
|
||||||
void emitLoad(memref::LoadOp op);
|
void emitLoad(memref::LoadOp op);
|
||||||
void emitStore(memref::StoreOp op);
|
void emitStore(memref::StoreOp op);
|
||||||
|
void emitMemCpy(memref::CopyOp op);
|
||||||
void emitTensorStore(memref::TensorStoreOp op);
|
void emitTensorStore(memref::TensorStoreOp op);
|
||||||
template <typename OpType> void emitReshape(OpType op);
|
template <typename OpType> void emitReshape(OpType op);
|
||||||
void emitTensorToMemref(bufferization::ToMemrefOp op);
|
void emitTensorToMemref(bufferization::ToMemrefOp op);
|
||||||
|
@ -400,6 +401,7 @@ public:
|
||||||
bool visitOp(memref::LoadOp op) { return emitter.emitLoad(op), true; }
|
bool visitOp(memref::LoadOp op) { return emitter.emitLoad(op), true; }
|
||||||
bool visitOp(memref::StoreOp op) { return emitter.emitStore(op), true; }
|
bool visitOp(memref::StoreOp op) { return emitter.emitStore(op), true; }
|
||||||
bool visitOp(memref::DeallocOp op) { return true; }
|
bool visitOp(memref::DeallocOp op) { return true; }
|
||||||
|
bool visitOp(memref::CopyOp op) { return emitter.emitMemCpy(op), true; }
|
||||||
bool visitOp(memref::TensorStoreOp op) {
|
bool visitOp(memref::TensorStoreOp op) {
|
||||||
return emitter.emitTensorStore(op), true;
|
return emitter.emitTensorStore(op), true;
|
||||||
}
|
}
|
||||||
|
@ -1180,6 +1182,20 @@ void ModuleEmitter::emitStore(memref::StoreOp op) {
|
||||||
emitInfoAndNewLine(op);
|
emitInfoAndNewLine(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModuleEmitter::emitMemCpy(memref::CopyOp op) {
|
||||||
|
indent() << "memcpy(";
|
||||||
|
emitValue(op.target());
|
||||||
|
os << ", ";
|
||||||
|
emitValue(op.source());
|
||||||
|
os << ", ";
|
||||||
|
|
||||||
|
auto type = op.target().getType().cast<MemRefType>();
|
||||||
|
os << type.getNumElements() << " * sizeof(" << getTypeName(op.target())
|
||||||
|
<< "));";
|
||||||
|
emitInfoAndNewLine(op);
|
||||||
|
os << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
void ModuleEmitter::emitTensorStore(memref::TensorStoreOp op) {
|
void ModuleEmitter::emitTensorStore(memref::TensorStoreOp op) {
|
||||||
auto rank = emitNestedLoopHeader(op.getOperand(0));
|
auto rank = emitNestedLoopHeader(op.getOperand(0));
|
||||||
indent();
|
indent();
|
||||||
|
@ -1656,9 +1672,11 @@ void ModuleEmitter::emitFunctionDirectives(FuncOp func,
|
||||||
indent();
|
indent();
|
||||||
os << "#pragma HLS interface";
|
os << "#pragma HLS interface";
|
||||||
// For now, we set the offset of all m_axi interfaces as slave.
|
// For now, we set the offset of all m_axi interfaces as slave.
|
||||||
if (MemoryKind(memrefType.getMemorySpaceAsInt()) == MemoryKind::DRAM)
|
if (MemoryKind(memrefType.getMemorySpaceAsInt()) ==
|
||||||
os << " m_axi offset=slave";
|
MemoryKind::DRAM) {
|
||||||
else
|
os << " m_axi offset=slave bundle=";
|
||||||
|
emitValue(port);
|
||||||
|
} else
|
||||||
os << " bram";
|
os << " bram";
|
||||||
|
|
||||||
os << " port=";
|
os << " port=";
|
||||||
|
@ -1722,7 +1740,7 @@ void ModuleEmitter::emitFunction(FuncOp func) {
|
||||||
|
|
||||||
if (auto resource = getResource(func)) {
|
if (auto resource = getResource(func)) {
|
||||||
os << "/// DSP=" << resource.getDsp();
|
os << "/// DSP=" << resource.getDsp();
|
||||||
// os << ", BRAM=" << resource.getBram();
|
os << ", BRAM=" << resource.getBram();
|
||||||
// os << ", LUT=" << resource.getLut();
|
// os << ", LUT=" << resource.getLut();
|
||||||
os << "\n";
|
os << "\n";
|
||||||
}
|
}
|
||||||
|
@ -1799,6 +1817,7 @@ void ModuleEmitter::emitModule(ModuleOp module) {
|
||||||
#include <hls_stream.h>
|
#include <hls_stream.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue