mirror of https://github.com/llvm/circt.git
[FIRRTL] Add ExtractInstances pass (#3017)
Add a `ExtractInstances` pass which implements a combined version of the Scala `ExtractBlackBoxes`, `ExtractClockGates`, and `ExtractSeqMems` passes. The pass is split into two phases: As a first step it traverses modules and instances in the design and marks the ones appropriately annotated for extraction. In a second step it repeatedly pushes the marked instances up one level of the hierarchy until they reach the desired parent module (usually indicated with a `MarkDUTAnnotation`). The extracted instances are then optionally grouped into a submodule at their extracted location. Finally the pass also emits a text file indicating where the extracted instances were originally located in the design.
This commit is contained in:
parent
da0c16a8ac
commit
30dad68da3
|
@ -876,6 +876,90 @@ Example:
|
|||
}
|
||||
```
|
||||
|
||||
### Instance Extraction
|
||||
|
||||
#### ExtractBlackBoxAnnotation
|
||||
|
||||
| Property | Type | Description |
|
||||
| -------- | ---- | ----------- |
|
||||
| class | string | `sifive.enterprise.firrtl.ExtractBlackBoxAnnotation` |
|
||||
| target | string | Reference target to the instance to be extracted |
|
||||
| filename | string | Output file to be filled with the applied hierarchy changes |
|
||||
| prefix | string | Prefix for the extracted instance |
|
||||
| dest | string | Name of an optional wrapper module under which to group extracted instances |
|
||||
|
||||
This annotation causes the `ExtractInstances` pass to move the annotated
|
||||
instance, or all instances if the annotation is on a module, upwards in the
|
||||
hierarchy. If the `dest` field is present and non-empty, the instances are
|
||||
placed in a module underneath the DUT (marked by `MarkDUTAnnotation`) with the
|
||||
name provided in that field. If the `dest` field is empty, the instances are
|
||||
extracted out of the DUT, such that the DUT gains additional ports that
|
||||
correspond to the extracted instance ports. This allows the DUT to be
|
||||
instantiated and custom implementations for the extracted instances to be
|
||||
provided at the instantiation site. Instances are never extracted out of the
|
||||
root module of the design.
|
||||
|
||||
Applies to modules and instances.
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"class": "sifive.enterprise.firrtl.ExtractBlackBoxAnnotation",
|
||||
"target": "~TestHarness|MyBlackBox",
|
||||
"filename": "BlackBoxes.txt",
|
||||
"prefix": "bb",
|
||||
"dest": "BlackBoxes" // optional
|
||||
}
|
||||
```
|
||||
|
||||
#### ExtractClockGatesFileAnnotation
|
||||
|
||||
| Property | Type | Description |
|
||||
| -------- | ---- | ----------- |
|
||||
| class | string | `sifive.enterprise.firrtl.ExtractClockGatesFileAnnotation` |
|
||||
| filename | string | Output file to be filled with the applied hierarchy changes |
|
||||
| group | string | Name of an optional wrapper module under which to group extracted instances |
|
||||
|
||||
This annotation causes the `ExtractInstances` pass to move instances of
|
||||
extmodules with defname `EICG_wrapper` upwards in the hierarchy, either out of
|
||||
the DUT if `group` is omitted or empty, or into a submodule of the DUT with the
|
||||
name given in `group`. The wiring prefix is hard-coded to `clock_gate`.
|
||||
|
||||
Applies to the circuit.
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"class": "sifive.enterprise.firrtl.ExtractClockGatesFileAnnotation",
|
||||
"filename": "ClockGates.txt",
|
||||
"group": "ClockGates" // optional
|
||||
}
|
||||
```
|
||||
|
||||
#### ExtractSeqMemsFileAnnotation
|
||||
|
||||
| Property | Type | Description |
|
||||
| -------- | ---- | ----------- |
|
||||
| class | string | `sifive.enterprise.firrtl.ExtractSeqMemsFileAnnotation` |
|
||||
| filename | string | Output file to be filled with the applied hierarchy changes |
|
||||
| group | string | Name of an optional wrapper module under which to group extracted instances |
|
||||
|
||||
This annotation causes the `ExtractInstances` pass to move memory instances
|
||||
upwards in the hierarchy, either out of the DUT if `group` is omitted or empty,
|
||||
or into a submodule of the DUT with the name given in `group`. The wiring
|
||||
prefix is hard-coded to `mem_wiring`.
|
||||
|
||||
Applies to the circuit.
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"class": "sifive.enterprise.firrtl.ExtractSeqMemsFileAnnotation",
|
||||
"filename": "SeqMems.txt",
|
||||
"group": "SeqMems" // optional
|
||||
}
|
||||
```
|
||||
|
||||
## FIRRTL specific attributes applied to HW Modules
|
||||
|
||||
### Design Under Test
|
||||
|
|
|
@ -65,6 +65,14 @@ constexpr const char *testbenchDirAnnoClass =
|
|||
constexpr const char *injectDUTHierarchyAnnoClass =
|
||||
"sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation";
|
||||
|
||||
// Instance Extraction
|
||||
constexpr const char *extractBlackBoxAnnoClass =
|
||||
"sifive.enterprise.firrtl.ExtractBlackBoxAnnotation";
|
||||
constexpr const char *extractClockGatesAnnoClass =
|
||||
"sifive.enterprise.firrtl.ExtractClockGatesFileAnnotation";
|
||||
constexpr const char *extractSeqMemsAnnoClass =
|
||||
"sifive.enterprise.firrtl.ExtractSeqMemsFileAnnotation";
|
||||
|
||||
} // namespace firrtl
|
||||
} // namespace circt
|
||||
|
||||
|
|
|
@ -93,6 +93,8 @@ createMergeConnectionsPass(bool enableAggressiveMerging = false);
|
|||
|
||||
std::unique_ptr<mlir::Pass> createInjectDUTHierarchyPass();
|
||||
|
||||
std::unique_ptr<mlir::Pass> createExtractInstancesPass();
|
||||
|
||||
/// Generate the code for registering passes.
|
||||
#define GEN_PASS_REGISTRATION
|
||||
#include "circt/Dialect/FIRRTL/Passes.h.inc"
|
||||
|
|
|
@ -431,4 +431,20 @@ def InjectDUTHierarchy : Pass<"firrtl-inject-dut-hier", "firrtl::CircuitOp"> {
|
|||
let constructor = "circt::firrtl::createInjectDUTHierarchyPass()";
|
||||
}
|
||||
|
||||
def ExtractInstances : Pass<"firrtl-extract-instances", "firrtl::CircuitOp"> {
|
||||
let summary = "Move annotated instances upwards in the module hierarchy";
|
||||
let description = [{
|
||||
This pass takes instances in the design annotated with one out of a
|
||||
particular set of annotations and pulls them upwards to a location further
|
||||
up in the module hierarchy.
|
||||
|
||||
The annotations that control the behaviour of this pass are:
|
||||
- `MarkDUTAnnotation`
|
||||
- `ExtractBlackBoxAnnotation`
|
||||
- `ExtractClockGatesFileAnnotation`
|
||||
}];
|
||||
let constructor = "circt::firrtl::createExtractInstancesPass()";
|
||||
let dependentDialects = ["sv::SVDialect", "circt::hw::HWDialect"];
|
||||
}
|
||||
|
||||
#endif // CIRCT_DIALECT_FIRRTL_PASSES_TD
|
||||
|
|
|
@ -6,6 +6,7 @@ add_circt_dialect_library(CIRCTFIRRTLTransforms
|
|||
Dedup.cpp
|
||||
EmitOMIR.cpp
|
||||
ExpandWhens.cpp
|
||||
ExtractInstances.cpp
|
||||
FlattenMemory.cpp
|
||||
GrandCentral.cpp
|
||||
GrandCentralTaps.cpp
|
||||
|
|
|
@ -0,0 +1,930 @@
|
|||
//===- ExtractInstances.cpp - Move instances up the hierarchy ---*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Moves annotated instances upwards in the module hierarchy. Corresponds to the
|
||||
// `ExtractBlackBoxes`, `ExtractClockGates`, and `ExtractSeqMems` passes in the
|
||||
// Scala FIRRTL implementation.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "PassDetails.h"
|
||||
#include "circt/Dialect/FIRRTL/AnnotationDetails.h"
|
||||
#include "circt/Dialect/FIRRTL/FIRRTLAnnotations.h"
|
||||
#include "circt/Dialect/FIRRTL/FIRRTLInstanceGraph.h"
|
||||
#include "circt/Dialect/FIRRTL/NLATable.h"
|
||||
#include "circt/Dialect/FIRRTL/Namespace.h"
|
||||
#include "circt/Dialect/FIRRTL/Passes.h"
|
||||
#include "circt/Dialect/HW/HWAttributes.h"
|
||||
#include "circt/Dialect/HW/HWDialect.h"
|
||||
#include "circt/Dialect/SV/SVOps.h"
|
||||
#include "circt/Support/Path.h"
|
||||
#include "mlir/IR/Attributes.h"
|
||||
#include "mlir/IR/ImplicitLocOpBuilder.h"
|
||||
#include "mlir/Support/FileUtilities.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
#define DEBUG_TYPE "firrtl-extract-instances"
|
||||
|
||||
using namespace circt;
|
||||
using namespace firrtl;
|
||||
using hw::InnerRefAttr;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Pass Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
/// All information necessary to move instances about.
|
||||
struct ExtractionInfo {
|
||||
/// A filename into which the performed hierarchy modifications are emitted.
|
||||
StringRef traceFilename;
|
||||
/// A prefix to attach to the wiring generated by the extraction.
|
||||
StringRef prefix;
|
||||
/// Optional name of the wrapper module that will hold the moved instance.
|
||||
StringRef wrapperModule;
|
||||
/// Whether the extraction should stop at the root of the DUT instead of going
|
||||
/// past that and extracting into the test harness.
|
||||
bool stopAtDUT;
|
||||
};
|
||||
|
||||
struct ExtractInstancesPass
|
||||
: public ExtractInstancesBase<ExtractInstancesPass> {
|
||||
void runOnOperation() override;
|
||||
void collectAnnos();
|
||||
void collectAnno(InstanceOp inst, Annotation anno);
|
||||
void extractInstances();
|
||||
void groupInstances();
|
||||
void createTraceFiles();
|
||||
|
||||
/// Get the cached namespace for a module.
|
||||
ModuleNamespace &getModuleNamespace(FModuleLike module) {
|
||||
auto it = moduleNamespaces.find(module);
|
||||
if (it != moduleNamespaces.end())
|
||||
return it->second;
|
||||
return moduleNamespaces.try_emplace(module, ModuleNamespace(module))
|
||||
.first->second;
|
||||
}
|
||||
|
||||
/// Returns an operation's `inner_sym`, adding one if necessary.
|
||||
StringAttr getOrAddInnerSym(Operation *op) {
|
||||
auto attr = op->getAttrOfType<StringAttr>("inner_sym");
|
||||
if (attr)
|
||||
return attr;
|
||||
auto module = op->getParentOfType<FModuleOp>();
|
||||
auto name = getModuleNamespace(module).newName("extraction_sym");
|
||||
attr = StringAttr::get(op->getContext(), name);
|
||||
op->setAttr("inner_sym", attr);
|
||||
return attr;
|
||||
}
|
||||
|
||||
/// Obtain an inner reference to an operation, possibly adding an `inner_sym`
|
||||
/// to that operation.
|
||||
InnerRefAttr getInnerRefTo(Operation *op) {
|
||||
return InnerRefAttr::get(
|
||||
SymbolTable::getSymbolName(op->getParentOfType<FModuleOp>()),
|
||||
getOrAddInnerSym(op));
|
||||
}
|
||||
|
||||
bool anythingChanged;
|
||||
bool anyFailures;
|
||||
|
||||
InstanceGraph *instanceGraph = nullptr;
|
||||
|
||||
/// The modules in the design that are annotated with one or more annotations
|
||||
/// relevant for instance extraction.
|
||||
DenseMap<Operation *, SmallVector<Annotation, 1>> annotatedModules;
|
||||
|
||||
/// All modules that are marked as DUT themselves. Realistically this is only
|
||||
/// ever one module in the design.
|
||||
DenseSet<Operation *> dutRootModules;
|
||||
/// All modules that are marked as DUT themselves, or that have a DUT parent
|
||||
/// module.
|
||||
DenseSet<Operation *> dutModules;
|
||||
/// All DUT module names.
|
||||
DenseSet<Attribute> dutModuleNames;
|
||||
|
||||
/// A worklist of instances that need to be moved.
|
||||
SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractionWorklist;
|
||||
|
||||
/// The path along which instances have been extracted. This essentially
|
||||
/// documents the original location of the instance in reverse. Every push
|
||||
/// upwards in the hierarchy adds another entry to this path documenting along
|
||||
/// which instantiation path each instance was extracted.
|
||||
DenseMap<Operation *, SmallVector<InnerRefAttr>> extractionPaths;
|
||||
|
||||
/// A map of the original parent modules of instances before they were
|
||||
/// extracted. This is used in a corner case during trace file emission.
|
||||
DenseMap<Operation *, StringAttr> originalInstanceParents;
|
||||
|
||||
/// All extracted instances in their position after moving upwards in the
|
||||
/// hierarchy, but before being grouped into an optional submodule.
|
||||
SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractedInstances;
|
||||
|
||||
// The uniquified wiring prefix for each instance.
|
||||
DenseMap<Operation *, SmallString<16>> instPrefices;
|
||||
|
||||
/// The current circuit namespace valid within the call to `runOnOperation`.
|
||||
CircuitNamespace circuitNamespace;
|
||||
/// Cached module namespaces.
|
||||
DenseMap<Operation *, ModuleNamespace> moduleNamespaces;
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
/// Emit the annotated source code for black boxes in a circuit.
|
||||
void ExtractInstancesPass::runOnOperation() {
|
||||
CircuitOp circuitOp = getOperation();
|
||||
anythingChanged = false;
|
||||
anyFailures = false;
|
||||
annotatedModules.clear();
|
||||
dutRootModules.clear();
|
||||
dutModules.clear();
|
||||
extractionWorklist.clear();
|
||||
extractionPaths.clear();
|
||||
originalInstanceParents.clear();
|
||||
extractedInstances.clear();
|
||||
instPrefices.clear();
|
||||
moduleNamespaces.clear();
|
||||
circuitNamespace.clear();
|
||||
circuitNamespace.add(circuitOp);
|
||||
|
||||
// Walk the IR and gather all the annotations relevant for extraction that
|
||||
// appear on instances and the instantiated modules.
|
||||
instanceGraph = &getAnalysis<InstanceGraph>();
|
||||
collectAnnos();
|
||||
if (anyFailures)
|
||||
return signalPassFailure();
|
||||
|
||||
// Actually move instances upwards.
|
||||
extractInstances();
|
||||
if (anyFailures)
|
||||
return signalPassFailure();
|
||||
|
||||
// Group instances into submodules, if requested.
|
||||
groupInstances();
|
||||
if (anyFailures)
|
||||
return signalPassFailure();
|
||||
|
||||
// Generate the trace files that list where each instance was extracted from.
|
||||
createTraceFiles();
|
||||
if (anyFailures)
|
||||
return signalPassFailure();
|
||||
|
||||
// If nothing has changed we can preseve the analysis.
|
||||
LLVM_DEBUG(llvm::dbgs() << "\n");
|
||||
if (!anythingChanged)
|
||||
markAllAnalysesPreserved();
|
||||
markAnalysesPreserved<InstanceGraph>();
|
||||
}
|
||||
|
||||
static bool isAnnoInteresting(Annotation anno) {
|
||||
return anno.isClass(extractBlackBoxAnnoClass);
|
||||
}
|
||||
|
||||
/// Gather the modules and instances annotated to be moved by this pass. This
|
||||
/// populates the corresponding lists and maps of the pass.
|
||||
void ExtractInstancesPass::collectAnnos() {
|
||||
CircuitOp circuit = getOperation();
|
||||
|
||||
// Grab the clock gate extraction annotation on the circuit.
|
||||
StringRef clkgateFileName;
|
||||
StringRef clkgateWrapperModule;
|
||||
AnnotationSet::removeAnnotations(circuit, [&](Annotation anno) {
|
||||
if (!anno.isClass(extractClockGatesAnnoClass))
|
||||
return false;
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "Clock gate extraction config: " << anno.getDict() << "\n");
|
||||
auto filenameAttr = anno.getMember<StringAttr>("filename");
|
||||
auto groupAttr = anno.getMember<StringAttr>("group");
|
||||
if (!filenameAttr) {
|
||||
circuit.emitError("missing `filename` attribute in `")
|
||||
<< anno.getClass() << "` annotation";
|
||||
anyFailures = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!clkgateFileName.empty()) {
|
||||
circuit.emitError("multiple `")
|
||||
<< anno.getClass() << "` annotations on circuit";
|
||||
anyFailures = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
clkgateFileName = filenameAttr.getValue();
|
||||
if (groupAttr)
|
||||
clkgateWrapperModule = groupAttr.getValue();
|
||||
return true;
|
||||
});
|
||||
|
||||
// Grab the memory extraction annotation on the circuit.
|
||||
StringRef memoryFileName;
|
||||
StringRef memoryWrapperModule;
|
||||
AnnotationSet::removeAnnotations(circuit, [&](Annotation anno) {
|
||||
if (!anno.isClass(extractSeqMemsAnnoClass))
|
||||
return false;
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "Memory extraction config: " << anno.getDict() << "\n");
|
||||
auto filenameAttr = anno.getMember<StringAttr>("filename");
|
||||
auto groupAttr = anno.getMember<StringAttr>("group");
|
||||
if (!filenameAttr) {
|
||||
circuit.emitError("missing `filename` attribute in `")
|
||||
<< anno.getClass() << "` annotation";
|
||||
anyFailures = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!memoryFileName.empty()) {
|
||||
circuit.emitError("multiple `")
|
||||
<< anno.getClass() << "` annotations on circuit";
|
||||
anyFailures = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
memoryFileName = filenameAttr.getValue();
|
||||
if (groupAttr)
|
||||
memoryWrapperModule = groupAttr.getValue();
|
||||
return true;
|
||||
});
|
||||
|
||||
// Gather the annotations on modules. These complement the later per-instance
|
||||
// annotations.
|
||||
for (auto module : circuit.getOps<FModuleLike>()) {
|
||||
AnnotationSet::removeAnnotations(module, [&](Annotation anno) {
|
||||
if (anno.isClass(dutAnnoClass)) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "Marking DUT `" << module.moduleName() << "`\n");
|
||||
dutRootModules.insert(module);
|
||||
dutModules.insert(module);
|
||||
return false; // other passes may rely on this anno; keep it
|
||||
}
|
||||
if (!isAnnoInteresting(anno))
|
||||
return false;
|
||||
LLVM_DEBUG(llvm::dbgs() << "Annotated module `" << module.moduleName()
|
||||
<< "`:\n " << anno.getDict() << "\n");
|
||||
annotatedModules[module].push_back(anno);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// Gather the annotations on instances to be extracted.
|
||||
circuit.walk([&](InstanceOp inst) {
|
||||
SmallVector<Annotation, 1> instAnnos;
|
||||
Operation *module = instanceGraph->getReferencedModule(inst);
|
||||
|
||||
// Module-level annotations.
|
||||
auto it = annotatedModules.find(module);
|
||||
if (it != annotatedModules.end())
|
||||
instAnnos.append(it->second);
|
||||
|
||||
// Instance-level annotations.
|
||||
AnnotationSet::removeAnnotations(inst, [&](Annotation anno) {
|
||||
if (!isAnnoInteresting(anno))
|
||||
return false;
|
||||
LLVM_DEBUG(llvm::dbgs() << "Annotated instance `" << inst.name()
|
||||
<< "`:\n " << anno.getDict() << "\n");
|
||||
instAnnos.push_back(anno);
|
||||
return true;
|
||||
});
|
||||
|
||||
// No need to do anything about unannotated instances.
|
||||
if (instAnnos.empty())
|
||||
return;
|
||||
|
||||
// Ensure there are no conflicting annotations.
|
||||
if (instAnnos.size() > 1) {
|
||||
auto d = inst.emitError("multiple extraction annotations on instance `")
|
||||
<< inst.name() << "`";
|
||||
d.attachNote(inst.getLoc()) << "instance has the following annotations, "
|
||||
"but at most one is allowed:";
|
||||
for (auto anno : instAnnos)
|
||||
d.attachNote(inst.getLoc()) << anno.getDict();
|
||||
anyFailures = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the annotation.
|
||||
collectAnno(inst, instAnnos[0]);
|
||||
});
|
||||
|
||||
// Propagate the DUT marking to all arbitrarily nested submodules of the DUT.
|
||||
LLVM_DEBUG(llvm::dbgs() << "Marking DUT hierarchy\n");
|
||||
SmallVector<InstanceGraphNode *> worklist;
|
||||
for (Operation *op : dutModules)
|
||||
worklist.push_back(instanceGraph->lookup(cast<hw::HWModuleLike>(op)));
|
||||
while (!worklist.empty()) {
|
||||
auto *module = worklist.pop_back_val();
|
||||
dutModuleNames.insert(module->getModule().moduleNameAttr());
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "- " << module->getModule().moduleName() << "\n");
|
||||
for (auto *instRecord : *module) {
|
||||
auto *target = instRecord->getTarget();
|
||||
if (dutModules.insert(target->getModule()).second)
|
||||
worklist.push_back(target);
|
||||
}
|
||||
}
|
||||
|
||||
// If clock gate extraction is requested, find instances of extmodules with
|
||||
// the corresponding `defname` and mark them as to be extracted.
|
||||
// TODO: This defname really shouldn't be hardcoded here. Make this at least
|
||||
// somewhat configurable.
|
||||
if (!clkgateFileName.empty()) {
|
||||
auto clkgateDefNameAttr = StringAttr::get(&getContext(), "EICG_wrapper");
|
||||
for (auto module : circuit.getOps<FExtModuleOp>()) {
|
||||
if (module.defnameAttr() != clkgateDefNameAttr)
|
||||
continue;
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "Clock gate `" << module.moduleName() << "`\n");
|
||||
if (!dutModules.contains(module)) {
|
||||
LLVM_DEBUG(llvm::dbgs() << "- Ignored (outside DUT)\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
ExtractionInfo info;
|
||||
info.traceFilename = clkgateFileName;
|
||||
info.prefix = "clock_gate"; // TODO: Don't hardcode this
|
||||
info.wrapperModule = clkgateWrapperModule;
|
||||
info.stopAtDUT = !info.wrapperModule.empty();
|
||||
for (auto *instRecord :
|
||||
instanceGraph->lookup(cast<hw::HWModuleLike>(*module))->uses()) {
|
||||
if (auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "- Marking `"
|
||||
<< inst->getParentOfType<FModuleLike>().moduleName() << "."
|
||||
<< inst.name() << "`\n");
|
||||
extractionWorklist.push_back({inst, info});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process an extraction annotation on an instance into a corresponding
|
||||
/// `ExtractionInfo` and a spot on the worklist for later moving things around.
|
||||
void ExtractInstancesPass::collectAnno(InstanceOp inst, Annotation anno) {
|
||||
LLVM_DEBUG(llvm::dbgs() << "Processing instance `" << inst.name() << "` "
|
||||
<< anno.getDict() << "\n");
|
||||
|
||||
auto getStringOrError = [&](StringRef member) {
|
||||
auto attr = anno.getMember<StringAttr>(member);
|
||||
if (!attr) {
|
||||
inst.emitError("missing `")
|
||||
<< member << "` attribute in `" << anno.getClass() << "` annotation";
|
||||
anyFailures = true;
|
||||
}
|
||||
return attr;
|
||||
};
|
||||
|
||||
if (anno.isClass(extractBlackBoxAnnoClass)) {
|
||||
auto filename = getStringOrError("filename");
|
||||
auto prefix = getStringOrError("prefix");
|
||||
auto dest = anno.getMember<StringAttr>("dest"); // optional
|
||||
if (anyFailures)
|
||||
return;
|
||||
|
||||
ExtractionInfo info;
|
||||
info.traceFilename = filename;
|
||||
info.prefix = prefix;
|
||||
info.wrapperModule = (dest ? dest.getValue() : "");
|
||||
|
||||
// CAVEAT: If the instance has a wrapper module configured then extraction
|
||||
// should stop at the DUT module instead of extracting past the DUT into the
|
||||
// surrounding test harness. This is all very ugly and hacky.
|
||||
info.stopAtDUT = !info.wrapperModule.empty();
|
||||
|
||||
extractionWorklist.push_back({inst, info});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Move instances in the extraction worklist upwards in the hierarchy. This
|
||||
/// iteratively pushes instances up one level of hierarchy until they have
|
||||
/// arrived in the desired container module.
|
||||
void ExtractInstancesPass::extractInstances() {
|
||||
// The list of ports to be added to an instance's parent module. Cleared and
|
||||
// reused across instances.
|
||||
SmallVector<std::pair<unsigned, PortInfo>> newPorts;
|
||||
// The number of instances with the same prefix. Used to uniquify prefices.
|
||||
DenseMap<StringRef, unsigned> prefixUniqueIDs;
|
||||
|
||||
SmallPtrSet<Operation *, 4> nlasToRemove;
|
||||
SmallPtrSet<Attribute, 4> nlasToRemoveFromParent;
|
||||
|
||||
auto &nlaTable = getAnalysis<NLATable>();
|
||||
|
||||
// Keep track of where the instance was originally.
|
||||
for (auto &[inst, info] : extractionWorklist)
|
||||
originalInstanceParents[inst] =
|
||||
inst->getParentOfType<FModuleLike>().moduleNameAttr();
|
||||
|
||||
while (!extractionWorklist.empty()) {
|
||||
auto [inst, info] = extractionWorklist.pop_back_val();
|
||||
auto parent = inst->getParentOfType<FModuleOp>();
|
||||
|
||||
// Figure out the wiring prefix to use for this instance. If we are supposed
|
||||
// to use a wiring prefix (`info.prefix` is non-empty), we assemble a
|
||||
// `<prefix>_<N>` string, where `N` is an unsigned integer used to uniquifiy
|
||||
// the prefix. This is very close to what the original Scala implementation
|
||||
// of the pass does, which would group instances to be extracted by prefix
|
||||
// and then iterate over them with the index in the group being used as `N`.
|
||||
StringRef prefix;
|
||||
if (!info.prefix.empty()) {
|
||||
auto &prefixSlot = instPrefices[inst];
|
||||
if (prefixSlot.empty()) {
|
||||
auto idx = prefixUniqueIDs[info.prefix]++;
|
||||
(Twine(info.prefix) + "_" + Twine(idx)).toVector(prefixSlot);
|
||||
}
|
||||
prefix = prefixSlot;
|
||||
}
|
||||
|
||||
// If the instance is already in the right place (outside the DUT or already
|
||||
// in the root module), there's nothing left for us to do. Otherwise we
|
||||
// proceed to bubble it up one level in the hierarchy and add the resulting
|
||||
// instances back to the worklist.
|
||||
if (!dutModules.contains(parent) ||
|
||||
instanceGraph->lookup(parent)->noUses() ||
|
||||
(info.stopAtDUT && dutRootModules.contains(parent))) {
|
||||
LLVM_DEBUG(llvm::dbgs() << "\nNo need to further move " << inst << "\n");
|
||||
extractedInstances.push_back({inst, info});
|
||||
continue;
|
||||
}
|
||||
LLVM_DEBUG({
|
||||
llvm::dbgs() << "\nMoving ";
|
||||
if (!prefix.empty())
|
||||
llvm::dbgs() << "`" << prefix << "` ";
|
||||
llvm::dbgs() << inst << "\n";
|
||||
});
|
||||
|
||||
// Add additional ports to the parent module as a replacement for the
|
||||
// instance port signals once the instance is extracted.
|
||||
unsigned numParentPorts = parent.getNumPorts();
|
||||
unsigned numInstPorts = inst.getNumResults();
|
||||
|
||||
for (unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
|
||||
// Assemble the new port name as "<prefix>_<name>", where the prefix is
|
||||
// provided by the extraction annotation.
|
||||
auto name = inst.getPortNameStr(portIdx);
|
||||
auto nameAttr = StringAttr::get(
|
||||
&getContext(),
|
||||
prefix.empty() ? Twine(name) : Twine(prefix) + "_" + name);
|
||||
|
||||
PortInfo newPort{nameAttr,
|
||||
inst.getResult(portIdx).getType().cast<FIRRTLType>(),
|
||||
direction::flip(inst.getPortDirection(portIdx))};
|
||||
newPort.loc = inst.getResult(portIdx).getLoc();
|
||||
newPorts.push_back({numParentPorts, newPort});
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "- Adding port " << newPort.direction << " "
|
||||
<< newPort.name.getValue() << ": " << newPort.type << "\n");
|
||||
}
|
||||
parent.insertPorts(newPorts);
|
||||
anythingChanged = true;
|
||||
|
||||
// Replace all uses of the existing instance ports with the newly-created
|
||||
// module ports.
|
||||
for (unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
|
||||
inst.getResult(portIdx).replaceAllUsesWith(
|
||||
parent.getArgument(numParentPorts + portIdx));
|
||||
}
|
||||
assert(inst.use_empty() && "instance ports should have been detached");
|
||||
|
||||
// Move the original instance one level up such that it is right next to
|
||||
// the instances of the parent module, and wire the instance ports up to
|
||||
// the newly added parent module ports.
|
||||
for (auto *instRecord :
|
||||
instanceGraph->lookup(cast<hw::HWModuleLike>(*parent))->uses()) {
|
||||
auto oldParentInst = cast<InstanceOp>(*instRecord->getInstance());
|
||||
auto newParent = oldParentInst->getParentOfType<FModuleLike>();
|
||||
LLVM_DEBUG(llvm::dbgs() << "- Updating " << oldParentInst << "\n");
|
||||
auto newParentInst = oldParentInst.cloneAndInsertPorts(newPorts);
|
||||
|
||||
// Migrate connections to existing ports.
|
||||
for (unsigned portIdx = 0; portIdx < numParentPorts; ++portIdx)
|
||||
oldParentInst.getResult(portIdx).replaceAllUsesWith(
|
||||
newParentInst.getResult(portIdx));
|
||||
|
||||
// Add the moved instance and hook it up to the added ports.
|
||||
ImplicitLocOpBuilder builder(inst.getLoc(), newParentInst);
|
||||
auto newInst = inst.cloneAndInsertPorts({});
|
||||
newInst->remove();
|
||||
builder.setInsertionPointAfter(newParentInst);
|
||||
builder.insert(newInst);
|
||||
for (unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
|
||||
auto dst = newInst.getResult(portIdx);
|
||||
auto src = newParentInst.getResult(numParentPorts + portIdx);
|
||||
if (newPorts[portIdx].second.direction == Direction::In)
|
||||
std::swap(src, dst);
|
||||
builder.create<StrictConnectOp>(dst, src);
|
||||
}
|
||||
|
||||
// Move the wiring prefix from the old to the new instance. We just look
|
||||
// up the prefix for the old instance and if it exists, we remove it and
|
||||
// assign it to the new instance. This has the effect of making the first
|
||||
// new instance we create inherit the wiring prefix, and all additional
|
||||
// new instances (e.g. through multiple instantiation of the parent) will
|
||||
// pick a new prefix.
|
||||
auto oldPrefix = instPrefices.find(inst);
|
||||
if (oldPrefix != instPrefices.end()) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< " - Reusing prefix `" << oldPrefix->second << "`\n");
|
||||
auto newPrefix = std::move(oldPrefix->second);
|
||||
instPrefices.erase(oldPrefix);
|
||||
instPrefices.insert({newInst, newPrefix});
|
||||
}
|
||||
|
||||
// Inherit the old instance's extraction path.
|
||||
auto &extractionPath = (extractionPaths[newInst] = extractionPaths[inst]);
|
||||
extractionPath.push_back(getInnerRefTo(newParentInst));
|
||||
originalInstanceParents[newInst] = originalInstanceParents[inst];
|
||||
|
||||
// Update all NLAs that touch the moved instance. We do this through
|
||||
// `removeAnnotations` which gives us an opportunity to drop annotations
|
||||
// that we want to replace with an updated version. We add the new ones to
|
||||
// `newInstAnnos` since we cannot modify the `AnnotationSet` within
|
||||
// `removeAnnotations`.
|
||||
AnnotationSet annos(newInst);
|
||||
SmallVector<Attribute> newInstAnnos;
|
||||
annos.removeAnnotations([&](Annotation anno) {
|
||||
auto nlaName = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal");
|
||||
if (!nlaName)
|
||||
return false;
|
||||
auto nla = nlaTable.getNLA(nlaName.getAttr());
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Updating " << nla << "\n");
|
||||
|
||||
// Find the position of the instance in the NLA path. This is going to
|
||||
// be the position at which we have to modify the NLA.
|
||||
SmallVector<Attribute> nlaPath(nla.namepath().begin(),
|
||||
nla.namepath().end());
|
||||
unsigned nlaIdx;
|
||||
unsigned nlaLen = nlaPath.size();
|
||||
for (nlaIdx = 0; nlaIdx < nlaLen; ++nlaIdx) {
|
||||
auto innerRef = nlaPath[nlaIdx].dyn_cast<InnerRefAttr>();
|
||||
if (!innerRef)
|
||||
continue;
|
||||
if (innerRef.getModule() == parent.moduleNameAttr() &&
|
||||
innerRef.getName() == newInst.inner_symAttr())
|
||||
break;
|
||||
}
|
||||
assert(nlaIdx < nlaLen && "instance not found in its own NLA");
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Position " << nlaIdx << "\n");
|
||||
|
||||
// Since we're moving the instance out of its parent module, this NLA
|
||||
// will no longer pass through that parent module. Mark it to be removed
|
||||
// from the parent later.
|
||||
nlasToRemoveFromParent.insert(nla.sym_nameAttr());
|
||||
|
||||
// There are two interesting cases now:
|
||||
// - If `nlaIdx == 0`, the NLA is rooted at the module the instance was
|
||||
// located in prior to extraction. This indicates that the NLA applies
|
||||
// to all instances of that parent module. Since we are extracting
|
||||
// *out* of that module, we have to create a new NLA rooted at the new
|
||||
// parent module after extraction.
|
||||
// - If `nlaIdx > 0`, the NLA is rooted further up in the hierarchy and
|
||||
// we can simply remove the old parent module from the path.
|
||||
|
||||
// Handle the case where we need to come up with a new NLA for this
|
||||
// instance since we've moved it past the module at which the old NLA
|
||||
// was rooted at.
|
||||
if (nlaIdx == 0) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< " - Re-rooting " << nlaPath[nlaIdx] << "\n");
|
||||
nlaPath[nlaIdx] =
|
||||
InnerRefAttr::get(newParent.moduleNameAttr(),
|
||||
nlaPath[nlaIdx].cast<InnerRefAttr>().getName());
|
||||
auto builder = OpBuilder::atBlockBegin(getOperation().getBody());
|
||||
// CAVEAT(fschuiki): There are likely cases where the following
|
||||
// creates multiple NLAs with the same name. This is going to happen
|
||||
// if an NLA has to be re-rooted and the parent is instantiated
|
||||
// multiple times, requiring us to create a new NLA for each of the
|
||||
// parent instances. To fix this you'll have to pick a name through
|
||||
// `circuitNamespace.newName(...)` and visit every module/instance in
|
||||
// the old NLA, remove the reference to the old NLA, and add
|
||||
// references for all the newly-created ones.
|
||||
auto newNla = builder.create<NonLocalAnchor>(
|
||||
newInst.getLoc(), nla.sym_nameAttr(),
|
||||
builder.getArrayAttr(nlaPath));
|
||||
nlasToRemove.insert(nla);
|
||||
nlaTable.addNLA(newNla);
|
||||
// This modifies our local copy of the anno, not the actual anno on
|
||||
// the operation.
|
||||
anno.setMember("circt.nonlocal",
|
||||
FlatSymbolRefAttr::get(newNla.sym_nameAttr()));
|
||||
newInstAnnos.push_back(anno.getDict());
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Created " << newNla << "\n");
|
||||
return true; // delete the old anno
|
||||
}
|
||||
|
||||
// In the subequent code block we are going to remove one element from
|
||||
// the NLA path, corresponding to the fact that the extracted instance
|
||||
// has moved up in the hierarchy by one level. Removing that element may
|
||||
// leave the NLA in a degenerate state, with only a single element in
|
||||
// its path. If that is the case we have to convert the NLA into a
|
||||
// regular local annotation.
|
||||
if (nlaPath.size() == 2) {
|
||||
anno.removeMember("circt.nonlocal");
|
||||
newInstAnnos.push_back(anno.getDict());
|
||||
nlasToRemove.insert(nla);
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< " - Converted to local " << anno.getDict() << "\n");
|
||||
return true; // delete the old anno
|
||||
}
|
||||
|
||||
// At this point the NLA looks like `NewParent::X, OldParent::BB`, and
|
||||
// the `nlaIdx` points at `OldParent::BB`. To make our lives easier,
|
||||
// since we know that `nlaIdx` is a `InnerRefAttr`, we'll modify
|
||||
// `OldParent::BB` to be `NewParent::BB` and delete `NewParent::X`.
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Merging " << nlaPath[nlaIdx - 1]
|
||||
<< " and " << nlaPath[nlaIdx] << "\n");
|
||||
nlaPath[nlaIdx] = InnerRefAttr::get(
|
||||
nlaPath[nlaIdx - 1].cast<InnerRefAttr>().getModule(),
|
||||
nlaPath[nlaIdx].cast<InnerRefAttr>().getName());
|
||||
nlaPath.erase(nlaPath.begin() + nlaIdx - 1);
|
||||
nla.namepathAttr(builder.getArrayAttr(nlaPath));
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Modified to " << nla << "\n");
|
||||
return false;
|
||||
});
|
||||
annos.addAnnotations(newInstAnnos);
|
||||
annos.applyToOperation(newInst);
|
||||
|
||||
// Add the moved instance to the extraction worklist such that it gets
|
||||
// bubbled up further if needed.
|
||||
extractionWorklist.push_back({newInst, info});
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Updated to " << newInst << "\n");
|
||||
|
||||
// Remove the obsolete NLAs from the instance of the parent module, since
|
||||
// the extracted instance no longer resides in that module and any NLAs to
|
||||
// it no longer go through the parent module.
|
||||
AnnotationSet::removeAnnotations(newParentInst, [&](Annotation anno) {
|
||||
auto nlaName = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal");
|
||||
if (nlaName && nlasToRemoveFromParent.contains(nlaName.getAttr())) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< " - Remove from parent: " << anno.getDict() << "\n");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
nlasToRemoveFromParent.clear();
|
||||
|
||||
// Keep instance graph up-to-date.
|
||||
instanceGraph->replaceInstance(oldParentInst, newParentInst);
|
||||
oldParentInst.erase();
|
||||
}
|
||||
|
||||
// Clean up the original instance.
|
||||
inst.erase();
|
||||
newPorts.clear();
|
||||
}
|
||||
|
||||
// Remove unused NLAs.
|
||||
for (Operation *op : nlasToRemove) {
|
||||
LLVM_DEBUG(llvm::dbgs() << "Removing obsolete " << *op << "\n");
|
||||
op->erase();
|
||||
}
|
||||
}
|
||||
|
||||
/// Group instances into submodules after they have been moved upwards. This
|
||||
/// only occurs for instances that had the corresponding `dest` field of the
|
||||
/// annotation set.
|
||||
void ExtractInstancesPass::groupInstances() {
|
||||
// Group the extracted instances by their wrapper module name and their parent
|
||||
// module. Note that we cannot group instances that landed in different parent
|
||||
// modules into the same submodule, so we use that parent module as a grouping
|
||||
// key.
|
||||
SmallDenseMap<std::pair<Operation *, StringRef>, SmallVector<InstanceOp>>
|
||||
instsByWrapper;
|
||||
for (auto &[inst, info] : extractedInstances) {
|
||||
if (!info.wrapperModule.empty())
|
||||
instsByWrapper[{inst->getParentOfType<FModuleOp>(), info.wrapperModule}]
|
||||
.push_back(inst);
|
||||
}
|
||||
if (instsByWrapper.empty())
|
||||
return;
|
||||
LLVM_DEBUG(llvm::dbgs() << "\nGrouping instances into wrappers\n");
|
||||
|
||||
// Generate the wrappers.
|
||||
SmallVector<PortInfo> ports;
|
||||
SmallVector<Attribute> wrapperInstAnnos;
|
||||
auto &nlaTable = getAnalysis<NLATable>();
|
||||
|
||||
for (auto &[parentAndWrapperName, insts] : instsByWrapper) {
|
||||
auto [parentOp, wrapperName] = parentAndWrapperName;
|
||||
auto parent = cast<FModuleOp>(parentOp);
|
||||
LLVM_DEBUG(llvm::dbgs() << "- Wrapper `" << wrapperName << "` in `"
|
||||
<< parent.moduleName() << "` with " << insts.size()
|
||||
<< " instances\n");
|
||||
OpBuilder builder(parentOp);
|
||||
|
||||
// Uniquify the wrapper name.
|
||||
wrapperName = circuitNamespace.newName(wrapperName);
|
||||
auto wrapperInstName =
|
||||
builder.getStringAttr(getModuleNamespace(parent).newName(wrapperName));
|
||||
|
||||
// Assemble a list of ports for the wrapper module, which is basically just
|
||||
// a concatenation of the wrapped instance ports. Also keep track of the
|
||||
// NLAs that target the grouped instances since these will have to pass
|
||||
// through the wrapper module.
|
||||
ports.clear();
|
||||
wrapperInstAnnos.clear();
|
||||
for (auto inst : insts) {
|
||||
// Determine the ports for the wrapper.
|
||||
StringRef prefix(instPrefices[inst]);
|
||||
unsigned portNum = inst.getNumResults();
|
||||
for (unsigned portIdx = 0; portIdx < portNum; ++portIdx) {
|
||||
auto name = inst.getPortNameStr(portIdx);
|
||||
auto nameAttr = builder.getStringAttr(
|
||||
prefix.empty() ? Twine(name) : Twine(prefix) + "_" + name);
|
||||
PortInfo port{nameAttr,
|
||||
inst.getResult(portIdx).getType().cast<FIRRTLType>(),
|
||||
inst.getPortDirection(portIdx)};
|
||||
port.loc = inst.getResult(portIdx).getLoc();
|
||||
ports.push_back(port);
|
||||
}
|
||||
|
||||
// Determine the additional NLA breadcrumbs to put on the wrapper instance
|
||||
// and update the NLA's path to include the wrapper instance we're about
|
||||
// to construct.
|
||||
for (auto anno : AnnotationSet(inst)) {
|
||||
auto nlaName = anno.getMember<FlatSymbolRefAttr>("circt.nonlocal");
|
||||
if (!nlaName)
|
||||
continue;
|
||||
auto nla = nlaTable.getNLA(nlaName.getAttr());
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Updating " << nla << "\n");
|
||||
|
||||
// Find the position of the instance in the NLA path. This is going to
|
||||
// be the position at which we have to modify the NLA.
|
||||
SmallVector<Attribute> nlaPath(nla.namepath().begin(),
|
||||
nla.namepath().end());
|
||||
unsigned nlaIdx;
|
||||
unsigned nlaLen = nlaPath.size();
|
||||
for (nlaIdx = 0; nlaIdx < nlaLen; ++nlaIdx) {
|
||||
auto innerRef = nlaPath[nlaIdx].dyn_cast<InnerRefAttr>();
|
||||
if (!innerRef)
|
||||
continue;
|
||||
if (innerRef.getModule() == parent.moduleNameAttr() &&
|
||||
innerRef.getName() == inst.inner_symAttr())
|
||||
break;
|
||||
}
|
||||
assert(nlaIdx < nlaLen && "instance not found in its own NLA");
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Position " << nlaIdx << "\n");
|
||||
|
||||
// The relevant part of the NLA is of the form `Top::bb`, which we want
|
||||
// to expand to `Top::wrapperInst` and `Wrapper::bb`.
|
||||
auto ref1 = InnerRefAttr::get(
|
||||
nlaPath[nlaIdx].cast<InnerRefAttr>().getModule(), wrapperInstName);
|
||||
auto ref2 =
|
||||
InnerRefAttr::get(builder.getStringAttr(wrapperName),
|
||||
nlaPath[nlaIdx].cast<InnerRefAttr>().getName());
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Expanding " << nlaPath[nlaIdx]
|
||||
<< " to (" << ref1 << ", " << ref2 << ")\n");
|
||||
nlaPath[nlaIdx] = ref1;
|
||||
nlaPath.insert(nlaPath.begin() + nlaIdx + 1, ref2);
|
||||
nla.namepathAttr(builder.getArrayAttr(nlaPath));
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Modified to " << nla << "\n");
|
||||
|
||||
// Create a breadcrumb annotation for the wrapper instance which we'll
|
||||
// create further down.
|
||||
wrapperInstAnnos.push_back(builder.getDictionaryAttr(
|
||||
{{builder.getStringAttr("class"),
|
||||
builder.getStringAttr("circt.nonlocal")},
|
||||
{builder.getStringAttr("circt.nonlocal"),
|
||||
FlatSymbolRefAttr::get(nla.sym_nameAttr())}}));
|
||||
}
|
||||
}
|
||||
|
||||
// Create the wrapper module.
|
||||
auto wrapper = builder.create<FModuleOp>(
|
||||
builder.getUnknownLoc(), builder.getStringAttr(wrapperName), ports);
|
||||
|
||||
// Instantiate the wrapper module in the parent and replace uses of the
|
||||
// extracted instances' ports with the corresponding wrapper module ports.
|
||||
// This will essentially disconnect the extracted instances.
|
||||
builder.setInsertionPointToStart(parent.getBody());
|
||||
auto wrapperInst = builder.create<InstanceOp>(
|
||||
wrapper.getLoc(), wrapper, wrapperName, wrapperInstAnnos,
|
||||
/*portAnnotations=*/ArrayRef<Attribute>{}, /*lowerToBind=*/false,
|
||||
wrapperInstName);
|
||||
unsigned portIdx = 0;
|
||||
for (auto inst : insts)
|
||||
for (auto result : inst.getResults())
|
||||
result.replaceAllUsesWith(wrapperInst.getResult(portIdx++));
|
||||
|
||||
// Move all instances into the wrapper module and wire them up to the
|
||||
// wrapper ports.
|
||||
portIdx = 0;
|
||||
builder.setInsertionPointToStart(wrapper.getBody());
|
||||
for (auto inst : insts) {
|
||||
inst->remove();
|
||||
builder.insert(inst);
|
||||
for (auto result : inst.getResults()) {
|
||||
Value dst = result;
|
||||
Value src = wrapper.getArgument(portIdx);
|
||||
if (ports[portIdx].direction == Direction::Out)
|
||||
std::swap(dst, src);
|
||||
builder.create<StrictConnectOp>(result.getLoc(), dst, src);
|
||||
++portIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate trace files, which are plain text metadata files that list the
|
||||
/// hierarchical path where each instance was extracted from. The file lists one
|
||||
/// instance per line in the form `<prefix> -> <original-path>`.
|
||||
void ExtractInstancesPass::createTraceFiles() {
|
||||
LLVM_DEBUG(llvm::dbgs() << "\nGenerating trace files\n");
|
||||
auto builder = OpBuilder::atBlockEnd(getOperation().getBody());
|
||||
|
||||
// Group the extracted instances by their trace file name.
|
||||
SmallDenseMap<StringRef, SmallVector<InstanceOp>> instsByTraceFile;
|
||||
for (auto &[inst, info] : extractedInstances)
|
||||
if (!info.traceFilename.empty())
|
||||
instsByTraceFile[info.traceFilename].push_back(inst);
|
||||
|
||||
// Generate the trace files.
|
||||
SmallVector<Attribute> symbols;
|
||||
SmallDenseMap<Attribute, unsigned> symbolIndices;
|
||||
|
||||
for (auto &[fileName, insts] : instsByTraceFile) {
|
||||
LLVM_DEBUG(llvm::dbgs() << "- " << fileName << "\n");
|
||||
std::string buffer;
|
||||
llvm::raw_string_ostream os(buffer);
|
||||
symbols.clear();
|
||||
symbolIndices.clear();
|
||||
|
||||
auto addSymbol = [&](Attribute symbol) {
|
||||
unsigned id;
|
||||
auto it = symbolIndices.find(symbol);
|
||||
if (it != symbolIndices.end()) {
|
||||
id = it->second;
|
||||
} else {
|
||||
id = symbols.size();
|
||||
symbols.push_back(symbol);
|
||||
symbolIndices.insert({symbol, id});
|
||||
}
|
||||
os << "{{" << id << "}}";
|
||||
};
|
||||
|
||||
for (auto inst : insts) {
|
||||
StringRef prefix(instPrefices[inst]);
|
||||
if (prefix.empty()) {
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Skipping `" << inst.name()
|
||||
<< "` since it has no extraction prefix\n");
|
||||
continue;
|
||||
}
|
||||
ArrayRef<InnerRefAttr> path(extractionPaths[inst]);
|
||||
if (path.empty()) {
|
||||
LLVM_DEBUG(llvm::dbgs() << " - Skipping `" << inst.name()
|
||||
<< "` since it has not been moved\n");
|
||||
continue;
|
||||
}
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< " - " << prefix << ": " << inst.name() << "\n");
|
||||
os << prefix << " -> ";
|
||||
|
||||
// HACK: To match the Scala implementation, we strip all non-DUT modules
|
||||
// from the path and make the path look like it's rooted at the first DUT
|
||||
// module (so `TestHarness.dut.foo.bar` becomes `DUTModule.foo.bar`).
|
||||
while (!path.empty() &&
|
||||
!dutModuleNames.contains(path.back().getModule())) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< " - Dropping non-DUT segment " << path.back() << "\n");
|
||||
path = path.drop_back();
|
||||
}
|
||||
// HACK: This is extremely ugly. In case the instance was just moved by a
|
||||
// single level, the path may become empty. In that case we simply use the
|
||||
// instance's original parent before it was moved.
|
||||
addSymbol(FlatSymbolRefAttr::get(path.empty()
|
||||
? originalInstanceParents[inst]
|
||||
: path.back().getModule()));
|
||||
for (auto sym : llvm::reverse(path)) {
|
||||
os << ".";
|
||||
addSymbol(sym);
|
||||
}
|
||||
os << ".";
|
||||
addSymbol(getInnerRefTo(inst));
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
// Put the information in a verbatim operation.
|
||||
auto verbatimOp = builder.create<sv::VerbatimOp>(
|
||||
builder.getUnknownLoc(), buffer, ValueRange{},
|
||||
builder.getArrayAttr(symbols));
|
||||
auto fileAttr = hw::OutputFileAttr::getFromFilename(
|
||||
builder.getContext(), fileName, /*excludeFromFilelist=*/true);
|
||||
verbatimOp->setAttr("output_file", fileAttr);
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Pass Creation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
std::unique_ptr<mlir::Pass> circt::firrtl::createExtractInstancesPass() {
|
||||
return std::make_unique<ExtractInstancesPass>();
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// RUN: circt-opt --firrtl-inject-dut-hier --firrtl-extract-instances --verify-diagnostics %s | FileCheck %s
|
||||
|
||||
// Tests extracted from:
|
||||
// - test/scala/firrtl/ExtractClockGates.scala
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ExtractClockGates Multigrouping
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// CHECK: firrtl.circuit "ExtractClockGatesMultigrouping"
|
||||
firrtl.circuit "ExtractClockGatesMultigrouping" attributes {annotations = [{class = "sifive.enterprise.firrtl.ExtractClockGatesFileAnnotation", filename = "ClockGates.txt", group = "ClockGatesGroup"}, {class = "sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation", name = "InjectedSubmodule"}]} {
|
||||
firrtl.extmodule private @EICG_wrapper(in in: !firrtl.clock, in en: !firrtl.uint<1>, out out: !firrtl.clock) attributes {defname = "EICG_wrapper"}
|
||||
// CHECK-LABEL: firrtl.module private @SomeModule
|
||||
firrtl.module private @SomeModule(in %clock: !firrtl.clock, in %en: !firrtl.uint<1>) {
|
||||
// CHECK-NOT: firrtl.instance gate @EICG_wrapper
|
||||
%gate_in, %gate_en, %gate_out = firrtl.instance gate @EICG_wrapper(in in: !firrtl.clock, in en: !firrtl.uint<1>, out out: !firrtl.clock)
|
||||
firrtl.connect %gate_in, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %gate_en, %en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
}
|
||||
// CHECK-LABEL: firrtl.module private @InjectedSubmodule
|
||||
// CHECK: firrtl.instance inst0 sym [[INST0_SYM:@.+]] @SomeModule
|
||||
// CHECK: firrtl.instance inst1 sym [[INST1_SYM:@.+]] @SomeModule
|
||||
|
||||
// CHECK-LABEL: firrtl.module @ClockGatesGroup
|
||||
// CHECK: firrtl.instance gate sym [[CKG0_SYM:@.+]] @EICG_wrapper
|
||||
// CHECK: firrtl.instance gate sym [[CKG1_SYM:@.+]] @EICG_wrapper
|
||||
|
||||
// CHECK-LABEL: firrtl.module private @DUTModule
|
||||
firrtl.module private @DUTModule(in %clock: !firrtl.clock, in %foo_en: !firrtl.uint<1>, in %bar_en: !firrtl.uint<1>) attributes {annotations = [{class = "sifive.enterprise.firrtl.MarkDUTAnnotation"}]} {
|
||||
// CHECK-NOT: firrtl.instance gate @EICG_wrapper
|
||||
// CHECK-NOT: firrtl.instance gate @EICG_wrapper
|
||||
%inst0_clock, %inst0_en = firrtl.instance inst0 @SomeModule(in clock: !firrtl.clock, in en: !firrtl.uint<1>)
|
||||
%inst1_clock, %inst1_en = firrtl.instance inst1 @SomeModule(in clock: !firrtl.clock, in en: !firrtl.uint<1>)
|
||||
// CHECK: firrtl.instance ClockGatesGroup sym [[CLKGRP_SYM:@.+]] @ClockGatesGroup
|
||||
// CHECK: firrtl.instance InjectedSubmodule sym [[INJMOD_SYM:@.+]] @InjectedSubmodule
|
||||
firrtl.connect %inst0_clock, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %inst1_clock, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %inst0_en, %foo_en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
firrtl.connect %inst1_en, %bar_en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
}
|
||||
// CHECK-LABEL: firrtl.module @ExtractClockGatesMultigrouping
|
||||
firrtl.module @ExtractClockGatesMultigrouping(in %clock: !firrtl.clock, in %foo_en: !firrtl.uint<1>, in %bar_en: !firrtl.uint<1>) {
|
||||
%dut_clock, %dut_foo_en, %dut_bar_en = firrtl.instance dut @DUTModule(in clock: !firrtl.clock, in foo_en: !firrtl.uint<1>, in bar_en: !firrtl.uint<1>)
|
||||
firrtl.connect %dut_clock, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %dut_bar_en, %bar_en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
firrtl.connect %dut_foo_en, %foo_en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
}
|
||||
// CHECK: sv.verbatim "
|
||||
// CHECK-SAME{LITERAL}: clock_gate_1 -> {{0}}.{{1}}.{{2}}.{{3}}\0A
|
||||
// CHECK-SAME{LITERAL}: clock_gate_0 -> {{0}}.{{1}}.{{4}}.{{5}}\0A
|
||||
// CHECK-SAME: output_file = #hw.output_file<"ClockGates.txt", excludeFromFileList>
|
||||
// CHECK-SAME: symbols = [
|
||||
// CHECK-SAME: @DUTModule
|
||||
// CHECK-SAME: #hw.innerNameRef<@DUTModule::[[INJMOD_SYM]]>
|
||||
// CHECK-SAME: #hw.innerNameRef<@InjectedSubmodule::[[INST0_SYM]]>
|
||||
// CHECK-SAME: #hw.innerNameRef<@ClockGatesGroup::[[CKG0_SYM]]>
|
||||
// CHECK-SAME: #hw.innerNameRef<@InjectedSubmodule::[[INST1_SYM]]>
|
||||
// CHECK-SAME: #hw.innerNameRef<@ClockGatesGroup::[[CKG1_SYM]]>
|
||||
// CHECK-SAME: ]
|
||||
}
|
|
@ -0,0 +1,362 @@
|
|||
// RUN: circt-opt --firrtl-extract-instances --verify-diagnostics %s | FileCheck %s
|
||||
|
||||
// Tests extracted from:
|
||||
// - test/scala/firrtl/ExtractBlackBoxes.scala
|
||||
// - test/scala/firrtl/ExtractClockGates.scala
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ExtractBlackBoxes Simple
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// CHECK: firrtl.circuit "ExtractBlackBoxesSimple"
|
||||
firrtl.circuit "ExtractBlackBoxesSimple" attributes {annotations = [{class = "firrtl.transforms.BlackBoxTargetDirAnno", targetDir = "BlackBoxes"}]} {
|
||||
// CHECK-LABEL: firrtl.extmodule private @MyBlackBox
|
||||
firrtl.extmodule private @MyBlackBox(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>) attributes {annotations = [{class = "sifive.enterprise.firrtl.ExtractBlackBoxAnnotation", filename = "BlackBoxes.txt", prefix = "bb"}], defname = "MyBlackBox"}
|
||||
// CHECK-LABEL: firrtl.module private @BBWrapper
|
||||
// CHECK-SAME: out %bb_0_in: !firrtl.uint<8>
|
||||
// CHECK-SAME: in %bb_0_out: !firrtl.uint<8>
|
||||
firrtl.module private @BBWrapper(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) {
|
||||
// CHECK-NOT: firrtl.instance bb @MyBlackBox
|
||||
%bb_in, %bb_out = firrtl.instance bb @MyBlackBox(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
%invalid_ui8 = firrtl.invalidvalue : !firrtl.uint<8>
|
||||
firrtl.strictconnect %bb_in, %invalid_ui8 : !firrtl.uint<8>
|
||||
// CHECK: firrtl.connect %out, %bb_0_out
|
||||
// CHECK: firrtl.connect %bb_0_in, %in
|
||||
firrtl.connect %out, %bb_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %bb_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK-LABEL: firrtl.module private @DUTModule
|
||||
// CHECK-SAME: out %bb_0_in: !firrtl.uint<8>
|
||||
// CHECK-SAME: in %bb_0_out: !firrtl.uint<8>
|
||||
firrtl.module private @DUTModule(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) attributes {annotations = [{class = "sifive.enterprise.firrtl.MarkDUTAnnotation"}]} {
|
||||
// CHECK-NOT: firrtl.instance bb @MyBlackBox
|
||||
// CHECK: %mod_in, %mod_out, %mod_bb_0_in, %mod_bb_0_out = firrtl.instance mod sym [[WRAPPER_SYM:@.+]] @BBWrapper
|
||||
// CHECK-NEXT: firrtl.strictconnect %bb_0_in, %mod_bb_0_in
|
||||
// CHECK-NEXT: firrtl.strictconnect %mod_bb_0_out, %bb_0_out
|
||||
%mod_in, %mod_out = firrtl.instance mod @BBWrapper(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
firrtl.connect %out, %mod_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %mod_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK-LABEL: firrtl.module @ExtractBlackBoxesSimple
|
||||
firrtl.module @ExtractBlackBoxesSimple(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) {
|
||||
// CHECK: %dut_in, %dut_out, %dut_bb_0_in, %dut_bb_0_out = firrtl.instance dut sym {{@.+}} @DUTModule
|
||||
// CHECK-NEXT: %bb_in, %bb_out = firrtl.instance bb sym [[BB_SYM:@.+]] @MyBlackBox
|
||||
// CHECK-NEXT: firrtl.strictconnect %bb_in, %dut_bb_0_in
|
||||
// CHECK-NEXT: firrtl.strictconnect %dut_bb_0_out, %bb_out
|
||||
%dut_in, %dut_out = firrtl.instance dut @DUTModule(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
firrtl.connect %out, %dut_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %dut_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK: sv.verbatim "
|
||||
// CHECK-SAME{LITERAL}: bb_0 -> {{0}}.{{1}}.{{2}}\0A
|
||||
// CHECK-SAME: output_file = #hw.output_file<"BlackBoxes.txt", excludeFromFileList>
|
||||
// CHECK-SAME: symbols = [
|
||||
// CHECK-SAME: @DUTModule
|
||||
// CHECK-SAME: #hw.innerNameRef<@DUTModule::[[WRAPPER_SYM]]>
|
||||
// CHECK-SAME: #hw.innerNameRef<@ExtractBlackBoxesSimple::[[BB_SYM]]>
|
||||
// CHECK-SAME: ]
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ExtractBlackBoxes Simple (modified)
|
||||
// ExtractBlackBoxes RenameTargets (modified)
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// CHECK: firrtl.circuit "ExtractBlackBoxesSimple2"
|
||||
firrtl.circuit "ExtractBlackBoxesSimple2" attributes {annotations = [{class = "firrtl.transforms.BlackBoxTargetDirAnno", targetDir = "BlackBoxes"}]} {
|
||||
// CHECK: firrtl.nla @nla_1 [#hw.innerNameRef<@ExtractBlackBoxesSimple2::@bb>, @MyBlackBox]
|
||||
// CHECK-NOT: firrtl.nla @nla_2
|
||||
// CHECK-NOT: firrtl.nla @nla_3
|
||||
firrtl.nla @nla_1 [
|
||||
#hw.innerNameRef<@BBWrapper::@bb>,
|
||||
@MyBlackBox
|
||||
]
|
||||
firrtl.nla @nla_2 [
|
||||
#hw.innerNameRef<@DUTModule::@mod>,
|
||||
#hw.innerNameRef<@BBWrapper::@bb>
|
||||
]
|
||||
firrtl.nla @nla_3 [
|
||||
#hw.innerNameRef<@ExtractBlackBoxesSimple2::@dut>,
|
||||
#hw.innerNameRef<@DUTModule::@mod>,
|
||||
#hw.innerNameRef<@BBWrapper::@bb>
|
||||
]
|
||||
// Annotation on the extmodule itself
|
||||
// CHECK-LABEL: firrtl.extmodule private @MyBlackBox
|
||||
firrtl.extmodule private @MyBlackBox(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>) attributes {annotations = [
|
||||
{class = "sifive.enterprise.firrtl.ExtractBlackBoxAnnotation", filename = "BlackBoxes.txt", prefix = "prefix"},
|
||||
{circt.nonlocal = @nla_1, class = "DummyA"}
|
||||
], defname = "MyBlackBox"}
|
||||
// Annotation will be on the instance
|
||||
// CHECK-LABEL: firrtl.extmodule private @MyBlackBox2
|
||||
firrtl.extmodule private @MyBlackBox2(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>) attributes {defname = "MyBlackBox"}
|
||||
|
||||
// CHECK-LABEL: firrtl.module private @BBWrapper
|
||||
// CHECK-SAME: out %prefix_0_in: !firrtl.uint<8>
|
||||
// CHECK-SAME: in %prefix_0_out: !firrtl.uint<8>
|
||||
// CHECK-SAME: out %prefix_1_in: !firrtl.uint<8>
|
||||
// CHECK-SAME: in %prefix_1_out: !firrtl.uint<8>
|
||||
firrtl.module private @BBWrapper(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) {
|
||||
// CHECK-NOT: firrtl.instance bb @MyBlackBox
|
||||
// CHECK-NOT: firrtl.instance bb2 @MyBlackBox2
|
||||
%bb_in, %bb_out = firrtl.instance bb sym @bb {annotations = [
|
||||
{circt.nonlocal = @nla_1, class = "circt.nonlocal"},
|
||||
{circt.nonlocal = @nla_2, class = "DummyB"},
|
||||
{circt.nonlocal = @nla_3, class = "DummyC"}
|
||||
]} @MyBlackBox(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
%bb2_in, %bb2_out = firrtl.instance bb2 {annotations = [{class = "sifive.enterprise.firrtl.ExtractBlackBoxAnnotation", filename = "BlackBoxes.txt", prefix = "prefix"}]} @MyBlackBox2(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
// CHECK: firrtl.connect %out, %prefix_0_out
|
||||
// CHECK: firrtl.connect %prefix_0_in, %prefix_1_out
|
||||
// CHECK: firrtl.connect %prefix_1_in, %in
|
||||
firrtl.connect %out, %bb2_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %bb2_in, %bb_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %bb_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK-LABEL: firrtl.module private @DUTModule
|
||||
// CHECK-SAME: out %prefix_0_in: !firrtl.uint<8>
|
||||
// CHECK-SAME: in %prefix_0_out: !firrtl.uint<8>
|
||||
// CHECK-SAME: out %prefix_1_in: !firrtl.uint<8>
|
||||
// CHECK-SAME: in %prefix_1_out: !firrtl.uint<8>
|
||||
firrtl.module private @DUTModule(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) attributes {annotations = [{class = "sifive.enterprise.firrtl.MarkDUTAnnotation"}]} {
|
||||
// CHECK-NOT: firrtl.instance bb @MyBlackBox
|
||||
// CHECK-NOT: firrtl.instance bb2 @MyBlackBox2
|
||||
// CHECK: %mod_in, %mod_out, %mod_prefix_0_in, %mod_prefix_0_out, %mod_prefix_1_in, %mod_prefix_1_out = firrtl.instance mod
|
||||
// CHECK-NOT: annotations =
|
||||
// CHECK-SAME: sym [[WRAPPER_SYM:@.+]] @BBWrapper
|
||||
// CHECK-NEXT: firrtl.strictconnect %prefix_1_in, %mod_prefix_1_in
|
||||
// CHECK-NEXT: firrtl.strictconnect %mod_prefix_1_out, %prefix_1_out
|
||||
// CHECK-NEXT: firrtl.strictconnect %prefix_0_in, %mod_prefix_0_in
|
||||
// CHECK-NEXT: firrtl.strictconnect %mod_prefix_0_out, %prefix_0_out
|
||||
%mod_in, %mod_out = firrtl.instance mod sym @mod {annotations = [
|
||||
{circt.nonlocal = @nla_2, class = "circt.nonlocal"},
|
||||
{circt.nonlocal = @nla_3, class = "circt.nonlocal"}
|
||||
]} @BBWrapper(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
firrtl.connect %out, %mod_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %mod_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK-LABEL: firrtl.module @ExtractBlackBoxesSimple2
|
||||
firrtl.module @ExtractBlackBoxesSimple2(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) {
|
||||
// CHECK: %dut_in, %dut_out, %dut_prefix_0_in, %dut_prefix_0_out, %dut_prefix_1_in, %dut_prefix_1_out = firrtl.instance dut
|
||||
// CHECK-NOT: annotations =
|
||||
// CHECK-SAME: sym {{@.+}} @DUTModule
|
||||
// CHECK-NEXT: %bb_in, %bb_out = firrtl.instance bb sym [[BB_SYM:@.+]] {annotations = [{class = "DummyB"}, {class = "DummyC"}, {circt.nonlocal = @nla_1, class = "circt.nonlocal"}]} @MyBlackBox
|
||||
// CHECK-NEXT: firrtl.strictconnect %bb_in, %dut_prefix_1_in
|
||||
// CHECK-NEXT: firrtl.strictconnect %dut_prefix_1_out, %bb_out
|
||||
// CHECK-NEXT: %bb2_in, %bb2_out = firrtl.instance bb2 sym [[BB2_SYM:@.+]] @MyBlackBox2
|
||||
// CHECK-NEXT: firrtl.strictconnect %bb2_in, %dut_prefix_0_in
|
||||
// CHECK-NEXT: firrtl.strictconnect %dut_prefix_0_out, %bb2_out
|
||||
%dut_in, %dut_out = firrtl.instance dut sym @dut {annotations = [
|
||||
{circt.nonlocal = @nla_3, class = "circt.nonlocal"}
|
||||
]} @DUTModule(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
firrtl.connect %out, %dut_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %dut_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK: sv.verbatim "
|
||||
// CHECK-SAME{LITERAL}: prefix_0 -> {{0}}.{{1}}.{{2}}\0A
|
||||
// CHECK-SAME{LITERAL}: prefix_1 -> {{0}}.{{1}}.{{3}}\0A
|
||||
// CHECK-SAME: output_file = #hw.output_file<"BlackBoxes.txt", excludeFromFileList>
|
||||
// CHECK-SAME: symbols = [
|
||||
// CHECK-SAME: @DUTModule
|
||||
// CHECK-SAME: #hw.innerNameRef<@DUTModule::[[WRAPPER_SYM]]>
|
||||
// CHECK-SAME: #hw.innerNameRef<@ExtractBlackBoxesSimple2::[[BB2_SYM]]>
|
||||
// CHECK-SAME: #hw.innerNameRef<@ExtractBlackBoxesSimple2::[[BB_SYM]]>
|
||||
// CHECK-SAME: ]
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ExtractBlackBoxes IntoDUTSubmodule
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// CHECK: firrtl.circuit "ExtractBlackBoxesIntoDUTSubmodule"
|
||||
firrtl.circuit "ExtractBlackBoxesIntoDUTSubmodule" {
|
||||
// CHECK-LABEL: firrtl.nla @nla_1 [
|
||||
// CHECK-SAME: #hw.innerNameRef<@ExtractBlackBoxesIntoDUTSubmodule::@tb>
|
||||
// CHECK-SAME: #hw.innerNameRef<@TestHarness::@dut>
|
||||
// CHECK-SAME: #hw.innerNameRef<@DUTModule::@BlackBoxes>
|
||||
// CHECK-SAME: #hw.innerNameRef<@BlackBoxes::@bb1>
|
||||
// CHECK-SAME: ]
|
||||
firrtl.nla @nla_1 [
|
||||
#hw.innerNameRef<@ExtractBlackBoxesIntoDUTSubmodule::@tb>,
|
||||
#hw.innerNameRef<@TestHarness::@dut>,
|
||||
#hw.innerNameRef<@DUTModule::@mod>,
|
||||
#hw.innerNameRef<@BBWrapper::@bb1>
|
||||
]
|
||||
// CHECK-LABEL: firrtl.nla @nla_2 [
|
||||
// CHECK-SAME: #hw.innerNameRef<@ExtractBlackBoxesIntoDUTSubmodule::@tb>
|
||||
// CHECK-SAME: #hw.innerNameRef<@TestHarness::@dut>
|
||||
// CHECK-SAME: #hw.innerNameRef<@DUTModule::@BlackBoxes>
|
||||
// CHECK-SAME: #hw.innerNameRef<@BlackBoxes::@bb2>
|
||||
// CHECK-SAME: ]
|
||||
firrtl.nla @nla_2 [
|
||||
#hw.innerNameRef<@ExtractBlackBoxesIntoDUTSubmodule::@tb>,
|
||||
#hw.innerNameRef<@TestHarness::@dut>,
|
||||
#hw.innerNameRef<@DUTModule::@mod>,
|
||||
#hw.innerNameRef<@BBWrapper::@bb2>
|
||||
]
|
||||
firrtl.extmodule private @MyBlackBox(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>) attributes {annotations = [{class = "sifive.enterprise.firrtl.ExtractBlackBoxAnnotation", dest = "BlackBoxes", filename = "BlackBoxes.txt", prefix = "bb"}], defname = "MyBlackBox"}
|
||||
firrtl.module private @BBWrapper(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) {
|
||||
%bb1_in, %bb1_out = firrtl.instance bb1 sym @bb1 {annotations = [{circt.nonlocal = @nla_1, class = "Dummy1"}]} @MyBlackBox(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
%bb2_in, %bb2_out = firrtl.instance bb2 sym @bb2 {annotations = [{circt.nonlocal = @nla_2, class = "Dummy2"}]} @MyBlackBox(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
firrtl.connect %out, %bb2_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %bb2_in, %bb1_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %bb1_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK-LABEL: firrtl.module @BlackBoxes(
|
||||
// CHECK-SAME: in %bb_0_in: !firrtl.uint<8>
|
||||
// CHECK-SAME: out %bb_0_out: !firrtl.uint<8>
|
||||
// CHECK-SAME: in %bb_1_in: !firrtl.uint<8>
|
||||
// CHECK-SAME: out %bb_1_out: !firrtl.uint<8>
|
||||
// CHECK-SAME: ) {
|
||||
// CHECK-NEXT: %bb2_in, %bb2_out = firrtl.instance bb2 sym [[BB2_SYM:@.+]] {annotations = [{circt.nonlocal = @nla_2, class = "Dummy2"}]} @MyBlackBox(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
// CHECK-NEXT: firrtl.strictconnect %bb2_in, %bb_0_in : !firrtl.uint<8>
|
||||
// CHECK-NEXT: firrtl.strictconnect %bb_0_out, %bb2_out : !firrtl.uint<8>
|
||||
// CHECK-NEXT: %bb1_in, %bb1_out = firrtl.instance bb1 sym [[BB1_SYM:@.+]] {annotations = [{circt.nonlocal = @nla_1, class = "Dummy1"}]} @MyBlackBox(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
// CHECK-NEXT: firrtl.strictconnect %bb1_in, %bb_1_in : !firrtl.uint<8>
|
||||
// CHECK-NEXT: firrtl.strictconnect %bb_1_out, %bb1_out : !firrtl.uint<8>
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-LABEL: firrtl.module private @DUTModule
|
||||
firrtl.module private @DUTModule(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) attributes {annotations = [{class = "sifive.enterprise.firrtl.MarkDUTAnnotation"}]} {
|
||||
// CHECK: %BlackBoxes_bb_0_in, %BlackBoxes_bb_0_out, %BlackBoxes_bb_1_in, %BlackBoxes_bb_1_out = firrtl.instance BlackBoxes sym @BlackBoxes
|
||||
// CHECK-SAME: {circt.nonlocal = @nla_2, class = "circt.nonlocal"}
|
||||
// CHECK-SAME: {circt.nonlocal = @nla_1, class = "circt.nonlocal"}
|
||||
// CHECK-SAME: @BlackBoxes
|
||||
// CHECK-NEXT: %mod_in, %mod_out, %mod_bb_0_in, %mod_bb_0_out, %mod_bb_1_in, %mod_bb_1_out = firrtl.instance mod
|
||||
// CHECK-NOT: annotations =
|
||||
// CHECK-SAME: sym [[WRAPPER_SYM:@.+]] @BBWrapper
|
||||
// CHECK-NEXT: firrtl.strictconnect %BlackBoxes_bb_1_in, %mod_bb_1_in
|
||||
// CHECK-NEXT: firrtl.strictconnect %mod_bb_1_out, %BlackBoxes_bb_1_out
|
||||
// CHECK-NEXT: firrtl.strictconnect %BlackBoxes_bb_0_in, %mod_bb_0_in
|
||||
// CHECK-NEXT: firrtl.strictconnect %mod_bb_0_out, %BlackBoxes_bb_0_out
|
||||
%mod_in, %mod_out = firrtl.instance mod sym @mod {annotations = [{circt.nonlocal = @nla_1, class = "circt.nonlocal"}, {circt.nonlocal = @nla_2, class = "circt.nonlocal"}]} @BBWrapper(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
firrtl.connect %out, %mod_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %mod_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
firrtl.module @TestHarness(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) {
|
||||
%dut_in, %dut_out = firrtl.instance dut sym @dut {annotations = [{circt.nonlocal = @nla_1, class = "circt.nonlocal"}, {circt.nonlocal = @nla_2, class = "circt.nonlocal"}]} @DUTModule(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
firrtl.connect %out, %dut_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %dut_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
firrtl.module @ExtractBlackBoxesIntoDUTSubmodule(in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>) {
|
||||
%tb_in, %tb_out = firrtl.instance tb sym @tb {annotations = [{circt.nonlocal = @nla_1, class = "circt.nonlocal"}, {circt.nonlocal = @nla_2, class = "circt.nonlocal"}]} @TestHarness(in in: !firrtl.uint<8>, out out: !firrtl.uint<8>)
|
||||
firrtl.connect %out, %tb_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %tb_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK: sv.verbatim "
|
||||
// CHECK-SAME{LITERAL}: bb_0 -> {{0}}.{{1}}.{{2}}\0A
|
||||
// CHECK-SAME{LITERAL}: bb_1 -> {{0}}.{{1}}.{{3}}\0A
|
||||
// CHECK-SAME: output_file = #hw.output_file<"BlackBoxes.txt", excludeFromFileList>
|
||||
// CHECK-SAME: symbols = [
|
||||
// CHECK-SAME: @DUTModule
|
||||
// CHECK-SAME: #hw.innerNameRef<@DUTModule::[[WRAPPER_SYM]]>
|
||||
// CHECK-SAME: #hw.innerNameRef<@BlackBoxes::[[BB2_SYM]]>
|
||||
// CHECK-SAME: #hw.innerNameRef<@BlackBoxes::[[BB1_SYM]]>
|
||||
// CHECK-SAME: ]
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ExtractClockGates Simple
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// CHECK: firrtl.circuit "ExtractClockGatesSimple"
|
||||
firrtl.circuit "ExtractClockGatesSimple" attributes {annotations = [{class = "sifive.enterprise.firrtl.ExtractClockGatesFileAnnotation", filename = "ClockGates.txt"}]} {
|
||||
firrtl.extmodule private @EICG_wrapper(in in: !firrtl.clock, in en: !firrtl.uint<1>, out out: !firrtl.clock) attributes {defname = "EICG_wrapper"}
|
||||
firrtl.module private @DUTModule(in %clock: !firrtl.clock, in %en: !firrtl.uint<1>) attributes {annotations = [{class = "sifive.enterprise.firrtl.MarkDUTAnnotation"}]} {
|
||||
%gate_in, %gate_en, %gate_out = firrtl.instance gate @EICG_wrapper(in in: !firrtl.clock, in en: !firrtl.uint<1>, out out: !firrtl.clock)
|
||||
firrtl.connect %gate_in, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %gate_en, %en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
}
|
||||
// CHECK-LABEL: firrtl.module @ExtractClockGatesSimple
|
||||
firrtl.module @ExtractClockGatesSimple(in %clock: !firrtl.clock, in %en: !firrtl.uint<1>) {
|
||||
// CHECK: firrtl.instance gate sym [[CKG_SYM:@.+]] @EICG_wrapper
|
||||
%dut_clock, %dut_en = firrtl.instance dut @DUTModule(in clock: !firrtl.clock, in en: !firrtl.uint<1>)
|
||||
firrtl.connect %dut_clock, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %dut_en, %en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
}
|
||||
// CHECK: sv.verbatim "
|
||||
// CHECK-SAME{LITERAL}: clock_gate_0 -> {{0}}.{{1}}\0A
|
||||
// CHECK-SAME: output_file = #hw.output_file<"ClockGates.txt", excludeFromFileList>
|
||||
// CHECK-SAME: symbols = [
|
||||
// CHECK-SAME: @DUTModule
|
||||
// CHECK-SAME: #hw.innerNameRef<@ExtractClockGatesSimple::[[CKG_SYM]]>
|
||||
// CHECK-SAME: ]
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ExtractClockGates TestHarnessOnly
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// CHECK: firrtl.circuit "ExtractClockGatesTestHarnessOnly"
|
||||
firrtl.circuit "ExtractClockGatesTestHarnessOnly" attributes {annotations = [{class = "sifive.enterprise.firrtl.ExtractClockGatesFileAnnotation", filename = "ClockGates.txt"}]} {
|
||||
firrtl.extmodule private @EICG_wrapper(in in: !firrtl.clock, in en: !firrtl.uint<1>, out out: !firrtl.clock) attributes {defname = "EICG_wrapper"}
|
||||
firrtl.module private @DUTModule(in %clock: !firrtl.clock, in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>, in %en: !firrtl.uint<1>) attributes {annotations = [{class = "sifive.enterprise.firrtl.MarkDUTAnnotation"}]} {
|
||||
%0 = firrtl.add %in, %en : (!firrtl.uint<8>, !firrtl.uint<1>) -> !firrtl.uint<9>
|
||||
%_io_out_T = firrtl.node %0 : !firrtl.uint<9>
|
||||
%1 = firrtl.tail %_io_out_T, 1 : (!firrtl.uint<9>) -> !firrtl.uint<8>
|
||||
%_io_out_T_1 = firrtl.node %1 : !firrtl.uint<8>
|
||||
firrtl.connect %out, %_io_out_T_1 : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
firrtl.module @ExtractClockGatesTestHarnessOnly(in %clock: !firrtl.clock, in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>, in %en: !firrtl.uint<1>) {
|
||||
%gate_in, %gate_en, %gate_out = firrtl.instance gate @EICG_wrapper(in in: !firrtl.clock, in en: !firrtl.uint<1>, out out: !firrtl.clock)
|
||||
firrtl.connect %gate_in, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %gate_en, %en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
%dut_clock, %dut_in, %dut_out, %dut_en = firrtl.instance dut @DUTModule(in clock: !firrtl.clock, in in: !firrtl.uint<8>, out out: !firrtl.uint<8>, in en: !firrtl.uint<1>)
|
||||
firrtl.connect %dut_clock, %gate_out : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %dut_en, %en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
firrtl.connect %out, %dut_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %dut_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK-NOT: sv.verbatim "clock_gate
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ExtractClockGates Mixed
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Mixed ClockGate extraction should only extract clock gates in the DUT
|
||||
// CHECK: firrtl.circuit "ExtractClockGatesMixed"
|
||||
firrtl.circuit "ExtractClockGatesMixed" attributes {annotations = [{class = "sifive.enterprise.firrtl.ExtractClockGatesFileAnnotation", filename = "ClockGates.txt"}]} {
|
||||
firrtl.extmodule private @EICG_wrapper(in in: !firrtl.clock, in en: !firrtl.uint<1>, out out: !firrtl.clock) attributes {defname = "EICG_wrapper"}
|
||||
// CHECK-LABEL: firrtl.module private @Child
|
||||
firrtl.module private @Child(in %clock: !firrtl.clock, in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>, in %en: !firrtl.uint<1>) {
|
||||
// CHECK-NOT: firrtl.instance gate sym @ckg1 @EICG_wrapper
|
||||
%gate_in, %gate_en, %gate_out = firrtl.instance gate sym @ckg1 @EICG_wrapper(in in: !firrtl.clock, in en: !firrtl.uint<1>, out out: !firrtl.clock)
|
||||
firrtl.connect %gate_in, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %gate_en, %en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
firrtl.connect %out, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK-LABEL: firrtl.module private @DUTModule
|
||||
firrtl.module private @DUTModule(in %clock: !firrtl.clock, in %in: !firrtl.uint<8>, out %out: !firrtl.uint<8>, in %en: !firrtl.uint<1>) attributes {annotations = [{class = "sifive.enterprise.firrtl.MarkDUTAnnotation"}]} {
|
||||
%inst_clock, %inst_in, %inst_out, %inst_en = firrtl.instance inst sym @inst @Child(in clock: !firrtl.clock, in in: !firrtl.uint<8>, out out: !firrtl.uint<8>, in en: !firrtl.uint<1>)
|
||||
firrtl.connect %inst_clock, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %inst_in, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %inst_en, %en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
// CHECK-NOT: firrtl.instance gate sym @ckg2 @EICG_wrapper
|
||||
%gate_in, %gate_en, %gate_out = firrtl.instance gate sym @ckg2 @EICG_wrapper(in in: !firrtl.clock, in en: !firrtl.uint<1>, out out: !firrtl.clock)
|
||||
firrtl.connect %gate_in, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %gate_en, %en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
firrtl.connect %out, %in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK-LABEL: firrtl.module @ExtractClockGatesMixed
|
||||
firrtl.module @ExtractClockGatesMixed(in %clock: !firrtl.clock, in %intf_in: !firrtl.uint<8>, out %intf_out: !firrtl.uint<8>, in %intf_en: !firrtl.uint<1>, in %en: !firrtl.uint<1>) {
|
||||
// CHECK: firrtl.instance gate sym @ckg3 @EICG_wrapper
|
||||
%gate_in, %gate_en, %gate_out = firrtl.instance gate sym @ckg3 @EICG_wrapper(in in: !firrtl.clock, in en: !firrtl.uint<1>, out out: !firrtl.clock)
|
||||
firrtl.connect %gate_in, %clock : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %gate_en, %en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
%dut_clock, %dut_in, %dut_out, %dut_en = firrtl.instance dut sym @dut @DUTModule(in clock: !firrtl.clock, in in: !firrtl.uint<8>, out out: !firrtl.uint<8>, in en: !firrtl.uint<1>)
|
||||
// CHECK: firrtl.instance gate sym @ckg2 @EICG_wrapper
|
||||
// CHECK: firrtl.instance gate sym @ckg1 @EICG_wrapper
|
||||
firrtl.connect %dut_clock, %gate_out : !firrtl.clock, !firrtl.clock
|
||||
firrtl.connect %dut_en, %intf_en : !firrtl.uint<1>, !firrtl.uint<1>
|
||||
firrtl.connect %intf_out, %dut_out : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
firrtl.connect %dut_in, %intf_in : !firrtl.uint<8>, !firrtl.uint<8>
|
||||
}
|
||||
// CHECK: sv.verbatim "
|
||||
// CHECK-SAME{LITERAL}: clock_gate_0 -> {{0}}.{{1}}.{{2}}\0A
|
||||
// CHECK-SAME{LITERAL}: clock_gate_1 -> {{0}}.{{3}}\0A
|
||||
// CHECK-SAME: output_file = #hw.output_file<"ClockGates.txt", excludeFromFileList>
|
||||
// CHECK-SAME: symbols = [
|
||||
// CHECK-SAME: @DUTModule
|
||||
// CHECK-SAME: #hw.innerNameRef<@DUTModule::@inst>
|
||||
// CHECK-SAME: #hw.innerNameRef<@ExtractClockGatesMixed::@ckg1>
|
||||
// CHECK-SAME: #hw.innerNameRef<@ExtractClockGatesMixed::@ckg2>
|
||||
// CHECK-SAME: ]
|
||||
}
|
|
@ -204,6 +204,11 @@ static cl::opt<bool>
|
|||
cl::desc("add a level of hierarchy to the DUT"),
|
||||
cl::init(true), cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
extractInstances("extract-instances",
|
||||
cl::desc("extract black boxes, seq mems, and clock gates"),
|
||||
cl::init(true), cl::cat(mainCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
prefixModules("prefix-modules",
|
||||
cl::desc("prefix modules with NestedPrefixAnnotation"),
|
||||
|
@ -550,6 +555,9 @@ processBuffer(MLIRContext &context, TimingScope &ts, llvm::SourceMgr &sourceMgr,
|
|||
firrtl::createRemoveUnusedPortsPass());
|
||||
}
|
||||
|
||||
if (extractInstances)
|
||||
pm.addNestedPass<firrtl::CircuitOp>(firrtl::createExtractInstancesPass());
|
||||
|
||||
if (emitMetadata)
|
||||
pm.nest<firrtl::CircuitOp>().addPass(firrtl::createCreateSiFiveMetadataPass(
|
||||
replSeqMem, replSeqMemCircuit, replSeqMemFile));
|
||||
|
|
Loading…
Reference in New Issue