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

box_derive: New command to derive modules for boxes #4404

Merged
merged 12 commits into from
May 31, 2024
1 change: 1 addition & 0 deletions passes/cmds/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ OBJS += passes/cmds/clean_zerowidth.o
OBJS += passes/cmds/xprop.o
OBJS += passes/cmds/dft_tag.o
OBJS += passes/cmds/future.o
OBJS += passes/cmds/box_derive.o
116 changes: 116 additions & 0 deletions passes/cmds/box_derive.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 Martin Povišer <povik@cutebit.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "kernel/yosys.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct BoxDerivePass : Pass {
BoxDerivePass() : Pass("box_derive", "derive box modules") {}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" box_derive [-base <base_module>] [-naming_attr <attr>] [selection]\n");
log("\n");
log("As part of the assembly of the design hierarchy done by the 'hierarchy' command,\n");
log("specializations of parametric modules are derived on demand: for each choice of\n");
log("parameter values appearing in the design, a copy of the parametric module is\n");
log("derived which is specialized to that choice.\n");
log("\n");
log("This derivation process ignores blackboxes and whiteboxes (boxes). To supplement,\n");
log("this 'box_derive' command can be used to request the derivation of modules based\n");
log("on box instances appearing in the design, which is desirable in certain use\n");
log("cases. Only the selected cells are considered as the instances that steer the\n");
log("derivation process.\n");
log("\n");
log(" -base <base_module>\n");
log(" instead of deriving the module that directly corresponds to each box\n");
log(" instance, derive a specialization of <base_module> (this option applies\n");
log(" to all selected box cells)\n");
log("\n");
log(" -naming_attr <attr>\n");
log(" once a specialization is derived, use the value of the module attribute\n");
log(" <attr> for a name which should be used for the derived module (this\n");
log(" replaces the internal Yosys naming scheme in which the names of derived\n");
log(" modules start with '$paramod$')\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *d) override
{
log_header(d, "Executing BOX_DERIVE pass. (derive modules for boxes)\n");

size_t argidx;
IdString naming_attr;
IdString base_name;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-naming_attr" && argidx + 1 < args.size())
naming_attr = RTLIL::escape_id(args[++argidx]);
else if (args[argidx] == "-base" && argidx + 1 < args.size())
base_name = RTLIL::escape_id(args[++argidx]);
else
break;
}
extra_args(args, argidx, d);

Module *base_override = nullptr;
if (!base_name.empty()) {
base_override = d->module(base_name);
if (!base_override)
log_cmd_error("Base module %s not found.\n", log_id(base_name));
}

dict<std::pair<RTLIL::IdString, dict<RTLIL::IdString, RTLIL::Const>>, Module*> done;

for (auto module : d->selected_modules()) {
for (auto cell : module->selected_cells()) {
Module *inst_module = d->module(cell->type);
if (!inst_module || !inst_module->get_blackbox_attribute())
continue;

Module *base = inst_module;
if (base_override)
base = base_override;

auto index = std::make_pair(base->name, cell->parameters);

if (cell->parameters.empty() || done.count(index))
continue;

IdString derived_type = base->derive(d, cell->parameters);
Module *derived = d->module(derived_type);
log_assert(derived && "Failed to derive module\n");
log_debug("derived %s\n", derived_type.c_str());

if (!naming_attr.empty() && derived->has_attribute(naming_attr)) {
IdString new_name = RTLIL::escape_id(derived->get_string_attribute(naming_attr));
if (!new_name.isPublic())
log_error("Derived module %s cannot be renamed to private name %s.\n",
log_id(derived), log_id(new_name));
derived->attributes.erase(naming_attr);
d->rename(derived, new_name);
}

done[index] = derived;
}
}
}
} BoxDerivePass;

