mirror of https://github.com/llvm/circt.git
[SV][LowerToHW] Add ExportModuleHierarchy pass. (#1792)
* [LowerToHW] Lower mainModule and DesignUnderTest attributes into moduleHierarchyFile attribute. Since these are currently only used for the export module hierarchy pass, these have been replaced with a more generic attribute marking which modules should have their hierarchies exported to output files. * [SV] Add ExportModuleHierarchy pass. This adds a pass that collects the full module hierarchy into a JSON string, which is then exported as part of an sv.verbatim op. The pass collects a hierarchy for each module with the firrtl.moduleHierarchyFile attribute. The pass can be enabled in firtool by providing the --export-module-hierarchy option.
This commit is contained in:
parent
e0cd23fe85
commit
e97cff3fd5
|
@ -34,6 +34,7 @@ std::unique_ptr<mlir::Pass> createHWLegalizeModulesPass();
|
|||
std::unique_ptr<mlir::Pass> createHWGeneratorCalloutPass();
|
||||
std::unique_ptr<mlir::Pass> createHWMemSimImplPass();
|
||||
std::unique_ptr<mlir::Pass> createSVExtractTestCodePass();
|
||||
std::unique_ptr<mlir::Pass> createHWExportModuleHierarchyPass();
|
||||
/// Generate the code for registering passes.
|
||||
#define GEN_PASS_REGISTRATION
|
||||
#include "circt/Dialect/SV/SVPasses.h.inc"
|
||||
|
|
|
@ -117,4 +117,17 @@ def SVExtractTestCode : Pass<"sv-extract-test-code", "ModuleOp"> {
|
|||
let dependentDialects = ["circt::sv::SVDialect"];
|
||||
}
|
||||
|
||||
def HWExportModuleHierarchy : Pass<"hw-export-module-hierarchy",
|
||||
"mlir::ModuleOp"> {
|
||||
let summary = "Export module and instance hierarchy information";
|
||||
let description = [{
|
||||
This pass exports the module and instance hierarchy tree for each module
|
||||
with the firrtl.moduleHierarchyFile attribute. These are lowered to
|
||||
sv.verbatim ops with the output_file attribute.
|
||||
}];
|
||||
|
||||
let constructor = "circt::sv::createHWExportModuleHierarchyPass()";
|
||||
let dependentDialects = ["circt::sv::SVDialect"];
|
||||
}
|
||||
|
||||
#endif // CIRCT_DIALECT_SV_SVPASSES
|
||||
|
|
|
@ -41,6 +41,10 @@ static const char coverAnnoClass[] =
|
|||
"sifive.enterprise.firrtl.ExtractCoverageAnnotation";
|
||||
static const char dutAnnoClass[] = "sifive.enterprise.firrtl.MarkDUTAnnotation";
|
||||
|
||||
/// Attribute that indicates that the module hierarchy starting at the annotated
|
||||
/// module should be dumped to a file.
|
||||
static const char moduleHierarchyFileAttrName[] = "firrtl.moduleHierarchyFile";
|
||||
|
||||
/// Given a FIRRTL type, return the corresponding type for the HW dialect.
|
||||
/// This returns a null type if it cannot be lowered.
|
||||
static Type lowerType(Type type) {
|
||||
|
@ -425,19 +429,27 @@ void FIRRTLModuleLowering::runOnOperation() {
|
|||
bind->moveBefore(bind->getParentOfType<hw::HWModuleOp>());
|
||||
}
|
||||
|
||||
// Add attributes specific to the new main module, since the notion of a
|
||||
// "main" module goes away after lowering to HW.
|
||||
auto *newMainModule = state.oldToNewModuleMap[circuit.getMainModule()];
|
||||
newMainModule->setAttr(
|
||||
moduleHierarchyFileAttrName,
|
||||
hw::OutputFileAttr::get(
|
||||
StringAttr::get(circuit.getContext(), ""),
|
||||
StringAttr::get(circuit.getContext(), "testharness_hier.json"),
|
||||
/*exclude_from_filelist=*/
|
||||
BoolAttr::get(circuit.getContext(), true),
|
||||
/*exclude_replicated_ops=*/
|
||||
BoolAttr::get(circuit.getContext(), true), circuit.getContext()));
|
||||
|
||||
// Finally delete all the old modules.
|
||||
for (auto oldNew : state.oldToNewModuleMap)
|
||||
oldNew.first->erase();
|
||||
|
||||
// Now that the modules are moved over, remove the Circuit. We pop the 'main
|
||||
// module' specified in the Circuit into an attribute on the top level module.
|
||||
getOperation()->setAttr(
|
||||
"firrtl.mainModule",
|
||||
StringAttr::get(circuit.getContext(), circuit.name()));
|
||||
|
||||
// Emit all the macros and preprocessor gunk at the start of the file.
|
||||
lowerFileHeader(circuit, state);
|
||||
|
||||
// Now that the modules are moved over, remove the Circuit.
|
||||
circuit.erase();
|
||||
}
|
||||
|
||||
|
@ -724,8 +736,16 @@ FIRRTLModuleLowering::lowerModule(FModuleOp oldModule, Block *topLevelModule,
|
|||
builder.create<hw::HWModuleOp>(oldModule.getLoc(), nameAttr, ports);
|
||||
if (auto outputFile = oldModule->getAttr("output_file"))
|
||||
newModule->setAttr("output_file", outputFile);
|
||||
// Mark the design under test as a module of interest for exporting module
|
||||
// hierarchy information.
|
||||
if (AnnotationSet::removeAnnotations(oldModule, dutAnnoClass))
|
||||
newModule->setAttr("firrtl.DesignUnderTest", builder.getUnitAttr());
|
||||
newModule->setAttr(moduleHierarchyFileAttrName,
|
||||
hw::OutputFileAttr::get(
|
||||
builder.getStringAttr(""),
|
||||
builder.getStringAttr("module_hier.json"),
|
||||
/*exclude_from_filelist=*/builder.getBoolAttr(true),
|
||||
/*exclude_replicated_ops=*/builder.getBoolAttr(true),
|
||||
&getContext()));
|
||||
loweringState.processRemainingAnnotations(oldModule,
|
||||
AnnotationSet(oldModule));
|
||||
return newModule;
|
||||
|
|
|
@ -7,6 +7,7 @@ add_circt_dialect_library(CIRCTSVTransforms
|
|||
HWMemSimImpl.cpp
|
||||
PrettifyVerilog.cpp
|
||||
SVExtractTestCode.cpp
|
||||
HWExportModuleHierarchy.cpp
|
||||
|
||||
DEPENDS
|
||||
CIRCTSVTransformsIncGen
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
//===- HWExportModuleHierarchy.cpp - Export Module 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
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Export the module and instance hierarchy information to JSON. This pass looks
|
||||
// for modules with the firrtl.moduleHierarchyFile attribute and collects the
|
||||
// hierarchy starting at those modules. The hierarchy information is then
|
||||
// encoded as JSON in an sv.verbatim op with the output_file attribute set.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "PassDetail.h"
|
||||
#include "circt/Dialect/HW/HWOps.h"
|
||||
#include "circt/Dialect/SV/SVPasses.h"
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "llvm/Support/JSON.h"
|
||||
|
||||
using namespace circt;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Pass Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
struct HWExportModuleHierarchyPass
|
||||
: public sv::HWExportModuleHierarchyBase<HWExportModuleHierarchyPass> {
|
||||
void runOnOperation() override;
|
||||
};
|
||||
|
||||
/// Recursively print the module hierarchy as serialized as JSON.
|
||||
static void printHierarchy(hw::InstanceOp &inst, SymbolTable &symbolTable,
|
||||
llvm::json::OStream &J) {
|
||||
J.object([&] {
|
||||
J.attribute("instance_name", inst.instanceName());
|
||||
J.attribute("module_name", inst.moduleName());
|
||||
J.attributeArray("instances", [&] {
|
||||
auto moduleOp =
|
||||
symbolTable.lookup<hw::HWModuleOp>(inst.moduleNameAttr().getValue());
|
||||
|
||||
// Only recurse on module ops, not extern or generated ops, whose internal
|
||||
// are opaque.
|
||||
if (moduleOp) {
|
||||
for (auto op : moduleOp.getOps<hw::InstanceOp>()) {
|
||||
printHierarchy(op, symbolTable, J);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Return the JSON-serialized module hierarchy for the given module as the top
|
||||
/// of the hierarchy.
|
||||
static std::string extractHierarchyFromTop(hw::HWModuleOp op,
|
||||
SymbolTable &symbolTable) {
|
||||
std::string resultBuffer;
|
||||
llvm::raw_string_ostream os(resultBuffer);
|
||||
llvm::json::OStream J(os);
|
||||
|
||||
// As a special case for top-level module, set instance name to module name,
|
||||
// since the top-level module is not instantiated.
|
||||
J.object([&] {
|
||||
J.attribute("instance_name", op.getName());
|
||||
J.attribute("module_name", op.getName());
|
||||
J.attributeArray("instances", [&] {
|
||||
for (auto op : op.getOps<hw::InstanceOp>())
|
||||
printHierarchy(op, symbolTable, J);
|
||||
});
|
||||
});
|
||||
|
||||
return resultBuffer;
|
||||
}
|
||||
|
||||
/// Find the modules corresponding to the firrtl mainModule and DesignUnderTest,
|
||||
/// and if they exist, emit a verbatim op with the module hierarchy for each.
|
||||
void HWExportModuleHierarchyPass::runOnOperation() {
|
||||
mlir::ModuleOp mlirModule = getOperation();
|
||||
auto builder = OpBuilder::atBlockEnd(mlirModule.getBody());
|
||||
SymbolTable symbolTable(mlirModule);
|
||||
|
||||
for (auto op : mlirModule.getOps<hw::HWModuleOp>()) {
|
||||
if (auto attr = op->getAttr("firrtl.moduleHierarchyFile")) {
|
||||
auto verbatimOp = builder.create<sv::VerbatimOp>(
|
||||
builder.getUnknownLoc(), extractHierarchyFromTop(op, symbolTable));
|
||||
verbatimOp->setAttr("output_file", attr);
|
||||
op->removeAttr("firrtl.moduleHierarchyFile");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Pass Creation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
std::unique_ptr<mlir::Pass> sv::createHWExportModuleHierarchyPass() {
|
||||
return std::make_unique<HWExportModuleHierarchyPass>();
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
// RUN: circt-opt -lower-firrtl-to-hw %s -verify-diagnostics | FileCheck %s
|
||||
|
||||
// The firrtl.circuit should be removed, the main module name moved to an
|
||||
// attribute on the module.
|
||||
// CHECK-LABEL: {{^}}module attributes {firrtl.mainModule = "Simple"} {
|
||||
// The firrtl.circuit should be removed.
|
||||
// CHECK-NOT: firrtl.circuit
|
||||
|
||||
// We should get a large header boilerplate.
|
||||
|
@ -20,7 +18,7 @@ firrtl.circuit "Simple" {
|
|||
FORMAT = "xyz_timeout=%d\0A",
|
||||
WIDTH = 32 : i8}}
|
||||
|
||||
// CHECK-LABEL: hw.module @Simple(%in1: i4, %in2: i2, %in3: i8) -> (out4: i4) {
|
||||
// CHECK-LABEL: hw.module @Simple(%in1: i4, %in2: i2, %in3: i8) -> (out4: i4) attributes {firrtl.moduleHierarchyFile
|
||||
firrtl.module @Simple(in %in1: !firrtl.uint<4>,
|
||||
in %in2: !firrtl.uint<2>,
|
||||
in %in3: !firrtl.sint<8>,
|
||||
|
|
|
@ -900,7 +900,7 @@ firrtl.circuit "Simple" attributes {annotations = [{class =
|
|||
}
|
||||
|
||||
// CHECK-LABEL: hw.module @FooDUT
|
||||
// CHECK: attributes {firrtl.DesignUnderTest}
|
||||
// CHECK: attributes {firrtl.moduleHierarchyFile
|
||||
firrtl.module @FooDUT() attributes {annotations = [
|
||||
{class = "sifive.enterprise.firrtl.MarkDUTAnnotation"}]} {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
// RUN: circt-opt -pass-pipeline=hw-export-module-hierarchy %s | FileCheck %s
|
||||
|
||||
// CHECK: sv.verbatim "{\22instance_name\22:\22TestHarness\22,\22module_name\22:\22TestHarness\22,\22instances\22:[{\22instance_name\22:\22main_design\22,\22module_name\22:\22MainDesign\22,\22instances\22:[{\22instance_name\22:\22inner\22,\22module_name\22:\22InnerModule\22,\22instances\22:[]}]}]}" {output_file = {directory = "", exclude_from_filelist = true, exclude_replicated_ops = true, name = "testharness_hier.json"}}
|
||||
|
||||
hw.module @InnerModule(%in: i1) -> (out: i1) {
|
||||
hw.output %in : i1
|
||||
}
|
||||
|
||||
hw.module @MainDesign(%in: i1) -> (out: i1) {
|
||||
%0 = hw.instance "inner" @InnerModule(in: %in: i1) -> (out: i1)
|
||||
hw.output %0 : i1
|
||||
}
|
||||
|
||||
hw.module @TestHarness() attributes {firrtl.moduleHierarchyFile = {directory = "", exclude_from_filelist = true, exclude_replicated_ops = true, name = "testharness_hier.json"}} {
|
||||
%0 = hw.constant 1 : i1
|
||||
hw.instance "main_design" @MainDesign(in: %0: i1) -> (out: i1)
|
||||
}
|
|
@ -150,6 +150,9 @@ static cl::opt<bool>
|
|||
cl::desc("create interfaces and data/memory taps from SiFive "
|
||||
"Grand Central annotations"),
|
||||
cl::init(false));
|
||||
static cl::opt<bool> exportModuleHierarchy(
|
||||
"export-module-hierarchy",
|
||||
cl::desc("export module and instance hierarchy as JSON"), cl::init(false));
|
||||
|
||||
static cl::opt<bool>
|
||||
checkCombCycles("firrtl-check-comb-cycles",
|
||||
|
@ -359,6 +362,9 @@ processBuffer(MLIRContext &context, TimingScope &ts, llvm::SourceMgr &sourceMgr,
|
|||
auto &modulePM = pm.nest<hw::HWModuleOp>();
|
||||
modulePM.addPass(sv::createPrettifyVerilogPass());
|
||||
}
|
||||
|
||||
if (exportModuleHierarchy)
|
||||
pm.addPass(sv::createHWExportModuleHierarchyPass());
|
||||
}
|
||||
|
||||
// Load the emitter options from the command line. Command line options if
|
||||
|
|
Loading…
Reference in New Issue