diff --git a/include/circt/Dialect/HW/HWOps.h b/include/circt/Dialect/HW/HWOps.h index 02bff24c6d75..f575ba351847 100644 --- a/include/circt/Dialect/HW/HWOps.h +++ b/include/circt/Dialect/HW/HWOps.h @@ -19,6 +19,7 @@ #include "circt/Dialect/HW/HWOpInterfaces.h" #include "circt/Dialect/HW/HWTypes.h" #include "circt/Support/BuilderUtils.h" +#include "circt/Support/ProceduralRegionTrait.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/ImplicitLocOpBuilder.h" #include "mlir/IR/OpImplementation.h" diff --git a/include/circt/Dialect/HW/HWStructure.td b/include/circt/Dialect/HW/HWStructure.td index 7e434deb85a4..f44de0b67909 100644 --- a/include/circt/Dialect/HW/HWStructure.td +++ b/include/circt/Dialect/HW/HWStructure.td @@ -18,6 +18,7 @@ include "circt/Dialect/HW/HWDialect.td" include "circt/Dialect/HW/HWOpInterfaces.td" include "circt/Dialect/HW/HWTypes.td" include "circt/Dialect/Emit/EmitOpInterfaces.td" +include "circt/Support/ProceduralRegionTrait.td" include "mlir/Interfaces/FunctionInterfaces.td" include "mlir/IR/OpAsmInterface.td" include "mlir/IR/OpBase.td" @@ -114,7 +115,7 @@ class HWModuleOpBase traits = []> : def HWModuleOp : HWModuleOpBase<"module", [IsolatedFromAbove, RegionKindInterface, SingleBlockImplicitTerminator<"OutputOp">, - HWEmittableModuleLike]>{ + HWEmittableModuleLike, NonProceduralRegion]>{ let summary = "HW Module"; let description = [{ The "hw.module" operation represents a Verilog module, including a given @@ -621,7 +622,8 @@ def EventControlAttr : I32EnumAttr<"EventControl", "edge control trigger", } def TriggeredOp : HWOp<"triggered", [ - IsolatedFromAbove, SingleBlock, NoTerminator]> { + IsolatedFromAbove, SingleBlock, NoTerminator, NonProceduralOp, + ProceduralRegion]> { let summary = "A procedural region with a trigger condition"; let description = [{ A procedural region that can be triggered by an event. The trigger diff --git a/include/circt/Dialect/SV/SV.td b/include/circt/Dialect/SV/SV.td index 7902aaa27fbe..92daa5d8bf1c 100644 --- a/include/circt/Dialect/SV/SV.td +++ b/include/circt/Dialect/SV/SV.td @@ -26,14 +26,6 @@ include "circt/Dialect/SV/SVDialect.td" class SVOp traits = []> : Op; -def ProceduralOp : NativeOpTrait<"ProceduralOp"> { - let cppNamespace = "::circt::sv"; -} - -def NonProceduralOp : NativeOpTrait<"NonProceduralOp"> { - let cppNamespace = "::circt::sv"; -} - /// Mark an operation as being a vendor extension. def VendorExtension : NativeOpTrait<"VendorExtension"> { let cppNamespace = "::circt::sv"; diff --git a/include/circt/Dialect/SV/SVInOutOps.td b/include/circt/Dialect/SV/SVInOutOps.td index fe9d084a8357..d1f3c01068bb 100644 --- a/include/circt/Dialect/SV/SVInOutOps.td +++ b/include/circt/Dialect/SV/SVInOutOps.td @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// include "circt/Dialect/HW/HWOpInterfaces.td" +include "circt/Support/ProceduralRegionTrait.td" include "circt/Types.td" include "mlir/Interfaces/InferTypeOpInterface.td" diff --git a/include/circt/Dialect/SV/SVOps.h b/include/circt/Dialect/SV/SVOps.h index f84b81fe3180..b629263a4864 100644 --- a/include/circt/Dialect/SV/SVOps.h +++ b/include/circt/Dialect/SV/SVOps.h @@ -166,32 +166,6 @@ struct CaseInfo { // Other Supporting Logic //===----------------------------------------------------------------------===// -/// Return true if the specified operation is in a procedural region. -LogicalResult verifyInProceduralRegion(Operation *op); -/// Return true if the specified operation is not in a procedural region. -LogicalResult verifyInNonProceduralRegion(Operation *op); - -/// This class verifies that the specified op is located in a procedural region. -template -class ProceduralOp - : public mlir::OpTrait::TraitBase { -public: - static LogicalResult verifyTrait(Operation *op) { - return verifyInProceduralRegion(op); - } -}; - -/// This class verifies that the specified op is not located in a procedural -/// region. -template -class NonProceduralOp - : public mlir::OpTrait::TraitBase { -public: - static LogicalResult verifyTrait(Operation *op) { - return verifyInNonProceduralRegion(op); - } -}; - /// This class provides a verifier for ops that are expecting their parent /// to be one of the given parent ops template diff --git a/include/circt/Dialect/SV/SVStatements.td b/include/circt/Dialect/SV/SVStatements.td index b2bc78de5092..961b970fcc79 100644 --- a/include/circt/Dialect/SV/SVStatements.td +++ b/include/circt/Dialect/SV/SVStatements.td @@ -18,6 +18,7 @@ include "mlir/Interfaces/CallInterfaces.td" include "mlir/Interfaces/ControlFlowInterfaces.td" include "circt/Dialect/Emit/EmitOpInterfaces.td" include "circt/Dialect/HW/HWOpInterfaces.td" +include "circt/Support/ProceduralRegionTrait.td" //===----------------------------------------------------------------------===// // Control flow like-operations @@ -48,6 +49,7 @@ def OrderedOutputOp : SVOp<"ordered", [SingleBlock, NoTerminator, NoRegionArgume def IfDefOp : SVOp<"ifdef", [ NoRegionArguments, NonProceduralOp, + NonProceduralRegion, GraphRegionNoTerminator, DeclareOpInterfaceMethods ]> { diff --git a/include/circt/Dialect/Sim/SimOps.h b/include/circt/Dialect/Sim/SimOps.h index a5225138d026..93d4ae107854 100644 --- a/include/circt/Dialect/Sim/SimOps.h +++ b/include/circt/Dialect/Sim/SimOps.h @@ -19,6 +19,7 @@ #include "circt/Dialect/Sim/SimDialect.h" #include "circt/Dialect/Sim/SimTypes.h" #include "circt/Support/BuilderUtils.h" +#include "circt/Support/ProceduralRegionTrait.h" #include "mlir/Bytecode/BytecodeOpInterface.h" #include "mlir/IR/OpImplementation.h" #include "mlir/IR/SymbolTable.h" diff --git a/include/circt/Dialect/Sim/SimOps.td b/include/circt/Dialect/Sim/SimOps.td index 7ed9f5bb200f..962041426441 100644 --- a/include/circt/Dialect/Sim/SimOps.td +++ b/include/circt/Dialect/Sim/SimOps.td @@ -15,6 +15,7 @@ include "circt/Dialect/Seq/SeqTypes.td" include "circt/Dialect/Sim/SimDialect.td" include "circt/Dialect/Sim/SimOpInterfaces.td" include "circt/Dialect/Sim/SimTypes.td" +include "circt/Support/ProceduralRegionTrait.td" include "mlir/Interfaces/FunctionInterfaces.td" include "mlir/Interfaces/InferTypeOpInterface.td" include "mlir/Interfaces/SideEffectInterfaces.td" @@ -546,7 +547,7 @@ def FormatStringConcatOp : SimOp<"fmt.concat", [Pure]> { }]; } -def PrintFormattedOp : SimOp<"print"> { +def PrintFormattedOp : SimOp<"print", [NonProceduralOp]> { let summary = "Print a formatted string on a given clock and condition"; let description = [{ Evaluate a format string and print it to the simulation console on the @@ -577,7 +578,7 @@ def PrintFormattedOp : SimOp<"print"> { let assemblyFormat = "$input `on` $clock `if` $condition (`to` $stream^)? attr-dict"; } -def PrintFormattedProcOp : SimOp<"proc.print"> { +def PrintFormattedProcOp : SimOp<"proc.print", [ProceduralOp]> { let summary = "Print a formatted string within a procedural region"; let description = [{ Evaluate a format string and print it to the simulation console. @@ -595,7 +596,6 @@ def PrintFormattedProcOp : SimOp<"proc.print"> { build($_builder, $_state, input, mlir::Value()); }]> ]; - let hasVerifier = true; let hasCanonicalizeMethod = true; let assemblyFormat = "$input (`to` $stream^)? attr-dict"; } @@ -1026,7 +1026,7 @@ def QueueConcatOp : SimOp<"queue.concat", [Pure]> { // Simulation Control //===----------------------------------------------------------------------===// -def ClockedTerminateOp : SimOp<"clocked_terminate"> { +def ClockedTerminateOp : SimOp<"clocked_terminate", [NonProceduralOp]> { let summary = "Terminate the simulation"; let description = [{ Implements the semantics of `sim.terminate` if the given condition is true @@ -1045,7 +1045,7 @@ def ClockedTerminateOp : SimOp<"clocked_terminate"> { }]; } -def ClockedPauseOp : SimOp<"clocked_pause"> { +def ClockedPauseOp : SimOp<"clocked_pause", [NonProceduralOp]> { let summary = "Pause the simulation"; let description = [{ Implements the semantics of `sim.pause` if the given condition is true on @@ -1062,7 +1062,7 @@ def ClockedPauseOp : SimOp<"clocked_pause"> { }]; } -def TerminateOp : SimOp<"terminate"> { +def TerminateOp : SimOp<"terminate", [ProceduralOp]> { let summary = "Terminate the simulation"; let description = [{ Terminate the simulation with a success or failure exit code. Depending on @@ -1092,7 +1092,7 @@ def TerminateOp : SimOp<"terminate"> { }]; } -def PauseOp : SimOp<"pause"> { +def PauseOp : SimOp<"pause", [ProceduralOp]> { let summary = "Pause the simulation"; let description = [{ Interrupt the simulation and give control back to the user in case of an diff --git a/include/circt/Support/ProceduralRegionTrait.h b/include/circt/Support/ProceduralRegionTrait.h index a2be345d7723..594df7439a0c 100644 --- a/include/circt/Support/ProceduralRegionTrait.h +++ b/include/circt/Support/ProceduralRegionTrait.h @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This header file defines the `ProceduralRegion` trait. +// This header file defines traits for (non-)procedural regions and operations. // //===----------------------------------------------------------------------===// @@ -19,6 +19,20 @@ namespace circt { +/// Returns `success` if the operation has no closer surrounding parent +/// marked as procedural region than its closest parent marked as +/// non-procedural region. +/// Also returns `success` if no parent is marked as either procedural or +/// non-procedural region. +LogicalResult verifyNotInProceduralRegion(Operation *op); + +/// Returns `success` if the operation has no closer surrounding parent +/// marked as non-procedural region than its closest parent marked as +/// procedural region. +/// Also returns `success` if no parent is marked as either procedural or +/// non-procedural region. +LogicalResult verifyNotInNonProceduralRegion(Operation *op); + /// Signals that an operation's regions are procedural. template class ProceduralRegion @@ -28,6 +42,35 @@ class ProceduralRegion } }; +/// Signals that an operation's regions are non-procedural. +template +class NonProceduralRegion + : public mlir::OpTrait::TraitBase { + static LogicalResult verifyTrait(Operation *op) { + return mlir::OpTrait::impl::verifyNRegions(op, 1); + } +}; + +/// Signals that an operation must not be in a non-procedural region. +template +class ProceduralOp + : public mlir::OpTrait::TraitBase { +public: + static LogicalResult verifyTrait(Operation *op) { + return verifyNotInNonProceduralRegion(op); + } +}; + +/// Signals that an operation must not be in a procedural region. +template +class NonProceduralOp + : public mlir::OpTrait::TraitBase { +public: + static LogicalResult verifyTrait(Operation *op) { + return verifyNotInProceduralRegion(op); + } +}; + } // namespace circt #endif // CIRCT_SUPPORT_PROCEDURALREGIONTRAIT_H diff --git a/include/circt/Support/ProceduralRegionTrait.td b/include/circt/Support/ProceduralRegionTrait.td index a1653a967d97..186cb90d5230 100644 --- a/include/circt/Support/ProceduralRegionTrait.td +++ b/include/circt/Support/ProceduralRegionTrait.td @@ -11,8 +11,24 @@ include "mlir/IR/OpBase.td" +// Note: The procedural and non-procedural traits are intended to be mutually +// exclusive. If an operation or region is not strictly procedural or +// non-procedural, it should carry neither trait. + def ProceduralRegion : NativeOpTrait<"ProceduralRegion"> { let cppNamespace = "::circt"; } +def NonProceduralRegion : NativeOpTrait<"NonProceduralRegion"> { + let cppNamespace = "::circt"; +} + +def ProceduralOp : NativeOpTrait<"ProceduralOp"> { + let cppNamespace = "::circt"; +} + +def NonProceduralOp : NativeOpTrait<"NonProceduralOp"> { + let cppNamespace = "::circt"; +} + #endif // CIRCT_SUPPORT_PROCDURALREGIONTRAIT_TD diff --git a/lib/Dialect/LLHD/IR/CMakeLists.txt b/lib/Dialect/LLHD/IR/CMakeLists.txt index a60aa27bcabd..b9bdf7864d74 100644 --- a/lib/Dialect/LLHD/IR/CMakeLists.txt +++ b/lib/Dialect/LLHD/IR/CMakeLists.txt @@ -14,6 +14,7 @@ add_circt_dialect_library(CIRCTLLHD LINK_LIBS PUBLIC CIRCTHW CIRCTComb + CIRCTSupport MLIRIR MLIRSideEffectInterfaces MLIRControlFlowInterfaces diff --git a/lib/Dialect/SV/SVOps.cpp b/lib/Dialect/SV/SVOps.cpp index b4a7bf1c49c2..4e0b0ea4d5e4 100644 --- a/lib/Dialect/SV/SVOps.cpp +++ b/lib/Dialect/SV/SVOps.cpp @@ -56,20 +56,6 @@ bool sv::isExpression(Operation *op) { MacroRefExprOp, MacroRefExprSEOp>(op); } -LogicalResult sv::verifyInProceduralRegion(Operation *op) { - if (op->getParentOp()->hasTrait()) - return success(); - op->emitError() << op->getName() << " should be in a procedural region"; - return failure(); -} - -LogicalResult sv::verifyInNonProceduralRegion(Operation *op) { - if (!op->getParentOp()->hasTrait()) - return success(); - op->emitError() << op->getName() << " should be in a non-procedural region"; - return failure(); -} - /// Returns the operation registered with the given symbol name with the regions /// of 'symbolTableOp'. recurse through nested regions which don't contain the /// symboltable trait. Returns nullptr if no valid symbol was found. diff --git a/lib/Dialect/Sim/CMakeLists.txt b/lib/Dialect/Sim/CMakeLists.txt index a9e2c03083dc..08ec677c82ee 100644 --- a/lib/Dialect/Sim/CMakeLists.txt +++ b/lib/Dialect/Sim/CMakeLists.txt @@ -28,6 +28,7 @@ add_circt_dialect_library(CIRCTSim LINK_LIBS PUBLIC CIRCTHW CIRCTSeq + CIRCTSupport CIRCTSV MLIRFuncDialect MLIRLLVMDialect diff --git a/lib/Dialect/Sim/SimOps.cpp b/lib/Dialect/Sim/SimOps.cpp index 3e74b53b51dd..f6b27a13b679 100644 --- a/lib/Dialect/Sim/SimOps.cpp +++ b/lib/Dialect/Sim/SimOps.cpp @@ -626,29 +626,6 @@ LogicalResult PrintFormattedOp::canonicalize(PrintFormattedOp op, return failure(); } -LogicalResult PrintFormattedProcOp::verify() { - // Check if we know for sure that the parent is not procedural. - auto *parentOp = getOperation()->getParentOp(); - - if (!parentOp) - return emitOpError("must be within a procedural region."); - - if (isa_and_nonnull(parentOp->getDialect())) { - if (!isa(parentOp)) - return emitOpError("must be within a procedural region."); - return success(); - } - - if (isa_and_nonnull(parentOp->getDialect())) { - if (!parentOp->hasTrait()) - return emitOpError("must be within a procedural region."); - return success(); - } - - // Don't fail for dialects that are not explicitly handled. - return success(); -} - LogicalResult PrintFormattedProcOp::canonicalize(PrintFormattedProcOp op, PatternRewriter &rewriter) { // Remove empty prints. diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt index 9fdcc3e20d63..5933662ebec8 100644 --- a/lib/Support/CMakeLists.txt +++ b/lib/Support/CMakeLists.txt @@ -17,6 +17,7 @@ add_circt_library(CIRCTSupport Path.cpp PrettyPrinter.cpp PrettyPrinterHelpers.cpp + ProceduralRegionTrait.cpp SATSolver.cpp SymCache.cpp TruthTable.cpp diff --git a/lib/Support/ProceduralRegionTrait.cpp b/lib/Support/ProceduralRegionTrait.cpp new file mode 100644 index 000000000000..4d3a51037e38 --- /dev/null +++ b/lib/Support/ProceduralRegionTrait.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "circt/Support/ProceduralRegionTrait.h" + +using namespace llvm; +using namespace mlir; + +namespace circt { + +LogicalResult verifyNotInProceduralRegion(Operation *op) { + auto *parent = op; + while ((parent = parent->getParentOp())) { + if (parent->hasTrait()) + return success(); + if (parent->hasTrait()) + return op->emitOpError("must not be in a procedural region"); + } + return success(); +} + +LogicalResult verifyNotInNonProceduralRegion(Operation *op) { + auto *parent = op; + while ((parent = parent->getParentOp())) { + if (parent->hasTrait()) + return success(); + if (parent->hasTrait()) + return op->emitOpError("must not be in a non-procedural region"); + } + return success(); +} + +} // namespace circt diff --git a/test/Dialect/HW/errors.mlir b/test/Dialect/HW/errors.mlir index 5f997bfd9d6c..cbdcac76e43f 100644 --- a/test/Dialect/HW/errors.mlir +++ b/test/Dialect/HW/errors.mlir @@ -544,3 +544,14 @@ hw.array_get %0[%1] : !hw.array<1000xi42>, i9 %2 = hw.constant 0 : i42 // expected-error @below {{index bit width equals ceil(log2(length(input))), or 0 or 1 if input contains only one element}} hw.array_inject %0[%1], %2 : !hw.array<1000xi42>, i9 + +// ----- + +hw.module @triggeredInTriggered(in %trigger : i1) { + hw.triggered posedge %trigger (%trigger) : i1 { + ^bb0(%arg: i1): + // expected-error @below {{must not be in a procedural region}} + hw.triggered posedge %arg { + } + } +} diff --git a/test/Dialect/SV/errors.mlir b/test/Dialect/SV/errors.mlir index 49ee496ce0f8..e091a5c72269 100644 --- a/test/Dialect/SV/errors.mlir +++ b/test/Dialect/SV/errors.mlir @@ -35,42 +35,42 @@ hw.module @Aliasing(inout %a : i42, inout %b : i42, // ----- hw.module @Fwrite() { %fd = hw.constant 0x80000002 : i32 - // expected-error @+1 {{sv.fwrite should be in a procedural region}} + // expected-error @+1 {{'sv.fwrite' op must not be in a non-procedural region}} sv.fwrite %fd, "error" } // ----- hw.module @Bpassign(in %arg0: i1) { %reg = sv.reg : !hw.inout - // expected-error @+1 {{sv.bpassign should be in a procedural region}} + // expected-error @+1 {{'sv.bpassign' op must not be in a non-procedural region}} sv.bpassign %reg, %arg0 : i1 } // ----- hw.module @Passign(in %arg0: i1) { %reg = sv.reg : !hw.inout - // expected-error @+1 {{sv.passign should be in a procedural region}} + // expected-error @+1 {{'sv.passign' op must not be in a non-procedural region}} sv.passign %reg, %arg0 : i1 } // ----- hw.module @ForcePassign(in %arg0: i1) { %reg = sv.reg : !hw.inout - // expected-error @+1 {{sv.force should be in a procedural region}} + // expected-error @+1 {{'sv.force' op must not be in a non-procedural region}} sv.force %reg, %arg0 : i1 } // ----- hw.module @ReleasePassign(in %arg0: i1) { %reg = sv.reg : !hw.inout - // expected-error @+1 {{sv.release should be in a procedural region}} + // expected-error @+1 {{'sv.release' op must not be in a non-procedural region}} sv.release %reg : !hw.inout } // ----- hw.module @IfOp(in %arg0: i1) { %fd = hw.constant 0x80000002 : i32 - // expected-error @+1 {{sv.if should be in a procedural region}} + // expected-error @+1 {{'sv.if' op must not be in a non-procedural region}} sv.if %arg0 { sv.fwrite %fd, "Foo" } @@ -78,38 +78,38 @@ hw.module @IfOp(in %arg0: i1) { // ----- hw.module @FatalProcedural() { - // expected-error @+1 {{sv.fatal.procedural should be in a procedural region}} + // expected-error @+1 {{'sv.fatal.procedural' op must not be in a non-procedural region}} sv.fatal.procedural 1 } // ----- hw.module @Finish() { - // expected-error @+1 {{sv.finish should be in a procedural region}} + // expected-error @+1 {{'sv.finish' op must not be in a non-procedural region}} sv.finish 1 } // ----- hw.module @ErrorProcedural() { - // expected-error @+1 {{sv.error.procedural should be in a procedural region}} + // expected-error @+1 {{'sv.error.procedural' op must not be in a non-procedural region}} sv.error.procedural } // ----- hw.module @WarningProcedural() { - // expected-error @+1 {{sv.warning.procedural should be in a procedural region}} + // expected-error @+1 {{'sv.warning.procedural' op must not be in a non-procedural region}} sv.warning.procedural } // ----- hw.module @InfoProcedural() { - // expected-error @+1 {{sv.info.procedural should be in a procedural region}} + // expected-error @+1 {{'sv.info.procedural' op must not be in a non-procedural region}} sv.info.procedural } // ----- hw.module @ErrorInProcedural() { sv.initial { - // expected-error @+1 {{sv.error should be in a non-procedural region}} + // expected-error @+1 {{'sv.error' op must not be in a procedural region}} sv.error } } @@ -117,7 +117,7 @@ hw.module @ErrorInProcedural() { // ----- hw.module @WarningInProcedural() { sv.initial { - // expected-error @+1 {{sv.warning should be in a non-procedural region}} + // expected-error @+1 {{'sv.warning' op must not be in a procedural region}} sv.warning } } @@ -125,7 +125,7 @@ hw.module @WarningInProcedural() { // ----- hw.module @InfoInProcedural() { sv.initial { - // expected-error @+1 {{sv.info should be in a non-procedural region}} + // expected-error @+1 {{'sv.info' op must not be in a procedural region}} sv.info } } @@ -133,7 +133,7 @@ hw.module @InfoInProcedural() { // ----- hw.module @CaseZ(in %arg8: i8) { %fd = hw.constant 0x80000002 : i32 - // expected-error @+1 {{sv.case should be in a procedural region}} + // expected-error @+1 {{'sv.case' op must not be in a non-procedural region}} sv.case %arg8 : i8 case b0000001x: { sv.fwrite %fd, "x" @@ -146,7 +146,7 @@ hw.module @CaseZ(in %arg8: i8) { // ----- hw.module @Initial() { sv.initial { - // expected-error @+1 {{sv.initial should be in a non-procedural region}} + // expected-error @+1 {{'sv.initial' op must not be in a procedural region}} sv.initial {} } } @@ -154,7 +154,7 @@ hw.module @Initial() { // ----- hw.module @IfDef() { sv.initial { - // expected-error @+1 {{sv.ifdef should be in a non-procedural region}} + // expected-error @+1 {{'sv.ifdef' op must not be in a procedural region}} sv.ifdef @SYNTHESIS {} } } @@ -162,7 +162,7 @@ hw.module @IfDef() { // ----- hw.module @Always(in %arg0: i1) { sv.initial { - // expected-error @+1 {{sv.always should be in a non-procedural region}} + // expected-error @+1 {{'sv.always' op must not be in a procedural region}} sv.always posedge %arg0 {} } } @@ -170,7 +170,7 @@ hw.module @Always(in %arg0: i1) { // ----- hw.module @AlwaysFF(in %arg0: i1) { sv.initial { - // expected-error @+1 {{sv.alwaysff should be in a non-procedural region}} + // expected-error @+1 {{'sv.alwaysff' op must not be in a procedural region}} sv.alwaysff (posedge %arg0) {} } } @@ -178,26 +178,26 @@ hw.module @AlwaysFF(in %arg0: i1) { // ----- hw.module @Wire() { sv.initial { - // expected-error @+1 {{sv.wire should be in a non-procedural region}} + // expected-error @+1 {{'sv.wire' op must not be in a procedural region}} %wire = sv.wire : !hw.inout } } // ----- hw.module @Assert(in %arg0: i1) { - // expected-error @+1 {{sv.assert should be in a procedural region}} + // expected-error @+1 {{'sv.assert' op must not be in a non-procedural region}} sv.assert %arg0, immediate } // ----- hw.module @Assume(in %arg0: i1) { - // expected-error @+1 {{sv.assume should be in a procedural region}} + // expected-error @+1 {{'sv.assume' op must not be in a non-procedural region}} sv.assume %arg0, immediate } // ----- hw.module @Cover(in %arg0: i1) { - // expected-error @+1 {{sv.cover should be in a procedural region}} + // expected-error @+1 {{'sv.cover' op must not be in a non-procedural region}} sv.cover %arg0, immediate } diff --git a/test/Dialect/Sim/round-trip.mlir b/test/Dialect/Sim/round-trip.mlir index 3040e0d4af92..c4332ad51fc2 100644 --- a/test/Dialect/Sim/round-trip.mlir +++ b/test/Dialect/Sim/round-trip.mlir @@ -100,6 +100,7 @@ func.func @DynamicStrings(%idx: i32) { return } +// CHECK-LABEL: hw.module @PrintFormattedWithStream hw.module @PrintFormattedWithStream(in %clock: !seq.clock, in %condition: i1, in %idx: i32) { // CHECK: %[[FMT:.*]] = sim.fmt.literal "literal string" %str = sim.fmt.literal "literal string" @@ -118,3 +119,26 @@ hw.module @PrintFormattedWithStream(in %clock: !seq.clock, in %condition: i1, in // CHECK: sim.print %[[FMT]] on %clock if %condition to %[[FILE]] sim.print %str on %clock if %condition to %file } + +// CHECK-LABEL: hw.module @ProceduralPrint +hw.module @ProceduralPrint(in %trigger: i1, in %condition: i1) { +// CHECK-NEXT: hw.triggered + hw.triggered posedge %trigger (%condition) : i1 { + ^bb0(%c : i1): + // CHECK-COUNT-3: sim.fmt.literal + %foo = sim.fmt.literal "foo" + %bar = sim.fmt.literal "bar" + %baz = sim.fmt.literal "baz" + // CHECK-NEXT: sim.proc.print + sim.proc.print %foo + // CHECK-NEXT: scf.if + scf.if %c { + // CHECK: sim.proc.print + sim.proc.print %bar + // CHECK: else + } else { + // CHECK: sim.proc.print + sim.proc.print %baz + } + } +} diff --git a/test/Dialect/Sim/sim-errors.mlir b/test/Dialect/Sim/sim-errors.mlir index 48de9014a0ed..0a99bed53404 100644 --- a/test/Dialect/Sim/sim-errors.mlir +++ b/test/Dialect/Sim/sim-errors.mlir @@ -26,20 +26,63 @@ hw.module @fmt_infinite_concat_canonicalize(in %val : i8, out res: !sim.fstring) // ----- -hw.module @proc_print_hw() { +hw.module @procedural_ops_print() { %lit = sim.fmt.literal "Nope" - // expected-error @below {{must be within a procedural region.}} + // expected-error @below {{must not be in a non-procedural region}} sim.proc.print %lit } // ----- -sv.macro.decl @SOMEMACRO -hw.module @proc_print_sv() { - %lit = sim.fmt.literal "Nope" - sv.ifdef @SOMEMACRO { - // expected-error @below {{must be within a procedural region.}} - sim.proc.print %lit +hw.module @procedural_ops_pause() { + // expected-error @below {{must not be in a non-procedural region}} + sim.pause quiet +} + +// ----- + +hw.module @procedural_ops_terminate() { + // expected-error @below {{must not be in a non-procedural region}} + sim.terminate success, quiet +} + +// ----- + +hw.module @nonprocedural_ops_print(in %trigger : i1, in %clock : !seq.clock, in %cond : i1) { + hw.triggered posedge %trigger { + %lit = sim.fmt.literal "Nope" + // expected-error @below {{must not be in a procedural region}} + sim.print %lit on %clock if %cond + } +} + +// ----- + +hw.module @nonprocedural_ops_pause(in %trigger : i1, in %clock : !seq.clock, in %cond : i1) { + hw.triggered posedge %trigger { + // expected-error @below {{must not be in a procedural region}} + sim.clocked_pause %clock, %cond, quiet + } +} + +// ----- + +hw.module @nonprocedural_ops_terminate(in %trigger : i1, in %clock : !seq.clock, in %cond : i1) { + hw.triggered posedge %trigger { + // expected-error @below {{must not be in a procedural region}} + sim.clocked_terminate %clock, %cond, success, quiet + } +} + +// ----- + +hw.module @nonproc_print_scf_if(in %trigger : i1, in %clock : !seq.clock, in %cond : i1) { + hw.triggered posedge %trigger { + %lit = sim.fmt.literal "Nope" + scf.if %cond { + // expected-error @below {{must not be in a procedural region}} + sim.print %lit on %clock if %cond + } } }