mirror of https://github.com/llvm/circt.git
[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:
parent
2f869728d5
commit
fa95071921
|
@ -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` |
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()";
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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}},
|
||||
|
|
|
@ -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>();
|
||||
})) {
|
||||
|
|
|
@ -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) {}
|
||||
}
|
|
@ -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"}]]} {}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue