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()) {
101 function_ref<
void(Value, StringRef)> setNameFn) {
105 auto isOkCharacter = [](
char c) {
return llvm::isAlnum(c) || c ==
'_'; };
106 auto name = op->getAttrOfType<StringAttr>(
"format_string").getValue();
108 if (name.startswith(
"`"))
109 name = name.drop_front();
110 name = name.take_while(isOkCharacter);
112 setNameFn(op->getResult(0), name);
116 function_ref<
void(Value, StringRef)> setNameFn) {
121 function_ref<
void(Value, StringRef)> setNameFn) {
130 function_ref<
void(Value, StringRef)> setNameFn) {
131 setNameFn(getResult(), getMacroName());
135 function_ref<
void(Value, StringRef)> setNameFn) {
136 setNameFn(getResult(), getMacroName());
141 FlatSymbolRefAttr macroName) {
143 if (
auto *result = cache->getDefinition(macroName.getAttr()))
144 return cast<MacroDeclOp>(result);
146 auto topLevelModuleOp = op->getParentOfType<ModuleOp>();
147 return topLevelModuleOp.lookupSymbol<MacroDeclOp>(macroName.getValue());
167 SymbolTableCollection &symbolTable) {
168 auto macro = symbolTable.lookupNearestSymbolFrom<MacroDeclOp>(op, name);
170 return op->emitError(
"Referenced macro doesn't exist ") << name;
177 MacroRefExprOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
183 MacroRefExprSEOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
188 LogicalResult MacroDefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
197 function_ref<
void(Value, StringRef)> setNameFn) {
198 SmallVector<char, 32> specialNameBuffer;
199 llvm::raw_svector_ostream specialName(specialNameBuffer);
201 setNameFn(getResult(), specialName.str());
204 LogicalResult ConstantXOp::verify() {
207 return emitError(
"unsupported type");
212 function_ref<
void(Value, StringRef)> setNameFn) {
213 SmallVector<char, 32> specialNameBuffer;
214 llvm::raw_svector_ostream specialName(specialNameBuffer);
216 setNameFn(getResult(), specialName.str());
219 LogicalResult ConstantZOp::verify() {
222 return emitError(
"unsupported type");
232 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
233 if (!nameAttr.getValue().empty())
234 setNameFn(getResult(), nameAttr.getValue());
237 LogicalResult LocalParamOp::verify() {
249 std::optional<OpAsmParser::UnresolvedOperand> &initValue,
250 mlir::Type &initType) {
251 if (!initValue.has_value())
256 return p.emitError(p.getCurrentLocation(),
"expected inout type for reg");
258 initType = ioType.getElementType();
263 mlir::Type regType, mlir::Value initValue,
264 mlir::Type initType) {}
266 void RegOp::build(OpBuilder &
builder, OperationState &odsState,
267 Type
elementType, StringAttr name, hw::InnerSymAttr innerSym,
268 mlir::Value initValue) {
270 name =
builder.getStringAttr(
"");
271 odsState.addAttribute(
"name", name);
277 odsState.addOperands(initValue);
284 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
285 if (!nameAttr.getValue().empty())
286 setNameFn(getResult(), nameAttr.getValue());
289 std::optional<size_t> RegOp::getTargetResultIndex() {
return 0; }
292 LogicalResult RegOp::canonicalize(
RegOp op, PatternRewriter &rewriter) {
298 if (op.getInnerSymAttr())
302 for (
auto &use : op.getResult().getUses())
303 if (!isa<AssignOp>(use.getOwner()))
307 for (
auto &use : op.getResult().getUses())
308 rewriter.eraseOp(use.getOwner());
311 rewriter.eraseOp(op);
319 void LogicOp::build(OpBuilder &
builder, OperationState &odsState,
321 hw::InnerSymAttr innerSym) {
323 name =
builder.getStringAttr(
"");
324 odsState.addAttribute(
"name", name);
335 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
336 if (!nameAttr.getValue().empty())
337 setNameFn(getResult(), nameAttr.getValue());
340 std::optional<size_t> LogicOp::getTargetResultIndex() {
return 0; }
350 void IfDefOp::build(OpBuilder &
builder, OperationState &result, StringRef cond,
351 std::function<
void()> thenCtor,
352 std::function<
void()> elseCtor) {
353 build(
builder, result,
builder.getStringAttr(cond), std::move(thenCtor),
354 std::move(elseCtor));
357 void IfDefOp::build(OpBuilder &
builder, OperationState &result, StringAttr cond,
358 std::function<
void()> thenCtor,
359 std::function<
void()> elseCtor) {
361 std::move(thenCtor), std::move(elseCtor));
364 void IfDefOp::build(OpBuilder &
builder, OperationState &result,
365 MacroIdentAttr cond, std::function<
void()> thenCtor,
366 std::function<
void()> elseCtor) {
367 OpBuilder::InsertionGuard guard(
builder);
369 result.addAttribute(
"cond", cond);
370 builder.createBlock(result.addRegion());
376 Region *elseRegion = result.addRegion();
378 builder.createBlock(elseRegion);
386 if (!op.getThenBlock()->empty())
389 if (op.hasElse() && !op.getElseBlock()->empty())
392 rewriter.eraseOp(op);
396 LogicalResult IfDefOp::canonicalize(
IfDefOp op, PatternRewriter &rewriter) {
404 void IfDefProceduralOp::build(OpBuilder &
builder, OperationState &result,
405 StringRef cond, std::function<
void()> thenCtor,
406 std::function<
void()> elseCtor) {
407 build(
builder, result,
builder.getStringAttr(cond), std::move(thenCtor),
408 std::move(elseCtor));
411 void IfDefProceduralOp::build(OpBuilder &
builder, OperationState &result,
412 StringAttr cond, std::function<
void()> thenCtor,
413 std::function<
void()> elseCtor) {
415 std::move(thenCtor), std::move(elseCtor));
418 void IfDefProceduralOp::build(OpBuilder &
builder, OperationState &result,
420 std::function<
void()> thenCtor,
421 std::function<
void()> elseCtor) {
422 OpBuilder::InsertionGuard guard(
builder);
424 result.addAttribute(
"cond", cond);
425 builder.createBlock(result.addRegion());
431 Region *elseRegion = result.addRegion();
433 builder.createBlock(elseRegion);
438 LogicalResult IfDefProceduralOp::canonicalize(IfDefProceduralOp op,
439 PatternRewriter &rewriter) {
447 void IfOp::build(OpBuilder &
builder, OperationState &result, Value cond,
448 std::function<
void()> thenCtor,
449 std::function<
void()> elseCtor) {
450 OpBuilder::InsertionGuard guard(
builder);
452 result.addOperands(cond);
453 builder.createBlock(result.addRegion());
459 Region *elseRegion = result.addRegion();
461 builder.createBlock(elseRegion);
469 assert(llvm::hasSingleElement(region) &&
"expected single-region block");
470 Block *fromBlock = ®ion.front();
472 op->getBlock()->getOperations().splice(Block::iterator(op),
473 fromBlock->getOperations());
476 LogicalResult IfOp::canonicalize(IfOp op, PatternRewriter &rewriter) {
481 if (
auto constant = op.getCond().getDefiningOp<
hw::ConstantOp>()) {
483 if (constant.getValue().isAllOnes())
485 else if (!op.getElseRegion().empty())
488 rewriter.eraseOp(op);
494 if (!op.getThenBlock()->empty() && op.hasElse() &&
495 op.getElseBlock()->empty()) {
496 rewriter.eraseBlock(op.getElseBlock());
503 if (!op.getThenBlock()->empty())
507 if (!op.hasElse() || op.getElseBlock()->empty()) {
508 rewriter.eraseOp(op);
519 auto *thenBlock = op.getThenBlock(), *elseBlock = op.getElseBlock();
522 thenBlock->getOperations().splice(thenBlock->end(),
523 elseBlock->getOperations());
524 rewriter.eraseBlock(elseBlock);
534 AlwaysOp::Condition AlwaysOp::getCondition(
size_t idx) {
535 return Condition{EventControl(getEvents()[idx].cast<IntegerAttr>().
getInt()),
539 void AlwaysOp::build(OpBuilder &
builder, OperationState &result,
540 ArrayRef<sv::EventControl> events, ArrayRef<Value> clocks,
541 std::function<
void()> bodyCtor) {
542 assert(events.size() == clocks.size() &&
543 "mismatch between event and clock list");
544 OpBuilder::InsertionGuard guard(
builder);
546 SmallVector<Attribute> eventAttrs;
547 for (
auto event : events)
548 eventAttrs.push_back(
549 builder.getI32IntegerAttr(
static_cast<int32_t
>(event)));
550 result.addAttribute(
"events",
builder.getArrayAttr(eventAttrs));
551 result.addOperands(clocks);
554 builder.createBlock(result.addRegion());
562 LogicalResult AlwaysOp::verify() {
563 if (getEvents().size() != getNumOperands())
564 return emitError(
"different number of operands and events");
569 OpAsmParser &p, Attribute &eventsAttr,
570 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &clocksOperands) {
573 SmallVector<Attribute> events;
575 auto loc = p.getCurrentLocation();
577 if (!p.parseOptionalKeyword(&keyword)) {
579 auto kind = sv::symbolizeEventControl(keyword);
580 if (!kind.has_value())
581 return p.emitError(loc,
"expected 'posedge', 'negedge', or 'edge'");
582 auto eventEnum =
static_cast<int32_t
>(*kind);
583 events.push_back(p.getBuilder().getI32IntegerAttr(eventEnum));
585 clocksOperands.push_back({});
586 if (p.parseOperand(clocksOperands.back()))
589 if (failed(p.parseOptionalComma()))
591 if (p.parseKeyword(&keyword))
595 eventsAttr = p.getBuilder().getArrayAttr(events);
600 OperandRange operands) {
601 for (
size_t i = 0, e = op.getNumConditions(); i != e; ++i) {
604 auto cond = op.getCondition(i);
605 p << stringifyEventControl(cond.event);
607 p.printOperand(cond.value);
615 void AlwaysFFOp::build(OpBuilder &
builder, OperationState &result,
616 EventControl clockEdge, Value clock,
617 std::function<
void()> bodyCtor) {
618 OpBuilder::InsertionGuard guard(
builder);
621 "clockEdge",
builder.getI32IntegerAttr(
static_cast<int32_t
>(clockEdge)));
622 result.addOperands(clock);
625 builder.getI32IntegerAttr(
static_cast<int32_t
>(ResetType::NoReset)));
628 builder.createBlock(result.addRegion());
637 void AlwaysFFOp::build(OpBuilder &
builder, OperationState &result,
638 EventControl clockEdge, Value clock,
639 ResetType resetStyle, EventControl resetEdge,
640 Value reset, std::function<
void()> bodyCtor,
641 std::function<
void()> resetCtor) {
642 OpBuilder::InsertionGuard guard(
builder);
645 "clockEdge",
builder.getI32IntegerAttr(
static_cast<int32_t
>(clockEdge)));
646 result.addOperands(clock);
647 result.addAttribute(
"resetStyle",
builder.getI32IntegerAttr(
648 static_cast<int32_t
>(resetStyle)));
650 "resetEdge",
builder.getI32IntegerAttr(
static_cast<int32_t
>(resetEdge)));
651 result.addOperands(reset);
654 builder.createBlock(result.addRegion());
660 builder.createBlock(result.addRegion());
670 void AlwaysCombOp::build(OpBuilder &
builder, OperationState &result,
671 std::function<
void()> bodyCtor) {
672 OpBuilder::InsertionGuard guard(
builder);
674 builder.createBlock(result.addRegion());
684 void InitialOp::build(OpBuilder &
builder, OperationState &result,
685 std::function<
void()> bodyCtor) {
686 OpBuilder::InsertionGuard guard(
builder);
688 builder.createBlock(result.addRegion());
711 llvm_unreachable(
"invalid casez PatternBit");
716 return CasePatternBit(
unsigned(intAttr.getValue()[bitNumber * 2]) +
717 2 *
unsigned(intAttr.getValue()[bitNumber * 2 + 1]));
721 for (
size_t i = 0, e =
getWidth(); i != e; ++i)
728 for (
size_t i = 0, e =
getWidth(); i != e; ++i)
734 SmallVector<CasePatternBit> result;
735 result.reserve(
value.getBitWidth());
736 for (
size_t i = 0, e =
value.getBitWidth(); i != e; ++i)
752 MLIRContext *context)
754 APInt pattern(bits.size() * 2, 0);
755 for (
auto elt : llvm::reverse(bits)) {
757 pattern |= unsigned(elt);
763 auto CaseOp::getCases() -> SmallVector<CaseInfo, 4> {
764 SmallVector<CaseInfo, 4> result;
765 assert(getCasePatterns().size() == getNumRegions() &&
766 "case pattern / region count mismatch");
767 size_t nextRegion = 0;
768 for (
auto elt : getCasePatterns()) {
769 llvm::TypeSwitch<Attribute>(elt)
770 .Case<hw::EnumFieldAttr>([&](
auto enumAttr) {
771 result.push_back({std::make_unique<CaseEnumPattern>(enumAttr),
772 &getRegion(nextRegion++).front()});
774 .Case<IntegerAttr>([&](
auto intAttr) {
775 result.push_back({std::make_unique<CaseBitPattern>(intAttr),
776 &getRegion(nextRegion++).front()});
778 .Case<CaseDefaultPattern::AttrType>([&](
auto) {
779 result.push_back({std::make_unique<CaseDefaultPattern>(getContext()),
780 &getRegion(nextRegion++).front()});
783 assert(
false &&
"invalid case pattern attribute type");
791 return enumAttr.cast<hw::EnumFieldAttr>().getField();
801 ParseResult CaseOp::parse(OpAsmParser &parser, OperationState &result) {
802 auto &
builder = parser.getBuilder();
804 OpAsmParser::UnresolvedOperand condOperand;
807 auto loc = parser.getCurrentLocation();
810 if (!parser.parseOptionalKeyword(&keyword, {
"case",
"casex",
"casez"})) {
811 auto kind = symbolizeCaseStmtType(keyword);
812 auto caseEnum =
static_cast<int32_t
>(kind.value());
813 result.addAttribute(
"caseStyle",
builder.getI32IntegerAttr(caseEnum));
817 if (!parser.parseOptionalKeyword(
818 &keyword, {
"plain",
"priority",
"unique",
"unique0"})) {
819 auto kind = symbolizeValidationQualifierTypeEnum(keyword);
820 result.addAttribute(
"validationQualifier",
822 builder.getContext(), kind.value()));
825 if (parser.parseOperand(condOperand) || parser.parseColonType(condType) ||
826 parser.parseOptionalAttrDict(result.attributes) ||
827 parser.resolveOperand(condOperand, condType, result.operands))
832 hw::EnumType enumType = canonicalCondType.dyn_cast<hw::EnumType>();
833 unsigned condWidth = 0;
835 if (!result.operands[0].getType().isSignlessInteger())
836 return parser.emitError(loc,
"condition must have signless integer type");
837 condWidth = condType.getIntOrFloatBitWidth();
841 SmallVector<Attribute> casePatterns;
842 SmallVector<CasePatternBit, 16> caseBits;
844 if (succeeded(parser.parseOptionalKeyword(
"default"))) {
845 casePatterns.push_back(CaseDefaultPattern(parser.getContext()).attr());
846 }
else if (failed(parser.parseOptionalKeyword(
"case"))) {
849 }
else if (enumType) {
853 if (parser.parseKeyword(&caseVal))
856 if (!enumType.contains(caseVal))
857 return parser.emitError(loc)
858 <<
"case value '" + caseVal +
"' is not a member of enum type "
860 casePatterns.push_back(
862 builder.getStringAttr(caseVal), condType));
867 loc = parser.getCurrentLocation();
868 if (parser.parseKeyword(&caseVal))
871 if (caseVal.front() !=
'b')
872 return parser.emitError(loc,
"expected case value starting with 'b'");
873 caseVal = caseVal.drop_front();
876 for (; !caseVal.empty(); caseVal = caseVal.drop_front()) {
878 switch (caseVal.front()) {
892 return parser.emitError(loc,
"unexpected case bit '")
893 << caseVal.front() <<
"'";
895 caseBits.push_back(bit);
898 if (caseVal.size() > condWidth)
899 return parser.emitError(loc,
"too many bits specified in pattern");
900 std::reverse(caseBits.begin(), caseBits.end());
903 if (caseBits.size() < condWidth)
906 auto resultPattern = CaseBitPattern(caseBits,
builder.getContext());
907 casePatterns.push_back(resultPattern.attr());
912 auto caseRegion = std::make_unique<Region>();
913 if (parser.parseColon() || parser.parseRegion(*caseRegion))
915 result.addRegion(std::move(caseRegion));
918 result.addAttribute(
"casePatterns",
builder.getArrayAttr(casePatterns));
922 void CaseOp::print(OpAsmPrinter &p) {
924 if (getCaseStyle() == CaseStmtType::CaseXStmt)
926 else if (getCaseStyle() == CaseStmtType::CaseZStmt)
929 if (getValidationQualifier() !=
930 ValidationQualifierTypeEnum::ValidationQualifierPlain)
931 p << stringifyValidationQualifierTypeEnum(getValidationQualifier()) <<
' ';
933 p << getCond() <<
" : " << getCond().getType();
934 p.printOptionalAttrDict(
936 {
"casePatterns",
"caseStyle",
"validationQualifier"});
938 for (
auto &caseInfo : getCases()) {
940 auto &pattern = caseInfo.pattern;
942 llvm::TypeSwitch<CasePattern *>(pattern.get())
943 .Case<CaseBitPattern>([&](
auto bitPattern) {
945 for (
size_t bit = 0, e = bitPattern->getWidth(); bit != e; ++bit)
946 p <<
getLetter(bitPattern->getBit(e - bit - 1));
948 .Case<CaseEnumPattern>([&](
auto enumPattern) {
949 p <<
"case " << enumPattern->getFieldValue();
951 .Case<CaseDefaultPattern>([&](
auto) { p <<
"default"; })
952 .Default([&](
auto) {
assert(
false &&
"unhandled case pattern"); });
955 p.printRegion(*caseInfo.block->getParent(),
false,
960 LogicalResult CaseOp::verify() {
963 return emitError(
"condition must have either integer or enum type");
966 if (getCasePatterns().size() != getNumRegions())
967 return emitOpError(
"case pattern / region count mismatch");
974 OpBuilder &
builder, OperationState &result, CaseStmtType caseStyle,
975 ValidationQualifierTypeEnum validationQualifier, Value cond,
977 std::function<std::unique_ptr<CasePattern>(
size_t)> caseCtor) {
978 result.addOperands(cond);
979 result.addAttribute(
"caseStyle",
981 result.addAttribute(
"validationQualifier",
983 builder.getContext(), validationQualifier));
984 SmallVector<Attribute> casePatterns;
986 OpBuilder::InsertionGuard guard(
builder);
989 for (
size_t i = 0, e = numCases; i != e; ++i) {
990 builder.createBlock(result.addRegion());
991 casePatterns.push_back(caseCtor(i)->attr());
994 result.addAttribute(
"casePatterns",
builder.getArrayAttr(casePatterns));
998 LogicalResult CaseOp::canonicalize(CaseOp op, PatternRewriter &rewriter) {
999 if (op.getCaseStyle() == CaseStmtType::CaseStmt)
1001 if (op.getCond().getType().isa<hw::EnumType>())
1004 auto caseInfo = op.getCases();
1005 bool noXZ = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1006 return !ci.pattern.get()->hasX() && !ci.pattern.get()->hasZ();
1008 bool noX = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1009 if (isa<CaseDefaultPattern>(ci.pattern))
1011 return !ci.pattern.get()->hasX();
1013 bool noZ = llvm::all_of(caseInfo, [](
const CaseInfo &ci) {
1014 if (isa<CaseDefaultPattern>(ci.pattern))
1016 return !ci.pattern.get()->hasZ();
1019 if (op.getCaseStyle() == CaseStmtType::CaseXStmt) {
1021 rewriter.updateRootInPlace(op, [&]() {
1022 op.setCaseStyleAttr(
1028 rewriter.updateRootInPlace(op, [&]() {
1029 op.setCaseStyleAttr(
1036 if (op.getCaseStyle() == CaseStmtType::CaseZStmt && noZ) {
1037 rewriter.updateRootInPlace(op, [&]() {
1038 op.setCaseStyleAttr(
1051 void OrderedOutputOp::build(OpBuilder &
builder, OperationState &result,
1052 std::function<
void()> body) {
1053 OpBuilder::InsertionGuard guard(
builder);
1055 builder.createBlock(result.addRegion());
1066 void ForOp::build(OpBuilder &
builder, OperationState &result,
1067 int64_t lowerBound, int64_t upperBound, int64_t step,
1068 IntegerType type, StringRef name,
1069 llvm::function_ref<
void(BlockArgument)> body) {
1073 build(
builder, result, lb, ub, st, name, body);
1075 void ForOp::build(OpBuilder &
builder, OperationState &result, Value lowerBound,
1076 Value upperBound, Value step, StringRef name,
1077 llvm::function_ref<
void(BlockArgument)> body) {
1078 OpBuilder::InsertionGuard guard(
builder);
1079 build(
builder, result, lowerBound, upperBound, step, name);
1080 auto *region = result.regions.front().get();
1082 BlockArgument blockArgument =
1083 region->addArgument(lowerBound.getType(), result.location);
1086 body(blockArgument);
1089 void ForOp::getAsmBlockArgumentNames(mlir::Region ®ion,
1091 auto *block = ®ion.front();
1092 setNameFn(block->getArgument(0), getInductionVarNameAttr());
1095 ParseResult ForOp::parse(OpAsmParser &parser, OperationState &result) {
1096 auto &
builder = parser.getBuilder();
1099 OpAsmParser::Argument inductionVariable;
1100 OpAsmParser::UnresolvedOperand lb, ub, step;
1102 SmallVector<OpAsmParser::Argument, 4> regionArgs;
1105 if (parser.parseOperand(inductionVariable.ssaName) || parser.parseEqual() ||
1107 parser.parseOperand(lb) || parser.parseKeyword(
"to") ||
1108 parser.parseOperand(ub) || parser.parseKeyword(
"step") ||
1109 parser.parseOperand(step) || parser.parseColon() ||
1110 parser.parseType(type))
1113 regionArgs.push_back(inductionVariable);
1116 regionArgs.front().type = type;
1117 if (parser.resolveOperand(lb, type, result.operands) ||
1118 parser.resolveOperand(ub, type, result.operands) ||
1119 parser.resolveOperand(step, type, result.operands))
1123 Region *body = result.addRegion();
1124 if (parser.parseRegion(*body, regionArgs))
1128 if (parser.parseOptionalAttrDict(result.attributes))
1131 if (!inductionVariable.ssaName.name.empty()) {
1132 if (!
isdigit(inductionVariable.ssaName.name[1]))
1134 result.attributes.append(
1135 {
builder.getStringAttr(
"inductionVarName"),
1136 builder.getStringAttr(inductionVariable.ssaName.name.drop_front())});
1142 void ForOp::print(OpAsmPrinter &p) {
1143 p <<
" " << getInductionVar() <<
" = " << getLowerBound() <<
" to "
1144 << getUpperBound() <<
" step " << getStep();
1145 p <<
" : " << getInductionVar().getType() <<
' ';
1146 p.printRegion(getRegion(),
1149 p.printOptionalAttrDict((*this)->getAttrs(), {
"inductionVarName"});
1152 LogicalResult ForOp::canonicalize(ForOp op, PatternRewriter &rewriter) {
1154 if (matchPattern(op.getLowerBound(), mlir::m_ConstantInt(&lb)) &&
1155 matchPattern(op.getUpperBound(), mlir::m_ConstantInt(&ub)) &&
1156 matchPattern(op.getStep(), mlir::m_ConstantInt(&step)) &&
1159 op.getInductionVar().replaceAllUsesWith(op.getLowerBound());
1161 rewriter.eraseOp(op);
1171 LogicalResult BPAssignOp::verify() {
1172 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1174 "Verilog disallows procedural assignment to a net type (did you intend "
1175 "to use a variable type, e.g., sv.reg?)");
1179 LogicalResult PAssignOp::verify() {
1180 if (isa<sv::WireOp>(getDest().getDefiningOp()))
1182 "Verilog disallows procedural assignment to a net type (did you intend "
1183 "to use a variable type, e.g., sv.reg?)");
1196 static std::optional<ArraySlice> getArraySlice(Value v) {
1197 auto *op = v.getDefiningOp();
1199 return std::nullopt;
1200 return TypeSwitch<Operation *, std::optional<ArraySlice>>(op)
1201 .Case<hw::ArrayGetOp, ArrayIndexInOutOp>(
1202 [](
auto arrayIndex) -> std::optional<ArraySlice> {
1204 arrayIndex.getIndex()
1205 .template getDefiningOp<hw::ConstantOp>();
1207 return std::nullopt;
1208 return ArraySlice{arrayIndex.getInput(),
1213 -> std::optional<ArraySlice> {
1214 auto constant = slice.getLowIndex().getDefiningOp<
hw::ConstantOp>();
1216 return std::nullopt;
1218 slice.getInput(), constant,
1220 hw::type_cast<hw::ArrayType>(slice.getType()).getNumElements()};
1222 .Case<sv::IndexedPartSelectInOutOp>(
1223 [](sv::IndexedPartSelectInOutOp index)
1224 -> std::optional<ArraySlice> {
1226 if (!constant || index.getDecrement())
1227 return std::nullopt;
1228 return ArraySlice{index.getInput(),
1232 .Default([](
auto) {
return std::nullopt; });
1236 static std::optional<std::pair<ArraySlice, ArraySlice>>
1237 getAssignedRange(Operation *op) {
1238 assert((isa<PAssignOp, BPAssignOp>(op) &&
"assignments are expected"));
1239 auto srcRange = ArraySlice::getArraySlice(op->getOperand(1));
1241 return std::nullopt;
1242 auto destRange = ArraySlice::getArraySlice(op->getOperand(0));
1244 return std::nullopt;
1246 return std::make_pair(*destRange, *srcRange);
1257 template <
typename AssignTy>
1259 PatternRewriter &rewriter) {
1261 auto assignedRangeOpt = ArraySlice::getAssignedRange(op);
1262 if (!assignedRangeOpt)
1265 auto [dest, src] = *assignedRangeOpt;
1266 AssignTy nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1267 bool changed =
false;
1268 SmallVector<Location> loc{op.getLoc()};
1270 while (nextAssign) {
1271 auto nextAssignedRange = ArraySlice::getAssignedRange(nextAssign);
1272 if (!nextAssignedRange)
1274 auto [nextDest, nextSrc] = *nextAssignedRange;
1276 if (dest.array != nextDest.array || src.array != nextSrc.array ||
1277 !
hw::isOffset(dest.start, nextDest.start, dest.size) ||
1281 dest.size += nextDest.size;
1282 src.size += nextSrc.size;
1284 loc.push_back(nextAssign.getLoc());
1285 rewriter.eraseOp(nextAssign);
1286 nextAssign = dyn_cast_or_null<AssignTy>(op->getNextNode());
1294 hw::type_cast<hw::ArrayType>(src.array.getType()).getElementType(),
1296 auto newDest = rewriter.create<sv::IndexedPartSelectInOutOp>(
1297 op.getLoc(), dest.array, dest.start, dest.size);
1299 src.array, src.start);
1300 auto newLoc = rewriter.getFusedLoc(loc);
1301 auto newOp = rewriter.replaceOpWithNewOp<AssignTy>(op, newDest, newSrc);
1302 newOp->setLoc(newLoc);
1306 LogicalResult PAssignOp::canonicalize(PAssignOp op, PatternRewriter &rewriter) {
1310 LogicalResult BPAssignOp::canonicalize(BPAssignOp op,
1311 PatternRewriter &rewriter) {
1319 void InterfaceOp::build(OpBuilder &
builder, OperationState &result,
1320 StringRef sym_name, std::function<
void()> body) {
1321 OpBuilder::InsertionGuard guard(
builder);
1323 result.addAttribute(::SymbolTable::getSymbolAttrName(),
1324 builder.getStringAttr(sym_name));
1325 builder.createBlock(result.addRegion());
1330 ModportType InterfaceOp::getModportType(StringRef modportName) {
1331 assert(lookupSymbol<InterfaceModportOp>(modportName) &&
1332 "Modport symbol not found.");
1333 auto *ctxt = getContext();
1340 Type InterfaceOp::getSignalType(StringRef signalName) {
1341 InterfaceSignalOp signal = lookupSymbol<InterfaceSignalOp>(signalName);
1342 assert(signal &&
"Interface signal symbol not found.");
1343 return signal.getType();
1347 ArrayAttr &portsAttr) {
1349 auto context = parser.getBuilder().getContext();
1351 SmallVector<Attribute, 8> ports;
1352 auto parseElement = [&]() -> ParseResult {
1353 auto direction = ModportDirectionAttr::parse(parser, {});
1357 FlatSymbolRefAttr signal;
1358 if (parser.parseAttribute(signal))
1362 context, direction.cast<ModportDirectionAttr>(), signal));
1365 if (parser.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren,
1374 ArrayAttr portsAttr) {
1376 llvm::interleaveComma(portsAttr, p, [&](Attribute attr) {
1377 auto port = attr.cast<ModportStructAttr>();
1378 p << stringifyEnum(port.getDirection().getValue());
1380 p.printSymbolName(port.getSignal().getRootReference().getValue());
1385 void InterfaceSignalOp::build(mlir::OpBuilder &
builder,
1386 ::mlir::OperationState &state, StringRef name,
1391 void InterfaceModportOp::build(OpBuilder &
builder, OperationState &state,
1392 StringRef name, ArrayRef<StringRef>
inputs,
1393 ArrayRef<StringRef>
outputs) {
1394 auto *ctxt =
builder.getContext();
1395 SmallVector<Attribute, 8> directions;
1398 for (
auto input :
inputs)
1407 std::optional<size_t> InterfaceInstanceOp::getTargetResultIndex() {
1409 return std::nullopt;
1415 setNameFn(getResult(),
getName());
1419 LogicalResult InterfaceInstanceOp::verify() {
1421 return emitOpError(
"requires non-empty name");
1423 auto *symtable = SymbolTable::getNearestSymbolTable(*
this);
1425 return emitError(
"sv.interface.instance must exist within a region "
1426 "which has a symbol table.");
1427 auto ifaceTy = getType();
1429 SymbolTable::lookupSymbolIn(symtable, ifaceTy.getInterface());
1431 return emitError(
"Symbol not found: ") << ifaceTy.getInterface() <<
".";
1432 if (!isa<InterfaceOp>(referencedOp))
1433 return emitError(
"Symbol ")
1434 << ifaceTy.getInterface() <<
" is not an InterfaceOp.";
1440 LogicalResult GetModportOp::verify() {
1441 auto *symtable = SymbolTable::getNearestSymbolTable(*
this);
1443 return emitError(
"sv.interface.instance must exist within a region "
1444 "which has a symbol table.");
1445 auto ifaceTy = getType();
1447 SymbolTable::lookupSymbolIn(symtable, ifaceTy.getModport());
1449 return emitError(
"Symbol not found: ") << ifaceTy.getModport() <<
".";
1450 if (!isa<InterfaceModportOp>(referencedOp))
1451 return emitError(
"Symbol ")
1452 << ifaceTy.getModport() <<
" is not an InterfaceModportOp.";
1456 void GetModportOp::build(OpBuilder &
builder, OperationState &state, Value
value,
1458 auto ifaceTy =
value.getType().dyn_cast<InterfaceType>();
1459 assert(ifaceTy &&
"GetModportOp expects an InterfaceType.");
1470 GetModportOp::getReferencedDecl(
const hw::HWSymbolCache &cache) {
1471 return dyn_cast_or_null<InterfaceModportOp>(
1472 cache.getDefinition(getFieldAttr()));
1475 void ReadInterfaceSignalOp::build(OpBuilder &
builder, OperationState &state,
1476 Value iface, StringRef signalName) {
1477 auto ifaceTy = iface.getType().dyn_cast<InterfaceType>();
1478 assert(ifaceTy &&
"ReadInterfaceSignalOp expects an InterfaceType.");
1480 InterfaceOp ifaceDefOp = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1481 iface.getDefiningOp(), ifaceTy.getInterface());
1483 "ReadInterfaceSignalOp could not resolve an InterfaceOp.");
1484 build(
builder, state, ifaceDefOp.getSignalType(signalName), iface, fieldAttr);
1490 ReadInterfaceSignalOp::getReferencedDecl(
const hw::HWSymbolCache &cache) {
1491 return dyn_cast_or_null<InterfaceSignalOp>(
1492 cache.getDefinition(getSignalNameAttr()));
1496 FlatSymbolRefAttr &signalName) {
1497 SymbolRefAttr fullSym;
1498 if (p.parseAttribute(fullSym) || fullSym.getNestedReferences().size() != 1)
1501 auto *ctxt = p.getBuilder().getContext();
1509 FlatSymbolRefAttr signalName) {
1510 InterfaceType ifaceTy = type.dyn_cast<InterfaceType>();
1511 assert(ifaceTy &&
"Expected an InterfaceType");
1518 auto ifaceTy = ifaceVal.getType().dyn_cast<InterfaceType>();
1521 InterfaceOp iface = SymbolTable::lookupNearestSymbolFrom<InterfaceOp>(
1522 ifaceVal.getDefiningOp(), ifaceTy.getInterface());
1525 InterfaceSignalOp signal = iface.lookupSymbol<InterfaceSignalOp>(signalName);
1532 InterfaceInstanceOp::getReferencedInterface(
const hw::HWSymbolCache *cache) {
1533 FlatSymbolRefAttr
interface = getInterfaceType().getInterface();
1535 if (
auto *result = cache->getDefinition(interface))
1538 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1539 if (!topLevelModuleOp)
1542 return topLevelModuleOp.lookupSymbol(interface);
1545 LogicalResult AssignInterfaceSignalOp::verify() {
1549 LogicalResult ReadInterfaceSignalOp::verify() {
1557 void WireOp::build(OpBuilder &
builder, OperationState &odsState,
1559 hw::InnerSymAttr innerSym) {
1561 name =
builder.getStringAttr(
"");
1566 odsState.addAttribute(
"name", name);
1574 auto nameAttr = (*this)->getAttrOfType<StringAttr>(
"name");
1575 if (!nameAttr.getValue().empty())
1576 setNameFn(getResult(), nameAttr.getValue());
1579 std::optional<size_t> WireOp::getTargetResultIndex() {
return 0; }
1582 LogicalResult WireOp::canonicalize(
WireOp wire, PatternRewriter &rewriter) {
1588 if (wire.getInnerSymAttr())
1593 SmallVector<sv::ReadInOutOp> reads;
1596 for (
auto *user : wire->getUsers()) {
1597 if (
auto read = dyn_cast<sv::ReadInOutOp>(user)) {
1598 reads.push_back(read);
1603 auto assign = dyn_cast<sv::AssignOp>(user);
1606 if (!assign || write)
1622 connected = rewriter.
create<ConstantZOp>(
1624 wire.getResult().getType().cast<
InOutType>().getElementType());
1625 }
else if (isa<hw::HWModuleOp>(write->getParentOp()))
1626 connected = write.getSrc();
1633 if (
auto *connectedOp = connected.getDefiningOp())
1634 if (!wire.getName().empty())
1635 rewriter.updateRootInPlace(connectedOp, [&] {
1636 connectedOp->setAttr(
"sv.namehint", wire.getNameAttr());
1640 for (
auto read : reads)
1641 rewriter.replaceOp(read, connected);
1645 rewriter.eraseOp(write);
1646 rewriter.eraseOp(wire);
1657 if (elemTy.isa<IntegerType>())
1659 if (elemTy.isa<hw::ArrayType>())
1661 elemTy.cast<hw::ArrayType>().getElementType(),
width));
1666 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1667 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1668 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1669 auto width = attrs.get(
"width");
1674 operands[0].getType(),
1675 width.cast<IntegerAttr>().getValue().getZExtValue());
1678 results.push_back(typ);
1682 LogicalResult IndexedPartSelectInOutOp::verify() {
1683 unsigned inputWidth = 0, resultWidth = 0;
1685 auto inputElemTy = getInput().getType().cast<
InOutType>().getElementType();
1686 auto resultElemTy = getType().cast<
InOutType>().getElementType();
1687 if (
auto i = inputElemTy.dyn_cast<IntegerType>())
1688 inputWidth = i.getWidth();
1689 else if (
auto i = hw::type_cast<hw::ArrayType>(inputElemTy))
1690 inputWidth = i.getNumElements();
1692 return emitError(
"input element type must be Integer or Array");
1694 if (
auto resType = resultElemTy.dyn_cast<IntegerType>())
1695 resultWidth = resType.getWidth();
1696 else if (
auto resType = hw::type_cast<hw::ArrayType>(resultElemTy))
1697 resultWidth = resType.getNumElements();
1699 return emitError(
"result element type must be Integer or Array");
1701 if (opWidth > inputWidth)
1702 return emitError(
"slice width should not be greater than input width");
1703 if (opWidth != resultWidth)
1704 return emitError(
"result width must be equal to slice width");
1708 OpFoldResult IndexedPartSelectInOutOp::fold(FoldAdaptor) {
1709 if (getType() == getInput().getType())
1719 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1720 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1721 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1722 auto width = attrs.get(
"width");
1731 LogicalResult IndexedPartSelectOp::verify() {
1734 unsigned resultWidth = getType().cast<IntegerType>().
getWidth();
1735 unsigned inputWidth = getInput().getType().cast<IntegerType>().
getWidth();
1737 if (opWidth > inputWidth)
1738 return emitError(
"slice width should not be greater than input width");
1739 if (opWidth != resultWidth)
1740 return emitError(
"result width must be equal to slice width");
1749 MLIRContext *context, std::optional<Location> loc, ValueRange operands,
1750 DictionaryAttr attrs, mlir::OpaqueProperties properties,
1751 mlir::RegionRange regions, SmallVectorImpl<Type> &results) {
1752 auto field = attrs.get(
"field");
1757 auto resultType = structType.getFieldType(field.cast<StringAttr>());
1769 LogicalResult AliasOp::verify() {
1771 if (getAliases().size() < 2)
1772 return emitOpError(
"alias must have at least two operands");
1785 for (
auto &op : llvm::reverse(body->getOperations())) {
1786 if (
auto instance = dyn_cast<Op>(op)) {
1787 if (
auto innerSym = instance.getInnerSym())
1788 if (innerSym->getSymName() == name)
1792 if (
auto ifdef = dyn_cast<IfDefOp>(op)) {
1794 findInstanceSymbolInBlock<Op>(name, ifdef.getThenBlock()))
1796 if (ifdef.hasElse())
1798 findInstanceSymbolInBlock<Op>(name, ifdef.getElseBlock()))
1805 hw::InstanceOp BindOp::getReferencedInstance(
const hw::HWSymbolCache *cache) {
1808 auto result = cache->getInnerDefinition(getInstance());
1809 return cast<hw::InstanceOp>(result.getOp());
1813 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1814 if (!topLevelModuleOp)
1817 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
1818 topLevelModuleOp.lookupSymbol(getInstance().getModule()));
1823 return findInstanceSymbolInBlock<hw::InstanceOp>(getInstance().
getName(),
1824 hwModule.getBodyBlock());
1828 LogicalResult BindOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1829 auto module = (*this)->getParentOfType<mlir::ModuleOp>();
1830 auto hwModule = dyn_cast_or_null<hw::HWModuleOp>(
1831 symbolTable.lookupSymbolIn(module, getInstance().getModule()));
1833 return emitError(
"Referenced module doesn't exist ")
1834 << getInstance().getModule() <<
"::" << getInstance().getName();
1836 auto inst = findInstanceSymbolInBlock<hw::InstanceOp>(
1837 getInstance().
getName(), hwModule.getBodyBlock());
1839 return emitError(
"Referenced instance doesn't exist ")
1840 << getInstance().getModule() <<
"::" << getInstance().getName();
1841 if (!inst->getAttr(
"doNotPrint"))
1842 return emitError(
"Referenced instance isn't marked as doNotPrint");
1846 void BindOp::build(OpBuilder &
builder, OperationState &odsState, StringAttr mod,
1849 odsState.addAttribute(
"instance", ref);
1856 sv::InterfaceInstanceOp
1857 BindInterfaceOp::getReferencedInstance(
const hw::HWSymbolCache *cache) {
1860 auto result = cache->getInnerDefinition(getInstance());
1861 return cast<sv::InterfaceInstanceOp>(result.getOp());
1865 auto *symbolTable = SymbolTable::getNearestSymbolTable(*
this);
1874 return findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
1875 getInstance().
getName(), &parentOp->getRegion(0).front());
1880 BindInterfaceOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1882 symbolTable.lookupNearestSymbolFrom(*
this, getInstance().getModule());
1884 return emitError(
"Referenced module doesn't exist ")
1885 << getInstance().getModule() <<
"::" << getInstance().getName();
1887 auto inst = findInstanceSymbolInBlock<sv::InterfaceInstanceOp>(
1888 getInstance().
getName(), &parentOp->getRegion(0).front());
1890 return emitError(
"Referenced interface doesn't exist ")
1891 << getInstance().getModule() <<
"::" << getInstance().getName();
1892 if (!inst->getAttr(
"doNotPrint"))
1893 return emitError(
"Referenced interface isn't marked as doNotPrint");
1902 StringAttr &terminalAttr) {
1903 SmallVector<Attribute> strings;
1904 ParseResult ret = parser.parseCommaSeparatedList([&]() {
1907 if (succeeded(parser.parseOptionalKeyword(&keyword))) {
1908 strings.push_back(parser.getBuilder().getStringAttr(keyword));
1911 if (succeeded(parser.parseAttribute(
1912 result, parser.getBuilder().getType<NoneType>()))) {
1913 strings.push_back(result);
1918 if (succeeded(ret)) {
1919 pathAttr = parser.getBuilder().getArrayAttr(
1920 ArrayRef<Attribute>(strings).drop_back());
1921 terminalAttr = (*strings.rbegin()).cast<StringAttr>();
1927 StringAttr terminalAttr) {
1928 llvm::interleaveComma(pathAttr, p);
1929 p <<
", " << terminalAttr;
1933 LogicalResult XMRRefOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1934 auto *table = SymbolTable::getNearestSymbolTable(*
this);
1935 auto path = dyn_cast_or_null<hw::HierPathOp>(
1936 symbolTable.lookupSymbolIn(table, getRefAttr()));
1938 return emitError(
"Referenced path doesn't exist ") << getRefAttr();
1943 hw::HierPathOp XMRRefOp::getReferencedPath(
const hw::HWSymbolCache *cache) {
1945 if (
auto *result = cache->getDefinition(getRefAttr().
getAttr()))
1946 return cast<hw::HierPathOp>(result);
1948 auto topLevelModuleOp = (*this)->getParentOfType<ModuleOp>();
1949 return topLevelModuleOp.lookupSymbol<hw::HierPathOp>(getRefAttr().getValue());
1957 PatternRewriter &rewriter,
1960 if (constant.getValue().isZero() == eraseIfZero) {
1961 rewriter.eraseOp(op);
1968 template <
class Op,
bool EraseIfZero = false>
1970 PatternRewriter &rewriter) {
1974 void AssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
1975 MLIRContext *context) {
1976 results.add(canonicalizeImmediateVerifOp<AssertOp>);
1979 void AssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
1980 MLIRContext *context) {
1981 results.add(canonicalizeImmediateVerifOp<AssumeOp>);
1984 void CoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
1985 MLIRContext *context) {
1986 results.add(canonicalizeImmediateVerifOp<CoverOp, /* EraseIfZero = */ true>);
1989 template <
class Op,
bool EraseIfZero = false>
1991 PatternRewriter &rewriter) {
1995 void AssertConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
1996 MLIRContext *context) {
1997 results.add(canonicalizeConcurrentVerifOp<AssertConcurrentOp>);
2000 void AssumeConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2001 MLIRContext *context) {
2002 results.add(canonicalizeConcurrentVerifOp<AssumeConcurrentOp>);
2005 void CoverConcurrentOp::getCanonicalizationPatterns(RewritePatternSet &results,
2006 MLIRContext *context) {
2008 canonicalizeConcurrentVerifOp<CoverConcurrentOp, /* EraseIfZero */ true>);
2018 ArrayAttr &caseNamesArray,
2019 SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
2021 SmallVector<Attribute> names;
2022 while (!p.parseOptionalKeyword(
"case")) {
2025 std::unique_ptr<Region> region = std::make_unique<Region>();
2026 if (p.parseLParen() || p.parseAttribute(pattern) || p.parseComma() ||
2027 p.parseAttribute(name) || p.parseRParen() || p.parseRegion(*region))
2030 names.push_back(name);
2031 if (region->empty())
2032 region->push_back(
new Block());
2033 caseRegions.push_back(std::move(region));
2035 patternsArray = p.getBuilder().getArrayAttr(
patterns);
2036 caseNamesArray = p.getBuilder().getArrayAttr(names);
2043 ArrayAttr namesArray,
2044 MutableArrayRef<Region> caseRegions) {
2045 assert(patternsArray.size() == caseRegions.size());
2046 assert(patternsArray.size() == namesArray.size());
2047 for (
size_t i = 0, e = caseRegions.size(); i < e; ++i) {
2049 p <<
"case (" << patternsArray[i] <<
", " << namesArray[i] <<
") ";
2050 p.printRegion(caseRegions[i]);
2055 LogicalResult GenerateCaseOp::verify() {
2056 size_t numPatterns = getCasePatterns().size();
2057 if (getCaseRegions().size() != numPatterns ||
2058 getCaseNames().size() != numPatterns)
2060 "Size of caseRegions, patterns, and caseNames must match");
2062 StringSet<> usedNames;
2063 for (Attribute name : getCaseNames()) {
2064 StringAttr nameStr = name.dyn_cast<StringAttr>();
2066 return emitOpError(
"caseNames must all be string attributes");
2067 if (usedNames.contains(nameStr.getValue()))
2068 return emitOpError(
"caseNames must be unique");
2069 usedNames.insert(nameStr.getValue());
2078 ModportDirection direction,
2079 FlatSymbolRefAttr signal) {
2088 #define GET_OP_CLASSES
2089 #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 verifyMacroSymbolUse(Operation *op, StringAttr name, SymbolTableCollection &symbolTable)
Ensure that the symbol being instantiated exists and is a MacroDeclOp.
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 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.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn