CIRCT  20.0.0git
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 
10 #include "circt/Dialect/HW/HWOps.h"
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 
27 using namespace mlir;
28 using namespace circt;
29 using namespace hw;
30 
31 namespace 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 
40 namespace {
41 struct LowerToBMCPass : public circt::impl::LowerToBMCBase<LowerToBMCPass> {
42  using LowerToBMCBase::LowerToBMCBase;
43  void runOnOperation() override;
44 };
45 } // namespace
46 
47 void 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  // 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(moduleOp, "printf", ptrTy, voidTy, true);
80 
81  // Replace the top-module with a function performing the BMC
82  auto entryFunc = builder.create<func::FuncOp>(
83  loc, topModule, builder.getFunctionType({}, {}));
84  builder.createBlock(&entryFunc.getBody());
85 
86  {
87  OpBuilder::InsertionGuard guard(builder);
88  auto *terminator = hwModule.getBody().front().getTerminator();
89  builder.setInsertionPoint(terminator);
90  builder.create<verif::YieldOp>(loc, terminator->getOperands());
91  terminator->erase();
92  }
93 
94  // Double the bound given to the BMC op, as a clock cycle takes 2 BMC
95  // iterations
96  verif::BoundedModelCheckingOp bmcOp;
97  auto numRegs = hwModule->getAttrOfType<IntegerAttr>("num_regs");
98  auto initialValues = hwModule->getAttrOfType<ArrayAttr>("initial_values");
99  if (numRegs && initialValues) {
100  for (auto value : initialValues) {
101  if (!isa<IntegerAttr, UnitAttr>(value)) {
102  hwModule->emitError("initial_values attribute must contain only "
103  "integer or unit attributes");
104  return signalPassFailure();
105  }
106  }
107  bmcOp = builder.create<verif::BoundedModelCheckingOp>(
108  loc, 2 * bound, cast<IntegerAttr>(numRegs).getValue().getZExtValue(),
109  initialValues);
110  } else {
111  hwModule->emitOpError("no num_regs or initial_values attribute found - "
112  "please run externalize "
113  "registers pass first");
114  return signalPassFailure();
115  }
116 
117  // Check that there's only one clock input to the module
118  // TODO: supporting multiple clocks isn't too hard, an interleaving of clock
119  // toggles just needs to be generated
120  bool hasClk = false;
121  for (auto input : hwModule.getInputTypes()) {
122  if (isa<seq::ClockType>(input)) {
123  if (hasClk) {
124  hwModule.emitError("designs with multiple clocks not yet supported");
125  return signalPassFailure();
126  }
127  hasClk = true;
128  }
129  if (auto hwStruct = dyn_cast<hw::StructType>(input)) {
130  for (auto field : hwStruct.getElements()) {
131  if (isa<seq::ClockType>(field.type)) {
132  if (hasClk) {
133  hwModule.emitError(
134  "designs with multiple clocks not yet supported");
135  return signalPassFailure();
136  }
137  hasClk = true;
138  }
139  }
140  }
141  }
142  {
143  OpBuilder::InsertionGuard guard(builder);
144  // Initialize clock to 0 if it exists, otherwise just yield nothing
145  auto *initBlock = builder.createBlock(&bmcOp.getInit());
146  builder.setInsertionPointToStart(initBlock);
147  if (hasClk) {
148  auto initVal =
149  builder.create<hw::ConstantOp>(loc, builder.getI1Type(), 0);
150  auto toClk = builder.create<seq::ToClockOp>(loc, initVal);
151  builder.create<verif::YieldOp>(loc, ValueRange{toClk});
152  } else {
153  builder.create<verif::YieldOp>(loc, ValueRange{});
154  }
155 
156  // Toggle clock in loop region if it exists, otherwise just yield nothing
157  auto *loopBlock = builder.createBlock(&bmcOp.getLoop());
158  builder.setInsertionPointToStart(loopBlock);
159  if (hasClk) {
160  loopBlock->addArgument(seq::ClockType::get(ctx), loc);
161  auto fromClk =
162  builder.create<seq::FromClockOp>(loc, loopBlock->getArgument(0));
163  auto cNeg1 = builder.create<hw::ConstantOp>(loc, builder.getI1Type(), -1);
164  auto nClk = builder.create<comb::XorOp>(loc, fromClk, cNeg1);
165  auto toClk = builder.create<seq::ToClockOp>(loc, nClk);
166  // Only yield clock value
167  builder.create<verif::YieldOp>(loc, ValueRange{toClk});
168  } else {
169  builder.create<verif::YieldOp>(loc, ValueRange{});
170  }
171  }
172  bmcOp.getCircuit().takeBody(hwModule.getBody());
173  hwModule->erase();
174 
175  // Define global string constants to print on success/failure
176  auto createUniqueStringGlobal = [&](StringRef str) -> FailureOr<Value> {
177  Location loc = moduleOp.getLoc();
178 
179  OpBuilder b = OpBuilder::atBlockEnd(moduleOp.getBody());
180  auto arrayTy = LLVM::LLVMArrayType::get(b.getI8Type(), str.size() + 1);
181  auto global = b.create<LLVM::GlobalOp>(
182  loc, arrayTy, /*isConstant=*/true, LLVM::linkage::Linkage::Private,
183  "resultString",
184  StringAttr::get(b.getContext(), Twine(str).concat(Twine('\00'))));
185  SymbolTable symTable(moduleOp);
186  if (failed(symTable.renameToUnique(global, {&symTable}))) {
187  return mlir::failure();
188  }
189 
190  return success(
191  builder.create<LLVM::AddressOfOp>(loc, global)->getResult(0));
192  };
193 
194  auto successStrAddr =
195  createUniqueStringGlobal("Bound reached with no violations!\n");
196  auto failureStrAddr =
197  createUniqueStringGlobal("Assertion can be violated!\n");
198 
199  if (failed(successStrAddr) || failed(failureStrAddr)) {
200  moduleOp->emitOpError("could not create result message strings");
201  return signalPassFailure();
202  }
203 
204  auto formatString = builder.create<LLVM::SelectOp>(
205  loc, bmcOp.getResult(), successStrAddr.value(), failureStrAddr.value());
206  builder.create<LLVM::CallOp>(loc, printfFunc, ValueRange{formatString});
207  builder.create<func::ReturnOp>(loc);
208 
209  if (insertMainFunc) {
210  builder.setInsertionPointToEnd(getOperation().getBody());
211  Type i32Ty = builder.getI32Type();
212  auto mainFunc = builder.create<func::FuncOp>(
213  loc, "main", builder.getFunctionType({i32Ty, ptrTy}, {i32Ty}));
214  builder.createBlock(&mainFunc.getBody(), {}, {i32Ty, ptrTy}, {loc, loc});
215  builder.create<func::CallOp>(loc, entryFunc, ValueRange{});
216  // TODO: don't use LLVM here
217  Value constZero = builder.create<LLVM::ConstantOp>(loc, i32Ty, 0);
218  builder.create<func::ReturnOp>(loc, constZero);
219  }
220 }
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
def create(data_type, value)
Definition: hw.py:433
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: hw.py:1