Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wrapcell: Optionally track unused outputs #4799

Merged
merged 4 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 156 additions & 26 deletions passes/cmds/wrapcell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,37 @@
*/
#include "kernel/yosys.h"
#include "kernel/celltypes.h"
#include "kernel/sigtools.h"
#include "backends/rtlil/rtlil_backend.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

std::optional<std::string> format(std::string fmt, const dict<IdString, Const> &parameters)
bool has_fmt_field(std::string fmt, std::string field_name)
{
auto it = fmt.begin();
while (it != fmt.end()) {
if (*it == '{') {
it++;
auto beg = it;
while (it != fmt.end() && *it != '}') it++;
if (it == fmt.end())
return false;

if (std::string(beg, it) == field_name)
return true;
}
it++;
}
return false;
}

struct ContextData {
std::string unused_outputs;
};

std::optional<std::string> format(std::string fmt, const dict<IdString, Const> &parameters,
povik marked this conversation as resolved.
Show resolved Hide resolved
ContextData &context)
{
std::stringstream result;

Expand All @@ -38,13 +63,19 @@
return {};
}

auto id = RTLIL::escape_id(std::string(beg, it));
if (!parameters.count(id)) {
log("Parameter %s referenced in format string '%s' not found\n", log_id(id), fmt.c_str());
return {};
}
std::string param_name = {beg, it};

RTLIL_BACKEND::dump_const(result, parameters.at(id));
if (param_name == "%unused") {
result << context.unused_outputs;
} else {
auto id = RTLIL::escape_id(std::string(beg, it));
if (!parameters.count(id)) {
log("Parameter %s referenced in format string '%s' not found\n", log_id(id), fmt.c_str());
return {};
}

RTLIL_BACKEND::dump_const(result, parameters.at(id));
}
} else {
result << *it;
}
Expand All @@ -54,6 +85,44 @@
return {result.str()};
}

struct Chunk {
IdString port;
int base, len;

Chunk(IdString id, int base, int len)
: port(id), base(base), len(len) {}

IdString format(Cell *cell)
{
if (len == cell->getPort(port).size())
return port;
else if (len == 1)
return stringf("%s[%d]", port.c_str(), base);
else
return stringf("%s[%d:%d]", port.c_str(), base + len - 1, base);
}

SigSpec sample(Cell *cell)
{
return cell->getPort(port).extract(base, len);
}
};

std::vector<Chunk> collect_chunks(std::vector<std::pair<IdString, int>> bits)
povik marked this conversation as resolved.
Show resolved Hide resolved
{
std::vector<Chunk> ret;
std::sort(bits.begin(), bits.end());
for (auto it = bits.begin(); it != bits.end();) {
auto sep = it + 1;
for (; sep != bits.end() &&
sep->first == it->first &&
sep->second == (sep - 1)->second + 1; sep++);
ret.emplace_back(it->first, it->second, sep - it);
it = sep;
}
return ret;
}

