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)); });
363 if (mlir::matchPattern(op.getClockEnable(), mlir::m_ConstantInt(&en))) {
364 if (
en.isAllOnes()) {
366 op, op.getInput(), op.getClk(), op.getNameAttr(), op.getReset(),
367 op.getResetValue(), op.getInitialValue(), op.getInnerSymAttr());
382 setNameFn(getResult(), *name);
385std::optional<size_t> ShiftRegOp::getTargetResultIndex() {
return 0; }
387LogicalResult ShiftRegOp::verify() {
397void FirRegOp::build(OpBuilder &builder, OperationState &result, Value input,
398 Value clk, StringAttr name, hw::InnerSymAttr innerSym,
401 OpBuilder::InsertionGuard guard(builder);
403 result.addOperands(input);
404 result.addOperands(clk);
406 result.addAttribute(getNameAttrName(result.name), name);
409 result.addAttribute(getInnerSymAttrName(result.name), innerSym);
412 result.addAttribute(getPresetAttrName(result.name), preset);
414 result.addTypes(input.getType());
417void FirRegOp::build(OpBuilder &builder, OperationState &result, Value input,
418 Value clk, StringAttr name, Value reset, Value resetValue,
419 hw::InnerSymAttr innerSym,
bool isAsync) {
421 OpBuilder::InsertionGuard guard(builder);
423 result.addOperands(input);
424 result.addOperands(clk);
425 result.addOperands(reset);
426 result.addOperands(resetValue);
428 result.addAttribute(getNameAttrName(result.name), name);
430 result.addAttribute(getIsAsyncAttrName(result.name), builder.getUnitAttr());
433 result.addAttribute(getInnerSymAttrName(result.name), innerSym);
435 result.addTypes(input.getType());
438ParseResult FirRegOp::parse(OpAsmParser &parser, OperationState &result) {
439 auto &builder = parser.getBuilder();
440 llvm::SMLoc loc = parser.getCurrentLocation();
442 using Op = OpAsmParser::UnresolvedOperand;
445 if (parser.parseOperand(next) || parser.parseKeyword(
"clock") ||
446 parser.parseOperand(clk))
449 if (succeeded(parser.parseOptionalKeyword(
"sym"))) {
450 hw::InnerSymAttr innerSym;
451 if (parser.parseCustomAttributeWithFallback(innerSym,
nullptr,
452 "inner_sym", result.attributes))
457 std::optional<std::pair<Op, Op>> resetAndValue;
458 if (succeeded(parser.parseOptionalKeyword(
"reset"))) {
460 if (succeeded(parser.parseOptionalKeyword(
"async")))
462 else if (succeeded(parser.parseOptionalKeyword(
"sync")))
465 return parser.emitError(loc,
"invalid reset, expected 'sync' or 'async'");
467 result.attributes.append(
"isAsync", builder.getUnitAttr());
469 resetAndValue = {{}, {}};
470 if (parser.parseOperand(resetAndValue->first) || parser.parseComma() ||
471 parser.parseOperand(resetAndValue->second))
475 std::optional<APInt> presetValue;
476 llvm::SMLoc presetValueLoc;
477 if (succeeded(parser.parseOptionalKeyword(
"preset"))) {
478 presetValueLoc = parser.getCurrentLocation();
479 OptionalParseResult presetIntResult =
480 parser.parseOptionalInteger(presetValue.emplace());
481 if (!presetIntResult.has_value() || failed(*presetIntResult))
482 return parser.emitError(loc,
"expected integer value");
486 if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
487 parser.parseType(ty))
489 result.addTypes({ty});
493 if (hw::type_isa<seq::ClockType>(ty)) {
498 return parser.emitError(presetValueLoc,
499 "cannot preset register of unknown width");
503 APInt presetResult = presetValue->sextOrTrunc(width);
504 if (presetResult.zextOrTrunc(presetValue->getBitWidth()) != *presetValue)
505 return parser.emitError(loc,
"preset value too large");
507 auto builder = parser.getBuilder();
508 auto presetTy = builder.getIntegerType(width);
509 auto resultAttr = builder.getIntegerAttr(presetTy, presetResult);
510 result.addAttribute(
"preset", resultAttr);
515 if (parser.resolveOperand(next, ty, result.operands))
518 Type clkTy = ClockType::get(result.getContext());
519 if (parser.resolveOperand(clk, clkTy, result.operands))
523 Type i1 = IntegerType::get(result.getContext(), 1);
524 if (parser.resolveOperand(resetAndValue->first, i1, result.operands) ||
525 parser.resolveOperand(resetAndValue->second, ty, result.operands))
532void FirRegOp::print(::mlir::OpAsmPrinter &p) {
533 SmallVector<StringRef> elidedAttrs = {
534 getInnerSymAttrName(), getIsAsyncAttrName(), getPresetAttrName()};
536 p <<
' ' << getNext() <<
" clock " << getClk();
538 if (
auto sym = getInnerSymAttr()) {
544 p <<
" reset " << (getIsAsync() ?
"async" :
"sync") <<
' ';
545 p << getReset() <<
", " << getResetValue();
548 if (
auto preset = getPresetAttr()) {
549 p <<
" preset " << preset.getValue();
553 elidedAttrs.push_back(
"name");
555 p.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
556 p <<
" : " << getNext().getType();
560LogicalResult FirRegOp::verify() {
561 if (getReset() || getResetValue() || getIsAsync()) {
562 if (!getReset() || !getResetValue())
563 return emitOpError(
"must specify reset and reset value");
566 return emitOpError(
"register with no reset cannot be async");
568 if (
auto preset = getPresetAttr()) {
571 if (preset.getType() != getType() && presetWidth != width)
572 return emitOpError(
"preset type width must match register type");
582 setNameFn(getResult(),
getName());
585std::optional<size_t> FirRegOp::getTargetResultIndex() {
return 0; }
587LogicalResult FirRegOp::canonicalize(FirRegOp op, PatternRewriter &rewriter) {
591 if (
auto reset = op.getReset()) {
593 if (constOp.getValue().isZero()) {
594 rewriter.replaceOpWithNewOp<FirRegOp>(
595 op, op.getNext(), op.getClk(), op.getNameAttr(),
596 op.getInnerSymAttr(), op.getPresetAttr());
603 if (op.getInnerSymAttr())
611 if (op.getNext() == op.getResult())
613 if (
auto clk = op.getClk().getDefiningOp<seq::ToClockOp>())
619 bool replaceWithConstZero =
true;
620 if (
auto preset = op.getPresetAttr())
621 if (!preset.getValue().isZero())
622 replaceWithConstZero =
false;
624 if (
isConstant() && !op.getResetValue() && replaceWithConstZero) {
625 if (isa<seq::ClockType>(op.getType())) {
626 rewriter.replaceOpWithNewOp<seq::ConstClockOp>(
627 op, seq::ClockConstAttr::get(rewriter.getContext(), ClockConst::Low));
631 rewriter.replaceOpWithNewOp<
hw::BitcastOp>(op, op.getType(), constant);
641 if (
auto nextMux = op.getNext().getDefiningOp<
comb::MuxOp>()) {
643 if (op.getPresetAttr())
650 if (nextMux.getTrueValue() == op.getResult() &&
651 matchPattern(nextMux.getFalseValue(), m_Constant(&value))) {
652 replacedValue = nextMux.getFalseValue();
655 else if (nextMux.getFalseValue() == op.getResult() &&
656 matchPattern(nextMux.getTrueValue(), m_Constant(&value))) {
657 replacedValue = nextMux.getTrueValue();
665 if (op.getResetValue()) {
666 Attribute resetConst;
667 if (matchPattern(op.getResetValue(), m_Constant(&resetConst))) {
668 if (resetConst != value)
678 rewriter.replaceOp(op, replacedValue);
688 if (!op.getReset() && !op.getPresetAttr()) {
692 if (isa<IntegerType>(
693 hw::type_cast<hw::ArrayType>(op.getResult().getType())
694 .getElementType())) {
695 SmallVector<Value> nextOperands;
696 bool changed =
false;
697 for (
const auto &[i, value] :
698 llvm::enumerate(arrayCreate.getOperands())) {
699 auto index = arrayCreate.getOperands().size() - i - 1;
703 if (arrayGet.getInput() == op.getResult() &&
704 matchPattern(arrayGet.getIndex(),
705 m_ConstantInt(&elementIndex)) &&
706 elementIndex == index) {
713 nextOperands.push_back(value);
718 arrayCreate.getLoc(), nextOperands);
719 if (arrayCreate->hasOneUse())
722 rewriter.replaceOp(arrayCreate, newNextVal);
725 rewriter.replaceOpWithNewOp<FirRegOp>(op, newNextVal, op.getClk(),
727 op.getInnerSymAttr());
739OpFoldResult FirRegOp::fold(FoldAdaptor adaptor) {
742 if (getInnerSymAttr())
745 auto presetAttr = getPresetAttr();
755 if (
auto reset = getReset())
757 if (constOp.getValue().isOne())
758 return getResetValue();
763 bool isTrivialFeedback = (getNext() == getResult());
764 bool isNeverClocked =
765 adaptor.getClk() !=
nullptr;
766 if (!isTrivialFeedback && !isNeverClocked)
771 if (
auto resetValue = getResetValue()) {
772 if (
auto *op = resetValue.getDefiningOp()) {
773 if (op->hasTrait<OpTrait::ConstantLike>() && !presetAttr)
775 if (
auto constOp = dyn_cast<hw::ConstantOp>(op))
776 if (presetAttr.getValue() == constOp.getValue())
784 auto intType = dyn_cast<IntegerType>(getType());
790 return IntegerAttr::get(intType, 0);
797OpFoldResult ClockGateOp::fold(FoldAdaptor adaptor) {
806 return ClockConstAttr::get(getContext(), ClockConst::Low);
809 if (
auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput()))
810 if (clockAttr.getValue() == ClockConst::Low)
811 return ClockConstAttr::get(getContext(), ClockConst::Low);
815 auto clockGateInputOp = getInput().getDefiningOp<ClockGateOp>();
816 while (clockGateInputOp) {
817 if (clockGateInputOp.getEnable() == getEnable() &&
818 clockGateInputOp.getTestEnable() == getTestEnable())
820 clockGateInputOp = clockGateInputOp.getInput().getDefiningOp<ClockGateOp>();
826LogicalResult ClockGateOp::canonicalize(ClockGateOp op,
827 PatternRewriter &rewriter) {
829 if (
auto testEnable = op.getTestEnable()) {
831 if (constOp.getValue().isZero()) {
832 rewriter.modifyOpInPlace(op,
833 [&] { op.getTestEnableMutable().clear(); });
842std::optional<size_t> ClockGateOp::getTargetResultIndex() {
850OpFoldResult ClockMuxOp::fold(FoldAdaptor adaptor) {
852 return getTrueClock();
854 return getFalseClock();
862LogicalResult FirMemOp::canonicalize(FirMemOp op, PatternRewriter &rewriter) {
864 if (op.getInnerSymAttr())
867 bool readOnly =
true, writeOnly =
true;
870 for (
auto *user : op->getUsers()) {
871 if (isa<FirMemReadOp, FirMemReadWriteOp>(user)) {
874 if (isa<FirMemWriteOp, FirMemReadWriteOp>(user)) {
877 assert((isa<FirMemReadOp, FirMemWriteOp, FirMemReadWriteOp>(user)) &&
878 "invalid seq.firmem user");
881 for (
auto *user :
llvm::make_early_inc_range(op->getUsers()))
882 rewriter.eraseOp(user);
884 rewriter.eraseOp(op);
888 if (readOnly && !op.getInit()) {
890 for (
auto *user :
llvm::make_early_inc_range(op->getUsers())) {
891 auto readOp = cast<FirMemReadOp>(user);
894 if (readOp.getType() != zero.getType())
897 rewriter.replaceOp(readOp, zero);
899 rewriter.eraseOp(op);
906 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
907 if (!nameAttr.getValue().empty())
908 setNameFn(getResult(), nameAttr.getValue());
911std::optional<size_t> FirMemOp::getTargetResultIndex() {
return 0; }
915 if (
auto mask = op.getMask()) {
916 auto memType = op.getMemory().getType();
917 if (!memType.getMaskWidth())
918 return op.emitOpError(
"has mask operand but memory type '")
919 << memType <<
"' has no mask";
920 auto expected = IntegerType::get(op.getContext(), *memType.getMaskWidth());
921 if (mask.getType() != expected)
922 return op.emitOpError(
"has mask operand of type '")
923 << mask.getType() <<
"', but memory type requires '" << expected
930LogicalResult FirMemReadWriteOp::verify() {
return verifyFirMemMask(*
this); }
935 return value.getDefiningOp<seq::ConstClockOp>();
941 return constOp.getValue().isZero();
948 return constOp.getValue().isAllOnes();
952LogicalResult FirMemReadOp::canonicalize(FirMemReadOp op,
953 PatternRewriter &rewriter) {
956 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
962LogicalResult FirMemWriteOp::canonicalize(FirMemWriteOp op,
963 PatternRewriter &rewriter) {
967 auto memOp = op.getMemory().getDefiningOp<FirMemOp>();
968 if (memOp.getInnerSymAttr())
970 rewriter.eraseOp(op);
973 bool anyChanges =
false;
977 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
983 rewriter.modifyOpInPlace(op, [&] { op.getMaskMutable().erase(0); });
987 return success(anyChanges);
990LogicalResult FirMemReadWriteOp::canonicalize(FirMemReadWriteOp op,
991 PatternRewriter &rewriter) {
996 auto opAttrs = op->getAttrs();
997 auto opAttrNames = op.getAttributeNames();
998 auto newOp = rewriter.replaceOpWithNewOp<FirMemReadOp>(
999 op, op.getMemory(), op.getAddress(), op.getClk(), op.getEnable());
1000 for (
auto namedAttr : opAttrs)
1001 if (!
llvm::is_contained(opAttrNames, namedAttr.
getName()))
1002 newOp->setAttr(namedAttr.
getName(), namedAttr.getValue());
1005 bool anyChanges =
false;
1009 rewriter.modifyOpInPlace(op, [&] { op.getEnableMutable().erase(0); });
1015 rewriter.modifyOpInPlace(op, [&] { op.getMaskMutable().erase(0); });
1019 return success(anyChanges);
1026OpFoldResult ConstClockOp::fold(FoldAdaptor adaptor) {
1027 return ClockConstAttr::get(getContext(), getValue());
1034LogicalResult ToClockOp::canonicalize(ToClockOp op, PatternRewriter &rewriter) {
1035 if (
auto fromClock = op.getInput().getDefiningOp<FromClockOp>()) {
1036 rewriter.replaceOp(op, fromClock.getInput());
1042OpFoldResult ToClockOp::fold(FoldAdaptor adaptor) {
1043 if (
auto fromClock = getInput().getDefiningOp<FromClockOp>())
1044 return fromClock.getInput();
1045 if (
auto intAttr = dyn_cast_or_null<IntegerAttr>(adaptor.getInput())) {
1047 intAttr.getValue().isZero() ? ClockConst::Low : ClockConst::High;
1048 return ClockConstAttr::get(getContext(), value);
1053LogicalResult FromClockOp::canonicalize(FromClockOp op,
1054 PatternRewriter &rewriter) {
1055 if (
auto toClock = op.getInput().getDefiningOp<ToClockOp>()) {
1056 rewriter.replaceOp(op, toClock.getInput());
1062OpFoldResult FromClockOp::fold(FoldAdaptor adaptor) {
1063 if (
auto toClock = getInput().getDefiningOp<ToClockOp>())
1064 return toClock.getInput();
1065 if (
auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput())) {
1066 auto ty = IntegerType::get(getContext(), 1);
1067 return IntegerAttr::get(ty, clockAttr.getValue() == ClockConst::High);
1076OpFoldResult ClockInverterOp::fold(FoldAdaptor adaptor) {
1077 if (
auto chainedInv = getInput().getDefiningOp<ClockInverterOp>())
1078 return chainedInv.getInput();
1079 if (
auto clockAttr = dyn_cast_or_null<ClockConstAttr>(adaptor.getInput())) {
1080 auto clockIn = clockAttr.getValue() == ClockConst::High;
1081 return ClockConstAttr::get(getContext(),
1082 clockIn ? ClockConst::Low : ClockConst::High);
1092 depth = op->getAttrOfType<IntegerAttr>(
"depth").
getInt();
1093 numReadPorts = op->getAttrOfType<IntegerAttr>(
"numReadPorts").getUInt();
1094 numWritePorts = op->getAttrOfType<IntegerAttr>(
"numWritePorts").getUInt();
1096 op->getAttrOfType<IntegerAttr>(
"numReadWritePorts").getUInt();
1097 readLatency = op->getAttrOfType<IntegerAttr>(
"readLatency").getUInt();
1098 writeLatency = op->getAttrOfType<IntegerAttr>(
"writeLatency").getUInt();
1099 dataWidth = op->getAttrOfType<IntegerAttr>(
"width").getUInt();
1100 if (op->hasAttrOfType<IntegerAttr>(
"maskGran"))
1101 maskGran = op->getAttrOfType<IntegerAttr>(
"maskGran").getUInt();
1103 maskGran = dataWidth;
1104 readUnderWrite = op->getAttrOfType<seq::RUWAttr>(
"readUnderWrite").getValue();
1106 op->getAttrOfType<seq::WUWAttr>(
"writeUnderWrite").getValue();
1107 if (
auto clockIDsAttr = op->getAttrOfType<ArrayAttr>(
"writeClockIDs"))
1108 for (
auto clockID : clockIDsAttr)
1109 writeClockIDs.push_back(
1110 cast<IntegerAttr>(clockID).getValue().getZExtValue());
1111 initFilename = op->getAttrOfType<StringAttr>(
"initFilename").getValue();
1112 initIsBinary = op->getAttrOfType<BoolAttr>(
"initIsBinary").getValue();
1113 initIsInline = op->getAttrOfType<BoolAttr>(
"initIsInline").getValue();
1116LogicalResult InitialOp::verify() {
1118 auto *terminator = this->getBody().front().getTerminator();
1119 if (terminator->getOperands().size() != getNumResults())
1120 return emitError() <<
"result type doesn't match with the terminator";
1121 for (
auto [lhs, rhs] :
1122 llvm::zip(terminator->getOperands().getTypes(), getResultTypes())) {
1123 if (cast<seq::ImmutableType>(rhs).getInnerType() != lhs)
1124 return emitError() << cast<seq::ImmutableType>(rhs).getInnerType()
1125 <<
" is expected but got " << lhs;
1128 auto blockArgs = this->getBody().front().getArguments();
1130 if (blockArgs.size() != getNumOperands())
1131 return emitError() <<
"operand type doesn't match with the block arg";
1133 for (
auto [blockArg, operand] :
llvm::zip(blockArgs, getOperands())) {
1134 if (blockArg.getType() !=
1135 cast<ImmutableType>(operand.getType()).getInnerType())
1137 << blockArg.getType() <<
" is expected but got "
1138 << cast<ImmutableType>(operand.getType()).getInnerType();
1142void InitialOp::build(OpBuilder &builder, OperationState &result,
1143 TypeRange resultTypes, std::function<
void()> ctor) {
1144 OpBuilder::InsertionGuard guard(builder);
1146 builder.createBlock(result.addRegion());
1147 SmallVector<Type> types;
1148 for (
auto t : resultTypes)
1149 types.push_back(
seq::ImmutableType::
get(t));
1151 result.addTypes(types);
1157TypedValue<seq::ImmutableType>
1159 mlir::IntegerAttr attr) {
1160 auto initial = builder.create<seq::InitialOp>(loc, attr.getType(), [&]() {
1162 builder.
create<seq::YieldOp>(loc, ArrayRef<Value>{constant});
1164 return cast<TypedValue<seq::ImmutableType>>(initial->getResult(0));
1167mlir::TypedValue<seq::ImmutableType>
1169 assert(op->getNumResults() == 1 &&
1170 op->hasTrait<mlir::OpTrait::ConstantLike>());
1172 builder.create<seq::InitialOp>(op->getLoc(), op->getResultTypes(), [&]() {
1173 auto clonedOp = builder.clone(*op);
1174 builder.create<seq::YieldOp>(op->getLoc(), clonedOp->getResults());
1176 return cast<mlir::TypedValue<seq::ImmutableType>>(initial.getResult(0));
1180 auto resultNum = cast<OpResult>(value).getResultNumber();
1181 auto initialOp = value.getDefiningOp<seq::InitialOp>();
1183 return initialOp.getBodyBlock()->getTerminator()->getOperand(resultNum);
1187 SmallVector<Operation *> initialOps;
1188 for (
auto &op : *block)
1189 if (isa<seq::InitialOp>(op))
1190 initialOps.push_back(&op);
1192 if (!mlir::computeTopologicalSorting(initialOps, {}))
1193 return block->getParentOp()->emitError() <<
"initial ops cannot be "
1194 <<
"topologically sorted";
1197 if (initialOps.size() <= 1)
1198 return initialOps.empty() ? seq::InitialOp()
1199 : cast<seq::InitialOp>(initialOps[0]);
1201 auto initialOp = cast<seq::InitialOp>(initialOps.front());
1202 auto yieldOp = cast<seq::YieldOp>(initialOp.getBodyBlock()->getTerminator());
1204 llvm::MapVector<Value, Value>
1205 resultToYieldOperand;
1207 for (
auto [result, operand] :
1208 llvm::zip(initialOp.getResults(), yieldOp->getOperands()))
1209 resultToYieldOperand.insert({result, operand});
1211 for (
size_t i = 1; i < initialOps.size(); ++i) {
1212 auto currentInitialOp = cast<seq::InitialOp>(initialOps[i]);
1213 auto operands = currentInitialOp->getOperands();
1214 for (
auto [blockArg, operand] :
1216 if (
auto initOp = operand.getDefiningOp<seq::InitialOp>()) {
1217 assert(resultToYieldOperand.count(operand) &&
1218 "it must be visited already");
1219 blockArg.replaceAllUsesWith(resultToYieldOperand.lookup(operand));
1222 initialOp.getBodyBlock()->addArgument(
1223 cast<seq::ImmutableType>(operand.getType()).getInnerType(),
1225 initialOp.getInputsMutable().append(operand);
1229 auto currentYieldOp =
1230 cast<seq::YieldOp>(currentInitialOp.getBodyBlock()->getTerminator());
1232 for (
auto [result, operand] :
llvm::zip(currentInitialOp.getResults(),
1233 currentYieldOp->getOperands()))
1234 resultToYieldOperand.insert({result, operand});
1237 yieldOp.getOperandsMutable().append(currentYieldOp.getOperands());
1238 currentYieldOp->erase();
1242 initialOp.getBodyBlock()->getOperations().splice(
1243 initialOp.end(), currentInitialOp.getBodyBlock()->getOperations());
1247 yieldOp->moveBefore(initialOp.getBodyBlock(),
1248 initialOp.getBodyBlock()->end());
1250 auto builder = OpBuilder::atBlockBegin(block);
1251 SmallVector<Type> types;
1252 for (
auto [result, operand] : resultToYieldOperand)
1253 types.push_back(operand.getType());
1257 auto newInitial = builder.create<seq::InitialOp>(initialOp.getLoc(), types);
1258 newInitial.getInputsMutable().append(initialOp.getInputs());
1260 for (
auto [resultAndOperand, newResult] :
1261 llvm::zip(resultToYieldOperand, newInitial.getResults()))
1262 resultAndOperand.first.replaceAllUsesWith(newResult);
1265 for (
auto oldBlockArg : initialOp.
getBodyBlock()->getArguments()) {
1266 auto blockArg = newInitial.getBodyBlock()->addArgument(
1267 oldBlockArg.getType(), oldBlockArg.getLoc());
1268 oldBlockArg.replaceAllUsesWith(blockArg);
1271 newInitial.getBodyBlock()->getOperations().splice(
1272 newInitial.end(), initialOp.getBodyBlock()->getOperations());
1275 while (!initialOps.empty())
1276 initialOps.pop_back_val()->erase();
1286#define GET_OP_CLASSES
1287#include "circt/Dialect/Seq/Seq.cpp.inc"
assert(baseType &&"element must be base type")
static bool isConstZero(Value value)
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)
static InstancePath empty
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)