/*
* 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/macc.h"
#include "kernel/celltypes.h"
#include "kernel/binding.h"
#include "frontends/verilog/verilog_frontend.h"
#include "frontends/verilog/preproc.h"
#include "backends/rtlil/rtlil_backend.h"
#include <string.h>
#include <algorithm>
YOSYS_NAMESPACE_BEGIN
RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard;
std::vector<char*> RTLIL::IdString::global_id_storage_;
dict<char*, int, hash_cstr_ops> RTLIL::IdString::global_id_index_;
#ifndef YOSYS_NO_IDS_REFCNT
std::vector<int> RTLIL::IdString::global_refcount_storage_;
std::vector<int> RTLIL::IdString::global_free_idx_list_;
#endif
#ifdef YOSYS_USE_STICKY_IDS
int RTLIL::IdString::last_created_idx_[8];
int RTLIL::IdString::last_created_idx_ptr_;
#endif
#define X(_id) IdString RTLIL::ID::_id;
#include "kernel/constids.inc"
#undef X
dict<std::string, std::string> RTLIL::constpad;
const pool<IdString> &RTLIL::builtin_ff_cell_types() {
static const pool<IdString> res = {
ID($sr),
ID($ff),
ID($dff),
ID($dffe),
ID($dffsr),
ID($dffsre),
ID($adff),
ID($adffe),
ID($aldff),
ID($aldffe),
ID($sdff),
ID($sdffe),
ID($sdffce),
ID($dlatch),
ID($adlatch),
ID($dlatchsr),
ID($_DFFE_NN_),
ID($_DFFE_NP_),
ID($_DFFE_PN_),
ID($_DFFE_PP_),
ID($_DFFSR_NNN_),
ID($_DFFSR_NNP_),
ID($_DFFSR_NPN_),
ID($_DFFSR_NPP_),
ID($_DFFSR_PNN_),
ID($_DFFSR_PNP_),
ID($_DFFSR_PPN_),
ID($_DFFSR_PPP_),
ID($_DFFSRE_NNNN_),
ID($_DFFSRE_NNNP_),
ID($_DFFSRE_NNPN_),
ID($_DFFSRE_NNPP_),
ID($_DFFSRE_NPNN_),
ID($_DFFSRE_NPNP_),
ID($_DFFSRE_NPPN_),
ID($_DFFSRE_NPPP_),
ID($_DFFSRE_PNNN_),
ID($_DFFSRE_PNNP_),
ID($_DFFSRE_PNPN_),
ID($_DFFSRE_PNPP_),
ID($_DFFSRE_PPNN_),
ID($_DFFSRE_PPNP_),
ID($_DFFSRE_PPPN_),
ID($_DFFSRE_PPPP_),
ID($_DFF_N_),
ID($_DFF_P_),
ID($_DFF_NN0_),
ID($_DFF_NN1_),
ID($_DFF_NP0_),
ID($_DFF_NP1_),
ID($_DFF_PN0_),
ID($_DFF_PN1_),
ID($_DFF_PP0_),
ID($_DFF_PP1_),
ID($_DFFE_NN0N_),
ID($_DFFE_NN0P_),
ID($_DFFE_NN1N_),
ID($_DFFE_NN1P_),
ID($_DFFE_NP0N_),
ID($_DFFE_NP0P_),
ID($_DFFE_NP1N_),
ID($_DFFE_NP1P_),
ID($_DFFE_PN0N_),
ID($_DFFE_PN0P_),
ID($_DFFE_PN1N_),
ID($_DFFE_PN1P_),
ID($_DFFE_PP0N_),
ID($_DFFE_PP0P_),
ID($_DFFE_PP1N_),
ID($_DFFE_PP1P_),
ID($_ALDFF_NN_),
ID($_ALDFF_NP_),
ID($_ALDFF_PN_),
ID($_ALDFF_PP_),
ID($_ALDFFE_NNN_),
ID($_ALDFFE_NNP_),
ID($_ALDFFE_NPN_),
ID($_ALDFFE_NPP_),
ID($_ALDFFE_PNN_),
ID($_ALDFFE_PNP_),
ID($_ALDFFE_PPN_),
ID($_ALDFFE_PPP_),
ID($_SDFF_NN0_),
ID($_SDFF_NN1_),
ID($_SDFF_NP0_),
ID($_SDFF_NP1_),
ID($_SDFF_PN0_),
ID($_SDFF_PN1_),
ID($_SDFF_PP0_),
ID($_SDFF_PP1_),
ID($_SDFFE_NN0N_),
ID($_SDFFE_NN0P_),
ID($_SDFFE_NN1N_),
ID($_SDFFE_NN1P_),
ID($_SDFFE_NP0N_),
ID($_SDFFE_NP0P_),
ID($_SDFFE_NP1N_),
ID($_SDFFE_NP1P_),
ID($_SDFFE_PN0N_),
ID($_SDFFE_PN0P_),
ID($_SDFFE_PN1N_),
ID($_SDFFE_PN1P_),
ID($_SDFFE_PP0N_),
ID($_SDFFE_PP0P_),
ID($_SDFFE_PP1N_),
ID($_SDFFE_PP1P_),
ID($_SDFFCE_NN0N_),
ID($_SDFFCE_NN0P_),
ID($_SDFFCE_NN1N_),
ID($_SDFFCE_NN1P_),
ID($_SDFFCE_NP0N_),
ID($_SDFFCE_NP0P_),
ID($_SDFFCE_NP1N_),
ID($_SDFFCE_NP1P_),
ID($_SDFFCE_PN0N_),
ID($_SDFFCE_PN0P_),
ID($_SDFFCE_PN1N_),
ID($_SDFFCE_PN1P_),
ID($_SDFFCE_PP0N_),
ID($_SDFFCE_PP0P_),
ID($_SDFFCE_PP1N_),
ID($_SDFFCE_PP1P_),
ID($_SR_NN_),
ID($_SR_NP_),
ID($_SR_PN_),
ID($_SR_PP_),
ID($_DLATCH_N_),
ID($_DLATCH_P_),
ID($_DLATCH_NN0_),
ID($_DLATCH_NN1_),
ID($_DLATCH_NP0_),
ID($_DLATCH_NP1_),
ID($_DLATCH_PN0_),
ID($_DLATCH_PN1_),
ID($_DLATCH_PP0_),
ID($_DLATCH_PP1_),
ID($_DLATCHSR_NNN_),
ID($_DLATCHSR_NNP_),
ID($_DLATCHSR_NPN_),
ID($_DLATCHSR_NPP_),
ID($_DLATCHSR_PNN_),
ID($_DLATCHSR_PNP_),
ID($_DLATCHSR_PPN_),
ID($_DLATCHSR_PPP_),
ID($_FF_),
};
return res;
}
RTLIL::Const::Const(const std::string &str)
{
flags = RTLIL::CONST_FLAG_STRING;
bits.reserve(str.size() * 8);
for (int i = str.size()-1; i >= 0; i--) {
unsigned char ch = str[i];
for (int j = 0; j < 8; j++) {
bits.push_back((ch & 1) != 0 ? State::S1 : State::S0);
ch = ch >> 1;
}
}
}
RTLIL::Const::Const(int val, int width)
{
flags = RTLIL::CONST_FLAG_NONE;
bits.reserve(width);
for (int i = 0; i < width; i++) {
bits.push_back((val & 1) != 0 ? State::S1 : State::S0);
val = val >> 1;
}
}
RTLIL::Const::Const(RTLIL::State bit, int width)
{
flags = RTLIL::CONST_FLAG_NONE;
bits.reserve(width);
for (int i = 0; i < width; i++)
bits.push_back(bit);
}
RTLIL::Const::Const(const std::vector<bool> &bits)
{
flags = RTLIL::CONST_FLAG_NONE;
this->bits.reserve(bits.size());
for (const auto &b : bits)
this->bits.emplace_back(b ? State::S1 : State::S0);
}
bool RTLIL::Const::operator <(const RTLIL::Const &other) const
{
if (bits.size() != other.bits.size())
return bits.size() < other.bits.size();
for (size_t i = 0; i < bits.size(); i++)
if (bits[i] != other.bits[i])
return bits[i] < other.bits[i];
return false;
}
bool RTLIL::Const::operator ==(const RTLIL::Const &other) const
{
return bits == other.bits;
}
bool RTLIL::Const::operator !=(const RTLIL::Const &other) const
{
return bits != other.bits;
}
bool RTLIL::Const::as_bool() const
{
for (size_t i = 0; i < bits.size(); i++)
if (bits[i] == State::S1)
return true;
return false;
}
int RTLIL::Const::as_int(bool is_signed) const
{
int32_t ret = 0;
for (size_t i = 0; i < bits.size() && i < 32; i++)
if (bits[i] == State::S1)
ret |= 1 << i;
if (is_signed && bits.back() == State::S1)
for (size_t i = bits.size(); i < 32; i++)
ret |= 1 << i;
return ret;
}
std::string RTLIL::Const::as_string() const
{
std::string ret;
ret.reserve(bits.size());
for (size_t i = bits.size(); i > 0; i--)
switch (bits[i-1]) {
case S0: ret += "0"; break;
case S1: ret += "1"; break;
case Sx: ret += "x"; break;
case Sz: ret += "z"; break;
case Sa: ret += "-"; break;
case Sm: ret += "m"; break;
}
return ret;
}
RTLIL::Const RTLIL::Const::from_string(const std::string &str)
{
Const c;
c.bits.reserve(str.size());
for (auto it = str.rbegin(); it != str.rend(); it++)
switch (*it) {
case '0': c.bits.push_back(State::S0); break;
case '1': c.bits.push_back(State::S1); break;
case 'x': c.bits.push_back(State::Sx); break;
case 'z': c.bits.push_back(State::Sz); break;
case 'm': c.bits.push_back(State::Sm); break;
default: c.bits.push_back(State::Sa);
}
return c;
}
std::string RTLIL::Const::decode_string() const
{
std::string string;
string.reserve(GetSize(bits)/8);
for (int i = 0; i < GetSize(bits); i += 8) {
char ch = 0;
for (int j = 0; j < 8 && i + j < int (bits.size()); j++)
if (bits[i + j] == RTLIL::State::S1)
ch |= 1 << j;
if (ch != 0)
string.append({ch});
}
std::reverse(string.begin(), string.end());
return string;
}
bool RTLIL::Const::is_fully_zero() const
{
cover("kernel.rtlil.const.is_fully_zero");
for (const auto &bit : bits)
if (bit != RTLIL::State::S0)
return false;
return true;
}
bool RTLIL::Const::is_fully_ones() const
{
cover("kernel.rtlil.const.is_fully_ones");
for (const auto &bit : bits)
if (bit != RTLIL::State::S1)
return false;
return true;
}
bool RTLIL::Const::is_fully_def() const
{
cover("kernel.rtlil.const.is_fully_def");
for (const auto &bit : bits)
if (bit != RTLIL::State::S0 && bit != RTLIL::State::S1)
return false;
return true;
}
bool RTLIL::Const::is_fully_undef() const
{
cover("kernel.rtlil.const.is_fully_undef");
for (const auto &bit : bits)
if (bit != RTLIL::State::Sx && bit != RTLIL::State::Sz)
return false;
return true;
}
bool RTLIL::Const::is_onehot(int *pos) const
{
cover("kernel.rtlil.const.is_onehot");
bool found = false;
for (int i = 0; i < GetSize(*this); i++) {
auto &bit = bits[i];
if (bit != RTLIL::State::S0 && bit != RTLIL::State::S1)
return false;
if (bit == RTLIL::State::S1) {
if (found)
return false;
if (pos)
*pos = i;
found = true;
}
}
return found;
}
bool RTLIL::AttrObject::has_attribute(const RTLIL::IdString &id) const
{
return attributes.count(id);
}
void RTLIL::AttrObject::set_bool_attribute(const RTLIL::IdString &id, bool value)
{
if (value)
attributes[id] = RTLIL::Const(1);
else
attributes.erase(id);
}
bool RTLIL::AttrObject::get_bool_attribute(const RTLIL::IdString &id) const
{
const auto it = attributes.find(id);
if (it == attributes.end())
return false;
return it->second.as_bool();
}
void RTLIL::AttrObject::set_string_attribute(const RTLIL::IdString& id, string value)
{
if (value.empty())
attributes.erase(id);
else
attributes[id] = value;
}
string RTLIL::AttrObject::get_string_attribute(const RTLIL::IdString &id) const
{
std::string value;
const auto it = attributes.find(id);
if (it != attributes.end())
value = it->second.decode_string();
return value;
}
void RTLIL::AttrObject::set_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data)
{
string attrval;
for (const auto &s : data) {
if (!attrval.empty())
attrval += "|";
attrval += s;
}
set_string_attribute(id, attrval);
}
void RTLIL::AttrObject::add_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data)
{
pool<string> union_data = get_strpool_attribute(id);
union_data.insert(data.begin(), data.end());
if (!union_data.empty())
set_strpool_attribute(id, union_data);
}
pool<string> RTLIL::AttrObject::get_strpool_attribute(const RTLIL::IdString &id) const
{
pool<string> data;
if (attributes.count(id) != 0)
for (auto s : split_tokens(get_string_attribute(id), "|"))
data.insert(s);
return data;
}
void RTLIL::AttrObject::set_hdlname_attribute(const vector<string> &hierarchy)
{
string attrval;
for (const auto &ident : hierarchy) {
if (!attrval.empty())
attrval += " ";
attrval += ident;
}
set_string_attribute(ID::hdlname, attrval);
}
vector<string> RTLIL::AttrObject::get_hdlname_attribute() const
{
return split_tokens(get_string_attribute(ID::hdlname), " ");
}
void RTLIL::AttrObject::set_intvec_attribute(const RTLIL::IdString& id, const vector<int> &data)
{
std::stringstream attrval;
for (auto &i : data) {
if (attrval.tellp() > 0)
attrval << " ";
attrval << i;
}
attributes[id] = RTLIL::Const(attrval.str());
}
vector<int> RTLIL::AttrObject::get_intvec_attribute(const RTLIL::IdString &id) const
{
vector<int> data;
auto it = attributes.find(id);
if (it != attributes.end())
for (const auto &s : split_tokens(attributes.at(id).decode_string())) {
char *end = nullptr;
errno = 0;
long value = strtol(s.c_str(), &end, 10);
if (end != s.c_str() + s.size())
log_cmd_error("Literal for intvec attribute has invalid format");
if (errno == ERANGE || value < INT_MIN || value > INT_MAX)
log_cmd_error("Literal for intvec attribute is out of range");
data.push_back(value);
}
return data;
}
bool RTLIL::Selection::selected_module(const RTLIL::IdString &mod_name) const
{
if (full_selection)
return true;
if (selected_modules.count(mod_name) > 0)
return true;
if (selected_members.count(mod_name) > 0)
return true;
return false;
}
bool RTLIL::Selection::selected_whole_module(const RTLIL::IdString &mod_name) const
{
if (full_selection)
return true;
if (selected_modules.count(mod_name) > 0)
return true;
return false;
}
bool RTLIL::Selection::selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const
{
if (full_selection)
return true;
if (selected_modules.count(mod_name) > 0)
return true;
if (selected_members.count(mod_name) > 0)
if (selected_members.at(mod_name).count(memb_name) > 0)
return true;
return false;
}
void RTLIL::Selection::optimize(RTLIL::Design *design)
{
if (full_selection) {
selected_modules.clear();
selected_members.clear();
return;
}
std::vector<RTLIL::IdString> del_list, add_list;
del_list.clear();
for (auto mod_name : selected_modules) {
if (design->modules_.count(mod_name) == 0)
del_list.push_back(mod_name);
selected_members.erase(mod_name);
}
for (auto mod_name : del_list)
selected_modules.erase(mod_name);
del_list.clear();
for (auto &it : selected_members)
if (design->modules_.count(it.first) == 0)
del_list.push_back(it.first);
for (auto mod_name : del_list)
selected_members.erase(mod_name);
for (auto &it : selected_members) {
del_list.clear();
for (auto memb_name : it.second)
if (design->modules_[it.first]->count_id(memb_name) == 0)
del_list.push_back(memb_name);
for (auto memb_name : del_list)
it.second.erase(memb_name);
}
del_list.clear();
add_list.clear();
for (auto &it : selected_members)
if (it.second.size() == 0)
del_list.push_back(it.first);
else if (it.second.size() == design->modules_[it.first]->wires_.size() + design->modules_[it.first]->memories.size() +
design->modules_[it.first]->cells_.size() + design->modules_[it.first]->processes.size())
add_list.push_back(it.first);
for (auto mod_name : del_list)
selected_members.erase(mod_name);
for (auto mod_name : add_list) {
selected_members.erase(mod_name);
selected_modules.insert(mod_name);
}
if (selected_modules.size() == design->modules_.size()) {
full_selection = true;
selected_modules.clear();
selected_members.clear();
}
}
RTLIL::Design::Design()
: verilog_defines (new define_map_t)
{
static unsigned int hashidx_count = 123456789;
hashidx_count = mkhash_xorshift(hashidx_count);
hashidx_ = hashidx_count;
refcount_modules_ = 0;
selection_stack.push_back(RTLIL::Selection());
#ifdef WITH_PYTHON
RTLIL::Design::get_all_designs()->insert(std::pair<unsigned int, RTLIL::Design*>(hashidx_, this));
#endif
}
RTLIL::Design::~Design()
{
for (auto &pr : modules_)
delete pr.second;
for (auto n : bindings_)
delete n;
for (auto n : verilog_packages)
delete n;
for (auto n : verilog_globals)
delete n;
#ifdef WITH_PYTHON
RTLIL::Design::get_all_designs()->erase(hashidx_);
#endif
}
#ifdef WITH_PYTHON
static std::map<unsigned int, RTLIL::Design*> all_designs;
std::map<unsigned int, RTLIL::Design*> *RTLIL::Design::get_all_designs(void)
{
return &all_designs;
}
#endif
RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules()
{
return RTLIL::ObjRange<RTLIL::Module*>(&modules_, &refcount_modules_);
}
RTLIL::Module *RTLIL::Design::module(const RTLIL::IdString& name)
{
return modules_.count(name) ? modules_.at(name) : NULL;
}
const RTLIL::Module *RTLIL::Design::module(const RTLIL::IdString& name) const
{
return modules_.count(name) ? modules_.at(name) : NULL;
}
RTLIL::Module *RTLIL::Design::top_module()
{
RTLIL::Module *module = nullptr;
int module_count = 0;
for (auto mod : selected_modules()) {
if (mod->get_bool_attribute(ID::top))
return mod;
module_count++;
module = mod;
}
return module_count == 1 ? module : nullptr;
}
void RTLIL::Design::add(RTLIL::Module *module)
{
log_assert(modules_.count(module->name) == 0);
log_assert(refcount_modules_ == 0);
modules_[module->name] = module;
module->design = this;
for (auto mon : monitors)
mon->notify_module_add(module);
if (yosys_xtrace) {
log("#X# New Module: %s\n", log_id(module));
log_backtrace("-X- ", yosys_xtrace-1);
}
}
void RTLIL::Design::add(RTLIL::Binding *binding)
{
log_assert(binding != nullptr);
bindings_.push_back(binding);
}
RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name)
{
if (modules_.count(name) != 0)
log_error("Attempted to add new module named '%s', but a module by that name already exists\n", name.c_str());
log_assert(refcount_modules_ == 0);
RTLIL::Module *module = new RTLIL::Module;
modules_[name] = module;
module->design = this;
module->name = name;
for (auto mon : monitors)
mon->notify_module_add(module);
if (yosys_xtrace) {
log("#X# New Module: %s\n", log_id(module));
log_backtrace("-X- ", yosys_xtrace-1);
}
return module;
}
void RTLIL::Design::scratchpad_unset(const std::string &varname)
{
scratchpad.erase(varname);
}
void RTLIL::Design::scratchpad_set_int(const std::string &varname, int value)
{
scratchpad[varname] = stringf("%d", value);
}
void RTLIL::Design::scratchpad_set_bool(const std::string &varname, bool value)
{
scratchpad[varname] = value ? "true" : "false";
}
void RTLIL::Design::scratchpad_set_string(const std::string &varname, std::string value)
{
scratchpad[varname] = std::move(value);
}
int RTLIL::Design::scratchpad_get_int(const std::string &varname, int default_value) const
{
auto it = scratchpad.find(varname);
if (it == scratchpad.end())
return default_value;
const std::string &str = it->second;
if (str == "0" || str == "false")
return 0;
if (str == "1" || str == "true")
return 1;
char *endptr = nullptr;
long int parsed_value = strtol(str.c_str(), &endptr, 10);
return *endptr ? default_value : parsed_value;
}
bool RTLIL::Design::scratchpad_get_bool(const std::string &varname, bool default_value) const
{
auto it = scratchpad.find(varname);
if (it == scratchpad.end())
return default_value;
const std::string &str = it->second;
if (str == "0" || str == "false")
return false;
if (str == "1" || str == "true")
return true;
return default_value;
}
std::string RTLIL::Design::scratchpad_get_string(const std::string &varname, const std::string &default_value) const
{
auto it = scratchpad.find(varname);
if (it == scratchpad.end())
return default_value;
return it->second;
}
void RTLIL::Design::remove(RTLIL::Module *module)
{
for (auto mon : monitors)
mon->notify_module_del(module);
if (yosys_xtrace) {
log("#X# Remove Module: %s\n", log_id(module));
log_backtrace("-X- ", yosys_xtrace-1);
}
log_assert(modules_.at(module->name) == module);
log_assert(refcount_modules_ == 0);
modules_.erase(module->name);
delete module;
}
void RTLIL::Design::rename(RTLIL::Module *module, RTLIL::IdString new_name)
{
modules_.erase(module->name);
module->name = new_name;
add(module);
}
void RTLIL::Design::sort()
{
scratchpad.sort();
modules_.sort(sort_by_id_str());
for (auto &it : modules_)
it.second->sort();
}
void RTLIL::Design::check()
{
#ifndef NDEBUG
for (auto &it : modules_) {
log_assert(this == it.second->design);
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
it.second->check();
}
#endif
}
void RTLIL::Design::optimize()