26 #include "mlir/IR/ImplicitLocOpBuilder.h"
27 #include "mlir/Pass/Pass.h"
28 #include "mlir/Pass/PassManager.h"
29 #include "mlir/Transforms/DialectConversion.h"
30 #include "llvm/ADT/TypeSwitch.h"
31 #include "llvm/Support/MathExtras.h"
36 #define GEN_PASS_DEF_DCTOHW
37 #include "circt/Conversion/Passes.h.inc"
41 using namespace circt;
49 auto *ctx = tuple.getContext();
50 mlir::SmallVector<hw::StructType::FieldInfo, 8> hwfields;
51 for (
auto [i,
innerType] : llvm::enumerate(tuple)) {
53 if (
auto tupleInnerType = dyn_cast<TupleType>(
innerType))
68 return TypeSwitch<Type, Type>(t)
70 .Case([](hw::StructType st) {
71 llvm::SmallVector<hw::StructType::FieldInfo> structFields(
73 for (
auto &field : structFields)
78 .Default([](Type t) {
return t; });
83 llvm::TypeSwitch<Type, Type>(t)
84 .Case([](ValueType vt) {
88 .Case([](TokenType tt) {
92 .Default([](
auto t) {
return toHWType(t); });
101 struct DCLoweringState {
102 ModuleOp parentModule;
109 class ESITypeConverter :
public TypeConverter {
112 addConversion([](Type type) -> Type {
return toESIHWType(type); });
114 addTargetMaterialization([](mlir::OpBuilder &builder, mlir::Type resultType,
115 mlir::ValueRange inputs,
116 mlir::Location loc) -> mlir::Value {
117 if (inputs.size() != 1)
121 .create<UnrealizedConversionCastOp>(loc, resultType, inputs[0])
125 addSourceMaterialization([](mlir::OpBuilder &builder, mlir::Type resultType,
126 mlir::ValueRange inputs,
127 mlir::Location loc) -> mlir::Value {
128 if (inputs.size() != 1)
132 .create<UnrealizedConversionCastOp>(loc, resultType, inputs[0])
148 struct InputHandshake {
151 std::optional<Backedge> ready;
157 struct OutputHandshake {
159 std::optional<Backedge> valid;
161 std::optional<Backedge>
data;
165 static void connect(InputHandshake &input, OutputHandshake &output) {
166 output.valid->setValue(input.valid);
167 input.ready->setValue(output.ready);
170 template <
typename T,
typename TInner>
171 llvm::SmallVector<T> extractValues(llvm::SmallVector<TInner> &container,
172 llvm::function_ref<T(TInner &)> extractor) {
173 llvm::SmallVector<T> result;
174 llvm::transform(container, std::back_inserter(result), extractor);
181 llvm::SmallVector<InputHandshake> inputs;
182 llvm::SmallVector<OutputHandshake> outputs;
184 llvm::SmallVector<Value> getInputValids() {
185 return extractValues<Value, InputHandshake>(
186 inputs, [](
auto &hs) {
return hs.valid; });
188 llvm::SmallVector<std::optional<Backedge>> getInputReadys() {
189 return extractValues<std::optional<Backedge>, InputHandshake>(
190 inputs, [](
auto &hs) {
return hs.ready; });
192 llvm::SmallVector<std::optional<Backedge>> getOutputValids() {
193 return extractValues<std::optional<Backedge>, OutputHandshake>(
194 outputs, [](
auto &hs) {
return hs.valid; });
196 llvm::SmallVector<Value> getInputDatas() {
197 return extractValues<Value, InputHandshake>(
198 inputs, [](
auto &hs) {
return hs.data; });
200 llvm::SmallVector<Value> getOutputReadys() {
201 return extractValues<Value, OutputHandshake>(
202 outputs, [](
auto &hs) {
return hs.ready; });
205 llvm::SmallVector<Value> getOutputChannels() {
206 return extractValues<Value, OutputHandshake>(
207 outputs, [](
auto &hs) {
return hs.channel; });
209 llvm::SmallVector<std::optional<Backedge>> getOutputDatas() {
210 return extractValues<std::optional<Backedge>, OutputHandshake>(
211 outputs, [](
auto &hs) {
return hs.data; });
219 RTLBuilder(Location loc, OpBuilder &builder, Value clk = Value(),
221 : b(builder), loc(loc),
clk(
clk), rst(rst) {}
223 Value constant(
const APInt &apv, StringRef name = {}) {
226 bool isZeroWidth = apv.getBitWidth() == 0;
228 auto it = constants.find(apv);
229 if (it != constants.end())
235 constants[apv] = cval;
239 Value constant(
unsigned width, int64_t value, StringRef name = {}) {
241 APInt(width, value,
false,
true));
243 std::pair<Value, Value>
wrap(Value data, Value valid, StringRef name = {}) {
244 auto wrapOp = b.create<esi::WrapValidReadyOp>(loc,
data, valid);
245 return {wrapOp.getResult(0), wrapOp.getResult(1)};
247 std::pair<Value, Value>
unwrap(Value channel, Value ready,
248 StringRef name = {}) {
249 auto unwrapOp = b.create<esi::UnwrapValidReadyOp>(loc, channel, ready);
250 return {unwrapOp.getResult(0), unwrapOp.getResult(1)};
254 Value
reg(StringRef name, Value in, Value rstValue, Value clk = Value(),
255 Value rst = Value()) {
256 Value resolvedClk =
clk ?
clk : this->
clk;
257 Value resolvedRst = rst ? rst : this->rst;
259 "No global clock provided to this RTLBuilder - a clock "
260 "signal must be provided to the reg(...) function.");
262 "No global reset provided to this RTLBuilder - a reset "
263 "signal must be provided to the reg(...) function.");
265 return b.create<
seq::CompRegOp>(loc, in, resolvedClk, resolvedRst, rstValue,
269 Value cmp(Value lhs, Value rhs, comb::ICmpPredicate predicate,
270 StringRef name = {}) {
271 return b.
create<comb::ICmpOp>(loc, predicate, lhs, rhs);
274 Value buildNamedOp(llvm::function_ref<Value()> f, StringRef name) {
277 Operation *op = v.getDefiningOp();
279 op->setAttr(
"sv.namehint", b.getStringAttr(name));
280 nameAttr = b.getStringAttr(name);
286 Value bitAnd(ValueRange values, StringRef name = {}) {
288 [&]() {
return b.create<
comb::AndOp>(loc, values,
false); }, name);
292 Value bitOr(ValueRange values, StringRef name = {}) {
294 [&]() {
return b.create<
comb::OrOp>(loc, values,
false); }, name);
298 Value bitNot(Value value, StringRef name = {}) {
299 auto allOnes = constant(value.getType().getIntOrFloatBitWidth(), -1);
300 std::string inferedName;
304 value.getDefiningOp()->getAttrOfType<StringAttr>(
"sv.namehint")) {
305 inferedName = (
"not_" +
valueName.getValue()).str();
311 [&]() {
return b.create<
comb::XorOp>(loc, value, allOnes); }, name);
314 Value shl(Value value, Value shift, StringRef name = {}) {
316 [&]() {
return b.create<
comb::ShlOp>(loc, value, shift); }, name);
319 Value
concat(ValueRange values, StringRef name = {}) {
320 return buildNamedOp([&]() {
return b.create<
comb::ConcatOp>(loc, values); },
324 llvm::SmallVector<Value>
extractBits(Value v, StringRef name = {}) {
325 llvm::SmallVector<Value> bits;
326 for (
unsigned i = 0, e = v.getType().getIntOrFloatBitWidth(); i != e; ++i)
332 Value reduceOr(Value v, StringRef name = {}) {
333 return buildNamedOp([&]() {
return bitOr(
extractBits(v)); }, name);
337 Value extract(Value v,
unsigned lo,
unsigned hi, StringRef name = {}) {
338 unsigned width = hi - lo + 1;
344 Value truncate(Value value,
unsigned width, StringRef name = {}) {
345 return extract(value, 0, width - 1, name);
348 Value zext(Value value,
unsigned outWidth, StringRef name = {}) {
349 unsigned inWidth = value.getType().getIntOrFloatBitWidth();
350 assert(inWidth <= outWidth &&
"zext: input width must be <= output width.");
351 if (inWidth == outWidth)
353 auto c0 = constant(outWidth - inWidth, 0);
354 return concat({c0, value}, name);
357 Value sext(Value value,
unsigned outWidth, StringRef name = {}) {
362 Value bit(Value v,
unsigned index, StringRef name = {}) {
363 return extract(v, index, index, name);
367 Value arrayCreate(ValueRange values, StringRef name = {}) {
373 Value arrayGet(Value array, Value index, StringRef name = {}) {
375 [&]() {
return b.create<
hw::ArrayGetOp>(loc, array, index); }, name);
381 Value mux(Value index, ValueRange values, StringRef name = {}) {
382 if (values.size() == 2) {
385 return b.create<
comb::MuxOp>(loc, index, values[1], values[0]);
389 return arrayGet(arrayCreate(values), index, name);
394 Value oneHotMux(Value index, ValueRange inputs) {
396 unsigned numInputs = inputs.size();
397 assert(numInputs == index.getType().getIntOrFloatBitWidth() &&
398 "mismatch between width of one-hot select input and the number of "
399 "inputs to be selected");
402 auto dataType = inputs[0].getType();
404 isa<NoneType>(dataType) ? 0 : dataType.getIntOrFloatBitWidth();
405 Value muxValue = constant(width, 0);
408 for (
size_t i = numInputs - 1; i != 0; --i) {
409 Value input = inputs[i];
410 Value selectBit = bit(index, i);
411 muxValue = mux(selectBit, {muxValue, input});
420 DenseMap<APInt, Value> constants;
423 static bool isZeroWidthType(Type type) {
424 if (
auto intType = dyn_cast<IntegerType>(type))
425 return intType.getWidth() == 0;
426 return isa<NoneType>(type);
429 static UnwrappedIO unwrapIO(Location loc, ValueRange operands,
431 ConversionPatternRewriter &rewriter,
433 RTLBuilder rtlb(loc, rewriter);
434 UnwrappedIO unwrapped;
435 for (
auto in : operands) {
436 assert(isa<esi::ChannelType>(in.getType()));
437 auto ready = bb.
get(rtlb.b.getI1Type());
438 auto [
data, valid] = rtlb.unwrap(in, ready);
439 unwrapped.inputs.push_back(InputHandshake{in, valid, ready,
data});
441 for (
auto outputType : results) {
450 rewriter.create<
hw::ConstantOp>(loc, rewriter.getIntegerType(0), 0);
454 hs.data = dataBackedge;
457 auto valid = bb.
get(rewriter.getI1Type());
458 auto [dataCh, ready] = rtlb.wrap(data, valid);
462 unwrapped.outputs.push_back(hs);
467 static UnwrappedIO unwrapIO(Operation *op, ValueRange operands,
468 ConversionPatternRewriter &rewriter,
470 return unwrapIO(op->getLoc(), operands, op->getResultTypes(), rewriter, bb);
475 static FailureOr<std::pair<Value, Value>> getClockAndReset(Operation *op) {
476 auto *parent = op->getParentOp();
477 auto parentFuncOp = dyn_cast<HWModuleLike>(parent);
479 return parent->emitOpError(
"parent op does not implement HWModuleLike");
481 auto argAttrs = parentFuncOp.getAllInputAttrs();
483 std::optional<size_t> clockIdx, resetIdx;
485 for (
auto [idx, battrs] : llvm::enumerate(argAttrs)) {
486 auto attrs = cast<DictionaryAttr>(battrs);
487 if (attrs.get(
"dc.clock")) {
489 return parent->emitOpError(
490 "multiple arguments contains a 'dc.clock' attribute");
494 if (attrs.get(
"dc.reset")) {
496 return parent->emitOpError(
497 "multiple arguments contains a 'dc.reset' attribute");
503 return parent->emitOpError(
"no argument contains a 'dc.clock' attribute");
506 return parent->emitOpError(
"no argument contains a 'dc.reset' attribute");
508 return {std::make_pair(parentFuncOp.getArgumentForInput(*clockIdx),
509 parentFuncOp.getArgumentForInput(*resetIdx))};
516 matchAndRewrite(ForkOp op, OpAdaptor operands,
517 ConversionPatternRewriter &rewriter)
const override {
519 auto crRes = getClockAndReset(op);
522 auto [clock, reset] = *crRes;
523 RTLBuilder rtlb(op.getLoc(), rewriter, clock, reset);
524 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
526 auto &input = io.inputs[0];
528 Value c0I1 = rtlb.constant(1, 0);
529 llvm::SmallVector<Value> doneWires;
530 for (
auto [i, output] : llvm::enumerate(io.outputs)) {
532 Value emitted = rtlb.bitAnd({doneBE, rtlb.bitNot(*input.ready)});
534 rtlb.reg(
"emitted_" + std::to_string(i), emitted, c0I1);
535 Value outValid = rtlb.bitAnd({rtlb.bitNot(emittedReg), input.valid});
536 output.valid->setValue(outValid);
537 Value validReady = rtlb.bitAnd({output.ready, outValid});
539 rtlb.bitOr({validReady, emittedReg},
"done" + std::to_string(i));
541 doneWires.push_back(done);
543 input.ready->setValue(rtlb.bitAnd(doneWires,
"allDone"));
545 rewriter.replaceOp(op, io.getOutputChannels());
555 matchAndRewrite(JoinOp op, OpAdaptor operands,
556 ConversionPatternRewriter &rewriter)
const override {
558 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
559 RTLBuilder rtlb(op.getLoc(), rewriter);
560 auto &output = io.outputs[0];
562 Value allValid = rtlb.bitAnd(io.getInputValids());
563 output.valid->setValue(allValid);
565 auto validAndReady = rtlb.bitAnd({output.ready, allValid});
566 for (
auto &input : io.inputs)
567 input.ready->setValue(validAndReady);
569 rewriter.replaceOp(op, io.outputs[0].channel);
579 matchAndRewrite(SelectOp op, OpAdaptor operands,
580 ConversionPatternRewriter &rewriter)
const override {
582 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
583 RTLBuilder rtlb(op.getLoc(), rewriter);
586 auto select = io.inputs[0];
587 io.inputs.erase(io.inputs.begin());
588 buildMuxLogic(rtlb, io, select);
590 rewriter.replaceOp(op, io.outputs[0].channel);
597 void buildMuxLogic(RTLBuilder &rtlb, UnwrappedIO &unwrapped,
598 InputHandshake &select)
const {
601 size_t numInputs = unwrapped.inputs.size();
602 size_t selectWidth = llvm::Log2_64_Ceil(numInputs);
603 Value truncatedSelect =
604 select.data.getType().getIntOrFloatBitWidth() > selectWidth
605 ? rtlb.truncate(select.data, selectWidth)
609 auto selectZext = rtlb.zext(truncatedSelect, numInputs);
610 auto select1h = rtlb.shl(rtlb.constant(numInputs, 1), selectZext);
611 auto &res = unwrapped.outputs[0];
614 auto selectedInputValid =
615 rtlb.mux(truncatedSelect, unwrapped.getInputValids());
617 auto selAndInputValid = rtlb.bitAnd({selectedInputValid, select.valid});
618 res.valid->setValue(selAndInputValid);
619 auto resValidAndReady = rtlb.bitAnd({selAndInputValid, res.ready});
622 select.ready->setValue(resValidAndReady);
625 for (
auto [inIdx, in] : llvm::enumerate(unwrapped.inputs)) {
627 auto isSelected = rtlb.bit(select1h, inIdx);
631 auto activeAndResultValidAndReady =
632 rtlb.bitAnd({isSelected, resValidAndReady});
633 in.ready->setValue(activeAndResultValidAndReady);
640 using OpConversionPattern::OpConversionPattern;
642 matchAndRewrite(BranchOp op, OpAdaptor operands,
643 ConversionPatternRewriter &rewriter)
const override {
645 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
646 RTLBuilder rtlb(op.getLoc(), rewriter);
647 auto cond = io.inputs[0];
648 auto trueRes = io.outputs[0];
649 auto falseRes = io.outputs[1];
652 trueRes.valid->setValue(rtlb.bitAnd({cond.data, cond.valid}));
653 falseRes.valid->setValue(rtlb.bitAnd({rtlb.bitNot(cond.data), cond.valid}));
656 Value selectedResultReady =
657 rtlb.mux(cond.data, {falseRes.ready, trueRes.ready});
658 Value condReady = rtlb.bitAnd({selectedResultReady, cond.valid});
659 cond.ready->setValue(condReady);
661 rewriter.replaceOp(op,
662 SmallVector<Value>{trueRes.channel, falseRes.channel});
669 using OpConversionPattern::OpConversionPattern;
671 matchAndRewrite(MergeOp op, OpAdaptor operands,
672 ConversionPatternRewriter &rewriter)
const override {
674 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
675 auto output = io.outputs[0];
676 RTLBuilder rtlb(op.getLoc(), rewriter);
679 Value hasWin = rtlb.bitOr(io.getInputValids());
685 Value winWasFirst = io.inputs[0].valid;
686 Value winWasSecond = rtlb.bitNot(winWasFirst);
687 Value winIndex = winWasSecond;
689 output.valid->setValue(hasWin);
690 output.data->setValue(winIndex);
694 Value outValidAndReady = rtlb.bitAnd({hasWin, output.ready});
698 io.inputs[0].ready->setValue(rtlb.bitAnd({outValidAndReady, winWasFirst}));
699 io.inputs[1].ready->setValue(rtlb.bitAnd({outValidAndReady, winWasSecond}));
701 rewriter.replaceOp(op, output.channel);
711 using OpConversionPattern::OpConversionPattern;
714 matchAndRewrite(ToESIOp op, OpAdaptor operands,
715 ConversionPatternRewriter &rewriter)
const override {
716 rewriter.replaceOp(op, operands.getOperands());
725 using OpConversionPattern::OpConversionPattern;
728 matchAndRewrite(FromESIOp op, OpAdaptor operands,
729 ConversionPatternRewriter &rewriter)
const override {
730 rewriter.replaceOp(op, operands.getOperands());
740 matchAndRewrite(SinkOp op, OpAdaptor operands,
741 ConversionPatternRewriter &rewriter)
const override {
743 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
744 io.inputs[0].ready->setValue(
745 RTLBuilder(op.getLoc(), rewriter).constant(1, 1));
746 rewriter.eraseOp(op);
755 matchAndRewrite(SourceOp op, OpAdaptor operands,
756 ConversionPatternRewriter &rewriter)
const override {
758 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
759 RTLBuilder rtlb(op.getLoc(), rewriter);
760 io.outputs[0].valid->setValue(rtlb.constant(1, 1));
761 rewriter.replaceOp(op, io.outputs[0].channel);
770 matchAndRewrite(PackOp op, OpAdaptor operands,
771 ConversionPatternRewriter &rewriter)
const override {
773 UnwrappedIO io = unwrapIO(op, llvm::SmallVector<Value>{operands.getToken()},
775 RTLBuilder rtlb(op.getLoc(), rewriter);
776 auto &input = io.inputs[0];
777 auto &output = io.outputs[0];
778 output.data->setValue(operands.getInput());
780 rewriter.replaceOp(op, output.channel);
789 matchAndRewrite(UnpackOp op, OpAdaptor operands,
790 ConversionPatternRewriter &rewriter)
const override {
792 UnwrappedIO io = unwrapIO(
793 op.getLoc(), llvm::SmallVector<Value>{operands.getInput()},
795 llvm::SmallVector<Type>{op.getToken().getType()}, rewriter, bb);
796 RTLBuilder rtlb(op.getLoc(), rewriter);
797 auto &input = io.inputs[0];
798 auto &output = io.outputs[0];
800 llvm::SmallVector<Value> unpackedValues;
801 unpackedValues.push_back(input.data);
804 llvm::SmallVector<Value> outputs;
805 outputs.push_back(output.channel);
806 outputs.append(unpackedValues.begin(), unpackedValues.end());
807 rewriter.replaceOp(op, outputs);
817 matchAndRewrite(BufferOp op, OpAdaptor operands,
818 ConversionPatternRewriter &rewriter)
const override {
819 if (op.getInitValuesAttr())
820 return rewriter.notifyMatchFailure(
821 op,
"BufferOp with initial values not supported");
823 auto crRes = getClockAndReset(op);
826 auto [clock, reset] = *crRes;
830 Type channelType = operands.getInput().getType();
831 rewriter.replaceOpWithNewOp<esi::ChannelBufferOp>(
832 op, channelType, clock, reset, operands.getInput(), op.getSizeAttr(),
840 static bool isDCType(Type type) {
return isa<TokenType, ValueType>(type); }
845 if (
auto funcOp = dyn_cast<HWModuleLike>(op))
846 return llvm::none_of(funcOp.getPortTypes(),
isDCType);
848 bool operandsOK = llvm::none_of(op->getOperandTypes(),
isDCType);
849 bool resultsOK = llvm::none_of(op->getResultTypes(),
isDCType);
850 return operandsOK && resultsOK;
858 class DCToHWPass :
public circt::impl::DCToHWBase<DCToHWPass> {
860 void runOnOperation()
override {
861 Operation *parent = getOperation();
865 auto walkRes = parent->walk([&](Operation *op) {
866 for (
auto res : op->getResults()) {
867 if (isa<dc::TokenType, dc::ValueType>(res.getType())) {
868 if (res.use_empty()) {
869 op->emitOpError() <<
"DCToHW: value " << res <<
" is unused.";
870 return WalkResult::interrupt();
872 if (!res.hasOneUse()) {
874 <<
"DCToHW: value " << res <<
" has multiple uses.";
875 return WalkResult::interrupt();
879 return WalkResult::advance();
882 if (walkRes.wasInterrupted()) {
883 parent->emitOpError()
884 <<
"DCToHW: failed to verify that all values "
885 "are used exactly once. Remember to run the "
886 "fork/sink materialization pass before HW lowering.";
891 ESITypeConverter typeConverter;
892 ConversionTarget target(getContext());
893 target.markUnknownOpDynamicallyLegal(
isLegalOp);
897 target.addIllegalDialect<dc::DCDialect>();
899 RewritePatternSet
patterns(parent->getContext());
902 ForkConversionPattern, JoinConversionPattern, SelectConversionPattern,
903 BranchConversionPattern, PackConversionPattern, UnpackConversionPattern,
904 BufferConversionPattern, SourceConversionPattern, SinkConversionPattern,
906 FromESIConversionPattern>(typeConverter, parent->getContext());
908 if (failed(applyPartialConversion(parent, target, std::move(
patterns))))
915 return std::make_unique<DCToHWPass>();
assert(baseType &&"element must be base type")
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static std::string valueName(Operation *scopeOp, Value v)
Convenience function for getting the SSA name of v under the scope of operation scopeOp.
static SmallVector< T > concat(const SmallVectorImpl< T > &a, const SmallVectorImpl< T > &b)
Returns a new vector containing the concatenation of vectors a and b.
static SmallVector< Value > extractBits(ConversionPatternRewriter &rewriter, Value val)
static Type toHWType(Type t)
Converts any type 't' into a hw-compatible type.
static bool isDCType(Type type)
static Type tupleToStruct(TupleType tuple)
static bool isLegalOp(Operation *op)
Returns true if the given op is considered as legal - i.e.
std::function< std::string(Operation *)> NameUniquer
static Type toESIHWType(Type t)
static EvaluatorValuePtr unwrap(OMEvaluatorValue c)
Instantiate one of these and use it to build typed backedges.
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
Backedge is a wrapper class around a Value.
void setValue(mlir::Value)
Channels are the basic communication primitives.
const Type * getInner() const
def create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
def connect(destination, source)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Value createOrFoldSExt(Location loc, Value value, Type destTy, OpBuilder &builder)
Create a sign extension operation from a value of integer type to an equal or larger integer type.
mlir::Type innerType(mlir::Type type)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< mlir::Pass > createDCToHWPass()
def reg(value, clock, reset=None, reset_value=None, name=None, sym_name=None)
Generic pattern which replaces an operation by one of the same operation name, but with converted att...