mirror of https://github.com/llvm/circt.git
[SV] Implement $fatal, $finish, lowering of firrtl::StopOp, and
EmitVerilog support for them.
This commit is contained in:
parent
cdf0ac589f
commit
a0fcf444f7
|
@ -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 }];
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue