[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.
This commit is contained in:
Fabian Schuiki 2021-10-13 17:01:09 +02:00 committed by GitHub
parent 8c3cc01766
commit e4878fdef7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 355 additions and 68 deletions

View File

@ -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<Confined<I8Attr,
[IntMinValue<0>, IntMaxValue<2>]>, "1">;
/// Commonalities between `StopOp` and `FinishOp`.
class FinishOrStopOp<string mnemonic, list<OpTrait> traits = []> :
SVOp<mnemonic, [ProceduralOp] # traits> {
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<StrAttr>:$message, Variadic<AnyType>:$operands);
let assemblyFormat = [{
$verbosity
(`,` $message^ (`(` $operands^ `)` `:` type($operands))?)?
attr-dict
}];
let builders = [
OpBuilder<(ins CArg<VerbosityIntAttr.returnType,"1">:$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<string mnemonic, list<OpTrait> traits = []> :
SVOp<mnemonic, [ProceduralOp] # traits> {
string messageDescription = [{
If present, the optional message is printed with any additional operands
interpolated into the message string.
}];
let arguments = (ins
OptionalAttr<StrAttr>:$message, Variadic<AnyType>:$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;
}

View File

@ -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
};

View File

@ -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<unsigned> verbosity);
LogicalResult visitSV(StopOp op);
LogicalResult visitSV(FinishOp op);
LogicalResult visitSV(ExitOp op);
LogicalResult emitSeverityMessageTask(Operation *op, StringRef taskName,
Optional<unsigned> 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<Operation *, 8> &ops);
@ -2367,14 +2380,6 @@ LogicalResult StmtEmitter::visitSV(FWriteOp op) {
return success();
}
LogicalResult StmtEmitter::visitSV(FatalOp op) {
SmallPtrSet<Operation *, 8> ops;
ops.insert(op);
indent() << "$fatal;";
emitLocationInfoAndNewLine(ops);
return success();
}
LogicalResult StmtEmitter::visitSV(VerbatimOp op) {
SmallPtrSet<Operation *, 8> 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<unsigned> verbosity) {
SmallPtrSet<Operation *, 8> 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<unsigned> verbosity,
StringAttr message,
ValueRange operands) {
SmallPtrSet<Operation *, 8> 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 `<label>:` portion of an immediate or concurrent verification
/// operation. If a label has been stored for the operation through
/// `addLegalName` in the pre-pass, that label is used. Otherwise, if the

View File

@ -173,6 +173,48 @@ hw.module @test1(%arg0: i1, %arg1: i1, %arg8: i8) {
%reg24 = sv.reg sym @regSym1 : !hw.inout<i23>
%wire25 = sv.wire sym @wireSym1 : !hw.inout<i23>
// Simulation Control Tasks
// CHECK-NEXT: sv.initial {
// CHECK-NEXT: sv.stop 1
// CHECK-NEXT: sv.finish 1
// CHECK-NEXT: sv.exit
// CHECK-NEXT: }
sv.initial {
sv.stop 1
sv.finish 1
sv.exit
}
// Severity Message Tasks
// CHECK-NEXT: sv.initial {
// CHECK-NEXT: sv.fatal 1
// CHECK-NEXT: sv.fatal 1, "hello"
// CHECK-NEXT: sv.fatal 1, "hello %d"(%arg0) : i1
// CHECK-NEXT: sv.error
// CHECK-NEXT: sv.error "hello"
// CHECK-NEXT: sv.error "hello %d"(%arg0) : i1
// CHECK-NEXT: sv.warning
// CHECK-NEXT: sv.warning "hello"
// CHECK-NEXT: sv.warning "hello %d"(%arg0) : i1
// CHECK-NEXT: sv.info
// CHECK-NEXT: sv.info "hello"
// CHECK-NEXT: sv.info "hello %d"(%arg0) : i1
// CHECK-NEXT: }
sv.initial {
sv.fatal 1
sv.fatal 1, "hello"
sv.fatal 1, "hello %d"(%arg0) : i1
sv.error
sv.error "hello"
sv.error "hello %d"(%arg0) : i1
sv.warning
sv.warning "hello"
sv.warning "hello %d"(%arg0) : i1
sv.info
sv.info "hello"
sv.info "hello %d"(%arg0) : i1
}
// CHECK-NEXT: hw.output
hw.output
}

View File

@ -77,13 +77,13 @@ hw.module @IfOp(%arg0: i1) {
// -----
hw.module @Fatal() {
// expected-error @+1 {{sv.fatal should be in a procedural region}}
sv.fatal
sv.fatal 1
}
// -----
hw.module @Fatal() {
hw.module @Finish() {
// expected-error @+1 {{sv.finish should be in a procedural region}}
sv.finish
sv.finish 1
}
// -----

View File

@ -78,7 +78,7 @@ hw.module @array_create_get_default(%arg0: i8, %arg1: i8, %arg2: i8, %arg3: i8,
// CHECK: sv.if %1 {
%cond = comb.icmp eq %2, %arg2 : i8
sv.if %cond {
sv.fatal
sv.fatal 1
}
}
}

View File

@ -27,7 +27,7 @@ hw.module @unary_ops(%arg0: i8, %arg1: i8, %arg2: i8, %arg3: i1)
// CHECK: [[XOR3:%.+]] = comb.xor %arg3, [[TRUE2]]
// CHECK: sv.if [[XOR3]]
sv.if %c {
sv.fatal
sv.fatal 1
}
}
@ -124,17 +124,17 @@ hw.module @sink_expression(%clock: i1, %a: i1, %a2: i1, %a3: i1, %a4: i1) {
// CHECK: [[OR:%.*]] = comb.or %a2, %a3 : i1
// CHECK: sv.if [[OR]]
sv.if %0 {
sv.fatal
sv.fatal 1
}
// CHECK: sv.if [[XOR]]
sv.if %2 {
sv.fatal
sv.fatal 1
}
}
// CHECK: sv.if [[XOR]]
sv.if %2 {
sv.fatal
sv.fatal 1
}
}
hw.output
@ -152,12 +152,12 @@ hw.module @dont_sink_se_expression(%clock: i1, %a: i1, %a2: i1, %a3: i1, %a4: i1
// CHECK: [[SINK:%.*]] = sv.verbatim.expr "SINK_ME"
// CHECK: sv.if [[SINK]]
sv.if %0 {
sv.fatal
sv.fatal 1
}
// CHECK: sv.if [[DONT_TOUCH]]
sv.if %1 {
sv.fatal
sv.fatal 1
}
}
hw.output

