CIRCT 24.0.0git
Loading...
Searching...
No Matches
HWToBTOR2.cpp
Go to the documentation of this file.
1//===- HWToBTOR2.cpp - HW to BTOR2 translation ------------------*- C++ -*-===//
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// Converts a hw module to a btor2 format and prints it out
9//
10//===----------------------------------------------------------------------===//
11
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"
36
37namespace circt {
38#define GEN_PASS_DEF_CONVERTHWTOBTOR2
39#include "circt/Conversion/Passes.h.inc"
40} // namespace circt
41
42using namespace circt;
43using namespace hw;
44
45namespace {
46// The goal here is to traverse the operations in order and convert them one by
47// one into btor2
48struct ConvertHWToBTOR2Pass
49 : public circt::impl::ConvertHWToBTOR2Base<ConvertHWToBTOR2Pass>,
50 public comb::CombinationalVisitor<ConvertHWToBTOR2Pass>,
51 public sv::Visitor<ConvertHWToBTOR2Pass>,
52 public hw::TypeOpVisitor<ConvertHWToBTOR2Pass>,
53 public verif::Visitor<ConvertHWToBTOR2Pass> {
54public:
55 using verif::Visitor<ConvertHWToBTOR2Pass>::visitVerif;
56
57 ConvertHWToBTOR2Pass(raw_ostream &os) : os(os) {}
58 // Executes the pass
59 void runOnOperation() override;
60
61private:
62 // Output stream in which the btor2 will be emitted
63 raw_ostream &os;
64
65 // Create a counter that attributes a unique id to each generated btor2 line
66 size_t lid = 1; // btor2 line identifiers usually start at 1
67 Value foundClock;
68
69 // Create maps to keep track of lid associations
70 // We need these in order to reference results as operands in btor2
71
72 // Keeps track of the ids associated to each declared sort
73 // This is used in order to guarantee that sorts are unique and to allow for
74 // instructions to reference the given sorts (key: width, value: LID)
75 DenseMap<size_t, size_t> sortToLIDMap;
76 // Keeps track of {constant, width} -> LID mappings
77 // This is used in order to avoid duplicating constant declarations
78 // in the output btor2. It is also useful when tracking
79 // constants declarations that aren't tied to MLIR ops.
80 DenseMap<APInt, size_t> constToLIDMap;
81 // Keeps track of the most recent update line for each operation
82 // This allows for operations to be used throughout the btor file
83 // with their most recent expression. Btor uses unique identifiers for each
84 // instruction, so we need to have an association between those and MLIR Ops.
85 DenseMap<Operation *, size_t> opLIDMap;
86 // Map from register operations to the LIDs representing their direct inputs,
87 // including reset behaviour that we've made explicit
88 DenseMap<Operation *, size_t> regToInputLIDMap;
89 // Stores the LID of the associated input.
90 // This holds a similar function as the opLIDMap but keeps
91 // track of block argument index -> LID mappings
92 DenseMap<size_t, size_t> inputLIDs;
93 // Stores all of the register declaration ops.
94 // This allows for the emission of transition arcs for the regs
95 // to be deferred to the end of the pass.
96 // This is necessary, as we need to wait for the `next` operation to
97 // have been converted to btor2 before we can emit the transition.
98 SmallVector<Operation *> regOps;
99
100 // Used to perform a DFS search through the module to declare all operands
101 // before they are used
102 llvm::SmallMapVector<Operation *, OperandRange::iterator, 16> worklist;
103
104 // Keeps track of operations that have been declared
105 DenseSet<Operation *> handledOps;
106
107 // Constants used during the conversion
108 static constexpr size_t noLID = -1UL;
109 [[maybe_unused]] static constexpr int64_t noWidth = -1L;
110
111 // Tracks symbols in use to avoid naming conflicts
112 Namespace symbolNamespace;
113
114 /// Field helper functions
115public:
116 // Checks if an operation was declared
117 // If so, its lid will be returned
118 // Otherwise a new lid will be assigned to the op
119 size_t getOpLID(Operation *op) {
120 // Look for the original operation declaration
121 // Make sure that wires are considered when looking for an lid
122 Operation *defOp = op;
123 auto &f = opLIDMap[defOp];
124
125 // If the op isn't associated to an lid, assign it a new one
126 if (!f)
127 f = lid++;
128 return f;
129 }
130
131 // Associates the current lid to an operation
132 // The LID is then incremented to maintain uniqueness
133 size_t setOpLID(Operation *op) {
134 size_t oplid = lid++;
135 opLIDMap[op] = oplid;
136 return oplid;
137 }
138
139 // Checks if an operation was declared
140 // If so, its lid will be returned
141 // Otherwise -1 will be returned
142 size_t getOpLID(Value value) {
143 Operation *defOp = value.getDefiningOp();
144
145 if (auto it = opLIDMap.find(defOp); it != opLIDMap.end())
146 return it->second;
147
148 // Check for special case where op is actually a port
149 // To do so, we start by checking if our operation is a block argument
150 if (BlockArgument barg = dyn_cast<BlockArgument>(value)) {
151 // Extract the block argument index and use that to get the line number
152 size_t argIdx = barg.getArgNumber();
153
154 // Check that the extracted argument is in range before using it
155 if (auto it = inputLIDs.find(argIdx); it != inputLIDs.end())
156 return it->second;
157 }
158
159 // Return -1 if no LID was found
160 return noLID;
161 }
162
163 // Fetches the LID corresponding to a register input (including reset logic)
164 size_t getRegInputLID(Operation *regOp) {
165 auto &f = regToInputLIDMap[regOp];
166
167 // If the op isn't associated to an lid, assign it a new one
168 if (!f)
169 f = lid++;
170 return f;
171 }
172
173private:
174 // Checks if a sort was declared with the given width
175 // If so, its lid will be returned
176 // Otherwise -1 will be returned
177 size_t getSortLID(size_t w) {
178 if (auto it = sortToLIDMap.find(w); it != sortToLIDMap.end())
179 return it->second;
180
181 // If no lid was found return -1
182 return noLID;
183 }
184
185 // Associate the sort with a new lid
186 size_t setSortLID(size_t w) {
187 size_t sortlid = lid;
188 // Add the width to the declared sorts along with the associated line id
189 sortToLIDMap[w] = lid++;
190 return sortlid;
191 }
192
193 // Checks if a constant of a given size has been declared.
194 // If so, its lid will be returned.
195 // Otherwise -1 will be returned.
196 size_t getConstLID(int64_t val, size_t w) {
197 if (auto it = constToLIDMap.find(APInt(w, val)); it != constToLIDMap.end())
198 return it->second;
199
200 // if no lid was found return -1
201 return noLID;
202 }
203
204 // Associates a constant declaration to a new lid
205 size_t setConstLID(int64_t val, size_t w) {
206 size_t constlid = lid;
207 // Keep track of this value in a constant declaration tracker
208 constToLIDMap[APInt(w, val)] = lid++;
209 return constlid;
210 }
211
212 /// String generation helper functions
213
214 // Generates a sort declaration instruction given a type ("bitvec" or array)
215 // and a width.
216 void genSort(StringRef type, size_t width) {
217 // Check that the sort wasn't already declared
218 if (getSortLID(width) != noLID) {
219 return; // If it has already been declared then return an empty string
220 }
221
222 size_t sortlid = setSortLID(width);
223
224 // Build and return a sort declaration
225 os << sortlid << " "
226 << "sort"
227 << " " << type << " " << width << "\n";
228 }
229
230 // Generates an input declaration given a sort lid and a name.
231 void genInput(size_t inlid, size_t width, StringRef name) {
232 // Retrieve the lid associated with the sort (sid)
233 size_t sid = sortToLIDMap.at(width);
234
235 // Generate input declaration
236 os << inlid << " "
237 << "input"
238 << " " << sid << " " << name << "\n";
239 }
240
241 // Generates a constant declaration given a value, a width and a name.
242 void genConst(APInt value, size_t width, Operation *op) {
243 // For now we're going to assume that the name isn't taken, given that hw
244 // is already in SSA form
245 size_t opLID = getOpLID(op);
246
247 // Retrieve the lid associated with the sort (sid)
248 size_t sid = sortToLIDMap.at(width);
249
250 os << opLID << " "
251 << "constd"
252 << " " << sid << " " << value << "\n";
253 }
254
255 // Generates a zero constant expression
256 size_t genZero(size_t width) {
257 // Check if the constant has been created yet
258 size_t zlid = getConstLID(0, width);
259 if (zlid != noLID)
260 return zlid;
261
262 // Retrieve the lid associated with the sort (sid)
263 size_t sid = sortToLIDMap.at(width);
264
265 // Associate an lid to the new constant
266 size_t constlid = setConstLID(0, width);
267
268 // Build and return the zero btor instruction
269 os << constlid << " "
270 << "zero"
271 << " " << sid << "\n";
272 return constlid;
273 }
274
275 // Generates an init statement, which allows for the use of initial values
276 // operands in compreg registers
277 void genInit(Operation *reg, Value initVal, int64_t width) {
278 // Retrieve the various identifiers we require for this
279 size_t regLID = getOpLID(reg);
280 size_t sid = sortToLIDMap.at(width);
281 size_t initValLID = getOpLID(initVal);
282
283 // Build and emit the string (the lid here doesn't need to be associated
284 // to an op as it won't be used)
285 os << lid++ << " "
286 << "init"
287 << " " << sid << " " << regLID << " " << initValLID << "\n";
288 }
289
290 // Generates a binary operation instruction given an op name, two operands
291 // and a result width.
292 void genBinOp(StringRef inst, Operation *binop, Value op1, Value op2,
293 size_t width) {
294 // Set the LID for this operation
295 size_t opLID = getOpLID(binop);
296
297 // Find the sort's lid
298 size_t sid = sortToLIDMap.at(width);
299
300 // Assuming that the operands were already emitted
301 // Find the LIDs associated to the operands
302 size_t op1LID = getOpLID(op1);
303 size_t op2LID = getOpLID(op2);
304
305 // Build and return the string
306 os << opLID << " " << inst << " " << sid << " " << op1LID << " " << op2LID
307 << "\n";
308 }
309
310 // Expands a variadic operation into multiple binary operation instructions
311 void genVariadicOp(StringRef inst, Operation *op, size_t width) {
312 auto operands = op->getOperands();
313 size_t sid = sortToLIDMap.at(width);
314
315 if (operands.size() == 0) {
316 op->emitError("variadic operations with no operands are not supported");
317 return;
318 }
319
320 // If there's only one operand, then we don't generate a BTOR2 instruction,
321 // we just reuse the operand's existing LID
322 if (operands.size() == 1) {
323 auto existingLID = getOpLID(operands[0]);
324 // Check that we haven't somehow got a value that doesn't have a
325 // corresponding LID
326 assert(existingLID != noLID);
327 opLIDMap[op] = existingLID;
328 return;
329 }
330
331 // Special case for concat since intermediate results need different sorts
332 auto isConcat = isa<comb::ConcatOp>(op);
333
334 // Unroll variadic op into series of binary ops
335 // This will represent the previous operand in the chain:
336 auto prevOperandLID = getOpLID(operands[0]);
337
338 // Track the current width so we can work out new types if this is a concat
339 auto currentWidth = operands[0].getType().getIntOrFloatBitWidth();
340
341 for (auto operand : operands.drop_front()) {
342 // Manually increment lid since we need multiple per op
343
344 if (isConcat) {
345 // For concat, the sort width increases with each operand
346 currentWidth += operand.getType().getIntOrFloatBitWidth();
347 // Ensure that the sort exists
348 genSort("bitvec", currentWidth);
349 }
350
351 auto thisLid = lid++;
352 auto thisOperandLID = getOpLID(operand);
353 os << thisLid << " " << inst << " "
354 << (isConcat ? sortToLIDMap.at(currentWidth) : sid) << " "
355 << prevOperandLID << " " << thisOperandLID << "\n";
356 prevOperandLID = thisLid;
357 }
358
359 // Send lookups of the op's LID to the final binary op in the chain
360 opLIDMap[op] = prevOperandLID;
361 }
362
363 // Generates a slice instruction given an operand, the lowbit, and the width
364 void genSlice(Operation *srcop, Value op0, size_t lowbit, int64_t width) {
365 // Assign a LID to this operation
366 size_t opLID = getOpLID(srcop);
367
368 // Find the sort's associated lid in order to use it in the instruction
369 size_t sid = sortToLIDMap.at(width);
370
371 // Assuming that the operand has already been emitted
372 // Find the LID associated to the operand
373 size_t op0LID = getOpLID(op0);
374
375 // Build and return the slice instruction
376 os << opLID << " "
377 << "slice"
378 << " " << sid << " " << op0LID << " " << (lowbit + width - 1) << " "
379 << lowbit << "\n";
380 }
381
382 /// Generates a chain of concats to represent a replicate op
383 void genReplicateAsConcats(Operation *srcop, Value op0, size_t count,
384 unsigned int inputWidth) {
385 auto currentWidth = inputWidth;
386
387 auto prevOperandLID = getOpLID(op0);
388 for (size_t i = 1; i < count; ++i) {
389 currentWidth += inputWidth;
390 // Ensure that the sort exists
391 genSort("bitvec", currentWidth);
392
393 auto thisLid = lid++;
394 os << thisLid << " "
395 << "concat"
396 << " " << sortToLIDMap.at(currentWidth) << " " << prevOperandLID << " "
397 << getOpLID(op0) << "\n";
398 prevOperandLID = thisLid;
399 }
400
401 // Link LID of final instruction to original operation
402 opLIDMap[srcop] = prevOperandLID;
403 }
404
405 // Generates a constant declaration given a value, a width and a name
406 void genUnaryOp(Operation *srcop, Operation *op0, StringRef inst,
407 size_t width) {
408 // Register the source operation with the current line id
409 size_t opLID = getOpLID(srcop);
410
411 // Retrieve the lid associated with the sort (sid)
412 size_t sid = sortToLIDMap.at(width);
413
414 // Assuming that the operand has already been emitted
415 // Find the LID associated to the operand
416 size_t op0LID = getOpLID(op0);
417
418 os << opLID << " " << inst << " " << sid << " " << op0LID << "\n";
419 }
420
421 // Generates a constant declaration given a value, a width and a name and
422 // returns the LID associated to it
423 void genUnaryOp(Operation *srcop, Value op0, StringRef inst, size_t width) {
424 genUnaryOp(srcop, op0.getDefiningOp(), inst, width);
425 }
426
427 // Generates a constant declaration given a operand lid, a width and a name
428 size_t genUnaryOp(size_t op0LID, StringRef inst, size_t width) {
429 // Register the source operation with the current line id
430 size_t curLid = lid++;
431
432 // Retrieve the lid associated with the sort (sid)
433 size_t sid = sortToLIDMap.at(width);
434
435 os << curLid << " " << inst << " " << sid << " " << op0LID << "\n";
436 return curLid;
437 }
438
439 // Generates a constant declaration given a value, a width and a name
440 size_t genUnaryOp(Operation *op0, StringRef inst, size_t width) {
441 return genUnaryOp(getOpLID(op0), inst, width);
442 }
443
444 // Generates a constant declaration given a value, a width and a name and
445 // returns the LID associated to it
446 size_t genUnaryOp(Value op0, StringRef inst, size_t width) {
447 return genUnaryOp(getOpLID(op0), inst, width);
448 }
449
450 // Generate a btor2 assertion given an assertion operation
451 // Note that a predicate inversion must have already been generated at this
452 // point
453 void genBad(Operation *assertop) {
454 // Start by finding the expression lid
455 size_t assertLID = getOpLID(assertop);
456 genBad(assertLID);
457 }
458
459 // Generate a btor2 assertion given an assertion operation's LID
460 // Note that a predicate inversion must have already been generated at this
461 // point
462 void genBad(size_t assertLID) {
463 // Build and return the btor2 string
464 // Also update the lid as this instruction is not associated to an mlir op
465 os << lid++ << " "
466 << "bad"
467 << " " << assertLID << "\n";
468 }
469
470 // Generate a btor2 constraint given an expression from an assumption
471 // operation
472 void genConstraint(Value expr) {
473 // Start by finding the expression lid
474 size_t exprLID = getOpLID(expr);
475
476 genConstraint(exprLID);
477 }
478
479 // Generate a btor2 constraint given an expression from an assumption
480 // operation
481 void genConstraint(size_t exprLID) {
482 // Build and return the btor2 string
483 // Also update the lid as this instruction is not associated to an mlir op
484 os << lid++ << " "
485 << "constraint"
486 << " " << exprLID << "\n";
487 }
488
489 // Generate an ite instruction (if then else) given a predicate, two values
490 // and a res width
491 void genIte(Operation *srcop, Value cond, Value t, Value f, int64_t width,
492 bool forReset = false) {
493 // Retrieve the operand lids, assuming they were emitted
494 size_t condLID = getOpLID(cond);
495 size_t tLID = getOpLID(t);
496 size_t fLID = getOpLID(f);
497
498 genIte(srcop, condLID, tLID, fLID, width, forReset);
499 }
500
501 // Generate an ite instruction (if then else) given a predicate, two values
502 // and a res width
503 void genIte(Operation *srcop, size_t condLID, size_t tLID, size_t fLID,
504 int64_t width, bool forReset = false) {
505 // Register the source operation with the current line id
506 size_t iteLID;
507 // If this is being generated as reset logic, we update the register input
508 // mapping instead of the op LID mapping
509 iteLID = forReset ? getRegInputLID(srcop) : getOpLID(srcop);
510
511 // Retrieve the lid associated with the sort (sid)
512 size_t sid = sortToLIDMap.at(width);
513
514 // Build and return the ite instruction
515 os << iteLID << " "
516 << "ite"
517 << " " << sid << " " << condLID << " " << tLID << " " << fLID << "\n";
518 }
519
520 // Generate a logical implication given a lhs and a rhs
521 size_t genImplies(Operation *srcop, Value lhs, Value rhs) {
522 // Retrieve LIDs for the lhs and rhs
523 size_t lhsLID = getOpLID(lhs);
524 size_t rhsLID = getOpLID(rhs);
525
526 return genImplies(srcop, lhsLID, rhsLID);
527 }
528
529 // Generate a logical implication given a lhs and a rhs
530 size_t genImplies(Operation *srcop, size_t lhsLID, size_t rhsLID) {
531 // Register the source operation with the current line id
532 size_t opLID = getOpLID(srcop);
533 return genImplies(opLID, lhsLID, rhsLID);
534 }
535
536 size_t genImplies(size_t opLID, size_t lhsLID, size_t rhsLID) {
537 // Retrieve the lid associated with the sort (sid)
538 size_t sid = sortToLIDMap.at(1);
539 // Build and emit the implies operation
540 os << opLID << " "
541 << "implies"
542 << " " << sid << " " << lhsLID << " " << rhsLID << "\n";
543 return opLID;
544 }
545
546 // Generates a state instruction given a width and a name
547 void genState(Operation *srcop, int64_t width, StringRef name) {
548 // Register the source operation with the current line id
549 size_t opLID = getOpLID(srcop);
550
551 // Retrieve the lid associated with the sort (sid)
552 size_t sid = sortToLIDMap.at(width);
553
554 // Build and return the state instruction
555 os << opLID << " "
556 << "state"
557 << " " << sid << " " << symbolNamespace.newName(name) << "\n";
558 }
559
560 // Generates a next instruction, given a width and a register op
561 void genNext(Operation *reg, int64_t width) {
562 // Retrieve the lid associated with the sort (sid)
563 size_t sid = sortToLIDMap.at(width);
564
565 // Retrieve the LIDs associated to reg and next
566 size_t regLID = getOpLID(reg);
567 size_t nextLID = getRegInputLID(reg);
568
569 // Build and return the next instruction
570 // Also update the lid as this instruction is not associated to an mlir op
571 os << lid++ << " "
572 << "next"
573 << " " << sid << " " << regLID << " " << nextLID << "\n";
574 }
575
576 // Generates a delimiter comment to mark the start or end of a module given a
577 // module symbol
578 void genModuleComment(StringRef name, bool end = false) {
579 os << "; **************************************"
580 << "\n"
581 << "; " << (end ? "END" : "START") << " OF MODULE: " << name << "\n"
582 << "; **************************************"
583 << "\n";
584 }
585
586 // Verifies that the sort required for the given operation's btor2 emission
587 // has been generated
588 int64_t requireSort(mlir::Type type) {
589 // Start by figuring out what sort needs to be generated
590 int64_t width = hw::getBitWidth(type);
591
592 // Sanity check: getBitWidth can technically return -1 it is a type with
593 // no width (like a clock). This shouldn't be allowed as width is required
594 // to generate a sort
595 assert(width != noWidth);
596
597 // Generate the sort regardles of resulting width (nothing will be added
598 // if the sort already exists)
599 genSort("bitvec", width);
600 return width;
601 }
602
603 // Calls the right function to fetch `next` operand
604 Value extractRegNext(seq::CompRegOp reg) const { return reg.getInput(); }
605 Value extractRegNext(seq::FirRegOp reg) const { return reg.getNext(); }
606
607 // Extracts the arguments from a given register op
608 template <typename RegT>
609 void extractRegArgs(RegT reg, int64_t &width, Value &next, Value &reset,
610 Value &resetVal, Value &clk) const {
611 width = hw::getBitWidth(reg.getType());
612 reset = reg.getReset();
613 resetVal = reg.getResetValue();
614 clk = reg.getClk();
615
616 // Next is weird: same input, different function
617 next = extractRegNext(reg);
618 }
619
620 // Generates the transitions required to finalize the register to state
621 // transition system conversion
622 void finalizeRegVisit(Operation *op) {
623 int64_t width;
624 Value next, reset, resetVal, clk;
625
626 // Extract the operands depending on the register type
627 auto extract = TypeSwitch<Operation *, LogicalResult>(op)
628 .Case<seq::CompRegOp, seq::FirRegOp>([&](auto reg) {
629 extractRegArgs(reg, width, next, reset, resetVal, clk);
630 return success();
631 })
632 .Default([&](auto) {
633 op->emitError("Invalid register operation !");
634 return failure();
635 });
636
637 // Exit if an invalid register op was detected
638 if (failed(extract))
639 return;
640
641 // Check for multiple clocks
642 if (foundClock) {
643 if (clk != foundClock) {
644 op->emitError("Multi-clock designs are not currently supported.");
645 return;
646 }
647 } else {
648 foundClock = clk;
649 }
650
651 genSort("bitvec", width);
652
653 // Next should already be associated to an LID at this point
654 // As we are going to override it, we need to keep track of the original
655 // instruction
656 size_t nextLID = noLID;
657
658 // We need to check if the next value is a port to avoid nullptrs
659 // To do so, we start by checking if our operation is a block argument
660 if (BlockArgument barg = dyn_cast<BlockArgument>(next)) {
661 // Extract the block argument index and use that to get the line number
662 size_t argIdx = barg.getArgNumber();
663
664 // Check that the extracted argument is in range before using it
665 nextLID = inputLIDs[argIdx];
666
667 } else {
668 nextLID = getOpLID(next);
669 }
670
671 // Check if the register has a reset
672 if (reset) {
673 size_t resetValLID = noLID;
674
675 // Check if the reset signal is a port to avoid nullptrs (as done above
676 // with next)
677 size_t resetLID = noLID;
678 if (BlockArgument barg = dyn_cast<BlockArgument>(reset)) {
679 // Extract the block argument index and use that to get the line
680 // number
681 size_t argIdx = barg.getArgNumber();
682
683 // Check that the extracted argument is in range before using it
684 resetLID = inputLIDs[argIdx];
685
686 } else {
687 resetLID = getOpLID(reset);
688 }
689
690 // Check for a reset value, if none exists assume it's zero
691 if (resetVal) {
692 if (auto resetValArg = dyn_cast<BlockArgument>(resetVal))
693 resetValLID = inputLIDs[resetValArg.getArgNumber()];
694 else
695 resetValLID = getOpLID(resetVal.getDefiningOp());
696 } else
697 resetValLID = genZero(width);
698
699 // Sanity check: at this point the next operation should have had it's
700 // btor2 counterpart emitted if not then something terrible must have
701 // happened.
702 assert(nextLID != noLID);
703
704 // Generate the ite for the register update reset condition
705 // i.e. reg <= reset ? 0 : next
706 genIte(op, resetLID, resetValLID, nextLID, width, /*forReset=*/true);
707 } else {
708 // Sanity check: next should have been assigned
709 if (nextLID == noLID) {
710 next.getDefiningOp()->emitError(
711 "Register input does not point to a valid op!");
712 return;
713 }
714 // Associate the register with an input LID
715 regToInputLIDMap[op] = nextLID;
716 }
717
718 // Finally generate the next statement
719 genNext(op, width);
720 }
721
722public:
723 // Ignore all other explicitly mentioned operations
724 // ** Purposefully left empty **
725 void ignore(Operation *op) {}
726
727 /// Visitor Methods used later on for pattern matching
728
729 // Visitor for the inputs of the module.
730 // This will generate additional sorts and input declaration explicitly for
731 // btor2 Note that outputs are ignored in btor2 as they do not contribute to
732 // the final assertions
733 void visit(hw::PortInfo &port) {
734 // Separate the inputs from outputs and generate the first btor2 lines for
735 // input declaration. We only consider ports with an explicit bit-width (so
736 // ignore clocks and immutables)
737 if (port.isInput() && !isa<seq::ClockType, seq::ImmutableType>(port.type)) {
738 // Generate the associated btor declaration for the inputs
739 StringRef iName = port.getName();
740
741 // Guarantees that a sort will exist for the generation of this port's
742 // translation into btor2
743 int64_t w = requireSort(port.type);
744
745 // Save lid for later
746 size_t inlid = lid;
747
748 // Record the defining operation's line ID (the module itself in the
749 // case of ports)
750 inputLIDs[port.argNum] = lid;
751
752 // Increment the lid to keep it unique
753 lid++;
754
755 genInput(inlid, w, iName);
756 }
757 }
758
759 // Emits the associated btor2 operation for a constant. Note that for
760 // simplicity, we will only emit `constd` in order to avoid bit-string
761 // conversions
762 void visitTypeOp(hw::ConstantOp op) {
763 // Make sure the constant hasn't already been created
764 if (handledOps.contains(op))
765 return;
766
767 // Make sure that a sort has been created for our operation
768 int64_t w = requireSort(op.getType());
769
770 // Prepare for for const generation by extracting the const value and
771 // generting the btor2 string
772 genConst(op.getValue(), w, op);
773 }
774
775 // Wires should have been removed in PrepareForFormal
776 void visit(hw::WireOp op) {
777 op->emitError("Wires are not supported in btor!");
778 return signalPassFailure();
779 }
780
781 void visitTypeOp(Operation *op) { visitInvalidTypeOp(op); }
782
783 // Handles non-hw operations
784 void visitInvalidTypeOp(Operation *op) {
785 // Try comb ops
786 dispatchCombinationalVisitor(op);
787 }
788
789 // Binary operations are all emitted the same way, so we can group them into
790 // a single method.
791 template <typename Op>
792 void visitBinOp(Op op, StringRef inst) {
793 // Generate the sort
794 int64_t w = requireSort(op.getType());
795
796 // Start by extracting the operands
797 Value op1 = op.getOperand(0);
798 Value op2 = op.getOperand(1);
799
800 // Generate the line
801 genBinOp(inst, op, op1, op2, w);
802 }
803
804 template <typename Op>
805 void visitVariadicOp(Op op, StringRef inst) {
806 // Generate the sort
807 int64_t w = requireSort(op.getType());
808
809 // Generate the line
810 genVariadicOp(inst, op, w);
811 }
812
813 // Visitors for the binary ops
814 void visitComb(comb::AddOp op) { visitVariadicOp(op, "add"); }
815 void visitComb(comb::SubOp op) { visitBinOp(op, "sub"); }
816 void visitComb(comb::MulOp op) { visitVariadicOp(op, "mul"); }
817 void visitComb(comb::DivSOp op) { visitBinOp(op, "sdiv"); }
818 void visitComb(comb::DivUOp op) { visitBinOp(op, "udiv"); }
819 void visitComb(comb::ModSOp op) { visitBinOp(op, "smod"); }
820 void visitComb(comb::ShlOp op) { visitBinOp(op, "sll"); }
821 void visitComb(comb::ShrUOp op) { visitBinOp(op, "srl"); }
822 void visitComb(comb::ShrSOp op) { visitBinOp(op, "sra"); }
823 void visitComb(comb::AndOp op) { visitVariadicOp(op, "and"); }
824 void visitComb(comb::OrOp op) { visitVariadicOp(op, "or"); }
825 void visitComb(comb::XorOp op) { visitVariadicOp(op, "xor"); }
826 void visitComb(comb::ConcatOp op) { visitVariadicOp(op, "concat"); }
827
828 // Extract ops translate to a slice operation in btor2 in a one-to-one
829 // manner
830 void visitComb(comb::ExtractOp op) {
831 int64_t w = requireSort(op.getType());
832
833 // Start by extracting the necessary information for the emission (i.e.
834 // operand, low bit, ...)
835 Value op0 = op.getOperand();
836 size_t lb = op.getLowBit();
837
838 // Generate the slice instruction
839 genSlice(op, op0, lb, w);
840 }
841
842 // Btor2 uses similar syntax as hw for its comparisons
843 // So we simply need to emit the cmpop name and check for corner cases
844 // where the namings differ.
845 void visitComb(comb::ICmpOp op) {
846 Value lhs = op.getOperand(0);
847 Value rhs = op.getOperand(1);
848
849 // Extract the predicate name (assuming that its a valid btor2
850 // predicate)
851 StringRef pred = stringifyICmpPredicate(op.getPredicate());
852
853 // Check for special cases where hw doesn't align with btor syntax
854 if (pred == "ne")
855 pred = "neq";
856 else if (pred == "ule")
857 pred = "ulte";
858 else if (pred == "sle")
859 pred = "slte";
860 else if (pred == "uge")
861 pred = "ugte";
862 else if (pred == "sge")
863 pred = "sgte";
864
865 // Width of result is always 1 for comparison
866 genSort("bitvec", 1);
867
868 // With the special cases out of the way, the emission is the same as that
869 // of a binary op
870 genBinOp(pred, op, lhs, rhs, 1);
871 }
872
873 // Muxes generally convert to an ite statement
874 void visitComb(comb::MuxOp op) {
875 // Extract predicate, true and false values
876 Value pred = op.getCond();
877 Value tval = op.getTrueValue();
878 Value fval = op.getFalseValue();
879
880 // We assume that both tval and fval have the same width
881 // This width should be the same as the output width
882 int64_t w = requireSort(op.getType());
883
884 // Generate the ite instruction
885 genIte(op, pred, tval, fval, w);
886 }
887
888 // Replicate ops are expanded as a series of concats
889 void visitComb(comb::ReplicateOp op) {
890 Value op0 = op.getOperand();
891 auto count = op.getMultiple();
892 auto inputWidth = op0.getType().getIntOrFloatBitWidth();
893
894 // Generate the concat chain
895 genReplicateAsConcats(op, op0, count, inputWidth);
896 }
897
898 void visitComb(Operation *op) { visitInvalidComb(op); }
899
900 // Try sv ops when comb is done
901 void visitInvalidComb(Operation *op) { dispatchSVVisitor(op); }
902
903 // Assertions are negated then converted to a btor2 bad instruction
904 void visitSV(sv::AssertOp op) {
905 // Expression is what we will try to invert for our assertion
906 Value expr = op.getExpression();
907
908 // This sort is for assertion inversion and potential implies
909 genSort("bitvec", 1);
910
911 // Check for an overaching enable
912 // In our case the sv.if operation will probably only be used when
913 // conditioning an sv.assert on an enable signal. This means that
914 // its condition is probably used to imply our assertion
915 if (auto ifop = dyn_cast<sv::IfOp>(((Operation *)op)->getParentOp())) {
916 Value en = ifop.getOperand();
917
918 // Generate the implication
919 genImplies(ifop, en, expr);
920
921 // Generate the implies inversion
922 genUnaryOp(op, ifop, "not", 1);
923 } else {
924 // Generate the expression inversion
925 genUnaryOp(op, expr, "not", 1);
926 }
927
928 // Generate the bad btor2 instruction
929 genBad(op);
930 }
931 // Assumptions are converted to a btor2 constraint instruction
932 void visitSV(sv::AssumeOp op) {
933 // Extract the expression that we want our constraint to be about
934 Value expr = op.getExpression();
935 genConstraint(expr);
936 }
937
938 // Our only concern with an AlwaysOp is that it follows clocking constraints
939 void visitSV(sv::AlwaysOp op) {
940 if (op.getEvents().size() > 1) {
941 op->emitError("Multiple events in sv.always are not supported.");
942 return signalPassFailure();
943 }
944
945 auto cond = op.getCondition(0);
946
947 if (cond.event != sv::EventControl::AtPosEdge) {
948 op->emitError("Only posedge clocking is supported in sv.always.");
949 return signalPassFailure();
950 }
951
952 if (isa<BlockArgument>(cond.value) ||
953 !isa<seq::FromClockOp>(cond.value.getDefiningOp())) {
954 op->emitError("This pass only currently supports sv.always ops that use "
955 "a top-level seq.clock input (converted using "
956 "seq.from_clock) as their clock.");
957 return signalPassFailure();
958 }
959
960 // By now we know that the condition is a clock signal coming from a
961 // seq.from_clock op
962 auto clk = cond.value.getDefiningOp()->getOperand(0);
963 if (foundClock) {
964 if (clk != foundClock) {
965 op->emitError("Multi-clock designs are not currently supported.");
966 return signalPassFailure();
967 }
968 } else {
969 foundClock = clk;
970 }
971 }
972
973 void visitSV(Operation *op) { visitInvalidSV(op); }
974
975 // Once SV Ops are visited, we need to check for seq ops
976 void visitInvalidSV(Operation *op) { dispatchVerifVisitor(op); }
977
978 template <typename Op>
979 void visitAssertLike(Op op) {
980 // Expression is what we will try to invert for our assertion
981 Value prop = op.getProperty();
982 Value en = op.getEnable();
983
984 // This sort is for assertion inversion and potential implies
985 genSort("bitvec", 1);
986
987 size_t assertLID = noLID;
988 // Check for a related enable signal
989 if (en) {
990 // Generate the implication
991 genImplies(op, en, prop);
992
993 // Generate the implies inversion
994 assertLID = genUnaryOp(op, "not", 1);
995 } else {
996 // Generate the expression inversion
997 assertLID = genUnaryOp(prop.getDefiningOp(), "not", 1);
998 }
999
1000 // Generate the bad btor2 instruction
1001 genBad(assertLID);
1002 }
1003
1004 template <typename Op>
1005 void visitAssumeLike(Op op) {
1006 // Expression is what we will try to invert for our assertion
1007 Value prop = op.getProperty();
1008 Value en = op.getEnable();
1009
1010 size_t assumeLID = getOpLID(prop);
1011 // Check for a related enable signal
1012 if (en) {
1013 // This sort is for assertion inversion and potential implies
1014 genSort("bitvec", 1);
1015 // Generate the implication
1016 assumeLID = genImplies(op, en, prop);
1017 }
1018
1019 // Generate the bad btor2 instruction
1020 genConstraint(assumeLID);
1021 }
1022
1023 // Folds the enable signal into the property and converts the result into a
1024 // bad instruction.
1025 void visitVerif(verif::AssertOp op) { visitAssertLike(op); }
1026 void visitVerif(verif::ClockedAssertOp op) { visitAssertLike(op); }
1027
1028 // Fold the enable into the property and convert the assumption into a
1029 // constraint instruction.
1030 void visitVerif(verif::AssumeOp op) { visitAssumeLike(op); }
1031 void visitVerif(verif::ClockedAssumeOp op) { visitAssumeLike(op); }
1032
1033 // Symbolic values get handled the same way as block arguments.
1034 // The one difference if that we treat them as regular opertions rather than
1035 // block arguments, i.e. we don't need to special case store these inputLIDs
1036 // and can simply store them in opLIDs like the other operations.
1037 void visitVerif(verif::SymbolicValueOp op) {
1038 // Retrieve name, make sure it's unique
1039 auto name = symbolNamespace.newName(op.getName().value_or(""));
1040
1041 // Guarantees that a sort will exist for the generation of this symbolic
1042 // value's translation into btor2
1043 int64_t w = requireSort(op.getType());
1044
1045 // Store and generate a new lid
1046 size_t inlid = setOpLID(op.getOperation());
1047
1048 // Generate the input in btor2
1049 genInput(inlid, w, name);
1050 }
1051
1052 // Error out on most unhandled verif ops
1053 void visitUnhandledVerif(Operation *op) {
1054 op->emitError("not supported in btor2!");
1055 return signalPassFailure();
1056 }
1057
1058 // Dispatch next visitors
1059 void visitInvalidVerif(Operation *op) { visit(op); }
1060
1061 // Seq operation visitor, that dispatches to other seq ops
1062 // Also handles all remaining operations that should be explicitly ignored
1063 void visit(Operation *op) {
1064 // Typeswitch is used here because other seq types will be supported
1065 // like all operations relating to memories and CompRegs
1066 TypeSwitch<Operation *, void>(op)
1067 .Case<seq::FirRegOp, seq::CompRegOp, seq::FromClockOp, seq::ToClockOp>(
1068 [&](auto expr) { visit(expr); })
1069 .Default([&](auto expr) { visitUnsupportedOp(op); });
1070 }
1071
1072 // Firrtl registers generate a state instruction
1073 // The final update is also used to generate a set of next btor
1074 // instructions
1075 void visit(seq::FirRegOp reg) {
1076 // Start by retrieving the register's name and width
1077 StringRef regName = reg.getName();
1078 auto type = reg.getType();
1079 if (!isa<mlir::IntegerType>(type)) {
1080 reg.emitError("Only integer typed seq.firregs are supported in BTOR2.");
1081 return signalPassFailure();
1082 }
1083 int64_t w = requireSort(type);
1084
1085 // Generate state instruction (represents the register declaration)
1086 genState(reg, w, regName);
1087
1088 // Record the operation for future `next` instruction generation
1089 // This is required to model transitions between states (i.e. how a
1090 // register's value evolves over time)
1091 regOps.push_back(reg);
1092 }
1093
1094 // Compregs behave in a similar way as firregs for btor2 emission
1095 void visit(seq::CompRegOp reg) {
1096 // Start by retrieving the register's name and width
1097 StringRef regName = reg.getName().value();
1098 auto type = reg.getType();
1099 if (!isa<mlir::IntegerType>(type)) {
1100 reg.emitError("Only integer typed seq.compregs are supported in BTOR2.");
1101 return signalPassFailure();
1102 }
1103 int64_t w = requireSort(type);
1104
1105 // Check for initial values which must be emitted before the state in
1106 // btor2
1107 auto init = reg.getInitialValue();
1108 auto resetVal = reg.getResetValue();
1109
1110 // If there's an initial value, we need to generate a constant for the
1111 // initial value, then declare the state, then generate the init statement
1112 // (BTOR2 parsers are picky about it being in this order)
1113 auto shouldInitReset = assumeInitReset && resetVal;
1114 // We should create an init statement either if we have an initial value or
1115 // if we assume an initial reset
1116 if (init || shouldInitReset) {
1117 hw::ConstantOp initialConstant;
1118 // Assuming an initial reset takes priority over an initial value if
1119 // both are present
1120 if (shouldInitReset) {
1121 initialConstant = resetVal.getDefiningOp<hw::ConstantOp>();
1122 if (!initialConstant) {
1123 reg->emitError(
1124 "Reset value must be emitted directly by a hw.constant "
1125 "op when --assume-init-reset is in use.");
1126 return;
1127 }
1128 } else {
1129 if (!init.getDefiningOp<seq::InitialOp>()) {
1130 reg->emitError(
1131 "Initial value must be emitted directly by a seq.initial op");
1132 return;
1133 }
1134 // Check that the initial value is a non-null constant
1135 initialConstant = circt::seq::unwrapImmutableValue(init)
1136 .getDefiningOp<hw::ConstantOp>();
1137 if (!initialConstant)
1138 reg->emitError("initial value must be constant");
1139 }
1140
1141 // Visit the initial Value to generate the constant
1142 dispatchTypeOpVisitor(initialConstant);
1143
1144 // Add it to the list of visited operations
1145 handledOps.insert(initialConstant);
1146
1147 // Now we can declare the state
1148 genState(reg, w, regName);
1149
1150 // Finally generate the init statement
1151 genInit(reg, initialConstant, w);
1152 } else {
1153 // Just generate state instruction (represents the register declaration)
1154 genState(reg, w, regName);
1155 }
1156
1157 // Record the operation for future `next` instruction generation
1158 // This is required to model transitions between states (i.e. how a
1159 // register's value evolves over time)
1160 regOps.push_back(reg);
1161 }
1162
1163 void visit(seq::FromClockOp op) {
1164 for (auto *user : op->getResult(0).getUsers()) {
1165 if (!isa<sv::AlwaysOp, verif::ClockedAssertOp>(user)) {
1166 op->emitError("This pass only supports seq.from_clock results being "
1167 "used by sv.always and verif.clocked_assert operations.");
1168 signalPassFailure();
1169 }
1170 }
1171 }
1172
1173 void visit(seq::ToClockOp op) {
1174 // Make sure this value is top-level
1175 if (!isa<BlockArgument>(op.getInput())) {
1176 op->emitError("This pass only supports seq.to_clock operations that take "
1177 "a top-level input as their argument.");
1178 }
1179 // Make sure this clock is never used by anything other than a register so
1180 // we can safely make it implicit
1181 for (auto *user : op->getResult(0).getUsers())
1182 if (!isa<seq::FirRegOp, seq::CompRegOp>(user)) {
1183 op->emitError("This pass only supports seq.to_clock results being "
1184 "used by seq.firreg and seq.compreg operations.");
1185 signalPassFailure();
1186 }
1187 }
1188
1189 // Tail method that handles all operations that weren't handled by previous
1190 // visitors. Here we simply make the pass fail or ignore the op
1191 void visitUnsupportedOp(Operation *op) {
1192 // Check for explicitly ignored ops vs unsupported ops (which cause a
1193 // failure)
1194 TypeSwitch<Operation *, void>(op)
1195 // All explicitly ignored operations are defined here
1196 .Case<sv::MacroDefOp, sv::MacroDeclOp, sv::VerbatimOp,
1197 sv::VerbatimExprOp, sv::VerbatimExprSEOp, sv::IfOp, sv::IfDefOp,
1198 sv::IfDefProceduralOp, sv::AlwaysCombOp, seq::InitialOp,
1199 sv::AlwaysFFOp, seq::InitialOp, seq::YieldOp, hw::OutputOp,
1200 hw::HWModuleOp, verif::FormalOp,
1201 // Specifically ignore printfs, as we can't do anything with them
1202 // in btor2
1203 verif::FormatVerilogStringOp, verif::PrintOp>(
1204 [&](auto expr) { ignore(op); })
1205
1206 // Anything else is considered unsupported and might cause a wrong
1207 // behavior if ignored, so an error is thrown
1208 .Default([&](auto expr) {
1209 op->emitOpError("is an unsupported operation");
1210 return signalPassFailure();
1211 });
1212 }
1213
1214 /// Prepares and visits all of the ports in a module
1215 LogicalResult visitPorts(hw::HWModuleOp module) {
1216 // Start by extracting the inputs and generating appropriate instructions
1217 for (auto &port : module.getPortList()) {
1218 // Check whether the port is used as a clock
1219 if (port.isInput()) {
1220 auto portVal = module.getArgumentForInput(port.argNum);
1221 auto usedAsClock =
1222 llvm::any_of(portVal.getUsers(), [](Operation *user) {
1223 return isa<seq::ToClockOp>(user);
1224 });
1225 if (usedAsClock) {
1226 // If it's used as a clock, it can't be used anywhere else (as clocks
1227 // are implicit in BTOR2)
1228 if (portVal.getNumUses() > 1) {
1229 module.emitError(
1230 "Inputs converted to clocks may only have one user.");
1231 return failure();
1232 }
1233 // Ports used as clocks should be implicit so don't visit them
1234 continue;
1235 }
1236 }
1237 // Once ready, run actual port visitor
1238 visit(port);
1239 }
1240 return success();
1241 }
1242
1243 /// Checks if a given operation is a supported modulelike
1244 bool isSuportedModule(Operation *module) {
1245 bool supported = isa<hw::HWModuleOp, verif::FormalOp>(module);
1246 if (!supported)
1247 module->emitError("Unsupported module type!");
1248 return supported;
1249 }
1250
1251 /// Previsits all registers in a given module (avoids dependency cycles)
1252 LogicalResult preVisitRegs(Operation *module) {
1253 // Sanity check
1254 if (!isSuportedModule(module))
1255 return failure();
1256
1257 // Find all supported registers and visit them
1258 module->walk([&](Operation *op) {
1259 TypeSwitch<Operation *, void>(op)
1260 .Case<seq::FirRegOp, seq::CompRegOp>([&](auto reg) {
1261 visit(reg);
1262 handledOps.insert(op);
1263 })
1264 .Default([&](auto expr) {});
1265 });
1266
1267 return success();
1268 }
1269
1270 /// Visits all of the regular operations in our module
1271 LogicalResult visitBodyOps(Operation *module) {
1272 // Sanity check
1273 if (!isSuportedModule(module))
1274 return failure();
1275
1276 // Visit all of the operations in our module
1277 module->walk([&](Operation *op) {
1278 // Check: instances are not (yet) supported
1279 if (isa<hw::InstanceOp>(op)) {
1280 op->emitOpError("not supported in BTOR2 conversion");
1281 return signalPassFailure();
1282 }
1283
1284 // Don't process ops that have already been emitted
1285 if (handledOps.contains(op))
1286 return;
1287
1288 // Fill in our worklist
1289 worklist.insert({op, op->operand_begin()});
1290
1291 // Process the elements in our worklist
1292 while (!worklist.empty()) {
1293 auto &[op, operandIt] = worklist.back();
1294 if (operandIt == op->operand_end()) {
1295 // All of the operands have been emitted, it is safe to emit our
1296 // op
1297 dispatchTypeOpVisitor(op);
1298
1299 // Record that our op has been emitted
1300 handledOps.insert(op);
1301 worklist.pop_back();
1302 continue;
1303 }
1304
1305 // Send the operands of our op to the worklist in case they are
1306 // still un-emitted
1307 Value operand = *(operandIt++);
1308 auto *defOp = operand.getDefiningOp();
1309
1310 // Make sure that we don't emit the same operand twice
1311 if (!defOp || handledOps.contains(defOp))
1312 continue;
1313
1314 // This is triggered if our operand is already in the worklist and
1315 // wasn't handled
1316 if (!worklist.insert({defOp, defOp->operand_begin()}).second) {
1317 defOp->emitError("dependency cycle");
1318 return signalPassFailure();
1319 }
1320 }
1321 });
1322
1323 return success();
1324 }
1325
1326 /// Clear all data structures to allow for pass reuse
1327 void clearData() {
1328 sortToLIDMap.clear();
1329 constToLIDMap.clear();
1330 opLIDMap.clear();
1331 inputLIDs.clear();
1332 regOps.clear();
1333 handledOps.clear();
1334 worklist.clear();
1335 }
1336
1337 /// Handles the core logic of the pass, in a generic manner
1338 LogicalResult handleTopLevel(Operation *module) {
1339 // Previsit all registers in the module in order to avoid dependency
1340 // cycles
1341 if (failed(preVisitRegs(module)))
1342 return failure();
1343
1344 // Visit all of the operations in our module
1345 if (failed(visitBodyOps(module)))
1346 return failure();
1347
1348 // Iterate through the registers and generate the `next` instructions
1349 for (size_t i = 0; i < regOps.size(); ++i)
1350 finalizeRegVisit(regOps[i]);
1351
1352 // Clear data structures to allow for pass reuse
1353 clearData();
1354
1355 return success();
1356 }
1357};
1358} // end anonymous namespace
1359
1360void ConvertHWToBTOR2Pass::runOnOperation() {
1361 // Btor2 does not have the concept of modules or module
1362 // hierarchies, so we assume that no nested modules exist at this point.
1363 // This greatly simplifies translation.
1364 // We also consider hw::HWModuleOp and verif::FormalOp as the same
1365 auto top = getOperation();
1366 top.walk([&](Operation *op) {
1367 // Skip all non-module or formal ops
1368 if (!isa<hw::HWModuleOp, verif::FormalOp>(op))
1369 return;
1370
1371 // Generate the start comment for the module
1372 auto moduleName = SymbolTable::getSymbolName(op);
1373 genModuleComment(moduleName);
1374
1375 // Start by extracting the inputs and generating appropriate
1376 // instructions when block arguments exist
1377 if (auto module = dyn_cast<hw::HWModuleOp>(op))
1378 if (failed(visitPorts(module)))
1379 return signalPassFailure();
1380
1381 // Handle the rest
1382 if (failed(handleTopLevel(op)))
1383 return signalPassFailure();
1384
1385 // Generate ending comment
1386 genModuleComment(moduleName, true);
1387 });
1388
1389 // Clear data structures to allow for pass reuse
1390 clearData();
1391}
1392
1393// Constructor with a custom ostream
1394std::unique_ptr<mlir::Pass>
1396 return std::make_unique<ConvertHWToBTOR2Pass>(os);
1397}
1398
1399// Basic default constructor
1400std::unique_ptr<mlir::Pass> circt::createConvertHWToBTOR2Pass() {
1401 return std::make_unique<ConvertHWToBTOR2Pass>(llvm::outs());
1402}
assert(baseType &&"element must be base type")
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
Value unwrapImmutableValue(mlir::TypedValue< seq::ImmutableType > immutableVal)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createConvertHWToBTOR2Pass()
Definition hw.py:1
reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition seq.py:21
mlir::Type type
Definition HWTypes.h:34
StringRef getName() const
size_t argNum
This is the argument index or the result index depending on the direction.