CIRCT  19.0.0git
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 
13 #include "../PassDetail.h"
19 #include "circt/Dialect/HW/HWOps.h"
25 #include "circt/Dialect/SV/SVOps.h"
30 #include "llvm/ADT/MapVector.h"
31 #include "llvm/ADT/TypeSwitch.h"
32 #include "llvm/Support/raw_ostream.h"
33 
34 using namespace circt;
35 using namespace hw;
36 
37 namespace {
38 // The goal here is to traverse the operations in order and convert them one by
39 // one into btor2
40 struct ConvertHWToBTOR2Pass
41  : public ConvertHWToBTOR2Base<ConvertHWToBTOR2Pass>,
42  public comb::CombinationalVisitor<ConvertHWToBTOR2Pass>,
43  public sv::Visitor<ConvertHWToBTOR2Pass>,
44  public hw::TypeOpVisitor<ConvertHWToBTOR2Pass> {
45 public:
46  ConvertHWToBTOR2Pass(raw_ostream &os) : os(os) {}
47  // Executes the pass
48  void runOnOperation() override;
49 
50 private:
51  // Output stream in which the btor2 will be emitted
52  raw_ostream &os;
53 
54  // Create a counter that attributes a unique id to each generated btor2 line
55  size_t lid = 1; // btor2 line identifiers usually start at 1
56  size_t nclocks = 0;
57 
58  // Create maps to keep track of lid associations
59  // We need these in order to reference results as operands in btor2
60 
61  // Keeps track of the ids associated to each declared sort
62  // This is used in order to guarantee that sorts are unique and to allow for
63  // instructions to reference the given sorts (key: width, value: LID)
64  DenseMap<size_t, size_t> sortToLIDMap;
65  // Keeps track of {constant, width} -> LID mappings
66  // This is used in order to avoid duplicating constant declarations
67  // in the output btor2. It is also useful when tracking
68  // constants declarations that aren't tied to MLIR ops.
69  DenseMap<APInt, size_t> constToLIDMap;
70  // Keeps track of the most recent update line for each operation
71  // This allows for operations to be used throughout the btor file
72  // with their most recent expression. Btor uses unique identifiers for each
73  // instruction, so we need to have an association between those and MLIR Ops.
74  DenseMap<Operation *, size_t> opLIDMap;
75  // Keeps track of operation aliases. This is used for wire inlining, as
76  // btor2 does not have the concept of a wire. This means that wires in
77  // hw will simply create an alias for the operation that will point to
78  // the same LID as the original op.
79  // key: alias, value: original op
80  DenseMap<Operation *, Operation *> opAliasMap;
81  // Stores the LID of the associated input.
82  // This holds a similar function as the opLIDMap but keeps
83  // track of block argument index -> LID mappings
84  DenseMap<size_t, size_t> inputLIDs;
85  // Stores all of the register declaration ops.
86  // This allows for the emission of transition arcs for the regs
87  // to be deferred to the end of the pass.
88  // This is necessary, as we need to wait for the `next` operation to
89  // have been converted to btor2 before we can emit the transition.
90  SmallVector<Operation *> regOps;
91 
92  // Used to perform a DFS search through the module to declare all operands
93  // before they are used
94  llvm::SmallMapVector<Operation *, OperandRange::iterator, 16> worklist;
95 
96  // Keeps track of operations that have been declared
97  DenseSet<Operation *> handledOps;
98 
99  // Constants used during the conversion
100  static constexpr size_t noLID = -1UL;
101  static constexpr int64_t noWidth = -1L;
102 
103  /// Field helper functions
104 public:
105  // Checks if an operation was declared
106  // If so, its lid will be returned
107  // Otherwise a new lid will be assigned to the op
108  size_t getOpLID(Operation *op) {
109  // Look for the original operation declaration
110  // Make sure that wires are considered when looking for an lid
111  Operation *defOp = getOpAlias(op);
112  auto &f = opLIDMap[defOp];
113 
114  // If the op isn't associated to an lid, assign it a new one
115  if (!f)
116  f = lid++;
117  return f;
118  }
119 
120  // Associates the current lid to an operation
121  // The LID is then incremented to maintain uniqueness
122  size_t setOpLID(Operation *op) {
123  size_t oplid = lid++;
124  opLIDMap[op] = oplid;
125  return oplid;
126  }
127 
128  // Checks if an operation was declared
129  // If so, its lid will be returned
130  // Otherwise -1 will be returned
131  size_t getOpLID(Value value) {
132  // Check for an operation alias
133  Operation *defOp = getOpAlias(value.getDefiningOp());
134 
135  if (auto it = opLIDMap.find(defOp); it != opLIDMap.end())
136  return it->second;
137 
138  // Check for special case where op is actually a port
139  // To do so, we start by checking if our operation is a block argument
140  if (BlockArgument barg = dyn_cast<BlockArgument>(value)) {
141  // Extract the block argument index and use that to get the line number
142  size_t argIdx = barg.getArgNumber();
143 
144  // Check that the extracted argument is in range before using it
145  if (auto it = inputLIDs.find(argIdx); it != inputLIDs.end())
146  return it->second;
147  }
148 
149  // Return -1 if no LID was found
150  return noLID;
151  }
152 
153 private:
154  // Checks if an operation has an alias. This is the case for wires
155  // If so, the original operation is returned
156  // Otherwise the argument is returned as it is the original op
157  Operation *getOpAlias(Operation *op) {
158 
159  // Remove the alias until none are left (for wires of wires of wires ...)
160  if (auto it = opAliasMap.find(op); it != opAliasMap.end()) {
161  // check for aliases of inputs
162  if (!it->second) {
163  op->emitError("BTOR2 emission does not support for wires of inputs!");
164  return op;
165  }
166  return it->second;
167  }
168 
169  // If the op isn't an alias then simply return it
170  return op;
171  }
172 
173  // Updates or creates an entry for the given operation
174  // associating it with the current lid
175  void setOpAlias(Operation *alias, Operation *op) {
176  opAliasMap[alias] = getOpAlias(op);
177  }
178 
179  // Checks if a sort was declared with the given width
180  // If so, its lid will be returned
181  // Otherwise -1 will be returned
182  size_t getSortLID(size_t w) {
183  if (auto it = sortToLIDMap.find(w); it != sortToLIDMap.end())
184  return it->second;
185 
186  // If no lid was found return -1
187  return noLID;
188  }
189 
190  // Associate the sort with a new lid
191  size_t setSortLID(size_t w) {
192  size_t sortlid = lid;
193  // Add the width to the declared sorts along with the associated line id
194  sortToLIDMap[w] = lid++;
195  return sortlid;
196  }
197 
198  // Checks if a constant of a given size has been declared.
199  // If so, its lid will be returned.
200  // Otherwise -1 will be returned.
201  size_t getConstLID(int64_t val, size_t w) {
202  if (auto it = constToLIDMap.find(APInt(w, val)); it != constToLIDMap.end())
203  return it->second;
204 
205  // if no lid was found return -1
206  return noLID;
207  }
208 
209  // Associates a constant declaration to a new lid
210  size_t setConstLID(int64_t val, size_t w) {
211  size_t constlid = lid;
212  // Keep track of this value in a constant declaration tracker
213  constToLIDMap[APInt(w, val)] = lid++;
214  return constlid;
215  }
216 
217  /// String generation helper functions
218 
219  // Generates a sort declaration instruction given a type ("bitvec" or array)
220  // and a width.
221  void genSort(StringRef type, size_t width) {
222  // Check that the sort wasn't already declared
223  if (getSortLID(width) != noLID) {
224  return; // If it has already been declared then return an empty string
225  }
226 
227  size_t sortlid = setSortLID(width);
228 
229  // Build and return a sort declaration
230  os << sortlid << " "
231  << "sort"
232  << " " << type << " " << width << "\n";
233  }
234 
235  // Generates an input declaration given a sort lid and a name.
236  void genInput(size_t inlid, size_t width, StringRef name) {
237  // Retrieve the lid associated with the sort (sid)
238  size_t sid = sortToLIDMap.at(width);
239 
240  // Generate input declaration
241  os << inlid << " "
242  << "input"
243  << " " << sid << " " << name << "\n";
244  }
245 
246  // Generates a constant declaration given a value, a width and a name.
247  void genConst(int64_t value, size_t width, Operation *op) {
248  // For now we're going to assume that the name isn't taken, given that hw is
249  // already in SSA form
250  size_t opLID = getOpLID(op);
251 
252  // Retrieve the lid associated with the sort (sid)
253  size_t sid = sortToLIDMap.at(width);
254 
255  os << opLID << " "
256  << "constd"
257  << " " << sid << " " << value << "\n";
258  }
259 
260  // Generates a zero constant expression
261  size_t genZero(size_t width) {
262  // Check if the constant has been created yet
263  size_t zlid = getConstLID(0, width);
264  if (zlid != noLID)
265  return zlid;
266 
267  // Retrieve the lid associated with the sort (sid)
268  size_t sid = sortToLIDMap.at(width);
269 
270  // Associate an lid to the new constant
271  size_t constlid = setConstLID(0, width);
272 
273  // Build and return the zero btor instruction
274  os << constlid << " "
275  << "zero"
276  << " " << sid << "\n";
277  return constlid;
278  }
279 
280  // Generates an init statement, which allows for the use of powerOnValue
281  // operands in compreg registers
282  void genInit(Operation *reg, Value initVal, int64_t width) {
283  // Retrieve the various identifiers we require for this
284  size_t regLID = getOpLID(reg);
285  size_t sid = sortToLIDMap.at(width);
286  size_t initValLID = getOpLID(initVal);
287 
288  // Build and emit the string (the lid here doesn't need to be associated to
289  // an op as it won't be used)
290  os << lid++ << " "
291  << "init"
292  << " " << sid << " " << regLID << " " << initValLID << "\n";
293  }
294 
295  // Generates a binary operation instruction given an op name, two operands and
296  // a result width.
297  void genBinOp(StringRef inst, Operation *binop, Value op1, Value op2,
298  size_t width) {
299  // Set the LID for this operation
300  size_t opLID = getOpLID(binop);
301 
302  // Find the sort's lid
303  size_t sid = sortToLIDMap.at(width);
304 
305  // Assuming that the operands were already emitted
306  // Find the LIDs associated to the operands
307  size_t op1LID = getOpLID(op1);
308  size_t op2LID = getOpLID(op2);
309 
310  // Build and return the string
311  os << opLID << " " << inst << " " << sid << " " << op1LID << " " << op2LID
312  << "\n";
313  }
314 
315  // Generates a slice instruction given an operand, the lowbit, and the width
316  void genSlice(Operation *srcop, Value op0, size_t lowbit, int64_t width) {
317  // Assign a LID to this operation
318  size_t opLID = getOpLID(srcop);
319 
320  // Find the sort's associated lid in order to use it in the instruction
321  size_t sid = sortToLIDMap.at(width);
322 
323  // Assuming that the operand has already been emitted
324  // Find the LID associated to the operand
325  size_t op0LID = getOpLID(op0);
326 
327  // Build and return the slice instruction
328  os << opLID << " "
329  << "slice"
330  << " " << sid << " " << op0LID << " " << (width - 1) << " " << lowbit
331  << "\n";
332  }
333 
334  // Generates a constant declaration given a value, a width and a name
335  void genUnaryOp(Operation *srcop, Operation *op0, StringRef inst,
336  size_t width) {
337  // Register the source operation with the current line id
338  size_t opLID = getOpLID(srcop);
339 
340  // Retrieve the lid associated with the sort (sid)
341  size_t sid = sortToLIDMap.at(width);
342 
343  // Assuming that the operand has already been emitted
344  // Find the LID associated to the operand
345  size_t op0LID = getOpLID(op0);
346 
347  os << opLID << " " << inst << " " << sid << " " << op0LID << "\n";
348  }
349 
350  // Generates a constant declaration given a value, a width and a name
351  void genUnaryOp(Operation *srcop, Value op0, StringRef inst, size_t width) {
352  genUnaryOp(srcop, op0.getDefiningOp(), inst, width);
353  }
354 
355  // Generate a btor2 assertion given an assertion operation
356  // Note that a predicate inversion must have already been generated at this
357  // point
358  void genBad(Operation *assertop) {
359  // Start by finding the expression lid
360  size_t assertLID = getOpLID(assertop);
361 
362  // Build and return the btor2 string
363  // Also update the lid as this instruction is not associated to an mlir op
364  os << lid++ << " "
365  << "bad"
366  << " " << assertLID << "\n";
367  }
368 
369  // Generate a btor2 constraint given an expression from an assumption
370  // operation
371  void genConstraint(Value expr) {
372  // Start by finding the expression lid
373  size_t exprLID = getOpLID(expr);
374 
375  genConstraint(exprLID);
376  }
377 
378  // Generate a btor2 constraint given an expression from an assumption
379  // operation
380  void genConstraint(size_t exprLID) {
381  // Build and return the btor2 string
382  // Also update the lid as this instruction is not associated to an mlir op
383  os << lid++ << " "
384  << "constraint"
385  << " " << exprLID << "\n";
386  }
387 
388  // Generate an ite instruction (if then else) given a predicate, two values
389  // and a res width
390  void genIte(Operation *srcop, Value cond, Value t, Value f, int64_t width) {
391  // Retrieve the operand lids, assuming they were emitted
392  size_t condLID = getOpLID(cond);
393  size_t tLID = getOpLID(t);
394  size_t fLID = getOpLID(f);
395 
396  genIte(srcop, condLID, tLID, fLID, width);
397  }
398 
399  // Generate an ite instruction (if then else) given a predicate, two values
400  // and a res width
401  void genIte(Operation *srcop, size_t condLID, size_t tLID, size_t fLID,
402  int64_t width) {
403  // Register the source operation with the current line id
404  size_t opLID = getOpLID(srcop);
405 
406  // Retrieve the lid associated with the sort (sid)
407  size_t sid = sortToLIDMap.at(width);
408 
409  // Build and return the ite instruction
410  os << opLID << " "
411  << "ite"
412  << " " << sid << " " << condLID << " " << tLID << " " << fLID << "\n";
413  }
414 
415  // Generate a logical implication given a lhs and a rhs
416  void genImplies(Operation *srcop, Value lhs, Value rhs) {
417  // Retrieve LIDs for the lhs and rhs
418  size_t lhsLID = getOpLID(lhs);
419  size_t rhsLID = getOpLID(rhs);
420 
421  genImplies(srcop, lhsLID, rhsLID);
422  }
423 
424  // Generate a logical implication given a lhs and a rhs
425  void genImplies(Operation *srcop, size_t lhsLID, size_t rhsLID) {
426  // Register the source operation with the current line id
427  size_t opLID = getOpLID(srcop);
428 
429  // Retrieve the lid associated with the sort (sid)
430  size_t sid = sortToLIDMap.at(1);
431 
432  // Build and emit the implies operation
433  os << opLID << " "
434  << "implies"
435  << " " << sid << " " << lhsLID << " " << rhsLID << "\n";
436  }
437 
438  // Generates a state instruction given a width and a name
439  void genState(Operation *srcop, int64_t width, StringRef name) {
440  // Register the source operation with the current line id
441  size_t opLID = getOpLID(srcop);
442 
443  // Retrieve the lid associated with the sort (sid)
444  size_t sid = sortToLIDMap.at(width);
445 
446  // Build and return the state instruction
447  os << opLID << " "
448  << "state"
449  << " " << sid << " " << name << "\n";
450  }
451 
452  // Generates a next instruction, given a width, a state LID, and a next value
453  // LID
454  void genNext(Value next, Operation *reg, int64_t width) {
455  // Retrieve the lid associated with the sort (sid)
456  size_t sid = sortToLIDMap.at(width);
457 
458  // Retrieve the LIDs associated to reg and next
459  size_t regLID = getOpLID(reg);
460  size_t nextLID = getOpLID(next);
461 
462  // Build and return the next instruction
463  // Also update the lid as this instruction is not associated to an mlir op
464  os << lid++ << " "
465  << "next"
466  << " " << sid << " " << regLID << " " << nextLID << "\n";
467  }
468 
469  // Verifies that the sort required for the given operation's btor2 emission
470  // has been generated
471  int64_t requireSort(mlir::Type type) {
472  // Start by figuring out what sort needs to be generated
473  int64_t width = hw::getBitWidth(type);
474 
475  // Sanity check: getBitWidth can technically return -1 it is a type with no
476  // width (like a clock). This shouldn't be allowed as width is required to
477  // generate a sort
478  assert(width != noWidth);
479 
480  // Generate the sort regardles of resulting width (nothing will be added if
481  // the sort already exists)
482  genSort("bitvec", width);
483  return width;
484  }
485 
486  // Generates the transitions required to finalize the register to state
487  // transition system conversion
488  void finalizeRegVisit(Operation *op) {
489  int64_t width;
490  Value next, reset, resetVal;
491 
492  // Extract the operands depending on the register type
493  if (auto reg = dyn_cast<seq::CompRegOp>(op)) {
494  width = hw::getBitWidth(reg.getType());
495  next = reg.getInput();
496  reset = reg.getReset();
497  resetVal = reg.getResetValue();
498  } else if (auto reg = dyn_cast<seq::FirRegOp>(op)) {
499  width = hw::getBitWidth(reg.getType());
500  next = reg.getNext();
501  reset = reg.getReset();
502  resetVal = reg.getResetValue();
503  } else {
504  op->emitError("Invalid register operation !");
505  return;
506  }
507 
508  genSort("bitvec", width);
509 
510  // Next should already be associated to an LID at this point
511  // As we are going to override it, we need to keep track of the original
512  // instruction
513  size_t nextLID = noLID;
514 
515  // We need to check if the next value is a port to avoid nullptrs
516  // To do so, we start by checking if our operation is a block argument
517  if (BlockArgument barg = dyn_cast<BlockArgument>(next)) {
518  // Extract the block argument index and use that to get the line number
519  size_t argIdx = barg.getArgNumber();
520 
521  // Check that the extracted argument is in range before using it
522  nextLID = inputLIDs[argIdx];
523 
524  } else {
525  nextLID = getOpLID(next);
526  }
527 
528  // Check if the register has a reset
529  if (reset) {
530  size_t resetValLID = noLID;
531 
532  // Check if the reset signal is a port to avoid nullptrs (as done above
533  // with next)
534  size_t resetLID = noLID;
535  if (BlockArgument barg = dyn_cast<BlockArgument>(reset)) {
536  // Extract the block argument index and use that to get the line number
537  size_t argIdx = barg.getArgNumber();
538 
539  // Check that the extracted argument is in range before using it
540  resetLID = inputLIDs[argIdx];
541 
542  } else {
543  resetLID = getOpLID(reset);
544  }
545 
546  // Check for a reset value, if none exists assume it's zero
547  if (resetVal)
548  resetValLID = getOpLID(resetVal.getDefiningOp());
549  else
550  resetValLID = genZero(width);
551 
552  // Assign a new LID to next
553  setOpLID(next.getDefiningOp());
554 
555  // Sanity check: at this point the next operation should have had it's
556  // btor2 counterpart emitted if not then something terrible must have
557  // happened.
558  assert(nextLID != noLID);
559 
560  // Generate the ite for the register update reset condition
561  // i.e. reg <= reset ? 0 : next
562  genIte(next.getDefiningOp(), resetLID, resetValLID, nextLID, width);
563  } else {
564  // Assign a new LID to next and perform a sanity check
565  setOpLID(next.getDefiningOp());
566  assert(nextLID != noLID);
567  }
568 
569  // Finally generate the next statement
570  genNext(next, op, width);
571  }
572 
573 public:
574  /// Visitor Methods used later on for pattern matching
575 
576  // Visitor for the inputs of the module.
577  // This will generate additional sorts and input declaration explicitly for
578  // btor2 Note that outputs are ignored in btor2 as they do not contribute to
579  // the final assertions
580  void visit(hw::PortInfo &port) {
581  // Separate the inputs from outputs and generate the first btor2 lines for
582  // input declaration We only consider ports with an explicit bit-width (so
583  // ignore clocks)
584  if (port.isInput() && !port.type.isa<seq::ClockType>()) {
585  // Generate the associated btor declaration for the inputs
586  StringRef iName = port.getName();
587 
588  // Guarantees that a sort will exist for the generation of this port's
589  // translation into btor2
590  int64_t w = requireSort(port.type);
591 
592  // Save lid for later
593  size_t inlid = lid;
594 
595  // Record the defining operation's line ID (the module itself in the case
596  // of ports)
597  inputLIDs[port.argNum] = lid;
598 
599  // Increment the lid to keep it unique
600  lid++;
601 
602  genInput(inlid, w, iName);
603  }
604  }
605 
606  // Emits the associated btor2 operation for a constant. Note that for
607  // simplicity, we will only emit `constd` in order to avoid bit-string
608  // conversions
609  void visitTypeOp(hw::ConstantOp op) {
610  // Make sure that a sort has been created for our operation
611  int64_t w = requireSort(op.getType());
612 
613  // Prepare for for const generation by extracting the const value and
614  // generting the btor2 string
615  int64_t value = op.getValue().getSExtValue();
616  genConst(value, w, op);
617  }
618 
619  // Wires can generally be ignored in bto2, however we do need
620  // to keep track of the new alias it creates
621  void visit(hw::WireOp op) {
622  // Retrieve the aliased operation
623  Operation *defOp = op.getOperand().getDefiningOp();
624  // Wires don't output anything so just record alias
625  setOpAlias(op, defOp);
626  }
627 
628  void visitTypeOp(Operation *op) { visitInvalidTypeOp(op); }
629 
630  // Handles non-hw operations
631  void visitInvalidTypeOp(Operation *op) {
632  // Try comb ops
633  dispatchCombinationalVisitor(op);
634  }
635 
636  // Binary operations are all emitted the same way, so we can group them into
637  // a single method.
638  void visitBinOp(Operation *op, StringRef inst, int64_t w) {
639 
640  // Start by extracting the operands
641  Value op1 = op->getOperand(0);
642  Value op2 = op->getOperand(1);
643 
644  // Generate the line
645  genBinOp(inst, op, op1, op2, w);
646  }
647 
648  // Visitors for the binary ops
649  void visitComb(comb::AddOp op) {
650  visitBinOp(op, "add", requireSort(op.getType()));
651  }
652  void visitComb(comb::SubOp op) {
653  visitBinOp(op, "sub", requireSort(op.getType()));
654  }
655  void visitComb(comb::MulOp op) {
656  visitBinOp(op, "mul", requireSort(op.getType()));
657  }
658  void visitComb(comb::DivSOp op) {
659  visitBinOp(op, "sdiv", requireSort(op.getType()));
660  }
661  void visitComb(comb::DivUOp op) {
662  visitBinOp(op, "udiv", requireSort(op.getType()));
663  }
664  void visitComb(comb::ModSOp op) {
665  visitBinOp(op, "smod", requireSort(op.getType()));
666  }
667  void visitComb(comb::ShlOp op) {
668  visitBinOp(op, "sll", requireSort(op.getType()));
669  }
670  void visitComb(comb::ShrUOp op) {
671  visitBinOp(op, "srl", requireSort(op.getType()));
672  }
673  void visitComb(comb::ShrSOp op) {
674  visitBinOp(op, "sra", requireSort(op.getType()));
675  }
676  void visitComb(comb::AndOp op) {
677  visitBinOp(op, "and", requireSort(op.getType()));
678  }
679  void visitComb(comb::OrOp op) {
680  visitBinOp(op, "or", requireSort(op.getType()));
681  }
682  void visitComb(comb::XorOp op) {
683  visitBinOp(op, "xor", requireSort(op.getType()));
684  }
685  void visitComb(comb::ConcatOp op) {
686  visitBinOp(op, "concat", requireSort(op.getType()));
687  }
688 
689  // Extract ops translate to a slice operation in btor2 in a one-to-one manner
690  void visitComb(comb::ExtractOp op) {
691  int64_t w = requireSort(op.getType());
692 
693  // Start by extracting the necessary information for the emission (i.e.
694  // operand, low bit, ...)
695  Value op0 = op.getOperand();
696  size_t lb = op.getLowBit();
697 
698  // Generate the slice instruction
699  genSlice(op, op0, lb, w);
700  }
701 
702  // Btor2 uses similar syntax as hw for its comparisons
703  // So we simply need to emit the cmpop name and check for corner cases
704  // where the namings differ.
705  void visitComb(comb::ICmpOp op) {
706  Value lhs = op.getOperand(0);
707  Value rhs = op.getOperand(1);
708 
709  // Extract the predicate name (assuming that its a valid btor2
710  // predicate)
711  StringRef pred = stringifyICmpPredicate(op.getPredicate());
712 
713  // Check for special cases where hw doesn't align with btor syntax
714  if (pred == "ne")
715  pred = "neq";
716 
717  // Width of result is always 1 for comparison
718  genSort("bitvec", 1);
719 
720  // With the special cases out of the way, the emission is the same as that
721  // of a binary op
722  genBinOp(pred, op, lhs, rhs, 1);
723  }
724 
725  // Muxes generally convert to an ite statement
726  void visitComb(comb::MuxOp op) {
727  // Extract predicate, true and false values
728  Value pred = op.getCond();
729  Value tval = op.getTrueValue();
730  Value fval = op.getFalseValue();
731 
732  // We assume that both tval and fval have the same width
733  // This width should be the same as the output width
734  int64_t w = requireSort(op.getType());
735 
736  // Generate the ite instruction
737  genIte(op, pred, tval, fval, w);
738  }
739 
740  void visitComb(Operation *op) { visitInvalidComb(op); }
741 
742  // Try sv ops when comb is done
743  void visitInvalidComb(Operation *op) { dispatchSVVisitor(op); }
744 
745  // Assertions are negated then converted to a btor2 bad instruction
746  void visitSV(sv::AssertOp op) {
747  // Expression is what we will try to invert for our assertion
748  Value expr = op.getExpression();
749 
750  // This sort is for assertion inversion and potential implies
751  genSort("bitvec", 1);
752 
753  // Check for an overaching enable
754  // In our case the sv.if operation will probably only be used when
755  // conditioning an sv.assert on an enable signal. This means that
756  // its condition is probably used to imply our assertion
757  if (auto ifop = dyn_cast<sv::IfOp>(((Operation *)op)->getParentOp())) {
758  Value en = ifop.getOperand();
759 
760  // Generate the implication
761  genImplies(ifop, en, expr);
762 
763  // Generate the implies inversion
764  genUnaryOp(op, ifop, "not", 1);
765  } else {
766  // Generate the expression inversion
767  genUnaryOp(op, expr, "not", 1);
768  }
769 
770  // Genrate the bad btor2 intruction
771  genBad(op);
772  }
773  // Assumptions are converted to a btor2 constraint instruction
774  void visitSV(sv::AssumeOp op) {
775  // Extract the expression that we want our constraint to be about
776  Value expr = op.getExpression();
777  genConstraint(expr);
778  }
779 
780  void visitSV(Operation *op) { visitInvalidSV(op); }
781 
782  // Once SV Ops are visited, we need to check for seq ops
783  void visitInvalidSV(Operation *op) { visit(op); }
784 
785  // Seq operation visitor, that dispatches to other seq ops
786  // Also handles all remaining operations that should be explicitly ignored
787  void visit(Operation *op) {
788  // Typeswitch is used here because other seq types will be supported
789  // like all operations relating to memories and CompRegs
790  TypeSwitch<Operation *, void>(op)
791  .Case<seq::FirRegOp, hw::WireOp>([&](auto expr) { visit(expr); })
792  .Default([&](auto expr) { visitUnsupportedOp(op); });
793  }
794 
795  // Firrtl registers generate a state instruction
796  // The final update is also used to generate a set of next btor
797  // instructions
798  void visit(seq::FirRegOp reg) {
799  // Start by retrieving the register's name and width
800  StringRef regName = reg.getName();
801  int64_t w = requireSort(reg.getType());
802 
803  // Generate state instruction (represents the register declaration)
804  genState(reg, w, regName);
805 
806  // Record the operation for future `next` instruction generation
807  // This is required to model transitions between states (i.e. how a
808  // register's value evolves over time)
809  regOps.push_back(reg);
810  }
811 
812  // Compregs behave in a similar way as firregs for btor2 emission
813  void visit(seq::CompRegOp reg) {
814  // Start by retrieving the register's name and width
815  StringRef regName = reg.getName().value();
816  int64_t w = requireSort(reg.getType());
817 
818  // Check for initial values which must be emitted before the state in btor2
819  Value pov = reg.getPowerOnValue();
820  if (pov) {
821  // Check that the powerOn value is a non-null constant
822  if (!isa_and_nonnull<hw::ConstantOp>(pov.getDefiningOp()))
823  reg->emitError("PowerOn Value must be constant!!");
824 
825  // Visit the powerOn Value to generate the constant
826  dispatchTypeOpVisitor(pov.getDefiningOp());
827 
828  // Add it to the list of visited operations
829  handledOps.insert(pov.getDefiningOp());
830 
831  // Generate state instruction (represents the register declaration)
832  genState(reg, w, regName);
833 
834  // Finally generate the init statement
835  genInit(reg, pov, w);
836 
837  } else {
838  // Only generate the state instruction and nothing else
839  genState(reg, w, regName);
840  }
841 
842  // Record the operation for future `next` instruction generation
843  // This is required to model transitions between states (i.e. how a
844  // register's value evolves over time)
845  regOps.push_back(reg);
846  }
847 
848  // Ignore all other explicitly mentionned operations
849  // ** Purposefully left empty **
850  void ignore(Operation *op) {}
851 
852  // Tail method that handles all operations that weren't handled by previous
853  // visitors. Here we simply make the pass fail or ignore the op
854  void visitUnsupportedOp(Operation *op) {
855  // Check for explicitly ignored ops vs unsupported ops (which cause a
856  // failure)
857  TypeSwitch<Operation *, void>(op)
858  // All explicitly ignored operations are defined here
859  .Case<sv::MacroDefOp, sv::MacroDeclOp, sv::VerbatimOp,
860  sv::VerbatimExprOp, sv::VerbatimExprSEOp, sv::IfOp, sv::IfDefOp,
861  sv::IfDefProceduralOp, sv::AlwaysOp, sv::AlwaysCombOp,
862  sv::AlwaysFFOp, seq::FromClockOp, hw::OutputOp, hw::HWModuleOp>(
863  [&](auto expr) { ignore(op); })
864 
865  // Make sure that the design only contains one clock
866  .Case<seq::FromClockOp>([&](auto expr) {
867  if (++nclocks > 1UL) {
868  op->emitOpError("Mutli-clock designs are not supported!");
869  return signalPassFailure();
870  }
871  })
872 
873  // Anything else is considered unsupported and might cause a wrong
874  // behavior if ignored, so an error is thrown
875  .Default([&](auto expr) {
876  op->emitOpError("is an unsupported operation");
877  return signalPassFailure();
878  });
879  }
880 };
881 } // end anonymous namespace
882 
883 void ConvertHWToBTOR2Pass::runOnOperation() {
884  // Btor2 does not have the concept of modules or module
885  // hierarchies, so we assume that no nested modules exist at this point.
886  // This greatly simplifies translation.
887  getOperation().walk([&](hw::HWModuleOp module) {
888  // Start by extracting the inputs and generating appropriate instructions
889  for (auto &port : module.getPortList()) {
890  visit(port);
891  }
892 
893  // Previsit all registers in the module in order to avoid dependency cylcles
894  module.walk([&](Operation *op) {
895  TypeSwitch<Operation *, void>(op)
896  .Case<seq::FirRegOp, seq::CompRegOp>([&](auto reg) {
897  visit(reg);
898  handledOps.insert(op);
899  })
900  .Default([&](auto expr) {});
901  });
902 
903  // Visit all of the operations in our module
904  module.walk([&](Operation *op) {
905  // Check: instances are not (yet) supported
906  if (isa<hw::InstanceOp>(op)) {
907  op->emitOpError("not supported in BTOR2 conversion");
908  return;
909  }
910 
911  // Don't process ops that have already been emitted
912  if (handledOps.contains(op))
913  return;
914 
915  // Fill in our worklist
916  worklist.insert({op, op->operand_begin()});
917 
918  // Process the elements in our worklist
919  while (!worklist.empty()) {
920  auto &[op, operandIt] = worklist.back();
921  if (operandIt == op->operand_end()) {
922  // All of the operands have been emitted, it is safe to emit our op
923  dispatchTypeOpVisitor(op);
924 
925  // Record that our op has been emitted
926  handledOps.insert(op);
927  worklist.pop_back();
928  continue;
929  }
930 
931  // Send the operands of our op to the worklist in case they are still
932  // un-emitted
933  Value operand = *(operandIt++);
934  auto *defOp = operand.getDefiningOp();
935 
936  // Make sure that we don't emit the same operand twice
937  if (!defOp || handledOps.contains(defOp))
938  continue;
939 
940  // This is triggered if our operand is already in the worklist and
941  // wasn't handled
942  if (!worklist.insert({defOp, defOp->operand_begin()}).second) {
943  op->emitError("dependency cycle");
944  return;
945  }
946  }
947  });
948 
949  // Iterate through the registers and generate the `next` instructions
950  for (size_t i = 0; i < regOps.size(); ++i) {
951  finalizeRegVisit(regOps[i]);
952  }
953  });
954  // Clear data structures to allow for pass reuse
955  sortToLIDMap.clear();
956  constToLIDMap.clear();
957  opLIDMap.clear();
958  opAliasMap.clear();
959  inputLIDs.clear();
960  regOps.clear();
961  handledOps.clear();
962  worklist.clear();
963 }
964 
965 // Constructor with a custom ostream
966 std::unique_ptr<mlir::Pass>
967 circt::createConvertHWToBTOR2Pass(llvm::raw_ostream &os) {
968  return std::make_unique<ConvertHWToBTOR2Pass>(os);
969 }
970 
971 // Basic default constructor
972 std::unique_ptr<mlir::Pass> circt::createConvertHWToBTOR2Pass() {
973  return std::make_unique<ConvertHWToBTOR2Pass>(llvm::outs());
974 }
assert(baseType &&"element must be base type")
int32_t width
Definition: FIRRTL.cpp:36
Definition: sv.py:15
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition: HWTypes.cpp:102
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
std::unique_ptr< mlir::Pass > createConvertHWToBTOR2Pass(llvm::raw_ostream &os)
Definition: HWToBTOR2.cpp:967
Definition: hw.py:1
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Definition: seq.py:20