View File

@ -21,7 +21,7 @@ hw.module @side_effect_expr(%clock: i1) -> (a: i1, a2: i1) {
// CHECK: if (INLINE_OK)
// DISALLOW: if ([[COND]])
sv.if %0 {
sv.fatal
sv.fatal 1
}
// This should go through a reg when in "disallow" mode.
@ -30,7 +30,7 @@ hw.module @side_effect_expr(%clock: i1) -> (a: i1, a2: i1) {
// DISALLOW: if ([[SE_REG]])
%1 = sv.verbatim.expr.se "SIDE_EFFECT" : () -> i1
sv.if %1 {
sv.fatal
sv.fatal 1
}
}
@ -66,7 +66,7 @@ hw.module @hoist_expressions(%clock: i1, %x: i8, %y: i8, %z: i8) {
// DISALLOW: $fwrite(32'h80000002, "Hi %x\n", [[MUL]]);
%2 = comb.mul %0, %z : i8
sv.fwrite "Hi %x\0A"(%2) : i8
sv.fatal
sv.fatal 1
}
}
@ -87,7 +87,7 @@ hw.module @hoist_expressions(%clock: i1, %x: i8, %y: i8, %z: i8) {
// CHECK: if (x + myWire == z)
// DISALLOW: if ([[COND]])
sv.if %4 {
sv.fatal
sv.fatal 1
}
}

View File

