31#include "mlir/Pass/Pass.h"
32#include "llvm/ADT/MapVector.h"
33#include "llvm/ADT/TypeSwitch.h"
34#include "llvm/Support/raw_ostream.h"
37#define GEN_PASS_DEF_CONVERTHWTOBTOR2
38#include "circt/Conversion/Passes.h.inc"
47struct ConvertHWToBTOR2Pass
48 :
public circt::impl::ConvertHWToBTOR2Base<ConvertHWToBTOR2Pass>,
56 ConvertHWToBTOR2Pass(raw_ostream &os) : os(os) {}
58 void runOnOperation()
override;
74 DenseMap<size_t, size_t> sortToLIDMap;
79 DenseMap<APInt, size_t> constToLIDMap;
84 DenseMap<Operation *, size_t> opLIDMap;
88 DenseMap<size_t, size_t> inputLIDs;
94 SmallVector<Operation *> regOps;
98 llvm::SmallMapVector<Operation *, OperandRange::iterator, 16> worklist;
101 DenseSet<Operation *> handledOps;
104 static constexpr size_t noLID = -1UL;
105 [[maybe_unused]]
static constexpr int64_t noWidth = -1L;
112 size_t getOpLID(Operation *op) {
115 Operation *defOp = op;
116 auto &f = opLIDMap[defOp];
126 size_t setOpLID(Operation *op) {
127 size_t oplid = lid++;
128 opLIDMap[op] = oplid;
135 size_t getOpLID(Value value) {
136 Operation *defOp = value.getDefiningOp();
138 if (
auto it = opLIDMap.find(defOp); it != opLIDMap.end())
143 if (BlockArgument barg = dyn_cast<BlockArgument>(value)) {
145 size_t argIdx = barg.getArgNumber();
148 if (
auto it = inputLIDs.find(argIdx); it != inputLIDs.end())
160 size_t getSortLID(
size_t w) {
161 if (
auto it = sortToLIDMap.find(w); it != sortToLIDMap.end())
169 size_t setSortLID(
size_t w) {
170 size_t sortlid = lid;
172 sortToLIDMap[w] = lid++;
179 size_t getConstLID(int64_t val,
size_t w) {
180 if (
auto it = constToLIDMap.find(APInt(w, val)); it != constToLIDMap.end())
188 size_t setConstLID(int64_t val,
size_t w) {
189 size_t constlid = lid;
191 constToLIDMap[APInt(w, val)] = lid++;
199 void genSort(StringRef type,
size_t width) {
201 if (getSortLID(width) != noLID) {
205 size_t sortlid = setSortLID(width);
210 <<
" " << type <<
" " << width <<
"\n";
214 void genInput(
size_t inlid,
size_t width, StringRef name) {
216 size_t sid = sortToLIDMap.at(width);
221 <<
" " << sid <<
" " << name <<
"\n";
225 void genConst(APInt value,
size_t width, Operation *op) {
228 size_t opLID = getOpLID(op);
231 size_t sid = sortToLIDMap.at(width);
235 <<
" " << sid <<
" " << value <<
"\n";
239 size_t genZero(
size_t width) {
241 size_t zlid = getConstLID(0, width);
246 size_t sid = sortToLIDMap.at(width);
249 size_t constlid = setConstLID(0, width);
252 os << constlid <<
" "
254 <<
" " << sid <<
"\n";
260 void genInit(Operation *
reg, Value initVal, int64_t width) {
262 size_t regLID = getOpLID(
reg);
263 size_t sid = sortToLIDMap.at(width);
264 size_t initValLID = getOpLID(initVal);
270 <<
" " << sid <<
" " << regLID <<
" " << initValLID <<
"\n";
275 void genBinOp(StringRef inst, Operation *binop, Value op1, Value op2,
278 size_t opLID = getOpLID(binop);
281 size_t sid = sortToLIDMap.at(width);
285 size_t op1LID = getOpLID(op1);
286 size_t op2LID = getOpLID(op2);
289 os << opLID <<
" " << inst <<
" " << sid <<
" " << op1LID <<
" " << op2LID
294 void genVariadicOp(StringRef inst, Operation *op,
size_t width) {
295 auto operands = op->getOperands();
296 size_t sid = sortToLIDMap.at(width);
298 if (operands.size() == 0) {
299 op->emitError(
"variadic operations with no operands are not supported");
305 if (operands.size() == 1) {
306 auto existingLID = getOpLID(operands[0]);
309 assert(existingLID != noLID);
310 opLIDMap[op] = existingLID;
315 auto isConcat = isa<comb::ConcatOp>(op);
319 auto prevOperandLID = getOpLID(operands[0]);
322 auto currentWidth = operands[0].getType().getIntOrFloatBitWidth();
324 for (
auto operand : operands.drop_front()) {
329 currentWidth += operand.getType().getIntOrFloatBitWidth();
331 genSort(
"bitvec", currentWidth);
334 auto thisLid = lid++;
335 auto thisOperandLID = getOpLID(operand);
336 os << thisLid <<
" " << inst <<
" "
337 << (isConcat ? sortToLIDMap.at(currentWidth) : sid) <<
" "
338 << prevOperandLID <<
" " << thisOperandLID <<
"\n";
339 prevOperandLID = thisLid;
343 opLIDMap[op] = prevOperandLID;
347 void genSlice(Operation *srcop, Value op0,
size_t lowbit, int64_t width) {
349 size_t opLID = getOpLID(srcop);
352 size_t sid = sortToLIDMap.at(width);
356 size_t op0LID = getOpLID(op0);
361 <<
" " << sid <<
" " << op0LID <<
" " << (lowbit + width - 1) <<
" "
366 void genReplicateAsConcats(Operation *srcop, Value op0,
size_t count,
367 unsigned int inputWidth) {
368 auto currentWidth = inputWidth;
370 auto prevOperandLID = getOpLID(op0);
371 for (
size_t i = 1; i < count; ++i) {
372 currentWidth += inputWidth;
374 genSort(
"bitvec", currentWidth);
376 auto thisLid = lid++;
379 <<
" " << sortToLIDMap.at(currentWidth) <<
" " << prevOperandLID <<
" "
380 << getOpLID(op0) <<
"\n";
381 prevOperandLID = thisLid;
385 opLIDMap[srcop] = prevOperandLID;
389 void genUnaryOp(Operation *srcop, Operation *op0, StringRef inst,
392 size_t opLID = getOpLID(srcop);
395 size_t sid = sortToLIDMap.at(width);
399 size_t op0LID = getOpLID(op0);
401 os << opLID <<
" " << inst <<
" " << sid <<
" " << op0LID <<
"\n";
406 void genUnaryOp(Operation *srcop, Value op0, StringRef inst,
size_t width) {
407 genUnaryOp(srcop, op0.getDefiningOp(), inst, width);
411 size_t genUnaryOp(
size_t op0LID, StringRef inst,
size_t width) {
413 size_t curLid = lid++;
416 size_t sid = sortToLIDMap.at(width);
418 os << curLid <<
" " << inst <<
" " << sid <<
" " << op0LID <<
"\n";
423 size_t genUnaryOp(Operation *op0, StringRef inst,
size_t width) {
424 return genUnaryOp(getOpLID(op0), inst, width);
429 size_t genUnaryOp(Value op0, StringRef inst,
size_t width) {
430 return genUnaryOp(getOpLID(op0), inst, width);
436 void genBad(Operation *assertop) {
438 size_t assertLID = getOpLID(assertop);
445 void genBad(
size_t assertLID) {
450 <<
" " << assertLID <<
"\n";
455 void genConstraint(Value expr) {
457 size_t exprLID = getOpLID(expr);
459 genConstraint(exprLID);
464 void genConstraint(
size_t exprLID) {
469 <<
" " << exprLID <<
"\n";
474 void genIte(Operation *srcop, Value cond, Value t, Value f, int64_t width) {
476 size_t condLID = getOpLID(cond);
477 size_t tLID = getOpLID(t);
478 size_t fLID = getOpLID(f);
480 genIte(srcop, condLID, tLID, fLID, width);
485 void genIte(Operation *srcop,
size_t condLID,
size_t tLID,
size_t fLID,
488 size_t opLID = getOpLID(srcop);
491 size_t sid = sortToLIDMap.at(width);
496 <<
" " << sid <<
" " << condLID <<
" " << tLID <<
" " << fLID <<
"\n";
500 size_t genImplies(Operation *srcop, Value lhs, Value rhs) {
502 size_t lhsLID = getOpLID(lhs);
503 size_t rhsLID = getOpLID(rhs);
505 return genImplies(srcop, lhsLID, rhsLID);
509 size_t genImplies(Operation *srcop,
size_t lhsLID,
size_t rhsLID) {
511 size_t opLID = getOpLID(srcop);
512 return genImplies(opLID, lhsLID, rhsLID);
515 size_t genImplies(
size_t opLID,
size_t lhsLID,
size_t rhsLID) {
517 size_t sid = sortToLIDMap.at(1);
521 <<
" " << sid <<
" " << lhsLID <<
" " << rhsLID <<
"\n";
526 void genState(Operation *srcop, int64_t width, StringRef name) {
528 size_t opLID = getOpLID(srcop);
531 size_t sid = sortToLIDMap.at(width);
536 <<
" " << sid <<
" " << name <<
"\n";
541 void genNext(Value next, Operation *
reg, int64_t width) {
543 size_t sid = sortToLIDMap.at(width);
546 size_t regLID = getOpLID(
reg);
547 size_t nextLID = getOpLID(next);
553 <<
" " << sid <<
" " << regLID <<
" " << nextLID <<
"\n";
558 int64_t requireSort(mlir::Type type) {
560 int64_t width = hw::getBitWidth(type);
569 genSort(
"bitvec", width);
575 Value extractRegNext(seq::FirRegOp
reg)
const {
return reg.getNext(); }
578 template <
typename RegT>
579 void extractRegArgs(RegT
reg, int64_t &width, Value &next, Value &reset,
580 Value &resetVal, Value &
clk)
const {
581 width = hw::getBitWidth(
reg.getType());
582 reset =
reg.getReset();
583 resetVal =
reg.getResetValue();
587 next = extractRegNext(
reg);
592 void finalizeRegVisit(Operation *op) {
594 Value next, reset, resetVal,
clk;
597 auto extract = TypeSwitch<Operation *, LogicalResult>(op)
599 extractRegArgs(
reg, width, next, reset, resetVal,
clk);
603 op->emitError(
"Invalid register operation !");
613 if (
clk != foundClock) {
614 op->emitError(
"Multi-clock designs are not currently supported.");
621 genSort(
"bitvec", width);
626 size_t nextLID = noLID;
630 if (BlockArgument barg = dyn_cast<BlockArgument>(next)) {
632 size_t argIdx = barg.getArgNumber();
635 nextLID = inputLIDs[argIdx];
638 nextLID = getOpLID(next);
643 size_t resetValLID = noLID;
647 size_t resetLID = noLID;
648 if (BlockArgument barg = dyn_cast<BlockArgument>(reset)) {
651 size_t argIdx = barg.getArgNumber();
654 resetLID = inputLIDs[argIdx];
657 resetLID = getOpLID(reset);
662 resetValLID = getOpLID(resetVal.getDefiningOp());
664 resetValLID = genZero(width);
667 setOpLID(next.getDefiningOp());
676 genIte(next.getDefiningOp(), resetLID, resetValLID, nextLID, width);
679 if (nextLID == noLID) {
680 next.getDefiningOp()->emitError(
681 "Register input does not point to a valid op!");
687 genNext(next, op, width);
693 void ignore(Operation *op) {}
705 if (port.
isInput() && !isa<seq::ClockType, seq::ImmutableType>(port.
type)) {
707 StringRef iName = port.
getName();
711 int64_t w = requireSort(port.
type);
718 inputLIDs[port.
argNum] = lid;
723 genInput(inlid, w, iName);
732 if (handledOps.contains(op))
736 int64_t w = requireSort(op.getType());
740 genConst(op.getValue(), w, op);
744 void visit(hw::WireOp op) {
745 op->emitError(
"Wires are not supported in btor!");
746 return signalPassFailure();
749 void visitTypeOp(Operation *op) { visitInvalidTypeOp(op); }
752 void visitInvalidTypeOp(Operation *op) {
754 dispatchCombinationalVisitor(op);
759 template <
typename Op>
760 void visitBinOp(Op op, StringRef inst) {
762 int64_t w = requireSort(op.getType());
765 Value op1 = op.getOperand(0);
766 Value op2 = op.getOperand(1);
769 genBinOp(inst, op, op1, op2, w);
772 template <
typename Op>
773 void visitVariadicOp(Op op, StringRef inst) {
775 int64_t w = requireSort(op.getType());
778 genVariadicOp(inst, op, w);
782 void visitComb(
comb::AddOp op) { visitVariadicOp(op,
"add"); }
783 void visitComb(
comb::SubOp op) { visitBinOp(op,
"sub"); }
784 void visitComb(
comb::MulOp op) { visitVariadicOp(op,
"mul"); }
785 void visitComb(
comb::DivSOp op) { visitBinOp(op,
"sdiv"); }
786 void visitComb(
comb::DivUOp op) { visitBinOp(op,
"udiv"); }
787 void visitComb(
comb::ModSOp op) { visitBinOp(op,
"smod"); }
788 void visitComb(
comb::ShlOp op) { visitBinOp(op,
"sll"); }
789 void visitComb(
comb::ShrUOp op) { visitBinOp(op,
"srl"); }
790 void visitComb(
comb::ShrSOp op) { visitBinOp(op,
"sra"); }
791 void visitComb(
comb::AndOp op) { visitVariadicOp(op,
"and"); }
792 void visitComb(
comb::OrOp op) { visitVariadicOp(op,
"or"); }
793 void visitComb(
comb::XorOp op) { visitVariadicOp(op,
"xor"); }
794 void visitComb(
comb::ConcatOp op) { visitVariadicOp(op,
"concat"); }
799 int64_t w = requireSort(op.getType());
803 Value op0 = op.getOperand();
804 size_t lb = op.getLowBit();
807 genSlice(op, op0, lb, w);
813 void visitComb(comb::ICmpOp op) {
814 Value lhs = op.getOperand(0);
815 Value rhs = op.getOperand(1);
819 StringRef pred = stringifyICmpPredicate(op.getPredicate());
824 else if (pred ==
"ule")
826 else if (pred ==
"sle")
828 else if (pred ==
"uge")
830 else if (pred ==
"sge")
834 genSort(
"bitvec", 1);
838 genBinOp(pred, op, lhs, rhs, 1);
844 Value pred = op.getCond();
845 Value tval = op.getTrueValue();
846 Value fval = op.getFalseValue();
850 int64_t w = requireSort(op.getType());
853 genIte(op, pred, tval, fval, w);
857 void visitComb(comb::ReplicateOp op) {
858 Value op0 = op.getOperand();
859 auto count = op.getMultiple();
860 auto inputWidth = op0.getType().getIntOrFloatBitWidth();
863 genReplicateAsConcats(op, op0, count, inputWidth);
866 void visitComb(Operation *op) { visitInvalidComb(op); }
869 void visitInvalidComb(Operation *op) { dispatchSVVisitor(op); }
872 void visitSV(sv::AssertOp op) {
874 Value expr = op.getExpression();
877 genSort(
"bitvec", 1);
883 if (
auto ifop = dyn_cast<sv::IfOp>(((Operation *)op)->getParentOp())) {
884 Value
en = ifop.getOperand();
887 genImplies(ifop,
en, expr);
890 genUnaryOp(op, ifop,
"not", 1);
893 genUnaryOp(op, expr,
"not", 1);
900 void visitSV(sv::AssumeOp op) {
902 Value expr = op.getExpression();
907 void visitSV(sv::AlwaysOp op) {
908 if (op.getEvents().size() > 1) {
909 op->emitError(
"Multiple events in sv.always are not supported.");
910 return signalPassFailure();
913 auto cond = op.getCondition(0);
915 if (cond.event != sv::EventControl::AtPosEdge) {
916 op->emitError(
"Only posedge clocking is supported in sv.always.");
917 return signalPassFailure();
920 if (isa<BlockArgument>(cond.value) ||
921 !isa<seq::FromClockOp>(cond.value.getDefiningOp())) {
922 op->emitError(
"This pass only currently supports sv.always ops that use "
923 "a top-level seq.clock input (converted using "
924 "seq.from_clock) as their clock.");
925 return signalPassFailure();
930 auto clk = cond.value.getDefiningOp()->getOperand(0);
932 if (
clk != foundClock) {
933 op->emitError(
"Multi-clock designs are not currently supported.");
934 return signalPassFailure();
941 void visitSV(Operation *op) { visitInvalidSV(op); }
944 void visitInvalidSV(Operation *op) { dispatchVerifVisitor(op); }
946 template <
typename Op>
947 void visitAssertLike(Op op) {
949 Value prop = op.getProperty();
950 Value
en = op.getEnable();
953 genSort(
"bitvec", 1);
955 size_t assertLID = noLID;
959 genImplies(op,
en, prop);
962 assertLID = genUnaryOp(op,
"not", 1);
965 assertLID = genUnaryOp(prop.getDefiningOp(),
"not", 1);
972 template <
typename Op>
973 void visitAssumeLike(Op op) {
975 Value prop = op.getProperty();
976 Value
en = op.getEnable();
978 size_t assumeLID = getOpLID(prop);
982 genSort(
"bitvec", 1);
984 assumeLID = genImplies(op,
en, prop);
988 genConstraint(assumeLID);
993 void visitVerif(verif::AssertOp op) { visitAssertLike(op); }
994 void visitVerif(verif::ClockedAssertOp op) { visitAssertLike(op); }
998 void visitVerif(verif::AssumeOp op) { visitAssumeLike(op); }
999 void visitVerif(verif::ClockedAssumeOp op) { visitAssumeLike(op); }
1002 void visitUnhandledVerif(Operation *op) {
1003 op->emitError(
"not supported in btor2!");
1004 return signalPassFailure();
1008 void visitInvalidVerif(Operation *op) { visit(op); }
1012 void visit(Operation *op) {
1015 TypeSwitch<Operation *, void>(op)
1016 .Case<seq::FirRegOp,
seq::CompRegOp, seq::FromClockOp, seq::ToClockOp>(
1017 [&](
auto expr) { visit(expr); })
1018 .Default([&](
auto expr) { visitUnsupportedOp(op); });
1024 void visit(seq::FirRegOp
reg) {
1026 StringRef regName =
reg.getName();
1027 auto type =
reg.getType();
1028 if (!isa<mlir::IntegerType>(type)) {
1029 reg.emitError(
"Only integer typed seq.firregs are supported in BTOR2.");
1030 return signalPassFailure();
1032 int64_t w = requireSort(type);
1035 genState(
reg, w, regName);
1040 regOps.push_back(
reg);
1046 StringRef regName =
reg.getName().value();
1047 auto type =
reg.getType();
1048 if (!isa<mlir::IntegerType>(type)) {
1049 reg.emitError(
"Only integer typed seq.compregs are supported in BTOR2.");
1050 return signalPassFailure();
1052 int64_t w = requireSort(type);
1056 auto init =
reg.getInitialValue();
1057 auto resetVal =
reg.getResetValue();
1062 auto shouldInitReset = assumeInitReset && resetVal;
1065 if (init || shouldInitReset) {
1069 if (shouldInitReset) {
1071 if (!initialConstant) {
1073 "Reset value must be emitted directly by a hw.constant "
1074 "op when --assume-init-reset is in use.");
1078 if (!init.getDefiningOp<seq::InitialOp>()) {
1080 "Initial value must be emitted directly by a seq.initial op");
1086 if (!initialConstant)
1087 reg->emitError(
"initial value must be constant");
1091 dispatchTypeOpVisitor(initialConstant);
1094 handledOps.insert(initialConstant);
1097 genState(
reg, w, regName);
1100 genInit(
reg, initialConstant, w);
1103 genState(
reg, w, regName);
1109 regOps.push_back(
reg);
1112 void visit(seq::FromClockOp op) {
1113 for (
auto *user : op->getResult(0).getUsers()) {
1114 if (!isa<sv::AlwaysOp, verif::ClockedAssertOp>(user)) {
1115 op->emitError(
"This pass only supports seq.from_clock results being "
1116 "used by sv.always and verif.clocked_assert operations.");
1117 signalPassFailure();
1122 void visit(seq::ToClockOp op) {
1124 if (!isa<BlockArgument>(op.getInput())) {
1125 op->emitError(
"This pass only supports seq.to_clock operations that take "
1126 "a top-level input as their argument.");
1130 for (
auto *user : op->getResult(0).getUsers())
1131 if (!isa<seq::FirRegOp, seq::CompRegOp>(user)) {
1132 op->emitError(
"This pass only supports seq.to_clock results being "
1133 "used by seq.firreg and seq.compreg operations.");
1134 signalPassFailure();
1140 void visitUnsupportedOp(Operation *op) {
1143 TypeSwitch<Operation *, void>(op)
1145 .Case<sv::MacroDefOp, sv::MacroDeclOp, sv::VerbatimOp,
1146 sv::VerbatimExprOp, sv::VerbatimExprSEOp, sv::IfOp,
sv::IfDefOp,
1147 sv::IfDefProceduralOp, sv::AlwaysCombOp, seq::InitialOp,
1148 sv::AlwaysFFOp, seq::InitialOp, seq::YieldOp, hw::OutputOp,
1152 verif::FormatVerilogStringOp, verif::PrintOp>(
1153 [&](
auto expr) { ignore(op); })
1157 .Default([&](
auto expr) {
1158 op->emitOpError(
"is an unsupported operation");
1159 return signalPassFailure();
1165void ConvertHWToBTOR2Pass::runOnOperation() {
1173 if (port.isInput()) {
1174 auto portVal =
module.getArgumentForInput(port.argNum);
1176 llvm::any_of(portVal.getUsers(), [](Operation *user) {
1177 return isa<seq::ToClockOp>(user);
1182 if (portVal.getNumUses() > 1) {
1184 "Inputs converted to clocks may only have one user.");
1185 return signalPassFailure();
1195 module.walk([&](Operation *op) {
1196 TypeSwitch<Operation *, void>(op)
1197 .Case<seq::FirRegOp, seq::CompRegOp>([&](auto reg) {
1199 handledOps.insert(op);
1201 .Default([&](
auto expr) {});
1205 module.walk([&](Operation *op) {
1207 if (isa<hw::InstanceOp>(op)) {
1208 op->emitOpError("not supported in BTOR2 conversion");
1213 if (handledOps.contains(op))
1217 worklist.insert({op, op->operand_begin()});
1220 while (!worklist.empty()) {
1221 auto &[op, operandIt] = worklist.back();
1222 if (operandIt == op->operand_end()) {
1224 dispatchTypeOpVisitor(op);
1227 handledOps.insert(op);
1228 worklist.pop_back();
1234 Value operand = *(operandIt++);
1235 auto *defOp = operand.getDefiningOp();
1238 if (!defOp || handledOps.contains(defOp))
1243 if (!worklist.insert({defOp, defOp->operand_begin()}).second) {
1244 defOp->emitError(
"dependency cycle");
1251 for (
size_t i = 0; i < regOps.size(); ++i) {
1252 finalizeRegVisit(regOps[i]);
1256 sortToLIDMap.clear();
1257 constToLIDMap.clear();
1266std::unique_ptr<mlir::Pass>
1268 return std::make_unique<ConvertHWToBTOR2Pass>(os);
1273 return std::make_unique<ConvertHWToBTOR2Pass>(llvm::outs());
assert(baseType &&"element must be base type")
static SmallVector< PortInfo > getPortList(ModuleTy &mod)
This helps visit Combinational nodes.
This helps visit TypeOp nodes.
Value unwrapImmutableValue(mlir::TypedValue< seq::ImmutableType > immutableVal)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createConvertHWToBTOR2Pass()
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
This holds the name, type, direction of a module's ports.
StringRef getName() const
size_t argNum
This is the argument index or the result index depending on the direction.