[SV] Implement $fatal, $finish, lowering of firrtl::StopOp, and

EmitVerilog support for them.
This commit is contained in:
Chris Lattner 2020-09-07 09:55:18 -07:00
parent cdf0ac589f
commit a0fcf444f7
6 changed files with 145 additions and 22 deletions

View File

@ -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<AnyType>:$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 }];
}

View File

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

View File

@ -81,6 +81,7 @@ struct FIRRTLLowering : public LowerFIRRTLToRTLBase<FIRRTLLowering>,
// 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<sv::AlwaysAtPosEdgeOp>(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<sv::IfDefOp>(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<sv::TextualValueOp>(
op.getLoc(), cond.getType(), "PRINTF_COND_");
op.getLoc(), cond.getType(), "`PRINTF_COND_");
ifCond = builder->create<rtl::AndOp>(op.getLoc(), ValueRange{ifCond, cond},
ArrayRef<NamedAttribute>{});
auto svIf = builder->create<sv::IfOp>(op.getLoc(), ifCond);
@ -452,6 +452,41 @@ LogicalResult FIRRTLLowering::visitStmt(PrintFOp op) {
builder->create<sv::FWriteOp>(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<sv::AlwaysAtPosEdgeOp>(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<sv::IfDefOp>(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<sv::TextualValueOp>(
op.getLoc(), cond.getType(), "`STOP_COND_");
ifCond = builder->create<rtl::AndOp>(op.getLoc(), ValueRange{ifCond, cond},
ArrayRef<NamedAttribute>{});
auto svIf = builder->create<sv::IfOp>(op.getLoc(), ifCond);
// Emit the sv.fatal or sv.finish.
builder->setInsertionPointToStart(svIf.getBodyBlock());
if (op.exitCode().getLimitedValue())
builder->create<sv::FatalOp>(op.getLoc());
else
builder->create<sv::FinishOp>(op.getLoc());
builder->setInsertionPoint(oldIP);
return success();
}

View File

@ -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<Operation *, 8> ops;
ops.insert(op);
indent() << "$fatal;";
emitLocationInfoAndNewLine(ops);
}
void ModuleEmitter::emitStatement(sv::FinishOp op) {
SmallPtrSet<Operation *, 8> 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<sv::FWriteOp>(op);
return isa<sv::FWriteOp>(op) || isa<sv::FinishOp>(op) ||
isa<sv::FatalOp>(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; }

View File

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

View File

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