@ -174,10 +174,49 @@ hw.module @M1<param1: i42>(%clock : i1, %cond : i1, %val : i8) {
// CHECK-NEXT: cover_0: cover(cond);
sv.cover %cond, immediate label "cover_0"
// CHECK-NEXT: $fatal
sv.fatal
// CHECK-NEXT: $finish
sv.finish
// Simulator Control Tasks
// CHECK-NEXT: $stop;
// CHECK-NEXT: $stop(0);
sv.stop 1
sv.stop 0
// CHECK-NEXT: $finish;
// CHECK-NEXT: $finish(0);
sv.finish 1
sv.finish 0
// CHECK-NEXT: $exit;
sv.exit
// Severity Message Tasks
// CHECK-NEXT: $fatal;
// CHECK-NEXT: $fatal(1, "foo");
// CHECK-NEXT: $fatal(1, "foo", val);
// CHECK-NEXT: $fatal(0);
// CHECK-NEXT: $fatal(0, "foo");
// CHECK-NEXT: $fatal(0, "foo", val);
sv.fatal 1
sv.fatal 1, "foo"
sv.fatal 1, "foo"(%val) : i8
sv.fatal 0
sv.fatal 0, "foo"
sv.fatal 0, "foo"(%val) : i8
// CHECK-NEXT: $error;
// CHECK-NEXT: $error("foo");
// CHECK-NEXT: $error("foo", val);
sv.error
sv.error "foo"
sv.error "foo"(%val) : i8
// CHECK-NEXT: $warning;
// CHECK-NEXT: $warning("foo");
// CHECK-NEXT: $warning("foo", val);
sv.warning
sv.warning "foo"
sv.warning "foo"(%val) : i8
// CHECK-NEXT: $info;
// CHECK-NEXT: $info("foo");
// CHECK-NEXT: $info("foo", val);
sv.info
sv.info "foo"
sv.info "foo"(%val) : i8
// CHECK-NEXT: Emit some stuff in verilog
// CHECK-NEXT: Great power and responsibility!
@ -221,7 +260,7 @@ hw.module @M1<param1: i42>(%clock : i1, %cond : i1, %val : i8) {
// CHECK-NOT: begin
sv.initial {
// CHECK-NEXT: $fatal
sv.fatal
sv.fatal 1
}
// CHECK-NEXT: initial begin
@ -505,7 +544,7 @@ hw.module @issue720(%clock: i1, %arg1: i1, %arg2: i1, %arg3: i1) {
// CHECK: if (arg1)
// CHECK: $fatal;
sv.if %arg1 {
sv.fatal
sv.fatal 1
}
// CHECK: if (_T)
@ -515,13 +554,13 @@ hw.module @issue720(%clock: i1, %arg1: i1, %arg2: i1, %arg3: i1) {
%610 = comb.and %arg1, %arg2 : i1
%611 = comb.and %arg3, %610 : i1
sv.if %610 {
sv.fatal
sv.fatal 1
}
// CHECK: if (arg3 & _T)
// CHECK: $fatal;
sv.if %611 {
sv.fatal
sv.fatal 1
}
} // CHECK: end // always @(posedge)
@ -539,7 +578,7 @@ hw.module @issue720ifdef(%clock: i1, %arg1: i1, %arg2: i1, %arg3: i1) {
// CHECK: if (arg1)
// CHECK: $fatal;
sv.if %arg1 {
sv.fatal
sv.fatal 1
}
// CHECK: `ifdef FUN_AND_GAMES
@ -550,13 +589,13 @@ hw.module @issue720ifdef(%clock: i1, %arg1: i1, %arg2: i1, %arg3: i1) {
// CHECK: $fatal;
%610 = comb.and %arg1, %arg2 : i1
sv.if %610 {
sv.fatal
sv.fatal 1
}
// CHECK: if (arg3 & _T)
// CHECK: $fatal;
%611 = comb.and %arg3, %610 : i1
sv.if %611 {
sv.fatal
sv.fatal 1
}
// CHECK: `endif
// CHECK: end // always @(posedge)

View File

@ -348,7 +348,7 @@ hw.module @Stop(%clock: i1, %reset: i1) {
%0 = sv.verbatim.expr "`STOP_COND_" : () -> i1
%1 = comb.and %0, %reset : i1
sv.if %1 {
sv.fatal
sv.fatal 1
}
}
}