18 #include "mlir/Dialect/Func/IR/FuncOps.h"
19 #include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
20 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
21 #include "mlir/IR/Builders.h"
22 #include "mlir/IR/Location.h"
23 #include "mlir/IR/SymbolTable.h"
24 #include "llvm/Support/LogicalResult.h"
27 using namespace circt;
31 #define GEN_PASS_DEF_LOWERTOBMC
32 #include "circt/Tools/circt-bmc/Passes.h.inc"
40 struct LowerToBMCPass :
public circt::impl::LowerToBMCBase<LowerToBMCPass> {
41 using LowerToBMCBase::LowerToBMCBase;
42 void runOnOperation()
override;
46 void LowerToBMCPass::runOnOperation() {
49 auto moduleOp = getOperation();
52 moduleOp.emitError(
"hw.module named '") << topModule <<
"' not found";
53 return signalPassFailure();
57 if (hwModule.getOps<verif::AssertOp>().empty() &&
58 hwModule.getOps<hw::InstanceOp>().empty()) {
59 hwModule.emitError(
"no property provided to check in module");
60 return signalPassFailure();
64 auto *ctx = &getContext();
65 OpBuilder builder(ctx);
66 Location loc = moduleOp->getLoc();
67 builder.setInsertionPointToEnd(moduleOp.getBody());
73 LLVM::lookupOrCreateFn(moduleOp,
"printf", ptrTy, voidTy,
true);
76 auto entryFunc = builder.create<func::FuncOp>(
77 loc, topModule, builder.getFunctionType({}, {}));
78 builder.createBlock(&entryFunc.getBody());
81 OpBuilder::InsertionGuard guard(builder);
82 auto *terminator = hwModule.getBody().front().getTerminator();
83 builder.setInsertionPoint(terminator);
84 builder.create<verif::YieldOp>(loc, terminator->getOperands());
90 verif::BoundedModelCheckingOp bmcOp;
91 auto numRegs = hwModule->getAttrOfType<IntegerAttr>(
"num_regs");
92 auto initialValues = hwModule->getAttrOfType<ArrayAttr>(
"initial_values");
93 if (numRegs && initialValues) {
94 for (
auto value : initialValues) {
95 if (!isa<IntegerAttr, UnitAttr>(value)) {
96 hwModule->emitError(
"initial_values attribute must contain only "
97 "integer or unit attributes");
98 return signalPassFailure();
101 bmcOp = builder.create<verif::BoundedModelCheckingOp>(
102 loc, 2 * bound, cast<IntegerAttr>(numRegs).getValue().getZExtValue(),
105 hwModule->emitOpError(
"no num_regs or initial_values attribute found - "
106 "please run externalize "
107 "registers pass first");
108 return signalPassFailure();
115 for (
auto input : hwModule.getInputTypes()) {
116 if (isa<seq::ClockType>(input)) {
118 hwModule.emitError(
"designs with multiple clocks not yet supported");
119 return signalPassFailure();
123 if (
auto hwStruct = dyn_cast<hw::StructType>(input)) {
124 for (
auto field : hwStruct.getElements()) {
125 if (isa<seq::ClockType>(field.type)) {
128 "designs with multiple clocks not yet supported");
129 return signalPassFailure();
137 OpBuilder::InsertionGuard guard(builder);
139 auto *initBlock = builder.createBlock(&bmcOp.getInit());
140 builder.setInsertionPointToStart(initBlock);
144 auto toClk = builder.
create<seq::ToClockOp>(loc, initVal);
145 builder.create<verif::YieldOp>(loc, ValueRange{toClk});
147 builder.create<verif::YieldOp>(loc, ValueRange{});
151 auto *loopBlock = builder.createBlock(&bmcOp.getLoop());
152 builder.setInsertionPointToStart(loopBlock);
156 builder.create<seq::FromClockOp>(loc, loopBlock->getArgument(0));
157 auto cNeg1 = builder.create<
hw::ConstantOp>(loc, builder.getI1Type(), -1);
159 auto toClk = builder.create<seq::ToClockOp>(loc, nClk);
161 builder.create<verif::YieldOp>(loc, ValueRange{toClk});
163 builder.create<verif::YieldOp>(loc, ValueRange{});
166 bmcOp.getCircuit().takeBody(hwModule.getBody());
170 auto createUniqueStringGlobal = [&](StringRef str) -> FailureOr<Value> {
171 Location loc = moduleOp.getLoc();
173 OpBuilder b = OpBuilder::atBlockEnd(moduleOp.getBody());
175 auto global = b.create<LLVM::GlobalOp>(
176 loc, arrayTy,
true, LLVM::linkage::Linkage::Private,
179 SymbolTable symTable(moduleOp);
180 if (failed(symTable.renameToUnique(global, {&symTable}))) {
181 return mlir::failure();
185 builder.create<LLVM::AddressOfOp>(loc, global)->getResult(0));
188 auto successStrAddr =
189 createUniqueStringGlobal(
"Bound reached with no violations!\n");
190 auto failureStrAddr =
191 createUniqueStringGlobal(
"Assertion can be violated!\n");
193 if (failed(successStrAddr) || failed(failureStrAddr)) {
194 moduleOp->emitOpError(
"could not create result message strings");
195 return signalPassFailure();
198 auto formatString = builder.create<LLVM::SelectOp>(
199 loc, bmcOp.getResult(), successStrAddr.value(), failureStrAddr.value());
200 builder.create<LLVM::CallOp>(loc, printfFunc, ValueRange{formatString});
201 builder.create<func::ReturnOp>(loc);
203 if (insertMainFunc) {
204 builder.setInsertionPointToEnd(getOperation().getBody());
205 Type i32Ty = builder.getI32Type();
206 auto mainFunc = builder.create<func::FuncOp>(
207 loc,
"main", builder.getFunctionType({i32Ty, ptrTy}, {i32Ty}));
208 builder.createBlock(&mainFunc.getBody(), {}, {i32Ty, ptrTy}, {loc, loc});
209 builder.create<func::CallOp>(loc, entryFunc, ValueRange{});
211 Value constZero = builder.create<LLVM::ConstantOp>(loc, i32Ty, 0);
212 builder.create<func::ReturnOp>(loc, constZero);
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
A namespace that is used to store existing names and generate new names in some scope within the IR.
def create(data_type, value)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.