struct WrapcellPass : Pass {
WrapcellPass() : Pass("wrapcell", "wrap individual cells into new modules") {}

Expand All @@ -68,6 +137,10 @@
log("parameter values as specified in curly brackets. If the named module already\n");
log("exists, it is reused.\n");
log("\n");
log("If the template contains the special string '{%unused}', the command tracks\n");

Check warning on line 140 in passes/cmds/wrapcell.cc

View workflow job for this annotation

GitHub Actions / test-compile (ubuntu-latest, clang-14)

more '%' conversions than data arguments [-Wformat-insufficient-args]

Check warning on line 140 in passes/cmds/wrapcell.cc

View workflow job for this annotation

GitHub Actions / test-compile (ubuntu-latest, gcc-10)

format ‘%u’ expects a matching ‘unsigned int’ argument [-Wformat=]

Check warning on line 140 in passes/cmds/wrapcell.cc

View workflow job for this annotation

GitHub Actions / test-compile (ubuntu-20.04, clang-10)

more '%' conversions than data arguments [-Wformat]

Check warning on line 140 in passes/cmds/wrapcell.cc

View workflow job for this annotation

GitHub Actions / test-compile (macos-13, clang)

more '%' conversions than data arguments [-Wformat-insufficient-args]

Check warning on line 140 in passes/cmds/wrapcell.cc

View workflow job for this annotation

GitHub Actions / test-compile (ubuntu-latest, clang-18)

more '%' conversions than data arguments [-Wformat-insufficient-args]

Check warning on line 140 in passes/cmds/wrapcell.cc

View workflow job for this annotation

GitHub Actions / test-compile (ubuntu-latest, clang-18)

more '%' conversions than data arguments [-Wformat-insufficient-args]

Check warning on line 140 in passes/cmds/wrapcell.cc

View workflow job for this annotation

GitHub Actions / test-compile (ubuntu-latest, gcc-13)

format ‘%u’ expects a matching ‘unsigned int’ argument [-Wformat=]

Check warning on line 140 in passes/cmds/wrapcell.cc

View workflow job for this annotation

GitHub Actions / test-compile (ubuntu-latest, gcc-13)

format ‘%u’ expects a matching ‘unsigned int’ argument [-Wformat=]
log("unused output ports -- specialized wrapper modules will be generated per every\n");
log("set of unused ports as appearing on a selected cell.\n");
log("\n");
log(" -setattr <attribute-name>\n");
log(" set the given boolean attribute on each created wrapper module\n");
log("\n");
Expand Down Expand Up @@ -114,43 +187,88 @@
CellTypes ct;
ct.setup();

bool tracking_unused = has_fmt_field(name_fmt, "%unused");

for (auto module : d->selected_modules()) {
SigPool unused;

for (auto wire : module->wires())
if (wire->has_attribute(ID::unused_bits)) {
std::string str = wire->get_string_attribute(ID::unused_bits);
for (auto it = str.begin(); it != str.end();) {
auto sep = it;
for (; sep != str.end() && *sep != ' '; sep++);
unused.add(SigBit(wire, std::stoi(std::string(it, sep))));
for (it = sep; it != str.end() && *it == ' '; it++);
}
}

for (auto cell : module->selected_cells()) {
std::optional<std::string> unescaped_name = format(name_fmt, cell->parameters);
Module *subm;
Cell *subcell;

if (!ct.cell_known(cell->type))
log_error("Non-internal cell type '%s' on cell '%s' in module '%s' unsupported\n",
log_id(cell->type), log_id(cell), log_id(module));

std::vector<std::pair<IdString, int>> unused_outputs, used_outputs;
for (auto conn : cell->connections()) {
if (ct.cell_output(cell->type, conn.first))
for (int i = 0; i < conn.second.size(); i++) {
if (tracking_unused && unused.check(conn.second[i]))
unused_outputs.emplace_back(conn.first, i);
else
used_outputs.emplace_back(conn.first, i);
}
}

ContextData context;
if (!unused_outputs.empty()) {
context.unused_outputs += "_unused";
for (auto chunk : collect_chunks(unused_outputs))
context.unused_outputs += "_" + RTLIL::unescape_id(chunk.format(cell));
}

std::optional<std::string> unescaped_name = format(name_fmt, cell->parameters, context);
if (!unescaped_name)
log_error("Formatting error when processing cell '%s' in module '%s'\n",
log_id(cell), log_id(module));

IdString name = RTLIL::escape_id(unescaped_name.value());
if (d->module(name))
goto replace_cell;

if (d->module(name)) {
cell->type = name;
cell->parameters.clear();
continue;
subm = d->addModule(name);
subcell = subm->addCell("$1", cell->type);
for (auto conn : cell->connections()) {
if (ct.cell_output(cell->type, conn.first)) {
subcell->setPort(conn.first, SigSpec(RTLIL::Sm, conn.second.size()));
povik marked this conversation as resolved.
Show resolved Hide resolved
} else {
Wire *w = subm->addWire(conn.first, conn.second.size());
w->port_input = true;
subcell->setPort(conn.first, w);
}
}

if (!ct.cell_known(cell->type))
log_error("Non-internal cell type '%s' on cell '%s' in module '%s' unsupported\n",
log_id(cell->type), log_id(cell), log_id(module));
for (auto chunk : collect_chunks(used_outputs)) {
Wire *w = subm->addWire(chunk.format(cell), chunk.len);
w->port_output = true;
subcell->connections_[chunk.port].replace(chunk.base, w);
}

Module *subm = d->addModule(name);
Cell *subcell = subm->addCell("$1", cell->type);
for (auto conn : cell->connections()) {
Wire *w = subm->addWire(conn.first, conn.second.size());
if (ct.cell_output(cell->type, w->name))
w->port_output = true;
else
w->port_input = true;
subcell->setPort(conn.first, w);
for (auto chunk : collect_chunks(unused_outputs)) {
widlarizer marked this conversation as resolved.
Show resolved Hide resolved
Wire *w = subm->addWire(chunk.format(cell), chunk.len);
subcell->connections_[chunk.port].replace(chunk.base, w);
}

subcell->parameters = cell->parameters;
subm->fixup_ports();

for (auto rule : attributes) {
if (rule.value_fmt.empty()) {
subm->set_bool_attribute(rule.name);
} else {
std::optional<std::string> value = format(rule.value_fmt, cell->parameters);
std::optional<std::string> value = format(rule.value_fmt, cell->parameters, context);

if (!value)
log_error("Formatting error when processing cell '%s' in module '%s'\n",
Expand All @@ -160,8 +278,20 @@
}
}

cell->type = name;
replace_cell:
cell->parameters.clear();

dict<IdString, SigSpec> new_connections;

for (auto conn : cell->connections())
if (!ct.cell_output(cell->type, conn.first))
new_connections[conn.first] = conn.second;

for (auto chunk : collect_chunks(used_outputs))
new_connections[chunk.format(cell)] = chunk.sample(cell);

cell->type = name;
cell->connections_ = new_connections;
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions tests/various/wrapcell.ys
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,33 @@ select -assert-count 1 top/t:OR_3_4_4
select -assert-none top/t:OR_2_3_3 top/t:OR_3_4_4 %% top/t:* %D
select -assert-mod-count 2 OR_2_3_3 OR_3_4_4
select -assert-mod-count 2 A:bar=w3 A:bar=w4

design -reset
read_verilog <<EOF
module top(
input [1:0] a,
input [2:0] b,
output [2:0] y,
input [2:0] a2,
input [3:0] b2,
output [3:0] y2,
input [1:0] a3,
input [2:0] b3,
output [2:0] y3
);
assign y = a | (*keep*) b;
assign y2 = a2 | (*keep*) b2;
wire [2:0] y3_ = a3 | (*keep*) b3;
assign y3 = {y3_[2], y3_[0]};
endmodule
EOF

opt_clean
wreduce
wrapcell -setattr foo -formatattr bar w{Y_WIDTH} -name OR_{A_WIDTH}_{B_WIDTH}_{Y_WIDTH}{%unused}
select -assert-count 1 top/t:OR_2_3_3
select -assert-count 1 top/t:OR_2_3_3_unused_Y[1]
select -assert-count 1 top/t:OR_3_4_4
select -assert-none top/t:OR_2_3_3 top/t:OR_3_4_4 top/t:OR_2_3_3_unused_Y[1] %% top/t:* %D
select -assert-mod-count 2 OR_2_3_3 OR_3_4_4
select -assert-mod-count 3 A:bar=w3 A:bar=w4
Loading