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+ const 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,45 @@ 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+ // Joins contiguous runs of bits into a 'Chunk'
112+ std::vector<Chunk> collect_chunks (std::vector<std::pair<IdString, int >> bits)
113+ {
114+ std::vector<Chunk> ret;
115+ std::sort (bits.begin (), bits.end ());
116+ for (auto it = bits.begin (); it != bits.end ();) {
117+ auto sep = it + 1 ;
118+ for (; sep != bits.end () &&
119+ sep->first == it->first &&
120+ sep->second == (sep - 1 )->second + 1 ; sep++);
121+ ret.emplace_back (it->first , it->second , sep - it);
122+ it = sep;
123+ }
124+ return ret;
125+ }
126+
57127struct WrapcellPass : Pass {
58128 WrapcellPass () : Pass(" wrapcell" , " wrap individual cells into new modules" ) {}
59129
@@ -68,6 +138,10 @@ struct WrapcellPass : Pass {
68138 log (" parameter values as specified in curly brackets. If the named module already\n " );
69139 log (" exists, it is reused.\n " );
70140 log (" \n " );
141+ log (" If the template contains the special string '{%%unused}', the command tracks\n " );
142+ log (" unused output ports -- specialized wrapper modules will be generated per every\n " );
143+ log (" distinct set of unused port bits as appearing on any selected cell.\n " );
144+ log (" \n " );
71145 log (" -setattr <attribute-name>\n " );
72146 log (" set the given boolean attribute on each created wrapper module\n " );
73147 log (" \n " );
@@ -114,43 +188,89 @@ struct WrapcellPass : Pass {
114188 CellTypes ct;
115189 ct.setup ();
116190
191+ bool tracking_unused = has_fmt_field (name_fmt, " %unused" );
192+
117193 for (auto module : d->selected_modules ()) {
194+ SigPool unused;
195+
196+ for (auto wire : module ->wires ())
197+ if (wire->has_attribute (ID::unused_bits)) {
198+ std::string str = wire->get_string_attribute (ID::unused_bits);
199+ for (auto it = str.begin (); it != str.end ();) {
200+ auto sep = it;
201+ for (; sep != str.end () && *sep != ' ' ; sep++);
202+ unused.add (SigBit (wire, std::stoi (std::string (it, sep))));
203+ for (it = sep; it != str.end () && *it == ' ' ; it++);
204+ }
205+ }
206+
118207 for (auto cell : module ->selected_cells ()) {
119- std::optional<std::string> unescaped_name = format (name_fmt, cell->parameters );
208+ Module *subm;
209+ Cell *subcell;
210+
211+ if (!ct.cell_known (cell->type ))
212+ log_error (" Non-internal cell type '%s' on cell '%s' in module '%s' unsupported\n " ,
213+ log_id (cell->type ), log_id (cell), log_id (module ));
214+
215+ std::vector<std::pair<IdString, int >> unused_outputs, used_outputs;
216+ for (auto conn : cell->connections ()) {
217+ if (ct.cell_output (cell->type , conn.first ))
218+ for (int i = 0 ; i < conn.second .size (); i++) {
219+ if (tracking_unused && unused.check (conn.second [i]))
220+ unused_outputs.emplace_back (conn.first , i);
221+ else
222+ used_outputs.emplace_back (conn.first , i);
223+ }
224+ }
225+
226+ ContextData context;
227+ if (!unused_outputs.empty ()) {
228+ context.unused_outputs += " _unused" ;
229+ for (auto chunk : collect_chunks (unused_outputs))
230+ context.unused_outputs += " _" + RTLIL::unescape_id (chunk.format (cell));
231+ }
232+
233+ std::optional<std::string> unescaped_name = format (name_fmt, cell->parameters , context);
120234 if (!unescaped_name)
121235 log_error (" Formatting error when processing cell '%s' in module '%s'\n " ,
122236 log_id (cell), log_id (module ));
123237
124238 IdString name = RTLIL::escape_id (unescaped_name.value ());
239+ if (d->module (name))
240+ goto replace_cell;
125241
126- if (d->module (name)) {
127- cell->type = name;
128- cell->parameters .clear ();
129- continue ;
242+ subm = d->addModule (name);
243+ subcell = subm->addCell (" $1" , cell->type );
244+ for (auto conn : cell->connections ()) {
245+ if (ct.cell_output (cell->type , conn.first )) {
246+ // Insert marker bits as placehodlers which need to be replaced
247+ subcell->setPort (conn.first , SigSpec (RTLIL::Sm, conn.second .size ()));
248+ } else {
249+ Wire *w = subm->addWire (conn.first , conn.second .size ());
250+ w->port_input = true ;
251+ subcell->setPort (conn.first , w);
252+ }
130253 }
131254
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 ));
255+ for (auto chunk : collect_chunks (used_outputs)) {
256+ Wire *w = subm->addWire (chunk.format (cell), chunk.len );
257+ w->port_output = true ;
258+ subcell->connections_ [chunk.port ].replace (chunk.base , w);
259+ }
135260
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);
261+ for (auto chunk : collect_chunks (unused_outputs)) {
262+ Wire *w = subm->addWire (chunk.format (cell), chunk.len );
263+ subcell->connections_ [chunk.port ].replace (chunk.base , w);
145264 }
265+
146266 subcell->parameters = cell->parameters ;
147267 subm->fixup_ports ();
148268
149269 for (auto rule : attributes) {
150270 if (rule.value_fmt .empty ()) {
151271 subm->set_bool_attribute (rule.name );
152272 } else {
153- std::optional<std::string> value = format (rule.value_fmt , cell->parameters );
273+ std::optional<std::string> value = format (rule.value_fmt , cell->parameters , context );
154274
155275 if (!value)
156276 log_error (" Formatting error when processing cell '%s' in module '%s'\n " ,
@@ -160,8 +280,20 @@ struct WrapcellPass : Pass {
160280 }
161281 }
162282
163- cell-> type = name;
283+ replace_cell:
164284 cell->parameters .clear ();
285+
286+ dict<IdString, SigSpec> new_connections;
287+
288+ for (auto conn : cell->connections ())
289+ if (!ct.cell_output (cell->type , conn.first ))
290+ new_connections[conn.first ] = conn.second ;
291+
292+ for (auto chunk : collect_chunks (used_outputs))
293+ new_connections[chunk.format (cell)] = chunk.sample (cell);
294+
295+ cell->type = name;
296+ cell->connections_ = new_connections;
165297 }
166298 }
167299 }
0 commit comments