32#include "mlir/Pass/Pass.h"
33#include "llvm/ADT/MapVector.h"
34#include "llvm/ADT/TypeSwitch.h"
35#include "llvm/Support/raw_ostream.h"
38#define GEN_PASS_DEF_CONVERTHWTOBTOR2
39#include "circt/Conversion/Passes.h.inc"
48struct ConvertHWToBTOR2Pass
49 :
public circt::impl::ConvertHWToBTOR2Base<ConvertHWToBTOR2Pass>,
57 ConvertHWToBTOR2Pass(raw_ostream &os) : os(os) {}
59 void runOnOperation()
override;
75 DenseMap<size_t, size_t> sortToLIDMap;
80 DenseMap<APInt, size_t> constToLIDMap;
85 DenseMap<Operation *, size_t> opLIDMap;
89 DenseMap<size_t, size_t> inputLIDs;
95 SmallVector<Operation *> regOps;
99 llvm::SmallMapVector<Operation *, OperandRange::iterator, 16> worklist;
102 DenseSet<Operation *> handledOps;
105 static constexpr size_t noLID = -1UL;
106 [[maybe_unused]]
static constexpr int64_t noWidth = -1L;
113 size_t getOpLID(Operation *op) {
116 Operation *defOp = op;
117 auto &f = opLIDMap[defOp];
127 size_t setOpLID(Operation *op) {
128 size_t oplid = lid++;
129 opLIDMap[op] = oplid;
136 size_t getOpLID(Value value) {
137 Operation *defOp = value.getDefiningOp();
139 if (
auto it = opLIDMap.find(defOp); it != opLIDMap.end())
144 if (BlockArgument barg = dyn_cast<BlockArgument>(value)) {
146 size_t argIdx = barg.getArgNumber();
149 if (
auto it = inputLIDs.find(argIdx); it != inputLIDs.end())
161 size_t getSortLID(
size_t w) {
162 if (
auto it = sortToLIDMap.find(w); it != sortToLIDMap.end())
170 size_t setSortLID(
size_t w) {
171 size_t sortlid = lid;
173 sortToLIDMap[w] = lid++;
180 size_t getConstLID(int64_t val,
size_t w) {
181 if (
auto it = constToLIDMap.find(APInt(w, val)); it != constToLIDMap.end())
189 size_t setConstLID(int64_t val,
size_t w) {
190 size_t constlid = lid;
192 constToLIDMap[APInt(w, val)] = lid++;
200 void genSort(StringRef type,
size_t width) {
202 if (getSortLID(width) != noLID) {
206 size_t sortlid = setSortLID(width);
211 <<
" " << type <<
" " << width <<
"\n";
215 void genInput(
size_t inlid,
size_t width, StringRef name) {
217 size_t sid = sortToLIDMap.at(width);
222 <<
" " << sid <<
" " << name <<
"\n";
226 void genConst(APInt value,
size_t width, Operation *op) {
229 size_t opLID = getOpLID(op);
232 size_t sid = sortToLIDMap.at(width);
236 <<
" " << sid <<
" " << value <<
"\n";
240 size_t genZero(
size_t width) {
242 size_t zlid = getConstLID(0, width);
247 size_t sid = sortToLIDMap.at(width);
250 size_t constlid = setConstLID(0, width);
253 os << constlid <<
" "
255 <<
" " << sid <<
"\n";
261 void genInit(Operation *
reg, Value initVal, int64_t width) {
263 size_t regLID = getOpLID(
reg);
264 size_t sid = sortToLIDMap.at(width);
265 size_t initValLID = getOpLID(initVal);
271 <<
" " << sid <<
" " << regLID <<
" " << initValLID <<
"\n";
276 void genBinOp(StringRef inst, Operation *binop, Value op1, Value op2,
279 if (binop->getNumOperands() != 2) {
280 binop->emitError(
"variadic operations not are not currently supported");
285 size_t opLID = getOpLID(binop);
288 size_t sid = sortToLIDMap.at(width);
292 size_t op1LID = getOpLID(op1);
293 size_t op2LID = getOpLID(op2);
296 os << opLID <<
" " << inst <<
" " << sid <<
" " << op1LID <<
" " << op2LID
301 void genSlice(Operation *srcop, Value op0,
size_t lowbit, int64_t width) {
303 size_t opLID = getOpLID(srcop);
306 size_t sid = sortToLIDMap.at(width);
310 size_t op0LID = getOpLID(op0);
315 <<
" " << sid <<
" " << op0LID <<
" " << (lowbit + width - 1) <<
" "
320 void genUnaryOp(Operation *srcop, Operation *op0, StringRef inst,
323 size_t opLID = getOpLID(srcop);
326 size_t sid = sortToLIDMap.at(width);
330 size_t op0LID = getOpLID(op0);
332 os << opLID <<
" " << inst <<
" " << sid <<
" " << op0LID <<
"\n";
337 void genUnaryOp(Operation *srcop, Value op0, StringRef inst,
size_t width) {
338 genUnaryOp(srcop, op0.getDefiningOp(), inst, width);
342 size_t genUnaryOp(
size_t op0LID, StringRef inst,
size_t width) {
344 size_t curLid = lid++;
347 size_t sid = sortToLIDMap.at(width);
349 os << curLid <<
" " << inst <<
" " << sid <<
" " << op0LID <<
"\n";
354 size_t genUnaryOp(Operation *op0, StringRef inst,
size_t width) {
355 return genUnaryOp(getOpLID(op0), inst, width);
360 size_t genUnaryOp(Value op0, StringRef inst,
size_t width) {
361 return genUnaryOp(getOpLID(op0), inst, width);
367 void genBad(Operation *assertop) {
369 size_t assertLID = getOpLID(assertop);
376 void genBad(
size_t assertLID) {
381 <<
" " << assertLID <<
"\n";
386 void genConstraint(Value expr) {
388 size_t exprLID = getOpLID(expr);
390 genConstraint(exprLID);
395 void genConstraint(
size_t exprLID) {
400 <<
" " << exprLID <<
"\n";
405 void genIte(Operation *srcop, Value cond, Value t, Value f, int64_t width) {
407 size_t condLID = getOpLID(cond);
408 size_t tLID = getOpLID(t);
409 size_t fLID = getOpLID(f);
411 genIte(srcop, condLID, tLID, fLID, width);
416 void genIte(Operation *srcop,
size_t condLID,
size_t tLID,
size_t fLID,
419 size_t opLID = getOpLID(srcop);
422 size_t sid = sortToLIDMap.at(width);
427 <<
" " << sid <<
" " << condLID <<
" " << tLID <<
" " << fLID <<
"\n";
431 size_t genImplies(Operation *srcop, Value lhs, Value rhs) {
433 size_t lhsLID = getOpLID(lhs);
434 size_t rhsLID = getOpLID(rhs);
436 return genImplies(srcop, lhsLID, rhsLID);
440 size_t genImplies(Operation *srcop,
size_t lhsLID,
size_t rhsLID) {
442 size_t opLID = getOpLID(srcop);
443 return genImplies(opLID, lhsLID, rhsLID);
446 size_t genImplies(
size_t opLID,
size_t lhsLID,
size_t rhsLID) {
448 size_t sid = sortToLIDMap.at(1);
452 <<
" " << sid <<
" " << lhsLID <<
" " << rhsLID <<
"\n";
457 void genState(Operation *srcop, int64_t width, StringRef name) {
459 size_t opLID = getOpLID(srcop);
462 size_t sid = sortToLIDMap.at(width);
467 <<
" " << sid <<
" " << name <<
"\n";
472 void genNext(Value next, Operation *
reg, int64_t width) {
474 size_t sid = sortToLIDMap.at(width);
477 size_t regLID = getOpLID(
reg);
478 size_t nextLID = getOpLID(next);
484 <<
" " << sid <<
" " << regLID <<
" " << nextLID <<
"\n";
489 int64_t requireSort(mlir::Type type) {
491 int64_t width = hw::getBitWidth(type);
500 genSort(
"bitvec", width);
506 void finalizeRegVisit(Operation *op) {
508 Value next, reset, resetVal;
511 if (
auto reg = dyn_cast<seq::CompRegOp>(op)) {
512 width = hw::getBitWidth(
reg.getType());
513 next =
reg.getInput();
514 reset =
reg.getReset();
515 resetVal =
reg.getResetValue();
516 }
else if (
auto reg = dyn_cast<seq::FirRegOp>(op)) {
517 width = hw::getBitWidth(
reg.getType());
518 next =
reg.getNext();
519 reset =
reg.getReset();
520 resetVal =
reg.getResetValue();
522 op->emitError(
"Invalid register operation !");
526 genSort(
"bitvec", width);
531 size_t nextLID = noLID;
535 if (BlockArgument barg = dyn_cast<BlockArgument>(next)) {
537 size_t argIdx = barg.getArgNumber();
540 nextLID = inputLIDs[argIdx];
543 nextLID = getOpLID(next);
548 size_t resetValLID = noLID;
552 size_t resetLID = noLID;
553 if (BlockArgument barg = dyn_cast<BlockArgument>(reset)) {
556 size_t argIdx = barg.getArgNumber();
559 resetLID = inputLIDs[argIdx];
562 resetLID = getOpLID(reset);
567 resetValLID = getOpLID(resetVal.getDefiningOp());
569 resetValLID = genZero(width);
572 setOpLID(next.getDefiningOp());
581 genIte(next.getDefiningOp(), resetLID, resetValLID, nextLID, width);
584 if (nextLID == noLID) {
585 next.getDefiningOp()->emitError(
586 "Register input does not point to a valid op!");
592 genNext(next, op, width);
606 if (port.
isInput() && !isa<seq::ClockType, seq::ImmutableType>(port.
type)) {
608 StringRef iName = port.
getName();
612 int64_t w = requireSort(port.
type);
619 inputLIDs[port.
argNum] = lid;
624 genInput(inlid, w, iName);
633 if (handledOps.contains(op))
637 int64_t w = requireSort(op.getType());
641 genConst(op.getValue(), w, op);
645 void visit(hw::WireOp op) {
646 op->emitError(
"Wires are not supported in btor!");
647 return signalPassFailure();
650 void visitTypeOp(Operation *op) { visitInvalidTypeOp(op); }
653 void visitInvalidTypeOp(Operation *op) {
655 dispatchCombinationalVisitor(op);
660 template <
typename Op>
661 void visitBinOp(Op op, StringRef inst) {
663 int64_t w = requireSort(op.getType());
666 Value op1 = op.getOperand(0);
667 Value op2 = op.getOperand(1);
670 genBinOp(inst, op, op1, op2, w);
674 void visitComb(
comb::AddOp op) { visitBinOp(op,
"add"); }
675 void visitComb(
comb::SubOp op) { visitBinOp(op,
"sub"); }
676 void visitComb(
comb::MulOp op) { visitBinOp(op,
"mul"); }
677 void visitComb(
comb::DivSOp op) { visitBinOp(op,
"sdiv"); }
678 void visitComb(
comb::DivUOp op) { visitBinOp(op,
"udiv"); }
679 void visitComb(
comb::ModSOp op) { visitBinOp(op,
"smod"); }
680 void visitComb(
comb::ShlOp op) { visitBinOp(op,
"sll"); }
681 void visitComb(
comb::ShrUOp op) { visitBinOp(op,
"srl"); }
682 void visitComb(
comb::ShrSOp op) { visitBinOp(op,
"sra"); }
683 void visitComb(
comb::AndOp op) { visitBinOp(op,
"and"); }
684 void visitComb(
comb::OrOp op) { visitBinOp(op,
"or"); }
685 void visitComb(
comb::XorOp op) { visitBinOp(op,
"xor"); }
691 int64_t w = requireSort(op.getType());
695 Value op0 = op.getOperand();
696 size_t lb = op.getLowBit();
699 genSlice(op, op0, lb, w);
705 void visitComb(comb::ICmpOp op) {
706 Value lhs = op.getOperand(0);
707 Value rhs = op.getOperand(1);
711 StringRef pred = stringifyICmpPredicate(op.getPredicate());
716 else if (pred ==
"ule")
718 else if (pred ==
"sle")
720 else if (pred ==
"uge")
722 else if (pred ==
"sge")
726 genSort(
"bitvec", 1);
730 genBinOp(pred, op, lhs, rhs, 1);
736 Value pred = op.getCond();
737 Value tval = op.getTrueValue();
738 Value fval = op.getFalseValue();
742 int64_t w = requireSort(op.getType());
745 genIte(op, pred, tval, fval, w);
748 void visitComb(Operation *op) { visitInvalidComb(op); }
751 void visitInvalidComb(Operation *op) { dispatchSVVisitor(op); }
754 void visitSV(sv::AssertOp op) {
756 Value expr = op.getExpression();
759 genSort(
"bitvec", 1);
765 if (
auto ifop = dyn_cast<sv::IfOp>(((Operation *)op)->getParentOp())) {
766 Value
en = ifop.getOperand();
769 genImplies(ifop,
en, expr);
772 genUnaryOp(op, ifop,
"not", 1);
775 genUnaryOp(op, expr,
"not", 1);
782 void visitSV(sv::AssumeOp op) {
784 Value expr = op.getExpression();
788 void visitSV(Operation *op) { visitInvalidSV(op); }
791 void visitInvalidSV(Operation *op) { dispatchVerifVisitor(op); }
793 template <
typename Op>
794 void visitAssertLike(Op op) {
796 Value prop = op.getProperty();
797 Value
en = op.getEnable();
800 genSort(
"bitvec", 1);
802 size_t assertLID = noLID;
806 genImplies(op,
en, prop);
809 assertLID = genUnaryOp(op,
"not", 1);
812 assertLID = genUnaryOp(prop.getDefiningOp(),
"not", 1);
819 template <
typename Op>
820 void visitAssumeLike(Op op) {
822 Value prop = op.getProperty();
823 Value
en = op.getEnable();
825 size_t assumeLID = getOpLID(prop);
829 genSort(
"bitvec", 1);
831 assumeLID = genImplies(op,
en, prop);
835 genConstraint(assumeLID);
840 void visitVerif(verif::AssertOp op) { visitAssertLike(op); }
841 void visitVerif(verif::ClockedAssertOp op) { visitAssertLike(op); }
845 void visitVerif(verif::AssumeOp op) { visitAssumeLike(op); }
846 void visitVerif(verif::ClockedAssumeOp op) { visitAssumeLike(op); }
848 void visitUnhandledVerif(Operation *op) {
849 op->emitError(
"not supported in btor2!");
850 return signalPassFailure();
853 void visitInvalidVerif(Operation *op) { visit(op); }
857 void visit(Operation *op) {
860 TypeSwitch<Operation *, void>(op)
861 .Case<seq::FirRegOp,
seq::CompRegOp>([&](
auto expr) { visit(expr); })
862 .Default([&](
auto expr) { visitUnsupportedOp(op); });
868 void visit(seq::FirRegOp
reg) {
870 StringRef regName =
reg.getName();
871 int64_t w = requireSort(
reg.getType());
874 genState(
reg, w, regName);
879 regOps.push_back(
reg);
885 StringRef regName =
reg.getName().value();
886 int64_t w = requireSort(
reg.getType());
890 auto init =
reg.getInitialValue();
896 if (!init.getDefiningOp<seq::InitialOp>()) {
898 "Initial value must be emitted directly by a seq.initial op");
904 if (!initialConstant)
905 reg->emitError(
"initial value must be constant");
908 dispatchTypeOpVisitor(initialConstant);
911 handledOps.insert(initialConstant);
914 genState(
reg, w, regName);
917 genInit(
reg, initialConstant, w);
920 genState(
reg, w, regName);
926 regOps.push_back(
reg);
931 void ignore(Operation *op) {}
935 void visitUnsupportedOp(Operation *op) {
938 TypeSwitch<Operation *, void>(op)
940 .Case<sv::MacroDefOp, sv::MacroDeclOp, sv::VerbatimOp,
941 sv::VerbatimExprOp, sv::VerbatimExprSEOp, sv::IfOp,
sv::IfDefOp,
942 sv::IfDefProceduralOp, sv::AlwaysOp, sv::AlwaysCombOp,
943 seq::InitialOp, sv::AlwaysFFOp, seq::FromClockOp, seq::InitialOp,
945 [&](
auto expr) { ignore(op); })
948 .Case<seq::FromClockOp>([&](
auto expr) {
949 if (++nclocks > 1UL) {
950 op->emitOpError(
"Mutli-clock designs are not supported!");
951 return signalPassFailure();
957 .Default([&](
auto expr) {
958 op->emitOpError(
"is an unsupported operation");
959 return signalPassFailure();
965void ConvertHWToBTOR2Pass::runOnOperation() {
976 module.walk([&](Operation *op) {
977 TypeSwitch<Operation *, void>(op)
978 .Case<seq::FirRegOp, seq::CompRegOp>([&](auto reg) {
980 handledOps.insert(op);
982 .Default([&](
auto expr) {});
986 module.walk([&](Operation *op) {
988 if (isa<hw::InstanceOp>(op)) {
989 op->emitOpError("not supported in BTOR2 conversion");
994 if (handledOps.contains(op))
998 worklist.insert({op, op->operand_begin()});
1001 while (!worklist.empty()) {
1002 auto &[op, operandIt] = worklist.back();
1003 if (operandIt == op->operand_end()) {
1005 dispatchTypeOpVisitor(op);
1008 handledOps.insert(op);
1009 worklist.pop_back();
1015 Value operand = *(operandIt++);
1016 auto *defOp = operand.getDefiningOp();
1019 if (!defOp || handledOps.contains(defOp))
1024 if (!worklist.insert({defOp, defOp->operand_begin()}).second) {
1025 defOp->emitError(
"dependency cycle");
1032 for (
size_t i = 0; i < regOps.size(); ++i) {
1033 finalizeRegVisit(regOps[i]);
1037 sortToLIDMap.clear();
1038 constToLIDMap.clear();
1047std::unique_ptr<mlir::Pass>
1049 return std::make_unique<ConvertHWToBTOR2Pass>(os);
1054 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.