From e4878fdef768b6982f91432ee8e6e55e4f4c88c7 Mon Sep 17 00:00:00 2001 From: Fabian Schuiki Date: Wed, 13 Oct 2021 17:01:09 +0200 Subject: [PATCH] [SV] Add sim ctrl and severity message system tasks (#1989) Extend the SV dialect to cover all simulation control system tasks (`$stop`, `$finish`, and `$exit`), and all severity message tasks (`$fatal`, `$error`, `$warning`, `$info`). This change includes added support for diagnostic verbosity in stop, finish, and fatal, as well as optional messages and interpolation operands for fatal, error, warning, and info. A subsequent PR will make use of these to lower from the FIRRTL dialect. --- include/circt/Dialect/SV/SVStatements.td | 157 +++++++++++++++--- include/circt/Dialect/SV/SVVisitors.h | 21 ++- .../ExportVerilog/ExportVerilog.cpp | 110 ++++++++++-- test/Dialect/SV/basic.mlir | 42 +++++ test/Dialect/SV/errors.mlir | 6 +- .../SV/hw-legalize-modules-packed-arrays.mlir | 4 +- test/Dialect/SV/prettify-verilog.mlir | 12 +- test/ExportVerilog/disallow-local-vars.mlir | 8 +- test/ExportVerilog/sv-dialect.mlir | 61 +++++-- test/ExportVerilog/verilog-basic.mlir | 2 +- 10 files changed, 355 insertions(+), 68 deletions(-) diff --git a/include/circt/Dialect/SV/SVStatements.td b/include/circt/Dialect/SV/SVStatements.td index f3d76704c1..214b7cad37 100644 --- a/include/circt/Dialect/SV/SVStatements.td +++ b/include/circt/Dialect/SV/SVStatements.td @@ -432,30 +432,6 @@ def FWriteOp : SVOp<"fwrite", [ProceduralOp]> { "$string attr-dict (`(` $operands^ `)` `:` type($operands))?"; } -def FinishOp : SVOp<"finish", [ProceduralOp]> { - let summary = "'$finish' statement"; - let description = [{ - Finishes a simulation and exits the simulation process. - }]; - - let arguments = (ins); - let results = (outs); - - let assemblyFormat = "attr-dict"; -} - -def FatalOp : SVOp<"fatal", [ProceduralOp]> { - let summary = "'$fatal' statement"; - let description = [{ - Run-time fatal assertion error. - }]; - - let arguments = (ins); - let results = (outs); - - let assemblyFormat = "attr-dict"; -} - def VerbatimOp : SVOp<"verbatim"> { let summary = "Verbatim opaque text emitted inline."; let description = [{ @@ -523,5 +499,136 @@ def BindInterfaceOp : SVOp<"bind.interface", []> { sv::InterfaceInstanceOp getReferencedInstance(const hw::SymbolCache *cache = nullptr); }]; - +} + +//===----------------------------------------------------------------------===// +// Simulation Control Tasks +//===----------------------------------------------------------------------===// + +/// A number passed to `$stop` and `$finish` that indicates the level of +/// verbosity of the diagnostic message to be printed (see section 20.2 of IEEE +/// 1800-2017): +/// +/// - `0`: Prints nothing +/// - `1`: Prints simulation time and location (default) +/// - `2`: Prints simulation time, location, and statistics about the memory and +/// CPU time used in simulation +def VerbosityIntAttr : DefaultValuedAttr, IntMaxValue<2>]>, "1">; + +/// Commonalities between `StopOp` and `FinishOp`. +class FinishOrStopOp traits = []> : + SVOp { + let arguments = (ins VerbosityIntAttr:$verbosity); + let assemblyFormat = "$verbosity attr-dict"; + + // First line intentionally left blank. + string verbosityDescription = [{ + The optional `verbosity` parameter controls how much diagnostic information + is printed when the system task is executed (see section 20.2 of IEEE + 1800-2017): + + - `0`: Prints nothing + - `1`: Prints simulation time and location (default) + - `2`: Prints simulation time, location, and statistics about the memory and + CPU time used in simulation + }]; +} + +def StopOp : FinishOrStopOp<"stop"> { + let summary = "`$stop` system task"; + let description = [{ + Causes the simulation to be suspended. Does not terminate the simulator. + }] # verbosityDescription; +} + +def FinishOp : FinishOrStopOp<"finish"> { + let summary = "`$finish` system task"; + let description = [{ + Stops the simulation and exits/terminates the simulator process. In practice + most GUI-based simulators will show a prompt to the user offering them an + opportunity to not close the simulator altogether. + + Other tasks such as `$exit` or `$fatal` implicitly call this system task. + }] # verbosityDescription; +} + +def ExitOp : SVOp<"exit", [ProceduralOp]> { + let summary = "`$exit` system task"; + let description = [{ + Waits for all `program` blocks to complete and then makes an implicit call + to `$finish` with default verbosity (level 1) to conclude the simulation. + }]; + let assemblyFormat = "attr-dict"; +} + +//===----------------------------------------------------------------------===// +// Severity Message Tasks +//===----------------------------------------------------------------------===// + +def FatalOp : SVOp<"fatal", [ProceduralOp]> { + let summary = "`$fatal` severity message task"; + let description = [{ + Generates a run-time fatal error which terminates the simulation with an + error code. Makes an implicit call to `$finish`, forwarding the `verbosity` + operand. If present, the optional message is printed with any additional + operands interpolated into the message string. + }]; + let arguments = (ins + VerbosityIntAttr:$verbosity, + OptionalAttr:$message, Variadic:$operands); + let assemblyFormat = [{ + $verbosity + (`,` $message^ (`(` $operands^ `)` `:` type($operands))?)? + attr-dict + }]; + let builders = [ + OpBuilder<(ins CArg:$verbosity), + "build(odsBuilder, odsState, verbosity, StringAttr{}, ValueRange{});">, + OpBuilder<(ins VerbosityIntAttr.returnType:$verbosity, + "mlir::StringAttr":$message), + "build(odsBuilder, odsState, verbosity, message, ValueRange{});">, + ]; +} + +/// Commonalities between `ErrorOp`, `WarningOp`, and `InfoOp`. +class NonfatalMessageOp traits = []> : + SVOp { + string messageDescription = [{ + If present, the optional message is printed with any additional operands + interpolated into the message string. + }]; + let arguments = (ins + OptionalAttr:$message, Variadic:$operands); + let assemblyFormat = [{ + ($message^ (`(` $operands^ `)` `:` type($operands))?)? + attr-dict + }]; + let builders = [ + OpBuilder<(ins), + "build(odsBuilder, odsState, StringAttr{}, ValueRange{});">, + OpBuilder<(ins "mlir::StringAttr":$message), + "build(odsBuilder, odsState, message, ValueRange{});">, + ]; +} + +def ErrorOp : NonfatalMessageOp<"error"> { + let summary = "`$error` severity message task"; + let description = [{ + This system task indicates a run-time error. + }] # messageDescription; +} + +def WarningOp : NonfatalMessageOp<"warning"> { + let summary = "`$warning` severity message task"; + let description = [{ + This system task indicates a run-time warning. + }] # messageDescription; +} + +def InfoOp : NonfatalMessageOp<"info"> { + let summary = "`$info` severity message task"; + let description = [{ + This system task indicates a message with no specific severity. + }] # messageDescription; } diff --git a/include/circt/Dialect/SV/SVVisitors.h b/include/circt/Dialect/SV/SVVisitors.h index 130d756769..2ff56f09b1 100644 --- a/include/circt/Dialect/SV/SVVisitors.h +++ b/include/circt/Dialect/SV/SVVisitors.h @@ -37,7 +37,7 @@ public: AlwaysFFOp, InitialOp, CaseZOp, // Other Statements. AssignOp, BPAssignOp, PAssignOp, ForceOp, ReleaseOp, AliasOp, - FWriteOp, FatalOp, FinishOp, VerbatimOp, + FWriteOp, VerbatimOp, // Type declarations. InterfaceOp, InterfaceSignalOp, InterfaceModportOp, InterfaceInstanceOp, GetModportOp, AssignInterfaceSignalOp, @@ -46,7 +46,11 @@ public: AssertOp, AssumeOp, CoverOp, AssertConcurrentOp, AssumeConcurrentOp, CoverConcurrentOp, // Bind Statements - BindOp>([&](auto expr) -> ResultType { + BindOp, + // Simulator control tasks + StopOp, FinishOp, ExitOp, + // Severity message tasks + FatalOp, ErrorOp, WarningOp, InfoOp>([&](auto expr) -> ResultType { return thisCast->visitSV(expr, args...); }) .Default([&](auto expr) -> ResultType { @@ -103,8 +107,6 @@ public: HANDLE(ReleaseOp, Unhandled); HANDLE(AliasOp, Unhandled); HANDLE(FWriteOp, Unhandled); - HANDLE(FatalOp, Unhandled); - HANDLE(FinishOp, Unhandled); HANDLE(VerbatimOp, Unhandled); // Type declarations. @@ -126,6 +128,17 @@ public: // Bind statements. HANDLE(BindOp, Unhandled); + + // Simulator control tasks + HANDLE(StopOp, Unhandled); + HANDLE(FinishOp, Unhandled); + HANDLE(ExitOp, Unhandled); + + // Severity message tasks + HANDLE(FatalOp, Unhandled); + HANDLE(ErrorOp, Unhandled); + HANDLE(WarningOp, Unhandled); + HANDLE(InfoOp, Unhandled); #undef HANDLE }; diff --git a/lib/Conversion/ExportVerilog/ExportVerilog.cpp b/lib/Conversion/ExportVerilog/ExportVerilog.cpp index d61feae8a6..6a3926b47c 100644 --- a/lib/Conversion/ExportVerilog/ExportVerilog.cpp +++ b/lib/Conversion/ExportVerilog/ExportVerilog.cpp @@ -2075,10 +2075,23 @@ private: LogicalResult visitSV(InitialOp op); LogicalResult visitSV(CaseZOp op); LogicalResult visitSV(FWriteOp op); - LogicalResult visitSV(FatalOp op); - LogicalResult visitSV(FinishOp op); LogicalResult visitSV(VerbatimOp op); + LogicalResult emitSimulationControlTask(Operation *op, StringRef taskName, + Optional verbosity); + LogicalResult visitSV(StopOp op); + LogicalResult visitSV(FinishOp op); + LogicalResult visitSV(ExitOp op); + + LogicalResult emitSeverityMessageTask(Operation *op, StringRef taskName, + Optional verbosity, + StringAttr message, + ValueRange operands); + LogicalResult visitSV(FatalOp op); + LogicalResult visitSV(ErrorOp op); + LogicalResult visitSV(WarningOp op); + LogicalResult visitSV(InfoOp op); + void emitAssertionLabel(Operation *op, StringRef opName); void emitAssertionMessage(StringAttr message, ValueRange args, SmallPtrSet &ops); @@ -2367,14 +2380,6 @@ LogicalResult StmtEmitter::visitSV(FWriteOp op) { return success(); } -LogicalResult StmtEmitter::visitSV(FatalOp op) { - SmallPtrSet ops; - ops.insert(op); - indent() << "$fatal;"; - emitLocationInfoAndNewLine(ops); - return success(); -} - LogicalResult StmtEmitter::visitSV(VerbatimOp op) { SmallPtrSet ops; ops.insert(op); @@ -2416,14 +2421,95 @@ LogicalResult StmtEmitter::visitSV(VerbatimOp op) { return success(); } -LogicalResult StmtEmitter::visitSV(FinishOp op) { +/// Emit one of the simulation control tasks `$stop`, `$finish`, or `$exit`. +LogicalResult +StmtEmitter::emitSimulationControlTask(Operation *op, StringRef taskName, + Optional verbosity) { SmallPtrSet ops; ops.insert(op); - indent() << "$finish;"; + indent() << taskName; + if (verbosity && *verbosity != 1) + os << "(" << *verbosity << ")"; + os << ";"; emitLocationInfoAndNewLine(ops); return success(); } +LogicalResult StmtEmitter::visitSV(StopOp op) { + return emitSimulationControlTask(op, "$stop", op.verbosity()); +} + +LogicalResult StmtEmitter::visitSV(FinishOp op) { + return emitSimulationControlTask(op, "$finish", op.verbosity()); +} + +LogicalResult StmtEmitter::visitSV(ExitOp op) { + return emitSimulationControlTask(op, "$exit", {}); +} + +/// Emit one of the severity message tasks `$fatal`, `$error`, `$warning`, or +/// `$info`. +LogicalResult StmtEmitter::emitSeverityMessageTask(Operation *op, + StringRef taskName, + Optional verbosity, + StringAttr message, + ValueRange operands) { + SmallPtrSet ops; + ops.insert(op); + indent() << taskName; + + // In case we have a message to print, or the operation has an optional + // verbosity and that verbosity is present, print the parenthesized parameter + // list. + if ((verbosity && *verbosity != 1) || message) { + os << "("; + + // If the operation takes a verbosity, print it if it is set, or print the + // default "1". + if (verbosity) + os << *verbosity; + + // Print the message and interpolation operands if present. + if (message) { + if (verbosity) + os << ", "; + os << "\""; + os.write_escaped(message.getValue()); + os << "\""; + for (auto operand : operands) { + os << ", "; + emitExpression(operand, ops); + } + } + + os << ")"; + } + + os << ";"; + emitLocationInfoAndNewLine(ops); + return success(); +} + +LogicalResult StmtEmitter::visitSV(FatalOp op) { + return emitSeverityMessageTask(op, "$fatal", op.verbosity(), op.messageAttr(), + op.operands()); +} + +LogicalResult StmtEmitter::visitSV(ErrorOp op) { + return emitSeverityMessageTask(op, "$error", {}, op.messageAttr(), + op.operands()); +} + +LogicalResult StmtEmitter::visitSV(WarningOp op) { + return emitSeverityMessageTask(op, "$warning", {}, op.messageAttr(), + op.operands()); +} + +LogicalResult StmtEmitter::visitSV(InfoOp op) { + return emitSeverityMessageTask(op, "$info", {}, op.messageAttr(), + op.operands()); +} + /// Emit the `