Loading [MathJax]/extensions/tex2jax.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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
15#include "circt/Support/LLVM.h"
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"
26
27using namespace mlir;
28using namespace circt;
29using namespace hw;
30
31namespace circt {
32#define GEN_PASS_DEF_LOWERTOBMC
33#include "circt/Tools/circt-bmc/Passes.h.inc"
34} // namespace circt
35
36//===----------------------------------------------------------------------===//
37// Convert Lower To BMC pass
38//===----------------------------------------------------------------------===//
39
40namespace {
41struct LowerToBMCPass : public circt::impl::LowerToBMCBase<LowerToBMCPass> {
42 using LowerToBMCBase::LowerToBMCBase;
43 void runOnOperation() override;
44};
45} // namespace
46
47void LowerToBMCPass::runOnOperation() {
48 Namespace names;
49 // Fetch the 'hw.module' operation to model check.
50 auto moduleOp = getOperation();
51 auto hwModule = moduleOp.lookupSymbol<hw::HWModuleOp>(topModule);
52 if (!hwModule) {
53 moduleOp.emitError("hw.module named '") << topModule << "' not found";
54 return signalPassFailure();
55 }
56
57 // TODO: Check whether instances contain properties to check
58 if (hwModule.getOps<verif::AssertOp>().empty() &&
59 hwModule.getOps<hw::InstanceOp>().empty()) {
60 hwModule.emitError("no property provided to check in module");
61 return signalPassFailure();
62 }
63
64 if (!sortTopologically(&hwModule.getBodyRegion().front())) {
65 hwModule->emitError("could not resolve cycles in module");
66 return signalPassFailure();
67 }
68
69 if (bound < ignoreAssertionsUntil) {
70 hwModule->emitError(
71 "number of ignored cycles must be less than or equal to bound");
72 return signalPassFailure();
73 }
74
75 // Create necessary function declarations and globals
76 auto *ctx = &getContext();
77 OpBuilder builder(ctx);
78 Location loc = moduleOp->getLoc();
79 builder.setInsertionPointToEnd(moduleOp.getBody());
80 auto ptrTy = LLVM::LLVMPointerType::get(ctx);
81 auto voidTy = LLVM::LLVMVoidType::get(ctx);
82
83 // Lookup or declare printf function.
84 auto printfFunc =
85 LLVM::lookupOrCreateFn(builder, moduleOp, "printf", ptrTy, voidTy, true);
86 if (failed(printfFunc)) {
87 moduleOp->emitError("failed to lookup or create printf");
88 return signalPassFailure();
89 }
90
91 // Replace the top-module with a function performing the BMC
92 auto entryFunc = builder.create<func::FuncOp>(
93 loc, topModule, builder.getFunctionType({}, {}));
94 builder.createBlock(&entryFunc.getBody());
95
96 {
97 OpBuilder::InsertionGuard guard(builder);
98 auto *terminator = hwModule.getBody().front().getTerminator();
99 builder.setInsertionPoint(terminator);
100 builder.create<verif::YieldOp>(loc, terminator->getOperands());
101 terminator->erase();
102 }
103
104 // Double the bound given to the BMC op unless in rising clocks only mode, as
105 // a clock cycle involves two negations
106 verif::BoundedModelCheckingOp bmcOp;
107 auto numRegs = hwModule->getAttrOfType<IntegerAttr>("num_regs");
108 auto initialValues = hwModule->getAttrOfType<ArrayAttr>("initial_values");
109 if (numRegs && initialValues) {
110 for (auto value : initialValues) {
111 if (!isa<IntegerAttr, UnitAttr>(value)) {
112 hwModule->emitError("initial_values attribute must contain only "
113 "integer or unit attributes");
114 return signalPassFailure();
115 }
116 }
117 bmcOp = builder.create<verif::BoundedModelCheckingOp>(
118 loc, risingClocksOnly ? bound : 2 * bound,
119 cast<IntegerAttr>(numRegs).getValue().getZExtValue(), initialValues);
120 // Annotate the op with how many cycles to ignore - again, we may need to
121 // double this to account for rising and falling edges
122 if (ignoreAssertionsUntil)
123 bmcOp->setAttr("ignore_asserts_until",
124 builder.getI32IntegerAttr(
125 risingClocksOnly ? ignoreAssertionsUntil
126 : 2 * ignoreAssertionsUntil));
127 } else {
128 hwModule->emitOpError("no num_regs or initial_values attribute found - "
129 "please run externalize "
130 "registers pass first");
131 return signalPassFailure();
132 }
133
134 // Check that there's only one clock input to the module
135 // TODO: supporting multiple clocks isn't too hard, an interleaving of clock
136 // toggles just needs to be generated
137 bool hasClk = false;
138 for (auto input : hwModule.getInputTypes()) {
139 if (isa<seq::ClockType>(input)) {
140 if (hasClk) {
141 hwModule.emitError("designs with multiple clocks not yet supported");
142 return signalPassFailure();
143 }
144 hasClk = true;
145 }
146 if (auto hwStruct = dyn_cast<hw::StructType>(input)) {
147 for (auto field : hwStruct.getElements()) {
148 if (isa<seq::ClockType>(field.type)) {
149 if (hasClk) {
150 hwModule.emitError(
151 "designs with multiple clocks not yet supported");
152 return signalPassFailure();
153 }
154 hasClk = true;
155 }
156 }
157 }
158 }
159 {
160 OpBuilder::InsertionGuard guard(builder);
161 // Initialize clock to 0 if it exists, otherwise just yield nothing
162 // We initialize to 1 if we're in rising clocks only mode
163 auto *initBlock = builder.createBlock(&bmcOp.getInit());
164 builder.setInsertionPointToStart(initBlock);
165 if (hasClk) {
166 auto initVal = builder.create<hw::ConstantOp>(loc, builder.getI1Type(),
167 risingClocksOnly ? 1 : 0);
168 auto toClk = builder.create<seq::ToClockOp>(loc, initVal);
169 builder.create<verif::YieldOp>(loc, ValueRange{toClk});
170 } else {
171 builder.create<verif::YieldOp>(loc, ValueRange{});
172 }
173
174 // Toggle clock in loop region if it exists, otherwise just yield nothing
175 auto *loopBlock = builder.createBlock(&bmcOp.getLoop());
176 builder.setInsertionPointToStart(loopBlock);
177 if (hasClk) {
178 loopBlock->addArgument(seq::ClockType::get(ctx), loc);
179 if (risingClocksOnly) {
180 // In rising clocks only mode we don't need to toggle the clock
181 builder.create<verif::YieldOp>(loc,
182 ValueRange{loopBlock->getArgument(0)});
183 } else {
184 auto fromClk =
185 builder.create<seq::FromClockOp>(loc, loopBlock->getArgument(0));
186 auto cNeg1 =
187 builder.create<hw::ConstantOp>(loc, builder.getI1Type(), -1);
188 auto nClk = builder.create<comb::XorOp>(loc, fromClk, cNeg1);
189 auto toClk = builder.create<seq::ToClockOp>(loc, nClk);
190 // Only yield clock value
191 builder.create<verif::YieldOp>(loc, ValueRange{toClk});
192 }
193 } else {
194 builder.create<verif::YieldOp>(loc, ValueRange{});
195 }
196 }
197 bmcOp.getCircuit().takeBody(hwModule.getBody());
198 hwModule->erase();
199
200 // Define global string constants to print on success/failure
201 auto createUniqueStringGlobal = [&](StringRef str) -> FailureOr<Value> {
202 Location loc = moduleOp.getLoc();
203
204 OpBuilder b = OpBuilder::atBlockEnd(moduleOp.getBody());
205 auto arrayTy = LLVM::LLVMArrayType::get(b.getI8Type(), str.size() + 1);
206 auto global = b.create<LLVM::GlobalOp>(
207 loc, arrayTy, /*isConstant=*/true, LLVM::linkage::Linkage::Private,
208 "resultString",
209 StringAttr::get(b.getContext(), Twine(str).concat(Twine('\00'))));
210 SymbolTable symTable(moduleOp);
211 if (failed(symTable.renameToUnique(global, {&symTable}))) {
212 return mlir::failure();
213 }
214
215 return success(
216 builder.create<LLVM::AddressOfOp>(loc, global)->getResult(0));
217 };
218
219 auto successStrAddr =
220 createUniqueStringGlobal("Bound reached with no violations!\n");
221 auto failureStrAddr =
222 createUniqueStringGlobal("Assertion can be violated!\n");
223
224 if (failed(successStrAddr) || failed(failureStrAddr)) {
225 moduleOp->emitOpError("could not create result message strings");
226 return signalPassFailure();
227 }
228
229 auto formatString = builder.create<LLVM::SelectOp>(
230 loc, bmcOp.getResult(), successStrAddr.value(), failureStrAddr.value());
231 builder.create<LLVM::CallOp>(loc, printfFunc.value(),
232 ValueRange{formatString});
233 builder.create<func::ReturnOp>(loc);
234
235 if (insertMainFunc) {
236 builder.setInsertionPointToEnd(getOperation().getBody());
237 Type i32Ty = builder.getI32Type();
238 auto mainFunc = builder.create<func::FuncOp>(
239 loc, "main", builder.getFunctionType({i32Ty, ptrTy}, {i32Ty}));
240 builder.createBlock(&mainFunc.getBody(), {}, {i32Ty, ptrTy}, {loc, loc});
241 builder.create<func::CallOp>(loc, entryFunc, ValueRange{});
242 // TODO: don't use LLVM here
243 Value constZero = builder.create<LLVM::ConstantOp>(loc, i32Ty, 0);
244 builder.create<func::ReturnOp>(loc, constZero);
245 }
246}
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
Definition CalyxOps.cpp:540
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 hw.py:1