3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-10-15 12:00:28 +00:00

Merge pull request #5266 from rocallahan/abc-parallel

Run ABC passes in parallel
This commit is contained in:
Jannis Harder 2025-09-15 18:49:27 +02:00 committed by GitHub
commit 09742e27f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 690 additions and 177 deletions

View file

@ -44,7 +44,12 @@ LINK_ABC := 0
# Needed for environments that can't run executables (i.e. emscripten, wasm) # Needed for environments that can't run executables (i.e. emscripten, wasm)
DISABLE_SPAWN := 0 DISABLE_SPAWN := 0
# Needed for environments that don't have proper thread support (i.e. emscripten, wasm--for now) # Needed for environments that don't have proper thread support (i.e. emscripten, wasm--for now)
ENABLE_THREADS := 1
ifeq ($(ENABLE_THREADS),1)
DISABLE_ABC_THREADS := 0 DISABLE_ABC_THREADS := 0
else
DISABLE_ABC_THREADS := 1
endif
# clang sanitizers # clang sanitizers
SANITIZER = SANITIZER =
@ -300,6 +305,7 @@ DISABLE_SPAWN := 1
ifeq ($(ENABLE_ABC),1) ifeq ($(ENABLE_ABC),1)
LINK_ABC := 1 LINK_ABC := 1
ENABLE_THREADS := 0
DISABLE_ABC_THREADS := 1 DISABLE_ABC_THREADS := 1
endif endif
@ -457,6 +463,11 @@ CXXFLAGS := -Og -DDEBUG $(filter-out $(OPT_LEVEL),$(CXXFLAGS))
STRIP := STRIP :=
endif endif
ifeq ($(ENABLE_THREADS),1)
CXXFLAGS += -DYOSYS_ENABLE_THREADS
LIBS += -lpthread
endif
ifeq ($(ENABLE_ABC),1) ifeq ($(ENABLE_ABC),1)
CXXFLAGS += -DYOSYS_ENABLE_ABC CXXFLAGS += -DYOSYS_ENABLE_ABC
ifeq ($(LINK_ABC),1) ifeq ($(LINK_ABC),1)
@ -612,6 +623,7 @@ $(eval $(call add_include_file,kernel/satgen.h))
$(eval $(call add_include_file,kernel/scopeinfo.h)) $(eval $(call add_include_file,kernel/scopeinfo.h))
$(eval $(call add_include_file,kernel/sexpr.h)) $(eval $(call add_include_file,kernel/sexpr.h))
$(eval $(call add_include_file,kernel/sigtools.h)) $(eval $(call add_include_file,kernel/sigtools.h))
$(eval $(call add_include_file,kernel/threading.h))
$(eval $(call add_include_file,kernel/timinginfo.h)) $(eval $(call add_include_file,kernel/timinginfo.h))
$(eval $(call add_include_file,kernel/utils.h)) $(eval $(call add_include_file,kernel/utils.h))
$(eval $(call add_include_file,kernel/yosys.h)) $(eval $(call add_include_file,kernel/yosys.h))
@ -638,7 +650,7 @@ OBJS += kernel/log_compat.o
endif endif
OBJS += kernel/binding.o kernel/tclapi.o OBJS += kernel/binding.o kernel/tclapi.o
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o kernel/sexpr.o OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o kernel/sexpr.o
OBJS += kernel/drivertools.o kernel/functional.o OBJS += kernel/drivertools.o kernel/functional.o kernel/threading.o
ifeq ($(ENABLE_ZLIB),1) ifeq ($(ENABLE_ZLIB),1)
OBJS += kernel/fstdata.o OBJS += kernel/fstdata.o
endif endif

45
kernel/threading.cc Normal file
View file

@ -0,0 +1,45 @@
#include "kernel/yosys_common.h"
#include "kernel/threading.h"
YOSYS_NAMESPACE_BEGIN
void DeferredLogs::flush()
{
for (auto &m : logs)
if (m.error)
YOSYS_NAMESPACE_PREFIX log_error("%s", m.text.c_str());
else
YOSYS_NAMESPACE_PREFIX log("%s", m.text.c_str());
}
int ThreadPool::pool_size(int reserved_cores, int max_threads)
{
#ifdef YOSYS_ENABLE_THREADS
int num_threads = std::min<int>(std::thread::hardware_concurrency() - reserved_cores, max_threads);
return std::max(0, num_threads);
#else
return 0;
#endif
}
ThreadPool::ThreadPool(int pool_size, std::function<void(int)> b)
: body(std::move(b))
{
#ifdef YOSYS_ENABLE_THREADS
threads.reserve(pool_size);
for (int i = 0; i < pool_size; i++)
threads.emplace_back([i, this]{ body(i); });
#else
log_assert(pool_size == 0);
#endif
}
ThreadPool::~ThreadPool()
{
#ifdef YOSYS_ENABLE_THREADS
for (auto &t : threads)
t.join();
#endif
}
YOSYS_NAMESPACE_END

186
kernel/threading.h Normal file
View file

