mirror of https://github.com/llvm/circt.git
[FIRRTL] Add EmitMetada pass with RetimeModulesAnnotation support
This adds a new `EmitMetadata` pass to FIRRTL which a dumping ground for any simple metadata generation passes that don't make sense to implement as a standalone pass. The first pass implemented is `RetimeModules` which just collects the name of any annotated module and emits it into a JSON array.
This commit is contained in:
parent
ef83a402af
commit
f4bfc260a2
|
@ -418,6 +418,42 @@ Example:
|
||||||
"inclusive": true
|
"inclusive": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### RetimeModuleAnnotation
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
| ---------- | ------ | ------------- |
|
||||||
|
| class | string | `sifive.enterprise.firrtl.RetimeModuleAnnotation` |
|
||||||
|
|
||||||
|
This annotation is used to mark modules which should be retimed, and is
|
||||||
|
generally just passed through to other tools.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"class": "sifive.enterprise.firrtl.RetimeModuleAnnotation"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### RetimeModulesAnnotation
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
| ---------- | ------ | ------------- |
|
||||||
|
| class | string | `sifive.enterprise.firrtl.RetimeModulesAnnotation` |
|
||||||
|
| filename | string | The filename with full path where it will be written |
|
||||||
|
|
||||||
|
This annotation triggers the creation of a file containing a JSON array
|
||||||
|
containing the names of all modules annotated with the
|
||||||
|
`RetimeModuleAnnotation`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"class": "sifive.enterprise.firrtl.RetimeModuleAnnotation",
|
||||||
|
"filename": "retime_modules.json"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## FIRRTL specific attributes applied to HW Modules
|
## FIRRTL specific attributes applied to HW Modules
|
||||||
|
|
||||||
### Design Under Test
|
### Design Under Test
|
||||||
|
|
|
@ -40,6 +40,8 @@ std::unique_ptr<mlir::Pass> createInlinerPass();
|
||||||
|
|
||||||
std::unique_ptr<mlir::Pass> createBlackBoxMemoryPass();
|
std::unique_ptr<mlir::Pass> createBlackBoxMemoryPass();
|
||||||
|
|
||||||
|
std::unique_ptr<mlir::Pass> createEmitMetadataPass();
|
||||||
|
|
||||||
std::unique_ptr<mlir::Pass> createExpandWhensPass();
|
std::unique_ptr<mlir::Pass> createExpandWhensPass();
|
||||||
|
|
||||||
std::unique_ptr<mlir::Pass> createInferWidthsPass();
|
std::unique_ptr<mlir::Pass> createInferWidthsPass();
|
||||||
|
|
|
@ -90,6 +90,14 @@ def BlackBoxMemory : Pass<"firrtl-blackbox-memory", "firrtl::CircuitOp"> {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def EmitMetadata : Pass<"firrtl-emit-metadata", "firrtl::CircuitOp"> {
|
||||||
|
let summary = "Emit metadata of the FIRRTL modules";
|
||||||
|
let description = [{
|
||||||
|
This pass handles the emission of several different kinds of metadata.
|
||||||
|
}];
|
||||||
|
let constructor = "circt::firrtl::createEmitMetadataPass()";
|
||||||
|
}
|
||||||
|
|
||||||
def ExpandWhens : Pass<"firrtl-expand-whens", "firrtl::FModuleOp"> {
|
def ExpandWhens : Pass<"firrtl-expand-whens", "firrtl::FModuleOp"> {
|
||||||
let summary = "Remove all when conditional blocks.";
|
let summary = "Remove all when conditional blocks.";
|
||||||
let description = [{
|
let description = [{
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
add_circt_dialect_library(CIRCTFIRRTLTransforms
|
add_circt_dialect_library(
|
||||||
|
CIRCTFIRRTLTransforms
|
||||||
BlackBoxMemory.cpp
|
BlackBoxMemory.cpp
|
||||||
BlackBoxReader.cpp
|
BlackBoxReader.cpp
|
||||||
CheckCombCycles.cpp
|
CheckCombCycles.cpp
|
||||||
|
EmitMetadata.cpp
|
||||||
ExpandWhens.cpp
|
ExpandWhens.cpp
|
||||||
GrandCentral.cpp
|
GrandCentral.cpp
|
||||||
GrandCentralTaps.cpp
|
GrandCentralTaps.cpp
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
//===- EmitMetadata.cpp - Emit various types of metadata --------*- 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file defines the EmitMetadata pass.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "PassDetails.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 "circt/Dialect/HW/HWAttributes.h"
|
||||||
|
#include "circt/Dialect/SV/SVOps.h"
|
||||||
|
#include "llvm/ADT/STLExtras.h"
|
||||||
|
#include "llvm/Support/JSON.h"
|
||||||
|
|
||||||
|
using namespace circt;
|
||||||
|
using namespace firrtl;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
class EmitMetadataPass : public EmitMetadataBase<EmitMetadataPass> {
|
||||||
|
LogicalResult emitRetimeModulesMetadata();
|
||||||
|
void getDependentDialects(mlir::DialectRegistry ®istry) const override;
|
||||||
|
void runOnOperation() override;
|
||||||
|
};
|
||||||
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
/// This will search for a target annotation and remove it from the operation.
|
||||||
|
/// If the annotation has a filename, it will be returned in the output
|
||||||
|
/// argument. If the annotation is missing the filename member, or if more than
|
||||||
|
/// one matching annotation is attached, it will print an error and return
|
||||||
|
/// failure.
|
||||||
|
static LogicalResult removeAnnotationWithFilename(Operation *op,
|
||||||
|
StringRef annoClass,
|
||||||
|
StringRef &filename) {
|
||||||
|
filename = "";
|
||||||
|
bool error = false;
|
||||||
|
AnnotationSet::removeAnnotations(op, [&](Annotation anno) {
|
||||||
|
// If there was a previous error or its not a match, continue.
|
||||||
|
if (error || !anno.isClass(annoClass))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If we have already found a matching annotation, error.
|
||||||
|
if (!filename.empty()) {
|
||||||
|
op->emitError("More than one ") << annoClass << " annotation attached";
|
||||||
|
error = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the filename from the annotation.
|
||||||
|
auto filenameAttr = anno.getMember<StringAttr>("filename");
|
||||||
|
if (!filenameAttr) {
|
||||||
|
op->emitError(annoClass) << " requires a filename";
|
||||||
|
error = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require a non-empty filename.
|
||||||
|
filename = filenameAttr.getValue();
|
||||||
|
if (filename.empty()) {
|
||||||
|
op->emitError(annoClass) << " requires a non-empty filename";
|
||||||
|
error = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// If there was a problem above, return failure.
|
||||||
|
return failure(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function collects the name of each module annotated and prints them
|
||||||
|
/// all as a JSON array.
|
||||||
|
LogicalResult EmitMetadataPass::emitRetimeModulesMetadata() {
|
||||||
|
|
||||||
|
// Circuit level annotation.
|
||||||
|
auto *retimeModulesAnnoClass =
|
||||||
|
"sifive.enterprise.firrtl.RetimeModulesAnnotation";
|
||||||
|
// Per module annotation.
|
||||||
|
auto *retimeModuleAnnoClass =
|
||||||
|
"sifive.enterprise.firrtl.RetimeModuleAnnotation";
|
||||||
|
|
||||||
|
auto *context = &getContext();
|
||||||
|
auto circuitOp = getOperation();
|
||||||
|
|
||||||
|
// Get the filename, removing the annotation from the circuit.
|
||||||
|
StringRef filename;
|
||||||
|
if (failed(removeAnnotationWithFilename(circuitOp, retimeModulesAnnoClass,
|
||||||
|
filename)))
|
||||||
|
return failure();
|
||||||
|
|
||||||
|
if (filename.empty())
|
||||||
|
return success();
|
||||||
|
|
||||||
|
// Create a string buffer for the json data.
|
||||||
|
std::string buffer;
|
||||||
|
llvm::raw_string_ostream os(buffer);
|
||||||
|
llvm::json::OStream j(os);
|
||||||
|
|
||||||
|
// The output is a json array with each element a module name.
|
||||||
|
unsigned index = 0;
|
||||||
|
SmallVector<Attribute> symbols;
|
||||||
|
SmallString<3> placeholder;
|
||||||
|
j.array([&] {
|
||||||
|
for (auto module : circuitOp.getBody()->getOps<FModuleLike>()) {
|
||||||
|
// The annotation has no supplemental information, just remove it.
|
||||||
|
if (!AnnotationSet::removeAnnotations(module, retimeModuleAnnoClass))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// We use symbol substitution to make sure we output the correct thing
|
||||||
|
// when the module goes through renaming.
|
||||||
|
j.value(("{{" + Twine(index++) + "}}").str());
|
||||||
|
symbols.push_back(SymbolRefAttr::get(context, module.moduleName()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Put the retime information in a verbatim operation.
|
||||||
|
auto builder = OpBuilder::atBlockEnd(circuitOp.getBody());
|
||||||
|
auto verbatimOp = builder.create<sv::VerbatimOp>(
|
||||||
|
circuitOp.getLoc(), buffer, ValueRange(), builder.getArrayAttr(symbols));
|
||||||
|
auto fileAttr = hw::OutputFileAttr::getFromFilename(
|
||||||
|
context, filename, /*excludeFromFilelist=*/true);
|
||||||
|
verbatimOp->setAttr("output_file", fileAttr);
|
||||||
|
return success();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitMetadataPass::getDependentDialects(
|
||||||
|
mlir::DialectRegistry ®istry) const {
|
||||||
|
// We need this for SV verbatim and HW attributes.
|
||||||
|
registry.insert<hw::HWDialect, sv::SVDialect>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitMetadataPass::runOnOperation() {
|
||||||
|
if (failed(emitRetimeModulesMetadata()))
|
||||||
|
return signalPassFailure();
|
||||||
|
|
||||||
|
// This pass does not modify the hierarchy.
|
||||||
|
markAnalysesPreserved<InstanceGraph>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<mlir::Pass> circt::firrtl::createEmitMetadataPass() {
|
||||||
|
return std::make_unique<EmitMetadataPass>();
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
// RUN: circt-opt --pass-pipeline='firrtl.circuit(firrtl-emit-metadata)' --verify-diagnostics --split-input-file %s
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// RetimeModules
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// expected-error @+1 {{sifive.enterprise.firrtl.RetimeModulesAnnotation requires a filename}}
|
||||||
|
firrtl.circuit "NoFilename" attributes { annotations = [{
|
||||||
|
class = "sifive.enterprise.firrtl.RetimeModulesAnnotation"
|
||||||
|
}]} {
|
||||||
|
firrtl.module @NoFilename() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----
|
||||||
|
|
||||||
|
// expected-error @+1 {{sifive.enterprise.firrtl.RetimeModulesAnnotation requires a non-empty filename}}
|
||||||
|
firrtl.circuit "EmptyFilename" attributes { annotations = [{
|
||||||
|
class = "sifive.enterprise.firrtl.RetimeModulesAnnotation",
|
||||||
|
filename = ""
|
||||||
|
}]} {
|
||||||
|
firrtl.module @EmptyFilename() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----
|
||||||
|
|
||||||
|
// expected-error @+1 {{more than one sifive.enterprise.firrtl.RetimeModulesAnnotation annotation attached}}
|
||||||
|
firrtl.circuit "MultipleAnnotations" attributes { annotations = [{
|
||||||
|
class = "sifive.enterprise.firrtl.RetimeModulesAnnotation",
|
||||||
|
filename = "test0.json"
|
||||||
|
}, {
|
||||||
|
class = "sifive.enterprise.firrtl.RetimeModulesAnnotation",
|
||||||
|
filename = "test1.json"
|
||||||
|
}]} {
|
||||||
|
firrtl.module @MultipleAnnotations() { }
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
// RUN: circt-opt --pass-pipeline='firrtl.circuit(firrtl-emit-metadata)' %s | FileCheck %s
|
||||||
|
|
||||||
|
firrtl.circuit "empty" {
|
||||||
|
firrtl.module @empty() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: firrtl.circuit "empty" {
|
||||||
|
// CHECK-NEXT: firrtl.module @empty() {
|
||||||
|
// CHECK-NEXT: }
|
||||||
|
// CHECK-NEXT: }
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// RetimeModules
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
firrtl.circuit "retime0" attributes { annotations = [{
|
||||||
|
class = "sifive.enterprise.firrtl.RetimeModulesAnnotation",
|
||||||
|
filename = "./tmp/retime_modules.json"
|
||||||
|
}]} {
|
||||||
|
|
||||||
|
firrtl.module @retime0() attributes { annotations = [{
|
||||||
|
class = "sifive.enterprise.firrtl.RetimeModuleAnnotation"
|
||||||
|
}]} { }
|
||||||
|
|
||||||
|
firrtl.module @retime1() { }
|
||||||
|
|
||||||
|
firrtl.module @retime2() attributes { annotations = [{
|
||||||
|
class = "sifive.enterprise.firrtl.RetimeModuleAnnotation"
|
||||||
|
}]} { }
|
||||||
|
}
|
||||||
|
// CHECK-LABEL: firrtl.circuit "retime0" {
|
||||||
|
// CHECK: firrtl.module @retime0() {
|
||||||
|
// CHECK: firrtl.module @retime1() {
|
||||||
|
// CHECK: firrtl.module @retime2() {
|
||||||
|
// CHECK{LITERAL}: sv.verbatim "[\22{{0}}\22,\22{{1}}\22]"
|
||||||
|
// CHECK-SAME: output_file = #hw.output_file<"tmp/retime_modules.json", excludeFromFileList>
|
||||||
|
// CHECK-SAME: symbols = [@retime0, @retime2]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue