18#include "mlir/Analysis/TopologicalSortUtils.h"
19#include "mlir/Dialect/Func/IR/FuncOps.h"
20#include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
21#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
22#include "mlir/IR/Builders.h"
23#include "mlir/IR/Location.h"
24#include "mlir/IR/SymbolTable.h"
25#include "llvm/Support/LogicalResult.h"
32#define GEN_PASS_DEF_LOWERTOBMC
33#include "circt/Tools/circt-bmc/Passes.h.inc"
41struct LowerToBMCPass :
public circt::impl::LowerToBMCBase<LowerToBMCPass> {
42 using LowerToBMCBase::LowerToBMCBase;
43 void runOnOperation()
override;
47void LowerToBMCPass::runOnOperation() {
50 auto moduleOp = getOperation();
53 moduleOp.emitError(
"hw.module named '") << topModule <<
"' not found";
54 return signalPassFailure();
57 if (!sortTopologically(&hwModule.getBodyRegion().front())) {
58 hwModule->emitError(
"could not resolve cycles in module");
59 return signalPassFailure();
62 if (bound < ignoreAssertionsUntil) {
64 "number of ignored cycles must be less than or equal to bound");
65 return signalPassFailure();
69 auto *ctx = &getContext();
70 OpBuilder builder(ctx);
71 Location loc = moduleOp->getLoc();
72 builder.setInsertionPointToEnd(moduleOp.getBody());
73 auto ptrTy = LLVM::LLVMPointerType::get(ctx);
74 auto voidTy = LLVM::LLVMVoidType::get(ctx);
78 LLVM::lookupOrCreateFn(builder, moduleOp,
"printf", ptrTy, voidTy,
true);
79 if (failed(printfFunc)) {
80 moduleOp->emitError(
"failed to lookup or create printf");
81 return signalPassFailure();
85 auto entryFunc = func::FuncOp::create(builder, loc, topModule,
86 builder.getFunctionType({}, {}));
87 builder.createBlock(&entryFunc.getBody());
90 OpBuilder::InsertionGuard guard(builder);
91 auto *terminator = hwModule.getBody().front().getTerminator();
92 builder.setInsertionPoint(terminator);
93 verif::YieldOp::create(builder, loc, terminator->getOperands());
99 verif::BoundedModelCheckingOp bmcOp;
100 auto numRegs = hwModule->getAttrOfType<IntegerAttr>(
"num_regs");
101 auto initialValues = hwModule->getAttrOfType<ArrayAttr>(
"initial_values");
102 if (numRegs && initialValues) {
103 for (
auto value : initialValues) {
104 if (!isa<IntegerAttr, UnitAttr>(value)) {
105 hwModule->emitError(
"initial_values attribute must contain only "
106 "integer or unit attributes");
107 return signalPassFailure();
110 bmcOp = verif::BoundedModelCheckingOp::create(
111 builder, loc, risingClocksOnly ? bound : 2 * bound,
112 cast<IntegerAttr>(numRegs).getValue().getZExtValue(), initialValues);
115 if (ignoreAssertionsUntil)
116 bmcOp->setAttr(
"ignore_asserts_until",
117 builder.getI32IntegerAttr(
118 risingClocksOnly ? ignoreAssertionsUntil
119 : 2 * ignoreAssertionsUntil));
121 hwModule->emitOpError(
"no num_regs or initial_values attribute found - "
122 "please run externalize "
123 "registers pass first");
124 return signalPassFailure();
131 for (
auto input : hwModule.getInputTypes()) {
132 if (isa<seq::ClockType>(input)) {
134 hwModule.emitError(
"designs with multiple clocks not yet supported");
135 return signalPassFailure();
139 if (
auto hwStruct = dyn_cast<hw::StructType>(input)) {
140 for (
auto field : hwStruct.getElements()) {
141 if (isa<seq::ClockType>(field.type)) {
144 "designs with multiple clocks not yet supported");
145 return signalPassFailure();
153 OpBuilder::InsertionGuard guard(builder);
156 auto *initBlock = builder.createBlock(&bmcOp.getInit());
157 builder.setInsertionPointToStart(initBlock);
160 risingClocksOnly ? 1 : 0);
161 auto toClk = seq::ToClockOp::create(builder, loc, initVal);
162 verif::YieldOp::create(builder, loc, ValueRange{toClk});
164 verif::YieldOp::create(builder, loc, ValueRange{});
168 auto *loopBlock = builder.createBlock(&bmcOp.getLoop());
169 builder.setInsertionPointToStart(loopBlock);
171 loopBlock->addArgument(seq::ClockType::get(ctx), loc);
172 if (risingClocksOnly) {
174 verif::YieldOp::create(builder, loc,
175 ValueRange{loopBlock->getArgument(0)});
178 seq::FromClockOp::create(builder, loc, loopBlock->getArgument(0));
181 auto nClk = comb::XorOp::create(builder, loc, fromClk, cNeg1);
182 auto toClk = seq::ToClockOp::create(builder, loc, nClk);
184 verif::YieldOp::create(builder, loc, ValueRange{toClk});
187 verif::YieldOp::create(builder, loc, ValueRange{});
190 bmcOp.getCircuit().takeBody(hwModule.getBody());
194 auto createUniqueStringGlobal = [&](StringRef str) -> FailureOr<Value> {
195 Location loc = moduleOp.getLoc();
197 OpBuilder b = OpBuilder::atBlockEnd(moduleOp.getBody());
198 auto arrayTy = LLVM::LLVMArrayType::get(b.getI8Type(), str.size() + 1);
199 auto global = LLVM::GlobalOp::create(
200 b, loc, arrayTy,
true, LLVM::linkage::Linkage::Private,
202 StringAttr::get(b.getContext(), Twine(str).
concat(Twine(
'\00'))));
203 SymbolTable symTable(moduleOp);
204 if (failed(symTable.renameToUnique(global, {&symTable}))) {
205 return mlir::failure();
209 LLVM::AddressOfOp::create(builder, loc, global)->getResult(0));
212 auto successStrAddr =
213 createUniqueStringGlobal(
"Bound reached with no violations!\n");
214 auto failureStrAddr =
215 createUniqueStringGlobal(
"Assertion can be violated!\n");
217 if (failed(successStrAddr) || failed(failureStrAddr)) {
218 moduleOp->emitOpError(
"could not create result message strings");
219 return signalPassFailure();
223 LLVM::SelectOp::create(builder, loc, bmcOp.getResult(),
224 successStrAddr.value(), failureStrAddr.value());
226 LLVM::CallOp::create(builder, loc, printfFunc.value(),
227 ValueRange{formatString});
228 func::ReturnOp::create(builder, loc);
230 if (insertMainFunc) {
231 builder.setInsertionPointToEnd(getOperation().getBody());
232 Type i32Ty = builder.getI32Type();
233 auto mainFunc = func::FuncOp::create(
234 builder, loc,
"main", builder.getFunctionType({i32Ty, ptrTy}, {i32Ty}));
235 builder.createBlock(&mainFunc.getBody(), {}, {i32Ty, ptrTy}, {loc, loc});
236 func::CallOp::create(builder, loc, entryFunc, ValueRange{});
238 Value constZero = LLVM::ConstantOp::create(builder, loc, i32Ty, 0);
239 func::ReturnOp::create(builder, 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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.