CIRCT  20.0.0git
DesequentializationPass.cpp
Go to the documentation of this file.
1 //===- DesequentializationPass.cpp - Convert processes to registers -------===//
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 //
9 // Implements a pass to lower sequential processes to registers. This is not
10 // always possible. In that case, it just leaves the process unconverted.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "TemporalRegions.h"
17 #include "circt/Dialect/HW/HWOps.h"
21 #include "circt/Support/FVInt.h"
22 #include "mlir/IR/PatternMatch.h"
23 #include "llvm/Support/Debug.h"
24 
25 #define DEBUG_TYPE "llhd-desequentialization"
26 
27 namespace circt {
28 namespace llhd {
29 #define GEN_PASS_DEF_DESEQUENTIALIZATION
30 #include "circt/Dialect/LLHD/Transforms/Passes.h.inc"
31 } // namespace llhd
32 } // namespace circt
33 
34 using namespace circt;
35 using namespace mlir;
36 
37 namespace {
38 /// Visits boolean comb operations from a 'root' value to a list of given
39 /// 'primitives' and computes the result of this expression for each possible
40 /// input combination.
41 class CombInterpreter : public comb::CombinationalVisitor<CombInterpreter> {
42 public:
43  /// Takes a list of variables to be considered primitive. The 'truthTable'
44  /// list must have the same number of elements and each 'APInt' represents the
45  /// possible configurations those primitive variables can be in. This visitor
46  /// then computes an 'APInt' representing the result of the 'truthTable' in a
47  /// vectorized fashion.
48  APInt compute(ArrayRef<Value> primitives, ArrayRef<APInt> truthTable,
49  uint64_t width, Value root) {
50  assert(primitives.size() == truthTable.size() && "must have same size");
51 
52  for (auto [p, t] : llvm::zip(primitives, truthTable))
53  results[p] = t;
54 
55  this->width = width;
56 
57  DenseSet<Operation *> visited;
58  SmallVector<Value> worklist;
59  worklist.push_back(root);
60 
61  while (!worklist.empty()) {
62  auto curr = worklist.back();
63  if (results.contains(curr)) {
64  worklist.pop_back();
65  continue;
66  }
67 
68  auto *defOp = curr.getDefiningOp();
69  if (!defOp) {
70  worklist.pop_back();
71  continue;
72  }
73 
74  if (!visited.contains(defOp)) {
75  visited.insert(defOp);
76 
77  bool addedToWorklist =
78  TypeSwitch<Operation *, bool>(defOp)
79  .Case<comb::AndOp, comb::OrOp, comb::XorOp>([&](auto op) {
80  worklist.append(llvm::to_vector(op->getOperands()));
81  return true;
82  })
83  .Case([&](comb::ICmpOp op) {
84  if ((op.getPredicate() == circt::comb::ICmpPredicate::eq ||
85  op.getPredicate() == circt::comb::ICmpPredicate::ne) &&
86  op.getLhs().getType().isSignlessInteger(1)) {
87  worklist.append(llvm::to_vector(op->getOperands()));
88  return true;
89  }
90  return false;
91  })
92  .Case<hw::ConstantOp>([&](hw::ConstantOp op) {
93  results[op.getResult()] = op.getValue().getBoolValue()
94  ? APInt::getAllOnes(width)
95  : APInt(width, 0);
96  worklist.pop_back();
97  return true;
98  });
99  if (addedToWorklist)
100  continue;
101  }
102 
103  dispatchCombinationalVisitor(defOp);
104  worklist.pop_back();
105  }
106 
107  return results[root];
108  }
109 
110  void visitComb(comb::AndOp op) {
111  auto res = APInt::getAllOnes(width);
112  for (auto operand : op->getOperands())
113  res &= results[operand];
114  results[op.getResult()] = res;
115  }
116 
117  void visitComb(comb::OrOp op) {
118  auto res = APInt(width, 0);
119  for (auto operand : op->getOperands())
120  res |= results[operand];
121  results[op.getResult()] = res;
122  }
123 
124  void visitComb(comb::XorOp op) {
125  auto res = results[op->getOperands()[0]];
126  for (auto operand : op->getOperands().drop_front())
127  res ^= results[operand];
128  results[op.getResult()] = res;
129  }
130 
131  void visitComb(comb::ICmpOp op) {
132  auto res = results[op.getLhs()];
133  res ^= results[op.getRhs()];
134  if (op.getPredicate() == comb::ICmpPredicate::eq)
135  res ^= APInt::getAllOnes(width);
136  results[op.getResult()] = res;
137  }
138 
139  // Define the following methods to make the compiler happy. They will never be
140  // called.
141  void visitComb(comb::AddOp op) { visitInvalidComb(op); }
142  void visitComb(comb::SubOp op) { visitInvalidComb(op); }
143  void visitComb(comb::MulOp op) { visitInvalidComb(op); }
144  void visitComb(comb::DivUOp op) { visitInvalidComb(op); }
145  void visitComb(comb::DivSOp op) { visitInvalidComb(op); }
146  void visitComb(comb::ModUOp op) { visitInvalidComb(op); }
147  void visitComb(comb::ModSOp op) { visitInvalidComb(op); }
148  void visitComb(comb::ShlOp op) { visitInvalidComb(op); }
149  void visitComb(comb::ShrUOp op) { visitInvalidComb(op); }
150  void visitComb(comb::ShrSOp op) { visitInvalidComb(op); }
151  void visitComb(comb::ParityOp op) { visitInvalidComb(op); }
152  void visitComb(comb::ConcatOp op) { visitInvalidComb(op); }
153  void visitComb(comb::ReplicateOp op) { visitInvalidComb(op); }
154  void visitComb(comb::ExtractOp op) { visitInvalidComb(op); }
155  void visitComb(comb::MuxOp op) { visitInvalidComb(op); }
156 
157 private:
158  DenseMap<Value, APInt> results;
159  unsigned width;
160 };
161 
162 /// Represents a single register trigger. This is basically one row in the DNF
163 /// truth table. However, several ones can typically combined into one.
164 struct Trigger {
165  enum class Kind {
166  PosEdge,
167  NegEdge,
168  Edge,
169  };
170 
171  static StringRef stringify(const Trigger::Kind &kind) {
172  switch (kind) {
173  case Trigger::Kind::PosEdge:
174  return "posedge";
175  case Trigger::Kind::NegEdge:
176  return "negedge";
177  case Trigger::Kind::Edge:
178  return "edge";
179  }
180  llvm::llvm_unreachable_internal("all cases considered above");
181  }
182 
183  /// Clock and async reset values.
184  SmallVector<Value> clocks;
185 
186  /// Determines whether this trigger is a clock and at which edge or an async
187  /// reset.
188  SmallVector<Kind> kinds;
189 
190  /// Null Value when AsyncReset or no enable present.
191  Value enable;
192 };
193 } // namespace
194 
195 template <typename T>
196 static T &operator<<(T &os, const Trigger::Kind &kind) {
197  return os << Trigger::stringify(kind);
198 }
199 
200 namespace {
201 /// Analyses a boolean expression and converts it to a list of register
202 /// triggers.
203 class DnfAnalyzer {
204 public:
205  DnfAnalyzer(Value value, function_ref<bool(Value)> sampledInPast)
206  : root(value) {
207  assert(value.getType().isSignlessInteger(1) &&
208  "only 1-bit signless integers supported");
209 
210  DenseSet<Value> alreadyAdded;
211 
212  SmallVector<Value> worklist;
213  worklist.push_back(value);
214 
215  while (!worklist.empty()) {
216  Value curr = worklist.pop_back_val();
217  auto *defOp = curr.getDefiningOp();
218  if (!defOp) {
219  if (!alreadyAdded.contains(curr)) {
220  primitives.push_back(curr);
221  primitiveSampledInPast.push_back(sampledInPast(curr));
222  alreadyAdded.insert(curr);
223  }
224  continue;
225  }
226 
227  TypeSwitch<Operation *>(defOp)
228  .Case<comb::AndOp, comb::OrOp, comb::XorOp>([&](auto op) {
229  worklist.append(llvm::to_vector(op->getOperands()));
230  })
231  .Case([&](comb::ICmpOp op) {
232  if ((op.getPredicate() == circt::comb::ICmpPredicate::eq ||
233  op.getPredicate() == circt::comb::ICmpPredicate::ne) &&
234  op.getLhs().getType().isSignlessInteger(1)) {
235  worklist.append(llvm::to_vector(op->getOperands()));
236  } else {
237  if (!alreadyAdded.contains(curr)) {
238  primitives.push_back(curr);
239  primitiveSampledInPast.push_back(sampledInPast(curr));
240  alreadyAdded.insert(curr);
241  }
242  }
243  })
244  .Case<hw::ConstantOp>([](auto op) { /* ignore */ })
245  .Default([&](auto op) {
246  if (!alreadyAdded.contains(curr)) {
247  primitives.push_back(curr);
248  primitiveSampledInPast.push_back(sampledInPast(curr));
249  alreadyAdded.insert(curr);
250  }
251  });
252  }
253 
254  LLVM_DEBUG({
255  for (auto val : primitives)
256  llvm::dbgs() << " - Primitive variable: " << val << "\n";
257  });
258 
259  this->isClock = SmallVector<bool>(primitives.size(), false);
260  this->dontCare = SmallVector<APInt>(primitives.size(),
261  APInt(1ULL << primitives.size(), 0));
262  }
263 
264  /// Note that clocks can be dual edge triggered, but this is not directly
265  /// returned as a single Trigger but instead two Triggers with the same clock
266  /// value, one negedge and one posedge kind. The enable should also match in
267  /// that case.
268  LogicalResult
269  computeTriggers(OpBuilder &builder, Location loc,
270  function_ref<bool(Value, Value)> sampledFromSameSignal,
271  SmallVectorImpl<Trigger> &triggers, unsigned maxPrimitives) {
272  if (primitives.size() > maxPrimitives) {
273  LLVM_DEBUG({ llvm::dbgs() << " Too many primitives, skipping...\n"; });
274  return failure();
275  }
276 
277  // Populate the truth table and the result APInt.
278  computeTruthTable();
279 
280  // Detect primitive variable pairs that form a clock and mark them as such.
281  // If a variable sampled in the past cannot be matched with a sample in the
282  // present to form a clock, it will return failure.
283  if (failed(computeClockValuePairs(sampledFromSameSignal)))
284  return failure();
285 
286  // Perform boolean expression simplification.
287  simplifyTruthTable();
288 
289  LLVM_DEBUG({
290  llvm::dbgs() << " - Truth table:\n";
291 
292  for (auto [t, d] : llvm::zip(truthTable, dontCare))
293  llvm::dbgs() << " " << FVInt(std::move(t), std::move(d)) << "\n";
294 
295  SmallVector<char> str;
296  result.toString(str, 2, false);
297 
298  llvm::dbgs() << " ";
299  for (unsigned i = 0; i < result.getBitWidth() - str.size(); ++i)
300  llvm::dbgs() << '0';
301 
302  llvm::dbgs() << str << "\n";
303  });
304 
305  // Compute the enable expressions for each trigger. Make sure that the SSA
306  // value for a specific configuration is reused, allowing for easier
307  // canonicalization and merging of triggers later on.
308  materializeTriggerEnables(builder, loc);
309 
310  // Iterate over the truth table and extract the triggers.
311  extractTriggerList(triggers);
312 
313  // Canonicalize and merge triggers in the trigger list.
314  canonicalizeTriggerList(triggers, builder, loc);
315 
316  return success();
317  }
318 
319 private:
320  FVInt computeEnableKey(unsigned tableRow) {
321  FVInt key = FVInt::getAllX(primitives.size());
322  for (unsigned k = 0; k < primitives.size(); ++k) {
323  if (dontCare[k][tableRow])
324  continue;
325 
326  if (primitiveSampledInPast[k])
327  continue;
328 
329  // TODO: allow the present value of the clock if it is not trivially
330  // satisfied by the trigger
331  if (isClock[k])
332  continue;
333 
334  key.setBit(k, truthTable[k][tableRow]);
335  }
336 
337  return key;
338  }
339 
340  void extractTriggerList(SmallVectorImpl<Trigger> &triggers) {
341  for (uint64_t i = 0, e = 1ULL << primitives.size(); i < e; ++i) {
342  if (!result[i])
343  continue;
344 
345  auto key = computeEnableKey(i);
346  Trigger trigger;
347  for (auto clk : clockPairs) {
348  if (dontCare[clk.second][i] && dontCare[clk.first][i])
349  continue;
350 
351  trigger.clocks.push_back(primitives[clk.second]);
352  trigger.kinds.push_back(truthTable[clk.second][i]
353  ? Trigger::Kind::PosEdge
354  : Trigger::Kind::NegEdge);
355  }
356  trigger.enable = enableMap[key];
357 
358  if (!trigger.clocks.empty())
359  triggers.push_back(trigger);
360  }
361  }
362 
363  void materializeTriggerEnables(OpBuilder &builder, Location loc) {
364  Value trueVal =
365  builder.create<hw::ConstantOp>(loc, builder.getBoolAttr(true));
366  for (uint64_t i = 0, e = 1ULL << primitives.size(); i < e; ++i) {
367  if (!result[i])
368  continue;
369 
370  auto key = computeEnableKey(i);
371 
372  if (!enableMap.contains(key)) {
373  SmallVector<Value> conjuncts;
374  for (unsigned k = 0; k < primitives.size(); ++k) {
375  if (dontCare[k][i])
376  continue;
377 
378  if (primitiveSampledInPast[k])
379  continue;
380 
381  // TODO: allow the present value of the clock if it is not trivially
382  // satisfied by the trigger
383  if (isClock[k])
384  continue;
385 
386  if (truthTable[k][i]) {
387  conjuncts.push_back(primitives[k]);
388  continue;
389  }
390  conjuncts.push_back(
391  builder.create<comb::XorOp>(loc, primitives[k], trueVal));
392  }
393  if (!conjuncts.empty())
394  enableMap[key] =
395  builder.createOrFold<comb::AndOp>(loc, conjuncts, false);
396  }
397  }
398  }
399 
400  LogicalResult computeClockValuePairs(
401  function_ref<bool(Value, Value)> sampledFromSameSignal) {
402  for (unsigned k = 0; k < primitives.size(); ++k) {
403  if (isClock[k])
404  continue;
405 
406  for (unsigned l = k + 1; l < primitives.size(); ++l) {
407  if (sampledFromSameSignal(primitives[k], primitives[l]) &&
408  (primitiveSampledInPast[k] != primitiveSampledInPast[l])) {
409  if (primitiveSampledInPast[k])
410  clockPairs.emplace_back(k, l);
411  else
412  clockPairs.emplace_back(l, k);
413  isClock[k] = true;
414  isClock[l] = true;
415  }
416  }
417  if (primitiveSampledInPast[k] && !isClock[k])
418  return failure();
419  }
420 
421  return success();
422  }
423 
424  void simplifyTruthTable() {
425  uint64_t numEntries = 1 << primitives.size();
426 
427  // Perform boolean expression simplifification (see Karnaugh maps).
428  // NOTE: This is a very simple algorithm that may become a bottleneck.
429  // Fortunately, there exist better algorithms we could implement if that
430  // becomes necessay.
431  for (uint64_t i = 0; i < numEntries; ++i) {
432  if (!result[i])
433  continue;
434 
435  for (uint64_t k = i + 1; k < numEntries; ++k) {
436  if (!result[i])
437  continue;
438 
439  unsigned differenceCount = 0;
440  for (unsigned l = 0; l < primitives.size(); ++l) {
441  if (truthTable[l][i] != truthTable[l][k])
442  ++differenceCount;
443  if (differenceCount > 1)
444  break;
445  }
446 
447  if (differenceCount == 1) {
448  for (unsigned l = 0; l < primitives.size(); ++l) {
449  dontCare[l].setBit(k);
450  if (truthTable[l][i] != truthTable[l][k])
451  dontCare[l].setBit(i);
452  }
453  }
454  }
455  }
456  }
457 
458  void computeTruthTable() {
459  uint64_t numEntries = 1 << primitives.size();
460  for (auto _ [[maybe_unused]] : primitives)
461  truthTable.push_back(APInt(numEntries, 0));
462 
463  for (uint64_t i = 0; i < numEntries; ++i)
464  for (unsigned k = 0; k < primitives.size(); ++k)
465  truthTable[k].setBitVal(i, APInt(64, i)[k]);
466 
467  result =
468  CombInterpreter().compute(primitives, truthTable, numEntries, root);
469  }
470 
471  void canonicalizeTriggerList(SmallVectorImpl<Trigger> &triggers,
472  OpBuilder &builder, Location loc) {
473  for (auto *iter1 = triggers.begin(); iter1 != triggers.end(); ++iter1) {
474  for (auto *iter2 = iter1 + 1; iter2 != triggers.end(); ++iter2) {
475  if (iter1->clocks == iter2->clocks && iter1->kinds == iter2->kinds) {
476  iter1->enable =
477  builder.create<comb::OrOp>(loc, iter1->enable, iter2->enable);
478  triggers.erase(iter2--);
479  }
480  }
481  }
482 
483  // TODO: merge negedge and posedge triggers on the same clock with the same
484  // enables to an 'edge' trigger.
485  }
486 
487  Value root;
488  SmallVector<Value> primitives;
489  SmallVector<bool> isClock;
490  SmallVector<bool> primitiveSampledInPast;
491  SmallVector<APInt> truthTable;
492  SmallVector<APInt> dontCare;
493  SmallVector<std::pair<unsigned, unsigned>> clockPairs;
494  DenseMap<FVInt, Value> enableMap;
495  APInt result;
496 };
497 
498 struct DesequentializationPass
499  : public llhd::impl::DesequentializationBase<DesequentializationPass> {
500  DesequentializationPass()
501  : llhd::impl::DesequentializationBase<DesequentializationPass>() {}
502  DesequentializationPass(const llhd::DesequentializationOptions &options)
503  : llhd::impl::DesequentializationBase<DesequentializationPass>(options) {
504  maxPrimitives.setValue(options.maxPrimitives);
505  }
506  void runOnOperation() override;
507  void runOnProcess(llhd::ProcessOp procOp) const;
508  LogicalResult
509  isSupportedSequentialProcess(llhd::ProcessOp procOp,
510  const llhd::TemporalRegionAnalysis &trAnalysis,
511  SmallVectorImpl<Value> &observed) const;
512 };
513 } // namespace
514 
515 LogicalResult DesequentializationPass::isSupportedSequentialProcess(
516  llhd::ProcessOp procOp, const llhd::TemporalRegionAnalysis &trAnalysis,
517  SmallVectorImpl<Value> &observed) const {
518  unsigned numTRs = trAnalysis.getNumTemporalRegions();
519 
520  // We only consider the case with three basic blocks and two TRs, because
521  // combinatorial circuits have fewer blocks and don't need
522  // desequentialization and more are not supported for now
523  // NOTE: 3 basic blocks because of the entry block and one for each TR
524  if (numTRs == 1) {
525  LLVM_DEBUG({
526  llvm::dbgs() << " Combinational process -> no need to desequentialize\n";
527  });
528  return failure();
529  }
530 
531  if (numTRs > 2 || procOp.getBody().getBlocks().size() != 3) {
532  LLVM_DEBUG(
533  { llvm::dbgs() << " Complex sequential process -> not supported\n"; });
534  return failure();
535  }
536 
537  bool seenWait = false;
538  WalkResult result = procOp.walk([&](llhd::WaitOp op) -> WalkResult {
539  LLVM_DEBUG({ llvm::dbgs() << " Analyzing Wait Operation:\n"; });
540  for (auto obs : op.getObserved()) {
541  observed.push_back(obs);
542  LLVM_DEBUG({ llvm::dbgs() << " - Observes: " << obs << "\n"; });
543  }
544  LLVM_DEBUG({ llvm::dbgs() << "\n"; });
545 
546  if (seenWait)
547  return failure();
548 
549  // Check that the block containing the wait is the only exiting block of
550  // that TR
551  if (!trAnalysis.hasSingleExitBlock(
552  trAnalysis.getBlockTR(op.getOperation()->getBlock())))
553  return failure();
554 
555  seenWait = true;
556  return WalkResult::advance();
557  });
558 
559  if (result.wasInterrupted() || !seenWait) {
560  LLVM_DEBUG(
561  { llvm::dbgs() << " Complex sequential process -> not supported\n"; });
562  return failure();
563  }
564 
565  LLVM_DEBUG(
566  { llvm::dbgs() << " Sequential process, attempt lowering...\n"; });
567 
568  return success();
569 }
570 
571 void DesequentializationPass::runOnProcess(llhd::ProcessOp procOp) const {
572  LLVM_DEBUG({
573  std::string line(74, '-');
574  llvm::dbgs() << "\n===" << line << "===\n";
575  llvm::dbgs() << "=== Process\n";
576  llvm::dbgs() << "===" << line << "===\n";
577  });
578 
579  llhd::TemporalRegionAnalysis trAnalysis(procOp);
580 
581  // If we don't support it, just skip it.
582  SmallVector<Value> observed;
583  if (failed(isSupportedSequentialProcess(procOp, trAnalysis, observed)))
584  return;
585 
586  OpBuilder builder(procOp);
587  WalkResult result = procOp.walk([&](llhd::DrvOp op) {
588  LLVM_DEBUG({ llvm::dbgs() << "\n Lowering Drive Operation\n"; });
589 
590  if (!op.getEnable()) {
591  LLVM_DEBUG({ llvm::dbgs() << " - No enable condition -> skip\n"; });
592  return WalkResult::advance();
593  }
594 
595  Location loc = op.getLoc();
596  builder.setInsertionPoint(op);
597  int presentTR = trAnalysis.getBlockTR(op.getOperation()->getBlock());
598 
599  auto sampledInPast = [&](Value value) -> bool {
600  if (isa<BlockArgument>(value))
601  return false;
602 
603  if (!procOp->isAncestor(value.getDefiningOp()))
604  return false;
605 
606  return trAnalysis.getBlockTR(value.getDefiningOp()->getBlock()) !=
607  presentTR;
608  };
609 
610  LLVM_DEBUG({ llvm::dbgs() << " - Analyzing enable condition...\n"; });
611 
612  SmallVector<Trigger> triggers;
613  auto sampledFromSameSignal = [](Value val1, Value val2) -> bool {
614  if (auto prb1 = val1.getDefiningOp<llhd::PrbOp>())
615  if (auto prb2 = val2.getDefiningOp<llhd::PrbOp>())
616  return prb1.getSignal() == prb2.getSignal();
617 
618  // TODO: consider signals not represented by hw.inout (and thus don't have
619  // an llhd.prb op to look at)
620  return false;
621  };
622 
623  if (failed(DnfAnalyzer(op.getEnable(), sampledInPast)
624  .computeTriggers(builder, loc, sampledFromSameSignal,
625  triggers, maxPrimitives))) {
626  LLVM_DEBUG({
627  llvm::dbgs() << " Unable to compute trigger list for drive condition, "
628  "skipping...\n";
629  });
630  return WalkResult::interrupt();
631  }
632 
633  LLVM_DEBUG({
634  if (triggers.empty())
635  llvm::dbgs() << " - no triggers found!\n";
636  });
637 
638  LLVM_DEBUG({
639  for (auto trigger : triggers) {
640  llvm::dbgs() << " - Trigger\n";
641  for (auto [clk, kind] : llvm::zip(trigger.clocks, trigger.kinds))
642  llvm::dbgs() << " - " << kind << " "
643  << "clock: " << clk << "\n";
644 
645  if (trigger.enable)
646  llvm::dbgs() << " with enable: " << trigger.enable << "\n";
647  }
648  });
649 
650  // TODO: add support
651  if (triggers.size() > 2 || triggers.empty())
652  return WalkResult::interrupt();
653 
654  // TODO: add support
655  if (triggers[0].clocks.size() != 1 || triggers[0].clocks.size() != 1)
656  return WalkResult::interrupt();
657 
658  // TODO: add support
659  if (triggers[0].kinds[0] == Trigger::Kind::Edge)
660  return WalkResult::interrupt();
661 
662  if (!llvm::any_of(observed, [&](Value val) {
663  return sampledFromSameSignal(val, triggers[0].clocks[0]) &&
664  val.getParentRegion() != procOp.getBody();
665  }))
666  return WalkResult::interrupt();
667 
668  Value clock = builder.create<seq::ToClockOp>(loc, triggers[0].clocks[0]);
669  Value reset, resetValue;
670 
671  if (triggers[0].kinds[0] == Trigger::Kind::NegEdge)
672  clock = builder.create<seq::ClockInverterOp>(loc, clock);
673 
674  if (triggers[0].enable)
675  clock = builder.create<seq::ClockGateOp>(loc, clock, triggers[0].enable);
676 
677  if (triggers.size() == 2) {
678  // TODO: add support
679  if (triggers[1].clocks.size() != 1 || triggers[1].kinds.size() != 1)
680  return WalkResult::interrupt();
681 
682  // TODO: add support
683  if (triggers[1].kinds[0] == Trigger::Kind::Edge)
684  return WalkResult::interrupt();
685 
686  // TODO: add support
687  if (triggers[1].enable)
688  return WalkResult::interrupt();
689 
690  if (!llvm::any_of(observed, [&](Value val) {
691  return sampledFromSameSignal(val, triggers[1].clocks[0]) &&
692  val.getParentRegion() != procOp.getBody();
693  }))
694  return WalkResult::interrupt();
695 
696  reset = triggers[1].clocks[0];
697  resetValue = op.getValue();
698 
699  if (triggers[1].kinds[0] == Trigger::Kind::NegEdge) {
700  Value trueVal =
701  builder.create<hw::ConstantOp>(loc, builder.getBoolAttr(true));
702  reset = builder.create<comb::XorOp>(loc, reset, trueVal);
703  }
704  }
705 
706  // FIXME: this adds async resets as sync resets and might also add the reset
707  // as clock and clock as reset.
708  Value regOut = builder.create<seq::CompRegOp>(loc, op.getValue(), clock,
709  reset, resetValue);
710 
711  op.getEnableMutable().clear();
712  op.getValueMutable().assign(regOut);
713 
714  LLVM_DEBUG(
715  { llvm::dbgs() << " Lowered Drive Operation successfully!\n\n"; });
716 
717  return WalkResult::advance();
718  });
719 
720  if (result.wasInterrupted())
721  return;
722 
723  IRRewriter rewriter(builder);
724  auto &entryBlock = procOp.getBody().getBlocks().front();
725 
726  // Delete the terminator of all blocks in the process.
727  for (Block &block : procOp.getBody().getBlocks()) {
728  block.getTerminator()->erase();
729 
730  if (!block.isEntryBlock())
731  entryBlock.getOperations().splice(entryBlock.end(),
732  block.getOperations());
733  }
734 
735  rewriter.inlineBlockBefore(&entryBlock, procOp);
736  procOp.erase();
737 
738  LLVM_DEBUG({ llvm::dbgs() << "Lowered process successfully!\n"; });
739 }
740 
741 void DesequentializationPass::runOnOperation() {
742  hw::HWModuleOp moduleOp = getOperation();
743  for (auto procOp :
744  llvm::make_early_inc_range(moduleOp.getOps<llhd::ProcessOp>()))
745  runOnProcess(procOp);
746 }
assert(baseType &&"element must be base type")
Four-valued arbitrary precision integers.
Definition: FVInt.h:37
SmallString< 16 > toString(unsigned radix=10, bool uppercase=true) const
Convert an FVInt to a string.
Definition: FVInt.h:616
static FVInt getAllX(unsigned numBits)
Construct an FVInt with all bits set to X.
Definition: FVInt.h:71
void setBit(unsigned index, Bit bit)
Set the value of an individual bit.
Definition: FVInt.h:199
def create(data_type, value)
Definition: hw.py:433
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
raw_ostream & operator<<(raw_ostream &os, const FVInt &value)
Definition: FVInt.h:653