mirror of https://github.com/YosysHQ/yosys.git
Compare commits
42 Commits
461059eddf
...
be8e06b140
| Author | SHA1 | Date |
|---|---|---|
|
|
be8e06b140 | |
|
|
5d0847f6fb | |
|
|
24b69cabaa | |
|
|
691d6b8508 | |
|
|
a16fc9b4f3 | |
|
|
3a23d4458e | |
|
|
dc9a787025 | |
|
|
2bf7aac9d1 | |
|
|
fdcc4c1507 | |
|
|
75ce33c7b2 | |
|
|
90553267b0 | |
|
|
504b668ea6 | |
|
|
b0a3d6a3e7 | |
|
|
bf29f6dc11 | |
|
|
4fac7a1b20 | |
|
|
547e254a9b | |
|
|
66d8fc5c28 | |
|
|
054de3c236 | |
|
|
7371388e1d | |
|
|
cd47727c8b | |
|
|
609da65bb4 | |
|
|
e4a5bd7cb2 | |
|
|
a534fda855 | |
|
|
2075b3416f | |
|
|
442a969812 | |
|
|
bf732df591 | |
|
|
5cc3f27a5f | |
|
|
3c2caffffe | |
|
|
bc7895505e | |
|
|
b3f79ed8e7 | |
|
|
2ca7b2f7d7 | |
|
|
8b8939e219 | |
|
|
3a4fa325cc | |
|
|
d70924ace2 | |
|
|
ae219cb8f9 | |
|
|
f7ac724ea9 | |
|
|
c599d6a67e | |
|
|
10a55119a9 | |
|
|
5d2d544109 | |
|
|
37ba29482e | |
|
|
7bb0a1913e | |
|
|
bbceaa6b5e |
14
Makefile
14
Makefile
|
|
@ -95,14 +95,14 @@ TARGETS = $(PROGRAM_PREFIX)yosys$(EXE) $(PROGRAM_PREFIX)yosys-config
|
|||
PRETTY = 1
|
||||
SMALL = 0
|
||||
|
||||
# Unit test
|
||||
UNITESTPATH := tests/unit
|
||||
|
||||
all: top-all
|
||||
|
||||
YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST)))
|
||||
VPATH := $(YOSYS_SRC)
|
||||
|
||||
# Unit test
|
||||
UNITESTPATH := $(YOSYS_SRC)/tests/unit
|
||||
|
||||
export CXXSTD ?= c++17
|
||||
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include
|
||||
LIBS := $(LIBS) -lstdc++ -lm
|
||||
|
|
@ -161,7 +161,7 @@ ifeq ($(OS), Haiku)
|
|||
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.58+138
|
||||
YOSYS_VER := 0.58+162
|
||||
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
||||
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1)
|
||||
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)
|
||||
|
|
@ -1135,7 +1135,7 @@ DOC_TARGET ?= html
|
|||
docs: docs/prep
|
||||
$(Q) $(MAKE) -C docs $(DOC_TARGET)
|
||||
|
||||
clean: clean-py
|
||||
clean: clean-py clean-unit-test
|
||||
rm -rf share
|
||||
rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS)
|
||||
rm -f kernel/version_*.o kernel/version_*.cc
|
||||
|
|
@ -1150,7 +1150,7 @@ clean: clean-py
|
|||
rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff
|
||||
rm -f tests/tools/cmp_tbdata
|
||||
rm -f $(addsuffix /run-test.mk,$(MK_TEST_DIRS))
|
||||
-$(MAKE) -C docs clean
|
||||
-$(MAKE) -C $(YOSYS_SRC)/docs clean
|
||||
rm -rf docs/util/__pycache__
|
||||
rm -f libyosys.so
|
||||
|
||||
|
|
@ -1162,7 +1162,7 @@ clean-py:
|
|||
rm -rf kernel/*.pyh
|
||||
|
||||
clean-abc:
|
||||
$(MAKE) -C abc DEP= clean
|
||||
$(MAKE) -C $(YOSYS_SRC)/abc DEP= clean
|
||||
rm -f $(PROGRAM_PREFIX)yosys-abc$(EXE) $(PROGRAM_PREFIX)yosys-libabc.a abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a .git-abc-submodule-hash
|
||||
|
||||
mrproper: clean
|
||||
|
|
|
|||
2
abc
2
abc
|
|
@ -1 +1 @@
|
|||
Subproject commit fa186342baefea06e7c2aa13fe51f338ffc84912
|
||||
Subproject commit 1c5ed1ce378cc04beac30bb31abc4c37c8467042
|
||||
|
|
@ -108,22 +108,30 @@ IdString initial_id;
|
|||
|
||||
void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
|
||||
{
|
||||
const char *str = id.c_str();
|
||||
|
||||
if (*str == '$' && may_rename && !norename)
|
||||
auto_name_map[id] = auto_name_counter++;
|
||||
|
||||
if (str[0] != '\\' || str[1] != '_' || str[2] == 0)
|
||||
auto it = id.begin();
|
||||
auto it_end = id.end();
|
||||
if (it == it_end)
|
||||
return;
|
||||
|
||||
for (int i = 2; str[i] != 0; i++) {
|
||||
if (str[i] == '_' && str[i+1] == 0)
|
||||
if (*it == '$' && may_rename && !norename)
|
||||
auto_name_map[id] = auto_name_counter++;
|
||||
|
||||
if (*it != '\\' || *it != '_' || (it + 1) == it_end)
|
||||
return;
|
||||
|
||||
it += 2;
|
||||
auto start = it;
|
||||
while (it != it_end) {
|
||||
char ch = *it;
|
||||
if (ch == '_' && (it + 1) == it_end)
|
||||
continue;
|
||||
if (str[i] < '0' || str[i] > '9')
|
||||
if (ch < '0' || ch > '9')
|
||||
return;
|
||||
}
|
||||
|
||||
int num = atoi(str+2);
|
||||
std::string s;
|
||||
std::copy(start, it_end, std::back_inserter(s));
|
||||
int num = atoi(s.c_str());
|
||||
if (num >= auto_name_offset)
|
||||
auto_name_offset = num + 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Yosys and there are currently no plans to add support
|
|||
for them:
|
||||
|
||||
- Non-synthesizable language features as defined in
|
||||
IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002
|
||||
IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002
|
||||
|
||||
- The ``tri``, ``triand`` and ``trior`` net types
|
||||
|
||||
|
|
@ -356,21 +356,29 @@ from SystemVerilog:
|
|||
files being read into the same design afterwards.
|
||||
|
||||
- typedefs are supported (including inside packages)
|
||||
- type casts are currently not supported
|
||||
|
||||
- type casts are currently not supported
|
||||
|
||||
- enums are supported (including inside packages)
|
||||
- but are currently not strongly typed
|
||||
|
||||
- but are currently not strongly typed
|
||||
|
||||
- packed structs and unions are supported
|
||||
- arrays of packed structs/unions are currently not supported
|
||||
- structure literals are currently not supported
|
||||
|
||||
- arrays of packed structs/unions are currently not supported
|
||||
- structure literals are currently not supported
|
||||
|
||||
- multidimensional arrays are supported
|
||||
- array assignment of unpacked arrays is currently not supported
|
||||
- array literals are currently not supported
|
||||
|
||||
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
|
||||
ports are inputs or outputs are supported.
|
||||
- array assignment of unpacked arrays is currently not supported
|
||||
- array literals are currently not supported
|
||||
|
||||
- SystemVerilog interfaces (SVIs), including modports for specifying whether
|
||||
ports are inputs or outputs, are partially supported.
|
||||
|
||||
- interfaces must be provided as *named* arguments, not positional arguments.
|
||||
i.e. ``foo bar(.intf(intf0), .x(x));`` is supported but ``foo bar(intf0,
|
||||
x);`` is not.
|
||||
|
||||
- Assignments within expressions are supported.
|
||||
|
||||
|
|
|
|||
|
|
@ -709,6 +709,8 @@ int main(int argc, char **argv)
|
|||
total_ns += it.second->runtime_ns + 1;
|
||||
timedat.insert(make_tuple(it.second->runtime_ns + 1, it.second->call_counter, it.first));
|
||||
}
|
||||
timedat.insert(make_tuple(RTLIL::OwningIdString::garbage_collection_ns() + 1,
|
||||
RTLIL::OwningIdString::garbage_collection_count(), "id_gc"));
|
||||
|
||||
if (timing_details)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -606,7 +606,7 @@ void format_emit_idstring(std::string &result, std::string_view spec, int *dynam
|
|||
{
|
||||
if (spec == "%s") {
|
||||
// Format checking will have guaranteed num_dynamic_ints == 0.
|
||||
result += arg.c_str();
|
||||
arg.append_to(&result);
|
||||
return;
|
||||
}
|
||||
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg.c_str());
|
||||
|
|
|
|||
|
|
@ -42,6 +42,23 @@ std::map<std::string, Backend*> backend_register;
|
|||
|
||||
std::vector<std::string> Frontend::next_args;
|
||||
|
||||
bool GarbageCollectionGuard::is_enabled_ = true;
|
||||
|
||||
static bool garbage_collection_requested = false;
|
||||
|
||||
void request_garbage_collection()
|
||||
{
|
||||
garbage_collection_requested = true;
|
||||
}
|
||||
|
||||
void try_collect_garbage()
|
||||
{
|
||||
if (!GarbageCollectionGuard::is_enabled() || !garbage_collection_requested)
|
||||
return;
|
||||
garbage_collection_requested = false;
|
||||
RTLIL::OwningIdString::collect_garbage();
|
||||
}
|
||||
|
||||
Pass::Pass(std::string name, std::string short_help, source_location location) :
|
||||
pass_name(name), short_help(short_help), location(location)
|
||||
{
|
||||
|
|
@ -112,6 +129,11 @@ void Pass::post_execute(Pass::pre_post_exec_state_t state)
|
|||
int64_t time_ns = PerformanceTimer::query() - state.begin_ns;
|
||||
runtime_ns += time_ns;
|
||||
current_pass = state.parent_pass;
|
||||
subtract_from_current_runtime_ns(time_ns);
|
||||
}
|
||||
|
||||
void Pass::subtract_from_current_runtime_ns(int64_t time_ns)
|
||||
{
|
||||
if (current_pass)
|
||||
current_pass->runtime_ns -= time_ns;
|
||||
}
|
||||
|
|
@ -263,14 +285,19 @@ void Pass::call(RTLIL::Design *design, std::vector<std::string> args)
|
|||
|
||||
if (pass_register.count(args[0]) == 0)
|
||||
log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[0]);
|
||||
Pass *pass = pass_register[args[0]];
|
||||
|
||||
if (pass_register[args[0]]->experimental_flag)
|
||||
// Collect garbage before the next pass if requested. No need to collect garbage after the last pass.
|
||||
try_collect_garbage();
|
||||
GarbageCollectionGuard gc_guard(pass->allow_garbage_collection_during_pass());
|
||||
|
||||
if (pass->experimental_flag)
|
||||
log_experimental(args[0]);
|
||||
|
||||
size_t orig_sel_stack_pos = design->selection_stack.size();
|
||||
auto state = pass_register[args[0]]->pre_execute();
|
||||
pass_register[args[0]]->execute(args, design);
|
||||
pass_register[args[0]]->post_execute(state);
|
||||
auto state = pass->pre_execute();
|
||||
pass->execute(args, design);
|
||||
pass->post_execute(state);
|
||||
while (design->selection_stack.size() > orig_sel_stack_pos)
|
||||
design->pop_selection();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,30 @@ struct source_location { // dummy placeholder
|
|||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
// Track whether garbage collection is enabled. Garbage collection must be disabled
|
||||
// while any RTLIL objects (e.g. non-owning non-immortal IdStrings) exist outside Designs.
|
||||
// Garbage collection is disabled whenever any GarbageCollectionGuard(false) is on the
|
||||
// stack. These objects must be stack-allocated on the main thread.
|
||||
class GarbageCollectionGuard
|
||||
{
|
||||
bool was_enabled;
|
||||
static bool is_enabled_;
|
||||
public:
|
||||
GarbageCollectionGuard(bool allow) : was_enabled(is_enabled_) {
|
||||
is_enabled_ &= allow;
|
||||
}
|
||||
~GarbageCollectionGuard() {
|
||||
is_enabled_ = was_enabled;
|
||||
}
|
||||
static bool is_enabled() { return is_enabled_; }
|
||||
};
|
||||
|
||||
// Call from anywhere to request GC at the next safe point.
|
||||
void request_garbage_collection();
|
||||
|
||||
// GC if GarbageCollectionGuard::is_enabled() and GC was requested.
|
||||
void try_collect_garbage();
|
||||
|
||||
struct Pass
|
||||
{
|
||||
std::string pass_name, short_help;
|
||||
|
|
@ -71,6 +95,8 @@ struct Pass
|
|||
bool experimental_flag = false;
|
||||
bool internal_flag = false;
|
||||
|
||||
static void subtract_from_current_runtime_ns(int64_t time_ns);
|
||||
|
||||
void experimental() {
|
||||
experimental_flag = true;
|
||||
}
|
||||
|
|
@ -108,6 +134,10 @@ struct Pass
|
|||
virtual void on_register();
|
||||
virtual void on_shutdown();
|
||||
virtual bool replace_existing_pass() const { return false; }
|
||||
|
||||
// This should return false if the pass holds onto RTLIL objects outside a Design while it
|
||||
// calls nested passes. For safety, we default to assuming the worst.
|
||||
virtual bool allow_garbage_collection_during_pass() const { return false; }
|
||||
};
|
||||
|
||||
struct ScriptPass : Pass
|
||||
|
|
@ -126,6 +156,8 @@ struct ScriptPass : Pass
|
|||
void run_nocheck(std::string command, std::string info = std::string());
|
||||
void run_script(RTLIL::Design *design, std::string run_from = std::string(), std::string run_to = std::string());
|
||||
void help_script();
|
||||
|
||||
bool allow_garbage_collection_during_pass() const override { return true; }
|
||||
};
|
||||
|
||||
struct Frontend : Pass
|
||||
|
|
|
|||
245
kernel/rtlil.cc
245
kernel/rtlil.cc
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
|
|
@ -35,20 +36,14 @@ YOSYS_NAMESPACE_BEGIN
|
|||
|
||||
bool RTLIL::IdString::destruct_guard_ok = false;
|
||||
RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard;
|
||||
std::vector<char*> RTLIL::IdString::global_id_storage_;
|
||||
std::vector<RTLIL::IdString::Storage> RTLIL::IdString::global_id_storage_;
|
||||
std::unordered_map<std::string_view, int> RTLIL::IdString::global_id_index_;
|
||||
std::unordered_map<int, const std::string*> RTLIL::IdString::global_autoidx_id_prefix_storage_;
|
||||
std::unordered_map<int, char*> RTLIL::IdString::global_autoidx_id_storage_;
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
std::vector<uint32_t> RTLIL::IdString::global_refcount_storage_;
|
||||
std::unordered_map<int, 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) const RTLIL::IdString RTLIL::IDInternal::_id(RTLIL::StaticId::_id);
|
||||
#include "kernel/constids.inc"
|
||||
#undef X
|
||||
|
||||
static void populate(std::string_view name)
|
||||
{
|
||||
|
|
@ -57,21 +52,89 @@ static void populate(std::string_view name)
|
|||
name = name.substr(1);
|
||||
}
|
||||
RTLIL::IdString::global_id_index_.insert({name, GetSize(RTLIL::IdString::global_id_storage_)});
|
||||
RTLIL::IdString::global_id_storage_.push_back(const_cast<char*>(name.data()));
|
||||
RTLIL::IdString::global_id_storage_.push_back({const_cast<char*>(name.data()), GetSize(name)});
|
||||
}
|
||||
|
||||
void RTLIL::IdString::prepopulate()
|
||||
{
|
||||
int size = static_cast<short>(RTLIL::StaticId::STATIC_ID_END);
|
||||
global_id_storage_.reserve(size);
|
||||
RTLIL::IdString::global_id_storage_.push_back(const_cast<char*>(""));
|
||||
global_id_index_.reserve(size);
|
||||
global_refcount_storage_.resize(size, 1);
|
||||
RTLIL::IdString::global_id_index_.insert({"", 0});
|
||||
RTLIL::IdString::global_id_storage_.push_back({const_cast<char*>(""), 0});
|
||||
#define X(N) populate("\\" #N);
|
||||
#include "kernel/constids.inc"
|
||||
#undef X
|
||||
}
|
||||
|
||||
static std::optional<int> parse_autoidx(std::string_view v)
|
||||
{
|
||||
// autoidx values can never be <= 0, so there can never be a leading 0 digit.
|
||||
if (v.empty() || v[0] == '0')
|
||||
return std::nullopt;
|
||||
for (char ch : v) {
|
||||
if (ch < '0' || ch > '9')
|
||||
return std::nullopt;
|
||||
}
|
||||
int p_autoidx;
|
||||
if (std::from_chars(v.data(), v.data() + v.size(), p_autoidx).ec != std::errc())
|
||||
return std::nullopt;
|
||||
return p_autoidx;
|
||||
}
|
||||
|
||||
int RTLIL::IdString::really_insert(std::string_view p, std::unordered_map<std::string_view, int>::iterator &it)
|
||||
{
|
||||
ensure_prepopulated();
|
||||
|
||||
log_assert(p[0] == '$' || p[0] == '\\');
|
||||
for (char ch : p)
|
||||
if ((unsigned)ch <= (unsigned)' ')
|
||||
log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", ch, std::string(p).c_str());
|
||||
|
||||
if (p.substr(0, 6) == "$auto$") {
|
||||
size_t autoidx_pos = p.find_last_of('$') + 1;
|
||||
std::optional<int> p_autoidx = parse_autoidx(p.substr(autoidx_pos));
|
||||
if (p_autoidx.has_value()) {
|
||||
auto prefix_it = global_autoidx_id_prefix_storage_.find(-*p_autoidx);
|
||||
if (prefix_it != global_autoidx_id_prefix_storage_.end() && p.substr(0, autoidx_pos) == *prefix_it->second)
|
||||
return -*p_autoidx;
|
||||
// Ensure NEW_ID/NEW_ID_SUFFIX will not create collisions with the ID
|
||||
// we're about to create.
|
||||
autoidx = std::max(autoidx, *p_autoidx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
if (global_free_idx_list_.empty()) {
|
||||
log_assert(global_id_storage_.size() < 0x40000000);
|
||||
global_free_idx_list_.push_back(global_id_storage_.size());
|
||||
global_id_storage_.push_back({nullptr, 0});
|
||||
}
|
||||
|
||||
int idx = global_free_idx_list_.back();
|
||||
global_free_idx_list_.pop_back();
|
||||
#else
|
||||
int idx = global_id_storage_.size();
|
||||
global_id_index_[global_id_storage_.back()] = idx;
|
||||
#endif
|
||||
char* buf = static_cast<char*>(malloc(p.size() + 1));
|
||||
memcpy(buf, p.data(), p.size());
|
||||
buf[p.size()] = 0;
|
||||
global_id_storage_.at(idx) = {buf, GetSize(p)};
|
||||
global_id_index_.insert(it, {std::string_view(buf, p.size()), idx});
|
||||
|
||||
if (yosys_xtrace) {
|
||||
log("#X# New IdString '%s' with index %d.\n", global_id_storage_.at(idx).buf, idx);
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
}
|
||||
|
||||
#ifdef YOSYS_XTRACE_GET_PUT
|
||||
if (yosys_xtrace)
|
||||
log("#X# GET-BY-NAME '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx), idx, refcount(idx));
|
||||
#endif
|
||||
return idx;
|
||||
}
|
||||
|
||||
static constexpr bool check_well_known_id_order()
|
||||
{
|
||||
int size = sizeof(IdTable) / sizeof(IdTable[0]);
|
||||
|
|
@ -85,6 +148,156 @@ static constexpr bool check_well_known_id_order()
|
|||
// and in sorted ascii order, as required by the ID macro.
|
||||
static_assert(check_well_known_id_order());
|
||||
|
||||
struct IdStringCollector {
|
||||
void trace(IdString id) {
|
||||
live.insert(id.index_);
|
||||
}
|
||||
template <typename T> void trace(const T* v) {
|
||||
trace(*v);
|
||||
}
|
||||
template <typename V> void trace(const std::vector<V> &v) {
|
||||
for (const auto &element : v)
|
||||
trace(element);
|
||||
}
|
||||
template <typename K> void trace(const pool<K> &p) {
|
||||
for (const auto &element : p)
|
||||
trace(element);
|
||||
}
|
||||
template <typename K, typename V> void trace(const dict<K, V> &d) {
|
||||
for (const auto &[key, value] : d) {
|
||||
trace(key);
|
||||
trace(value);
|
||||
}
|
||||
}
|
||||
template <typename K, typename V> void trace_keys(const dict<K, V> &d) {
|
||||
for (const auto &[key, value] : d) {
|
||||
trace(key);
|
||||
}
|
||||
}
|
||||
template <typename K, typename V> void trace_values(const dict<K, V> &d) {
|
||||
for (const auto &[key, value] : d) {
|
||||
trace(value);
|
||||
}
|
||||
}
|
||||
template <typename K> void trace(const idict<K> &d) {
|
||||
for (const auto &element : d)
|
||||
trace(element);
|
||||
}
|
||||
|
||||
void trace(const RTLIL::Design &design) {
|
||||
trace_values(design.modules_);
|
||||
trace(design.selection_vars);
|
||||
}
|
||||
void trace(const RTLIL::Selection &selection_var) {
|
||||
trace(selection_var.selected_modules);
|
||||
trace(selection_var.selected_members);
|
||||
}
|
||||
void trace_named(const RTLIL::NamedObject named) {
|
||||
trace_keys(named.attributes);
|
||||
trace(named.name);
|
||||
}
|
||||
void trace(const RTLIL::Module &module) {
|
||||
trace_named(module);
|
||||
trace_values(module.wires_);
|
||||
trace_values(module.cells_);
|
||||
trace(module.avail_parameters);
|
||||
trace_keys(module.parameter_default_values);
|
||||
trace_values(module.memories);
|
||||
trace_values(module.processes);
|
||||
}
|
||||
void trace(const RTLIL::Wire &wire) {
|
||||
trace_named(wire);
|
||||
if (wire.known_driver())
|
||||
trace(wire.driverPort());
|
||||
}
|
||||
void trace(const RTLIL::Cell &cell) {
|
||||
trace_named(cell);
|
||||
trace(cell.type);
|
||||
trace_keys(cell.connections_);
|
||||
trace_keys(cell.parameters);
|
||||
}
|
||||
void trace(const RTLIL::Memory &mem) {
|
||||
trace_named(mem);
|
||||
}
|
||||
void trace(const RTLIL::Process &proc) {
|
||||
trace_named(proc);
|
||||
trace(proc.root_case);
|
||||
trace(proc.syncs);
|
||||
}
|
||||
void trace(const RTLIL::CaseRule &rule) {
|
||||
trace_keys(rule.attributes);
|
||||
trace(rule.switches);
|
||||
}
|
||||
void trace(const RTLIL::SwitchRule &rule) {
|
||||
trace_keys(rule.attributes);
|
||||
trace(rule.cases);
|
||||
}
|
||||
void trace(const RTLIL::SyncRule &rule) {
|
||||
trace(rule.mem_write_actions);
|
||||
}
|
||||
void trace(const RTLIL::MemWriteAction &action) {
|
||||
trace_keys(action.attributes);
|
||||
trace(action.memid);
|
||||
}
|
||||
|
||||
std::unordered_set<int> live;
|
||||
};
|
||||
|
||||
int64_t RTLIL::OwningIdString::gc_ns;
|
||||
int RTLIL::OwningIdString::gc_count;
|
||||
|
||||
void RTLIL::OwningIdString::collect_garbage()
|
||||
{
|
||||
int64_t start = PerformanceTimer::query();
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
IdStringCollector collector;
|
||||
for (auto &[idx, design] : *RTLIL::Design::get_all_designs()) {
|
||||
collector.trace(*design);
|
||||
}
|
||||
int size = GetSize(global_id_storage_);
|
||||
for (int i = static_cast<int>(StaticId::STATIC_ID_END); i < size; ++i) {
|
||||
RTLIL::IdString::Storage &storage = global_id_storage_.at(i);
|
||||
if (storage.buf == nullptr)
|
||||
continue;
|
||||
if (collector.live.find(i) != collector.live.end())
|
||||
continue;
|
||||
if (global_refcount_storage_.find(i) != global_refcount_storage_.end())
|
||||
continue;
|
||||
|
||||
if (yosys_xtrace) {
|
||||
log("#X# Removed IdString '%s' with index %d.\n", storage.buf, i);
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
}
|
||||
|
||||
global_id_index_.erase(std::string_view(storage.buf, storage.size));
|
||||
free(storage.buf);
|
||||
storage = {nullptr, 0};
|
||||
global_free_idx_list_.push_back(i);
|
||||
}
|
||||
|
||||
for (auto it = global_autoidx_id_prefix_storage_.begin(); it != global_autoidx_id_prefix_storage_.end();) {
|
||||
if (collector.live.find(it->first) != collector.live.end()) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
if (global_refcount_storage_.find(it->first) != global_refcount_storage_.end()) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
auto str_it = global_autoidx_id_storage_.find(it->first);
|
||||
if (str_it != global_autoidx_id_storage_.end()) {
|
||||
delete[] str_it->second;
|
||||
global_autoidx_id_storage_.erase(str_it);
|
||||
}
|
||||
it = global_autoidx_id_prefix_storage_.erase(it);
|
||||
}
|
||||
#endif
|
||||
int64_t time_ns = PerformanceTimer::query() - start;
|
||||
Pass::subtract_from_current_runtime_ns(time_ns);
|
||||
gc_ns += time_ns;
|
||||
++gc_count;
|
||||
}
|
||||
|
||||
dict<std::string, std::string> RTLIL::constpad;
|
||||
|
||||
static const pool<IdString> &builtin_ff_cell_types_internal() {
|
||||
|
|
@ -1083,9 +1296,7 @@ RTLIL::Design::Design()
|
|||
refcount_modules_ = 0;
|
||||
push_full_selection();
|
||||
|
||||
#ifdef YOSYS_ENABLE_PYTHON
|
||||
RTLIL::Design::get_all_designs()->insert(std::pair<unsigned int, RTLIL::Design*>(hashidx_, this));
|
||||
#endif
|
||||
}
|
||||
|
||||
RTLIL::Design::~Design()
|
||||
|
|
@ -1094,18 +1305,14 @@ RTLIL::Design::~Design()
|
|||
delete pr.second;
|
||||
for (auto n : bindings_)
|
||||
delete n;
|
||||
#ifdef YOSYS_ENABLE_PYTHON
|
||||
RTLIL::Design::get_all_designs()->erase(hashidx_);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef YOSYS_ENABLE_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()
|
||||
{
|
||||
|
|
|
|||
570
kernel/rtlil.h
570
kernel/rtlil.h
|
|
@ -120,26 +120,22 @@ namespace RTLIL
|
|||
struct Process;
|
||||
struct Binding;
|
||||
struct IdString;
|
||||
struct StaticIdString;
|
||||
struct OwningIdString;
|
||||
|
||||
typedef std::pair<SigSpec, SigSpec> SigSig;
|
||||
|
||||
struct StaticIdString {
|
||||
constexpr StaticIdString(StaticId id, const IdString &id_str) : id_str(id_str), id(id) {}
|
||||
constexpr inline operator const IdString &() const { return id_str; }
|
||||
constexpr inline int index() const { return static_cast<short>(id); }
|
||||
constexpr inline const IdString &id_string() const { return id_str; }
|
||||
|
||||
const IdString &id_str;
|
||||
const StaticId id;
|
||||
};
|
||||
};
|
||||
|
||||
struct RTLIL::IdString
|
||||
{
|
||||
struct Storage {
|
||||
char *buf;
|
||||
int size;
|
||||
|
||||
std::string_view str_view() const { return {buf, static_cast<size_t>(size)}; }
|
||||
};
|
||||
|
||||
#undef YOSYS_XTRACE_GET_PUT
|
||||
#undef YOSYS_SORT_ID_FREE_LIST
|
||||
#undef YOSYS_USE_STICKY_IDS
|
||||
#undef YOSYS_NO_IDS_REFCNT
|
||||
|
||||
// the global id string cache
|
||||
|
|
@ -150,200 +146,85 @@ struct RTLIL::IdString
|
|||
~destruct_guard_t() { destruct_guard_ok = false; }
|
||||
} destruct_guard;
|
||||
|
||||
static std::vector<char*> global_id_storage_;
|
||||
// String storage for non-autoidx IDs
|
||||
static std::vector<Storage> global_id_storage_;
|
||||
// Lookup table for non-autoidx IDs
|
||||
static std::unordered_map<std::string_view, int> global_id_index_;
|
||||
// Shared prefix string storage for autoidx IDs, which have negative
|
||||
// indices. Append the negated (i.e. positive) ID to this string to get
|
||||
// the real string. The prefix strings must live forever.
|
||||
static std::unordered_map<int, const std::string*> global_autoidx_id_prefix_storage_;
|
||||
// Explicit string storage for autoidx IDs
|
||||
static std::unordered_map<int, char*> global_autoidx_id_storage_;
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
// For prepopulated IdStrings, the refcount is meaningless since they
|
||||
// are never freed even if the refcount is zero. For code efficiency
|
||||
// we increment the refcount of prepopulated IdStrings like any other string,
|
||||
// but we never decrement the refcount or check whether it's zero.
|
||||
// So, make this unsigned because refcounts of preopulated IdStrings may overflow
|
||||
// and overflow of signed integers is undefined behavior.
|
||||
static std::vector<uint32_t> global_refcount_storage_;
|
||||
// All (index, refcount) pairs in this map have refcount > 0.
|
||||
static std::unordered_map<int, int> global_refcount_storage_;
|
||||
static std::vector<int> global_free_idx_list_;
|
||||
#endif
|
||||
|
||||
#ifdef YOSYS_USE_STICKY_IDS
|
||||
static int last_created_idx_ptr_;
|
||||
static int last_created_idx_[8];
|
||||
#endif
|
||||
static int refcount(int idx) {
|
||||
auto it = global_refcount_storage_.find(idx);
|
||||
if (it == global_refcount_storage_.end())
|
||||
return 0;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static inline void xtrace_db_dump()
|
||||
{
|
||||
#ifdef YOSYS_XTRACE_GET_PUT
|
||||
for (int idx = 0; idx < GetSize(global_id_storage_); idx++)
|
||||
{
|
||||
if (global_id_storage_.at(idx) == nullptr)
|
||||
if (global_id_storage_.at(idx).buf == nullptr)
|
||||
log("#X# DB-DUMP index %d: FREE\n", idx);
|
||||
else
|
||||
log("#X# DB-DUMP index %d: '%s' (ref %u)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx));
|
||||
log("#X# DB-DUMP index %d: '%s' (ref %u)\n", idx, refcount(idx).buf, refcount);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void checkpoint()
|
||||
{
|
||||
#ifdef YOSYS_USE_STICKY_IDS
|
||||
last_created_idx_ptr_ = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (last_created_idx_[i])
|
||||
put_reference(last_created_idx_[i]);
|
||||
last_created_idx_[i] = 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef YOSYS_SORT_ID_FREE_LIST
|
||||
std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater<int>());
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int get_reference(int idx)
|
||||
{
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
global_refcount_storage_[idx]++;
|
||||
#endif
|
||||
#ifdef YOSYS_XTRACE_GET_PUT
|
||||
if (yosys_xtrace && idx >= static_cast<short>(StaticId::STATIC_ID_END))
|
||||
log("#X# GET-BY-INDEX '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
|
||||
#endif
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int get_reference(const char *p)
|
||||
{
|
||||
return get_reference(std::string_view(p));
|
||||
}
|
||||
|
||||
static int get_reference(std::string_view p)
|
||||
static int insert(std::string_view p)
|
||||
{
|
||||
log_assert(destruct_guard_ok);
|
||||
|
||||
auto it = global_id_index_.find(p);
|
||||
if (it != global_id_index_.end()) {
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
global_refcount_storage_.at(it->second)++;
|
||||
#endif
|
||||
#ifdef YOSYS_XTRACE_GET_PUT
|
||||
if (yosys_xtrace)
|
||||
log("#X# GET-BY-NAME '%s' (index %d, refcount %u)\n", global_id_storage_.at(it->second), it->second, global_refcount_storage_.at(it->second));
|
||||
log("#X# GET-BY-NAME '%s' (index %d, refcount %u)\n", global_id_storage_.at(it->second).buf, it->second, refcount(it->second));
|
||||
#endif
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ensure_prepopulated();
|
||||
|
||||
if (p.empty())
|
||||
return 0;
|
||||
|
||||
log_assert(p[0] == '$' || p[0] == '\\');
|
||||
for (char ch : p)
|
||||
if ((unsigned)ch <= (unsigned)' ')
|
||||
log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", ch, std::string(p).c_str());
|
||||
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
if (global_free_idx_list_.empty()) {
|
||||
log_assert(global_id_storage_.size() < 0x40000000);
|
||||
global_free_idx_list_.push_back(global_id_storage_.size());
|
||||
global_id_storage_.push_back(nullptr);
|
||||
global_refcount_storage_.push_back(0);
|
||||
}
|
||||
|
||||
int idx = global_free_idx_list_.back();
|
||||
global_free_idx_list_.pop_back();
|
||||
char* buf = static_cast<char*>(malloc(p.size() + 1));
|
||||
memcpy(buf, p.data(), p.size());
|
||||
buf[p.size()] = 0;
|
||||
global_id_storage_.at(idx) = buf;
|
||||
global_id_index_.insert(it, {std::string_view(buf, p.size()), idx});
|
||||
global_refcount_storage_.at(idx)++;
|
||||
#else
|
||||
int idx = global_id_storage_.size();
|
||||
global_id_storage_.push_back(strdup(p));
|
||||
global_id_index_[global_id_storage_.back()] = idx;
|
||||
#endif
|
||||
|
||||
if (yosys_xtrace) {
|
||||
log("#X# New IdString '%s' with index %d.\n", global_id_storage_.at(idx), idx);
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
}
|
||||
|
||||
#ifdef YOSYS_XTRACE_GET_PUT
|
||||
if (yosys_xtrace)
|
||||
log("#X# GET-BY-NAME '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
|
||||
#endif
|
||||
|
||||
#ifdef YOSYS_USE_STICKY_IDS
|
||||
// Avoid Create->Delete->Create pattern
|
||||
if (last_created_idx_[last_created_idx_ptr_])
|
||||
put_reference(last_created_idx_[last_created_idx_ptr_]);
|
||||
last_created_idx_[last_created_idx_ptr_] = idx;
|
||||
get_reference(last_created_idx_[last_created_idx_ptr_]);
|
||||
last_created_idx_ptr_ = (last_created_idx_ptr_ + 1) & 7;
|
||||
#endif
|
||||
|
||||
return idx;
|
||||
return really_insert(p, it);
|
||||
}
|
||||
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
static inline void put_reference(int idx)
|
||||
{
|
||||
// put_reference() may be called from destructors after the destructor of
|
||||
// global_refcount_storage_ has been run. in this case we simply do nothing.
|
||||
if (idx < static_cast<short>(StaticId::STATIC_ID_END) || !destruct_guard_ok)
|
||||
return;
|
||||
|
||||
#ifdef YOSYS_XTRACE_GET_PUT
|
||||
if (yosys_xtrace) {
|
||||
log("#X# PUT '%s' (index %d, refcount %u)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t &refcount = global_refcount_storage_[idx];
|
||||
|
||||
if (--refcount > 0)
|
||||
return;
|
||||
|
||||
free_reference(idx);
|
||||
// Inserts an ID with string `prefix + autoidx', incrementing autoidx.
|
||||
// `prefix` must start with '$auto$', end with '$', and live forever.
|
||||
static IdString new_autoidx_with_prefix(const std::string *prefix) {
|
||||
int index = -(autoidx++);
|
||||
global_autoidx_id_prefix_storage_.insert({index, prefix});
|
||||
return from_index(index);
|
||||
}
|
||||
static inline void free_reference(int idx)
|
||||
{
|
||||
if (yosys_xtrace) {
|
||||
log("#X# Removed IdString '%s' with index %d.\n", global_id_storage_.at(idx), idx);
|
||||
log_backtrace("-X- ", yosys_xtrace-1);
|
||||
}
|
||||
log_assert(idx >= static_cast<short>(StaticId::STATIC_ID_END));
|
||||
|
||||
global_id_index_.erase(global_id_storage_.at(idx));
|
||||
free(global_id_storage_.at(idx));
|
||||
global_id_storage_.at(idx) = nullptr;
|
||||
global_free_idx_list_.push_back(idx);
|
||||
}
|
||||
#else
|
||||
static inline void put_reference(int) { }
|
||||
#endif
|
||||
|
||||
// the actual IdString object is just is a single int
|
||||
|
||||
int index_;
|
||||
|
||||
inline IdString() : index_(0) { }
|
||||
inline IdString(const char *str) : index_(get_reference(str)) { }
|
||||
inline IdString(const IdString &str) : index_(get_reference(str.index_)) { }
|
||||
constexpr inline IdString() : index_(0) { }
|
||||
inline IdString(const char *str) : index_(insert(std::string_view(str))) { }
|
||||
constexpr inline IdString(const IdString &str) : index_(str.index_) { }
|
||||
inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; }
|
||||
inline IdString(const std::string &str) : index_(get_reference(std::string_view(str))) { }
|
||||
inline IdString(std::string_view str) : index_(get_reference(str)) { }
|
||||
inline IdString(StaticId id) : index_(static_cast<short>(id)) {}
|
||||
inline ~IdString() { put_reference(index_); }
|
||||
inline IdString(const std::string &str) : index_(insert(std::string_view(str))) { }
|
||||
inline IdString(std::string_view str) : index_(insert(str)) { }
|
||||
constexpr inline IdString(StaticId id) : index_(static_cast<short>(id)) {}
|
||||
|
||||
inline void operator=(const IdString &rhs) {
|
||||
put_reference(index_);
|
||||
index_ = get_reference(rhs.index_);
|
||||
}
|
||||
|
||||
inline void operator=(IdString &&rhs) {
|
||||
put_reference(index_);
|
||||
index_ = rhs.index_;
|
||||
rhs.index_ = 0;
|
||||
}
|
||||
IdString &operator=(const IdString &rhs) = default;
|
||||
|
||||
inline void operator=(const char *rhs) {
|
||||
IdString id(rhs);
|
||||
|
|
@ -358,11 +239,156 @@ struct RTLIL::IdString
|
|||
constexpr inline const IdString &id_string() const { return *this; }
|
||||
|
||||
inline const char *c_str() const {
|
||||
return global_id_storage_.at(index_);
|
||||
if (index_ >= 0)
|
||||
return global_id_storage_.at(index_).buf;
|
||||
auto it = global_autoidx_id_storage_.find(index_);
|
||||
if (it != global_autoidx_id_storage_.end())
|
||||
return it->second;
|
||||
|
||||
const std::string &prefix = *global_autoidx_id_prefix_storage_.at(index_);
|
||||
std::string suffix = std::to_string(-index_);
|
||||
char *c = new char[prefix.size() + suffix.size() + 1];
|
||||
memcpy(c, prefix.data(), prefix.size());
|
||||
memcpy(c + prefix.size(), suffix.c_str(), suffix.size() + 1);
|
||||
global_autoidx_id_storage_.insert(it, {index_, c});
|
||||
return c;
|
||||
}
|
||||
|
||||
inline std::string str() const {
|
||||
return std::string(global_id_storage_.at(index_));
|
||||
std::string result;
|
||||
append_to(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void append_to(std::string *out) const {
|
||||
if (index_ >= 0) {
|
||||
*out += global_id_storage_.at(index_).str_view();
|
||||
return;
|
||||
}
|
||||
*out += *global_autoidx_id_prefix_storage_.at(index_);
|
||||
*out += std::to_string(-index_);
|
||||
}
|
||||
|
||||
class Substrings {
|
||||
std::string_view first_;
|
||||
int suffix_number;
|
||||
char buf[10];
|
||||
public:
|
||||
Substrings(const Storage &storage) : first_(storage.str_view()), suffix_number(-1) {}
|
||||
// suffix_number must be non-negative
|
||||
Substrings(const std::string *prefix, int suffix_number)
|
||||
: first_(*prefix), suffix_number(suffix_number) {}
|
||||
std::string_view first() { return first_; }
|
||||
std::optional<std::string_view> next() {
|
||||
if (suffix_number < 0)
|
||||
return std::nullopt;
|
||||
int i = sizeof(buf);
|
||||
do {
|
||||
--i;
|
||||
buf[i] = (suffix_number % 10) + '0';
|
||||
suffix_number /= 10;
|
||||
} while (suffix_number > 0);
|
||||
suffix_number = -1;
|
||||
return std::string_view(buf + i, sizeof(buf) - i);
|
||||
}
|
||||
};
|
||||
|
||||
class const_iterator {
|
||||
const std::string *prefix;
|
||||
std::string suffix;
|
||||
const char *c_str;
|
||||
int c_str_len;
|
||||
// When this is INT_MAX it's the generic "end" value.
|
||||
int index;
|
||||
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = char;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const char*;
|
||||
using reference = const char&;
|
||||
|
||||
const_iterator(const Storage &storage) : prefix(nullptr), c_str(storage.buf), c_str_len(storage.size), index(0) {}
|
||||
const_iterator(const std::string *prefix, int number) :
|
||||
prefix(prefix), suffix(std::to_string(number)), c_str(nullptr), c_str_len(0), index(0) {}
|
||||
// Construct end-marker
|
||||
const_iterator() : prefix(nullptr), c_str(nullptr), c_str_len(0), index(INT_MAX) {}
|
||||
|
||||
int size() const {
|
||||
if (c_str != nullptr)
|
||||
return c_str_len;
|
||||
return GetSize(*prefix) + GetSize(suffix);
|
||||
}
|
||||
|
||||
char operator*() const {
|
||||
if (c_str != nullptr)
|
||||
return c_str[index];
|
||||
int prefix_size = GetSize(*prefix);
|
||||
if (index < prefix_size)
|
||||
return prefix->at(index);
|
||||
return suffix[index - prefix_size];
|
||||
}
|
||||
|
||||
const_iterator& operator++() { ++index; return *this; }
|
||||
const_iterator operator++(int) { const_iterator result(*this); ++index; return result; }
|
||||
const_iterator& operator+=(int i) { index += i; return *this; }
|
||||
|
||||
const_iterator operator+(int add) {
|
||||
const_iterator result = *this;
|
||||
result += add;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const const_iterator& other) const {
|
||||
return index == other.index || (other.index == INT_MAX && index == size())
|
||||
|| (index == INT_MAX && other.index == other.size());
|
||||
}
|
||||
bool operator!=(const const_iterator& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
const_iterator begin() const {
|
||||
if (index_ >= 0) {
|
||||
return const_iterator(global_id_storage_.at(index_));
|
||||
}
|
||||
return const_iterator(global_autoidx_id_prefix_storage_.at(index_), -index_);
|
||||
}
|
||||
const_iterator end() const {
|
||||
return const_iterator();
|
||||
}
|
||||
|
||||
Substrings substrings() const {
|
||||
if (index_ >= 0) {
|
||||
return Substrings(global_id_storage_.at(index_));
|
||||
}
|
||||
return Substrings(global_autoidx_id_prefix_storage_.at(index_), -index_);
|
||||
}
|
||||
|
||||
inline bool lt_by_name(const IdString &rhs) const {
|
||||
Substrings lhs_it = substrings();
|
||||
Substrings rhs_it = rhs.substrings();
|
||||
std::string_view lhs_substr = lhs_it.first();
|
||||
std::string_view rhs_substr = rhs_it.first();
|
||||
while (true) {
|
||||
int min = std::min(GetSize(lhs_substr), GetSize(rhs_substr));
|
||||
int diff = memcmp(lhs_substr.data(), rhs_substr.data(), min);
|
||||
if (diff != 0)
|
||||
return diff < 0;
|
||||
lhs_substr = lhs_substr.substr(min);
|
||||
rhs_substr = rhs_substr.substr(min);
|
||||
if (rhs_substr.empty()) {
|
||||
if (std::optional<std::string_view> s = rhs_it.next())
|
||||
rhs_substr = *s;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
if (lhs_substr.empty()) {
|
||||
if (std::optional<std::string_view> s = lhs_it.next())
|
||||
lhs_substr = *s;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator<(const IdString &rhs) const {
|
||||
|
|
@ -371,8 +397,6 @@ struct RTLIL::IdString
|
|||
|
||||
inline bool operator==(const IdString &rhs) const { return index_ == rhs.index_; }
|
||||
inline bool operator!=(const IdString &rhs) const { return index_ != rhs.index_; }
|
||||
inline bool operator==(const StaticIdString &rhs) const;
|
||||
inline bool operator!=(const StaticIdString &rhs) const;
|
||||
|
||||
// The methods below are just convenience functions for better compatibility with std::string.
|
||||
|
||||
|
|
@ -383,45 +407,84 @@ struct RTLIL::IdString
|
|||
bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; }
|
||||
|
||||
char operator[](size_t i) const {
|
||||
const char *p = c_str();
|
||||
if (index_ >= 0) {
|
||||
const Storage &storage = global_id_storage_.at(index_);
|
||||
#ifndef NDEBUG
|
||||
for (; i != 0; i--, p++)
|
||||
log_assert(*p != 0);
|
||||
return *p;
|
||||
#else
|
||||
return *(p + i);
|
||||
log_assert(static_cast<int>(i) < storage.size);
|
||||
#endif
|
||||
return *(storage.buf + i);
|
||||
}
|
||||
const std::string &id_start = *global_autoidx_id_prefix_storage_.at(index_);
|
||||
if (i < id_start.size())
|
||||
return id_start[i];
|
||||
i -= id_start.size();
|
||||
std::string suffix = std::to_string(-index_);
|
||||
#ifndef NDEBUG
|
||||
// Allow indexing to access the trailing null.
|
||||
log_assert(i <= suffix.size());
|
||||
#endif
|
||||
return suffix[i];
|
||||
}
|
||||
|
||||
std::string substr(size_t pos = 0, size_t len = std::string::npos) const {
|
||||
if (len == std::string::npos || len >= strlen(c_str() + pos))
|
||||
return std::string(c_str() + pos);
|
||||
else
|
||||
return std::string(c_str() + pos, len);
|
||||
std::string result;
|
||||
const_iterator it = begin() + pos;
|
||||
const_iterator end_it = end();
|
||||
if (len != std::string::npos && len < it.size() - pos) {
|
||||
end_it = it + len;
|
||||
}
|
||||
std::copy(it, end_it, std::back_inserter(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
int compare(size_t pos, size_t len, const char* s) const {
|
||||
return strncmp(c_str()+pos, s, len);
|
||||
const_iterator it = begin() + pos;
|
||||
const_iterator end_it = end();
|
||||
while (len > 0 && *s != 0 && it != end_it) {
|
||||
int diff = *it - *s;
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
++it;
|
||||
++s;
|
||||
--len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool begins_with(const char* prefix) const {
|
||||
size_t len = strlen(prefix);
|
||||
if (size() < len) return false;
|
||||
return compare(0, len, prefix) == 0;
|
||||
bool begins_with(std::string_view prefix) const {
|
||||
Substrings it = substrings();
|
||||
std::string_view substr = it.first();
|
||||
while (true) {
|
||||
int min = std::min(GetSize(substr), GetSize(prefix));
|
||||
if (memcmp(substr.data(), prefix.data(), min) != 0)
|
||||
return false;
|
||||
prefix = prefix.substr(min);
|
||||
if (prefix.empty())
|
||||
return true;
|
||||
substr = substr.substr(min);
|
||||
if (substr.empty()) {
|
||||
if (std::optional<std::string_view> s = it.next())
|
||||
substr = *s;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ends_with(const char* suffix) const {
|
||||
size_t len = strlen(suffix);
|
||||
if (size() < len) return false;
|
||||
return compare(size()-len, len, suffix) == 0;
|
||||
bool ends_with(std::string_view suffix) const {
|
||||
size_t sz = size();
|
||||
if (sz < suffix.size()) return false;
|
||||
return compare(sz - suffix.size(), suffix.size(), suffix.data()) == 0;
|
||||
}
|
||||
|
||||
bool contains(const char* str) const {
|
||||
return strstr(c_str(), str);
|
||||
bool contains(std::string_view s) const {
|
||||
if (index_ >= 0)
|
||||
return global_id_storage_.at(index_).str_view().find(s) != std::string::npos;
|
||||
return str().find(s) != std::string::npos;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return strlen(c_str());
|
||||
return begin().size();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
|
|
@ -458,7 +521,6 @@ struct RTLIL::IdString
|
|||
}
|
||||
|
||||
bool in(const IdString &rhs) const { return *this == rhs; }
|
||||
bool in(const StaticIdString &rhs) const { return *this == rhs; }
|
||||
bool in(const char *rhs) const { return *this == rhs; }
|
||||
bool in(const std::string &rhs) const { return *this == rhs; }
|
||||
inline bool in(const pool<IdString> &rhs) const;
|
||||
|
|
@ -468,6 +530,14 @@ struct RTLIL::IdString
|
|||
|
||||
private:
|
||||
static void prepopulate();
|
||||
static int really_insert(std::string_view p, std::unordered_map<std::string_view, int>::iterator &it);
|
||||
|
||||
protected:
|
||||
static IdString from_index(int index) {
|
||||
IdString result;
|
||||
result.index_ = index;
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
static void ensure_prepopulated() {
|
||||
|
|
@ -476,6 +546,95 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
struct RTLIL::OwningIdString : public RTLIL::IdString {
|
||||
inline OwningIdString() { }
|
||||
inline OwningIdString(const OwningIdString &str) : IdString(str) { get_reference(); }
|
||||
inline OwningIdString(const char *str) : IdString(str) { get_reference(); }
|
||||
inline OwningIdString(const IdString &str) : IdString(str) { get_reference(); }
|
||||
inline OwningIdString(IdString &&str) : IdString(str) { get_reference(); }
|
||||
inline OwningIdString(const std::string &str) : IdString(str) { get_reference(); }
|
||||
inline OwningIdString(std::string_view str) : IdString(str) { get_reference(); }
|
||||
inline OwningIdString(StaticId id) : IdString(id) {}
|
||||
inline ~OwningIdString() {
|
||||
put_reference();
|
||||
}
|
||||
|
||||
inline OwningIdString &operator=(const OwningIdString &rhs) {
|
||||
put_reference();
|
||||
index_ = rhs.index_;
|
||||
get_reference();
|
||||
return *this;
|
||||
}
|
||||
inline OwningIdString &operator=(const IdString &rhs) {
|
||||
put_reference();
|
||||
index_ = rhs.index_;
|
||||
get_reference();
|
||||
return *this;
|
||||
}
|
||||
inline OwningIdString &operator=(OwningIdString &&rhs) {
|
||||
std::swap(index_, rhs.index_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Collect all non-owning references.
|
||||
static void collect_garbage();
|
||||
static int64_t garbage_collection_ns() { return gc_ns; }
|
||||
static int garbage_collection_count() { return gc_count; }
|
||||
|
||||
// Used by the ID() macro to create an IdString with no destructor whose string will
|
||||
// never be released. If ID() creates a closure-static `OwningIdString` then
|
||||
// initialization of the static registers its destructor to run at exit, which is
|
||||
// wasteful.
|
||||
static IdString immortal(const char* str) {
|
||||
IdString result(str);
|
||||
get_reference(result.index_);
|
||||
return result;
|
||||
}
|
||||
private:
|
||||
static int64_t gc_ns;
|
||||
static int gc_count;
|
||||
|
||||
void get_reference()
|
||||
{
|
||||
get_reference(index_);
|
||||
}
|
||||
static void get_reference(int idx)
|
||||
{
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
if (idx < static_cast<short>(StaticId::STATIC_ID_END))
|
||||
return;
|
||||
auto it = global_refcount_storage_.find(idx);
|
||||
if (it == global_refcount_storage_.end())
|
||||
global_refcount_storage_.insert(it, {idx, 1});
|
||||
else
|
||||
++it->second;
|
||||
#endif
|
||||
#ifdef YOSYS_XTRACE_GET_PUT
|
||||
if (yosys_xtrace && idx >= static_cast<short>(StaticId::STATIC_ID_END))
|
||||
log("#X# GET-BY-INDEX '%s' (index %d, refcount %u)\n", from_index(idx), idx, refcount(idx));
|
||||
#endif
|
||||
}
|
||||
|
||||
void put_reference()
|
||||
{
|
||||
#ifndef YOSYS_NO_IDS_REFCNT
|
||||
// put_reference() may be called from destructors after the destructor of
|
||||
// global_refcount_storage_ has been run. in this case we simply do nothing.
|
||||
if (index_ < static_cast<short>(StaticId::STATIC_ID_END) || !destruct_guard_ok)
|
||||
return;
|
||||
#ifdef YOSYS_XTRACE_GET_PUT
|
||||
if (yosys_xtrace)
|
||||
log("#X# PUT '%s' (index %d, refcount %u)\n", from_index(index_), index_, refcount(index_));
|
||||
#endif
|
||||
auto it = global_refcount_storage_.find(index_);
|
||||
log_assert(it != global_refcount_storage_.end() && it->second >= 1);
|
||||
if (--it->second == 0) {
|
||||
global_refcount_storage_.erase(it);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
namespace hashlib {
|
||||
template <>
|
||||
struct hash_ops<RTLIL::IdString> {
|
||||
|
|
@ -501,21 +660,9 @@ inline bool RTLIL::IdString::in(const pool<IdString> &rhs) const { return rhs.co
|
|||
[[deprecated]]
|
||||
inline bool RTLIL::IdString::in(const pool<IdString> &&rhs) const { return rhs.count(*this) != 0; }
|
||||
|
||||
inline bool RTLIL::IdString::operator==(const RTLIL::StaticIdString &rhs) const {
|
||||
return index_ == rhs.index();
|
||||
}
|
||||
inline bool RTLIL::IdString::operator!=(const RTLIL::StaticIdString &rhs) const {
|
||||
return index_ != rhs.index();
|
||||
}
|
||||
|
||||
namespace RTLIL {
|
||||
namespace IDInternal {
|
||||
#define X(_id) extern const IdString _id;
|
||||
#include "kernel/constids.inc"
|
||||
#undef X
|
||||
}
|
||||
namespace ID {
|
||||
#define X(_id) constexpr StaticIdString _id(StaticId::_id, IDInternal::_id);
|
||||
#define X(_id) constexpr IdString _id(StaticId::_id);
|
||||
#include "kernel/constids.inc"
|
||||
#undef X
|
||||
}
|
||||
|
|
@ -523,7 +670,7 @@ namespace RTLIL {
|
|||
|
||||
struct IdTableEntry {
|
||||
const std::string_view name;
|
||||
const RTLIL::StaticIdString static_id;
|
||||
const RTLIL::IdString static_id;
|
||||
};
|
||||
|
||||
constexpr IdTableEntry IdTable[] = {
|
||||
|
|
@ -556,15 +703,15 @@ constexpr int lookup_well_known_id(std::string_view name)
|
|||
//
|
||||
// sed -i.orig -r 's/"\\\\([a-zA-Z0-9_]+)"/ID(\1)/g; s/"(\$[a-zA-Z0-9_]+)"/ID(\1)/g;' <filename>
|
||||
//
|
||||
typedef const RTLIL::IdString &IDMacroHelperFunc();
|
||||
typedef RTLIL::IdString IDMacroHelperFunc();
|
||||
|
||||
template <int IdTableIndex> struct IDMacroHelper {
|
||||
static constexpr RTLIL::StaticIdString eval(IDMacroHelperFunc) {
|
||||
static constexpr RTLIL::IdString eval(IDMacroHelperFunc) {
|
||||
return IdTable[IdTableIndex].static_id;
|
||||
}
|
||||
};
|
||||
template <> struct IDMacroHelper<-1> {
|
||||
static constexpr const RTLIL::IdString &eval(IDMacroHelperFunc func) {
|
||||
static constexpr RTLIL::IdString eval(IDMacroHelperFunc func) {
|
||||
return func();
|
||||
}
|
||||
};
|
||||
|
|
@ -574,9 +721,10 @@ template <> struct IDMacroHelper<-1> {
|
|||
YOSYS_NAMESPACE_PREFIX IDMacroHelper< \
|
||||
YOSYS_NAMESPACE_PREFIX lookup_well_known_id(#_id) \
|
||||
>::eval([]() \
|
||||
-> const YOSYS_NAMESPACE_PREFIX RTLIL::IdString & { \
|
||||
-> YOSYS_NAMESPACE_PREFIX RTLIL::IdString { \
|
||||
const char *p = "\\" #_id, *q = p[1] == '$' ? p+1 : p; \
|
||||
static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); \
|
||||
static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id = \
|
||||
YOSYS_NAMESPACE_PREFIX RTLIL::OwningIdString::immortal(q); \
|
||||
return id; \
|
||||
})
|
||||
|
||||
|
|
@ -620,13 +768,13 @@ namespace RTLIL {
|
|||
|
||||
template <typename T> struct sort_by_name_str {
|
||||
bool operator()(T *a, T *b) const {
|
||||
return strcmp(a->name.c_str(), b->name.c_str()) < 0;
|
||||
return a->name.lt_by_name(b->name);
|
||||
}
|
||||
};
|
||||
|
||||
struct sort_by_id_str {
|
||||
bool operator()(const RTLIL::IdString &a, const RTLIL::IdString &b) const {
|
||||
return strcmp(a.c_str(), b.c_str()) < 0;
|
||||
return a.lt_by_name(b);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1695,9 +1843,7 @@ struct RTLIL::Design
|
|||
// returns all selected unboxed whole modules, warning the user if any
|
||||
// partially selected or boxed modules have been ignored
|
||||
std::vector<RTLIL::Module*> selected_unboxed_whole_modules_warn() const { return selected_modules(SELECT_WHOLE_WARN, SB_UNBOXED_WARN); }
|
||||
#ifdef YOSYS_ENABLE_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Design*> *get_all_designs(void);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct RTLIL::Module : public RTLIL::NamedObject
|
||||
|
|
|
|||
|
|
@ -295,35 +295,35 @@ void yosys_shutdown()
|
|||
#endif
|
||||
}
|
||||
|
||||
RTLIL::IdString new_id(std::string file, int line, std::string func)
|
||||
const std::string *create_id_prefix(std::string_view file, int line, std::string_view func)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
size_t pos = file.find_last_of("/\\");
|
||||
#else
|
||||
size_t pos = file.find_last_of('/');
|
||||
#endif
|
||||
if (pos != std::string::npos)
|
||||
if (pos != std::string_view::npos)
|
||||
file = file.substr(pos+1);
|
||||
|
||||
pos = func.find_last_of(':');
|
||||
if (pos != std::string::npos)
|
||||
if (pos != std::string_view::npos)
|
||||
func = func.substr(pos+1);
|
||||
|
||||
return stringf("$auto$%s:%d:%s$%d", file, line, func, autoidx++);
|
||||
return new std::string(stringf("$auto$%s:%d:%s$", file, line, func));
|
||||
}
|
||||
|
||||
RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix)
|
||||
RTLIL::IdString new_id_suffix(std::string_view file, int line, std::string_view func, std::string_view suffix)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
size_t pos = file.find_last_of("/\\");
|
||||
#else
|
||||
size_t pos = file.find_last_of('/');
|
||||
#endif
|
||||
if (pos != std::string::npos)
|
||||
if (pos != std::string_view::npos)
|
||||
file = file.substr(pos+1);
|
||||
|
||||
pos = func.find_last_of(':');
|
||||
if (pos != std::string::npos)
|
||||
if (pos != std::string_view::npos)
|
||||
func = func.substr(pos+1);
|
||||
|
||||
return stringf("$auto$%s:%d:%s$%s$%d", file, line, func, suffix, autoidx++);
|
||||
|
|
|
|||
|
|
@ -271,11 +271,14 @@ extern int autoidx;
|
|||
extern int yosys_xtrace;
|
||||
extern bool yosys_write_versions;
|
||||
|
||||
RTLIL::IdString new_id(std::string file, int line, std::string func);
|
||||
RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix);
|
||||
const std::string *create_id_prefix(std::string_view file, int line, std::string_view func);
|
||||
RTLIL::IdString new_id_suffix(std::string_view file, int line, std::string_view func, std::string_view suffix);
|
||||
|
||||
#define NEW_ID \
|
||||
YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__)
|
||||
YOSYS_NAMESPACE_PREFIX RTLIL::IdString::new_autoidx_with_prefix([](std::string_view func) -> const std::string * { \
|
||||
static const std::string *prefix = create_id_prefix(__FILE__, __LINE__, func); \
|
||||
return prefix; \
|
||||
}(__FUNCTION__))
|
||||
#define NEW_ID_SUFFIX(suffix) \
|
||||
YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix)
|
||||
|
||||
|
|
|
|||
|
|
@ -156,6 +156,21 @@ std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
|
|||
return basicType;
|
||||
}
|
||||
|
||||
// Try to read an IdString as a numbered connection name ("$123" or similar),
|
||||
// writing the result to dst. If the string isn't of the right format, ignore
|
||||
// dst and return false.
|
||||
bool read_id_num(RTLIL::IdString str, int *dst)
|
||||
{
|
||||
log_assert(dst);
|
||||
|
||||
const char *c_str = str.c_str();
|
||||
if (c_str[0] != '$' || !('0' <= c_str[1] && c_str[1] <= '9'))
|
||||
return false;
|
||||
|
||||
*dst = atoi(c_str + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// A helper struct for expanding a module's interface connections in expand_module
|
||||
struct IFExpander
|
||||
{
|
||||
|
|
@ -283,15 +298,42 @@ struct IFExpander
|
|||
RTLIL::IdString conn_name,
|
||||
const RTLIL::SigSpec &conn_signals)
|
||||
{
|
||||
// Check if the connection is present as an interface in the sub-module's port list
|
||||
const RTLIL::Wire *wire = submodule.wire(conn_name);
|
||||
if (!wire || !wire->get_bool_attribute(ID::is_interface))
|
||||
// Does the connection look like an interface
|
||||
if (
|
||||
conn_signals.size() != 1 ||
|
||||
conn_signals[0].wire == nullptr ||
|
||||
conn_signals[0].wire->get_bool_attribute(ID::is_interface) == false ||
|
||||
conn_signals[0].wire->name.str().find("$dummywireforinterface") != 0
|
||||
)
|
||||
return;
|
||||
|
||||
// Check if the connection is present as an interface in the sub-module's port list
|
||||
int id;
|
||||
if (read_id_num(conn_name, &id)) {
|
||||
/* Interface expansion is incompatible with positional arguments
|
||||
* during expansion, the port list gets each interface signal
|
||||
* inserted after the interface itself which means that the argument
|
||||
* positions in the parent module no longer match.
|
||||
*
|
||||
* Supporting this would require expanding the interfaces in the
|
||||
* parent module, renumbering the arguments to match, and then
|
||||
* iterating over the ports list to find the matching interface
|
||||
* (refactoring on_interface to accept different conn_names on the
|
||||
* parent and child).
|
||||
*/
|
||||
log_error("Unable to connect `%s' to submodule `%s' with positional interface argument `%s'!\n",
|
||||
module.name,
|
||||
submodule.name,
|
||||
conn_signals[0].wire->name.str().substr(23)
|
||||
);
|
||||
} else {
|
||||
// Lookup connection by name
|
||||
const RTLIL::Wire *wire = submodule.wire(conn_name);
|
||||
if (!wire || !wire->get_bool_attribute(ID::is_interface))
|
||||
return;
|
||||
}
|
||||
// If the connection looks like an interface, handle it.
|
||||
const auto &bits = conn_signals;
|
||||
if (bits.size() == 1 && bits[0].wire->get_bool_attribute(ID::is_interface))
|
||||
on_interface(submodule, conn_name, conn_signals);
|
||||
on_interface(submodule, conn_name, conn_signals);
|
||||
}
|
||||
|
||||
// Iterate over the connections in a cell, tracking any interface
|
||||
|
|
@ -376,21 +418,6 @@ RTLIL::Module *get_module(RTLIL::Design &design,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Try to read an IdString as a numbered connection name ("$123" or similar),
|
||||
// writing the result to dst. If the string isn't of the right format, ignore
|
||||
// dst and return false.
|
||||
bool read_id_num(RTLIL::IdString str, int *dst)
|
||||
{
|
||||
log_assert(dst);
|
||||
|
||||
const char *c_str = str.c_str();
|
||||
if (c_str[0] != '$' || !('0' <= c_str[1] && c_str[1] <= '9'))
|
||||
return false;
|
||||
|
||||
*dst = atoi(c_str + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check that the connections on the cell match those that are defined
|
||||
// on the type: each named connection should match the name of a port
|
||||
// and each positional connection should have an index smaller than
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo
|
|||
if (attrs1 != attrs2)
|
||||
return attrs2 > attrs1;
|
||||
|
||||
return strcmp(w2->name.c_str(), w1->name.c_str()) < 0;
|
||||
return w2->name.lt_by_name(w1->name);
|
||||
}
|
||||
|
||||
bool check_public_name(RTLIL::IdString id)
|
||||
|
|
@ -722,6 +722,8 @@ struct OptCleanPass : public Pass {
|
|||
ct_reg.clear();
|
||||
ct_all.clear();
|
||||
log_pop();
|
||||
|
||||
request_garbage_collection();
|
||||
}
|
||||
} OptCleanPass;
|
||||
|
||||
|
|
@ -784,6 +786,8 @@ struct CleanPass : public Pass {
|
|||
keep_cache.reset();
|
||||
ct_reg.clear();
|
||||
ct_all.clear();
|
||||
|
||||
request_garbage_collection();
|
||||
}
|
||||
} CleanPass;
|
||||
|
||||
|
|
|
|||
|
|
@ -611,7 +611,7 @@ std::string AbcModuleState::remap_name(RTLIL::IdString abc_name, RTLIL::Wire **o
|
|||
}
|
||||
}
|
||||
}
|
||||
return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1);
|
||||
return stringf("$abc$%d$%s", map_autoidx, abc_name.substr(1));
|
||||
}
|
||||
|
||||
void AbcModuleState::dump_loop_graph(FILE *f, int &nr, dict<int, pool<int>> &edges, pool<int> &workpool, std::vector<int> &in_counts)
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std
|
|||
return false;
|
||||
}
|
||||
|
||||
auto pin_names = pool<std::string>{};
|
||||
auto pin_names = std::unordered_set<std::string>{};
|
||||
tree.get_pin_names(pin_names);
|
||||
|
||||
// from the `ff` block, we know the flop output signal name for loopback.
|
||||
|
|
@ -156,7 +156,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std
|
|||
auto pins = std::vector<std::string>(pin_names.begin(), pin_names.end());
|
||||
int lut = 0;
|
||||
for (int n = 0; n < 8; n++) {
|
||||
auto values = dict<std::string, bool>{};
|
||||
auto values = std::unordered_map<std::string, bool>{};
|
||||
values.insert(std::make_pair(pins[0], (n & 1) == 1));
|
||||
values.insert(std::make_pair(pins[1], (n & 2) == 2));
|
||||
values.insert(std::make_pair(ff_output, (n & 4) == 4));
|
||||
|
|
|
|||
|
|
@ -25,9 +25,22 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef FILTERLIB
|
||||
#ifdef FILTERLIB
|
||||
#undef log_assert
|
||||
void log_assert(bool cond) {
|
||||
if (!cond)
|
||||
fprintf(stderr, "Unspecified assertion failed\n");
|
||||
}
|
||||
void warn(std::string str) {
|
||||
std::cerr << str;
|
||||
}
|
||||
#else
|
||||
#include "kernel/log.h"
|
||||
void warn(std::string str) {
|
||||
Yosys::log_formatted_warning("", str);
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace Yosys;
|
||||
|
|
@ -162,13 +175,15 @@ void LibertyAst::dump(FILE *f, sieve &blacklist, sieve &whitelist, std::string i
|
|||
fprintf(f, " ;\n");
|
||||
}
|
||||
|
||||
#ifndef FILTERLIB
|
||||
|
||||
// binary operators excluding ' '
|
||||
bool LibertyExpression::is_nice_binop(char c) {
|
||||
bool LibertyExpression::char_is_nice_binop(char c) {
|
||||
return c == '*' || c == '&' || c == '^' || c == '+' || c == '|';
|
||||
}
|
||||
|
||||
bool LibertyExpression::is_binop() {
|
||||
return kind == AND || kind == OR || kind == XOR;
|
||||
}
|
||||
|
||||
// https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
|
||||
LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
|
||||
if (s.empty())
|
||||
|
|
@ -177,7 +192,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
|
|||
char c = s.peek();
|
||||
auto lhs = LibertyExpression{};
|
||||
|
||||
while (isspace(c)) {
|
||||
while (isspace(c) || c == '"') {
|
||||
if (s.empty())
|
||||
return lhs;
|
||||
s.next();
|
||||
|
|
@ -191,7 +206,9 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
|
|||
s.next();
|
||||
lhs = parse(s);
|
||||
if (s.peek() != ')') {
|
||||
log_warning("expected ')' instead of '%c' while parsing Liberty expression '%s'\n", s.peek(), s.full_expr());
|
||||
std::stringstream ss;
|
||||
ss << "expected ')' instead of " << s.peek() << " while parsing Liberty expression '" << s.full_expr() << "'\n";
|
||||
warn(ss.str());
|
||||
return lhs;
|
||||
}
|
||||
s.next();
|
||||
|
|
@ -200,10 +217,11 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
|
|||
lhs.kind = Kind::NOT;
|
||||
lhs.children.push_back(parse(s, 7));
|
||||
} else {
|
||||
log_warning("unrecognised character '%c' while parsing Liberty expression '%s'\n", c, s.full_expr());
|
||||
std::stringstream ss;
|
||||
ss << "unrecognised character " << c << " while parsing Liberty expression " << s.full_expr() << "\n";
|
||||
warn(ss.str());
|
||||
return lhs;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (s.empty())
|
||||
break;
|
||||
|
|
@ -246,9 +264,10 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
|
|||
s.next();
|
||||
c = s.peek();
|
||||
}
|
||||
if (is_nice_binop(c)) {
|
||||
if (char_is_nice_binop(c) || c == ')' || c == '\'' || c == '\"') {
|
||||
// We found a real binop, so this space wasn't an AND
|
||||
// and we just discard it as meaningless whitespace
|
||||
// Tail operators also imply this isn't an AND
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -286,7 +305,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
|
|||
return lhs;
|
||||
}
|
||||
|
||||
void LibertyExpression::get_pin_names(pool<std::string>& names) {
|
||||
void LibertyExpression::get_pin_names(std::unordered_set<std::string>& names) {
|
||||
if (kind == Kind::PIN) {
|
||||
names.insert(name);
|
||||
} else {
|
||||
|
|
@ -295,7 +314,7 @@ void LibertyExpression::get_pin_names(pool<std::string>& names) {
|
|||
}
|
||||
}
|
||||
|
||||
bool LibertyExpression::eval(dict<std::string, bool>& values) {
|
||||
bool LibertyExpression::eval(std::unordered_map<std::string, bool>& values) {
|
||||
bool result = false;
|
||||
switch (kind) {
|
||||
case Kind::AND:
|
||||
|
|
@ -324,7 +343,7 @@ bool LibertyExpression::eval(dict<std::string, bool>& values) {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::string LibertyExpression::str(int indent)
|
||||
std::string LibertyExpression::sexpr_str(int indent)
|
||||
{
|
||||
std::string prefix;
|
||||
switch (kind) {
|
||||
|
|
@ -355,16 +374,55 @@ std::string LibertyExpression::str(int indent)
|
|||
if (!first) {
|
||||
prefix += "\n" + std::string(indent + add_indent, ' ');
|
||||
}
|
||||
prefix += child.str(indent + add_indent);
|
||||
prefix += child.sexpr_str(indent + add_indent);
|
||||
first = false;
|
||||
}
|
||||
prefix += ")";
|
||||
return prefix;
|
||||
}
|
||||
|
||||
#endif
|
||||
std::string LibertyExpression::vlog_str()
|
||||
{
|
||||
std::string prefix;
|
||||
if (kind != PIN)
|
||||
prefix += "(";
|
||||
if (is_binop()) {
|
||||
log_assert(children.size() == 2);
|
||||
prefix += children[0].vlog_str();
|
||||
switch (kind) {
|
||||
case AND:
|
||||
prefix += "&";
|
||||
break;
|
||||
case OR:
|
||||
prefix += "|";
|
||||
break;
|
||||
case XOR:
|
||||
prefix += "^";
|
||||
break;
|
||||
default:
|
||||
log_assert(false);
|
||||
}
|
||||
prefix += children[1].vlog_str();
|
||||
} else {
|
||||
switch (kind) {
|
||||
case NOT:
|
||||
log_assert(children.size() == 1);
|
||||
prefix += "~";
|
||||
prefix += children[0].vlog_str();
|
||||
break;
|
||||
case PIN:
|
||||
prefix += name;
|
||||
break;
|
||||
default:
|
||||
log_assert(false);
|
||||
}
|
||||
}
|
||||
if (kind != PIN)
|
||||
prefix += ")";
|
||||
return prefix;
|
||||
}
|
||||
|
||||
int LibertyParser::lexer(std::string &str)
|
||||
int LibertyParser::lexer_inner(std::string &str)
|
||||
{
|
||||
int c;
|
||||
|
||||
|
|
@ -390,11 +448,9 @@ int LibertyParser::lexer(std::string &str)
|
|||
|
||||
if (str == "+" || str == "-") {
|
||||
/* Single operator is not an identifier */
|
||||
// fprintf(stderr, "LEX: char >>%s<<\n", str.c_str());
|
||||
return str[0];
|
||||
}
|
||||
else {
|
||||
// fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
|
||||
return 'v';
|
||||
}
|
||||
}
|
||||
|
|
@ -402,24 +458,25 @@ int LibertyParser::lexer(std::string &str)
|
|||
// if it wasn't an identifer, number of array range,
|
||||
// maybe it's a string?
|
||||
if (c == '"') {
|
||||
f.consume(1);
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
c = f.peek(i);
|
||||
line += (c == '\n');
|
||||
if (c != '"')
|
||||
if (c != '"' && c != EOF)
|
||||
i += 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
str.clear();
|
||||
#ifdef FILTERLIB
|
||||
f.unget();
|
||||
str.append(f.buffered_data(), f.buffered_data() + i + 2);
|
||||
f.consume(i + 2);
|
||||
#else
|
||||
str.append(f.buffered_data(), f.buffered_data() + i);
|
||||
f.consume(i + 1);
|
||||
str.append(f.buffered_data(), f.buffered_data() + i + 1);
|
||||
// Usage in filterlib is expected to retain quotes
|
||||
// but yosys expects to get unquoted
|
||||
#ifdef FILTERLIB
|
||||
str = "\"" + str + "\"";
|
||||
#endif
|
||||
f.consume(i + 2);
|
||||
return 'v';
|
||||
}
|
||||
|
||||
|
|
@ -442,13 +499,12 @@ int LibertyParser::lexer(std::string &str)
|
|||
return lexer(str);
|
||||
}
|
||||
f.unget();
|
||||
// fprintf(stderr, "LEX: char >>/<<\n");
|
||||
return '/'; // a single '/' charater.
|
||||
}
|
||||
|
||||
// check for a backslash
|
||||
if (c == '\\') {
|
||||
c = f.get();
|
||||
c = f.get();
|
||||
if (c == '\r')
|
||||
c = f.get();
|
||||
if (c == '\n') {
|
||||
|
|
@ -467,14 +523,22 @@ int LibertyParser::lexer(std::string &str)
|
|||
|
||||
// anything else, such as ';' will get passed
|
||||
// through as literal items.
|
||||
|
||||
// if (c >= 32 && c < 255)
|
||||
// fprintf(stderr, "LEX: char >>%c<<\n", c);
|
||||
// else
|
||||
// fprintf(stderr, "LEX: char %d\n", c);
|
||||
return c;
|
||||
}
|
||||
|
||||
int LibertyParser::lexer(std::string &str)
|
||||
{
|
||||
int ret = lexer_inner(str);
|
||||
// if (ret >= 32 && ret < 255) {
|
||||
// fprintf(stdout, "LEX: ret >>%c<<\n", ret);
|
||||
// } else if (ret == 'v') {
|
||||
// fprintf(stdout, "LEX: ret v str %s\n", str.c_str());
|
||||
// } else {
|
||||
// fprintf(stdout, "LEX: ret %d\n", ret);
|
||||
// }
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LibertyParser::report_unexpected_token(int tok)
|
||||
{
|
||||
std::string eReport;
|
||||
|
|
@ -545,6 +609,25 @@ void LibertyParser::parse_vector_range(int tok)
|
|||
}
|
||||
}
|
||||
|
||||
// Consume into out_str any string-ish tokens, seperated with spaces
|
||||
// to cope with abuse of the underdefined spec by real world PDKs
|
||||
// enabled by proprietary implementations.
|
||||
// Sorry.
|
||||
int LibertyParser::consume_wrecked_str(int tok, std::string& out_str) {
|
||||
std::string str = "";
|
||||
while (tok != ';' && tok != EOF && tok != 'n') {
|
||||
out_str += " ";
|
||||
if (tok == 'v')
|
||||
out_str += str;
|
||||
else
|
||||
out_str += tok;
|
||||
tok = lexer(str);
|
||||
}
|
||||
if (tok == EOF)
|
||||
error("wrecked string EOF");
|
||||
return tok;
|
||||
}
|
||||
|
||||
LibertyAst *LibertyParser::parse(bool top_level)
|
||||
{
|
||||
std::string str;
|
||||
|
|
@ -591,7 +674,14 @@ LibertyAst *LibertyParser::parse(bool top_level)
|
|||
if (tok == '[') {
|
||||
parse_vector_range(tok);
|
||||
tok = lexer(str);
|
||||
} else {
|
||||
// Hack for when an expression string is unquoted
|
||||
tok = consume_wrecked_str(tok, ast->value);
|
||||
}
|
||||
} else if (tok == '(') {
|
||||
// Hack for when an expression string is unquoted and starts with
|
||||
// parentheses
|
||||
tok = consume_wrecked_str(tok, ast->value);
|
||||
}
|
||||
while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') {
|
||||
ast->value += tok;
|
||||
|
|
@ -601,7 +691,7 @@ LibertyAst *LibertyParser::parse(bool top_level)
|
|||
ast->value += str;
|
||||
tok = lexer(str);
|
||||
}
|
||||
|
||||
|
||||
// In a liberty file, all key : value pairs should end in ';'
|
||||
// However, there are some liberty files in the wild that
|
||||
// just have a newline. We'll be kind and accept a newline
|
||||
|
|
@ -621,11 +711,11 @@ LibertyAst *LibertyParser::parse(bool top_level)
|
|||
continue;
|
||||
if (tok == ')')
|
||||
break;
|
||||
|
||||
|
||||
if (tok == '[')
|
||||
{
|
||||
parse_vector_range(tok);
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
if (tok == 'n')
|
||||
continue;
|
||||
|
|
@ -727,42 +817,13 @@ const LibertyAst *find_non_null(const LibertyAst *node, const char *name)
|
|||
|
||||
std::string func2vl(std::string str)
|
||||
{
|
||||
for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) {
|
||||
char c_left = pos > 0 ? str[pos-1] : ' ';
|
||||
char c_right = pos+1 < str.size() ? str[pos+1] : ' ';
|
||||
if (std::string("\" \t*+").find(c_left) != std::string::npos)
|
||||
str.erase(pos, 1);
|
||||
else if (std::string("\" \t*+").find(c_right) != std::string::npos)
|
||||
str.erase(pos, 1);
|
||||
else
|
||||
str[pos] = '*';
|
||||
}
|
||||
|
||||
std::vector<size_t> group_start;
|
||||
for (size_t pos = 0; pos < str.size(); pos++) {
|
||||
if (str[pos] == '(')
|
||||
group_start.push_back(pos);
|
||||
if (str[pos] == ')' && group_start.size() > 0) {
|
||||
if (pos+1 < str.size() && str[pos+1] == '\'') {
|
||||
std::string group = str.substr(group_start.back(), pos-group_start.back()+1);
|
||||
str[group_start.back()] = '~';
|
||||
str.replace(group_start.back()+1, group.size(), group);
|
||||
pos++;
|
||||
}
|
||||
group_start.pop_back();
|
||||
}
|
||||
if (str[pos] == '\'' && pos > 0) {
|
||||
size_t start = str.find_last_of("()'*+^&| ", pos-1)+1;
|
||||
std::string group = str.substr(start, pos-start);
|
||||
str[start] = '~';
|
||||
str.replace(start+1, group.size(), group);
|
||||
}
|
||||
if (str[pos] == '*')
|
||||
str[pos] = '&';
|
||||
if (str[pos] == '+')
|
||||
str[pos] = '|';
|
||||
}
|
||||
auto helper = LibertyExpression::Lexer(str);
|
||||
return LibertyExpression::parse(helper).vlog_str();
|
||||
}
|
||||
|
||||
std::string vlog_identifier(std::string str)
|
||||
{
|
||||
str.erase(std::remove(str.begin(), str.end(), '\"'), str.end());
|
||||
return str;
|
||||
}
|
||||
|
||||
|
|
@ -772,11 +833,13 @@ void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr)
|
|||
expr.clear();
|
||||
|
||||
if (ast != NULL) {
|
||||
expr = func2vl(ast->value);
|
||||
if (expr.size() > 0 && expr[0] == '~')
|
||||
edge = "negedge " + expr.substr(1);
|
||||
auto helper = LibertyExpression::Lexer(ast->value);
|
||||
auto parsed = LibertyExpression::parse(helper);
|
||||
expr = parsed.vlog_str();
|
||||
if (parsed.kind == LibertyExpression::Kind::NOT)
|
||||
edge = "negedge " + parsed.children[0].vlog_str();
|
||||
else
|
||||
edge = "posedge " + expr;
|
||||
edge = "posedge " + parsed.vlog_str();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -806,13 +869,13 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
|||
return;
|
||||
|
||||
CHECK_NV(ast->args.size(), == 1);
|
||||
printf("module %s (", ast->args[0].c_str());
|
||||
printf("module %s (", vlog_identifier(ast->args[0]).c_str());
|
||||
bool first = true;
|
||||
for (auto child : ast->children) {
|
||||
if (child->id != "pin")
|
||||
continue;
|
||||
CHECK_NV(child->args.size(), == 1);
|
||||
printf("%s%s", first ? "" : ", ", child->args[0].c_str());
|
||||
printf("%s%s", first ? "" : ", ", vlog_identifier(child->args[0]).c_str());
|
||||
first = false;
|
||||
}
|
||||
printf(");\n");
|
||||
|
|
@ -823,7 +886,7 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
|||
printf(" reg ");
|
||||
first = true;
|
||||
for (auto arg : child->args) {
|
||||
printf("%s%s", first ? "" : ", ", arg.c_str());
|
||||
printf("%s%s", first ? "" : ", ", vlog_identifier(arg).c_str());
|
||||
first = false;
|
||||
}
|
||||
printf(";\n");
|
||||
|
|
@ -835,9 +898,10 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
|||
CHECK_NV(child->args.size(), == 1);
|
||||
const LibertyAst *dir = find_non_null(child, "direction");
|
||||
const LibertyAst *func = child->find("function");
|
||||
printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str());
|
||||
std::string var = vlog_identifier(child->args[0]);
|
||||
printf(" %s %s;\n", dir->value.c_str(), var.c_str());
|
||||
if (func != NULL)
|
||||
printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str());
|
||||
printf(" assign %s = %s; // %s\n", var.c_str(), func2vl(func->value).c_str(), func->value.c_str());
|
||||
}
|
||||
|
||||
for (auto child : ast->children)
|
||||
|
|
@ -845,8 +909,8 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
|||
if (child->id != "ff" || child->args.size() != 2)
|
||||
continue;
|
||||
|
||||
std::string iq_var = child->args[0];
|
||||
std::string iqn_var = child->args[1];
|
||||
std::string iq_var = vlog_identifier(child->args[0]);
|
||||
std::string iqn_var = vlog_identifier(child->args[1]);
|
||||
|
||||
std::string clock_edge, clock_expr;
|
||||
event2vl(child->find("clocked_on"), clock_edge, clock_expr);
|
||||
|
|
@ -909,8 +973,8 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
|||
if (child->id != "latch" || child->args.size() != 2)
|
||||
continue;
|
||||
|
||||
std::string iq_var = child->args[0];
|
||||
std::string iqn_var = child->args[1];
|
||||
std::string iq_var = vlog_identifier(child->args[0]);
|
||||
std::string iqn_var = vlog_identifier(child->args[1]);
|
||||
|
||||
std::string enable_edge, enable_expr;
|
||||
event2vl(child->find("enable"), enable_edge, enable_expr);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ namespace Yosys
|
|||
}
|
||||
|
||||
std::string pin() {
|
||||
auto length = s.find_first_of("\t()'!^*& +|");
|
||||
auto length = s.find_first_of("\t()'!^*& +|\"");
|
||||
if (length == std::string::npos) {
|
||||
// nothing found so use size of s
|
||||
length = s.size();
|
||||
|
|
@ -91,11 +91,13 @@ namespace Yosys
|
|||
LibertyExpression() : kind(Kind::EMPTY) {}
|
||||
|
||||
static LibertyExpression parse(Lexer &s, int min_prio = 0);
|
||||
void get_pin_names(pool<std::string>& names);
|
||||
bool eval(dict<std::string, bool>& values);
|
||||
std::string str(int indent = 0);
|
||||
void get_pin_names(std::unordered_set<std::string>& names);
|
||||
bool eval(std::unordered_map<std::string, bool>& values);
|
||||
std::string sexpr_str(int indent = 0);
|
||||
std::string vlog_str();
|
||||
private:
|
||||
static bool is_nice_binop(char c);
|
||||
static bool char_is_nice_binop(char c);
|
||||
bool is_binop();
|
||||
};
|
||||
|
||||
class LibertyInputStream {
|
||||
|
|
@ -170,10 +172,12 @@ namespace Yosys
|
|||
'n': newline
|
||||
anything else is a single character.
|
||||
*/
|
||||
int lexer_inner(std::string &str);
|
||||
int lexer(std::string &str);
|
||||
|
||||
void report_unexpected_token(int tok);
|
||||
void parse_vector_range(int tok);
|
||||
int consume_wrecked_str(int tok, std::string& out_str);
|
||||
LibertyAst *parse(bool top_level);
|
||||
void error() const;
|
||||
void error(const std::string &str) const;
|
||||
|
|
|
|||
|
|
@ -164,6 +164,8 @@ pyosys_headers = [
|
|||
{
|
||||
"global_id_storage_",
|
||||
"global_id_index_",
|
||||
"global_negative_id_storage_",
|
||||
"global_negative_id_prefix_storage_",
|
||||
"global_refcount_storage_",
|
||||
"global_free_idx_list_",
|
||||
"last_created_idx_ptr_",
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip
|
|||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd fsm # Constrain all select calls below inside the top module
|
||||
|
||||
select -assert-count 2 t:LUT2
|
||||
select -assert-count 4 t:LUT3
|
||||
select -assert-count 4 t:dffepc
|
||||
select -assert-count 1 t:logic_0
|
||||
select -assert-count 1 t:logic_1
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@ equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad
|
|||
design -load postopt
|
||||
cd cascade
|
||||
select -assert-count 2 t:DSP48E1
|
||||
select -assert-none t:DSP48E1 t:BUFG %% t:* %D
|
||||
# TODO Disabled check, FDREs emitted due to order sensitivity
|
||||
# select -assert-none t:DSP48E1 t:BUFG %% t:* %D
|
||||
# Very crude method of checking that DSP48E1.PCOUT -> DSP48E1.PCIN
|
||||
# (see above for explanation)
|
||||
select -assert-count 1 t:DSP48E1 %co:+[PCOUT] t:DSP48E1 %d %co:+[PCIN] w:* %d t:DSP48E1 %i
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@ module XNOR2X1 (B, A, Y);
|
|||
input B;
|
||||
input A;
|
||||
output Y;
|
||||
assign Y = !(B&!A|!B&A); // "!(B&!A|!B&A)"
|
||||
assign Y = (~((B&(~A))|((~B)&A))); // "!(B&!A|!B&A)"
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ library(dff) {
|
|||
area : 1;
|
||||
ff("IQ", "IQN") {
|
||||
next_state : "(D)";
|
||||
clocked_on : "CLK";
|
||||
clocked_on : (CLK);
|
||||
}
|
||||
pin(D) {
|
||||
direction : input;
|
||||
|
|
@ -15,7 +15,7 @@ library(dff) {
|
|||
}
|
||||
pin(Q) {
|
||||
direction: output;
|
||||
function : "IQ";
|
||||
function : IQ;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ library(dff) {
|
|||
area : 1 ;
|
||||
ff("IQ", "IQN") {
|
||||
next_state : "(D)" ;
|
||||
clocked_on : "CLK" ;
|
||||
clocked_on : ( CLK ) ;
|
||||
}
|
||||
pin(D) {
|
||||
direction : input ;
|
||||
|
|
@ -13,7 +13,7 @@ library(dff) {
|
|||
}
|
||||
pin(Q) {
|
||||
direction : output ;
|
||||
function : "IQ" ;
|
||||
function : IQ ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
module dff (D, CLK, Q);
|
||||
reg "IQ", "IQN";
|
||||
reg IQ, IQN;
|
||||
input D;
|
||||
input CLK;
|
||||
output Q;
|
||||
assign Q = IQ; // "IQ"
|
||||
assign Q = IQ; // IQ
|
||||
always @(posedge CLK) begin
|
||||
// "(D)"
|
||||
"IQ" <= (D);
|
||||
"IQN" <= ~((D));
|
||||
IQ <= D;
|
||||
IQN <= ~(D);
|
||||
end
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
module inv (A, Y);
|
||||
input A;
|
||||
output Y;
|
||||
assign Y = ~A; // "A'"
|
||||
assign Y = (~A); // "A'"
|
||||
endmodule
|
||||
module tri_inv (A, S, Z);
|
||||
input A;
|
||||
input S;
|
||||
output Z;
|
||||
assign Z = ~A; // "A'"
|
||||
assign Z = (~A); // "A'"
|
||||
endmodule
|
||||
module buffer (A, Y);
|
||||
input A;
|
||||
|
|
@ -18,29 +18,29 @@ module nand2 (A, B, Y);
|
|||
input A;
|
||||
input B;
|
||||
output Y;
|
||||
assign Y = ~(A&B); // "(A * B)'"
|
||||
assign Y = (~(A&B)); // "(A * B)'"
|
||||
endmodule
|
||||
module nor2 (A, B, Y);
|
||||
input A;
|
||||
input B;
|
||||
output Y;
|
||||
assign Y = ~(A|B); // "(A + B)'"
|
||||
assign Y = (~(A|B)); // "(A + B)'"
|
||||
endmodule
|
||||
module xor2 (A, B, Y);
|
||||
input A;
|
||||
input B;
|
||||
output Y;
|
||||
assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)"
|
||||
assign Y = ((A&(~B))|((~A)&B)); // "(A *B') + (A' * B)"
|
||||
endmodule
|
||||
module imux2 (A, B, S, Y);
|
||||
input A;
|
||||
input B;
|
||||
input S;
|
||||
output Y;
|
||||
assign Y = ~(&(A&S)|(B&~S)&); // "( (A * S) + (B * S') )'"
|
||||
assign Y = (~((A&S)|(B&(~S)))); // "( (A * S) + (B * S') )'"
|
||||
endmodule
|
||||
module dff (D, CLK, RESET, PRESET, Q, QN);
|
||||
reg "IQ", "IQN";
|
||||
reg IQ, IQN;
|
||||
input D;
|
||||
input CLK;
|
||||
input RESET;
|
||||
|
|
@ -51,26 +51,26 @@ module dff (D, CLK, RESET, PRESET, Q, QN);
|
|||
assign QN = IQN; // "IQN"
|
||||
always @(posedge CLK, posedge RESET, posedge PRESET) begin
|
||||
if ((RESET) && (PRESET)) begin
|
||||
"IQ" <= 0;
|
||||
"IQN" <= 0;
|
||||
IQ <= 0;
|
||||
IQN <= 0;
|
||||
end
|
||||
else if (RESET) begin
|
||||
"IQ" <= 0;
|
||||
"IQN" <= 1;
|
||||
IQ <= 0;
|
||||
IQN <= 1;
|
||||
end
|
||||
else if (PRESET) begin
|
||||
"IQ" <= 1;
|
||||
"IQN" <= 0;
|
||||
IQ <= 1;
|
||||
IQN <= 0;
|
||||
end
|
||||
else begin
|
||||
// "D"
|
||||
"IQ" <= D;
|
||||
"IQN" <= ~(D);
|
||||
IQ <= D;
|
||||
IQN <= ~(D);
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
module latch (D, G, Q, QN);
|
||||
reg "IQ", "IQN";
|
||||
reg IQ, IQN;
|
||||
input D;
|
||||
input G;
|
||||
output Q;
|
||||
|
|
@ -79,8 +79,8 @@ module latch (D, G, Q, QN);
|
|||
assign QN = IQN; // "IQN"
|
||||
always @* begin
|
||||
if (G) begin
|
||||
"IQ" <= D;
|
||||
"IQN" <= ~(D);
|
||||
IQ <= D;
|
||||
IQN <= ~(D);
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -89,14 +89,14 @@ module aoi211 (A, B, C, Y);
|
|||
input B;
|
||||
input C;
|
||||
output Y;
|
||||
assign Y = ~((A&B)|C); // "((A * B) + C)'"
|
||||
assign Y = (~((A&B)|C)); // "((A * B) + C)'"
|
||||
endmodule
|
||||
module oai211 (A, B, C, Y);
|
||||
input A;
|
||||
input B;
|
||||
input C;
|
||||
output Y;
|
||||
assign Y = ~((A|B)&C); // "((A + B) * C)'"
|
||||
assign Y = (~((A|B)&C)); // "((A + B) * C)'"
|
||||
endmodule
|
||||
module halfadder (A, B, C, Y);
|
||||
input A;
|
||||
|
|
@ -104,7 +104,7 @@ module halfadder (A, B, C, Y);
|
|||
output C;
|
||||
assign C = (A&B); // "(A * B)"
|
||||
output Y;
|
||||
assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)"
|
||||
assign Y = ((A&(~B))|((~A)&B)); // "(A *B') + (A' * B)"
|
||||
endmodule
|
||||
module fulladder (A, B, CI, CO, Y);
|
||||
input A;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
library(dff_unquoted) {
|
||||
cell (dff1) {
|
||||
area : 1;
|
||||
ff("IQ", "IQN") {
|
||||
next_state : !D;
|
||||
clocked_on : (CLK);
|
||||
}
|
||||
pin(D) {
|
||||
direction : input;
|
||||
}
|
||||
pin(CLK) {
|
||||
direction : input;
|
||||
}
|
||||
pin(Q) {
|
||||
direction: output;
|
||||
function : IQ;
|
||||
}
|
||||
}
|
||||
cell (dff2) {
|
||||
area : 1;
|
||||
ff(IQ, IQN) {
|
||||
next_state : D';
|
||||
clocked_on : CLK;
|
||||
}
|
||||
pin(D) {
|
||||
direction : input;
|
||||
}
|
||||
pin(CLK) {
|
||||
direction : input;
|
||||
}
|
||||
pin(Q) {
|
||||
direction: output;
|
||||
function : "IQ";
|
||||
}
|
||||
}
|
||||
cell (dffe) {
|
||||
area : 6;
|
||||
ff("IQ", "IQN") {
|
||||
next_state : (D&EN) | (IQ&!EN);
|
||||
clocked_on : !CLK;
|
||||
}
|
||||
pin(D) {
|
||||
direction : input;
|
||||
}
|
||||
pin(EN) {
|
||||
direction : input;
|
||||
}
|
||||
pin(CLK) {
|
||||
direction : input;
|
||||
}
|
||||
pin(Q) {
|
||||
direction: output;
|
||||
function : "IQ";
|
||||
}
|
||||
pin(QN) {
|
||||
direction: output;
|
||||
function : "IQN";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
library(dff_unquoted) {
|
||||
cell(dff1) {
|
||||
area : 1 ;
|
||||
ff("IQ", "IQN") {
|
||||
next_state : !D ;
|
||||
clocked_on : ( CLK ) ;
|
||||
}
|
||||
pin(D) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(CLK) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(Q) {
|
||||
direction : output ;
|
||||
function : IQ ;
|
||||
}
|
||||
}
|
||||
cell(dff2) {
|
||||
area : 1 ;
|
||||
ff(IQ, IQN) {
|
||||
next_state : D ' ;
|
||||
clocked_on : CLK ;
|
||||
}
|
||||
pin(D) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(CLK) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(Q) {
|
||||
direction : output ;
|
||||
function : "IQ" ;
|
||||
}
|
||||
}
|
||||
cell(dffe) {
|
||||
area : 6 ;
|
||||
ff("IQ", "IQN") {
|
||||
next_state : ( D & EN ) | ( IQ & ! EN ) ;
|
||||
clocked_on : !CLK ;
|
||||
}
|
||||
pin(D) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(EN) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(CLK) {
|
||||
direction : input ;
|
||||
}
|
||||
pin(Q) {
|
||||
direction : output ;
|
||||
function : "IQ" ;
|
||||
}
|
||||
pin(QN) {
|
||||
direction : output ;
|
||||
function : "IQN" ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
module dff1 (D, CLK, Q);
|
||||
reg IQ, IQN;
|
||||
input D;
|
||||
input CLK;
|
||||
output Q;
|
||||
assign Q = IQ; // IQ
|
||||
always @(posedge CLK) begin
|
||||
// !D
|
||||
IQ <= (~D);
|
||||
IQN <= ~((~D));
|
||||
end
|
||||
endmodule
|
||||
module dff2 (D, CLK, Q);
|
||||
reg IQ, IQN;
|
||||
input D;
|
||||
input CLK;
|
||||
output Q;
|
||||
assign Q = IQ; // "IQ"
|
||||
always @(posedge CLK) begin
|
||||
// D '
|
||||
IQ <= (~D);
|
||||
IQN <= ~((~D));
|
||||
end
|
||||
endmodule
|
||||
module dffe (D, EN, CLK, Q, QN);
|
||||
reg IQ, IQN;
|
||||
input D;
|
||||
input EN;
|
||||
input CLK;
|
||||
output Q;
|
||||
assign Q = IQ; // "IQ"
|
||||
output QN;
|
||||
assign QN = IQN; // "IQN"
|
||||
always @(negedge CLK) begin
|
||||
// ( D & EN ) | ( IQ & ! EN )
|
||||
IQ <= ((D&EN)|(IQ&(~EN)));
|
||||
IQN <= ~(((D&EN)|(IQ&(~EN))));
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
read_verilog -sv << EOF
|
||||
interface simple_if;
|
||||
logic receiver;
|
||||
logic driver;
|
||||
endinterface
|
||||
|
||||
module driver_mod(simple_if intf, input in);
|
||||
assign intf.driver = in;
|
||||
endmodule
|
||||
|
||||
module receiver_mod(simple_if intf);
|
||||
assign intf.receiver = intf.driver;
|
||||
endmodule
|
||||
|
||||
module top(
|
||||
input logic [1:0] inputs,
|
||||
output logic [1:0] outputs
|
||||
);
|
||||
simple_if intf0();
|
||||
simple_if intf1();
|
||||
|
||||
driver_mod d0(intf0, inputs[0]);
|
||||
driver_mod d1(intf1, inputs[1]);
|
||||
|
||||
receiver_mod r0(intf0);
|
||||
receiver_mod r1(intf1);
|
||||
|
||||
assign outputs = {intf0.receiver, intf1.receiver};
|
||||
endmodule
|
||||
EOF
|
||||
|
||||
logger -expect error "Unable to connect.* with positional interface" 1
|
||||
hierarchy -top top
|
||||
|
|
@ -5,3 +5,4 @@
|
|||
|
||||
./run_simple.sh load_and_derive
|
||||
./run_simple.sh resolve_types
|
||||
./run_simple.sh positional_args
|
||||
|
|
|
|||
|
|
@ -30,11 +30,11 @@ TESTS := $(addprefix $(BINTEST)/, $(basename $(ALLTESTFILE:%Test.cc=%Test.o)))
|
|||
|
||||
all: prepare $(TESTS) run-tests
|
||||
|
||||
$(BINTEST)/%: $(OBJTEST)/%.o
|
||||
$(BINTEST)/%: $(OBJTEST)/%.o | prepare
|
||||
$(CXX) -L$(ROOTPATH) $(RPATH) $(LINKFLAGS) -o $@ $^ $(LIBS) \
|
||||
$(GTEST_LDFLAGS) $(EXTRAFLAGS)
|
||||
|
||||
$(OBJTEST)/%.o: $(basename $(subst $(OBJTEST),.,%)).cc
|
||||
$(OBJTEST)/%.o: $(basename $(subst $(OBJTEST),.,%)).cc | prepare
|
||||
$(CXX) -o $@ -c -I$(ROOTPATH) $(CPPFLAGS) $(CXXFLAGS) $(GTEST_CXXFLAGS) $^
|
||||
|
||||
.PHONY: prepare run-tests clean
|
||||
|
|
|
|||
|
|
@ -361,6 +361,36 @@ namespace RTLIL {
|
|||
EXPECT_FALSE(Const().is_onehot(&pos));
|
||||
}
|
||||
|
||||
TEST_F(KernelRtlilTest, OwningIdString) {
|
||||
OwningIdString own("\\figblortle");
|
||||
OwningIdString::collect_garbage();
|
||||
EXPECT_EQ(own.str(), "\\figblortle");
|
||||
}
|
||||
|
||||
TEST_F(KernelRtlilTest, LookupAutoidxId) {
|
||||
IdString id = NEW_ID;
|
||||
IdString id2 = IdString(id.str());
|
||||
EXPECT_EQ(id, id2);
|
||||
}
|
||||
|
||||
TEST_F(KernelRtlilTest, NewIdBeginsWith) {
|
||||
IdString id = NEW_ID;
|
||||
EXPECT_TRUE(id.begins_with("$auto"));
|
||||
EXPECT_FALSE(id.begins_with("xyz"));
|
||||
EXPECT_TRUE(id.begins_with("$auto$"));
|
||||
EXPECT_FALSE(id.begins_with("abcdefghijklmn"));
|
||||
EXPECT_TRUE(id.begins_with("$auto$rtlilTest"));
|
||||
EXPECT_FALSE(id.begins_with("$auto$rtlilX"));
|
||||
}
|
||||
|
||||
TEST_F(KernelRtlilTest, NewIdIndexing) {
|
||||
IdString id = NEW_ID;
|
||||
std::string str = id.str();
|
||||
for (int i = 0; i < GetSize(str) + 1; ++i) {
|
||||
EXPECT_EQ(id[i], str.c_str()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
class WireRtlVsHdlIndexConversionTest :
|
||||
public KernelRtlilTest,
|
||||
public testing::WithParamInterface<std::tuple<bool, int, int>>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace RTLIL {
|
|||
void checkAll(std::initializer_list<std::string> expressions, std::string expected) {
|
||||
for (const auto& e : expressions) {
|
||||
auto helper = LibertyExpression::Lexer(e);
|
||||
auto tree_s = LibertyExpression::parse(helper).str();
|
||||
auto tree_s = LibertyExpression::parse(helper).sexpr_str();
|
||||
EXPECT_EQ(tree_s, expected);
|
||||
}
|
||||
}
|
||||
|
|
@ -82,6 +82,11 @@ namespace RTLIL {
|
|||
}, "(and (pin \"x\")\n"
|
||||
" (not (pin \"y\")))"
|
||||
);
|
||||
checkAll({
|
||||
"( D & EN )",
|
||||
}, "(and (pin \"D\")\n"
|
||||
" (pin \"EN\"))"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue