@@ -29,7 +29,17 @@ struct QlIoffPass : public Pass {
2929 if (!module )
3030 return ;
3131 modwalker.setup (module );
32- pool<RTLIL::Cell *> cells_to_replace;
32+ pool<RTLIL::Cell *> input_ffs;
33+ dict<RTLIL::Wire *, std::vector<Cell*>> output_ffs;
34+ dict<SigBit, pool<SigBit>> output_bit_aliases;
35+
36+ for (Wire* wire : module ->wires ())
37+ if (wire->port_output ) {
38+ output_ffs[wire].resize (wire->width , nullptr );
39+ for (SigBit bit : SigSpec (wire))
40+ output_bit_aliases[modwalker.sigmap (bit)].insert (bit);
41+ }
42+
3343 for (auto cell : module ->selected_cells ()) {
3444 if (cell->type .in (ID (dffsre), ID (sdffsre))) {
3545 log_debug (" Checking cell %s.\n " , cell->name .c_str ());
@@ -53,32 +63,62 @@ struct QlIoffPass : public Pass {
5363 log_debug (" not promoting: D has other consumers\n " );
5464 continue ;
5565 }
56- cells_to_replace .insert (cell);
57- continue ; // no need to check Q if we already put it on the list
66+ input_ffs .insert (cell);
67+ continue ; // prefer input FFs over output FFs
5868 }
69+
5970 SigSpec q = cell->getPort (ID::Q);
6071 log_assert (GetSize (q) == 1 );
61- if (modwalker.has_outputs (q)) {
72+ if (modwalker.has_outputs (q) && !modwalker. has_consumers (q) ) {
6273 log_debug (" Cell %s is potentially eligible for promotion to output IOFF.\n " , cell->name .c_str ());
63- // check that q_sig has no other consumers
64- pool<ModWalker::PortBit> portbits;
65- modwalker.get_consumers (portbits, q);
66- if (GetSize (portbits) > 0 ) {
67- log_debug (" not promoting: Q has other consumers\n " );
68- continue ;
74+ for (SigBit bit : output_bit_aliases[modwalker.sigmap (q)]) {
75+ log_assert (bit.is_wire ());
76+ output_ffs[bit.wire ][bit.offset ] = cell;
6977 }
70- cells_to_replace. insert (cell);
78+
7179 }
7280 }
7381 }
7482
75- for (auto cell : cells_to_replace ) {
76- log (" Promoting register %s to IOFF.\n " , log_signal (cell->getPort (ID::Q)));
83+ for (auto cell : input_ffs ) {
84+ log (" Promoting register %s to input IOFF.\n " , log_signal (cell->getPort (ID::Q)));
7785 cell->type = ID (dff);
7886 cell->unsetPort (ID::E);
7987 cell->unsetPort (ID::R);
8088 cell->unsetPort (ID::S);
8189 }
90+ for (auto & [old_port_output, ioff_cells] : output_ffs) {
91+ if (std::any_of (ioff_cells.begin (), ioff_cells.end (), [](Cell * c) { return c != nullptr ; }))
92+ {
93+ // create replacement output wire
94+ RTLIL::Wire* new_port_output = module ->addWire (NEW_ID, old_port_output->width );
95+ new_port_output->start_offset = old_port_output->start_offset ;
96+ module ->swap_names (old_port_output, new_port_output);
97+ std::swap (old_port_output->port_id , new_port_output->port_id );
98+ std::swap (old_port_output->port_input , new_port_output->port_input );
99+ std::swap (old_port_output->port_output , new_port_output->port_output );
100+ std::swap (old_port_output->upto , new_port_output->upto );
101+ std::swap (old_port_output->is_signed , new_port_output->is_signed );
102+ std::swap (old_port_output->attributes , new_port_output->attributes );
103+
104+ // create new output FFs
105+ SigSpec sig_o (old_port_output);
106+ SigSpec sig_n (new_port_output);
107+ for (int i = 0 ; i < new_port_output->width ; i++) {
108+ if (ioff_cells[i]) {
109+ log (" Promoting %s to output IOFF.\n " , log_signal (sig_n[i]));
110+
111+ RTLIL::Cell *new_cell = module ->addCell (NEW_ID, ID (dff));
112+ new_cell->setPort (ID::C, ioff_cells[i]->getPort (ID::C));
113+ new_cell->setPort (ID::D, ioff_cells[i]->getPort (ID::D));
114+ new_cell->setPort (ID::Q, sig_n[i]);
115+ new_cell->set_bool_attribute (ID::keep);
116+ } else {
117+ module ->connect (sig_n[i], sig_o[i]);
118+ }
119+ }
120+ }
121+ }
82122 }
83123} QlIoffPass;
84124
0 commit comments