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
|
||||
}
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
### Design Under Test
|
||||
|
|
|
@ -40,6 +40,8 @@ std::unique_ptr<mlir::Pass> createInlinerPass();
|
|||
|
||||
std::unique_ptr<mlir::Pass> createBlackBoxMemoryPass();
|
||||
|
||||
std::unique_ptr<mlir::Pass> createEmitMetadataPass();
|
||||
|
||||
std::unique_ptr<mlir::Pass> createExpandWhensPass();
|
||||
|
||||
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"> {
|
||||
let summary = "Remove all when conditional blocks.";
|
||||
let description = [{
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
add_circt_dialect_library(CIRCTFIRRTLTransforms
|
||||
add_circt_dialect_library(
|
||||
CIRCTFIRRTLTransforms
|
||||
BlackBoxMemory.cpp
|
||||
BlackBoxReader.cpp
|
||||
CheckCombCycles.cpp
|
||||
EmitMetadata.cpp
|
||||
ExpandWhens.cpp
|
||||
GrandCentral.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