@ -0,0 +1,186 @@
#include <deque>
#ifdef YOSYS_ENABLE_THREADS
#include <condition_variable>
#include <mutex>
#include <thread>
#endif
#include "kernel/yosys_common.h"
#include "kernel/log.h"
#ifndef YOSYS_THREADING_H
#define YOSYS_THREADING_H
YOSYS_NAMESPACE_BEGIN
// Concurrent queue implementation. Not fast, but simple.
// Multi-producer, multi-consumer, optionally bounded.
// When YOSYS_ENABLE_THREADS is not defined, this is just a non-thread-safe non-blocking deque.
template <typename T>
class ConcurrentQueue
{
public:
ConcurrentQueue(int capacity = INT_MAX)
: capacity(capacity) {}
// Push an element into the queue. If it's at capacity, block until there is room.
void push_back(T t)
{
#ifdef YOSYS_ENABLE_THREADS
std::unique_lock<std::mutex> lock(mutex);
not_full_condition.wait(lock, [this] { return static_cast<int>(contents.size()) < capacity; });
if (contents.empty())
not_empty_condition.notify_one();
#endif
log_assert(!closed);
contents.push_back(std::move(t));
#ifdef YOSYS_ENABLE_THREADS
if (static_cast<int>(contents.size()) < capacity)
not_full_condition.notify_one();
#endif
}
// Signal that no more elements will be produced. `pop_front()` will return nullopt.
void close()
{
#ifdef YOSYS_ENABLE_THREADS
std::unique_lock<std::mutex> lock(mutex);
not_empty_condition.notify_all();
#endif
closed = true;
}
// Pop an element from the queue. Blocks until an element is available
// or the queue is closed and empty.
std::optional<T> pop_front()
{
return pop_front_internal(true);
}
// Pop an element from the queue. Does not block, just returns nullopt if the
// queue is empty.
std::optional<T> try_pop_front()
{
return pop_front_internal(false);
}
private:
#ifdef YOSYS_ENABLE_THREADS
std::optional<T> pop_front_internal(bool wait)
{
std::unique_lock<std::mutex> lock(mutex);
if (wait) {
not_empty_condition.wait(lock, [this] { return !contents.empty() || closed; });
}
#else
std::optional<T> pop_front_internal(bool)
{
#endif
if (contents.empty())
return std::nullopt;
#ifdef YOSYS_ENABLE_THREADS
if (static_cast<int>(contents.size()) == capacity)
not_full_condition.notify_one();
#endif
T result = std::move(contents.front());
contents.pop_front();
#ifdef YOSYS_ENABLE_THREADS
if (!contents.empty())
not_empty_condition.notify_one();
#endif
return std::move(result);
}
#ifdef YOSYS_ENABLE_THREADS
std::mutex mutex;
// Signals one waiter thread when the queue changes and is not full.
std::condition_variable not_full_condition;
// Signals one waiter thread when the queue changes and is not empty.
std::condition_variable not_empty_condition;
#endif
std::deque<T> contents;
int capacity;
bool closed = false;
};
class DeferredLogs
{
public:
template <typename... Args>
void log(FmtString<TypeIdentity<Args>...> fmt, Args... args)
{
logs.push_back({fmt.format(args...), false});
}
template <typename... Args>
void log_error(FmtString<TypeIdentity<Args>...> fmt, Args... args)
{
logs.push_back({fmt.format(args...), true});
}
void flush();
private:
struct Message
{
std::string text;
bool error;
};
std::vector<Message> logs;
};
class ThreadPool
{
public:
// Computes the number of worker threads to use.
// `reserved_cores` cores are set aside for other threads (e.g. work on the main thread).
// `max_threads` --- don't return more workers than this.
// The result may be 0.
static int pool_size(int reserved_cores, int max_threads);
// Create a pool of threads running the given closure (parameterized by thread number).
// `pool_size` must be the result of a `pool_size()` call.
ThreadPool(int pool_size, std::function<void(int)> b);
ThreadPool(ThreadPool &&other) = delete;
// Waits for all threads to terminate. Make sure those closures return!
~ThreadPool();
// Return the number of threads in the pool.
int num_threads() const
{
#ifdef YOSYS_ENABLE_THREADS
return threads.size();
#else
return 0;
#endif
}
private:
std::function<void(int)> body;
#ifdef YOSYS_ENABLE_THREADS
std::vector<std::thread> threads;
#endif
};
template <class T>
class ConcurrentStack
{
public:
void push_back(T &&t) {
#ifdef YOSYS_ENABLE_THREADS
std::lock_guard<std::mutex> lock(mutex);
#endif
contents.push_back(std::move(t));
}
std::optional<T> try_pop_back() {
#ifdef YOSYS_ENABLE_THREADS
std::lock_guard<std::mutex> lock(mutex);
#endif
if (contents.empty())
return std::nullopt;
T result = std::move(contents.back());
contents.pop_back();
return result;
}
private:
#ifdef YOSYS_ENABLE_THREADS
std::mutex mutex;
#endif
std::vector<T> contents;
};
YOSYS_NAMESPACE_END
#endif // YOSYS_THREADING_H

View file

@ -177,7 +177,7 @@ int run_command(const std::string &command, std::function<void(const std::string
int ret = pclose(f); int ret = pclose(f);
if (ret < 0) if (ret < 0)
return -1; return -2;
#ifdef _WIN32 #ifdef _WIN32
return ret; return ret;
#else #else

View file

@ -38,6 +38,7 @@ popd
} > "$vcxsrc"/YosysVS/YosysVS.vcxproj.new } > "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
sed -i 's,</AdditionalIncludeDirectories>,</AdditionalIncludeDirectories>\n <LanguageStandard>stdcpp17</LanguageStandard>\n <AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new sed -i 's,</AdditionalIncludeDirectories>,</AdditionalIncludeDirectories>\n <LanguageStandard>stdcpp17</LanguageStandard>\n <AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
sed -i 's,<PreprocessorDefinitions>,<PreprocessorDefinitions>YOSYS_ENABLE_THREADS;,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
if [ -f "/usr/include/FlexLexer.h" ] ; then if [ -f "/usr/include/FlexLexer.h" ] ; then
sed -i 's,</AdditionalIncludeDirectories>,;..\\yosys\\libs\\flex</AdditionalIncludeDirectories>,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new sed -i 's,</AdditionalIncludeDirectories>,;..\\yosys\\libs\\flex</AdditionalIncludeDirectories>,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
fi fi

View file

@ -48,6 +48,7 @@
#include "kernel/ff.h" #include "kernel/ff.h"
#include "kernel/cost.h" #include "kernel/cost.h"
#include "kernel/log.h" #include "kernel/log.h"
#include "kernel/threading.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -55,8 +56,14 @@
#include <cerrno> #include <cerrno>
#include <sstream> #include <sstream>
#include <climits> #include <climits>
#include <memory>
#include <vector> #include <vector>
#ifdef __linux__
# include <fcntl.h>
# include <spawn.h>
# include <sys/wait.h>
#endif
#ifndef _WIN32 #ifndef _WIN32
# include <unistd.h> # include <unistd.h>
# include <dirent.h> # include <dirent.h>
@ -104,8 +111,10 @@ struct gate_t
gate_type_t type; gate_type_t type;
int in1, in2, in3, in4; int in1, in2, in3, in4;
bool is_port; bool is_port;
RTLIL::SigBit bit; bool bit_is_wire;
bool bit_is_1;
RTLIL::State init; RTLIL::State init;
std::string bit_str;
}; };
bool map_mux4; bool map_mux4;
@ -119,6 +128,7 @@ bool cmos_cost;
struct AbcConfig struct AbcConfig
{ {
std::string global_tempdir_name;
std::string script_file; std::string script_file;
std::string exe_file; std::string exe_file;
std::vector<std::string> liberty_files; std::vector<std::string> liberty_files;
@ -148,31 +158,158 @@ struct AbcSigVal {
} }
}; };
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
struct AbcProcess
{
pid_t pid;
int to_child_pipe;
int from_child_pipe;
AbcProcess() : pid(0), to_child_pipe(-1), from_child_pipe(-1) {}
AbcProcess(AbcProcess &&other) {
pid = other.pid;
to_child_pipe = other.to_child_pipe;
from_child_pipe = other.from_child_pipe;
other.pid = 0;
other.to_child_pipe = other.from_child_pipe = -1;
}
AbcProcess &operator=(AbcProcess &&other) {
if (this != &other) {
pid = other.pid;
to_child_pipe = other.to_child_pipe;
from_child_pipe = other.from_child_pipe;
other.pid = 0;
other.to_child_pipe = other.from_child_pipe = -1;
}
return *this;
}
~AbcProcess() {
if (pid == 0)
return;
if (to_child_pipe >= 0)
close(to_child_pipe);
int status;
int ret = waitpid(pid, &status, 0);
if (ret != pid) {
log_error("waitpid(%d) failed", pid);
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
log_error("ABC failed with status %X", status);
}
if (from_child_pipe >= 0)
close(from_child_pipe);
}
};
std::optional<AbcProcess> spawn_abc(const char* abc_exe, DeferredLogs &logs) {
// Open pipes O_CLOEXEC so we don't leak any of the fds into racing
// fork()s.
int to_child_pipe[2];
if (pipe2(to_child_pipe, O_CLOEXEC) != 0) {
logs.log_error("pipe failed");
return std::nullopt;
}
int from_child_pipe[2];
if (pipe2(from_child_pipe, O_CLOEXEC) != 0) {
logs.log_error("pipe failed");
return std::nullopt;
}
AbcProcess result;
result.to_child_pipe = to_child_pipe[1];
result.from_child_pipe = from_child_pipe[0];
// Allow the child side of the pipes to be inherited.
fcntl(to_child_pipe[0], F_SETFD, 0);
fcntl(from_child_pipe[1], F_SETFD, 0);
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");
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");
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");
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");
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");
return std::nullopt;
}
if (posix_spawn_file_actions_adddup2(&file_actions, from_child_pipe[1], STDERR_FILENO) != 0) {
logs.log_error("posix_spawn_file_actions_adddup2 failed");
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");
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");
return std::nullopt;
}
char arg1[] = "-s";
char* argv[] = { strdup(abc_exe), arg1, nullptr };
if (0 != posix_spawn(&result.pid, abc_exe, &file_actions, nullptr, argv, environ)) {
logs.log_error("posix_spawn %s failed", abc_exe);
return std::nullopt;
}
free(argv[0]);
posix_spawn_file_actions_destroy(&file_actions);
close(to_child_pipe[0]);
close(from_child_pipe[1]);
return result;
}
#else
struct AbcProcess {};
#endif
using AbcSigMap = SigValMap<AbcSigVal>; using AbcSigMap = SigValMap<AbcSigVal>;
struct AbcModuleState { // Used by off-main-threads. Contains no direct or indirect access to RTLIL.
struct RunAbcState {
const AbcConfig &config; const AbcConfig &config;
int map_autoidx = 0; std::string tempdir_name;
std::vector<gate_t> signal_list; std::vector<gate_t> signal_list;
bool did_run = false;
bool err = false;
DeferredLogs logs;
dict<int, std::string> pi_map, po_map;
RunAbcState(const AbcConfig &config) : config(config) {}
void run(ConcurrentStack<AbcProcess> &process_pool);
};
struct AbcModuleState {
RunAbcState run_abc;
int map_autoidx = 0;
std::vector<RTLIL::SigBit> signal_bits;
dict<RTLIL::SigBit, int> signal_map; dict<RTLIL::SigBit, int> signal_map;
FfInitVals &initvals; FfInitVals &initvals;
bool had_init = false; bool had_init = false;
bool did_run_abc = false;
bool clk_polarity = false; bool clk_polarity = false;
bool en_polarity = false; bool en_polarity = false;
bool arst_polarity = false; bool arst_polarity = false;
bool srst_polarity = false; bool srst_polarity = false;
RTLIL::SigSpec clk_sig, en_sig, arst_sig, srst_sig; RTLIL::SigSpec clk_sig, en_sig, arst_sig, srst_sig;
dict<int, std::string> pi_map, po_map;
int undef_bits_lost = 0; int undef_bits_lost = 0;
std::string tempdir_name;
AbcModuleState(const AbcConfig &config, FfInitVals &initvals) AbcModuleState(const AbcConfig &config, FfInitVals &initvals)
: config(config), initvals(initvals) {} : run_abc(config), initvals(initvals) {}
AbcModuleState(AbcModuleState&&) = delete;
int map_signal(const AbcSigMap &assign_map, RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1); int map_signal(const AbcSigMap &assign_map, RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1);
void mark_port(const AbcSigMap &assign_map, RTLIL::SigSpec sig); void mark_port(const AbcSigMap &assign_map, RTLIL::SigSpec sig);
@ -180,7 +317,7 @@ struct AbcModuleState {
std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr); std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr);
void dump_loop_graph(FILE *f, int &nr, dict<int, pool<int>> &edges, pool<int> &workpool, std::vector<int> &in_counts); void dump_loop_graph(FILE *f, int &nr, dict<int, pool<int>> &edges, pool<int> &workpool, std::vector<int> &in_counts);
void handle_loops(AbcSigMap &assign_map, RTLIL::Module *module); void handle_loops(AbcSigMap &assign_map, RTLIL::Module *module);
void abc_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector<RTLIL::Cell*> &cells, void prepare_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector<RTLIL::Cell*> &cells,
bool dff_mode, std::string clk_str); bool dff_mode, std::string clk_str);
void extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module); void extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module);
void finish(); void finish();
@ -195,20 +332,23 @@ int AbcModuleState::map_signal(const AbcSigMap &assign_map, RTLIL::SigBit bit, g
if (signal_map.count(bit) == 0) { if (signal_map.count(bit) == 0) {
gate_t gate; gate_t gate;
gate.id = signal_list.size(); gate.id = run_abc.signal_list.size();
gate.type = G(NONE); gate.type = G(NONE);
gate.in1 = -1; gate.in1 = -1;
gate.in2 = -1; gate.in2 = -1;
gate.in3 = -1; gate.in3 = -1;
gate.in4 = -1; gate.in4 = -1;
gate.is_port = bit.wire != nullptr && val.is_port; gate.is_port = bit.wire != nullptr && val.is_port;
gate.bit = bit; gate.bit_is_wire = bit.wire != nullptr;
gate.bit_is_1 = bit == State::S1;
gate.init = initvals(bit); gate.init = initvals(bit);
signal_list.push_back(gate); gate.bit_str = std::string(log_signal(bit));
signal_map[bit] = gate.id; signal_map[bit] = gate.id;
run_abc.signal_list.push_back(std::move(gate));
signal_bits.push_back(bit);
} }
gate_t &gate = signal_list[signal_map[bit]]; gate_t &gate = run_abc.signal_list[signal_map[bit]];
if (gate_type != G(NONE)) if (gate_type != G(NONE))
gate.type = gate_type; gate.type = gate_type;
@ -228,7 +368,7 @@ void AbcModuleState::mark_port(const AbcSigMap &assign_map, RTLIL::SigSpec sig)
{ {
for (auto &bit : assign_map(sig)) for (auto &bit : assign_map(sig))
if (bit.wire != nullptr && signal_map.count(bit) > 0) if (bit.wire != nullptr && signal_map.count(bit) > 0)
signal_list[signal_map[bit]].is_port = true; run_abc.signal_list[signal_map[bit]].is_port = true;
} }
bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, bool keepff) bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, bool keepff)
@ -307,7 +447,7 @@ bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *mo
if (keepff) { if (keepff) {
SigBit bit = ff.sig_q; SigBit bit = ff.sig_q;
if (assign_map(bit).wire != nullptr) { if (assign_map(bit).wire != nullptr) {
signal_list[gate_id].is_port = true; run_abc.signal_list[gate_id].is_port = true;
} }
if (bit.wire != nullptr) if (bit.wire != nullptr)
bit.wire->attributes[ID::keep] = 1; bit.wire->attributes[ID::keep] = 1;
@ -459,19 +599,19 @@ std::string AbcModuleState::remap_name(RTLIL::IdString abc_name, RTLIL::Wire **o
size_t postfix_start = abc_sname.find_first_not_of("0123456789"); size_t postfix_start = abc_sname.find_first_not_of("0123456789");
std::string postfix = postfix_start != std::string::npos ? abc_sname.substr(postfix_start) : ""; std::string postfix = postfix_start != std::string::npos ? abc_sname.substr(postfix_start) : "";
if (sid < GetSize(signal_list)) if (sid < GetSize(run_abc.signal_list))
{ {
auto sig = signal_list.at(sid); const auto &bit = signal_bits.at(sid);
if (sig.bit.wire != nullptr) if (bit.wire != nullptr)
{ {
std::string s = stringf("$abc$%d$%s", map_autoidx, sig.bit.wire->name.c_str()+1); std::string s = stringf("$abc$%d$%s", map_autoidx, bit.wire->name.c_str()+1);
if (sig.bit.wire->width != 1) if (bit.wire->width != 1)
s += stringf("[%d]", sig.bit.offset); s += stringf("[%d]", bit.offset);
if (isnew) if (isnew)
s += "_new"; s += "_new";
s += postfix; s += postfix;
if (orig_wire != nullptr) if (orig_wire != nullptr)
*orig_wire = sig.bit.wire; *orig_wire = bit.wire;
return s; return s;
} }
} }
@ -499,7 +639,7 @@ void AbcModuleState::dump_loop_graph(FILE *f, int &nr, dict<int, pool<int>> &edg
} }
for (auto n : nodes) for (auto n : nodes)
fprintf(f, " ys__n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit), fprintf(f, " ys__n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, run_abc.signal_list[n].bit_str.c_str(),
n, in_counts[n], workpool.count(n) ? ", shape=box" : ""); n, in_counts[n], workpool.count(n) ? ", shape=box" : "");
for (auto &e : edges) for (auto &e : edges)
@ -521,7 +661,7 @@ void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module)
// (Kahn, Arthur B. (1962), "Topological sorting of large networks") // (Kahn, Arthur B. (1962), "Topological sorting of large networks")
dict<int, pool<int>> edges; dict<int, pool<int>> edges;
std::vector<int> in_edges_count(signal_list.size()); std::vector<int> in_edges_count(run_abc.signal_list.size());
pool<int> workpool; pool<int> workpool;
FILE *dot_f = nullptr; FILE *dot_f = nullptr;
@ -530,7 +670,7 @@ void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module)
// uncomment for troubleshooting the loop detection code // uncomment for troubleshooting the loop detection code
// dot_f = fopen("test.dot", "w"); // dot_f = fopen("test.dot", "w");
for (auto &g : signal_list) { for (auto &g : run_abc.signal_list) {
if (g.type == G(NONE) || g.type == G(FF) || g.type == G(FF0) || g.type == G(FF1)) { if (g.type == G(NONE) || g.type == G(FF) || g.type == G(FF0) || g.type == G(FF1)) {
workpool.insert(g.id); workpool.insert(g.id);
} else { } else {
@ -560,7 +700,7 @@ void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module)
int id = *workpool.begin(); int id = *workpool.begin();
workpool.erase(id); workpool.erase(id);
// log("Removing non-loop node %d from graph: %s\n", id, log_signal(signal_list[id].bit)); // log("Removing non-loop node %d from graph: %s\n", id, signal_list[id].bit_str);
for (int id2 : edges[id]) { for (int id2 : edges[id]) {
log_assert(in_edges_count[id2] > 0); log_assert(in_edges_count[id2] > 0);
@ -580,8 +720,8 @@ void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module)
for (auto &edge_it : edges) { for (auto &edge_it : edges) {
int id2 = edge_it.first; int id2 = edge_it.first;
RTLIL::Wire *w1 = signal_list[id1].bit.wire; RTLIL::Wire *w1 = signal_bits[id1].wire;
RTLIL::Wire *w2 = signal_list[id2].bit.wire; RTLIL::Wire *w2 = signal_bits[id2].wire;
if (w1 == nullptr) if (w1 == nullptr)
id1 = id2; id1 = id2;
else if (w2 == nullptr) else if (w2 == nullptr)
@ -603,7 +743,7 @@ void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module)
continue; continue;
} }
log_assert(signal_list[id1].bit.wire != nullptr); log_assert(signal_bits[id1].wire != nullptr);
std::stringstream sstr; std::stringstream sstr;
sstr << "$abcloop$" << (autoidx++); sstr << "$abcloop$" << (autoidx++);
@ -613,33 +753,33 @@ void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module)
for (int id2 : edges[id1]) { for (int id2 : edges[id1]) {
if (first_line) if (first_line)
log("Breaking loop using new signal %s: %s -> %s\n", log_signal(RTLIL::SigSpec(wire)), log("Breaking loop using new signal %s: %s -> %s\n", log_signal(RTLIL::SigSpec(wire)),
log_signal(signal_list[id1].bit), log_signal(signal_list[id2].bit)); run_abc.signal_list[id1].bit_str, run_abc.signal_list[id2].bit_str);
else else
log(" %*s %s -> %s\n", int(strlen(log_signal(RTLIL::SigSpec(wire)))), "", log(" %*s %s -> %s\n", int(strlen(log_signal(RTLIL::SigSpec(wire)))), "",
log_signal(signal_list[id1].bit), log_signal(signal_list[id2].bit)); run_abc.signal_list[id1].bit_str, run_abc.signal_list[id2].bit_str);
first_line = false; first_line = false;
} }
int id3 = map_signal(assign_map, RTLIL::SigSpec(wire)); int id3 = map_signal(assign_map, RTLIL::SigSpec(wire));
signal_list[id1].is_port = true; run_abc.signal_list[id1].is_port = true;
signal_list[id3].is_port = true; run_abc.signal_list[id3].is_port = true;
log_assert(id3 == int(in_edges_count.size())); log_assert(id3 == int(in_edges_count.size()));
in_edges_count.push_back(0); in_edges_count.push_back(0);
workpool.insert(id3); workpool.insert(id3);
for (int id2 : edges[id1]) { for (int id2 : edges[id1]) {
if (signal_list[id2].in1 == id1) if (run_abc.signal_list[id2].in1 == id1)
signal_list[id2].in1 = id3; run_abc.signal_list[id2].in1 = id3;
if (signal_list[id2].in2 == id1) if (run_abc.signal_list[id2].in2 == id1)
signal_list[id2].in2 = id3; run_abc.signal_list[id2].in2 = id3;
if (signal_list[id2].in3 == id1) if (run_abc.signal_list[id2].in3 == id1)
signal_list[id2].in3 = id3; run_abc.signal_list[id2].in3 = id3;
if (signal_list[id2].in4 == id1) if (run_abc.signal_list[id2].in4 == id1)
signal_list[id2].in4 = id3; run_abc.signal_list[id2].in4 = id3;
} }
edges[id1].swap(edges[id3]); edges[id1].swap(edges[id3]);
connect(assign_map, module, RTLIL::SigSig(signal_list[id3].bit, signal_list[id1].bit)); connect(assign_map, module, RTLIL::SigSig(signal_bits[id3], signal_bits[id1]));
dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count);
} }
} }
@ -716,14 +856,14 @@ std::string replace_tempdir(std::string text, std::string tempdir_name, bool sho
struct abc_output_filter struct abc_output_filter
{ {
const AbcModuleState &state; RunAbcState &state;
bool got_cr; bool got_cr;
int escape_seq_state; int escape_seq_state;
std::string linebuf; std::string linebuf;
std::string tempdir_name; std::string tempdir_name;
bool show_tempdir; bool show_tempdir;
abc_output_filter(const AbcModuleState& state, std::string 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) : state(state), tempdir_name(tempdir_name), show_tempdir(show_tempdir)
{ {
got_cr = false; got_cr = false;
@ -751,7 +891,7 @@ struct abc_output_filter
return; return;
} }
if (ch == '\n') { if (ch == '\n') {
log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir)); state.logs.log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir));
got_cr = false, linebuf.clear(); got_cr = false, linebuf.clear();
return; return;
} }
@ -764,7 +904,7 @@ struct abc_output_filter
{ {
int pi, po; int pi, po;
if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) { if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) {
log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n", state.logs.log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n",
pi, state.pi_map.count(pi) ? state.pi_map.at(pi).c_str() : "???", pi, state.pi_map.count(pi) ? state.pi_map.at(pi).c_str() : "???",
po, state.po_map.count(po) ? state.po_map.at(po).c_str() : "???"); po, state.po_map.count(po) ? state.po_map.at(po).c_str() : "???");
return; return;
@ -775,7 +915,7 @@ struct abc_output_filter
} }
}; };
void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector<RTLIL::Cell*> &cells, void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector<RTLIL::Cell*> &cells,
bool dff_mode, std::string clk_str) bool dff_mode, std::string clk_str)
{ {
map_autoidx = autoidx++; map_autoidx = autoidx++;
@ -850,16 +990,17 @@ void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, Ab
if (dff_mode && clk_sig.empty()) if (dff_mode && clk_sig.empty())
log_cmd_error("Clock domain %s not found.\n", clk_str.c_str()); log_cmd_error("Clock domain %s not found.\n", clk_str.c_str());
const AbcConfig &config = run_abc.config;
if (config.cleanup) if (config.cleanup)
tempdir_name = get_base_tmpdir() + "/"; run_abc.tempdir_name = get_base_tmpdir() + "/";
else else
tempdir_name = "_tmp_"; run_abc.tempdir_name = "_tmp_";
tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX"; run_abc.tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX";
tempdir_name = make_temp_dir(tempdir_name); run_abc.tempdir_name = make_temp_dir(run_abc.tempdir_name);
log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n", log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n",
module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, config.show_tempdir).c_str()); module->name.c_str(), replace_tempdir(run_abc.tempdir_name, run_abc.tempdir_name, config.show_tempdir).c_str());
std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", tempdir_name); std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", run_abc.tempdir_name);
if (!config.liberty_files.empty() || !config.genlib_files.empty()) { if (!config.liberty_files.empty() || !config.genlib_files.empty()) {
std::string dont_use_args; std::string dont_use_args;
@ -877,9 +1018,9 @@ void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, Ab
abc_script += stringf("read_constr -v \"%s\"; ", config.constr_file); abc_script += stringf("read_constr -v \"%s\"; ", config.constr_file);
} else } else
if (!config.lut_costs.empty()) if (!config.lut_costs.empty())
abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name); abc_script += stringf("read_lut %s/lutdefs.txt; ", config.global_tempdir_name);
else else
abc_script += stringf("read_library %s/stdcells.genlib; ", tempdir_name); abc_script += stringf("read_library %s/stdcells.genlib; ", config.global_tempdir_name);
if (!config.script_file.empty()) { if (!config.script_file.empty()) {
const std::string &script_file = config.script_file; const std::string &script_file = config.script_file;
@ -925,15 +1066,15 @@ void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, Ab
for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos)) 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); abc_script = abc_script.substr(0, pos) + config.lutin_shared + abc_script.substr(pos+3);
if (config.abc_dress) if (config.abc_dress)
abc_script += stringf("; dress \"%s/input.blif\"", tempdir_name); abc_script += stringf("; dress \"%s/input.blif\"", run_abc.tempdir_name);
abc_script += stringf("; write_blif %s/output.blif", tempdir_name); abc_script += stringf("; write_blif %s/output.blif", run_abc.tempdir_name);
abc_script = add_echos_to_abc_cmd(abc_script); abc_script = add_echos_to_abc_cmd(abc_script);
for (size_t i = 0; i+1 < abc_script.size(); i++) for (size_t i = 0; i+1 < abc_script.size(); i++)
if (abc_script[i] == ';' && abc_script[i+1] == ' ') if (abc_script[i] == ';' && abc_script[i+1] == ' ')
abc_script[i+1] = '\n'; abc_script[i+1] = '\n';
std::string buffer = stringf("%s/abc.script", tempdir_name); std::string buffer = stringf("%s/abc.script", run_abc.tempdir_name);
FILE *f = fopen(buffer.c_str(), "wt"); FILE *f = fopen(buffer.c_str(), "wt");
if (f == nullptr) if (f == nullptr)
log_error("Opening %s for writing failed: %s\n", buffer, strerror(errno)); log_error("Opening %s for writing failed: %s\n", buffer, strerror(errno));
@ -987,11 +1128,52 @@ void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, Ab
mark_port(assign_map, srst_sig); mark_port(assign_map, srst_sig);
handle_loops(assign_map, module); handle_loops(assign_map, module);
}
buffer = stringf("%s/input.blif", tempdir_name); bool read_until_abc_done(abc_output_filter &filt, int fd, DeferredLogs &logs) {
f = fopen(buffer.c_str(), "wt"); std::string line;
if (f == nullptr) char buf[1024];
log_error("Opening %s for writing failed: %s\n", buffer, strerror(errno)); while (true) {
int ret = read(fd, buf, sizeof(buf) - 1);
if (ret < 0) {
logs.log_error("Failed to read from ABC, errno=%d", errno);
return false;
}
if (ret == 0) {
logs.log_error("ABC exited prematurely");
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;
}
line.append(start, p + 1 - start);
// ABC seems to actually print "ABC_DONE \n", but we probably shouldn't
// rely on that extra space being output.
if (line.substr(0, 8) == "ABC_DONE") {
// Ignore any leftover output, there should only be a prompt perhaps
return true;
}
filt.next_line(line);
line.clear();
start = p + 1;
}
line.append(start, end - start);
}
}
void RunAbcState::run(ConcurrentStack<AbcProcess> &process_pool)
{
std::string buffer = stringf("%s/input.blif", tempdir_name);
FILE *f = fopen(buffer.c_str(), "wt");
if (f == nullptr) {
logs.log("Opening %s for writing failed: %s\n", buffer, strerror(errno));
err = true;
return;
}
fprintf(f, ".model netlist\n"); fprintf(f, ".model netlist\n");
@ -1001,7 +1183,7 @@ void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, Ab
if (!si.is_port || si.type != G(NONE)) if (!si.is_port || si.type != G(NONE))
continue; continue;
fprintf(f, " ys__n%d", si.id); fprintf(f, " ys__n%d", si.id);
pi_map[count_input++] = log_signal(si.bit); pi_map[count_input++] = si.bit_str;
} }
if (count_input == 0) if (count_input == 0)
fprintf(f, " dummy_input\n"); fprintf(f, " dummy_input\n");
@ -1013,17 +1195,17 @@ void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, Ab
if (!si.is_port || si.type == G(NONE)) if (!si.is_port || si.type == G(NONE))
continue; continue;
fprintf(f, " ys__n%d", si.id); fprintf(f, " ys__n%d", si.id);
po_map[count_output++] = log_signal(si.bit); po_map[count_output++] = si.bit_str;
} }
fprintf(f, "\n"); fprintf(f, "\n");
for (auto &si : signal_list) for (auto &si : signal_list)
fprintf(f, "# ys__n%-5d %s\n", si.id, log_signal(si.bit)); fprintf(f, "# ys__n%-5d %s\n", si.id, si.bit_str.c_str());
for (auto &si : signal_list) { for (auto &si : signal_list) {
if (si.bit.wire == nullptr) { if (!si.bit_is_wire) {
fprintf(f, ".names ys__n%d\n", si.id); fprintf(f, ".names ys__n%d\n", si.id);
if (si.bit == RTLIL::State::S1) if (si.bit_is_1)
fprintf(f, "1\n"); fprintf(f, "1\n");
} }
} }
@ -1106,19 +1288,112 @@ void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, Ab
fprintf(f, ".end\n"); fprintf(f, ".end\n");
fclose(f); fclose(f);
log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n", 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); count_gates, GetSize(signal_list), count_input, count_output);
log_push();
if (count_output > 0) if (count_output > 0)
{ {
log_header(design, "Executing ABC.\n"); 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));
errno = 0;
abc_output_filter filt(*this, tempdir_name, config.show_tempdir);
#ifdef YOSYS_LINK_ABC
string temp_stdouterr_name = stringf("%s/stdouterr.txt", 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");
fflush(stdout);
fflush(stderr);
FILE *old_stdout = fopen(temp_stdouterr_name.c_str(), "r"); // need any fd for renumbering
FILE *old_stderr = fopen(temp_stdouterr_name.c_str(), "r"); // need any fd for renumbering
#if defined(__wasm)
#define fd_renumber(from, to) (void)__wasi_fd_renumber(from, to)
#else
#define fd_renumber(from, to) dup2(from, to)
#endif
fd_renumber(fileno(stdout), fileno(old_stdout));
fd_renumber(fileno(stderr), fileno(old_stderr));
fd_renumber(fileno(temp_stdouterr_w), fileno(stdout));
fd_renumber(fileno(temp_stdouterr_w), fileno(stderr));
fclose(temp_stdouterr_w);
// These needs to be mutable, supposedly due to getopt
char *abc_argv[5];
abc_argv[0] = strdup(config.exe_file.c_str());
abc_argv[1] = strdup("-s");
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);
free(abc_argv[0]);
free(abc_argv[1]);
free(abc_argv[2]);
free(abc_argv[3]);
fflush(stdout);
fflush(stderr);
fd_renumber(fileno(old_stdout), fileno(stdout));
fd_renumber(fileno(old_stderr), fileno(stderr));
fclose(old_stdout);
fclose(old_stderr);
std::ifstream temp_stdouterr_r(temp_stdouterr_name);
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(
// This makes ABC switch stdout to line buffering, which we need
// to see our ABC_DONE message.
"set abcout /dev/stdout\n"
"empty\n"
"source %s\n"
"echo \"ABC_DONE\"\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));
}
#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;
}
did_run = true;
return;
}
log("Don't call ABC as there is nothing to map.\n");
}
void emit_global_input_files(const AbcConfig &config)
{
if (!config.lut_costs.empty()) {
std::string buffer = stringf("%s/lutdefs.txt", config.global_tempdir_name.c_str());
FILE *f = fopen(buffer.c_str(), "wt");
if (f == nullptr)
log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
for (int i = 0; i < GetSize(config.lut_costs); i++)
fprintf(f, "%d %d.00 1.00\n", i+1, config.lut_costs.at(i));
fclose(f);
} else {
auto &cell_cost = cmos_cost ? CellCosts::cmos_gate_cost() : CellCosts::default_gate_cost(); auto &cell_cost = cmos_cost ? CellCosts::cmos_gate_cost() : CellCosts::default_gate_cost();
buffer = stringf("%s/stdcells.genlib", tempdir_name); std::string buffer = stringf("%s/stdcells.genlib", config.global_tempdir_name.c_str());
f = fopen(buffer.c_str(), "wt"); FILE *f = fopen(buffer.c_str(), "wt");
if (f == nullptr) if (f == nullptr)
log_error("Opening %s for writing failed: %s\n", buffer, strerror(errno)); log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
fprintf(f, "GATE ZERO 1 Y=CONST0;\n"); fprintf(f, "GATE ZERO 1 Y=CONST0;\n");
fprintf(f, "GATE ONE 1 Y=CONST1;\n"); fprintf(f, "GATE ONE 1 Y=CONST1;\n");
fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_BUF_))); fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", cell_cost.at(ID($_BUF_)));
@ -1158,92 +1433,28 @@ void AbcModuleState::abc_module(RTLIL::Design *design, RTLIL::Module *module, Ab
if (map_mux16) if (map_mux16)
fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*cell_cost.at(ID($_MUX_))); fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*cell_cost.at(ID($_MUX_)));
fclose(f); fclose(f);
if (!config.lut_costs.empty()) {
buffer = stringf("%s/lutdefs.txt", tempdir_name);
f = fopen(buffer.c_str(), "wt");
if (f == nullptr)
log_error("Opening %s for writing failed: %s\n", buffer, strerror(errno));
for (int i = 0; i < GetSize(config.lut_costs); i++)
fprintf(f, "%d %d.00 1.00\n", i+1, config.lut_costs.at(i));
fclose(f);
}
buffer = stringf("\"%s\" -s -f %s/abc.script 2>&1", config.exe_file, tempdir_name);
log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, config.show_tempdir));
#ifndef YOSYS_LINK_ABC
abc_output_filter filt(*this, tempdir_name, config.show_tempdir);
int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1));
#else
string temp_stdouterr_name = stringf("%s/stdouterr.txt", 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");
fflush(stdout);
fflush(stderr);
FILE *old_stdout = fopen(temp_stdouterr_name.c_str(), "r"); // need any fd for renumbering
FILE *old_stderr = fopen(temp_stdouterr_name.c_str(), "r"); // need any fd for renumbering
#if defined(__wasm)
#define fd_renumber(from, to) (void)__wasi_fd_renumber(from, to)
#else
#define fd_renumber(from, to) dup2(from, to)
#endif
fd_renumber(fileno(stdout), fileno(old_stdout));
fd_renumber(fileno(stderr), fileno(old_stderr));
fd_renumber(fileno(temp_stdouterr_w), fileno(stdout));
fd_renumber(fileno(temp_stdouterr_w), fileno(stderr));
fclose(temp_stdouterr_w);
// These needs to be mutable, supposedly due to getopt
char *abc_argv[5];
string tmp_script_name = stringf("%s/abc.script", tempdir_name);
abc_argv[0] = strdup(config.exe_file.c_str());
abc_argv[1] = strdup("-s");
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);
free(abc_argv[0]);
free(abc_argv[1]);
free(abc_argv[2]);
free(abc_argv[3]);
fflush(stdout);
fflush(stderr);
fd_renumber(fileno(old_stdout), fileno(stdout));
fd_renumber(fileno(old_stderr), fileno(stderr));
fclose(old_stdout);
fclose(old_stderr);
std::ifstream temp_stdouterr_r(temp_stdouterr_name);
abc_output_filter filt(*this, tempdir_name, config.show_tempdir);
for (std::string line; std::getline(temp_stdouterr_r, line); )
filt.next_line(line + "\n");
temp_stdouterr_r.close();
#endif
if (ret != 0) {
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer, ret);
return;
}
did_run_abc = true;
return;
} }
log("Don't call ABC as there is nothing to map.\n");
} }
void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module) void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module)
{ {
if (!did_run_abc) { log_push();
log_header(design, "Executed ABC.\n");
run_abc.logs.flush();
if (!run_abc.did_run) {
finish();
return; return;
} }
std::string buffer = stringf("%s/%s", tempdir_name, "output.blif"); std::string buffer = stringf("%s/%s", run_abc.tempdir_name, "output.blif");
std::ifstream ifs; std::ifstream ifs;
ifs.open(buffer); ifs.open(buffer);
if (ifs.fail()) if (ifs.fail())
log_error("Can't open ABC output file `%s'.\n", buffer); log_error("Can't open ABC output file `%s'.\n", buffer);
bool builtin_lib = config.liberty_files.empty() && config.genlib_files.empty(); bool builtin_lib = run_abc.config.liberty_files.empty() && run_abc.config.genlib_files.empty();
RTLIL::Design *mapped_design = new RTLIL::Design; RTLIL::Design *mapped_design = new RTLIL::Design;
parse_blif(mapped_design, ifs, builtin_lib ? ID(DFF) : ID(_dff_), false, config.sop_mode); parse_blif(mapped_design, ifs, builtin_lib ? ID(DFF) : ID(_dff_), false, run_abc.config.sop_mode);
ifs.close(); ifs.close();
@ -1492,37 +1703,37 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL
for (auto &it : cell_stats) for (auto &it : cell_stats)
log("ABC RESULTS: %15s cells: %8d\n", it.first, it.second); log("ABC RESULTS: %15s cells: %8d\n", it.first, it.second);
int in_wires = 0, out_wires = 0; int in_wires = 0, out_wires = 0;
for (auto &si : signal_list) for (auto &si : run_abc.signal_list)
if (si.is_port) { if (si.is_port) {
char buffer[100]; char buffer[100];
snprintf(buffer, 100, "\\ys__n%d", si.id); snprintf(buffer, 100, "\\ys__n%d", si.id);
RTLIL::SigSig conn; RTLIL::SigSig conn;
if (si.type != G(NONE)) { if (si.type != G(NONE)) {
conn.first = si.bit; conn.first = signal_bits[si.id];
conn.second = module->wire(remap_name(buffer)); conn.second = module->wire(remap_name(buffer));
out_wires++; out_wires++;
} else { } else {
conn.first = module->wire(remap_name(buffer)); conn.first = module->wire(remap_name(buffer));
conn.second = si.bit; conn.second = signal_bits[si.id];
in_wires++; in_wires++;
} }
connect(assign_map, module, conn); connect(assign_map, module, conn);
} }
log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); log("ABC RESULTS: internal signals: %8d\n", int(run_abc.signal_list.size()) - in_wires - out_wires);
log("ABC RESULTS: input signals: %8d\n", in_wires); log("ABC RESULTS: input signals: %8d\n", in_wires);
log("ABC RESULTS: output signals: %8d\n", out_wires); log("ABC RESULTS: output signals: %8d\n", out_wires);
delete mapped_design; delete mapped_design;
finish();
} }
void AbcModuleState::finish() void AbcModuleState::finish()
{ {
if (config.cleanup) if (run_abc.config.cleanup)
{ {
log("Removing temp directory.\n"); log("Removing temp directory.\n");
remove_directory(tempdir_name); remove_directory(run_abc.tempdir_name);
} }
log_pop(); log_pop();
} }
@ -1811,6 +2022,13 @@ struct AbcPass : public Pass {
config.show_tempdir = design->scratchpad_get_bool("abc.showtmp", false); config.show_tempdir = design->scratchpad_get_bool("abc.showtmp", false);
markgroups = design->scratchpad_get_bool("abc.markgroups", markgroups); markgroups = design->scratchpad_get_bool("abc.markgroups", markgroups);
if (config.cleanup)
config.global_tempdir_name = get_base_tmpdir() + "/";
else
config.global_tempdir_name = "_tmp_";
config.global_tempdir_name += proc_program_prefix() + "yosys-abc-XXXXXX";
config.global_tempdir_name = make_temp_dir(config.global_tempdir_name);
if (design->scratchpad_get_bool("abc.debug")) { if (design->scratchpad_get_bool("abc.debug")) {
config.cleanup = false; config.cleanup = false;
config.show_tempdir = true; config.show_tempdir = true;
@ -2139,6 +2357,8 @@ struct AbcPass : public Pass {
// enabled_gates.insert("NMUX"); // enabled_gates.insert("NMUX");
} }
emit_global_input_files(config);
for (auto mod : design->selected_modules()) for (auto mod : design->selected_modules())
{ {
if (mod->processes.size() > 0) { if (mod->processes.size() > 0) {
@ -2163,9 +2383,10 @@ struct AbcPass : public Pass {
assign_cell_connection_ports(mod, {&cells}, assign_map); assign_cell_connection_ports(mod, {&cells}, assign_map);
AbcModuleState state(config, initvals); AbcModuleState state(config, initvals);
state.abc_module(design, mod, assign_map, cells, dff_mode, clk_str); state.prepare_module(design, mod, assign_map, cells, dff_mode, clk_str);
ConcurrentStack<AbcProcess> process_pool;
state.run_abc.run(process_pool);
state.extract(assign_map, design, mod); state.extract(assign_map, design, mod);
state.finish();
continue; continue;
} }
@ -2310,33 +2531,81 @@ struct AbcPass : public Pass {
} }
log_header(design, "Summary of detected clock domains:\n"); log_header(design, "Summary of detected clock domains:\n");
for (auto &it : assigned_cells)
log(" %d cells in clk=%s%s, en=%s%s, arst=%s%s, srst=%s%s\n", GetSize(it.second),
std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)),
std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first)),
std::get<4>(it.first) ? "" : "!", log_signal(std::get<5>(it.first)),
std::get<6>(it.first) ? "" : "!", log_signal(std::get<7>(it.first)));
{ {
std::vector<std::vector<RTLIL::Cell*>*> cell_sets; std::vector<std::vector<RTLIL::Cell*>*> cell_sets;
for (auto &it : assigned_cells) for (auto &it : assigned_cells) {
log(" %d cells in clk=%s%s, en=%s%s, arst=%s%s, srst=%s%s\n", GetSize(it.second),
std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)),
std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first)),
std::get<4>(it.first) ? "" : "!", log_signal(std::get<5>(it.first)),
std::get<6>(it.first) ? "" : "!", log_signal(std::get<7>(it.first)));
cell_sets.push_back(&it.second); cell_sets.push_back(&it.second);
}
assign_cell_connection_ports(mod, cell_sets, assign_map); assign_cell_connection_ports(mod, cell_sets, assign_map);
} }
for (auto &it : assigned_cells) {
AbcModuleState state(config, initvals); // Reserve one core for our main thread, and don't create more worker threads
state.clk_polarity = std::get<0>(it.first); // than ABC runs.
state.clk_sig = assign_map(std::get<1>(it.first)); int max_threads = assigned_cells.size();
state.en_polarity = std::get<2>(it.first); if (max_threads <= 1) {
state.en_sig = assign_map(std::get<3>(it.first)); // Just do everything on the main thread.
state.arst_polarity = std::get<4>(it.first); max_threads = 0;
state.arst_sig = assign_map(std::get<5>(it.first));
state.srst_polarity = std::get<6>(it.first);
state.srst_sig = assign_map(std::get<7>(it.first));
state.abc_module(design, mod, assign_map, it.second, !state.clk_sig.empty(), "$");
state.extract(assign_map, design, mod);
state.finish();
} }
#ifdef YOSYS_LINK_ABC
// ABC does't support multithreaded calls so don't call it off the main thread.
max_threads = 0;
#endif
int num_worker_threads = ThreadPool::pool_size(1, max_threads);
ConcurrentQueue<std::unique_ptr<AbcModuleState>> work_queue(num_worker_threads);
ConcurrentQueue<std::unique_ptr<AbcModuleState>> work_finished_queue;
int work_finished_count = 0;
ConcurrentStack<AbcProcess> process_pool;
ThreadPool worker_threads(num_worker_threads, [&](int){
while (std::optional<std::unique_ptr<AbcModuleState>> work =
work_queue.pop_front()) {
// Only the `run_abc` component is safe to touch here!
(*work)->run_abc.run(process_pool);
work_finished_queue.push_back(std::move(*work));
}
});
for (auto &it : assigned_cells) {
// Process ABC results that have already finished before queueing another ABC.
// This should keep our memory usage down.
while (std::optional<std::unique_ptr<AbcModuleState>> work =
work_finished_queue.try_pop_front()) {
(*work)->extract(assign_map, design, mod);
++work_finished_count;
}
std::unique_ptr<AbcModuleState> state = std::make_unique<AbcModuleState>(config, initvals);
state->clk_polarity = std::get<0>(it.first);
state->clk_sig = assign_map(std::get<1>(it.first));
state->en_polarity = std::get<2>(it.first);
state->en_sig = assign_map(std::get<3>(it.first));
state->arst_polarity = std::get<4>(it.first);
state->arst_sig = assign_map(std::get<5>(it.first));
state->srst_polarity = std::get<6>(it.first);
state->srst_sig = assign_map(std::get<7>(it.first));
state->prepare_module(design, mod, assign_map, it.second, !state->clk_sig.empty(), "$");
if (num_worker_threads > 0) {
work_queue.push_back(std::move(state));
} else {
// Just run everything on the main thread.
state->run_abc.run(process_pool);
work_finished_queue.push_back(std::move(state));
}
}
work_queue.close();
while (work_finished_count < static_cast<int>(assigned_cells.size())) {
std::optional<std::unique_ptr<AbcModuleState>> work =
work_finished_queue.pop_front();
(*work)->extract(assign_map, design, mod);
++work_finished_count;
}
}
if (config.cleanup) {
log("Removing global temp directory.\n");
remove_directory(config.global_tempdir_name);
} }
log_pop(); log_pop();