CIRCT 20.0.0git
Loading...
Searching...
No Matches
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"
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
27namespace circt {
28namespace llhd {
29#define GEN_PASS_DEF_DESEQUENTIALIZATION
30#include "circt/Dialect/LLHD/Transforms/Passes.h.inc"
31} // namespace llhd
32} // namespace circt
33
34using namespace circt;
35using namespace mlir;
36
37namespace {
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.
41class CombInterpreter : public comb::CombinationalVisitor<CombInterpreter> {
42public:
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
157private:
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.
164struct 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
195template <typename T>
196static T &operator<<(T &os, const Trigger::Kind &kind) {
197 return os << Trigger::stringify(kind);
198}
199
200namespace {
201/// Analyses a boolean expression and converts it to a list of register
202/// triggers.
203class DnfAnalyzer {
204public:
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
319private:
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
498struct 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
515LogicalResult 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
571void 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
741void 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
This helps visit Combinational nodes.
create(data_type, value)
Definition hw.py:433
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.