CIRCT 24.0.0git
Loading...
Searching...
No Matches
PrepareForBMC.cpp
Go to the documentation of this file.
1//===- PrepareForBMC.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
14#include "mlir/Pass/Pass.h"
15
16using namespace mlir;
17using namespace circt;
18
19namespace circt {
20#define GEN_PASS_DEF_PREPAREFORBMC
21#include "circt/Tools/circt-bmc/Passes.h.inc"
22} // namespace circt
23
24namespace {
25struct PrepareForBMCPass
26 : public circt::impl::PrepareForBMCBase<PrepareForBMCPass> {
27 using PrepareForBMCBase::PrepareForBMCBase;
28
29 FailureOr<Value> getPreviousClock(Value clock, OpBuilder &builder) {
30 if (auto it = previousClockValues.find(clock);
31 it != previousClockValues.end())
32 return it->second;
33
34 auto fromClock = clock.getDefiningOp<seq::FromClockOp>();
35 if (!fromClock) {
36 emitError(clock.getLoc(),
37 "expected a clock normalized by seq.from_clock");
38 return failure();
39 }
40
41 auto initialValue = seq::createConstantInitialValue(
42 builder, clock.getLoc(),
43 builder.getIntegerAttr(builder.getI1Type(), 0));
44 // ExternalizeRegisters turns this into BMC state whose next value is the
45 // current clock, giving every property the clock value from the preceding
46 // BMC transition.
47 auto previousClock = seq::CompRegOp::create(
48 builder, clock.getLoc(), clock, fromClock.getInput(), /*reset=*/Value{},
49 /*rstValue=*/Value{}, initialValue);
50 previousClockValues.try_emplace(clock, previousClock);
51 return previousClock.getData();
52 }
53
54 template <typename ClockedOp, typename UnclockedOp>
55 LogicalResult lowerClockedProperty(ClockedOp op) {
56 if (!op.getProperty().getType().isInteger(1)) {
57 op.emitError("unsupported clocked property after LTL lowering");
58 return failure();
59 }
60
61 OpBuilder builder(op);
62 auto previousClock = getPreviousClock(op.getClock(), builder);
63 if (failed(previousClock))
64 return failure();
65 auto trueValue =
66 hw::ConstantOp::create(builder, op.getLoc(), builder.getI1Type(), 1);
67 Value active;
68 switch (op.getEdge()) {
69 case verif::ClockEdge::Pos: {
70 auto notPreviousClock =
71 comb::XorOp::create(builder, op.getLoc(), *previousClock, trueValue);
72 active = comb::AndOp::create(builder, op.getLoc(), op.getClock(),
73 notPreviousClock);
74 break;
75 }
76 case verif::ClockEdge::Neg: {
77 auto notCurrentClock =
78 comb::XorOp::create(builder, op.getLoc(), op.getClock(), trueValue);
79 active = comb::AndOp::create(builder, op.getLoc(), notCurrentClock,
80 *previousClock);
81 break;
82 }
83 case verif::ClockEdge::Both:
84 active = comb::XorOp::create(builder, op.getLoc(), op.getClock(),
85 *previousClock);
86 break;
87 }
88 if (op.getEnable())
89 active =
90 comb::AndOp::create(builder, op.getLoc(), active, op.getEnable());
91
92 auto inactive =
93 comb::XorOp::create(builder, op.getLoc(), active, trueValue);
94 auto property =
95 comb::OrOp::create(builder, op.getLoc(), inactive, op.getProperty());
96 UnclockedOp::create(builder, op.getLoc(), property, /*enable=*/Value{},
97 op.getLabelAttr());
98 op.erase();
99 return success();
100 }
101
102 void normalizeClockPorts(hw::HWModuleOp module) {
103 // SystemVerilog import represents clocks as i1 ports converted by
104 // seq.to_clock. BMC needs a native clock block argument so it can own the
105 // clock waveform and update registers only on rising edges.
106 auto *body = module.getBodyBlock();
107 SmallVector<BlockArgument> clockArguments;
108 for (auto argument : body->getArguments()) {
109 if (!argument.getType().isInteger(1))
110 continue;
111 if (llvm::any_of(argument.getUsers(), [](Operation *user) {
112 return isa<seq::ToClockOp>(user);
113 }))
114 clockArguments.push_back(argument);
115 }
116 if (clockArguments.empty())
117 return;
118
119 SmallVector<hw::ModulePort> ports(module.getHWModuleType().getPorts());
120 auto clockType = seq::ClockType::get(&getContext());
121 for (auto argument : clockArguments) {
122 SmallVector<seq::ToClockOp> toClockOps;
123 SmallVector<OpOperand *> rawClockUses;
124 for (auto &use : argument.getUses()) {
125 if (auto toClock = dyn_cast<seq::ToClockOp>(use.getOwner()))
126 toClockOps.push_back(toClock);
127 else
128 rawClockUses.push_back(&use);
129 }
130
131 argument.setType(clockType);
132 OpBuilder builder = OpBuilder::atBlockBegin(body);
133 auto rawClock =
134 seq::FromClockOp::create(builder, argument.getLoc(), argument);
135 for (auto *use : rawClockUses)
136 use->set(rawClock);
137 for (auto toClock : toClockOps) {
138 toClock.replaceAllUsesWith(argument);
139 toClock.erase();
140 }
141
142 auto portID =
143 module.getHWModuleType().getPortIdForInputId(argument.getArgNumber());
144 ports[portID].type = clockType;
145 }
146 module.setHWModuleType(hw::ModuleType::get(&getContext(), ports));
147 }
148
149 void runOnOperation() override {
150 previousClockValues.clear();
151 auto module = getOperation().lookupSymbol<hw::HWModuleOp>(topModule);
152 if (!module)
153 return;
154
155 normalizeClockPorts(module);
156 LogicalResult result = success();
157 module->walk([&](Operation *operation) {
158 if (failed(result))
159 return;
160 if (auto assertOp = dyn_cast<verif::ClockedAssertOp>(operation))
161 result = lowerClockedProperty<verif::ClockedAssertOp, verif::AssertOp>(
162 assertOp);
163 else if (auto assumeOp = dyn_cast<verif::ClockedAssumeOp>(operation))
164 result = lowerClockedProperty<verif::ClockedAssumeOp, verif::AssumeOp>(
165 assumeOp);
166 });
167 if (failed(result))
168 signalPassFailure();
169 }
170
171 DenseMap<Value, Value> previousClockValues;
172};
173} // namespace
create(data_type, value)
Definition hw.py:433
create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
Definition seq.py:157
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.