1818 */
1919#include " kernel/yosys.h"
2020#include " kernel/celltypes.h"
21+ #include " kernel/sigtools.h"
2122#include " backends/rtlil/rtlil_backend.h"
2223
2324USING_YOSYS_NAMESPACE
2425PRIVATE_NAMESPACE_BEGIN
2526
26- std::optional<std::string> format (std::string fmt, const dict<IdString, Const> ¶meters)
27+ bool has_fmt_field (std::string fmt, std::string field_name)
28+ {
29+ auto it = fmt.begin ();
30+ while (it != fmt.end ()) {
31+ if (*it == ' {' ) {
32+ it++;
33+ auto beg = it;
34+ while (it != fmt.end () && *it != ' }' ) it++;
35+ if (it == fmt.end ())
36+ return false ;
37+
38+ if (std::string (beg, it) == field_name)
39+ return true ;
40+ }
41+ it++;
42+ }
43+ return false ;
44+ }
45+
46+ struct ContextData {
47+ std::string unused_outputs;
48+ };
49+
50+ std::optional<std::string> format (std::string fmt, const dict<IdString, Const> ¶meters,
51+ ContextData &context)
2752{
2853 std::stringstream result;
2954
@@ -38,13 +63,19 @@ std::optional<std::string> format(std::string fmt, const dict<IdString, Const> &
3863 return {};
3964 }
4065
41- auto id = RTLIL::escape_id (std::string (beg, it));
42- if (!parameters.count (id)) {
43- log (" Parameter %s referenced in format string '%s' not found\n " , log_id (id), fmt.c_str ());
44- return {};
45- }
66+ std::string param_name = {beg, it};
4667
47- RTLIL_BACKEND::dump_const (result, parameters.at (id));
68+ if (param_name == " %unused" ) {
69+ result << context.unused_outputs ;
70+ } else {
71+ auto id = RTLIL::escape_id (std::string (beg, it));
72+ if (!parameters.count (id)) {
73+ log (" Parameter %s referenced in format string '%s' not found\n " , log_id (id), fmt.c_str ());
74+ return {};
75+ }
76+
77+ RTLIL_BACKEND::dump_const (result, parameters.at (id));
78+ }
4879 } else {
4980 result << *it;
5081 }
@@ -54,6 +85,44 @@ std::optional<std::string> format(std::string fmt, const dict<IdString, Const> &
5485 return {result.str ()};
5586}
5687
88+ struct Chunk {
89+ IdString port;
90+ int base, len;
91+
92+ Chunk (IdString id, int base, int len)
93+ : port(id), base(base), len(len) {}
94+
95+ IdString format (Cell *cell)
96+ {
97+ if (len == cell->getPort (port).size ())
98+ return port;
99+ else if (len == 1 )
100+ return stringf (" %s[%d]" , port.c_str (), base);
101+ else
102+ return stringf (" %s[%d:%d]" , port.c_str (), base + len - 1 , base);
103+ }
104+
105+ SigSpec sample (Cell *cell)
106+ {
107+ return cell->getPort (port).extract (base, len);
108+ }
109+ };
110+
111+ std::vector<Chunk> collect_chunks (std::vector<std::pair<IdString, int >> bits)
112+ {
113+ std::vector<Chunk> ret;
114+ std::sort (bits.begin (), bits.end ());
115+ for (auto it = bits.begin (); it != bits.end ();) {
116+ auto sep = it + 1 ;
117+ for (; sep != bits.end () &&
118+ sep->first == it->first &&
119+ sep->second == (sep - 1 )->second + 1 ; sep++);
120+ ret.emplace_back (it->first , it->second , sep - it);
121+ it = sep;
122+ }
123+ return ret;
124+ }
125+
57126struct WrapcellPass : Pass {
58127 WrapcellPass () : Pass(" wrapcell" , " wrap individual cells into new modules" ) {}
59128
@@ -68,6 +137,10 @@ struct WrapcellPass : Pass {
68137 log (" parameter values as specified in curly brackets. If the named module already\n " );
69138 log (" exists, it is reused.\n " );
70139 log (" \n " );
140+ log (" If the template contains the special string '{%unused}', the command tracks\n " );
141+ log (" unused output ports -- specialized wrapper modules will be generated per every\n " );
142+ log (" set of unused ports as appearing on a selected cell.\n " );
143+ log (" \n " );
71144 log (" -setattr <attribute-name>\n " );
72145 log (" set the given boolean attribute on each created wrapper module\n " );
73146 log (" \n " );
@@ -114,43 +187,88 @@ struct WrapcellPass : Pass {
114187 CellTypes ct;
115188 ct.setup ();
116189
190+ bool tracking_unused = has_fmt_field (name_fmt, " %unused" );
191+
117192 for (auto module : d->selected_modules ()) {
193+ SigPool unused;
194+
195+ for (auto wire : module ->wires ())
196+ if (wire->has_attribute (ID::unused_bits)) {
197+ std::string str = wire->get_string_attribute (ID::unused_bits);
198+ for (auto it = str.begin (); it != str.end ();) {
199+ auto sep = it;
200+ for (; sep != str.end () && *sep != ' ' ; sep++);
201+ unused.add (SigBit (wire, std::stoi (std::string (it, sep))));
202+ for (it = sep; it != str.end () && *it == ' ' ; it++);
203+ }
204+ }
205+
118206 for (auto cell : module ->selected_cells ()) {
119- std::optional<std::string> unescaped_name = format (name_fmt, cell->parameters );
207+ Module *subm;
208+ Cell *subcell;
209+
210+ if (!ct.cell_known (cell->type ))
211+ log_error (" Non-internal cell type '%s' on cell '%s' in module '%s' unsupported\n " ,
212+ log_id (cell->type ), log_id (cell), log_id (module ));
213+
214+ std::vector<std::pair<IdString, int >> unused_outputs, used_outputs;
215+ for (auto conn : cell->connections ()) {
216+ if (ct.cell_output (cell->type , conn.first ))
217+ for (int i = 0 ; i < conn.second .size (); i++) {
218+ if (tracking_unused && unused.check (conn.second [i]))
219+ unused_outputs.emplace_back (conn.first , i);
220+ else
221+ used_outputs.emplace_back (conn.first , i);
222+ }
223+ }
224+
225+ ContextData context;
226+ if (!unused_outputs.empty ()) {
227+ context.unused_outputs += " _unused" ;
228+ for (auto chunk : collect_chunks (unused_outputs))
229+ context.unused_outputs += " _" + RTLIL::unescape_id (chunk.format (cell));
230+ }
231+
232+ std::optional<std::string> unescaped_name = format (name_fmt, cell->parameters , context);
120233 if (!unescaped_name)
121234 log_error (" Formatting error when processing cell '%s' in module '%s'\n " ,
122235 log_id (cell), log_id (module ));
123236
124237 IdString name = RTLIL::escape_id (unescaped_name.value ());
238+ if (d->module (name))
239+ goto replace_cell;
125240
126- if (d->module (name)) {
127- cell->type = name;
128- cell->parameters .clear ();
129- continue ;
241+ subm = d->addModule (name);
242+ subcell = subm->addCell (" $1" , cell->type );
243+ for (auto conn : cell->connections ()) {
244+ if (ct.cell_output (cell->type , conn.first )) {
245+ subcell->setPort (conn.first , SigSpec (RTLIL::Sm, conn.second .size ()));
246+ } else {
247+ Wire *w = subm->addWire (conn.first , conn.second .size ());
248+ w->port_input = true ;
249+ subcell->setPort (conn.first , w);
250+ }
130251 }
131252
132- if (!ct.cell_known (cell->type ))
133- log_error (" Non-internal cell type '%s' on cell '%s' in module '%s' unsupported\n " ,
134- log_id (cell->type ), log_id (cell), log_id (module ));
253+ for (auto chunk : collect_chunks (used_outputs)) {
254+ Wire *w = subm->addWire (chunk.format (cell), chunk.len );
255+ w->port_output = true ;
256+ subcell->connections_ [chunk.port ].replace (chunk.base , w);
257+ }
135258
136- Module *subm = d->addModule (name);
137- Cell *subcell = subm->addCell (" $1" , cell->type );
138- for (auto conn : cell->connections ()) {
139- Wire *w = subm->addWire (conn.first , conn.second .size ());
140- if (ct.cell_output (cell->type , w->name ))
141- w->port_output = true ;
142- else
143- w->port_input = true ;
144- subcell->setPort (conn.first , w);
259+ for (auto chunk : collect_chunks (unused_outputs)) {
260+ Wire *w = subm->addWire (chunk.format (cell), chunk.len );
261+ subcell->connections_ [chunk.port ].replace (chunk.base , w);
145262 }
263+
146264 subcell->parameters = cell->parameters ;
147265 subm->fixup_ports ();
148266
149267 for (auto rule : attributes) {
150268 if (rule.value_fmt .empty ()) {
151269 subm->set_bool_attribute (rule.name );
152270 } else {
153- std::optional<std::string> value = format (rule.value_fmt , cell->parameters );
271+ std::optional<std::string> value = format (rule.value_fmt , cell->parameters , context );
154272
155273 if (!value)
156274 log_error (" Formatting error when processing cell '%s' in module '%s'\n " ,
@@ -160,8 +278,20 @@ struct WrapcellPass : Pass {
160278 }
161279 }
162280
163- cell-> type = name;
281+ replace_cell:
164282 cell->parameters .clear ();
283+
284+ dict<IdString, SigSpec> new_connections;
285+
286+ for (auto conn : cell->connections ())
287+ if (!ct.cell_output (cell->type , conn.first ))
288+ new_connections[conn.first ] = conn.second ;
289+
290+ for (auto chunk : collect_chunks (used_outputs))
291+ new_connections[chunk.format (cell)] = chunk.sample (cell);
292+
293+ cell->type = name;
294+ cell->connections_ = new_connections;
165295 }
166296 }
167297 }
0 commit comments