2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include "kernel/yosys.h"
21 #include "kernel/sigtools.h"
24 PRIVATE_NAMESPACE_BEGIN
31 bool initialized
= false;
33 pool
<SigBit
> init_ones
;
34 dict
<SigSpec
, pool
<SigSpec
>> sig_sources_db
;
35 dict
<SigSpec
, bool> sig_onehot_cache
;
36 pool
<SigSpec
> recursion_guard
;
38 OnehotDatabase(Module
*module
, const SigMap
&sigmap
) : module(module
), sigmap(sigmap
)
44 log_assert(!initialized
);
47 for (auto wire
: module
->wires())
49 auto it
= wire
->attributes
.find(ID(init
));
50 if (it
== wire
->attributes
.end())
53 auto &val
= it
->second
;
54 int width
= std::max(GetSize(wire
), GetSize(val
));
56 for (int i
= 0; i
< width
; i
++)
57 if (val
[i
] == State::S1
)
58 init_ones
.insert(sigmap(SigBit(wire
, i
)));
61 for (auto cell
: module
->cells())
63 vector
<SigSpec
> inputs
;
66 if (cell
->type
.in(ID($adff
), ID($dff
), ID($dffe
), ID($dlatch
), ID($ff
)))
68 output
= cell
->getPort(ID(Q
));
69 if (cell
->type
== ID($adff
))
70 inputs
.push_back(cell
->getParam(ID(ARST_VALUE
)));
71 inputs
.push_back(cell
->getPort(ID(D
)));
74 if (cell
->type
.in(ID($mux
), ID($pmux
)))
76 output
= cell
->getPort(ID(Y
));
77 inputs
.push_back(cell
->getPort(ID(A
)));
78 SigSpec B
= cell
->getPort(ID(B
));
79 for (int i
= 0; i
< GetSize(B
); i
+= GetSize(output
))
80 inputs
.push_back(B
.extract(i
, GetSize(output
)));
85 output
= sigmap(output
);
86 auto &srcs
= sig_sources_db
[output
];
87 for (auto src
: inputs
) {
88 while (!src
.empty() && src
[GetSize(src
)-1] == State::S0
)
89 src
.remove(GetSize(src
)-1);
90 srcs
.insert(sigmap(src
));
96 void query_worker(const SigSpec
&sig
, bool &retval
, bool &cache
, int indent
)
99 log("%*s %s\n", indent
, "", log_signal(sig
));
102 if (recursion_guard
.count(sig
)) {
104 log("%*s - recursion\n", indent
, "");
109 auto it
= sig_onehot_cache
.find(sig
);
110 if (it
!= sig_onehot_cache
.end()) {
112 log("%*s - cached (%s)\n", indent
, "", it
->second
? "true" : "false");
118 bool found_init_ones
= false;
119 for (auto bit
: sig
) {
120 if (init_ones
.count(bit
)) {
121 if (found_init_ones
) {
123 log("%*s - non-onehot init value\n", indent
, "");
127 found_init_ones
= true;
133 if (sig
.is_fully_const())
135 bool found_ones
= false;
136 for (auto bit
: sig
) {
137 if (bit
== State::S1
) {
140 log("%*s - non-onehot constant\n", indent
, "");
150 auto srcs
= sig_sources_db
.find(sig
);
151 if (srcs
== sig_sources_db
.end()) {
153 log("%*s - no sources for non-const signal\n", indent
, "");
156 for (auto &src
: srcs
->second
) {
157 bool child_cache
= true;
158 recursion_guard
.insert(sig
);
159 query_worker(src
, retval
, child_cache
, indent
+4);
160 recursion_guard
.erase(sig
);
170 // it is always safe to cache a negative result
171 if (cache
|| !retval
)
172 sig_onehot_cache
[sig
] = retval
;
175 bool query(const SigSpec
&sig
)
181 log("** ONEHOT QUERY START (%s)\n", log_signal(sig
));
186 query_worker(sig
, retval
, cache
, 3);
189 log("** ONEHOT QUERY RESULT = %s\n", retval
? "true" : "false");
191 // it is always safe to cache the root result of a query
193 sig_onehot_cache
[sig
] = retval
;
199 struct Pmux2ShiftxPass
: public Pass
{
200 Pmux2ShiftxPass() : Pass("pmux2shiftx", "transform $pmux cells to $shiftx cells") { }
201 void help() YS_OVERRIDE
203 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
205 log(" pmux2shiftx [options] [selection]\n");
207 log("This pass transforms $pmux cells to $shiftx cells.\n");
210 log(" verbose output\n");
212 log(" -min_density <percentage>\n");
213 log(" specifies the minimum density for the shifter\n");
214 log(" default: 50\n");
216 log(" -min_choices <int>\n");
217 log(" specified the minimum number of choices for a control signal\n");
218 log(" default: 3\n");
220 log(" -onehot ignore|pmux|shiftx\n");
221 log(" select strategy for one-hot encoded control signals\n");
222 log(" default: pmux\n");
225 log(" disable $sub inference for \"range decoders\"\n");
228 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
230 int min_density
= 50;
232 bool allow_onehot
= false;
233 bool optimize_onehot
= true;
234 bool verbose
= false;
235 bool verbose_onehot
= false;
236 bool norange
= false;
238 log_header(design
, "Executing PMUX2SHIFTX pass.\n");
241 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
242 if (args
[argidx
] == "-min_density" && argidx
+1 < args
.size()) {
243 min_density
= atoi(args
[++argidx
].c_str());
246 if (args
[argidx
] == "-min_choices" && argidx
+1 < args
.size()) {
247 min_choices
= atoi(args
[++argidx
].c_str());
250 if (args
[argidx
] == "-onehot" && argidx
+1 < args
.size() && args
[argidx
+1] == "ignore") {
252 allow_onehot
= false;
253 optimize_onehot
= false;
256 if (args
[argidx
] == "-onehot" && argidx
+1 < args
.size() && args
[argidx
+1] == "pmux") {
258 allow_onehot
= false;
259 optimize_onehot
= true;
262 if (args
[argidx
] == "-onehot" && argidx
+1 < args
.size() && args
[argidx
+1] == "shiftx") {
265 optimize_onehot
= false;
268 if (args
[argidx
] == "-v") {
272 if (args
[argidx
] == "-vv") {
274 verbose_onehot
= true;
277 if (args
[argidx
] == "-norange") {
283 extra_args(args
, argidx
, design
);
285 for (auto module
: design
->selected_modules())
287 SigMap
sigmap(module
);
288 OnehotDatabase
onehot_db(module
, sigmap
);
289 onehot_db
.verbose
= verbose_onehot
;
291 dict
<SigBit
, pair
<SigSpec
, Const
>> eqdb
;
293 for (auto cell
: module
->cells())
295 if (cell
->type
== ID($eq
))
297 dict
<SigBit
, State
> bits
;
299 SigSpec A
= sigmap(cell
->getPort(ID(A
)));
300 SigSpec B
= sigmap(cell
->getPort(ID(B
)));
302 int a_width
= cell
->getParam(ID(A_WIDTH
)).as_int();
303 int b_width
= cell
->getParam(ID(B_WIDTH
)).as_int();
305 if (a_width
< b_width
) {
306 bool a_signed
= cell
->getParam(ID(A_SIGNED
)).as_int();
307 A
.extend_u0(b_width
, a_signed
);
310 if (b_width
< a_width
) {
311 bool b_signed
= cell
->getParam(ID(B_SIGNED
)).as_int();
312 B
.extend_u0(a_width
, b_signed
);
315 for (int i
= 0; i
< GetSize(A
); i
++) {
316 SigBit a_bit
= A
[i
], b_bit
= B
[i
];
317 if (b_bit
.wire
&& !a_bit
.wire
) {
318 std::swap(a_bit
, b_bit
);
320 if (!a_bit
.wire
|| b_bit
.wire
)
322 if (bits
.count(a_bit
))
324 bits
[a_bit
] = b_bit
.data
;
327 if (GetSize(bits
) > 20)
331 pair
<SigSpec
, Const
> entry
;
333 for (auto it
: bits
) {
334 entry
.first
.append_bit(it
.first
);
335 entry
.second
.bits
.push_back(it
.second
);
338 eqdb
[sigmap(cell
->getPort(ID(Y
))[0])] = entry
;
342 if (cell
->type
== ID($logic_not
))
344 dict
<SigBit
, State
> bits
;
346 SigSpec A
= sigmap(cell
->getPort(ID(A
)));
348 for (int i
= 0; i
< GetSize(A
); i
++)
349 bits
[A
[i
]] = State::S0
;
352 pair
<SigSpec
, Const
> entry
;
354 for (auto it
: bits
) {
355 entry
.first
.append_bit(it
.first
);
356 entry
.second
.bits
.push_back(it
.second
);
359 eqdb
[sigmap(cell
->getPort(ID(Y
))[0])] = entry
;
365 for (auto cell
: module
->selected_cells())
367 if (cell
->type
!= ID($pmux
))
370 string src
= cell
->get_src_attribute();
371 int width
= cell
->getParam(ID(WIDTH
)).as_int();
372 int width_bits
= ceil_log2(width
);
373 int extwidth
= width
;
375 while (extwidth
& (extwidth
-1))
378 dict
<SigSpec
, pool
<int>> seldb
;
380 SigSpec A
= cell
->getPort(ID(A
));
381 SigSpec B
= cell
->getPort(ID(B
));
382 SigSpec S
= sigmap(cell
->getPort(ID(S
)));
383 for (int i
= 0; i
< GetSize(S
); i
++)
385 if (!eqdb
.count(S
[i
]))
388 auto &entry
= eqdb
.at(S
[i
]);
389 seldb
[entry
.first
].insert(i
);
395 bool printed_pmux_header
= false;
398 printed_pmux_header
= true;
399 log("Inspecting $pmux cell %s/%s.\n", log_id(module
), log_id(cell
));
400 log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width
, extwidth
, width_bits
);
403 SigSpec updated_S
= cell
->getPort(ID(S
));
404 SigSpec updated_B
= cell
->getPort(ID(B
));
406 while (!seldb
.empty())
408 // pick the largest entry in seldb
409 SigSpec sig
= seldb
.begin()->first
;
410 for (auto &it
: seldb
) {
411 if (GetSize(sig
) < GetSize(it
.first
))
413 else if (GetSize(seldb
.at(sig
)) < GetSize(it
.second
))
417 // find the relevant choices
418 bool is_onehot
= GetSize(sig
) > 2;
419 dict
<Const
, int> choices
;
420 for (int i
: seldb
.at(sig
)) {
421 Const val
= eqdb
.at(S
[i
]).second
;
423 for (auto b
: val
.bits
)
431 bool full_pmux
= GetSize(choices
) == GetSize(S
);
433 // TBD: also find choices that are using signals that are subsets of the bits in "sig"
437 if (is_onehot
&& !allow_onehot
&& !optimize_onehot
) {
442 if (GetSize(choices
) < min_choices
) {
448 if (!printed_pmux_header
) {
449 printed_pmux_header
= true;
450 log("Inspecting $pmux cell %s/%s.\n", log_id(module
), log_id(cell
));
451 log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width
, extwidth
, width_bits
);
454 log(" checking ctrl signal %s\n", log_signal(sig
));
456 auto print_choices
= [&]() {
457 log(" table of choices:\n");
458 for (auto &it
: choices
)
459 log(" %3d: %s: %s\n", it
.second
, log_signal(it
.first
),
460 log_signal(B
.extract(it
.second
*width
, width
)));
465 if (is_onehot
&& !allow_onehot
&& !optimize_onehot
) {
467 log(" ignoring one-hot encoding.\n");
472 if (GetSize(choices
) < min_choices
) {
474 log(" insufficient choices.\n");
480 if (is_onehot
&& optimize_onehot
)
483 if (!onehot_db
.query(sig
))
485 log(" failed to detect onehot driver. do not optimize.\n");
489 log(" optimizing one-hot encoding.\n");
490 for (auto &it
: choices
)
492 const Const
&val
= it
.first
;
495 for (int i
= 0; i
< GetSize(val
); i
++)
496 if (val
[i
] == State::S1
) {
497 log_assert(index
< 0);
502 log(" %3d: zero encoding.\n", it
.second
);
506 SigBit new_ctrl
= sig
[index
];
507 log(" %3d: new crtl signal is %s.\n", it
.second
, log_signal(new_ctrl
));
508 updated_S
[it
.second
] = new_ctrl
;
515 // find the best permutation
516 vector
<int> perm_new_from_old(GetSize(sig
));
517 Const
perm_xormask(State::S0
, GetSize(sig
));
519 vector
<int> values(GetSize(choices
));
520 vector
<bool> used_src_columns(GetSize(sig
));
521 vector
<vector
<bool>> columns(GetSize(sig
), vector
<bool>(GetSize(values
)));
523 for (int i
= 0; i
< GetSize(choices
); i
++) {
524 Const val
= choices
.element(i
)->first
;
525 for (int k
= 0; k
< GetSize(val
); k
++)
526 if (val
[k
] == State::S1
)
527 columns
[k
][i
] = true;
530 for (int dst_col
= GetSize(sig
)-1; dst_col
>= 0; dst_col
--)
532 int best_src_col
= -1;
533 bool best_inv
= false;
537 // find best src column for this dst column
538 for (int src_col
= 0; src_col
< GetSize(sig
); src_col
++)
540 if (used_src_columns
[src_col
])
544 int this_minval
= 1 << 30;
546 int this_inv_maxval
= 0;
547 int this_inv_minval
= 1 << 30;
549 for (int i
= 0; i
< GetSize(values
); i
++)
554 if (columns
[src_col
][i
])
557 inv_val
|= 1 << dst_col
;
559 this_maxval
= std::max(this_maxval
, val
);
560 this_minval
= std::min(this_minval
, val
);
562 this_inv_maxval
= std::max(this_inv_maxval
, inv_val
);
563 this_inv_minval
= std::min(this_inv_minval
, inv_val
);
566 int this_delta
= this_maxval
- this_minval
;
567 int this_inv_delta
= this_maxval
- this_minval
;
568 bool this_inv
= false;
570 if (!norange
&& this_delta
!= this_inv_delta
)
571 this_inv
= this_inv_delta
< this_delta
;
572 else if (this_maxval
!= this_inv_maxval
)
573 this_inv
= this_inv_maxval
< this_maxval
;
576 this_delta
= this_inv_delta
;
577 this_maxval
= this_inv_maxval
;
578 this_minval
= this_inv_minval
;
581 bool this_is_better
= false;
583 if (best_src_col
< 0)
584 this_is_better
= true;
585 else if (!norange
&& this_delta
!= best_delta
)
586 this_is_better
= this_delta
< best_delta
;
587 else if (this_maxval
!= best_maxval
)
588 this_is_better
= this_maxval
< best_maxval
;
590 this_is_better
= sig
[best_src_col
] < sig
[src_col
];
592 if (this_is_better
) {
593 best_src_col
= src_col
;
595 best_maxval
= this_maxval
;
596 best_delta
= this_delta
;
600 used_src_columns
[best_src_col
] = true;
601 perm_new_from_old
[dst_col
] = best_src_col
;
602 perm_xormask
[dst_col
] = best_inv
? State::S1
: State::S0
;
607 SigSpec
perm_sig(State::S0
, GetSize(sig
));
608 for (int i
= 0; i
< GetSize(sig
); i
++)
609 perm_sig
[i
] = sig
[perm_new_from_old
[i
]];
611 log(" best permutation: %s\n", log_signal(perm_sig
));
612 log(" best xor mask: %s\n", log_signal(perm_xormask
));
614 // permutated choices
615 int min_choice
= 1 << 30;
617 dict
<Const
, int> perm_choices
;
619 for (auto &it
: choices
)
621 Const
&old_c
= it
.first
;
622 Const
new_c(State::S0
, GetSize(old_c
));
624 for (int i
= 0; i
< GetSize(old_c
); i
++)
625 new_c
[i
] = old_c
[perm_new_from_old
[i
]];
627 Const new_c_before_xor
= new_c
;
628 new_c
= const_xor(new_c
, perm_xormask
, false, false, GetSize(new_c
));
630 perm_choices
[new_c
] = it
.second
;
632 min_choice
= std::min(min_choice
, new_c
.as_int());
633 max_choice
= std::max(max_choice
, new_c
.as_int());
635 log(" %3d: %s -> %s -> %s: %s\n", it
.second
, log_signal(old_c
), log_signal(new_c_before_xor
),
636 log_signal(new_c
), log_signal(B
.extract(it
.second
*width
, width
)));
639 int range_density
= 100*GetSize(choices
) / (max_choice
-min_choice
+1);
640 int absolute_density
= 100*GetSize(choices
) / (max_choice
+1);
642 log(" choices: %d\n", GetSize(choices
));
643 log(" min choice: %d\n", min_choice
);
644 log(" max choice: %d\n", max_choice
);
645 log(" range density: %d%%\n", range_density
);
646 log(" absolute density: %d%%\n", absolute_density
);
649 int full_density
= 100*GetSize(choices
) / (1 << GetSize(sig
));
650 log(" full density: %d%%\n", full_density
);
651 if (full_density
< min_density
) {
655 max_choice
= (1 << GetSize(sig
))-1;
656 log(" update to full case.\n");
657 log(" new min choice: %d\n", min_choice
);
658 log(" new max choice: %d\n", max_choice
);
662 bool full_case
= (min_choice
== 0) && (max_choice
== (1 << GetSize(sig
))-1) && (full_pmux
|| max_choice
+1 == GetSize(choices
));
663 log(" full case: %s\n", full_case
? "true" : "false");
665 // check density percentages
666 Const
offset(State::S0
, GetSize(sig
));
667 if (!norange
&& absolute_density
< min_density
&& range_density
>= min_density
)
669 offset
= Const(min_choice
, GetSize(sig
));
670 log(" offset: %s\n", log_signal(offset
));
672 min_choice
-= offset
.as_int();
673 max_choice
-= offset
.as_int();
675 dict
<Const
, int> new_perm_choices
;
676 for (auto &it
: perm_choices
)
677 new_perm_choices
[const_sub(it
.first
, offset
, false, false, GetSize(sig
))] = it
.second
;
678 perm_choices
.swap(new_perm_choices
);
680 if (absolute_density
< min_density
) {
681 log(" insufficient density.\n");
687 SigSpec cmp
= perm_sig
;
688 if (perm_xormask
.as_bool())
689 cmp
= module
->Xor(NEW_ID
, cmp
, perm_xormask
, false, src
);
690 if (offset
.as_bool())
691 cmp
= module
->Sub(NEW_ID
, cmp
, offset
, false, src
);
693 // create enable signal
694 SigBit en
= State::S1
;
696 Const
enable_mask(State::S0
, max_choice
+1);
697 for (auto &it
: perm_choices
)
698 enable_mask
[it
.first
.as_int()] = State::S1
;
699 en
= module
->addWire(NEW_ID
);
700 module
->addShift(NEW_ID
, enable_mask
, cmp
, en
, false, src
);
703 // create data signal
704 SigSpec
data(State::Sx
, (max_choice
+1)*extwidth
);
706 for (int i
= 0; i
<= max_choice
; i
++)
707 data
.replace(i
*extwidth
, A
);
709 for (auto &it
: perm_choices
) {
710 int position
= it
.first
.as_int()*extwidth
;
711 int data_index
= it
.second
;
712 data
.replace(position
, B
.extract(data_index
*width
, width
));
713 updated_S
[data_index
] = State::S0
;
714 updated_B
.replace(data_index
*width
, SigSpec(State::Sx
, width
));
717 // create shiftx cell
718 SigSpec shifted_cmp
= {cmp
, SigSpec(State::S0
, width_bits
)};
719 SigSpec outsig
= module
->addWire(NEW_ID
, width
);
720 Cell
*c
= module
->addShiftx(NEW_ID
, data
, shifted_cmp
, outsig
, false, src
);
721 updated_S
.append(en
);
722 updated_B
.append(outsig
);
723 log(" created $shiftx cell %s.\n", log_id(c
));
725 // remove this sig and continue with the next block
730 cell
->setPort(ID(S
), updated_S
);
731 cell
->setPort(ID(B
), updated_B
);
732 cell
->setParam(ID(S_WIDTH
), GetSize(updated_S
));
738 struct OnehotPass
: public Pass
{
739 OnehotPass() : Pass("onehot", "optimize $eq cells for onehot signals") { }
740 void help() YS_OVERRIDE
742 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
744 log(" onehot [options] [selection]\n");
746 log("This pass optimizes $eq cells that compare one-hot signals against constants\n");
749 log(" verbose output\n");
752 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
754 bool verbose
= false;
755 bool verbose_onehot
= false;
757 log_header(design
, "Executing ONEHOT pass.\n");
760 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
761 if (args
[argidx
] == "-v") {
765 if (args
[argidx
] == "-vv") {
767 verbose_onehot
= true;
772 extra_args(args
, argidx
, design
);
774 for (auto module
: design
->selected_modules())
776 SigMap
sigmap(module
);
777 OnehotDatabase
onehot_db(module
, sigmap
);
778 onehot_db
.verbose
= verbose_onehot
;
780 for (auto cell
: module
->selected_cells())
782 if (cell
->type
!= ID($eq
))
785 SigSpec A
= sigmap(cell
->getPort(ID(A
)));
786 SigSpec B
= sigmap(cell
->getPort(ID(B
)));
788 int a_width
= cell
->getParam(ID(A_WIDTH
)).as_int();
789 int b_width
= cell
->getParam(ID(B_WIDTH
)).as_int();
791 if (a_width
< b_width
) {
792 bool a_signed
= cell
->getParam(ID(A_SIGNED
)).as_int();
793 A
.extend_u0(b_width
, a_signed
);
796 if (b_width
< a_width
) {
797 bool b_signed
= cell
->getParam(ID(B_SIGNED
)).as_int();
798 B
.extend_u0(a_width
, b_signed
);
801 if (A
.is_fully_const())
804 if (!B
.is_fully_const())
808 log("Checking $eq(%s, %s) cell %s/%s.\n", log_signal(A
), log_signal(B
), log_id(module
), log_id(cell
));
810 if (!onehot_db
.query(A
)) {
812 log(" onehot driver test on %s failed.\n", log_signal(A
));
817 bool not_onehot
= false;
819 for (int i
= 0; i
< GetSize(B
); i
++) {
820 if (B
[i
] != State::S1
)
829 log(" not optimizing the zero pattern.\n");
833 SigSpec Y
= cell
->getPort(ID(Y
));
838 log(" replacing with constant 0 driver.\n");
840 log("Replacing one-hot $eq(%s, %s) cell %s/%s with constant 0 driver.\n", log_signal(A
), log_signal(B
), log_id(module
), log_id(cell
));
841 module
->connect(Y
, SigSpec(1, GetSize(Y
)));
845 SigSpec sig
= A
[index
];
847 log(" replacing with signal %s.\n", log_signal(sig
));
849 log("Replacing one-hot $eq(%s, %s) cell %s/%s with signal %s.\n",log_signal(A
), log_signal(B
), log_id(module
), log_id(cell
), log_signal(sig
));
850 sig
.extend_u0(GetSize(Y
));
851 module
->connect(Y
, sig
);
854 module
->remove(cell
);
860 PRIVATE_NAMESPACE_END