CIRCT 21.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/LLHDPasses.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) : root(value) {
206 assert(value.getType().isSignlessInteger(1) &&
207 "only 1-bit signless integers supported");
208 }
209
210 LogicalResult prepareAnalyzer(function_ref<bool(Value)> sampledInPast,
211 unsigned maxPrimitives) {
212 DenseSet<Value> alreadyAdded;
213
214 SmallVector<Value> worklist;
215 worklist.push_back(root);
216
217 while (!worklist.empty()) {
218 Value curr = worklist.pop_back_val();
219 auto *defOp = curr.getDefiningOp();
220 if (!defOp) {
221 if (!alreadyAdded.contains(curr)) {
222 primitives.push_back(curr);
223 primitiveSampledInPast.push_back(sampledInPast(curr));
224 alreadyAdded.insert(curr);
225 }
226 continue;
227 }
228
229 TypeSwitch<Operation *>(defOp)
230 .Case<comb::AndOp, comb::OrOp, comb::XorOp>([&](auto op) {
231 worklist.append(llvm::to_vector(op->getOperands()));
232 })
233 .Case([&](comb::ICmpOp op) {
234 if ((op.getPredicate() == circt::comb::ICmpPredicate::eq ||
235 op.getPredicate() == circt::comb::ICmpPredicate::ne) &&
236 op.getLhs().getType().isSignlessInteger(1)) {
237 worklist.append(llvm::to_vector(op->getOperands()));
238 } else {
239 if (!alreadyAdded.contains(curr)) {
240 primitives.push_back(curr);
241 primitiveSampledInPast.push_back(sampledInPast(curr));
242 alreadyAdded.insert(curr);
243 }
244 }
245 })
246 .Case<hw::ConstantOp>([](auto op) { /* ignore */ })
247 .Default([&](auto op) {
248 if (!alreadyAdded.contains(curr)) {
249 primitives.push_back(curr);
250 primitiveSampledInPast.push_back(sampledInPast(curr));
251 alreadyAdded.insert(curr);
252 }
253 });
254 }
255
256 LLVM_DEBUG({
257 for (auto val : primitives)
258 llvm::dbgs() << " - Primitive variable: " << val << "\n";
259 });
260
261 if (primitives.size() > maxPrimitives) {
262 LLVM_DEBUG({ llvm::dbgs() << " Too many primitives, skipping...\n"; });
263 return failure();
264 }
265
266 this->isClock = SmallVector<bool>(primitives.size(), false);
267 this->dontCare = SmallVector<APInt>(primitives.size(),
268 APInt(1ULL << primitives.size(), 0));
269 return success();
270 }
271
272 /// Note that clocks can be dual edge triggered, but this is not directly
273 /// returned as a single Trigger but instead two Triggers with the same clock
274 /// value, one negedge and one posedge kind. The enable should also match in
275 /// that case.
276 LogicalResult
277 computeTriggers(OpBuilder &builder, Location loc,
278 function_ref<bool(Value, Value)> sampledFromSameSignal,
279 SmallVectorImpl<Trigger> &triggers) {
280 // Populate the truth table and the result APInt.
281 computeTruthTable();
282
283 // Detect primitive variable pairs that form a clock and mark them as such.
284 // If a variable sampled in the past cannot be matched with a sample in the
285 // present to form a clock, it will return failure.
286 if (failed(computeClockValuePairs(sampledFromSameSignal)))
287 return failure();
288
289 // Perform boolean expression simplification.
290 simplifyTruthTable();
291
292 LLVM_DEBUG({
293 llvm::dbgs() << " - Truth table:\n";
294
295 for (auto [t, d] : llvm::zip(truthTable, dontCare))
296 llvm::dbgs() << " " << FVInt(std::move(t), std::move(d)) << "\n";
297
298 SmallVector<char> str;
299 result.toString(str, 2, false);
300
301 llvm::dbgs() << " ";
302 for (unsigned i = 0; i < result.getBitWidth() - str.size(); ++i)
303 llvm::dbgs() << '0';
304
305 llvm::dbgs() << str << "\n";
306 });
307
308 // Compute the enable expressions for each trigger. Make sure that the SSA
309 // value for a specific configuration is reused, allowing for easier
310 // canonicalization and merging of triggers later on.
311 materializeTriggerEnables(builder, loc);
312
313 // Iterate over the truth table and extract the triggers.
314 extractTriggerList(triggers);
315
316 // Canonicalize and merge triggers in the trigger list.
317 canonicalizeTriggerList(triggers, builder, loc);
318
319 return success();
320 }
321
322private:
323 FVInt computeEnableKey(unsigned tableRow) {
324 FVInt key = FVInt::getAllX(primitives.size());
325 for (unsigned k = 0; k < primitives.size(); ++k) {
326 if (dontCare[k][tableRow])
327 continue;
328
329 if (primitiveSampledInPast[k])
330 continue;
331
332 // TODO: allow the present value of the clock if it is not trivially
333 // satisfied by the trigger
334 if (isClock[k])
335 continue;
336
337 key.setBit(k, truthTable[k][tableRow]);
338 }
339
340 return key;
341 }
342
343 void extractTriggerList(SmallVectorImpl<Trigger> &triggers) {
344 for (uint64_t i = 0, e = 1ULL << primitives.size(); i < e; ++i) {
345 if (!result[i])
346 continue;
347
348 auto key = computeEnableKey(i);
349 Trigger trigger;
350 for (auto clk : clockPairs) {
351 if (dontCare[clk.second][i] && dontCare[clk.first][i])
352 continue;
353
354 trigger.clocks.push_back(primitives[clk.second]);
355 trigger.kinds.push_back(truthTable[clk.second][i]
356 ? Trigger::Kind::PosEdge
357 : Trigger::Kind::NegEdge);
358 }
359 trigger.enable = enableMap[key];
360
361 if (!trigger.clocks.empty())
362 triggers.push_back(trigger);
363 }
364 }
365
366 void materializeTriggerEnables(OpBuilder &builder, Location loc) {
367 Value trueVal =
368 builder.create<hw::ConstantOp>(loc, builder.getBoolAttr(true));
369 for (uint64_t i = 0, e = 1ULL << primitives.size(); i < e; ++i) {
370 if (!result[i])
371 continue;
372
373 auto key = computeEnableKey(i);
374
375 if (!enableMap.contains(key)) {
376 SmallVector<Value> conjuncts;
377 for (unsigned k = 0; k < primitives.size(); ++k) {
378 if (dontCare[k][i])
379 continue;
380
381 if (primitiveSampledInPast[k])
382 continue;
383
384 // TODO: allow the present value of the clock if it is not trivially
385 // satisfied by the trigger
386 if (isClock[k])
387 continue;
388
389 if (truthTable[k][i]) {
390 conjuncts.push_back(primitives[k]);
391 continue;
392 }
393 conjuncts.push_back(
394 builder.create<comb::XorOp>(loc, primitives[k], trueVal));
395 }
396 if (!conjuncts.empty())
397 enableMap[key] =
398 builder.createOrFold<comb::AndOp>(loc, conjuncts, false);
399 }
400 }
401 }
402
403 LogicalResult computeClockValuePairs(
404 function_ref<bool(Value, Value)> sampledFromSameSignal) {
405 for (unsigned k = 0; k < primitives.size(); ++k) {
406 if (isClock[k])
407 continue;
408
409 for (unsigned l = k + 1; l < primitives.size(); ++l) {
410 if (sampledFromSameSignal(primitives[k], primitives[l]) &&
411 (primitiveSampledInPast[k] != primitiveSampledInPast[l])) {
412 if (primitiveSampledInPast[k])
413 clockPairs.emplace_back(k, l);
414 else
415 clockPairs.emplace_back(l, k);
416 isClock[k] = true;
417 isClock[l] = true;
418 }
419 }
420 if (primitiveSampledInPast[k] && !isClock[k])
421 return failure();
422 }
423
424 return success();
425 }
426
427 void simplifyTruthTable() {
428 uint64_t numEntries = 1 << primitives.size();
429
430 // Perform boolean expression simplifification (see Karnaugh maps).
431 // NOTE: This is a very simple algorithm that may become a bottleneck.
432 // Fortunately, there exist better algorithms we could implement if that
433 // becomes necessay.
434 for (uint64_t i = 0; i < numEntries; ++i) {
435 if (!result[i])
436 continue;
437
438 for (uint64_t k = i + 1; k < numEntries; ++k) {
439 if (!result[i])
440 continue;
441
442 unsigned differenceCount = 0;
443 for (unsigned l = 0; l < primitives.size(); ++l) {
444 if (truthTable[l][i] != truthTable[l][k])
445 ++differenceCount;
446 if (differenceCount > 1)
447 break;
448 }
449
450 if (differenceCount == 1) {
451 for (unsigned l = 0; l < primitives.size(); ++l) {
452 dontCare[l].setBit(k);
453 if (truthTable[l][i] != truthTable[l][k])
454 dontCare[l].setBit(i);
455 }
456 }
457 }
458 }
459 }
460
461 void computeTruthTable() {
462 uint64_t numEntries = 1 << primitives.size();
463 for (auto _ [[maybe_unused]] : primitives)
464 truthTable.push_back(APInt(numEntries, 0));
465
466 for (uint64_t i = 0; i < numEntries; ++i)
467 for (unsigned k = 0; k < primitives.size(); ++k)
468 truthTable[k].setBitVal(i, APInt(64, i)[k]);
469
470 result =
471 CombInterpreter().compute(primitives, truthTable, numEntries, root);
472 }
473
474 void canonicalizeTriggerList(SmallVectorImpl<Trigger> &triggers,
475 OpBuilder &builder, Location loc) {
476 for (auto *iter1 = triggers.begin(); iter1 != triggers.end(); ++iter1) {
477 for (auto *iter2 = iter1 + 1; iter2 != triggers.end(); ++iter2) {
478 if (iter1->clocks == iter2->clocks && iter1->kinds == iter2->kinds) {
479 iter1->enable =
480 builder.create<comb::OrOp>(loc, iter1->enable, iter2->enable);
481 triggers.erase(iter2--);
482 }
483 }
484 }
485
486 // TODO: merge negedge and posedge triggers on the same clock with the same
487 // enables to an 'edge' trigger.
488 }
489
490 Value root;
491 SmallVector<Value> primitives;
492 SmallVector<bool> isClock;
493 SmallVector<bool> primitiveSampledInPast;
494 SmallVector<APInt> truthTable;
495 SmallVector<APInt> dontCare;
496 SmallVector<std::pair<unsigned, unsigned>> clockPairs;
497 DenseMap<FVInt, Value> enableMap;
498 APInt result;
499};
500
501struct DesequentializationPass
502 : public llhd::impl::DesequentializationBase<DesequentializationPass> {
503 DesequentializationPass()
504 : llhd::impl::DesequentializationBase<DesequentializationPass>() {}
505 DesequentializationPass(const llhd::DesequentializationOptions &options)
506 : llhd::impl::DesequentializationBase<DesequentializationPass>(options) {
507 maxPrimitives.setValue(options.maxPrimitives);
508 }
509 void runOnOperation() override;
510 void runOnProcess(llhd::ProcessOp procOp) const;
511 LogicalResult
512 isSupportedSequentialProcess(llhd::ProcessOp procOp,
513 const llhd::TemporalRegionAnalysis &trAnalysis,
514 SmallVectorImpl<Value> &observed) const;
515};
516} // namespace
517
518LogicalResult DesequentializationPass::isSupportedSequentialProcess(
519 llhd::ProcessOp procOp, const llhd::TemporalRegionAnalysis &trAnalysis,
520 SmallVectorImpl<Value> &observed) const {
521 unsigned numTRs = trAnalysis.getNumTemporalRegions();
522
523 // We only consider the case with three basic blocks and two TRs, because
524 // combinatorial circuits have fewer blocks and don't need
525 // desequentialization and more are not supported for now
526 // NOTE: 3 basic blocks because of the entry block and one for each TR
527 if (numTRs == 1) {
528 LLVM_DEBUG({
529 llvm::dbgs() << " Combinational process -> no need to desequentialize\n";
530 });
531 return failure();
532 }
533
534 if (numTRs > 2 || procOp.getBody().getBlocks().size() != 3) {
535 LLVM_DEBUG(
536 { llvm::dbgs() << " Complex sequential process -> not supported\n"; });
537 return failure();
538 }
539
540 bool seenWait = false;
541 WalkResult result = procOp.walk([&](llhd::WaitOp op) -> WalkResult {
542 LLVM_DEBUG({ llvm::dbgs() << " Analyzing Wait Operation:\n"; });
543 for (auto obs : op.getObserved()) {
544 observed.push_back(obs);
545 LLVM_DEBUG({ llvm::dbgs() << " - Observes: " << obs << "\n"; });
546 }
547 LLVM_DEBUG({ llvm::dbgs() << "\n"; });
548
549 if (seenWait)
550 return failure();
551
552 // Check that the block containing the wait is the only exiting block of
553 // that TR
554 if (!trAnalysis.hasSingleExitBlock(
555 trAnalysis.getBlockTR(op.getOperation()->getBlock())))
556 return failure();
557
558 seenWait = true;
559 return WalkResult::advance();
560 });
561
562 if (result.wasInterrupted() || !seenWait) {
563 LLVM_DEBUG(
564 { llvm::dbgs() << " Complex sequential process -> not supported\n"; });
565 return failure();
566 }
567
568 LLVM_DEBUG(
569 { llvm::dbgs() << " Sequential process, attempt lowering...\n"; });
570
571 return success();
572}
573
574void DesequentializationPass::runOnProcess(llhd::ProcessOp procOp) const {
575 LLVM_DEBUG({
576 std::string line(74, '-');
577 llvm::dbgs() << "\n===" << line << "===\n";
578 llvm::dbgs() << "=== Process\n";
579 llvm::dbgs() << "===" << line << "===\n";
580 });
581
582 llhd::TemporalRegionAnalysis trAnalysis(procOp);
583
584 // If we don't support it, just skip it.
585 SmallVector<Value> observed;
586 if (failed(isSupportedSequentialProcess(procOp, trAnalysis, observed)))
587 return;
588
589 OpBuilder builder(procOp);
590 WalkResult result = procOp.walk([&](llhd::DrvOp op) {
591 LLVM_DEBUG({ llvm::dbgs() << "\n Lowering Drive Operation\n"; });
592
593 if (!op.getEnable()) {
594 LLVM_DEBUG({ llvm::dbgs() << " - No enable condition -> skip\n"; });
595 return WalkResult::advance();
596 }
597
598 Location loc = op.getLoc();
599 builder.setInsertionPoint(op);
600 int presentTR = trAnalysis.getBlockTR(op.getOperation()->getBlock());
601
602 auto sampledInPast = [&](Value value) -> bool {
603 if (isa<BlockArgument>(value))
604 return false;
605
606 if (!procOp->isAncestor(value.getDefiningOp()))
607 return false;
608
609 return trAnalysis.getBlockTR(value.getDefiningOp()->getBlock()) !=
610 presentTR;
611 };
612
613 LLVM_DEBUG({ llvm::dbgs() << " - Analyzing enable condition...\n"; });
614
615 SmallVector<Trigger> triggers;
616 auto sampledFromSameSignal = [](Value val1, Value val2) -> bool {
617 if (auto prb1 = val1.getDefiningOp<llhd::PrbOp>())
618 if (auto prb2 = val2.getDefiningOp<llhd::PrbOp>())
619 return prb1.getSignal() == prb2.getSignal();
620
621 // TODO: consider signals not represented by hw.inout (and thus don't have
622 // an llhd.prb op to look at)
623 return false;
624 };
625
626 DnfAnalyzer analyzer(op.getEnable());
627 if (failed(analyzer.prepareAnalyzer(sampledInPast, maxPrimitives)) ||
628 failed(analyzer.computeTriggers(builder, loc, sampledFromSameSignal,
629 triggers))) {
630 LLVM_DEBUG({
631 llvm::dbgs() << " Unable to compute trigger list for drive condition, "
632 "skipping...\n";
633 });
634 return WalkResult::interrupt();
635 }
636
637 LLVM_DEBUG({
638 if (triggers.empty())
639 llvm::dbgs() << " - no triggers found!\n";
640 });
641
642 LLVM_DEBUG({
643 for (auto trigger : triggers) {
644 llvm::dbgs() << " - Trigger\n";
645 for (auto [clk, kind] : llvm::zip(trigger.clocks, trigger.kinds))
646 llvm::dbgs() << " - " << kind << " "
647 << "clock: " << clk << "\n";
648
649 if (trigger.enable)
650 llvm::dbgs() << " with enable: " << trigger.enable << "\n";
651 }
652 });
653
654 // TODO: add support
655 if (triggers.size() > 2 || triggers.empty())
656 return WalkResult::interrupt();
657
658 // TODO: add support
659 if (triggers[0].clocks.size() != 1 || triggers[0].clocks.size() != 1)
660 return WalkResult::interrupt();
661
662 // TODO: add support
663 if (triggers[0].kinds[0] == Trigger::Kind::Edge)
664 return WalkResult::interrupt();
665
666 if (!llvm::any_of(observed, [&](Value val) {
667 return sampledFromSameSignal(val, triggers[0].clocks[0]) &&
668 val.getParentRegion() != procOp.getBody();
669 }))
670 return WalkResult::interrupt();
671
672 Value clock = builder.create<seq::ToClockOp>(loc, triggers[0].clocks[0]);
673 Value reset, resetValue;
674
675 if (triggers[0].kinds[0] == Trigger::Kind::NegEdge)
676 clock = builder.create<seq::ClockInverterOp>(loc, clock);
677
678 if (triggers[0].enable)
679 clock = builder.create<seq::ClockGateOp>(loc, clock, triggers[0].enable);
680
681 if (triggers.size() == 2) {
682 // TODO: add support
683 if (triggers[1].clocks.size() != 1 || triggers[1].kinds.size() != 1)
684 return WalkResult::interrupt();
685
686 // TODO: add support
687 if (triggers[1].kinds[0] == Trigger::Kind::Edge)
688 return WalkResult::interrupt();
689
690 // TODO: add support
691 if (triggers[1].enable)
692 return WalkResult::interrupt();
693
694 if (!llvm::any_of(observed, [&](Value val) {
695 return sampledFromSameSignal(val, triggers[1].clocks[0]) &&
696 val.getParentRegion() != procOp.getBody();
697 }))
698 return WalkResult::interrupt();
699
700 reset = triggers[1].clocks[0];
701 resetValue = op.getValue();
702
703 if (triggers[1].kinds[0] == Trigger::Kind::NegEdge) {
704 Value trueVal =
705 builder.create<hw::ConstantOp>(loc, builder.getBoolAttr(true));
706 reset = builder.create<comb::XorOp>(loc, reset, trueVal);
707 }
708 }
709
710 // FIXME: this adds async resets as sync resets and might also add the reset
711 // as clock and clock as reset.
712 Value regOut = builder.create<seq::CompRegOp>(loc, op.getValue(), clock,
713 reset, resetValue);
714
715 op.getEnableMutable().clear();
716 op.getValueMutable().assign(regOut);
717 Value epsilonTime =
718 builder.create<llhd::ConstantTimeOp>(loc, 0, "ns", 0, 1);
719 op.getTimeMutable().assign(epsilonTime);
720
721 LLVM_DEBUG(
722 { llvm::dbgs() << " Lowered Drive Operation successfully!\n\n"; });
723
724 return WalkResult::advance();
725 });
726
727 if (result.wasInterrupted())
728 return;
729
730 IRRewriter rewriter(builder);
731 auto &entryBlock = procOp.getBody().getBlocks().front();
732
733 // Delete the terminator of all blocks in the process.
734 for (Block &block : procOp.getBody().getBlocks()) {
735 block.getTerminator()->erase();
736
737 if (!block.isEntryBlock())
738 entryBlock.getOperations().splice(entryBlock.end(),
739 block.getOperations());
740 }
741
742 rewriter.inlineBlockBefore(&entryBlock, procOp);
743 procOp.erase();
744
745 LLVM_DEBUG({ llvm::dbgs() << "Lowered process successfully!\n"; });
746}
747
748void DesequentializationPass::runOnOperation() {
749 hw::HWModuleOp moduleOp = getOperation();
750 for (auto procOp :
751 llvm::make_early_inc_range(moduleOp.getOps<llhd::ProcessOp>()))
752 runOnProcess(procOp);
753}
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
create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
Definition seq.py:157
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.