20 #include "../PassDetail.h"
26 #include "mlir/IR/ImplicitLocOpBuilder.h"
27 #include "llvm/ADT/DenseSet.h"
28 #include "llvm/ADT/SmallPtrSet.h"
29 #include "llvm/ADT/TypeSwitch.h"
30 #include "llvm/Support/CommandLine.h"
31 #include "llvm/Support/Debug.h"
33 #define DEBUG_TYPE "prepare-for-emission"
35 using namespace circt;
39 using namespace ExportVerilog;
43 if (v.isa<BlockArgument>())
45 auto vOp = v.getDefiningOp();
50 auto read = dyn_cast<ReadInOutOp>(vOp);
53 auto readSrc = read.getInput().getDefiningOp();
56 return isa<sv::WireOp, RegOp, LogicOp, XMROp, XMRRefOp>(readSrc);
70 Block *block = op->getParentOfType<HWModuleOp>().getBodyBlock();
71 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
73 SmallString<32> nameTmp{
"_", op.getInstanceName(),
"_"};
74 auto namePrefixSize = nameTmp.size();
77 auto ports = op.getPortList();
78 for (
auto &port : ports.getInputs()) {
79 auto src = op.getOperand(nextOpNo);
85 nameTmp.resize(namePrefixSize);
87 nameTmp += port.name.getValue().str();
89 nameTmp += std::to_string(nextOpNo - 1);
91 auto newWire =
builder.create<sv::WireOp>(src.getType(), nameTmp);
92 auto newWireRead =
builder.create<ReadInOutOp>(newWire);
94 newWireRead->moveBefore(op);
96 op.setOperand(nextOpNo - 1, newWireRead);
102 Block *block = op->getParentOfType<HWModuleOp>().getBodyBlock();
103 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
105 SmallString<32> nameTmp{
"_", op.getInstanceName(),
"_"};
106 auto namePrefixSize = nameTmp.size();
108 size_t nextResultNo = 0;
109 auto ports = op.getPortList();
110 for (
auto &port : ports.getOutputs()) {
111 auto result = op.getResult(nextResultNo);
121 if (result.hasOneUse()) {
122 OpOperand &use = *result.getUses().begin();
123 if (dyn_cast_or_null<OutputOp>(use.getOwner()))
125 if (
auto assign = dyn_cast_or_null<AssignOp>(use.getOwner())) {
127 assign->moveAfter(op);
132 nameTmp.resize(namePrefixSize);
134 nameTmp += port.name.getValue().str();
136 nameTmp += std::to_string(nextResultNo - 1);
137 Value newWire =
builder.create<sv::WireOp>(result.getType(), nameTmp);
139 while (!result.use_empty()) {
140 auto newWireRead =
builder.create<ReadInOutOp>(newWire);
141 OpOperand &use = *result.getUses().begin();
142 use.set(newWireRead);
143 newWireRead->moveBefore(use.getOwner());
158 bool emitWireAtBlockBegin =
false) {
159 Block *block = op.getBlock();
160 auto builder = ImplicitLocOpBuilder::atBlockBegin(op.getLoc(), block);
161 bool isProceduralRegion = op.getParentOp()->hasTrait<ProceduralRegion>();
163 auto createWireForResult = [&](Value result, StringAttr name) {
166 if (isProceduralRegion)
167 newWire =
builder.create<LogicOp>(result.getType(), name);
169 newWire =
builder.create<sv::WireOp>(result.getType(), name);
171 while (!result.use_empty()) {
172 auto newWireRead =
builder.create<ReadInOutOp>(newWire);
173 OpOperand &use = *result.getUses().begin();
174 use.set(newWireRead);
175 newWireRead->moveBefore(use.getOwner());
179 if (isProceduralRegion)
186 if (!emitWireAtBlockBegin) {
192 newWire.getDefiningOp()->moveAfter(&op);
198 if (op.getNumResults() == 1) {
200 op.removeAttr(
"sv.namehint");
201 createWireForResult(op.getResult(0), namehint);
206 for (
auto result : op.getResults())
207 createWireForResult(result, StringAttr());
214 assert(op->getNumResults() == 1 &&
215 "only support 'always inline' ops with one result");
221 auto recursivelyHandleOperands = [&](Operation *op) {
222 for (
auto operand : op->getOperands()) {
223 if (
auto *operandOp = operand.getDefiningOp()) {
237 auto skipToWireImmediatelyBefore = [](Operation *user) {
238 if (!isa<BPAssignOp, AssignOp>(user))
240 auto *wireOp = user->getOperand(0).getDefiningOp();
241 if (wireOp && wireOp->getNextNode() == user)
248 while (!op->hasOneUse()) {
249 OpOperand &use = *op->getUses().begin();
250 Operation *user = skipToWireImmediatelyBefore(use.getOwner());
253 auto *newOp = op->clone();
254 user->getBlock()->getOperations().insert(Block::iterator(user), newOp);
256 use.set(newOp->getResult(0));
260 recursivelyHandleOperands(newOp);
265 Operation *user = skipToWireImmediatelyBefore(*op->getUsers().begin());
266 op->moveBefore(user);
270 recursivelyHandleOperands(op);
277 static std::pair<Block *, Block::iterator>
280 if (isa<IfDefProceduralOp>(op->getParentOp()))
282 return {op->getBlock(), op->getBlock()->begin()};
289 SmallVector<Operation *> &newOps) {
291 auto name = op.getAttr(
"sv.namehint");
293 op.removeAttr(
"sv.namehint");
295 switch (operands.size()) {
297 assert(0 &&
"cannot be called with empty operand range");
306 auto firstHalf = operands.size() / 2;
312 OperationState state(op.getLoc(), op.getName());
313 state.addOperands(ValueRange{lhs, rhs});
314 state.addTypes(op.getResult(0).getType());
315 auto *newOp = Operation::create(state);
316 op.getBlock()->getOperations().insert(Block::iterator(&op), newOp);
317 newOps.push_back(newOp);
319 newOp->setAttr(
"sv.namehint", name);
320 if (
auto twoState = op.getAttr(
"twoState"))
321 newOp->setAttr(
"twoState", twoState);
322 return newOp->getResult(0);
328 hw::ConstantOp rhsCst) {
329 ImplicitLocOpBuilder
builder(add.getLoc(), add);
332 auto negCst =
builder.create<hw::ConstantOp>(-rhsCst.getValue());
334 builder.create<comb::SubOp>(add.getOperand(0), negCst, add.getTwoState());
335 add.getResult().replaceAllUsesWith(sub);
337 if (rhsCst.use_empty())
345 Operation *firstOp =
nullptr;
346 ImplicitLocOpBuilder
builder(op.getLoc(), op);
347 StructType structType = op.getInput().getType().cast<StructType>();
348 for (
auto [res, field] :
349 llvm::zip(op.getResults(), structType.getElements())) {
351 builder.create<hw::StructExtractOp>(op.getInput(), field.name);
354 res.replaceAllUsesWith(extract);
366 Operation *parentOp = op->getParentOp();
367 assert(parentOp->hasTrait<ProceduralRegion>() &&
368 "we should only be hoisting from procedural");
369 while (parentOp->getParentOp()->hasTrait<ProceduralRegion>())
370 parentOp = parentOp->getParentOp();
382 assert(op->getNumResults() == 1 &&
"isn't a verilog expression");
385 if (op->hasOneUse()) {
386 if (
auto assign = dyn_cast<BPAssignOp>(*op->user_begin()))
393 Value opValue = op->getResult(0);
398 auto reg =
builder.create<RegOp>(op->getLoc(), opValue.getType());
401 auto value =
builder.create<ReadInOutOp>(op->getLoc(),
reg);
402 opValue.replaceAllUsesWith(value);
406 builder.setInsertionPointAfter(op);
407 builder.create<BPAssignOp>(op->getLoc(),
reg, opValue);
419 !(isa<sv::ReadInOutOp>(op) ||
429 bool cantHoist =
false;
430 if (llvm::any_of(op->getOperands(), [&](Value operand) ->
bool {
436 if (BlockArgument block = operand.dyn_cast<BlockArgument>()) {
438 if (isa<hw::HWModuleOp>(block.getParentBlock()->getParentOp()))
444 Operation *operandOp = operand.getDefiningOp();
446 if (operandOp->getParentOp()->hasTrait<ProceduralRegion>()) {
447 cantHoist |= operandOp->getBlock() == op->getBlock();
460 parentOp = op->getParentOp();
463 op->moveBefore(parentOp);
469 return op->getNumResults() == 1 &&
470 op->getResult(0).getType().isa<
InOutType, sv::InterfaceType>() &&
471 op->getNumOperands() == 0;
488 :
public hw::TypeOpVisitor<EmittedExpressionStateManager,
489 EmittedExpressionState>,
490 public comb::CombinationalVisitor<EmittedExpressionStateManager,
491 EmittedExpressionState>,
492 public sv::Visitor<EmittedExpressionStateManager,
493 EmittedExpressionState> {
496 : options(options){};
501 bool dispatchHeuristic(Operation &op);
505 bool shouldSpillWireBasedOnState(Operation &op);
514 LLVM_DEBUG(op->emitWarning() <<
"unhandled by EmittedExpressionState");
515 if (op->getNumOperands() == 0)
517 return mergeOperandsStates(op);
520 return dispatchTypeOpVisitor(op);
523 return visitUnhandledExpr(op);
526 return dispatchSVVisitor(op);
529 return visitUnhandledExpr(op);
532 return visitUnhandledExpr(op);
535 using CombinationalVisitor::visitComb;
536 using Visitor::visitSV;
549 auto it = expressionStates.find(v);
550 if (it != expressionStates.end())
554 if (
auto blockArg = v.dyn_cast<BlockArgument>())
558 dispatchCombinationalVisitor(v.getDefiningOp());
559 expressionStates.insert({v, state});
566 for (
auto operand : op->getOperands())
567 state.
mergeState(getExpressionState(operand));
578 SmallVector<OpOperand *> uses;
581 for (OpOperand &use : op->getUses()) {
583 if (
auto assignUse = dyn_cast<AssignOp>(use.getOwner())) {
590 if (!isa<HWModuleOp>(assignUse->getParentOp()))
599 uses.push_back(&use);
603 if (!assign || uses.empty())
606 if (
auto *cop = assign.getSrc().getDefiningOp())
607 if (isa<ConstantOp>(cop))
611 ImplicitLocOpBuilder
builder(assign.getDest().getLoc(), op->getContext());
612 for (OpOperand *use : uses) {
613 builder.setInsertionPoint(use->getOwner());
614 auto read =
builder.create<ReadInOutOp>(assign.getDest());
617 if (
auto *destOp = assign.getDest().getDefiningOp())
625 if (options.isWireSpillingHeuristicEnabled(
627 if (
auto hint = op.getAttrOfType<StringAttr>(
"sv.namehint")) {
629 if (!hint.getValue().startswith(
"_"))
633 if (getExpressionState(op.getResult(0)).size >=
634 options.wireSpillingNamehintTermLimit)
646 if (op.getNumResults() == 0 ||
648 isa<ReadInOutOp, ConstantOp>(op))
653 if (op.hasOneUse()) {
654 auto *singleUser = *op.getUsers().begin();
655 if (isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp, hw::InstanceOp>(
661 if (singleUser->hasOneUse() && isa<hw::BitcastOp>(singleUser) &&
662 isa<hw::OutputOp, sv::AssignOp, sv::BPAssignOp>(
663 *singleUser->getUsers().begin()))
669 if (options.maximumNumberOfTermsPerExpression <
670 getExpressionState(op.getResult(0)).size)
672 return dispatchHeuristic(op);
681 if (block.getParentOp()->hasTrait<ProceduralRegion>())
684 for (
auto &op : llvm::make_early_inc_range(block)) {
693 for (
auto &op : block) {
695 for (
auto ®ion : op.getRegions()) {
714 SmallVectorImpl<WireLowering> &wireLowerings) {
715 for (
auto hwWireOp : block.getOps<hw::WireOp>()) {
719 Block::iterator assignPoint = block.begin();
720 if (
auto *defOp = hwWireOp.getInput().getDefiningOp())
721 if (defOp && defOp->getBlock() == &block)
722 assignPoint = ++Block::iterator(defOp);
727 Block::iterator declarePoint = assignPoint;
728 for (
auto *user : hwWireOp->getUsers()) {
729 while (user->getBlock() != &block)
730 user = user->getParentOp();
731 if (user->isBeforeInBlock(&*declarePoint))
732 declarePoint = Block::iterator(user);
735 wireLowerings.push_back({hwWireOp, declarePoint, assignPoint});
743 ArrayRef<WireLowering> wireLowerings) {
744 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
746 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings) {
748 ImplicitLocOpBuilder
builder(hwWireOp.getLoc(), &block, declarePoint);
750 if (isProceduralRegion) {
751 decl =
builder.create<LogicOp>(hwWireOp.getType(), hwWireOp.getNameAttr(),
752 hwWireOp.getInnerSymAttr());
755 builder.create<sv::WireOp>(hwWireOp.getType(), hwWireOp.getNameAttr(),
756 hwWireOp.getInnerSymAttr());
760 auto defaultAttrNames = hwWireOp.getAttributeNames();
761 for (
auto namedAttr : hwWireOp->getAttrs())
762 if (!llvm::is_contained(defaultAttrNames, namedAttr.getName()))
763 decl.getDefiningOp()->setAttr(namedAttr.getName(),
764 namedAttr.getValue());
769 if (assignPoint != declarePoint)
770 builder.setInsertionPoint(&block, assignPoint);
771 if (isProceduralRegion)
772 builder.create<BPAssignOp>(decl, hwWireOp.getInput());
774 builder.create<AssignOp>(decl, hwWireOp.getInput());
780 if (assignPoint != declarePoint)
781 builder.setInsertionPointAfterValue(decl);
782 auto readOp =
builder.create<sv::ReadInOutOp>(decl);
785 hwWireOp.replaceAllUsesWith(readOp.getResult());
788 for (
auto [hwWireOp, declarePoint, assignPoint] : wireLowerings)
799 for (
auto &op : block) {
801 for (
auto ®ion : op.getRegions()) {
812 SmallVector<WireLowering> wireLowerings;
820 bool isProceduralRegion = block.getParentOp()->hasTrait<ProceduralRegion>();
824 DenseSet<Operation *> visitedAlwaysInlineOperations;
826 for (Block::iterator opIterator = block.begin(), e = block.end();
828 auto &op = *opIterator++;
830 if (!isa<CombDialect, SVDialect, HWDialect, ltl::LTLDialect,
831 verif::VerifDialect>(op.getDialect())) {
832 auto d = op.emitError() <<
"dialect \"" << op.getDialect()->getNamespace()
833 <<
"\" not supported for direct Verilog emission";
834 d.attachNote() <<
"ExportVerilog cannot emit this operation; it needs "
835 "to be lowered before running ExportVerilog";
840 if (isa<ltl::LTLDialect>(op.getDialect()))
848 if (
auto instance = dyn_cast<InstanceOp>(op)) {
859 if (isProceduralRegion && isa<LogicOp>(op)) {
864 op.moveBefore(parentOp);
871 isProceduralRegion) {
877 if (!mlir::isMemoryEffectFree(&op)) {
897 if (op.use_empty()) {
903 if (visitedAlwaysInlineOperations.insert(&op).second)
909 if (
auto aggregateConstantOp = dyn_cast<hw::AggregateConstantOp>(op);
911 if (hw::StructType structType =
912 type_dyn_cast<hw::StructType>(aggregateConstantOp.getType())) {
914 SmallVector<Value> operands;
915 ImplicitLocOpBuilder
builder(op.getLoc(), op.getContext());
916 builder.setInsertionPointAfter(&op);
917 for (
auto [value, field] :
918 llvm::zip(aggregateConstantOp.getFieldsAttr(),
919 structType.getElements())) {
920 if (
auto arrayAttr = dyn_cast<mlir::ArrayAttr>(value))
922 builder.create<hw::AggregateConstantOp>(field.type, arrayAttr));
924 operands.push_back(
builder.create<hw::ConstantOp>(
925 field.type, cast<mlir::IntegerAttr>(value)));
929 builder.create<hw::StructCreateOp>(structType, operands);
930 aggregateConstantOp.getResult().replaceAllUsesWith(structCreate);
932 opIterator = std::next(op.getIterator());
939 if (
auto structCreateOp = dyn_cast<hw::StructCreateOp>(op);
944 ImplicitLocOpBuilder
builder(op.getLoc(), &op);
945 hw::StructType structType =
946 structCreateOp.getResult().getType().cast<hw::StructType>();
947 bool procedural = op.getParentOp()->hasTrait<ProceduralRegion>();
949 wireOp =
builder.create<LogicOp>(structType);
951 wireOp =
builder.create<sv::WireOp>(structType);
953 for (
auto [input, field] :
954 llvm::zip(structCreateOp.getInput(), structType.getElements())) {
956 builder.create<sv::StructFieldInOutOp>(wireOp, field.name);
958 builder.create<BPAssignOp>(target, input);
960 builder.create<AssignOp>(target, input);
964 llvm::make_early_inc_range(structCreateOp.getResult().getUses()))
965 use.set(
builder.create<ReadInOutOp>(wireOp));
967 structCreateOp.erase();
974 isa<ArraySliceOp, ArrayGetOp, ArrayIndexInOutOp,
975 IndexedPartSelectInOutOp, IndexedPartSelectOp>(&op)) {
980 if (
auto maybeReadOp =
981 op.getOperand(1).getDefiningOp<sv::ReadInOutOp>()) {
982 if (isa_and_nonnull<sv::WireOp, LogicOp>(
983 maybeReadOp.getInput().getDefiningOp())) {
984 wireOp = maybeReadOp.getInput();
985 readOp = maybeReadOp;
990 ImplicitLocOpBuilder
builder(op.getLoc(), &op);
992 auto type = op.getOperand(1).getType();
993 const auto *name =
"_GEN_ARRAY_IDX";
994 if (op.getParentOp()->hasTrait<ProceduralRegion>()) {
995 wireOp =
builder.create<LogicOp>(type, name);
996 builder.create<BPAssignOp>(wireOp, op.getOperand(1));
998 wireOp =
builder.create<sv::WireOp>(type, name);
999 builder.create<AssignOp>(wireOp, op.getOperand(1));
1001 readOp =
builder.create<ReadInOutOp>(wireOp);
1003 op.setOperand(1, readOp);
1021 if (!isProceduralRegion ||
1025 if (!op.getParentOp()->hasTrait<ProceduralRegion>())
1032 if (isProceduralRegion)
1049 if (op.getNumOperands() > 2 && op.getNumResults() == 1 &&
1050 op.hasTrait<mlir::OpTrait::IsCommutative>() &&
1051 mlir::isMemoryEffectFree(&op) && op.getNumRegions() == 0 &&
1052 op.getNumSuccessors() == 0 &&
1053 llvm::all_of(op.getAttrs(), [](NamedAttribute attr) {
1054 return attr.getNameDialect() != nullptr ||
1055 attr.getName() ==
"twoState";
1058 SmallVector<Operation *> newOps;
1060 op.getResult(0).replaceAllUsesWith(result);
1064 opIterator = Block::iterator(newOps.front());
1069 if (
auto addOp = dyn_cast<comb::AddOp>(op)) {
1070 if (
auto cst = addOp.getOperand(1).getDefiningOp<hw::ConstantOp>()) {
1071 assert(addOp.getNumOperands() == 2 &&
"commutative lowering is done");
1072 if (cst.getValue().isNegative()) {
1074 opIterator = Block::iterator(firstOp);
1082 if (
auto structExplodeOp = dyn_cast<hw::StructExplodeOp>(op)) {
1084 opIterator = Block::iterator(firstOp);
1095 if (isProceduralRegion) {
1104 std::pair<Block *, Block::iterator> logicOpInsertionPoint =
1106 for (
auto &op : llvm::make_early_inc_range(block)) {
1107 if (
auto logic = dyn_cast<LogicOp>(&op)) {
1110 if (logicOpInsertionPoint.second == logic->getIterator()) {
1111 ++logicOpInsertionPoint.second;
1115 logic->moveBefore(logicOpInsertionPoint.first,
1116 logicOpInsertionPoint.second);
1126 SmallPtrSet<Operation *, 32> seenOperations;
1128 for (
auto &op : llvm::make_early_inc_range(block)) {
1130 if (isa<ltl::LTLDialect>(op.getDialect()))
1136 bool haveAnyOutOfOrderUses =
false;
1137 for (
auto *userOp : op.getUsers()) {
1140 while (&block != &userOp->getParentRegion()->front())
1141 userOp = userOp->getParentOp();
1143 if (seenOperations.count(userOp)) {
1144 haveAnyOutOfOrderUses =
true;
1150 seenOperations.insert(&op);
1153 if (!haveAnyOutOfOrderUses)
1159 op.moveBefore(&block.front());
1165 op.moveBefore(&block.front());
1171 if (
auto readInOut = dyn_cast<ReadInOutOp>(op)) {
1172 auto *def = readInOut.getInput().getDefiningOp();
1174 op.moveBefore(&block.front());
1175 def->moveBefore(&block.front());
1205 struct PrepareForEmissionPass
1206 :
public PrepareForEmissionBase<PrepareForEmissionPass> {
1207 void runOnOperation()
override {
1208 HWModuleOp module = getOperation();
1211 signalPassFailure();
1218 return std::make_unique<PrepareForEmissionPass>();
assert(baseType &&"element must be base type")
static void lowerUsersToTemporaryWire(Operation &op, bool emitWireAtBlockBegin=false)
Emit an explicit wire or logic to assign operation's result.
static std::pair< Block *, Block::iterator > findLogicOpInsertionPoint(Operation *op)
static bool rewriteSideEffectingExpr(Operation *op)
This function is invoked on side effecting Verilog expressions when we're in 'disallowLocalVariables'...
static Operation * lowerStructExplodeOp(hw::StructExplodeOp op)
static void buildWireLowerings(Block &block, SmallVectorImpl< WireLowering > &wireLowerings)
Determine the insertion location of declaration and assignment ops for hw::WireOps in a block.
static void prettifyAfterLegalization(Block &block, EmittedExpressionStateManager &expressionStateManager)
After the legalization, we are able to know accurate verilog AST structures.
static bool hoistNonSideEffectExpr(Operation *op)
This function is called for non-side-effecting Verilog expressions when we're in 'disallowLocalVariab...
static Operation * findParentInNonProceduralRegion(Operation *op)
Given an operation in a procedural region, scan up the region tree to find the first operation in a g...
static Operation * rewriteAddWithNegativeConstant(comb::AddOp add, hw::ConstantOp rhsCst)
Transform "a + -cst" ==> "a - cst" for prettier output.
static void lowerAlwaysInlineOperation(Operation *op, const LoweringOptions &options)
static bool reuseExistingInOut(Operation *op, const LoweringOptions &options)
If exactly one use of this op is an assign, replace the other uses with a read from the assigned wire...
static void spillWiresForInstanceInputs(InstanceOp op)
static void lowerInstanceResults(InstanceOp op)
static bool isMovableDeclaration(Operation *op)
Check whether an op is a declaration that can be moved.
static LogicalResult legalizeHWModule(Block &block, const LoweringOptions &options)
For each module we emit, do a prepass over the structure, pre-lowering and otherwise rewriting operat...
static Value lowerFullyAssociativeOp(Operation &op, OperandRange operands, SmallVector< Operation * > &newOps)
Lower a variadic fully-associative operation into an expression tree.
static bool shouldSpillWire(Operation &op, const LoweringOptions &options)
static void applyWireLowerings(Block &block, ArrayRef< WireLowering > wireLowerings)
Materialize the SV wire declaration and assignment operations in the locations previously determined ...
static int64_t size(hw::ArrayType mType, capnp::schema::Field::Reader cField)
Returns the expected size of an array (capnp list) in 64-bit words.
This class handles information about AST structures of each expressions.
DenseMap< Value, EmittedExpressionState > expressionStates
bool dispatchHeuristic(Operation &op)
EmittedExpressionState visitUnhandledSV(Operation *op)
EmittedExpressionStateManager(const LoweringOptions &options)
EmittedExpressionState visitInvalidComb(Operation *op)
EmittedExpressionState visitUnhandledTypeOp(Operation *op)
EmittedExpressionState getExpressionState(Value value)
const LoweringOptions & options
EmittedExpressionState visitUnhandledComb(Operation *op)
EmittedExpressionState mergeOperandsStates(Operation *op)
EmittedExpressionState visitUnhandledExpr(Operation *op)
bool shouldSpillWireBasedOnState(Operation &op)
Return true if it is beneficial to spill the operation under the specified spilling heuristic.
EmittedExpressionState visitInvalidTypeOp(Operation *op)
def connect(destination, source)
LogicalResult prepareHWModule(Block &block, const LoweringOptions &options)
For each module we emit, do a prepass over the structure, pre-lowering and otherwise rewriting operat...
bool isExpressionEmittedInline(Operation *op, const LoweringOptions &options)
Return true if this expression should be emitted inline into any statement that uses it.
bool isVerilogExpression(Operation *op)
This predicate returns true if the specified operation is considered a potentially inlinable Verilog ...
StringAttr inferStructuralNameForTemporary(Value expr)
Given an expression that is spilled into a temporary wire, try to synthesize a better name than "_T_4...
static bool isConstantExpression(Operation *op)
Return whether an operation is a constant.
bool isZeroBitType(Type type)
Return true if this is a zero bit type, e.g.
void pruneZeroValuedLogic(hw::HWModuleOp module)
bool isSimpleReadOrPort(Value v)
Check if the value is from read of a wire or reg or is a port.
static bool isExpressionAlwaysInline(Operation *op)
Return true for operations that must always be inlined into a containing expression for correctness.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
circt::hw::InOutType InOutType
unsigned addSVAttributes(mlir::Operation *op, llvm::ArrayRef< SVAttributeAttr > attrs)
Add a list of SV attributes to an operation.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...
std::unique_ptr< mlir::Pass > createPrepareForEmissionPass()
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
static EmittedExpressionState getBaseState()
void mergeState(const EmittedExpressionState &state)
Block::iterator declarePoint
Block::iterator assignPoint
Options which control the emission from CIRCT to Verilog.
bool mitigateVivadoArrayIndexConstPropBug
If true, every expression used as an array index is driven by a wire, and the wire is marked as (* ke...
bool disallowLocalVariables
If true, do not emit SystemVerilog locally scoped "automatic" or logic declarations - emit top level ...
bool disallowExpressionInliningInPorts
If true, every expression passed to an instance port is driven by a wire.
@ SpillLargeTermsWithNamehints
bool disallowPackedStructAssignments
If true, eliminate packed struct assignments in favor of a wire + assignments to the individual field...