18#include "mlir/Analysis/TopologicalSortUtils.h"
19#include "mlir/Dialect/Arith/IR/Arith.h"
20#include "mlir/IR/Builders.h"
21#include "mlir/IR/DialectImplementation.h"
22#include "mlir/IR/Matchers.h"
23#include "mlir/IR/PatternMatch.h"
26#include "llvm/ADT/SmallString.h"
33 auto memType = cast<seq::HLMemType>(hlmemHandle.getType());
34 auto shape = memType.getShape();
35 if (shape.size() != addresses.size())
38 for (
auto [dim, addr] : llvm::zip(shape, addresses)) {
39 auto addrType = dyn_cast<IntegerType>(addr.getType());
42 if (addrType.getIntOrFloatBitWidth() != llvm::Log2_64_Ceil(dim))
51 if (result.attributes.getNamed(
"name"))
55 StringRef resultName = parser.getResultName(0).first;
56 if (!resultName.empty() &&
isdigit(resultName[0]))
58 result.addAttribute(
"name", parser.getBuilder().getStringAttr(resultName));
62 if (!op->hasAttr(
"name"))
65 auto name = op->getAttrOfType<StringAttr>(
"name").getValue();
69 SmallString<32> resultNameStr;
70 llvm::raw_svector_ostream tmpStream(resultNameStr);
71 p.printOperand(op->getResult(0), tmpStream);
72 auto actualName = tmpStream.str().drop_front();
73 return actualName == name;
78 std::optional<OpAsmParser::UnresolvedOperand> operand,
86 Value operand, Type type) {
91 OpAsmParser &parser, Type refType,
92 std::optional<OpAsmParser::UnresolvedOperand> operand, Type &type) {
94 type = seq::ImmutableType::get(refType);
99 Type refType, Value operand,
108ParseResult ReadPortOp::parse(OpAsmParser &parser, OperationState &result) {
109 llvm::SMLoc loc = parser.getCurrentLocation();
111 OpAsmParser::UnresolvedOperand memOperand, rdenOperand;
112 bool hasRdEn =
false;
113 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> addressOperands;
114 seq::HLMemType memType;
116 if (parser.parseOperand(memOperand) ||
117 parser.parseOperandList(addressOperands, OpAsmParser::Delimiter::Square))
120 if (succeeded(parser.parseOptionalKeyword(
"rden"))) {
121 if (failed(parser.parseOperand(rdenOperand)))
126 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
127 parser.parseType(memType))
130 llvm::SmallVector<Type> operandTypes = memType.getAddressTypes();
131 operandTypes.insert(operandTypes.begin(), memType);
133 llvm::SmallVector<OpAsmParser::UnresolvedOperand> allOperands = {memOperand};
134 llvm::copy(addressOperands, std::back_inserter(allOperands));
136 operandTypes.push_back(parser.getBuilder().getI1Type());
137 allOperands.push_back(rdenOperand);
140 if (parser.resolveOperands(allOperands, operandTypes, loc, result.operands))
143 result.addTypes(memType.getElementType());
145 llvm::SmallVector<int32_t, 2> operandSizes;
146 operandSizes.push_back(1);
147 operandSizes.push_back(addressOperands.size());
148 operandSizes.push_back(hasRdEn ? 1 : 0);
149 result.addAttribute(
"operandSegmentSizes",
150 parser.getBuilder().getDenseI32ArrayAttr(operandSizes));
154void ReadPortOp::print(OpAsmPrinter &p) {
155 p <<
" " << getMemory() <<
"[" << getAddresses() <<
"]";
157 p <<
" rden " << getRdEn();
158 p.printOptionalAttrDict((*this)->getAttrs(), {
"operandSegmentSizes"});
159 p <<
" : " << getMemory().getType();
163 auto memName = getMemory().getDefiningOp<seq::HLMemOp>().
getName();
164 setNameFn(getReadData(), (memName +
"_rdata").str());
167void ReadPortOp::build(OpBuilder &builder, OperationState &result, Value memory,
168 ValueRange addresses, Value rdEn,
unsigned latency) {
169 auto memType = cast<seq::HLMemType>(memory.getType());
170 ReadPortOp::build(builder, result, memType.getElementType(), memory,
171 addresses, rdEn, latency);
178ParseResult WritePortOp::parse(OpAsmParser &parser, OperationState &result) {
179 llvm::SMLoc loc = parser.getCurrentLocation();
180 OpAsmParser::UnresolvedOperand memOperand, dataOperand, wrenOperand;
181 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> addressOperands;
182 seq::HLMemType memType;
184 if (parser.parseOperand(memOperand) ||
185 parser.parseOperandList(addressOperands,
186 OpAsmParser::Delimiter::Square) ||
187 parser.parseOperand(dataOperand) || parser.parseKeyword(
"wren") ||
188 parser.parseOperand(wrenOperand) ||
189 parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
190 parser.parseType(memType))
193 llvm::SmallVector<Type> operandTypes = memType.getAddressTypes();
194 operandTypes.insert(operandTypes.begin(), memType);
195 operandTypes.push_back(memType.getElementType());
196 operandTypes.push_back(parser.getBuilder().getI1Type());
198 llvm::SmallVector<OpAsmParser::UnresolvedOperand, 2> allOperands(
200 allOperands.insert(allOperands.begin(), memOperand);
201 allOperands.push_back(dataOperand);
202 allOperands.push_back(wrenOperand);
204 if (parser.resolveOperands(allOperands, operandTypes, loc, result.operands))
210void WritePortOp::print(OpAsmPrinter &p) {
211 p <<
" " << getMemory() <<
"[" << getAddresses() <<
"] " << getInData()
212 <<
" wren " << getWrEn();
213 p.printOptionalAttrDict((*this)->getAttrs());
214 p <<
" : " << getMemory().getType();
222 setNameFn(getHandle(),
getName());
225void HLMemOp::build(OpBuilder &builder, OperationState &result, Value clk,
226 Value rst, StringRef name, llvm::ArrayRef<int64_t> shape,
228 HLMemType t = HLMemType::get(builder.getContext(), shape,
elementType);
229 HLMemOp::build(builder, result, t, clk, rst, name);
238 IntegerAttr &threshold,
239 Type &outputFlagType,
240 StringRef directive) {
242 if (succeeded(parser.parseOptionalKeyword(directive))) {
243 int64_t thresholdValue;
244 if (succeeded(parser.parseInteger(thresholdValue))) {
245 threshold = parser.getBuilder().getI64IntegerAttr(thresholdValue);
246 outputFlagType = parser.getBuilder().getI1Type();
249 return parser.emitError(parser.getNameLoc(),
250 "expected integer value after " + directive +
257 Type &outputFlagType) {
263 Type &outputFlagType) {
269 Type outputFlagType) {
272 <<
" " << threshold.getInt();
276 Type outputFlagType) {
279 <<
" " << threshold.getInt();
283 setNameFn(getOutput(),
"out");
284 setNameFn(getEmpty(),
"empty");
285 setNameFn(getFull(),
"full");
286 if (
auto ae = getAlmostEmpty())
287 setNameFn(ae,
"almostEmpty");
288 if (
auto af = getAlmostFull())
289 setNameFn(af,
"almostFull");
292LogicalResult FIFOOp::verify() {
293 auto aet = getAlmostEmptyThreshold();
294 auto aft = getAlmostFullThreshold();
295 size_t depth = getDepth();
296 if (aft.has_value() && aft.value() > depth)
297 return emitOpError(
"almost full threshold must be <= FIFO depth");
299 if (aet.has_value() && aet.value() > depth)
300 return emitOpError(
"almost empty threshold must be <= FIFO depth");
313 setNameFn(getResult(), *name);
316template <
typename TOp>
318 if ((op.getReset() ==
nullptr) ^ (op.getResetValue() ==
nullptr))
319 return op->emitOpError(
320 "either reset and resetValue or neither must be specified");
321 bool hasReset = op.getReset() !=
nullptr;
322 if (hasReset && op.getResetValue().getType() != op.getInput().getType())
323 return op->emitOpError(
"reset value must be the same type as the input");
328std::optional<size_t> CompRegOp::getTargetResultIndex() {
return 0; }
330LogicalResult CompRegOp::verify() {
return verifyResets(*
this); }
340 setNameFn(getResult(), *name);
343std::optional<size_t> CompRegClockEnabledOp::getTargetResultIndex() {
347LogicalResult CompRegClockEnabledOp::verify() {
return verifyResets(*
this); }
350 PatternRewriter &rewriter) {
353 auto *inputOp = op.getInput().getDefiningOp();
354 if (isa_and_nonnull<comb::MuxOp, arith::SelectOp>(inputOp) &&
355 inputOp->getOperand(0) == op.getClockEnable()) {
356 rewriter.modifyOpInPlace(
357 op, [&] { op.getInputMutable().assign(inputOp->getOperand(1)); });
371 setNameFn(getResult(), *name);
374std::optional<size_t> ShiftRegOp::getTargetResultIndex() {
return 0; }
376LogicalResult ShiftRegOp::verify() {
386void FirRegOp::build(OpBuilder &builder, OperationState &result, Value input,
387 Value clk, StringAttr name, hw::InnerSymAttr innerSym,
390 OpBuilder::InsertionGuard guard(builder);
392 result.addOperands(input);
393 result.addOperands(clk);
395 result.addAttribute(getNameAttrName(result.name), name);
398 result.addAttribute(getInnerSymAttrName(result.name), innerSym);
401 result.addAttribute(getPresetAttrName(result.name), preset);
403 result.addTypes(input.getType());
406void FirRegOp::build(OpBuilder &builder, OperationState &result, Value input,
407 Value clk, StringAttr name, Value reset, Value resetValue,
408 hw::InnerSymAttr innerSym,
bool isAsync) {
410 OpBuilder::InsertionGuard guard(builder);
412 result.addOperands(input);
413 result.addOperands(clk);
414 result.addOperands(reset);
415 result.addOperands(resetValue);
417 result.addAttribute(getNameAttrName(result.name), name);
419 result.addAttribute(getIsAsyncAttrName(result.name), builder.getUnitAttr());
422 result.addAttribute(getInnerSymAttrName(result.name), innerSym);
424 result.addTypes(input.getType());
427ParseResult FirRegOp::parse(OpAsmParser &parser, OperationState &result) {
428 auto &builder = parser.getBuilder();
429 llvm::SMLoc loc = parser.getCurrentLocation();
431 using Op = OpAsmParser::UnresolvedOperand;
434 if (parser.parseOperand(next) || parser.parseKeyword(
"clock") ||
435 parser.parseOperand(clk))
438 if (succeeded(parser.parseOptionalKeyword(
"sym"))) {
439 hw::InnerSymAttr innerSym;
440 if (parser.parseCustomAttributeWithFallback(innerSym,
nullptr,
441 "inner_sym", result.attributes))
446 std::optional<std::pair<Op, Op>> resetAndValue;
447 if (succeeded(parser.parseOptionalKeyword(
"reset"))) {
449 if (succeeded(parser.parseOptionalKeyword(
"async")))
451 else if (succeeded(parser.parseOptionalKeyword(
"sync")))
454 return parser.emitError(loc,
"invalid reset, expected 'sync' or 'async'");
456 result.attributes.append(
"isAsync", builder.getUnitAttr());
458 resetAndValue = {{}, {}};
459 if (parser.parseOperand(resetAndValue->first) || parser.parseComma() ||
460 parser.parseOperand(resetAndValue->second))
464 std::optional<APInt> presetValue;
465 llvm::SMLoc presetValueLoc;
466 if (succeeded(parser.parseOptionalKeyword(
"preset"))) {
467 presetValueLoc = parser.getCurrentLocation();
468 OptionalParseResult presetIntResult =
469 parser.parseOptionalInteger(presetValue.emplace());
470 if (!presetIntResult.has_value() || failed(*presetIntResult))
471 return parser.emitError(loc,
"expected integer value");
475 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
476 parser.parseType(ty))
478 result.addTypes({ty});
482 if (hw::type_isa<seq::ClockType>(ty)) {
487 return parser.emitError(presetValueLoc,
488 "cannot preset register of unknown width");
492 APInt presetResult = presetValue->sextOrTrunc(width);
493 if (presetResult.zextOrTrunc(presetValue->getBitWidth()) != *presetValue)
494 return parser.emitError(loc,
"preset value too large");
496 auto builder = parser.getBuilder();
497 auto presetTy = builder.getIntegerType(width);
498 auto resultAttr = builder.getIntegerAttr(presetTy, presetResult);
499 result.addAttribute(
"preset", resultAttr);
504 if (parser.resolveOperand(next, ty, result.operands))
507 Type clkTy = ClockType::get(result.getContext());
508 if (parser.resolveOperand(clk, clkTy, result.operands))
512 Type i1 = IntegerType::get(result.getContext(), 1);
513 if (parser.resolveOperand(resetAndValue->first, i1, result.operands) ||
514 parser.resolveOperand(resetAndValue->second, ty, result.operands))
521void FirRegOp::print(::mlir::OpAsmPrinter &p) {
522 SmallVector<StringRef> elidedAttrs = {
523 getInnerSymAttrName(), getIsAsyncAttrName(), getPresetAttrName()};
525 p <<
' ' << getNext() <<
" clock " << getClk();
527 if (
auto sym = getInnerSymAttr()) {
533 p <<
" reset " << (getIsAsync() ?
"async" :
"sync") <<
' ';
534 p << getReset() <<
", " << getResetValue();
537 if (
auto preset = getPresetAttr()) {
538 p <<
" preset " << preset.getValue();
542 elidedAttrs.push_back(
"name");
544 p.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
545 p <<
" : " << getNext().getType();
549LogicalResult FirRegOp::verify() {
550 if (getReset() || getResetValue() || getIsAsync()) {
551 if (!getReset() || !getResetValue())
552 return emitOpError(
"must specify reset and reset value");
555 return emitOpError(
"register with no reset cannot be async");
557 if (
auto preset = getPresetAttr()) {
560 if (preset.getType() != getType() && presetWidth != width)
561 return emitOpError(
"preset type width must match register type");
571 setNameFn(getResult(),
getName());
574std::optional<size_t> FirRegOp::getTargetResultIndex() {
return 0; }
576LogicalResult FirRegOp::canonicalize(FirRegOp op, PatternRewriter &rewriter) {
580 if (
auto reset = op.getReset()) {
582 if (constOp.getValue().isZero()) {
583 rewriter.replaceOpWithNewOp<FirRegOp>(
584 op, op.getNext(), op.getClk(), op.getNameAttr(),
585 op.getInnerSymAttr(), op.getPresetAttr());
592 if (op.getInnerSymAttr())
600 if (op.getNext() == op.getResult())
602 if (
auto clk = op.getClk().getDefiningOp<seq::ToClockOp>())
608 bool replaceWithConstZero =
true;
609 if (
auto preset = op.getPresetAttr())
610 if (!preset.getValue().isZero())
611 replaceWithConstZero =
false;
613 if (
isConstant() && !op.getResetValue() && replaceWithConstZero) {
614 if (isa<seq::ClockType>(op.getType())) {
615 rewriter.replaceOpWithNewOp<seq::ConstClockOp>(
616 op, seq::ClockConstAttr::get(rewriter.getContext(), ClockConst::Low));
620 rewriter.replaceOpWithNewOp<
hw::BitcastOp>(op, op.getType(), constant);
630 if (
auto nextMux = op.getNext().getDefiningOp<
comb::MuxOp>()) {
632 if (op.getPresetAttr())
639 if (nextMux.getTrueValue() == op.getResult() &&
640 matchPattern(nextMux.getFalseValue(), m_Constant(&value))) {
641 replacedValue = nextMux.getFalseValue();
644 else if (nextMux.getFalseValue() == op.getResult() &&
645 matchPattern(nextMux.getTrueValue(), m_Constant(&value))) {
646 replacedValue = nextMux.getTrueValue();
654 if (op.getResetValue()) {
655 Attribute resetConst;
656 if (matchPattern(op.getResetValue(), m_Constant(&resetConst))) {
657 if (resetConst != value)
667 rewriter.replaceOp(op, replacedValue);
677 if (!op.getReset() && !op.getPresetAttr()) {
681 if (isa<IntegerType>(
682 hw::type_cast<hw::ArrayType>(op.getResult().getType())
683 .getElementType())) {
684 SmallVector<Value> nextOperands;
685 bool changed =
false;
686 for (
const auto &[i, value] :
687 llvm::enumerate(arrayCreate.getOperands())) {
688 auto index = arrayCreate.getOperands().size() - i - 1;
692 if (arrayGet.getInput() == op.getResult() &&
693 matchPattern(arrayGet.getIndex(),
694 m_ConstantInt(&elementIndex)) &&
695 elementIndex == index) {
702 nextOperands.push_back(value);
707 arrayCreate.getLoc(), nextOperands);
708 if (arrayCreate->hasOneUse())
711 rewriter.replaceOp(arrayCreate, newNextVal);
714 rewriter.replaceOpWithNewOp<FirRegOp>(op, newNextVal, op.getClk(),
716 op.getInnerSymAttr());
728OpFoldResult FirRegOp::fold(FoldAdaptor adaptor) {
731 if (getInnerSymAttr())
734 auto presetAttr = getPresetAttr();
744 if (
auto reset = getReset())
746 if (constOp.getValue().isOne())
747 return getResetValue();
752 bool isTrivialFeedback = (getNext() == getResult());
753 bool isNeverClocked =
754 adaptor.getClk() !=
nullptr;
755 if (!isTrivialFeedback && !isNeverClocked)
760 if (
auto resetValue = getResetValue()) {
761 if (
auto *op = resetValue.getDefiningOp()) {
762 if (op->hasTrait<OpTrait::ConstantLike>() && !presetAttr)
764 if (
auto constOp = dyn_cast<hw::ConstantOp>(op))
765 if (presetAttr.getValue() == constOp.getValue())
773 auto intType = dyn_cast<IntegerType>(getType());
779 return IntegerAttr::get(intType, 0);
786OpFoldResult ClockGateOp::fold(FoldAdaptor adaptor) {
795 return ClockConstAttr::get(getContext(), ClockConst::Low);
798 if (
auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput()))
799 if (clockAttr.getValue() == ClockConst::Low)
800 return ClockConstAttr::get(getContext(), ClockConst::Low);
804 auto clockGateInputOp = getInput().getDefiningOp<ClockGateOp>();
805 while (clockGateInputOp) {
806 if (clockGateInputOp.getEnable() == getEnable() &&
807 clockGateInputOp.getTestEnable() == getTestEnable())
809 clockGateInputOp = clockGateInputOp.getInput().getDefiningOp<ClockGateOp>();
815LogicalResult ClockGateOp::canonicalize(ClockGateOp op,
816 PatternRewriter &rewriter) {
818 if (
auto testEnable = op.getTestEnable()) {
820 if (constOp.getValue().isZero()) {
821 rewriter.modifyOpInPlace(op,
822 [&] { op.getTestEnableMutable().clear(); });
831std::optional<size_t> ClockGateOp::getTargetResultIndex() {
839OpFoldResult ClockMuxOp::fold(FoldAdaptor adaptor) {
841 return getTrueClock();
843 return getFalseClock();
851LogicalResult FirMemOp::canonicalize(FirMemOp op, PatternRewriter &rewriter) {
853 if (op.getInnerSymAttr())
856 bool readOnly =
true, writeOnly =
true;
859 for (
auto *user : op->getUsers()) {
860 if (isa<FirMemReadOp, FirMemReadWriteOp>(user)) {
863 if (isa<FirMemWriteOp, FirMemReadWriteOp>(user)) {
866 assert((isa<FirMemReadOp, FirMemWriteOp, FirMemReadWriteOp>(user)) &&
867 "invalid seq.firmem user");
870 for (
auto *user :
llvm::make_early_inc_range(op->getUsers()))
871 rewriter.eraseOp(user);
873 rewriter.eraseOp(op);
877 if (readOnly && !op.getInit()) {
879 for (
auto *user :
llvm::make_early_inc_range(op->getUsers())) {
880 auto readOp = cast<FirMemReadOp>(user);
883 if (readOp.getType() != zero.getType())
886 rewriter.replaceOp(readOp, zero);
888 rewriter.eraseOp(op);
895 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
896 if (!nameAttr.getValue().empty())
897 setNameFn(getResult(), nameAttr.getValue());
900std::optional<size_t> FirMemOp::getTargetResultIndex() {
return 0; }
904 if (
auto mask = op.getMask()) {
905 auto memType = op.getMemory().getType();
906 if (!memType.getMaskWidth())
907 return op.emitOpError(
"has mask operand but memory type '")
908 << memType <<
"' has no mask";
909 auto expected = IntegerType::get(op.getContext(), *memType.getMaskWidth());
910 if (mask.getType() != expected)
911 return op.emitOpError(
"has mask operand of type '")
912 << mask.getType() <<
"', but memory type requires '" << expected
919LogicalResult FirMemReadWriteOp::verify() {
return verifyFirMemMask(*
this); }
924 return value.getDefiningOp<seq::ConstClockOp>();
930 return constOp.getValue().isZero();
937 return constOp.getValue().isAllOnes();
941LogicalResult FirMemReadOp::canonicalize(FirMemReadOp op,
942 PatternRewriter &rewriter) {
945 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
951LogicalResult FirMemWriteOp::canonicalize(FirMemWriteOp op,
952 PatternRewriter &rewriter) {
956 auto memOp = op.getMemory().getDefiningOp<FirMemOp>();
957 if (memOp.getInnerSymAttr())
959 rewriter.eraseOp(op);
962 bool anyChanges =
false;
966 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
972 rewriter.modifyOpInPlace(op, [&] { op.getMaskMutable().erase(0); });
976 return success(anyChanges);
979LogicalResult FirMemReadWriteOp::canonicalize(FirMemReadWriteOp op,
980 PatternRewriter &rewriter) {
985 auto opAttrs = op->getAttrs();
986 auto opAttrNames = op.getAttributeNames();
987 auto newOp = rewriter.replaceOpWithNewOp<FirMemReadOp>(
988 op, op.getMemory(), op.getAddress(), op.getClk(), op.getEnable());
989 for (
auto namedAttr : opAttrs)
990 if (!
llvm::is_contained(opAttrNames, namedAttr.
getName()))
991 newOp->setAttr(namedAttr.
getName(), namedAttr.getValue());
994 bool anyChanges =
false;
998 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
1004 rewriter.modifyOpInPlace(op, [&] { op.getMaskMutable().erase(0); });
1008 return success(anyChanges);
1015OpFoldResult ConstClockOp::fold(FoldAdaptor adaptor) {
1016 return ClockConstAttr::get(getContext(), getValue());
1023LogicalResult ToClockOp::canonicalize(ToClockOp op, PatternRewriter &rewriter) {
1024 if (
auto fromClock = op.getInput().getDefiningOp<FromClockOp>()) {
1025 rewriter.replaceOp(op, fromClock.getInput());
1031OpFoldResult ToClockOp::fold(FoldAdaptor adaptor) {
1032 if (
auto fromClock = getInput().getDefiningOp<FromClockOp>())
1033 return fromClock.getInput();
1034 if (
auto intAttr = dyn_cast_or_null<IntegerAttr>(adaptor.getInput())) {
1036 intAttr.getValue().isZero() ? ClockConst::Low : ClockConst::High;
1037 return ClockConstAttr::get(getContext(), value);
1042LogicalResult FromClockOp::canonicalize(FromClockOp op,
1043 PatternRewriter &rewriter) {
1044 if (
auto toClock = op.getInput().getDefiningOp<ToClockOp>()) {
1045 rewriter.replaceOp(op, toClock.getInput());
1051OpFoldResult FromClockOp::fold(FoldAdaptor adaptor) {
1052 if (
auto toClock = getInput().getDefiningOp<ToClockOp>())
1053 return toClock.getInput();
1054 if (
auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput())) {
1055 auto ty = IntegerType::get(getContext(), 1);
1056 return IntegerAttr::get(ty, clockAttr.getValue() == ClockConst::High);
1065OpFoldResult ClockInverterOp::fold(FoldAdaptor adaptor) {
1066 if (
auto chainedInv = getInput().getDefiningOp<ClockInverterOp>())
1067 return chainedInv.getInput();
1068 if (
auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput())) {
1069 auto clockIn = clockAttr.getValue() == ClockConst::High;
1070 return ClockConstAttr::get(getContext(),
1071 clockIn ? ClockConst::Low : ClockConst::High);
1081 depth = op->getAttrOfType<IntegerAttr>(
"depth").
getInt();
1082 numReadPorts = op->getAttrOfType<IntegerAttr>(
"numReadPorts").getUInt();
1083 numWritePorts = op->getAttrOfType<IntegerAttr>(
"numWritePorts").getUInt();
1085 op->getAttrOfType<IntegerAttr>(
"numReadWritePorts").getUInt();
1086 readLatency = op->getAttrOfType<IntegerAttr>(
"readLatency").getUInt();
1087 writeLatency = op->getAttrOfType<IntegerAttr>(
"writeLatency").getUInt();
1088 dataWidth = op->getAttrOfType<IntegerAttr>(
"width").getUInt();
1089 if (op->hasAttrOfType<IntegerAttr>(
"maskGran"))
1090 maskGran = op->getAttrOfType<IntegerAttr>(
"maskGran").getUInt();
1092 maskGran = dataWidth;
1093 readUnderWrite = op->getAttrOfType<seq::RUWAttr>(
"readUnderWrite").getValue();
1095 op->getAttrOfType<seq::WUWAttr>(
"writeUnderWrite").getValue();
1096 if (
auto clockIDsAttr = op->getAttrOfType<ArrayAttr>(
"writeClockIDs"))
1097 for (
auto clockID : clockIDsAttr)
1098 writeClockIDs.push_back(
1099 cast<IntegerAttr>(clockID).getValue().getZExtValue());
1100 initFilename = op->getAttrOfType<StringAttr>(
"initFilename").getValue();
1101 initIsBinary = op->getAttrOfType<BoolAttr>(
"initIsBinary").getValue();
1102 initIsInline = op->getAttrOfType<BoolAttr>(
"initIsInline").getValue();
1105LogicalResult InitialOp::verify() {
1107 auto *terminator = this->getBody().front().getTerminator();
1108 if (terminator->getOperands().size() != getNumResults())
1109 return emitError() <<
"result type doesn't match with the terminator";
1110 for (
auto [lhs, rhs] :
1111 llvm::zip(terminator->getOperands().getTypes(), getResultTypes())) {
1112 if (cast<seq::ImmutableType>(rhs).getInnerType() != lhs)
1113 return emitError() << cast<seq::ImmutableType>(rhs).getInnerType()
1114 <<
" is expected but got " << lhs;
1117 auto blockArgs = this->getBody().front().getArguments();
1119 if (blockArgs.size() != getNumOperands())
1120 return emitError() <<
"operand type doesn't match with the block arg";
1122 for (
auto [blockArg, operand] :
llvm::zip(blockArgs, getOperands())) {
1123 if (blockArg.getType() !=
1124 cast<ImmutableType>(operand.getType()).getInnerType())
1126 << blockArg.getType() <<
" is expected but got "
1127 << cast<ImmutableType>(operand.getType()).getInnerType();
1131void InitialOp::build(OpBuilder &builder, OperationState &result,
1132 TypeRange resultTypes, std::function<
void()> ctor) {
1133 OpBuilder::InsertionGuard guard(builder);
1135 builder.createBlock(result.addRegion());
1136 SmallVector<Type> types;
1137 for (
auto t : resultTypes)
1138 types.push_back(
seq::ImmutableType::
get(t));
1140 result.addTypes(types);
1146TypedValue<seq::ImmutableType>
1148 mlir::IntegerAttr attr) {
1149 auto initial = builder.create<seq::InitialOp>(loc, attr.getType(), [&]() {
1151 builder.
create<seq::YieldOp>(loc, ArrayRef<Value>{constant});
1153 return cast<TypedValue<seq::ImmutableType>>(initial->getResult(0));
1156mlir::TypedValue<seq::ImmutableType>
1158 assert(op->getNumResults() == 1 &&
1159 op->hasTrait<mlir::OpTrait::ConstantLike>());
1161 builder.create<seq::InitialOp>(op->getLoc(), op->getResultTypes(), [&]() {
1162 auto clonedOp = builder.clone(*op);
1163 builder.create<seq::YieldOp>(op->getLoc(), clonedOp->getResults());
1165 return cast<mlir::TypedValue<seq::ImmutableType>>(initial.getResult(0));
1169 auto resultNum = cast<OpResult>(value).getResultNumber();
1170 auto initialOp = value.getDefiningOp<seq::InitialOp>();
1172 return initialOp.getBodyBlock()->getTerminator()->getOperand(resultNum);
1176 SmallVector<Operation *> initialOps;
1177 for (
auto &op : *block)
1178 if (isa<seq::InitialOp>(op))
1179 initialOps.push_back(&op);
1181 if (!mlir::computeTopologicalSorting(initialOps, {}))
1182 return block->getParentOp()->emitError() <<
"initial ops cannot be "
1183 <<
"topologically sorted";
1186 if (initialOps.size() <= 1)
1187 return initialOps.empty() ? seq::InitialOp()
1188 : cast<seq::InitialOp>(initialOps[0]);
1190 auto initialOp = cast<seq::InitialOp>(initialOps.front());
1191 auto yieldOp = cast<seq::YieldOp>(initialOp.getBodyBlock()->getTerminator());
1193 llvm::MapVector<Value, Value>
1194 resultToYieldOperand;
1196 for (
auto [result, operand] :
1197 llvm::zip(initialOp.getResults(), yieldOp->getOperands()))
1198 resultToYieldOperand.insert({result, operand});
1200 for (
size_t i = 1; i < initialOps.size(); ++i) {
1201 auto currentInitialOp = cast<seq::InitialOp>(initialOps[i]);
1202 auto operands = currentInitialOp->getOperands();
1203 for (
auto [blockArg, operand] :
1205 if (
auto initOp = operand.getDefiningOp<seq::InitialOp>()) {
1206 assert(resultToYieldOperand.count(operand) &&
1207 "it must be visited already");
1208 blockArg.replaceAllUsesWith(resultToYieldOperand.lookup(operand));
1211 initialOp.getBodyBlock()->addArgument(
1212 cast<seq::ImmutableType>(operand.getType()).getInnerType(),
1214 initialOp.getInputsMutable().append(operand);
1218 auto currentYieldOp =
1219 cast<seq::YieldOp>(currentInitialOp.getBodyBlock()->getTerminator());
1221 for (
auto [result, operand] :
llvm::zip(currentInitialOp.getResults(),
1222 currentYieldOp->getOperands()))
1223 resultToYieldOperand.insert({result, operand});
1226 yieldOp.getOperandsMutable().append(currentYieldOp.getOperands());
1227 currentYieldOp->erase();
1231 initialOp.getBodyBlock()->getOperations().splice(
1232 initialOp.end(), currentInitialOp.getBodyBlock()->getOperations());
1236 yieldOp->moveBefore(initialOp.getBodyBlock(),
1237 initialOp.getBodyBlock()->end());
1239 auto builder = OpBuilder::atBlockBegin(block);
1240 SmallVector<Type> types;
1241 for (
auto [result, operand] : resultToYieldOperand)
1242 types.push_back(operand.getType());
1246 auto newInitial = builder.create<seq::InitialOp>(initialOp.getLoc(), types);
1247 newInitial.getInputsMutable().append(initialOp.getInputs());
1249 for (
auto [resultAndOperand, newResult] :
1250 llvm::zip(resultToYieldOperand, newInitial.getResults()))
1251 resultAndOperand.first.replaceAllUsesWith(newResult);
1254 for (
auto oldBlockArg : initialOp.
getBodyBlock()->getArguments()) {
1255 auto blockArg = newInitial.getBodyBlock()->addArgument(
1256 oldBlockArg.getType(), oldBlockArg.getLoc());
1257 oldBlockArg.replaceAllUsesWith(blockArg);
1260 newInitial.getBodyBlock()->getOperations().splice(
1261 newInitial.end(), initialOp.getBodyBlock()->getOperations());
1264 while (!initialOps.empty())
1265 initialOps.pop_back_val()->erase();
1275#define GET_OP_CLASSES
1276#include "circt/Dialect/Seq/Seq.cpp.inc"
assert(baseType &&"element must be base type")
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)
static bool isConstClock(Value value)
static ParseResult parseFIFOFlagThreshold(OpAsmParser &parser, IntegerAttr &threshold, Type &outputFlagType, StringRef directive)
static void printOptionalTypeMatch(OpAsmPrinter &p, Operation *op, Type refType, Value operand, Type type)
static bool isConstAllOnes(Value value)
static ParseResult parseOptionalImmutableTypeMatch(OpAsmParser &parser, Type refType, std::optional< OpAsmParser::UnresolvedOperand > operand, Type &type)
void printFIFOAEThreshold(OpAsmPrinter &p, Operation *op, IntegerAttr threshold, Type outputFlagType)
LogicalResult verifyResets(TOp op)
static bool canElideName(OpAsmPrinter &p, Operation *op)
ParseResult parseFIFOAEThreshold(OpAsmParser &parser, IntegerAttr &threshold, Type &outputFlagType)
static LogicalResult verifyFirMemMask(Op op)
static void printOptionalImmutableTypeMatch(OpAsmPrinter &p, Operation *op, Type refType, Value operand, Type type)
static ParseResult parseOptionalTypeMatch(OpAsmParser &parser, Type refType, std::optional< OpAsmParser::UnresolvedOperand > operand, Type &type)
static void setNameFromResult(OpAsmParser &parser, OperationState &result)
ParseResult parseFIFOAFThreshold(OpAsmParser &parser, IntegerAttr &threshold, Type &outputFlagType)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
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.
FailureOr< seq::InitialOp > mergeInitialOps(Block *block)
bool isValidIndexValues(Value hlmemHandle, ValueRange addresses)
mlir::TypedValue< seq::ImmutableType > createConstantInitialValue(OpBuilder builder, Location loc, mlir::IntegerAttr attr)
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.
static bool isConstantOne(Attribute operand)
Determine whether a constant operand is a one value.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
FirMemory(hw::HWModuleGeneratedOp op)