3
0
Fork 0
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:
Arnim Läuger 2026-03-12 16:22:28 +01:00 committed by GitHub
commit f8b5da5058
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
226 changed files with 16144 additions and 2736 deletions

View file

@ -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

View file

@ -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
View 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

View file

@ -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
View 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

View file

@ -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");

View file

@ -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");

View file

@ -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);

View file

@ -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') {

View file

@ -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) {

View file

@ -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"

View file

@ -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
View 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

View file

@ -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;
}

View file

@ -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++;

View file

@ -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;

View file

@ -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

View file

@ -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.)");

View 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

View file

@ -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 &regs, 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

View file

@ -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));

View file

@ -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);

View file

@ -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 buckets 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;
}

View file

@ -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?

View file

@ -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");

View file

@ -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();

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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());

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -78,7 +78,6 @@ struct DffunmapPass : public Pass {
continue;
FfData ff(&initvals, cell);
IdString name = cell->name;
if (!ff.has_clk)
continue;

View file

@ -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;
}

View 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

View file

@ -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);
}
}

View file

@ -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;