mirror of
https://github.com/YosysHQ/yosys
synced 2025-06-20 04:43:40 +00:00
Merge remote-tracking branch 'origin/master' into xaig_dff
This commit is contained in:
commit
8f5710c464
174 changed files with 26477 additions and 2398 deletions
|
@ -16,6 +16,7 @@ endif
|
|||
|
||||
ifneq ($(SMALL),1)
|
||||
OBJS += passes/techmap/iopadmap.o
|
||||
OBJS += passes/techmap/clkbufmap.o
|
||||
OBJS += passes/techmap/hilomap.o
|
||||
OBJS += passes/techmap/extract.o
|
||||
OBJS += passes/techmap/extract_fa.o
|
||||
|
@ -39,6 +40,7 @@ OBJS += passes/techmap/attrmap.o
|
|||
OBJS += passes/techmap/zinit.o
|
||||
OBJS += passes/techmap/dff2dffs.o
|
||||
OBJS += passes/techmap/flowmap.o
|
||||
OBJS += passes/techmap/extractinv.o
|
||||
endif
|
||||
|
||||
GENFILES += passes/techmap/techmap.inc
|
||||
|
|
|
@ -76,8 +76,7 @@ inline std::string remap_name(RTLIL::IdString abc_name)
|
|||
return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1);
|
||||
}
|
||||
|
||||
void handle_loops(RTLIL::Design *design,
|
||||
const dict<IdString,pool<IdString>> &scc_break_inputs)
|
||||
void handle_loops(RTLIL::Design *design)
|
||||
{
|
||||
Pass::call(design, "scc -set_attr abc_scc_id {}");
|
||||
|
||||
|
@ -85,7 +84,7 @@ void handle_loops(RTLIL::Design *design,
|
|||
// cell in the component, and select (and mark) all its output
|
||||
// wires
|
||||
pool<RTLIL::Const> ids_seen;
|
||||
for (auto cell : module->selected_cells()) {
|
||||
for (auto cell : module->cells()) {
|
||||
auto it = cell->attributes.find(ID(abc_scc_id));
|
||||
if (it != cell->attributes.end()) {
|
||||
auto r = ids_seen.insert(it->second);
|
||||
|
@ -114,30 +113,6 @@ void handle_loops(RTLIL::Design *design,
|
|||
}
|
||||
cell->attributes.erase(it);
|
||||
}
|
||||
|
||||
auto jt = scc_break_inputs.find(cell->type);
|
||||
if (jt != scc_break_inputs.end())
|
||||
for (auto port_name : jt->second) {
|
||||
RTLIL::SigSpec sig;
|
||||
auto &rhs = cell->connections_.at(port_name);
|
||||
for (auto b : rhs) {
|
||||
Wire *w = b.wire;
|
||||
if (!w) continue;
|
||||
w->port_output = true;
|
||||
w->set_bool_attribute(ID(abc_scc_break));
|
||||
w = module->wire(stringf("%s.abci", w->name.c_str()));
|
||||
if (!w) {
|
||||
w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire));
|
||||
w->port_input = true;
|
||||
}
|
||||
else {
|
||||
log_assert(b.offset < GetSize(w));
|
||||
log_assert(w->port_input);
|
||||
}
|
||||
sig.append(RTLIL::SigBit(w, b.offset));
|
||||
}
|
||||
rhs = sig;
|
||||
}
|
||||
}
|
||||
|
||||
module->fixup_ports();
|
||||
|
@ -269,11 +244,10 @@ struct abc_output_filter
|
|||
};
|
||||
|
||||
void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file,
|
||||
bool cleanup, vector<int> lut_costs, bool /*dff_mode*/, std::string clk_str,
|
||||
bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
|
||||
bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
|
||||
bool show_tempdir, std::string box_file, std::string lut_file,
|
||||
std::string wire_delay, const dict<int,IdString> &box_lookup,
|
||||
const dict<IdString,pool<IdString>> &scc_break_inputs
|
||||
std::string wire_delay, const dict<int,IdString> &box_lookup
|
||||
)
|
||||
{
|
||||
module = current_module;
|
||||
|
@ -309,8 +283,8 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0));
|
||||
}
|
||||
|
||||
//if (dff_mode && clk_sig.empty())
|
||||
// log_cmd_error("Clock domain %s not found.\n", clk_str.c_str());
|
||||
if (dff_mode && clk_sig.empty())
|
||||
log_cmd_error("Clock domain %s not found.\n", clk_str.c_str());
|
||||
|
||||
std::string tempdir_name = "/tmp/yosys-abc-XXXXXX";
|
||||
if (!cleanup)
|
||||
|
@ -383,7 +357,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
fprintf(f, "%s\n", abc_script.c_str());
|
||||
fclose(f);
|
||||
|
||||
if (/*dff_mode ||*/ !clk_str.empty())
|
||||
if (dff_mode || !clk_str.empty())
|
||||
{
|
||||
if (clk_sig.size() == 0)
|
||||
log("No%s clock domain found. Not extracting any FF cells.\n", clk_str.empty() ? "" : " matching");
|
||||
|
@ -413,16 +387,13 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
RTLIL::Selection& sel = design->selection_stack.back();
|
||||
sel.select(module);
|
||||
|
||||
handle_loops(design, scc_break_inputs);
|
||||
handle_loops(design);
|
||||
|
||||
Pass::call(design, "aigmap");
|
||||
|
||||
//log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
|
||||
// count_gates, GetSize(signal_list), count_input, count_output);
|
||||
|
||||
#if 0
|
||||
Pass::call(design, stringf("write_verilog -noexpr -norename %s/before.v", tempdir_name.c_str()));
|
||||
#endif
|
||||
Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str()));
|
||||
|
||||
std::string buffer;
|
||||
|
@ -531,12 +502,6 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
for (int i = 0; i < GetSize(w); i++)
|
||||
output_bits.insert({wire, i});
|
||||
}
|
||||
|
||||
auto jt = w->attributes.find("\\init");
|
||||
if (jt != w->attributes.end()) {
|
||||
auto r = remap_wire->attributes.insert(std::make_pair("\\init", jt->second));
|
||||
log_assert(r.second);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : module->connections_) {
|
||||
|
@ -578,6 +543,8 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
if (mapped_cell->type == ID($_NOT_)) {
|
||||
RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A);
|
||||
RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y);
|
||||
bit_users[a_bit].insert(mapped_cell->name);
|
||||
bit_drivers[y_bit].insert(mapped_cell->name);
|
||||
|
||||
if (!a_bit.wire) {
|
||||
mapped_cell->setPort(ID::Y, module->addWire(NEW_ID));
|
||||
|
@ -585,8 +552,8 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
log_assert(wire);
|
||||
module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1);
|
||||
}
|
||||
else {
|
||||
RTLIL::Cell* driving_lut = nullptr;
|
||||
else if (!lut_costs.empty() || !lut_file.empty()) {
|
||||
RTLIL::Cell* driver_lut = nullptr;
|
||||
// ABC can return NOT gates that drive POs
|
||||
if (!a_bit.wire->port_input) {
|
||||
// If it's not a NOT gate that that comes from a PI directly,
|
||||
|
@ -598,10 +565,10 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
driver_name = stringf("%s$lut", a_bit.wire->name.c_str());
|
||||
else
|
||||
driver_name = stringf("%s[%d]$lut", a_bit.wire->name.c_str(), a_bit.offset);
|
||||
driving_lut = mapped_mod->cell(driver_name);
|
||||
driver_lut = mapped_mod->cell(driver_name);
|
||||
}
|
||||
|
||||
if (!driving_lut) {
|
||||
if (!driver_lut) {
|
||||
// If a driver couldn't be found (could be from PI or box CI)
|
||||
// then implement using a LUT
|
||||
cell = module->addLut(remap_name(stringf("%s$lut", mapped_cell->name.c_str())),
|
||||
|
@ -610,13 +577,13 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
RTLIL::Const::from_string("01"));
|
||||
bit2sinks[cell->getPort(ID::A)].push_back(cell);
|
||||
cell_stats[ID($lut)]++;
|
||||
bit_users[a_bit].insert(mapped_cell->name);
|
||||
bit_drivers[y_bit].insert(mapped_cell->name);
|
||||
}
|
||||
else
|
||||
not2drivers[mapped_cell] = driving_lut;
|
||||
not2drivers[mapped_cell] = driver_lut;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
log_abort();
|
||||
if (cell && markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
|
||||
continue;
|
||||
}
|
||||
|
@ -700,32 +667,31 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
}
|
||||
|
||||
for (auto &it : cell_stats)
|
||||
log("ABC RESULTS: %15s cells: %8d\n", log_id(it.first), it.second);
|
||||
log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
|
||||
int in_wires = 0, out_wires = 0;
|
||||
|
||||
// Stitch in mapped_mod's inputs/outputs into module
|
||||
for (auto port_name : mapped_mod->ports) {
|
||||
RTLIL::Wire *port = mapped_mod->wire(port_name);
|
||||
log_assert(port);
|
||||
RTLIL::Wire *wire = module->wire(port->name);
|
||||
for (auto port : mapped_mod->ports) {
|
||||
RTLIL::Wire *w = mapped_mod->wire(port);
|
||||
RTLIL::Wire *wire = module->wire(port);
|
||||
log_assert(wire);
|
||||
RTLIL::Wire *remap_wire = module->wire(remap_name(port->name));
|
||||
RTLIL::Wire *remap_wire = module->wire(remap_name(port));
|
||||
RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire));
|
||||
log_assert(GetSize(signal) >= GetSize(remap_wire));
|
||||
|
||||
RTLIL::SigSig conn;
|
||||
if (port->port_input) {
|
||||
conn.first = remap_wire;
|
||||
conn.second = signal;
|
||||
in_wires++;
|
||||
module->connect(conn);
|
||||
}
|
||||
if (port->port_output) {
|
||||
if (w->port_output) {
|
||||
conn.first = signal;
|
||||
conn.second = remap_wire;
|
||||
out_wires++;
|
||||
module->connect(conn);
|
||||
}
|
||||
else if (w->port_input) {
|
||||
conn.first = remap_wire;
|
||||
conn.second = signal;
|
||||
in_wires++;
|
||||
module->connect(conn);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : bit_users)
|
||||
|
@ -733,21 +699,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
for (auto driver_cell : bit_drivers.at(it.first))
|
||||
for (auto user_cell : it.second)
|
||||
toposort.edge(driver_cell, user_cell);
|
||||
#if 0
|
||||
toposort.analyze_loops = true;
|
||||
#endif
|
||||
bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
|
||||
#if 0
|
||||
unsigned i = 0;
|
||||
for (auto &it : toposort.loops) {
|
||||
log(" loop %d\n", i++);
|
||||
for (auto cell_name : it) {
|
||||
auto cell = mapped_mod->cell(cell_name);
|
||||
log_assert(cell);
|
||||
log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
log_assert(no_loops);
|
||||
|
||||
for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) {
|
||||
|
@ -1048,7 +1000,7 @@ struct Abc9Pass : public Pass {
|
|||
fast_mode = true;
|
||||
continue;
|
||||
}
|
||||
//if (arg == "-retime") {
|
||||
//if (arg == "-dff") {
|
||||
// dff_mode = true;
|
||||
// continue;
|
||||
//}
|
||||
|
@ -1075,9 +1027,6 @@ struct Abc9Pass : public Pass {
|
|||
}
|
||||
if (arg == "-box" && argidx+1 < args.size()) {
|
||||
box_file = args[++argidx];
|
||||
rewrite_filename(box_file);
|
||||
if (!box_file.empty() && !is_absolute_path(box_file))
|
||||
box_file = std::string(pwd) + "/" + box_file;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-W" && argidx+1 < args.size()) {
|
||||
|
@ -1088,11 +1037,15 @@ struct Abc9Pass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (lut_costs.empty() && lut_file.empty())
|
||||
log_cmd_error("abc9 must be called with '-lut' or '-luts'\n");
|
||||
// ABC expects a box file for XAIG
|
||||
if (box_file.empty())
|
||||
box_file = "+/dummy.box";
|
||||
|
||||
rewrite_filename(box_file);
|
||||
if (!box_file.empty() && !is_absolute_path(box_file))
|
||||
box_file = std::string(pwd) + "/" + box_file;
|
||||
|
||||
dict<int,IdString> box_lookup;
|
||||
dict<IdString,pool<IdString>> scc_break_inputs;
|
||||
for (auto m : design->modules()) {
|
||||
auto it = m->attributes.find(ID(abc_box_id));
|
||||
if (it == m->attributes.end())
|
||||
|
@ -1110,17 +1063,13 @@ struct Abc9Pass : public Pass {
|
|||
for (auto p : m->ports) {
|
||||
auto w = m->wire(p);
|
||||
log_assert(w);
|
||||
if (w->port_input) {
|
||||
if (w->attributes.count(ID(abc_scc_break)))
|
||||
scc_break_inputs[m->name].insert(p);
|
||||
if (w->attributes.count(ID(abc_carry))) {
|
||||
if (w->attributes.count(ID(abc_carry))) {
|
||||
if (w->port_input) {
|
||||
if (carry_in)
|
||||
log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
|
||||
carry_in = w;
|
||||
}
|
||||
}
|
||||
if (w->port_output) {
|
||||
if (w->attributes.count(ID(abc_carry))) {
|
||||
else if (w->port_output) {
|
||||
if (carry_out)
|
||||
log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
|
||||
carry_out = w;
|
||||
|
@ -1177,7 +1126,8 @@ struct Abc9Pass : public Pass {
|
|||
|
||||
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, false, clk_str, keepff,
|
||||
delay_target, lutin_shared, fast_mode, show_tempdir,
|
||||
box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
|
||||
box_file, lut_file, wire_delay, box_lookup);
|
||||
|
||||
design->selection_stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
@ -1361,36 +1311,20 @@ struct Abc9Pass : public Pass {
|
|||
std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)),
|
||||
std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first)));
|
||||
|
||||
design->selection_stack.emplace_back(false);
|
||||
|
||||
for (auto &it : assigned_cells) {
|
||||
// FIXME: abc9_module calls below can delete cells,
|
||||
// leaving a dangling pointer here...
|
||||
clk_polarity = std::get<0>(it.first);
|
||||
clk_sig = assign_map(std::get<1>(it.first));
|
||||
en_polarity = std::get<2>(it.first);
|
||||
en_sig = assign_map(std::get<3>(it.first));
|
||||
|
||||
pool<RTLIL::IdString> assigned_names;
|
||||
for (auto i : it.second)
|
||||
assigned_names.insert(i->name);
|
||||
RTLIL::Selection& sel = design->selection_stack.back();
|
||||
sel.selected_members[mod->name] = std::move(assigned_names);
|
||||
|
||||
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$",
|
||||
keepff, delay_target, lutin_shared, fast_mode, show_tempdir,
|
||||
box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
|
||||
box_file, lut_file, wire_delay, box_lookup);
|
||||
assign_map.set(mod);
|
||||
}
|
||||
|
||||
design->selection_stack.pop_back();
|
||||
}
|
||||
|
||||
assign_map.clear();
|
||||
|
||||
// The "clean" pass also contains a design->check() call
|
||||
Pass::call(design, "clean");
|
||||
|
||||
log_pop();
|
||||
}
|
||||
} Abc9Pass;
|
||||
|
|
|
@ -48,14 +48,25 @@ struct AlumaccWorker
|
|||
RTLIL::SigSpec cached_cf, cached_of, cached_sf;
|
||||
|
||||
RTLIL::SigSpec get_lt() {
|
||||
if (GetSize(cached_lt) == 0)
|
||||
cached_lt = is_signed ? alu_cell->module->Xor(NEW_ID, get_of(), get_sf()) : get_cf();
|
||||
if (GetSize(cached_lt) == 0) {
|
||||
if (is_signed) {
|
||||
get_of();
|
||||
get_sf();
|
||||
cached_lt = alu_cell->module->Xor(NEW_ID, cached_of, cached_sf);
|
||||
}
|
||||
else
|
||||
cached_lt = get_cf();
|
||||
}
|
||||
return cached_lt;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec get_gt() {
|
||||
if (GetSize(cached_gt) == 0)
|
||||
cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq()), false, alu_cell->get_src_attribute());
|
||||
if (GetSize(cached_gt) == 0) {
|
||||
get_lt();
|
||||
get_eq();
|
||||
SigSpec Or = alu_cell->module->Or(NEW_ID, cached_lt, cached_eq);
|
||||
cached_gt = alu_cell->module->Not(NEW_ID, Or, false, alu_cell->get_src_attribute());
|
||||
}
|
||||
return cached_gt;
|
||||
}
|
||||
|
||||
|
|
|
@ -143,6 +143,82 @@ void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actio
|
|||
attributes.swap(new_attributes);
|
||||
}
|
||||
|
||||
void log_attrmap_paramap_options()
|
||||
{
|
||||
log(" -tocase <name>\n");
|
||||
log(" Match attribute names case-insensitively and set it to the specified\n");
|
||||
log(" name.\n");
|
||||
log("\n");
|
||||
log(" -rename <old_name> <new_name>\n");
|
||||
log(" Rename attributes as specified\n");
|
||||
log("\n");
|
||||
log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
|
||||
log(" Map key/value pairs as indicated.\n");
|
||||
log("\n");
|
||||
log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
|
||||
log(" Like -map, but use case-insensitive match for <old_value> when\n");
|
||||
log(" it is a string value.\n");
|
||||
log("\n");
|
||||
log(" -remove <name>=<value>\n");
|
||||
log(" Remove attributes matching this pattern.\n");
|
||||
}
|
||||
|
||||
bool parse_attrmap_paramap_options(size_t &argidx, std::vector<std::string> &args, vector<std::unique_ptr<AttrmapAction>> &actions)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-tocase" && argidx+1 < args.size()) {
|
||||
auto action = new AttrmapTocase;
|
||||
action->name = args[++argidx];
|
||||
actions.push_back(std::unique_ptr<AttrmapAction>(action));
|
||||
return true;
|
||||
}
|
||||
if (arg == "-rename" && argidx+2 < args.size()) {
|
||||
auto action = new AttrmapRename;
|
||||
action->old_name = args[++argidx];
|
||||
action->new_name = args[++argidx];
|
||||
actions.push_back(std::unique_ptr<AttrmapAction>(action));
|
||||
return true;
|
||||
}
|
||||
if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
|
||||
string arg1 = args[++argidx];
|
||||
string arg2 = args[++argidx];
|
||||
string val1, val2;
|
||||
size_t p = arg1.find("=");
|
||||
if (p != string::npos) {
|
||||
val1 = arg1.substr(p+1);
|
||||
arg1 = arg1.substr(0, p);
|
||||
}
|
||||
p = arg2.find("=");
|
||||
if (p != string::npos) {
|
||||
val2 = arg2.substr(p+1);
|
||||
arg2 = arg2.substr(0, p);
|
||||
}
|
||||
auto action = new AttrmapMap;
|
||||
action->imap = (arg == "-map");
|
||||
action->old_name = arg1;
|
||||
action->new_name = arg2;
|
||||
action->old_value = val1;
|
||||
action->new_value = val2;
|
||||
actions.push_back(std::unique_ptr<AttrmapAction>(action));
|
||||
return true;
|
||||
}
|
||||
if (arg == "-remove" && argidx+1 < args.size()) {
|
||||
string arg1 = args[++argidx], val1;
|
||||
size_t p = arg1.find("=");
|
||||
if (p != string::npos) {
|
||||
val1 = arg1.substr(p+1);
|
||||
arg1 = arg1.substr(0, p);
|
||||
}
|
||||
auto action = new AttrmapRemove;
|
||||
action->name = arg1;
|
||||
action->has_value = (p != string::npos);
|
||||
action->value = val1;
|
||||
actions.push_back(std::unique_ptr<AttrmapAction>(action));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct AttrmapPass : public Pass {
|
||||
AttrmapPass() : Pass("attrmap", "renaming attributes") { }
|
||||
void help() YS_OVERRIDE
|
||||
|
@ -151,25 +227,10 @@ struct AttrmapPass : public Pass {
|
|||
log("\n");
|
||||
log(" attrmap [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This command renames attributes and/or mapps key/value pairs to\n");
|
||||
log("This command renames attributes and/or maps key/value pairs to\n");
|
||||
log("other key/value pairs.\n");
|
||||
log("\n");
|
||||
log(" -tocase <name>\n");
|
||||
log(" Match attribute names case-insensitively and set it to the specified\n");
|
||||
log(" name.\n");
|
||||
log("\n");
|
||||
log(" -rename <old_name> <new_name>\n");
|
||||
log(" Rename attributes as specified\n");
|
||||
log("\n");
|
||||
log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
|
||||
log(" Map key/value pairs as indicated.\n");
|
||||
log("\n");
|
||||
log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
|
||||
log(" Like -map, but use case-insensitive match for <old_value> when\n");
|
||||
log(" it is a string value.\n");
|
||||
log("\n");
|
||||
log(" -remove <name>=<value>\n");
|
||||
log(" Remove attributes matching this pattern.\n");
|
||||
log_attrmap_paramap_options();
|
||||
log("\n");
|
||||
log(" -modattr\n");
|
||||
log(" Operate on module attributes instead of attributes on wires and cells.\n");
|
||||
|
@ -190,58 +251,9 @@ struct AttrmapPass : public Pass {
|
|||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-tocase" && argidx+1 < args.size()) {
|
||||
auto action = new AttrmapTocase;
|
||||
action->name = args[++argidx];
|
||||
actions.push_back(std::unique_ptr<AttrmapAction>(action));
|
||||
if (parse_attrmap_paramap_options(argidx, args, actions))
|
||||
continue;
|
||||
}
|
||||
if (arg == "-rename" && argidx+2 < args.size()) {
|
||||
auto action = new AttrmapRename;
|
||||
action->old_name = args[++argidx];
|
||||
action->new_name = args[++argidx];
|
||||
actions.push_back(std::unique_ptr<AttrmapAction>(action));
|
||||
continue;
|
||||
}
|
||||
if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
|
||||
string arg1 = args[++argidx];
|
||||
string arg2 = args[++argidx];
|
||||
string val1, val2;
|
||||
size_t p = arg1.find("=");
|
||||
if (p != string::npos) {
|
||||
val1 = arg1.substr(p+1);
|
||||
arg1 = arg1.substr(0, p);
|
||||
}
|
||||
p = arg2.find("=");
|
||||
if (p != string::npos) {
|
||||
val2 = arg2.substr(p+1);
|
||||
arg2 = arg2.substr(0, p);
|
||||
}
|
||||
auto action = new AttrmapMap;
|
||||
action->imap = (arg == "-map");
|
||||
action->old_name = arg1;
|
||||
action->new_name = arg2;
|
||||
action->old_value = val1;
|
||||
action->new_value = val2;
|
||||
actions.push_back(std::unique_ptr<AttrmapAction>(action));
|
||||
continue;
|
||||
}
|
||||
if (arg == "-remove" && argidx+1 < args.size()) {
|
||||
string arg1 = args[++argidx], val1;
|
||||
size_t p = arg1.find("=");
|
||||
if (p != string::npos) {
|
||||
val1 = arg1.substr(p+1);
|
||||
arg1 = arg1.substr(0, p);
|
||||
}
|
||||
auto action = new AttrmapRemove;
|
||||
action->name = arg1;
|
||||
action->has_value = (p != string::npos);
|
||||
action->value = val1;
|
||||
actions.push_back(std::unique_ptr<AttrmapAction>(action));
|
||||
continue;
|
||||
}
|
||||
if (arg == "-modattr") {
|
||||
if (args[argidx] == "-modattr") {
|
||||
modattr_mode = true;
|
||||
continue;
|
||||
}
|
||||
|
@ -287,4 +299,43 @@ struct AttrmapPass : public Pass {
|
|||
}
|
||||
} AttrmapPass;
|
||||
|
||||
struct ParamapPass : public Pass {
|
||||
ParamapPass() : Pass("paramap", "renaming cell parameters") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" paramap [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This command renames cell parameters and/or maps key/value pairs to\n");
|
||||
log("other key/value pairs.\n");
|
||||
log("\n");
|
||||
log_attrmap_paramap_options();
|
||||
log("\n");
|
||||
log("For example, mapping Diamond-style ECP5 \"init\" attributes to Yosys-style:\n");
|
||||
log("\n");
|
||||
log(" paramap -tocase INIT t:LUT4\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing PARAMAP pass (move or copy cell parameters).\n");
|
||||
|
||||
vector<std::unique_ptr<AttrmapAction>> actions;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (parse_attrmap_paramap_options(argidx, args, actions))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
for (auto cell : module->selected_cells())
|
||||
attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->parameters);
|
||||
}
|
||||
} ParamapPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
298
passes/techmap/clkbufmap.cc
Normal file
298
passes/techmap/clkbufmap.cc
Normal file
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
* Copyright (C) 2019 Marcin Kościelnicki <mwk@0x04.net>
|
||||
*
|
||||
* 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"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
void split_portname_pair(std::string &port1, std::string &port2)
|
||||
{
|
||||
size_t pos = port1.find_first_of(':');
|
||||
if (pos != std::string::npos) {
|
||||
port2 = port1.substr(pos+1);
|
||||
port1 = port1.substr(0, pos);
|
||||
}
|
||||
}
|
||||
|
||||
struct ClkbufmapPass : public Pass {
|
||||
ClkbufmapPass() : Pass("clkbufmap", "insert global buffers on clock networks") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" clkbufmap [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Inserts global buffers between nets connected to clock inputs and their drivers.\n");
|
||||
log("\n");
|
||||
log("In the absence of any selection, all wires without the 'clkbuf_inhibit'\n");
|
||||
log("attribute will be considered for global buffer insertion.\n");
|
||||
log("Alternatively, to consider all wires without the 'buffer_type' attribute set to\n");
|
||||
log("'none' or 'bufr' one would specify:\n");
|
||||
log(" 'w:* a:buffer_type=none a:buffer_type=bufr %%u %%d'\n");
|
||||
log("as the selection.\n");
|
||||
log("\n");
|
||||
log(" -buf <celltype> <portname_out>:<portname_in>\n");
|
||||
log(" Specifies the cell type to use for the global buffers\n");
|
||||
log(" and its port names. The first port will be connected to\n");
|
||||
log(" the clock network sinks, and the second will be connected\n");
|
||||
log(" to the actual clock source. This option is required.\n");
|
||||
log("\n");
|
||||
log(" -inpad <celltype> <portname_out>:<portname_in>\n");
|
||||
log(" If specified, a PAD cell of the given type is inserted on\n");
|
||||
log(" clock nets that are also top module's inputs (in addition\n");
|
||||
log(" to the global buffer).\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void module_queue(Design *design, Module *module, std::vector<Module *> &modules_sorted, pool<Module *> &modules_processed) {
|
||||
if (modules_processed.count(module))
|
||||
return;
|
||||
for (auto cell : module->cells()) {
|
||||
Module *submodule = design->module(cell->type);
|
||||
if (!submodule)
|
||||
continue;
|
||||
module_queue(design, submodule, modules_sorted, modules_processed);
|
||||
}
|
||||
modules_sorted.push_back(module);
|
||||
modules_processed.insert(module);
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing CLKBUFMAP pass (inserting global clock buffers).\n");
|
||||
|
||||
std::string buf_celltype, buf_portname, buf_portname2;
|
||||
std::string inpad_celltype, inpad_portname, inpad_portname2;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-buf" && argidx+2 < args.size()) {
|
||||
buf_celltype = args[++argidx];
|
||||
buf_portname = args[++argidx];
|
||||
split_portname_pair(buf_portname, buf_portname2);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-inpad" && argidx+2 < args.size()) {
|
||||
inpad_celltype = args[++argidx];
|
||||
inpad_portname = args[++argidx];
|
||||
split_portname_pair(inpad_portname, inpad_portname2);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
bool select = false;
|
||||
if (argidx < args.size()) {
|
||||
if (args[argidx].compare(0, 1, "-") != 0)
|
||||
select = true;
|
||||
extra_args(args, argidx, design);
|
||||
}
|
||||
|
||||
if (buf_celltype.empty())
|
||||
log_error("The -buf option is required.\n");
|
||||
|
||||
// Cell type, port name, bit index.
|
||||
pool<pair<IdString, pair<IdString, int>>> sink_ports;
|
||||
pool<pair<IdString, pair<IdString, int>>> buf_ports;
|
||||
|
||||
// Process submodules before module using them.
|
||||
std::vector<Module *> modules_sorted;
|
||||
pool<Module *> modules_processed;
|
||||
for (auto module : design->selected_modules())
|
||||
module_queue(design, module, modules_sorted, modules_processed);
|
||||
|
||||
for (auto module : modules_sorted)
|
||||
{
|
||||
if (module->get_blackbox_attribute()) {
|
||||
for (auto port : module->ports) {
|
||||
auto wire = module->wire(port);
|
||||
if (wire->get_bool_attribute("\\clkbuf_driver"))
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
|
||||
if (wire->get_bool_attribute("\\clkbuf_sink"))
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
pool<SigBit> sink_wire_bits;
|
||||
pool<SigBit> buf_wire_bits;
|
||||
pool<SigBit> driven_wire_bits;
|
||||
SigMap sigmap(module);
|
||||
// bit -> (buffer, buffer's input)
|
||||
dict<SigBit, pair<Cell *, Wire *>> buffered_bits;
|
||||
|
||||
// First, collect nets that could use a clock buffer.
|
||||
for (auto cell : module->cells())
|
||||
for (auto port : cell->connections())
|
||||
for (int i = 0; i < port.second.size(); i++)
|
||||
if (sink_ports.count(make_pair(cell->type, make_pair(port.first, i))))
|
||||
sink_wire_bits.insert(sigmap(port.second[i]));
|
||||
|
||||
// Second, collect ones that already have a clock buffer.
|
||||
for (auto cell : module->cells())
|
||||
for (auto port : cell->connections())
|
||||
for (int i = 0; i < port.second.size(); i++)
|
||||
if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i))))
|
||||
buf_wire_bits.insert(sigmap(port.second[i]));
|
||||
|
||||
// Collect all driven bits.
|
||||
for (auto cell : module->cells())
|
||||
for (auto port : cell->connections())
|
||||
if (cell->output(port.first))
|
||||
for (int i = 0; i < port.second.size(); i++)
|
||||
driven_wire_bits.insert(port.second[i]);
|
||||
|
||||
// Insert buffers.
|
||||
std::vector<pair<Wire *, Wire *>> input_queue;
|
||||
// Copy current wire list, as we will be adding new ones during iteration.
|
||||
std::vector<Wire *> wires(module->wires());
|
||||
for (auto wire : wires)
|
||||
{
|
||||
// Should not happen.
|
||||
if (wire->port_input && wire->port_output)
|
||||
continue;
|
||||
bool process_wire = module->selected(wire);
|
||||
if (!select && wire->get_bool_attribute("\\clkbuf_inhibit"))
|
||||
process_wire = false;
|
||||
if (!process_wire) {
|
||||
// This wire is supposed to be bypassed, so make sure we don't buffer it in
|
||||
// some buffer higher up in the hierarchy.
|
||||
if (wire->port_output)
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
|
||||
continue;
|
||||
}
|
||||
|
||||
pool<int> input_bits;
|
||||
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
{
|
||||
SigBit wire_bit(wire, i);
|
||||
SigBit mapped_wire_bit = sigmap(wire_bit);
|
||||
if (buf_wire_bits.count(mapped_wire_bit)) {
|
||||
// Already buffered downstream. If this is an output, mark it.
|
||||
if (wire->port_output)
|
||||
buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
|
||||
} else if (!sink_wire_bits.count(mapped_wire_bit)) {
|
||||
// Nothing to do.
|
||||
} else if (driven_wire_bits.count(wire_bit) || (wire->port_input && module->get_bool_attribute("\\top"))) {
|
||||
// Clock network not yet buffered, driven by one of
|
||||
// our cells or a top-level input -- buffer it.
|
||||
|
||||
log("Inserting %s on %s.%s[%d].\n", buf_celltype.c_str(), log_id(module), log_id(wire), i);
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(buf_celltype));
|
||||
Wire *iwire = module->addWire(NEW_ID);
|
||||
cell->setPort(RTLIL::escape_id(buf_portname), mapped_wire_bit);
|
||||
cell->setPort(RTLIL::escape_id(buf_portname2), iwire);
|
||||
if (wire->port_input && !inpad_celltype.empty() && module->get_bool_attribute("\\top")) {
|
||||
log("Inserting %s on %s.%s[%d].\n", inpad_celltype.c_str(), log_id(module), log_id(wire), i);
|
||||
RTLIL::Cell *cell2 = module->addCell(NEW_ID, RTLIL::escape_id(inpad_celltype));
|
||||
cell2->setPort(RTLIL::escape_id(inpad_portname), iwire);
|
||||
iwire = module->addWire(NEW_ID);
|
||||
cell2->setPort(RTLIL::escape_id(inpad_portname2), iwire);
|
||||
}
|
||||
buffered_bits[mapped_wire_bit] = make_pair(cell, iwire);
|
||||
|
||||
if (wire->port_input) {
|
||||
input_bits.insert(i);
|
||||
}
|
||||
} else if (wire->port_input) {
|
||||
// A clock input in a submodule -- mark it, let higher level
|
||||
// worry about it.
|
||||
if (wire->port_input)
|
||||
sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
|
||||
}
|
||||
}
|
||||
if (!input_bits.empty()) {
|
||||
// This is an input port and some buffers were inserted -- we need
|
||||
// to create a new input wire and transfer attributes.
|
||||
Wire *new_wire = module->addWire(NEW_ID, wire);
|
||||
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
SigBit wire_bit(wire, i);
|
||||
SigBit mapped_wire_bit = sigmap(wire_bit);
|
||||
auto it = buffered_bits.find(mapped_wire_bit);
|
||||
if (it != buffered_bits.end()) {
|
||||
|
||||
module->connect(it->second.second, SigSpec(new_wire, i));
|
||||
} else {
|
||||
module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
|
||||
}
|
||||
}
|
||||
input_queue.push_back(make_pair(wire, new_wire));
|
||||
}
|
||||
}
|
||||
|
||||
// Mark any newly-buffered output ports as such.
|
||||
for (auto wire : module->selected_wires()) {
|
||||
if (wire->port_input || !wire->port_output)
|
||||
continue;
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
{
|
||||
SigBit wire_bit(wire, i);
|
||||
SigBit mapped_wire_bit = sigmap(wire_bit);
|
||||
if (buffered_bits.count(mapped_wire_bit))
|
||||
buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
|
||||
}
|
||||
}
|
||||
|
||||
// Reconnect the drivers to buffer inputs.
|
||||
for (auto cell : module->cells())
|
||||
for (auto port : cell->connections()) {
|
||||
if (!cell->output(port.first))
|
||||
continue;
|
||||
SigSpec sig = port.second;
|
||||
bool newsig = false;
|
||||
for (auto &bit : sig) {
|
||||
const auto it = buffered_bits.find(sigmap(bit));
|
||||
if (it == buffered_bits.end())
|
||||
continue;
|
||||
// Avoid substituting buffer's own output pin.
|
||||
if (cell == it->second.first)
|
||||
continue;
|
||||
bit = it->second.second;
|
||||
newsig = true;
|
||||
}
|
||||
if (newsig)
|
||||
cell->setPort(port.first, sig);
|
||||
}
|
||||
|
||||
// This has to be done last, to avoid upsetting sigmap before the port reconnections.
|
||||
for (auto &it : input_queue) {
|
||||
Wire *wire = it.first;
|
||||
Wire *new_wire = it.second;
|
||||
module->swap_names(new_wire, wire);
|
||||
wire->attributes.clear();
|
||||
wire->port_id = 0;
|
||||
wire->port_input = false;
|
||||
wire->port_output = false;
|
||||
}
|
||||
|
||||
module->fixup_ports();
|
||||
}
|
||||
}
|
||||
} ClkbufmapPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -34,11 +34,16 @@ struct Dff2dffsPass : public Pass {
|
|||
log("Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before\n");
|
||||
log("dff2dffe for SR over CE priority.\n");
|
||||
log("\n");
|
||||
log(" -match-init\n");
|
||||
log(" Disallow merging synchronous set/reset that has polarity opposite of the\n");
|
||||
log(" output wire's init attribute (if any).\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n");
|
||||
|
||||
bool match_init = false;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
|
@ -46,6 +51,10 @@ struct Dff2dffsPass : public Pass {
|
|||
// singleton_mode = true;
|
||||
// continue;
|
||||
// }
|
||||
if (args[argidx] == "-match-init") {
|
||||
match_init = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -96,9 +105,6 @@ struct Dff2dffsPass : public Pass {
|
|||
SigBit bit_b = sigmap(mux_cell->getPort(ID::B));
|
||||
SigBit bit_s = sigmap(mux_cell->getPort(ID(S)));
|
||||
|
||||
log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
|
||||
log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
|
||||
|
||||
SigBit sr_val, sr_sig;
|
||||
bool invert_sr;
|
||||
sr_sig = bit_s;
|
||||
|
@ -113,6 +119,23 @@ struct Dff2dffsPass : public Pass {
|
|||
invert_sr = false;
|
||||
}
|
||||
|
||||
if (match_init) {
|
||||
SigBit bit_q = cell->getPort(ID(Q));
|
||||
if (bit_q.wire) {
|
||||
auto it = bit_q.wire->attributes.find(ID(init));
|
||||
if (it != bit_q.wire->attributes.end()) {
|
||||
auto init_val = it->second[bit_q.offset];
|
||||
if (init_val == State::S1 && sr_val != State::S1)
|
||||
continue;
|
||||
if (init_val == State::S0 && sr_val != State::S0)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
|
||||
log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
|
||||
|
||||
if (sr_val == State::S1) {
|
||||
if (cell->type == ID($_DFF_N_)) {
|
||||
if (invert_sr) cell->type = ID($__DFFS_NN1_);
|
||||
|
|
123
passes/techmap/extractinv.cc
Normal file
123
passes/techmap/extractinv.cc
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
* Copyright (C) 2019 Marcin Kościelnicki <mwk@0x04.net>
|
||||
*
|
||||
* 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"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
void split_portname_pair(std::string &port1, std::string &port2)
|
||||
{
|
||||
size_t pos = port1.find_first_of(':');
|
||||
if (pos != std::string::npos) {
|
||||
port2 = port1.substr(pos+1);
|
||||
port1 = port1.substr(0, pos);
|
||||
}
|
||||
}
|
||||
|
||||
struct ExtractinvPass : public Pass {
|
||||
ExtractinvPass() : Pass("extractinv", "extract explicit inverter cells for invertible cell pins") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" extractinv [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Searches the design for all cells with invertible pins controlled by a cell\n");
|
||||
log("parameter (eg. IS_CLK_INVERTED on many Xilinx cells) and removes the parameter.\n");
|
||||
log("If the parameter was set to 1, inserts an explicit inverter cell in front of\n");
|
||||
log("the pin instead. Normally used for output to ISE, which does not support the\n");
|
||||
log("inversion parameters.\n");
|
||||
log("\n");
|
||||
log("To mark a cell port as invertible, use (* invertible_pin = \"param_name\" *)\n");
|
||||
log("on the wire in the blackbox module. The parameter value should have\n");
|
||||
log("the same width as the port, and will be effectively XORed with it.\n");
|
||||
log("\n");
|
||||
log(" -inv <celltype> <portname_out>:<portname_in>\n");
|
||||
log(" Specifies the cell type to use for the inverters and its port names.\n");
|
||||
log(" This option is required.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing EXTRACTINV pass (extracting pin inverters).\n");
|
||||
|
||||
std::string inv_celltype, inv_portname, inv_portname2;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-inv" && argidx+2 < args.size()) {
|
||||
inv_celltype = args[++argidx];
|
||||
inv_portname = args[++argidx];
|
||||
split_portname_pair(inv_portname, inv_portname2);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (inv_celltype.empty())
|
||||
log_error("The -inv option is required.\n");
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
for (auto cell : module->selected_cells())
|
||||
for (auto port : cell->connections()) {
|
||||
auto cell_module = design->module(cell->type);
|
||||
if (!cell_module)
|
||||
continue;
|
||||
auto cell_wire = cell_module->wire(port.first);
|
||||
if (!cell_wire)
|
||||
continue;
|
||||
auto it = cell_wire->attributes.find("\\invertible_pin");
|
||||
if (it == cell_wire->attributes.end())
|
||||
continue;
|
||||
IdString param_name = RTLIL::escape_id(it->second.decode_string());
|
||||
auto it2 = cell->parameters.find(param_name);
|
||||
// Inversion not used -- skip.
|
||||
if (it2 == cell->parameters.end())
|
||||
continue;
|
||||
SigSpec sig = port.second;
|
||||
if (it2->second.size() != sig.size())
|
||||
log_error("The inversion parameter needs to be the same width as the port (%s.%s port %s parameter %s)", log_id(module->name), log_id(cell->type), log_id(port.first), log_id(param_name));
|
||||
RTLIL::Const invmask = it2->second;
|
||||
cell->parameters.erase(param_name);
|
||||
if (invmask.is_fully_zero())
|
||||
continue;
|
||||
Wire *iwire = module->addWire(NEW_ID, sig.size());
|
||||
for (int i = 0; i < sig.size(); i++)
|
||||
if (invmask[i] == State::S1) {
|
||||
RTLIL::Cell *icell = module->addCell(NEW_ID, RTLIL::escape_id(inv_celltype));
|
||||
icell->setPort(RTLIL::escape_id(inv_portname), SigSpec(iwire, i));
|
||||
icell->setPort(RTLIL::escape_id(inv_portname2), sig[i]);
|
||||
log("Inserting %s on %s.%s.%s[%d].\n", inv_celltype.c_str(), log_id(module), log_id(cell->type), log_id(port.first), i);
|
||||
sig[i] = SigBit(iwire, i);
|
||||
}
|
||||
cell->setPort(port.first, sig);
|
||||
}
|
||||
}
|
||||
}
|
||||
} ExtractinvPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -64,6 +64,11 @@ struct IopadmapPass : public Pass {
|
|||
log(" of the tristate driver and the 2nd portname is the internal output\n");
|
||||
log(" buffering the external signal.\n");
|
||||
log("\n");
|
||||
log(" -ignore <celltype> <portname>[:<portname>]*\n");
|
||||
log(" Skips mapping inputs/outputs that are already connected to given\n");
|
||||
log(" ports of the given cell. Can be used multiple times. This is in\n");
|
||||
log(" addition to the cells specified as mapping targets.\n");
|
||||
log("\n");
|
||||
log(" -widthparam <param_name>\n");
|
||||
log(" Use the specified parameter name to set the port width.\n");
|
||||
log("\n");
|
||||
|
@ -88,6 +93,7 @@ struct IopadmapPass : public Pass {
|
|||
std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;
|
||||
std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;
|
||||
std::string widthparam, nameparam;
|
||||
pool<pair<IdString, IdString>> ignore;
|
||||
bool flag_bits = false;
|
||||
|
||||
size_t argidx;
|
||||
|
@ -127,6 +133,18 @@ struct IopadmapPass : public Pass {
|
|||
split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-ignore" && argidx+2 < args.size()) {
|
||||
std::string ignore_celltype = args[++argidx];
|
||||
std::string ignore_portname = args[++argidx];
|
||||
std::string ignore_portname2;
|
||||
while (!ignore_portname.empty()) {
|
||||
split_portname_pair(ignore_portname, ignore_portname2);
|
||||
ignore.insert(make_pair(RTLIL::escape_id(ignore_celltype), RTLIL::escape_id(ignore_portname)));
|
||||
|
||||
ignore_portname = ignore_portname2;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (arg == "-widthparam" && argidx+1 < args.size()) {
|
||||
widthparam = args[++argidx];
|
||||
continue;
|
||||
|
@ -143,6 +161,23 @@ struct IopadmapPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (!inpad_portname2.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname2)));
|
||||
if (!outpad_portname2.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname2)));
|
||||
if (!inoutpad_portname2.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname2)));
|
||||
if (!toutpad_portname3.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname3)));
|
||||
if (!tinoutpad_portname4.empty())
|
||||
ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname4)));
|
||||
|
||||
for (auto module : design->modules())
|
||||
if (module->get_blackbox_attribute())
|
||||
for (auto wire : module->wires())
|
||||
if (wire->get_bool_attribute("\\iopad_external_pin"))
|
||||
ignore.insert(make_pair(module->name, wire->name));
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
dict<IdString, pool<int>> skip_wires;
|
||||
|
@ -150,28 +185,11 @@ struct IopadmapPass : public Pass {
|
|||
SigMap sigmap(module);
|
||||
|
||||
for (auto cell : module->cells())
|
||||
{
|
||||
if (cell->type == RTLIL::escape_id(inpad_celltype) && cell->hasPort(RTLIL::escape_id(inpad_portname2)))
|
||||
for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inpad_portname2))))
|
||||
for (auto port : cell->connections())
|
||||
if (ignore.count(make_pair(cell->type, port.first)))
|
||||
for (auto bit : sigmap(port.second))
|
||||
skip_wire_bits.insert(bit);
|
||||
|
||||
if (cell->type == RTLIL::escape_id(outpad_celltype) && cell->hasPort(RTLIL::escape_id(outpad_portname2)))
|
||||
for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(outpad_portname2))))
|
||||
skip_wire_bits.insert(bit);
|
||||
|
||||
if (cell->type == RTLIL::escape_id(inoutpad_celltype) && cell->hasPort(RTLIL::escape_id(inoutpad_portname2)))
|
||||
for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inoutpad_portname2))))
|
||||
skip_wire_bits.insert(bit);
|
||||
|
||||
if (cell->type == RTLIL::escape_id(toutpad_celltype) && cell->hasPort(RTLIL::escape_id(toutpad_portname3)))
|
||||
for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(toutpad_portname3))))
|
||||
skip_wire_bits.insert(bit);
|
||||
|
||||
if (cell->type == RTLIL::escape_id(tinoutpad_celltype) && cell->hasPort(RTLIL::escape_id(tinoutpad_portname4)))
|
||||
for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(tinoutpad_portname4))))
|
||||
skip_wire_bits.insert(bit);
|
||||
}
|
||||
|
||||
if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty())
|
||||
{
|
||||
dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits;
|
||||
|
|
|
@ -26,9 +26,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
struct ShregmapTech
|
||||
{
|
||||
virtual ~ShregmapTech() { }
|
||||
virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {}
|
||||
virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {}
|
||||
virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
|
||||
virtual bool analyze(vector<int> &taps) = 0;
|
||||
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
|
||||
};
|
||||
|
||||
|
@ -56,7 +54,7 @@ struct ShregmapOptions
|
|||
|
||||
struct ShregmapTechGreenpak4 : ShregmapTech
|
||||
{
|
||||
bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/)
|
||||
bool analyze(vector<int> &taps)
|
||||
{
|
||||
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
|
||||
taps.clear();
|
||||
|
@ -93,155 +91,6 @@ struct ShregmapTechGreenpak4 : ShregmapTech
|
|||
}
|
||||
};
|
||||
|
||||
struct ShregmapTechXilinx7 : ShregmapTech
|
||||
{
|
||||
dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset;
|
||||
const ShregmapOptions &opts;
|
||||
|
||||
ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {}
|
||||
|
||||
virtual void init(const Module* module, const SigMap &sigmap) override
|
||||
{
|
||||
for (const auto &i : module->cells_) {
|
||||
auto cell = i.second;
|
||||
if (cell->type == ID($shiftx)) {
|
||||
if (cell->getParam(ID(Y_WIDTH)) != 1) continue;
|
||||
int j = 0;
|
||||
for (auto bit : sigmap(cell->getPort(ID::A)))
|
||||
sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
|
||||
log_assert(j == cell->getParam(ID(A_WIDTH)).as_int());
|
||||
}
|
||||
else if (cell->type == ID($mux)) {
|
||||
int j = 0;
|
||||
for (auto bit : sigmap(cell->getPort(ID::A)))
|
||||
sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
|
||||
j = 0;
|
||||
for (auto bit : sigmap(cell->getPort(ID::B)))
|
||||
sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override
|
||||
{
|
||||
auto it = sigbit_to_shiftx_offset.find(bit);
|
||||
if (it == sigbit_to_shiftx_offset.end())
|
||||
return;
|
||||
if (cell) {
|
||||
if (cell->type == ID($shiftx) && port == ID::A)
|
||||
return;
|
||||
if (cell->type == ID($mux) && port.in(ID::A, ID::B))
|
||||
return;
|
||||
}
|
||||
sigbit_to_shiftx_offset.erase(it);
|
||||
}
|
||||
|
||||
virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override
|
||||
{
|
||||
if (GetSize(taps) == 1)
|
||||
return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]);
|
||||
|
||||
if (taps.back() < opts.minlen-1)
|
||||
return false;
|
||||
|
||||
Cell *shiftx = nullptr;
|
||||
int group = 0;
|
||||
for (int i = 0; i < GetSize(taps); ++i) {
|
||||
auto it = sigbit_to_shiftx_offset.find(qbits[i]);
|
||||
if (it == sigbit_to_shiftx_offset.end())
|
||||
return false;
|
||||
|
||||
// Check taps are sequential
|
||||
if (i != taps[i])
|
||||
return false;
|
||||
// Check taps are not connected to a shift register,
|
||||
// or sequential to the same shift register
|
||||
if (i == 0) {
|
||||
int offset;
|
||||
std::tie(shiftx,offset,group) = it->second;
|
||||
if (offset != i)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
Cell *shiftx_ = std::get<0>(it->second);
|
||||
if (shiftx_ != shiftx)
|
||||
return false;
|
||||
int offset = std::get<1>(it->second);
|
||||
if (offset != i)
|
||||
return false;
|
||||
int group_ = std::get<2>(it->second);
|
||||
if (group_ != group)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
log_assert(shiftx);
|
||||
|
||||
// Only map if $shiftx exclusively covers the shift register
|
||||
if (shiftx->type == ID($shiftx)) {
|
||||
if (GetSize(taps) > shiftx->getParam(ID(A_WIDTH)).as_int())
|
||||
return false;
|
||||
// Due to padding the most significant bits of A may be 1'bx,
|
||||
// and if so, discount them
|
||||
if (GetSize(taps) < shiftx->getParam(ID(A_WIDTH)).as_int()) {
|
||||
const SigSpec A = shiftx->getPort(ID::A);
|
||||
const int A_width = shiftx->getParam(ID(A_WIDTH)).as_int();
|
||||
for (int i = GetSize(taps); i < A_width; ++i)
|
||||
if (A[i] != RTLIL::Sx) return false;
|
||||
}
|
||||
else if (GetSize(taps) != shiftx->getParam(ID(A_WIDTH)).as_int())
|
||||
return false;
|
||||
}
|
||||
else if (shiftx->type == ID($mux)) {
|
||||
if (GetSize(taps) != 2)
|
||||
return false;
|
||||
}
|
||||
else log_abort();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override
|
||||
{
|
||||
const auto &tap = *taps.begin();
|
||||
auto bit = tap.second;
|
||||
|
||||
auto it = sigbit_to_shiftx_offset.find(bit);
|
||||
log_assert(it != sigbit_to_shiftx_offset.end());
|
||||
|
||||
auto newcell = cell->module->addCell(NEW_ID, ID($__XILINX_SHREG_));
|
||||
newcell->set_src_attribute(cell->get_src_attribute());
|
||||
newcell->setParam(ID(DEPTH), cell->getParam(ID(DEPTH)));
|
||||
newcell->setParam(ID(INIT), cell->getParam(ID(INIT)));
|
||||
newcell->setParam(ID(CLKPOL), cell->getParam(ID(CLKPOL)));
|
||||
newcell->setParam(ID(ENPOL), cell->getParam(ID(ENPOL)));
|
||||
|
||||
newcell->setPort(ID(C), cell->getPort(ID(C)));
|
||||
newcell->setPort(ID(D), cell->getPort(ID(D)));
|
||||
if (cell->hasPort(ID(E)))
|
||||
newcell->setPort(ID(E), cell->getPort(ID(E)));
|
||||
|
||||
Cell* shiftx = std::get<0>(it->second);
|
||||
RTLIL::SigSpec l_wire, q_wire;
|
||||
if (shiftx->type == ID($shiftx)) {
|
||||
l_wire = shiftx->getPort(ID::B);
|
||||
q_wire = shiftx->getPort(ID::Y);
|
||||
shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
|
||||
}
|
||||
else if (shiftx->type == ID($mux)) {
|
||||
l_wire = shiftx->getPort(ID(S));
|
||||
q_wire = shiftx->getPort(ID::Y);
|
||||
shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
|
||||
}
|
||||
else log_abort();
|
||||
|
||||
newcell->setPort(ID(Q), q_wire);
|
||||
newcell->setPort(ID(L), l_wire);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct ShregmapWorker
|
||||
{
|
||||
Module *module;
|
||||
|
@ -264,10 +113,8 @@ struct ShregmapWorker
|
|||
for (auto wire : module->wires())
|
||||
{
|
||||
if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
|
||||
for (auto bit : sigmap(wire)) {
|
||||
for (auto bit : sigmap(wire))
|
||||
sigbit_with_non_chain_users.insert(bit);
|
||||
if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
|
||||
}
|
||||
}
|
||||
|
||||
if (wire->attributes.count(ID(init))) {
|
||||
|
@ -317,10 +164,8 @@ struct ShregmapWorker
|
|||
|
||||
for (auto conn : cell->connections())
|
||||
if (cell->input(conn.first))
|
||||
for (auto bit : sigmap(conn.second)) {
|
||||
for (auto bit : sigmap(conn.second))
|
||||
sigbit_with_non_chain_users.insert(bit);
|
||||
if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,7 +191,7 @@ struct ShregmapWorker
|
|||
IdString q_port = opts.ffcells.at(c1->type).second;
|
||||
|
||||
auto c1_conn = c1->connections();
|
||||
auto c2_conn = c1->connections();
|
||||
auto c2_conn = c2->connections();
|
||||
|
||||
c1_conn.erase(d_port);
|
||||
c1_conn.erase(q_port);
|
||||
|
@ -425,7 +270,7 @@ struct ShregmapWorker
|
|||
if (taps.empty() || taps.back() < depth-1)
|
||||
taps.push_back(depth-1);
|
||||
|
||||
if (opts.tech->analyze(taps, qbits))
|
||||
if (opts.tech->analyze(taps))
|
||||
break;
|
||||
|
||||
taps.pop_back();
|
||||
|
@ -544,9 +389,6 @@ struct ShregmapWorker
|
|||
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
|
||||
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
|
||||
{
|
||||
if (opts.tech)
|
||||
opts.tech->init(module, sigmap);
|
||||
|
||||
make_sigbit_chain_next_prev();
|
||||
find_chain_start_cells();
|
||||
|
||||
|
@ -617,11 +459,6 @@ struct ShregmapPass : public Pass {
|
|||
log("\n");
|
||||
log(" -tech greenpak4\n");
|
||||
log(" map to greenpak4 shift registers.\n");
|
||||
log(" this option also implies -clkpol pos -zinit\n");
|
||||
log("\n");
|
||||
log(" -tech xilinx\n");
|
||||
log(" map to xilinx dynamic-length shift registers.\n");
|
||||
log(" this option also implies -params -init\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
|
@ -676,12 +513,6 @@ struct ShregmapPass : public Pass {
|
|||
clkpol = "pos";
|
||||
opts.zinit = true;
|
||||
opts.tech = new ShregmapTechGreenpak4;
|
||||
}
|
||||
else if (tech == "xilinx") {
|
||||
opts.init = true;
|
||||
opts.params = true;
|
||||
enpol = "any_or_none";
|
||||
opts.tech = new ShregmapTechXilinx7(opts);
|
||||
} else {
|
||||
argidx--;
|
||||
break;
|
||||
|
|
|
@ -205,20 +205,57 @@ struct TechmapWorker
|
|||
}
|
||||
|
||||
std::map<RTLIL::IdString, RTLIL::IdString> positional_ports;
|
||||
dict<Wire*, IdString> temp_renamed_wires;
|
||||
pool<SigBit> autopurge_tpl_bits;
|
||||
|
||||
for (auto &it : tpl->wires_) {
|
||||
for (auto &it : tpl->wires_)
|
||||
{
|
||||
if (it.second->port_id > 0)
|
||||
positional_ports[stringf("$%d", it.second->port_id)] = it.first;
|
||||
{
|
||||
IdString posportname = stringf("$%d", it.second->port_id);
|
||||
positional_ports[posportname] = it.first;
|
||||
|
||||
if (!flatten_mode && it.second->get_bool_attribute(ID(techmap_autopurge)) &&
|
||||
(!cell->hasPort(it.second->name) || !GetSize(cell->getPort(it.second->name))) &&
|
||||
(!cell->hasPort(posportname) || !GetSize(cell->getPort(posportname))))
|
||||
{
|
||||
if (sigmaps.count(tpl) == 0)
|
||||
sigmaps[tpl].set(tpl);
|
||||
|
||||
for (auto bit : sigmaps.at(tpl)(it.second))
|
||||
if (bit.wire != nullptr)
|
||||
autopurge_tpl_bits.insert(bit);
|
||||
}
|
||||
}
|
||||
IdString w_name = it.second->name;
|
||||
apply_prefix(cell->name, w_name);
|
||||
RTLIL::Wire *w = module->addWire(w_name, it.second);
|
||||
w->port_input = false;
|
||||
w->port_output = false;
|
||||
w->port_id = 0;
|
||||
if (it.second->get_bool_attribute(ID(_techmap_special_)))
|
||||
w->attributes.clear();
|
||||
if (w->attributes.count(ID(src)))
|
||||
w->add_strpool_attribute(ID(src), extra_src_attrs);
|
||||
RTLIL::Wire *w = module->wire(w_name);
|
||||
if (w != nullptr) {
|
||||
if (!flatten_mode || !w->get_bool_attribute(ID(hierconn))) {
|
||||
temp_renamed_wires[w] = w->name;
|
||||
module->rename(w, NEW_ID);
|
||||
w = nullptr;
|
||||
} else {
|
||||
w->attributes.erase(ID(hierconn));
|
||||
if (GetSize(w) < GetSize(it.second)) {
|
||||
log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(w),
|
||||
log_id(tpl), log_id(it.second), log_id(module), log_id(cell));
|
||||
w->width = GetSize(it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (w == nullptr) {
|
||||
w = module->addWire(w_name, it.second);
|
||||
w->port_input = false;
|
||||
w->port_output = false;
|
||||
w->port_id = 0;
|
||||
if (!flatten_mode)
|
||||
w->attributes.erase(ID(techmap_autopurge));
|
||||
if (it.second->get_bool_attribute(ID(_techmap_special_)))
|
||||
w->attributes.clear();
|
||||
if (w->attributes.count(ID(src)))
|
||||
w->add_strpool_attribute(ID(src), extra_src_attrs);
|
||||
}
|
||||
design->select(module, w);
|
||||
}
|
||||
|
||||
|
@ -322,6 +359,12 @@ struct TechmapWorker
|
|||
for (auto &attr : w->attributes) {
|
||||
if (attr.first == ID(src))
|
||||
continue;
|
||||
auto lhs = GetSize(extra_connect.first);
|
||||
auto rhs = GetSize(extra_connect.second);
|
||||
if (lhs > rhs)
|
||||
extra_connect.first.remove(rhs, lhs-rhs);
|
||||
else if (rhs > lhs)
|
||||
extra_connect.second.remove(lhs, rhs-lhs);
|
||||
module->connect(extra_connect);
|
||||
break;
|
||||
}
|
||||
|
@ -344,11 +387,31 @@ struct TechmapWorker
|
|||
if (!flatten_mode && c->type.begins_with("\\$"))
|
||||
c->type = c->type.substr(1);
|
||||
|
||||
for (auto &it2 : c->connections_) {
|
||||
apply_prefix(cell->name, it2.second, module);
|
||||
port_signal_map.apply(it2.second);
|
||||
vector<IdString> autopurge_ports;
|
||||
|
||||
for (auto &it2 : c->connections_)
|
||||
{
|
||||
bool autopurge = false;
|
||||
if (!autopurge_tpl_bits.empty()) {
|
||||
autopurge = GetSize(it2.second) != 0;
|
||||
for (auto &bit : sigmaps.at(tpl)(it2.second))
|
||||
if (!autopurge_tpl_bits.count(bit)) {
|
||||
autopurge = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (autopurge) {
|
||||
autopurge_ports.push_back(it2.first);
|
||||
} else {
|
||||
apply_prefix(cell->name, it2.second, module);
|
||||
port_signal_map.apply(it2.second);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it2 : autopurge_ports)
|
||||
c->unsetPort(it2);
|
||||
|
||||
if (c->type.in(ID($memrd), ID($memwr), ID($meminit))) {
|
||||
IdString memid = c->getParam(ID(MEMID)).decode_string();
|
||||
log_assert(memory_renames.count(memid) != 0);
|
||||
|
@ -380,6 +443,16 @@ struct TechmapWorker
|
|||
}
|
||||
|
||||
module->remove(cell);
|
||||
|
||||
for (auto &it : temp_renamed_wires)
|
||||
{
|
||||
Wire *w = it.first;
|
||||
IdString name = it.second;
|
||||
IdString altname = module->uniquify(name);
|
||||
Wire *other_w = module->wire(name);
|
||||
module->rename(other_w, altname);
|
||||
module->rename(w, name);
|
||||
}
|
||||
}
|
||||
|
||||
bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells,
|
||||
|
@ -396,6 +469,18 @@ struct TechmapWorker
|
|||
|
||||
SigMap sigmap(module);
|
||||
|
||||
dict<SigBit, State> init_bits;
|
||||
pool<SigBit> remove_init_bits;
|
||||
|
||||
for (auto wire : module->wires()) {
|
||||
if (wire->attributes.count("\\init")) {
|
||||
Const value = wire->attributes.at("\\init");
|
||||
for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
|
||||
if (value[i] != State::Sx)
|
||||
init_bits[sigmap(SigBit(wire, i))] = value[i];
|
||||
}
|
||||
}
|
||||
|
||||
TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
|
||||
std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit;
|
||||
std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell;
|
||||
|
@ -633,6 +718,17 @@ struct TechmapWorker
|
|||
bit = RTLIL::SigBit(RTLIL::State::Sx);
|
||||
parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const();
|
||||
}
|
||||
if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))) != 0) {
|
||||
auto sig = sigmap(conn.second);
|
||||
RTLIL::Const value(State::Sx, sig.size());
|
||||
for (int i = 0; i < sig.size(); i++) {
|
||||
auto it = init_bits.find(sig[i]);
|
||||
if (it != init_bits.end()) {
|
||||
value[i] = it->second;
|
||||
}
|
||||
}
|
||||
parameters[stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))] = value;
|
||||
}
|
||||
}
|
||||
|
||||
int unique_bit_id_counter = 0;
|
||||
|
@ -833,7 +929,7 @@ struct TechmapWorker
|
|||
|
||||
TechmapWires twd = techmap_find_special_wires(tpl);
|
||||
for (auto &it : twd) {
|
||||
if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
|
||||
if (it.first != "_TECHMAP_FAIL_" && (it.first.substr(0, 20) != "_TECHMAP_REMOVEINIT_" || it.first[it.first.size()-1] != '_') && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
|
||||
log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str());
|
||||
if (techmap_do_cache[tpl])
|
||||
for (auto &it2 : it.second)
|
||||
|
@ -864,6 +960,23 @@ struct TechmapWorker
|
|||
mkdebug.off();
|
||||
}
|
||||
|
||||
TechmapWires twd = techmap_find_special_wires(tpl);
|
||||
for (auto &it : twd) {
|
||||
if (it.first.substr(0, 20) == "_TECHMAP_REMOVEINIT_") {
|
||||
for (auto &it2 : it.second) {
|
||||
auto val = it2.value.as_const();
|
||||
auto wirename = RTLIL::escape_id(it.first.substr(20, it.first.size() - 20 - 1));
|
||||
auto it = cell->connections().find(wirename);
|
||||
if (it != cell->connections().end()) {
|
||||
auto sig = sigmap(it->second);
|
||||
for (int i = 0; i < sig.size(); i++)
|
||||
if (val[i] == State::S1)
|
||||
remove_init_bits.insert(sig[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (extern_mode && !in_recursion)
|
||||
{
|
||||
std::string m_name = stringf("$extern:%s", log_id(tpl));
|
||||
|
@ -907,6 +1020,25 @@ struct TechmapWorker
|
|||
handled_cells.insert(cell);
|
||||
}
|
||||
|
||||
if (!remove_init_bits.empty()) {
|
||||
for (auto wire : module->wires())
|
||||
if (wire->attributes.count("\\init")) {
|
||||
Const &value = wire->attributes.at("\\init");
|
||||
bool do_cleanup = true;
|
||||
for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
|
||||
SigBit bit = sigmap(SigBit(wire, i));
|
||||
if (remove_init_bits.count(bit))
|
||||
value[i] = State::Sx;
|
||||
else if (value[i] != State::Sx)
|
||||
do_cleanup = false;
|
||||
}
|
||||
if (do_cleanup) {
|
||||
log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
|
||||
wire->attributes.erase("\\init");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (log_continue) {
|
||||
log_header(design, "Continuing TECHMAP pass.\n");
|
||||
log_continue = false;
|
||||
|
@ -943,7 +1075,8 @@ struct TechmapPass : public Pass {
|
|||
log(" instead of inlining them.\n");
|
||||
log("\n");
|
||||
log(" -max_iter <number>\n");
|
||||
log(" only run the specified number of iterations.\n");
|
||||
log(" only run the specified number of iterations on each module.\n");
|
||||
log(" default: unlimited\n");
|
||||
log("\n");
|
||||
log(" -recursive\n");
|
||||
log(" instead of the iterative breadth-first algorithm use a recursive\n");
|
||||
|
@ -980,6 +1113,11 @@ struct TechmapPass : public Pass {
|
|||
log("will create a wrapper for the cell and then run the command string that the\n");
|
||||
log("attribute is set to on the wrapper module.\n");
|
||||
log("\n");
|
||||
log("When a port on a module in the map file has the 'techmap_autopurge' attribute\n");
|
||||
log("set, and that port is not connected in the instantiation that is mapped, then\n");
|
||||
log("then a cell port connected only to such wires will be omitted in the mapped\n");
|
||||
log("version of the circuit.\n");
|
||||
log("\n");
|
||||
log("All wires in the modules from the map file matching the pattern _TECHMAP_*\n");
|
||||
log("or *._TECHMAP_* are special wires that are used to pass instructions from\n");
|
||||
log("the mapping module to the techmap command. At the moment the following special\n");
|
||||
|
@ -1018,6 +1156,13 @@ struct TechmapPass : public Pass {
|
|||
log("\n");
|
||||
log(" It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\n");
|
||||
log("\n");
|
||||
log(" _TECHMAP_REMOVEINIT_<port-name>_\n");
|
||||
log(" When this wire is set to a constant value, the init attribute of the wire(s)\n");
|
||||
log(" connected to this port will be consumed. This wire must have the same\n");
|
||||
log(" width as the given port, and for every bit that is set to 1 in the value,\n");
|
||||
log(" the corresponding init attribute bit will be changed to 1'bx. If all\n");
|
||||
log(" bits of an init attribute are left as x, it will be removed.\n");
|
||||
log("\n");
|
||||
log("In addition to this special wires, techmap also supports special parameters in\n");
|
||||
log("modules in the map file:\n");
|
||||
log("\n");
|
||||
|
@ -1031,6 +1176,13 @@ struct TechmapPass : public Pass {
|
|||
log(" former has a 1-bit for each constant input bit and the latter has the\n");
|
||||
log(" value for this bit. The unused bits of the latter are set to undef (x).\n");
|
||||
log("\n");
|
||||
log(" _TECHMAP_WIREINIT_<port-name>_\n");
|
||||
log(" When a parameter with this name exists, it will be set to the initial\n");
|
||||
log(" value of the wire(s) connected to the given port, as specified by the init\n");
|
||||
log(" attribute. If the attribute doesn't exist, x will be filled for the\n");
|
||||
log(" missing bits. To remove the init attribute bits used, use the\n");
|
||||
log(" _TECHMAP_REMOVEINIT_*_ wires.\n");
|
||||
log("\n");
|
||||
log(" _TECHMAP_BITS_CONNMAP_\n");
|
||||
log(" _TECHMAP_CONNMAP_<port-name>_\n");
|
||||
log(" For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it\n");
|
||||
|
@ -1157,15 +1309,16 @@ struct TechmapPass : public Pass {
|
|||
RTLIL::Module *module = *worker.module_queue.begin();
|
||||
worker.module_queue.erase(module);
|
||||
|
||||
int module_max_iter = max_iter;
|
||||
bool did_something = true;
|
||||
std::set<RTLIL::Cell*> handled_cells;
|
||||
while (did_something) {
|
||||
did_something = false;
|
||||
if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
|
||||
did_something = true;
|
||||
if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
|
||||
did_something = true;
|
||||
if (did_something)
|
||||
module->check();
|
||||
if (max_iter > 0 && --max_iter == 0)
|
||||
if (module_max_iter > 0 && --module_max_iter == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue