[FIRRTL][InferResets] Generalize FART to support sync reset (#7476)

The FullAsyncResetAnnotation is deprecated and replaced by
FullResetAnnotation which includes a resetType argument that must be
'sync' or 'async'.  IgnoreFullAsyncResetAnnotation is deprecated and
replaced by ExcludeFromFullResetAnnotation. The new annotations are in
package circt.

The behavior of FullResetAnnotation with resetType == 'async' is
identical to that of the old FullAsyncResetAnnotation. The behavior of
FullResetAnnotation with resetType == 'sync' is very similar, except the
type of the reset wired through will be UInt<1>, and any registers with
an existing reset at all (both sync or async) will be left unchanged
(resetType == 'async' will add async resets to registers with existing
sync resets).
This commit is contained in:
Jack Koenig 2024-08-08 21:39:25 -07:00 committed by GitHub
parent 2f869728d5
commit fa95071921
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 487 additions and 194 deletions

View File

@ -474,8 +474,55 @@ Example:
}
```
### FullResetAnnotation
| Property | Type | Description |
| ---------- | ------ | ------------- |
| class | string | `circt.FullAsyncResetAnnotation` |
| target | string | Reference target |
| resetType | string | "async" or "sync" |
The target must be a signal that is a reset. The type of the signal must be (or inferred
to be) the same as the reset type specified in the annotation.
Indicates that all reset-less registers which are children of the module containing
the target will have the reset targeted attached, with a reset value of 0.
The module containing the target of this annotation is not allowed to reside in multiple
hierarchies.
Example:
```json
{
"class": "circt.FullResetAnnotation",
"target": "~Foo|Bar/d:Baz>reset",
"resetType": "async"
}
```
### ExcludeFromFullResetAnnotation
| Property | Type | Description |
| ---------- | ------ | ------------- |
| class | string | `circt.ExcludeFromFullResetAnnotation` |
| target | string | Reference target |
This annotation indicates that the target moudle should be excluded from the
FullResetAnnotation of a parent module.
Example:
```json
{
"class": "circt.IgnoreFullAsyncResetAnnotation",
"target": "~Foo|Bar/d:Baz"
}
```
### FullAsyncResetAnnotation
**Deprecated, use FullResetAnnotation**
| Property | Type | Description |
| ---------- | ------ | ------------- |
| class | string | `sifive.enterprise.firrtl.FullAsyncResetAnnotation` |
@ -500,6 +547,8 @@ Example:
### IgnoreFullAsyncResetAnnotation
**Deprecated, use ExcludeFromFullResetAnnotation**
| Property | Type | Description |
| ---------- | ------ | ------------- |
| class | string | `sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation` |

View File

@ -159,6 +159,11 @@ constexpr const char *elaborationArtefactsDirectoryAnnoClass =
constexpr const char *testHarnessPathAnnoClass =
"sifive.enterprise.firrtl.TestHarnessPathAnnotation";
/// Annotation that marks a reset (port or wire) and domain.
constexpr const char *fullResetAnnoClass = "circt.FullResetAnnotation";
/// Annotation that marks a module as not belonging to any reset domain.
constexpr const char *excludeFromFullResetAnnoClass =
"circt.ExcludeFromFullResetAnnotation";
/// Annotation that marks a reset (port or wire) and domain.
constexpr const char *fullAsyncResetAnnoClass =
"sifive.enterprise.firrtl.FullAsyncResetAnnotation";
/// Annotation that marks a module as not belonging to any reset domain.

View File

@ -397,11 +397,13 @@ def InferResets : Pass<"firrtl-infer-resets", "firrtl::CircuitOp"> {
let summary = "Infer reset synchronicity and add implicit resets";
let description = [{
This pass infers whether resets are synchronous or asynchronous, and extends
reset-less registers with an asynchronous reset based on the following
reset-less registers with a reset based on the following
annotations:
- `sifive.enterprise.firrtl.FullAsyncResetAnnotation`
- `sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation`
- `circt.FullResetAnnotation`
- `circt.ExcludeFromFullResetAnnotation`
- `sifive.enterprise.firrtl.FullAsyncResetAnnotation` (deprecated)
- `sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation` (deprecated)
}];
let constructor = "circt::firrtl::createInferResetsPass()";
}

View File

@ -1,4 +1,4 @@
//===- InferResets.cpp - Infer resets and add async reset -------*- C++ -*-===//
//===- InferResets.cpp - Infer resets and add full reset --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@ -270,6 +270,15 @@ using ResetNetwork = llvm::iterator_range<
/// Whether a reset is sync or async.
enum class ResetKind { Async, Sync };
static StringRef resetKindToStringRef(const ResetKind &kind) {
switch (kind) {
case ResetKind::Async:
return "async";
case ResetKind::Sync:
return "sync";
}
llvm_unreachable("unhandled reset kind");
}
} // namespace
namespace llvm {
@ -306,12 +315,11 @@ static T &operator<<(T &os, const ResetKind &kind) {
//===----------------------------------------------------------------------===//
namespace {
/// Infer concrete reset types and insert full async reset.
/// Infer concrete reset types and insert full reset.
///
/// This pass replaces `reset` types in the IR with a concrete `asyncreset` or
/// `uint<1>` depending on how the reset is used, and adds async resets to
/// registers in modules marked with the corresponding
/// `FullAsyncResetAnnotation`.
/// `uint<1>` depending on how the reset is used, and adds resets to registers
/// in modules marked with the corresponding `FullResetAnnotation`.
///
/// On a high level, the first stage of the pass that deals with reset inference
/// operates as follows:
@ -332,28 +340,27 @@ namespace {
/// found in step 2. This will replace all `reset` types in the IR with
/// a concrete type.
///
/// The second stage that deals with the addition of async resets operates as
/// The second stage that deals with the addition of full resets operates as
/// follows:
///
/// 4. Visit every module in the design and determine if it has an explicit
/// reset annotated. Ports of and wires in the module can have a
/// `FullAsyncResetAnnotation`, which marks that port or wire as the async
/// reset for the module. A module may also carry a
/// `IgnoreFullAsyncResetAnnotation`, which marks it as being explicitly not
/// in a reset domain. These annotations are sparse; it is very much possible
/// that just the top-level module in the design has a full async reset
/// annotation. A module can only ever carry one of these annotations, which
/// puts it into one of three categories from an async reset inference
/// perspective:
/// `FullResetAnnotation`, which marks that port or wire as the reset for
/// the module. A module may also carry a `ExcludeFromFullResetAnnotation`,
/// which marks it as being explicitly not in a reset domain. These
/// annotations are sparse; it is very much possible that just the top-level
/// module in the design has a full reset annotation. A module can only
/// ever carry one of these annotations, which puts it into one of three
/// categories from a full reset inference perspective:
///
/// a. unambiguously marks a port or wire as the module's async reset
/// b. explicitly marks it as not to have any async resets added
/// a. unambiguously marks a port or wire as the module's full reset
/// b. explicitly marks it as not to have any full resets added
/// c. inherit reset
///
/// 5. For every module in the design, determine the full async reset domain it
/// 5. For every module in the design, determine the full full reset domain it
/// is in. Note that this very narrowly deals with the inference of a
/// "default" async reset, which basically goes through the IR and attaches
/// all non-reset registers to a default async reset signal. If a module
/// "default" full reset, which basically goes through the IR and attaches
/// all non-reset registers to a default full reset signal. If a module
/// carries one of the annotations mentioned in (4), the annotated port or
/// wire is used as its reset domain. Otherwise, it inherits the reset domain
/// from parent modules. This conceptually involves looking at all the places
@ -365,7 +372,7 @@ namespace {
/// its local ports or wires, or a port or wire within one of its parent
/// modules.
///
/// 6. For every module in the design, determine how async resets shall be
/// 6. For every module in the design, determine how full resets shall be
/// implemented. This step handles the following distinct cases:
///
/// a. Skip a module because it is marked as having no reset domain.
@ -382,30 +389,30 @@ namespace {
/// value to reuse (port or wire), the index of an existing port to reuse,
/// and the name of an additional port to insert into its port list.
///
/// 7. For every module in the design, async resets are implemented. This
/// 7. For every module in the design, full resets are implemented. This
/// determines the local value to use as the reset signal and updates the
/// `reg` and `regreset` operations in the design. If the register already
/// has an async reset, it is left unchanged. If it has a sync reset, the
/// sync reset is moved into a `mux` operation on all `connect`s to the
/// register (which the Scala code base called the `RemoveResets` pass).
/// Finally the register is replaced with a `regreset` operation, with the
/// reset signal determined earlier, and a "zero" value constructed for the
/// register's type.
/// has an async reset, or if the type of the full reset is sync, the
/// register's reset is left unchanged. If it has a sync reset and the full
/// reset is async, the sync reset is moved into a `mux` operation on all
/// `connect`s to the register (which the Scala code base called the
/// `RemoveResets` pass). Finally the register is replaced with a `regreset`
/// operation, with the reset signal determined earlier, and a "zero" value
/// constructed for the register's type.
///
/// Determining the local reset value is trivial if step 6 found a module to
/// be of case a or b. Case c is the non-trivial one, because it requires
/// modifying the port list of the module. This is done by first determining
/// the name of the reset signal in the parent module, which is either the
/// name of the port or wire declaration. We then look for an existing
/// `asyncreset` port in the port list and reuse that as reset. If no port
/// with that name was found, or the existing port is of the wrong type, a
/// new port is inserted into the port list.
/// port of the same type in the port list and reuse that as reset. If no
/// port with that name was found, or the existing port is of the wrong type,
/// a new port is inserted into the port list.
///
/// TODO: This logic is *very* brittle and error-prone. It may make sense to
/// just add an additional port for the inferred async reset in any case,
/// with an optimization to use an existing `asyncreset` port if all of the
/// module's instantiations have that port connected to the desired signal
/// already.
/// just add an additional port for the inferred reset in any case, with an
/// optimization to use an existing port if all of the module's
/// instantiations have that port connected to the desired signal already.
///
struct InferResetsPass
: public circt::firrtl::impl::InferResetsBase<InferResetsPass> {
@ -432,12 +439,12 @@ struct InferResetsPass
bool updateReset(FieldRef field, FIRRTLBaseType resetType);
//===--------------------------------------------------------------------===//
// Async reset implementation
// Full reset implementation
LogicalResult collectAnnos(CircuitOp circuit);
// Collect async reset annotations in the module and return a reset signal.
// Collect reset annotations in the module and return a reset signal.
// Return `failure()` if there was an error in the annotation processing.
// Return `std::nullopt` if there was no async reset annotation.
// Return `std::nullopt` if there was no reset annotation.
// Return `nullptr` if there was `ignore` annotation.
// Return a non-null Value if the reset was actually provided.
FailureOr<std::optional<Value>> collectAnnos(FModuleOp module);
@ -450,9 +457,9 @@ struct InferResetsPass
void determineImpl();
void determineImpl(FModuleOp module, ResetDomain &domain);
LogicalResult implementAsyncReset();
LogicalResult implementAsyncReset(FModuleOp module, ResetDomain &domain);
void implementAsyncReset(Operation *op, FModuleOp module, Value actualReset);
LogicalResult implementFullReset();
LogicalResult implementFullReset(FModuleOp module, ResetDomain &domain);
void implementFullReset(Operation *op, FModuleOp module, Value actualReset);
LogicalResult verifyNoAbstractReset();
@ -536,8 +543,8 @@ void InferResetsPass::runOnOperationInner() {
// Determine how each reset shall be implemented.
determineImpl();
// Implement the async resets.
if (failed(implementAsyncReset()))
// Implement the full resets.
if (failed(implementFullReset()))
return signalPassFailure();
// Require that no Abstract Resets exist on ports in the design.
@ -1262,7 +1269,7 @@ bool InferResetsPass::updateReset(FieldRef field, FIRRTLBaseType resetType) {
LogicalResult InferResetsPass::collectAnnos(CircuitOp circuit) {
LLVM_DEBUG({
llvm::dbgs() << "\n";
debugHeader("Gather async reset annotations") << "\n\n";
debugHeader("Gather reset annotations") << "\n\n";
});
SmallVector<std::pair<FModuleOp, std::optional<Value>>> results;
for (auto module : circuit.getOps<FModuleOp>())
@ -1293,15 +1300,15 @@ InferResetsPass::collectAnnos(FModuleOp module) {
// explicitly assigns it no reset domain.
bool ignore = false;
AnnotationSet::removeAnnotations(module, [&](Annotation anno) {
if (anno.isClass(ignoreFullAsyncResetAnnoClass)) {
if (anno.isClass(excludeFromFullResetAnnoClass)) {
ignore = true;
conflictingAnnos.insert({anno, module.getLoc()});
return true;
}
if (anno.isClass(fullAsyncResetAnnoClass)) {
if (anno.isClass(fullResetAnnoClass)) {
anyFailed = true;
module.emitError("'FullAsyncResetAnnotation' cannot target module; "
"must target port or wire/node instead");
module.emitError("''FullResetAnnotation' cannot target module; must "
"target port or wire/node instead");
return true;
}
return false;
@ -1311,30 +1318,65 @@ InferResetsPass::collectAnnos(FModuleOp module) {
// Consume any reset annotations on module ports.
Value reset;
AnnotationSet::removePortAnnotations(module, [&](unsigned argNum,
Annotation anno) {
Value arg = module.getArgument(argNum);
if (anno.isClass(fullAsyncResetAnnoClass)) {
if (!isa<AsyncResetType>(arg.getType())) {
mlir::emitError(arg.getLoc(), "'FullAsyncResetAnnotation' must "
"target async reset, but targets ")
// Helper for checking annotations and determining the reset
auto checkAnnotations = [&](Annotation anno, Value arg) {
if (anno.isClass(fullResetAnnoClass)) {
ResetKind expectedResetKind;
if (auto rt = anno.getMember<StringAttr>("resetType")) {
if (rt == "sync") {
expectedResetKind = ResetKind::Sync;
} else if (rt == "async") {
expectedResetKind = ResetKind::Async;
} else {
mlir::emitError(arg.getLoc(),
"'FullResetAnnotation' requires resetType == 'sync' "
"| 'async', but got resetType == ")
<< rt;
anyFailed = true;
return true;
}
} else {
mlir::emitError(arg.getLoc(),
"'FullResetAnnotation' requires resetType == "
"'sync' | 'async', but got no resetType");
anyFailed = true;
return true;
}
// Check that the type is well-formed
bool isAsync = expectedResetKind == ResetKind::Async;
bool validUint = false;
if (auto uintT = dyn_cast<UIntType>(arg.getType()))
validUint = uintT.getWidth() == 1;
if ((isAsync && !isa<AsyncResetType>(arg.getType())) ||
(!isAsync && !validUint)) {
auto kind = resetKindToStringRef(expectedResetKind);
mlir::emitError(arg.getLoc(),
"'FullResetAnnotation' with resetType == '")
<< kind << "' must target " << kind << " reset, but targets "
<< arg.getType();
anyFailed = true;
return true;
}
reset = arg;
conflictingAnnos.insert({anno, reset.getLoc()});
return false;
}
if (anno.isClass(ignoreFullAsyncResetAnnoClass)) {
if (anno.isClass(excludeFromFullResetAnnoClass)) {
anyFailed = true;
mlir::emitError(arg.getLoc(),
"'IgnoreFullAsyncResetAnnotation' cannot target port; "
"must target module instead");
"'ExcludeFromFullResetAnnotation' cannot "
"target port/wire/node; must target module instead");
return true;
}
return false;
};
AnnotationSet::removePortAnnotations(module,
[&](unsigned argNum, Annotation anno) {
Value arg = module.getArgument(argNum);
return checkAnnotations(anno, arg);
});
if (anyFailed)
return failure();
@ -1343,8 +1385,8 @@ InferResetsPass::collectAnnos(FModuleOp module) {
module.getBody().walk([&](Operation *op) {
// Reset annotations must target wire/node ops.
if (!isa<WireOp, NodeOp>(op)) {
if (AnnotationSet::hasAnnotation(op, fullAsyncResetAnnoClass,
ignoreFullAsyncResetAnnoClass)) {
if (AnnotationSet::hasAnnotation(op, fullResetAnnoClass,
excludeFromFullResetAnnoClass)) {
anyFailed = true;
op->emitError(
"reset annotations must target module, port, or wire/node");
@ -1355,27 +1397,8 @@ InferResetsPass::collectAnnos(FModuleOp module) {
// At this point we know that we have a WireOp/NodeOp. Process the reset
// annotations.
AnnotationSet::removeAnnotations(op, [&](Annotation anno) {
if (anno.isClass(fullAsyncResetAnnoClass)) {
auto resultType = op->getResult(0).getType();
if (!isa<AsyncResetType>(resultType)) {
mlir::emitError(op->getLoc(), "'FullAsyncResetAnnotation' must "
"target async reset, but targets ")
<< resultType;
anyFailed = true;
return true;
}
reset = op->getResult(0);
conflictingAnnos.insert({anno, reset.getLoc()});
return false;
}
if (anno.isClass(ignoreFullAsyncResetAnnoClass)) {
anyFailed = true;
op->emitError(
"'IgnoreFullAsyncResetAnnotation' cannot target wire/node; must "
"target module instead");
return true;
}
return false;
auto arg = op->getResult(0);
return checkAnnotations(anno, arg);
});
});
if (anyFailed)
@ -1430,7 +1453,7 @@ InferResetsPass::collectAnnos(FModuleOp module) {
LogicalResult InferResetsPass::buildDomains(CircuitOp circuit) {
LLVM_DEBUG({
llvm::dbgs() << "\n";
debugHeader("Build async reset domains") << "\n\n";
debugHeader("Build full reset domains") << "\n\n";
});
// Gather the domains.
@ -1559,7 +1582,7 @@ void InferResetsPass::determineImpl() {
/// - If the domain is the place where the reset is defined ("top"), fills in
/// the existing port/wire/node as reset.
/// - If the module already has a port with the reset's name:
/// - If the type is `asyncreset`, reuses that port.
/// - If the port has the same type as the reset domain, reuses that port.
/// - Otherwise appends a `_N` suffix with increasing N to create a
/// yet-unused
/// port name, and marks that as to be created.
@ -1629,17 +1652,17 @@ void InferResetsPass::determineImpl(FModuleOp module, ResetDomain &domain) {
}
//===----------------------------------------------------------------------===//
// Async Reset Implementation
// Full Reset Implementation
//===----------------------------------------------------------------------===//
/// Implement the async resets gathered in the pass' `domains` map.
LogicalResult InferResetsPass::implementAsyncReset() {
/// Implement the annotated resets gathered in the pass' `domains` map.
LogicalResult InferResetsPass::implementFullReset() {
LLVM_DEBUG({
llvm::dbgs() << "\n";
debugHeader("Implement async resets") << "\n\n";
debugHeader("Implement full resets") << "\n\n";
});
for (auto &it : domains)
if (failed(implementAsyncReset(cast<FModuleOp>(it.first),
if (failed(implementFullReset(cast<FModuleOp>(it.first),
it.second.back().first)))
return failure();
return success();
@ -1650,9 +1673,9 @@ LogicalResult InferResetsPass::implementAsyncReset() {
/// This will add ports to the module as appropriate, update the register ops
/// in the module, and update any instantiated submodules with their
/// corresponding reset implementation details.
LogicalResult InferResetsPass::implementAsyncReset(FModuleOp module,
LogicalResult InferResetsPass::implementFullReset(FModuleOp module,
ResetDomain &domain) {
LLVM_DEBUG(llvm::dbgs() << "Implementing async reset for " << module.getName()
LLVM_DEBUG(llvm::dbgs() << "Implementing full reset for " << module.getName()
<< "\n");
// Nothing to do if the module was marked explicitly with no reset domain.
@ -1666,7 +1689,7 @@ LogicalResult InferResetsPass::implementAsyncReset(FModuleOp module,
Value actualReset = domain.existingValue;
if (domain.newPortName) {
PortInfo portInfo{domain.newPortName,
AsyncResetType::get(&getContext()),
domain.reset.getType(),
Direction::In,
{},
domain.reset.getLoc()};
@ -1771,14 +1794,14 @@ LogicalResult InferResetsPass::implementAsyncReset(FModuleOp module,
// Update the operations.
for (auto *op : opsToUpdate)
implementAsyncReset(op, module, actualReset);
implementFullReset(op, module, actualReset);
return success();
}
/// Modify an operation in a module to implement an async reset for that
/// Modify an operation in a module to implement an full reset for that
/// module.
void InferResetsPass::implementAsyncReset(Operation *op, FModuleOp module,
void InferResetsPass::implementFullReset(Operation *op, FModuleOp module,
Value actualReset) {
ImplicitLocOpBuilder builder(op->getLoc(), op);
@ -1840,7 +1863,7 @@ void InferResetsPass::implementAsyncReset(Operation *op, FModuleOp module,
if (AnnotationSet::removeAnnotations(regOp, excludeMemToRegAnnoClass))
return;
LLVM_DEBUG(llvm::dbgs() << "- Adding async reset to " << regOp << "\n");
LLVM_DEBUG(llvm::dbgs() << "- Adding full reset to " << regOp << "\n");
auto zero = createZeroValue(builder, regOp.getResult().getType());
auto newRegOp = builder.create<RegResetOp>(
regOp.getResult().getType(), regOp.getClockVal(), actualReset, zero,
@ -1855,10 +1878,11 @@ void InferResetsPass::implementAsyncReset(Operation *op, FModuleOp module,
// Handle registers with reset.
if (auto regOp = dyn_cast<RegResetOp>(op)) {
// If the register already has an async reset, leave it untouched.
if (type_isa<AsyncResetType>(regOp.getResetSignal().getType())) {
LLVM_DEBUG(llvm::dbgs()
<< "- Skipping (has async reset) " << regOp << "\n");
// If the register already has an async reset or if the type of the added
// reset is sync, leave it alone.
if (type_isa<AsyncResetType>(regOp.getResetSignal().getType()) ||
type_isa<UIntType>(actualReset.getType())) {
LLVM_DEBUG(llvm::dbgs() << "- Skipping (has reset) " << regOp << "\n");
// The following performs the logic of `CheckResets` in the original
// Scala source code.
if (failed(regOp.verifyInvariants()))
@ -1870,9 +1894,9 @@ void InferResetsPass::implementAsyncReset(Operation *op, FModuleOp module,
auto reset = regOp.getResetSignal();
auto value = regOp.getResetValue();
// If we arrive here, the register has a sync reset. In order to add an
// async reset, we have to move the sync reset into a mux in front of the
// register.
// If we arrive here, the register has a sync reset and the added reset is
// async. In order to add an async reset, we have to move the sync reset
// into a mux in front of the register.
insertResetMux(builder, regOp.getResult(), reset, value);
builder.setInsertionPointAfterValue(regOp.getResult());
auto mux = builder.create<MuxPrimOp>(reset, value, regOp.getResult());

View File

@ -435,6 +435,45 @@ static LogicalResult applyOutputDirAnno(const AnnoPathValue &target,
return success();
}
/// Convert from FullAsyncResetAnnotation to FullResetAnnotation
static LogicalResult convertToFullResetAnnotation(const AnnoPathValue &target,
DictionaryAttr anno,
ApplyState &state) {
auto *op = target.ref.getOp();
auto *context = op->getContext();
mlir::emitWarning(op->getLoc())
<< "'" << fullAsyncResetAnnoClass << "' is deprecated, use '"
<< fullResetAnnoClass << "' instead";
NamedAttrList newAnno(anno.getValue());
newAnno.set("class", StringAttr::get(context, fullResetAnnoClass));
newAnno.append("resetType", StringAttr::get(context, "async"));
DictionaryAttr newDictionary = DictionaryAttr::get(op->getContext(), newAnno);
return applyWithoutTarget<false>(target, newDictionary, state);
}
/// Convert from IgnoreFullAsyncResetAnnotation to
/// ExcludeFromFullResetAnnotation
static LogicalResult convertToExcludeFromFullResetAnnotation(
const AnnoPathValue &target, DictionaryAttr anno, ApplyState &state) {
auto *op = target.ref.getOp();
auto *context = op->getContext();
mlir::emitWarning(op->getLoc())
<< "'" << ignoreFullAsyncResetAnnoClass << "' is deprecated, use '"
<< excludeFromFullResetAnnoClass << "' instead";
NamedAttrList newAnno(anno.getValue());
newAnno.set("class", StringAttr::get(context, excludeFromFullResetAnnoClass));
DictionaryAttr newDictionary = DictionaryAttr::get(op->getContext(), newAnno);
return applyWithoutTarget<true, FModuleOp>(target, newDictionary, state);
}
//===----------------------------------------------------------------------===//
// Driving table
//===----------------------------------------------------------------------===//
@ -543,9 +582,12 @@ static llvm::StringMap<AnnoRecord> annotationRecords{{
{addSeqMemPortsFileAnnoClass, NoTargetAnnotation},
{extractClockGatesAnnoClass, NoTargetAnnotation},
{extractBlackBoxAnnoClass, {stdResolve, applyWithoutTarget<false>}},
{fullAsyncResetAnnoClass, {stdResolve, applyWithoutTarget<false>}},
{ignoreFullAsyncResetAnnoClass,
{fullResetAnnoClass, {stdResolve, applyWithoutTarget<false>}},
{excludeFromFullResetAnnoClass,
{stdResolve, applyWithoutTarget<true, FModuleOp>}},
{fullAsyncResetAnnoClass, {stdResolve, convertToFullResetAnnotation}},
{ignoreFullAsyncResetAnnoClass,
{stdResolve, convertToExcludeFromFullResetAnnotation}},
{decodeTableAnnotation, {noResolve, drop}},
{blackBoxTargetDirAnnoClass, NoTargetAnnotation},
{traceNameAnnoClass, {stdResolve, applyTraceName}},

View File

@ -54,21 +54,18 @@ void SFCCompatPass::runOnOperation() {
bool madeModifications = false;
SmallVector<InvalidValueOp> invalidOps;
auto fullAsyncResetAttr =
StringAttr::get(&getContext(), fullAsyncResetAnnoClass);
auto isFullAsyncResetAnno = [fullAsyncResetAttr](Annotation anno) {
return anno.getClassAttr() == fullAsyncResetAttr;
auto fullResetAttr = StringAttr::get(&getContext(), fullResetAnnoClass);
auto isFullResetAnno = [fullResetAttr](Annotation anno) {
auto annoClassAttr = anno.getClassAttr();
return annoClassAttr == fullResetAttr;
};
bool fullAsyncResetExists = AnnotationSet::removePortAnnotations(
getOperation(), [&](unsigned argNum, Annotation anno) {
return isFullAsyncResetAnno(anno);
bool fullResetExists = AnnotationSet::removePortAnnotations(
getOperation(),
[&](unsigned argNum, Annotation anno) { return isFullResetAnno(anno); });
getOperation()->walk([isFullResetAnno, &fullResetExists](Operation *op) {
fullResetExists |= AnnotationSet::removeAnnotations(op, isFullResetAnno);
});
getOperation()->walk(
[isFullAsyncResetAnno, &fullAsyncResetExists](Operation *op) {
fullAsyncResetExists |=
AnnotationSet::removeAnnotations(op, isFullAsyncResetAnno);
});
madeModifications |= fullAsyncResetExists;
madeModifications |= fullResetExists;
auto result = getOperation()->walk([&](Operation *op) {
// Populate invalidOps for later handling.
@ -82,8 +79,7 @@ void SFCCompatPass::runOnOperation() {
// If the `RegResetOp` has an invalidated initialization and we
// are not running FART, then replace it with a `RegOp`.
if (!fullAsyncResetExists &&
walkDrivers(reg.getResetValue(), true, true, false,
if (!fullResetExists && walkDrivers(reg.getResetValue(), true, true, false,
[](FieldRef dst, FieldRef src) {
return src.isa<InvalidValueOp>();
})) {

View File

@ -1,4 +1,4 @@
// RUN: circt-opt --pass-pipeline='builtin.module(firrtl.circuit(firrtl-lower-annotations{allow-adding-ports-on-public-modules=true}))' --split-input-file %s | FileCheck %s
// RUN: circt-opt --pass-pipeline='builtin.module(firrtl.circuit(firrtl-lower-annotations{allow-adding-ports-on-public-modules=true}))' --verify-diagnostics --split-input-file %s | FileCheck %s
// circt.test copies the annotation to the target
// circt.testNT puts the targetless annotation on the circuit
@ -1953,3 +1953,35 @@ firrtl.circuit "Top" attributes {
// CHECK-SAME: attributes {output_file = #hw.output_file<"foobarbaz{{/|\\\\}}qux{{/|\\\\}}">}
firrtl.module @Top() {}
}
// -----
// Check that FullAsyncResetAnnotation is lowered to FullResetAnnotation (and prints a warning)
// CHECK-LABEL: firrtl.circuit "Top"
firrtl.circuit "Top" attributes {
rawAnnotations = [
{
class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation",
target = "~Top|Top>reset"
}]
} {
// CHECK-LABEL: firrtl.module @Top
// CHECK-SAME: (in %reset: !firrtl.asyncreset [{class = "circt.FullResetAnnotation", resetType = "async"}])
// expected-warning @+1 {{'sifive.enterprise.firrtl.FullAsyncResetAnnotation' is deprecated, use 'circt.FullResetAnnotation' instead}}
firrtl.module @Top(in %reset: !firrtl.asyncreset) {}
}
// -----
// Check that IgnoreFullAsyncResetAnnotation is lowered to ExcludeFromFullResetAnnotation (and prints a warning)
// CHECK-LABEL: firrtl.circuit "Top"
firrtl.circuit "Top" attributes {
rawAnnotations = [
{
class = "sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation",
target = "~Top|Top"
}]
} {
// CHECK-LABEL: firrtl.module @Top
// CHECK-SAME: (in %reset: !firrtl.asyncreset) attributes {annotations = [{class = "circt.ExcludeFromFullResetAnnotation"}]}
// expected-warning @+1 {{'sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation' is deprecated, use 'circt.ExcludeFromFullResetAnnotation' instead}}
firrtl.module @Top(in %reset: !firrtl.asyncreset) {}
}

View File

@ -6,7 +6,6 @@
// - github.com/sifive/$internal:
// - test/scala/firrtl/FullAsyncResetTransform.scala
//===----------------------------------------------------------------------===//
// Reset Inference
//===----------------------------------------------------------------------===//
@ -152,34 +151,43 @@ firrtl.circuit "top" {
}
//===----------------------------------------------------------------------===//
// Full Async Reset
// Full Reset
//===----------------------------------------------------------------------===//
// -----
// Reset annotation cannot target module
firrtl.circuit "top" {
// expected-error @+1 {{FullAsyncResetAnnotation' cannot target module; must target port or wire/node instead}}
firrtl.module @top() attributes {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} {
// expected-error @+1 {{'FullResetAnnotation' cannot target module; must target port or wire/node instead}}
firrtl.module @top() attributes {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} {
}
}
// -----
// Reset annotation cannot target synchronous reset signals
// Reset annotation resetType must match type of signal
firrtl.circuit "top" {
firrtl.module @top() {
// expected-error @below {{'FullAsyncResetAnnotation' must target async reset, but targets '!firrtl.uint<1>'}}
%innerReset = firrtl.wire {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.uint<1>
// expected-error @below {{'FullResetAnnotation' with resetType == 'async' must target async reset, but targets '!firrtl.uint<1>'}}
%innerReset = firrtl.wire {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.uint<1>
// expected-error @below {{'FullResetAnnotation' with resetType == 'sync' must target sync reset, but targets '!firrtl.asyncreset'}}
%innerReset2 = firrtl.wire {annotations = [{class = "circt.FullResetAnnotation", resetType = "sync"}]} : !firrtl.asyncreset
// expected-error @below {{'FullResetAnnotation' with resetType == 'sync' must target sync reset, but targets '!firrtl.uint<2>'}}
%innerReset3 = firrtl.wire {annotations = [{class = "circt.FullResetAnnotation", resetType = "sync"}]} : !firrtl.uint<2>
}
}
// -----
// Reset annotation cannot target reset signals which are inferred to be synchronous
// Reset annotation cannot target reset signals which are inferred to the wrong type
firrtl.circuit "top" {
firrtl.module @top() {
// expected-error @below {{'FullAsyncResetAnnotation' must target async reset, but targets '!firrtl.uint<1>'}}
%innerReset = firrtl.wire {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.reset
// expected-error @below {{'FullResetAnnotation' with resetType == 'async' must target async reset, but targets '!firrtl.uint<1>'}}
%innerReset = firrtl.wire {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.reset
%invalid = firrtl.invalidvalue : !firrtl.reset
firrtl.matchingconnect %innerReset, %invalid : !firrtl.reset
// expected-error @below {{'FullResetAnnotation' with resetType == 'sync' must target sync reset, but targets '!firrtl.asyncreset'}}
%innerReset2 = firrtl.wire {annotations = [{class = "circt.FullResetAnnotation", resetType = "sync"}]} : !firrtl.reset
%asyncWire = firrtl.wire : !firrtl.asyncreset
firrtl.connect %innerReset2, %asyncWire : !firrtl.reset, !firrtl.asyncreset
}
}
@ -187,8 +195,8 @@ firrtl.circuit "top" {
// -----
// Ignore reset annotation cannot target port
firrtl.circuit "top" {
// expected-error @+1 {{IgnoreFullAsyncResetAnnotation' cannot target port; must target module instead}}
firrtl.module @top(in %reset: !firrtl.asyncreset) attributes {portAnnotations =[[{class = "sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation"}]]} {
// expected-error @+1 {{ExcludeFromFullResetAnnotation' cannot target port/wire/node; must target module instead}}
firrtl.module @top(in %reset: !firrtl.asyncreset) attributes {portAnnotations =[[{class = "circt.ExcludeFromFullResetAnnotation"}]]} {
}
}
@ -196,14 +204,14 @@ firrtl.circuit "top" {
// Ignore reset annotation cannot target wire/node
firrtl.circuit "top" {
firrtl.module @top() {
// expected-error @+1 {{IgnoreFullAsyncResetAnnotation' cannot target wire/node; must target module instead}}
%0 = firrtl.wire {annotations = [{class = "sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation"}]} : !firrtl.asyncreset
// expected-error @+1 {{IgnoreFullAsyncResetAnnotation' cannot target wire/node; must target module instead}}
%1 = firrtl.node %0 {annotations = [{class = "sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation"}]} : !firrtl.asyncreset
// expected-error @+1 {{ExcludeFromFullResetAnnotation' cannot target port/wire/node; must target module instead}}
%0 = firrtl.wire {annotations = [{class = "circt.ExcludeFromFullResetAnnotation"}]} : !firrtl.asyncreset
// expected-error @+1 {{ExcludeFromFullResetAnnotation' cannot target port/wire/node; must target module instead}}
%1 = firrtl.node %0 {annotations = [{class = "circt.ExcludeFromFullResetAnnotation"}]} : !firrtl.asyncreset
// expected-error @+1 {{reset annotations must target module, port, or wire/node}}
%2 = firrtl.asUInt %0 {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : (!firrtl.asyncreset) -> !firrtl.uint<1>
%2 = firrtl.asUInt %0 {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : (!firrtl.asyncreset) -> !firrtl.uint<1>
// expected-error @+1 {{reset annotations must target module, port, or wire/node}}
%3 = firrtl.asUInt %0 {annotations = [{class = "sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation"}]} : (!firrtl.asyncreset) -> !firrtl.uint<1>
%3 = firrtl.asUInt %0 {annotations = [{class = "circt.ExcludeFromFullResetAnnotation"}]} : (!firrtl.asyncreset) -> !firrtl.uint<1>
}
}
@ -211,12 +219,12 @@ firrtl.circuit "top" {
// Cannot have multiple reset annotations on a module
firrtl.circuit "top" {
// expected-error @+2 {{multiple reset annotations on module 'top'}}
// expected-note @+1 {{conflicting "sifive.enterprise.firrtl.FullAsyncResetAnnotation":}}
firrtl.module @top(in %outerReset: !firrtl.asyncreset) attributes {portAnnotations = [[{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]]} {
// expected-note @+1 {{conflicting "sifive.enterprise.firrtl.FullAsyncResetAnnotation":}}
%innerReset = firrtl.wire {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
// expected-note @+1 {{conflicting "sifive.enterprise.firrtl.FullAsyncResetAnnotation":}}
%anotherReset = firrtl.node %innerReset {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
// expected-note @+1 {{conflicting "circt.FullResetAnnotation":}}
firrtl.module @top(in %outerReset: !firrtl.asyncreset) attributes {portAnnotations = [[{class = "circt.FullResetAnnotation", resetType = "async"}]]} {
// expected-note @+1 {{conflicting "circt.FullResetAnnotation":}}
%innerReset = firrtl.wire {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
// expected-note @+1 {{conflicting "circt.FullResetAnnotation":}}
%anotherReset = firrtl.node %innerReset {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
}
}
@ -228,18 +236,18 @@ firrtl.circuit "Top" {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8>
}
// expected-note @+1 {{reset domain 'otherReset' of module 'Child' declared here:}}
firrtl.module @Child(in %clock: !firrtl.clock, in %otherReset: !firrtl.asyncreset) attributes {portAnnotations = [[],[{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]]} {
firrtl.module @Child(in %clock: !firrtl.clock, in %otherReset: !firrtl.asyncreset) attributes {portAnnotations = [[],[{class = "circt.FullResetAnnotation", resetType = "async"}]]} {
// expected-note @+1 {{instance 'child/inst' is in reset domain rooted at 'otherReset' of module 'Child'}}
%inst_clock = firrtl.instance inst @Foo(in clock: !firrtl.clock)
firrtl.connect %inst_clock, %clock : !firrtl.clock, !firrtl.clock
}
firrtl.module @Other(in %clock: !firrtl.clock) attributes {annotations = [{class = "sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation"}]} {
firrtl.module @Other(in %clock: !firrtl.clock) attributes {annotations = [{class = "circt.ExcludeFromFullResetAnnotation"}]} {
// expected-note @+1 {{instance 'other/inst' is in no reset domain}}
%inst_clock = firrtl.instance inst @Foo(in clock: !firrtl.clock)
firrtl.connect %inst_clock, %clock : !firrtl.clock, !firrtl.clock
}
// expected-note @+1 {{reset domain 'reset' of module 'Top' declared here:}}
firrtl.module @Top(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset) attributes {portAnnotations = [[],[{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]]} {
firrtl.module @Top(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset) attributes {portAnnotations = [[],[{class = "circt.FullResetAnnotation", resetType = "async"}]]} {
%child_clock, %child_otherReset = firrtl.instance child @Child(in clock: !firrtl.clock, in otherReset: !firrtl.asyncreset)
%other_clock = firrtl.instance other @Other(in clock: !firrtl.clock)
// expected-note @+1 {{instance 'foo' is in reset domain rooted at 'reset' of module 'Top'}}
@ -266,3 +274,10 @@ firrtl.circuit "UninferredRefReset" {
// expected-note @+1 {{the module with this uninferred reset port was defined here}}
firrtl.module private @UninferredRefResetPriv(out %reset: !firrtl.probe<reset>) {}
}
// -----
// Invalid FullResetAnnotation resetType
firrtl.circuit "Top" {
// expected-error @+1 {{'FullResetAnnotation' requires resetType == 'sync' | 'async', but got resetType == "potato"}}
firrtl.module @Top(in %reset: !firrtl.asyncreset) attributes {portAnnotations = [[{class = "circt.FullResetAnnotation", resetType = "potato"}]]} {}
}

View File

@ -358,18 +358,18 @@ firrtl.module @ForeignTypes(out %out: !firrtl.reset) {
// CHECK-LABEL: firrtl.module @ConsumeIgnoreAnno
// CHECK-NOT: IgnoreFullAsyncResetAnnotation
firrtl.module @ConsumeIgnoreAnno() attributes {annotations = [{class = "sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation"}]} {
// CHECK-NOT: ExcludeFromFullResetAnnotation
firrtl.module @ConsumeIgnoreAnno() attributes {annotations = [{class = "circt.ExcludeFromFullResetAnnotation"}]} {
}
} // firrtl.circuit
// -----
// Reset-less registers should inherit the annotated async reset signal.
// AsyncReset-less registers should inherit the annotated async reset signal.
firrtl.circuit "Top" {
// CHECK-LABEL: firrtl.module @Top
firrtl.module @Top(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset, in %init: !firrtl.uint<1>, in %in: !firrtl.uint<8>, in %extraReset: !firrtl.asyncreset ) attributes {
portAnnotations = [[],[],[],[],[{class = "firrtl.transforms.DontTouchAnnotation"}, {class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]]} {
portAnnotations = [[],[],[],[],[{class = "firrtl.transforms.DontTouchAnnotation"}, {class = "circt.FullResetAnnotation", resetType = "async"}]]} {
%c1_ui8 = firrtl.constant 1 : !firrtl.uint<8>
// CHECK: %reg1 = firrtl.regreset sym @reg1 %clock, %extraReset, %c0_ui8
%reg1 = firrtl.reg sym @reg1 %clock : !firrtl.clock, !firrtl.uint<8>
@ -434,13 +434,36 @@ firrtl.circuit "Top" {
}
}
// -----
// Reset-less registers should inherit the annotated sync reset signal.
firrtl.circuit "Top" {
// CHECK-LABEL: firrtl.module @Top
firrtl.module @Top(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset, in %init: !firrtl.uint<1>, in %in: !firrtl.uint<8>, in %extraReset: !firrtl.uint<1> ) attributes {
portAnnotations = [[],[],[],[],[{class = "firrtl.transforms.DontTouchAnnotation"}, {class = "circt.FullResetAnnotation", resetType = "sync"}]]} {
%c1_ui8 = firrtl.constant 1 : !firrtl.uint<8>
// CHECK: %reg1 = firrtl.regreset sym @reg1 %clock, %extraReset, %c0_ui8
%reg1 = firrtl.reg sym @reg1 %clock : !firrtl.clock, !firrtl.uint<8>
firrtl.matchingconnect %reg1, %in : !firrtl.uint<8>
// Existing async reset remains untouched.
// CHECK: %reg2 = firrtl.regreset %clock, %reset, %c1_ui8
%reg2 = firrtl.regreset %clock, %reset, %c1_ui8 : !firrtl.clock, !firrtl.asyncreset, !firrtl.uint<8>, !firrtl.uint<8>
firrtl.matchingconnect %reg2, %in : !firrtl.uint<8>
// Existing sync reset remains untouched.
// CHECK: %reg3 = firrtl.regreset %clock, %init, %c1_ui8
%reg3 = firrtl.regreset %clock, %init, %c1_ui8 : !firrtl.clock, !firrtl.uint<1>, !firrtl.uint<8>, !firrtl.uint<8>
firrtl.matchingconnect %reg3, %in : !firrtl.uint<8>
}
}
// -----
// Async reset inference should be able to construct reset values for aggregate
// types.
firrtl.circuit "Top" {
// CHECK-LABEL: firrtl.module @Top
firrtl.module @Top(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset) attributes {
portAnnotations = [[],[{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]]} {
portAnnotations = [[],[{class = "circt.FullResetAnnotation", resetType = "async"}]]} {
// CHECK: %c0_ui = firrtl.constant 0 : !firrtl.const.uint
// CHECK: %reg_uint = firrtl.regreset %clock, %reset, %c0_ui
%reg_uint = firrtl.reg %clock : !firrtl.clock, !firrtl.uint
@ -476,8 +499,8 @@ firrtl.circuit "Top" {
}
// -----
// Reset should reuse ports if name and type matches.
firrtl.circuit "ReusePorts" {
// Reset should reuse ports if name and type matches for async wiring.
firrtl.circuit "ReusePortsAsync" {
// CHECK-LABEL: firrtl.module @Child
// CHECK-SAME: in %clock: !firrtl.clock
// CHECK-SAME: in %reset: !firrtl.asyncreset
@ -502,8 +525,8 @@ firrtl.circuit "ReusePorts" {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8>
}
// CHECK-LABEL: firrtl.module @ReusePorts
firrtl.module @ReusePorts(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset) attributes {
portAnnotations = [[],[{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]]} {
firrtl.module @ReusePortsAsync(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset) attributes {
portAnnotations = [[],[{class = "circt.FullResetAnnotation", resetType = "async"}]]} {
// CHECK: %child_clock, %child_reset = firrtl.instance child
// CHECK: firrtl.matchingconnect %child_reset, %reset
// CHECK: %badName_reset, %badName_clock, %badName_existingReset = firrtl.instance badName
@ -516,6 +539,47 @@ firrtl.circuit "ReusePorts" {
}
}
// -----
// Reset should reuse ports if name and type matches for sync wiring.
firrtl.circuit "ReusePortsSync" {
// CHECK-LABEL: firrtl.module @Child
// CHECK-SAME: in %clock: !firrtl.clock
// CHECK-SAME: in %reset: !firrtl.uint<1>
// CHECK: %reg = firrtl.regreset %clock, %reset, %c0_ui8
firrtl.module @Child(in %clock: !firrtl.clock, in %reset: !firrtl.uint<1>) {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8>
}
// CHECK-LABEL: firrtl.module @BadName
// CHECK-SAME: in %reset: !firrtl.uint<1>,
// CHECK-SAME: in %clock: !firrtl.clock
// CHECK-SAME: in %existingReset: !firrtl.uint<1>
// CHECK: %reg = firrtl.regreset %clock, %reset, %c0_ui8
firrtl.module @BadName(in %clock: !firrtl.clock, in %existingReset: !firrtl.uint<1>) {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8>
}
// CHECK-LABEL: firrtl.module @BadType
// CHECK-SAME: in %reset_0: !firrtl.uint<1>
// CHECK-SAME: in %clock: !firrtl.clock
// CHECK-SAME: in %reset: !firrtl.asyncreset
// CHECK: %reg = firrtl.regreset %clock, %reset_0, %c0_ui8
firrtl.module @BadType(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset) {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8>
}
// CHECK-LABEL: firrtl.module @ReusePorts
firrtl.module @ReusePortsSync(in %clock: !firrtl.clock, in %reset: !firrtl.uint<1>) attributes {
portAnnotations = [[],[{class = "circt.FullResetAnnotation", resetType = "sync"}]]} {
// CHECK: %child_clock, %child_reset = firrtl.instance child
// CHECK: firrtl.matchingconnect %child_reset, %reset
// CHECK: %badName_reset, %badName_clock, %badName_existingReset = firrtl.instance badName
// CHECK: firrtl.matchingconnect %badName_reset, %reset
// CHECK: %badType_reset_0, %badType_clock, %badType_reset = firrtl.instance badType
// CHECK: firrtl.matchingconnect %badType_reset_0, %reset
%child_clock, %child_reset = firrtl.instance child @Child(in clock: !firrtl.clock, in reset: !firrtl.uint<1>)
%badName_clock, %badName_existingReset = firrtl.instance badName @BadName(in clock: !firrtl.clock, in existingReset: !firrtl.uint<1>)
%badType_clock, %badType_reset = firrtl.instance badType @BadType(in clock: !firrtl.clock, in reset: !firrtl.asyncreset)
}
}
// -----
// Infer async reset: nested
firrtl.circuit "FullAsyncNested" {
@ -544,7 +608,7 @@ firrtl.circuit "FullAsyncNested" {
}
// CHECK-LABEL: firrtl.module @FullAsyncNested
firrtl.module @FullAsyncNested(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset, in %io_in: !firrtl.uint<8>, out %io_out: !firrtl.uint<8>) attributes {
portAnnotations=[[],[{class = "firrtl.transforms.DontTouchAnnotation"}, {class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}], [], []] } {
portAnnotations=[[],[{class = "firrtl.transforms.DontTouchAnnotation"}, {class = "circt.FullResetAnnotation", resetType = "async"}], [], []] } {
%inst_clock, %inst_reset, %inst_io_in, %inst_io_out = firrtl.instance inst @FullAsyncNestedChild(in clock: !firrtl.clock, in reset: !firrtl.asyncreset, in io_in: !firrtl.uint<8>, out io_out: !firrtl.uint<8>)
firrtl.matchingconnect %inst_clock, %clock : !firrtl.clock
firrtl.matchingconnect %inst_reset, %reset : !firrtl.asyncreset
@ -560,7 +624,7 @@ firrtl.circuit "FullAsyncNested" {
firrtl.circuit "FullAsyncExcluded" {
// CHECK-LABEL: firrtl.module @FullAsyncExcludedChild
// CHECK-SAME: (in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset, in %io_in: !firrtl.uint<8>, out %io_out: !firrtl.uint<8>)
firrtl.module @FullAsyncExcludedChild(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset, in %io_in: !firrtl.uint<8>, out %io_out: !firrtl.uint<8>) attributes {annotations = [{class = "sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation"}]} {
firrtl.module @FullAsyncExcludedChild(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset, in %io_in: !firrtl.uint<8>, out %io_out: !firrtl.uint<8>) attributes {annotations = [{class = "circt.ExcludeFromFullResetAnnotation"}]} {
// CHECK: %io_out_REG = firrtl.reg %clock
%io_out_REG = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8>
firrtl.matchingconnect %io_out_REG, %io_in : !firrtl.uint<8>
@ -568,7 +632,7 @@ firrtl.circuit "FullAsyncExcluded" {
}
// CHECK-LABEL: firrtl.module @FullAsyncExcluded
firrtl.module @FullAsyncExcluded(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset, in %io_in: !firrtl.uint<8>, out %io_out: !firrtl.uint<8>, in %extraReset: !firrtl.asyncreset) attributes {
portAnnotations = [[],[],[],[],[{class = "firrtl.transforms.DontTouchAnnotation"}, {class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]]} {
portAnnotations = [[],[],[],[],[{class = "firrtl.transforms.DontTouchAnnotation"}, {class = "circt.FullResetAnnotation", resetType = "async"}]]} {
// CHECK: %inst_clock, %inst_reset, %inst_io_in, %inst_io_out = firrtl.instance inst @FullAsyncExcludedChild
%inst_clock, %inst_reset, %inst_io_in, %inst_io_out = firrtl.instance inst @FullAsyncExcludedChild(in clock: !firrtl.clock, in reset: !firrtl.asyncreset, in io_in: !firrtl.uint<8>, out io_out: !firrtl.uint<8>)
firrtl.matchingconnect %inst_clock, %clock : !firrtl.clock
@ -586,7 +650,7 @@ firrtl.circuit "WireShouldDominate" {
// CHECK-LABEL: firrtl.module @WireShouldDominate
firrtl.module @WireShouldDominate(in %clock: !firrtl.clock) {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8> // gets wired to localReset
%localReset = firrtl.wire {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
%localReset = firrtl.wire {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
// CHECK-NEXT: %localReset = firrtl.wire
// CHECK-NEXT: [[RV:%.+]] = firrtl.constant 0
// CHECK-NEXT: %reg = firrtl.regreset %clock, %localReset, [[RV]]
@ -602,7 +666,7 @@ firrtl.circuit "MovableNodeShouldDominate" {
firrtl.module @MovableNodeShouldDominate(in %clock: !firrtl.clock, in %ui1: !firrtl.uint<1>) {
%0 = firrtl.asAsyncReset %ui1 : (!firrtl.uint<1>) -> !firrtl.asyncreset // does not block move of node
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8> // gets wired to localReset
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
// CHECK-NEXT: %0 = firrtl.asAsyncReset %ui1
// CHECK-NEXT: %localReset = firrtl.node sym @theReset %0
// CHECK-NEXT: [[RV:%.+]] = firrtl.constant 0
@ -620,7 +684,7 @@ firrtl.circuit "UnmovableNodeShouldDominate" {
firrtl.module @UnmovableNodeShouldDominate(in %clock: !firrtl.clock, in %ui1: !firrtl.uint<1>) {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8> // gets wired to localReset
%0 = firrtl.asAsyncReset %ui1 : (!firrtl.uint<1>) -> !firrtl.asyncreset // blocks move of node
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
// CHECK-NEXT: %localReset = firrtl.wire sym @theReset
// CHECK-NEXT: [[RV:%.+]] = firrtl.constant 0
// CHECK-NEXT: %reg = firrtl.regreset %clock, %localReset, [[RV]]
@ -638,7 +702,7 @@ firrtl.circuit "UnmovableForceableNodeShouldDominate" {
firrtl.module @UnmovableForceableNodeShouldDominate(in %clock: !firrtl.clock, in %ui1: !firrtl.uint<1>) {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8> // gets wired to localReset
%0 = firrtl.asAsyncReset %ui1 : (!firrtl.uint<1>) -> !firrtl.asyncreset // blocks move of node
%localReset, %ref = firrtl.node sym @theReset %0 forceable {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
%localReset, %ref = firrtl.node sym @theReset %0 forceable {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
// CHECK-NEXT: %localReset, %{{.+}} = firrtl.wire sym @theReset
// CHECK-NEXT: [[RV:%.+]] = firrtl.constant 0
// CHECK-NEXT: %reg = firrtl.regreset %clock, %localReset, [[RV]]
@ -660,7 +724,7 @@ firrtl.circuit "MoveAcrossBlocks1" {
}
firrtl.when %ui1 : !firrtl.uint<1> {
%0 = firrtl.asAsyncReset %ui1 : (!firrtl.uint<1>) -> !firrtl.asyncreset // blocks move of node
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
}
// CHECK-NEXT: %localReset = firrtl.wire
// CHECK-NEXT: firrtl.when %ui1 : !firrtl.uint<1> {
@ -683,7 +747,7 @@ firrtl.circuit "MoveAcrossBlocks2" {
// <-- should move reset here
firrtl.when %ui1 : !firrtl.uint<1> {
%0 = firrtl.asAsyncReset %ui1 : (!firrtl.uint<1>) -> !firrtl.asyncreset // blocks move of node
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
}
firrtl.when %ui1 : !firrtl.uint<1> {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8> // gets wired to localReset
@ -710,7 +774,7 @@ firrtl.circuit "MoveAcrossBlocks3" {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8> // gets wired to localReset
firrtl.when %ui1 : !firrtl.uint<1> {
%0 = firrtl.asAsyncReset %ui1 : (!firrtl.uint<1>) -> !firrtl.asyncreset // blocks move of node
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
}
// CHECK-NEXT: %localReset = firrtl.wire
// CHECK-NEXT: [[RV:%.+]] = firrtl.constant 0
@ -733,7 +797,7 @@ firrtl.circuit "MoveAcrossBlocks4" {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<8> // gets wired to localReset
}
%0 = firrtl.asAsyncReset %ui1 : (!firrtl.uint<1>) -> !firrtl.asyncreset // blocks move of node
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
%localReset = firrtl.node sym @theReset %0 {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
// CHECK-NEXT: %localReset = firrtl.wire
// CHECK-NEXT: firrtl.when %ui1 : !firrtl.uint<1> {
// CHECK-NEXT: [[RV:%.+]] = firrtl.constant 0
@ -750,7 +814,7 @@ firrtl.circuit "MoveAcrossBlocks4" {
firrtl.circuit "SubAccess" {
firrtl.module @SubAccess(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset, in %init: !firrtl.uint<1>, in %in: !firrtl.uint<8>, in %extraReset: !firrtl.asyncreset ) attributes {
// CHECK-LABEL: firrtl.module @SubAccess
portAnnotations = [[],[],[],[],[{class = "firrtl.transforms.DontTouchAnnotation"}, {class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]]} {
portAnnotations = [[],[],[],[],[{class = "firrtl.transforms.DontTouchAnnotation"}, {class = "circt.FullResetAnnotation", resetType = "async"}]]} {
%c1_ui8 = firrtl.constant 1 : !firrtl.uint<2>
%arr = firrtl.wire : !firrtl.vector<uint<8>, 1>
%reg6 = firrtl.regreset %clock, %init, %c1_ui8 : !firrtl.clock, !firrtl.uint<1>, !firrtl.uint<2>, !firrtl.uint<2>
@ -772,7 +836,7 @@ firrtl.circuit "SubAccess" {
// CHECK-LABEL: firrtl.module @ZeroWidthRegister
firrtl.circuit "ZeroWidthRegister" {
firrtl.module @ZeroWidthRegister(in %clock: !firrtl.clock, in %reset: !firrtl.asyncreset) attributes {
portAnnotations = [[],[{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]]} {
portAnnotations = [[],[{class = "circt.FullResetAnnotation", resetType = "async"}]]} {
%reg = firrtl.reg %clock : !firrtl.clock, !firrtl.uint<0>
// CHECK-NEXT: [[TMP:%.+]] = firrtl.constant 0 : !firrtl.const.uint<0>
// CHECK-NEXT: %reg = firrtl.regreset %clock, %reset, [[TMP]]
@ -1143,8 +1207,8 @@ firrtl.circuit "MovableNodeShouldDominateInstance" {
firrtl.connect %child_clock, %clock : !firrtl.clock
%ui1 = firrtl.constant 1 : !firrtl.uint<1>
%0 = firrtl.asAsyncReset %ui1 : (!firrtl.uint<1>) -> !firrtl.asyncreset
%localReset = firrtl.node %0 {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
// CHECK: %localReset = firrtl.wire {annotations = [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]} : !firrtl.asyncreset
%localReset = firrtl.node %0 {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
// CHECK: %localReset = firrtl.wire {annotations = [{class = "circt.FullResetAnnotation", resetType = "async"}]} : !firrtl.asyncreset
// CHECK: %child_localReset, %child_clock = firrtl.instance child @Child(in localReset: !firrtl.asyncreset, in clock: !firrtl.clock
}
firrtl.module @Child(in %clock: !firrtl.clock) {

View File

@ -1,22 +1,24 @@
; RUN: firtool %s -parse-only | circt-opt -pass-pipeline='builtin.module(firrtl.circuit(firrtl-infer-resets))' | FileCheck %s --check-prefixes COMMON,POST-INFER-RESETS
; RUN: firtool %s -parse-only | circt-opt -pass-pipeline='builtin.module(firrtl.circuit(firrtl-infer-resets,firrtl.module(firrtl-sfc-compat)))' | FileCheck %s --check-prefixes COMMON,POST-SFC-COMPAT
; Check that FullAsyncResetAnnotation exists after infer-resets pass
; Check that FullResetAnnotation exists after infer-resets pass
; but is deleted after sfc-compat
FIRRTL version 3.3.0
circuit test :%[[
{ "class":"sifive.enterprise.firrtl.FullAsyncResetAnnotation",
"target":"~test|test>reset" },
{ "class":"sifive.enterprise.firrtl.FullAsyncResetAnnotation",
"target":"~test|foo>r" }
{ "class":"circt.FullResetAnnotation",
"target":"~test|test>reset",
"resetType":"async" },
{ "class":"circt.FullResetAnnotation",
"target":"~test|foo>r",
"resetType":"async" }
]]
; COMMON-LABEL: module @test
module test :
input clock : Clock
input reset : AsyncReset
; POST-INFER-RESETS: [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]
; POST-SFC-COMPAT-NOT: [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]
; POST-INFER-RESETS: [{class = "circt.FullResetAnnotation", resetType = "async"}]
; POST-SFC-COMPAT-NOT: [{class = "circt.FullResetAnnotation", resetType = "async"}]
input in : { foo : UInt<8>, bar : UInt<8>}
output out : { foo : UInt<8>, bar : UInt<8>}
connect out, in
@ -28,7 +30,7 @@ circuit test :%[[
input in : { foo : UInt<8>, bar : UInt<8>}
output out : { foo : UInt<8>, bar : UInt<8>}
; POST-INFER-RESETS: [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]
; POST-SFC-COMPAT-NOT: [{class = "sifive.enterprise.firrtl.FullAsyncResetAnnotation"}]
; POST-INFER-RESETS: [{class = "circt.FullResetAnnotation", resetType = "async"}]
; POST-SFC-COMPAT-NOT: [{class = "circt.FullResetAnnotation", resetType = "async"}]
wire r : AsyncReset
connect out, in

View File

@ -3,8 +3,9 @@
FIRRTL version 3.3.0
; CHECK-LABEL: module test(
circuit test :%[[{
"class":"sifive.enterprise.firrtl.FullAsyncResetAnnotation",
"target":"~test|test>reset"
"class":"circt.FullResetAnnotation",
"target":"~test|test>reset",
"resetType":"async"
}]]
module test :
input clock : Clock
@ -31,8 +32,9 @@ circuit test :%[[{
; CHECK-LABEL: module test_wire(
FIRRTL version 3.3.0
circuit test_wire :%[[{
"class":"sifive.enterprise.firrtl.FullAsyncResetAnnotation",
"target":"~test_wire|test_wire>reset"
"class":"circt.FullResetAnnotation",
"target":"~test_wire|test_wire>reset",
"resetType":"async"
}]]
module test_wire :
input clock : Clock
@ -56,3 +58,63 @@ circuit test_wire :%[[{
connect reg1, in
connect reg2, reg1
connect out, reg2
;// -----
; CHECK-LABEL: module test_sync(
FIRRTL version 3.3.0
circuit test_sync :%[[{
"class":"circt.FullResetAnnotation",
"target":"~test_sync|test_sync>reset",
"resetType":"sync"
}]]
module test_sync :
input clock : Clock
input reset : UInt<1>
input in : { foo : UInt<8>, bar : UInt<8>}
output out : { foo : UInt<8>, bar : UInt<8>}
wire reg1_w : { foo : UInt<8>, bar : UInt<8>}
invalidate reg1_w.bar
invalidate reg1_w.foo
connect reg1_w.foo, UInt<8>(0hc)
invalidate reg1_w.bar
; CHECK: always @(posedge clock) begin
; CHECK-NEXT: if (reset) begin
; CHECK-NEXT: reg1_foo <= 8'hC;
; CHECK-NEXT: reg1_bar <= 8'h0;
regreset reg1 : { foo : UInt<8>, bar : UInt<8>}, clock, reset, reg1_w
wire reg2 : { foo : UInt<8>, bar : UInt<8>}
connect reg1, in
connect reg2, reg1
connect out, reg2
;// -----
; CHECK-LABEL: module test_wire_sync(
FIRRTL version 3.3.0
circuit test_wire_sync :%[[{
"class":"circt.FullResetAnnotation",
"target":"~test_wire_sync|test_wire_sync>reset",
"resetType":"sync"
}]]
module test_wire_sync :
input clock : Clock
input rst : UInt<1>
input in : { foo : UInt<8>, bar : UInt<8>}
output out : { foo : UInt<8>, bar : UInt<8>}
node reset = rst
wire reg1_w : { foo : UInt<8>, bar : UInt<8>}
invalidate reg1_w.bar
invalidate reg1_w.foo
connect reg1_w.foo, UInt<8>(0hc)
invalidate reg1_w.bar
; CHECK: always @(posedge clock) begin
; CHECK-NEXT: if (rst) begin
; CHECK-NEXT: reg1_foo <= 8'hC;
; CHECK-NEXT: reg1_bar <= 8'h0;
regreset reg1 : { foo : UInt<8>, bar : UInt<8>}, clock, reset, reg1_w
wire reg2 : { foo : UInt<8>, bar : UInt<8>}
connect reg1, in
connect reg2, reg1
connect out, reg2