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,
277 if (binop->getNumOperands() != 2) {
279 "only the binary form of this operation is currently supported");
284 size_t opLID = getOpLID(binop);
287 size_t sid = sortToLIDMap.at(width);
291 size_t op1LID = getOpLID(op1);
292 size_t op2LID = getOpLID(op2);
295 os << opLID <<
" " << inst <<
" " << sid <<
" " << op1LID <<
" " << op2LID
300 void genVariadicOp(StringRef inst, Operation *op,
size_t width) {
301 auto operands = op->getOperands();
302 size_t sid = sortToLIDMap.at(width);
304 if (operands.size() < 2) {
305 op->emitError(
"variadic operations with less than 2 operands are not "
306 "currently supported");
312 if (!op->hasTrait<mlir::OpTrait::SameOperandsAndResultType>()) {
314 "variadic operations with differing operand and result types are "
315 "not currently supported");
321 auto prevOperandLID = getOpLID(operands[0]);
323 for (
auto operand : operands.drop_front()) {
325 auto thisLid = lid++;
326 auto thisOperandLID = getOpLID(operand);
327 os << thisLid <<
" " << inst <<
" " << sid <<
" " << prevOperandLID <<
" "
328 << thisOperandLID <<
"\n";
329 prevOperandLID = thisLid;
333 opLIDMap[op] = prevOperandLID;
337 void genSlice(Operation *srcop, Value op0,
size_t lowbit, int64_t width) {
339 size_t opLID = getOpLID(srcop);
342 size_t sid = sortToLIDMap.at(width);
346 size_t op0LID = getOpLID(op0);
351 <<
" " << sid <<
" " << op0LID <<
" " << (lowbit + width - 1) <<
" "
356 void genUnaryOp(Operation *srcop, Operation *op0, StringRef inst,
359 size_t opLID = getOpLID(srcop);
362 size_t sid = sortToLIDMap.at(width);
366 size_t op0LID = getOpLID(op0);
368 os << opLID <<
" " << inst <<
" " << sid <<
" " << op0LID <<
"\n";
373 void genUnaryOp(Operation *srcop, Value op0, StringRef inst,
size_t width) {
374 genUnaryOp(srcop, op0.getDefiningOp(), inst, width);
378 size_t genUnaryOp(
size_t op0LID, StringRef inst,
size_t width) {
380 size_t curLid = lid++;
383 size_t sid = sortToLIDMap.at(width);
385 os << curLid <<
" " << inst <<
" " << sid <<
" " << op0LID <<
"\n";
390 size_t genUnaryOp(Operation *op0, StringRef inst,
size_t width) {
391 return genUnaryOp(getOpLID(op0), inst, width);
396 size_t genUnaryOp(Value op0, StringRef inst,
size_t width) {
397 return genUnaryOp(getOpLID(op0), inst, width);
403 void genBad(Operation *assertop) {
405 size_t assertLID = getOpLID(assertop);
412 void genBad(
size_t assertLID) {
417 <<
" " << assertLID <<
"\n";
422 void genConstraint(Value expr) {
424 size_t exprLID = getOpLID(expr);
426 genConstraint(exprLID);
431 void genConstraint(
size_t exprLID) {
436 <<
" " << exprLID <<
"\n";
441 void genIte(Operation *srcop, Value cond, Value t, Value f, int64_t width) {
443 size_t condLID = getOpLID(cond);
444 size_t tLID = getOpLID(t);
445 size_t fLID = getOpLID(f);
447 genIte(srcop, condLID, tLID, fLID, width);
452 void genIte(Operation *srcop,
size_t condLID,
size_t tLID,
size_t fLID,
455 size_t opLID = getOpLID(srcop);
458 size_t sid = sortToLIDMap.at(width);
463 <<
" " << sid <<
" " << condLID <<
" " << tLID <<
" " << fLID <<
"\n";
467 size_t genImplies(Operation *srcop, Value lhs, Value rhs) {
469 size_t lhsLID = getOpLID(lhs);
470 size_t rhsLID = getOpLID(rhs);
472 return genImplies(srcop, lhsLID, rhsLID);
476 size_t genImplies(Operation *srcop,
size_t lhsLID,
size_t rhsLID) {
478 size_t opLID = getOpLID(srcop);
479 return genImplies(opLID, lhsLID, rhsLID);
482 size_t genImplies(
size_t opLID,
size_t lhsLID,
size_t rhsLID) {
484 size_t sid = sortToLIDMap.at(1);
488 <<
" " << sid <<
" " << lhsLID <<
" " << rhsLID <<
"\n";
493 void genState(Operation *srcop, int64_t width, StringRef name) {
495 size_t opLID = getOpLID(srcop);
498 size_t sid = sortToLIDMap.at(width);
503 <<
" " << sid <<
" " << name <<
"\n";
508 void genNext(Value next, Operation *
reg, int64_t width) {
510 size_t sid = sortToLIDMap.at(width);
513 size_t regLID = getOpLID(
reg);
514 size_t nextLID = getOpLID(next);
520 <<
" " << sid <<
" " << regLID <<
" " << nextLID <<
"\n";
525 int64_t requireSort(mlir::Type type) {
527 int64_t width = hw::getBitWidth(type);
536 genSort(
"bitvec", width);
542 void finalizeRegVisit(Operation *op) {
544 Value next, reset, resetVal;
547 if (
auto reg = dyn_cast<seq::CompRegOp>(op)) {
548 width = hw::getBitWidth(
reg.getType());
549 next =
reg.getInput();
550 reset =
reg.getReset();
551 resetVal =
reg.getResetValue();
552 }
else if (
auto reg = dyn_cast<seq::FirRegOp>(op)) {
553 width = hw::getBitWidth(
reg.getType());
554 next =
reg.getNext();
555 reset =
reg.getReset();
556 resetVal =
reg.getResetValue();
558 op->emitError(
"Invalid register operation !");
562 genSort(
"bitvec", width);
567 size_t nextLID = noLID;
571 if (BlockArgument barg = dyn_cast<BlockArgument>(next)) {
573 size_t argIdx = barg.getArgNumber();
576 nextLID = inputLIDs[argIdx];
579 nextLID = getOpLID(next);
584 size_t resetValLID = noLID;
588 size_t resetLID = noLID;
589 if (BlockArgument barg = dyn_cast<BlockArgument>(reset)) {
592 size_t argIdx = barg.getArgNumber();
595 resetLID = inputLIDs[argIdx];
598 resetLID = getOpLID(reset);
603 resetValLID = getOpLID(resetVal.getDefiningOp());
605 resetValLID = genZero(width);
608 setOpLID(next.getDefiningOp());
617 genIte(next.getDefiningOp(), resetLID, resetValLID, nextLID, width);
620 if (nextLID == noLID) {
621 next.getDefiningOp()->emitError(
622 "Register input does not point to a valid op!");
628 genNext(next, op, width);
642 if (port.
isInput() && !isa<seq::ClockType, seq::ImmutableType>(port.
type)) {
644 StringRef iName = port.
getName();
648 int64_t w = requireSort(port.
type);
655 inputLIDs[port.
argNum] = lid;
660 genInput(inlid, w, iName);
669 if (handledOps.contains(op))
673 int64_t w = requireSort(op.getType());
677 genConst(op.getValue(), w, op);
681 void visit(hw::WireOp op) {
682 op->emitError(
"Wires are not supported in btor!");
683 return signalPassFailure();
686 void visitTypeOp(Operation *op) { visitInvalidTypeOp(op); }
689 void visitInvalidTypeOp(Operation *op) {
691 dispatchCombinationalVisitor(op);
696 template <
typename Op>
697 void visitBinOp(Op op, StringRef inst) {
699 int64_t w = requireSort(op.getType());
702 Value op1 = op.getOperand(0);
703 Value op2 = op.getOperand(1);
706 genBinOp(inst, op, op1, op2, w);
709 template <
typename Op>
710 void visitVariadicOp(Op op, StringRef inst) {
712 int64_t w = requireSort(op.getType());
715 genVariadicOp(inst, op, w);
719 void visitComb(
comb::AddOp op) { visitVariadicOp(op,
"add"); }
720 void visitComb(
comb::SubOp op) { visitBinOp(op,
"sub"); }
721 void visitComb(
comb::MulOp op) { visitVariadicOp(op,
"mul"); }
722 void visitComb(
comb::DivSOp op) { visitBinOp(op,
"sdiv"); }
723 void visitComb(
comb::DivUOp op) { visitBinOp(op,
"udiv"); }
724 void visitComb(
comb::ModSOp op) { visitBinOp(op,
"smod"); }
725 void visitComb(
comb::ShlOp op) { visitBinOp(op,
"sll"); }
726 void visitComb(
comb::ShrUOp op) { visitBinOp(op,
"srl"); }
727 void visitComb(
comb::ShrSOp op) { visitBinOp(op,
"sra"); }
728 void visitComb(
comb::AndOp op) { visitVariadicOp(op,
"and"); }
729 void visitComb(
comb::OrOp op) { visitVariadicOp(op,
"or"); }
730 void visitComb(
comb::XorOp op) { visitVariadicOp(op,
"xor"); }
736 int64_t w = requireSort(op.getType());
740 Value op0 = op.getOperand();
741 size_t lb = op.getLowBit();
744 genSlice(op, op0, lb, w);
750 void visitComb(comb::ICmpOp op) {
751 Value lhs = op.getOperand(0);
752 Value rhs = op.getOperand(1);
756 StringRef pred = stringifyICmpPredicate(op.getPredicate());
761 else if (pred ==
"ule")
763 else if (pred ==
"sle")
765 else if (pred ==
"uge")
767 else if (pred ==
"sge")
771 genSort(
"bitvec", 1);
775 genBinOp(pred, op, lhs, rhs, 1);
781 Value pred = op.getCond();
782 Value tval = op.getTrueValue();
783 Value fval = op.getFalseValue();
787 int64_t w = requireSort(op.getType());
790 genIte(op, pred, tval, fval, w);
793 void visitComb(Operation *op) { visitInvalidComb(op); }
796 void visitInvalidComb(Operation *op) { dispatchSVVisitor(op); }
799 void visitSV(sv::AssertOp op) {
801 Value expr = op.getExpression();
804 genSort(
"bitvec", 1);
810 if (
auto ifop = dyn_cast<sv::IfOp>(((Operation *)op)->getParentOp())) {
811 Value
en = ifop.getOperand();
814 genImplies(ifop,
en, expr);
817 genUnaryOp(op, ifop,
"not", 1);
820 genUnaryOp(op, expr,
"not", 1);
827 void visitSV(sv::AssumeOp op) {
829 Value expr = op.getExpression();
833 void visitSV(Operation *op) { visitInvalidSV(op); }
836 void visitInvalidSV(Operation *op) { dispatchVerifVisitor(op); }
838 template <
typename Op>
839 void visitAssertLike(Op op) {
841 Value prop = op.getProperty();
842 Value
en = op.getEnable();
845 genSort(
"bitvec", 1);
847 size_t assertLID = noLID;
851 genImplies(op,
en, prop);
854 assertLID = genUnaryOp(op,
"not", 1);
857 assertLID = genUnaryOp(prop.getDefiningOp(),
"not", 1);
864 template <
typename Op>
865 void visitAssumeLike(Op op) {
867 Value prop = op.getProperty();
868 Value
en = op.getEnable();
870 size_t assumeLID = getOpLID(prop);
874 genSort(
"bitvec", 1);
876 assumeLID = genImplies(op,
en, prop);
880 genConstraint(assumeLID);
885 void visitVerif(verif::AssertOp op) { visitAssertLike(op); }
886 void visitVerif(verif::ClockedAssertOp op) { visitAssertLike(op); }
890 void visitVerif(verif::AssumeOp op) { visitAssumeLike(op); }
891 void visitVerif(verif::ClockedAssumeOp op) { visitAssumeLike(op); }
893 void visitUnhandledVerif(Operation *op) {
894 op->emitError(
"not supported in btor2!");
895 return signalPassFailure();
898 void visitInvalidVerif(Operation *op) { visit(op); }
902 void visit(Operation *op) {
905 TypeSwitch<Operation *, void>(op)
906 .Case<seq::FirRegOp,
seq::CompRegOp>([&](
auto expr) { visit(expr); })
907 .Default([&](
auto expr) { visitUnsupportedOp(op); });
913 void visit(seq::FirRegOp
reg) {
915 StringRef regName =
reg.getName();
916 int64_t w = requireSort(
reg.getType());
919 genState(
reg, w, regName);
924 regOps.push_back(
reg);
930 StringRef regName =
reg.getName().value();
931 int64_t w = requireSort(
reg.getType());
935 auto init =
reg.getInitialValue();
941 if (!init.getDefiningOp<seq::InitialOp>()) {
943 "Initial value must be emitted directly by a seq.initial op");
949 if (!initialConstant)
950 reg->emitError(
"initial value must be constant");
953 dispatchTypeOpVisitor(initialConstant);
956 handledOps.insert(initialConstant);
959 genState(
reg, w, regName);
962 genInit(
reg, initialConstant, w);
965 genState(
reg, w, regName);
971 regOps.push_back(
reg);
976 void ignore(Operation *op) {}
980 void visitUnsupportedOp(Operation *op) {
983 TypeSwitch<Operation *, void>(op)
985 .Case<sv::MacroDefOp, sv::MacroDeclOp, sv::VerbatimOp,
986 sv::VerbatimExprOp, sv::VerbatimExprSEOp, sv::IfOp,
sv::IfDefOp,
987 sv::IfDefProceduralOp, sv::AlwaysOp, sv::AlwaysCombOp,
988 seq::InitialOp, sv::AlwaysFFOp, seq::FromClockOp, seq::InitialOp,
990 [&](
auto expr) { ignore(op); })
993 .Case<seq::FromClockOp>([&](
auto expr) {
994 if (++nclocks > 1UL) {
995 op->emitOpError(
"Mutli-clock designs are not supported!");
996 return signalPassFailure();
1002 .Default([&](
auto expr) {
1003 op->emitOpError(
"is an unsupported operation");
1004 return signalPassFailure();
1010void ConvertHWToBTOR2Pass::runOnOperation() {
1021 module.walk([&](Operation *op) {
1022 TypeSwitch<Operation *, void>(op)
1023 .Case<seq::FirRegOp, seq::CompRegOp>([&](auto reg) {
1025 handledOps.insert(op);
1027 .Default([&](
auto expr) {});
1031 module.walk([&](Operation *op) {
1033 if (isa<hw::InstanceOp>(op)) {
1034 op->emitOpError("not supported in BTOR2 conversion");
1039 if (handledOps.contains(op))
1043 worklist.insert({op, op->operand_begin()});
1046 while (!worklist.empty()) {
1047 auto &[op, operandIt] = worklist.back();
1048 if (operandIt == op->operand_end()) {
1050 dispatchTypeOpVisitor(op);
1053 handledOps.insert(op);
1054 worklist.pop_back();
1060 Value operand = *(operandIt++);
1061 auto *defOp = operand.getDefiningOp();
1064 if (!defOp || handledOps.contains(defOp))
1069 if (!worklist.insert({defOp, defOp->operand_begin()}).second) {
1070 defOp->emitError(
"dependency cycle");
1077 for (
size_t i = 0; i < regOps.size(); ++i) {
1078 finalizeRegVisit(regOps[i]);
1082 sortToLIDMap.clear();
1083 constToLIDMap.clear();
1092std::unique_ptr<mlir::Pass>
1094 return std::make_unique<ConvertHWToBTOR2Pass>(os);
1099 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.