21 #include "mlir/IR/Builders.h"
22 #include "mlir/IR/BuiltinTypes.h"
23 #include "mlir/IR/Matchers.h"
24 #include "mlir/IR/PatternMatch.h"
25 #include "llvm/ADT/SmallString.h"
26 #include "llvm/ADT/StringExtras.h"
27 #include "llvm/ADT/TypeSwitch.h"
31 using namespace circt;
33 using mlir::TypedAttr;
39 if (
auto *op = v.getDefiningOp()) {
40 if (
auto attr = op->getAttrOfType<UnitAttr>(
"twoState"))
49 return isa<VerbatimExprOp, VerbatimExprSEOp, GetModportOp,
50 ReadInterfaceSignalOp, ConstantXOp, ConstantZOp, ConstantStrOp,
51 MacroRefExprOp, MacroRefExprSEOp>(op);
57 op->emitError() << op->getName() <<
" should be in a procedural region";
64 op->emitError() << op->getName() <<
" should be in a non-procedural region";
73 Region ®ion = symbolTableOp->getRegion(0);
79 SymbolTable::getSymbolAttrName());
80 for (Block &block : region)
81 for (Operation &nestedOp : block) {
82 auto nameAttr = nestedOp.getAttrOfType<StringAttr>(symbolNameId);
83 if (nameAttr && nameAttr.getValue() == symbol)
85 if (!nestedOp.hasTrait<OpTrait::SymbolTable>() &&
86 nestedOp.getNumRegions()) {
97 SymbolTableCollection &symbolTable) {
98 auto *refOp = symbolTable.lookupNearestSymbolFrom(op, attr);
100 return op->emitError(
"references an undefined symbol: ") << attr;
101 if (!isa<MacroDeclOp>(refOp))
102 return op->emitError(
"must reference a macro declaration");
113 function_ref<
void(Value, StringRef)> setNameFn) {
117 auto isOkCharacter = [](
char c) {
return llvm::isAlnum(c) || c ==
'_'; };
118 auto name = op->getAttrOfType<StringAttr>(
"format_string").getValue();
120 if (name.starts_with(
"`"))
121 name = name.drop_front();
122 name = name.take_while(isOkCharacter);
124 setNameFn(op->getResult(0), name);
128 function_ref<
void(Value, StringRef)> setNameFn) {
133 function_ref<
void(Value, StringRef)> setNameFn) {
142 function_ref<
void(Value, StringRef)> setNameFn) {
143 setNameFn(getResult(), getMacroName());
147 function_ref<
void(Value, StringRef)> setNameFn) {
148 setNameFn(getResult(), getMacroName());
153 FlatSymbolRefAttr macroName) {
155 if (
auto *result = cache->getDefinition(macroName.getAttr()))
156 return cast<MacroDeclOp>(result);
158 auto topLevelModuleOp = op->getParentOfType<ModuleOp>();
159 return topLevelModuleOp.lookupSymbol<MacroDeclOp>(macroName.getValue());
179 MacroRefExprOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
185 MacroRefExprSEOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
190 LogicalResult MacroDefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
199 function_ref<
void(Value, StringRef)> setNameFn) {
200 SmallVector<char, 32> specialNameBuffer;
201 llvm::raw_svector_ostream specialName(specialNameBuffer);
203 setNameFn(getResult(), specialName.str());
206 LogicalResult ConstantXOp::verify() {
209 return emitError(
"unsupported type");
214 function_ref<
void(Value, StringRef)> setNameFn) {
215 SmallVector<char, 32> specialNameBuffer;
216 llvm::raw_svector_ostream specialName(specialNameBuffer);
218 setNameFn(getResult(), specialName.str());
221 LogicalResult ConstantZOp::verify() {
224 return emitError(
"unsupported type");
234 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
235 if (!nameAttr.getValue().empty())
236 setNameFn(getResult(), nameAttr.getValue());
239 LogicalResult LocalParamOp::verify() {
251 std::optional<OpAsmParser::UnresolvedOperand> &initValue,
252 mlir::Type &initType) {
253 if (!initValue.has_value())
258 return p.emitError(p.getCurrentLocation(),
"expected inout type for reg");
260 initType = ioType.getElementType();
265 mlir::Type regType, mlir::Value initValue,
266 mlir::Type initType) {}
268 void RegOp::build(OpBuilder &
builder, OperationState &odsState,
269 Type
elementType, StringAttr name, hw::InnerSymAttr innerSym,
270 mlir::Value initValue) {
272 name =
builder.getStringAttr(
"");
273 odsState.addAttribute(
"name", name);
279 odsState.addOperands(initValue);
286 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
287 if (!nameAttr.getValue().empty())
288 setNameFn(getResult(), nameAttr.getValue());
291 std::optional<size_t> RegOp::getTargetResultIndex() {
return 0; }
294 LogicalResult RegOp::canonicalize(
RegOp op, PatternRewriter &rewriter) {
300 if (op.getInnerSymAttr())
304 for (
auto &use : op.getResult().getUses())
305 if (!isa<AssignOp>(use.getOwner()))
309 for (
auto &use : op.getResult().getUses())
310 rewriter.eraseOp(use.getOwner());
313 rewriter.eraseOp(op);
321 void LogicOp::build(OpBuilder &
builder, OperationState &odsState,
323 hw::InnerSymAttr innerSym) {
325 name =
builder.getStringAttr(
"");
326 odsState.addAttribute(
"name", name);
337 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
338 if (!nameAttr.getValue().empty())
339 setNameFn(getResult(), nameAttr.getValue());
342 std::optional<size_t> LogicOp::getTargetResultIndex() {
return 0; }
352 void IfDefOp::build(OpBuilder &
builder, OperationState &result, StringRef cond,
353 std::function<
void()> thenCtor,
354 std::function<
void()> elseCtor) {
355 build(
builder, result,
builder.getStringAttr(cond), std::move(thenCtor),
356 std::move(elseCtor));
359 void IfDefOp::build(OpBuilder &
builder, OperationState &result, StringAttr cond,
360 std::function<
void()> thenCtor,
361 std::function<
void()> elseCtor) {
363 std::move(thenCtor), std::move(elseCtor));
366 void IfDefOp::build(OpBuilder &
builder, OperationState &result,
367 FlatSymbolRefAttr cond, std::function<
void()> thenCtor,
368 std::function<
void()> elseCtor) {
370 std::move(thenCtor), std::move(elseCtor));
373 void IfDefOp::build(OpBuilder &
builder, OperationState &result,
374 MacroIdentAttr cond, std::function<
void()> thenCtor,
375 std::function<
void()> elseCtor) {
376 OpBuilder::InsertionGuard guard(
builder);
378 result.addAttribute(
"cond", cond);
379 builder.createBlock(result.addRegion());
385 Region *elseRegion = result.addRegion();
387 builder.createBlock(elseRegion);
392 LogicalResult IfDefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
399 if (!op.getThenBlock()->empty())
402 if (op.hasElse() && !op.getElseBlock()->empty())
405 rewriter.eraseOp(op);
409 LogicalResult IfDefOp::canonicalize(
IfDefOp op, PatternRewriter &rewriter) {
417 void IfDefProceduralOp::build(OpBuilder &
builder, OperationState &result,
418 StringRef cond, std::function<
void()> thenCtor,
419 std::function<
void()> elseCtor) {
420 build(
builder, result,
builder.getStringAttr(cond), std::move(thenCtor),
421 std::move(elseCtor));
424 void IfDefProceduralOp::build(OpBuilder &
builder, OperationState &result,
425 StringAttr cond, std::function<
void()> thenCtor,
426 std::function<
void()> elseCtor) {
428 std::move(thenCtor), std::move(elseCtor));
431 void IfDefProceduralOp::build(OpBuilder &
builder, OperationState &result,
432 FlatSymbolRefAttr cond,
433 std::function<
void()> thenCtor,
434 std::function<
void()> elseCtor) {
436 std::move(thenCtor), std::move(elseCtor));
439 void IfDefProceduralOp::build(OpBuilder &
builder, OperationState &result,
441 std::function<
void()> thenCtor,
442 std::function<
void()> elseCtor) {
443 OpBuilder::InsertionGuard guard(
builder);
445 result.addAttribute(
"cond", cond);
446 builder.createBlock(result.addRegion());
452 Region *elseRegion = result.addRegion();
454 builder.createBlock(elseRegion);
459 LogicalResult IfDefProceduralOp::canonicalize(IfDefProceduralOp op,
460 PatternRewriter &rewriter) {
465 IfDefProceduralOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
473 void IfOp::build(OpBuilder &
builder, OperationState &result, Value cond,
474 std::function<
void()> thenCtor,
475 std::function<
void()> elseCtor) {
476 OpBuilder::InsertionGuard guard(
builder);
478 result.addOperands(cond);
479 builder.createBlock(result.addRegion());
485 Region *elseRegion = result.addRegion();
487 builder.createBlock(elseRegion);
495 assert(llvm::hasSingleElement(region) &&
"expected single-region block");
496 Block *fromBlock = ®ion.front();
498 op->getBlock()->getOperations().splice(Block::iterator(op),
499 fromBlock->getOperations());
502 LogicalResult IfOp::canonicalize(IfOp op, PatternRewriter &rewriter) {
507 if (
auto constant = op.getCond().getDefiningOp<
hw::ConstantOp>()) {
509 if (constant.getValue().isAllOnes())
511 else if (!op.getElseRegion().empty())
514 rewriter.eraseOp(op);
520 if (!op.getThenBlock()->empty() && op.hasElse() &&
521 op.getElseBlock()->empty()) {
522 rewriter.eraseBlock(op.getElseBlock());
529 if (!op.getThenBlock()->empty())
533 if (!op.hasElse() || op.getElseBlock()->empty()) {
534 rewriter.eraseOp(op);
545 auto *thenBlock = op.getThenBlock(), *elseBlock = op.getElseBlock();
548 thenBlock->getOperations().splice(thenBlock->end(),
549 elseBlock->getOperations());
550 rewriter.eraseBlock(elseBlock);
560 AlwaysOp::Condition AlwaysOp::getCondition(
size_t idx) {
561 return Condition{EventControl(getEvents()[idx].cast<IntegerAttr>().
getInt()),
565 void AlwaysOp::build(OpBuilder &
builder, OperationState &result,
566 ArrayRef<sv::EventControl> events, ArrayRef<Value> clocks,
567 std::function<
void()> bodyCtor) {
568 assert(events.size() == clocks.size() &&
569 "mismatch between event and clock list");
570 OpBuilder::InsertionGuard guard(
builder);
572 SmallVector<Attribute> eventAttrs;
573 for (
auto event : events)
574 eventAttrs.push_back(
575 builder.getI32IntegerAttr(
static_cast<int32_t
>(event)));
576 result.addAttribute(
"events",
builder.getArrayAttr(eventAttrs));
577 result.addOperands(clocks);
580 builder.createBlock(result.addRegion());
588 LogicalResult AlwaysOp::verify() {
589 if (getEvents().size() != getNumOperands())
590 return emitError(
"different number of operands and events");
595 OpAsmParser &p, Attribute &eventsAttr,
596 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &clocksOperands) {
599 SmallVector<Attribute> events;
601 auto loc = p.getCurrentLocation();
603 if (!p.parseOptionalKeyword(&keyword)) {
605 auto kind = sv::symbolizeEventControl(keyword);
606 if (!kind.has_value())
607 return p.emitError(loc,
"expected 'posedge', 'negedge', or 'edge'");
608 auto eventEnum =
static_cast<int32_t
>(*kind);
609 events.push_back(p.getBuilder().getI32IntegerAttr(eventEnum));
611 clocksOperands.push_back({});
612 if (p.parseOperand(clocksOperands.back()))
615 if (failed(p.parseOptionalComma()))
617 if (p.parseKeyword(&keyword))
621 eventsAttr = p.getBuilder().getArrayAttr(events);
626 OperandRange operands) {
627 for (
size_t i = 0, e = op.getNumConditions(); i != e; ++i) {
630 auto cond = op.getCondition(i);
631 p << stringifyEventControl(cond.event);
633 p.printOperand(cond.value);
641 void AlwaysFFOp::build(OpBuilder &
builder, OperationState &result,
642 EventControl clockEdge, Value clock,
643 std::function<
void()> bodyCtor) {
644 OpBuilder::InsertionGuard guard(
builder);
647 "clockEdge",
builder.getI32IntegerAttr(
static_cast<int32_t
>(clockEdge)));
648 result.addOperands(clock);
651 builder.getI32IntegerAttr(
static_cast<int32_t
>(ResetType::NoReset)));
654 builder.createBlock(result.addRegion());
663 void AlwaysFFOp::build(OpBuilder &
builder, OperationState &result,
664 EventControl clockEdge, Value clock,
665 ResetType resetStyle, EventControl resetEdge,
666 Value reset, std::function<
void()> bodyCtor,
667 std::function<
void()> resetCtor) {
668 OpBuilder::InsertionGuard guard(
builder);
671 "clockEdge",
builder.getI32IntegerAttr(
static_cast<int32_t
>(clockEdge)));
672 result.addOperands(clock);
673 result.addAttribute(
"resetStyle",
builder.getI32IntegerAttr(
674 static_cast<int32_t
>(resetStyle)));
676 "resetEdge",
builder.getI32IntegerAttr(
static_cast<int32_t
>(resetEdge)));
677 result.addOperands(reset);
680 builder.createBlock(result.addRegion());
686 builder.createBlock(result.addRegion());
696 void AlwaysCombOp::build(OpBuilder &
builder, OperationState &result,
697 std::function<
void()> bodyCtor) {
698 OpBuilder::InsertionGuard guard(
builder);
700 builder.createBlock(result.addRegion());
710 void InitialOp::build(OpBuilder &
builder, OperationState &result,
711 std::function<
void()> bodyCtor) {
712 OpBuilder::InsertionGuard guard(
builder);
714 builder.createBlock(result.addRegion());
737 llvm_unreachable(
"invalid casez PatternBit");
742 return CasePatternBit(
unsigned(intAttr.getValue()[bitNumber * 2]) +
743 2 *
unsigned(intAttr.getValue()[bitNumber * 2 + 1]));
747 for (
size_t i = 0, e =
getWidth(); i != e; ++i)
754 for (
size_t i = 0, e =
getWidth(); i != e; ++i)
760 SmallVector<CasePatternBit> result;
761 result.reserve(value.getBitWidth());
762 for (
size_t i = 0, e = value.getBitWidth(); i != e; ++i)
778 MLIRContext *context)
780 APInt pattern(bits.size() * 2, 0);
781 for (
auto elt : llvm::reverse(bits)) {
783 pattern |= unsigned(elt);
789 auto CaseOp::getCases() -> SmallVector<CaseInfo, 4> {
790 SmallVector<CaseInfo, 4> result;
791 assert(getCasePatterns().size() == getNumRegions() &&
792 "case pattern / region count mismatch");
793 size_t nextRegion = 0;
794 for (
auto elt : getCasePatterns()) {
795 llvm::TypeSwitch<Attribute>(elt)
796 .Case<hw::EnumFieldAttr>([&](
auto enumAttr) {
797 result.push_back({std::make_unique<CaseEnumPattern>(enumAttr),
798 &getRegion(nextRegion++).front()});
800 .Case<IntegerAttr>([&](
auto intAttr) {
801 result.push_back({std::make_unique<CaseBitPattern>(intAttr),
802 &getRegion(nextRegion++).front()});
804 .Case<CaseDefaultPattern::AttrType>([&](
auto) {
805 result.push_back({std::make_unique<CaseDefaultPattern>(getContext()),
806 &getRegion(nextRegion++).front()});
809 assert(
false &&
"invalid case pattern attribute type");
817 return enumAttr.cast<hw::EnumFieldAttr>().getField();
827 ParseResult CaseOp::parse(OpAsmParser &parser, OperationState &result) {
828 auto &
builder = parser.getBuilder();
830 OpAsmParser::UnresolvedOperand condOperand;
833 auto loc = parser.getCurrentLocation();
836 if (!parser.parseOptionalKeyword(&keyword, {
"case",
"casex",
"casez"})) {
837 auto kind = symbolizeCaseStmtType(keyword);
838 auto caseEnum =
static_cast<int32_t
>(kind.value());
839 result.addAttribute(
"caseStyle",
builder.getI32IntegerAttr(caseEnum));
843 if (!parser.parseOptionalKeyword(
844 &keyword, {
"plain",
"priority",
"unique",
"unique0"})) {
845 auto kind = symbolizeValidationQualifierTypeEnum(keyword);
846 result.addAttribute(
"validationQualifier",
848 builder.getContext(), kind.value()));
851 if (parser.parseOperand(condOperand) || parser.parseColonType(condType) ||
852 parser.parseOptionalAttrDict(result.attributes) ||
853 parser.resolveOperand(condOperand, condType, result.operands))
858 hw::EnumType enumType = canonicalCondType.dyn_cast<hw::EnumType>();
859 unsigned condWidth = 0;
861 if (!result.operands[0].getType().isSignlessInteger())
862 return parser.emitError(loc,
"condition must have signless integer type");
863 condWidth = condType.getIntOrFloatBitWidth();
867 SmallVector<Attribute> casePatterns;
868 SmallVector<CasePatternBit, 16> caseBits;
870 if (succeeded(parser.parseOptionalKeyword(
"default"))) {
871 casePatterns.push_back(CaseDefaultPattern(parser.getContext()).attr());
872 }
else if (failed(parser.parseOptionalKeyword(
"case"))) {
875 }
else if (enumType) {
879 if (parser.parseKeyword(&caseVal))
882 if (!enumType.contains(caseVal))
883 return parser.emitError(loc)
884 <<
"case value '" + caseVal +
"' is not a member of enum type "
886 casePatterns.push_back(
888 builder.getStringAttr(caseVal), condType));
893 loc = parser.getCurrentLocation();
894 if (parser.parseKeyword(&caseVal))
897 if (caseVal.front() !=
'b')
898 return parser.emitError(loc,
"expected case value starting with 'b'");
899 caseVal = caseVal.drop_front();
902 for (; !caseVal.empty(); caseVal = caseVal.drop_front()) {
904 switch (caseVal.front()) {
918 return parser.emitError(loc,
"unexpected case bit '")
919 << caseVal.front() <<
"'";
921 caseBits.push_back(bit);
924 if (caseVal.size() > condWidth)
925 return parser.emitError(loc,
"too many bits specified in pattern");
926 std::reverse(caseBits.begin(), caseBits.end());
929 if (caseBits.size() < condWidth)
932 auto resultPattern = CaseBitPattern(caseBits,
builder.getContext());
933 casePatterns.push_back(resultPattern.attr());
938 auto caseRegion = std::make_unique<Region>();
939 if (parser.parseColon() || parser.parseRegion(*caseRegion))
941 result.addRegion(std::move(caseRegion));
944 result.addAttribute(
"casePatterns",
builder.getArrayAttr(casePatterns));
948 void CaseOp::print(OpAsmPrinter &p) {
950 if (getCaseStyle() == CaseStmtType::CaseXStmt)
952 else if (getCaseStyle() == CaseStmtType::CaseZStmt)
955 if (getValidationQualifier() !=
956 ValidationQualifierTypeEnum::ValidationQualifierPlain)
957 p << stringifyValidationQualifierTypeEnum(getValidationQualifier()) <<
' ';
959 p << getCond() <<
" : " << getCond().getType();
960 p.printOptionalAttrDict(
962 {
"casePatterns",
"caseStyle",
"validationQualifier"});
964 for (
auto &caseInfo : getCases()) {
966 auto &pattern = caseInfo.pattern;
968 llvm::TypeSwitch<CasePattern *>(pattern.get())
969 .Case<CaseBitPattern>([&](
auto bitPattern) {
971 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
972 p <<
getLetter(bitPattern->getBit(e - bit - 1));
974 .Case<CaseEnumPattern>([&](
auto enumPattern) {
975 p <<
"case " << enumPattern->getFieldValue();
977 .Case<CaseDefaultPattern>([&](
auto) { p <<
"default"; })
978 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
981 p.printRegion(*caseInfo.block->getParent(),
false,
986 LogicalResult CaseOp::verify() {
989 return emitError(
"condition must have either integer or enum type");
992 if (getCasePatterns().size() != getNumRegions())
993 return emitOpError(
"case pattern / region count mismatch");
1000 OpBuilder &
builder, OperationState &result, CaseStmtType caseStyle,
1001 ValidationQualifierTypeEnum validationQualifier, Value cond,
1003 std::function<std::unique_ptr<CasePattern>(
size_t)> caseCtor) {
1004 result.addOperands(cond);
1005 result.addAttribute(
"caseStyle",
1007 result.addAttribute(
"validationQualifier",
1009 builder.getContext(), validationQualifier));
1010 SmallVector<Attribute> casePatterns;
1012 OpBuilder::InsertionGuard guard(
builder);
1015 for (
size_t i = 0, e = numCases; i != e; ++i) {
1016 builder.createBlock(result.addRegion());
1017 casePatterns.push_back(caseCtor(i)->attr());
1020 result.addAttribute(
"casePatterns",
builder.getArrayAttr(casePatterns));
1024 LogicalResult CaseOp::canonicalize(CaseOp op, PatternRewriter &rewriter) {
1025 if (op.getCaseStyle() == CaseStmtType::CaseStmt)
1027 if (op.getCond().getType().isa<hw::EnumType>())
1030 auto caseInfo = op.getCases();
1031 bool noXZ = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1032 return !ci.pattern.get()->hasX() && !ci.pattern.get()->hasZ();
1034 bool noX = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1035 if (isa<CaseDefaultPattern>(ci.pattern))
1037 return !ci.pattern.get()->hasX();
1039 bool noZ = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1040 if (isa<CaseDefaultPattern>(ci.pattern))
1042 return !ci.pattern.get()->hasZ();
1045 if (op.getCaseStyle() == CaseStmtType::CaseXStmt) {
1047 rewriter.modifyOpInPlace(op, [&]() {
1048 op.setCaseStyleAttr(
1054 rewriter.modifyOpInPlace(op, [&]() {
1055 op.setCaseStyleAttr(
1062 if (op.getCaseStyle() == CaseStmtType::CaseZStmt && noZ) {
1063 rewriter.modifyOpInPlace(op, [&]() {
1064 op.setCaseStyleAttr(
1077 void OrderedOutputOp::build(OpBuilder &
builder, OperationState &result,
1078 std::function<
void()> body) {
1079 OpBuilder::InsertionGuard guard(
builder);
1081 builder.createBlock(result.addRegion());
1092 void ForOp::build(OpBuilder &
builder, OperationState &result,
1093 int64_t lowerBound, int64_t upperBound, int64_t step,
1094 IntegerType type, StringRef name,
1095 llvm::function_ref<
void(BlockArgument)> body) {
1099 build(
builder, result, lb, ub, st, name, body);
1101 void ForOp::build(OpBuilder &
builder, OperationState &result, Value lowerBound,
1102 Value upperBound, Value step, StringRef name,
1103 llvm::function_ref<
void(BlockArgument)> body) {
1104 OpBuilder::InsertionGuard guard(
builder);
1105 build(
builder, result, lowerBound, upperBound, step, name);
1106 auto *region = result.regions.front().get();
1108 BlockArgument blockArgument =
1109 region->addArgument(lowerBound.getType(), result.location);
1112 body(blockArgument);
1115 void ForOp::getAsmBlockArgumentNames(mlir::Region ®ion,
1117 auto *block = ®ion.front();
1118 setNameFn(block->getArgument(0), getInductionVarNameAttr());
1121 ParseResult ForOp::parse(OpAsmParser &parser, OperationState &result) {
1122 auto &
builder = parser.getBuilder();
1125 OpAsmParser::Argument inductionVariable;
1126 OpAsmParser::UnresolvedOperand lb, ub, step;
1128 SmallVector<OpAsmParser::Argument, 4> regionArgs;
1131 if (parser.parseOperand(inductionVariable.ssaName) || parser.parseEqual() ||
1133 parser.parseOperand(lb) || parser.parseKeyword(
"to") ||
1134 parser.parseOperand(ub) || parser.parseKeyword(
"step") ||
1135 parser.parseOperand(step) || parser.parseColon() ||
1136 parser.parseType(type))
1139 regionArgs.push_back(inductionVariable);
1142 regionArgs.front().type = type;
1143 if (parser.resolveOperand(lb, type, result.operands) ||
1144 parser.resolveOperand(ub, type, result.operands) ||
1145 parser.resolveOperand(step, type, result.operands))
1149 Region *body = result.addRegion();
1150 if (parser.parseRegion(*body, regionArgs))
1154 if (parser.parseOptionalAttrDict(result.attributes))
1157 if (!inductionVariable.ssaName.name.empty()) {
1158 if (!
isdigit(inductionVariable.ssaName.name[1]))
1160 result.attributes.append(
1161 {
builder.getStringAttr(
"inductionVarName"),
1162 builder.getStringAttr(inductionVariable.ssaName.name.drop_front())});
1168 void ForOp::print(OpAsmPrinter &p) {
1169 p <<
" " << getInductionVar() <<
" = " << getLowerBound() <<
" to "
1170 << getUpperBound() <<
" step " << getStep();
1171 p <<
" : " << getInductionVar().getType() <<
' ';
1172 p.printRegion(getRegion(),
1175 p.printOptionalAttrDict((*this)->getAttrs(), {
"inductionVarName"});
1178 LogicalResult ForOp::canonicalize(ForOp op, PatternRewriter &rewriter) {
1180 if (matchPattern(op.getLowerBound(), mlir::m_ConstantInt(&lb)) &&
1181 matchPattern(op.getUpperBound(), mlir::m_ConstantInt(&ub)) &&
1182 matchPattern(op.getStep(), mlir::m_ConstantInt(&step)) &&
1185 op.getInductionVar().replaceAllUsesWith(op.getLowerBound());
1187 rewriter.eraseOp(op);
1197 LogicalResult BPAssignOp::verify() {
1198 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1200 "Verilog disallows procedural assignment to a net type (did you intend "
1201 "to use a variable type, e.g., sv.reg?)");
1205 LogicalResult PAssignOp::verify() {
1206 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1208 "Verilog disallows procedural assignment to a net type (did you intend "
1209 "to use a variable type, e.g., sv.reg?)");
1222 static std::optional<ArraySlice> getArraySlice(Value v) {
1223 auto *op = v.getDefiningOp();
1225 return std::nullopt;
1226 return TypeSwitch<Operation *, std::optional<ArraySlice>>(op)
1227 .Case<hw::ArrayGetOp, ArrayIndexInOutOp>(
1228 [](
auto arrayIndex) -> std::optional<ArraySlice> {
1230 arrayIndex.getIndex()
1231 .template getDefiningOp<hw::ConstantOp>();
1233 return std::nullopt;
1234 return ArraySlice{arrayIndex.getInput(),
1239 -> std::optional<ArraySlice> {
1240 auto constant = slice.getLowIndex().getDefiningOp<
hw::ConstantOp>();
1242 return std::nullopt;
1244 slice.getInput(), constant,
1246 hw::type_cast<hw::ArrayType>(slice.getType()).getNumElements()};
1248 .Case<sv::IndexedPartSelectInOutOp>(
1249 [](sv::IndexedPartSelectInOutOp index)
1250 -> std::optional<ArraySlice> {
1252 if (!constant || index.getDecrement())
1253 return std::nullopt;
1254 return ArraySlice{index.getInput(),
1258 .Default([](
auto) {
return std::nullopt; });
1262 static std::optional<std::pair<ArraySlice, ArraySlice>>
1263 getAssignedRange(Operation *op) {
1264 assert((isa<PAssignOp, BPAssignOp>(op) &&
"assignments are expected"));
1265 auto srcRange = ArraySlice::getArraySlice(op->getOperand(1));
1267 return std::nullopt;
1268 auto destRange = ArraySlice::getArraySlice(op->getOperand(0));
1270 return std::nullopt;
1272 return std::make_pair(*destRange, *srcRange);
1283 template <
typename AssignTy>
1285 PatternRewriter &rewriter) {
1287 auto assignedRangeOpt = ArraySlice::getAssignedRange(op);
1288 if (!assignedRangeOpt)
1291 auto [dest, src] = *assignedRangeOpt;
1292 AssignTy nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1293 bool changed =
false;
1294 SmallVector<Location> loc{op.getLoc()};
1296 while (nextAssign) {
1297 auto nextAssignedRange = ArraySlice::getAssignedRange(nextAssign);
1298 if (!nextAssignedRange)
1300 auto [nextDest, nextSrc] = *nextAssignedRange;
1302 if (dest.array != nextDest.array || src.array != nextSrc.array ||
1303 !
hw::isOffset(dest.start, nextDest.start, dest.size) ||
1307 dest.size += nextDest.size;
1308 src.size += nextSrc.size;
1310 loc.push_back(nextAssign.getLoc());
1311 rewriter.eraseOp(nextAssign);
1312 nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1320 hw::type_cast<hw::ArrayType>(src.array.getType()).getElementType(),
1322 auto newDest = rewriter.create<sv::IndexedPartSelectInOutOp>(
1323 op.getLoc(), dest.array, dest.start, dest.size);
1325 src.array, src.start);
1326 auto newLoc = rewriter.getFusedLoc(loc);
1327 auto newOp = rewriter.replaceOpWithNewOp<AssignTy>(op, newDest, newSrc);
1328 newOp->setLoc(newLoc);
1332 LogicalResult PAssignOp::canonicalize(PAssignOp op, PatternRewriter &rewriter) {
1336 LogicalResult BPAssignOp::canonicalize(BPAssignOp op,
1337 PatternRewriter &rewriter) {
1345 void InterfaceOp::build(OpBuilder &
builder, OperationState &result,
1346 StringRef sym_name, std::function<
void()> body) {
1347 OpBuilder::InsertionGuard guard(
builder);
1349 result.addAttribute(::SymbolTable::getSymbolAttrName(),
1350 builder.getStringAttr(sym_name));
1351 builder.createBlock(result.addRegion());
1356 ModportType InterfaceOp::getModportType(StringRef modportName) {
1357 assert(lookupSymbol<InterfaceModportOp>(modportName) &&
1358 "Modport symbol not found.");
1359 auto *
ctxt = getContext();
1366 Type InterfaceOp::getSignalType(StringRef signalName) {
1367 InterfaceSignalOp signal = lookupSymbol<InterfaceSignalOp>(signalName);
1368 assert(signal &&
"Interface signal symbol not found.");
1369 return signal.getType();
1373 ArrayAttr &portsAttr) {
1375 auto context = parser.getBuilder().getContext();
1377 SmallVector<Attribute, 8> ports;
1378 auto parseElement = [&]() -> ParseResult {
1379 auto direction = ModportDirectionAttr::parse(parser, {});
1383 FlatSymbolRefAttr signal;
1384 if (parser.parseAttribute(signal))
1388 context, direction.cast<ModportDirectionAttr>(), signal));
1391 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
1400 ArrayAttr portsAttr) {
1402 llvm::interleaveComma(portsAttr, p, [&](Attribute attr) {
1403 auto port = attr.cast<ModportStructAttr>();
1404 p << stringifyEnum(port.getDirection().getValue());
1406 p.printSymbolName(port.getSignal().getRootReference().getValue());
1411 void InterfaceSignalOp::build(mlir::OpBuilder &
builder,
1412 ::mlir::OperationState &state, StringRef name,
1417 void InterfaceModportOp::build(OpBuilder &
builder, OperationState &state,
1418 StringRef name, ArrayRef<StringRef>
inputs,
1419 ArrayRef<StringRef>
outputs) {
1421 SmallVector<Attribute, 8> directions;
1424 for (
auto input :
inputs)
1433 std::optional<size_t> InterfaceInstanceOp::getTargetResultIndex() {
1435 return std::nullopt;
1441 setNameFn(getResult(),
getName());
1445 LogicalResult InterfaceInstanceOp::verify() {
1447 return emitOpError(
"requires non-empty name");
1449 auto *symtable = SymbolTable::getNearestSymbolTable(*
this);
1451 return emitError(
"sv.interface.instance must exist within a region "
1452 "which has a symbol table.");
1453 auto ifaceTy = getType();
1455 SymbolTable::lookupSymbolIn(symtable, ifaceTy.getInterface());
1457 return emitError(
"Symbol not found: ") << ifaceTy.getInterface() <<
".";
1458 if (!isa<InterfaceOp>(referencedOp))
1459 return emitError(
"Symbol ")
1460 << ifaceTy.getInterface() <<
" is not an InterfaceOp.";
1466 LogicalResult GetModportOp::verify() {
1467 auto *symtable = SymbolTable::getNearestSymbolTable(*
this);
1469 return emitError(
"sv.interface.instance must exist within a region "
1470 "which has a symbol table.");
1471 auto ifaceTy = getType();
1473 SymbolTable::lookupSymbolIn(symtable, ifaceTy.getModport());
1475 return emitError(
"Symbol not found: ") << ifaceTy.getModport() <<
".";
1476 if (!isa<InterfaceModportOp>(referencedOp))
1477 return emitError(
"Symbol ")
1478 << ifaceTy.getModport() <<
" is not an InterfaceModportOp.";
1482 void GetModportOp::build(OpBuilder &
builder, OperationState &state, Value value,
1484 auto ifaceTy = value.getType().dyn_cast<InterfaceType>();
1485 assert(ifaceTy &&
"GetModportOp expects an InterfaceType.");
1496 GetModportOp::getReferencedDecl(
const hw::HWSymbolCache &cache) {
1497 return dyn_cast_or_null<InterfaceModportOp>(
1498 cache.getDefinition(getFieldAttr()));
1501 void ReadInterfaceSignalOp::build(OpBuilder &
builder, OperationState &state,
1502 Value iface, StringRef signalName) {
1503 auto ifaceTy = iface.getType().dyn_cast<InterfaceType>();
1504 assert(ifaceTy &&
"ReadInterfaceSignalOp expects an InterfaceType.");
1506 InterfaceOp ifaceDefOp = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1507 iface.getDefiningOp(), ifaceTy.getInterface());
1509 "ReadInterfaceSignalOp could not resolve an InterfaceOp.");
1510 build(
builder, state, ifaceDefOp.getSignalType(signalName), iface, fieldAttr);
1516 ReadInterfaceSignalOp::getReferencedDecl(
const hw::HWSymbolCache &cache) {
1517 return dyn_cast_or_null<InterfaceSignalOp>(
1518 cache.getDefinition(getSignalNameAttr()));
1522 FlatSymbolRefAttr &signalName) {
1523 SymbolRefAttr fullSym;
1524 if (p.parseAttribute(fullSym) || fullSym.getNestedReferences().size() != 1)
1527 auto *
ctxt = p.getBuilder().getContext();
1535 FlatSymbolRefAttr signalName) {
1536 InterfaceType ifaceTy = type.dyn_cast<InterfaceType>();
1537 assert(ifaceTy &&
"Expected an InterfaceType");
1544 auto ifaceTy = ifaceVal.getType().dyn_cast<InterfaceType>();
1547 InterfaceOp iface = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1548 ifaceVal.getDefiningOp(), ifaceTy.getInterface());
1551 InterfaceSignalOp signal = iface.lookupSymbol<InterfaceSignalOp>(signalName);
1558 InterfaceInstanceOp::getReferencedInterface(
const hw::HWSymbolCache *cache) {
1559 FlatSymbolRefAttr
interface = getInterfaceType().getInterface();
1561 if (
auto *result = cache->getDefinition(interface))
1564 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1565 if (!topLevelModuleOp)
1568 return topLevelModuleOp.lookupSymbol(interface);
1571 LogicalResult AssignInterfaceSignalOp::verify() {
1575 LogicalResult ReadInterfaceSignalOp::verify() {
1583 void WireOp::build(OpBuilder &
builder, OperationState &odsState,
1585 hw::InnerSymAttr innerSym) {
1587 name =
builder.getStringAttr(
"");
1592 odsState.addAttribute(
"name", name);
1600 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
1601 if (!nameAttr.getValue().empty())
1602 setNameFn(getResult(), nameAttr.getValue());
1605 std::optional<size_t> WireOp::getTargetResultIndex() {
return 0; }
1608 LogicalResult WireOp::canonicalize(
WireOp wire, PatternRewriter &rewriter) {
1614 if (wire.getInnerSymAttr())
1619 SmallVector<sv::ReadInOutOp> reads;
1622 for (
auto *user : wire->getUsers()) {
1623 if (
auto read = dyn_cast<sv::ReadInOutOp>(user)) {
1624 reads.push_back(read);
1629 auto assign = dyn_cast<sv::AssignOp>(user);
1632 if (!assign || write)
1648 connected = rewriter.
create<ConstantZOp>(
1650 wire.getResult().getType().cast<
InOutType>().getElementType());
1651 }
else if (isa<hw::HWModuleOp>(write->getParentOp()))
1652 connected = write.getSrc();
1659 if (
auto *connectedOp = connected.getDefiningOp())
1660 if (!wire.getName().empty())
1661 rewriter.modifyOpInPlace(connectedOp, [&] {
1662 connectedOp->setAttr(
"sv.namehint", wire.getNameAttr());
1666 for (
auto read : reads)
1667 rewriter.replaceOp(read, connected);
1671 rewriter.eraseOp(write);
1672 rewriter.eraseOp(wire);
1683 if (elemTy.isa<IntegerType>())
1685 if (elemTy.isa<hw::ArrayType>())
1687 elemTy.cast<hw::ArrayType>().getElementType(),
width));
1692 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1693 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1694 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1695 auto width = attrs.get(
"width");
1700 operands[0].getType(),
1701 width.cast<IntegerAttr>().getValue().getZExtValue());
1704 results.push_back(typ);
1708 LogicalResult IndexedPartSelectInOutOp::verify() {
1709 unsigned inputWidth = 0, resultWidth = 0;
1711 auto inputElemTy = getInput().getType().cast<
InOutType>().getElementType();
1712 auto resultElemTy = getType().cast<
InOutType>().getElementType();
1713 if (
auto i = inputElemTy.dyn_cast<IntegerType>())
1714 inputWidth = i.getWidth();
1715 else if (
auto i = hw::type_cast<hw::ArrayType>(inputElemTy))
1716 inputWidth = i.getNumElements();
1718 return emitError(
"input element type must be Integer or Array");
1720 if (
auto resType = resultElemTy.dyn_cast<IntegerType>())
1721 resultWidth = resType.getWidth();
1722 else if (
auto resType = hw::type_cast<hw::ArrayType>(resultElemTy))
1723 resultWidth = resType.getNumElements();
1725 return emitError(
"result element type must be Integer or Array");
1727 if (opWidth > inputWidth)
1728 return emitError(
"slice width should not be greater than input width");
1729 if (opWidth != resultWidth)
1730 return emitError(
"result width must be equal to slice width");
1734 OpFoldResult IndexedPartSelectInOutOp::fold(FoldAdaptor) {
1735 if (getType() == getInput().getType())
1745 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1746 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1747 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1748 auto width = attrs.get(
"width");
1757 LogicalResult IndexedPartSelectOp::verify() {
1760 unsigned resultWidth = getType().cast<IntegerType>().
getWidth();
1761 unsigned inputWidth = getInput().getType().cast<IntegerType>().
getWidth();
1763 if (opWidth > inputWidth)
1764 return emitError(
"slice width should not be greater than input width");
1765 if (opWidth != resultWidth)
1766 return emitError(
"result width must be equal to slice width");
1775 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1776 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1777 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1778 auto field = attrs.get(
"field");
1783 auto resultType = structType.getFieldType(field.cast<StringAttr>());
1795 LogicalResult AliasOp::verify() {
1797 if (getAliases().size() < 2)
1798 return emitOpError(
"alias must have at least two operands");
1811 for (
auto &op : llvm::reverse(body->getOperations())) {
1812 if (
auto instance = dyn_cast<Op>(op)) {
1813 if (
auto innerSym = instance.getInnerSym())
1814 if (innerSym->getSymName() == name)
1818 if (
auto ifdef = dyn_cast<IfDefOp>(op)) {
1820 findInstanceSymbolInBlock<Op>(name, ifdef.getThenBlock()))
1822 if (ifdef.hasElse())
1824 findInstanceSymbolInBlock<Op>(name, ifdef.getElseBlock()))
1831 hw::InstanceOp BindOp::getReferencedInstance(
const hw::HWSymbolCache *cache) {
1834 auto result = cache->getInnerDefinition(getInstance());
1835 return cast<hw::InstanceOp>(result.getOp());
1839 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1840 if (!topLevelModuleOp)
1843 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
1844 topLevelModuleOp.lookupSymbol(getInstance().getModule()));
1849 return findInstanceSymbolInBlock<hw::InstanceOp>(getInstance().
getName(),
1850 hwModule.getBodyBlock());
1854 LogicalResult BindOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1855 auto module = (*this)->getParentOfType<mlir::ModuleOp>();
1856 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
1857 symbolTable.lookupSymbolIn(module, getInstance().getModule()));
1859 return emitError(
"Referenced module doesn't exist ")
1860 << getInstance().getModule() <<
"::" << getInstance().getName();
1862 auto inst = findInstanceSymbolInBlock<hw::InstanceOp>(
1863 getInstance().
getName(), hwModule.getBodyBlock());
1865 return emitError(
"Referenced instance doesn't exist ")
1866 << getInstance().getModule() <<
"::" << getInstance().getName();
1867 if (!inst->getAttr(
"doNotPrint"))
1868 return emitError(
"Referenced instance isn't marked as doNotPrint");
1872 void BindOp::build(OpBuilder &
builder, OperationState &odsState, StringAttr mod,
1875 odsState.addAttribute(
"instance", ref);
1882 sv::InterfaceInstanceOp
1883 BindInterfaceOp::getReferencedInstance(
const hw::HWSymbolCache *cache) {
1886 auto result = cache->getInnerDefinition(getInstance());
1887 return cast<sv::InterfaceInstanceOp>(result.getOp());
1891 auto *symbolTable = SymbolTable::getNearestSymbolTable(*
this);
1900 return findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
1901 getInstance().
getName(), &parentOp->getRegion(0).front());
1906 BindInterfaceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1908 symbolTable.lookupNearestSymbolFrom(*
this, getInstance().getModule());
1910 return emitError(
"Referenced module doesn't exist ")
1911 << getInstance().getModule() <<
"::" << getInstance().getName();
1913 auto inst = findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
1914 getInstance().
getName(), &parentOp->getRegion(0).front());
1916 return emitError(
"Referenced interface doesn't exist ")
1917 << getInstance().getModule() <<
"::" << getInstance().getName();
1918 if (!inst->getAttr(
"doNotPrint"))
1919 return emitError(
"Referenced interface isn't marked as doNotPrint");
1928 StringAttr &terminalAttr) {
1929 SmallVector<Attribute> strings;
1930 ParseResult ret = parser.parseCommaSeparatedList([&]() {
1933 if (succeeded(parser.parseOptionalKeyword(&keyword))) {
1934 strings.push_back(parser.getBuilder().getStringAttr(keyword));
1937 if (succeeded(parser.parseAttribute(
1938 result, parser.getBuilder().getType<NoneType>()))) {
1939 strings.push_back(result);
1944 if (succeeded(ret)) {
1945 pathAttr = parser.getBuilder().getArrayAttr(
1946 ArrayRef<Attribute>(strings).drop_back());
1947 terminalAttr = (*strings.rbegin()).cast<StringAttr>();
1953 StringAttr terminalAttr) {
1954 llvm::interleaveComma(pathAttr, p);
1955 p <<
", " << terminalAttr;
1959 LogicalResult XMRRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1960 auto *table = SymbolTable::getNearestSymbolTable(*
this);
1961 auto path = dyn_cast_or_null<hw::HierPathOp>(
1962 symbolTable.lookupSymbolIn(table, getRefAttr()));
1964 return emitError(
"Referenced path doesn't exist ") << getRefAttr();
1969 hw::HierPathOp XMRRefOp::getReferencedPath(
const hw::HWSymbolCache *cache) {
1971 if (
auto *result = cache->getDefinition(getRefAttr().
getAttr()))
1972 return cast<hw::HierPathOp>(result);
1974 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1975 return topLevelModuleOp.lookupSymbol<hw::HierPathOp>(getRefAttr().getValue());
1983 PatternRewriter &rewriter,
1986 if (constant.getValue().isZero() == eraseIfZero) {
1987 rewriter.eraseOp(op);
1994 template <
class Op,
bool EraseIfZero = false>
1996 PatternRewriter &rewriter) {
2000 void AssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
2001 MLIRContext *context) {
2002 results.add(canonicalizeImmediateVerifOp<AssertOp>);
2005 void AssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2006 MLIRContext *context) {
2007 results.add(canonicalizeImmediateVerifOp<AssumeOp>);
2010 void CoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
2011 MLIRContext *context) {
2012 results.add(canonicalizeImmediateVerifOp<CoverOp, /* EraseIfZero = */ true>);
2015 template <
class Op,
bool EraseIfZero = false>
2017 PatternRewriter &rewriter) {
2021 void AssertConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2022 MLIRContext *context) {
2023 results.add(canonicalizeConcurrentVerifOp<AssertConcurrentOp>);
2026 void AssumeConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2027 MLIRContext *context) {
2028 results.add(canonicalizeConcurrentVerifOp<AssumeConcurrentOp>);
2031 void CoverConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2032 MLIRContext *context) {
2034 canonicalizeConcurrentVerifOp<CoverConcurrentOp, /* EraseIfZero */ true>);
2044 ArrayAttr &caseNamesArray,
2045 SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
2047 SmallVector<Attribute> names;
2048 while (!p.parseOptionalKeyword(
"case")) {
2051 std::unique_ptr<Region> region = std::make_unique<Region>();
2052 if (p.parseLParen() || p.parseAttribute(pattern) || p.parseComma() ||
2053 p.parseAttribute(name) || p.parseRParen() || p.parseRegion(*region))
2056 names.push_back(name);
2057 if (region->empty())
2058 region->push_back(
new Block());
2059 caseRegions.push_back(std::move(region));
2061 patternsArray = p.getBuilder().getArrayAttr(
patterns);
2062 caseNamesArray = p.getBuilder().getArrayAttr(names);
2069 ArrayAttr namesArray,
2070 MutableArrayRef<Region> caseRegions) {
2071 assert(patternsArray.size() == caseRegions.size());
2072 assert(patternsArray.size() == namesArray.size());
2073 for (
size_t i = 0, e = caseRegions.size(); i < e; ++i) {
2075 p <<
"case (" << patternsArray[i] <<
", " << namesArray[i] <<
") ";
2076 p.printRegion(caseRegions[i]);
2081 LogicalResult GenerateCaseOp::verify() {
2082 size_t numPatterns = getCasePatterns().size();
2083 if (getCaseRegions().size() != numPatterns ||
2084 getCaseNames().size() != numPatterns)
2086 "Size of caseRegions, patterns, and caseNames must match");
2088 StringSet<> usedNames;
2089 for (Attribute name : getCaseNames()) {
2090 StringAttr nameStr = name.dyn_cast<StringAttr>();
2092 return emitOpError(
"caseNames must all be string attributes");
2093 if (usedNames.contains(nameStr.getValue()))
2094 return emitOpError(
"caseNames must be unique");
2095 usedNames.insert(nameStr.getValue());
2104 ModportDirection direction,
2105 FlatSymbolRefAttr signal) {
2114 #define GET_OP_CLASSES
2115 #include "circt/Dialect/SV/SV.cpp.inc"
assert(baseType &&"element must be base type")
static Attribute getAttr(ArrayRef< NamedAttribute > attrs, StringRef name)
Get an attribute by name from a list of named attributes.
static InstancePath empty
llvm::SmallVector< StringAttr > inputs
llvm::SmallVector< StringAttr > outputs
static std::optional< APInt > getInt(Value value)
Helper to convert a value to a constant integer if it is one.
ParseResult parseIfaceTypeAndSignal(OpAsmParser &p, Type &ifaceTy, FlatSymbolRefAttr &signalName)
static LogicalResult eraseIfZeroOrNotZero(Operation *op, Value value, PatternRewriter &rewriter, bool eraseIfZero)
static LogicalResult canonicalizeImmediateVerifOp(Op op, PatternRewriter &rewriter)
LogicalResult verifySignalExists(Value ifaceVal, FlatSymbolRefAttr signalName)
static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op, Region ®ion)
Replaces the given op with the contents of the given single-block region.
bool parseCaseRegions(OpAsmParser &p, ArrayAttr &patternsArray, ArrayAttr &caseNamesArray, SmallVectorImpl< std::unique_ptr< Region >> &caseRegions)
Parse cases formatted like: case (pattern, "name") { ...
void printCaseRegions(OpAsmPrinter &p, Operation *, ArrayAttr patternsArray, ArrayAttr namesArray, MutableArrayRef< Region > caseRegions)
Print cases formatted like: case (pattern, "name") { ...
void printIfaceTypeAndSignal(OpAsmPrinter &p, Operation *op, Type type, FlatSymbolRefAttr signalName)
static void printModportStructs(OpAsmPrinter &p, Operation *, ArrayAttr portsAttr)
static LogicalResult canonicalizeConcurrentVerifOp(Op op, PatternRewriter &rewriter)
static ParseResult parseEventList(OpAsmParser &p, Attribute &eventsAttr, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &clocksOperands)
static MacroDeclOp getReferencedMacro(const hw::HWSymbolCache *cache, Operation *op, FlatSymbolRefAttr macroName)
static LogicalResult canonicalizeIfDefLike(Op op, PatternRewriter &rewriter)
static SmallVector< CasePatternBit > getPatternBitsForValue(const APInt &value)
ParseResult parseXMRPath(::mlir::OpAsmParser &parser, ArrayAttr &pathAttr, StringAttr &terminalAttr)
static Type getElementTypeOfWidth(Type type, int32_t width)
static LogicalResult mergeNeiboringAssignments(AssignTy op, PatternRewriter &rewriter)
static Op findInstanceSymbolInBlock(StringAttr name, Block *body)
Instances must be at the top level of the hw.module (or within a `ifdef)
static void printEventList(OpAsmPrinter &p, AlwaysOp op, ArrayAttr portsAttr, OperandRange operands)
static Operation * lookupSymbolInNested(Operation *symbolTableOp, StringRef symbol)
Returns the operation registered with the given symbol name with the regions of 'symbolTableOp'.
static ParseResult parseImplicitInitType(OpAsmParser &p, mlir::Type regType, std::optional< OpAsmParser::UnresolvedOperand > &initValue, mlir::Type &initType)
static LogicalResult verifyMacroIdentSymbolUses(Operation *op, FlatSymbolRefAttr attr, SymbolTableCollection &symbolTable)
Verifies symbols referenced by macro identifiers.
static void getVerbatimExprAsmResultNames(Operation *op, function_ref< void(Value, StringRef)> setNameFn)
Get the asm name for sv.verbatim.expr and sv.verbatim.expr.se.
static void printImplicitInitType(OpAsmPrinter &p, Operation *op, mlir::Type regType, mlir::Value initValue, mlir::Type initType)
static ParseResult parseModportStructs(OpAsmParser &parser, ArrayAttr &portsAttr)
void printXMRPath(OpAsmPrinter &p, XMROp op, ArrayAttr pathAttr, StringAttr terminalAttr)
static StringRef getInnerSymbolAttrName()
Return the name of the attribute used for inner symbol names.
CasePatternBit getBit(size_t bitNumber) const
Return the specified bit, bit 0 is the least significant bit.
bool hasZ() const override
Return true if this pattern has an Z.
CaseBitPattern(ArrayRef< CasePatternBit > bits, MLIRContext *context)
Get a CasePattern from a specified list of CasePatternBit.
bool hasX() const override
Return true if this pattern has an X.
hw::EnumFieldAttr enumAttr
StringRef getFieldValue() const
Signals that an operations regions are procedural.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
uint64_t getWidth(Type t)
LogicalResult inferReturnTypes(MLIRContext *context, std::optional< Location > loc, ValueRange operands, DictionaryAttr attrs, mlir::OpaqueProperties properties, mlir::RegionRange regions, SmallVectorImpl< Type > &results, llvm::function_ref< FIRRTLType(ValueRange, ArrayRef< NamedAttribute >, std::optional< Location >)> callback)
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
void getAsmResultNames(OpAsmSetValueNameFn setNameFn, StringRef instanceName, ArrayAttr resultNames, ValueRange results)
Suggest a name for each result value based on the saved result names attribute.
bool isHWIntegerType(mlir::Type type)
Return true if the specified type is a value HW Integer type.
bool isOffset(Value base, Value index, uint64_t offset)
LogicalResult checkParameterInContext(Attribute value, Operation *module, Operation *usingOp, bool disallowParamRefs=false)
Check parameter specified by value to see if it is valid within the scope of the specified module mod...
bool isHWEnumType(mlir::Type type)
Return true if the specified type is a HW Enum type.
mlir::Type getCanonicalType(mlir::Type type)
LogicalResult verifyInNonProceduralRegion(Operation *op)
Return true if the specified operation is not in a procedural region.
CasePatternBit
This describes the bit in a pattern, 0/1/x/z.
bool isExpression(Operation *op)
Return true if the specified operation is an expression.
char getLetter(CasePatternBit bit)
Return the letter for the specified pattern bit, e.g. "0", "1", "x" or "z".
bool hasSVAttributes(mlir::Operation *op)
Helper functions to handle SV attributes.
circt::hw::InOutType InOutType
bool is2StateExpression(Value v)
Returns if the expression is known to be 2-state (binary)
mlir::Type getInOutElementType(mlir::Type type)
Return the element type of an InOutType or null if the operand isn't an InOut type.
LogicalResult verifyInProceduralRegion(Operation *op)
Return true if the specified operation is in a procedural region.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn