From a0fcf444f7ca733daeb6af9ac11d081e16a6d48c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 7 Sep 2020 09:55:18 -0700 Subject: [PATCH] [SV] Implement $fatal, $finish, lowering of firrtl::StopOp, and EmitVerilog support for them. --- include/circt/Dialect/SV/Statements.td | 26 ++++++++++++++- include/circt/Dialect/SV/Visitors.h | 9 ++++-- lib/Dialect/FIRRTL/LowerToRTL.cpp | 45 +++++++++++++++++++++++--- lib/EmitVerilog/EmitVerilog.cpp | 21 +++++++++++- test/EmitVerilog/sv-dialect.mlir | 26 ++++++++------- test/firrtl/lower-to-rtl.mlir | 40 ++++++++++++++++++++++- 6 files changed, 145 insertions(+), 22 deletions(-) diff --git a/include/circt/Dialect/SV/Statements.td b/include/circt/Dialect/SV/Statements.td index 9f30a2764c..a705f34918 100644 --- a/include/circt/Dialect/SV/Statements.td +++ b/include/circt/Dialect/SV/Statements.td @@ -108,7 +108,7 @@ def YieldOp // TODO: This needs to model the file descriptor to write on. This is currently // hard coded to 32'h80000002. -def FWriteOp : SVOp<"fwrite", []> { +def FWriteOp : SVOp<"fwrite"> { let summary = "'$fwrite' statement"; let arguments = (ins StrAttr:$string, Variadic:$operands); @@ -118,3 +118,27 @@ def FWriteOp : SVOp<"fwrite", []> { $string attr-dict (`(` $operands^ `)` `:` type($operands))? }]; } + +def FinishOp : SVOp<"finish"> { + 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"> { + let summary = "'$fatal' statement"; + let description = [{ + Run-time fatal assertion error. + }]; + + let arguments = (ins); + let results = (outs); + + let assemblyFormat = [{ attr-dict }]; +} diff --git a/include/circt/Dialect/SV/Visitors.h b/include/circt/Dialect/SV/Visitors.h index 0f18d664db..19171bb302 100644 --- a/include/circt/Dialect/SV/Visitors.h +++ b/include/circt/Dialect/SV/Visitors.h @@ -24,9 +24,10 @@ public: // Control flow. IfDefOp, IfOp, AlwaysAtPosEdgeOp, // Other Statements. - YieldOp, FWriteOp>([&](auto expr) -> ResultType { - return thisCast->visitSV(expr, args...); - }) + YieldOp, FWriteOp, FatalOp, FinishOp>( + [&](auto expr) -> ResultType { + return thisCast->visitSV(expr, args...); + }) .Default([&](auto expr) -> ResultType { return thisCast->visitInvalidSV(op, args...); }); @@ -60,6 +61,8 @@ public: // Other Statements. HANDLE(YieldOp, Unhandled); HANDLE(FWriteOp, Unhandled); + HANDLE(FatalOp, Unhandled); + HANDLE(FinishOp, Unhandled); #undef HANDLE }; diff --git a/lib/Dialect/FIRRTL/LowerToRTL.cpp b/lib/Dialect/FIRRTL/LowerToRTL.cpp index 185bee8bb8..6e22a41681 100644 --- a/lib/Dialect/FIRRTL/LowerToRTL.cpp +++ b/lib/Dialect/FIRRTL/LowerToRTL.cpp @@ -81,6 +81,7 @@ struct FIRRTLLowering : public LowerFIRRTLToRTLBase, // Statements LogicalResult visitStmt(ConnectOp op); LogicalResult visitStmt(PrintFOp op); + LogicalResult visitStmt(StopOp op); private: /// This builder is set to the right location for each visit call. @@ -427,18 +428,17 @@ LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) { auto always = builder->create(op.getLoc(), clock); // We're going to move insertion points. - auto oldIPBlock = builder->getInsertionBlock(); - auto oldIP = builder->getInsertionPoint(); + auto oldIP = &*builder->getInsertionPoint(); // Emit an "#ifndef SYNTHESIS" guard into the always block. builder->setInsertionPointToStart(always.getBody()); auto ifndef = builder->create(op.getLoc(), "!SYNTHESIS"); - // Emit an "sv.if 'PRINTF_COND_ & cond' into the #ifndef. + // Emit an "sv.if '`PRINTF_COND_ & cond' into the #ifndef. builder->setInsertionPointToStart(ifndef.getBodyBlock()); auto cond = getLoweredValue(op.cond()); Value ifCond = builder->create( - op.getLoc(), cond.getType(), "PRINTF_COND_"); + op.getLoc(), cond.getType(), "`PRINTF_COND_"); ifCond = builder->create(op.getLoc(), ValueRange{ifCond, cond}, ArrayRef{}); auto svIf = builder->create(op.getLoc(), ifCond); @@ -452,6 +452,41 @@ LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) { builder->create(op.getLoc(), op.formatString(), operands); - builder->setInsertionPoint(oldIPBlock, oldIP); + builder->setInsertionPoint(oldIP); return success(); } + +// Stop lowers into a nested series of behavioral statements plus $fatal or +// $finish. +LogicalResult FIRRTLLowering::visitStmt(StopOp op) { + // Emit this into an "sv.alwaysat_posedge" body. + auto clock = getLoweredValue(op.clock()); + auto always = builder->create(op.getLoc(), clock); + + // We're going to move insertion points. + auto oldIP = &*builder->getInsertionPoint(); + + // Emit an "#ifndef SYNTHESIS" guard into the always block. + builder->setInsertionPointToStart(always.getBody()); + auto ifndef = builder->create(op.getLoc(), "!SYNTHESIS"); + + // Emit an "sv.if '`STOP_COND_ & cond' into the #ifndef. + builder->setInsertionPointToStart(ifndef.getBodyBlock()); + auto cond = getLoweredValue(op.cond()); + Value ifCond = builder->create( + op.getLoc(), cond.getType(), "`STOP_COND_"); + ifCond = builder->create(op.getLoc(), ValueRange{ifCond, cond}, + ArrayRef{}); + auto svIf = builder->create(op.getLoc(), ifCond); + + // Emit the sv.fatal or sv.finish. + builder->setInsertionPointToStart(svIf.getBodyBlock()); + + if (op.exitCode().getLimitedValue()) + builder->create(op.getLoc()); + else + builder->create(op.getLoc()); + + builder->setInsertionPoint(oldIP); + return success(); +} \ No newline at end of file diff --git a/lib/EmitVerilog/EmitVerilog.cpp b/lib/EmitVerilog/EmitVerilog.cpp index 361a79c70d..508d264394 100644 --- a/lib/EmitVerilog/EmitVerilog.cpp +++ b/lib/EmitVerilog/EmitVerilog.cpp @@ -309,6 +309,8 @@ public: void emitStatement(sv::IfOp op); void emitStatement(sv::AlwaysAtPosEdgeOp op); void emitStatement(sv::FWriteOp op); + void emitStatement(sv::FatalOp op); + void emitStatement(sv::FinishOp op); void emitDecl(NodeOp op); void emitDecl(InstanceOp op); void emitDecl(RegOp op); @@ -1428,6 +1430,20 @@ void ModuleEmitter::emitStatement(sv::FWriteOp op) { emitLocationInfoAndNewLine(ops); } +void ModuleEmitter::emitStatement(sv::FatalOp op) { + SmallPtrSet ops; + ops.insert(op); + indent() << "$fatal;"; + emitLocationInfoAndNewLine(ops); +} + +void ModuleEmitter::emitStatement(sv::FinishOp op) { + SmallPtrSet ops; + ops.insert(op); + indent() << "$finish;"; + emitLocationInfoAndNewLine(ops); +} + void ModuleEmitter::emitStatement(sv::IfDefOp op) { auto cond = op.cond(); @@ -1460,7 +1476,8 @@ static void emitBeginEndRegion(Block *block, // Not all expressions and statements are guaranteed to emit a single // Verilog statement (for the purposes of if statements). Just do a simple // check here for now. This can be improved over time. - return isa(op); + return isa(op) || isa(op) || + isa(op); }; // Determine if we can omit the begin/end keywords. @@ -2036,6 +2053,8 @@ void ModuleEmitter::emitOperation(Operation *op) { return emitter.emitStatement(op), true; } bool visitSV(sv::FWriteOp op) { return emitter.emitStatement(op), true; } + bool visitSV(sv::FatalOp op) { return emitter.emitStatement(op), true; } + bool visitSV(sv::FinishOp op) { return emitter.emitStatement(op), true; } bool visitUnhandledSV(Operation *op) { return false; } bool visitInvalidSV(Operation *op) { return false; } diff --git a/test/EmitVerilog/sv-dialect.mlir b/test/EmitVerilog/sv-dialect.mlir index c8ea3245a6..926617e0d9 100644 --- a/test/EmitVerilog/sv-dialect.mlir +++ b/test/EmitVerilog/sv-dialect.mlir @@ -4,6 +4,12 @@ firrtl.circuit "Circuit" { // CHECK-LABEL: module M1( firrtl.module @M1(%clock : i1, %cond : i1, %val : i8) { + // CHECK: always @(posedge clock) begin + // CHECK-NEXT: #ifndef SYNTHESIS + // CHECK-NEXT: if (PRINTF_COND_ & cond) + // CHECK-NEXT: $fwrite(32'h80000002, "Hi\n"); + // CHECK-NEXT: #endif + // CHECK-NEXT: end // always @(posedge) sv.alwaysat_posedge %clock { sv.ifdef "!SYNTHESIS" { %tmp = sv.textual_value "PRINTF_COND_" : i1 @@ -14,23 +20,21 @@ firrtl.circuit "Circuit" { } } - // CHECK: always @(posedge clock) begin - // CHECK-NEXT: #ifndef SYNTHESIS - // CHECK-NEXT: if (PRINTF_COND_ & cond) - // CHECK-NEXT: $fwrite(32'h80000002, "Hi\n"); - // CHECK-NEXT: #endif - // CHECK-NEXT: end // always @(posedge) + // CHECK-NEXT: if (cond) begin sv.if %cond { + // CHECK-NEXT: $fwrite(32'h80000002, "Hi\n"); sv.fwrite "Hi\n" + // CHECK-NEXT: $fwrite(32'h80000002, "Bye %x\n", val + val); %tmp = rtl.add %val, %val : i8 sv.fwrite "Bye %x\n"(%tmp) : i8 - } - // CHECK-NEXT: if (cond) begin - // CHECK-NEXT: $fwrite(32'h80000002, "Hi\n"); - // CHECK-NEXT: $fwrite(32'h80000002, "Bye %x\n", val + val); - // CHECK-NEXT: end + // CHECK-NEXT: $fatal + sv.fatal + // CHECK-NEXT: $finish + sv.finish + // CHECK-NEXT: end + } } } diff --git a/test/firrtl/lower-to-rtl.mlir b/test/firrtl/lower-to-rtl.mlir index eae878c5d4..832c63702e 100644 --- a/test/firrtl/lower-to-rtl.mlir +++ b/test/firrtl/lower-to-rtl.mlir @@ -112,7 +112,7 @@ // CHECK-NEXT: sv.alwaysat_posedge [[CL]] { // CHECK-NEXT: sv.ifdef "!SYNTHESIS" { // CHECK-NEXT: [[R:%.+]] = firrtl.stdIntCast %reset : (!firrtl.uint<1>) -> i1 - // CHECK-NEXT: [[TV:%.+]] = sv.textual_value "PRINTF_COND_" : i1 + // CHECK-NEXT: [[TV:%.+]] = sv.textual_value "`PRINTF_COND_" : i1 // CHECK-NEXT: [[AND:%.+]] = rtl.and [[TV]], [[R]] // CHECK-NEXT: sv.if [[AND]] { // CHECK-NEXT: sv.fwrite "No operands!\0A" @@ -127,4 +127,42 @@ // CHECK: sv.fwrite "Hi %x %x\0A"({{.*}}) : i5, i4 firrtl.printf %clock, %reset, "Hi %x %x\0A"(%0, %b) : !firrtl.uint<5>, !firrtl.uint<4> } + + + +// module Stop3 : +// input clock1: Clock +// input clock2: Clock +// input reset: UInt<1> +// stop(clock1, reset, 42) +// stop(clock2, reset, 0) + + // CHECK-LABEL: firrtl.module @Stop + firrtl.module @Stop(%clock1: !firrtl.clock, %clock2: !firrtl.clock, %reset: !firrtl.uint<1>) { + // CHECK-NEXT: %0 = firrtl.stdIntCast %clock1 : (!firrtl.clock) -> i1 + // CHECK-NEXT: sv.alwaysat_posedge %0 { + // CHECK-NEXT: sv.ifdef "!SYNTHESIS" { + // CHECK-NEXT: %2 = firrtl.stdIntCast %reset : (!firrtl.uint<1>) -> i1 + // CHECK-NEXT: %3 = sv.textual_value "`STOP_COND_" : i1 + // CHECK-NEXT: %4 = rtl.and %3, %2 : i1 + // CHECK-NEXT: sv.if %4 { + // CHECK-NEXT: sv.fatal + // CHECK-NEXT: } + // CHECK-NEXT: } + // CHECK-NEXT: } + firrtl.stop %clock1, %reset, 42 + + // CHECK-NEXT: %1 = firrtl.stdIntCast %clock2 : (!firrtl.clock) -> i1 + // CHECK-NEXT: sv.alwaysat_posedge %1 { + // CHECK-NEXT: sv.ifdef "!SYNTHESIS" { + // CHECK-NEXT: %2 = firrtl.stdIntCast %reset : (!firrtl.uint<1>) -> i1 + // CHECK-NEXT: %3 = sv.textual_value "`STOP_COND_" : i1 + // CHECK-NEXT: %4 = rtl.and %3, %2 : i1 + // CHECK-NEXT: sv.if %4 { + // CHECK-NEXT: sv.finish + // CHECK-NEXT: } + // CHECK-NEXT: } + // CHECK-NEXT: } + firrtl.stop %clock2, %reset, 0 + } }