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/Dialect/Func/IR/FuncOps.h"
19 #include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
20 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
21 #include "mlir/IR/Builders.h"
22 #include "mlir/IR/Location.h"
23 #include "mlir/IR/SymbolTable.h"
24 #include "llvm/Support/LogicalResult.h"
25 
26 using namespace mlir;
27 using namespace circt;
28 using namespace hw;
29 
30 namespace circt {
31 #define GEN_PASS_DEF_LOWERTOBMC
32 #include "circt/Tools/circt-bmc/Passes.h.inc"
33 } // namespace circt
34 
35 //===----------------------------------------------------------------------===//
36 // Convert Lower To BMC pass
37 //===----------------------------------------------------------------------===//
38 
39 namespace {
40 struct LowerToBMCPass : public circt::impl::LowerToBMCBase<LowerToBMCPass> {
41  using LowerToBMCBase::LowerToBMCBase;
42  void runOnOperation() override;
43 };
44 } // namespace
45 
46 void LowerToBMCPass::runOnOperation() {
47  Namespace names;
48  // Fetch the 'hw.module' operation to model check.
49  auto moduleOp = getOperation();
50  auto hwModule = moduleOp.lookupSymbol<hw::HWModuleOp>(topModule);
51  if (!hwModule) {
52  moduleOp.emitError("hw.module named '") << topModule << "' not found";
53  return signalPassFailure();
54  }
55 
56  // TODO: Check whether instances contain properties to check
57  if (hwModule.getOps<verif::AssertOp>().empty() &&
58  hwModule.getOps<hw::InstanceOp>().empty()) {
59  hwModule.emitError("no property provided to check in module");
60  return signalPassFailure();
61  }
62 
63  // Create necessary function declarations and globals
64  auto *ctx = &getContext();
65  OpBuilder builder(ctx);
66  Location loc = moduleOp->getLoc();
67  builder.setInsertionPointToEnd(moduleOp.getBody());
68  auto ptrTy = LLVM::LLVMPointerType::get(ctx);
69  auto voidTy = LLVM::LLVMVoidType::get(ctx);
70 
71  // Lookup or declare printf function.
72  auto printfFunc =
73  LLVM::lookupOrCreateFn(moduleOp, "printf", ptrTy, voidTy, true);
74 
75  // Replace the top-module with a function performing the BMC
76  auto entryFunc = builder.create<func::FuncOp>(
77  loc, topModule, builder.getFunctionType({}, {}));
78  builder.createBlock(&entryFunc.getBody());
79 
80  {
81  OpBuilder::InsertionGuard guard(builder);
82  auto *terminator = hwModule.getBody().front().getTerminator();
83  builder.setInsertionPoint(terminator);
84  builder.create<verif::YieldOp>(loc, terminator->getOperands());
85  terminator->erase();
86  }
87 
88  // Double the bound given to the BMC op, as a clock cycle takes 2 BMC
89  // iterations
90  verif::BoundedModelCheckingOp bmcOp;
91  auto numRegs = hwModule->getAttrOfType<IntegerAttr>("num_regs");
92  auto initialValues = hwModule->getAttrOfType<ArrayAttr>("initial_values");
93  if (numRegs && initialValues) {
94  for (auto value : initialValues) {
95  if (!isa<IntegerAttr, UnitAttr>(value)) {
96  hwModule->emitError("initial_values attribute must contain only "
97  "integer or unit attributes");
98  return signalPassFailure();
99  }
100  }
101  bmcOp = builder.create<verif::BoundedModelCheckingOp>(
102  loc, 2 * bound, cast<IntegerAttr>(numRegs).getValue().getZExtValue(),
103  initialValues);
104  } else {
105  hwModule->emitOpError("no num_regs or initial_values attribute found - "
106  "please run externalize "
107  "registers pass first");
108  return signalPassFailure();
109  }
110 
111  // Check that there's only one clock input to the module
112  // TODO: supporting multiple clocks isn't too hard, an interleaving of clock
113  // toggles just needs to be generated
114  bool hasClk = false;
115  for (auto input : hwModule.getInputTypes()) {
116  if (isa<seq::ClockType>(input)) {
117  if (hasClk) {
118  hwModule.emitError("designs with multiple clocks not yet supported");
119  return signalPassFailure();
120  }
121  hasClk = true;
122  }
123  if (auto hwStruct = dyn_cast<hw::StructType>(input)) {
124  for (auto field : hwStruct.getElements()) {
125  if (isa<seq::ClockType>(field.type)) {
126  if (hasClk) {
127  hwModule.emitError(
128  "designs with multiple clocks not yet supported");
129  return signalPassFailure();
130  }
131  hasClk = true;
132  }
133  }
134  }
135  }
136  {
137  OpBuilder::InsertionGuard guard(builder);
138  // Initialize clock to 0 if it exists, otherwise just yield nothing
139  auto *initBlock = builder.createBlock(&bmcOp.getInit());
140  builder.setInsertionPointToStart(initBlock);
141  if (hasClk) {
142  auto initVal =
143  builder.create<hw::ConstantOp>(loc, builder.getI1Type(), 0);
144  auto toClk = builder.create<seq::ToClockOp>(loc, initVal);
145  builder.create<verif::YieldOp>(loc, ValueRange{toClk});
146  } else {
147  builder.create<verif::YieldOp>(loc, ValueRange{});
148  }
149 
150  // Toggle clock in loop region if it exists, otherwise just yield nothing
151  auto *loopBlock = builder.createBlock(&bmcOp.getLoop());
152  builder.setInsertionPointToStart(loopBlock);
153  if (hasClk) {
154  loopBlock->addArgument(seq::ClockType::get(ctx), loc);
155  auto fromClk =
156  builder.create<seq::FromClockOp>(loc, loopBlock->getArgument(0));
157  auto cNeg1 = builder.create<hw::ConstantOp>(loc, builder.getI1Type(), -1);
158  auto nClk = builder.create<comb::XorOp>(loc, fromClk, cNeg1);
159  auto toClk = builder.create<seq::ToClockOp>(loc, nClk);
160  // Only yield clock value
161  builder.create<verif::YieldOp>(loc, ValueRange{toClk});
162  } else {
163  builder.create<verif::YieldOp>(loc, ValueRange{});
164  }
165  }
166  bmcOp.getCircuit().takeBody(hwModule.getBody());
167  hwModule->erase();
168 
169  // Define global string constants to print on success/failure
170  auto createUniqueStringGlobal = [&](StringRef str) -> FailureOr<Value> {
171  Location loc = moduleOp.getLoc();
172 
173  OpBuilder b = OpBuilder::atBlockEnd(moduleOp.getBody());
174  auto arrayTy = LLVM::LLVMArrayType::get(b.getI8Type(), str.size() + 1);
175  auto global = b.create<LLVM::GlobalOp>(
176  loc, arrayTy, /*isConstant=*/true, LLVM::linkage::Linkage::Private,
177  "resultString",
178  StringAttr::get(b.getContext(), Twine(str).concat(Twine('\00'))));
179  SymbolTable symTable(moduleOp);
180  if (failed(symTable.renameToUnique(global, {&symTable}))) {
181  return mlir::failure();
182  }
183 
184  return success(
185  builder.create<LLVM::AddressOfOp>(loc, global)->getResult(0));
186  };
187 
188  auto successStrAddr =
189  createUniqueStringGlobal("Bound reached with no violations!\n");
190  auto failureStrAddr =
191  createUniqueStringGlobal("Assertion can be violated!\n");
192 
193  if (failed(successStrAddr) || failed(failureStrAddr)) {
194  moduleOp->emitOpError("could not create result message strings");
195  return signalPassFailure();
196  }
197 
198  auto formatString = builder.create<LLVM::SelectOp>(
199  loc, bmcOp.getResult(), successStrAddr.value(), failureStrAddr.value());
200  builder.create<LLVM::CallOp>(loc, printfFunc, ValueRange{formatString});
201  builder.create<func::ReturnOp>(loc);
202 
203  if (insertMainFunc) {
204  builder.setInsertionPointToEnd(getOperation().getBody());
205  Type i32Ty = builder.getI32Type();
206  auto mainFunc = builder.create<func::FuncOp>(
207  loc, "main", builder.getFunctionType({i32Ty, ptrTy}, {i32Ty}));
208  builder.createBlock(&mainFunc.getBody(), {}, {i32Ty, ptrTy}, {loc, loc});
209  builder.create<func::CallOp>(loc, entryFunc, ValueRange{});
210  // TODO: don't use LLVM here
211  Value constZero = builder.create<LLVM::ConstantOp>(loc, i32Ty, 0);
212  builder.create<func::ReturnOp>(loc, constZero);
213  }
214 }
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:393
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