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