CIRCT 23.0.0git
Loading...
Searching...
No Matches
LowerToBMC.cpp
Go to the documentation of this file.
1//===- LowerToBMC.cpp -----------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
16#include "circt/Support/LLVM.h"
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"
27
28using namespace mlir;
29using namespace circt;
30using namespace hw;
31
32namespace circt {
33#define GEN_PASS_DEF_LOWERTOBMC
34#include "circt/Tools/circt-bmc/Passes.h.inc"
35} // namespace circt
36
37//===----------------------------------------------------------------------===//
38// Convert Lower To BMC pass
39//===----------------------------------------------------------------------===//
40
41namespace {
42struct LowerToBMCPass : public circt::impl::LowerToBMCBase<LowerToBMCPass> {
43 using LowerToBMCBase::LowerToBMCBase;
44 void runOnOperation() override;
45};
46} // namespace
47
48void LowerToBMCPass::runOnOperation() {
49 Namespace names;
50 // Fetch the 'hw.module' operation to model check.
51 auto moduleOp = getOperation();
52 auto hwModule = moduleOp.lookupSymbol<hw::HWModuleOp>(topModule);
53 if (!hwModule) {
54 moduleOp.emitError("hw.module named '") << topModule << "' not found";
55 return signalPassFailure();
56 }
57
58 if (!sortTopologically(&hwModule.getBodyRegion().front())) {
59 hwModule->emitError("could not resolve cycles in module");
60 return signalPassFailure();
61 }
62
63 if (bound < ignoreAssertionsUntil) {
64 hwModule->emitError(
65 "number of ignored cycles must be less than or equal to bound");
66 return signalPassFailure();
67 }
68
69 // Create necessary function declarations and globals
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);
76
77 // Lookup or declare printf function.
78 auto printfFunc =
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();
83 }
84
85 // Replace the top-module with a function performing the BMC
86 auto entryFunc = func::FuncOp::create(builder, loc, topModule,
87 builder.getFunctionType({}, {}));
88 builder.createBlock(&entryFunc.getBody());
89
90 // Save module name and port info before the module is erased
91 auto *hwOutput = hwModule.getBody().front().getTerminator();
92 SmallVector<std::pair<StringAttr, Value>> namedPorts;
93 for (auto &port : hwModule.getPortList()) {
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});
98 }
99
100 {
101 OpBuilder::InsertionGuard guard(builder);
102 auto *terminator = hwModule.getBody().front().getTerminator();
103 builder.setInsertionPoint(terminator);
104 verif::YieldOp::create(builder, loc, terminator->getOperands());
105 terminator->erase();
106 }
107
108 // Double the bound given to the BMC op unless in rising clocks only mode, as
109 // a clock cycle involves two negations
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();
119 }
120 }
121 bmcOp = verif::BoundedModelCheckingOp::create(
122 builder, loc, risingClocksOnly ? bound : 2 * bound,
123 cast<IntegerAttr>(numRegs).getValue().getZExtValue(), initialValues);
124 // Annotate the op with how many cycles to ignore - again, we may need to
125 // double this to account for rising and falling edges
126 if (ignoreAssertionsUntil)
127 bmcOp->setAttr("ignore_asserts_until",
128 builder.getI32IntegerAttr(
129 risingClocksOnly ? ignoreAssertionsUntil
130 : 2 * ignoreAssertionsUntil));
131 } else {
132 hwModule->emitOpError("no num_regs or initial_values attribute found - "
133 "please run externalize "
134 "registers pass first");
135 return signalPassFailure();
136 }
137
138 // Check that there's only one clock input to the module
139 // TODO: supporting multiple clocks isn't too hard, an interleaving of clock
140 // toggles just needs to be generated
141 bool hasClk = false;
142 for (auto input : hwModule.getInputTypes()) {
143 if (isa<seq::ClockType>(input)) {
144 if (hasClk) {
145 hwModule.emitError("designs with multiple clocks not yet supported");
146 return signalPassFailure();
147 }
148 hasClk = true;
149 }
150 if (auto hwStruct = dyn_cast<hw::StructType>(input)) {
151 for (auto field : hwStruct.getElements()) {
152 if (isa<seq::ClockType>(field.type)) {
153 if (hasClk) {
154 hwModule.emitError(
155 "designs with multiple clocks not yet supported");
156 return signalPassFailure();
157 }
158 hasClk = true;
159 }
160 }
161 }
162 }
163 {
164 OpBuilder::InsertionGuard guard(builder);
165 // Initialize clock to 0 if it exists, otherwise just yield nothing
166 // We initialize to 1 if we're in rising clocks only mode
167 auto *initBlock = builder.createBlock(&bmcOp.getInit());
168 builder.setInsertionPointToStart(initBlock);
169 if (hasClk) {
170 auto initVal = hw::ConstantOp::create(builder, loc, builder.getI1Type(),
171 risingClocksOnly ? 1 : 0);
172 auto toClk = seq::ToClockOp::create(builder, loc, initVal);
173 verif::YieldOp::create(builder, loc, ValueRange{toClk});
174 } else {
175 verif::YieldOp::create(builder, loc, ValueRange{});
176 }
177
178 // Toggle clock in loop region if it exists, otherwise just yield nothing
179 auto *loopBlock = builder.createBlock(&bmcOp.getLoop());
180 builder.setInsertionPointToStart(loopBlock);
181 if (hasClk) {
182 loopBlock->addArgument(seq::ClockType::get(ctx), loc);
183 if (risingClocksOnly) {
184 // In rising clocks only mode we don't need to toggle the clock
185 verif::YieldOp::create(builder, loc,
186 ValueRange{loopBlock->getArgument(0)});
187 } else {
188 auto fromClk =
189 seq::FromClockOp::create(builder, loc, loopBlock->getArgument(0));
190 auto cNeg1 =
191 hw::ConstantOp::create(builder, loc, builder.getI1Type(), -1);
192 auto nClk = comb::XorOp::create(builder, loc, fromClk, cNeg1);
193 auto toClk = seq::ToClockOp::create(builder, loc, nClk);
194 // Only yield clock value
195 verif::YieldOp::create(builder, loc, ValueRange{toClk});
196 }
197 } else {
198 verif::YieldOp::create(builder, loc, ValueRange{});
199 }
200 }
201 auto moduleName = hwModule.getNameAttr();
202 bmcOp.getCircuit().takeBody(hwModule.getBody());
203 hwModule->erase();
204
205 // signal names for counter-example generation.
206 {
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(),
212 // TODO: Hierarchy support would require walking parent InstanceOps,
213 // but LowerToBMC operates on a single top-level module with no
214 // instance context available at this point.
215 moduleName, nullptr);
216 for (auto &[name, value] : namedPorts)
217 debug::VariableOp::create(builder, loc, name, value, scope);
218 }
219 // Define global string constants to print on success/failure
220 auto createUniqueStringGlobal = [&](StringRef str) -> FailureOr<Value> {
221 Location loc = moduleOp.getLoc();
222
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, /*isConstant=*/true, LLVM::linkage::Linkage::Private,
227 "resultString",
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();
232 }
233
234 return success(
235 LLVM::AddressOfOp::create(builder, loc, global)->getResult(0));
236 };
237
238 auto successStrAddr =
239 createUniqueStringGlobal("Bound reached with no violations!\n");
240 auto failureStrAddr =
241 createUniqueStringGlobal("Assertion can be violated!\n");
242
243 if (failed(successStrAddr) || failed(failureStrAddr)) {
244 moduleOp->emitOpError("could not create result message strings");
245 return signalPassFailure();
246 }
247
248 auto formatString =
249 LLVM::SelectOp::create(builder, loc, bmcOp.getResult(),
250 successStrAddr.value(), failureStrAddr.value());
251
252 LLVM::CallOp::create(builder, loc, printfFunc.value(),
253 ValueRange{formatString});
254 func::ReturnOp::create(builder, loc);
255
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{});
263 // TODO: don't use LLVM here
264 Value constZero = LLVM::ConstantOp::create(builder, loc, i32Ty, 0);
265 func::ReturnOp::create(builder, loc, constZero);
266 }
267}
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
Definition HWOps.cpp:1438
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
create(data_type, value)
Definition hw.py:433
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition debug.py:1
Definition hw.py:1