19#include "mlir/Analysis/TopologicalSortUtils.h"
20#include "mlir/Dialect/Func/IR/FuncOps.h"
21#include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
22#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
23#include "mlir/IR/Builders.h"
24#include "mlir/IR/Location.h"
25#include "mlir/IR/SymbolTable.h"
26#include "llvm/Support/LogicalResult.h"
33#define GEN_PASS_DEF_LOWERTOBMC
34#include "circt/Tools/circt-bmc/Passes.h.inc"
42struct LowerToBMCPass :
public circt::impl::LowerToBMCBase<LowerToBMCPass> {
43 using LowerToBMCBase::LowerToBMCBase;
44 void runOnOperation()
override;
48void LowerToBMCPass::runOnOperation() {
51 auto moduleOp = getOperation();
54 moduleOp.emitError(
"hw.module named '") << topModule <<
"' not found";
55 return signalPassFailure();
58 if (!sortTopologically(&hwModule.getBodyRegion().front())) {
59 hwModule->emitError(
"could not resolve cycles in module");
60 return signalPassFailure();
63 if (bound < ignoreAssertionsUntil) {
65 "number of ignored cycles must be less than or equal to bound");
66 return signalPassFailure();
70 auto *ctx = &getContext();
71 OpBuilder builder(ctx);
72 Location loc = moduleOp->getLoc();
73 builder.setInsertionPointToEnd(moduleOp.getBody());
74 auto ptrTy = LLVM::LLVMPointerType::get(ctx);
75 auto voidTy = LLVM::LLVMVoidType::get(ctx);
79 LLVM::lookupOrCreateFn(builder, moduleOp,
"printf", ptrTy, voidTy,
true);
80 if (failed(printfFunc)) {
81 moduleOp->emitError(
"failed to lookup or create printf");
82 return signalPassFailure();
86 auto entryFunc = func::FuncOp::create(builder, loc, topModule,
87 builder.getFunctionType({}, {}));
88 builder.createBlock(&entryFunc.getBody());
91 auto *hwOutput = hwModule.getBody().front().getTerminator();
92 SmallVector<std::pair<StringAttr, Value>> namedPorts;
94 Value portValue = port.isInput()
95 ? hwModule.getBody().front().getArgument(port.argNum)
96 : hwOutput->getOperand(port.argNum);
97 namedPorts.push_back({builder.getStringAttr(port.getName()), portValue});
101 OpBuilder::InsertionGuard guard(builder);
102 auto *terminator = hwModule.getBody().front().getTerminator();
103 builder.setInsertionPoint(terminator);
104 verif::YieldOp::create(builder, loc, terminator->getOperands());
110 verif::BoundedModelCheckingOp bmcOp;
111 auto numRegs = hwModule->getAttrOfType<IntegerAttr>(
"num_regs");
112 auto initialValues = hwModule->getAttrOfType<ArrayAttr>(
"initial_values");
113 if (numRegs && initialValues) {
114 for (
auto value : initialValues) {
115 if (!isa<IntegerAttr, UnitAttr>(value)) {
116 hwModule->emitError(
"initial_values attribute must contain only "
117 "integer or unit attributes");
118 return signalPassFailure();
121 bmcOp = verif::BoundedModelCheckingOp::create(
122 builder, loc, risingClocksOnly ? bound : 2 * bound,
123 cast<IntegerAttr>(numRegs).getValue().getZExtValue(), initialValues);
126 if (ignoreAssertionsUntil)
127 bmcOp->setAttr(
"ignore_asserts_until",
128 builder.getI32IntegerAttr(
129 risingClocksOnly ? ignoreAssertionsUntil
130 : 2 * ignoreAssertionsUntil));
132 hwModule->emitOpError(
"no num_regs or initial_values attribute found - "
133 "please run externalize "
134 "registers pass first");
135 return signalPassFailure();
142 for (
auto input : hwModule.getInputTypes()) {
143 if (isa<seq::ClockType>(input)) {
145 hwModule.emitError(
"designs with multiple clocks not yet supported");
146 return signalPassFailure();
150 if (
auto hwStruct = dyn_cast<hw::StructType>(input)) {
151 for (
auto field : hwStruct.getElements()) {
152 if (isa<seq::ClockType>(field.type)) {
155 "designs with multiple clocks not yet supported");
156 return signalPassFailure();
164 OpBuilder::InsertionGuard guard(builder);
167 auto *initBlock = builder.createBlock(&bmcOp.getInit());
168 builder.setInsertionPointToStart(initBlock);
171 risingClocksOnly ? 1 : 0);
172 auto toClk = seq::ToClockOp::create(builder, loc, initVal);
173 verif::YieldOp::create(builder, loc, ValueRange{toClk});
175 verif::YieldOp::create(builder, loc, ValueRange{});
179 auto *loopBlock = builder.createBlock(&bmcOp.getLoop());
180 builder.setInsertionPointToStart(loopBlock);
182 loopBlock->addArgument(seq::ClockType::get(ctx), loc);
183 if (risingClocksOnly) {
185 verif::YieldOp::create(builder, loc,
186 ValueRange{loopBlock->getArgument(0)});
189 seq::FromClockOp::create(builder, loc, loopBlock->getArgument(0));
192 auto nClk = comb::XorOp::create(builder, loc, fromClk, cNeg1);
193 auto toClk = seq::ToClockOp::create(builder, loc, nClk);
195 verif::YieldOp::create(builder, loc, ValueRange{toClk});
198 verif::YieldOp::create(builder, loc, ValueRange{});
201 auto moduleName = hwModule.getNameAttr();
202 bmcOp.getCircuit().takeBody(hwModule.getBody());
207 OpBuilder::InsertionGuard guard(builder);
208 auto &circuitBlock = bmcOp.getCircuit().front();
209 builder.setInsertionPoint(circuitBlock.getTerminator());
210 auto scope = debug::ScopeOp::create(
211 builder, loc, moduleName.getValue(),
215 moduleName,
nullptr);
216 for (
auto &[name, value] : namedPorts)
217 debug::VariableOp::create(builder, loc, name, value, scope);
220 auto createUniqueStringGlobal = [&](StringRef str) -> FailureOr<Value> {
221 Location loc = moduleOp.getLoc();
223 OpBuilder
b = OpBuilder::atBlockEnd(moduleOp.getBody());
224 auto arrayTy = LLVM::LLVMArrayType::get(
b.getI8Type(), str.size() + 1);
225 auto global = LLVM::GlobalOp::create(
226 b, loc, arrayTy,
true, LLVM::linkage::Linkage::Private,
228 StringAttr::get(
b.getContext(), Twine(str).concat(Twine(
'\00'))));
229 SymbolTable symTable(moduleOp);
230 if (failed(symTable.renameToUnique(global, {&symTable}))) {
231 return mlir::failure();
235 LLVM::AddressOfOp::create(builder, loc, global)->getResult(0));
238 auto successStrAddr =
239 createUniqueStringGlobal(
"Bound reached with no violations!\n");
240 auto failureStrAddr =
241 createUniqueStringGlobal(
"Assertion can be violated!\n");
243 if (failed(successStrAddr) || failed(failureStrAddr)) {
244 moduleOp->emitOpError(
"could not create result message strings");
245 return signalPassFailure();
249 LLVM::SelectOp::create(builder, loc, bmcOp.getResult(),
250 successStrAddr.value(), failureStrAddr.value());
252 LLVM::CallOp::create(builder, loc, printfFunc.value(),
253 ValueRange{formatString});
254 func::ReturnOp::create(builder, loc);
256 if (insertMainFunc) {
257 builder.setInsertionPointToEnd(getOperation().getBody());
258 Type i32Ty = builder.getI32Type();
259 auto mainFunc = func::FuncOp::create(
260 builder, loc,
"main", builder.getFunctionType({i32Ty, ptrTy}, {i32Ty}));
261 builder.createBlock(&mainFunc.getBody(), {}, {i32Ty, ptrTy}, {loc, loc});
262 func::CallOp::create(builder, loc, entryFunc, ValueRange{});
264 Value constZero = LLVM::ConstantOp::create(builder, loc, i32Ty, 0);
265 func::ReturnOp::create(builder, loc, constZero);
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
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.