231 bool emitWireAtBlockBegin =
false) {
232 Block *block = op.getBlock();
233 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
234 bool isProceduralRegion = op.getParentOp()->hasTrait<ProceduralRegion>();
236 auto createWireForResult = [&](Value result, StringAttr name) {
238 Type wireElementType = result.getType();
239 bool isResultInOut =
false;
242 if (
auto inoutType = hw::type_dyn_cast<hw::InOutType>(result.getType())) {
243 wireElementType = inoutType.getElementType();
244 isResultInOut =
true;
248 if (isProceduralRegion)
249 newWire = builder.create<LogicOp>(wireElementType, name);
251 newWire = builder.create<
sv::WireOp>(wireElementType, name);
254 while (!result.use_empty()) {
255 OpOperand &use = *result.getUses().begin();
260 use.set(newWireRead);
261 newWireRead->moveBefore(use.getOwner());
272 if (isProceduralRegion)
273 connect = builder.create<BPAssignOp>(
274 newWire, isResultInOut ? resultRead.getResult() : result);
277 newWire, isResultInOut ? resultRead.getResult() : result);
279 connect->moveAfter(&op);
281 resultRead->moveBefore(connect);
284 if (!emitWireAtBlockBegin) {
290 newWire.getDefiningOp()->moveAfter(&op);
296 if (op.getNumResults() == 1) {
298 op.removeAttr(
"sv.namehint");
299 createWireForResult(op.getResult(0), namehint);
304 for (
auto result : op.getResults())
305 createWireForResult(result, StringAttr());
847 ArrayRef<WireLowering> wireLowerings) {
848 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
850 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings) {
852 ImplicitLocOpBuilder builder(hwWireOp.getLoc(), &block, declarePoint);
854 if (isProceduralRegion) {
855 decl = builder.create<LogicOp>(hwWireOp.getType(), hwWireOp.getNameAttr(),
856 hwWireOp.getInnerSymAttr());
859 builder.create<
sv::WireOp>(hwWireOp.getType(), hwWireOp.getNameAttr(),
860 hwWireOp.getInnerSymAttr());
864 auto defaultAttrNames = hwWireOp.getAttributeNames();
865 for (
auto namedAttr : hwWireOp->getAttrs())
866 if (!llvm::is_contained(defaultAttrNames, namedAttr.getName()))
867 decl.getDefiningOp()->setAttr(namedAttr.getName(),
868 namedAttr.getValue());
873 if (assignPoint != declarePoint)
874 builder.setInsertionPoint(&block, assignPoint);
875 if (isProceduralRegion)
876 builder.
create<BPAssignOp>(decl, hwWireOp.getInput());
878 builder.create<
AssignOp>(decl, hwWireOp.getInput());
884 if (assignPoint != declarePoint)
885 builder.setInsertionPointAfterValue(decl);
889 hwWireOp.replaceAllUsesWith(readOp.getResult());
892 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings)
903 for (
auto &op : block) {
905 for (
auto ®ion : op.getRegions()) {
916 SmallVector<WireLowering> wireLowerings;
924 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
928 DenseSet<Operation *> visitedAlwaysInlineOperations;
932 SmallVector<Operation *> debugOpsToMoveToEnd;
934 for (Block::iterator opIterator = block.begin(), e = block.end();
936 auto &op = *opIterator++;
938 if (!isa<CombDialect, SVDialect, HWDialect, ltl::LTLDialect,
939 verif::VerifDialect, debug::DebugDialect>(op.getDialect())) {
940 auto d = op.emitError() <<
"dialect \"" << op.getDialect()->getNamespace()
941 <<
"\" not supported for direct Verilog emission";
942 d.attachNote() <<
"ExportVerilog cannot emit this operation; it needs "
943 "to be lowered before running ExportVerilog";
948 if (isa<ltl::LTLDialect>(op.getDialect()))
952 if (isa<debug::DebugDialect>(op.getDialect())) {
953 debugOpsToMoveToEnd.push_back(&op);
962 if (
auto inst = dyn_cast<HWInstanceLike>(op)) {
971 if (
auto call = dyn_cast<mlir::CallOpInterface>(op))
976 if (isProceduralRegion && isa<LogicOp, RegOp>(op)) {
981 op.moveBefore(parentOp);
988 isProceduralRegion) {
994 if (!mlir::isMemoryEffectFree(&op)) {
1014 if (op.use_empty()) {
1020 if (visitedAlwaysInlineOperations.insert(&op).second)
1026 if (
auto aggregateConstantOp = dyn_cast<hw::AggregateConstantOp>(op);
1028 if (hw::StructType structType =
1029 type_dyn_cast<hw::StructType>(aggregateConstantOp.getType())) {
1031 SmallVector<Value> operands;
1032 ImplicitLocOpBuilder builder(op.getLoc(), op.getContext());
1033 builder.setInsertionPointAfter(&op);
1034 for (
auto [value, field] :
1035 llvm::zip(aggregateConstantOp.getFieldsAttr(),
1036 structType.getElements())) {
1037 if (
auto arrayAttr = dyn_cast<mlir::ArrayAttr>(value))
1039 builder.create<hw::AggregateConstantOp>(field.type, arrayAttr));
1042 field.type, cast<mlir::IntegerAttr>(value)));
1047 aggregateConstantOp.getResult().replaceAllUsesWith(structCreate);
1049 opIterator = std::next(op.getIterator());
1056 if (
auto structCreateOp = dyn_cast<hw::StructCreateOp>(op);
1061 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1062 hw::StructType structType =
1063 cast<hw::StructType>(structCreateOp.getResult().getType());
1064 bool procedural = op.getParentOp()->hasTrait<ProceduralRegion>();
1066 wireOp = builder.create<LogicOp>(structType);
1068 wireOp = builder.create<
sv::WireOp>(structType);
1070 for (
auto [input, field] :
1071 llvm::zip(structCreateOp.getInput(), structType.getElements())) {
1073 builder.
create<sv::StructFieldInOutOp>(wireOp, field.name);
1075 builder.create<BPAssignOp>(target, input);
1077 builder.create<
AssignOp>(target, input);
1081 llvm::make_early_inc_range(structCreateOp.getResult().getUses()))
1084 structCreateOp.erase();
1090 if (
auto arrayInjectOp = dyn_cast<hw::ArrayInjectOp>(op)) {
1091 bool procedural = op.getParentOp()->hasTrait<ProceduralRegion>();
1092 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1095 decl = builder.create<LogicOp>(arrayInjectOp.getType());
1097 decl = builder.create<
sv::RegOp>(arrayInjectOp.getType());
1098 for (
auto &use : llvm::make_early_inc_range(arrayInjectOp->getUses()))
1104 auto alwaysOp = builder.create<sv::AlwaysCombOp>();
1105 builder.setInsertionPointToStart(alwaysOp.getBodyBlock());
1109 builder.create<sv::BPAssignOp>(decl, arrayInjectOp.getInput());
1113 builder.create<sv::ArrayIndexInOutOp>(decl, arrayInjectOp.getIndex());
1114 builder.create<sv::BPAssignOp>(target, arrayInjectOp.getElement());
1116 arrayInjectOp.erase();
1124 IndexedPartSelectInOutOp, IndexedPartSelectOp>(&op)) {
1129 if (
auto maybeReadOp =
1131 if (isa_and_nonnull<sv::WireOp, LogicOp>(
1132 maybeReadOp.getInput().getDefiningOp())) {
1133 wireOp = maybeReadOp.getInput();
1134 readOp = maybeReadOp;
1139 ImplicitLocOpBuilder builder(op.getLoc(), &op);
1141 auto type = op.getOperand(1).getType();
1142 const auto *name =
"_GEN_ARRAY_IDX";
1143 if (op.getParentOp()->hasTrait<ProceduralRegion>()) {
1144 wireOp = builder.create<LogicOp>(type, name);
1145 builder.create<BPAssignOp>(wireOp, op.getOperand(1));
1147 wireOp = builder.create<
sv::WireOp>(type, name);
1152 op.setOperand(1, readOp);
1155 sv::addSVAttributes(wireOp.getDefiningOp(),
1156 SVAttributeAttr::get(wireOp.getContext(),
"keep",
1170 if (!isProceduralRegion ||
1174 if (!op.getParentOp()->hasTrait<ProceduralRegion>())
1181 if (isProceduralRegion)
1198 if (op.getNumOperands() > 2 && op.getNumResults() == 1 &&
1199 op.hasTrait<mlir::OpTrait::IsCommutative>() &&
1200 mlir::isMemoryEffectFree(&op) && op.getNumRegions() == 0 &&
1201 op.getNumSuccessors() == 0 &&
1202 llvm::all_of(op.getAttrs(), [](NamedAttribute attr) {
1203 return attr.getNameDialect() != nullptr ||
1204 attr.getName() ==
"twoState";
1207 SmallVector<Operation *> newOps;
1209 op.getResult(0).replaceAllUsesWith(result);
1213 opIterator = Block::iterator(newOps.front());
1218 if (
auto addOp = dyn_cast<comb::AddOp>(op)) {
1219 if (
auto cst = addOp.getOperand(1).getDefiningOp<
hw::ConstantOp>()) {
1220 assert(addOp.getNumOperands() == 2 &&
"commutative lowering is done");
1221 if (cst.getValue().isNegative()) {
1223 opIterator = Block::iterator(firstOp);
1231 if (
auto structExplodeOp = dyn_cast<hw::StructExplodeOp>(op)) {
1233 opIterator = Block::iterator(firstOp);
1245 auto debugBuilder = OpBuilder::atBlockEnd(&block);
1246 if (!block.empty() && block.back().mightHaveTrait<OpTrait::IsTerminator>())
1247 debugBuilder.setInsertionPoint(&block.back());
1248 for (
auto *op : debugOpsToMoveToEnd) {
1250 debugBuilder.insert(op);
1253 if (isProceduralRegion) {
1262 std::pair<Block *, Block::iterator> logicOpInsertionPoint =
1264 for (
auto &op : llvm::make_early_inc_range(block)) {
1265 if (
auto logic = dyn_cast<LogicOp>(&op)) {
1268 if (logicOpInsertionPoint.second == logic->getIterator()) {
1269 ++logicOpInsertionPoint.second;
1273 logic->moveBefore(logicOpInsertionPoint.first,
1274 logicOpInsertionPoint.second);
1284 SmallPtrSet<Operation *, 32> seenOperations;
1286 for (
auto &op : llvm::make_early_inc_range(block)) {
1289 if (isa<ltl::LTLDialect, debug::DebugDialect>(op.getDialect()))
1295 bool haveAnyOutOfOrderUses =
false;
1296 for (
auto *userOp : op.getUsers()) {
1299 while (&block != &userOp->getParentRegion()->front())
1300 userOp = userOp->getParentOp();
1302 if (seenOperations.count(userOp)) {
1303 haveAnyOutOfOrderUses =
true;
1309 seenOperations.insert(&op);
1312 if (!haveAnyOutOfOrderUses)
1318 op.moveBefore(&block.front());
1324 op.moveBefore(&block.front());
1330 if (
auto readInOut = dyn_cast<ReadInOutOp>(op)) {
1331 auto *def = readInOut.getInput().getDefiningOp();
1333 op.moveBefore(&block.front());
1334 def->moveBefore(&block.front());