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