[firld] Add firld to link FIRRTL circuits (#8561)

This commit is contained in:
unlsycn 2025-06-24 12:44:54 +08:00 committed by GitHub
parent 6227f4aec0
commit 5dc56952a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 651 additions and 2 deletions

View File

@ -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();

View File

@ -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.";

View File

@ -25,6 +25,7 @@ add_circt_dialect_library(CIRCTFIRRTLTransforms
InjectDUTHierarchy.cpp
InnerSymbolDCE.cpp
LegacyWiring.cpp
LinkCircuits.cpp
Lint.cpp
LayerMerge.cpp
LayerSink.cpp

View File

@ -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);
}

View File

@ -35,6 +35,7 @@ set(CIRCT_TEST_DEPENDS
circt-translate
circt-reduce
handshake-runner
firld
firtool
hlstool
kanagawatool

View File

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

View File

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

View File

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

23
test/firld/mangle-om.mlir Normal file
View File

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

View File

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

View File

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

View File

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

186
tools/firld/firld.cpp Normal file
View File

@ -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)));
}