PRIVATE_NAMESPACE_END
41 changes: 30 additions & 11 deletions passes/cmds/select.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,10 @@ struct SelectPass : public Pass {
log(" selection is non-empty. i.e. produce an error if no object or module\n");
log(" matching the selection is found.\n");
log("\n");
log(" -assert-mod-count N\n");
log(" do not modify the current selection. instead assert that the given\n");
log(" selection contains exactly N modules (partially or fully selected).\n");
log("\n");
log(" -assert-count N\n");
log(" do not modify the current selection. instead assert that the given\n");
log(" selection contains exactly N objects.\n");
Expand Down Expand Up @@ -1263,6 +1267,7 @@ struct SelectPass : public Pass {
bool got_module = false;
bool assert_none = false;
bool assert_any = false;
int assert_modcount = -1;
int assert_count = -1;
int assert_max = -1;
int assert_min = -1;
Expand Down Expand Up @@ -1291,6 +1296,10 @@ struct SelectPass : public Pass {
assert_any = true;
continue;
}
if (arg == "-assert-mod-count" && argidx+1 < args.size()) {
assert_modcount = atoi(args[++argidx].c_str());
continue;
}
if (arg == "-assert-count" && argidx+1 < args.size()) {
assert_count = atoi(args[++argidx].c_str());
continue;
Expand Down Expand Up @@ -1345,7 +1354,8 @@ struct SelectPass : public Pass {
}
if (arg.size() > 0 && arg[0] == '-')
log_cmd_error("Unknown option %s.\n", arg.c_str());
bool disable_empty_warning = count_mode || assert_none || assert_any || (assert_count != -1) || (assert_max != -1) || (assert_min != -1);
bool disable_empty_warning = count_mode || assert_none || assert_any || (assert_modcount != -1) ||
(assert_count != -1) || (assert_max != -1) || (assert_min != -1);
select_stmt(design, arg, disable_empty_warning);
sel_str += " " + arg;
}
Expand Down Expand Up @@ -1385,17 +1395,20 @@ struct SelectPass : public Pass {
if (none_mode && args.size() != 2)
log_cmd_error("Option -none can not be combined with any other options.\n");

if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) + (assert_max >= 0) + (assert_min >= 0) > 1)
log_cmd_error("Options -add, -del, -assert-none, -assert-any, assert-count, -assert-max or -assert-min can not be combined.\n");
int common_flagset_tally = add_mode + del_mode + assert_none + assert_any + (assert_modcount >= 0) + (assert_count >= 0) + (assert_max >= 0) + (assert_min >= 0);
const char *common_flagset = "-add, -del, -assert-none, -assert-any, -assert-mod-count, -assert-count, -assert-max, or -assert-min";

if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any, assert-count, -assert-max, or -assert-min.\n");
if (common_flagset_tally > 1)
log_cmd_error("Options %s can not be combined.\n", common_flagset);

if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || !unset_name.empty() || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -unset, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n");
if ((list_mode || !write_file.empty() || count_mode) && common_flagset_tally)
log_cmd_error("Options -list, -write and -count can not be combined with %s.\n", common_flagset);

if (!unset_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || !set_name.empty() || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0))
log_cmd_error("Option -unset can not be combined with -list, -write, -count, -add, -del, -set, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n");
if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || !unset_name.empty() || common_flagset_tally))
log_cmd_error("Option -set can not be combined with -list, -write, -count, -unset, %s.\n", common_flagset);

if (!unset_name.empty() && (list_mode || !write_file.empty() || count_mode || !set_name.empty() || common_flagset_tally))
log_cmd_error("Option -unset can not be combined with -list, -write, -count, -set, %s.\n", common_flagset);

if (work_stack.size() == 0 && got_module) {
RTLIL::Selection sel;
Expand Down Expand Up @@ -1514,15 +1527,16 @@ struct SelectPass : public Pass {
return;
}

if (assert_count >= 0 || assert_max >= 0 || assert_min >= 0)
if (assert_modcount >= 0 || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)
{
int total_count = 0;
int module_count = 0, total_count = 0;
if (work_stack.size() == 0)
log_cmd_error("No selection to check.\n");
RTLIL::Selection *sel = &work_stack.back();
sel->optimize(design);
for (auto mod : design->modules())
if (sel->selected_module(mod->name)) {
module_count++;
for (auto wire : mod->wires())
if (sel->selected_member(mod->name, wire->name))
total_count++;
Expand All @@ -1536,6 +1550,11 @@ struct SelectPass : public Pass {
if (sel->selected_member(mod->name, it.first))
total_count++;
}
if (assert_modcount >= 0 && assert_modcount != module_count)
{
log_error("Assertion failed: selection contains %d modules instead of the asserted %d:%s\n",
module_count, assert_modcount, sel_str.c_str());
}
if (assert_count >= 0 && assert_count != total_count)
{
std::string desc = describe_selection_for_assert(design, sel);
Expand Down
53 changes: 53 additions & 0 deletions tests/various/box_derive.ys
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
read_verilog <<EOF
(* whitebox *)
(* final_name=$sformatf("aa%d", X) *)
module aa(input wire d, output wire q);
parameter [1:0] X = 0;
assign q = X[d];
endmodule

(* whitebox *)
(* final_name=$sformatf("bb%d", X) *)
module bb(input wire d, output wire q);
parameter [1:0] X = 0;
assign q = X[~d];
endmodule

(* whitebox *)
(* final_name=$sformatf("cc%d", X) *)
module cc(input wire d, output wire q);
parameter [1:0] X = 0;
assign q = ~X[d];
endmodule

module top;
wire d, q1, q2, q3, q3, q4, q5, q6;

aa #(.X(1)) aa1(.d(d), .q(q1));
aa #(.X(2)) aa2(.d(d), .q(q2));

bb #(.X(1)) bb1(.d(d), .q(q3));
bb #(.X(3)) bb2(.d(d), .q(q4));

cc #(.X(1)) cc1(.d(d), .q(q5));
cc #(.X(1)) cc2(.d(d), .q(q6));
endmodule
EOF

box_derive -naming_attr final_name top

select -assert-mod-count 1 =aa1
select -assert-mod-count 1 =aa2
select -assert-mod-count 0 =aa3

select -assert-mod-count 1 =bb1
select -assert-mod-count 0 =bb2
select -assert-mod-count 1 =bb3

select -assert-mod-count 1 =cc1
select -assert-mod-count 0 =cc2
select -assert-mod-count 0 =cc3
povik marked this conversation as resolved.
Show resolved Hide resolved

# we are expecting the original aa, bb, cc modules
# and 5 specializations generated by box_derive
select -assert-mod-count 8 =A:whitebox
Loading