[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::AllocOp, memref::AllocaOp, memref::LoadOp, memref::StoreOp,
|
||||
memref::DeallocOp, memref::TensorStoreOp, tensor::ReshapeOp,
|
||||
memref::ReshapeOp, memref::CollapseShapeOp, memref::ExpandShapeOp,
|
||||
memref::ReinterpretCastOp, bufferization::ToMemrefOp,
|
||||
bufferization::ToTensorOp,
|
||||
memref::DeallocOp, memref::CopyOp, memref::TensorStoreOp,
|
||||
tensor::ReshapeOp, memref::ReshapeOp, memref::CollapseShapeOp,
|
||||
memref::ExpandShapeOp, memref::ReinterpretCastOp,
|
||||
bufferization::ToMemrefOp, bufferization::ToTensorOp,
|
||||
|
||||
// HLSCpp primitive operations.
|
||||
MulPrimOp, CastPrimOp, AssignOp,
|
||||
|
@ -135,6 +135,7 @@ public:
|
|||
HANDLE(memref::LoadOp);
|
||||
HANDLE(memref::StoreOp);
|
||||
HANDLE(memref::DeallocOp);
|
||||
HANDLE(memref::CopyOp);
|
||||
HANDLE(memref::TensorStoreOp);
|
||||
HANDLE(tensor::ReshapeOp);
|
||||
HANDLE(memref::ReshapeOp);
|
||||
|
|
|
@ -46,6 +46,9 @@ def MultipleLevelDSE : Pass<"dse", "ModuleOp"> {
|
|||
let options = [
|
||||
Option<"topFunc", "top-func", "std::string", /*default=*/"\"main\"",
|
||||
"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",
|
||||
/*default=*/"\"./\"",
|
||||
"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 = [
|
||||
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) {
|
||||
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.
|
||||
#define HANDLE(OPTYPE, KEYNAME) \
|
||||
|
|
|
@ -69,7 +69,7 @@ bool applyRemoveVariableBound(AffineLoopBand &band);
|
|||
/// the innermost loop with the original loop order.
|
||||
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
|
||||
/// loops are automatically fully unrolled.
|
||||
|
|
|
@ -54,7 +54,8 @@ struct MemrefStoreRewritePattern : public OpRewritePattern<memref::StoreOp> {
|
|||
};
|
||||
} // namespace
|
||||
|
||||
bool scalehls::applyLegalizeToHLSCpp(FuncOp func, bool isTopFunc) {
|
||||
bool scalehls::applyLegalizeToHLSCpp(FuncOp func, bool isTopFunc,
|
||||
bool axiInterf) {
|
||||
auto builder = OpBuilder(func);
|
||||
|
||||
// We constrain functions to only contain one block.
|
||||
|
@ -72,6 +73,38 @@ bool scalehls::applyLegalizeToHLSCpp(FuncOp func, bool isTopFunc) {
|
|||
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
|
||||
// connected to ReturnOp.
|
||||
auto returnOp = func.front().getTerminator();
|
||||
|
@ -108,7 +141,8 @@ struct LegalizeToHLSCpp : public LegalizeToHLSCppBase<LegalizeToHLSCpp> {
|
|||
|
||||
void runOnOperation() override {
|
||||
auto func = getOperation();
|
||||
applyLegalizeToHLSCpp(func, func.getName() == topFunc);
|
||||
auto isTop = func.getName() == topFunc;
|
||||
applyLegalizeToHLSCpp(func, isTop, isTop && axiInterf);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
|
|
@ -889,7 +889,9 @@ struct MultipleLevelDSE : public MultipleLevelDSEBase<MultipleLevelDSE> {
|
|||
// Optimize the top function.
|
||||
// TODO: Handle sub-functions and dataflowed or pipelined functions.
|
||||
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,
|
||||
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,
|
||||
// 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);
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -146,9 +146,6 @@ bool scalehls::applyOptStrategy(AffineLoopBand &band, FuncOp func,
|
|||
if (!func->isProperAncestor(band.front()))
|
||||
return false;
|
||||
|
||||
// Apply legalization. TODO: Only support top-function for now.
|
||||
applyLegalizeToHLSCpp(func, /*isTopFunc=*/true);
|
||||
|
||||
// Apply loop tiling.
|
||||
if (!applyLoopTiling(band, tileList))
|
||||
return false;
|
||||
|
@ -172,9 +169,6 @@ bool scalehls::applyOptStrategy(FuncOp func, ArrayRef<TileList> tileLists,
|
|||
assert(bands.size() == tileLists.size() && bands.size() == targetIIs.size() &&
|
||||
"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.
|
||||
for (unsigned i = 0, e = bands.size(); i < e; ++i)
|
||||
if (!applyLoopTiling(bands[i], tileLists[i]))
|
||||
|
|
|
@ -218,6 +218,7 @@ public:
|
|||
template <typename OpType> void emitAlloc(OpType op);
|
||||
void emitLoad(memref::LoadOp op);
|
||||
void emitStore(memref::StoreOp op);
|
||||
void emitMemCpy(memref::CopyOp op);
|
||||
void emitTensorStore(memref::TensorStoreOp op);
|
||||
template <typename OpType> void emitReshape(OpType op);
|
||||
void emitTensorToMemref(bufferization::ToMemrefOp op);
|
||||
|
@ -400,6 +401,7 @@ public:
|
|||
bool visitOp(memref::LoadOp op) { return emitter.emitLoad(op), true; }
|
||||
bool visitOp(memref::StoreOp op) { return emitter.emitStore(op), true; }
|
||||
bool visitOp(memref::DeallocOp op) { return true; }
|
||||
bool visitOp(memref::CopyOp op) { return emitter.emitMemCpy(op), true; }
|
||||
bool visitOp(memref::TensorStoreOp op) {
|
||||
return emitter.emitTensorStore(op), true;
|
||||
}
|
||||
|
@ -1180,6 +1182,20 @@ void ModuleEmitter::emitStore(memref::StoreOp 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) {
|
||||
auto rank = emitNestedLoopHeader(op.getOperand(0));
|
||||
indent();
|
||||
|
@ -1656,9 +1672,11 @@ void ModuleEmitter::emitFunctionDirectives(FuncOp func,
|
|||
indent();
|
||||
os << "#pragma HLS interface";
|
||||
// For now, we set the offset of all m_axi interfaces as slave.
|
||||
if (MemoryKind(memrefType.getMemorySpaceAsInt()) == MemoryKind::DRAM)
|
||||
os << " m_axi offset=slave";
|
||||
else
|
||||
if (MemoryKind(memrefType.getMemorySpaceAsInt()) ==
|
||||
MemoryKind::DRAM) {
|
||||
os << " m_axi offset=slave bundle=";
|
||||
emitValue(port);
|
||||
} else
|
||||
os << " bram";
|
||||
|
||||
os << " port=";
|
||||
|
@ -1722,7 +1740,7 @@ void ModuleEmitter::emitFunction(FuncOp func) {
|
|||
|
||||
if (auto resource = getResource(func)) {
|
||||
os << "/// DSP=" << resource.getDsp();
|
||||
// os << ", BRAM=" << resource.getBram();
|
||||
os << ", BRAM=" << resource.getBram();
|
||||
// os << ", LUT=" << resource.getLut();
|
||||
os << "\n";
|
||||
}
|
||||
|
@ -1799,6 +1817,7 @@ void ModuleEmitter::emitModule(ModuleOp module) {
|
|||
#include <hls_stream.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
|
Loading…
Reference in New Issue