Loading [MathJax]/extensions/tex2jax.js
CIRCT 21.0.0git
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
SeqOps.cpp
Go to the documentation of this file.
1//===- SeqOps.cpp - Implement the Seq operations ------------------------===//
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// This file implements sequential ops.
10//
11//===----------------------------------------------------------------------===//
12
17#include "mlir/Analysis/TopologicalSortUtils.h"
18#include "mlir/IR/Builders.h"
19#include "mlir/IR/DialectImplementation.h"
20#include "mlir/IR/Matchers.h"
21#include "mlir/IR/PatternMatch.h"
22
24#include "llvm/ADT/SmallString.h"
25
26using namespace mlir;
27using namespace circt;
28using namespace seq;
29
30bool circt::seq::isValidIndexValues(Value hlmemHandle, ValueRange addresses) {
31 auto memType = cast<seq::HLMemType>(hlmemHandle.getType());
32 auto shape = memType.getShape();
33 if (shape.size() != addresses.size())
34 return false;
35
36 for (auto [dim, addr] : llvm::zip(shape, addresses)) {
37 auto addrType = dyn_cast<IntegerType>(addr.getType());
38 if (!addrType)
39 return false;
40 if (addrType.getIntOrFloatBitWidth() != llvm::Log2_64_Ceil(dim))
41 return false;
42 }
43 return true;
44}
45
46// If there was no name specified, check to see if there was a useful name
47// specified in the asm file.
48static void setNameFromResult(OpAsmParser &parser, OperationState &result) {
49 if (result.attributes.getNamed("name"))
50 return;
51 // If there is no explicit name attribute, get it from the SSA result name.
52 // If numeric, just use an empty name.
53 StringRef resultName = parser.getResultName(0).first;
54 if (!resultName.empty() && isdigit(resultName[0]))
55 resultName = "";
56 result.addAttribute("name", parser.getBuilder().getStringAttr(resultName));
57}
58
59static bool canElideName(OpAsmPrinter &p, Operation *op) {
60 if (!op->hasAttr("name"))
61 return true;
62
63 auto name = op->getAttrOfType<StringAttr>("name").getValue();
64 if (name.empty())
65 return true;
66
67 SmallString<32> resultNameStr;
68 llvm::raw_svector_ostream tmpStream(resultNameStr);
69 p.printOperand(op->getResult(0), tmpStream);
70 auto actualName = tmpStream.str().drop_front();
71 return actualName == name;
72}
73
74static ParseResult
75parseOptionalTypeMatch(OpAsmParser &parser, Type refType,
76 std::optional<OpAsmParser::UnresolvedOperand> operand,
77 Type &type) {
78 if (operand)
79 type = refType;
80 return success();
81}
82
83static void printOptionalTypeMatch(OpAsmPrinter &p, Operation *op, Type refType,
84 Value operand, Type type) {
85 // Nothing to do - this is strictly an implicit parsing helper.
86}
87
89 OpAsmParser &parser, Type refType,
90 std::optional<OpAsmParser::UnresolvedOperand> operand, Type &type) {
91 if (operand)
92 type = seq::ImmutableType::get(refType);
93 return success();
94}
95
96static void printOptionalImmutableTypeMatch(OpAsmPrinter &p, Operation *op,
97 Type refType, Value operand,
98 Type type) {
99 // Nothing to do - this is strictly an implicit parsing helper.
100}
101
102//===----------------------------------------------------------------------===//
103// ReadPortOp
104//===----------------------------------------------------------------------===//
105
106ParseResult ReadPortOp::parse(OpAsmParser &parser, OperationState &result) {
107 llvm::SMLoc loc = parser.getCurrentLocation();
108
109 OpAsmParser::UnresolvedOperand memOperand, rdenOperand;
110 bool hasRdEn = false;
111 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> addressOperands;
112 seq::HLMemType memType;
113
114 if (parser.parseOperand(memOperand) ||
115 parser.parseOperandList(addressOperands, OpAsmParser::Delimiter::Square))
116 return failure();
117
118 if (succeeded(parser.parseOptionalKeyword("rden"))) {
119 if (failed(parser.parseOperand(rdenOperand)))
120 return failure();
121 hasRdEn = true;
122 }
123
124 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
125 parser.parseType(memType))
126 return failure();
127
128 llvm::SmallVector<Type> operandTypes = memType.getAddressTypes();
129 operandTypes.insert(operandTypes.begin(), memType);
130
131 llvm::SmallVector<OpAsmParser::UnresolvedOperand> allOperands = {memOperand};
132 llvm::copy(addressOperands, std::back_inserter(allOperands));
133 if (hasRdEn) {
134 operandTypes.push_back(parser.getBuilder().getI1Type());
135 allOperands.push_back(rdenOperand);
136 }
137
138 if (parser.resolveOperands(allOperands, operandTypes, loc, result.operands))
139 return failure();
140
141 result.addTypes(memType.getElementType());
142
143 llvm::SmallVector<int32_t, 2> operandSizes;
144 operandSizes.push_back(1); // memory handle
145 operandSizes.push_back(addressOperands.size());
146 operandSizes.push_back(hasRdEn ? 1 : 0);
147 result.addAttribute("operandSegmentSizes",
148 parser.getBuilder().getDenseI32ArrayAttr(operandSizes));
149 return success();
150}
151
152void ReadPortOp::print(OpAsmPrinter &p) {
153 p << " " << getMemory() << "[" << getAddresses() << "]";
154 if (getRdEn())
155 p << " rden " << getRdEn();
156 p.printOptionalAttrDict((*this)->getAttrs(), {"operandSegmentSizes"});
157 p << " : " << getMemory().getType();
158}
159
160void ReadPortOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
161 auto memName = getMemory().getDefiningOp<seq::HLMemOp>().getName();
162 setNameFn(getReadData(), (memName + "_rdata").str());
163}
164
165void ReadPortOp::build(OpBuilder &builder, OperationState &result, Value memory,
166 ValueRange addresses, Value rdEn, unsigned latency) {
167 auto memType = cast<seq::HLMemType>(memory.getType());
168 ReadPortOp::build(builder, result, memType.getElementType(), memory,
169 addresses, rdEn, latency);
170}
171
172//===----------------------------------------------------------------------===//
173// WritePortOp
174//===----------------------------------------------------------------------===//
175
176ParseResult WritePortOp::parse(OpAsmParser &parser, OperationState &result) {
177 llvm::SMLoc loc = parser.getCurrentLocation();
178 OpAsmParser::UnresolvedOperand memOperand, dataOperand, wrenOperand;
179 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> addressOperands;
180 seq::HLMemType memType;
181
182 if (parser.parseOperand(memOperand) ||
183 parser.parseOperandList(addressOperands,
184 OpAsmParser::Delimiter::Square) ||
185 parser.parseOperand(dataOperand) || parser.parseKeyword("wren") ||
186 parser.parseOperand(wrenOperand) ||
187 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
188 parser.parseType(memType))
189 return failure();
190
191 llvm::SmallVector<Type> operandTypes = memType.getAddressTypes();
192 operandTypes.insert(operandTypes.begin(), memType);
193 operandTypes.push_back(memType.getElementType());
194 operandTypes.push_back(parser.getBuilder().getI1Type());
195
196 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> allOperands(
197 addressOperands);
198 allOperands.insert(allOperands.begin(), memOperand);
199 allOperands.push_back(dataOperand);
200 allOperands.push_back(wrenOperand);
201
202 if (parser.resolveOperands(allOperands, operandTypes, loc, result.operands))
203 return failure();
204
205 return success();
206}
207
208void WritePortOp::print(OpAsmPrinter &p) {
209 p << " " << getMemory() << "[" << getAddresses() << "] " << getInData()
210 << " wren " << getWrEn();
211 p.printOptionalAttrDict((*this)->getAttrs());
212 p << " : " << getMemory().getType();
213}
214
215//===----------------------------------------------------------------------===//
216// HLMemOp
217//===----------------------------------------------------------------------===//
218
219void HLMemOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
220 setNameFn(getHandle(), getName());
221}
222
223void HLMemOp::build(OpBuilder &builder, OperationState &result, Value clk,
224 Value rst, StringRef name, llvm::ArrayRef<int64_t> shape,
225 Type elementType) {
226 HLMemType t = HLMemType::get(builder.getContext(), shape, elementType);
227 HLMemOp::build(builder, result, t, clk, rst, name);
228}
229
230//===----------------------------------------------------------------------===//
231// FIFOOp
232//===----------------------------------------------------------------------===//
233
234// Flag threshold custom directive
235static ParseResult parseFIFOFlagThreshold(OpAsmParser &parser,
236 IntegerAttr &threshold,
237 Type &outputFlagType,
238 StringRef directive) {
239 // look for an optional "almost_full $threshold" group.
240 if (succeeded(parser.parseOptionalKeyword(directive))) {
241 int64_t thresholdValue;
242 if (succeeded(parser.parseInteger(thresholdValue))) {
243 threshold = parser.getBuilder().getI64IntegerAttr(thresholdValue);
244 outputFlagType = parser.getBuilder().getI1Type();
245 return success();
246 }
247 return parser.emitError(parser.getNameLoc(),
248 "expected integer value after " + directive +
249 " directive");
250 }
251 return success();
252}
253
254ParseResult parseFIFOAFThreshold(OpAsmParser &parser, IntegerAttr &threshold,
255 Type &outputFlagType) {
256 return parseFIFOFlagThreshold(parser, threshold, outputFlagType,
257 "almost_full");
258}
259
260ParseResult parseFIFOAEThreshold(OpAsmParser &parser, IntegerAttr &threshold,
261 Type &outputFlagType) {
262 return parseFIFOFlagThreshold(parser, threshold, outputFlagType,
263 "almost_empty");
264}
265
266void printFIFOAFThreshold(OpAsmPrinter &p, Operation *op, IntegerAttr threshold,
267 Type outputFlagType) {
268 if (threshold)
269 p << "almost_full"
270 << " " << threshold.getInt();
271}
272
273void printFIFOAEThreshold(OpAsmPrinter &p, Operation *op, IntegerAttr threshold,
274 Type outputFlagType) {
275 if (threshold)
276 p << "almost_empty"
277 << " " << threshold.getInt();
278}
279
280void FIFOOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
281 setNameFn(getOutput(), "out");
282 setNameFn(getEmpty(), "empty");
283 setNameFn(getFull(), "full");
284 if (auto ae = getAlmostEmpty())
285 setNameFn(ae, "almostEmpty");
286 if (auto af = getAlmostFull())
287 setNameFn(af, "almostFull");
288}
289
290LogicalResult FIFOOp::verify() {
291 auto aet = getAlmostEmptyThreshold();
292 auto aft = getAlmostFullThreshold();
293 size_t depth = getDepth();
294 if (aft.has_value() && aft.value() > depth)
295 return emitOpError("almost full threshold must be <= FIFO depth");
296
297 if (aet.has_value() && aet.value() > depth)
298 return emitOpError("almost empty threshold must be <= FIFO depth");
299
300 return success();
301}
302
303//===----------------------------------------------------------------------===//
304// CompRegOp
305//===----------------------------------------------------------------------===//
306
307/// Suggest a name for each result value based on the saved result names
308/// attribute.
309void CompRegOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
310 // If the wire has an optional 'name' attribute, use it.
311 if (auto name = getName())
312 setNameFn(getResult(), *name);
313}
314
315LogicalResult CompRegOp::verify() {
316 if ((getReset() == nullptr) ^ (getResetValue() == nullptr))
317 return emitOpError(
318 "either reset and resetValue or neither must be specified");
319 return success();
320}
321
322std::optional<size_t> CompRegOp::getTargetResultIndex() { return 0; }
323
324template <typename TOp>
325LogicalResult verifyResets(TOp op) {
326 if ((op.getReset() == nullptr) ^ (op.getResetValue() == nullptr))
327 return op->emitOpError(
328 "either reset and resetValue or neither must be specified");
329 bool hasReset = op.getReset() != nullptr;
330 if (hasReset && op.getResetValue().getType() != op.getInput().getType())
331 return op->emitOpError("reset value must be the same type as the input");
332
333 return success();
334}
335
336/// Suggest a name for each result value based on the saved result names
337/// attribute.
338void CompRegClockEnabledOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
339 // If the wire has an optional 'name' attribute, use it.
340 if (auto name = getName())
341 setNameFn(getResult(), *name);
342}
343
344std::optional<size_t> CompRegClockEnabledOp::getTargetResultIndex() {
345 return 0;
346}
347
348LogicalResult CompRegClockEnabledOp::verify() {
349 if (failed(verifyResets(*this)))
350 return failure();
351 return success();
352}
353
354//===----------------------------------------------------------------------===//
355// ShiftRegOp
356//===----------------------------------------------------------------------===//
357
358void ShiftRegOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
359 // If the wire has an optional 'name' attribute, use it.
360 if (auto name = getName())
361 setNameFn(getResult(), *name);
362}
363
364std::optional<size_t> ShiftRegOp::getTargetResultIndex() { return 0; }
365
366LogicalResult ShiftRegOp::verify() {
367 if (failed(verifyResets(*this)))
368 return failure();
369 return success();
370}
371
372//===----------------------------------------------------------------------===//
373// FirRegOp
374//===----------------------------------------------------------------------===//
375
376void FirRegOp::build(OpBuilder &builder, OperationState &result, Value input,
377 Value clk, StringAttr name, hw::InnerSymAttr innerSym,
378 Attribute preset) {
379
380 OpBuilder::InsertionGuard guard(builder);
381
382 result.addOperands(input);
383 result.addOperands(clk);
384
385 result.addAttribute(getNameAttrName(result.name), name);
386
387 if (innerSym)
388 result.addAttribute(getInnerSymAttrName(result.name), innerSym);
389
390 if (preset)
391 result.addAttribute(getPresetAttrName(result.name), preset);
392
393 result.addTypes(input.getType());
394}
395
396void FirRegOp::build(OpBuilder &builder, OperationState &result, Value input,
397 Value clk, StringAttr name, Value reset, Value resetValue,
398 hw::InnerSymAttr innerSym, bool isAsync) {
399
400 OpBuilder::InsertionGuard guard(builder);
401
402 result.addOperands(input);
403 result.addOperands(clk);
404 result.addOperands(reset);
405 result.addOperands(resetValue);
406
407 result.addAttribute(getNameAttrName(result.name), name);
408 if (isAsync)
409 result.addAttribute(getIsAsyncAttrName(result.name), builder.getUnitAttr());
410
411 if (innerSym)
412 result.addAttribute(getInnerSymAttrName(result.name), innerSym);
413
414 result.addTypes(input.getType());
415}
416
417ParseResult FirRegOp::parse(OpAsmParser &parser, OperationState &result) {
418 auto &builder = parser.getBuilder();
419 llvm::SMLoc loc = parser.getCurrentLocation();
420
421 using Op = OpAsmParser::UnresolvedOperand;
422
423 Op next, clk;
424 if (parser.parseOperand(next) || parser.parseKeyword("clock") ||
425 parser.parseOperand(clk))
426 return failure();
427
428 if (succeeded(parser.parseOptionalKeyword("sym"))) {
429 hw::InnerSymAttr innerSym;
430 if (parser.parseCustomAttributeWithFallback(innerSym, /*type=*/nullptr,
431 "inner_sym", result.attributes))
432 return failure();
433 }
434
435 // Parse reset [sync|async] %reset, %value
436 std::optional<std::pair<Op, Op>> resetAndValue;
437 if (succeeded(parser.parseOptionalKeyword("reset"))) {
438 bool isAsync;
439 if (succeeded(parser.parseOptionalKeyword("async")))
440 isAsync = true;
441 else if (succeeded(parser.parseOptionalKeyword("sync")))
442 isAsync = false;
443 else
444 return parser.emitError(loc, "invalid reset, expected 'sync' or 'async'");
445 if (isAsync)
446 result.attributes.append("isAsync", builder.getUnitAttr());
447
448 resetAndValue = {{}, {}};
449 if (parser.parseOperand(resetAndValue->first) || parser.parseComma() ||
450 parser.parseOperand(resetAndValue->second))
451 return failure();
452 }
453
454 std::optional<APInt> presetValue;
455 llvm::SMLoc presetValueLoc;
456 if (succeeded(parser.parseOptionalKeyword("preset"))) {
457 presetValueLoc = parser.getCurrentLocation();
458 OptionalParseResult presetIntResult =
459 parser.parseOptionalInteger(presetValue.emplace());
460 if (!presetIntResult.has_value() || failed(*presetIntResult))
461 return parser.emitError(loc, "expected integer value");
462 }
463
464 Type ty;
465 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
466 parser.parseType(ty))
467 return failure();
468 result.addTypes({ty});
469
470 if (presetValue) {
471 uint64_t width = 0;
472 if (hw::type_isa<seq::ClockType>(ty)) {
473 width = 1;
474 } else {
475 int64_t maybeWidth = hw::getBitWidth(ty);
476 if (maybeWidth < 0)
477 return parser.emitError(presetValueLoc,
478 "cannot preset register of unknown width");
479 width = maybeWidth;
480 }
481
482 APInt presetResult = presetValue->sextOrTrunc(width);
483 if (presetResult.zextOrTrunc(presetValue->getBitWidth()) != *presetValue)
484 return parser.emitError(loc, "preset value too large");
485
486 auto builder = parser.getBuilder();
487 auto presetTy = builder.getIntegerType(width);
488 auto resultAttr = builder.getIntegerAttr(presetTy, presetResult);
489 result.addAttribute("preset", resultAttr);
490 }
491
492 setNameFromResult(parser, result);
493
494 if (parser.resolveOperand(next, ty, result.operands))
495 return failure();
496
497 Type clkTy = ClockType::get(result.getContext());
498 if (parser.resolveOperand(clk, clkTy, result.operands))
499 return failure();
500
501 if (resetAndValue) {
502 Type i1 = IntegerType::get(result.getContext(), 1);
503 if (parser.resolveOperand(resetAndValue->first, i1, result.operands) ||
504 parser.resolveOperand(resetAndValue->second, ty, result.operands))
505 return failure();
506 }
507
508 return success();
509}
510
511void FirRegOp::print(::mlir::OpAsmPrinter &p) {
512 SmallVector<StringRef> elidedAttrs = {
513 getInnerSymAttrName(), getIsAsyncAttrName(), getPresetAttrName()};
514
515 p << ' ' << getNext() << " clock " << getClk();
516
517 if (auto sym = getInnerSymAttr()) {
518 p << " sym ";
519 sym.print(p);
520 }
521
522 if (hasReset()) {
523 p << " reset " << (getIsAsync() ? "async" : "sync") << ' ';
524 p << getReset() << ", " << getResetValue();
525 }
526
527 if (auto preset = getPresetAttr()) {
528 p << " preset " << preset.getValue();
529 }
530
531 if (canElideName(p, *this))
532 elidedAttrs.push_back("name");
533
534 p.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
535 p << " : " << getNext().getType();
536}
537
538/// Verifier for the FIR register op.
539LogicalResult FirRegOp::verify() {
540 if (getReset() || getResetValue() || getIsAsync()) {
541 if (!getReset() || !getResetValue())
542 return emitOpError("must specify reset and reset value");
543 } else {
544 if (getIsAsync())
545 return emitOpError("register with no reset cannot be async");
546 }
547 if (auto preset = getPresetAttr()) {
548 int64_t presetWidth = hw::getBitWidth(preset.getType());
549 int64_t width = hw::getBitWidth(getType());
550 if (preset.getType() != getType() && presetWidth != width)
551 return emitOpError("preset type width must match register type");
552 }
553 return success();
554}
555
556/// Suggest a name for each result value based on the saved result names
557/// attribute.
558void FirRegOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
559 // If the register has an optional 'name' attribute, use it.
560 if (!getName().empty())
561 setNameFn(getResult(), getName());
562}
563
564std::optional<size_t> FirRegOp::getTargetResultIndex() { return 0; }
565
566LogicalResult FirRegOp::canonicalize(FirRegOp op, PatternRewriter &rewriter) {
567
568 // If the register has a constant zero reset, drop the reset and reset value
569 // altogether (And preserve the PresetAttr).
570 if (auto reset = op.getReset()) {
571 if (auto constOp = reset.getDefiningOp<hw::ConstantOp>()) {
572 if (constOp.getValue().isZero()) {
573 rewriter.replaceOpWithNewOp<FirRegOp>(
574 op, op.getNext(), op.getClk(), op.getNameAttr(),
575 op.getInnerSymAttr(), op.getPresetAttr());
576 return success();
577 }
578 }
579 }
580
581 // If the register has a symbol, we can't optimize it away.
582 if (op.getInnerSymAttr())
583 return failure();
584
585 // Replace a register with a trivial feedback or constant clock with a
586 // constant zero.
587 // TODO: Once HW aggregate constant values are supported, move this
588 // canonicalization to the folder.
589 auto isConstant = [&]() -> bool {
590 if (op.getNext() == op.getResult())
591 return true;
592 if (auto clk = op.getClk().getDefiningOp<seq::ToClockOp>())
593 return clk.getInput().getDefiningOp<hw::ConstantOp>();
594 return false;
595 };
596
597 // Preset can block canonicalization only if it is non-zero.
598 bool replaceWithConstZero = true;
599 if (auto preset = op.getPresetAttr())
600 if (!preset.getValue().isZero())
601 replaceWithConstZero = false;
602
603 if (isConstant() && !op.getResetValue() && replaceWithConstZero) {
604 if (isa<seq::ClockType>(op.getType())) {
605 rewriter.replaceOpWithNewOp<seq::ConstClockOp>(
606 op, seq::ClockConstAttr::get(rewriter.getContext(), ClockConst::Low));
607 } else {
608 auto constant = rewriter.create<hw::ConstantOp>(
609 op.getLoc(), APInt::getZero(hw::getBitWidth(op.getType())));
610 rewriter.replaceOpWithNewOp<hw::BitcastOp>(op, op.getType(), constant);
611 }
612 return success();
613 }
614
615 // For reset-less 1d array registers, replace an uninitialized element with
616 // constant zero. For example, let `r` be a 2xi1 register and its next value
617 // be `{foo, r[0]}`. `r[0]` is connected to itself so will never be
618 // initialized. If we don't enable aggregate preservation, `r_0` is replaced
619 // with `0`. Hence this canonicalization replaces 0th element of the next
620 // value with zero to match the behaviour.
621 if (!op.getReset() && !op.getPresetAttr()) {
622 if (auto arrayCreate = op.getNext().getDefiningOp<hw::ArrayCreateOp>()) {
623 // For now only support 1d arrays.
624 // TODO: Support nested arrays and bundles.
625 if (isa<IntegerType>(
626 hw::type_cast<hw::ArrayType>(op.getResult().getType())
627 .getElementType())) {
628 SmallVector<Value> nextOperands;
629 bool changed = false;
630 for (const auto &[i, value] :
631 llvm::enumerate(arrayCreate.getOperands())) {
632 auto index = arrayCreate.getOperands().size() - i - 1;
633 APInt elementIndex;
634 // Check that the corresponding operand is op's element.
635 if (auto arrayGet = value.getDefiningOp<hw::ArrayGetOp>())
636 if (arrayGet.getInput() == op.getResult() &&
637 matchPattern(arrayGet.getIndex(),
638 m_ConstantInt(&elementIndex)) &&
639 elementIndex == index) {
640 nextOperands.push_back(rewriter.create<hw::ConstantOp>(
641 op.getLoc(),
642 APInt::getZero(hw::getBitWidth(arrayGet.getType()))));
643 changed = true;
644 continue;
645 }
646 nextOperands.push_back(value);
647 }
648 // If one of the operands is self loop, update the next value.
649 if (changed) {
650 auto newNextVal = rewriter.create<hw::ArrayCreateOp>(
651 arrayCreate.getLoc(), nextOperands);
652 if (arrayCreate->hasOneUse())
653 // If the original next value has a single use, we can replace the
654 // value directly.
655 rewriter.replaceOp(arrayCreate, newNextVal);
656 else {
657 // Otherwise, replace the entire firreg with a new one.
658 rewriter.replaceOpWithNewOp<FirRegOp>(op, newNextVal, op.getClk(),
659 op.getNameAttr(),
660 op.getInnerSymAttr());
661 }
662
663 return success();
664 }
665 }
666 }
667 }
668
669 return failure();
670}
671
672OpFoldResult FirRegOp::fold(FoldAdaptor adaptor) {
673 // If the register has a symbol or preset value, we can't optimize it away.
674 // TODO: Handle a preset value.
675 if (getInnerSymAttr())
676 return {};
677
678 auto presetAttr = getPresetAttr();
679
680 // If the register is held in permanent reset, replace it with its reset
681 // value. This works trivially if the reset is asynchronous and therefore
682 // level-sensitive, in which case it will always immediately assume the reset
683 // value in silicon. If it is synchronous, the register value is undefined
684 // until the first clock edge at which point it becomes the reset value, in
685 // which case we simply define the initial value to already be the reset
686 // value. Works only if no preset.
687 if (!presetAttr)
688 if (auto reset = getReset())
689 if (auto constOp = reset.getDefiningOp<hw::ConstantOp>())
690 if (constOp.getValue().isOne())
691 return getResetValue();
692
693 // If the register's next value is trivially it's current value, or the
694 // register is never clocked, we can replace the register with a constant
695 // value.
696 bool isTrivialFeedback = (getNext() == getResult());
697 bool isNeverClocked =
698 adaptor.getClk() != nullptr; // clock operand is constant
699 if (!isTrivialFeedback && !isNeverClocked)
700 return {};
701
702 // If the register has a const reset value, and no preset, we can replace it
703 // with the const reset. We cannot replace it with a non-constant reset value.
704 if (auto resetValue = getResetValue()) {
705 if (auto *op = resetValue.getDefiningOp()) {
706 if (op->hasTrait<OpTrait::ConstantLike>() && !presetAttr)
707 return resetValue;
708 if (auto constOp = dyn_cast<hw::ConstantOp>(op))
709 if (presetAttr.getValue() == constOp.getValue())
710 return resetValue;
711 }
712 return {};
713 }
714
715 // Otherwise we want to replace the register with a constant 0. For now this
716 // only works with integer types.
717 auto intType = dyn_cast<IntegerType>(getType());
718 if (!intType)
719 return {};
720 // If preset present, then replace with preset.
721 if (presetAttr)
722 return presetAttr;
723 return IntegerAttr::get(intType, 0);
724}
725
726//===----------------------------------------------------------------------===//
727// ClockGateOp
728//===----------------------------------------------------------------------===//
729
730OpFoldResult ClockGateOp::fold(FoldAdaptor adaptor) {
731 // Forward the clock if one of the enables is always true.
732 if (isConstantOne(adaptor.getEnable()) ||
733 isConstantOne(adaptor.getTestEnable()))
734 return getInput();
735
736 // Fold to a constant zero clock if the enables are always false.
737 if (isConstantZero(adaptor.getEnable()) &&
738 (!getTestEnable() || isConstantZero(adaptor.getTestEnable())))
739 return ClockConstAttr::get(getContext(), ClockConst::Low);
740
741 // Forward constant zero clocks.
742 if (auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput()))
743 if (clockAttr.getValue() == ClockConst::Low)
744 return ClockConstAttr::get(getContext(), ClockConst::Low);
745
746 // Transitive clock gating - eliminate clock gates that are driven by an
747 // identical enable signal somewhere higher in the clock gate hierarchy.
748 auto clockGateInputOp = getInput().getDefiningOp<ClockGateOp>();
749 while (clockGateInputOp) {
750 if (clockGateInputOp.getEnable() == getEnable() &&
751 clockGateInputOp.getTestEnable() == getTestEnable())
752 return getInput();
753 clockGateInputOp = clockGateInputOp.getInput().getDefiningOp<ClockGateOp>();
754 }
755
756 return {};
757}
758
759LogicalResult ClockGateOp::canonicalize(ClockGateOp op,
760 PatternRewriter &rewriter) {
761 // Remove constant false test enable.
762 if (auto testEnable = op.getTestEnable()) {
763 if (auto constOp = testEnable.getDefiningOp<hw::ConstantOp>()) {
764 if (constOp.getValue().isZero()) {
765 rewriter.modifyOpInPlace(op,
766 [&] { op.getTestEnableMutable().clear(); });
767 return success();
768 }
769 }
770 }
771
772 return failure();
773}
774
775std::optional<size_t> ClockGateOp::getTargetResultIndex() {
776 return std::nullopt;
777}
778
779//===----------------------------------------------------------------------===//
780// ClockMuxOp
781//===----------------------------------------------------------------------===//
782
783OpFoldResult ClockMuxOp::fold(FoldAdaptor adaptor) {
784 if (isConstantOne(adaptor.getCond()))
785 return getTrueClock();
786 if (isConstantZero(adaptor.getCond()))
787 return getFalseClock();
788 return {};
789}
790
791//===----------------------------------------------------------------------===//
792// FirMemOp
793//===----------------------------------------------------------------------===//
794
795LogicalResult FirMemOp::canonicalize(FirMemOp op, PatternRewriter &rewriter) {
796 // Do not change memories if symbols point to them.
797 if (op.getInnerSymAttr())
798 return failure();
799
800 // If the memory has no read ports, erase it.
801 for (auto *user : op->getUsers()) {
802 if (isa<FirMemReadOp, FirMemReadWriteOp>(user))
803 return failure();
804 assert(isa<FirMemWriteOp>(user) && "invalid seq.firmem user");
805 }
806
807 for (auto *user : llvm::make_early_inc_range(op->getUsers()))
808 rewriter.eraseOp(user);
809
810 rewriter.eraseOp(op);
811 return success();
812}
813
814void FirMemOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
815 auto nameAttr = (*this)->getAttrOfType<StringAttr>("name");
816 if (!nameAttr.getValue().empty())
817 setNameFn(getResult(), nameAttr.getValue());
818}
819
820std::optional<size_t> FirMemOp::getTargetResultIndex() { return 0; }
821
822template <class Op>
823static LogicalResult verifyFirMemMask(Op op) {
824 if (auto mask = op.getMask()) {
825 auto memType = op.getMemory().getType();
826 if (!memType.getMaskWidth())
827 return op.emitOpError("has mask operand but memory type '")
828 << memType << "' has no mask";
829 auto expected = IntegerType::get(op.getContext(), *memType.getMaskWidth());
830 if (mask.getType() != expected)
831 return op.emitOpError("has mask operand of type '")
832 << mask.getType() << "', but memory type requires '" << expected
833 << "'";
834 }
835 return success();
836}
837
838LogicalResult FirMemWriteOp::verify() { return verifyFirMemMask(*this); }
839LogicalResult FirMemReadWriteOp::verify() { return verifyFirMemMask(*this); }
840
841static bool isConstClock(Value value) {
842 if (!value)
843 return false;
844 return value.getDefiningOp<seq::ConstClockOp>();
845}
846
847static bool isConstZero(Value value) {
848 if (value)
849 if (auto constOp = value.getDefiningOp<hw::ConstantOp>())
850 return constOp.getValue().isZero();
851 return false;
852}
853
854static bool isConstAllOnes(Value value) {
855 if (value)
856 if (auto constOp = value.getDefiningOp<hw::ConstantOp>())
857 return constOp.getValue().isAllOnes();
858 return false;
859}
860
861LogicalResult FirMemReadOp::canonicalize(FirMemReadOp op,
862 PatternRewriter &rewriter) {
863 // Remove the enable if it is constant true.
864 if (isConstAllOnes(op.getEnable())) {
865 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
866 return success();
867 }
868 return failure();
869}
870
871LogicalResult FirMemWriteOp::canonicalize(FirMemWriteOp op,
872 PatternRewriter &rewriter) {
873 // Remove the write port if it is trivially dead.
874 if (isConstZero(op.getEnable()) || isConstZero(op.getMask()) ||
875 isConstClock(op.getClk())) {
876 rewriter.eraseOp(op);
877 return success();
878 }
879 bool anyChanges = false;
880
881 // Remove the enable if it is constant true.
882 if (auto enable = op.getEnable(); isConstAllOnes(enable)) {
883 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
884 anyChanges = true;
885 }
886
887 // Remove the mask if it is all ones.
888 if (auto mask = op.getMask(); isConstAllOnes(mask)) {
889 rewriter.modifyOpInPlace(op, [&] { op.getMaskMutable().erase(0); });
890 anyChanges = true;
891 }
892
893 return success(anyChanges);
894}
895
896LogicalResult FirMemReadWriteOp::canonicalize(FirMemReadWriteOp op,
897 PatternRewriter &rewriter) {
898 // Replace the read-write port with a read port if the write behavior is
899 // trivially disabled.
900 if (isConstZero(op.getEnable()) || isConstZero(op.getMask()) ||
901 isConstClock(op.getClk()) || isConstZero(op.getMode())) {
902 auto opAttrs = op->getAttrs();
903 auto opAttrNames = op.getAttributeNames();
904 auto newOp = rewriter.replaceOpWithNewOp<FirMemReadOp>(
905 op, op.getMemory(), op.getAddress(), op.getClk(), op.getEnable());
906 for (auto namedAttr : opAttrs)
907 if (!llvm::is_contained(opAttrNames, namedAttr.getName()))
908 newOp->setAttr(namedAttr.getName(), namedAttr.getValue());
909 return success();
910 }
911 bool anyChanges = false;
912
913 // Remove the enable if it is constant true.
914 if (auto enable = op.getEnable(); isConstAllOnes(enable)) {
915 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
916 anyChanges = true;
917 }
918
919 // Remove the mask if it is all ones.
920 if (auto mask = op.getMask(); isConstAllOnes(mask)) {
921 rewriter.modifyOpInPlace(op, [&] { op.getMaskMutable().erase(0); });
922 anyChanges = true;
923 }
924
925 return success(anyChanges);
926}
927
928//===----------------------------------------------------------------------===//
929// ConstClockOp
930//===----------------------------------------------------------------------===//
931
932OpFoldResult ConstClockOp::fold(FoldAdaptor adaptor) {
933 return ClockConstAttr::get(getContext(), getValue());
934}
935
936//===----------------------------------------------------------------------===//
937// ToClockOp/FromClockOp
938//===----------------------------------------------------------------------===//
939
940LogicalResult ToClockOp::canonicalize(ToClockOp op, PatternRewriter &rewriter) {
941 if (auto fromClock = op.getInput().getDefiningOp<FromClockOp>()) {
942 rewriter.replaceOp(op, fromClock.getInput());
943 return success();
944 }
945 return failure();
946}
947
948OpFoldResult ToClockOp::fold(FoldAdaptor adaptor) {
949 if (auto fromClock = getInput().getDefiningOp<FromClockOp>())
950 return fromClock.getInput();
951 if (auto intAttr = dyn_cast_or_null<IntegerAttr>(adaptor.getInput())) {
952 auto value =
953 intAttr.getValue().isZero() ? ClockConst::Low : ClockConst::High;
954 return ClockConstAttr::get(getContext(), value);
955 }
956 return {};
957}
958
959LogicalResult FromClockOp::canonicalize(FromClockOp op,
960 PatternRewriter &rewriter) {
961 if (auto toClock = op.getInput().getDefiningOp<ToClockOp>()) {
962 rewriter.replaceOp(op, toClock.getInput());
963 return success();
964 }
965 return failure();
966}
967
968OpFoldResult FromClockOp::fold(FoldAdaptor adaptor) {
969 if (auto toClock = getInput().getDefiningOp<ToClockOp>())
970 return toClock.getInput();
971 if (auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput())) {
972 auto ty = IntegerType::get(getContext(), 1);
973 return IntegerAttr::get(ty, clockAttr.getValue() == ClockConst::High);
974 }
975 return {};
976}
977
978//===----------------------------------------------------------------------===//
979// ClockInverterOp
980//===----------------------------------------------------------------------===//
981
982OpFoldResult ClockInverterOp::fold(FoldAdaptor adaptor) {
983 if (auto chainedInv = getInput().getDefiningOp<ClockInverterOp>())
984 return chainedInv.getInput();
985 if (auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput())) {
986 auto clockIn = clockAttr.getValue() == ClockConst::High;
987 return ClockConstAttr::get(getContext(),
988 clockIn ? ClockConst::Low : ClockConst::High);
989 }
990 return {};
991}
992
993//===----------------------------------------------------------------------===//
994// FIR memory helper
995//===----------------------------------------------------------------------===//
996
997FirMemory::FirMemory(hw::HWModuleGeneratedOp op) {
998 depth = op->getAttrOfType<IntegerAttr>("depth").getInt();
999 numReadPorts = op->getAttrOfType<IntegerAttr>("numReadPorts").getUInt();
1000 numWritePorts = op->getAttrOfType<IntegerAttr>("numWritePorts").getUInt();
1001 numReadWritePorts =
1002 op->getAttrOfType<IntegerAttr>("numReadWritePorts").getUInt();
1003 readLatency = op->getAttrOfType<IntegerAttr>("readLatency").getUInt();
1004 writeLatency = op->getAttrOfType<IntegerAttr>("writeLatency").getUInt();
1005 dataWidth = op->getAttrOfType<IntegerAttr>("width").getUInt();
1006 if (op->hasAttrOfType<IntegerAttr>("maskGran"))
1007 maskGran = op->getAttrOfType<IntegerAttr>("maskGran").getUInt();
1008 else
1009 maskGran = dataWidth;
1010 readUnderWrite = op->getAttrOfType<seq::RUWAttr>("readUnderWrite").getValue();
1011 writeUnderWrite =
1012 op->getAttrOfType<seq::WUWAttr>("writeUnderWrite").getValue();
1013 if (auto clockIDsAttr = op->getAttrOfType<ArrayAttr>("writeClockIDs"))
1014 for (auto clockID : clockIDsAttr)
1015 writeClockIDs.push_back(
1016 cast<IntegerAttr>(clockID).getValue().getZExtValue());
1017 initFilename = op->getAttrOfType<StringAttr>("initFilename").getValue();
1018 initIsBinary = op->getAttrOfType<BoolAttr>("initIsBinary").getValue();
1019 initIsInline = op->getAttrOfType<BoolAttr>("initIsInline").getValue();
1020}
1021
1022LogicalResult InitialOp::verify() {
1023 // Check outputs.
1024 auto *terminator = this->getBody().front().getTerminator();
1025 if (terminator->getOperands().size() != getNumResults())
1026 return emitError() << "result type doesn't match with the terminator";
1027 for (auto [lhs, rhs] :
1028 llvm::zip(terminator->getOperands().getTypes(), getResultTypes())) {
1029 if (cast<seq::ImmutableType>(rhs).getInnerType() != lhs)
1030 return emitError() << cast<seq::ImmutableType>(rhs).getInnerType()
1031 << " is expected but got " << lhs;
1032 }
1033
1034 auto blockArgs = this->getBody().front().getArguments();
1035
1036 if (blockArgs.size() != getNumOperands())
1037 return emitError() << "operand type doesn't match with the block arg";
1038
1039 for (auto [blockArg, operand] : llvm::zip(blockArgs, getOperands())) {
1040 if (blockArg.getType() !=
1041 cast<ImmutableType>(operand.getType()).getInnerType())
1042 return emitError()
1043 << blockArg.getType() << " is expected but got "
1044 << cast<ImmutableType>(operand.getType()).getInnerType();
1045 }
1046 return success();
1047}
1048void InitialOp::build(OpBuilder &builder, OperationState &result,
1049 TypeRange resultTypes, std::function<void()> ctor) {
1050 OpBuilder::InsertionGuard guard(builder);
1051
1052 builder.createBlock(result.addRegion());
1053 SmallVector<Type> types;
1054 for (auto t : resultTypes)
1055 types.push_back(seq::ImmutableType::get(t));
1056
1057 result.addTypes(types);
1058
1059 if (ctor)
1060 ctor();
1061}
1062
1063TypedValue<seq::ImmutableType>
1064circt::seq::createConstantInitialValue(OpBuilder builder, Location loc,
1065 mlir::IntegerAttr attr) {
1066 auto initial = builder.create<seq::InitialOp>(loc, attr.getType(), [&]() {
1067 auto constant = builder.create<hw::ConstantOp>(loc, attr);
1068 builder.create<seq::YieldOp>(loc, ArrayRef<Value>{constant});
1069 });
1070 return cast<TypedValue<seq::ImmutableType>>(initial->getResult(0));
1071}
1072
1073mlir::TypedValue<seq::ImmutableType>
1074circt::seq::createConstantInitialValue(OpBuilder builder, Operation *op) {
1075 assert(op->getNumResults() == 1 &&
1076 op->hasTrait<mlir::OpTrait::ConstantLike>());
1077 auto initial =
1078 builder.create<seq::InitialOp>(op->getLoc(), op->getResultTypes(), [&]() {
1079 auto clonedOp = builder.clone(*op);
1080 builder.create<seq::YieldOp>(op->getLoc(), clonedOp->getResults());
1081 });
1082 return cast<mlir::TypedValue<seq::ImmutableType>>(initial.getResult(0));
1083}
1084
1085Value circt::seq::unwrapImmutableValue(TypedValue<seq::ImmutableType> value) {
1086 auto resultNum = cast<OpResult>(value).getResultNumber();
1087 auto initialOp = value.getDefiningOp<seq::InitialOp>();
1088 assert(initialOp);
1089 return initialOp.getBodyBlock()->getTerminator()->getOperand(resultNum);
1090}
1091
1092FailureOr<seq::InitialOp> circt::seq::mergeInitialOps(Block *block) {
1093 SmallVector<Operation *> initialOps;
1094 for (auto &op : *block)
1095 if (isa<seq::InitialOp>(op))
1096 initialOps.push_back(&op);
1097
1098 if (!mlir::computeTopologicalSorting(initialOps, {}))
1099 return block->getParentOp()->emitError() << "initial ops cannot be "
1100 << "topologically sorted";
1101
1102 // No need to merge if there is only one initial op.
1103 if (initialOps.size() <= 1)
1104 return initialOps.empty() ? seq::InitialOp()
1105 : cast<seq::InitialOp>(initialOps[0]);
1106
1107 auto initialOp = cast<seq::InitialOp>(initialOps.front());
1108 auto yieldOp = cast<seq::YieldOp>(initialOp.getBodyBlock()->getTerminator());
1109
1110 llvm::MapVector<Value, Value>
1111 resultToYieldOperand; // seq.immutable value to operand.
1112
1113 for (auto [result, operand] :
1114 llvm::zip(initialOp.getResults(), yieldOp->getOperands()))
1115 resultToYieldOperand.insert({result, operand});
1116
1117 for (size_t i = 1; i < initialOps.size(); ++i) {
1118 auto currentInitialOp = cast<seq::InitialOp>(initialOps[i]);
1119 auto operands = currentInitialOp->getOperands();
1120 for (auto [blockArg, operand] :
1121 llvm::zip(currentInitialOp.getBodyBlock()->getArguments(), operands)) {
1122 if (auto initOp = operand.getDefiningOp<seq::InitialOp>()) {
1123 assert(resultToYieldOperand.count(operand) &&
1124 "it must be visited already");
1125 blockArg.replaceAllUsesWith(resultToYieldOperand.lookup(operand));
1126 } else {
1127 // Otherwise add the operand to the current block.
1128 initialOp.getBodyBlock()->addArgument(
1129 cast<seq::ImmutableType>(operand.getType()).getInnerType(),
1130 operand.getLoc());
1131 initialOp.getInputsMutable().append(operand);
1132 }
1133 }
1134
1135 auto currentYieldOp =
1136 cast<seq::YieldOp>(currentInitialOp.getBodyBlock()->getTerminator());
1137
1138 for (auto [result, operand] : llvm::zip(currentInitialOp.getResults(),
1139 currentYieldOp->getOperands()))
1140 resultToYieldOperand.insert({result, operand});
1141
1142 // Append the operands of the current yield op to the original yield op.
1143 yieldOp.getOperandsMutable().append(currentYieldOp.getOperands());
1144 currentYieldOp->erase();
1145
1146 // Append the operations of the current initial op to the original initial
1147 // op.
1148 initialOp.getBodyBlock()->getOperations().splice(
1149 initialOp.end(), currentInitialOp.getBodyBlock()->getOperations());
1150 }
1151
1152 // Move the terminator to the end of the block.
1153 yieldOp->moveBefore(initialOp.getBodyBlock(),
1154 initialOp.getBodyBlock()->end());
1155
1156 auto builder = OpBuilder::atBlockBegin(block);
1157 SmallVector<Type> types;
1158 for (auto [result, operand] : resultToYieldOperand)
1159 types.push_back(operand.getType());
1160
1161 // Create a new initial op which accumulates the results of the merged initial
1162 // ops.
1163 auto newInitial = builder.create<seq::InitialOp>(initialOp.getLoc(), types);
1164 newInitial.getInputsMutable().append(initialOp.getInputs());
1165
1166 for (auto [resultAndOperand, newResult] :
1167 llvm::zip(resultToYieldOperand, newInitial.getResults()))
1168 resultAndOperand.first.replaceAllUsesWith(newResult);
1169
1170 // Update the block arguments of the new initial op.
1171 for (auto oldBlockArg : initialOp.getBodyBlock()->getArguments()) {
1172 auto blockArg = newInitial.getBodyBlock()->addArgument(
1173 oldBlockArg.getType(), oldBlockArg.getLoc());
1174 oldBlockArg.replaceAllUsesWith(blockArg);
1175 }
1176
1177 newInitial.getBodyBlock()->getOperations().splice(
1178 newInitial.end(), initialOp.getBodyBlock()->getOperations());
1179
1180 // Clean up.
1181 while (!initialOps.empty())
1182 initialOps.pop_back_val()->erase();
1183
1184 return newInitial;
1185}
1186
1187//===----------------------------------------------------------------------===//
1188// TableGen generated logic.
1189//===----------------------------------------------------------------------===//
1190
1191// Provide the autogenerated implementation guts for the Op classes.
1192#define GET_OP_CLASSES
1193#include "circt/Dialect/Seq/Seq.cpp.inc"
assert(baseType &&"element must be base type")
MlirType elementType
Definition CHIRRTL.cpp:29
#define isdigit(x)
Definition FIRLexer.cpp:26
static bool isConstZero(Value value)
static InstancePath empty
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
static Block * getBodyBlock(FModuleLike mod)
void printFIFOAFThreshold(OpAsmPrinter &p, Operation *op, IntegerAttr threshold, Type outputFlagType)
Definition SeqOps.cpp:266
static bool isConstClock(Value value)
Definition SeqOps.cpp:841
static ParseResult parseFIFOFlagThreshold(OpAsmParser &parser, IntegerAttr &threshold, Type &outputFlagType, StringRef directive)
Definition SeqOps.cpp:235
static void printOptionalTypeMatch(OpAsmPrinter &p, Operation *op, Type refType, Value operand, Type type)
Definition SeqOps.cpp:83
static bool isConstAllOnes(Value value)
Definition SeqOps.cpp:854
static ParseResult parseOptionalImmutableTypeMatch(OpAsmParser &parser, Type refType, std::optional< OpAsmParser::UnresolvedOperand > operand, Type &type)
Definition SeqOps.cpp:88
void printFIFOAEThreshold(OpAsmPrinter &p, Operation *op, IntegerAttr threshold, Type outputFlagType)
Definition SeqOps.cpp:273
LogicalResult verifyResets(TOp op)
Definition SeqOps.cpp:325
static bool canElideName(OpAsmPrinter &p, Operation *op)
Definition SeqOps.cpp:59
ParseResult parseFIFOAEThreshold(OpAsmParser &parser, IntegerAttr &threshold, Type &outputFlagType)
Definition SeqOps.cpp:260
static LogicalResult verifyFirMemMask(Op op)
Definition SeqOps.cpp:823
static void printOptionalImmutableTypeMatch(OpAsmPrinter &p, Operation *op, Type refType, Value operand, Type type)
Definition SeqOps.cpp:96
static ParseResult parseOptionalTypeMatch(OpAsmParser &parser, Type refType, std::optional< OpAsmParser::UnresolvedOperand > operand, Type &type)
Definition SeqOps.cpp:75
static void setNameFromResult(OpAsmParser &parser, OperationState &result)
Definition SeqOps.cpp:48
ParseResult parseFIFOAFThreshold(OpAsmParser &parser, IntegerAttr &threshold, Type &outputFlagType)
Definition SeqOps.cpp:254
create(data_type, value)
Definition hw.py:433
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition CalyxOps.cpp:55
bool isConstant(Operation *op)
Return true if the specified operation has a constant value.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:110
FailureOr< seq::InitialOp > mergeInitialOps(Block *block)
Definition SeqOps.cpp:1092
bool isValidIndexValues(Value hlmemHandle, ValueRange addresses)
Definition SeqOps.cpp:30
mlir::TypedValue< seq::ImmutableType > createConstantInitialValue(OpBuilder builder, Location loc, mlir::IntegerAttr attr)
Definition SeqOps.cpp:1064
Value unwrapImmutableValue(mlir::TypedValue< seq::ImmutableType > immutableVal)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
static bool isConstantZero(Attribute operand)
Determine whether a constant operand is a zero value.
Definition FoldUtils.h:27
static bool isConstantOne(Attribute operand)
Determine whether a constant operand is a one value.
Definition FoldUtils.h:34
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:183
Definition seq.py:1
FirMemory(hw::HWModuleGeneratedOp op)
Definition SeqOps.cpp:997