mirror of https://github.com/llvm/circt.git
[firld] Add firld to link FIRRTL circuits (#8561)
This commit is contained in:
parent
6227f4aec0
commit
5dc56952a8
|
@ -194,6 +194,10 @@ std::unique_ptr<mlir::Pass> createAdvancedLayerSinkPass();
|
|||
|
||||
std::unique_ptr<mlir::Pass> createMaterializeDebugInfoPass();
|
||||
|
||||
std::unique_ptr<mlir::Pass>
|
||||
createLinkCircuitsPass(mlir::StringRef baseCircuitName = "",
|
||||
bool noMangle = false);
|
||||
|
||||
std::unique_ptr<mlir::Pass> createLintingPass();
|
||||
|
||||
std::unique_ptr<mlir::Pass> createProbesToSignalsPass();
|
||||
|
|
|
@ -839,6 +839,22 @@ def MaterializeDebugInfo :
|
|||
let dependentDialects = ["debug::DebugDialect"];
|
||||
}
|
||||
|
||||
def LinkCircuits : Pass<"firrtl-link-circuits", "mlir::ModuleOp"> {
|
||||
let summary = "Links multiple circuits into a single one";
|
||||
let description = [{
|
||||
This pass concatenates all circuits into one, iterates through all extmodules
|
||||
to find matching public module implementations, replacing them when possible
|
||||
and any remaining extmodules are treated as blackboxes.
|
||||
}];
|
||||
let constructor = "circt::firrtl::createLinkCircuitsPass()";
|
||||
let options = [
|
||||
Option<"baseCircuitName", "base-circuit", "std::string", "",
|
||||
"The base circuit name.">,
|
||||
Option<"noMangle", "no-mangle", "bool", "",
|
||||
"Do not perform private symbol mangling.">
|
||||
];
|
||||
}
|
||||
|
||||
def Lint :
|
||||
Pass<"firrtl-lint", "firrtl::FModuleOp"> {
|
||||
let summary = "An analysis pass to detect static simulation failures.";
|
||||
|
|
|
@ -25,6 +25,7 @@ add_circt_dialect_library(CIRCTFIRRTLTransforms
|
|||
InjectDUTHierarchy.cpp
|
||||
InnerSymbolDCE.cpp
|
||||
LegacyWiring.cpp
|
||||
LinkCircuits.cpp
|
||||
Lint.cpp
|
||||
LayerMerge.cpp
|
||||
LayerSink.cpp
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
//===- LinkCircuits.cpp - Merge FIRRTL circuits --------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This pass links multiple circuits into a single one.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt/Dialect/FIRRTL/FIRRTLAnnotationHelper.h"
|
||||
#include "circt/Dialect/FIRRTL/FIRRTLAnnotations.h"
|
||||
#include "circt/Dialect/FIRRTL/FIRRTLOps.h"
|
||||
#include "circt/Dialect/FIRRTL/FIRRTLTypes.h"
|
||||
#include "circt/Dialect/FIRRTL/Passes.h"
|
||||
#include "mlir/IR/Attributes.h"
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "mlir/IR/BuiltinAttributes.h"
|
||||
#include "mlir/IR/Diagnostics.h"
|
||||
#include "mlir/IR/Operation.h"
|
||||
#include "mlir/IR/SymbolTable.h"
|
||||
#include "mlir/Pass/Pass.h"
|
||||
#include "mlir/Support/LLVM.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include <iterator>
|
||||
|
||||
namespace circt {
|
||||
namespace firrtl {
|
||||
#define GEN_PASS_DEF_LINKCIRCUITS
|
||||
#include "circt/Dialect/FIRRTL/Passes.h.inc"
|
||||
} // namespace firrtl
|
||||
} // namespace circt
|
||||
|
||||
using namespace mlir;
|
||||
using namespace circt;
|
||||
using namespace firrtl;
|
||||
|
||||
namespace {
|
||||
struct LinkCircuitsPass
|
||||
: public circt::firrtl::impl::LinkCircuitsBase<LinkCircuitsPass> {
|
||||
void runOnOperation() override;
|
||||
LogicalResult mergeCircuits();
|
||||
LinkCircuitsPass(StringRef baseCircuitNameOption, bool noMangleOption) {
|
||||
baseCircuitName = std::string(baseCircuitNameOption);
|
||||
noMangle = noMangleOption;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <typename CallableT>
|
||||
static DictionaryAttr transformAnnotationTarget(DictionaryAttr anno,
|
||||
CallableT transformTokensFn) {
|
||||
return DictionaryAttr::getWithSorted(
|
||||
anno.getContext(),
|
||||
to_vector(map_range(
|
||||
anno.getValue(), [&](NamedAttribute namedAttr) -> NamedAttribute {
|
||||
if (namedAttr.getName() == "target")
|
||||
if (auto target = dyn_cast<StringAttr>(namedAttr.getValue()))
|
||||
if (auto tokens = tokenizePath(target.getValue()))
|
||||
return {
|
||||
namedAttr.getName(),
|
||||
StringAttr::get(target.getContext(),
|
||||
transformTokensFn(tokens.value()).str())};
|
||||
return namedAttr;
|
||||
})));
|
||||
}
|
||||
|
||||
static LogicalResult mangleCircuitSymbols(CircuitOp circuit) {
|
||||
auto circuitName = circuit.getNameAttr();
|
||||
|
||||
llvm::MapVector<StringRef, Operation *> renameTable;
|
||||
auto symbolTable = SymbolTable(circuit.getOperation());
|
||||
auto manglePrivateSymbol = [&](SymbolOpInterface symbolOp) {
|
||||
auto symbolName = symbolOp.getNameAttr();
|
||||
auto newSymbolName =
|
||||
StringAttr::get(symbolOp->getContext(),
|
||||
circuitName.getValue() + "_" + symbolName.getValue());
|
||||
renameTable.insert(std::pair(symbolName.getValue(), symbolOp));
|
||||
return symbolTable.rename(symbolOp, newSymbolName);
|
||||
};
|
||||
|
||||
for (auto &op : circuit.getOps()) {
|
||||
auto symbolOp = dyn_cast<SymbolOpInterface>(op);
|
||||
if (!symbolOp)
|
||||
continue;
|
||||
|
||||
if (symbolOp.isPrivate())
|
||||
if (failed(manglePrivateSymbol(symbolOp)))
|
||||
return failure();
|
||||
}
|
||||
|
||||
circuit.walk([&](Operation *op) {
|
||||
auto updateType = [&](Type type) -> Type {
|
||||
if (auto cls = dyn_cast<ClassType>(type))
|
||||
if (auto *newOp = renameTable.lookup(cls.getName()))
|
||||
return ClassType::get(FlatSymbolRefAttr::get(newOp),
|
||||
cls.getElements());
|
||||
return type;
|
||||
};
|
||||
auto updateTypeAttr = [&](Attribute attr) -> Attribute {
|
||||
if (auto typeAttr = dyn_cast<TypeAttr>(attr)) {
|
||||
auto newType = updateType(typeAttr.getValue());
|
||||
if (newType != typeAttr.getValue())
|
||||
return TypeAttr::get(newType);
|
||||
}
|
||||
return attr;
|
||||
};
|
||||
auto updateResults = [&](auto &&results) {
|
||||
for (auto result : results)
|
||||
if (auto newType = updateType(result.getType());
|
||||
newType != result.getType())
|
||||
result.setType(newType);
|
||||
};
|
||||
|
||||
TypeSwitch<Operation *>(op)
|
||||
.Case<CircuitOp>([&](CircuitOp circuit) {
|
||||
SmallVector<Attribute> newAnnotations;
|
||||
llvm::transform(
|
||||
circuit.getAnnotationsAttr(), std::back_inserter(newAnnotations),
|
||||
[&](Attribute attr) {
|
||||
return transformAnnotationTarget(
|
||||
cast<DictionaryAttr>(attr), [&](TokenAnnoTarget &tokens) {
|
||||
if (auto *newModule = renameTable.lookup(tokens.module))
|
||||
tokens.module =
|
||||
cast<SymbolOpInterface>(newModule).getName();
|
||||
return tokens;
|
||||
});
|
||||
});
|
||||
circuit.setAnnotationsAttr(
|
||||
ArrayAttr::get(circuit.getContext(), newAnnotations));
|
||||
})
|
||||
.Case<ObjectOp>([&](ObjectOp obj) {
|
||||
auto resultTypeName = obj.getResult().getType().getName();
|
||||
if (auto *newOp = renameTable.lookup(resultTypeName))
|
||||
obj.getResult().setType(dyn_cast<ClassOp>(newOp).getInstanceType());
|
||||
})
|
||||
.Case<FModuleOp>([&](FModuleOp module) {
|
||||
SmallVector<Attribute> newPortTypes;
|
||||
llvm::transform(module.getPortTypesAttr().getValue(),
|
||||
std::back_inserter(newPortTypes), updateTypeAttr);
|
||||
module.setPortTypesAttr(
|
||||
ArrayAttr::get(module->getContext(), newPortTypes));
|
||||
updateResults(module.getBodyBlock()->getArguments());
|
||||
})
|
||||
.Case<InstanceOp>(
|
||||
[&](InstanceOp instance) { updateResults(instance->getResults()); })
|
||||
.Case<WireOp>([&](WireOp wire) { updateResults(wire->getResults()); })
|
||||
.Default([](Operation *op) {});
|
||||
});
|
||||
return success();
|
||||
}
|
||||
|
||||
/// return if the incomingOp has been erased
|
||||
static FailureOr<bool> linkExtmodule(SymbolOpInterface collidingOp,
|
||||
SymbolOpInterface incomingOp) {
|
||||
if (!((isa<FExtModuleOp>(collidingOp) && isa<FModuleOp>(incomingOp)) ||
|
||||
(isa<FExtModuleOp>(incomingOp) && isa<FModuleOp>(collidingOp))))
|
||||
return failure();
|
||||
auto [definition, declaration] = isa<FModuleOp>(collidingOp)
|
||||
? std::pair(collidingOp, incomingOp)
|
||||
: std::pair(incomingOp, collidingOp);
|
||||
if (!definition.isPublic())
|
||||
return definition->emitOpError("should be a public symbol");
|
||||
if (!declaration.isPublic())
|
||||
return declaration->emitOpError("should be a public symbol");
|
||||
|
||||
constexpr const StringRef attrsToCompare[] = {
|
||||
"portDirections", "portSymbols", "portNames", "portTypes", "layers"};
|
||||
auto allAttrsMatch = all_of(attrsToCompare, [&](StringRef attr) {
|
||||
return definition->getAttr(attr) == declaration->getAttr(attr);
|
||||
});
|
||||
|
||||
if (!allAttrsMatch)
|
||||
return false;
|
||||
|
||||
declaration->erase();
|
||||
return declaration == incomingOp;
|
||||
}
|
||||
|
||||
LogicalResult LinkCircuitsPass::mergeCircuits() {
|
||||
auto module = getOperation();
|
||||
|
||||
SmallVector<CircuitOp> circuits;
|
||||
for (CircuitOp circuitOp : module.getOps<CircuitOp>())
|
||||
circuits.push_back(circuitOp);
|
||||
|
||||
auto builder = OpBuilder(module);
|
||||
builder.setInsertionPointToEnd(module.getBody());
|
||||
auto mergedCircuit = builder.create<CircuitOp>(
|
||||
module.getLoc(), StringAttr::get(&getContext(), baseCircuitName));
|
||||
SmallVector<Attribute> mergedAnnotations;
|
||||
|
||||
for (auto circuit : circuits) {
|
||||
if (!noMangle)
|
||||
if (failed(mangleCircuitSymbols(circuit)))
|
||||
return circuit->emitError("failed to mangle private symbol");
|
||||
|
||||
// TODO: other circuit attributes (such as enable_layers...)
|
||||
llvm::transform(circuit.getAnnotations().getValue(),
|
||||
std::back_inserter(mergedAnnotations), [&](Attribute attr) {
|
||||
return transformAnnotationTarget(
|
||||
cast<DictionaryAttr>(attr),
|
||||
[&](TokenAnnoTarget &tokens) {
|
||||
tokens.circuit = mergedCircuit.getName();
|
||||
return tokens;
|
||||
});
|
||||
});
|
||||
|
||||
// reconstruct symbol table after each merge
|
||||
auto mergedSymbolTable = SymbolTable(mergedCircuit.getOperation());
|
||||
|
||||
SmallVector<Operation *> opsToMove;
|
||||
for (auto &op : circuit.getOps())
|
||||
opsToMove.push_back(&op);
|
||||
for (auto *op : opsToMove) {
|
||||
if (auto symbolOp = dyn_cast<SymbolOpInterface>(op))
|
||||
if (auto collidingOp = cast_if_present<SymbolOpInterface>(
|
||||
mergedSymbolTable.lookup(symbolOp.getNameAttr()))) {
|
||||
auto opErased = linkExtmodule(collidingOp, symbolOp);
|
||||
if (failed(opErased))
|
||||
return mergedCircuit->emitError("has colliding symbol " +
|
||||
symbolOp.getName() +
|
||||
" which cannot be merged");
|
||||
if (opErased.value())
|
||||
continue;
|
||||
}
|
||||
|
||||
op->moveBefore(mergedCircuit.getBodyBlock(),
|
||||
mergedCircuit.getBodyBlock()->end());
|
||||
}
|
||||
|
||||
circuit->erase();
|
||||
}
|
||||
|
||||
mergedCircuit.setAnnotationsAttr(
|
||||
ArrayAttr::get(mergedCircuit.getContext(), mergedAnnotations));
|
||||
|
||||
return mlir::detail::verifySymbolTable(mergedCircuit);
|
||||
}
|
||||
|
||||
void LinkCircuitsPass::runOnOperation() {
|
||||
if (failed(mergeCircuits()))
|
||||
signalPassFailure();
|
||||
}
|
||||
|
||||
std::unique_ptr<Pass>
|
||||
circt::firrtl::createLinkCircuitsPass(StringRef baseCircuitName,
|
||||
bool noMangle) {
|
||||
return std::make_unique<LinkCircuitsPass>(baseCircuitName, noMangle);
|
||||
}
|
|
@ -35,6 +35,7 @@ set(CIRCT_TEST_DEPENDS
|
|||
circt-translate
|
||||
circt-reduce
|
||||
handshake-runner
|
||||
firld
|
||||
firtool
|
||||
hlstool
|
||||
kanagawatool
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// RUN: split-file %s %t
|
||||
// RUN: firld %t/Outer.mlir %t/Inner.mlir --base-circuit Outer | FileCheck %s
|
||||
|
||||
//--- Outer.mlir
|
||||
module {
|
||||
// CHECK: firrtl.circuit "Outer"
|
||||
firrtl.circuit "Outer" {
|
||||
// CHECK: firrtl.module @Outer(in %i: !firrtl.uint<32>, out %o: !firrtl.uint<32>) attributes {convention = #firrtl<convention scalarized>}
|
||||
firrtl.module @Outer(in %i: !firrtl.uint<32>, out %o: !firrtl.uint<32>) attributes {convention = #firrtl<convention scalarized>} {
|
||||
%io = firrtl.wire : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%probe = firrtl.wire : !firrtl.bundle<>
|
||||
%0 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %0, %i : !firrtl.uint<32>
|
||||
%1 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %o, %1 : !firrtl.uint<32>
|
||||
%inner_i, %inner_o = firrtl.instance inner interesting_name @Inner(in i: !firrtl.uint<32>, out o: !firrtl.uint<32>)
|
||||
%inner_io = firrtl.wire : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%inner_probe = firrtl.wire : !firrtl.bundle<>
|
||||
%2 = firrtl.subfield %inner_io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %inner_i, %2 : !firrtl.uint<32>
|
||||
%3 = firrtl.subfield %inner_io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %3, %inner_o : !firrtl.uint<32>
|
||||
%4 = firrtl.subfield %inner_io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%5 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %4, %5 : !firrtl.uint<32>
|
||||
%6 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%7 = firrtl.subfield %inner_io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %6, %7 : !firrtl.uint<32>
|
||||
}
|
||||
// CHECK-NOT: firrtl.extmodule
|
||||
firrtl.extmodule @Inner(in i: !firrtl.uint<32>, out o: !firrtl.uint<32>) attributes {defname = "Inner"}
|
||||
}
|
||||
}
|
||||
|
||||
//--- Inner.mlir
|
||||
module {
|
||||
// CHECK-NOT: firrtl.circuit "Inner"
|
||||
firrtl.circuit "Inner" {
|
||||
// CHECK: firrtl.module @Inner(in %i: !firrtl.uint<32>, out %o: !firrtl.uint<32>) attributes {convention = #firrtl<convention scalarized>}
|
||||
firrtl.module @Inner(in %i: !firrtl.uint<32>, out %o: !firrtl.uint<32>) attributes {convention = #firrtl<convention scalarized>} {
|
||||
%io = firrtl.wire : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%probe = firrtl.wire : !firrtl.bundle<>
|
||||
%0 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %0, %i : !firrtl.uint<32>
|
||||
%1 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %o, %1 : !firrtl.uint<32>
|
||||
%2 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%3 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %2, %3 : !firrtl.uint<32>
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// RUN: split-file %s %t
|
||||
// RUN: firld %t/Outer.mlir %t/Inner.mlir --base-circuit Outer | FileCheck %s
|
||||
|
||||
// CHECK: firrtl.circuit "Outer" attributes {annotations = [{class = "circt.test", target = "~Outer|Outer>io"}, {class = "circt.test", target = "~Outer|Inner>io"}]}
|
||||
//--- Outer.mlir
|
||||
module {
|
||||
firrtl.circuit "Outer" attributes {annotations = [{class = "circt.test", target = "~Outer|Outer>io"}]} {
|
||||
firrtl.module @Outer(in %i: !firrtl.uint<32>, out %o: !firrtl.uint<32>) attributes {convention = #firrtl<convention scalarized>} {
|
||||
%io = firrtl.wire : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%probe = firrtl.wire : !firrtl.bundle<>
|
||||
%0 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %0, %i : !firrtl.uint<32>
|
||||
%1 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %o, %1 : !firrtl.uint<32>
|
||||
%inner_i, %inner_o = firrtl.instance inner interesting_name @Inner(in i: !firrtl.uint<32>, out o: !firrtl.uint<32>)
|
||||
%inner_io = firrtl.wire : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%inner_probe = firrtl.wire : !firrtl.bundle<>
|
||||
%2 = firrtl.subfield %inner_io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %inner_i, %2 : !firrtl.uint<32>
|
||||
%3 = firrtl.subfield %inner_io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %3, %inner_o : !firrtl.uint<32>
|
||||
%4 = firrtl.subfield %inner_io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%5 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %4, %5 : !firrtl.uint<32>
|
||||
%6 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%7 = firrtl.subfield %inner_io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %6, %7 : !firrtl.uint<32>
|
||||
}
|
||||
firrtl.extmodule @Inner(in i: !firrtl.uint<32>, out o: !firrtl.uint<32>) attributes {defname = "Inner"}
|
||||
}
|
||||
}
|
||||
|
||||
//--- Inner.mlir
|
||||
module {
|
||||
firrtl.circuit "Inner" attributes {annotations = [{class = "circt.test", target = "~Inner|Inner>io"}]} {
|
||||
firrtl.module @Inner(in %i: !firrtl.uint<32>, out %o: !firrtl.uint<32>) attributes {convention = #firrtl<convention scalarized>} {
|
||||
%io = firrtl.wire : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%probe = firrtl.wire : !firrtl.bundle<>
|
||||
%0 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %0, %i : !firrtl.uint<32>
|
||||
%1 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %o, %1 : !firrtl.uint<32>
|
||||
%2 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%3 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %2, %3 : !firrtl.uint<32>
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// RUN: firld %s --base-circuit Outer | FileCheck %s
|
||||
|
||||
module {
|
||||
// CHECK: {annotations = [{class = "circt.test", target = "~Outer|Outer_Inner>io"}]}
|
||||
firrtl.circuit "Outer" attributes {annotations = [{class = "circt.test", target = "~Outer|Inner>io"}]} {
|
||||
firrtl.module @Outer(in %i: !firrtl.uint<32>, out %o: !firrtl.uint<32>) attributes {convention = #firrtl<convention scalarized>} {
|
||||
%io = firrtl.wire : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%probe = firrtl.wire : !firrtl.bundle<>
|
||||
%0 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %0, %i : !firrtl.uint<32>
|
||||
%1 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %o, %1 : !firrtl.uint<32>
|
||||
%inner_i, %inner_o = firrtl.instance inner interesting_name @Inner(in i: !firrtl.uint<32>, out o: !firrtl.uint<32>)
|
||||
%inner_io = firrtl.wire : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%inner_probe = firrtl.wire : !firrtl.bundle<>
|
||||
%2 = firrtl.subfield %inner_io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %inner_i, %2 : !firrtl.uint<32>
|
||||
%3 = firrtl.subfield %inner_io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %3, %inner_o : !firrtl.uint<32>
|
||||
%4 = firrtl.subfield %inner_io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%5 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %4, %5 : !firrtl.uint<32>
|
||||
%6 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%7 = firrtl.subfield %inner_io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %6, %7 : !firrtl.uint<32>
|
||||
}
|
||||
|
||||
firrtl.module private @Inner(in %i: !firrtl.uint<32>, out %o: !firrtl.uint<32>) attributes {convention = #firrtl<convention scalarized>} {
|
||||
%io = firrtl.wire : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%probe = firrtl.wire : !firrtl.bundle<>
|
||||
%0 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %0, %i : !firrtl.uint<32>
|
||||
%1 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %o, %1 : !firrtl.uint<32>
|
||||
%2 = firrtl.subfield %io[o] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
%3 = firrtl.subfield %io[i] : !firrtl.bundle<i flip: uint<32>, o: uint<32>>
|
||||
firrtl.connect %2, %3 : !firrtl.uint<32>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// RUN: firld %s --base-circuit Outer | FileCheck %s
|
||||
|
||||
module {
|
||||
firrtl.circuit "Outer" {
|
||||
// CHECK: firrtl.class private @Outer_InnerOM
|
||||
firrtl.class private @InnerOM(out %width: !firrtl.integer) {
|
||||
%0 = firrtl.integer 16
|
||||
firrtl.propassign %width, %0 : !firrtl.integer
|
||||
}
|
||||
// CHECK: firrtl.module private @Outer_Inner(in %in: !firrtl.uint<16>, out %out: !firrtl.uint<16>, out %om: !firrtl.class<@Outer_InnerOM(out width: !firrtl.integer)>) {
|
||||
firrtl.module private @Inner(in %in: !firrtl.uint<16>, out %out: !firrtl.uint<16>, out %om: !firrtl.class<@InnerOM(out width: !firrtl.integer)>) {
|
||||
firrtl.matchingconnect %out, %in : !firrtl.uint<16>
|
||||
// CHECK: %omInstance = firrtl.object @Outer_InnerOM(out width: !firrtl.integer)
|
||||
%omInstance = firrtl.object @InnerOM(out width: !firrtl.integer)
|
||||
// CHECK: firrtl.propassign %om, %omInstance : !firrtl.class<@Outer_InnerOM(out width: !firrtl.integer)>
|
||||
firrtl.propassign %om, %omInstance : !firrtl.class<@InnerOM(out width: !firrtl.integer)>
|
||||
}
|
||||
firrtl.module @Outer() attributes {convention = #firrtl<convention scalarized>} {
|
||||
// CHECK: %Inner_in, %Inner_out, %Inner_om = firrtl.instance Inner interesting_name @Outer_Inner(in in: !firrtl.uint<16>, out out: !firrtl.uint<16>, out om: !firrtl.class<@Outer_InnerOM(out width: !firrtl.integer)>)
|
||||
%Inner_in, %Inner_out, %Inner_om = firrtl.instance Inner interesting_name @Inner(in in: !firrtl.uint<16>, out out: !firrtl.uint<16>, out om: !firrtl.class<@InnerOM(out width: !firrtl.integer)>)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,8 +62,8 @@ tools = [
|
|||
'circt-capi-firrtl-test', 'circt-capi-firtool-test',
|
||||
'circt-capi-rtg-pipelines-test', 'circt-capi-rtg-test',
|
||||
'circt-capi-rtgtest-test', 'circt-dis', 'circt-lec', 'circt-reduce',
|
||||
'circt-synth', 'circt-test', 'circt-translate', 'firtool', 'hlstool',
|
||||
'om-linker', 'kanagawatool'
|
||||
'circt-synth', 'circt-test', 'circt-translate', 'firld', 'firtool',
|
||||
'hlstool', 'om-linker', 'kanagawatool'
|
||||
]
|
||||
|
||||
if "CIRCT_OPT_CHECK_IR_ROUNDTRIP" in os.environ:
|
||||
|
|
|
@ -11,6 +11,7 @@ add_subdirectory(circt-rtl-sim)
|
|||
add_subdirectory(circt-synth)
|
||||
add_subdirectory(circt-test)
|
||||
add_subdirectory(circt-translate)
|
||||
add_subdirectory(firld)
|
||||
add_subdirectory(firtool)
|
||||
add_subdirectory(handshake-runner)
|
||||
add_subdirectory(hlstool)
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
set(LLVM_LINK_COMPONENTS
|
||||
Support
|
||||
)
|
||||
|
||||
get_property(dialect_libs GLOBAL PROPERTY CIRCT_DIALECT_LIBS)
|
||||
get_property(mlir_dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
|
||||
|
||||
set(LIBS
|
||||
${dialect_libs}
|
||||
${mlir_dialect_libs}
|
||||
|
||||
MLIRIR
|
||||
MLIRBytecodeReader
|
||||
MLIRBytecodeWriter
|
||||
MLIRParser
|
||||
MLIRSupport
|
||||
)
|
||||
|
||||
add_circt_tool(firld firld.cpp DEPENDS ${LIBS})
|
||||
target_link_libraries(firld PRIVATE ${LIBS})
|
||||
|
||||
llvm_update_compile_flags(firld)
|
||||
mlir_check_all_link_libraries(firld)
|
|
@ -0,0 +1,186 @@
|
|||
//===- firld.cpp - Link multiple circuits together ------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Link FIRRTL circuits (.mlir or .mlirbc files) into one.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "circt/Dialect/FIRRTL/FIRParser.h"
|
||||
#include "circt/Dialect/FIRRTL/FIRRTLOps.h"
|
||||
#include "circt/Dialect/FIRRTL/Passes.h"
|
||||
#include "circt/InitAllDialects.h"
|
||||
#include "circt/Support/Version.h"
|
||||
#include "mlir/Bytecode/BytecodeReader.h"
|
||||
#include "mlir/Bytecode/BytecodeWriter.h"
|
||||
#include "mlir/Dialect/Transform/IR/Utils.h"
|
||||
#include "mlir/IR/Block.h"
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "mlir/IR/BuiltinAttributes.h"
|
||||
#include "mlir/IR/Diagnostics.h"
|
||||
#include "mlir/IR/Location.h"
|
||||
#include "mlir/IR/MLIRContext.h"
|
||||
#include "mlir/IR/Operation.h"
|
||||
#include "mlir/IR/OperationSupport.h"
|
||||
#include "mlir/Parser/Parser.h"
|
||||
#include "mlir/Pass/PassManager.h"
|
||||
#include "mlir/Support/FileUtilities.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/InitLLVM.h"
|
||||
#include "llvm/Support/LogicalResult.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/ToolOutputFile.h"
|
||||
#include "llvm/Support/WithColor.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace mlir;
|
||||
using namespace circt;
|
||||
|
||||
static constexpr const char toolName[] = "firld";
|
||||
static cl::OptionCategory mainCategory("firld Options");
|
||||
|
||||
static cl::list<std::string> inputFilenames(cl::Positional,
|
||||
cl::desc("<input files>"),
|
||||
cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<std::string> baseCircuitName("base-circuit", cl::Required,
|
||||
cl::desc("Base circuit name"),
|
||||
cl::value_desc("circuit name"),
|
||||
cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<std::string> outputFilename("o",
|
||||
cl::desc("Override output filename"),
|
||||
cl::value_desc("filename"),
|
||||
cl::init("-"),
|
||||
cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
emitBytecode("emit-bytecode",
|
||||
cl::desc("Emit bytecode when generating MLIR output"),
|
||||
cl::init(false), cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool> force("f", cl::desc("Enable binary output on terminals"),
|
||||
cl::init(false), cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
noMangle("no-mangle", cl::desc("Do not perform private symbol mangling"),
|
||||
cl::init(false), cl::cat(mainCategory));
|
||||
|
||||
static LogicalResult emitError(const Twine &err) {
|
||||
WithColor::error(errs(), toolName) << err << "\n";
|
||||
return failure();
|
||||
}
|
||||
|
||||
static bool checkBytecodeOutputToConsole(raw_ostream &os) {
|
||||
if (os.is_displayed()) {
|
||||
WithColor::warning(errs(), toolName)
|
||||
<< "you're attempting to print out a bytecode file."
|
||||
<< " This is inadvisable as it may cause display problems\n";
|
||||
WithColor::remark(errs(), toolName)
|
||||
<< "if you really want to do this, force output with the `-f` option\n";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static LogicalResult printOp(Operation *op, raw_ostream &os) {
|
||||
if (emitBytecode && (force || !checkBytecodeOutputToConsole(os)))
|
||||
return writeBytecodeToFile(op, os, BytecodeWriterConfig(getCirctVersion()));
|
||||
op->print(os);
|
||||
return success();
|
||||
}
|
||||
|
||||
static LogicalResult mergeModules(MLIRContext &context,
|
||||
SmallVector<OwningOpRef<ModuleOp>> &&modules,
|
||||
ModuleOp mergedModule) {
|
||||
// destroy original modules after merge
|
||||
auto modulesToMerge = std::move(modules);
|
||||
|
||||
// firrtl.circuit doesn't have sym_name hence we have no need to process
|
||||
// SymbolTable
|
||||
SmallVector<Operation *> opsToMove;
|
||||
std::set<StringRef> circuitNames;
|
||||
for (auto &module : modulesToMerge) {
|
||||
for (auto &op : module->getBody()->getOperations()) {
|
||||
if (auto circuit = dyn_cast<firrtl::CircuitOp>(op)) {
|
||||
opsToMove.push_back(&op);
|
||||
circuitNames.insert(
|
||||
cast<StringAttr>(circuit->getAttr("name")).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (circuitNames.size() != opsToMove.size())
|
||||
return emitError(
|
||||
"Duplicate circuit names detected, all circuit names must be unique");
|
||||
if (circuitNames.find(baseCircuitName) == circuitNames.end())
|
||||
return emitError("Base circuit '" + baseCircuitName +
|
||||
"' not found in circuit names");
|
||||
|
||||
for (auto *op : opsToMove)
|
||||
op->moveBefore(mergedModule.getBody(), mergedModule.getBody()->end());
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
static LogicalResult execute(MLIRContext &context) {
|
||||
std::string errorMessage;
|
||||
SmallVector<OwningOpRef<ModuleOp>> modules;
|
||||
for (const auto &inputFile : inputFilenames) {
|
||||
auto input = openInputFile(inputFile, &errorMessage);
|
||||
if (!input)
|
||||
return emitError(errorMessage);
|
||||
|
||||
SourceMgr srcMgr;
|
||||
srcMgr.AddNewSourceBuffer(std::move(input), SMLoc());
|
||||
if (auto module = parseSourceFile<ModuleOp>(srcMgr, &context)) {
|
||||
modules.push_back(std::move(module));
|
||||
} else
|
||||
return emitError("Failed to parse input file:" + inputFile);
|
||||
}
|
||||
|
||||
auto mergedModule =
|
||||
OwningOpRef<ModuleOp>(ModuleOp::create(UnknownLoc::get(&context)));
|
||||
if (failed(mergeModules(context, std::move(modules), mergedModule.get())))
|
||||
return emitError("Failed to merge MLIR modules");
|
||||
|
||||
PassManager pm(&context);
|
||||
pm.addPass(firrtl::createLinkCircuitsPass(baseCircuitName, noMangle));
|
||||
if (failed(pm.run(mergedModule.get())))
|
||||
return emitError("Failed to link circuits together");
|
||||
|
||||
std::optional<std::unique_ptr<ToolOutputFile>> outputFile;
|
||||
outputFile.emplace(openOutputFile(outputFilename, &errorMessage));
|
||||
if (!(*outputFile))
|
||||
return emitError(errorMessage);
|
||||
|
||||
if (failed(printOp(*mergedModule, (*outputFile)->os())))
|
||||
return emitError("Failed to print output to" + outputFilename);
|
||||
if (outputFile.has_value())
|
||||
(*outputFile)->keep();
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
InitLLVM y(argc, argv);
|
||||
setBugReportMsg(circtBugReportMsg);
|
||||
|
||||
DialectRegistry registry;
|
||||
circt::registerAllDialects(registry);
|
||||
|
||||
cl::HideUnrelatedOptions({&mainCategory, &getColorCategory()});
|
||||
cl::ParseCommandLineOptions(argc, argv, "FIRRTL Circuits Linker\n");
|
||||
|
||||
MLIRContext context(registry);
|
||||
exit(failed(execute(context)));
|
||||
}
|
Loading…
Reference in New Issue