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(
115 [](mlir::OpBuilder &builder, mlir::Type resultType,
116 mlir::ValueRange inputs,
117 mlir::Location loc) -> std::optional<mlir::Value> {
118 if (inputs.size() != 1)
122 .create<UnrealizedConversionCastOp>(loc, resultType, inputs[0])
126 addSourceMaterialization(
127 [](mlir::OpBuilder &builder, mlir::Type resultType,
128 mlir::ValueRange inputs,
129 mlir::Location loc) -> std::optional<mlir::Value> {
130 if (inputs.size() != 1)
134 .create<UnrealizedConversionCastOp>(loc, resultType, inputs[0])
150 struct InputHandshake {
153 std::optional<Backedge> ready;
159 struct OutputHandshake {
161 std::optional<Backedge> valid;
163 std::optional<Backedge>
data;
167 static void connect(InputHandshake &input, OutputHandshake &output) {
168 output.valid->setValue(input.valid);
169 input.ready->setValue(output.ready);
172 template <
typename T,
typename TInner>
173 llvm::SmallVector<T> extractValues(llvm::SmallVector<TInner> &container,
174 llvm::function_ref<T(TInner &)> extractor) {
175 llvm::SmallVector<T> result;
176 llvm::transform(container, std::back_inserter(result), extractor);
183 llvm::SmallVector<InputHandshake> inputs;
184 llvm::SmallVector<OutputHandshake> outputs;
186 llvm::SmallVector<Value> getInputValids() {
187 return extractValues<Value, InputHandshake>(
188 inputs, [](
auto &hs) {
return hs.valid; });
190 llvm::SmallVector<std::optional<Backedge>> getInputReadys() {
191 return extractValues<std::optional<Backedge>, InputHandshake>(
192 inputs, [](
auto &hs) {
return hs.ready; });
194 llvm::SmallVector<std::optional<Backedge>> getOutputValids() {
195 return extractValues<std::optional<Backedge>, OutputHandshake>(
196 outputs, [](
auto &hs) {
return hs.valid; });
198 llvm::SmallVector<Value> getInputDatas() {
199 return extractValues<Value, InputHandshake>(
200 inputs, [](
auto &hs) {
return hs.data; });
202 llvm::SmallVector<Value> getOutputReadys() {
203 return extractValues<Value, OutputHandshake>(
204 outputs, [](
auto &hs) {
return hs.ready; });
207 llvm::SmallVector<Value> getOutputChannels() {
208 return extractValues<Value, OutputHandshake>(
209 outputs, [](
auto &hs) {
return hs.channel; });
211 llvm::SmallVector<std::optional<Backedge>> getOutputDatas() {
212 return extractValues<std::optional<Backedge>, OutputHandshake>(
213 outputs, [](
auto &hs) {
return hs.data; });
221 RTLBuilder(Location loc, OpBuilder &builder, Value clk = Value(),
223 : b(builder), loc(loc),
clk(
clk), rst(rst) {}
225 Value constant(
const APInt &apv, StringRef name = {}) {
228 bool isZeroWidth = apv.getBitWidth() == 0;
230 auto it = constants.find(apv);
231 if (it != constants.end())
237 constants[apv] = cval;
241 Value constant(
unsigned width, int64_t value, StringRef name = {}) {
242 return constant(APInt(
width, value));
244 std::pair<Value, Value>
wrap(Value data, Value valid, StringRef name = {}) {
245 auto wrapOp = b.create<esi::WrapValidReadyOp>(loc,
data, valid);
246 return {wrapOp.getResult(0), wrapOp.getResult(1)};
248 std::pair<Value, Value>
unwrap(Value channel, Value ready,
249 StringRef name = {}) {
250 auto unwrapOp = b.create<esi::UnwrapValidReadyOp>(loc, channel, ready);
251 return {unwrapOp.getResult(0), unwrapOp.getResult(1)};
255 Value
reg(StringRef name, Value in, Value rstValue, Value clk = Value(),
256 Value rst = Value()) {
257 Value resolvedClk =
clk ?
clk : this->
clk;
258 Value resolvedRst = rst ? rst : this->rst;
260 "No global clock provided to this RTLBuilder - a clock "
261 "signal must be provided to the reg(...) function.");
263 "No global reset provided to this RTLBuilder - a reset "
264 "signal must be provided to the reg(...) function.");
266 return b.create<
seq::CompRegOp>(loc, in, resolvedClk, resolvedRst, rstValue,
270 Value cmp(Value lhs, Value rhs, comb::ICmpPredicate predicate,
271 StringRef name = {}) {
272 return b.
create<comb::ICmpOp>(loc, predicate, lhs, rhs);
275 Value buildNamedOp(llvm::function_ref<Value()> f, StringRef name) {
278 Operation *op = v.getDefiningOp();
280 op->setAttr(
"sv.namehint", b.getStringAttr(name));
281 nameAttr = b.getStringAttr(name);
287 Value bitAnd(ValueRange values, StringRef name = {}) {
289 [&]() {
return b.create<
comb::AndOp>(loc, values,
false); }, name);
293 Value bitOr(ValueRange values, StringRef name = {}) {
295 [&]() {
return b.create<
comb::OrOp>(loc, values,
false); }, name);
299 Value bitNot(Value value, StringRef name = {}) {
300 auto allOnes = constant(value.getType().getIntOrFloatBitWidth(), -1);
301 std::string inferedName;
305 value.getDefiningOp()->getAttrOfType<StringAttr>(
"sv.namehint")) {
306 inferedName = (
"not_" +
valueName.getValue()).str();
312 [&]() {
return b.create<
comb::XorOp>(loc, value, allOnes); }, name);
315 Value shl(Value value, Value shift, StringRef name = {}) {
317 [&]() {
return b.create<
comb::ShlOp>(loc, value, shift); }, name);
320 Value
concat(ValueRange values, StringRef name = {}) {
321 return buildNamedOp([&]() {
return b.create<
comb::ConcatOp>(loc, values); },
325 llvm::SmallVector<Value>
extractBits(Value v, StringRef name = {}) {
326 llvm::SmallVector<Value> bits;
327 for (
unsigned i = 0, e = v.getType().getIntOrFloatBitWidth(); i != e; ++i)
333 Value reduceOr(Value v, StringRef name = {}) {
334 return buildNamedOp([&]() {
return bitOr(
extractBits(v)); }, name);
338 Value extract(Value v,
unsigned lo,
unsigned hi, StringRef name = {}) {
339 unsigned width = hi - lo + 1;
345 Value truncate(Value value,
unsigned width, StringRef name = {}) {
346 return extract(value, 0,
width - 1, name);
349 Value zext(Value value,
unsigned outWidth, StringRef name = {}) {
350 unsigned inWidth = value.getType().getIntOrFloatBitWidth();
351 assert(inWidth <= outWidth &&
"zext: input width must be <= output width.");
352 if (inWidth == outWidth)
354 auto c0 = constant(outWidth - inWidth, 0);
355 return concat({c0, value}, name);
358 Value sext(Value value,
unsigned outWidth, StringRef name = {}) {
363 Value bit(Value v,
unsigned index, StringRef name = {}) {
364 return extract(v, index, index, name);
368 Value arrayCreate(ValueRange values, StringRef name = {}) {
374 Value arrayGet(Value array, Value index, StringRef name = {}) {
376 [&]() {
return b.create<
hw::ArrayGetOp>(loc, array, index); }, name);
382 Value mux(Value index, ValueRange values, StringRef name = {}) {
383 if (values.size() == 2) {
386 return b.create<
comb::MuxOp>(loc, index, values[1], values[0]);
390 return arrayGet(arrayCreate(values), index, name);
395 Value oneHotMux(Value index, ValueRange inputs) {
397 unsigned numInputs = inputs.size();
398 assert(numInputs == index.getType().getIntOrFloatBitWidth() &&
399 "mismatch between width of one-hot select input and the number of "
400 "inputs to be selected");
403 auto dataType = inputs[0].getType();
405 isa<NoneType>(dataType) ? 0 : dataType.getIntOrFloatBitWidth();
406 Value muxValue = constant(
width, 0);
409 for (
size_t i = numInputs - 1; i != 0; --i) {
410 Value input = inputs[i];
411 Value selectBit = bit(index, i);
412 muxValue = mux(selectBit, {muxValue, input});
421 DenseMap<APInt, Value> constants;
424 static bool isZeroWidthType(Type type) {
425 if (
auto intType = dyn_cast<IntegerType>(type))
426 return intType.getWidth() == 0;
427 return isa<NoneType>(type);
430 static UnwrappedIO unwrapIO(Location loc, ValueRange operands,
432 ConversionPatternRewriter &rewriter,
434 RTLBuilder rtlb(loc, rewriter);
435 UnwrappedIO unwrapped;
436 for (
auto in : operands) {
437 assert(isa<esi::ChannelType>(in.getType()));
438 auto ready = bb.
get(rtlb.b.getI1Type());
439 auto [
data, valid] = rtlb.unwrap(in, ready);
440 unwrapped.inputs.push_back(InputHandshake{in, valid, ready,
data});
442 for (
auto outputType : results) {
451 rewriter.create<
hw::ConstantOp>(loc, rewriter.getIntegerType(0), 0);
455 hs.data = dataBackedge;
458 auto valid = bb.
get(rewriter.getI1Type());
459 auto [dataCh, ready] = rtlb.wrap(data, valid);
463 unwrapped.outputs.push_back(hs);
468 static UnwrappedIO unwrapIO(Operation *op, ValueRange operands,
469 ConversionPatternRewriter &rewriter,
471 return unwrapIO(op->getLoc(), operands, op->getResultTypes(), rewriter, bb);
476 static FailureOr<std::pair<Value, Value>> getClockAndReset(Operation *op) {
477 auto *parent = op->getParentOp();
478 auto parentFuncOp = dyn_cast<HWModuleLike>(parent);
480 return parent->emitOpError(
"parent op does not implement HWModuleLike");
482 auto argAttrs = parentFuncOp.getAllInputAttrs();
484 std::optional<size_t> clockIdx, resetIdx;
486 for (
auto [idx, battrs] : llvm::enumerate(argAttrs)) {
487 auto attrs = cast<DictionaryAttr>(battrs);
488 if (attrs.get(
"dc.clock")) {
490 return parent->emitOpError(
491 "multiple arguments contains a 'dc.clock' attribute");
495 if (attrs.get(
"dc.reset")) {
497 return parent->emitOpError(
498 "multiple arguments contains a 'dc.reset' attribute");
504 return parent->emitOpError(
"no argument contains a 'dc.clock' attribute");
507 return parent->emitOpError(
"no argument contains a 'dc.reset' attribute");
509 return {std::make_pair(parentFuncOp.getArgumentForInput(*clockIdx),
510 parentFuncOp.getArgumentForInput(*resetIdx))};
517 matchAndRewrite(ForkOp op, OpAdaptor operands,
518 ConversionPatternRewriter &rewriter)
const override {
520 auto crRes = getClockAndReset(op);
523 auto [clock, reset] = *crRes;
524 RTLBuilder rtlb(op.getLoc(), rewriter, clock, reset);
525 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
527 auto &input = io.inputs[0];
529 Value c0I1 = rtlb.constant(1, 0);
530 llvm::SmallVector<Value> doneWires;
531 for (
auto [i, output] : llvm::enumerate(io.outputs)) {
533 Value emitted = rtlb.bitAnd({doneBE, rtlb.bitNot(*input.ready)});
535 rtlb.reg(
"emitted_" + std::to_string(i), emitted, c0I1);
536 Value outValid = rtlb.bitAnd({rtlb.bitNot(emittedReg), input.valid});
537 output.valid->setValue(outValid);
538 Value validReady = rtlb.bitAnd({output.ready, outValid});
540 rtlb.bitOr({validReady, emittedReg},
"done" + std::to_string(i));
542 doneWires.push_back(done);
544 input.ready->setValue(rtlb.bitAnd(doneWires,
"allDone"));
546 rewriter.replaceOp(op, io.getOutputChannels());
556 matchAndRewrite(JoinOp op, OpAdaptor operands,
557 ConversionPatternRewriter &rewriter)
const override {
559 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
560 RTLBuilder rtlb(op.getLoc(), rewriter);
561 auto &output = io.outputs[0];
563 Value allValid = rtlb.bitAnd(io.getInputValids());
564 output.valid->setValue(allValid);
566 auto validAndReady = rtlb.bitAnd({output.ready, allValid});
567 for (
auto &input : io.inputs)
568 input.ready->setValue(validAndReady);
570 rewriter.replaceOp(op, io.outputs[0].channel);
580 matchAndRewrite(SelectOp op, OpAdaptor operands,
581 ConversionPatternRewriter &rewriter)
const override {
583 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
584 RTLBuilder rtlb(op.getLoc(), rewriter);
587 auto select = io.inputs[0];
588 io.inputs.erase(io.inputs.begin());
589 buildMuxLogic(rtlb, io, select);
591 rewriter.replaceOp(op, io.outputs[0].channel);
598 void buildMuxLogic(RTLBuilder &rtlb, UnwrappedIO &unwrapped,
599 InputHandshake &select)
const {
602 size_t numInputs = unwrapped.inputs.size();
603 size_t selectWidth = llvm::Log2_64_Ceil(numInputs);
604 Value truncatedSelect =
605 select.data.getType().getIntOrFloatBitWidth() > selectWidth
606 ? rtlb.truncate(select.data, selectWidth)
610 auto selectZext = rtlb.zext(truncatedSelect, numInputs);
611 auto select1h = rtlb.shl(rtlb.constant(numInputs, 1), selectZext);
612 auto &res = unwrapped.outputs[0];
615 auto selectedInputValid =
616 rtlb.mux(truncatedSelect, unwrapped.getInputValids());
618 auto selAndInputValid = rtlb.bitAnd({selectedInputValid, select.valid});
619 res.valid->setValue(selAndInputValid);
620 auto resValidAndReady = rtlb.bitAnd({selAndInputValid, res.ready});
623 select.ready->setValue(resValidAndReady);
626 for (
auto [inIdx, in] : llvm::enumerate(unwrapped.inputs)) {
628 auto isSelected = rtlb.bit(select1h, inIdx);
632 auto activeAndResultValidAndReady =
633 rtlb.bitAnd({isSelected, resValidAndReady});
634 in.ready->setValue(activeAndResultValidAndReady);
641 using OpConversionPattern::OpConversionPattern;
643 matchAndRewrite(BranchOp op, OpAdaptor operands,
644 ConversionPatternRewriter &rewriter)
const override {
646 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
647 RTLBuilder rtlb(op.getLoc(), rewriter);
648 auto cond = io.inputs[0];
649 auto trueRes = io.outputs[0];
650 auto falseRes = io.outputs[1];
653 trueRes.valid->setValue(rtlb.bitAnd({cond.data, cond.valid}));
654 falseRes.valid->setValue(rtlb.bitAnd({rtlb.bitNot(cond.data), cond.valid}));
657 Value selectedResultReady =
658 rtlb.mux(cond.data, {falseRes.ready, trueRes.ready});
659 Value condReady = rtlb.bitAnd({selectedResultReady, cond.valid});
660 cond.ready->setValue(condReady);
662 rewriter.replaceOp(op,
663 SmallVector<Value>{trueRes.channel, falseRes.channel});
670 using OpConversionPattern::OpConversionPattern;
672 matchAndRewrite(MergeOp op, OpAdaptor operands,
673 ConversionPatternRewriter &rewriter)
const override {
675 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
676 auto output = io.outputs[0];
677 RTLBuilder rtlb(op.getLoc(), rewriter);
680 Value hasWin = rtlb.bitOr(io.getInputValids());
686 Value winWasFirst = io.inputs[0].valid;
687 Value winWasSecond = rtlb.bitNot(winWasFirst);
688 Value winIndex = winWasSecond;
690 output.valid->setValue(hasWin);
691 output.data->setValue(winIndex);
695 Value outValidAndReady = rtlb.bitAnd({hasWin, output.ready});
699 io.inputs[0].ready->setValue(rtlb.bitAnd({outValidAndReady, winWasFirst}));
700 io.inputs[1].ready->setValue(rtlb.bitAnd({outValidAndReady, winWasSecond}));
702 rewriter.replaceOp(op, output.channel);
712 using OpConversionPattern::OpConversionPattern;
715 matchAndRewrite(ToESIOp op, OpAdaptor operands,
716 ConversionPatternRewriter &rewriter)
const override {
717 rewriter.replaceOp(op, operands.getOperands());
726 using OpConversionPattern::OpConversionPattern;
729 matchAndRewrite(FromESIOp op, OpAdaptor operands,
730 ConversionPatternRewriter &rewriter)
const override {
731 rewriter.replaceOp(op, operands.getOperands());
741 matchAndRewrite(SinkOp op, OpAdaptor operands,
742 ConversionPatternRewriter &rewriter)
const override {
744 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
745 io.inputs[0].ready->setValue(
746 RTLBuilder(op.getLoc(), rewriter).constant(1, 1));
747 rewriter.eraseOp(op);
756 matchAndRewrite(SourceOp op, OpAdaptor operands,
757 ConversionPatternRewriter &rewriter)
const override {
759 UnwrappedIO io = unwrapIO(op, operands.getOperands(), rewriter, bb);
760 RTLBuilder rtlb(op.getLoc(), rewriter);
761 io.outputs[0].valid->setValue(rtlb.constant(1, 1));
762 rewriter.replaceOp(op, io.outputs[0].channel);
771 matchAndRewrite(PackOp op, OpAdaptor operands,
772 ConversionPatternRewriter &rewriter)
const override {
774 UnwrappedIO io = unwrapIO(op, llvm::SmallVector<Value>{operands.getToken()},
776 RTLBuilder rtlb(op.getLoc(), rewriter);
777 auto &input = io.inputs[0];
778 auto &output = io.outputs[0];
779 output.data->setValue(operands.getInput());
781 rewriter.replaceOp(op, output.channel);
790 matchAndRewrite(UnpackOp op, OpAdaptor operands,
791 ConversionPatternRewriter &rewriter)
const override {
793 UnwrappedIO io = unwrapIO(
794 op.getLoc(), llvm::SmallVector<Value>{operands.getInput()},
796 llvm::SmallVector<Type>{op.getToken().getType()}, rewriter, bb);
797 RTLBuilder rtlb(op.getLoc(), rewriter);
798 auto &input = io.inputs[0];
799 auto &output = io.outputs[0];
801 llvm::SmallVector<Value> unpackedValues;
802 unpackedValues.push_back(input.data);
805 llvm::SmallVector<Value> outputs;
806 outputs.push_back(output.channel);
807 outputs.append(unpackedValues.begin(), unpackedValues.end());
808 rewriter.replaceOp(op, outputs);
818 matchAndRewrite(BufferOp op, OpAdaptor operands,
819 ConversionPatternRewriter &rewriter)
const override {
820 auto crRes = getClockAndReset(op);
823 auto [clock, reset] = *crRes;
827 Type channelType = operands.getInput().getType();
828 rewriter.replaceOpWithNewOp<esi::ChannelBufferOp>(
829 op, channelType, clock, reset, operands.getInput(), op.getSizeAttr(),
837 static bool isDCType(Type type) {
return isa<TokenType, ValueType>(type); }
842 if (
auto funcOp = dyn_cast<HWModuleLike>(op)) {
843 return llvm::none_of(funcOp.getPortTypes(),
isDCType) &&
844 llvm::none_of(funcOp.getBodyBlock()->getArgumentTypes(),
isDCType);
847 bool operandsOK = llvm::none_of(op->getOperandTypes(),
isDCType);
848 bool resultsOK = llvm::none_of(op->getResultTypes(),
isDCType);
849 return operandsOK && resultsOK;
857 class DCToHWPass :
public circt::impl::DCToHWBase<DCToHWPass> {
859 void runOnOperation()
override {
860 Operation *parent = getOperation();
864 auto walkRes = parent->walk([&](Operation *op) {
865 for (
auto res : op->getResults()) {
866 if (isa<dc::TokenType, dc::ValueType>(res.getType())) {
867 if (res.use_empty()) {
868 op->emitOpError() <<
"DCToHW: value " << res <<
" is unused.";
869 return WalkResult::interrupt();
871 if (!res.hasOneUse()) {
873 <<
"DCToHW: value " << res <<
" has multiple uses.";
874 return WalkResult::interrupt();
878 return WalkResult::advance();
881 if (walkRes.wasInterrupted()) {
882 parent->emitOpError()
883 <<
"DCToHW: failed to verify that all values "
884 "are used exactly once. Remember to run the "
885 "fork/sink materialization pass before HW lowering.";
890 ESITypeConverter typeConverter;
891 ConversionTarget target(getContext());
892 target.markUnknownOpDynamicallyLegal(
isLegalOp);
896 target.addIllegalDialect<dc::DCDialect>();
898 RewritePatternSet
patterns(parent->getContext());
901 ForkConversionPattern, JoinConversionPattern, SelectConversionPattern,
902 BranchConversionPattern, PackConversionPattern, UnpackConversionPattern,
903 BufferConversionPattern, SourceConversionPattern, SinkConversionPattern,
905 FromESIConversionPattern>(typeConverter, parent->getContext());
907 if (failed(applyPartialConversion(parent, target, std::move(
patterns))))
914 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 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 Value extractBits(OpBuilder &builder, Location loc, Value value, unsigned startBit, unsigned bitWidth)
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...