mirror of
https://github.com/YosysHQ/yosys
synced 2026-04-27 06:13:37 +00:00
Merge branch 'main' into related_load_data
This commit is contained in:
commit
f8b5da5058
226 changed files with 16144 additions and 2736 deletions
|
|
@ -5,6 +5,7 @@ endif
|
|||
OBJS += passes/cmds/add.o
|
||||
OBJS += passes/cmds/delete.o
|
||||
OBJS += passes/cmds/design.o
|
||||
OBJS += passes/cmds/design_equal.o
|
||||
OBJS += passes/cmds/select.o
|
||||
OBJS += passes/cmds/show.o
|
||||
OBJS += passes/cmds/viz.o
|
||||
|
|
@ -36,6 +37,7 @@ OBJS += passes/cmds/chformal.o
|
|||
OBJS += passes/cmds/chtype.o
|
||||
OBJS += passes/cmds/blackbox.o
|
||||
OBJS += passes/cmds/ltp.o
|
||||
OBJS += passes/cmds/linux_perf.o
|
||||
ifeq ($(DISABLE_SPAWN),0)
|
||||
OBJS += passes/cmds/bugpoint.o
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celledges.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/log_help.h"
|
||||
|
||||
|
|
|
|||
367
passes/cmds/design_equal.cc
Normal file
367
passes/cmds/design_equal.cc
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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/rtlil.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
class ModuleComparator
|
||||
{
|
||||
RTLIL::Module *mod_a;
|
||||
RTLIL::Module *mod_b;
|
||||
|
||||
public:
|
||||
ModuleComparator(RTLIL::Module *mod_a, RTLIL::Module *mod_b) : mod_a(mod_a), mod_b(mod_b) {}
|
||||
|
||||
template <typename... Args>
|
||||
[[noreturn]] void error(FmtString<TypeIdentity<Args>...> fmt, const Args &... args)
|
||||
{
|
||||
formatted_error(fmt.format(args...));
|
||||
}
|
||||
[[noreturn]]
|
||||
void formatted_error(std::string err)
|
||||
{
|
||||
log("Module A: %s\n", log_id(mod_a->name));
|
||||
log_module(mod_a, " ");
|
||||
log("Module B: %s\n", log_id(mod_b->name));
|
||||
log_module(mod_b, " ");
|
||||
log_cmd_error("Designs are different: %s\n", err);
|
||||
}
|
||||
|
||||
bool compare_sigbit(const RTLIL::SigBit &a, const RTLIL::SigBit &b)
|
||||
{
|
||||
if (a.wire == nullptr && b.wire == nullptr)
|
||||
return a.data == b.data;
|
||||
if (a.wire != nullptr && b.wire != nullptr)
|
||||
return a.wire->name == b.wire->name && a.offset == b.offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compare_sigspec(const RTLIL::SigSpec &a, const RTLIL::SigSpec &b)
|
||||
{
|
||||
if (a.size() != b.size()) return false;
|
||||
auto it_a = a.begin(), it_b = b.begin();
|
||||
for (; it_a != a.end(); ++it_a, ++it_b) {
|
||||
if (!compare_sigbit(*it_a, *it_b)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string compare_attributes(const RTLIL::AttrObject *a, const RTLIL::AttrObject *b)
|
||||
{
|
||||
for (const auto &it : a->attributes) {
|
||||
if (b->attributes.count(it.first) == 0)
|
||||
return "missing attribute " + std::string(log_id(it.first)) + " in second design";
|
||||
if (it.second != b->attributes.at(it.first))
|
||||
return "attribute " + std::string(log_id(it.first)) + " mismatch: " + log_const(it.second) + " != " + log_const(b->attributes.at(it.first));
|
||||
}
|
||||
for (const auto &it : b->attributes)
|
||||
if (a->attributes.count(it.first) == 0)
|
||||
return "missing attribute " + std::string(log_id(it.first)) + " in first design";
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_wires(const RTLIL::Wire *a, const RTLIL::Wire *b)
|
||||
{
|
||||
if (a->name != b->name)
|
||||
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
if (a->width != b->width)
|
||||
return "width mismatch: " + std::to_string(a->width) + " != " + std::to_string(b->width);
|
||||
if (a->start_offset != b->start_offset)
|
||||
return "start_offset mismatch: " + std::to_string(a->start_offset) + " != " + std::to_string(b->start_offset);
|
||||
if (a->port_id != b->port_id)
|
||||
return "port_id mismatch: " + std::to_string(a->port_id) + " != " + std::to_string(b->port_id);
|
||||
if (a->port_input != b->port_input)
|
||||
return "port_input mismatch: " + std::to_string(a->port_input) + " != " + std::to_string(b->port_input);
|
||||
if (a->port_output != b->port_output)
|
||||
return "port_output mismatch: " + std::to_string(a->port_output) + " != " + std::to_string(b->port_output);
|
||||
if (a->upto != b->upto)
|
||||
return "upto mismatch: " + std::to_string(a->upto) + " != " + std::to_string(b->upto);
|
||||
if (a->is_signed != b->is_signed)
|
||||
return "is_signed mismatch: " + std::to_string(a->is_signed) + " != " + std::to_string(b->is_signed);
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
return "";
|
||||
}
|
||||
|
||||
void check_wires()
|
||||
{
|
||||
for (const auto &it : mod_a->wires_) {
|
||||
if (mod_b->wires_.count(it.first) == 0)
|
||||
error("Module %s missing wire %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
if (std::string mismatch = compare_wires(it.second, mod_b->wires_.at(it.first)); !mismatch.empty())
|
||||
error("Module %s wire %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
|
||||
}
|
||||
for (const auto &it : mod_b->wires_)
|
||||
if (mod_a->wires_.count(it.first) == 0)
|
||||
error("Module %s missing wire %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
}
|
||||
|
||||
std::string compare_memories(const RTLIL::Memory *a, const RTLIL::Memory *b)
|
||||
{
|
||||
if (a->name != b->name)
|
||||
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
if (a->width != b->width)
|
||||
return "width mismatch: " + std::to_string(a->width) + " != " + std::to_string(b->width);
|
||||
if (a->start_offset != b->start_offset)
|
||||
return "start_offset mismatch: " + std::to_string(a->start_offset) + " != " + std::to_string(b->start_offset);
|
||||
if (a->size != b->size)
|
||||
return "size mismatch: " + std::to_string(a->size) + " != " + std::to_string(b->size);
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_cells(const RTLIL::Cell *a, const RTLIL::Cell *b)
|
||||
{
|
||||
if (a->name != b->name)
|
||||
return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
if (a->type != b->type)
|
||||
return "type mismatch: " + std::string(log_id(a->type)) + " != " + log_id(b->type);
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
|
||||
for (const auto &it : a->parameters) {
|
||||
if (b->parameters.count(it.first) == 0)
|
||||
return "parameter mismatch: missing parameter " + std::string(log_id(it.first)) + " in second design";
|
||||
if (it.second != b->parameters.at(it.first))
|
||||
return "parameter mismatch: " + std::string(log_id(it.first)) + " mismatch: " + log_const(it.second) + " != " + log_const(b->parameters.at(it.first));
|
||||
}
|
||||
for (const auto &it : b->parameters)
|
||||
if (a->parameters.count(it.first) == 0)
|
||||
return "parameter mismatch: missing parameter " + std::string(log_id(it.first)) + " in first design";
|
||||
|
||||
for (const auto &it : a->connections()) {
|
||||
if (b->connections().count(it.first) == 0)
|
||||
return "connection mismatch: missing connection " + std::string(log_id(it.first)) + " in second design";
|
||||
if (!compare_sigspec(it.second, b->connections().at(it.first)))
|
||||
return "connection " + std::string(log_id(it.first)) + " mismatch: " + log_signal(it.second) + " != " + log_signal(b->connections().at(it.first));
|
||||
}
|
||||
for (const auto &it : b->connections())
|
||||
if (a->connections().count(it.first) == 0)
|
||||
return "connection mismatch: missing connection " + std::string(log_id(it.first)) + " in first design";
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void check_cells()
|
||||
{
|
||||
for (const auto &it : mod_a->cells_) {
|
||||
if (mod_b->cells_.count(it.first) == 0)
|
||||
error("Module %s missing cell %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
if (std::string mismatch = compare_cells(it.second, mod_b->cells_.at(it.first)); !mismatch.empty())
|
||||
error("Module %s cell %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
|
||||
}
|
||||
for (const auto &it : mod_b->cells_)
|
||||
if (mod_a->cells_.count(it.first) == 0)
|
||||
error("Module %s missing cell %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
}
|
||||
|
||||
void check_memories()
|
||||
{
|
||||
for (const auto &it : mod_a->memories) {
|
||||
if (mod_b->memories.count(it.first) == 0)
|
||||
error("Module %s missing memory %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
if (std::string mismatch = compare_memories(it.second, mod_b->memories.at(it.first)); !mismatch.empty())
|
||||
error("Module %s memory %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch);
|
||||
}
|
||||
for (const auto &it : mod_b->memories)
|
||||
if (mod_a->memories.count(it.first) == 0)
|
||||
error("Module %s missing memory %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
}
|
||||
|
||||
std::string compare_case_rules(const RTLIL::CaseRule *a, const RTLIL::CaseRule *b)
|
||||
{
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty()) return mismatch;
|
||||
|
||||
if (a->compare.size() != b->compare.size())
|
||||
return "compare size mismatch: " + std::to_string(a->compare.size()) + " != " + std::to_string(b->compare.size());
|
||||
for (size_t i = 0; i < a->compare.size(); i++)
|
||||
if (!compare_sigspec(a->compare[i], b->compare[i]))
|
||||
return "compare " + std::to_string(i) + " mismatch: " + log_signal(a->compare[i]) + " != " + log_signal(b->compare[i]);
|
||||
|
||||
if (a->actions.size() != b->actions.size())
|
||||
return "actions size mismatch: " + std::to_string(a->actions.size()) + " != " + std::to_string(b->actions.size());
|
||||
for (size_t i = 0; i < a->actions.size(); i++) {
|
||||
if (!compare_sigspec(a->actions[i].first, b->actions[i].first))
|
||||
return "action " + std::to_string(i) + " first mismatch: " + log_signal(a->actions[i].first) + " != " + log_signal(b->actions[i].first);
|
||||
if (!compare_sigspec(a->actions[i].second, b->actions[i].second))
|
||||
return "action " + std::to_string(i) + " second mismatch: " + log_signal(a->actions[i].second) + " != " + log_signal(b->actions[i].second);
|
||||
}
|
||||
|
||||
if (a->switches.size() != b->switches.size())
|
||||
return "switches size mismatch: " + std::to_string(a->switches.size()) + " != " + std::to_string(b->switches.size());
|
||||
for (size_t i = 0; i < a->switches.size(); i++)
|
||||
if (std::string mismatch = compare_switch_rules(a->switches[i], b->switches[i]); !mismatch.empty())
|
||||
return "switch " + std::to_string(i) + " " + mismatch;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_switch_rules(const RTLIL::SwitchRule *a, const RTLIL::SwitchRule *b)
|
||||
{
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
if (!compare_sigspec(a->signal, b->signal))
|
||||
return "signal mismatch: " + log_signal(a->signal) + " != " + log_signal(b->signal);
|
||||
|
||||
if (a->cases.size() != b->cases.size())
|
||||
return "cases size mismatch: " + std::to_string(a->cases.size()) + " != " + std::to_string(b->cases.size());
|
||||
for (size_t i = 0; i < a->cases.size(); i++)
|
||||
if (std::string mismatch = compare_case_rules(a->cases[i], b->cases[i]); !mismatch.empty())
|
||||
return "case " + std::to_string(i) + " " + mismatch;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_sync_rules(const RTLIL::SyncRule *a, const RTLIL::SyncRule *b)
|
||||
{
|
||||
if (a->type != b->type)
|
||||
return "type mismatch: " + std::to_string(a->type) + " != " + std::to_string(b->type);
|
||||
if (!compare_sigspec(a->signal, b->signal))
|
||||
return "signal mismatch: " + log_signal(a->signal) + " != " + log_signal(b->signal);
|
||||
if (a->actions.size() != b->actions.size())
|
||||
return "actions size mismatch: " + std::to_string(a->actions.size()) + " != " + std::to_string(b->actions.size());
|
||||
for (size_t i = 0; i < a->actions.size(); i++) {
|
||||
if (!compare_sigspec(a->actions[i].first, b->actions[i].first))
|
||||
return "action " + std::to_string(i) + " first mismatch: " + log_signal(a->actions[i].first) + " != " + log_signal(b->actions[i].first);
|
||||
if (!compare_sigspec(a->actions[i].second, b->actions[i].second))
|
||||
return "action " + std::to_string(i) + " second mismatch: " + log_signal(a->actions[i].second) + " != " + log_signal(b->actions[i].second);
|
||||
}
|
||||
if (a->mem_write_actions.size() != b->mem_write_actions.size())
|
||||
return "mem_write_actions size mismatch: " + std::to_string(a->mem_write_actions.size()) + " != " + std::to_string(b->mem_write_actions.size());
|
||||
for (size_t i = 0; i < a->mem_write_actions.size(); i++) {
|
||||
const auto &ma = a->mem_write_actions[i];
|
||||
const auto &mb = b->mem_write_actions[i];
|
||||
if (ma.memid != mb.memid)
|
||||
return "mem_write_actions " + std::to_string(i) + " memid mismatch: " + log_id(ma.memid) + " != " + log_id(mb.memid);
|
||||
if (!compare_sigspec(ma.address, mb.address))
|
||||
return "mem_write_actions " + std::to_string(i) + " address mismatch: " + log_signal(ma.address) + " != " + log_signal(mb.address);
|
||||
if (!compare_sigspec(ma.data, mb.data))
|
||||
return "mem_write_actions " + std::to_string(i) + " data mismatch: " + log_signal(ma.data) + " != " + log_signal(mb.data);
|
||||
if (!compare_sigspec(ma.enable, mb.enable))
|
||||
return "mem_write_actions " + std::to_string(i) + " enable mismatch: " + log_signal(ma.enable) + " != " + log_signal(mb.enable);
|
||||
if (ma.priority_mask != mb.priority_mask)
|
||||
return "mem_write_actions " + std::to_string(i) + " priority_mask mismatch: " + log_const(ma.priority_mask) + " != " + log_const(mb.priority_mask);
|
||||
if (std::string mismatch = compare_attributes(&ma, &mb); !mismatch.empty())
|
||||
return "mem_write_actions " + std::to_string(i) + " " + mismatch;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string compare_processes(const RTLIL::Process *a, const RTLIL::Process *b)
|
||||
{
|
||||
if (a->name != b->name) return "name mismatch: " + std::string(log_id(a->name)) + " != " + log_id(b->name);
|
||||
if (std::string mismatch = compare_attributes(a, b); !mismatch.empty())
|
||||
return mismatch;
|
||||
if (std::string mismatch = compare_case_rules(&a->root_case, &b->root_case); !mismatch.empty())
|
||||
return "case rule " + mismatch;
|
||||
if (a->syncs.size() != b->syncs.size())
|
||||
return "sync count mismatch: " + std::to_string(a->syncs.size()) + " != " + std::to_string(b->syncs.size());
|
||||
for (size_t i = 0; i < a->syncs.size(); i++)
|
||||
if (std::string mismatch = compare_sync_rules(a->syncs[i], b->syncs[i]); !mismatch.empty())
|
||||
return "sync " + std::to_string(i) + " " + mismatch;
|
||||
return "";
|
||||
}
|
||||
|
||||
void check_processes()
|
||||
{
|
||||
for (auto &it : mod_a->processes) {
|
||||
if (mod_b->processes.count(it.first) == 0)
|
||||
error("Module %s missing process %s in second design.\n", log_id(mod_a->name), log_id(it.first));
|
||||
if (std::string mismatch = compare_processes(it.second, mod_b->processes.at(it.first)); !mismatch.empty())
|
||||
error("Module %s process %s %s.\n", log_id(mod_a->name), log_id(it.first), mismatch.c_str());
|
||||
}
|
||||
for (auto &it : mod_b->processes)
|
||||
if (mod_a->processes.count(it.first) == 0)
|
||||
error("Module %s missing process %s in first design.\n", log_id(mod_b->name), log_id(it.first));
|
||||
}
|
||||
|
||||
void check_connections()
|
||||
{
|
||||
const auto &conns_a = mod_a->connections();
|
||||
const auto &conns_b = mod_b->connections();
|
||||
if (conns_a.size() != conns_b.size()) {
|
||||
error("Module %s connection count differs: %zu != %zu\n", log_id(mod_a->name), conns_a.size(), conns_b.size());
|
||||
} else {
|
||||
for (size_t i = 0; i < conns_a.size(); i++) {
|
||||
if (!compare_sigspec(conns_a[i].first, conns_b[i].first))
|
||||
error("Module %s connection %zu LHS %s != %s.\n", log_id(mod_a->name), i, log_signal(conns_a[i].first), log_signal(conns_b[i].first));
|
||||
if (!compare_sigspec(conns_a[i].second, conns_b[i].second))
|
||||
error("Module %s connection %zu RHS %s != %s.\n", log_id(mod_a->name), i, log_signal(conns_a[i].second), log_signal(conns_b[i].second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check()
|
||||
{
|
||||
if (mod_a->name != mod_b->name)
|
||||
error("Modules have different names: %s != %s\n", log_id(mod_a->name), log_id(mod_b->name));
|
||||
if (std::string mismatch = compare_attributes(mod_a, mod_b); !mismatch.empty())
|
||||
error("Module %s %s.\n", log_id(mod_a->name), mismatch);
|
||||
check_wires();
|
||||
check_cells();
|
||||
check_memories();
|
||||
check_connections();
|
||||
check_processes();
|
||||
}
|
||||
};
|
||||
|
||||
struct DesignEqualPass : public Pass {
|
||||
DesignEqualPass() : Pass("design_equal", "check if two designs are the same") { }
|
||||
void help() override
|
||||
{
|
||||
log("\n");
|
||||
log(" design_equal <name>\n");
|
||||
log("\n");
|
||||
log("Compare the current design with the design previously saved under the given\n");
|
||||
log("name. Abort with an error if the designs are different.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
if (args.size() != 2)
|
||||
log_cmd_error("Missing argument.\n");
|
||||
|
||||
std::string check_name = args[1];
|
||||
if (saved_designs.count(check_name) == 0)
|
||||
log_cmd_error("No saved design '%s' found!\n", check_name.c_str());
|
||||
|
||||
RTLIL::Design *other = saved_designs.at(check_name);
|
||||
|
||||
for (auto &it : design->modules_) {
|
||||
RTLIL::Module *mod = it.second;
|
||||
if (!other->has(mod->name))
|
||||
log_error("Second design missing module %s.\n", log_id(mod->name));
|
||||
|
||||
ModuleComparator cmp(mod, other->module(mod->name));
|
||||
cmp.check();
|
||||
}
|
||||
for (auto &it : other->modules_) {
|
||||
RTLIL::Module *mod = it.second;
|
||||
if (!design->has(mod->name))
|
||||
log_error("First design missing module %s.\n", log_id(mod->name));
|
||||
}
|
||||
|
||||
log("Designs are identical.\n");
|
||||
}
|
||||
} DesignEqualPass;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#include "kernel/yosys.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/ff.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
|
@ -123,7 +124,7 @@ struct LibertyStubber {
|
|||
return;
|
||||
}
|
||||
|
||||
if (RTLIL::builtin_ff_cell_types().count(base_name))
|
||||
if (StaticCellTypes::categories.is_ff(base_name))
|
||||
return liberty_flop(base, derived, f);
|
||||
|
||||
auto& base_type = ct.cell_types[base_name];
|
||||
|
|
|
|||
102
passes/cmds/linux_perf.cc
Normal file
102
passes/cmds/linux_perf.cc
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2014 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* Copyright (C) 2014 Johann Glaser <Johann.Glaser@gmx.at>
|
||||
*
|
||||
* 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/log_help.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
struct LinuxPerf : public Pass {
|
||||
LinuxPerf() : Pass("linux_perf", "turn linux perf recording off or on") {
|
||||
internal();
|
||||
}
|
||||
bool formatted_help() override
|
||||
{
|
||||
auto *help = PrettyHelp::get_current();
|
||||
|
||||
auto content_root = help->get_root();
|
||||
|
||||
content_root->usage("linux_perf [on|off]");
|
||||
|
||||
content_root->paragraph(
|
||||
"This pass turns Linux 'perf' profiling on or off, when it has been configured to use control FIFOs."
|
||||
"YOSYS_PERF_CTL and YOSYS_PERF_ACK must point to Linux perf control FIFOs."
|
||||
);
|
||||
content_root->paragraph("Example shell command line:");
|
||||
content_root->codeblock(
|
||||
"mkfifo /tmp/perf.fifo /tmp/perf-ack.fifo\n"
|
||||
"YOSYS_PERF_CTL=/tmp/perf.fifo YOSYS_PERF_ACK=/tmp/perf-ack.fifo \\\n"
|
||||
" perf record --latency --delay=-1 \\\n"
|
||||
" --control=fifo:/tmp/perf.fifo,/tmp/perf-ack.fifo --call-graph=dwarf ./yosys \\\n"
|
||||
" -dt -p \"read_rtlil design.rtlil; linux_perf on; opt_clean; linux_perf off\"\n"
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *) override
|
||||
{
|
||||
if (args.size() > 2)
|
||||
cmd_error(args, 2, "Unexpected argument.");
|
||||
|
||||
std::string_view ctl_msg;
|
||||
if (args.size() == 2) {
|
||||
if (args[1] == "on")
|
||||
ctl_msg = "enable\n";
|
||||
else if (args[1] == "off")
|
||||
ctl_msg = "disable\n";
|
||||
else
|
||||
cmd_error(args, 1, "Unexpected argument.");
|
||||
}
|
||||
|
||||
const char *ctl_fifo = std::getenv("YOSYS_PERF_CTL");
|
||||
if (!ctl_fifo)
|
||||
log_error("YOSYS_PERF_CTL environment variable not set.");
|
||||
const char *ack_fifo = std::getenv("YOSYS_PERF_ACK");
|
||||
if (!ack_fifo)
|
||||
log_error("YOSYS_PERF_ACK environment variable not set.");
|
||||
|
||||
int ctl_fd = open(ctl_fifo, O_WRONLY);
|
||||
if (ctl_fd < 0)
|
||||
log_error("Failed to open YOSYS_PERF_CTL.");
|
||||
int ack_fd = open(ack_fifo, O_RDONLY);
|
||||
if (ack_fd < 0)
|
||||
log_error("Failed to open YOSYS_PERF_ACK.");
|
||||
int result = write(ctl_fd, ctl_msg.data(), ctl_msg.size());
|
||||
if (result != static_cast<int>(ctl_msg.size()))
|
||||
log_error("Failed to write to YOSYS_PERF_CTL.");
|
||||
char buffer[64];
|
||||
result = read(ack_fd, buffer, sizeof(buffer));
|
||||
close(ctl_fd);
|
||||
close(ack_fd);
|
||||
if (result <= 0)
|
||||
log_error("Failed to read from YOSYS_PERF_ACK.");
|
||||
if (strcmp(buffer, "ack\n") != 0)
|
||||
log_error("YOSYS_PERF_ACK did not return 'ack'.");
|
||||
}
|
||||
} LinuxPerf;
|
||||
#endif
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -254,18 +254,17 @@ struct RenamePass : public Pass {
|
|||
log("\n");
|
||||
log(" rename -enumerate [-pattern <pattern>] [selection]\n");
|
||||
log("\n");
|
||||
log("Assign short auto-generated names to all selected wires and cells with private\n");
|
||||
log("names. The -pattern option can be used to set the pattern for the new names.\n");
|
||||
log("The character %% in the pattern is replaced with a integer number. The default\n");
|
||||
log("pattern is '_%%_'.\n");
|
||||
log("Assigns auto-generated names to objects used in formal verification\n");
|
||||
log("that do not have a public name. This applies to all formal property\n");
|
||||
log("cells, $any*/$all* output wires, and their containing cells.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -witness\n");
|
||||
log("\n");
|
||||
log("Assigns auto-generated names to all $any*/$all* output wires and containing\n");
|
||||
log("cells that do not have a public name. This ensures that, during formal\n");
|
||||
log("verification, a solver-found trace can be fully specified using a public\n");
|
||||
log("hierarchical names.\n");
|
||||
log("Assigns auto-generated names to objects used in formal verification\n");
|
||||
log("that do not have a public name. This applies to all formal property\n");
|
||||
log("cells ($assert, $assume, $cover, $live, $fair, $check), $any*/$all*\n");
|
||||
log("output wires, and their containing cells.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -hide [selection]\n");
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ struct ScratchpadPass : public Pass {
|
|||
log("\n");
|
||||
log(" scratchpad [options]\n");
|
||||
log("\n");
|
||||
log("This pass allows to read and modify values from the scratchpad of the current\n");
|
||||
log("This pass allows reading and modifying values from the scratchpad of the current\n");
|
||||
log("design. Options:\n");
|
||||
log("\n");
|
||||
log(" -get <identifier>\n");
|
||||
|
|
|
|||
|
|
@ -165,7 +165,12 @@ struct SdcObjects {
|
|||
if (!top)
|
||||
log_error("Top module couldn't be determined. Check 'top' attribute usage");
|
||||
for (auto port : top->ports) {
|
||||
design_ports.push_back(std::make_pair(port.str().substr(1), top->wire(port)));
|
||||
RTLIL::Wire *wire = top->wire(port);
|
||||
if (!wire) {
|
||||
// This should not be possible. See https://github.com/YosysHQ/yosys/pull/5594#issue-3791198573
|
||||
log_error("Port %s doesn't exist", log_id(port));
|
||||
}
|
||||
design_ports.push_back(std::make_pair(port.str().substr(1), wire));
|
||||
}
|
||||
std::list<std::string> hierarchy{};
|
||||
sniff_module(hierarchy, top);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log_help.h"
|
||||
|
||||
|
|
@ -488,7 +488,7 @@ static int parse_comma_list(std::set<RTLIL::IdString> &tokens, const std::string
|
|||
}
|
||||
}
|
||||
|
||||
static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct, bool eval_only)
|
||||
static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, NewCellTypes &ct, bool eval_only)
|
||||
{
|
||||
int sel_objects = 0;
|
||||
bool is_input, is_output;
|
||||
|
|
@ -564,13 +564,13 @@ static void select_op_expand(RTLIL::Design *design, const std::string &arg, char
|
|||
std::vector<expand_rule_t> rules;
|
||||
std::set<RTLIL::IdString> limits;
|
||||
|
||||
CellTypes ct;
|
||||
NewCellTypes ct;
|
||||
|
||||
if (mode != 'x')
|
||||
ct.setup(design);
|
||||
|
||||
if (pos < int(arg.size()) && arg[pos] == '*') {
|
||||
levels = 1000000;
|
||||
levels = 1'000'000;
|
||||
pos++;
|
||||
} else
|
||||
if (pos < int(arg.size()) && '0' <= arg[pos] && arg[pos] <= '9') {
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ struct statdata_t {
|
|||
}
|
||||
}
|
||||
|
||||
if (tech == "xilinx") {
|
||||
if (tech == "xilinx" || tech == "analogdevices") {
|
||||
log("\n");
|
||||
log(" Estimated number of LCs: %10u\n", estimate_xilinx_lc());
|
||||
}
|
||||
|
|
@ -628,7 +628,7 @@ struct statdata_t {
|
|||
first_line = false;
|
||||
}
|
||||
log("\n }\n");
|
||||
if (tech == "xilinx") {
|
||||
if (tech == "xilinx" || tech == "analogdevices") {
|
||||
log(" \"estimated_num_lc\": %u,\n", estimate_xilinx_lc());
|
||||
}
|
||||
if (tech == "cmos") {
|
||||
|
|
@ -710,7 +710,7 @@ struct statdata_t {
|
|||
log("\n");
|
||||
log(" }");
|
||||
}
|
||||
if (tech == "xilinx") {
|
||||
if (tech == "xilinx" || tech == "analogdevices") {
|
||||
log(",\n");
|
||||
log(" \"estimated_num_lc\": %u", estimate_xilinx_lc());
|
||||
}
|
||||
|
|
@ -908,7 +908,7 @@ struct StatPass : public Pass {
|
|||
log("\n");
|
||||
log(" -tech <technology>\n");
|
||||
log(" print area estimate for the specified technology. Currently supported\n");
|
||||
log(" values for <technology>: xilinx, cmos\n");
|
||||
log(" values for <technology>: xilinx, analogdevices, cmos\n");
|
||||
log("\n");
|
||||
log(" -width\n");
|
||||
log(" annotate internal cell types with their word width.\n");
|
||||
|
|
@ -968,7 +968,7 @@ struct StatPass : public Pass {
|
|||
if (!json_mode)
|
||||
log_header(design, "Printing statistics.\n");
|
||||
|
||||
if (techname != "" && techname != "xilinx" && techname != "cmos" && !json_mode)
|
||||
if (techname != "" && techname != "xilinx" && techname != "analogdevices" && techname != "cmos" && !json_mode)
|
||||
log_cmd_error("Unsupported technology: '%s'\n", techname);
|
||||
|
||||
if (json_mode) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/log_help.h"
|
||||
|
|
|
|||
|
|
@ -115,16 +115,45 @@ struct DebugPass : public Pass {
|
|||
log("\n");
|
||||
log("Execute the specified command with debug log messages enabled\n");
|
||||
log("\n");
|
||||
log(" debug -on\n");
|
||||
log(" debug -off\n");
|
||||
log("\n");
|
||||
log("Enable or disable debug log messages globally\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
size_t argidx;
|
||||
bool mode_on = false;
|
||||
bool mode_off = false;
|
||||
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// .. parse options ..
|
||||
if (args[argidx] == "-on") {
|
||||
mode_on = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-off") {
|
||||
mode_off = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode_on && mode_off)
|
||||
log_cmd_error("Cannot specify both -on and -off\n");
|
||||
|
||||
if (mode_on) {
|
||||
log_force_debug++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode_off) {
|
||||
if (log_force_debug > 0)
|
||||
log_force_debug--;
|
||||
return;
|
||||
}
|
||||
|
||||
log_force_debug++;
|
||||
|
||||
try {
|
||||
|
|
|
|||
68
passes/equiv/equiv.h
Normal file
68
passes/equiv/equiv.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#ifndef EQUIV_H
|
||||
#define EQUIV_H
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/yosys_common.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/satgen.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct EquivBasicConfig {
|
||||
bool model_undef = false;
|
||||
int max_seq = 1;
|
||||
bool set_assumes = false;
|
||||
bool ignore_unknown_cells = false;
|
||||
|
||||
bool parse(const std::vector<std::string>& args, size_t& idx) {
|
||||
if (args[idx] == "-undef") {
|
||||
model_undef = true;
|
||||
return true;
|
||||
}
|
||||
if (args[idx] == "-seq" && idx+1 < args.size()) {
|
||||
max_seq = atoi(args[++idx].c_str());
|
||||
return true;
|
||||
}
|
||||
if (args[idx] == "-set-assumes") {
|
||||
set_assumes = true;
|
||||
return true;
|
||||
}
|
||||
if (args[idx] == "-ignore-unknown-cells") {
|
||||
ignore_unknown_cells = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static std::string help(const char* default_seq) {
|
||||
return stringf(
|
||||
" -undef\n"
|
||||
" enable modelling of undef states\n"
|
||||
"\n"
|
||||
" -seq <N>\n"
|
||||
" the max. number of time steps to be considered (default = %s)\n"
|
||||
"\n"
|
||||
" -set-assumes\n"
|
||||
" set all assumptions provided via $assume cells\n"
|
||||
"\n"
|
||||
" -ignore-unknown-cells\n"
|
||||
" ignore all cells that can not be matched to a SAT model\n"
|
||||
, default_seq);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Config = EquivBasicConfig>
|
||||
struct EquivWorker {
|
||||
RTLIL::Module *module;
|
||||
|
||||
ezSatPtr ez;
|
||||
SatGen satgen;
|
||||
Config cfg;
|
||||
|
||||
EquivWorker(RTLIL::Module *module, const SigMap *sigmap, Config cfg) : module(module), satgen(ez.get(), sigmap), cfg(cfg) {
|
||||
satgen.model_undef = cfg.model_undef;
|
||||
}
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
#endif // EQUIV_H
|
||||
|
|
@ -18,49 +18,34 @@
|
|||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/satgen.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "passes/equiv/equiv.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct EquivInductWorker
|
||||
struct EquivInductWorker : public EquivWorker<>
|
||||
{
|
||||
Module *module;
|
||||
SigMap sigmap;
|
||||
|
||||
vector<Cell*> cells;
|
||||
pool<Cell*> workset;
|
||||
|
||||
ezSatPtr ez;
|
||||
SatGen satgen;
|
||||
|
||||
int max_seq;
|
||||
int success_counter;
|
||||
bool set_assumes;
|
||||
|
||||
dict<int, int> ez_step_is_consistent;
|
||||
pool<Cell*> cell_warn_cache;
|
||||
SigPool undriven_signals;
|
||||
|
||||
EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, bool model_undef, int max_seq, bool set_assumes) : module(module), sigmap(module),
|
||||
EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, EquivBasicConfig cfg) : EquivWorker<>(module, &sigmap, cfg), sigmap(module),
|
||||
cells(module->selected_cells()), workset(unproven_equiv_cells),
|
||||
satgen(ez.get(), &sigmap), max_seq(max_seq), success_counter(0), set_assumes(set_assumes)
|
||||
{
|
||||
satgen.model_undef = model_undef;
|
||||
}
|
||||
success_counter(0) {}
|
||||
|
||||
void create_timestep(int step)
|
||||
{
|
||||
vector<int> ez_equal_terms;
|
||||
|
||||
for (auto cell : cells) {
|
||||
if (!satgen.importCell(cell, step) && !cell_warn_cache.count(cell)) {
|
||||
if (cell->is_builtin_ff())
|
||||
log_warning("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type));
|
||||
else
|
||||
log_warning("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
|
||||
cell_warn_cache.insert(cell);
|
||||
if (!satgen.importCell(cell, step)) {
|
||||
report_missing_model(cfg.ignore_unknown_cells, cell);
|
||||
}
|
||||
if (cell->type == ID($equiv)) {
|
||||
SigBit bit_a = sigmap(cell->getPort(ID::A)).as_bit();
|
||||
|
|
@ -78,7 +63,7 @@ struct EquivInductWorker
|
|||
}
|
||||
}
|
||||
|
||||
if (set_assumes) {
|
||||
if (cfg.set_assumes) {
|
||||
if (step == 1) {
|
||||
RTLIL::SigSpec assumes_a, assumes_en;
|
||||
satgen.getAssumes(assumes_a, assumes_en, step);
|
||||
|
|
@ -123,7 +108,7 @@ struct EquivInductWorker
|
|||
GetSize(satgen.initial_state), GetSize(undriven_signals));
|
||||
}
|
||||
|
||||
for (int step = 1; step <= max_seq; step++)
|
||||
for (int step = 1; step <= cfg.max_seq; step++)
|
||||
{
|
||||
ez->assume(ez_step_is_consistent[step]);
|
||||
|
||||
|
|
@ -146,7 +131,7 @@ struct EquivInductWorker
|
|||
return;
|
||||
}
|
||||
|
||||
log(" Proof for induction step failed. %s\n", step != max_seq ? "Extending to next time step." : "Trying to prove individual $equiv from workset.");
|
||||
log(" Proof for induction step failed. %s\n", step != cfg.max_seq ? "Extending to next time step." : "Trying to prove individual $equiv from workset.");
|
||||
}
|
||||
|
||||
workset.sort();
|
||||
|
|
@ -158,12 +143,12 @@ struct EquivInductWorker
|
|||
|
||||
log(" Trying to prove $equiv for %s:", log_signal(sigmap(cell->getPort(ID::Y))));
|
||||
|
||||
int ez_a = satgen.importSigBit(bit_a, max_seq+1);
|
||||
int ez_b = satgen.importSigBit(bit_b, max_seq+1);
|
||||
int ez_a = satgen.importSigBit(bit_a, cfg.max_seq+1);
|
||||
int ez_b = satgen.importSigBit(bit_b, cfg.max_seq+1);
|
||||
int cond = ez->XOR(ez_a, ez_b);
|
||||
|
||||
if (satgen.model_undef)
|
||||
cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, max_seq+1)));
|
||||
cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, cfg.max_seq+1)));
|
||||
|
||||
if (!ez->solve(cond)) {
|
||||
log(" success!\n");
|
||||
|
|
@ -189,14 +174,7 @@ struct EquivInductPass : public Pass {
|
|||
log("Only selected $equiv cells are proven and only selected cells are used to\n");
|
||||
log("perform the proof.\n");
|
||||
log("\n");
|
||||
log(" -undef\n");
|
||||
log(" enable modelling of undef states\n");
|
||||
log("\n");
|
||||
log(" -seq <N>\n");
|
||||
log(" the max. number of time steps to be considered (default = 4)\n");
|
||||
log("\n");
|
||||
log(" -set-assumes\n");
|
||||
log(" set all assumptions provided via $assume cells\n");
|
||||
log("%s", EquivBasicConfig::help("4"));
|
||||
log("\n");
|
||||
log("This command is very effective in proving complex sequential circuits, when\n");
|
||||
log("the internal state of the circuit quickly propagates to $equiv cells.\n");
|
||||
|
|
@ -214,25 +192,15 @@ struct EquivInductPass : public Pass {
|
|||
void execute(std::vector<std::string> args, Design *design) override
|
||||
{
|
||||
int success_counter = 0;
|
||||
bool model_undef = false, set_assumes = false;
|
||||
int max_seq = 4;
|
||||
EquivBasicConfig cfg {};
|
||||
cfg.max_seq = 4;
|
||||
|
||||
log_header(design, "Executing EQUIV_INDUCT pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-undef") {
|
||||
model_undef = true;
|
||||
if (cfg.parse(args, argidx))
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-seq" && argidx+1 < args.size()) {
|
||||
max_seq = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-set-assumes") {
|
||||
set_assumes = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
|
@ -253,7 +221,7 @@ struct EquivInductPass : public Pass {
|
|||
continue;
|
||||
}
|
||||
|
||||
EquivInductWorker worker(module, unproven_equiv_cells, model_undef, max_seq, set_assumes);
|
||||
EquivInductWorker worker(module, unproven_equiv_cells, cfg);
|
||||
worker.run();
|
||||
success_counter += worker.success_counter;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,15 +17,51 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/satgen.h"
|
||||
#include "passes/equiv/equiv.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct EquivSimpleWorker
|
||||
struct EquivSimpleConfig : EquivBasicConfig {
|
||||
bool verbose = false;
|
||||
bool short_cones = false;
|
||||
bool group = true;
|
||||
bool parse(const std::vector<std::string>& args, size_t& idx) {
|
||||
if (EquivBasicConfig::parse(args, idx))
|
||||
return true;
|
||||
if (args[idx] == "-v") {
|
||||
verbose = true;
|
||||
return true;
|
||||
}
|
||||
if (args[idx] == "-short") {
|
||||
short_cones = true;
|
||||
return true;
|
||||
}
|
||||
if (args[idx] == "-nogroup") {
|
||||
group = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static std::string help(const char* default_seq) {
|
||||
return EquivBasicConfig::help(default_seq) +
|
||||
" -v\n"
|
||||
" verbose output\n"
|
||||
"\n"
|
||||
" -short\n"
|
||||
" create shorter input cones that stop at shared nodes. This yields\n"
|
||||
" simpler SAT problems but sometimes fails to prove equivalence.\n"
|
||||
"\n"
|
||||
" -nogroup\n"
|
||||
" disabling grouping of $equiv cells by output wire\n"
|
||||
"\n";
|
||||
}
|
||||
};
|
||||
|
||||
struct EquivSimpleWorker : public EquivWorker<EquivSimpleConfig>
|
||||
{
|
||||
Module *module;
|
||||
const vector<Cell*> &equiv_cells;
|
||||
const vector<Cell*> &assume_cells;
|
||||
struct Cone {
|
||||
|
|
@ -43,27 +79,11 @@ struct EquivSimpleWorker
|
|||
};
|
||||
DesignModel model;
|
||||
|
||||
ezSatPtr ez;
|
||||
SatGen satgen;
|
||||
|
||||
struct Config {
|
||||
bool verbose = false;
|
||||
bool short_cones = false;
|
||||
bool model_undef = false;
|
||||
bool nogroup = false;
|
||||
bool set_assumes = false;
|
||||
int max_seq = 1;
|
||||
};
|
||||
Config cfg;
|
||||
|
||||
pool<pair<Cell*, int>> imported_cells_cache;
|
||||
|
||||
EquivSimpleWorker(const vector<Cell*> &equiv_cells, const vector<Cell*> &assume_cells, DesignModel model, Config cfg) :
|
||||
module(equiv_cells.front()->module), equiv_cells(equiv_cells), assume_cells(assume_cells),
|
||||
model(model), satgen(ez.get(), &model.sigmap), cfg(cfg)
|
||||
{
|
||||
satgen.model_undef = cfg.model_undef;
|
||||
}
|
||||
EquivSimpleWorker(const vector<Cell*> &equiv_cells, const vector<Cell*> &assume_cells, DesignModel model, EquivSimpleConfig cfg) :
|
||||
EquivWorker<EquivSimpleConfig>(equiv_cells.front()->module, &model.sigmap, cfg), equiv_cells(equiv_cells), assume_cells(assume_cells),
|
||||
model(model) {}
|
||||
|
||||
struct ConeFinder {
|
||||
DesignModel model;
|
||||
|
|
@ -229,14 +249,6 @@ struct EquivSimpleWorker
|
|||
return extra_problem_cells;
|
||||
}
|
||||
|
||||
static void report_missing_model(Cell* cell)
|
||||
{
|
||||
if (cell->is_builtin_ff())
|
||||
log_cmd_error("No SAT model available for async FF cell %s (%s). Consider running `async2sync` or `clk2fflogic` first.\n", log_id(cell), log_id(cell->type));
|
||||
else
|
||||
log_cmd_error("No SAT model available for cell %s (%s).\n", log_id(cell), log_id(cell->type));
|
||||
}
|
||||
|
||||
void prepare_ezsat(int ez_context, SigBit bit_a, SigBit bit_b)
|
||||
{
|
||||
if (satgen.model_undef)
|
||||
|
|
@ -257,7 +269,9 @@ struct EquivSimpleWorker
|
|||
}
|
||||
void construct_ezsat(const pool<SigBit>& input_bits, int step)
|
||||
{
|
||||
log("ezsat\n");
|
||||
if (cfg.set_assumes) {
|
||||
log("yep assume\n");
|
||||
if (cfg.verbose && step == cfg.max_seq) {
|
||||
RTLIL::SigSpec assumes_a, assumes_en;
|
||||
satgen.getAssumes(assumes_a, assumes_en, step+1);
|
||||
|
|
@ -323,7 +337,7 @@ struct EquivSimpleWorker
|
|||
for (auto cell : problem_cells) {
|
||||
auto key = pair<Cell*, int>(cell, step+1);
|
||||
if (!imported_cells_cache.count(key) && !satgen.importCell(cell, step+1)) {
|
||||
report_missing_model(cell);
|
||||
report_missing_model(cfg.ignore_unknown_cells, cell);
|
||||
}
|
||||
imported_cells_cache.insert(key);
|
||||
}
|
||||
|
|
@ -414,59 +428,20 @@ struct EquivSimplePass : public Pass {
|
|||
log("\n");
|
||||
log("This command tries to prove $equiv cells using a simple direct SAT approach.\n");
|
||||
log("\n");
|
||||
log(" -v\n");
|
||||
log(" verbose output\n");
|
||||
log("\n");
|
||||
log(" -undef\n");
|
||||
log(" enable modelling of undef states\n");
|
||||
log("\n");
|
||||
log(" -short\n");
|
||||
log(" create shorter input cones that stop at shared nodes. This yields\n");
|
||||
log(" simpler SAT problems but sometimes fails to prove equivalence.\n");
|
||||
log("\n");
|
||||
log(" -nogroup\n");
|
||||
log(" disabling grouping of $equiv cells by output wire\n");
|
||||
log("\n");
|
||||
log(" -seq <N>\n");
|
||||
log(" the max. number of time steps to be considered (default = 1)\n");
|
||||
log("\n");
|
||||
log(" -set-assumes\n");
|
||||
log(" set all assumptions provided via $assume cells\n");
|
||||
log("%s", EquivSimpleConfig::help("1"));
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, Design *design) override
|
||||
{
|
||||
EquivSimpleWorker::Config cfg = {};
|
||||
EquivSimpleConfig cfg {};
|
||||
int success_counter = 0;
|
||||
|
||||
log_header(design, "Executing EQUIV_SIMPLE pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-v") {
|
||||
cfg.verbose = true;
|
||||
if (cfg.parse(args, argidx))
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-short") {
|
||||
cfg.short_cones = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-undef") {
|
||||
cfg.model_undef = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nogroup") {
|
||||
cfg.nogroup = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-seq" && argidx+1 < args.size()) {
|
||||
cfg.max_seq = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-set-assumes") {
|
||||
cfg.set_assumes = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
|
@ -489,7 +464,7 @@ struct EquivSimplePass : public Pass {
|
|||
if (cell->type == ID($equiv) && cell->getPort(ID::A) != cell->getPort(ID::B)) {
|
||||
auto bit = sigmap(cell->getPort(ID::Y).as_bit());
|
||||
auto bit_group = bit;
|
||||
if (!cfg.nogroup && bit_group.wire)
|
||||
if (cfg.group && bit_group.wire)
|
||||
bit_group.offset = 0;
|
||||
unproven_equiv_cells[bit_group][bit] = cell;
|
||||
unproven_cells_counter++;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ struct PassOptions {
|
|||
bool no_auto_distributed;
|
||||
bool no_auto_block;
|
||||
bool no_auto_huge;
|
||||
bool force_params;
|
||||
double logic_cost_rom;
|
||||
double logic_cost_ram;
|
||||
};
|
||||
|
|
@ -1859,7 +1860,7 @@ void MemMapping::emit_port(const MemConfig &cfg, std::vector<Cell*> &cells, cons
|
|||
cell->setParam(stringf("\\PORT_%s_WR_BE_WIDTH", name), GetSize(hw_wren));
|
||||
} else {
|
||||
cell->setPort(stringf("\\PORT_%s_WR_EN", name), hw_wren);
|
||||
if (cfg.def->byte != 0 && cfg.def->width_mode != WidthMode::Single)
|
||||
if (cfg.def->byte != 0 && (cfg.def->width_mode != WidthMode::Single || opts.force_params))
|
||||
cell->setParam(stringf("\\PORT_%s_WR_EN_WIDTH", name), GetSize(hw_wren));
|
||||
}
|
||||
}
|
||||
|
|
@ -2068,8 +2069,10 @@ void MemMapping::emit(const MemConfig &cfg) {
|
|||
std::vector<Cell *> cells;
|
||||
for (int rd = 0; rd < cfg.repl_d; rd++) {
|
||||
Cell *cell = mem.module->addCell(stringf("%s.%d.%d", mem.memid, rp, rd), cfg.def->id);
|
||||
if (cfg.def->width_mode == WidthMode::Global)
|
||||
if (cfg.def->width_mode == WidthMode::Global || opts.force_params)
|
||||
cell->setParam(ID::WIDTH, cfg.def->dbits[cfg.base_width_log2]);
|
||||
if (opts.force_params)
|
||||
cell->setParam(ID::ABITS, cfg.def->abits);
|
||||
if (cfg.def->widthscale) {
|
||||
std::vector<State> val;
|
||||
for (auto &bit: init_swz.bits[rd])
|
||||
|
|
@ -2179,6 +2182,9 @@ struct MemoryLibMapPass : public Pass {
|
|||
log(" Disables automatic mapping of given kind of RAMs. Manual mapping\n");
|
||||
log(" (using ram_style or other attributes) is still supported.\n");
|
||||
log("\n");
|
||||
log(" -force-params\n");
|
||||
log(" Always generate memories with WIDTH and ABITS parameters.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
|
|
@ -2188,6 +2194,7 @@ struct MemoryLibMapPass : public Pass {
|
|||
opts.no_auto_distributed = false;
|
||||
opts.no_auto_block = false;
|
||||
opts.no_auto_huge = false;
|
||||
opts.force_params = false;
|
||||
opts.logic_cost_ram = 1.0;
|
||||
opts.logic_cost_rom = 1.0/16.0;
|
||||
log_header(design, "Executing MEMORY_LIBMAP pass (mapping memories to cells).\n");
|
||||
|
|
@ -2214,6 +2221,10 @@ struct MemoryLibMapPass : public Pass {
|
|||
opts.no_auto_huge = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-force-params") {
|
||||
opts.force_params = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-logic-cost-rom" && argidx+1 < args.size()) {
|
||||
opts.logic_cost_rom = strtod(args[++argidx].c_str(), nullptr);
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ OBJS += passes/opt/opt_lut_ins.o
|
|||
OBJS += passes/opt/opt_ffinv.o
|
||||
OBJS += passes/opt/pmux2shiftx.o
|
||||
OBJS += passes/opt/muxpack.o
|
||||
OBJS += passes/opt/opt_balance_tree.o
|
||||
|
||||
OBJS += passes/opt/peepopt.o
|
||||
GENFILES += passes/opt/peepopt_pm.h
|
||||
|
|
|
|||
|
|
@ -193,7 +193,6 @@ struct OptPass : public Pass {
|
|||
}
|
||||
|
||||
design->optimize();
|
||||
design->sort();
|
||||
design->check();
|
||||
|
||||
log_header(design, "Finished fast OPT passes.%s\n", fast_mode ? "" : " (There is nothing left to do.)");
|
||||
|
|
|
|||
379
passes/opt/opt_balance_tree.cc
Normal file
379
passes/opt/opt_balance_tree.cc
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
* 2019 Eddie Hung <eddie@fpgeh.com>
|
||||
* 2024 Akash Levy <akash@silimate.com>
|
||||
*
|
||||
* 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"
|
||||
#include <deque>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
struct OptBalanceTreeWorker {
|
||||
// Module and signal map
|
||||
Module *module;
|
||||
SigMap sigmap;
|
||||
|
||||
// Counts of each cell type that are getting balanced
|
||||
dict<IdString, int> cell_count;
|
||||
|
||||
// Check if cell is of the right type and has matching input/output widths
|
||||
// Only allow cells with "natural" output widths (no truncation) to prevent
|
||||
// equivalence issues when rebalancing (see YosysHQ/yosys#5605)
|
||||
bool is_right_type(Cell* cell, IdString cell_type) {
|
||||
if (cell->type != cell_type)
|
||||
return false;
|
||||
|
||||
int y_width = cell->getParam(ID::Y_WIDTH).as_int();
|
||||
int a_width = cell->getParam(ID::A_WIDTH).as_int();
|
||||
int b_width = cell->getParam(ID::B_WIDTH).as_int();
|
||||
|
||||
// Calculate the "natural" output width for this operation
|
||||
int natural_width;
|
||||
if (cell_type == ID($add)) {
|
||||
// Addition produces max(A_WIDTH, B_WIDTH) + 1 (for carry bit)
|
||||
natural_width = std::max(a_width, b_width) + 1;
|
||||
} else if (cell_type == ID($mul)) {
|
||||
// Multiplication produces A_WIDTH + B_WIDTH
|
||||
natural_width = a_width + b_width;
|
||||
} else {
|
||||
// Logic operations ($and/$or/$xor) produce max(A_WIDTH, B_WIDTH)
|
||||
natural_width = std::max(a_width, b_width);
|
||||
}
|
||||
|
||||
// Only allow cells where Y_WIDTH >= natural width (no truncation)
|
||||
// This prevents rebalancing chains where truncation semantics matter
|
||||
return y_width >= natural_width;
|
||||
}
|
||||
|
||||
// Create a balanced binary tree from a vector of source signals
|
||||
SigSpec create_balanced_tree(vector<SigSpec> &sources, IdString cell_type, Cell* cell) {
|
||||
// Base case: if we have no sources, return an empty signal
|
||||
if (sources.size() == 0)
|
||||
return SigSpec();
|
||||
|
||||
// Base case: if we have only one source, return it
|
||||
if (sources.size() == 1)
|
||||
return sources[0];
|
||||
|
||||
// Base case: if we have two sources, create a single cell
|
||||
if (sources.size() == 2) {
|
||||
// Create a new cell of the same type
|
||||
Cell* new_cell = module->addCell(NEW_ID, cell_type);
|
||||
|
||||
// Copy attributes from reference cell
|
||||
new_cell->attributes = cell->attributes;
|
||||
|
||||
// Create output wire
|
||||
int out_width = cell->getParam(ID::Y_WIDTH).as_int();
|
||||
if (cell_type == ID($add))
|
||||
out_width = max(sources[0].size(), sources[1].size()) + 1;
|
||||
else if (cell_type == ID($mul))
|
||||
out_width = sources[0].size() + sources[1].size();
|
||||
Wire* out_wire = module->addWire(NEW_ID, out_width);
|
||||
|
||||
// Connect ports and fix up parameters
|
||||
new_cell->setPort(ID::A, sources[0]);
|
||||
new_cell->setPort(ID::B, sources[1]);
|
||||
new_cell->setPort(ID::Y, out_wire);
|
||||
new_cell->fixup_parameters();
|
||||
new_cell->setParam(ID::A_SIGNED, cell->getParam(ID::A_SIGNED));
|
||||
new_cell->setParam(ID::B_SIGNED, cell->getParam(ID::B_SIGNED));
|
||||
|
||||
// Update count and return output wire
|
||||
cell_count[cell_type]++;
|
||||
return out_wire;
|
||||
}
|
||||
|
||||
// Recursive case: split sources into two groups and create subtrees
|
||||
int mid = (sources.size() + 1) / 2;
|
||||
vector<SigSpec> left_sources(sources.begin(), sources.begin() + mid);
|
||||
vector<SigSpec> right_sources(sources.begin() + mid, sources.end());
|
||||
|
||||
SigSpec left_tree = create_balanced_tree(left_sources, cell_type, cell);
|
||||
SigSpec right_tree = create_balanced_tree(right_sources, cell_type, cell);
|
||||
|
||||
// Create a cell to combine the two subtrees
|
||||
Cell* new_cell = module->addCell(NEW_ID, cell_type);
|
||||
|
||||
// Copy attributes from reference cell
|
||||
new_cell->attributes = cell->attributes;
|
||||
|
||||
// Create output wire
|
||||
int out_width = cell->getParam(ID::Y_WIDTH).as_int();
|
||||
if (cell_type == ID($add))
|
||||
out_width = max(left_tree.size(), right_tree.size()) + 1;
|
||||
else if (cell_type == ID($mul))
|
||||
out_width = left_tree.size() + right_tree.size();
|
||||
Wire* out_wire = module->addWire(NEW_ID, out_width);
|
||||
|
||||
// Connect ports and fix up parameters
|
||||
new_cell->setPort(ID::A, left_tree);
|
||||
new_cell->setPort(ID::B, right_tree);
|
||||
new_cell->setPort(ID::Y, out_wire);
|
||||
new_cell->fixup_parameters();
|
||||
new_cell->setParam(ID::A_SIGNED, cell->getParam(ID::A_SIGNED));
|
||||
new_cell->setParam(ID::B_SIGNED, cell->getParam(ID::B_SIGNED));
|
||||
|
||||
// Update count and return output wire
|
||||
cell_count[cell_type]++;
|
||||
return out_wire;
|
||||
}
|
||||
|
||||
OptBalanceTreeWorker(Module *module, const vector<IdString> cell_types) : module(module), sigmap(module) {
|
||||
// Do for each cell type
|
||||
for (auto cell_type : cell_types) {
|
||||
// Index all of the nets in the module
|
||||
dict<SigSpec, Cell*> sig_to_driver;
|
||||
dict<SigSpec, pool<Cell*>> sig_to_sink;
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (cell->output(conn.first))
|
||||
sig_to_driver[sigmap(conn.second)] = cell;
|
||||
|
||||
if (cell->input(conn.first))
|
||||
{
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
if (sig_to_sink.count(sig) == 0)
|
||||
sig_to_sink[sig] = pool<Cell*>();
|
||||
sig_to_sink[sig].insert(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to check if any wires connect to module ports
|
||||
pool<SigSpec> input_port_sigs;
|
||||
pool<SigSpec> output_port_sigs;
|
||||
for (auto wire : module->selected_wires())
|
||||
if (wire->port_input || wire->port_output) {
|
||||
SigSpec sig = sigmap(wire);
|
||||
for (auto bit : sig) {
|
||||
if (wire->port_input)
|
||||
input_port_sigs.insert(bit);
|
||||
if (wire->port_output)
|
||||
output_port_sigs.insert(bit);
|
||||
}
|
||||
}
|
||||
|
||||
// Actual logic starts here
|
||||
pool<Cell*> consumed_cells;
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
// If consumed or not the correct type, skip
|
||||
if (consumed_cells.count(cell) || !is_right_type(cell, cell_type))
|
||||
continue;
|
||||
|
||||
// BFS, following all chains until they hit a cell of a different type
|
||||
// Pick the longest one
|
||||
auto y = sigmap(cell->getPort(ID::Y));
|
||||
pool<Cell*> sinks;
|
||||
pool<Cell*> current_loads = sig_to_sink[y];
|
||||
pool<Cell*> next_loads;
|
||||
while (!current_loads.empty())
|
||||
{
|
||||
// Find each sink and see what they are
|
||||
for (auto x : current_loads)
|
||||
{
|
||||
// If not the correct type, don't follow any further
|
||||
// (but add the originating cell to the list of sinks)
|
||||
if (!is_right_type(x, cell_type))
|
||||
{
|
||||
sinks.insert(cell);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto xy = sigmap(x->getPort(ID::Y));
|
||||
|
||||
// If this signal drives a port, add it to the sinks
|
||||
// (even though it may not be the end of a chain)
|
||||
for (auto bit : xy) {
|
||||
if (output_port_sigs.count(bit) && !consumed_cells.count(x)) {
|
||||
sinks.insert(x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Search signal's fanout
|
||||
auto& next = sig_to_sink[xy];
|
||||
for (auto z : next)
|
||||
next_loads.insert(z);
|
||||
}
|
||||
|
||||
// If we couldn't find any downstream loads, stop.
|
||||
// Create a reduction for each of the max-length chains we found
|
||||
if (next_loads.empty())
|
||||
{
|
||||
for (auto s : current_loads)
|
||||
{
|
||||
// Not one of our gates? Don't follow any further
|
||||
if (!is_right_type(s, cell_type))
|
||||
continue;
|
||||
|
||||
sinks.insert(s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, continue down the chain
|
||||
current_loads = next_loads;
|
||||
next_loads.clear();
|
||||
}
|
||||
|
||||
// We have our list of sinks, now go tree balance the chains
|
||||
for (auto head_cell : sinks)
|
||||
{
|
||||
// Avoid duplication if we already were covered
|
||||
if (consumed_cells.count(head_cell))
|
||||
continue;
|
||||
|
||||
// Get sources of the chain
|
||||
dict<SigSpec, int> sources;
|
||||
dict<SigSpec, bool> signeds;
|
||||
int inner_cells = 0;
|
||||
std::deque<Cell*> bfs_queue = {head_cell};
|
||||
while (bfs_queue.size())
|
||||
{
|
||||
Cell* x = bfs_queue.front();
|
||||
bfs_queue.pop_front();
|
||||
|
||||
for (IdString port: {ID::A, ID::B}) {
|
||||
auto sig = sigmap(x->getPort(port));
|
||||
Cell* drv = sig_to_driver[sig];
|
||||
bool drv_ok = drv && is_right_type(drv, cell_type);
|
||||
for (auto bit : sig) {
|
||||
if (input_port_sigs.count(bit) && !consumed_cells.count(drv)) {
|
||||
drv_ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (drv_ok) {
|
||||
inner_cells++;
|
||||
bfs_queue.push_back(drv);
|
||||
} else {
|
||||
sources[sig]++;
|
||||
signeds[sig] = x->getParam(port == ID::A ? ID::A_SIGNED : ID::B_SIGNED).as_bool();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inner_cells)
|
||||
{
|
||||
// Create a tree
|
||||
log_debug(" Creating tree for %s with %d sources and %d inner cells...\n", log_id(head_cell), GetSize(sources), inner_cells);
|
||||
|
||||
// Build a vector of all source signals
|
||||
vector<SigSpec> source_signals;
|
||||
vector<bool> signed_flags;
|
||||
for (auto &source : sources) {
|
||||
for (int i = 0; i < source.second; i++) {
|
||||
source_signals.push_back(source.first);
|
||||
signed_flags.push_back(signeds[source.first]);
|
||||
}
|
||||
}
|
||||
|
||||
// If not all signed flags are the same, do not balance
|
||||
if (!std::all_of(signed_flags.begin(), signed_flags.end(), [&](bool flag) { return flag == signed_flags[0]; })) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create the balanced tree
|
||||
SigSpec tree_output = create_balanced_tree(source_signals, cell_type, head_cell);
|
||||
|
||||
// Connect the tree output to the head cell's output
|
||||
SigSpec head_output = sigmap(head_cell->getPort(ID::Y));
|
||||
int connect_width = std::min(head_output.size(), tree_output.size());
|
||||
module->connect(head_output.extract(0, connect_width), tree_output.extract(0, connect_width));
|
||||
if (head_output.size() > tree_output.size()) {
|
||||
SigBit sext_bit = head_cell->getParam(ID::A_SIGNED).as_bool() ? head_output[connect_width - 1] : State::S0;
|
||||
module->connect(head_output.extract(connect_width, head_output.size() - connect_width), SigSpec(sext_bit, head_output.size() - connect_width));
|
||||
}
|
||||
|
||||
// Mark consumed cell for removal
|
||||
consumed_cells.insert(head_cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all consumed cells, which now have been replaced by trees
|
||||
for (auto cell : consumed_cells)
|
||||
module->remove(cell);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct OptBalanceTreePass : public Pass {
|
||||
OptBalanceTreePass() : Pass("opt_balance_tree", "$and/$or/$xor/$add/$mul cascades to trees") { }
|
||||
void help() override {
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" opt_balance_tree [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass converts cascaded chains of $and/$or/$xor/$add/$mul cells into\n");
|
||||
log("trees of cells to improve timing.\n");
|
||||
log("\n");
|
||||
log(" -arith\n");
|
||||
log(" only convert arithmetic cells.\n");
|
||||
log("\n");
|
||||
log(" -logic\n");
|
||||
log(" only convert logic cells.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
|
||||
log_header(design, "Executing OPT_BALANCE_TREE pass (cell cascades to trees).\n");
|
||||
log_experimental("open_balance_tree");
|
||||
|
||||
// Handle arguments
|
||||
size_t argidx;
|
||||
vector<IdString> cell_types = {ID($and), ID($or), ID($xor), ID($add), ID($mul)};
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-arith") {
|
||||
cell_types = {ID($add), ID($mul)};
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-logic") {
|
||||
cell_types = {ID($and), ID($or), ID($xor)};
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
// Count of all cells that were packed
|
||||
dict<IdString, int> cell_count;
|
||||
for (auto module : design->selected_modules()) {
|
||||
OptBalanceTreeWorker worker(module, cell_types);
|
||||
for (auto cell : worker.cell_count) {
|
||||
cell_count[cell.first] += cell.second;
|
||||
}
|
||||
}
|
||||
|
||||
// Log stats
|
||||
for (auto cell_type : cell_types)
|
||||
log("Converted %d %s cells into trees.\n", cell_count[cell_type], log_id(cell_type));
|
||||
|
||||
// Clean up
|
||||
Yosys::run_pass("clean -purge");
|
||||
}
|
||||
} OptBalanceTreePass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/ffinit.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -101,7 +102,10 @@ struct keep_cache_t
|
|||
};
|
||||
|
||||
keep_cache_t keep_cache;
|
||||
CellTypes ct_reg, ct_all;
|
||||
static constexpr auto ct_reg = StaticCellTypes::Categories::join(
|
||||
StaticCellTypes::Compat::mem_ff,
|
||||
StaticCellTypes::categories.is_anyinit);
|
||||
NewCellTypes ct_all;
|
||||
int count_rm_cells, count_rm_wires;
|
||||
|
||||
void rmunused_module_cells(Module *module, bool verbose)
|
||||
|
|
@ -271,6 +275,9 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo
|
|||
return conns.check_any(s2);
|
||||
}
|
||||
|
||||
if (w1 == w2)
|
||||
return s2.offset < s1.offset;
|
||||
|
||||
if (w1->port_output != w2->port_output)
|
||||
return w2->port_output;
|
||||
|
||||
|
|
@ -307,10 +314,10 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
|||
if (!purge_mode)
|
||||
for (auto &it : module->cells_) {
|
||||
RTLIL::Cell *cell = it.second;
|
||||
if (ct_reg.cell_known(cell->type)) {
|
||||
if (ct_reg(cell->type)) {
|
||||
bool clk2fflogic = cell->get_bool_attribute(ID(clk2fflogic));
|
||||
for (auto &it2 : cell->connections())
|
||||
if (clk2fflogic ? it2.first == ID::D : ct_reg.cell_output(cell->type, it2.first))
|
||||
if (clk2fflogic ? it2.first == ID::D : ct_all.cell_output(cell->type, it2.first))
|
||||
register_signals.add(it2.second);
|
||||
}
|
||||
for (auto &it2 : cell->connections())
|
||||
|
|
@ -343,7 +350,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
|||
RTLIL::Wire *wire = it.second;
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
RTLIL::SigBit s1 = RTLIL::SigBit(wire, i), s2 = assign_map(s1);
|
||||
if (!compare_signals(s1, s2, register_signals, connected_signals, direct_wires))
|
||||
if (compare_signals(s2, s1, register_signals, connected_signals, direct_wires))
|
||||
assign_map.add(s1);
|
||||
}
|
||||
}
|
||||
|
|
@ -467,8 +474,6 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
|||
wire->attributes.erase(ID::init);
|
||||
else
|
||||
wire->attributes.at(ID::init) = initval;
|
||||
used_signals.add(new_conn.first);
|
||||
used_signals.add(new_conn.second);
|
||||
module->connect(new_conn);
|
||||
}
|
||||
|
||||
|
|
@ -516,14 +521,12 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
|||
bool rmunused_module_init(RTLIL::Module *module, bool verbose)
|
||||
{
|
||||
bool did_something = false;
|
||||
CellTypes fftypes;
|
||||
fftypes.setup_internals_mem();
|
||||
|
||||
SigMap sigmap(module);
|
||||
dict<SigBit, State> qbits;
|
||||
|
||||
for (auto cell : module->cells())
|
||||
if (fftypes.cell_known(cell->type) && cell->hasPort(ID::Q))
|
||||
if (StaticCellTypes::Compat::internals_mem_ff(cell->type) && cell->hasPort(ID::Q))
|
||||
{
|
||||
SigSpec sig = cell->getPort(ID::Q);
|
||||
|
||||
|
|
@ -696,10 +699,6 @@ struct OptCleanPass : public Pass {
|
|||
|
||||
keep_cache.reset(design, purge_mode);
|
||||
|
||||
ct_reg.setup_internals_mem();
|
||||
ct_reg.setup_internals_anyinit();
|
||||
ct_reg.setup_stdcells_mem();
|
||||
|
||||
ct_all.setup(design);
|
||||
|
||||
count_rm_cells = 0;
|
||||
|
|
@ -715,11 +714,9 @@ struct OptCleanPass : public Pass {
|
|||
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
|
||||
|
||||
design->optimize();
|
||||
design->sort();
|
||||
design->check();
|
||||
|
||||
keep_cache.reset();
|
||||
ct_reg.clear();
|
||||
ct_all.clear();
|
||||
log_pop();
|
||||
|
||||
|
|
@ -760,10 +757,6 @@ struct CleanPass : public Pass {
|
|||
|
||||
keep_cache.reset(design);
|
||||
|
||||
ct_reg.setup_internals_mem();
|
||||
ct_reg.setup_internals_anyinit();
|
||||
ct_reg.setup_stdcells_mem();
|
||||
|
||||
ct_all.setup(design);
|
||||
|
||||
count_rm_cells = 0;
|
||||
|
|
@ -780,11 +773,9 @@ struct CleanPass : public Pass {
|
|||
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
|
||||
|
||||
design->optimize();
|
||||
design->sort();
|
||||
design->check();
|
||||
|
||||
keep_cache.reset();
|
||||
ct_reg.clear();
|
||||
ct_all.clear();
|
||||
|
||||
request_garbage_collection();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -20,6 +20,7 @@
|
|||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/log.h"
|
||||
#include <stdlib.h>
|
||||
|
|
@ -31,7 +32,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
bool did_something;
|
||||
|
||||
void replace_undriven(RTLIL::Module *module, const CellTypes &ct)
|
||||
void replace_undriven(RTLIL::Module *module, const NewCellTypes &ct)
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
SigPool driven_signals;
|
||||
|
|
@ -407,9 +408,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
}
|
||||
}
|
||||
|
||||
CellTypes ct_memcells;
|
||||
ct_memcells.setup_stdcells_mem();
|
||||
|
||||
if (!noclkinv)
|
||||
for (auto cell : module->cells())
|
||||
if (design->selected(module, cell)) {
|
||||
|
|
@ -433,7 +431,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
if (cell->type.in(ID($dffe), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce), ID($dffsre), ID($dlatch), ID($adlatch), ID($dlatchsr)))
|
||||
handle_polarity_inv(cell, ID::EN, ID::EN_POLARITY, assign_map, invert_map);
|
||||
|
||||
if (!ct_memcells.cell_known(cell->type))
|
||||
if (!StaticCellTypes::Compat::stdcells_mem(cell->type))
|
||||
continue;
|
||||
|
||||
handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", ID::S, assign_map, invert_map);
|
||||
|
|
@ -1667,7 +1665,11 @@ skip_identity:
|
|||
int bit_idx;
|
||||
const auto onehot = sig_a.is_onehot(&bit_idx);
|
||||
|
||||
if (onehot) {
|
||||
// Power of two
|
||||
// A is unsigned or positive
|
||||
if (onehot && (!cell->parameters[ID::A_SIGNED].as_bool() || bit_idx < sig_a.size() - 1)) {
|
||||
cell->parameters[ID::A_SIGNED] = 0;
|
||||
// 2^B = 1<<B
|
||||
if (bit_idx == 1) {
|
||||
log_debug("Replacing pow cell `%s' in module `%s' with left-shift\n",
|
||||
cell->name.c_str(), module->name.c_str());
|
||||
|
|
@ -1679,7 +1681,6 @@ skip_identity:
|
|||
log_debug("Replacing pow cell `%s' in module `%s' with multiply and left-shift\n",
|
||||
cell->name.c_str(), module->name.c_str());
|
||||
cell->type = ID($mul);
|
||||
cell->parameters[ID::A_SIGNED] = 0;
|
||||
cell->setPort(ID::A, Const(bit_idx, cell->parameters[ID::A_WIDTH].as_int()));
|
||||
|
||||
SigSpec y_wire = module->addWire(NEW_ID, y_size);
|
||||
|
|
@ -2291,7 +2292,7 @@ struct OptExprPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
CellTypes ct(design);
|
||||
NewCellTypes ct(design);
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
log("Optimizing module %s.\n", log_id(module));
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ struct OptLutInsPass : public Pass {
|
|||
log("\n");
|
||||
log(" -tech <technology>\n");
|
||||
log(" Instead of generic $lut cells, operate on LUT cells specific\n");
|
||||
log(" to the given technology. Valid values are: xilinx, lattice, gowin.\n");
|
||||
log(" to the given technology. Valid values are: xilinx, lattice,\n");
|
||||
log(" gowin, analogdevices.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
|
|
@ -58,7 +59,7 @@ struct OptLutInsPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (techname != "" && techname != "xilinx" && techname != "lattice" && techname != "ecp5" && techname != "gowin")
|
||||
if (techname != "" && techname != "xilinx" && techname != "lattice" && techname != "analogdevices" && techname != "gowin")
|
||||
log_cmd_error("Unsupported technology: '%s'\n", techname);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
|
|
@ -81,7 +82,7 @@ struct OptLutInsPass : public Pass {
|
|||
inputs = cell->getPort(ID::A);
|
||||
output = cell->getPort(ID::Y);
|
||||
lut = cell->getParam(ID::LUT);
|
||||
} else if (techname == "xilinx" || techname == "gowin") {
|
||||
} else if (techname == "xilinx" || techname == "gowin" || techname == "analogdevices") {
|
||||
if (cell->type == ID(LUT1)) {
|
||||
inputs = {
|
||||
cell->getPort(ID(I0)),
|
||||
|
|
@ -126,11 +127,11 @@ struct OptLutInsPass : public Pass {
|
|||
continue;
|
||||
}
|
||||
lut = cell->getParam(ID::INIT);
|
||||
if (techname == "xilinx")
|
||||
if (techname == "xilinx" || techname == "analogdevices")
|
||||
output = cell->getPort(ID::O);
|
||||
else
|
||||
output = cell->getPort(ID::F);
|
||||
} else if (techname == "lattice" || techname == "ecp5") {
|
||||
} else if (techname == "lattice") {
|
||||
if (cell->type == ID(LUT4)) {
|
||||
inputs = {
|
||||
cell->getPort(ID::A),
|
||||
|
|
@ -236,7 +237,7 @@ struct OptLutInsPass : public Pass {
|
|||
} else {
|
||||
// xilinx, gowin
|
||||
cell->setParam(ID::INIT, new_lut);
|
||||
if (techname == "xilinx")
|
||||
if (techname == "xilinx" || techname == "analogdevices")
|
||||
log_assert(GetSize(new_inputs) <= 6);
|
||||
else
|
||||
log_assert(GetSize(new_inputs) <= 4);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "kernel/sigtools.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/threading.h"
|
||||
#include "libs/sha1/sha1.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -37,16 +38,73 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
template <typename T, typename U>
|
||||
inline Hasher hash_pair(const T &t, const U &u) { return hash_ops<std::pair<T, U>>::hash(t, u); }
|
||||
|
||||
struct OptMergeWorker
|
||||
// Some cell and its hash value.
|
||||
struct CellHash
|
||||
{
|
||||
RTLIL::Design *design;
|
||||
RTLIL::Module *module;
|
||||
SigMap assign_map;
|
||||
FfInitVals initvals;
|
||||
bool mode_share_all;
|
||||
// Index of a cell in the module
|
||||
int cell_index;
|
||||
Hasher::hash_t hash_value;
|
||||
};
|
||||
|
||||
CellTypes ct;
|
||||
int total_count;
|
||||
// The algorithm:
|
||||
// 1) Compute and store the hashes of all relevant cells, in parallel.
|
||||
// 2) Given N = the number of threads, partition the cells into N buckets by hash value:
|
||||
// bucket k contains the cells whose hash value mod N = k.
|
||||
// 3) For each bucket in parallel, build a hashtable of that bucket’s cells (using the
|
||||
// precomputed hashes) and record the duplicates found.
|
||||
// 4) On the main thread, process the list of duplicates to remove cells.
|
||||
// For efficiency we fuse the second step into the first step by having the parallel
|
||||
// threads write the cells into buckets directly.
|
||||
// To avoid synchronization overhead, we divide each bucket into N shards. Each
|
||||
// thread j adds a cell to bucket k by writing to shard j of bucket k —
|
||||
// no synchronization required. In the next phase, thread k builds the hashtable for
|
||||
// bucket k by iterating over all shards of the bucket.
|
||||
|
||||
// The input to each thread in the "compute cell hashes" phase.
|
||||
struct CellRange
|
||||
{
|
||||
int begin;
|
||||
int end;
|
||||
};
|
||||
|
||||
// The output from each thread in the "compute cell hashes" phase.
|
||||
struct CellHashes
|
||||
{
|
||||
// Entry i contains the hashes where hash_value % bucketed_cell_hashes.size() == i
|
||||
std::vector<std::vector<CellHash>> bucketed_cell_hashes;
|
||||
};
|
||||
|
||||
// A duplicate cell that has been found.
|
||||
struct DuplicateCell
|
||||
{
|
||||
// Remove this cell from the design
|
||||
int remove_cell;
|
||||
// ... and use this cell instead.
|
||||
int keep_cell;
|
||||
};
|
||||
|
||||
// The input to each thread in the "find duplicate cells" phase.
|
||||
// Shards of buckets of cell hashes
|
||||
struct Shards
|
||||
{
|
||||
std::vector<std::vector<std::vector<CellHash>>> &bucketed_cell_hashes;
|
||||
};
|
||||
|
||||
// The output from each thread in the "find duplicate cells" phase.
|
||||
struct FoundDuplicates
|
||||
{
|
||||
std::vector<DuplicateCell> duplicates;
|
||||
};
|
||||
|
||||
struct OptMergeThreadWorker
|
||||
{
|
||||
const RTLIL::Module *module;
|
||||
const SigMap &assign_map;
|
||||
const FfInitVals &initvals;
|
||||
const CellTypes &ct;
|
||||
int workers;
|
||||
bool mode_share_all;
|
||||
bool mode_keepdc;
|
||||
|
||||
static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h)
|
||||
{
|
||||
|
|
@ -62,8 +120,8 @@ struct OptMergeWorker
|
|||
|
||||
static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn)
|
||||
{
|
||||
SigSpec sig_s = conn.at(ID::S);
|
||||
SigSpec sig_b = conn.at(ID::B);
|
||||
const SigSpec &sig_s = conn.at(ID::S);
|
||||
const SigSpec &sig_b = conn.at(ID::B);
|
||||
|
||||
int s_width = GetSize(sig_s);
|
||||
int width = GetSize(sig_b) / s_width;
|
||||
|
|
@ -144,7 +202,6 @@ struct OptMergeWorker
|
|||
|
||||
if (cell1->parameters != cell2->parameters)
|
||||
return false;
|
||||
|
||||
if (cell1->connections_.size() != cell2->connections_.size())
|
||||
return false;
|
||||
for (const auto &it : cell1->connections_)
|
||||
|
|
@ -199,7 +256,7 @@ struct OptMergeWorker
|
|||
return conn1 == conn2;
|
||||
}
|
||||
|
||||
bool has_dont_care_initval(const RTLIL::Cell *cell)
|
||||
bool has_dont_care_initval(const RTLIL::Cell *cell) const
|
||||
{
|
||||
if (!cell->is_builtin_ff())
|
||||
return false;
|
||||
|
|
@ -207,36 +264,134 @@ struct OptMergeWorker
|
|||
return !initvals(cell->getPort(ID::Q)).is_fully_def();
|
||||
}
|
||||
|
||||
OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all, bool mode_keepdc) :
|
||||
design(design), module(module), mode_share_all(mode_share_all)
|
||||
OptMergeThreadWorker(const RTLIL::Module *module, const FfInitVals &initvals,
|
||||
const SigMap &assign_map, const CellTypes &ct, int workers,
|
||||
bool mode_share_all, bool mode_keepdc) :
|
||||
module(module), assign_map(assign_map), initvals(initvals), ct(ct),
|
||||
workers(workers), mode_share_all(mode_share_all), mode_keepdc(mode_keepdc)
|
||||
{
|
||||
total_count = 0;
|
||||
ct.setup_internals();
|
||||
ct.setup_internals_mem();
|
||||
ct.setup_stdcells();
|
||||
ct.setup_stdcells_mem();
|
||||
}
|
||||
|
||||
if (mode_nomux) {
|
||||
ct.cell_types.erase(ID($mux));
|
||||
ct.cell_types.erase(ID($pmux));
|
||||
CellHashes compute_cell_hashes(const CellRange &cell_range) const
|
||||
{
|
||||
std::vector<std::vector<CellHash>> bucketed_cell_hashes(workers);
|
||||
for (int cell_index = cell_range.begin; cell_index < cell_range.end; ++cell_index) {
|
||||
const RTLIL::Cell *cell = module->cell_at(cell_index);
|
||||
if (!module->selected(cell))
|
||||
continue;
|
||||
if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) {
|
||||
// Ignore those for performance: meminit can have an excessively large port,
|
||||
// mem can have an excessively large parameter holding the init data
|
||||
continue;
|
||||
}
|
||||
if (cell->type == ID($scopeinfo))
|
||||
continue;
|
||||
if (mode_keepdc && has_dont_care_initval(cell))
|
||||
continue;
|
||||
if (!cell->known())
|
||||
continue;
|
||||
if (!mode_share_all && !ct.cell_known(cell->type))
|
||||
continue;
|
||||
|
||||
Hasher::hash_t h = hash_cell_function(cell, Hasher()).yield();
|
||||
int bucket_index = h % workers;
|
||||
bucketed_cell_hashes[bucket_index].push_back({cell_index, h});
|
||||
}
|
||||
return {std::move(bucketed_cell_hashes)};
|
||||
}
|
||||
|
||||
ct.cell_types.erase(ID($tribuf));
|
||||
ct.cell_types.erase(ID($_TBUF_));
|
||||
ct.cell_types.erase(ID($anyseq));
|
||||
ct.cell_types.erase(ID($anyconst));
|
||||
ct.cell_types.erase(ID($allseq));
|
||||
ct.cell_types.erase(ID($allconst));
|
||||
ct.cell_types.erase(ID($check));
|
||||
ct.cell_types.erase(ID($assert));
|
||||
ct.cell_types.erase(ID($assume));
|
||||
ct.cell_types.erase(ID($live));
|
||||
ct.cell_types.erase(ID($cover));
|
||||
FoundDuplicates find_duplicate_cells(int index, const Shards &in) const
|
||||
{
|
||||
// We keep a set of known cells. They're hashed with our hash_cell_function
|
||||
// and compared with our compare_cell_parameters_and_connections.
|
||||
struct CellHashOp {
|
||||
std::size_t operator()(const CellHash &c) const {
|
||||
return (std::size_t)c.hash_value;
|
||||
}
|
||||
};
|
||||
struct CellEqualOp {
|
||||
const OptMergeThreadWorker& worker;
|
||||
CellEqualOp(const OptMergeThreadWorker& w) : worker(w) {}
|
||||
bool operator()(const CellHash &lhs, const CellHash &rhs) const {
|
||||
return worker.compare_cell_parameters_and_connections(
|
||||
worker.module->cell_at(lhs.cell_index),
|
||||
worker.module->cell_at(rhs.cell_index));
|
||||
}
|
||||
};
|
||||
std::unordered_set<
|
||||
CellHash,
|
||||
CellHashOp,
|
||||
CellEqualOp> known_cells(0, CellHashOp(), CellEqualOp(*this));
|
||||
|
||||
std::vector<DuplicateCell> duplicates;
|
||||
for (const std::vector<std::vector<CellHash>> &buckets : in.bucketed_cell_hashes) {
|
||||
// Clear out our buckets as we go. This keeps the work of deallocation
|
||||
// off the main thread.
|
||||
std::vector<CellHash> bucket = std::move(buckets[index]);
|
||||
for (CellHash c : bucket) {
|
||||
auto [cell_in_map, inserted] = known_cells.insert(c);
|
||||
if (inserted)
|
||||
continue;
|
||||
CellHash map_c = *cell_in_map;
|
||||
if (module->cell_at(c.cell_index)->has_keep_attr()) {
|
||||
if (module->cell_at(map_c.cell_index)->has_keep_attr())
|
||||
continue;
|
||||
known_cells.erase(map_c);
|
||||
known_cells.insert(c);
|
||||
std::swap(c, map_c);
|
||||
}
|
||||
duplicates.push_back({c.cell_index, map_c.cell_index});
|
||||
}
|
||||
}
|
||||
return {duplicates};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void initialize_queues(std::vector<ConcurrentQueue<T>> &queues, int size) {
|
||||
queues.reserve(size);
|
||||
for (int i = 0; i < size; ++i)
|
||||
queues.emplace_back(1);
|
||||
}
|
||||
|
||||
struct OptMergeWorker
|
||||
{
|
||||
int total_count;
|
||||
|
||||
OptMergeWorker(RTLIL::Module *module, const CellTypes &ct, bool mode_share_all, bool mode_keepdc) :
|
||||
total_count(0)
|
||||
{
|
||||
SigMap assign_map(module);
|
||||
FfInitVals initvals;
|
||||
initvals.set(&assign_map, module);
|
||||
|
||||
log("Finding identical cells in module `%s'.\n", module->name);
|
||||
assign_map.set(module);
|
||||
|
||||
initvals.set(&assign_map, module);
|
||||
// Use no more than one worker per thousand cells, rounded down, so
|
||||
// we only start multithreading with at least 2000 cells.
|
||||
int num_worker_threads = ThreadPool::pool_size(0, module->cells_size()/1000);
|
||||
int workers = std::max(1, num_worker_threads);
|
||||
|
||||
// The main thread doesn't do any work, so if there is only one worker thread,
|
||||
// just run everything on the main thread instead.
|
||||
// This avoids creating and waiting on a thread, which is pretty high overhead
|
||||
// for very small modules.
|
||||
if (num_worker_threads == 1)
|
||||
num_worker_threads = 0;
|
||||
OptMergeThreadWorker thread_worker(module, initvals, assign_map, ct, workers, mode_share_all, mode_keepdc);
|
||||
|
||||
std::vector<ConcurrentQueue<CellRange>> cell_ranges_queues(num_worker_threads);
|
||||
std::vector<ConcurrentQueue<CellHashes>> cell_hashes_queues(num_worker_threads);
|
||||
std::vector<ConcurrentQueue<Shards>> shards_queues(num_worker_threads);
|
||||
std::vector<ConcurrentQueue<FoundDuplicates>> duplicates_queues(num_worker_threads);
|
||||
|
||||
ThreadPool thread_pool(num_worker_threads, [&](int i) {
|
||||
while (std::optional<CellRange> c = cell_ranges_queues[i].pop_front()) {
|
||||
cell_hashes_queues[i].push_back(thread_worker.compute_cell_hashes(*c));
|
||||
std::optional<Shards> shards = shards_queues[i].pop_front();
|
||||
duplicates_queues[i].push_back(thread_worker.find_duplicate_cells(i, *shards));
|
||||
}
|
||||
});
|
||||
|
||||
bool did_something = true;
|
||||
// A cell may have to go through a lot of collisions if the hash
|
||||
|
|
@ -244,87 +399,99 @@ struct OptMergeWorker
|
|||
// beyond the user's control.
|
||||
while (did_something)
|
||||
{
|
||||
std::vector<RTLIL::Cell*> cells;
|
||||
cells.reserve(module->cells().size());
|
||||
for (auto cell : module->cells()) {
|
||||
if (!design->selected(module, cell))
|
||||
continue;
|
||||
if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) {
|
||||
// Ignore those for performance: meminit can have an excessively large port,
|
||||
// mem can have an excessively large parameter holding the init data
|
||||
continue;
|
||||
}
|
||||
if (cell->type == ID($scopeinfo))
|
||||
continue;
|
||||
if (mode_keepdc && has_dont_care_initval(cell))
|
||||
continue;
|
||||
if (!cell->known())
|
||||
continue;
|
||||
if (!mode_share_all && !ct.cell_known(cell->type))
|
||||
continue;
|
||||
cells.push_back(cell);
|
||||
}
|
||||
int cells_size = module->cells_size();
|
||||
log("Computing hashes of %d cells of `%s'.\n", cells_size, module->name);
|
||||
std::vector<std::vector<std::vector<CellHash>>> sharded_bucketed_cell_hashes(workers);
|
||||
|
||||
did_something = false;
|
||||
|
||||
// We keep a set of known cells. They're hashed with our hash_cell_function
|
||||
// and compared with our compare_cell_parameters_and_connections.
|
||||
// Both need to capture OptMergeWorker to access initvals
|
||||
struct CellPtrHash {
|
||||
const OptMergeWorker& worker;
|
||||
CellPtrHash(const OptMergeWorker& w) : worker(w) {}
|
||||
std::size_t operator()(const Cell* c) const {
|
||||
return (std::size_t)worker.hash_cell_function(c, Hasher()).yield();
|
||||
}
|
||||
};
|
||||
struct CellPtrEqual {
|
||||
const OptMergeWorker& worker;
|
||||
CellPtrEqual(const OptMergeWorker& w) : worker(w) {}
|
||||
bool operator()(const Cell* lhs, const Cell* rhs) const {
|
||||
return worker.compare_cell_parameters_and_connections(lhs, rhs);
|
||||
}
|
||||
};
|
||||
std::unordered_set<
|
||||
RTLIL::Cell*,
|
||||
CellPtrHash,
|
||||
CellPtrEqual> known_cells (0, CellPtrHash(*this), CellPtrEqual(*this));
|
||||
|
||||
for (auto cell : cells)
|
||||
int cell_index = 0;
|
||||
int cells_size_mod_workers = cells_size % workers;
|
||||
{
|
||||
auto [cell_in_map, inserted] = known_cells.insert(cell);
|
||||
if (!inserted) {
|
||||
// We've failed to insert since we already have an equivalent cell
|
||||
Cell* other_cell = *cell_in_map;
|
||||
if (cell->has_keep_attr()) {
|
||||
if (other_cell->has_keep_attr())
|
||||
continue;
|
||||
known_cells.erase(other_cell);
|
||||
known_cells.insert(cell);
|
||||
std::swap(other_cell, cell);
|
||||
}
|
||||
|
||||
did_something = true;
|
||||
log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name, other_cell->name);
|
||||
for (auto &it : cell->connections()) {
|
||||
if (cell->output(it.first)) {
|
||||
RTLIL::SigSpec other_sig = other_cell->getPort(it.first);
|
||||
log_debug(" Redirecting output %s: %s = %s\n", it.first,
|
||||
log_signal(it.second), log_signal(other_sig));
|
||||
Const init = initvals(other_sig);
|
||||
initvals.remove_init(it.second);
|
||||
initvals.remove_init(other_sig);
|
||||
module->connect(RTLIL::SigSig(it.second, other_sig));
|
||||
assign_map.add(it.second, other_sig);
|
||||
initvals.set_init(other_sig, init);
|
||||
}
|
||||
}
|
||||
log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type, cell->name, module->name);
|
||||
module->remove(cell);
|
||||
total_count++;
|
||||
Multithreading multithreading;
|
||||
for (int i = 0; i < workers; ++i) {
|
||||
int num_cells = cells_size/workers + ((i < cells_size_mod_workers) ? 1 : 0);
|
||||
CellRange c = { cell_index, cell_index + num_cells };
|
||||
cell_index += num_cells;
|
||||
if (num_worker_threads > 0)
|
||||
cell_ranges_queues[i].push_back(c);
|
||||
else
|
||||
sharded_bucketed_cell_hashes[i] = std::move(thread_worker.compute_cell_hashes(c).bucketed_cell_hashes);
|
||||
}
|
||||
log_assert(cell_index == cells_size);
|
||||
if (num_worker_threads > 0)
|
||||
for (int i = 0; i < workers; ++i)
|
||||
sharded_bucketed_cell_hashes[i] = std::move(cell_hashes_queues[i].pop_front()->bucketed_cell_hashes);
|
||||
}
|
||||
|
||||
log("Finding duplicate cells in `%s'.\n", module->name);
|
||||
std::vector<DuplicateCell> merged_duplicates;
|
||||
{
|
||||
Multithreading multithreading;
|
||||
for (int i = 0; i < workers; ++i) {
|
||||
Shards thread_shards = { sharded_bucketed_cell_hashes };
|
||||
if (num_worker_threads > 0)
|
||||
shards_queues[i].push_back(thread_shards);
|
||||
else {
|
||||
std::vector<DuplicateCell> d = std::move(thread_worker.find_duplicate_cells(i, thread_shards).duplicates);
|
||||
merged_duplicates.insert(merged_duplicates.end(), d.begin(), d.end());
|
||||
}
|
||||
}
|
||||
if (num_worker_threads > 0)
|
||||
for (int i = 0; i < workers; ++i) {
|
||||
std::vector<DuplicateCell> d = std::move(duplicates_queues[i].pop_front()->duplicates);
|
||||
merged_duplicates.insert(merged_duplicates.end(), d.begin(), d.end());
|
||||
}
|
||||
}
|
||||
std::sort(merged_duplicates.begin(), merged_duplicates.end(), [](const DuplicateCell &lhs, const DuplicateCell &rhs) {
|
||||
// Sort them by the order in which duplicates would have been detected in a single-threaded
|
||||
// run. The cell at which the duplicate would have been detected is the latter of the two
|
||||
// cells involved.
|
||||
return std::max(lhs.remove_cell, lhs.keep_cell) < std::max(rhs.remove_cell, rhs.keep_cell);
|
||||
});
|
||||
|
||||
// Convert to cell pointers because removing cells will invalidate the indices.
|
||||
std::vector<std::pair<RTLIL::Cell*, RTLIL::Cell*>> cell_ptrs;
|
||||
for (DuplicateCell dup : merged_duplicates)
|
||||
cell_ptrs.push_back({module->cell_at(dup.remove_cell), module->cell_at(dup.keep_cell)});
|
||||
|
||||
for (auto [remove_cell, keep_cell] : cell_ptrs)
|
||||
{
|
||||
log_debug(" Cell `%s' is identical to cell `%s'.\n", remove_cell->name, keep_cell->name);
|
||||
for (auto &it : remove_cell->connections()) {
|
||||
if (remove_cell->output(it.first)) {
|
||||
RTLIL::SigSpec keep_sig = keep_cell->getPort(it.first);
|
||||
log_debug(" Redirecting output %s: %s = %s\n", it.first,
|
||||
log_signal(it.second), log_signal(keep_sig));
|
||||
Const init = initvals(keep_sig);
|
||||
initvals.remove_init(it.second);
|
||||
initvals.remove_init(keep_sig);
|
||||
module->connect(RTLIL::SigSig(it.second, keep_sig));
|
||||
auto keep_sig_it = keep_sig.begin();
|
||||
for (SigBit remove_sig_bit : it.second) {
|
||||
assign_map.add(remove_sig_bit, *keep_sig_it);
|
||||
++keep_sig_it;
|
||||
}
|
||||
initvals.set_init(keep_sig, init);
|
||||
}
|
||||
}
|
||||
log_debug(" Removing %s cell `%s' from module `%s'.\n", remove_cell->type, remove_cell->name, module->name);
|
||||
module->remove(remove_cell);
|
||||
total_count++;
|
||||
}
|
||||
did_something = !merged_duplicates.empty();
|
||||
}
|
||||
|
||||
for (ConcurrentQueue<CellRange> &q : cell_ranges_queues)
|
||||
q.close();
|
||||
|
||||
for (ConcurrentQueue<Shards> &q : shards_queues)
|
||||
q.close();
|
||||
|
||||
for (ConcurrentQueue<CellRange> &q : cell_ranges_queues)
|
||||
q.close();
|
||||
|
||||
for (ConcurrentQueue<Shards> &q : shards_queues)
|
||||
q.close();
|
||||
|
||||
log_suppressed();
|
||||
}
|
||||
};
|
||||
|
|
@ -377,9 +544,25 @@ struct OptMergePass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
CellTypes ct;
|
||||
ct.setup_internals();
|
||||
ct.setup_internals_mem();
|
||||
ct.setup_stdcells();
|
||||
ct.setup_stdcells_mem();
|
||||
if (mode_nomux) {
|
||||
ct.cell_types.erase(ID($mux));
|
||||
ct.cell_types.erase(ID($pmux));
|
||||
}
|
||||
ct.cell_types.erase(ID($tribuf));
|
||||
ct.cell_types.erase(ID($_TBUF_));
|
||||
ct.cell_types.erase(ID($anyseq));
|
||||
ct.cell_types.erase(ID($anyconst));
|
||||
ct.cell_types.erase(ID($allseq));
|
||||
ct.cell_types.erase(ID($allconst));
|
||||
|
||||
int total_count = 0;
|
||||
for (auto module : design->selected_modules()) {
|
||||
OptMergeWorker worker(design, module, mode_nomux, mode_share_all, mode_keepdc);
|
||||
OptMergeWorker worker(module, ct, mode_share_all, mode_keepdc);
|
||||
total_count += worker.total_count;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ struct OptMuxtreeWorker
|
|||
RTLIL::Module *module;
|
||||
SigMap assign_map;
|
||||
int removed_count;
|
||||
int glob_evals_left = 10000000;
|
||||
int glob_evals_left = 10'000'000;
|
||||
|
||||
struct bitinfo_t {
|
||||
// Is bit directly used by non-mux cells or ports?
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "kernel/modtools.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/macc.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include <iterator>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
|
@ -38,19 +39,18 @@ struct ShareWorkerConfig
|
|||
bool opt_force;
|
||||
bool opt_aggressive;
|
||||
bool opt_fast;
|
||||
pool<RTLIL::IdString> generic_uni_ops, generic_bin_ops, generic_cbin_ops, generic_other_ops;
|
||||
StaticCellTypes::Categories::Category generic_uni_ops, generic_bin_ops, generic_cbin_ops, generic_other_ops;
|
||||
};
|
||||
|
||||
struct ShareWorker
|
||||
{
|
||||
const ShareWorkerConfig config;
|
||||
int limit;
|
||||
pool<RTLIL::IdString> generic_ops;
|
||||
StaticCellTypes::Categories::Category generic_ops;
|
||||
|
||||
RTLIL::Design *design;
|
||||
RTLIL::Module *module;
|
||||
|
||||
CellTypes fwd_ct, cone_ct;
|
||||
ModWalker modwalker;
|
||||
|
||||
pool<RTLIL::Cell*> cells_to_remove;
|
||||
|
|
@ -75,7 +75,7 @@ struct ShareWorker
|
|||
queue_bits.insert(modwalker.signal_outputs.begin(), modwalker.signal_outputs.end());
|
||||
|
||||
for (auto &it : module->cells_)
|
||||
if (!fwd_ct.cell_known(it.second->type)) {
|
||||
if (!StaticCellTypes::Compat::internals_nomem_noff(it.second->type)) {
|
||||
pool<RTLIL::SigBit> &bits = modwalker.cell_inputs[it.second];
|
||||
queue_bits.insert(bits.begin(), bits.end());
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@ struct ShareWorker
|
|||
queue_bits.insert(bits.begin(), bits.end());
|
||||
visited_cells.insert(pbit.cell);
|
||||
}
|
||||
if (fwd_ct.cell_known(pbit.cell->type) && visited_cells.count(pbit.cell) == 0) {
|
||||
if (StaticCellTypes::Compat::internals_nomem_noff(pbit.cell->type) && visited_cells.count(pbit.cell) == 0) {
|
||||
pool<RTLIL::SigBit> &bits = modwalker.cell_inputs[pbit.cell];
|
||||
terminal_bits.insert(bits.begin(), bits.end());
|
||||
queue_bits.insert(bits.begin(), bits.end());
|
||||
|
|
@ -388,7 +388,7 @@ struct ShareWorker
|
|||
continue;
|
||||
}
|
||||
|
||||
if (generic_ops.count(cell->type)) {
|
||||
if (generic_ops(cell->type)) {
|
||||
if (config.opt_aggressive)
|
||||
shareable_cells.insert(cell);
|
||||
continue;
|
||||
|
|
@ -412,7 +412,7 @@ struct ShareWorker
|
|||
return true;
|
||||
}
|
||||
|
||||
if (config.generic_uni_ops.count(c1->type))
|
||||
if (config.generic_uni_ops(c1->type))
|
||||
{
|
||||
if (!config.opt_aggressive)
|
||||
{
|
||||
|
|
@ -429,7 +429,7 @@ struct ShareWorker
|
|||
return true;
|
||||
}
|
||||
|
||||
if (config.generic_bin_ops.count(c1->type) || c1->type == ID($alu))
|
||||
if (config.generic_bin_ops(c1->type) || c1->type == ID($alu))
|
||||
{
|
||||
if (!config.opt_aggressive)
|
||||
{
|
||||
|
|
@ -449,7 +449,7 @@ struct ShareWorker
|
|||
return true;
|
||||
}
|
||||
|
||||
if (config.generic_cbin_ops.count(c1->type))
|
||||
if (config.generic_cbin_ops(c1->type))
|
||||
{
|
||||
if (!config.opt_aggressive)
|
||||
{
|
||||
|
|
@ -511,7 +511,7 @@ struct ShareWorker
|
|||
{
|
||||
log_assert(c1->type == c2->type);
|
||||
|
||||
if (config.generic_uni_ops.count(c1->type))
|
||||
if (config.generic_uni_ops(c1->type))
|
||||
{
|
||||
if (c1->parameters.at(ID::A_SIGNED).as_bool() != c2->parameters.at(ID::A_SIGNED).as_bool())
|
||||
{
|
||||
|
|
@ -560,11 +560,11 @@ struct ShareWorker
|
|||
return supercell;
|
||||
}
|
||||
|
||||
if (config.generic_bin_ops.count(c1->type) || config.generic_cbin_ops.count(c1->type) || c1->type == ID($alu))
|
||||
if (config.generic_bin_ops(c1->type) || config.generic_cbin_ops(c1->type) || c1->type == ID($alu))
|
||||
{
|
||||
bool modified_src_cells = false;
|
||||
|
||||
if (config.generic_cbin_ops.count(c1->type))
|
||||
if (config.generic_cbin_ops(c1->type))
|
||||
{
|
||||
int score_unflipped = max(c1->parameters.at(ID::A_WIDTH).as_int(), c2->parameters.at(ID::A_WIDTH).as_int()) +
|
||||
max(c1->parameters.at(ID::B_WIDTH).as_int(), c2->parameters.at(ID::B_WIDTH).as_int());
|
||||
|
|
@ -758,7 +758,7 @@ struct ShareWorker
|
|||
recursion_state.insert(cell);
|
||||
|
||||
for (auto c : consumer_cells)
|
||||
if (fwd_ct.cell_known(c->type)) {
|
||||
if (StaticCellTypes::Compat::internals_nomem_noff(c->type)) {
|
||||
const pool<RTLIL::SigBit> &bits = find_forbidden_controls(c);
|
||||
forbidden_controls_cache[cell].insert(bits.begin(), bits.end());
|
||||
}
|
||||
|
|
@ -897,7 +897,7 @@ struct ShareWorker
|
|||
return activation_patterns_cache.at(cell);
|
||||
}
|
||||
for (auto &pbit : modwalker.signal_consumers[bit]) {
|
||||
log_assert(fwd_ct.cell_known(pbit.cell->type));
|
||||
log_assert(StaticCellTypes::Compat::internals_nomem_noff(pbit.cell->type));
|
||||
if ((pbit.cell->type == ID($mux) || pbit.cell->type == ID($pmux)) && (pbit.port == ID::A || pbit.port == ID::B))
|
||||
driven_data_muxes.insert(pbit.cell);
|
||||
else
|
||||
|
|
@ -1214,24 +1214,10 @@ struct ShareWorker
|
|||
ShareWorker(ShareWorkerConfig config, RTLIL::Design* design) :
|
||||
config(config), design(design), modwalker(design)
|
||||
{
|
||||
generic_ops.insert(config.generic_uni_ops.begin(), config.generic_uni_ops.end());
|
||||
generic_ops.insert(config.generic_bin_ops.begin(), config.generic_bin_ops.end());
|
||||
generic_ops.insert(config.generic_cbin_ops.begin(), config.generic_cbin_ops.end());
|
||||
generic_ops.insert(config.generic_other_ops.begin(), config.generic_other_ops.end());
|
||||
|
||||
fwd_ct.setup_internals();
|
||||
|
||||
cone_ct.setup_internals();
|
||||
cone_ct.cell_types.erase(ID($mul));
|
||||
cone_ct.cell_types.erase(ID($mod));
|
||||
cone_ct.cell_types.erase(ID($div));
|
||||
cone_ct.cell_types.erase(ID($modfloor));
|
||||
cone_ct.cell_types.erase(ID($divfloor));
|
||||
cone_ct.cell_types.erase(ID($pow));
|
||||
cone_ct.cell_types.erase(ID($shl));
|
||||
cone_ct.cell_types.erase(ID($shr));
|
||||
cone_ct.cell_types.erase(ID($sshl));
|
||||
cone_ct.cell_types.erase(ID($sshr));
|
||||
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_uni_ops);
|
||||
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_bin_ops);
|
||||
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_cbin_ops);
|
||||
generic_ops = StaticCellTypes::Categories::join(generic_ops, config.generic_other_ops);
|
||||
}
|
||||
|
||||
void operator()(RTLIL::Module *module) {
|
||||
|
|
@ -1561,45 +1547,45 @@ struct SharePass : public Pass {
|
|||
config.opt_aggressive = false;
|
||||
config.opt_fast = false;
|
||||
|
||||
config.generic_uni_ops.insert(ID($not));
|
||||
// config.generic_uni_ops.insert(ID($pos));
|
||||
config.generic_uni_ops.insert(ID($neg));
|
||||
config.generic_uni_ops.set_id(ID($not));
|
||||
// config.generic_uni_ops.set_id(ID($pos));
|
||||
config.generic_uni_ops.set_id(ID($neg));
|
||||
|
||||
config.generic_cbin_ops.insert(ID($and));
|
||||
config.generic_cbin_ops.insert(ID($or));
|
||||
config.generic_cbin_ops.insert(ID($xor));
|
||||
config.generic_cbin_ops.insert(ID($xnor));
|
||||
config.generic_cbin_ops.set_id(ID($and));
|
||||
config.generic_cbin_ops.set_id(ID($or));
|
||||
config.generic_cbin_ops.set_id(ID($xor));
|
||||
config.generic_cbin_ops.set_id(ID($xnor));
|
||||
|
||||
config.generic_bin_ops.insert(ID($shl));
|
||||
config.generic_bin_ops.insert(ID($shr));
|
||||
config.generic_bin_ops.insert(ID($sshl));
|
||||
config.generic_bin_ops.insert(ID($sshr));
|
||||
config.generic_bin_ops.set_id(ID($shl));
|
||||
config.generic_bin_ops.set_id(ID($shr));
|
||||
config.generic_bin_ops.set_id(ID($sshl));
|
||||
config.generic_bin_ops.set_id(ID($sshr));
|
||||
|
||||
config.generic_bin_ops.insert(ID($lt));
|
||||
config.generic_bin_ops.insert(ID($le));
|
||||
config.generic_bin_ops.insert(ID($eq));
|
||||
config.generic_bin_ops.insert(ID($ne));
|
||||
config.generic_bin_ops.insert(ID($eqx));
|
||||
config.generic_bin_ops.insert(ID($nex));
|
||||
config.generic_bin_ops.insert(ID($ge));
|
||||
config.generic_bin_ops.insert(ID($gt));
|
||||
config.generic_bin_ops.set_id(ID($lt));
|
||||
config.generic_bin_ops.set_id(ID($le));
|
||||
config.generic_bin_ops.set_id(ID($eq));
|
||||
config.generic_bin_ops.set_id(ID($ne));
|
||||
config.generic_bin_ops.set_id(ID($eqx));
|
||||
config.generic_bin_ops.set_id(ID($nex));
|
||||
config.generic_bin_ops.set_id(ID($ge));
|
||||
config.generic_bin_ops.set_id(ID($gt));
|
||||
|
||||
config.generic_cbin_ops.insert(ID($add));
|
||||
config.generic_cbin_ops.insert(ID($mul));
|
||||
config.generic_cbin_ops.set_id(ID($add));
|
||||
config.generic_cbin_ops.set_id(ID($mul));
|
||||
|
||||
config.generic_bin_ops.insert(ID($sub));
|
||||
config.generic_bin_ops.insert(ID($div));
|
||||
config.generic_bin_ops.insert(ID($mod));
|
||||
config.generic_bin_ops.insert(ID($divfloor));
|
||||
config.generic_bin_ops.insert(ID($modfloor));
|
||||
// config.generic_bin_ops.insert(ID($pow));
|
||||
config.generic_bin_ops.set_id(ID($sub));
|
||||
config.generic_bin_ops.set_id(ID($div));
|
||||
config.generic_bin_ops.set_id(ID($mod));
|
||||
config.generic_bin_ops.set_id(ID($divfloor));
|
||||
config.generic_bin_ops.set_id(ID($modfloor));
|
||||
// config.generic_bin_ops.set_id(ID($pow));
|
||||
|
||||
config.generic_uni_ops.insert(ID($logic_not));
|
||||
config.generic_cbin_ops.insert(ID($logic_and));
|
||||
config.generic_cbin_ops.insert(ID($logic_or));
|
||||
config.generic_uni_ops.set_id(ID($logic_not));
|
||||
config.generic_cbin_ops.set_id(ID($logic_and));
|
||||
config.generic_cbin_ops.set_id(ID($logic_or));
|
||||
|
||||
config.generic_other_ops.insert(ID($alu));
|
||||
config.generic_other_ops.insert(ID($macc));
|
||||
config.generic_other_ops.set_id(ID($alu));
|
||||
config.generic_other_ops.set_id(ID($macc));
|
||||
|
||||
log_header(design, "Executing SHARE pass (SAT-based resource sharing).\n");
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did
|
|||
all_empty = false;
|
||||
if (all_empty)
|
||||
{
|
||||
did_something = true;
|
||||
for (auto cs : sw->cases)
|
||||
delete cs;
|
||||
sw->cases.clear();
|
||||
|
|
|
|||
|
|
@ -31,6 +31,22 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static SatSolver *find_satsolver(const std::string &name)
|
||||
{
|
||||
for (auto solver = yosys_satsolver_list; solver != nullptr; solver = solver->next)
|
||||
if (solver->name == name)
|
||||
return solver;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static std::string list_satsolvers()
|
||||
{
|
||||
std::string result;
|
||||
for (auto solver = yosys_satsolver_list; solver != nullptr; solver = solver->next)
|
||||
result += result.empty() ? solver->name : ", " + solver->name;
|
||||
return result;
|
||||
}
|
||||
|
||||
struct SatHelper
|
||||
{
|
||||
RTLIL::Design *design;
|
||||
|
|
@ -60,8 +76,8 @@ struct SatHelper
|
|||
int max_timestep, timeout;
|
||||
bool gotTimeout;
|
||||
|
||||
SatHelper(RTLIL::Design *design, RTLIL::Module *module, bool enable_undef, bool set_def_formal) :
|
||||
design(design), module(module), sigmap(module), ct(design), satgen(ez.get(), &sigmap)
|
||||
SatHelper(RTLIL::Design *design, RTLIL::Module *module, SatSolver *solver, bool enable_undef, bool set_def_formal) :
|
||||
design(design), module(module), sigmap(module), ct(design), ez(solver), satgen(ez.get(), &sigmap)
|
||||
{
|
||||
this->enable_undef = enable_undef;
|
||||
satgen.model_undef = enable_undef;
|
||||
|
|
@ -227,16 +243,12 @@ struct SatHelper
|
|||
int import_cell_counter = 0;
|
||||
for (auto cell : module->cells())
|
||||
if (design->selected(module, cell)) {
|
||||
// log("Import cell: %s\n", RTLIL::id2cstr(cell->name));
|
||||
if (satgen.importCell(cell, timestep)) {
|
||||
for (auto &p : cell->connections())
|
||||
if (ct.cell_output(cell->type, p.first))
|
||||
show_drivers.insert(sigmap(p.second), cell);
|
||||
import_cell_counter++;
|
||||
} else if (ignore_unknown_cells)
|
||||
log_warning("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
|
||||
else
|
||||
log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
|
||||
} else report_missing_model(ignore_unknown_cells, cell);
|
||||
}
|
||||
log("Imported %d cells to SAT database.\n", import_cell_counter);
|
||||
|
||||
|
|
@ -441,7 +453,7 @@ struct SatHelper
|
|||
log_assert(gotTimeout == false);
|
||||
ez->setSolverTimeout(timeout);
|
||||
bool success = ez->solve(modelExpressions, modelValues, assumptions);
|
||||
if (ez->getSolverTimoutStatus())
|
||||
if (ez->getSolverTimeoutStatus())
|
||||
gotTimeout = true;
|
||||
return success;
|
||||
}
|
||||
|
|
@ -451,7 +463,7 @@ struct SatHelper
|
|||
log_assert(gotTimeout == false);
|
||||
ez->setSolverTimeout(timeout);
|
||||
bool success = ez->solve(modelExpressions, modelValues, a, b, c, d, e, f);
|
||||
if (ez->getSolverTimoutStatus())
|
||||
if (ez->getSolverTimeoutStatus())
|
||||
gotTimeout = true;
|
||||
return success;
|
||||
}
|
||||
|
|
@ -961,10 +973,10 @@ struct SatPass : public Pass {
|
|||
log(" -show-regs, -show-public, -show-all\n");
|
||||
log(" show all registers, show signals with 'public' names, show all signals\n");
|
||||
log("\n");
|
||||
log(" -ignore_div_by_zero\n");
|
||||
log(" -ignore-div-by-zero\n");
|
||||
log(" ignore all solutions that involve a division by zero\n");
|
||||
log("\n");
|
||||
log(" -ignore_unknown_cells\n");
|
||||
log(" -ignore-unknown-cells\n");
|
||||
log(" ignore all cells that can not be matched to a SAT model\n");
|
||||
log("\n");
|
||||
log("The following options can be used to set up a sequential problem:\n");
|
||||
|
|
@ -1066,6 +1078,10 @@ struct SatPass : public Pass {
|
|||
log(" -timeout <N>\n");
|
||||
log(" Maximum number of seconds a single SAT instance may take.\n");
|
||||
log("\n");
|
||||
log(" -select-solver <name>\n");
|
||||
log(" Select SAT solver implementation for this invocation.\n");
|
||||
log(" If not given, uses scratchpad key 'sat.solver' if set, otherwise default.\n");
|
||||
log("\n");
|
||||
log(" -verify\n");
|
||||
log(" Return an error and stop the synthesis script if the proof fails.\n");
|
||||
log("\n");
|
||||
|
|
@ -1097,8 +1113,14 @@ struct SatPass : public Pass {
|
|||
|
||||
log_header(design, "Executing SAT pass (solving SAT problems in the circuit).\n");
|
||||
|
||||
std::string solver_name = design->scratchpad_get_string("sat.solver", "");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-select-solver" && argidx+1 < args.size()) {
|
||||
solver_name = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-all") {
|
||||
loopcount = -1;
|
||||
continue;
|
||||
|
|
@ -1141,7 +1163,7 @@ struct SatPass : public Pass {
|
|||
stepsize = max(1, atoi(args[++argidx].c_str()));
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-ignore_div_by_zero") {
|
||||
if (args[argidx] == "-ignore-div-by-zero" || args[argidx] == "-ignore_div_by_zero") {
|
||||
ignore_div_by_zero = true;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1316,7 +1338,7 @@ struct SatPass : public Pass {
|
|||
show_all = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-ignore_unknown_cells") {
|
||||
if (args[argidx] == "-ignore-unknown-cells" || args[argidx] == "-ignore_unknown_cells") {
|
||||
ignore_unknown_cells = true;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1336,6 +1358,14 @@ struct SatPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
SatSolver *solver = yosys_satsolver;
|
||||
if (!solver_name.empty()) {
|
||||
solver = find_satsolver(solver_name);
|
||||
if (solver == nullptr)
|
||||
log_cmd_error("Unknown SAT solver '%s'. Available solvers: %s\n",
|
||||
solver_name, list_satsolvers());
|
||||
}
|
||||
|
||||
RTLIL::Module *module = NULL;
|
||||
for (auto mod : design->selected_modules()) {
|
||||
if (module)
|
||||
|
|
@ -1398,13 +1428,15 @@ struct SatPass : public Pass {
|
|||
shows.push_back(wire->name.str());
|
||||
}
|
||||
|
||||
log("Using SAT solver `%s`.\n", solver->name.c_str());
|
||||
|
||||
if (tempinduct)
|
||||
{
|
||||
if (loopcount > 0 || max_undef)
|
||||
log_cmd_error("The options -max, -all, and -max_undef are not supported for temporal induction proofs!\n");
|
||||
|
||||
SatHelper basecase(design, module, enable_undef, set_def_formal);
|
||||
SatHelper inductstep(design, module, enable_undef, set_def_formal);
|
||||
SatHelper basecase(design, module, solver, enable_undef, set_def_formal);
|
||||
SatHelper inductstep(design, module, solver, enable_undef, set_def_formal);
|
||||
|
||||
basecase.sets = sets;
|
||||
basecase.set_assumes = set_assumes;
|
||||
|
|
@ -1593,7 +1625,7 @@ struct SatPass : public Pass {
|
|||
if (maxsteps > 0)
|
||||
log_cmd_error("The options -maxsteps is only supported for temporal induction proofs!\n");
|
||||
|
||||
SatHelper sathelper(design, module, enable_undef, set_def_formal);
|
||||
SatHelper sathelper(design, module, solver, enable_undef, set_def_formal);
|
||||
|
||||
sathelper.sets = sets;
|
||||
sathelper.set_assumes = set_assumes;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/mem.h"
|
||||
#include "kernel/fstdata.h"
|
||||
#include "kernel/ff.h"
|
||||
|
|
@ -549,31 +550,27 @@ struct SimInstance
|
|||
if (shared->debug)
|
||||
log("[%s] eval %s (%s)\n", hiername(), log_id(cell), log_id(cell->type));
|
||||
|
||||
// Simple (A -> Y) and (A,B -> Y) cells
|
||||
if (has_a && !has_c && !has_d && !has_s && has_y) {
|
||||
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b)));
|
||||
return;
|
||||
}
|
||||
bool err = false;
|
||||
RTLIL::Const eval_state;
|
||||
if (has_a && !has_c && !has_d && !has_s && has_y)
|
||||
// Simple (A -> Y) and (A,B -> Y) cells
|
||||
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), &err);
|
||||
else if (has_a && has_b && has_c && !has_d && !has_s && has_y)
|
||||
// (A,B,C -> Y) cells
|
||||
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_c), &err);
|
||||
else if (has_a && !has_b && !has_c && !has_d && has_s && has_y)
|
||||
// (A,S -> Y) cells
|
||||
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_s), &err);
|
||||
else if (has_a && has_b && !has_c && !has_d && has_s && has_y)
|
||||
// (A,B,S -> Y) cells
|
||||
eval_state = CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s), &err);
|
||||
else
|
||||
err = true;
|
||||
|
||||
// (A,B,C -> Y) cells
|
||||
if (has_a && has_b && has_c && !has_d && !has_s && has_y) {
|
||||
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_c)));
|
||||
return;
|
||||
}
|
||||
|
||||
// (A,S -> Y) cells
|
||||
if (has_a && !has_b && !has_c && !has_d && has_s && has_y) {
|
||||
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_s)));
|
||||
return;
|
||||
}
|
||||
|
||||
// (A,B,S -> Y) cells
|
||||
if (has_a && has_b && !has_c && !has_d && has_s && has_y) {
|
||||
set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s)));
|
||||
return;
|
||||
}
|
||||
|
||||
log_warning("Unsupported evaluable cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
if (err)
|
||||
log_warning("Unsupported evaluable cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
else
|
||||
set_state(sig_y, eval_state);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ OBJS += passes/techmap/muxcover.o
|
|||
OBJS += passes/techmap/aigmap.o
|
||||
OBJS += passes/techmap/tribuf.o
|
||||
OBJS += passes/techmap/lut2mux.o
|
||||
OBJS += passes/techmap/lut2bmux.o
|
||||
OBJS += passes/techmap/nlutmap.o
|
||||
OBJS += passes/techmap/shregmap.o
|
||||
OBJS += passes/techmap/deminout.o
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/ffinit.h"
|
||||
#include "kernel/ff.h"
|
||||
#include "kernel/cost.h"
|
||||
|
|
@ -143,6 +143,14 @@ struct AbcConfig
|
|||
bool markgroups = false;
|
||||
pool<std::string> enabled_gates;
|
||||
bool cmos_cost = false;
|
||||
|
||||
bool is_yosys_abc() const {
|
||||
#ifdef ABCEXTERNAL
|
||||
return false;
|
||||
#else
|
||||
return exe_file == yosys_abc_executable;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
struct AbcSigVal {
|
||||
|
|
@ -155,7 +163,12 @@ struct AbcSigVal {
|
|||
}
|
||||
};
|
||||
|
||||
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
|
||||
// REUSE_YOSYS_ABC_PROCESSES only works when ABC is built with ENABLE_READLINE.
|
||||
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN) && defined(YOSYS_ENABLE_READLINE)
|
||||
#define REUSE_YOSYS_ABC_PROCESSES
|
||||
#endif
|
||||
|
||||
#ifdef REUSE_YOSYS_ABC_PROCESSES
|
||||
struct AbcProcess
|
||||
{
|
||||
pid_t pid;
|
||||
|
|
@ -188,10 +201,10 @@ struct AbcProcess
|
|||
int status;
|
||||
int ret = waitpid(pid, &status, 0);
|
||||
if (ret != pid) {
|
||||
log_error("waitpid(%d) failed", pid);
|
||||
log_error("waitpid(%d) failed\n", pid);
|
||||
}
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
log_error("ABC failed with status %X", status);
|
||||
log_error("ABC failed with status %X\n", status);
|
||||
}
|
||||
if (from_child_pipe >= 0)
|
||||
close(from_child_pipe);
|
||||
|
|
@ -203,12 +216,12 @@ std::optional<AbcProcess> spawn_abc(const char* abc_exe, DeferredLogs &logs) {
|
|||
// fork()s.
|
||||
int to_child_pipe[2];
|
||||
if (pipe2(to_child_pipe, O_CLOEXEC) != 0) {
|
||||
logs.log_error("pipe failed");
|
||||
logs.log_error("pipe failed\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
int from_child_pipe[2];
|
||||
if (pipe2(from_child_pipe, O_CLOEXEC) != 0) {
|
||||
logs.log_error("pipe failed");
|
||||
logs.log_error("pipe failed\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
@ -221,39 +234,39 @@ std::optional<AbcProcess> spawn_abc(const char* abc_exe, DeferredLogs &logs) {
|
|||
|
||||
posix_spawn_file_actions_t file_actions;
|
||||
if (posix_spawn_file_actions_init(&file_actions) != 0) {
|
||||
logs.log_error("posix_spawn_file_actions_init failed");
|
||||
logs.log_error("posix_spawn_file_actions_init failed\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[1]) != 0) {
|
||||
logs.log_error("posix_spawn_file_actions_addclose failed");
|
||||
logs.log_error("posix_spawn_file_actions_addclose failed\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[0]) != 0) {
|
||||
logs.log_error("posix_spawn_file_actions_addclose failed");
|
||||
logs.log_error("posix_spawn_file_actions_addclose failed\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (posix_spawn_file_actions_adddup2(&file_actions, to_child_pipe[0], STDIN_FILENO) != 0) {
|
||||
logs.log_error("posix_spawn_file_actions_adddup2 failed");
|
||||
logs.log_error("posix_spawn_file_actions_adddup2 failed\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (posix_spawn_file_actions_adddup2(&file_actions, from_child_pipe[1], STDOUT_FILENO) != 0) {
|
||||
logs.log_error("posix_spawn_file_actions_adddup2 failed");
|
||||
logs.log_error("posix_spawn_file_actions_adddup2 failed\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&file_actions, to_child_pipe[0]) != 0) {
|
||||
logs.log_error("posix_spawn_file_actions_addclose failed");
|
||||
logs.log_error("posix_spawn_file_actions_addclose failed\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (posix_spawn_file_actions_addclose(&file_actions, from_child_pipe[1]) != 0) {
|
||||
logs.log_error("posix_spawn_file_actions_addclose failed");
|
||||
logs.log_error("posix_spawn_file_actions_addclose failed\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
char arg1[] = "-s";
|
||||
char* argv[] = { strdup(abc_exe), arg1, nullptr };
|
||||
if (0 != posix_spawnp(&result.pid, abc_exe, &file_actions, nullptr, argv, environ)) {
|
||||
logs.log_error("posix_spawnp %s failed (errno=%s)", abc_exe, strerrorname_np(errno));
|
||||
logs.log_error("posix_spawnp %s failed (errno=%s)\n", abc_exe, strerror(errno));
|
||||
return std::nullopt;
|
||||
}
|
||||
free(argv[0]);
|
||||
|
|
@ -272,7 +285,7 @@ using AbcSigMap = SigValMap<AbcSigVal>;
|
|||
struct RunAbcState {
|
||||
const AbcConfig &config;
|
||||
|
||||
std::string tempdir_name;
|
||||
std::string per_run_tempdir_name;
|
||||
std::vector<gate_t> signal_list;
|
||||
bool did_run = false;
|
||||
bool err = false;
|
||||
|
|
@ -823,16 +836,23 @@ std::string fold_abc_cmd(std::string str)
|
|||
return new_str;
|
||||
}
|
||||
|
||||
std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir)
|
||||
std::string replace_tempdir(std::string text, std::string_view global_tempdir_name, std::string_view per_run_tempdir_name, bool show_tempdir)
|
||||
{
|
||||
if (show_tempdir)
|
||||
return text;
|
||||
|
||||
while (1) {
|
||||
size_t pos = text.find(tempdir_name);
|
||||
size_t pos = text.find(global_tempdir_name);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name));
|
||||
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(global_tempdir_name));
|
||||
}
|
||||
|
||||
while (1) {
|
||||
size_t pos = text.find(per_run_tempdir_name);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(per_run_tempdir_name));
|
||||
}
|
||||
|
||||
std::string selfdir_name = proc_self_dirname();
|
||||
|
|
@ -854,11 +874,12 @@ struct abc_output_filter
|
|||
bool got_cr;
|
||||
int escape_seq_state;
|
||||
std::string linebuf;
|
||||
std::string tempdir_name;
|
||||
std::string global_tempdir_name;
|
||||
std::string per_run_tempdir_name;
|
||||
bool show_tempdir;
|
||||
|
||||
abc_output_filter(RunAbcState& state, std::string tempdir_name, bool show_tempdir)
|
||||
: state(state), tempdir_name(tempdir_name), show_tempdir(show_tempdir)
|
||||
abc_output_filter(RunAbcState& state, std::string global_tempdir_name, std::string per_run_tempdir_name, bool show_tempdir)
|
||||
: state(state), global_tempdir_name(global_tempdir_name), per_run_tempdir_name(per_run_tempdir_name), show_tempdir(show_tempdir)
|
||||
{
|
||||
got_cr = false;
|
||||
escape_seq_state = 0;
|
||||
|
|
@ -885,7 +906,7 @@ struct abc_output_filter
|
|||
return;
|
||||
}
|
||||
if (ch == '\n') {
|
||||
state.logs.log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir));
|
||||
state.logs.log("ABC: %s\n", replace_tempdir(linebuf, global_tempdir_name, per_run_tempdir_name, show_tempdir));
|
||||
got_cr = false, linebuf.clear();
|
||||
return;
|
||||
}
|
||||
|
|
@ -986,15 +1007,15 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
|
|||
|
||||
const AbcConfig &config = run_abc.config;
|
||||
if (config.cleanup)
|
||||
run_abc.tempdir_name = get_base_tmpdir() + "/";
|
||||
run_abc.per_run_tempdir_name = get_base_tmpdir() + "/";
|
||||
else
|
||||
run_abc.tempdir_name = "_tmp_";
|
||||
run_abc.tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX";
|
||||
run_abc.tempdir_name = make_temp_dir(run_abc.tempdir_name);
|
||||
run_abc.per_run_tempdir_name = "_tmp_";
|
||||
run_abc.per_run_tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX";
|
||||
run_abc.per_run_tempdir_name = make_temp_dir(run_abc.per_run_tempdir_name);
|
||||
log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n",
|
||||
module->name.c_str(), replace_tempdir(run_abc.tempdir_name, run_abc.tempdir_name, config.show_tempdir).c_str());
|
||||
module->name.c_str(), replace_tempdir(run_abc.per_run_tempdir_name, config.global_tempdir_name, run_abc.per_run_tempdir_name, config.show_tempdir).c_str());
|
||||
|
||||
std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", run_abc.tempdir_name);
|
||||
std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", run_abc.per_run_tempdir_name);
|
||||
|
||||
if (!config.liberty_files.empty() || !config.genlib_files.empty()) {
|
||||
std::string dont_use_args;
|
||||
|
|
@ -1060,18 +1081,19 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
|
|||
for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
|
||||
abc_script = abc_script.substr(0, pos) + config.lutin_shared + abc_script.substr(pos+3);
|
||||
if (config.abc_dress)
|
||||
abc_script += stringf("; dress \"%s/input.blif\"", run_abc.tempdir_name);
|
||||
abc_script += stringf("; write_blif %s/output.blif", run_abc.tempdir_name);
|
||||
abc_script += stringf("; dress \"%s/input.blif\"", run_abc.per_run_tempdir_name);
|
||||
abc_script += stringf("; write_blif %s/output.blif", run_abc.per_run_tempdir_name);
|
||||
abc_script = add_echos_to_abc_cmd(abc_script);
|
||||
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
|
||||
abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n";
|
||||
#if defined(REUSE_YOSYS_ABC_PROCESSES)
|
||||
if (config.is_yosys_abc())
|
||||
abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n";
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i+1 < abc_script.size(); i++)
|
||||
if (abc_script[i] == ';' && abc_script[i+1] == ' ')
|
||||
abc_script[i+1] = '\n';
|
||||
|
||||
std::string buffer = stringf("%s/abc.script", run_abc.tempdir_name);
|
||||
std::string buffer = stringf("%s/abc.script", run_abc.per_run_tempdir_name);
|
||||
FILE *f = fopen(buffer.c_str(), "wt");
|
||||
if (f == nullptr)
|
||||
log_error("Opening %s for writing failed: %s\n", buffer, strerror(errno));
|
||||
|
|
@ -1127,42 +1149,86 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
|
|||
handle_loops(assign_map, module);
|
||||
}
|
||||
|
||||
#if defined(REUSE_YOSYS_ABC_PROCESSES)
|
||||
static bool is_abc_prompt(const std::string &line, std::string &rest) {
|
||||
size_t pos = 0;
|
||||
while (true) {
|
||||
// The prompt may not start at the start of the line, because
|
||||
// ABC can output progress and maybe other data that isn't
|
||||
// newline-terminated.
|
||||
size_t start = line.find("abc ", pos);
|
||||
if (start == std::string::npos)
|
||||
return false;
|
||||
pos = start + 4;
|
||||
|
||||
size_t digits = 0;
|
||||
while (pos + digits < line.size() && line[pos + digits] >= '0' && line[pos + digits] <= '9')
|
||||
++digits;
|
||||
if (digits < 2)
|
||||
return false;
|
||||
if (line.substr(pos + digits, 2) == "> ") {
|
||||
rest = line.substr(pos + digits + 2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) {
|
||||
std::string line;
|
||||
char buf[1024];
|
||||
bool seen_source_cmd = false;
|
||||
bool seen_yosys_abc_done = false;
|
||||
while (true) {
|
||||
int ret = read(fd, buf, sizeof(buf) - 1);
|
||||
if (ret < 0) {
|
||||
logs.log_error("Failed to read from ABC, errno=%d", errno);
|
||||
logs.log_error("Failed to read from ABC, errno=%d\n", errno);
|
||||
return false;
|
||||
}
|
||||
if (ret == 0) {
|
||||
logs.log_error("ABC exited prematurely");
|
||||
logs.log_error("ABC exited prematurely\n");
|
||||
return false;
|
||||
}
|
||||
char *start = buf;
|
||||
char *end = buf + ret;
|
||||
while (start < end) {
|
||||
char *p = static_cast<char*>(memchr(start, '\n', end - start));
|
||||
if (p == nullptr) {
|
||||
break;
|
||||
char *upto = p == nullptr ? end : p + 1;
|
||||
line.append(start, upto - start);
|
||||
start = upto;
|
||||
|
||||
std::string rest;
|
||||
bool is_prompt = is_abc_prompt(line, rest);
|
||||
if (is_prompt && seen_source_cmd) {
|
||||
// This is the first prompt after we sourced the script.
|
||||
// We are done here.
|
||||
// We won't have seen a newline yet since ABC is waiting at the prompt.
|
||||
if (!seen_yosys_abc_done)
|
||||
logs.log_error("ABC script did not complete successfully\n");
|
||||
return seen_yosys_abc_done;
|
||||
}
|
||||
line.append(start, p + 1 - start);
|
||||
if (line.substr(0, 14) == "YOSYS_ABC_DONE") {
|
||||
// Ignore any leftover output, there should only be a prompt perhaps
|
||||
return true;
|
||||
if (line.empty() || line[line.size() - 1] != '\n') {
|
||||
// No newline yet, wait for more text
|
||||
continue;
|
||||
}
|
||||
filt.next_line(line);
|
||||
if (is_prompt && rest.substr(0, 7) == "source ")
|
||||
seen_source_cmd = true;
|
||||
if (line.substr(0, 14) == "YOSYS_ABC_DONE")
|
||||
seen_yosys_abc_done = true;
|
||||
line.clear();
|
||||
start = p + 1;
|
||||
}
|
||||
line.append(start, end - start);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(REUSE_YOSYS_ABC_PROCESSES)
|
||||
void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
|
||||
#else
|
||||
void RunAbcState::run(ConcurrentStack<AbcProcess> &)
|
||||
#endif
|
||||
{
|
||||
std::string buffer = stringf("%s/input.blif", tempdir_name);
|
||||
std::string buffer = stringf("%s/input.blif", per_run_tempdir_name);
|
||||
FILE *f = fopen(buffer.c_str(), "wt");
|
||||
if (f == nullptr) {
|
||||
logs.log("Opening %s for writing failed: %s\n", buffer, strerror(errno));
|
||||
|
|
@ -1285,15 +1351,19 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
|
|||
|
||||
logs.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 (count_output > 0)
|
||||
{
|
||||
std::string tmp_script_name = stringf("%s/abc.script", tempdir_name);
|
||||
logs.log("Running ABC script: %s\n", replace_tempdir(tmp_script_name, tempdir_name, config.show_tempdir));
|
||||
if (count_output == 0) {
|
||||
log("Don't call ABC as there is nothing to map.\n");
|
||||
return;
|
||||
}
|
||||
int ret;
|
||||
std::string tmp_script_name = stringf("%s/abc.script", per_run_tempdir_name);
|
||||
do {
|
||||
logs.log("Running ABC script: %s\n", replace_tempdir(tmp_script_name, config.global_tempdir_name, per_run_tempdir_name, config.show_tempdir));
|
||||
|
||||
errno = 0;
|
||||
abc_output_filter filt(*this, tempdir_name, config.show_tempdir);
|
||||
abc_output_filter filt(*this, config.global_tempdir_name, per_run_tempdir_name, config.show_tempdir);
|
||||
#ifdef YOSYS_LINK_ABC
|
||||
string temp_stdouterr_name = stringf("%s/stdouterr.txt", tempdir_name);
|
||||
string temp_stdouterr_name = stringf("%s/stdouterr.txt", per_run_tempdir_name);
|
||||
FILE *temp_stdouterr_w = fopen(temp_stdouterr_name.c_str(), "w");
|
||||
if (temp_stdouterr_w == NULL)
|
||||
log_error("ABC: cannot open a temporary file for output redirection");
|
||||
|
|
@ -1318,7 +1388,7 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
|
|||
abc_argv[2] = strdup("-f");
|
||||
abc_argv[3] = strdup(tmp_script_name.c_str());
|
||||
abc_argv[4] = 0;
|
||||
int ret = abc::Abc_RealMain(4, abc_argv);
|
||||
ret = abc::Abc_RealMain(4, abc_argv);
|
||||
free(abc_argv[0]);
|
||||
free(abc_argv[1]);
|
||||
free(abc_argv[2]);
|
||||
|
|
@ -1333,39 +1403,42 @@ void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
|
|||
for (std::string line; std::getline(temp_stdouterr_r, line); )
|
||||
filt.next_line(line + "\n");
|
||||
temp_stdouterr_r.close();
|
||||
#elif defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
|
||||
AbcProcess process;
|
||||
if (std::optional<AbcProcess> process_opt = process_pool.try_pop_back()) {
|
||||
process = std::move(process_opt.value());
|
||||
} else if (std::optional<AbcProcess> process_opt = spawn_abc(config.exe_file.c_str(), logs)) {
|
||||
process = std::move(process_opt.value());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
std::string cmd = stringf(
|
||||
"empty\n"
|
||||
"source %s\n", tmp_script_name);
|
||||
int ret = write(process.to_child_pipe, cmd.c_str(), cmd.size());
|
||||
if (ret != static_cast<int>(cmd.size())) {
|
||||
logs.log_error("write failed");
|
||||
return;
|
||||
}
|
||||
ret = read_until_abc_done(filt, process.from_child_pipe, logs) ? 0 : 1;
|
||||
if (ret == 0) {
|
||||
process_pool.push_back(std::move(process));
|
||||
}
|
||||
break;
|
||||
#else
|
||||
std::string cmd = stringf("\"%s\" -s -f %s/abc.script 2>&1", config.exe_file.c_str(), tempdir_name.c_str());
|
||||
int ret = run_command(cmd, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1));
|
||||
#endif
|
||||
if (ret != 0) {
|
||||
logs.log_error("ABC: execution of script \"%s\" failed: return code %d (errno=%d).\n", tmp_script_name, ret, errno);
|
||||
return;
|
||||
#if defined(REUSE_YOSYS_ABC_PROCESSES)
|
||||
if (config.is_yosys_abc()) {
|
||||
AbcProcess process;
|
||||
if (std::optional<AbcProcess> process_opt = process_pool.try_pop_back()) {
|
||||
process = std::move(process_opt.value());
|
||||
} else if (std::optional<AbcProcess> process_opt = spawn_abc(config.exe_file.c_str(), logs)) {
|
||||
process = std::move(process_opt.value());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
std::string cmd = stringf(
|
||||
"empty\n"
|
||||
"source %s\n", tmp_script_name);
|
||||
ret = write(process.to_child_pipe, cmd.c_str(), cmd.size());
|
||||
if (ret != static_cast<int>(cmd.size())) {
|
||||
logs.log_error("write failed");
|
||||
return;
|
||||
}
|
||||
ret = read_until_abc_done(filt, process.from_child_pipe, logs) ? 0 : 1;
|
||||
if (ret == 0) {
|
||||
process_pool.push_back(std::move(process));
|
||||
}
|
||||
break;
|
||||
}
|
||||
did_run = true;
|
||||
#endif
|
||||
std::string cmd = stringf("\"%s\" -s -f %s 2>&1", config.exe_file, tmp_script_name);
|
||||
ret = run_command(cmd, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1));
|
||||
#endif
|
||||
} while (false);
|
||||
if (ret != 0) {
|
||||
logs.log_error("ABC: execution of script \"%s\" failed: return code %d (errno=%d).\n", tmp_script_name, ret, errno);
|
||||
return;
|
||||
}
|
||||
log("Don't call ABC as there is nothing to map.\n");
|
||||
did_run = true;
|
||||
}
|
||||
|
||||
void emit_global_input_files(const AbcConfig &config)
|
||||
|
|
@ -1437,7 +1510,7 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL
|
|||
return;
|
||||
}
|
||||
|
||||
std::string buffer = stringf("%s/%s", run_abc.tempdir_name, "output.blif");
|
||||
std::string buffer = stringf("%s/%s", run_abc.per_run_tempdir_name, "output.blif");
|
||||
std::ifstream ifs;
|
||||
ifs.open(buffer);
|
||||
if (ifs.fail())
|
||||
|
|
@ -1724,7 +1797,7 @@ void AbcModuleState::finish()
|
|||
if (run_abc.config.cleanup)
|
||||
{
|
||||
log("Removing temp directory.\n");
|
||||
remove_directory(run_abc.tempdir_name);
|
||||
remove_directory(run_abc.per_run_tempdir_name);
|
||||
}
|
||||
log_pop();
|
||||
}
|
||||
|
|
@ -2382,7 +2455,7 @@ struct AbcPass : public Pass {
|
|||
continue;
|
||||
}
|
||||
|
||||
CellTypes ct(design);
|
||||
NewCellTypes ct(design);
|
||||
|
||||
std::vector<RTLIL::Cell*> all_cells = mod->selected_cells();
|
||||
pool<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end());
|
||||
|
|
|
|||
|
|
@ -219,9 +219,7 @@ struct Abc9Pass : public ScriptPass
|
|||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if ((arg == "-exe" || arg == "-script" || arg == "-D" ||
|
||||
/*arg == "-S" ||*/ arg == "-lut" || arg == "-luts" ||
|
||||
/*arg == "-box" ||*/ arg == "-W" || arg == "-genlib" ||
|
||||
arg == "-constr" || arg == "-dont_use" || arg == "-liberty") &&
|
||||
arg == "-lut" || arg == "-luts" || arg == "-W") &&
|
||||
argidx+1 < args.size()) {
|
||||
if (arg == "-lut" || arg == "-luts")
|
||||
lut_mode = true;
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe
|
|||
}
|
||||
|
||||
abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name);
|
||||
if (design->scratchpad_get_bool("abc9.verify")) {
|
||||
if (design->scratchpad_get_bool("abc9.verify", true)) {
|
||||
if (dff_mode)
|
||||
abc9_script += "; &verify -s";
|
||||
else
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@
|
|||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/utils.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/newcelltypes.h"
|
||||
#include "kernel/timinginfo.h"
|
||||
#include <optional>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
|
@ -587,6 +588,7 @@ void break_scc(RTLIL::Module *module)
|
|||
auto id = it->second;
|
||||
auto r = ids_seen.insert(id);
|
||||
cell->attributes.erase(it);
|
||||
// Cut exactly one representative cell per SCC id.
|
||||
if (!r.second)
|
||||
continue;
|
||||
for (auto &c : cell->connections_) {
|
||||
|
|
@ -710,8 +712,6 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
|
|||
|
||||
SigMap sigmap(module);
|
||||
|
||||
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
|
||||
TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
|
||||
dict<IdString, std::vector<IdString>> box_ports;
|
||||
|
||||
for (auto cell : module->cells()) {
|
||||
|
|
@ -750,39 +750,100 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (!yosys_celltypes.cell_known(cell->type))
|
||||
continue;
|
||||
|
||||
// TODO: Speed up toposort -- we care about box ordering only
|
||||
for (auto conn : cell->connections()) {
|
||||
if (cell->input(conn.first))
|
||||
for (auto bit : sigmap(conn.second))
|
||||
bit_users[bit].insert(cell->name);
|
||||
|
||||
if (cell->output(conn.first) && !abc9_flop)
|
||||
for (auto bit : sigmap(conn.second))
|
||||
bit_drivers[bit].insert(cell->name);
|
||||
}
|
||||
toposort.node(cell->name);
|
||||
}
|
||||
|
||||
if (box_ports.empty())
|
||||
return;
|
||||
|
||||
for (auto &it : bit_users)
|
||||
if (bit_drivers.count(it.first))
|
||||
for (auto driver_cell : bit_drivers.at(it.first))
|
||||
for (auto user_cell : it.second)
|
||||
toposort.edge(driver_cell, user_cell);
|
||||
// Build the same topo graph for the initial pass and the optional retry.
|
||||
auto build_toposort = [&](TopoSort<IdString, RTLIL::sort_by_id_str> &toposort) {
|
||||
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
|
||||
|
||||
if (ys_debug(1))
|
||||
toposort.analyze_loops = true;
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
|
||||
continue;
|
||||
if (cell->has_keep_attr())
|
||||
continue;
|
||||
|
||||
bool no_loops = toposort.sort();
|
||||
auto inst_module = design->module(cell->type);
|
||||
bool abc9_flop = inst_module && inst_module->get_bool_attribute(ID::abc9_flop);
|
||||
if (abc9_flop && !dff)
|
||||
continue;
|
||||
if (!(inst_module && inst_module->get_bool_attribute(ID::abc9_box)) && !yosys_celltypes.cell_known(cell->type))
|
||||
continue;
|
||||
|
||||
// TODO: Speed up toposort -- we care about box ordering only
|
||||
for (auto conn : cell->connections()) {
|
||||
if (cell->input(conn.first))
|
||||
for (auto bit : sigmap(conn.second))
|
||||
bit_users[bit].insert(cell->name);
|
||||
|
||||
if (cell->output(conn.first) && !abc9_flop)
|
||||
for (auto bit : sigmap(conn.second))
|
||||
bit_drivers[bit].insert(cell->name);
|
||||
}
|
||||
toposort.node(cell->name);
|
||||
}
|
||||
|
||||
// Build producer -> consumer edges on sigmapped nets.
|
||||
for (auto &it : bit_users)
|
||||
if (bit_drivers.count(it.first))
|
||||
for (auto driver_cell : bit_drivers.at(it.first))
|
||||
for (auto user_cell : it.second)
|
||||
toposort.edge(driver_cell, user_cell);
|
||||
if (ys_debug(1))
|
||||
toposort.analyze_loops = true;
|
||||
return toposort.sort();
|
||||
};
|
||||
|
||||
// Build TopoSort in a container, as we may need to conditionally rebuild it on retry.
|
||||
std::optional<TopoSort<IdString, RTLIL::sort_by_id_str>> toposort;
|
||||
toposort.emplace();
|
||||
bool no_loops = build_toposort(toposort.value());
|
||||
|
||||
// Fallback for residual loops after SCC cutting: insert additional
|
||||
// breakers on non-box loop cells, then re-run toposort checks.
|
||||
if (!no_loops) {
|
||||
SigSpec I, O;
|
||||
pool<IdString> broken_cells;
|
||||
|
||||
for (auto &loop : toposort.value().loops)
|
||||
for (auto cell_name : loop) {
|
||||
// Loop reports can overlap; cut each cell at most once.
|
||||
if (!broken_cells.insert(cell_name).second)
|
||||
continue;
|
||||
auto cell = module->cell(cell_name);
|
||||
log_assert(cell);
|
||||
auto inst_module = design->module(cell->type);
|
||||
if (inst_module && inst_module->get_bool_attribute(ID::abc9_box))
|
||||
continue;
|
||||
for (auto &c : cell->connections_) {
|
||||
if (c.second.is_fully_const()) continue;
|
||||
if (cell->output(c.first)) {
|
||||
Wire *w = module->addWire(NEW_ID, GetSize(c.second));
|
||||
I.append(w);
|
||||
O.append(c.second);
|
||||
c.second = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!I.empty()) {
|
||||
auto cell = module->addCell(NEW_ID, ID($__ABC9_SCC_BREAKER));
|
||||
log_assert(GetSize(I) == GetSize(O));
|
||||
cell->setParam(ID::WIDTH, GetSize(I));
|
||||
cell->setPort(ID::I, std::move(I));
|
||||
cell->setPort(ID::O, std::move(O));
|
||||
|
||||
// Rebuild topo ordering after inserting the additional breakers.
|
||||
toposort.emplace();
|
||||
no_loops = build_toposort(toposort.value());
|
||||
}
|
||||
}
|
||||
|
||||
if (ys_debug(1)) {
|
||||
unsigned i = 0;
|
||||
for (auto &it : toposort.loops) {
|
||||
for (auto &it : toposort.value().loops) {
|
||||
log(" loop %d\n", i++);
|
||||
for (auto cell_name : it) {
|
||||
auto cell = module->cell(cell_name);
|
||||
|
|
@ -806,7 +867,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
|
|||
TimingInfo timing;
|
||||
|
||||
int port_id = 1, box_count = 0;
|
||||
for (auto cell_name : toposort.sorted) {
|
||||
for (auto cell_name : toposort.value().sorted) {
|
||||
RTLIL::Cell *cell = module->cell(cell_name);
|
||||
log_assert(cell);
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ struct DffunmapPass : public Pass {
|
|||
continue;
|
||||
|
||||
FfData ff(&initvals, cell);
|
||||
IdString name = cell->name;
|
||||
|
||||
if (!ff.has_clk)
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -40,10 +40,10 @@ int bindec(unsigned char v)
|
|||
r += (~((v & 2) - 1)) & 10;
|
||||
r += (~((v & 4) - 1)) & 100;
|
||||
r += (~((v & 8) - 1)) & 1000;
|
||||
r += (~((v & 16) - 1)) & 10000;
|
||||
r += (~((v & 32) - 1)) & 100000;
|
||||
r += (~((v & 64) - 1)) & 1000000;
|
||||
r += (~((v & 128) - 1)) & 10000000;
|
||||
r += (~((v & 16) - 1)) & 10'000;
|
||||
r += (~((v & 32) - 1)) & 100'000;
|
||||
r += (~((v & 64) - 1)) & 1'000'000;
|
||||
r += (~((v & 128) - 1)) & 10'000'000;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
58
passes/techmap/lut2bmux.cc
Normal file
58
passes/techmap/lut2bmux.cc
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
*
|
||||
* 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
|
||||
|
||||
struct Lut2BmuxPass : public Pass {
|
||||
Lut2BmuxPass() : Pass("lut2bmux", "convert $lut to $bmux") { }
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" lut2bmux [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass converts $lut cells to $bmux cells.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing LUT2BMUX pass (convert $lut to $bmux).\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
for (auto cell : module->selected_cells()) {
|
||||
if (cell->type == ID($lut)) {
|
||||
cell->type = ID($bmux);
|
||||
cell->setPort(ID::S, cell->getPort(ID::A));
|
||||
cell->setPort(ID::A, cell->getParam(ID::LUT));
|
||||
cell->unsetParam(ID::LUT);
|
||||
cell->fixup_parameters();
|
||||
log("Converted %s.%s to BMUX cell.\n", log_id(module), log_id(cell));
|
||||
}
|
||||
}
|
||||
}
|
||||
} Lut2BmuxPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
int lut2mux(Cell *cell)
|
||||
int lut2mux(Cell *cell, bool word_mode)
|
||||
{
|
||||
SigSpec sig_a = cell->getPort(ID::A);
|
||||
SigSpec sig_y = cell->getPort(ID::Y);
|
||||
|
|
@ -32,7 +32,10 @@ int lut2mux(Cell *cell)
|
|||
|
||||
if (GetSize(sig_a) == 1)
|
||||
{
|
||||
cell->module->addMuxGate(NEW_ID, lut.extract(0)[0], lut.extract(1)[0], sig_a, sig_y);
|
||||
if (!word_mode)
|
||||
cell->module->addMuxGate(NEW_ID, lut.extract(0)[0], lut.extract(1)[0], sig_a, sig_y);
|
||||
else
|
||||
cell->module->addMux(NEW_ID, lut.extract(0)[0], lut.extract(1)[0], sig_a, sig_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -44,10 +47,13 @@ int lut2mux(Cell *cell)
|
|||
Const lut1 = lut.extract(0, GetSize(lut)/2);
|
||||
Const lut2 = lut.extract(GetSize(lut)/2, GetSize(lut)/2);
|
||||
|
||||
count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y1, lut1));
|
||||
count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y2, lut2));
|
||||
count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y1, lut1), word_mode);
|
||||
count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y2, lut2), word_mode);
|
||||
|
||||
cell->module->addMuxGate(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y);
|
||||
if (!word_mode)
|
||||
cell->module->addMuxGate(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y);
|
||||
else
|
||||
cell->module->addMux(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y);
|
||||
}
|
||||
|
||||
cell->module->remove(cell);
|
||||
|
|
@ -55,26 +61,33 @@ int lut2mux(Cell *cell)
|
|||
}
|
||||
|
||||
struct Lut2muxPass : public Pass {
|
||||
Lut2muxPass() : Pass("lut2mux", "convert $lut to $_MUX_") { }
|
||||
Lut2muxPass() : Pass("lut2mux", "convert $lut to $mux/$_MUX_") { }
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" lut2mux [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass converts $lut cells to $_MUX_ gates.\n");
|
||||
log("This pass converts $lut cells to $mux/$_MUX_ gates.\n");
|
||||
log("\n");
|
||||
log(" -word\n");
|
||||
log(" Convert $lut cells with a single input to word-level $mux gates.\n");
|
||||
log(" The default is to convert them to bit-level $_MUX_ gates.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing LUT2MUX pass (convert $lut to $_MUX_).\n");
|
||||
log_header(design, "Executing LUT2MUX pass (convert $lut to $mux/$_MUX_).\n");
|
||||
log("ARGS:"); for (auto &a: args) log(" [%s]", a.c_str()); log("\n");
|
||||
|
||||
size_t argidx;
|
||||
bool word_mode = false;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-v") {
|
||||
// continue;
|
||||
// }
|
||||
if (args[argidx] == "-word") {
|
||||
word_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
|
@ -83,7 +96,7 @@ struct Lut2muxPass : public Pass {
|
|||
for (auto cell : module->selected_cells()) {
|
||||
if (cell->type == ID($lut)) {
|
||||
IdString cell_name = cell->name;
|
||||
int count = lut2mux(cell);
|
||||
int count = lut2mux(cell, word_mode);
|
||||
log("Converted %s.%s to %d MUX cells.\n", log_id(module), log_id(cell_name), count);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,9 +333,6 @@ struct TechmapWorker
|
|||
|
||||
RTLIL::Cell *c = module->addCell(c_name, tpl_cell);
|
||||
design->select(module, c);
|
||||
|
||||
if (c->type.begins_with("\\$"))
|
||||
c->type = c->type.substr(1);
|
||||
|
||||
if (c->type == ID::_TECHMAP_PLACEHOLDER_ && tpl_cell->has_attribute(ID::techmap_chtype)) {
|
||||
c->type = RTLIL::escape_id(tpl_cell->get_string_attribute(ID::techmap_chtype));
|
||||
|
|
@ -436,13 +433,9 @@ struct TechmapWorker
|
|||
if (handled_cells.count(cell) > 0)
|
||||
continue;
|
||||
|
||||
std::string cell_type = cell->type.str();
|
||||
if (in_recursion && cell->type.begins_with("\\$"))
|
||||
cell_type = cell_type.substr(1);
|
||||
|
||||
if (celltypeMap.count(cell_type) == 0) {
|
||||
if (assert_mode && cell_type.back() != '_')
|
||||
log_error("(ASSERT MODE) No matching template cell for type %s found.\n", log_id(cell_type));
|
||||
if (celltypeMap.count(cell->type) == 0) {
|
||||
if (assert_mode && !cell->type.ends_with("_"))
|
||||
log_error("(ASSERT MODE) No matching template cell for type %s found.\n", log_id(cell->type));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -454,7 +447,7 @@ struct TechmapWorker
|
|||
if (GetSize(sig) == 0)
|
||||
continue;
|
||||
|
||||
for (auto &tpl_name : celltypeMap.at(cell_type)) {
|
||||
for (auto &tpl_name : celltypeMap.at(cell->type)) {
|
||||
RTLIL::Module *tpl = map->module(tpl_name);
|
||||
RTLIL::Wire *port = tpl->wire(conn.first);
|
||||
if (port && port->port_input)
|
||||
|
|
@ -481,12 +474,7 @@ struct TechmapWorker
|
|||
log_assert(cell == module->cell(cell->name));
|
||||
bool mapped_cell = false;
|
||||
|
||||
std::string cell_type = cell->type.str();
|
||||
|
||||
if (in_recursion && cell->type.begins_with("\\$"))
|
||||
cell_type = cell_type.substr(1);
|
||||
|
||||
for (auto &tpl_name : celltypeMap.at(cell_type))
|
||||
for (auto &tpl_name : celltypeMap.at(cell->type))
|
||||
{
|
||||
IdString derived_name = tpl_name;
|
||||
RTLIL::Module *tpl = map->module(tpl_name);
|
||||
|
|
@ -508,8 +496,6 @@ struct TechmapWorker
|
|||
|
||||
if (!extmapper_name.empty())
|
||||
{
|
||||
cell->type = cell_type;
|
||||
|
||||
if ((extern_mode && !in_recursion) || extmapper_name == "wrap")
|
||||
{
|
||||
std::string m_name = stringf("$extern:%s:%s", extmapper_name, log_id(cell->type));
|
||||
|
|
@ -935,11 +921,6 @@ struct TechmapWorker
|
|||
RTLIL::Module *m = design->addModule(m_name);
|
||||
tpl->cloneInto(m);
|
||||
|
||||
for (auto cell : m->cells()) {
|
||||
if (cell->type.begins_with("\\$"))
|
||||
cell->type = cell->type.substr(1);
|
||||
}
|
||||
|
||||
module_queue.insert(m);
|
||||
}
|
||||
|
||||
|
|
@ -1168,7 +1149,7 @@ struct TechmapPass : public Pass {
|
|||
|
||||
std::vector<std::string> map_files;
|
||||
std::vector<RTLIL::IdString> dont_map;
|
||||
std::string verilog_frontend = "verilog -nooverwrite -noblackbox";
|
||||
std::string verilog_frontend = "verilog -nooverwrite -noblackbox -icells";
|
||||
int max_iter = -1;
|
||||
|
||||
size_t argidx;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue