mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-02 05:26:07 +00:00
Merge branch 'YosysHQ:main' into master
This commit is contained in:
commit
c482bcef0d
86 changed files with 3620 additions and 835 deletions
39
.github/workflows/test-verific.yml
vendored
39
.github/workflows/test-verific.yml
vendored
|
|
@ -83,3 +83,42 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
make -j$procs unit-test ENABLE_LTO=1 ENABLE_LIBYOSYS=1
|
make -j$procs unit-test ENABLE_LTO=1 ENABLE_LIBYOSYS=1
|
||||||
|
|
||||||
|
test-pyosys:
|
||||||
|
needs: pre-job
|
||||||
|
if: ${{ needs.pre-job.outputs.should_skip != 'true' && github.repository == 'YosysHQ/Yosys' }}
|
||||||
|
runs-on: [self-hosted, linux, x64, fast]
|
||||||
|
steps:
|
||||||
|
- name: Checkout Yosys
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
submodules: true
|
||||||
|
- name: Install UV
|
||||||
|
run: |
|
||||||
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
|
- name: Runtime environment
|
||||||
|
run: |
|
||||||
|
echo "procs=$(nproc)" >> $GITHUB_ENV
|
||||||
|
echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Build pyosys
|
||||||
|
run: |
|
||||||
|
make config-clang
|
||||||
|
echo "ENABLE_VERIFIC := 1" >> Makefile.conf
|
||||||
|
echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf
|
||||||
|
echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf
|
||||||
|
echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf
|
||||||
|
echo "ENABLE_CCACHE := 1" >> Makefile.conf
|
||||||
|
echo "ENABLE_PYOSYS := 1" >> Makefile.conf
|
||||||
|
echo "PYTHON_DESTDIR := /usr/lib/python3/site-packages" >> Makefile.conf
|
||||||
|
make -j$procs
|
||||||
|
|
||||||
|
- name: Install pyosys
|
||||||
|
run: |
|
||||||
|
make install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX=
|
||||||
|
|
||||||
|
- name: Run pyosys tests
|
||||||
|
run: |
|
||||||
|
export PYTHONPATH=${GITHUB_WORKSPACE}/.local/usr/lib/python3/site-packages:$PYTHONPATH
|
||||||
|
python3 tests/pyosys/run_tests.py
|
||||||
|
|
|
||||||
13
CHANGELOG
13
CHANGELOG
|
|
@ -2,9 +2,20 @@
|
||||||
List of major changes and improvements between releases
|
List of major changes and improvements between releases
|
||||||
=======================================================
|
=======================================================
|
||||||
|
|
||||||
Yosys 0.59 .. Yosys 0.60-dev
|
Yosys 0.60 .. Yosys 0.61-dev
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
Yosys 0.59 .. Yosys 0.60
|
||||||
|
--------------------------
|
||||||
|
* Various
|
||||||
|
- read_verilog: suport unsized parameters.
|
||||||
|
- Added static library compile option.
|
||||||
|
|
||||||
|
* New commands and options
|
||||||
|
- Added "sdc" pass for reading SDC files.
|
||||||
|
- Added experimental "sdc_expand" and "opensta" for OpenSTA integration.
|
||||||
|
- Added "icell_liberty" pass for used internal cells.
|
||||||
|
|
||||||
Yosys 0.58 .. Yosys 0.59
|
Yosys 0.58 .. Yosys 0.59
|
||||||
--------------------------
|
--------------------------
|
||||||
* Various
|
* Various
|
||||||
|
|
|
||||||
27
Makefile
27
Makefile
|
|
@ -23,6 +23,7 @@ ENABLE_VERIFIC_EDIF := 0
|
||||||
ENABLE_VERIFIC_LIBERTY := 0
|
ENABLE_VERIFIC_LIBERTY := 0
|
||||||
ENABLE_COVER := 1
|
ENABLE_COVER := 1
|
||||||
ENABLE_LIBYOSYS := 0
|
ENABLE_LIBYOSYS := 0
|
||||||
|
ENABLE_LIBYOSYS_STATIC := 0
|
||||||
ENABLE_ZLIB := 1
|
ENABLE_ZLIB := 1
|
||||||
ENABLE_HELP_SOURCE := 0
|
ENABLE_HELP_SOURCE := 0
|
||||||
|
|
||||||
|
|
@ -161,7 +162,7 @@ ifeq ($(OS), Haiku)
|
||||||
CXXFLAGS += -D_DEFAULT_SOURCE
|
CXXFLAGS += -D_DEFAULT_SOURCE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
YOSYS_VER := 0.59+0
|
YOSYS_VER := 0.60+0
|
||||||
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1)
|
||||||
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1)
|
YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1)
|
||||||
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)
|
YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)
|
||||||
|
|
@ -184,7 +185,7 @@ endif
|
||||||
OBJS = kernel/version_$(GIT_REV).o
|
OBJS = kernel/version_$(GIT_REV).o
|
||||||
|
|
||||||
bumpversion:
|
bumpversion:
|
||||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 03eb220.. | wc -l`/;" Makefile
|
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 5bafeb7.. | wc -l`/;" Makefile
|
||||||
|
|
||||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
|
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
|
||||||
|
|
||||||
|
|
@ -342,6 +343,9 @@ endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||||
TARGETS += libyosys.so
|
TARGETS += libyosys.so
|
||||||
|
ifeq ($(ENABLE_LIBYOSYS_STATIC),1)
|
||||||
|
TARGETS += libyosys.a
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
PY_WRAPPER_FILE = pyosys/wrappers
|
PY_WRAPPER_FILE = pyosys/wrappers
|
||||||
|
|
@ -474,6 +478,9 @@ else
|
||||||
ifeq ($(ABCEXTERNAL),)
|
ifeq ($(ABCEXTERNAL),)
|
||||||
TARGETS := $(PROGRAM_PREFIX)yosys-abc$(EXE) $(TARGETS)
|
TARGETS := $(PROGRAM_PREFIX)yosys-abc$(EXE) $(TARGETS)
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(DISABLE_SPAWN),1)
|
||||||
|
$(error ENABLE_ABC=1 requires either LINK_ABC=1 or DISABLE_SPAWN=0)
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
@ -772,6 +779,9 @@ else
|
||||||
$(P) $(CXX) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC)
|
$(P) $(CXX) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
libyosys.a: $(filter-out kernel/driver.o,$(OBJS))
|
||||||
|
$(P) $(AR) rcs $@ $^
|
||||||
|
|
||||||
%.o: %.cc
|
%.o: %.cc
|
||||||
$(Q) mkdir -p $(dir $@)
|
$(Q) mkdir -p $(dir $@)
|
||||||
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
|
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||||
|
|
@ -902,6 +912,7 @@ MK_TEST_DIRS += tests/arch/xilinx
|
||||||
MK_TEST_DIRS += tests/bugpoint
|
MK_TEST_DIRS += tests/bugpoint
|
||||||
MK_TEST_DIRS += tests/opt
|
MK_TEST_DIRS += tests/opt
|
||||||
MK_TEST_DIRS += tests/sat
|
MK_TEST_DIRS += tests/sat
|
||||||
|
MK_TEST_DIRS += tests/sdc
|
||||||
MK_TEST_DIRS += tests/sim
|
MK_TEST_DIRS += tests/sim
|
||||||
MK_TEST_DIRS += tests/svtypes
|
MK_TEST_DIRS += tests/svtypes
|
||||||
MK_TEST_DIRS += tests/techmap
|
MK_TEST_DIRS += tests/techmap
|
||||||
|
|
@ -1022,7 +1033,7 @@ install-dev: $(PROGRAM_PREFIX)yosys-config share
|
||||||
|
|
||||||
install: $(TARGETS) $(EXTRA_TARGETS)
|
install: $(TARGETS) $(EXTRA_TARGETS)
|
||||||
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR)
|
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR)
|
||||||
$(INSTALL_SUDO) cp $(filter-out libyosys.so,$(TARGETS)) $(DESTDIR)$(BINDIR)
|
$(INSTALL_SUDO) cp $(filter-out libyosys.so libyosys.a,$(TARGETS)) $(DESTDIR)$(BINDIR)
|
||||||
ifneq ($(filter $(PROGRAM_PREFIX)yosys,$(TARGETS)),)
|
ifneq ($(filter $(PROGRAM_PREFIX)yosys,$(TARGETS)),)
|
||||||
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys; fi
|
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys; fi
|
||||||
endif
|
endif
|
||||||
|
|
@ -1038,9 +1049,12 @@ ifeq ($(ENABLE_LIBYOSYS),1)
|
||||||
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR)
|
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR)
|
||||||
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)/
|
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)/
|
||||||
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so; fi
|
if [ -n "$(STRIP)" ]; then $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so; fi
|
||||||
|
ifeq ($(ENABLE_LIBYOSYS_STATIC),1)
|
||||||
|
$(INSTALL_SUDO) cp libyosys.a $(DESTDIR)$(LIBDIR)/
|
||||||
|
endif
|
||||||
ifeq ($(ENABLE_PYOSYS),1)
|
ifeq ($(ENABLE_PYOSYS),1)
|
||||||
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys
|
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys
|
||||||
$(INSTALL_SUDO) cp pyosys/__init__.py $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py
|
$(INSTALL_SUDO) cp $(YOSYS_SRC)/pyosys/__init__.py $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py
|
||||||
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so
|
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so
|
||||||
$(INSTALL_SUDO) cp -r share $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys
|
$(INSTALL_SUDO) cp -r share $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys
|
||||||
ifeq ($(ENABLE_ABC),1)
|
ifeq ($(ENABLE_ABC),1)
|
||||||
|
|
@ -1060,6 +1074,9 @@ uninstall:
|
||||||
$(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR)
|
$(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR)
|
||||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||||
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
|
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
|
||||||
|
ifeq ($(ENABLE_LIBYOSYS_STATIC),1)
|
||||||
|
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.a
|
||||||
|
endif
|
||||||
ifeq ($(ENABLE_PYOSYS),1)
|
ifeq ($(ENABLE_PYOSYS),1)
|
||||||
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so
|
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/libyosys.so
|
||||||
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py
|
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/__init__.py
|
||||||
|
|
@ -1158,7 +1175,7 @@ clean-py:
|
||||||
rm -f $(PY_WRAPPER_FILE).inc.cc $(PY_WRAPPER_FILE).cc
|
rm -f $(PY_WRAPPER_FILE).inc.cc $(PY_WRAPPER_FILE).cc
|
||||||
rm -f $(PYTHON_OBJECTS)
|
rm -f $(PYTHON_OBJECTS)
|
||||||
rm -f *.whl
|
rm -f *.whl
|
||||||
rm -f libyosys.so
|
rm -f libyosys.so libyosys.a
|
||||||
rm -rf kernel/*.pyh
|
rm -rf kernel/*.pyh
|
||||||
|
|
||||||
clean-abc:
|
clean-abc:
|
||||||
|
|
|
||||||
2
abc
2
abc
|
|
@ -1 +1 @@
|
||||||
Subproject commit 1c5ed1ce378cc04beac30bb31abc4c37c8467042
|
Subproject commit 131a50dd773f21ebbfc51da1d182438382a04209
|
||||||
|
|
@ -188,20 +188,27 @@ struct SmtrModule {
|
||||||
Functional::IR ir;
|
Functional::IR ir;
|
||||||
SmtrScope scope;
|
SmtrScope scope;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
bool use_assoc_list_helpers;
|
||||||
|
std::optional<std::string> input_helper_name;
|
||||||
|
std::optional<std::string> output_helper_name;
|
||||||
|
|
||||||
SmtrStruct input_struct;
|
SmtrStruct input_struct;
|
||||||
SmtrStruct output_struct;
|
SmtrStruct output_struct;
|
||||||
SmtrStruct state_struct;
|
SmtrStruct state_struct;
|
||||||
|
|
||||||
SmtrModule(Module *module)
|
SmtrModule(Module *module, bool assoc_list_helpers)
|
||||||
: ir(Functional::IR::from_module(module))
|
: ir(Functional::IR::from_module(module)), scope(), name(scope.unique_name(module->name)), use_assoc_list_helpers(assoc_list_helpers),
|
||||||
, scope()
|
input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope),
|
||||||
, name(scope.unique_name(module->name))
|
output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope),
|
||||||
, input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope)
|
state_struct(scope.unique_name(module->name.str() + "_State"), scope)
|
||||||
, output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope)
|
|
||||||
, state_struct(scope.unique_name(module->name.str() + "_State"), scope)
|
|
||||||
{
|
{
|
||||||
scope.reserve(name + "_initial");
|
scope.reserve(name + "_initial");
|
||||||
|
if (assoc_list_helpers) {
|
||||||
|
input_helper_name = scope.unique_name(module->name.str() + "_inputs_helper");
|
||||||
|
scope.reserve(*input_helper_name);
|
||||||
|
output_helper_name = scope.unique_name(module->name.str() + "_outputs_helper");
|
||||||
|
scope.reserve(*output_helper_name);
|
||||||
|
}
|
||||||
for (auto input : ir.inputs())
|
for (auto input : ir.inputs())
|
||||||
input_struct.insert(input->name, input->sort);
|
input_struct.insert(input->name, input->sort);
|
||||||
for (auto output : ir.outputs())
|
for (auto output : ir.outputs())
|
||||||
|
|
@ -257,6 +264,45 @@ struct SmtrModule {
|
||||||
w.pop();
|
w.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write_assoc_list_helpers(SExprWriter &w)
|
||||||
|
{
|
||||||
|
log_assert(output_helper_name && input_helper_name);
|
||||||
|
|
||||||
|
// Input struct keyword-based constructor.
|
||||||
|
w.push();
|
||||||
|
w.open(list("define"));
|
||||||
|
const auto inputs_name = "inputs";
|
||||||
|
w.open(list(*input_helper_name, inputs_name));
|
||||||
|
w.close();
|
||||||
|
w.open(list(input_struct.name));
|
||||||
|
for (auto input : ir.inputs()) {
|
||||||
|
w.push();
|
||||||
|
w.open(list("let"));
|
||||||
|
w.push();
|
||||||
|
w.open(list());
|
||||||
|
w.open(list("assoc-result"));
|
||||||
|
w << list("assoc", "\"" + RTLIL::unescape_id(input->name) + "\"", inputs_name);
|
||||||
|
w.pop();
|
||||||
|
w.open(list("if", "assoc-result"));
|
||||||
|
w << list("cdr", "assoc-result");
|
||||||
|
w.open(list("begin"));
|
||||||
|
w << list("fprintf", list("current-error-port"), "\"%s not found in inputs\"");
|
||||||
|
w << "'not-found";
|
||||||
|
w.pop();
|
||||||
|
}
|
||||||
|
w.pop();
|
||||||
|
// Output struct keyword-based destructuring
|
||||||
|
w.push();
|
||||||
|
w.open(list("define"));
|
||||||
|
const auto outputs_name = "outputs";
|
||||||
|
w << list(*output_helper_name, outputs_name);
|
||||||
|
w.open(list("list"));
|
||||||
|
for (auto output : ir.outputs()) {
|
||||||
|
w << list("cons", "\"" + RTLIL::unescape_id(output->name) + "\"", output_struct.access("outputs", output->name));
|
||||||
|
}
|
||||||
|
w.pop();
|
||||||
|
}
|
||||||
|
|
||||||
void write(std::ostream &out)
|
void write(std::ostream &out)
|
||||||
{
|
{
|
||||||
SExprWriter w(out);
|
SExprWriter w(out);
|
||||||
|
|
@ -265,6 +311,10 @@ struct SmtrModule {
|
||||||
output_struct.write_definition(w);
|
output_struct.write_definition(w);
|
||||||
state_struct.write_definition(w);
|
state_struct.write_definition(w);
|
||||||
|
|
||||||
|
if (use_assoc_list_helpers) {
|
||||||
|
write_assoc_list_helpers(w);
|
||||||
|
}
|
||||||
|
|
||||||
write_eval(w);
|
write_eval(w);
|
||||||
write_initial(w);
|
write_initial(w);
|
||||||
}
|
}
|
||||||
|
|
@ -282,12 +332,16 @@ struct FunctionalSmtrBackend : public Backend {
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -provides\n");
|
log(" -provides\n");
|
||||||
log(" include 'provide' statement(s) for loading output as a module\n");
|
log(" include 'provide' statement(s) for loading output as a module\n");
|
||||||
|
log(" -assoc-list-helpers\n");
|
||||||
|
log(" provide helper functions which convert inputs/outputs from/to association lists\n");
|
||||||
|
log(" \n");
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
{
|
{
|
||||||
auto provides = false;
|
auto provides = false;
|
||||||
|
auto assoc_list_helpers = false;
|
||||||
|
|
||||||
log_header(design, "Executing Functional Rosette Backend.\n");
|
log_header(design, "Executing Functional Rosette Backend.\n");
|
||||||
|
|
||||||
|
|
@ -296,6 +350,8 @@ struct FunctionalSmtrBackend : public Backend {
|
||||||
{
|
{
|
||||||
if (args[argidx] == "-provides")
|
if (args[argidx] == "-provides")
|
||||||
provides = true;
|
provides = true;
|
||||||
|
else if (args[argidx] == "-assoc-list-helpers")
|
||||||
|
assoc_list_helpers = true;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -307,8 +363,8 @@ struct FunctionalSmtrBackend : public Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto module : design->selected_modules()) {
|
for (auto module : design->selected_modules()) {
|
||||||
log("Processing module `%s`.\n", module->name);
|
log("Processing module `%s`.\n", module->name.c_str());
|
||||||
SmtrModule smtr(module);
|
SmtrModule smtr(module, assoc_list_helpers);
|
||||||
smtr.write(*f);
|
smtr.write(*f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,9 @@ void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f << stringf("%d'", width);
|
if ((data.flags & RTLIL::CONST_FLAG_UNSIZED) == 0) {
|
||||||
|
f << stringf("%d'", width);
|
||||||
|
}
|
||||||
if (data.flags & RTLIL::CONST_FLAG_SIGNED) {
|
if (data.flags & RTLIL::CONST_FLAG_SIGNED) {
|
||||||
f << stringf("s");
|
f << stringf("s");
|
||||||
}
|
}
|
||||||
|
|
@ -173,9 +175,10 @@ void RTLIL_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::
|
||||||
dump_attributes(f, indent, cell);
|
dump_attributes(f, indent, cell);
|
||||||
f << stringf("%s" "cell %s %s\n", indent, cell->type, cell->name);
|
f << stringf("%s" "cell %s %s\n", indent, cell->type, cell->name);
|
||||||
for (const auto& [name, param] : reversed(cell->parameters)) {
|
for (const auto& [name, param] : reversed(cell->parameters)) {
|
||||||
f << stringf("%s parameter%s%s %s ", indent,
|
f << stringf("%s parameter%s%s%s %s ", indent,
|
||||||
(param.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "",
|
(param.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "",
|
||||||
(param.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "",
|
(param.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "",
|
||||||
|
(param.flags & RTLIL::CONST_FLAG_UNSIZED) != 0 ? " unsized" : "",
|
||||||
name);
|
name);
|
||||||
dump_const(f, param);
|
dump_const(f, param);
|
||||||
f << stringf("\n");
|
f << stringf("\n");
|
||||||
|
|
|
||||||
|
|
@ -108,22 +108,30 @@ IdString initial_id;
|
||||||
|
|
||||||
void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
|
void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
|
||||||
{
|
{
|
||||||
const char *str = id.c_str();
|
auto it = id.begin();
|
||||||
|
auto it_end = id.end();
|
||||||
if (*str == '$' && may_rename && !norename)
|
if (it == it_end)
|
||||||
auto_name_map[id] = auto_name_counter++;
|
|
||||||
|
|
||||||
if (str[0] != '\\' || str[1] != '_' || str[2] == 0)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i = 2; str[i] != 0; i++) {
|
if (*it == '$' && may_rename && !norename)
|
||||||
if (str[i] == '_' && str[i+1] == 0)
|
auto_name_map[id] = auto_name_counter++;
|
||||||
continue;
|
|
||||||
if (str[i] < '0' || str[i] > '9')
|
if (*it != '\\' || (it + 1) == it_end || *(it + 1) != '_' || (it + 2) == it_end)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string s;
|
||||||
|
it += 2;
|
||||||
|
while (it != it_end) {
|
||||||
|
char ch = *it;
|
||||||
|
if (ch == '_' && (it + 1) == it_end)
|
||||||
|
break;
|
||||||
|
if (ch < '0' || ch > '9')
|
||||||
return;
|
return;
|
||||||
|
s.push_back(ch);
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
int num = atoi(str+2);
|
int num = atoi(s.c_str());
|
||||||
if (num >= auto_name_offset)
|
if (num >= auto_name_offset)
|
||||||
auto_name_offset = num + 1;
|
auto_name_offset = num + 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import os
|
||||||
project = 'YosysHQ Yosys'
|
project = 'YosysHQ Yosys'
|
||||||
author = 'YosysHQ GmbH'
|
author = 'YosysHQ GmbH'
|
||||||
copyright ='2025 YosysHQ GmbH'
|
copyright ='2025 YosysHQ GmbH'
|
||||||
yosys_ver = "0.59"
|
yosys_ver = "0.60"
|
||||||
|
|
||||||
# select HTML theme
|
# select HTML theme
|
||||||
html_theme = 'furo-ys'
|
html_theme = 'furo-ys'
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,8 @@ struct Xaiger2Frontend : public Frontend {
|
||||||
for (int i = 0; i < (int) O; i++) {
|
for (int i = 0; i < (int) O; i++) {
|
||||||
int po;
|
int po;
|
||||||
*f >> po;
|
*f >> po;
|
||||||
log_assert(f->get() == '\n');
|
int c = f->get();
|
||||||
|
log_assert(c == '\n');
|
||||||
outputs.push_back(po);
|
outputs.push_back(po);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -993,6 +993,8 @@ RTLIL::Const AstNode::asParaConst() const
|
||||||
RTLIL::Const val = asAttrConst();
|
RTLIL::Const val = asAttrConst();
|
||||||
if (is_signed)
|
if (is_signed)
|
||||||
val.flags |= RTLIL::CONST_FLAG_SIGNED;
|
val.flags |= RTLIL::CONST_FLAG_SIGNED;
|
||||||
|
if (is_unsized)
|
||||||
|
val.flags |= RTLIL::CONST_FLAG_UNSIZED;
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1766,7 +1768,10 @@ static std::string serialize_param_value(const RTLIL::Const &val) {
|
||||||
res.push_back('s');
|
res.push_back('s');
|
||||||
if (val.flags & RTLIL::ConstFlags::CONST_FLAG_REAL)
|
if (val.flags & RTLIL::ConstFlags::CONST_FLAG_REAL)
|
||||||
res.push_back('r');
|
res.push_back('r');
|
||||||
res += stringf("%d", GetSize(val));
|
if (val.flags & RTLIL::ConstFlags::CONST_FLAG_UNSIZED)
|
||||||
|
res.push_back('u');
|
||||||
|
else
|
||||||
|
res += stringf("%d", GetSize(val));
|
||||||
res.push_back('\'');
|
res.push_back('\'');
|
||||||
res.append(val.as_string("?"));
|
res.append(val.as_string("?"));
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -1860,7 +1865,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
|
||||||
} else if ((it->second.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
} else if ((it->second.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||||
child->children[0] = AstNode::mkconst_str(loc, it->second.decode_string());
|
child->children[0] = AstNode::mkconst_str(loc, it->second.decode_string());
|
||||||
else
|
else
|
||||||
child->children[0] = AstNode::mkconst_bits(loc, it->second.to_bits(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0);
|
child->children[0] = AstNode::mkconst_bits(loc, it->second.to_bits(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0, (it->second.flags & RTLIL::CONST_FLAG_UNSIZED) != 0);
|
||||||
rewritten.insert(it->first);
|
rewritten.insert(it->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2265,9 +2265,13 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
||||||
}
|
}
|
||||||
if (children[0]->type == AST_CONSTANT) {
|
if (children[0]->type == AST_CONSTANT) {
|
||||||
if (width != int(children[0]->bits.size())) {
|
if (width != int(children[0]->bits.size())) {
|
||||||
RTLIL::SigSpec sig(children[0]->bits);
|
RTLIL::Const val;
|
||||||
sig.extend_u0(width, children[0]->is_signed);
|
if (children[0]->is_unsized) {
|
||||||
children[0] = mkconst_bits(location, sig.as_const().to_bits(), is_signed);
|
val = children[0]->bitsAsUnsizedConst(width);
|
||||||
|
} else {
|
||||||
|
val = children[0]->bitsAsConst(width);
|
||||||
|
}
|
||||||
|
children[0] = mkconst_bits(location, val.to_bits(), is_signed);
|
||||||
fixup_hierarchy_flags();
|
fixup_hierarchy_flags();
|
||||||
}
|
}
|
||||||
children[0]->is_signed = is_signed;
|
children[0]->is_signed = is_signed;
|
||||||
|
|
|
||||||
|
|
@ -245,7 +245,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
|
||||||
if (undef_wire != nullptr)
|
if (undef_wire != nullptr)
|
||||||
module->rename(undef_wire, stringf("$undef$%d", ++blif_maxnum));
|
module->rename(undef_wire, stringf("$undef$%d", ++blif_maxnum));
|
||||||
|
|
||||||
autoidx = std::max(autoidx, blif_maxnum+1);
|
autoidx.ensure_at_least(blif_maxnum+1);
|
||||||
blif_maxnum = 0;
|
blif_maxnum = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,14 +40,14 @@ static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&
|
||||||
expr[id_len] == '_' || expr[id_len] == '[' || expr[id_len] == ']') id_len++;
|
expr[id_len] == '_' || expr[id_len] == '[' || expr[id_len] == ']') id_len++;
|
||||||
|
|
||||||
if (id_len == 0)
|
if (id_len == 0)
|
||||||
log_error("Expected identifier at `%s'.\n", expr);
|
log_error("Expected identifier at `%s' in %s.\n", expr, RTLIL::unescape_id(module->name));
|
||||||
|
|
||||||
if (id_len == 1 && (*expr == '0' || *expr == '1'))
|
if (id_len == 1 && (*expr == '0' || *expr == '1'))
|
||||||
return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1;
|
return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1;
|
||||||
|
|
||||||
std::string id = RTLIL::escape_id(std::string(expr, id_len));
|
std::string id = RTLIL::escape_id(std::string(expr, id_len));
|
||||||
if (!module->wires_.count(id))
|
if (!module->wires_.count(id))
|
||||||
log_error("Can't resolve wire name %s.\n", RTLIL::unescape_id(id));
|
log_error("Can't resolve wire name %s in %s.\n", RTLIL::unescape_id(id), RTLIL::unescape_id(module->name));
|
||||||
|
|
||||||
expr += id_len;
|
expr += id_len;
|
||||||
return module->wires_.at(id);
|
return module->wires_.at(id);
|
||||||
|
|
@ -174,7 +174,7 @@ static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (stack.size() != 1 || stack.back().type != 3)
|
if (stack.size() != 1 || stack.back().type != 3)
|
||||||
log_error("Parser error in function expr `%s'.\n", orig_expr);
|
log_error("Parser error in function expr `%s'in %s.\n", orig_expr, RTLIL::unescape_id(module->name));
|
||||||
|
|
||||||
return stack.back().sig;
|
return stack.back().sig;
|
||||||
}
|
}
|
||||||
|
|
@ -191,11 +191,23 @@ static RTLIL::SigSpec create_tristate(RTLIL::Module *module, RTLIL::SigSpec func
|
||||||
return cell->getPort(ID::Y);
|
return cell->getPort(ID::Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void create_latch_ff_wires(RTLIL::Module *module, const LibertyAst *node)
|
||||||
|
{
|
||||||
|
module->addWire(RTLIL::escape_id(node->args.at(0)));
|
||||||
|
module->addWire(RTLIL::escape_id(node->args.at(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::pair<RTLIL::SigSpec, RTLIL::SigSpec> find_latch_ff_wires(RTLIL::Module *module, const LibertyAst *node)
|
||||||
|
{
|
||||||
|
auto* iq_wire = module->wire(RTLIL::escape_id(node->args.at(0)));
|
||||||
|
auto* iqn_wire = module->wire(RTLIL::escape_id(node->args.at(1)));
|
||||||
|
log_assert(iq_wire && iqn_wire);
|
||||||
|
return std::make_pair(iq_wire, iqn_wire);
|
||||||
|
}
|
||||||
|
|
||||||
static void create_ff(RTLIL::Module *module, const LibertyAst *node)
|
static void create_ff(RTLIL::Module *module, const LibertyAst *node)
|
||||||
{
|
{
|
||||||
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
|
auto [iq_sig, iqn_sig] = find_latch_ff_wires(module, node);
|
||||||
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
|
|
||||||
|
|
||||||
RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig;
|
RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig;
|
||||||
bool clk_polarity = true, clear_polarity = true, preset_polarity = true;
|
bool clk_polarity = true, clear_polarity = true, preset_polarity = true;
|
||||||
|
|
||||||
|
|
@ -211,7 +223,7 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clk_sig.size() == 0 || data_sig.size() == 0)
|
if (clk_sig.size() == 0 || data_sig.size() == 0)
|
||||||
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", log_id(module->name));
|
log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", RTLIL::unescape_id(module->name));
|
||||||
|
|
||||||
for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
|
for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
|
||||||
{
|
{
|
||||||
|
|
@ -270,9 +282,7 @@ static void create_ff(RTLIL::Module *module, const LibertyAst *node)
|
||||||
|
|
||||||
static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool flag_ignore_miss_data_latch)
|
static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool flag_ignore_miss_data_latch)
|
||||||
{
|
{
|
||||||
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
|
auto [iq_sig, iqn_sig] = find_latch_ff_wires(module, node);
|
||||||
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
|
|
||||||
|
|
||||||
RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig;
|
RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig;
|
||||||
bool enable_polarity = true, clear_polarity = true, preset_polarity = true;
|
bool enable_polarity = true, clear_polarity = true, preset_polarity = true;
|
||||||
|
|
||||||
|
|
@ -289,9 +299,9 @@ static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool fla
|
||||||
|
|
||||||
if (enable_sig.size() == 0 || data_sig.size() == 0) {
|
if (enable_sig.size() == 0 || data_sig.size() == 0) {
|
||||||
if (!flag_ignore_miss_data_latch)
|
if (!flag_ignore_miss_data_latch)
|
||||||
log_error("Latch cell %s has no data_in and/or enable attribute.\n", log_id(module->name));
|
log_error("Latch cell %s has no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
|
||||||
else
|
else
|
||||||
log("Ignored latch cell %s with no data_in and/or enable attribute.\n", log_id(module->name));
|
log("Ignored latch cell %s with no data_in and/or enable attribute.\n", RTLIL::unescape_id(module->name));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -582,9 +592,9 @@ struct LibertyFrontend : public Frontend {
|
||||||
{
|
{
|
||||||
if (!flag_ignore_miss_dir)
|
if (!flag_ignore_miss_dir)
|
||||||
{
|
{
|
||||||
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), log_id(module->name));
|
log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
|
||||||
} else {
|
} else {
|
||||||
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0));
|
log("Ignoring cell %s with missing or invalid direction for pin %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
|
||||||
delete module;
|
delete module;
|
||||||
goto skip_cell;
|
goto skip_cell;
|
||||||
}
|
}
|
||||||
|
|
@ -596,13 +606,13 @@ struct LibertyFrontend : public Frontend {
|
||||||
if (node->id == "bus" && node->args.size() == 1)
|
if (node->id == "bus" && node->args.size() == 1)
|
||||||
{
|
{
|
||||||
if (flag_ignore_buses) {
|
if (flag_ignore_buses) {
|
||||||
log("Ignoring cell %s with a bus interface %s.\n", log_id(module->name), node->args.at(0));
|
log("Ignoring cell %s with a bus interface %s.\n", RTLIL::unescape_id(module->name), node->args.at(0));
|
||||||
delete module;
|
delete module;
|
||||||
goto skip_cell;
|
goto skip_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!flag_lib)
|
if (!flag_lib)
|
||||||
log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", log_id(cell_name));
|
log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", RTLIL::unescape_id(cell_name));
|
||||||
|
|
||||||
const LibertyAst *dir = node->find("direction");
|
const LibertyAst *dir = node->find("direction");
|
||||||
|
|
||||||
|
|
@ -613,7 +623,7 @@ struct LibertyFrontend : public Frontend {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
|
if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
|
||||||
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), log_id(module->name));
|
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0), RTLIL::unescape_id(module->name));
|
||||||
|
|
||||||
simple_comb_cell = false;
|
simple_comb_cell = false;
|
||||||
|
|
||||||
|
|
@ -624,7 +634,7 @@ struct LibertyFrontend : public Frontend {
|
||||||
|
|
||||||
if (!bus_type_node || !type_map.count(bus_type_node->value))
|
if (!bus_type_node || !type_map.count(bus_type_node->value))
|
||||||
log_error("Unknown or unsupported type for bus interface %s on cell %s.\n",
|
log_error("Unknown or unsupported type for bus interface %s on cell %s.\n",
|
||||||
node->args.at(0).c_str(), log_id(cell_name));
|
node->args.at(0).c_str(), RTLIL::unescape_id(cell_name));
|
||||||
|
|
||||||
int bus_type_width = std::get<0>(type_map.at(bus_type_node->value));
|
int bus_type_width = std::get<0>(type_map.at(bus_type_node->value));
|
||||||
int bus_type_offset = std::get<1>(type_map.at(bus_type_node->value));
|
int bus_type_offset = std::get<1>(type_map.at(bus_type_node->value));
|
||||||
|
|
@ -646,6 +656,13 @@ struct LibertyFrontend : public Frontend {
|
||||||
{
|
{
|
||||||
// some liberty files do not put ff/latch at the beginning of a cell
|
// some liberty files do not put ff/latch at the beginning of a cell
|
||||||
// try to find "ff" or "latch" and create FF/latch _before_ processing all other nodes
|
// try to find "ff" or "latch" and create FF/latch _before_ processing all other nodes
|
||||||
|
// but first, in case of balloon retention cells, we need all ff/latch output wires
|
||||||
|
// defined before we add ff/latch cells
|
||||||
|
for (auto node : cell->children)
|
||||||
|
{
|
||||||
|
if ((node->id == "ff" && node->args.size() == 2) || (node->id == "latch" && node->args.size() == 2))
|
||||||
|
create_latch_ff_wires(module, node);
|
||||||
|
}
|
||||||
for (auto node : cell->children)
|
for (auto node : cell->children)
|
||||||
{
|
{
|
||||||
if (node->id == "ff" && node->args.size() == 2)
|
if (node->id == "ff" && node->args.size() == 2)
|
||||||
|
|
@ -701,9 +718,9 @@ struct LibertyFrontend : public Frontend {
|
||||||
if (dir->value != "inout") { // allow inout with missing function, can be used for power pins
|
if (dir->value != "inout") { // allow inout with missing function, can be used for power pins
|
||||||
if (!flag_ignore_miss_func)
|
if (!flag_ignore_miss_func)
|
||||||
{
|
{
|
||||||
log_error("Missing function on output %s of cell %s.\n", log_id(wire->name), log_id(module->name));
|
log_error("Missing function on output %s of cell %s.\n", RTLIL::unescape_id(wire->name), RTLIL::unescape_id(module->name));
|
||||||
} else {
|
} else {
|
||||||
log("Ignoring cell %s with missing function on output %s.\n", log_id(module->name), log_id(wire->name));
|
log("Ignoring cell %s with missing function on output %s.\n", RTLIL::unescape_id(module->name), RTLIL::unescape_id(wire->name));
|
||||||
delete module;
|
delete module;
|
||||||
goto skip_cell;
|
goto skip_cell;
|
||||||
}
|
}
|
||||||
|
|
@ -757,13 +774,13 @@ struct LibertyFrontend : public Frontend {
|
||||||
if (design->has(cell_name)) {
|
if (design->has(cell_name)) {
|
||||||
Module *existing_mod = design->module(cell_name);
|
Module *existing_mod = design->module(cell_name);
|
||||||
if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) {
|
if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) {
|
||||||
log_error("Re-definition of cell/module %s!\n", log_id(cell_name));
|
log_error("Re-definition of cell/module %s!\n", RTLIL::unescape_id(cell_name));
|
||||||
} else if (flag_nooverwrite) {
|
} else if (flag_nooverwrite) {
|
||||||
log("Ignoring re-definition of module %s.\n", log_id(cell_name));
|
log("Ignoring re-definition of module %s.\n", RTLIL::unescape_id(cell_name));
|
||||||
delete module;
|
delete module;
|
||||||
goto skip_cell;
|
goto skip_cell;
|
||||||
} else {
|
} else {
|
||||||
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", log_id(cell_name));
|
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", RTLIL::unescape_id(cell_name));
|
||||||
design->remove(existing_mod);
|
design->remove(existing_mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -567,10 +567,13 @@ struct RTLILFrontendWorker {
|
||||||
if (try_parse_keyword("parameter")) {
|
if (try_parse_keyword("parameter")) {
|
||||||
bool is_signed = false;
|
bool is_signed = false;
|
||||||
bool is_real = false;
|
bool is_real = false;
|
||||||
|
bool is_unsized = false;
|
||||||
if (try_parse_keyword("signed")) {
|
if (try_parse_keyword("signed")) {
|
||||||
is_signed = true;
|
is_signed = true;
|
||||||
} else if (try_parse_keyword("real")) {
|
} else if (try_parse_keyword("real")) {
|
||||||
is_real = true;
|
is_real = true;
|
||||||
|
} else if (try_parse_keyword("unsized")) {
|
||||||
|
is_unsized = true;
|
||||||
}
|
}
|
||||||
RTLIL::IdString param_name = parse_id();
|
RTLIL::IdString param_name = parse_id();
|
||||||
RTLIL::Const val = parse_const();
|
RTLIL::Const val = parse_const();
|
||||||
|
|
@ -578,6 +581,8 @@ struct RTLILFrontendWorker {
|
||||||
val.flags |= RTLIL::CONST_FLAG_SIGNED;
|
val.flags |= RTLIL::CONST_FLAG_SIGNED;
|
||||||
if (is_real)
|
if (is_real)
|
||||||
val.flags |= RTLIL::CONST_FLAG_REAL;
|
val.flags |= RTLIL::CONST_FLAG_REAL;
|
||||||
|
if (is_unsized)
|
||||||
|
val.flags |= RTLIL::CONST_FLAG_UNSIZED;
|
||||||
cell->parameters.insert({std::move(param_name), std::move(val)});
|
cell->parameters.insert({std::move(param_name), std::move(val)});
|
||||||
expect_eol();
|
expect_eol();
|
||||||
} else if (try_parse_keyword("connect")) {
|
} else if (try_parse_keyword("connect")) {
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,7 @@ X($bweqx)
|
||||||
X($bwmux)
|
X($bwmux)
|
||||||
X($check)
|
X($check)
|
||||||
X($concat)
|
X($concat)
|
||||||
|
X($connect)
|
||||||
X($cover)
|
X($cover)
|
||||||
X($demux)
|
X($demux)
|
||||||
X($dff)
|
X($dff)
|
||||||
|
|
@ -222,6 +223,7 @@ X($get_tag)
|
||||||
X($gt)
|
X($gt)
|
||||||
X($initstate)
|
X($initstate)
|
||||||
X($input)
|
X($input)
|
||||||
|
X($input_port)
|
||||||
X($lcu)
|
X($lcu)
|
||||||
X($le)
|
X($le)
|
||||||
X($live)
|
X($live)
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ extern char yosys_path[PATH_MAX];
|
||||||
#endif
|
#endif
|
||||||
#ifdef YOSYS_ENABLE_TCL
|
#ifdef YOSYS_ENABLE_TCL
|
||||||
namespace Yosys {
|
namespace Yosys {
|
||||||
extern int yosys_tcl_iterp_init(Tcl_Interp *interp);
|
extern int yosys_tcl_interp_init(Tcl_Interp *interp);
|
||||||
extern void yosys_tcl_activate_repl();
|
extern void yosys_tcl_activate_repl();
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -610,7 +610,7 @@ int main(int argc, char **argv)
|
||||||
if (run_tcl_shell) {
|
if (run_tcl_shell) {
|
||||||
#ifdef YOSYS_ENABLE_TCL
|
#ifdef YOSYS_ENABLE_TCL
|
||||||
yosys_tcl_activate_repl();
|
yosys_tcl_activate_repl();
|
||||||
Tcl_Main(argc, argv, yosys_tcl_iterp_init);
|
Tcl_Main(argc, argv, yosys_tcl_interp_init);
|
||||||
#else
|
#else
|
||||||
log_error("Can't exectue TCL shell: this version of yosys is not built with TCL support enabled.\n");
|
log_error("Can't exectue TCL shell: this version of yosys is not built with TCL support enabled.\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -706,9 +706,16 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
for (auto &it : pass_register)
|
for (auto &it : pass_register)
|
||||||
if (it.second->call_counter) {
|
if (it.second->call_counter) {
|
||||||
total_ns += it.second->runtime_ns + 1;
|
auto pass_ns = it.second->runtime_ns + 1;
|
||||||
timedat.insert(make_tuple(it.second->runtime_ns + 1, it.second->call_counter, it.first));
|
total_ns += pass_ns;
|
||||||
|
timedat.insert(make_tuple(pass_ns, it.second->call_counter, it.first));
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
auto gc_ns = RTLIL::OwningIdString::garbage_collection_ns() + 1;
|
||||||
|
total_ns += gc_ns;
|
||||||
|
timedat.insert(make_tuple(gc_ns,
|
||||||
|
RTLIL::OwningIdString::garbage_collection_count(), "id_gc"));
|
||||||
|
}
|
||||||
|
|
||||||
if (timing_details)
|
if (timing_details)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
489
kernel/ff.cc
489
kernel/ff.cc
|
|
@ -21,245 +21,316 @@
|
||||||
|
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
|
|
||||||
FfData::FfData(FfInitVals *initvals, Cell *cell_) : FfData(cell_->module, initvals, cell_->name)
|
// sorry
|
||||||
{
|
template<typename InputType, typename OutputType, typename = std::enable_if_t<std::is_base_of_v<FfTypeData, OutputType>>>
|
||||||
cell = cell_;
|
void manufacture_info(InputType flop, OutputType& info, FfInitVals *initvals) {
|
||||||
sig_q = cell->getPort(ID::Q);
|
Cell* cell = nullptr;
|
||||||
width = GetSize(sig_q);
|
IdString type;
|
||||||
attributes = cell->attributes;
|
constexpr bool have_cell = std::is_same_v<InputType, Cell*>;
|
||||||
|
if constexpr (std::is_same_v<InputType, IdString>) {
|
||||||
|
type = flop;
|
||||||
|
} else {
|
||||||
|
static_assert(std::is_same_v<InputType, Cell*>);
|
||||||
|
cell = flop;
|
||||||
|
type = flop->type;
|
||||||
|
}
|
||||||
|
if constexpr (have_cell) {
|
||||||
|
info.sig_q = cell->getPort(ID::Q);
|
||||||
|
info.width = GetSize(info.sig_q);
|
||||||
|
info.attributes = cell->attributes;
|
||||||
|
if (initvals)
|
||||||
|
info.val_init = (*initvals)(info.sig_q);
|
||||||
|
}
|
||||||
|
|
||||||
if (initvals)
|
|
||||||
val_init = (*initvals)(sig_q);
|
|
||||||
|
|
||||||
std::string type_str = cell->type.str();
|
std::string type_str = type.str();
|
||||||
|
|
||||||
if (cell->type.in(ID($anyinit), 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($sr))) {
|
if (type.in(ID($anyinit), 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($sr))) {
|
||||||
if (cell->type.in(ID($anyinit), ID($ff))) {
|
if (type.in(ID($anyinit), ID($ff))) {
|
||||||
has_gclk = true;
|
info.has_gclk = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
if constexpr (have_cell)
|
||||||
if (cell->type == ID($anyinit)) {
|
info.sig_d = cell->getPort(ID::D);
|
||||||
is_anyinit = true;
|
if (type == ID($anyinit)) {
|
||||||
log_assert(val_init.is_fully_undef());
|
info.is_anyinit = true;
|
||||||
|
if constexpr (have_cell)
|
||||||
|
log_assert(info.val_init.is_fully_undef());
|
||||||
}
|
}
|
||||||
} else if (cell->type == ID($sr)) {
|
} else if (type == ID($sr)) {
|
||||||
// No data input at all.
|
// No data input at all.
|
||||||
} else if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) {
|
} else if (type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) {
|
||||||
has_aload = true;
|
info.has_aload = true;
|
||||||
sig_aload = cell->getPort(ID::EN);
|
if constexpr (have_cell) {
|
||||||
pol_aload = cell->getParam(ID::EN_POLARITY).as_bool();
|
info.sig_aload = cell->getPort(ID::EN);
|
||||||
sig_ad = cell->getPort(ID::D);
|
info.pol_aload = cell->getParam(ID::EN_POLARITY).as_bool();
|
||||||
|
info.sig_ad = cell->getPort(ID::D);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
has_clk = true;
|
info.has_clk = true;
|
||||||
sig_clk = cell->getPort(ID::CLK);
|
if constexpr (have_cell) {
|
||||||
pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool();
|
info.sig_clk = cell->getPort(ID::CLK);
|
||||||
sig_d = cell->getPort(ID::D);
|
info.pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool();
|
||||||
|
info.sig_d = cell->getPort(ID::D);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce))) {
|
if (type.in(ID($dffe), ID($dffsre), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce))) {
|
||||||
has_ce = true;
|
info.has_ce = true;
|
||||||
sig_ce = cell->getPort(ID::EN);
|
if constexpr (have_cell) {
|
||||||
pol_ce = cell->getParam(ID::EN_POLARITY).as_bool();
|
info.sig_ce = cell->getPort(ID::EN);
|
||||||
|
info.pol_ce = cell->getParam(ID::EN_POLARITY).as_bool();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) {
|
if (type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) {
|
||||||
has_sr = true;
|
info.has_sr = true;
|
||||||
sig_clr = cell->getPort(ID::CLR);
|
if constexpr (have_cell) {
|
||||||
sig_set = cell->getPort(ID::SET);
|
info.sig_clr = cell->getPort(ID::CLR);
|
||||||
pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool();
|
info.sig_set = cell->getPort(ID::SET);
|
||||||
pol_set = cell->getParam(ID::SET_POLARITY).as_bool();
|
info.pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool();
|
||||||
|
info.pol_set = cell->getParam(ID::SET_POLARITY).as_bool();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (cell->type.in(ID($aldff), ID($aldffe))) {
|
if (type.in(ID($aldff), ID($aldffe))) {
|
||||||
has_aload = true;
|
info.has_aload = true;
|
||||||
sig_aload = cell->getPort(ID::ALOAD);
|
if constexpr (have_cell) {
|
||||||
pol_aload = cell->getParam(ID::ALOAD_POLARITY).as_bool();
|
info.sig_aload = cell->getPort(ID::ALOAD);
|
||||||
sig_ad = cell->getPort(ID::AD);
|
info.pol_aload = cell->getParam(ID::ALOAD_POLARITY).as_bool();
|
||||||
|
info.sig_ad = cell->getPort(ID::AD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) {
|
if (type.in(ID($adff), ID($adffe), ID($adlatch))) {
|
||||||
has_arst = true;
|
info.has_arst = true;
|
||||||
sig_arst = cell->getPort(ID::ARST);
|
if constexpr (have_cell) {
|
||||||
pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool();
|
info.sig_arst = cell->getPort(ID::ARST);
|
||||||
val_arst = cell->getParam(ID::ARST_VALUE);
|
info.pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool();
|
||||||
|
info.val_arst = cell->getParam(ID::ARST_VALUE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) {
|
if (type.in(ID($sdff), ID($sdffe), ID($sdffce))) {
|
||||||
has_srst = true;
|
info.has_srst = true;
|
||||||
sig_srst = cell->getPort(ID::SRST);
|
if constexpr (have_cell) {
|
||||||
pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool();
|
info.sig_srst = cell->getPort(ID::SRST);
|
||||||
val_srst = cell->getParam(ID::SRST_VALUE);
|
info.pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool();
|
||||||
ce_over_srst = cell->type == ID($sdffce);
|
info.val_srst = cell->getParam(ID::SRST_VALUE);
|
||||||
|
}
|
||||||
|
info.ce_over_srst = type == ID($sdffce);
|
||||||
}
|
}
|
||||||
} else if (cell->type == ID($_FF_)) {
|
} else if (type == ID($_FF_)) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
has_gclk = true;
|
info.has_gclk = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
if constexpr (have_cell)
|
||||||
|
info.sig_d = cell->getPort(ID::D);
|
||||||
} else if (type_str.substr(0, 5) == "$_SR_") {
|
} else if (type_str.substr(0, 5) == "$_SR_") {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
has_sr = true;
|
info.has_sr = true;
|
||||||
pol_set = type_str[5] == 'P';
|
info.pol_set = type_str[5] == 'P';
|
||||||
pol_clr = type_str[6] == 'P';
|
info.pol_clr = type_str[6] == 'P';
|
||||||
sig_set = cell->getPort(ID::S);
|
if constexpr (have_cell) {
|
||||||
sig_clr = cell->getPort(ID::R);
|
info.sig_set = cell->getPort(ID::S);
|
||||||
|
info.sig_clr = cell->getPort(ID::R);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) {
|
} else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[6] == 'P';
|
||||||
pol_clk = type_str[6] == 'P';
|
if constexpr (have_cell) {
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.sig_d = cell->getPort(ID::D);
|
||||||
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) {
|
} else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[7] == 'P';
|
||||||
pol_clk = type_str[7] == 'P';
|
info.has_ce = true;
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.pol_ce = type_str[8] == 'P';
|
||||||
has_ce = true;
|
if constexpr (have_cell) {
|
||||||
pol_ce = type_str[8] == 'P';
|
info.sig_d = cell->getPort(ID::D);
|
||||||
sig_ce = cell->getPort(ID::E);
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
|
info.sig_ce = cell->getPort(ID::E);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) {
|
} else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[6] == 'P';
|
||||||
pol_clk = type_str[6] == 'P';
|
info.has_arst = true;
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.pol_arst = type_str[7] == 'P';
|
||||||
has_arst = true;
|
info.val_arst = type_str[8] == '1' ? State::S1 : State::S0;
|
||||||
pol_arst = type_str[7] == 'P';
|
if constexpr (have_cell) {
|
||||||
sig_arst = cell->getPort(ID::R);
|
info.sig_d = cell->getPort(ID::D);
|
||||||
val_arst = type_str[8] == '1' ? State::S1 : State::S0;
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
|
info.sig_arst = cell->getPort(ID::R);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) {
|
} else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[7] == 'P';
|
||||||
pol_clk = type_str[7] == 'P';
|
info.has_arst = true;
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.pol_arst = type_str[8] == 'P';
|
||||||
has_arst = true;
|
info.val_arst = type_str[9] == '1' ? State::S1 : State::S0;
|
||||||
pol_arst = type_str[8] == 'P';
|
info.has_ce = true;
|
||||||
sig_arst = cell->getPort(ID::R);
|
info.pol_ce = type_str[10] == 'P';
|
||||||
val_arst = type_str[9] == '1' ? State::S1 : State::S0;
|
if constexpr (have_cell) {
|
||||||
has_ce = true;
|
info.sig_d = cell->getPort(ID::D);
|
||||||
pol_ce = type_str[10] == 'P';
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
sig_ce = cell->getPort(ID::E);
|
info.sig_arst = cell->getPort(ID::R);
|
||||||
|
info.sig_ce = cell->getPort(ID::E);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 8) == "$_ALDFF_" && type_str.size() == 11) {
|
} else if (type_str.substr(0, 8) == "$_ALDFF_" && type_str.size() == 11) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[8] == 'P';
|
||||||
pol_clk = type_str[8] == 'P';
|
info.has_aload = true;
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.pol_aload = type_str[9] == 'P';
|
||||||
has_aload = true;
|
if constexpr (have_cell) {
|
||||||
pol_aload = type_str[9] == 'P';
|
info.sig_d = cell->getPort(ID::D);
|
||||||
sig_aload = cell->getPort(ID::L);
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
sig_ad = cell->getPort(ID::AD);
|
info.sig_aload = cell->getPort(ID::L);
|
||||||
|
info.sig_ad = cell->getPort(ID::AD);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 9) == "$_ALDFFE_" && type_str.size() == 13) {
|
} else if (type_str.substr(0, 9) == "$_ALDFFE_" && type_str.size() == 13) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[9] == 'P';
|
||||||
pol_clk = type_str[9] == 'P';
|
info.has_aload = true;
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.pol_aload = type_str[10] == 'P';
|
||||||
has_aload = true;
|
info.has_ce = true;
|
||||||
pol_aload = type_str[10] == 'P';
|
info.pol_ce = type_str[11] == 'P';
|
||||||
sig_aload = cell->getPort(ID::L);
|
if constexpr (have_cell) {
|
||||||
sig_ad = cell->getPort(ID::AD);
|
info.sig_d = cell->getPort(ID::D);
|
||||||
has_ce = true;
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
pol_ce = type_str[11] == 'P';
|
info.sig_aload = cell->getPort(ID::L);
|
||||||
sig_ce = cell->getPort(ID::E);
|
info.sig_ad = cell->getPort(ID::AD);
|
||||||
|
info.sig_ce = cell->getPort(ID::E);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) {
|
} else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[8] == 'P';
|
||||||
pol_clk = type_str[8] == 'P';
|
info.has_sr = true;
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.pol_set = type_str[9] == 'P';
|
||||||
has_sr = true;
|
info.pol_clr = type_str[10] == 'P';
|
||||||
pol_set = type_str[9] == 'P';
|
if constexpr (have_cell) {
|
||||||
pol_clr = type_str[10] == 'P';
|
info.sig_d = cell->getPort(ID::D);
|
||||||
sig_set = cell->getPort(ID::S);
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
sig_clr = cell->getPort(ID::R);
|
info.sig_set = cell->getPort(ID::S);
|
||||||
|
info.sig_clr = cell->getPort(ID::R);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) {
|
} else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[9] == 'P';
|
||||||
pol_clk = type_str[9] == 'P';
|
info.has_sr = true;
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.pol_set = type_str[10] == 'P';
|
||||||
has_sr = true;
|
info.pol_clr = type_str[11] == 'P';
|
||||||
pol_set = type_str[10] == 'P';
|
info.has_ce = true;
|
||||||
pol_clr = type_str[11] == 'P';
|
info.pol_ce = type_str[12] == 'P';
|
||||||
sig_set = cell->getPort(ID::S);
|
if constexpr (have_cell) {
|
||||||
sig_clr = cell->getPort(ID::R);
|
info.sig_d = cell->getPort(ID::D);
|
||||||
has_ce = true;
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
pol_ce = type_str[12] == 'P';
|
info.sig_set = cell->getPort(ID::S);
|
||||||
sig_ce = cell->getPort(ID::E);
|
info.sig_clr = cell->getPort(ID::R);
|
||||||
|
info.sig_ce = cell->getPort(ID::E);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) {
|
} else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[7] == 'P';
|
||||||
pol_clk = type_str[7] == 'P';
|
info.has_srst = true;
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.pol_srst = type_str[8] == 'P';
|
||||||
has_srst = true;
|
info.val_srst = type_str[9] == '1' ? State::S1 : State::S0;
|
||||||
pol_srst = type_str[8] == 'P';
|
if constexpr (have_cell) {
|
||||||
sig_srst = cell->getPort(ID::R);
|
info.sig_d = cell->getPort(ID::D);
|
||||||
val_srst = type_str[9] == '1' ? State::S1 : State::S0;
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
|
info.sig_srst = cell->getPort(ID::R);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) {
|
} else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[8] == 'P';
|
||||||
pol_clk = type_str[8] == 'P';
|
info.has_srst = true;
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.pol_srst = type_str[9] == 'P';
|
||||||
has_srst = true;
|
info.val_srst = type_str[10] == '1' ? State::S1 : State::S0;
|
||||||
pol_srst = type_str[9] == 'P';
|
info.has_ce = true;
|
||||||
sig_srst = cell->getPort(ID::R);
|
info.pol_ce = type_str[11] == 'P';
|
||||||
val_srst = type_str[10] == '1' ? State::S1 : State::S0;
|
if constexpr (have_cell) {
|
||||||
has_ce = true;
|
info.sig_d = cell->getPort(ID::D);
|
||||||
pol_ce = type_str[11] == 'P';
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
sig_ce = cell->getPort(ID::E);
|
info.sig_srst = cell->getPort(ID::R);
|
||||||
|
info.sig_ce = cell->getPort(ID::E);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) {
|
} else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
sig_d = cell->getPort(ID::D);
|
info.has_clk = true;
|
||||||
has_clk = true;
|
info.pol_clk = type_str[9] == 'P';
|
||||||
pol_clk = type_str[9] == 'P';
|
info.has_srst = true;
|
||||||
sig_clk = cell->getPort(ID::C);
|
info.pol_srst = type_str[10] == 'P';
|
||||||
has_srst = true;
|
info.val_srst = type_str[11] == '1' ? State::S1 : State::S0;
|
||||||
pol_srst = type_str[10] == 'P';
|
info.has_ce = true;
|
||||||
sig_srst = cell->getPort(ID::R);
|
info.pol_ce = type_str[12] == 'P';
|
||||||
val_srst = type_str[11] == '1' ? State::S1 : State::S0;
|
info.ce_over_srst = true;
|
||||||
has_ce = true;
|
if constexpr (have_cell) {
|
||||||
pol_ce = type_str[12] == 'P';
|
info.sig_d = cell->getPort(ID::D);
|
||||||
sig_ce = cell->getPort(ID::E);
|
info.sig_clk = cell->getPort(ID::C);
|
||||||
ce_over_srst = true;
|
info.sig_srst = cell->getPort(ID::R);
|
||||||
|
info.sig_ce = cell->getPort(ID::E);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) {
|
} else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
has_aload = true;
|
info.has_aload = true;
|
||||||
sig_ad = cell->getPort(ID::D);
|
info.has_aload = true;
|
||||||
has_aload = true;
|
info.pol_aload = type_str[9] == 'P';
|
||||||
pol_aload = type_str[9] == 'P';
|
if constexpr (have_cell) {
|
||||||
sig_aload = cell->getPort(ID::E);
|
info.sig_ad = cell->getPort(ID::D);
|
||||||
|
info.sig_aload = cell->getPort(ID::E);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) {
|
} else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
has_aload = true;
|
info.has_aload = true;
|
||||||
sig_ad = cell->getPort(ID::D);
|
info.has_aload = true;
|
||||||
has_aload = true;
|
info.pol_aload = type_str[9] == 'P';
|
||||||
pol_aload = type_str[9] == 'P';
|
info.has_arst = true;
|
||||||
sig_aload = cell->getPort(ID::E);
|
info.pol_arst = type_str[10] == 'P';
|
||||||
has_arst = true;
|
info.val_arst = type_str[11] == '1' ? State::S1 : State::S0;
|
||||||
pol_arst = type_str[10] == 'P';
|
if constexpr (have_cell) {
|
||||||
sig_arst = cell->getPort(ID::R);
|
info.sig_ad = cell->getPort(ID::D);
|
||||||
val_arst = type_str[11] == '1' ? State::S1 : State::S0;
|
info.sig_aload = cell->getPort(ID::E);
|
||||||
|
info.sig_arst = cell->getPort(ID::R);
|
||||||
|
}
|
||||||
} else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) {
|
} else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) {
|
||||||
is_fine = true;
|
info.is_fine = true;
|
||||||
has_aload = true;
|
info.has_aload = true;
|
||||||
sig_ad = cell->getPort(ID::D);
|
info.has_aload = true;
|
||||||
has_aload = true;
|
info.pol_aload = type_str[11] == 'P';
|
||||||
pol_aload = type_str[11] == 'P';
|
info.has_sr = true;
|
||||||
sig_aload = cell->getPort(ID::E);
|
info.pol_set = type_str[12] == 'P';
|
||||||
has_sr = true;
|
info.pol_clr = type_str[13] == 'P';
|
||||||
pol_set = type_str[12] == 'P';
|
if constexpr (have_cell) {
|
||||||
pol_clr = type_str[13] == 'P';
|
info.sig_ad = cell->getPort(ID::D);
|
||||||
sig_set = cell->getPort(ID::S);
|
info.sig_aload = cell->getPort(ID::E);
|
||||||
sig_clr = cell->getPort(ID::R);
|
info.sig_set = cell->getPort(ID::S);
|
||||||
|
info.sig_clr = cell->getPort(ID::R);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log_assert(0);
|
log_assert(0);
|
||||||
}
|
}
|
||||||
if (has_aload && !has_clk && !has_sr && !has_arst && sig_ad.is_fully_const()) {
|
if constexpr (have_cell)
|
||||||
// Plain D latches with const D treated specially.
|
if (info.has_aload && !info.has_clk && !info.has_sr && !info.has_arst && info.sig_ad.is_fully_const()) {
|
||||||
has_aload = false;
|
// Plain D latches with const D treated specially.
|
||||||
has_arst = true;
|
info.has_aload = false;
|
||||||
sig_arst = sig_aload;
|
info.has_arst = true;
|
||||||
pol_arst = pol_aload;
|
info.sig_arst = info.sig_aload;
|
||||||
val_arst = sig_ad.as_const();
|
info.pol_arst = info.pol_aload;
|
||||||
}
|
info.val_arst = info.sig_ad.as_const();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FfTypeData::FfTypeData(IdString type) : FfTypeData()
|
||||||
|
{
|
||||||
|
manufacture_info(type, *this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
FfData::FfData(FfInitVals *initvals, Cell *cell_) : FfData(cell_->module, initvals, cell_->name)
|
||||||
|
{
|
||||||
|
cell = cell_;
|
||||||
|
manufacture_info(cell, *this, initvals);
|
||||||
}
|
}
|
||||||
|
|
||||||
FfData FfData::slice(const std::vector<int> &bits) {
|
FfData FfData::slice(const std::vector<int> &bits) {
|
||||||
|
|
|
||||||
78
kernel/ff.h
78
kernel/ff.h
|
|
@ -78,31 +78,20 @@ YOSYS_NAMESPACE_BEGIN
|
||||||
// - has_arst [does not correspond to a native cell, represented as dlatch with const D input]
|
// - has_arst [does not correspond to a native cell, represented as dlatch with const D input]
|
||||||
// - empty set [not a cell — will be emitted as a simple direct connection]
|
// - empty set [not a cell — will be emitted as a simple direct connection]
|
||||||
|
|
||||||
struct FfData {
|
struct FfTypeData {
|
||||||
Module *module;
|
FfTypeData(IdString type);
|
||||||
FfInitVals *initvals;
|
FfTypeData() {
|
||||||
Cell *cell;
|
has_clk = false;
|
||||||
IdString name;
|
has_gclk = false;
|
||||||
// The FF output.
|
has_ce = false;
|
||||||
SigSpec sig_q;
|
has_aload = false;
|
||||||
// The sync data input, present if has_clk or has_gclk.
|
has_srst = false;
|
||||||
SigSpec sig_d;
|
has_arst = false;
|
||||||
// The async data input, present if has_aload.
|
has_sr = false;
|
||||||
SigSpec sig_ad;
|
ce_over_srst = false;
|
||||||
// The sync clock, present if has_clk.
|
is_fine = false;
|
||||||
SigSpec sig_clk;
|
is_anyinit = false;
|
||||||
// The clock enable, present if has_ce.
|
}
|
||||||
SigSpec sig_ce;
|
|
||||||
// The async load enable, present if has_aload.
|
|
||||||
SigSpec sig_aload;
|
|
||||||
// The async reset, preset if has_arst.
|
|
||||||
SigSpec sig_arst;
|
|
||||||
// The sync reset, preset if has_srst.
|
|
||||||
SigSpec sig_srst;
|
|
||||||
// The async clear (per-lane), present if has_sr.
|
|
||||||
SigSpec sig_clr;
|
|
||||||
// The async set (per-lane), present if has_sr.
|
|
||||||
SigSpec sig_set;
|
|
||||||
// True if this is a clocked (edge-sensitive) flip-flop.
|
// True if this is a clocked (edge-sensitive) flip-flop.
|
||||||
bool has_clk;
|
bool has_clk;
|
||||||
// True if this is a $ff, exclusive with every other has_*.
|
// True if this is a $ff, exclusive with every other has_*.
|
||||||
|
|
@ -143,9 +132,38 @@ struct FfData {
|
||||||
bool pol_clr;
|
bool pol_clr;
|
||||||
bool pol_set;
|
bool pol_set;
|
||||||
// The value loaded by sig_arst.
|
// The value loaded by sig_arst.
|
||||||
|
// Zero length if unknowable from just type
|
||||||
Const val_arst;
|
Const val_arst;
|
||||||
// The value loaded by sig_srst.
|
// The value loaded by sig_srst.
|
||||||
|
// Zero length if unknowable from just type
|
||||||
Const val_srst;
|
Const val_srst;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FfData : FfTypeData {
|
||||||
|
Module *module;
|
||||||
|
FfInitVals *initvals;
|
||||||
|
Cell *cell;
|
||||||
|
IdString name;
|
||||||
|
// The FF output.
|
||||||
|
SigSpec sig_q;
|
||||||
|
// The sync data input, present if has_clk or has_gclk.
|
||||||
|
SigSpec sig_d;
|
||||||
|
// The async data input, present if has_aload.
|
||||||
|
SigSpec sig_ad;
|
||||||
|
// The sync clock, present if has_clk.
|
||||||
|
SigSpec sig_clk;
|
||||||
|
// The clock enable, present if has_ce.
|
||||||
|
SigSpec sig_ce;
|
||||||
|
// The async load enable, present if has_aload.
|
||||||
|
SigSpec sig_aload;
|
||||||
|
// The async reset, preset if has_arst.
|
||||||
|
SigSpec sig_arst;
|
||||||
|
// The sync reset, preset if has_srst.
|
||||||
|
SigSpec sig_srst;
|
||||||
|
// The async clear (per-lane), present if has_sr.
|
||||||
|
SigSpec sig_clr;
|
||||||
|
// The async set (per-lane), present if has_sr.
|
||||||
|
SigSpec sig_set;
|
||||||
// The initial value at power-up.
|
// The initial value at power-up.
|
||||||
Const val_init;
|
Const val_init;
|
||||||
// The FF data width in bits.
|
// The FF data width in bits.
|
||||||
|
|
@ -154,16 +172,6 @@ struct FfData {
|
||||||
|
|
||||||
FfData(Module *module = nullptr, FfInitVals *initvals = nullptr, IdString name = IdString()) : module(module), initvals(initvals), cell(nullptr), name(name) {
|
FfData(Module *module = nullptr, FfInitVals *initvals = nullptr, IdString name = IdString()) : module(module), initvals(initvals), cell(nullptr), name(name) {
|
||||||
width = 0;
|
width = 0;
|
||||||
has_clk = false;
|
|
||||||
has_gclk = false;
|
|
||||||
has_ce = false;
|
|
||||||
has_aload = false;
|
|
||||||
has_srst = false;
|
|
||||||
has_arst = false;
|
|
||||||
has_sr = false;
|
|
||||||
ce_over_srst = false;
|
|
||||||
is_fine = false;
|
|
||||||
is_anyinit = false;
|
|
||||||
pol_clk = false;
|
pol_clk = false;
|
||||||
pol_aload = false;
|
pol_aload = false;
|
||||||
pol_ce = false;
|
pol_ce = false;
|
||||||
|
|
|
||||||
|
|
@ -606,7 +606,7 @@ void format_emit_idstring(std::string &result, std::string_view spec, int *dynam
|
||||||
{
|
{
|
||||||
if (spec == "%s") {
|
if (spec == "%s") {
|
||||||
// Format checking will have guaranteed num_dynamic_ints == 0.
|
// Format checking will have guaranteed num_dynamic_ints == 0.
|
||||||
result += arg.c_str();
|
arg.append_to(&result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg.c_str());
|
format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg.c_str());
|
||||||
|
|
|
||||||
|
|
@ -720,7 +720,7 @@ dict<std::string, std::pair<std::string, int>> get_coverage_data()
|
||||||
if (coverage_data.count(p->id))
|
if (coverage_data.count(p->id))
|
||||||
log_warning("found duplicate coverage id \"%s\".\n", p->id);
|
log_warning("found duplicate coverage id \"%s\".\n", p->id);
|
||||||
coverage_data[p->id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
|
coverage_data[p->id].first = stringf("%s:%d:%s", p->file, p->line, p->func);
|
||||||
coverage_data[p->id].second += p->counter;
|
coverage_data[p->id].second += p->counter.load(std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &it : coverage_data)
|
for (auto &it : coverage_data)
|
||||||
|
|
|
||||||
10
kernel/log.h
10
kernel/log.h
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#define YS_REGEX_COMPILE(param) std::regex(param, \
|
#define YS_REGEX_COMPILE(param) std::regex(param, \
|
||||||
std::regex_constants::nosubs | \
|
std::regex_constants::nosubs | \
|
||||||
|
|
@ -298,15 +299,16 @@ void log_abort_internal(const char *file, int line);
|
||||||
|
|
||||||
#define cover(_id) do { \
|
#define cover(_id) do { \
|
||||||
static CoverData __d __attribute__((section("yosys_cover_list"), aligned(1), used)) = { __FILE__, __FUNCTION__, _id, __LINE__, 0 }; \
|
static CoverData __d __attribute__((section("yosys_cover_list"), aligned(1), used)) = { __FILE__, __FUNCTION__, _id, __LINE__, 0 }; \
|
||||||
__d.counter++; \
|
__d.counter.fetch_add(1, std::memory_order_relaxed); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
struct CoverData {
|
struct CoverData {
|
||||||
const char *file, *func, *id;
|
const char *file, *func, *id;
|
||||||
int line, counter;
|
int line;
|
||||||
} YS_ATTRIBUTE(packed);
|
std::atomic<int> counter;
|
||||||
|
};
|
||||||
|
|
||||||
// this two symbols are created by the linker for the "yosys_cover_list" ELF section
|
// this two symbols are created by the linker __start_yosys_cover_listfor the "yosys_cover_list" ELF section
|
||||||
extern "C" struct CoverData __start_yosys_cover_list[];
|
extern "C" struct CoverData __start_yosys_cover_list[];
|
||||||
extern "C" struct CoverData __stop_yosys_cover_list[];
|
extern "C" struct CoverData __stop_yosys_cover_list[];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,23 @@ std::map<std::string, Backend*> backend_register;
|
||||||
|
|
||||||
std::vector<std::string> Frontend::next_args;
|
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::Pass(std::string name, std::string short_help, source_location location) :
|
||||||
pass_name(name), short_help(short_help), 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;
|
int64_t time_ns = PerformanceTimer::query() - state.begin_ns;
|
||||||
runtime_ns += time_ns;
|
runtime_ns += time_ns;
|
||||||
current_pass = state.parent_pass;
|
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)
|
if (current_pass)
|
||||||
current_pass->runtime_ns -= time_ns;
|
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)
|
if (pass_register.count(args[0]) == 0)
|
||||||
log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[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]);
|
log_experimental(args[0]);
|
||||||
|
|
||||||
size_t orig_sel_stack_pos = design->selection_stack.size();
|
size_t orig_sel_stack_pos = design->selection_stack.size();
|
||||||
auto state = pass_register[args[0]]->pre_execute();
|
auto state = pass->pre_execute();
|
||||||
pass_register[args[0]]->execute(args, design);
|
pass->execute(args, design);
|
||||||
pass_register[args[0]]->post_execute(state);
|
pass->post_execute(state);
|
||||||
while (design->selection_stack.size() > orig_sel_stack_pos)
|
while (design->selection_stack.size() > orig_sel_stack_pos)
|
||||||
design->pop_selection();
|
design->pop_selection();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,30 @@ struct source_location { // dummy placeholder
|
||||||
|
|
||||||
YOSYS_NAMESPACE_BEGIN
|
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
|
struct Pass
|
||||||
{
|
{
|
||||||
std::string pass_name, short_help;
|
std::string pass_name, short_help;
|
||||||
|
|
@ -71,6 +95,8 @@ struct Pass
|
||||||
bool experimental_flag = false;
|
bool experimental_flag = false;
|
||||||
bool internal_flag = false;
|
bool internal_flag = false;
|
||||||
|
|
||||||
|
static void subtract_from_current_runtime_ns(int64_t time_ns);
|
||||||
|
|
||||||
void experimental() {
|
void experimental() {
|
||||||
experimental_flag = true;
|
experimental_flag = true;
|
||||||
}
|
}
|
||||||
|
|
@ -108,6 +134,10 @@ struct Pass
|
||||||
virtual void on_register();
|
virtual void on_register();
|
||||||
virtual void on_shutdown();
|
virtual void on_shutdown();
|
||||||
virtual bool replace_existing_pass() const { return false; }
|
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
|
struct ScriptPass : Pass
|
||||||
|
|
@ -126,6 +156,8 @@ struct ScriptPass : Pass
|
||||||
void run_nocheck(std::string command, std::string info = std::string());
|
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 run_script(RTLIL::Design *design, std::string run_from = std::string(), std::string run_to = std::string());
|
||||||
void help_script();
|
void help_script();
|
||||||
|
|
||||||
|
bool allow_garbage_collection_during_pass() const override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Frontend : Pass
|
struct Frontend : Pass
|
||||||
|
|
|
||||||
236
kernel/rtlil.cc
236
kernel/rtlil.cc
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <charconv>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
|
@ -35,20 +36,11 @@ YOSYS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
bool RTLIL::IdString::destruct_guard_ok = false;
|
bool RTLIL::IdString::destruct_guard_ok = false;
|
||||||
RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard;
|
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<std::string_view, int> RTLIL::IdString::global_id_index_;
|
||||||
#ifndef YOSYS_NO_IDS_REFCNT
|
std::unordered_map<int, RTLIL::IdString::AutoidxStorage> RTLIL::IdString::global_autoidx_id_storage_;
|
||||||
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_;
|
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)
|
static void populate(std::string_view name)
|
||||||
{
|
{
|
||||||
|
|
@ -57,21 +49,85 @@ static void populate(std::string_view name)
|
||||||
name = name.substr(1);
|
name = name.substr(1);
|
||||||
}
|
}
|
||||||
RTLIL::IdString::global_id_index_.insert({name, GetSize(RTLIL::IdString::global_id_storage_)});
|
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()
|
void RTLIL::IdString::prepopulate()
|
||||||
{
|
{
|
||||||
int size = static_cast<short>(RTLIL::StaticId::STATIC_ID_END);
|
int size = static_cast<short>(RTLIL::StaticId::STATIC_ID_END);
|
||||||
global_id_storage_.reserve(size);
|
global_id_storage_.reserve(size);
|
||||||
RTLIL::IdString::global_id_storage_.push_back(const_cast<char*>(""));
|
|
||||||
global_id_index_.reserve(size);
|
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);
|
#define X(N) populate("\\" #N);
|
||||||
#include "kernel/constids.inc"
|
#include "kernel/constids.inc"
|
||||||
#undef X
|
#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 autoidx_it = global_autoidx_id_storage_.find(-*p_autoidx);
|
||||||
|
if (autoidx_it != global_autoidx_id_storage_.end() &&
|
||||||
|
p.substr(0, autoidx_pos) == *autoidx_it->second.prefix)
|
||||||
|
return -*p_autoidx;
|
||||||
|
// Ensure NEW_ID/NEW_ID_SUFFIX will not create collisions with the ID
|
||||||
|
// we're about to create.
|
||||||
|
autoidx.ensure_at_least(*p_autoidx + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
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).buf, idx, refcount(idx));
|
||||||
|
#endif
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr bool check_well_known_id_order()
|
static constexpr bool check_well_known_id_order()
|
||||||
{
|
{
|
||||||
int size = sizeof(IdTable) / sizeof(IdTable[0]);
|
int size = sizeof(IdTable) / sizeof(IdTable[0]);
|
||||||
|
|
@ -85,6 +141,150 @@ static constexpr bool check_well_known_id_order()
|
||||||
// and in sorted ascii order, as required by the ID macro.
|
// and in sorted ascii order, as required by the ID macro.
|
||||||
static_assert(check_well_known_id_order());
|
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();
|
||||||
|
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_storage_.begin(); it != global_autoidx_id_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;
|
||||||
|
}
|
||||||
|
it = global_autoidx_id_storage_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
dict<std::string, std::string> RTLIL::constpad;
|
||||||
|
|
||||||
static const pool<IdString> &builtin_ff_cell_types_internal() {
|
static const pool<IdString> &builtin_ff_cell_types_internal() {
|
||||||
|
|
@ -1083,9 +1283,7 @@ RTLIL::Design::Design()
|
||||||
refcount_modules_ = 0;
|
refcount_modules_ = 0;
|
||||||
push_full_selection();
|
push_full_selection();
|
||||||
|
|
||||||
#ifdef YOSYS_ENABLE_PYTHON
|
|
||||||
RTLIL::Design::get_all_designs()->insert(std::pair<unsigned int, RTLIL::Design*>(hashidx_, this));
|
RTLIL::Design::get_all_designs()->insert(std::pair<unsigned int, RTLIL::Design*>(hashidx_, this));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RTLIL::Design::~Design()
|
RTLIL::Design::~Design()
|
||||||
|
|
@ -1094,18 +1292,14 @@ RTLIL::Design::~Design()
|
||||||
delete pr.second;
|
delete pr.second;
|
||||||
for (auto n : bindings_)
|
for (auto n : bindings_)
|
||||||
delete n;
|
delete n;
|
||||||
#ifdef YOSYS_ENABLE_PYTHON
|
|
||||||
RTLIL::Design::get_all_designs()->erase(hashidx_);
|
RTLIL::Design::get_all_designs()->erase(hashidx_);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef YOSYS_ENABLE_PYTHON
|
|
||||||
static std::map<unsigned int, RTLIL::Design*> all_designs;
|
static std::map<unsigned int, RTLIL::Design*> all_designs;
|
||||||
std::map<unsigned int, RTLIL::Design*> *RTLIL::Design::get_all_designs(void)
|
std::map<unsigned int, RTLIL::Design*> *RTLIL::Design::get_all_designs(void)
|
||||||
{
|
{
|
||||||
return &all_designs;
|
return &all_designs;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules()
|
RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
599
kernel/rtlil.h
599
kernel/rtlil.h
|
|
@ -53,10 +53,11 @@ namespace RTLIL
|
||||||
// Semantic metadata - how can this constant be interpreted?
|
// Semantic metadata - how can this constant be interpreted?
|
||||||
// Values may be generally non-exclusive
|
// Values may be generally non-exclusive
|
||||||
enum ConstFlags : unsigned char {
|
enum ConstFlags : unsigned char {
|
||||||
CONST_FLAG_NONE = 0,
|
CONST_FLAG_NONE = 0,
|
||||||
CONST_FLAG_STRING = 1,
|
CONST_FLAG_STRING = 1,
|
||||||
CONST_FLAG_SIGNED = 2, // only used for parameters
|
CONST_FLAG_SIGNED = 2, // only used for parameters
|
||||||
CONST_FLAG_REAL = 4 // only used for parameters
|
CONST_FLAG_REAL = 4, // only used for parameters
|
||||||
|
CONST_FLAG_UNSIZED = 8, // only used for parameters
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SelectPartials : unsigned char {
|
enum SelectPartials : unsigned char {
|
||||||
|
|
@ -120,27 +121,30 @@ namespace RTLIL
|
||||||
struct Process;
|
struct Process;
|
||||||
struct Binding;
|
struct Binding;
|
||||||
struct IdString;
|
struct IdString;
|
||||||
struct StaticIdString;
|
struct OwningIdString;
|
||||||
|
|
||||||
typedef std::pair<SigSpec, SigSpec> SigSig;
|
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 RTLIL::IdString
|
||||||
{
|
{
|
||||||
#undef YOSYS_XTRACE_GET_PUT
|
struct Storage {
|
||||||
#undef YOSYS_SORT_ID_FREE_LIST
|
char *buf;
|
||||||
#undef YOSYS_USE_STICKY_IDS
|
int size;
|
||||||
#undef YOSYS_NO_IDS_REFCNT
|
|
||||||
|
std::string_view str_view() const { return {buf, static_cast<size_t>(size)}; }
|
||||||
|
};
|
||||||
|
struct AutoidxStorage {
|
||||||
|
// Append the negated (i.e. positive) ID to this string to get
|
||||||
|
// the real string. The prefix strings must live forever.
|
||||||
|
const std::string *prefix;
|
||||||
|
// Cache of the full string, or nullptr if not cached yet.
|
||||||
|
std::atomic<char *> full_str;
|
||||||
|
|
||||||
|
AutoidxStorage(const std::string *prefix) : prefix(prefix), full_str(nullptr) {}
|
||||||
|
AutoidxStorage(AutoidxStorage&& other) : prefix(other.prefix), full_str(other.full_str.exchange(nullptr, std::memory_order_relaxed)) {}
|
||||||
|
~AutoidxStorage() { delete[] full_str.load(std::memory_order_acquire); }
|
||||||
|
};
|
||||||
|
|
||||||
// the global id string cache
|
// the global id string cache
|
||||||
|
|
||||||
|
|
@ -150,200 +154,82 @@ struct RTLIL::IdString
|
||||||
~destruct_guard_t() { destruct_guard_ok = false; }
|
~destruct_guard_t() { destruct_guard_ok = false; }
|
||||||
} destruct_guard;
|
} 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_;
|
static std::unordered_map<std::string_view, int> global_id_index_;
|
||||||
#ifndef YOSYS_NO_IDS_REFCNT
|
// Storage for autoidx IDs, which have negative indices, i.e. all entries in this
|
||||||
// For prepopulated IdStrings, the refcount is meaningless since they
|
// map have negative keys.
|
||||||
// are never freed even if the refcount is zero. For code efficiency
|
static std::unordered_map<int, AutoidxStorage> global_autoidx_id_storage_;
|
||||||
// we increment the refcount of prepopulated IdStrings like any other string,
|
// All (index, refcount) pairs in this map have refcount > 0.
|
||||||
// but we never decrement the refcount or check whether it's zero.
|
static std::unordered_map<int, int> global_refcount_storage_;
|
||||||
// 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_;
|
|
||||||
static std::vector<int> global_free_idx_list_;
|
static std::vector<int> global_free_idx_list_;
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef YOSYS_USE_STICKY_IDS
|
static int refcount(int idx) {
|
||||||
static int last_created_idx_ptr_;
|
auto it = global_refcount_storage_.find(idx);
|
||||||
static int last_created_idx_[8];
|
if (it == global_refcount_storage_.end())
|
||||||
#endif
|
return 0;
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void xtrace_db_dump()
|
static inline void xtrace_db_dump()
|
||||||
{
|
{
|
||||||
#ifdef YOSYS_XTRACE_GET_PUT
|
#ifdef YOSYS_XTRACE_GET_PUT
|
||||||
for (int idx = 0; idx < GetSize(global_id_storage_); idx++)
|
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);
|
log("#X# DB-DUMP index %d: FREE\n", idx);
|
||||||
else
|
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, global_id_storage_.at(idx).buf, refcount(idx));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void checkpoint()
|
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
|
#ifdef YOSYS_SORT_ID_FREE_LIST
|
||||||
std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater<int>());
|
std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater<int>());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int get_reference(int idx)
|
static int insert(std::string_view p)
|
||||||
{
|
|
||||||
#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)
|
|
||||||
{
|
{
|
||||||
log_assert(destruct_guard_ok);
|
log_assert(destruct_guard_ok);
|
||||||
|
log_assert(!Multithreading::active());
|
||||||
|
|
||||||
auto it = global_id_index_.find(p);
|
auto it = global_id_index_.find(p);
|
||||||
if (it != global_id_index_.end()) {
|
if (it != global_id_index_.end()) {
|
||||||
#ifndef YOSYS_NO_IDS_REFCNT
|
|
||||||
global_refcount_storage_.at(it->second)++;
|
|
||||||
#endif
|
|
||||||
#ifdef YOSYS_XTRACE_GET_PUT
|
#ifdef YOSYS_XTRACE_GET_PUT
|
||||||
if (yosys_xtrace)
|
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
|
#endif
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
return really_insert(p, it);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef YOSYS_NO_IDS_REFCNT
|
// Inserts an ID with string `prefix + autoidx', incrementing autoidx.
|
||||||
static inline void put_reference(int idx)
|
// `prefix` must start with '$auto$', end with '$', and live forever.
|
||||||
{
|
static IdString new_autoidx_with_prefix(const std::string *prefix) {
|
||||||
// put_reference() may be called from destructors after the destructor of
|
log_assert(!Multithreading::active());
|
||||||
// global_refcount_storage_ has been run. in this case we simply do nothing.
|
int index = -(autoidx++);
|
||||||
if (idx < static_cast<short>(StaticId::STATIC_ID_END) || !destruct_guard_ok)
|
global_autoidx_id_storage_.insert({index, prefix});
|
||||||
return;
|
return from_index(index);
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
}
|
||||||
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
|
// the actual IdString object is just is a single int
|
||||||
|
|
||||||
int index_;
|
int index_;
|
||||||
|
|
||||||
inline IdString() : index_(0) { }
|
constexpr inline IdString() : index_(0) { }
|
||||||
inline IdString(const char *str) : index_(get_reference(str)) { }
|
inline IdString(const char *str) : index_(insert(std::string_view(str))) { }
|
||||||
inline IdString(const IdString &str) : index_(get_reference(str.index_)) { }
|
constexpr inline IdString(const IdString &str) : index_(str.index_) { }
|
||||||
inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; }
|
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(const std::string &str) : index_(insert(std::string_view(str))) { }
|
||||||
inline IdString(std::string_view str) : index_(get_reference(str)) { }
|
inline IdString(std::string_view str) : index_(insert(str)) { }
|
||||||
inline IdString(StaticId id) : index_(static_cast<short>(id)) {}
|
constexpr inline IdString(StaticId id) : index_(static_cast<short>(id)) {}
|
||||||
inline ~IdString() { put_reference(index_); }
|
|
||||||
|
|
||||||
inline void operator=(const IdString &rhs) {
|
IdString &operator=(const IdString &rhs) = default;
|
||||||
put_reference(index_);
|
|
||||||
index_ = get_reference(rhs.index_);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator=(IdString &&rhs) {
|
|
||||||
put_reference(index_);
|
|
||||||
index_ = rhs.index_;
|
|
||||||
rhs.index_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void operator=(const char *rhs) {
|
inline void operator=(const char *rhs) {
|
||||||
IdString id(rhs);
|
IdString id(rhs);
|
||||||
|
|
@ -358,11 +244,159 @@ struct RTLIL::IdString
|
||||||
constexpr inline const IdString &id_string() const { return *this; }
|
constexpr inline const IdString &id_string() const { return *this; }
|
||||||
|
|
||||||
inline const char *c_str() const {
|
inline const char *c_str() const {
|
||||||
return global_id_storage_.at(index_);
|
if (index_ >= 0)
|
||||||
|
return global_id_storage_.at(index_).buf;
|
||||||
|
|
||||||
|
AutoidxStorage &s = global_autoidx_id_storage_.at(index_);
|
||||||
|
char *full_str = s.full_str.load(std::memory_order_acquire);
|
||||||
|
if (full_str != nullptr)
|
||||||
|
return full_str;
|
||||||
|
const std::string &prefix = *s.prefix;
|
||||||
|
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);
|
||||||
|
if (s.full_str.compare_exchange_strong(full_str, c, std::memory_order_acq_rel))
|
||||||
|
return c;
|
||||||
|
delete[] c;
|
||||||
|
return full_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string str() const {
|
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_storage_.at(index_).prefix;
|
||||||
|
*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_storage_.at(index_).prefix, -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_storage_.at(index_).prefix, -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 {
|
inline bool operator<(const IdString &rhs) const {
|
||||||
|
|
@ -371,8 +405,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 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.
|
// The methods below are just convenience functions for better compatibility with std::string.
|
||||||
|
|
||||||
|
|
@ -383,45 +415,84 @@ struct RTLIL::IdString
|
||||||
bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; }
|
bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; }
|
||||||
|
|
||||||
char operator[](size_t i) const {
|
char operator[](size_t i) const {
|
||||||
const char *p = c_str();
|
if (index_ >= 0) {
|
||||||
|
const Storage &storage = global_id_storage_.at(index_);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
for (; i != 0; i--, p++)
|
log_assert(static_cast<int>(i) < storage.size);
|
||||||
log_assert(*p != 0);
|
|
||||||
return *p;
|
|
||||||
#else
|
|
||||||
return *(p + i);
|
|
||||||
#endif
|
#endif
|
||||||
|
return *(storage.buf + i);
|
||||||
|
}
|
||||||
|
const std::string &id_start = *global_autoidx_id_storage_.at(index_).prefix;
|
||||||
|
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 {
|
std::string substr(size_t pos = 0, size_t len = std::string::npos) const {
|
||||||
if (len == std::string::npos || len >= strlen(c_str() + pos))
|
std::string result;
|
||||||
return std::string(c_str() + pos);
|
const_iterator it = begin() + pos;
|
||||||
else
|
const_iterator end_it = end();
|
||||||
return std::string(c_str() + pos, len);
|
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 {
|
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 {
|
bool begins_with(std::string_view prefix) const {
|
||||||
size_t len = strlen(prefix);
|
Substrings it = substrings();
|
||||||
if (size() < len) return false;
|
std::string_view substr = it.first();
|
||||||
return compare(0, len, prefix) == 0;
|
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 {
|
bool ends_with(std::string_view suffix) const {
|
||||||
size_t len = strlen(suffix);
|
size_t sz = size();
|
||||||
if (size() < len) return false;
|
if (sz < suffix.size()) return false;
|
||||||
return compare(size()-len, len, suffix) == 0;
|
return compare(sz - suffix.size(), suffix.size(), suffix.data()) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains(const char* str) const {
|
bool contains(std::string_view s) const {
|
||||||
return strstr(c_str(), str);
|
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 {
|
size_t size() const {
|
||||||
return strlen(c_str());
|
return begin().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const {
|
bool empty() const {
|
||||||
|
|
@ -458,7 +529,6 @@ struct RTLIL::IdString
|
||||||
}
|
}
|
||||||
|
|
||||||
bool in(const IdString &rhs) const { return *this == rhs; }
|
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 char *rhs) const { return *this == rhs; }
|
||||||
bool in(const std::string &rhs) const { return *this == rhs; }
|
bool in(const std::string &rhs) const { return *this == rhs; }
|
||||||
inline bool in(const pool<IdString> &rhs) const;
|
inline bool in(const pool<IdString> &rhs) const;
|
||||||
|
|
@ -468,6 +538,14 @@ struct RTLIL::IdString
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void prepopulate();
|
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:
|
public:
|
||||||
static void ensure_prepopulated() {
|
static void ensure_prepopulated() {
|
||||||
|
|
@ -476,6 +554,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)
|
||||||
|
{
|
||||||
|
log_assert(!Multithreading::active());
|
||||||
|
|
||||||
|
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;
|
||||||
|
#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()
|
||||||
|
{
|
||||||
|
log_assert(!Multithreading::active());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
namespace hashlib {
|
namespace hashlib {
|
||||||
template <>
|
template <>
|
||||||
struct hash_ops<RTLIL::IdString> {
|
struct hash_ops<RTLIL::IdString> {
|
||||||
|
|
@ -501,21 +668,9 @@ inline bool RTLIL::IdString::in(const pool<IdString> &rhs) const { return rhs.co
|
||||||
[[deprecated]]
|
[[deprecated]]
|
||||||
inline bool RTLIL::IdString::in(const pool<IdString> &&rhs) const { return rhs.count(*this) != 0; }
|
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 RTLIL {
|
||||||
namespace IDInternal {
|
|
||||||
#define X(_id) extern const IdString _id;
|
|
||||||
#include "kernel/constids.inc"
|
|
||||||
#undef X
|
|
||||||
}
|
|
||||||
namespace ID {
|
namespace ID {
|
||||||
#define X(_id) constexpr StaticIdString _id(StaticId::_id, IDInternal::_id);
|
#define X(_id) constexpr IdString _id(StaticId::_id);
|
||||||
#include "kernel/constids.inc"
|
#include "kernel/constids.inc"
|
||||||
#undef X
|
#undef X
|
||||||
}
|
}
|
||||||
|
|
@ -523,7 +678,7 @@ namespace RTLIL {
|
||||||
|
|
||||||
struct IdTableEntry {
|
struct IdTableEntry {
|
||||||
const std::string_view name;
|
const std::string_view name;
|
||||||
const RTLIL::StaticIdString static_id;
|
const RTLIL::IdString static_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr IdTableEntry IdTable[] = {
|
constexpr IdTableEntry IdTable[] = {
|
||||||
|
|
@ -556,15 +711,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>
|
// 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 {
|
template <int IdTableIndex> struct IDMacroHelper {
|
||||||
static constexpr RTLIL::StaticIdString eval(IDMacroHelperFunc) {
|
static constexpr RTLIL::IdString eval(IDMacroHelperFunc) {
|
||||||
return IdTable[IdTableIndex].static_id;
|
return IdTable[IdTableIndex].static_id;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <> struct IDMacroHelper<-1> {
|
template <> struct IDMacroHelper<-1> {
|
||||||
static constexpr const RTLIL::IdString &eval(IDMacroHelperFunc func) {
|
static constexpr RTLIL::IdString eval(IDMacroHelperFunc func) {
|
||||||
return func();
|
return func();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -574,16 +729,16 @@ template <> struct IDMacroHelper<-1> {
|
||||||
YOSYS_NAMESPACE_PREFIX IDMacroHelper< \
|
YOSYS_NAMESPACE_PREFIX IDMacroHelper< \
|
||||||
YOSYS_NAMESPACE_PREFIX lookup_well_known_id(#_id) \
|
YOSYS_NAMESPACE_PREFIX lookup_well_known_id(#_id) \
|
||||||
>::eval([]() \
|
>::eval([]() \
|
||||||
-> const YOSYS_NAMESPACE_PREFIX RTLIL::IdString & { \
|
-> YOSYS_NAMESPACE_PREFIX RTLIL::IdString { \
|
||||||
const char *p = "\\" #_id, *q = p[1] == '$' ? p+1 : p; \
|
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; \
|
return id; \
|
||||||
})
|
})
|
||||||
|
|
||||||
namespace RTLIL {
|
namespace RTLIL {
|
||||||
extern dict<std::string, std::string> constpad;
|
extern dict<std::string, std::string> constpad;
|
||||||
|
|
||||||
[[deprecated("Call cell->is_builtin_ff() instead")]]
|
|
||||||
const pool<IdString> &builtin_ff_cell_types();
|
const pool<IdString> &builtin_ff_cell_types();
|
||||||
|
|
||||||
static inline std::string escape_id(const std::string &str) {
|
static inline std::string escape_id(const std::string &str) {
|
||||||
|
|
@ -620,13 +775,13 @@ namespace RTLIL {
|
||||||
|
|
||||||
template <typename T> struct sort_by_name_str {
|
template <typename T> struct sort_by_name_str {
|
||||||
bool operator()(T *a, T *b) const {
|
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 {
|
struct sort_by_id_str {
|
||||||
bool operator()(const RTLIL::IdString &a, const RTLIL::IdString &b) const {
|
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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1581,6 +1736,8 @@ public:
|
||||||
operator std::vector<RTLIL::SigChunk>() const;
|
operator std::vector<RTLIL::SigChunk>() const;
|
||||||
operator std::vector<RTLIL::SigBit>() const { return to_sigbit_vector(); }
|
operator std::vector<RTLIL::SigBit>() const { return to_sigbit_vector(); }
|
||||||
const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < size() ? (*this)[offset] : defval; }
|
const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < size() ? (*this)[offset] : defval; }
|
||||||
|
RTLIL::SigBit& at(int offset) { return (*this)[offset]; }
|
||||||
|
RTLIL::SigBit at(int offset) const { return (*this)[offset]; }
|
||||||
|
|
||||||
[[nodiscard]] Hasher hash_into(Hasher h) const {
|
[[nodiscard]] Hasher hash_into(Hasher h) const {
|
||||||
Hasher::hash_t val;
|
Hasher::hash_t val;
|
||||||
|
|
@ -1876,9 +2033,7 @@ struct RTLIL::Design
|
||||||
// returns all selected unboxed whole modules, warning the user if any
|
// returns all selected unboxed whole modules, warning the user if any
|
||||||
// partially selected or boxed modules have been ignored
|
// 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); }
|
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);
|
static std::map<unsigned int, RTLIL::Design*> *get_all_designs(void);
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RTLIL::Module : public RTLIL::NamedObject
|
struct RTLIL::Module : public RTLIL::NamedObject
|
||||||
|
|
|
||||||
|
|
@ -533,7 +533,7 @@ static int tcl_set_param(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *cons
|
||||||
return TCL_OK;
|
return TCL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int yosys_tcl_iterp_init(Tcl_Interp *interp)
|
int yosys_tcl_interp_init(Tcl_Interp *interp)
|
||||||
{
|
{
|
||||||
if (Tcl_Init(interp)!=TCL_OK)
|
if (Tcl_Init(interp)!=TCL_OK)
|
||||||
log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
|
log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "kernel/yosys.h"
|
#include "kernel/yosys.h"
|
||||||
#include "kernel/celltypes.h"
|
#include "kernel/celltypes.h"
|
||||||
|
#include "kernel/log.h"
|
||||||
|
|
||||||
#ifdef YOSYS_ENABLE_READLINE
|
#ifdef YOSYS_ENABLE_READLINE
|
||||||
# include <readline/readline.h>
|
# include <readline/readline.h>
|
||||||
|
|
@ -80,7 +81,7 @@ extern "C" PyObject* PyInit_pyosys();
|
||||||
|
|
||||||
YOSYS_NAMESPACE_BEGIN
|
YOSYS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
int autoidx = 1;
|
Autoidx autoidx(1);
|
||||||
int yosys_xtrace = 0;
|
int yosys_xtrace = 0;
|
||||||
bool yosys_write_versions = true;
|
bool yosys_write_versions = true;
|
||||||
const char* yosys_maybe_version() {
|
const char* yosys_maybe_version() {
|
||||||
|
|
@ -95,6 +96,7 @@ CellTypes yosys_celltypes;
|
||||||
|
|
||||||
#ifdef YOSYS_ENABLE_TCL
|
#ifdef YOSYS_ENABLE_TCL
|
||||||
Tcl_Interp *yosys_tcl_interp = NULL;
|
Tcl_Interp *yosys_tcl_interp = NULL;
|
||||||
|
Tcl_Interp *yosys_sdc_interp = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::set<std::string> yosys_input_files, yosys_output_files;
|
std::set<std::string> yosys_input_files, yosys_output_files;
|
||||||
|
|
@ -107,9 +109,30 @@ uint32_t Hasher::fudge = 0;
|
||||||
std::string yosys_share_dirname;
|
std::string yosys_share_dirname;
|
||||||
std::string yosys_abc_executable;
|
std::string yosys_abc_executable;
|
||||||
|
|
||||||
|
bool Multithreading::active_ = false;
|
||||||
|
|
||||||
void init_share_dirname();
|
void init_share_dirname();
|
||||||
void init_abc_executable_name();
|
void init_abc_executable_name();
|
||||||
|
|
||||||
|
Multithreading::Multithreading() {
|
||||||
|
log_assert(!active_);
|
||||||
|
active_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Multithreading::~Multithreading() {
|
||||||
|
log_assert(active_);
|
||||||
|
active_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Autoidx::ensure_at_least(int v) {
|
||||||
|
value = std::max(value, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Autoidx::operator++(int) {
|
||||||
|
log_assert(!Multithreading::active());
|
||||||
|
return value++;
|
||||||
|
}
|
||||||
|
|
||||||
void memhasher_on()
|
void memhasher_on()
|
||||||
{
|
{
|
||||||
#if defined(__linux__) || defined(__FreeBSD__)
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
|
@ -260,6 +283,7 @@ void yosys_shutdown()
|
||||||
|
|
||||||
delete yosys_design;
|
delete yosys_design;
|
||||||
yosys_design = NULL;
|
yosys_design = NULL;
|
||||||
|
RTLIL::OwningIdString::collect_garbage();
|
||||||
|
|
||||||
for (auto f : log_files)
|
for (auto f : log_files)
|
||||||
if (f != stderr)
|
if (f != stderr)
|
||||||
|
|
@ -295,35 +319,35 @@ void yosys_shutdown()
|
||||||
#endif
|
#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
|
#ifdef _WIN32
|
||||||
size_t pos = file.find_last_of("/\\");
|
size_t pos = file.find_last_of("/\\");
|
||||||
#else
|
#else
|
||||||
size_t pos = file.find_last_of('/');
|
size_t pos = file.find_last_of('/');
|
||||||
#endif
|
#endif
|
||||||
if (pos != std::string::npos)
|
if (pos != std::string_view::npos)
|
||||||
file = file.substr(pos+1);
|
file = file.substr(pos+1);
|
||||||
|
|
||||||
pos = func.find_last_of(':');
|
pos = func.find_last_of(':');
|
||||||
if (pos != std::string::npos)
|
if (pos != std::string_view::npos)
|
||||||
func = func.substr(pos+1);
|
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
|
#ifdef _WIN32
|
||||||
size_t pos = file.find_last_of("/\\");
|
size_t pos = file.find_last_of("/\\");
|
||||||
#else
|
#else
|
||||||
size_t pos = file.find_last_of('/');
|
size_t pos = file.find_last_of('/');
|
||||||
#endif
|
#endif
|
||||||
if (pos != std::string::npos)
|
if (pos != std::string_view::npos)
|
||||||
file = file.substr(pos+1);
|
file = file.substr(pos+1);
|
||||||
|
|
||||||
pos = func.find_last_of(':');
|
pos = func.find_last_of(':');
|
||||||
if (pos != std::string::npos)
|
if (pos != std::string_view::npos)
|
||||||
func = func.substr(pos+1);
|
func = func.substr(pos+1);
|
||||||
|
|
||||||
return stringf("$auto$%s:%d:%s$%s$%d", file, line, func, suffix, autoidx++);
|
return stringf("$auto$%s:%d:%s$%s$%d", file, line, func, suffix, autoidx++);
|
||||||
|
|
@ -392,17 +416,18 @@ void rewrite_filename(std::string &filename)
|
||||||
#ifdef YOSYS_ENABLE_TCL
|
#ifdef YOSYS_ENABLE_TCL
|
||||||
|
|
||||||
// defined in tclapi.cc
|
// defined in tclapi.cc
|
||||||
extern int yosys_tcl_iterp_init(Tcl_Interp *interp);
|
extern int yosys_tcl_interp_init(Tcl_Interp *interp);
|
||||||
|
|
||||||
extern Tcl_Interp *yosys_get_tcl_interp()
|
extern Tcl_Interp *yosys_get_tcl_interp()
|
||||||
{
|
{
|
||||||
if (yosys_tcl_interp == NULL) {
|
if (yosys_tcl_interp == NULL) {
|
||||||
yosys_tcl_interp = Tcl_CreateInterp();
|
yosys_tcl_interp = Tcl_CreateInterp();
|
||||||
yosys_tcl_iterp_init(yosys_tcl_interp);
|
yosys_tcl_interp_init(yosys_tcl_interp);
|
||||||
}
|
}
|
||||||
return yosys_tcl_interp;
|
return yosys_tcl_interp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also see SdcPass
|
||||||
struct TclPass : public Pass {
|
struct TclPass : public Pass {
|
||||||
TclPass() : Pass("tcl", "execute a TCL script file") { }
|
TclPass() : Pass("tcl", "execute a TCL script file") { }
|
||||||
void help() override {
|
void help() override {
|
||||||
|
|
@ -445,6 +470,7 @@ struct TclPass : public Pass {
|
||||||
Tcl_Release(interp);
|
Tcl_Release(interp);
|
||||||
}
|
}
|
||||||
} TclPass;
|
} TclPass;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__CYGWIN__)
|
#if defined(__linux__) || defined(__CYGWIN__)
|
||||||
|
|
|
||||||
|
|
@ -267,15 +267,41 @@ int ceil_log2(int x) YS_ATTRIBUTE(const);
|
||||||
template<typename T> int GetSize(const T &obj) { return obj.size(); }
|
template<typename T> int GetSize(const T &obj) { return obj.size(); }
|
||||||
inline int GetSize(RTLIL::Wire *wire);
|
inline int GetSize(RTLIL::Wire *wire);
|
||||||
|
|
||||||
extern int autoidx;
|
// When multiple threads are accessing RTLIL, one of these guard objects
|
||||||
|
// must exist.
|
||||||
|
struct Multithreading
|
||||||
|
{
|
||||||
|
Multithreading();
|
||||||
|
~Multithreading();
|
||||||
|
// Returns true when multiple threads are accessing RTLIL.
|
||||||
|
// autoidx cannot be used during such times.
|
||||||
|
// IdStrings cannot be created during such times.
|
||||||
|
static bool active() { return active_; }
|
||||||
|
private:
|
||||||
|
static bool active_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Autoidx {
|
||||||
|
Autoidx(int value) : value(value) {}
|
||||||
|
operator int() const { return value; }
|
||||||
|
void ensure_at_least(int v);
|
||||||
|
int operator++(int);
|
||||||
|
private:
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Autoidx autoidx;
|
||||||
extern int yosys_xtrace;
|
extern int yosys_xtrace;
|
||||||
extern bool yosys_write_versions;
|
extern bool yosys_write_versions;
|
||||||
|
|
||||||
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);
|
||||||
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);
|
||||||
|
|
||||||
#define NEW_ID \
|
#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 = YOSYS_NAMESPACE_PREFIX create_id_prefix(__FILE__, __LINE__, func); \
|
||||||
|
return prefix; \
|
||||||
|
}(__FUNCTION__))
|
||||||
#define NEW_ID_SUFFIX(suffix) \
|
#define NEW_ID_SUFFIX(suffix) \
|
||||||
YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix)
|
YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -912,6 +912,10 @@ class SubCircuit::SolverWorker
|
||||||
bool pruneEnumerationMatrix(std::vector<std::set<int>> &enumerationMatrix, const GraphData &needle, const GraphData &haystack, int &nextRow, bool allowOverlap)
|
bool pruneEnumerationMatrix(std::vector<std::set<int>> &enumerationMatrix, const GraphData &needle, const GraphData &haystack, int &nextRow, bool allowOverlap)
|
||||||
{
|
{
|
||||||
bool didSomething = true;
|
bool didSomething = true;
|
||||||
|
|
||||||
|
// Map of j:[i where j is used]
|
||||||
|
std::map<int, std::set<int>> usedNodes;
|
||||||
|
|
||||||
while (didSomething)
|
while (didSomething)
|
||||||
{
|
{
|
||||||
nextRow = -1;
|
nextRow = -1;
|
||||||
|
|
@ -923,13 +927,23 @@ class SubCircuit::SolverWorker
|
||||||
didSomething = true;
|
didSomething = true;
|
||||||
else if (!allowOverlap && haystack.usedNodes[j])
|
else if (!allowOverlap && haystack.usedNodes[j])
|
||||||
didSomething = true;
|
didSomething = true;
|
||||||
else
|
else {
|
||||||
newRow.insert(j);
|
newRow.insert(j);
|
||||||
|
usedNodes[j].insert(i); // Store the needle index by haystack node index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This indicates there are no available haystack nodes to assign to the needle
|
||||||
if (newRow.size() == 0)
|
if (newRow.size() == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// If there are multiple needles assigned to the haystack node, the solution is invalid
|
||||||
|
if (newRow.size() == 1 && usedNodes[*newRow.begin()].size() > 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (newRow.size() >= 2 && (nextRow < 0 || needle.adjMatrix.at(nextRow).size() < needle.adjMatrix.at(i).size()))
|
if (newRow.size() >= 2 && (nextRow < 0 || needle.adjMatrix.at(nextRow).size() < needle.adjMatrix.at(i).size()))
|
||||||
nextRow = i;
|
nextRow = i;
|
||||||
|
|
||||||
enumerationMatrix[i].swap(newRow);
|
enumerationMatrix[i].swap(newRow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,3 +58,6 @@ OBJS += passes/cmds/test_select.o
|
||||||
OBJS += passes/cmds/timeest.o
|
OBJS += passes/cmds/timeest.o
|
||||||
OBJS += passes/cmds/linecoverage.o
|
OBJS += passes/cmds/linecoverage.o
|
||||||
OBJS += passes/cmds/sort.o
|
OBJS += passes/cmds/sort.o
|
||||||
|
OBJS += passes/cmds/icell_liberty.o
|
||||||
|
|
||||||
|
include $(YOSYS_SRC)/passes/cmds/sdc/Makefile.inc
|
||||||
|
|
|
||||||
207
passes/cmds/icell_liberty.cc
Normal file
207
passes/cmds/icell_liberty.cc
Normal file
|
|
@ -0,0 +1,207 @@
|
||||||
|
#include "kernel/yosys.h"
|
||||||
|
#include "kernel/celltypes.h"
|
||||||
|
#include "kernel/ff.h"
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct LibertyStubber {
|
||||||
|
CellTypes ct;
|
||||||
|
LibertyStubber() {
|
||||||
|
ct.setup();
|
||||||
|
ct.setup_internals_ff();
|
||||||
|
}
|
||||||
|
void liberty_prefix(std::ostream& f)
|
||||||
|
{
|
||||||
|
f << "/*\n";
|
||||||
|
f << stringf("\tModels interfaces of select Yosys internal cell.\n");
|
||||||
|
f << stringf("\tLikely contains INCORRECT POLARITIES.\n");
|
||||||
|
f << stringf("\tImpractical for any simulation, synthesis, or timing.\n");
|
||||||
|
f << stringf("\tIntended purely for SDC expansion.\n");
|
||||||
|
f << stringf("\tDo not microwave or tumble dry.\n");
|
||||||
|
f << stringf("\tGenerated by %s\n", yosys_maybe_version());
|
||||||
|
f << "*/\n";
|
||||||
|
f << "library (yosys) {\n";
|
||||||
|
f << "\tinput_threshold_pct_fall : 50;\n";
|
||||||
|
f << "\tinput_threshold_pct_rise : 50;\n";
|
||||||
|
f << "\toutput_threshold_pct_fall : 50;\n";
|
||||||
|
f << "\toutput_threshold_pct_rise : 50;\n";
|
||||||
|
f << "\tslew_lower_threshold_pct_fall : 1;\n";
|
||||||
|
f << "\tslew_lower_threshold_pct_rise : 1;\n";
|
||||||
|
f << "\tslew_upper_threshold_pct_fall : 99;\n";
|
||||||
|
f << "\tslew_upper_threshold_pct_rise : 99;\n";
|
||||||
|
}
|
||||||
|
void liberty_suffix(std::ostream& f)
|
||||||
|
{
|
||||||
|
f << "}\n";
|
||||||
|
}
|
||||||
|
struct LibertyItemizer {
|
||||||
|
std::ostream& f;
|
||||||
|
int indent;
|
||||||
|
LibertyItemizer(std::ostream& f) : f(f), indent(0) {};
|
||||||
|
void item(std::string key, std::string val)
|
||||||
|
{
|
||||||
|
f << std::string(indent, '\t') << key << " : \"" << val << "\";\n";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
void liberty_flop(Module* base, Module* derived, std::ostream& f)
|
||||||
|
{
|
||||||
|
auto base_name = base->name.str().substr(1);
|
||||||
|
auto derived_name = derived->name.str().substr(1);
|
||||||
|
|
||||||
|
FfTypeData ffType(base_name);
|
||||||
|
LibertyItemizer i(f);
|
||||||
|
|
||||||
|
if (ffType.has_gclk) {
|
||||||
|
log_warning("Formal flip flop %s can't be modeled\n", base_name.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ffType.has_ce) {
|
||||||
|
log_warning("DFFE %s can't be modeled\n", base_name.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
f << "\tcell (\"" << derived_name << "\") {\n";
|
||||||
|
auto& base_type = ct.cell_types[base_name];
|
||||||
|
i.indent = 3;
|
||||||
|
auto sorted_ports = derived->ports;
|
||||||
|
// Hack for CLK and C coming before Q does
|
||||||
|
auto cmp = [](IdString l, IdString r) { return l.str() < r.str(); };
|
||||||
|
std::sort(sorted_ports.begin(), sorted_ports.end(), cmp);
|
||||||
|
std::string clock_pin_name = "";
|
||||||
|
for (auto x : sorted_ports) {
|
||||||
|
std::string port_name = RTLIL::unescape_id(x);
|
||||||
|
bool is_input = base_type.inputs.count(x);
|
||||||
|
bool is_output = base_type.outputs.count(x);
|
||||||
|
f << "\t\tpin (" << RTLIL::unescape_id(x.str()) << ") {\n";
|
||||||
|
if (is_input && !is_output) {
|
||||||
|
i.item("direction", "input");
|
||||||
|
} else if (!is_input && is_output) {
|
||||||
|
i.item("direction", "output");
|
||||||
|
} else {
|
||||||
|
i.item("direction", "inout");
|
||||||
|
}
|
||||||
|
if (port_name == "CLK" || port_name == "C") {
|
||||||
|
i.item("clock", "true");
|
||||||
|
clock_pin_name = port_name;
|
||||||
|
}
|
||||||
|
if (port_name == "Q") {
|
||||||
|
i.item("function", "IQ");
|
||||||
|
f << "\t\t\ttiming () {\n";
|
||||||
|
i.indent++;
|
||||||
|
log_assert(clock_pin_name.size());
|
||||||
|
i.item("related_pin", clock_pin_name);
|
||||||
|
i.indent--;
|
||||||
|
f << "\t\t\t}\n";
|
||||||
|
}
|
||||||
|
f << "\t\t}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
f << "\t\tff (\"IQ\",\"IQ_N\") {\n";
|
||||||
|
i.indent = 3;
|
||||||
|
// TODO polarities?
|
||||||
|
if (ffType.has_clk) {
|
||||||
|
auto pin = ffType.is_fine ? "C" : "CLK";
|
||||||
|
i.item("clocked_on", pin);
|
||||||
|
}
|
||||||
|
if (ffType.has_arst) {
|
||||||
|
auto meaning = (ffType.val_arst == State::S1) ? "preset" : "clear";
|
||||||
|
auto pin = ffType.is_fine ? "R" : "ARST";
|
||||||
|
i.item(meaning, pin);
|
||||||
|
}
|
||||||
|
auto next_state = ffType.has_ce ? "D & EN" : "D";
|
||||||
|
i.item("next_state", next_state);
|
||||||
|
f << "\t\t}\n";
|
||||||
|
f << "\t}\n";
|
||||||
|
}
|
||||||
|
void liberty_cell(Module* base, Module* derived, std::ostream& f)
|
||||||
|
{
|
||||||
|
auto base_name = base->name.str().substr(1);
|
||||||
|
auto derived_name = derived->name.str().substr(1);
|
||||||
|
if (!ct.cell_types.count(base_name)) {
|
||||||
|
log_debug("skip skeleton for %s\n", base_name.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RTLIL::builtin_ff_cell_types().count(base_name))
|
||||||
|
return liberty_flop(base, derived, f);
|
||||||
|
|
||||||
|
auto& base_type = ct.cell_types[base_name];
|
||||||
|
f << "\tcell (\"" << derived_name << "\") {\n";
|
||||||
|
for (auto x : derived->ports) {
|
||||||
|
bool is_input = base_type.inputs.count(x);
|
||||||
|
bool is_output = base_type.outputs.count(x);
|
||||||
|
f << "\t\tpin (" << RTLIL::unescape_id(x.str()) << ") {\n";
|
||||||
|
if (is_input && !is_output) {
|
||||||
|
f << "\t\t\tdirection : input;\n";
|
||||||
|
} else if (!is_input && is_output) {
|
||||||
|
f << "\t\t\tdirection : output;\n";
|
||||||
|
} else {
|
||||||
|
f << "\t\t\tdirection : inout;\n";
|
||||||
|
}
|
||||||
|
f << "\t\t}\n";
|
||||||
|
}
|
||||||
|
f << "\t}\n";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IcellLiberty : Pass {
|
||||||
|
IcellLiberty() : Pass("icell_liberty", "write Liberty interfaces for used internal cells") {}
|
||||||
|
void help() override
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" icell_liberty <liberty_file>\n");
|
||||||
|
log("\n");
|
||||||
|
log("Write Liberty files modeling the interfaces of used internal cells.\n");
|
||||||
|
log("\n");
|
||||||
|
log("Models are not guaranteed to be logically sound.\n");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design *d) override
|
||||||
|
{
|
||||||
|
log_header(d, "Executing ICELL_LIBERTY pass.\n");
|
||||||
|
|
||||||
|
size_t argidx;
|
||||||
|
IdString naming_attr;
|
||||||
|
std::string liberty_filename;
|
||||||
|
auto liberty_file = std::make_unique<std::ofstream>();
|
||||||
|
|
||||||
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (argidx < args.size())
|
||||||
|
liberty_filename = args[argidx++];
|
||||||
|
else
|
||||||
|
log_cmd_error("no Liberty filename specified\n");
|
||||||
|
|
||||||
|
if (liberty_filename.size()) {
|
||||||
|
liberty_file->open(liberty_filename.c_str());
|
||||||
|
if (liberty_file->fail()) {
|
||||||
|
log_cmd_error("Can't open file `%s' for writing: %s\n", liberty_filename.c_str(), strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pool<RTLIL::IdString> done;
|
||||||
|
LibertyStubber stubber = {};
|
||||||
|
|
||||||
|
stubber.liberty_prefix(*liberty_file);
|
||||||
|
|
||||||
|
for (auto module : d->selected_modules()) {
|
||||||
|
for (auto cell : module->selected_cells()) {
|
||||||
|
Module *inst_module = d->module(cell->type);
|
||||||
|
if (!inst_module || !inst_module->get_blackbox_attribute())
|
||||||
|
continue;
|
||||||
|
Module *base = inst_module;
|
||||||
|
if (!done.count(base->name)) {
|
||||||
|
stubber.liberty_cell(base, base, *liberty_file);
|
||||||
|
done.insert(base->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stubber.liberty_suffix(*liberty_file);
|
||||||
|
}
|
||||||
|
} IcellLiberty;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
||||||
3
passes/cmds/sdc/Makefile.inc
Normal file
3
passes/cmds/sdc/Makefile.inc
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
OBJS += passes/cmds/sdc/sdc.o
|
||||||
|
|
||||||
|
$(eval $(call add_share_file,share/sdc,passes/cmds/sdc/graph-stubs.sdc))
|
||||||
42
passes/cmds/sdc/graph-stubs.sdc
Normal file
42
passes/cmds/sdc/graph-stubs.sdc
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
proc unknown {args} {
|
||||||
|
# Check if it's a getter
|
||||||
|
if {[llength $args] > 0} {
|
||||||
|
set first_arg [lindex $args 0]
|
||||||
|
if {[string match "get_*" $first_arg]} {
|
||||||
|
# It's a getter, has it been redirected from specialized C++ code?
|
||||||
|
if {[llength $args] > 1} {
|
||||||
|
set second_arg [lindex $args 1]
|
||||||
|
if {$second_arg ne "-getter-validated"} {
|
||||||
|
error "Unknown getter: $first_arg"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error "Unknown getter: $first_arg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# TODO this safety feature could be optional via a global
|
||||||
|
|
||||||
|
global sdc_call_index
|
||||||
|
global sdc_calls
|
||||||
|
if {![info exists sdc_call_index]} {
|
||||||
|
set sdc_call_index 0
|
||||||
|
}
|
||||||
|
if {![info exists sdc_calls]} {
|
||||||
|
set sdc_calls {}
|
||||||
|
}
|
||||||
|
set ret "YOSYS_SDC_MAGIC_NODE_$sdc_call_index"
|
||||||
|
incr sdc_call_index
|
||||||
|
lappend sdc_calls $args
|
||||||
|
# puts "unknown $args, returning YOSYS_SDC_MAGIC_NODE_$sdc_call_index"
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
proc list {args} {
|
||||||
|
return [unknown "list" {*}$args]
|
||||||
|
}
|
||||||
|
proc get_clocks {args} {
|
||||||
|
# get_clocks isn't a design object getter
|
||||||
|
# because clocks aren't design objects, just aliases
|
||||||
|
# so the referred to clock pin already are being tracked
|
||||||
|
# as arguments of uninterpreted create_clock command or similar
|
||||||
|
return [unknown "get_clocks" "-getter-validated" {*}$args]
|
||||||
|
}
|
||||||
790
passes/cmds/sdc/sdc.cc
Normal file
790
passes/cmds/sdc/sdc.cc
Normal file
|
|
@ -0,0 +1,790 @@
|
||||||
|
#ifdef YOSYS_ENABLE_TCL
|
||||||
|
|
||||||
|
#include "kernel/register.h"
|
||||||
|
#include "kernel/rtlil.h"
|
||||||
|
#include "kernel/log.h"
|
||||||
|
#include <tcl.h>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#if TCL_MAJOR_VERSION < 9
|
||||||
|
typedef int YS_Tcl_Size;
|
||||||
|
#else
|
||||||
|
typedef Tcl_Size YS_Tcl_Size;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct TclCall {
|
||||||
|
Tcl_Interp *interp;
|
||||||
|
int objc;
|
||||||
|
Tcl_Obj* const* objv;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int redirect_unknown(TclCall call);
|
||||||
|
static size_t get_node_count(Tcl_Interp* interp);
|
||||||
|
|
||||||
|
struct BitSelection {
|
||||||
|
bool all = false;
|
||||||
|
std::vector<bool> bits = {};
|
||||||
|
void set_all() {
|
||||||
|
bits.clear();
|
||||||
|
all = true;
|
||||||
|
}
|
||||||
|
void clear() {
|
||||||
|
bits.clear();
|
||||||
|
all = false;
|
||||||
|
}
|
||||||
|
void set(size_t idx) {
|
||||||
|
if (all)
|
||||||
|
return;
|
||||||
|
if (idx >= bits.size())
|
||||||
|
bits.resize(idx + 1);
|
||||||
|
bits[idx] = true;
|
||||||
|
}
|
||||||
|
void merge(const BitSelection& other) {
|
||||||
|
if (all)
|
||||||
|
return;
|
||||||
|
if (other.all) {
|
||||||
|
set_all();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (other.bits.size() > bits.size())
|
||||||
|
bits.resize(other.bits.size());
|
||||||
|
for (size_t other_idx = 0; other_idx < other.bits.size(); other_idx++) {
|
||||||
|
bool other_bit = other.bits[other_idx];
|
||||||
|
if (other_bit)
|
||||||
|
set(other_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void dump() {
|
||||||
|
if (!all) {
|
||||||
|
for (size_t i = 0; i < bits.size(); i++)
|
||||||
|
if (bits[i])
|
||||||
|
log("\t\t [%zu]\n", i);
|
||||||
|
} else {
|
||||||
|
log("\t\t FULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool is_set(size_t idx) const {
|
||||||
|
if (all)
|
||||||
|
return true;
|
||||||
|
if (idx >= bits.size())
|
||||||
|
return false;
|
||||||
|
return bits[idx];
|
||||||
|
}
|
||||||
|
// TODO actually use this
|
||||||
|
void compress(size_t size) {
|
||||||
|
if (bits.size() < size)
|
||||||
|
return;
|
||||||
|
for (size_t i = 0; i < size; i++)
|
||||||
|
if (!bits[i])
|
||||||
|
return;
|
||||||
|
bits.clear();
|
||||||
|
bits.shrink_to_fit();
|
||||||
|
all = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SdcObjects {
|
||||||
|
enum CollectMode {
|
||||||
|
// getter-side object tracking with minimal features
|
||||||
|
SimpleGetter,
|
||||||
|
// getter-side object tracking with everything
|
||||||
|
FullGetter,
|
||||||
|
// constraint-side tracking
|
||||||
|
FullConstraint,
|
||||||
|
} collect_mode;
|
||||||
|
using CellPin = std::pair<Cell*, IdString>;
|
||||||
|
Design* design;
|
||||||
|
std::vector<std::pair<std::string, Wire*>> design_ports;
|
||||||
|
std::vector<std::pair<std::string, Cell*>> design_cells;
|
||||||
|
std::vector<std::pair<std::string, CellPin>> design_pins;
|
||||||
|
std::vector<std::pair<std::string, Wire*>> design_nets;
|
||||||
|
|
||||||
|
using PortPattern = std::tuple<std::string, Wire*, BitSelection>;
|
||||||
|
using PinPattern = std::tuple<std::string, SdcObjects::CellPin, BitSelection>;
|
||||||
|
std::vector<std::vector<PortPattern>> resolved_port_pattern_sets;
|
||||||
|
std::vector<std::vector<PinPattern>> resolved_pin_pattern_sets;
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
dict<std::pair<std::string, Wire*>, BitSelection> constrained_ports;
|
||||||
|
pool<std::pair<std::string, Cell*>> constrained_cells;
|
||||||
|
dict<std::pair<std::string, CellPin>, BitSelection> constrained_pins;
|
||||||
|
dict<std::pair<std::string, Wire*>, BitSelection> constrained_nets;
|
||||||
|
|
||||||
|
void sniff_module(std::list<std::string>& hierarchy, Module* mod) {
|
||||||
|
std::string prefix;
|
||||||
|
for (auto mod_name : hierarchy) {
|
||||||
|
if (prefix.length())
|
||||||
|
prefix += "/"; // TODO seperator?
|
||||||
|
prefix += mod_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto* wire : mod->wires()) {
|
||||||
|
std::string name = wire->name.str();
|
||||||
|
log_assert(name.length());
|
||||||
|
// TODO: really skip internal wires?
|
||||||
|
if (name[0] == '$')
|
||||||
|
continue;
|
||||||
|
name = name.substr(1);
|
||||||
|
std::string path = prefix;
|
||||||
|
if (path.length())
|
||||||
|
path += "/";
|
||||||
|
path += name;
|
||||||
|
design_nets.push_back(std::make_pair(path, wire));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto* cell : mod->cells()) {
|
||||||
|
std::string name = cell->name.str();
|
||||||
|
// TODO: really skip internal cells?
|
||||||
|
if (name[0] == '$')
|
||||||
|
continue;
|
||||||
|
name = name.substr(1);
|
||||||
|
std::string path = prefix;
|
||||||
|
if (path.length())
|
||||||
|
path += "/";
|
||||||
|
path += name;
|
||||||
|
design_cells.push_back(std::make_pair(path, cell));
|
||||||
|
for (auto pin : cell->connections()) {
|
||||||
|
IdString pin_name = pin.first;
|
||||||
|
std::string pin_name_sdc = path + "/" + pin.first.str().substr(1);
|
||||||
|
design_pins.push_back(std::make_pair(pin_name_sdc, std::make_pair(cell, pin_name)));
|
||||||
|
}
|
||||||
|
if (auto sub_mod = mod->design->module(cell->type)) {
|
||||||
|
hierarchy.push_back(name);
|
||||||
|
sniff_module(hierarchy, sub_mod);
|
||||||
|
hierarchy.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SdcObjects(Design* design) : design(design) {
|
||||||
|
Module* top = design->top_module();
|
||||||
|
if (!top)
|
||||||
|
log_error("Top module couldn't be determined. Check 'top' attribute usage");
|
||||||
|
for (auto port : top->ports) {
|
||||||
|
design_ports.push_back(std::make_pair(port.str().substr(1), top->wire(port)));
|
||||||
|
}
|
||||||
|
std::list<std::string> hierarchy{};
|
||||||
|
sniff_module(hierarchy, top);
|
||||||
|
}
|
||||||
|
~SdcObjects() = default;
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
void build_normal_result(Tcl_Interp* interp, std::vector<std::tuple<std::string, T, BitSelection>>&& resolved, U& tgt, std::function<size_t(T&)> width, Tcl_Obj*& result) {
|
||||||
|
if (!result)
|
||||||
|
result = Tcl_NewListObj(resolved.size(), nullptr);
|
||||||
|
for (auto [name, obj, matching_bits] : resolved) {
|
||||||
|
for (size_t i = 0; i < width(obj); i++)
|
||||||
|
if (matching_bits.is_set(i)) {
|
||||||
|
Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(name.c_str(), name.size()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
size_t node_count = get_node_count(interp);
|
||||||
|
tgt.emplace_back(std::move(resolved));
|
||||||
|
log("%zu %zu\n", node_count, tgt.size());
|
||||||
|
log_assert(node_count == tgt.size());
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
void merge_as_constrained(std::vector<std::tuple<std::string, T, BitSelection>>&& resolved) {
|
||||||
|
for (auto [name, obj, matching_bits] : resolved) {
|
||||||
|
merge_or_init(std::make_pair(name, obj), constrained_pins, matching_bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void dump() {
|
||||||
|
std::sort(design_ports.begin(), design_ports.end());
|
||||||
|
std::sort(design_cells.begin(), design_cells.end());
|
||||||
|
std::sort(design_pins.begin(), design_pins.end());
|
||||||
|
std::sort(design_nets.begin(), design_nets.end());
|
||||||
|
constrained_ports.sort();
|
||||||
|
constrained_cells.sort();
|
||||||
|
constrained_pins.sort();
|
||||||
|
constrained_nets.sort();
|
||||||
|
// log("Design ports:\n");
|
||||||
|
// for (auto name : design_ports) {
|
||||||
|
// log("\t%s\n", name.c_str());
|
||||||
|
// }
|
||||||
|
// log("Design cells:\n");
|
||||||
|
// for (auto [name, cell] : design_cells) {
|
||||||
|
// (void)cell;
|
||||||
|
// log("\t%s\n", name.c_str());
|
||||||
|
// }
|
||||||
|
// log("Design pins:\n");
|
||||||
|
// for (auto [name, pin] : design_pins) {
|
||||||
|
// (void)pin;
|
||||||
|
// log("\t%s\n", name.c_str());
|
||||||
|
// }
|
||||||
|
// log("Design nets:\n");
|
||||||
|
// for (auto [name, net] : design_nets) {
|
||||||
|
// (void)net;
|
||||||
|
// log("\t%s\n", name.c_str());
|
||||||
|
// }
|
||||||
|
// log("\n");
|
||||||
|
log("Constrained ports:\n");
|
||||||
|
for (auto [ref, bits] : constrained_ports) {
|
||||||
|
auto [name, port] = ref;
|
||||||
|
(void)port;
|
||||||
|
log("\t%s\n", name.c_str());
|
||||||
|
bits.dump();
|
||||||
|
}
|
||||||
|
log("Constrained cells:\n");
|
||||||
|
for (auto& [name, cell] : constrained_cells) {
|
||||||
|
(void)cell;
|
||||||
|
log("\t%s\n", name.c_str());
|
||||||
|
}
|
||||||
|
log("Constrained pins:\n");
|
||||||
|
for (auto& [ref, bits] : constrained_pins) {
|
||||||
|
auto [name, pin] = ref;
|
||||||
|
(void)pin;
|
||||||
|
log("\t%s\n", name.c_str());
|
||||||
|
bits.dump();
|
||||||
|
}
|
||||||
|
log("Constrained nets:\n");
|
||||||
|
for (auto& [ref, bits] : constrained_nets) {
|
||||||
|
auto [name, net] = ref;
|
||||||
|
(void)net;
|
||||||
|
log("\t%s\n", name.c_str());
|
||||||
|
bits.dump();
|
||||||
|
}
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeepHierarchyWorker {
|
||||||
|
std::unordered_set<Module*> tracked_modules = {};
|
||||||
|
Design* design = nullptr;
|
||||||
|
bool mark(Module* mod) {
|
||||||
|
for (auto* cell : mod->cells()) {
|
||||||
|
if (auto* submod = design->module(cell->type))
|
||||||
|
if (mark(submod)) {
|
||||||
|
mod->set_bool_attribute(ID::keep_hierarchy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tracked_modules.count(mod)) {
|
||||||
|
mod->set_bool_attribute(ID::keep_hierarchy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
KeepHierarchyWorker(SdcObjects* objects, Design* d) : design(d) {
|
||||||
|
for (auto [ref, _] : objects->constrained_ports) {
|
||||||
|
tracked_modules.insert(ref.second->module);
|
||||||
|
}
|
||||||
|
for (auto& [_, cell] : objects->constrained_cells) {
|
||||||
|
tracked_modules.insert(cell->module);
|
||||||
|
}
|
||||||
|
for (auto& [ref, _] : objects->constrained_pins) {
|
||||||
|
tracked_modules.insert(ref.second.first->module);
|
||||||
|
}
|
||||||
|
for (auto& [ref, _] : objects->constrained_nets) {
|
||||||
|
tracked_modules.insert(ref.second->module);
|
||||||
|
}
|
||||||
|
log_debug("keep_hierarchy tracked modules:\n");
|
||||||
|
for (auto* mod : tracked_modules)
|
||||||
|
log_debug("\t%s\n", mod->name);
|
||||||
|
}
|
||||||
|
bool mark() {
|
||||||
|
return mark(design->top_module());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
void keep_hierarchy() {
|
||||||
|
(void)KeepHierarchyWorker(this, design).mark();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO vectors
|
||||||
|
// TODO cell arrays?
|
||||||
|
struct MatchConfig {
|
||||||
|
enum MatchMode {
|
||||||
|
WILDCARD,
|
||||||
|
REGEX,
|
||||||
|
} match;
|
||||||
|
bool match_case;
|
||||||
|
enum HierMode {
|
||||||
|
FLAT,
|
||||||
|
TREE,
|
||||||
|
} hier;
|
||||||
|
MatchConfig(bool regexp_flag, bool nocase_flag, bool hierarchical_flag) :
|
||||||
|
match(regexp_flag ? REGEX : WILDCARD),
|
||||||
|
match_case(!nocase_flag),
|
||||||
|
hier(hierarchical_flag ? FLAT : TREE) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::pair<bool, BitSelection> matches(std::string name, const std::string& pat, const MatchConfig& config) {
|
||||||
|
(void)config;
|
||||||
|
bool got_bit_index = false;;
|
||||||
|
int bit_idx;
|
||||||
|
std::string pat_base = pat;
|
||||||
|
size_t pos = pat.rfind('[');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
got_bit_index = true;
|
||||||
|
pat_base = pat.substr(0, pos);
|
||||||
|
std::string bit_selector = pat.substr(pos + 1, pat.rfind(']') - pos - 1);
|
||||||
|
for (auto c : bit_selector)
|
||||||
|
if (!std::isdigit(c))
|
||||||
|
log_error("Unsupported bit selector %s in SDC pattern %s\n",
|
||||||
|
bit_selector.c_str(), pat.c_str());
|
||||||
|
bit_idx = std::stoi(bit_selector);
|
||||||
|
|
||||||
|
}
|
||||||
|
BitSelection bits = {};
|
||||||
|
if (name == pat_base) {
|
||||||
|
if (got_bit_index) {
|
||||||
|
bits.set(bit_idx);
|
||||||
|
return std::make_pair(true, bits);
|
||||||
|
} else {
|
||||||
|
bits.set_all();
|
||||||
|
return std::make_pair(true, bits);
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return std::make_pair(false, bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getter_graph_node(TclCall call) {
|
||||||
|
// Insert -getter-validated as first argument for passing to unknown
|
||||||
|
// to distinguish resolved and unknown getters.
|
||||||
|
// For example, if call is ["get_foo", "-bar"]
|
||||||
|
// then newCall is ["get_foo", "-getter-validated", "-bar"]
|
||||||
|
Tcl_Obj* validity_tag = Tcl_NewStringObj("-getter-validated", -1);
|
||||||
|
Tcl_IncrRefCount(validity_tag);
|
||||||
|
std::vector<Tcl_Obj*> newObjv(call.objc + 1);
|
||||||
|
log_assert(call.objc > 0);
|
||||||
|
newObjv[0] = call.objv[0];
|
||||||
|
newObjv[1] = validity_tag;
|
||||||
|
for (int i = 1; i < call.objc; ++i) {
|
||||||
|
newObjv[i + 1] = call.objv[i];
|
||||||
|
}
|
||||||
|
// Send the vector to the Tcl land
|
||||||
|
Tcl_Obj** allocatedObjv = (Tcl_Obj**)Tcl_Alloc((call.objc + 1) * sizeof(Tcl_Obj*));
|
||||||
|
for (int i = 0; i < call.objc + 1; ++i) {
|
||||||
|
allocatedObjv[i] = newObjv[i];
|
||||||
|
}
|
||||||
|
TclCall newCall {
|
||||||
|
.interp = call.interp,
|
||||||
|
.objc = call.objc + 1,
|
||||||
|
.objv = allocatedObjv
|
||||||
|
};
|
||||||
|
// Finally, redirect to unknown handler
|
||||||
|
return redirect_unknown(newCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int redirect_unknown(TclCall call) {
|
||||||
|
// TODO redirect to different command
|
||||||
|
Tcl_Obj *newCmd = Tcl_NewStringObj("unknown", -1);
|
||||||
|
auto newObjc = call.objc + 1;
|
||||||
|
Tcl_Obj** newObjv = (Tcl_Obj**)Tcl_Alloc(newObjc * sizeof(Tcl_Obj*));
|
||||||
|
newObjv[0] = newCmd;
|
||||||
|
for (int i = 1; i < newObjc; i++) {
|
||||||
|
newObjv[i] = call.objv[i - 1];
|
||||||
|
}
|
||||||
|
int result = Tcl_EvalObjv(call.interp, newObjc, newObjv, 0);
|
||||||
|
Tcl_DecrRefCount(newCmd);
|
||||||
|
Tcl_Free((char*) newObjv);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct SdcGraphNode {
|
||||||
|
using Child = std::variant<SdcGraphNode*, std::string>;
|
||||||
|
std::vector<Child> children;
|
||||||
|
SdcGraphNode() = default;
|
||||||
|
void addChild(SdcGraphNode* child) {
|
||||||
|
children.push_back(child);
|
||||||
|
}
|
||||||
|
void addChild(std::string tcl_string) {
|
||||||
|
children.push_back(tcl_string);
|
||||||
|
}
|
||||||
|
void dump(std::ostream& os) const {
|
||||||
|
bool first = true;
|
||||||
|
for (auto child : children) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
os << " ";
|
||||||
|
}
|
||||||
|
if (auto* s = std::get_if<std::string>(&child))
|
||||||
|
os << *s;
|
||||||
|
else if (SdcGraphNode*& c = *std::get_if<SdcGraphNode*>(&child)) {
|
||||||
|
os << "[";
|
||||||
|
c->dump(os);
|
||||||
|
os << "]";
|
||||||
|
} else {
|
||||||
|
log_assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t get_node_count(Tcl_Interp* interp) {
|
||||||
|
const char* idx_raw = Tcl_GetVar(interp, "sdc_call_index", TCL_GLOBAL_ONLY);
|
||||||
|
log_assert(idx_raw);
|
||||||
|
std::string idx(idx_raw);
|
||||||
|
for (auto c : idx)
|
||||||
|
if (!std::isdigit(c))
|
||||||
|
log_error("sdc_call_index non-numeric value %s\n", idx.c_str());
|
||||||
|
return std::stoi(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<std::string>> gather_nested_calls(Tcl_Interp* interp) {
|
||||||
|
|
||||||
|
Tcl_Obj* listObj = Tcl_GetVar2Ex(interp, "sdc_calls", nullptr, TCL_GLOBAL_ONLY);
|
||||||
|
YS_Tcl_Size listLength;
|
||||||
|
|
||||||
|
std::vector<std::vector<std::string>> sdc_calls;
|
||||||
|
if (Tcl_ListObjLength(interp, listObj, &listLength) == TCL_OK) {
|
||||||
|
for (int i = 0; i < listLength; i++) {
|
||||||
|
Tcl_Obj* subListObj;
|
||||||
|
std::vector<std::string> subList;
|
||||||
|
if (Tcl_ListObjIndex(interp, listObj, i, &subListObj) != TCL_OK) {
|
||||||
|
log_error("broken list of lists\n");
|
||||||
|
}
|
||||||
|
YS_Tcl_Size subListLength;
|
||||||
|
if (Tcl_ListObjLength(interp, subListObj, &subListLength) == TCL_OK) {
|
||||||
|
// Valid list - extract elements
|
||||||
|
for (int j = 0; j < subListLength; j++) {
|
||||||
|
Tcl_Obj* elementObj;
|
||||||
|
if (Tcl_ListObjIndex(interp, subListObj, j, &elementObj) == TCL_OK) {
|
||||||
|
const char* elementStr = Tcl_GetString(elementObj);
|
||||||
|
subList.push_back(std::string(elementStr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Single element, not a list
|
||||||
|
const char* elementStr = Tcl_GetString(subListObj);
|
||||||
|
subList.push_back(std::string(elementStr));
|
||||||
|
}
|
||||||
|
sdc_calls.push_back(subList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log_assert(sdc_calls.size() == get_node_count(interp));
|
||||||
|
return sdc_calls;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SdcGraphNode> build_graph(const std::vector<std::vector<std::string>>& sdc_calls) {
|
||||||
|
size_t node_count = sdc_calls.size();
|
||||||
|
std::vector<SdcGraphNode> graph(node_count);
|
||||||
|
for (size_t i = 0; i < node_count; i++) {
|
||||||
|
auto& new_node = graph[i];
|
||||||
|
for (size_t j = 0; j < sdc_calls[i].size(); j++) {
|
||||||
|
auto arg = sdc_calls[i][j];
|
||||||
|
const std::string prefix = "YOSYS_SDC_MAGIC_NODE_";
|
||||||
|
auto pos = arg.find(prefix);
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
std::string rest = arg.substr(pos + prefix.length());
|
||||||
|
for (auto c : rest)
|
||||||
|
if (!std::isdigit(c))
|
||||||
|
log_error("weird thing %s in %s\n", rest.c_str(), arg.c_str());
|
||||||
|
size_t arg_node_idx = std::stoi(rest);
|
||||||
|
log_assert(arg_node_idx < graph.size());
|
||||||
|
new_node.addChild(&graph[arg_node_idx]);
|
||||||
|
} else {
|
||||||
|
new_node.addChild(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<bool> node_ownership(const std::vector<SdcGraphNode>& graph) {
|
||||||
|
std::vector<bool> has_parent(graph.size());
|
||||||
|
for (auto node : graph) {
|
||||||
|
for (auto child : node.children) {
|
||||||
|
if (SdcGraphNode** pp = std::get_if<SdcGraphNode*>(&child)) {
|
||||||
|
size_t idx = std::distance(&graph.front(), (const SdcGraphNode*)*pp);
|
||||||
|
log_assert(idx < has_parent.size());
|
||||||
|
has_parent[idx] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return has_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_sdc_graph(const std::vector<SdcGraphNode>& graph, const std::vector<bool>& has_parent) {
|
||||||
|
for (size_t i = 0; i < graph.size(); i++) {
|
||||||
|
if (!has_parent[i]) {
|
||||||
|
graph[i].dump(std::cout);
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inspect_globals(Tcl_Interp* interp, bool dump_mode) {
|
||||||
|
std::vector<std::vector<std::string>> sdc_calls = gather_nested_calls(interp);
|
||||||
|
std::vector<SdcGraphNode> graph = build_graph(sdc_calls);
|
||||||
|
if (dump_mode)
|
||||||
|
dump_sdc_graph(graph, node_ownership(graph));
|
||||||
|
}
|
||||||
|
|
||||||
|
// patterns -> (pattern-object-bit)s
|
||||||
|
template <typename T, typename U>
|
||||||
|
std::vector<std::tuple<std::string, T, BitSelection>>
|
||||||
|
find_matching(U objects, const MatchConfig& config, const std::vector<std::string> &patterns, const char* obj_type)
|
||||||
|
{
|
||||||
|
std::vector<std::tuple<std::string, T, BitSelection>> resolved;
|
||||||
|
for (auto pat : patterns) {
|
||||||
|
bool found = false;
|
||||||
|
for (auto [name, obj] : objects) {
|
||||||
|
auto [does_match, matching_bits] = matches(name, pat, config);
|
||||||
|
if (does_match) {
|
||||||
|
found = true;
|
||||||
|
resolved.push_back(std::make_tuple(name, obj, matching_bits));
|
||||||
|
// TODO add a continue statement, conditional on config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
log_warning("No matches in design for %s %s\n", obj_type, pat.c_str());
|
||||||
|
}
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TclOpts {
|
||||||
|
const char* name;
|
||||||
|
std::initializer_list<const char*> legals;
|
||||||
|
TclOpts(const char* name, std::initializer_list<const char*> legals) : name(name), legals(legals) {}
|
||||||
|
bool parse_opt(Tcl_Obj* obj, const char* opt_name) {
|
||||||
|
char* arg = Tcl_GetString(obj);
|
||||||
|
std::string expected = std::string("-") + opt_name;
|
||||||
|
if (expected == arg) {
|
||||||
|
if (!std::find_if(legals.begin(), legals.end(),
|
||||||
|
[&opt_name](const char* str) { return opt_name == str; }))
|
||||||
|
log_cmd_error("Illegal argument %s for %s.\n", expected.c_str(), name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GetterOpts : TclOpts {
|
||||||
|
bool hierarchical_flag = false;
|
||||||
|
bool regexp_flag = false;
|
||||||
|
bool nocase_flag = false;
|
||||||
|
std::string separator = "/";
|
||||||
|
Tcl_Obj* of_objects = nullptr;
|
||||||
|
std::vector<std::string> patterns = {};
|
||||||
|
GetterOpts(const char* name, std::initializer_list<const char*> legals) : TclOpts(name, legals) {}
|
||||||
|
template<typename T>
|
||||||
|
bool parse_flag(Tcl_Obj* obj, const char* flag_name, T& flag_var) {
|
||||||
|
bool ret = parse_opt(obj, flag_name);
|
||||||
|
if (ret)
|
||||||
|
flag_var = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void parse(int objc, Tcl_Obj* const objv[]) {
|
||||||
|
int i = 1;
|
||||||
|
for (; i < objc; i++) {
|
||||||
|
if (parse_flag(objv[i], "hierarchical", hierarchical_flag)) continue;
|
||||||
|
if (parse_flag(objv[i], "hier", hierarchical_flag)) continue;
|
||||||
|
if (parse_flag(objv[i], "regexp", regexp_flag)) continue;
|
||||||
|
if (parse_flag(objv[i], "nocase", nocase_flag)) continue;
|
||||||
|
if (parse_opt(objv[i], "hsc")) {
|
||||||
|
log_assert(i + 1 < objc);
|
||||||
|
separator = Tcl_GetString(objv[++i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (parse_opt(objv[i], "of_objects")) {
|
||||||
|
log_assert(i + 1 < objc);
|
||||||
|
of_objects = objv[++i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (; i < objc; i++) {
|
||||||
|
patterns.push_back(Tcl_GetString(objv[i]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
void check_simple() {
|
||||||
|
if (regexp_flag || hierarchical_flag || nocase_flag || separator != "/" || of_objects) {
|
||||||
|
log_error("%s got unexpected flags in simple mode\n", name);
|
||||||
|
}
|
||||||
|
if (patterns.size() != 1)
|
||||||
|
log_error("%s got unexpected number of patterns in simple mode: %zu\n", name, patterns.size());
|
||||||
|
}
|
||||||
|
void check_simple_sep() {
|
||||||
|
if (separator != "/")
|
||||||
|
log_error("Only '/' accepted as separator");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void merge_or_init(const T& key, dict<T, BitSelection>& dst, const BitSelection& src) {
|
||||||
|
if (dst.count(key) == 0) {
|
||||||
|
dst[key] = src;
|
||||||
|
} else {
|
||||||
|
dst[key].merge(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdc_get_pins_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[])
|
||||||
|
{
|
||||||
|
auto* objects = (SdcObjects*)data;
|
||||||
|
GetterOpts opts("get_pins", {"hierarchical", "hier", "regexp", "nocase", "hsc", "of_objects"});
|
||||||
|
opts.parse(objc, objv);
|
||||||
|
if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter)
|
||||||
|
opts.check_simple();
|
||||||
|
opts.check_simple_sep();
|
||||||
|
|
||||||
|
MatchConfig config(opts.regexp_flag, opts.nocase_flag, opts.hierarchical_flag);
|
||||||
|
std::vector<std::tuple<std::string, SdcObjects::CellPin, BitSelection>> resolved;
|
||||||
|
const auto& pins = objects->design_pins;
|
||||||
|
resolved = find_matching<SdcObjects::CellPin, decltype(pins)>(pins, config, opts.patterns, "pin");
|
||||||
|
|
||||||
|
return getter_graph_node(TclCall{interp, objc, objv});
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdc_get_ports_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[])
|
||||||
|
{
|
||||||
|
auto* objects = (SdcObjects*)data;
|
||||||
|
GetterOpts opts("get_ports", {"regexp", "nocase"});
|
||||||
|
opts.parse(objc, objv);
|
||||||
|
if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter)
|
||||||
|
opts.check_simple();
|
||||||
|
|
||||||
|
MatchConfig config(opts.regexp_flag, opts.nocase_flag, false);
|
||||||
|
std::vector<std::tuple<std::string, Wire*, BitSelection>> resolved;
|
||||||
|
const auto& ports = objects->design_ports;
|
||||||
|
resolved = find_matching<Wire*, decltype(ports)>(ports, config, opts.patterns, "port");
|
||||||
|
|
||||||
|
for (auto [name, wire, matching_bits] : resolved) {
|
||||||
|
if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint)
|
||||||
|
merge_or_init(std::make_pair(name, wire), objects->constrained_ports, matching_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getter_graph_node(TclCall{interp, objc, objv});
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdc_get_nets_cmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj* const objv[])
|
||||||
|
{
|
||||||
|
auto* objects = (SdcObjects*)data;
|
||||||
|
GetterOpts opts("get_nets", {"hierarchical", "hier", "regexp", "nocase", "hsc", "of_objects"});
|
||||||
|
opts.parse(objc, objv);
|
||||||
|
if (objects->collect_mode == SdcObjects::CollectMode::SimpleGetter)
|
||||||
|
opts.check_simple();
|
||||||
|
|
||||||
|
MatchConfig config(opts.regexp_flag, opts.nocase_flag, false);
|
||||||
|
std::vector<std::tuple<std::string, Wire*, BitSelection>> resolved;
|
||||||
|
const auto& ports = objects->design_nets;
|
||||||
|
resolved = find_matching<Wire*, decltype(ports)>(ports, config, opts.patterns, "net");
|
||||||
|
|
||||||
|
for (auto [name, wire, matching_bits] : resolved) {
|
||||||
|
if (objects->collect_mode != SdcObjects::CollectMode::FullConstraint)
|
||||||
|
merge_or_init(std::make_pair(name, wire), objects->constrained_nets, matching_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getter_graph_node(TclCall{interp, objc, objv});
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDCInterpreter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Tcl_Interp* interp = nullptr;
|
||||||
|
public:
|
||||||
|
std::unique_ptr<SdcObjects> objects;
|
||||||
|
~SDCInterpreter() {
|
||||||
|
if (interp)
|
||||||
|
Tcl_DeleteInterp(interp);
|
||||||
|
}
|
||||||
|
static SDCInterpreter& get() {
|
||||||
|
static SDCInterpreter instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
Tcl_Interp* fresh_interp(Design* design) {
|
||||||
|
if (interp)
|
||||||
|
Tcl_DeleteInterp(interp);
|
||||||
|
|
||||||
|
interp = Tcl_CreateInterp();
|
||||||
|
if (Tcl_Init(interp)!=TCL_OK)
|
||||||
|
log_error("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno()));
|
||||||
|
|
||||||
|
objects = std::make_unique<SdcObjects>(design);
|
||||||
|
objects->collect_mode = SdcObjects::CollectMode::SimpleGetter;
|
||||||
|
Tcl_CreateObjCommand(interp, "get_pins", sdc_get_pins_cmd, (ClientData) objects.get(), NULL);
|
||||||
|
Tcl_CreateObjCommand(interp, "get_nets", sdc_get_nets_cmd, (ClientData) objects.get(), NULL);
|
||||||
|
Tcl_CreateObjCommand(interp, "get_ports", sdc_get_ports_cmd, (ClientData) objects.get(), NULL);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Also see TclPass
|
||||||
|
struct SdcPass : public Pass {
|
||||||
|
void help() override
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" sdc [options] file\n");
|
||||||
|
log("\n");
|
||||||
|
log("Read the SDC file for the current design.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -dump\n");
|
||||||
|
log(" Dump the referenced design objects.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -dump-graph\n");
|
||||||
|
log(" Dump the uninterpreted call graph.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -keep_hierarchy\n");
|
||||||
|
log(" Add keep_hierarchy attributes while retaining SDC validity.\n");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
SdcPass() : Pass("sdc", "Read an SDC file") { }
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override {
|
||||||
|
log_header(design, "Executing SDC pass.\n");
|
||||||
|
log_experimental("sdc");
|
||||||
|
size_t argidx;
|
||||||
|
bool dump_mode = false;
|
||||||
|
bool dump_graph_mode = false;
|
||||||
|
bool keep_hierarchy_mode = false;
|
||||||
|
std::vector<std::string> stubs_paths;
|
||||||
|
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||||
|
if (args[argidx] == "-dump") {
|
||||||
|
dump_mode = true;
|
||||||
|
continue;
|
||||||
|
} else if (args[argidx] == "-dump-graph") {
|
||||||
|
dump_graph_mode = true;
|
||||||
|
continue;
|
||||||
|
} else if (args[argidx] == "-keep_hierarchy") {
|
||||||
|
keep_hierarchy_mode = true;
|
||||||
|
continue;
|
||||||
|
} else if (args[argidx] == "-stubs" && argidx+1 < args.size()) {
|
||||||
|
stubs_paths.push_back(args[++argidx]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (argidx >= args.size())
|
||||||
|
log_cmd_error("Missing SDC file.\n");
|
||||||
|
|
||||||
|
std::string sdc_path = args[argidx++];
|
||||||
|
if (argidx < args.size())
|
||||||
|
log_cmd_error("Unexpected extra positional argument %s after SDC file %s.\n", args[argidx], sdc_path);
|
||||||
|
SDCInterpreter& sdc = SDCInterpreter::get();
|
||||||
|
Tcl_Interp *interp = sdc.fresh_interp(design);
|
||||||
|
Tcl_Preserve(interp);
|
||||||
|
std::string stub_path = "+/sdc/graph-stubs.sdc";
|
||||||
|
rewrite_filename(stub_path);
|
||||||
|
if (Tcl_EvalFile(interp, stub_path.c_str()) != TCL_OK)
|
||||||
|
log_cmd_error("SDC interpreter returned an error in stub preamble file: %s\n", Tcl_GetStringResult(interp));
|
||||||
|
for (auto path : stubs_paths)
|
||||||
|
if (Tcl_EvalFile(interp, path.c_str()) != TCL_OK)
|
||||||
|
log_cmd_error("SDC interpreter returned an error in OpenSTA stub file %s: %s\n", path.c_str(), Tcl_GetStringResult(interp));
|
||||||
|
if (Tcl_EvalFile(interp, sdc_path.c_str()) != TCL_OK)
|
||||||
|
log_cmd_error("SDC interpreter returned an error: %s\n", Tcl_GetStringResult(interp));
|
||||||
|
if (dump_mode)
|
||||||
|
sdc.objects->dump();
|
||||||
|
if (keep_hierarchy_mode)
|
||||||
|
sdc.objects->keep_hierarchy();
|
||||||
|
inspect_globals(interp, dump_graph_mode);
|
||||||
|
Tcl_Release(interp);
|
||||||
|
}
|
||||||
|
} SdcPass;
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_END
|
||||||
|
#endif
|
||||||
|
|
@ -199,8 +199,15 @@ static void detect_fsm(RTLIL::Wire *wire, bool ignore_self_reset=false)
|
||||||
}
|
}
|
||||||
|
|
||||||
SigSpec sig_y = sig_d, sig_undef;
|
SigSpec sig_y = sig_d, sig_undef;
|
||||||
if (!ignore_self_reset && ce.eval(sig_y, sig_undef))
|
if (!ignore_self_reset) {
|
||||||
is_self_resetting = true;
|
if (cellport.first->type == ID($adff)) {
|
||||||
|
SigSpec sig_arst = assign_map(cellport.first->getPort(ID::ARST));
|
||||||
|
if (ce.eval(sig_arst, sig_undef))
|
||||||
|
is_self_resetting = true;
|
||||||
|
}
|
||||||
|
else if (ce.eval(sig_y, sig_undef))
|
||||||
|
is_self_resetting = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_fsm_encoding_attr)
|
if (has_fsm_encoding_attr)
|
||||||
|
|
|
||||||
|
|
@ -283,7 +283,7 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo
|
||||||
if (attrs1 != attrs2)
|
if (attrs1 != attrs2)
|
||||||
return attrs2 > attrs1;
|
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)
|
bool check_public_name(RTLIL::IdString id)
|
||||||
|
|
@ -722,6 +722,8 @@ struct OptCleanPass : public Pass {
|
||||||
ct_reg.clear();
|
ct_reg.clear();
|
||||||
ct_all.clear();
|
ct_all.clear();
|
||||||
log_pop();
|
log_pop();
|
||||||
|
|
||||||
|
request_garbage_collection();
|
||||||
}
|
}
|
||||||
} OptCleanPass;
|
} OptCleanPass;
|
||||||
|
|
||||||
|
|
@ -784,6 +786,8 @@ struct CleanPass : public Pass {
|
||||||
keep_cache.reset();
|
keep_cache.reset();
|
||||||
ct_reg.clear();
|
ct_reg.clear();
|
||||||
ct_all.clear();
|
ct_all.clear();
|
||||||
|
|
||||||
|
request_garbage_collection();
|
||||||
}
|
}
|
||||||
} CleanPass;
|
} 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)
|
void AbcModuleState::dump_loop_graph(FILE *f, int &nr, dict<int, pool<int>> &edges, pool<int> &workpool, std::vector<int> &in_counts)
|
||||||
|
|
@ -1064,7 +1064,7 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module
|
||||||
abc_script += stringf("; write_blif %s/output.blif", run_abc.tempdir_name);
|
abc_script += stringf("; write_blif %s/output.blif", run_abc.tempdir_name);
|
||||||
abc_script = add_echos_to_abc_cmd(abc_script);
|
abc_script = add_echos_to_abc_cmd(abc_script);
|
||||||
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
|
#if defined(__linux__) && !defined(YOSYS_DISABLE_SPAWN)
|
||||||
abc_script += "; echo \"YOSYS_ABC_DONE\"\n";
|
abc_script += "; echo; echo \"YOSYS_ABC_DONE\"\n";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (size_t i = 0; i+1 < abc_script.size(); i++)
|
for (size_t i = 0; i+1 < abc_script.size(); i++)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ std::vector<Module*> order_modules(Design *design, std::vector<Module *> modules
|
||||||
sort.edge(submodule, m);
|
sort.edge(submodule, m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log_assert(sort.sort());
|
bool is_sorted = sort.sort();
|
||||||
|
log_assert(is_sorted);
|
||||||
return sort.sorted;
|
return sort.sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -827,6 +827,17 @@ std::string vlog_identifier(std::string str)
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void event2vl_wire(std::string &edge, LibertyExpression& parsed, const std::string& wire)
|
||||||
|
{
|
||||||
|
edge.clear();
|
||||||
|
if (parsed.kind == LibertyExpression::Kind::NOT) {
|
||||||
|
edge = "negedge " + wire;
|
||||||
|
// parsed = parsed.children[0];
|
||||||
|
} else {
|
||||||
|
edge = "posedge " + wire;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr)
|
void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr)
|
||||||
{
|
{
|
||||||
edge.clear();
|
edge.clear();
|
||||||
|
|
@ -843,33 +854,160 @@ void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_preset_var(std::string var, std::string type)
|
enum ClearPresetVar {
|
||||||
|
Error,
|
||||||
|
L,
|
||||||
|
H,
|
||||||
|
T,
|
||||||
|
X,
|
||||||
|
};
|
||||||
|
|
||||||
|
ClearPresetVar clear_preset_var(std::string type)
|
||||||
{
|
{
|
||||||
if (type.find('L') != std::string::npos) {
|
if (type.find('L') != std::string::npos) {
|
||||||
|
return ClearPresetVar::L;
|
||||||
|
}
|
||||||
|
if (type.find('H') != std::string::npos) {
|
||||||
|
return ClearPresetVar::H;
|
||||||
|
}
|
||||||
|
if (type.find('T') != std::string::npos) {
|
||||||
|
return ClearPresetVar::T;
|
||||||
|
}
|
||||||
|
if (type.find('X') != std::string::npos) {
|
||||||
|
return ClearPresetVar::X;
|
||||||
|
}
|
||||||
|
return ClearPresetVar::X;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_clear_preset_var(std::string var, ClearPresetVar type)
|
||||||
|
{
|
||||||
|
if (type == ClearPresetVar::L) {
|
||||||
printf(" %s <= 0;\n", var.c_str());
|
printf(" %s <= 0;\n", var.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type.find('H') != std::string::npos) {
|
if (type == ClearPresetVar::H) {
|
||||||
printf(" %s <= 1;\n", var.c_str());
|
printf(" %s <= 1;\n", var.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type.find('T') != std::string::npos) {
|
if (type == ClearPresetVar::T) {
|
||||||
printf(" %s <= ~%s;\n", var.c_str(), var.c_str());
|
printf(" %s <= ~%s;\n", var.c_str(), var.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type.find('X') != std::string::npos) {
|
if (type == ClearPresetVar::X) {
|
||||||
printf(" %s <= 'bx;\n", var.c_str());
|
printf(" %s <= 'bx;\n", var.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FfEdge {
|
||||||
|
std::string edge;
|
||||||
|
std::string expr;
|
||||||
|
};
|
||||||
|
struct FfEdges {
|
||||||
|
FfEdge clock;
|
||||||
|
FfEdge clear;
|
||||||
|
FfEdge preset;
|
||||||
|
std::string edge;
|
||||||
|
void wired(FfEdge& edge, const LibertyAst* ast, const std::string& wire, const char* tag) {
|
||||||
|
auto* found = ast->find(tag);
|
||||||
|
if (!found)
|
||||||
|
return;
|
||||||
|
auto lexer = LibertyExpression::Lexer(found->value);
|
||||||
|
auto expr = LibertyExpression::parse(lexer);
|
||||||
|
event2vl_wire(edge.edge, expr, wire);
|
||||||
|
edge.expr = expr.vlog_str();
|
||||||
|
}
|
||||||
|
FfEdges(LibertyAst* child, const std::string& clear_wire, const std::string& preset_wire) {
|
||||||
|
wired(clear, child, clear_wire, "clear");
|
||||||
|
wired(preset, child, preset_wire, "preset");
|
||||||
|
event2vl(child->find("clocked_on"), clock.edge, clock.expr);
|
||||||
|
edge = "";
|
||||||
|
if (!clock.edge.empty())
|
||||||
|
edge += (edge.empty() ? "" : ", ") + clock.edge;
|
||||||
|
if (!clear.edge.empty())
|
||||||
|
edge += (edge.empty() ? "" : ", ") + clear.edge;
|
||||||
|
if (!preset.edge.empty())
|
||||||
|
edge += (edge.empty() ? "" : ", ") + preset.edge;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FfVar {
|
||||||
|
std::string var;
|
||||||
|
std::string edge;
|
||||||
|
FfEdge clear;
|
||||||
|
FfEdge preset;
|
||||||
|
// Value for both asserted
|
||||||
|
const char* clear_preset_var_name;
|
||||||
|
std::string next_state;
|
||||||
|
const char* else_prefix = "";
|
||||||
|
public:
|
||||||
|
void proc_header() {
|
||||||
|
printf(" always @(%s) begin\n", edge.c_str());
|
||||||
|
}
|
||||||
|
void proc_footer() {
|
||||||
|
if (*else_prefix)
|
||||||
|
printf(" end\n");
|
||||||
|
|
||||||
|
printf(" end\n");
|
||||||
|
}
|
||||||
|
void proc_cond(FfEdge& edge, const char* value) {
|
||||||
|
printf(" %sif (%s) begin\n", else_prefix, edge.expr.c_str());
|
||||||
|
printf(" %s <= %s;\n", var.c_str(), value);
|
||||||
|
printf(" end\n");
|
||||||
|
else_prefix = "else ";
|
||||||
|
}
|
||||||
|
void proc_cond_clear() { proc_cond(clear, "0"); }
|
||||||
|
void proc_cond_preset() { proc_cond(preset, "1"); }
|
||||||
|
void proc_next_state() {
|
||||||
|
if (*else_prefix)
|
||||||
|
printf(" %sbegin\n", else_prefix);
|
||||||
|
printf(" %s <= %s;\n", var.c_str(), next_state.c_str());
|
||||||
|
}
|
||||||
|
void proc(LibertyAst* child) {
|
||||||
|
else_prefix = "";
|
||||||
|
proc_header();
|
||||||
|
if (!clear.expr.empty() && !preset.expr.empty()) {
|
||||||
|
ClearPresetVar clear_preset = clear_preset_var(find_non_null(child, clear_preset_var_name)->value);
|
||||||
|
if (clear_preset == ClearPresetVar::L) {
|
||||||
|
proc_cond_clear();
|
||||||
|
proc_cond_preset();
|
||||||
|
proc_next_state();
|
||||||
|
proc_footer();
|
||||||
|
return;
|
||||||
|
} else if (clear_preset == ClearPresetVar::H) {
|
||||||
|
// Notice that preset and clear are swapped
|
||||||
|
proc_cond_preset();
|
||||||
|
proc_cond_clear();
|
||||||
|
proc_next_state();
|
||||||
|
proc_footer();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Boo, we have to emit non-synthesizable verilog
|
||||||
|
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear.expr.c_str(), preset.expr.c_str());
|
||||||
|
print_clear_preset_var(var, clear_preset);
|
||||||
|
printf(" end\n");
|
||||||
|
else_prefix = "else ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!clear.expr.empty()) {
|
||||||
|
proc_cond_clear();
|
||||||
|
}
|
||||||
|
if (!preset.expr.empty()) {
|
||||||
|
proc_cond_preset();
|
||||||
|
}
|
||||||
|
proc_next_state();
|
||||||
|
proc_footer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void gen_verilogsim_cell(const LibertyAst *ast)
|
void gen_verilogsim_cell(const LibertyAst *ast)
|
||||||
{
|
{
|
||||||
if (ast->find("statetable") != NULL)
|
if (ast->find("statetable") != NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CHECK_NV(ast->args.size(), == 1);
|
CHECK_NV(ast->args.size(), == 1);
|
||||||
printf("module %s (", vlog_identifier(ast->args[0]).c_str());
|
auto module_name = vlog_identifier(ast->args[0]);
|
||||||
|
printf("module %s (", module_name.c_str());
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto child : ast->children) {
|
for (auto child : ast->children) {
|
||||||
if (child->id != "pin")
|
if (child->id != "pin")
|
||||||
|
|
@ -883,13 +1021,29 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
||||||
for (auto child : ast->children) {
|
for (auto child : ast->children) {
|
||||||
if (child->id != "ff" && child->id != "latch")
|
if (child->id != "ff" && child->id != "latch")
|
||||||
continue;
|
continue;
|
||||||
printf(" reg ");
|
|
||||||
first = true;
|
first = true;
|
||||||
|
std::string iq = "";
|
||||||
for (auto arg : child->args) {
|
for (auto arg : child->args) {
|
||||||
|
if (first)
|
||||||
|
printf(" reg ");
|
||||||
printf("%s%s", first ? "" : ", ", vlog_identifier(arg).c_str());
|
printf("%s%s", first ? "" : ", ", vlog_identifier(arg).c_str());
|
||||||
|
if (first)
|
||||||
|
iq = vlog_identifier(arg);
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
printf(";\n");
|
if (!first)
|
||||||
|
printf(";\n");
|
||||||
|
first = true;
|
||||||
|
for (auto gchild : child->children) {
|
||||||
|
if (gchild->id == "clear" || gchild->id == "preset") {
|
||||||
|
if (first)
|
||||||
|
printf(" wire ");
|
||||||
|
printf("%s%s_%s", first ? "" : ", ", iq.c_str(), gchild->id.c_str());
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!first)
|
||||||
|
printf(";\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto child : ast->children) {
|
for (auto child : ast->children) {
|
||||||
|
|
@ -909,63 +1063,45 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
||||||
if (child->id != "ff" || child->args.size() != 2)
|
if (child->id != "ff" || child->args.size() != 2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string iq_var = vlog_identifier(child->args[0]);
|
auto iq_name = vlog_identifier(child->args[0]);
|
||||||
std::string iqn_var = vlog_identifier(child->args[1]);
|
auto clear_wire = iq_name + "_clear";
|
||||||
|
auto preset_wire = iq_name + "_preset";
|
||||||
|
FfEdges edges(child, clear_wire, preset_wire);
|
||||||
|
|
||||||
std::string clock_edge, clock_expr;
|
if (edges.edge.empty())
|
||||||
event2vl(child->find("clocked_on"), clock_edge, clock_expr);
|
|
||||||
|
|
||||||
std::string clear_edge, clear_expr;
|
|
||||||
event2vl(child->find("clear"), clear_edge, clear_expr);
|
|
||||||
|
|
||||||
std::string preset_edge, preset_expr;
|
|
||||||
event2vl(child->find("preset"), preset_edge, preset_expr);
|
|
||||||
|
|
||||||
std::string edge = "";
|
|
||||||
if (!clock_edge.empty())
|
|
||||||
edge += (edge.empty() ? "" : ", ") + clock_edge;
|
|
||||||
if (!clear_edge.empty())
|
|
||||||
edge += (edge.empty() ? "" : ", ") + clear_edge;
|
|
||||||
if (!preset_edge.empty())
|
|
||||||
edge += (edge.empty() ? "" : ", ") + preset_edge;
|
|
||||||
|
|
||||||
if (edge.empty())
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
printf(" always @(%s) begin\n", edge.c_str());
|
std::string next_state = func2vl(find_non_null(child, "next_state")->value);
|
||||||
|
std::string not_next_state = std::string("~(") + next_state + ")";
|
||||||
|
|
||||||
const char *else_prefix = "";
|
|
||||||
if (!clear_expr.empty() && !preset_expr.empty()) {
|
|
||||||
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
|
|
||||||
clear_preset_var(iq_var, find_non_null(child, "clear_preset_var1")->value);
|
|
||||||
clear_preset_var(iqn_var, find_non_null(child, "clear_preset_var2")->value);
|
|
||||||
printf(" end\n");
|
|
||||||
else_prefix = "else ";
|
|
||||||
}
|
|
||||||
if (!clear_expr.empty()) {
|
|
||||||
printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str());
|
|
||||||
printf(" %s <= 0;\n", iq_var.c_str());
|
|
||||||
printf(" %s <= 1;\n", iqn_var.c_str());
|
|
||||||
printf(" end\n");
|
|
||||||
else_prefix = "else ";
|
|
||||||
}
|
|
||||||
if (!preset_expr.empty()) {
|
|
||||||
printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str());
|
|
||||||
printf(" %s <= 1;\n", iq_var.c_str());
|
|
||||||
printf(" %s <= 0;\n", iqn_var.c_str());
|
|
||||||
printf(" end\n");
|
|
||||||
else_prefix = "else ";
|
|
||||||
}
|
|
||||||
if (*else_prefix)
|
|
||||||
printf(" %sbegin\n", else_prefix);
|
|
||||||
std::string expr = find_non_null(child, "next_state")->value;
|
|
||||||
printf(" // %s\n", expr.c_str());
|
|
||||||
printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str());
|
|
||||||
printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str());
|
|
||||||
if (*else_prefix)
|
|
||||||
printf(" end\n");
|
|
||||||
|
|
||||||
printf(" end\n");
|
if (edges.clear.expr.length())
|
||||||
|
std::swap(clear_wire, edges.clear.expr);
|
||||||
|
if (edges.preset.expr.length())
|
||||||
|
std::swap(preset_wire, edges.preset.expr);
|
||||||
|
auto iq = FfVar {
|
||||||
|
.var = vlog_identifier(child->args[0]),
|
||||||
|
.edge = edges.edge,
|
||||||
|
.clear = edges.clear,
|
||||||
|
.preset = edges.preset,
|
||||||
|
.clear_preset_var_name = "clear_preset_var1",
|
||||||
|
.next_state = next_state,
|
||||||
|
};
|
||||||
|
auto iqn = FfVar {
|
||||||
|
.var = vlog_identifier(child->args[1]),
|
||||||
|
.edge = edges.edge,
|
||||||
|
// Swapped clear and preset
|
||||||
|
.clear = edges.preset,
|
||||||
|
.preset = edges.clear,
|
||||||
|
.clear_preset_var_name = "clear_preset_var2",
|
||||||
|
.next_state = not_next_state,
|
||||||
|
};
|
||||||
|
iq.proc(child);
|
||||||
|
iqn.proc(child);
|
||||||
|
if (edges.clear.expr.length())
|
||||||
|
printf(" assign %s = %s;\n", edges.clear.expr.c_str(), clear_wire.c_str());
|
||||||
|
if (edges.preset.expr.length())
|
||||||
|
printf(" assign %s = %s;\n", edges.preset.expr.c_str(), preset_wire.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto child : ast->children)
|
for (auto child : ast->children)
|
||||||
|
|
@ -990,8 +1126,8 @@ void gen_verilogsim_cell(const LibertyAst *ast)
|
||||||
const char *else_prefix = "";
|
const char *else_prefix = "";
|
||||||
if (!clear_expr.empty() && !preset_expr.empty()) {
|
if (!clear_expr.empty() && !preset_expr.empty()) {
|
||||||
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
|
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
|
||||||
clear_preset_var(iq_var, find_non_null(child, "clear_preset_var1")->value);
|
print_clear_preset_var(iq_var, clear_preset_var(find_non_null(child, "clear_preset_var1")->value));
|
||||||
clear_preset_var(iqn_var, find_non_null(child, "clear_preset_var2")->value);
|
print_clear_preset_var(iqn_var, clear_preset_var(find_non_null(child, "clear_preset_var2")->value));
|
||||||
printf(" end\n");
|
printf(" end\n");
|
||||||
else_prefix = "else ";
|
else_prefix = "else ";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,11 +164,11 @@ pyosys_headers = [
|
||||||
{
|
{
|
||||||
"global_id_storage_",
|
"global_id_storage_",
|
||||||
"global_id_index_",
|
"global_id_index_",
|
||||||
|
"global_autoidx_id_storage_",
|
||||||
"global_refcount_storage_",
|
"global_refcount_storage_",
|
||||||
"global_free_idx_list_",
|
"global_free_idx_list_",
|
||||||
"last_created_idx_ptr_",
|
|
||||||
"last_created_idx_",
|
|
||||||
"builtin_ff_cell_types",
|
"builtin_ff_cell_types",
|
||||||
|
"substrings",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -192,7 +192,7 @@ pyosys_headers = [
|
||||||
),
|
),
|
||||||
PyosysClass("SigChunk"),
|
PyosysClass("SigChunk"),
|
||||||
PyosysClass("SigBit", hash_expr="s"),
|
PyosysClass("SigBit", hash_expr="s"),
|
||||||
PyosysClass("SigSpec", hash_expr="s", denylist={"chunks"}),
|
PyosysClass("SigSpec", hash_expr="s", denylist=frozenset({"chunks"})),
|
||||||
PyosysClass(
|
PyosysClass(
|
||||||
"Cell",
|
"Cell",
|
||||||
ref_only=True,
|
ref_only=True,
|
||||||
|
|
@ -453,7 +453,7 @@ class PyosysWrapperGenerator(object):
|
||||||
) -> str:
|
) -> str:
|
||||||
is_method = isinstance(function, Method)
|
is_method = isinstance(function, Method)
|
||||||
function_return_type = function.return_type.format()
|
function_return_type = function.return_type.format()
|
||||||
if class_basename == "Const" and function_return_type in {
|
if class_basename in {"Const","IdString"} and function_return_type in {
|
||||||
"iterator",
|
"iterator",
|
||||||
"const_iterator",
|
"const_iterator",
|
||||||
}:
|
}:
|
||||||
|
|
@ -538,6 +538,8 @@ class PyosysWrapperGenerator(object):
|
||||||
python_name_override = "__ne__"
|
python_name_override = "__ne__"
|
||||||
elif function.operator == "<":
|
elif function.operator == "<":
|
||||||
python_name_override = "__lt__"
|
python_name_override = "__lt__"
|
||||||
|
elif function.operator == "[]" and function.const:
|
||||||
|
python_name_override = "__getitem__"
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -591,7 +593,10 @@ class PyosysWrapperGenerator(object):
|
||||||
# care
|
# care
|
||||||
return
|
return
|
||||||
|
|
||||||
has_containers = self.register_containers(field)
|
self.register_containers(field)
|
||||||
|
rvp = "py::return_value_policy::copy"
|
||||||
|
if isinstance(field.type, Pointer):
|
||||||
|
rvp = "py::return_value_policy::reference_internal"
|
||||||
|
|
||||||
definition_fn = f"def_{'readonly' if field.type.const else 'readwrite'}"
|
definition_fn = f"def_{'readonly' if field.type.const else 'readwrite'}"
|
||||||
if field.static:
|
if field.static:
|
||||||
|
|
@ -603,7 +608,7 @@ class PyosysWrapperGenerator(object):
|
||||||
f'"{field_python_basename}"',
|
f'"{field_python_basename}"',
|
||||||
f"&{metadata.name}::{field.name}",
|
f"&{metadata.name}::{field.name}",
|
||||||
]
|
]
|
||||||
def_args.append("py::return_value_policy::copy")
|
def_args.append(rvp)
|
||||||
print(
|
print(
|
||||||
f"\t\t\t.{definition_fn}({', '.join(def_args)})",
|
f"\t\t\t.{definition_fn}({', '.join(def_args)})",
|
||||||
file=self.f,
|
file=self.f,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
ifneq ($(SMALL),1)
|
ifneq ($(SMALL),1)
|
||||||
OBJS += techlibs/common/synth.o
|
OBJS += techlibs/common/synth.o
|
||||||
OBJS += techlibs/common/prep.o
|
OBJS += techlibs/common/prep.o
|
||||||
|
OBJS += techlibs/common/opensta.o
|
||||||
|
OBJS += techlibs/common/sdc_expand.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
GENFILES += techlibs/common/simlib_help.inc
|
GENFILES += techlibs/common/simlib_help.inc
|
||||||
|
|
@ -28,7 +30,6 @@ $(eval $(call add_share_file,share,techlibs/common/adff2dff.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/dff2ff.v))
|
$(eval $(call add_share_file,share,techlibs/common/dff2ff.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/gate2lut.v))
|
$(eval $(call add_share_file,share,techlibs/common/gate2lut.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/cmp2lut.v))
|
$(eval $(call add_share_file,share,techlibs/common/cmp2lut.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/cells.lib))
|
|
||||||
$(eval $(call add_share_file,share,techlibs/common/mul2dsp.v))
|
$(eval $(call add_share_file,share,techlibs/common/mul2dsp.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/abc9_model.v))
|
$(eval $(call add_share_file,share,techlibs/common/abc9_model.v))
|
||||||
$(eval $(call add_share_file,share,techlibs/common/abc9_map.v))
|
$(eval $(call add_share_file,share,techlibs/common/abc9_map.v))
|
||||||
|
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
library(yosys_cells) {
|
|
||||||
cell(DFF_N) {
|
|
||||||
ff(IQ, IQN) {
|
|
||||||
clocked_on: "!C";
|
|
||||||
next_state: "D";
|
|
||||||
}
|
|
||||||
pin(D) { direction: input; }
|
|
||||||
pin(C) { direction: input; clock: true; }
|
|
||||||
pin(Q) { direction: output; function: "IQ"; }
|
|
||||||
}
|
|
||||||
cell(DFF_P) {
|
|
||||||
ff(IQ, IQN) {
|
|
||||||
clocked_on: "C";
|
|
||||||
next_state: "D";
|
|
||||||
}
|
|
||||||
pin(D) { direction: input; }
|
|
||||||
pin(C) { direction: input; clock: true; }
|
|
||||||
pin(Q) { direction: output; function: "IQ"; }
|
|
||||||
}
|
|
||||||
cell(DFF_NN0) {
|
|
||||||
ff(IQ, IQN) {
|
|
||||||
clocked_on: "!C";
|
|
||||||
next_state: "D";
|
|
||||||
clear: "!R";
|
|
||||||
}
|
|
||||||
pin(D) { direction: input; }
|
|
||||||
pin(R) { direction: input; }
|
|
||||||
pin(C) { direction: input; clock: true; }
|
|
||||||
pin(Q) { direction: output; function: "IQ"; }
|
|
||||||
}
|
|
||||||
cell(DFF_NN1) {
|
|
||||||
ff(IQ, IQN) {
|
|
||||||
clocked_on: "!C";
|
|
||||||
next_state: "D";
|
|
||||||
preset: "!R";
|
|
||||||
}
|
|
||||||
pin(D) { direction: input; }
|
|
||||||
pin(R) { direction: input; }
|
|
||||||
pin(C) { direction: input; clock: true; }
|
|
||||||
pin(Q) { direction: output; function: "IQ"; }
|
|
||||||
}
|
|
||||||
cell(DFF_NP0) {
|
|
||||||
ff(IQ, IQN) {
|
|
||||||
clocked_on: "!C";
|
|
||||||
next_state: "D";
|
|
||||||
clear: "R";
|
|
||||||
}
|
|
||||||
pin(D) { direction: input; }
|
|
||||||
pin(R) { direction: input; }
|
|
||||||
pin(C) { direction: input; clock: true; }
|
|
||||||
pin(Q) { direction: output; function: "IQ"; }
|
|
||||||
}
|
|
||||||
cell(DFF_NP1) {
|
|
||||||
ff(IQ, IQN) {
|
|
||||||
clocked_on: "!C";
|
|
||||||
next_state: "D";
|
|
||||||
preset: "R";
|
|
||||||
}
|
|
||||||
pin(D) { direction: input; }
|
|
||||||
pin(R) { direction: input; }
|
|
||||||
pin(C) { direction: input; clock: true; }
|
|
||||||
pin(Q) { direction: output; function: "IQ"; }
|
|
||||||
}
|
|
||||||
cell(DFF_PN0) {
|
|
||||||
ff(IQ, IQN) {
|
|
||||||
clocked_on: "C";
|
|
||||||
next_state: "D";
|
|
||||||
clear: "!R";
|
|
||||||
}
|
|
||||||
pin(D) { direction: input; }
|
|
||||||
pin(R) { direction: input; }
|
|
||||||
pin(C) { direction: input; clock: true; }
|
|
||||||
pin(Q) { direction: output; function: "IQ"; }
|
|
||||||
}
|
|
||||||
cell(DFF_PN1) {
|
|
||||||
ff(IQ, IQN) {
|
|
||||||
clocked_on: "C";
|
|
||||||
next_state: "D";
|
|
||||||
preset: "!R";
|
|
||||||
}
|
|
||||||
pin(D) { direction: input; }
|
|
||||||
pin(R) { direction: input; }
|
|
||||||
pin(C) { direction: input; clock: true; }
|
|
||||||
pin(Q) { direction: output; function: "IQ"; }
|
|
||||||
}
|
|
||||||
cell(DFF_PP0) {
|
|
||||||
ff(IQ, IQN) {
|
|
||||||
clocked_on: "C";
|
|
||||||
next_state: "D";
|
|
||||||
clear: "R";
|
|
||||||
}
|
|
||||||
pin(D) { direction: input; }
|
|
||||||
pin(R) { direction: input; }
|
|
||||||
pin(C) { direction: input; clock: true; }
|
|
||||||
pin(Q) { direction: output; function: "IQ"; }
|
|
||||||
}
|
|
||||||
cell(DFF_PP1) {
|
|
||||||
ff(IQ, IQN) {
|
|
||||||
clocked_on: "C";
|
|
||||||
next_state: "D";
|
|
||||||
preset: "R";
|
|
||||||
}
|
|
||||||
pin(D) { direction: input; }
|
|
||||||
pin(R) { direction: input; }
|
|
||||||
pin(C) { direction: input; clock: true; }
|
|
||||||
pin(Q) { direction: output; function: "IQ"; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
128
techlibs/common/opensta.cc
Normal file
128
techlibs/common/opensta.cc
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
#include "kernel/rtlil.h"
|
||||||
|
#include "kernel/log.h"
|
||||||
|
#include "techlibs/common/opensta.h"
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#if !defined(YOSYS_DISABLE_SPAWN)
|
||||||
|
struct OpenstaPass : public Pass
|
||||||
|
{
|
||||||
|
OpenstaPass() : Pass("opensta", "run OpenSTA") { }
|
||||||
|
|
||||||
|
void help() override
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" opensta [options]\n");
|
||||||
|
log("\n");
|
||||||
|
log("Expand SDC file with OpenSTA.\n");
|
||||||
|
log("Internal command like abc. Requires a well-formed design.\n");
|
||||||
|
log("For general SDC expansion with OpenSTA, use the sdc_expand command.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -exe <command>\n");
|
||||||
|
log(" use <command> to run OpenSTA instead of \"%s\"\n", default_opensta_cmd);
|
||||||
|
log("\n");
|
||||||
|
log(" -sdc-in <filename>\n");
|
||||||
|
log(" expand SDC input file <filename>\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -sdc-out <filename>\n");
|
||||||
|
log(" expand SDC file to output file <filename>\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -nocleanup\n");
|
||||||
|
log("\n");
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
|
{
|
||||||
|
string run_from, run_to;
|
||||||
|
string opensta_exe = design->scratchpad_get_string("opensta.exe", default_opensta_cmd);
|
||||||
|
string sdc_filename, sdc_expanded_filename;
|
||||||
|
string tempdir_name, script_filename;
|
||||||
|
string verilog_filename, liberty_filename;
|
||||||
|
bool cleanup = design->scratchpad_get_bool("opensta.cleanup", true);
|
||||||
|
|
||||||
|
log_header(design, "Executing OPENSTA pass.\n");
|
||||||
|
log_experimental("opensta");
|
||||||
|
log_push();
|
||||||
|
|
||||||
|
size_t argidx;
|
||||||
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
|
{
|
||||||
|
if (args[argidx] == "-exe" && argidx+1 < args.size()) {
|
||||||
|
opensta_exe = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-sdc-in" && argidx+1 < args.size()) {
|
||||||
|
sdc_filename = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-sdc-out" && argidx+1 < args.size()) {
|
||||||
|
sdc_expanded_filename = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-verilog" && argidx+1 < args.size()) {
|
||||||
|
verilog_filename = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-liberty" && argidx+1 < args.size()) {
|
||||||
|
liberty_filename = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-nocleanup") {
|
||||||
|
cleanup = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
extra_args(args, argidx, design);
|
||||||
|
if (!design->full_selection())
|
||||||
|
log_cmd_error("This command only operates on fully selected designs!\n");
|
||||||
|
|
||||||
|
if (cleanup)
|
||||||
|
tempdir_name = get_base_tmpdir() + "/";
|
||||||
|
else
|
||||||
|
tempdir_name = "_tmp_";
|
||||||
|
tempdir_name += proc_program_prefix() + "yosys-opensta-XXXXXX";
|
||||||
|
tempdir_name = make_temp_dir(tempdir_name);
|
||||||
|
script_filename = tempdir_name + "/opensta.tcl";
|
||||||
|
// script_filename
|
||||||
|
|
||||||
|
auto top_mod = design->top_module();
|
||||||
|
if (!top_mod)
|
||||||
|
log_error("Can't find top module in current design!\n");
|
||||||
|
|
||||||
|
std::ofstream f_script;
|
||||||
|
f_script.open(script_filename);
|
||||||
|
|
||||||
|
f_script << "read_verilog " << verilog_filename << "\n";
|
||||||
|
f_script << "read_lib " << liberty_filename << "\n";
|
||||||
|
f_script << "link_design " << RTLIL::unescape_id(top_mod->name) << "\n";
|
||||||
|
f_script << "read_sdc " << sdc_filename << "\n";
|
||||||
|
f_script << "write_sdc " << sdc_expanded_filename << "\n";
|
||||||
|
f_script.close();
|
||||||
|
std::string command = opensta_exe + " -exit " + script_filename;
|
||||||
|
auto process_line = [](const std::string &line) {
|
||||||
|
if (line.find("Creating black box") != std::string::npos)
|
||||||
|
return;
|
||||||
|
if (line.find("does not match net size") != std::string::npos)
|
||||||
|
return;
|
||||||
|
log("OpenSTA: %s", line.c_str());
|
||||||
|
};
|
||||||
|
int ret = run_command(command, process_line);
|
||||||
|
if (ret)
|
||||||
|
log_error("OpenSTA returned %d (error)\n", ret);
|
||||||
|
else
|
||||||
|
log("sdc_expanded_filename: %s\n", sdc_expanded_filename.c_str());
|
||||||
|
|
||||||
|
if (cleanup) {
|
||||||
|
log("Removing temp directory.\n");
|
||||||
|
remove_directory(tempdir_name);
|
||||||
|
}
|
||||||
|
log_pop();
|
||||||
|
}
|
||||||
|
} OpenstaPass;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
||||||
8
techlibs/common/opensta.h
Normal file
8
techlibs/common/opensta.h
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include "kernel/yosys_common.h"
|
||||||
|
#ifndef OPENSTA_H
|
||||||
|
YOSYS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
static const char* default_opensta_cmd = "sta";
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_END
|
||||||
|
#endif /* OPENSTA_H */
|
||||||
156
techlibs/common/sdc_expand.cc
Normal file
156
techlibs/common/sdc_expand.cc
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
#include "kernel/rtlil.h"
|
||||||
|
#include "kernel/log.h"
|
||||||
|
#include "techlibs/common/opensta.h"
|
||||||
|
|
||||||
|
USING_YOSYS_NAMESPACE
|
||||||
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct SdcExpandPass : public ScriptPass
|
||||||
|
{
|
||||||
|
SdcExpandPass() : ScriptPass("sdc_expand", "expand SDC design with opensta") { }
|
||||||
|
|
||||||
|
void help() override
|
||||||
|
{
|
||||||
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
|
log("\n");
|
||||||
|
log(" sdc_expand [options]\n");
|
||||||
|
log("\n");
|
||||||
|
log("Expand SDC file with opensta based on arbitrary current design.\n");
|
||||||
|
log("Changes the design but only temporarily.\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -exe <command>\n");
|
||||||
|
log(" use <command> to run OpenSTA instead of \"%s\"\n", default_opensta_cmd);
|
||||||
|
log("\n");
|
||||||
|
log(" -sdc-in <filename>\n");
|
||||||
|
log(" expand SDC file <filename>\n");
|
||||||
|
log("\n");
|
||||||
|
log(" -nocleanup\n");
|
||||||
|
log("\n");
|
||||||
|
log("\n");
|
||||||
|
log("The following commands are executed by this synthesis command:\n");
|
||||||
|
help_script();
|
||||||
|
log("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
string opensta_exe, sdc_filename, sdc_expanded_filename;
|
||||||
|
bool cleanup;
|
||||||
|
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||||
|
{
|
||||||
|
log_header(design, "Executing SDC_EXPAND pass.\n");
|
||||||
|
log_experimental("sdc_expand");
|
||||||
|
string run_from, run_to;
|
||||||
|
opensta_exe = "";
|
||||||
|
cleanup = design->scratchpad_get_bool("sdc_expand.cleanup", true);
|
||||||
|
|
||||||
|
size_t argidx;
|
||||||
|
for (argidx = 1; argidx < args.size(); argidx++)
|
||||||
|
{
|
||||||
|
if (args[argidx] == "-exe" && argidx+1 < args.size()) {
|
||||||
|
opensta_exe = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-sdc-in" && argidx+1 < args.size()) {
|
||||||
|
sdc_filename = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-sdc-out" && argidx+1 < args.size()) {
|
||||||
|
sdc_expanded_filename = args[++argidx];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-nocleanup") {
|
||||||
|
cleanup = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-run" && argidx+1 < args.size()) {
|
||||||
|
size_t pos = args[argidx+1].find(':');
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
run_from = args[++argidx];
|
||||||
|
run_to = args[argidx];
|
||||||
|
} else {
|
||||||
|
run_from = args[++argidx].substr(0, pos);
|
||||||
|
run_to = args[argidx].substr(pos+1);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdc_filename.empty())
|
||||||
|
log_cmd_error("Missing -sdc-in argument\n");
|
||||||
|
if (sdc_expanded_filename.empty())
|
||||||
|
log_cmd_error("Missing -sdc-out argument\n");
|
||||||
|
|
||||||
|
extra_args(args, argidx, design);
|
||||||
|
|
||||||
|
if (!design->full_selection())
|
||||||
|
log_cmd_error("This command only operates on fully selected designs!\n");
|
||||||
|
|
||||||
|
log_header(design, "Executing OPENSTA pass.\n");
|
||||||
|
log_push();
|
||||||
|
|
||||||
|
run_script(design, run_from, run_to);
|
||||||
|
|
||||||
|
log_pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void script() override
|
||||||
|
{
|
||||||
|
std::string tempdir_name;
|
||||||
|
|
||||||
|
run("design -save pre_expand");
|
||||||
|
run("proc");
|
||||||
|
run("memory");
|
||||||
|
// run("dfflegalize -cell $dff");
|
||||||
|
|
||||||
|
if (help_mode) {
|
||||||
|
tempdir_name = "<tmp_dir>";
|
||||||
|
} else {
|
||||||
|
if (cleanup)
|
||||||
|
tempdir_name = get_base_tmpdir() + "/";
|
||||||
|
else
|
||||||
|
tempdir_name = "_tmp_";
|
||||||
|
tempdir_name += proc_program_prefix() + "yosys-sdc_expand-XXXXXX";
|
||||||
|
tempdir_name = make_temp_dir(tempdir_name);
|
||||||
|
}
|
||||||
|
std::string verilog_path = tempdir_name + "/elaborated.v";
|
||||||
|
|
||||||
|
std::string write_verilog_cmd = "write_verilog -norename -noexpr -attr2comment -defparam ";
|
||||||
|
run(write_verilog_cmd + verilog_path);
|
||||||
|
run("read_verilog -setattr whitebox -defer -DSIMLIB_NOCHECKS +/simlib.v");
|
||||||
|
run("proc");
|
||||||
|
run("hierarchy -auto-top");
|
||||||
|
run("chtype -publish_icells");
|
||||||
|
|
||||||
|
std::string liberty_path = tempdir_name + "/yosys.lib";
|
||||||
|
run("icell_liberty " + liberty_path);
|
||||||
|
|
||||||
|
std::string opensta_pass_call = "opensta ";
|
||||||
|
if (opensta_exe.length()) {
|
||||||
|
opensta_pass_call += "-exe ";
|
||||||
|
opensta_pass_call += help_mode ? "<exe>" : opensta_exe;
|
||||||
|
}
|
||||||
|
opensta_pass_call += " -sdc-in ";
|
||||||
|
opensta_pass_call += help_mode ? "<sdc-in>" : sdc_filename;
|
||||||
|
opensta_pass_call += " -sdc-out ";
|
||||||
|
opensta_pass_call += help_mode ? "<sdc-out>" : sdc_expanded_filename;
|
||||||
|
opensta_pass_call += " -verilog ";
|
||||||
|
opensta_pass_call += help_mode ? "<verilog>" : verilog_path;
|
||||||
|
opensta_pass_call += " -liberty ";
|
||||||
|
opensta_pass_call += help_mode ? "<tmp_dir>/yosys.lib" : liberty_path;
|
||||||
|
if (!cleanup)
|
||||||
|
opensta_pass_call += " -nocleanup";
|
||||||
|
run(opensta_pass_call);
|
||||||
|
|
||||||
|
if (!help_mode) {
|
||||||
|
if (cleanup) {
|
||||||
|
log("Removing temp directory.\n");
|
||||||
|
remove_directory(tempdir_name);
|
||||||
|
} else {
|
||||||
|
log("Keeping temp directory %s\n", tempdir_name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run("design -load pre_expand");
|
||||||
|
}
|
||||||
|
} SdcExpandPass;
|
||||||
|
|
||||||
|
PRIVATE_NAMESPACE_END
|
||||||
|
|
@ -117,7 +117,7 @@ static void run_ice40_opts(Module *module)
|
||||||
|
|
||||||
if (GetSize(replacement_output)) {
|
if (GetSize(replacement_output)) {
|
||||||
optimized_co.insert(sigmap(cell->getPort(ID::CO)[0]));
|
optimized_co.insert(sigmap(cell->getPort(ID::CO)[0]));
|
||||||
auto it = cell->attributes.find(ID(SB_LUT4.name));
|
auto it = cell->attributes.find(IdString{"\\SB_LUT4.name"});
|
||||||
if (it != cell->attributes.end()) {
|
if (it != cell->attributes.end()) {
|
||||||
module->rename(cell, it->second.decode_string());
|
module->rename(cell, it->second.decode_string());
|
||||||
decltype(Cell::attributes) new_attr;
|
decltype(Cell::attributes) new_attr;
|
||||||
|
|
@ -126,7 +126,7 @@ static void run_ice40_opts(Module *module)
|
||||||
new_attr[a.first.c_str() + strlen("\\SB_LUT4.")] = a.second;
|
new_attr[a.first.c_str() + strlen("\\SB_LUT4.")] = a.second;
|
||||||
else if (a.first == ID::src)
|
else if (a.first == ID::src)
|
||||||
new_attr.insert(std::make_pair(a.first, a.second));
|
new_attr.insert(std::make_pair(a.first, a.second));
|
||||||
else if (a.first.in(ID(SB_LUT4.name), ID::keep, ID::module_not_derived))
|
else if (a.first.in(IdString{"\\SB_LUT4.name"}, ID::keep, ID::module_not_derived))
|
||||||
continue;
|
continue;
|
||||||
else if (a.first.begins_with("\\SB_CARRY.\\"))
|
else if (a.first.begins_with("\\SB_CARRY.\\"))
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm)
|
||||||
cell->attributes[stringf("\\SB_CARRY.%s", a.first)] = a.second;
|
cell->attributes[stringf("\\SB_CARRY.%s", a.first)] = a.second;
|
||||||
for (const auto &a : st.lut->attributes)
|
for (const auto &a : st.lut->attributes)
|
||||||
cell->attributes[stringf("\\SB_LUT4.%s", a.first)] = a.second;
|
cell->attributes[stringf("\\SB_LUT4.%s", a.first)] = a.second;
|
||||||
cell->attributes[ID(SB_LUT4.name)] = Const(st.lut->name.str());
|
cell->attributes[IdString{"\\SB_LUT4.name"}] = Const(st.lut->name.str());
|
||||||
if (st.carry->get_bool_attribute(ID::keep) || st.lut->get_bool_attribute(ID::keep))
|
if (st.carry->get_bool_attribute(ID::keep) || st.lut->get_bool_attribute(ID::keep))
|
||||||
cell->attributes[ID::keep] = true;
|
cell->attributes[ID::keep] = true;
|
||||||
|
|
||||||
|
|
@ -122,7 +122,7 @@ struct Ice40WrapCarryPass : public Pass {
|
||||||
carry->setPort(ID::CI, cell->getPort(ID::CI));
|
carry->setPort(ID::CI, cell->getPort(ID::CI));
|
||||||
carry->setPort(ID::CO, cell->getPort(ID::CO));
|
carry->setPort(ID::CO, cell->getPort(ID::CO));
|
||||||
module->swap_names(carry, cell);
|
module->swap_names(carry, cell);
|
||||||
auto lut_name = cell->attributes.at(ID(SB_LUT4.name), Const(NEW_ID.str())).decode_string();
|
auto lut_name = cell->attributes.at(IdString{"\\SB_LUT4.name"}, Const(NEW_ID.str())).decode_string();
|
||||||
auto lut = module->addCell(lut_name, ID($lut));
|
auto lut = module->addCell(lut_name, ID($lut));
|
||||||
lut->setParam(ID::WIDTH, 4);
|
lut->setParam(ID::WIDTH, 4);
|
||||||
lut->setParam(ID::LUT, cell->getParam(ID::LUT));
|
lut->setParam(ID::LUT, cell->getParam(ID::LUT));
|
||||||
|
|
@ -138,7 +138,7 @@ struct Ice40WrapCarryPass : public Pass {
|
||||||
lut->attributes[a.first.c_str() + strlen("\\SB_LUT4.")] = a.second;
|
lut->attributes[a.first.c_str() + strlen("\\SB_LUT4.")] = a.second;
|
||||||
else if (a.first == ID::src)
|
else if (a.first == ID::src)
|
||||||
src = a.second;
|
src = a.second;
|
||||||
else if (a.first.in(ID(SB_LUT4.name), ID::keep, ID::module_not_derived))
|
else if (a.first.in(IdString{"\\SB_LUT4.name"}, ID::keep, ID::module_not_derived))
|
||||||
continue;
|
continue;
|
||||||
else
|
else
|
||||||
log_abort();
|
log_abort();
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,7 @@ endmatch
|
||||||
code
|
code
|
||||||
if (postAdd)
|
if (postAdd)
|
||||||
{
|
{
|
||||||
if (postAdd->type.in(ID($sub)) && postAddAB == \A) {
|
if (postAdd->type.in($sub) && postAddAB == \A) {
|
||||||
// if $sub, the multiplier output must match to $sub.B, otherwise no match
|
// if $sub, the multiplier output must match to $sub.B, otherwise no match
|
||||||
} else {
|
} else {
|
||||||
u_postAddAB = postAddAB;
|
u_postAddAB = postAddAB;
|
||||||
|
|
|
||||||
|
|
@ -115,9 +115,9 @@ finally
|
||||||
Wire *cascade = module->addWire(NEW_ID, 48);
|
Wire *cascade = module->addWire(NEW_ID, 48);
|
||||||
|
|
||||||
// zero port C and move wire to cascade
|
// zero port C and move wire to cascade
|
||||||
dsp_pcin->setPort(ID(C), Const(0, 48));
|
dsp_pcin->setPort(\C, Const(0, 48));
|
||||||
dsp_pcin->setPort(ID(CDIN), cascade);
|
dsp_pcin->setPort(\CDIN, cascade);
|
||||||
dsp->setPort(ID(CDOUT), cascade);
|
dsp->setPort(\CDOUT, cascade);
|
||||||
|
|
||||||
// Configure wire to cascade the dsps
|
// Configure wire to cascade the dsps
|
||||||
add_siguser(cascade, dsp_pcin);
|
add_siguser(cascade, dsp_pcin);
|
||||||
|
|
|
||||||
|
|
@ -90,9 +90,9 @@ finally
|
||||||
if (i % MAX_DSP_CASCADE > 0) {
|
if (i % MAX_DSP_CASCADE > 0) {
|
||||||
if (P >= 0) {
|
if (P >= 0) {
|
||||||
Wire *cascade = module->addWire(NEW_ID, 48);
|
Wire *cascade = module->addWire(NEW_ID, 48);
|
||||||
dsp_pcin->setPort(ID(C), Const(0, 48));
|
dsp_pcin->setPort(\C, Const(0, 48));
|
||||||
dsp_pcin->setPort(ID(PCIN), cascade);
|
dsp_pcin->setPort(\PCIN, cascade);
|
||||||
dsp->setPort(ID(PCOUT), cascade);
|
dsp->setPort(\PCOUT, cascade);
|
||||||
add_siguser(cascade, dsp_pcin);
|
add_siguser(cascade, dsp_pcin);
|
||||||
add_siguser(cascade, dsp);
|
add_siguser(cascade, dsp);
|
||||||
|
|
||||||
|
|
@ -118,15 +118,15 @@ finally
|
||||||
}
|
}
|
||||||
if (AREG >= 0) {
|
if (AREG >= 0) {
|
||||||
Wire *cascade = module->addWire(NEW_ID, 30);
|
Wire *cascade = module->addWire(NEW_ID, 30);
|
||||||
dsp_pcin->setPort(ID(A), Const(0, 30));
|
dsp_pcin->setPort(\A, Const(0, 30));
|
||||||
dsp_pcin->setPort(ID(ACIN), cascade);
|
dsp_pcin->setPort(\ACIN, cascade);
|
||||||
dsp->setPort(ID(ACOUT), cascade);
|
dsp->setPort(\ACOUT, cascade);
|
||||||
add_siguser(cascade, dsp_pcin);
|
add_siguser(cascade, dsp_pcin);
|
||||||
add_siguser(cascade, dsp);
|
add_siguser(cascade, dsp);
|
||||||
|
|
||||||
if (dsp->type.in(\DSP48E1))
|
if (dsp->type.in(\DSP48E1))
|
||||||
dsp->setParam(ID(ACASCREG), AREG);
|
dsp->setParam(\ACASCREG, AREG);
|
||||||
dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE"));
|
dsp_pcin->setParam(\A_INPUT, Const("CASCADE"));
|
||||||
|
|
||||||
log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
||||||
}
|
}
|
||||||
|
|
@ -138,18 +138,18 @@ finally
|
||||||
// BCOUT from an adjacent DSP48A1 slice. The tools then
|
// BCOUT from an adjacent DSP48A1 slice. The tools then
|
||||||
// translate BCOUT cascading to the dedicated BCIN input
|
// translate BCOUT cascading to the dedicated BCIN input
|
||||||
// and set the B_INPUT attribute for implementation."
|
// and set the B_INPUT attribute for implementation."
|
||||||
dsp_pcin->setPort(ID(B), cascade);
|
dsp_pcin->setPort(\B, cascade);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dsp_pcin->setPort(ID(B), Const(0, 18));
|
dsp_pcin->setPort(\B, Const(0, 18));
|
||||||
dsp_pcin->setPort(ID(BCIN), cascade);
|
dsp_pcin->setPort(\BCIN, cascade);
|
||||||
}
|
}
|
||||||
dsp->setPort(ID(BCOUT), cascade);
|
dsp->setPort(\BCOUT, cascade);
|
||||||
add_siguser(cascade, dsp_pcin);
|
add_siguser(cascade, dsp_pcin);
|
||||||
add_siguser(cascade, dsp);
|
add_siguser(cascade, dsp);
|
||||||
|
|
||||||
if (dsp->type.in(\DSP48E1)) {
|
if (dsp->type.in(\DSP48E1)) {
|
||||||
dsp->setParam(ID(BCASCREG), BREG);
|
dsp->setParam(\BCASCREG, BREG);
|
||||||
// According to UG389 p13 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
|
// According to UG389 p13 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf]
|
||||||
// "The attribute is only used by place and route tools and
|
// "The attribute is only used by place and route tools and
|
||||||
// is not necessary for the users to set for synthesis. The
|
// is not necessary for the users to set for synthesis. The
|
||||||
|
|
@ -158,7 +158,7 @@ finally
|
||||||
// BCOUT of another DSP48A1 slice, then the tools automatically
|
// BCOUT of another DSP48A1 slice, then the tools automatically
|
||||||
// set the attribute to 'CASCADE', otherwise it is set to
|
// set the attribute to 'CASCADE', otherwise it is set to
|
||||||
// 'DIRECT'".
|
// 'DIRECT'".
|
||||||
dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE"));
|
dsp_pcin->setParam(\B_INPUT, Const("CASCADE"));
|
||||||
}
|
}
|
||||||
|
|
||||||
log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import os
|
|
||||||
from pyosys import libyosys as ys
|
|
||||||
|
|
||||||
__dir__ = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
add_sub = os.path.join(__dir__, "..", "common", "add_sub.v")
|
|
||||||
|
|
||||||
base = ys.Design()
|
|
||||||
ys.run_pass(f"read_verilog {add_sub}", base)
|
|
||||||
ys.run_pass("hierarchy -top top", base)
|
|
||||||
ys.run_pass("proc", base)
|
|
||||||
ys.run_pass("equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5", base)
|
|
||||||
|
|
||||||
postopt = ys.Design()
|
|
||||||
ys.run_pass("design -load postopt", postopt)
|
|
||||||
ys.run_pass("cd top", postopt)
|
|
||||||
ys.run_pass("select -assert-min 25 t:LUT4", postopt)
|
|
||||||
ys.run_pass("select -assert-max 26 t:LUT4", postopt)
|
|
||||||
ys.run_pass("select -assert-count 10 t:PFUMX", postopt)
|
|
||||||
ys.run_pass("select -assert-count 6 t:L6MUX21", postopt)
|
|
||||||
ys.run_pass("select -assert-none t:LUT4 t:PFUMX t:L6MUX21 %% t:* %D", postopt)
|
|
||||||
|
|
@ -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)
|
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
|
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 4 t:dffepc
|
||||||
select -assert-count 1 t:logic_0
|
select -assert-count 1 t:logic_0
|
||||||
select -assert-count 1 t:logic_1
|
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
|
design -load postopt
|
||||||
cd cascade
|
cd cascade
|
||||||
select -assert-count 2 t:DSP48E1
|
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
|
# Very crude method of checking that DSP48E1.PCOUT -> DSP48E1.PCIN
|
||||||
# (see above for explanation)
|
# (see above for explanation)
|
||||||
select -assert-count 1 t:DSP48E1 %co:+[PCOUT] t:DSP48E1 %d %co:+[PCIN] w:* %d t:DSP48E1 %i
|
select -assert-count 1 t:DSP48E1 %co:+[PCOUT] t:DSP48E1 %d %co:+[PCIN] w:* %d t:DSP48E1 %i
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,20 @@ def write_vcd(filename: Path, signals: SignalStepMap, timescale='1 ns', date='to
|
||||||
if change_time == time:
|
if change_time == time:
|
||||||
f.write(f"{value} {signal_name}\n")
|
f.write(f"{value} {signal_name}\n")
|
||||||
|
|
||||||
def simulate_rosette(rkt_file_path: Path, vcd_path: Path, num_steps: int, rnd: Random):
|
|
||||||
|
def simulate_rosette(
|
||||||
|
rkt_file_path: Path,
|
||||||
|
vcd_path: Path,
|
||||||
|
num_steps: int,
|
||||||
|
rnd: Random,
|
||||||
|
use_assoc_list_helpers: bool = False,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
- use_assoc_list_helpers: If True, will use the association list helpers
|
||||||
|
in the Racket file. The file should have been generated with the
|
||||||
|
-assoc-list-helpers flag in the yosys command.
|
||||||
|
"""
|
||||||
signals: dict[str, list[str]] = {}
|
signals: dict[str, list[str]] = {}
|
||||||
inputs: SignalWidthMap = {}
|
inputs: SignalWidthMap = {}
|
||||||
outputs: SignalWidthMap = {}
|
outputs: SignalWidthMap = {}
|
||||||
|
|
@ -83,12 +96,32 @@ def simulate_rosette(rkt_file_path: Path, vcd_path: Path, num_steps: int, rnd: R
|
||||||
for step in range(num_steps):
|
for step in range(num_steps):
|
||||||
this_step = f"step_{step}"
|
this_step = f"step_{step}"
|
||||||
value_list: list[str] = []
|
value_list: list[str] = []
|
||||||
for signal, width in inputs.items():
|
if use_assoc_list_helpers:
|
||||||
value = signals[signal][step]
|
# Generate inputs as a list of cons pairs making up the
|
||||||
value_list.append(f"(bv #b{value} {width})")
|
# association list.
|
||||||
gold_Inputs = f"(gold_Inputs {' '.join(value_list)})"
|
for signal, width in inputs.items():
|
||||||
|
value = signals[signal][step]
|
||||||
|
value_list.append(f'(cons "{signal}" (bv #b{value} {width}))')
|
||||||
|
else:
|
||||||
|
# Otherwise, we generate the inputs as a list of bitvectors.
|
||||||
|
for signal, width in inputs.items():
|
||||||
|
value = signals[signal][step]
|
||||||
|
value_list.append(f"(bv #b{value} {width})")
|
||||||
|
gold_Inputs = (
|
||||||
|
f"(gold_inputs_helper (list {' '.join(value_list)}))"
|
||||||
|
if use_assoc_list_helpers
|
||||||
|
else f"(gold_Inputs {' '.join(value_list)})"
|
||||||
|
)
|
||||||
gold_State = f"(cdr step_{step-1})" if step else "gold_initial"
|
gold_State = f"(cdr step_{step-1})" if step else "gold_initial"
|
||||||
test_rkt_file.write(f"(define {this_step} (gold {gold_Inputs} {gold_State})) (car {this_step})\n")
|
get_value_expr = (
|
||||||
|
f"(gold_outputs_helper (car {this_step}))"
|
||||||
|
if use_assoc_list_helpers
|
||||||
|
else f"(car {this_step})"
|
||||||
|
)
|
||||||
|
test_rkt_file.write(
|
||||||
|
f"(define {this_step} (gold {gold_Inputs} {gold_State})) {get_value_expr}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
cmd = ["racket", test_rkt_file_path]
|
cmd = ["racket", test_rkt_file_path]
|
||||||
status = subprocess.run(cmd, capture_output=True)
|
status = subprocess.run(cmd, capture_output=True)
|
||||||
|
|
@ -98,9 +131,23 @@ def simulate_rosette(rkt_file_path: Path, vcd_path: Path, num_steps: int, rnd: R
|
||||||
signals[signal] = []
|
signals[signal] = []
|
||||||
|
|
||||||
for line in status.stdout.decode().splitlines():
|
for line in status.stdout.decode().splitlines():
|
||||||
m = re.match(r'\(gold_Outputs( \(bv \S+ \d+\))+\)', line)
|
m = (
|
||||||
|
re.match(r"\(list( \(cons \"\S+\" \(bv \S+ \d+\)\))+\)", line)
|
||||||
|
if use_assoc_list_helpers
|
||||||
|
else re.match(r"\(gold_Outputs( \(bv \S+ \d+\))+\)", line)
|
||||||
|
)
|
||||||
assert m, f"Incomplete output definition {line!r}"
|
assert m, f"Incomplete output definition {line!r}"
|
||||||
for output, (value, width) in zip(outputs.keys(), re.findall(r'\(bv (\S+) (\d+)\)', line)):
|
outputs_values_and_widths = (
|
||||||
|
{
|
||||||
|
output: re.findall(
|
||||||
|
r"\(cons \"" + output + r"\" \(bv (\S+) (\d+)\)\)", line
|
||||||
|
)[0]
|
||||||
|
for output in outputs.keys()
|
||||||
|
}.items()
|
||||||
|
if use_assoc_list_helpers
|
||||||
|
else zip(outputs.keys(), re.findall(r"\(bv (\S+) (\d+)\)", line))
|
||||||
|
)
|
||||||
|
for output, (value, width) in outputs_values_and_widths:
|
||||||
assert isinstance(value, str), f"Bad value {value!r}"
|
assert isinstance(value, str), f"Bad value {value!r}"
|
||||||
assert value.startswith(('#b', '#x')), f"Non-binary value {value!r}"
|
assert value.startswith(('#b', '#x')), f"Non-binary value {value!r}"
|
||||||
assert int(width) == outputs[output], f"Width mismatch for output {output!r} (got {width}, expected {outputs[output]})"
|
assert int(width) == outputs[output], f"Width mismatch for output {output!r} (got {width}, expected {outputs[output]})"
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
pytest -v -m "not smt and not rkt" "$@"
|
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
|
||||||
|
pytest -v -m "not smt and not rkt" "$SCRIPT_DIR" "$@"
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,8 @@ def test_smt(cell, parameters, tmp_path, num_steps, rnd):
|
||||||
yosys_sim(rtlil_file, vcd_functional_file, vcd_yosys_sim_file, getattr(cell, 'sim_preprocessing', ''))
|
yosys_sim(rtlil_file, vcd_functional_file, vcd_yosys_sim_file, getattr(cell, 'sim_preprocessing', ''))
|
||||||
|
|
||||||
@pytest.mark.rkt
|
@pytest.mark.rkt
|
||||||
def test_rkt(cell, parameters, tmp_path, num_steps, rnd):
|
@pytest.mark.parametrize("use_assoc_list_helpers", [True, False])
|
||||||
|
def test_rkt(cell, parameters, tmp_path, num_steps, rnd, use_assoc_list_helpers):
|
||||||
import rkt_vcd
|
import rkt_vcd
|
||||||
|
|
||||||
rtlil_file = tmp_path / 'rtlil.il'
|
rtlil_file = tmp_path / 'rtlil.il'
|
||||||
|
|
@ -83,8 +84,9 @@ def test_rkt(cell, parameters, tmp_path, num_steps, rnd):
|
||||||
vcd_yosys_sim_file = tmp_path / 'yosys.vcd'
|
vcd_yosys_sim_file = tmp_path / 'yosys.vcd'
|
||||||
|
|
||||||
cell.write_rtlil_file(rtlil_file, parameters)
|
cell.write_rtlil_file(rtlil_file, parameters)
|
||||||
yosys(f"read_rtlil {quote(rtlil_file)} ; clk2fflogic ; write_functional_rosette -provides {quote(rkt_file)}")
|
use_assoc_helpers_flag = '-assoc-list-helpers' if use_assoc_list_helpers else ''
|
||||||
rkt_vcd.simulate_rosette(rkt_file, vcd_functional_file, num_steps, rnd(cell.name + "-rkt"))
|
yosys(f"read_rtlil {quote(rtlil_file)} ; clk2fflogic ; write_functional_rosette -provides {use_assoc_helpers_flag} {quote(rkt_file)}")
|
||||||
|
rkt_vcd.simulate_rosette(rkt_file, vcd_functional_file, num_steps, rnd(cell.name + "-rkt"), use_assoc_list_helpers=use_assoc_list_helpers)
|
||||||
yosys_sim(rtlil_file, vcd_functional_file, vcd_yosys_sim_file, getattr(cell, 'sim_preprocessing', ''))
|
yosys_sim(rtlil_file, vcd_functional_file, vcd_yosys_sim_file, getattr(cell, 'sim_preprocessing', ''))
|
||||||
|
|
||||||
def test_print_graph(tmp_path):
|
def test_print_graph(tmp_path):
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ module dff (D, CLK, Q);
|
||||||
output Q;
|
output Q;
|
||||||
assign Q = IQ; // IQ
|
assign Q = IQ; // IQ
|
||||||
always @(posedge CLK) begin
|
always @(posedge CLK) begin
|
||||||
// "(D)"
|
|
||||||
IQ <= D;
|
IQ <= D;
|
||||||
|
end
|
||||||
|
always @(posedge CLK) begin
|
||||||
IQN <= ~(D);
|
IQN <= ~(D);
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ module imux2 (A, B, S, Y);
|
||||||
endmodule
|
endmodule
|
||||||
module dff (D, CLK, RESET, PRESET, Q, QN);
|
module dff (D, CLK, RESET, PRESET, Q, QN);
|
||||||
reg IQ, IQN;
|
reg IQ, IQN;
|
||||||
|
wire IQ_clear, IQ_preset;
|
||||||
input D;
|
input D;
|
||||||
input CLK;
|
input CLK;
|
||||||
input RESET;
|
input RESET;
|
||||||
|
|
@ -49,25 +50,30 @@ module dff (D, CLK, RESET, PRESET, Q, QN);
|
||||||
assign Q = IQ; // "IQ"
|
assign Q = IQ; // "IQ"
|
||||||
output QN;
|
output QN;
|
||||||
assign QN = IQN; // "IQN"
|
assign QN = IQN; // "IQN"
|
||||||
always @(posedge CLK, posedge RESET, posedge PRESET) begin
|
always @(posedge CLK, posedge IQ_clear, posedge IQ_preset) begin
|
||||||
if ((RESET) && (PRESET)) begin
|
if (IQ_clear) begin
|
||||||
IQ <= 0;
|
IQ <= 0;
|
||||||
IQN <= 0;
|
|
||||||
end
|
end
|
||||||
else if (RESET) begin
|
else if (IQ_preset) begin
|
||||||
IQ <= 0;
|
|
||||||
IQN <= 1;
|
|
||||||
end
|
|
||||||
else if (PRESET) begin
|
|
||||||
IQ <= 1;
|
IQ <= 1;
|
||||||
IQN <= 0;
|
|
||||||
end
|
end
|
||||||
else begin
|
else begin
|
||||||
// "D"
|
|
||||||
IQ <= D;
|
IQ <= D;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
always @(posedge CLK, posedge IQ_clear, posedge IQ_preset) begin
|
||||||
|
if (IQ_preset) begin
|
||||||
|
IQN <= 0;
|
||||||
|
end
|
||||||
|
else if (IQ_clear) begin
|
||||||
|
IQN <= 1;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
IQN <= ~(D);
|
IQN <= ~(D);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
assign IQ_clear = RESET;
|
||||||
|
assign IQ_preset = PRESET;
|
||||||
endmodule
|
endmodule
|
||||||
module latch (D, G, Q, QN);
|
module latch (D, G, Q, QN);
|
||||||
reg IQ, IQN;
|
reg IQ, IQN;
|
||||||
|
|
|
||||||
9
tests/liberty/read_liberty.ys
Normal file
9
tests/liberty/read_liberty.ys
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
read_liberty retention.lib
|
||||||
|
rename retention_cell retention_cell_lib
|
||||||
|
read_verilog retention.lib.verilogsim
|
||||||
|
proc
|
||||||
|
rename retention_cell retention_cell_vlog
|
||||||
|
async2sync
|
||||||
|
equiv_make retention_cell_lib retention_cell_vlog equiv
|
||||||
|
equiv_induct equiv
|
||||||
|
equiv_status -assert equiv
|
||||||
57
tests/liberty/retention.lib
Normal file
57
tests/liberty/retention.lib
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
library (retention) {
|
||||||
|
delay_model : table_lookup;
|
||||||
|
voltage_unit : 1V;
|
||||||
|
current_unit : 1mA;
|
||||||
|
leakage_power_unit : 1nW;
|
||||||
|
time_unit : 1ns;
|
||||||
|
capacitive_load_unit (1, pf);
|
||||||
|
pulling_resistance_unit : 1kohm;
|
||||||
|
input_threshold_pct_rise : 50;
|
||||||
|
input_threshold_pct_fall : 50;
|
||||||
|
output_threshold_pct_rise : 50;
|
||||||
|
output_threshold_pct_fall : 50;
|
||||||
|
slew_lower_threshold_pct_rise : 30;
|
||||||
|
slew_upper_threshold_pct_rise : 70;
|
||||||
|
slew_upper_threshold_pct_fall : 70;
|
||||||
|
slew_lower_threshold_pct_fall : 30;
|
||||||
|
cell ("retention_cell") {
|
||||||
|
ff (Q1,QN1) {
|
||||||
|
clocked_on : "CK";
|
||||||
|
next_state : "(D * !SE + SI * SE)";
|
||||||
|
clear : "(((!B2B) * !Q2) + !RD)";
|
||||||
|
preset : "((!B2B) * Q2)";
|
||||||
|
clear_preset_var1 : "L";
|
||||||
|
clear_preset_var2 : "H";
|
||||||
|
}
|
||||||
|
latch (Q2,QN2) {
|
||||||
|
enable : "B1";
|
||||||
|
data_in : "Q1";
|
||||||
|
}
|
||||||
|
pin (B1) {
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (B2B) {
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (CK) {
|
||||||
|
clock : true;
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (D) {
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (Q) {
|
||||||
|
direction : output;
|
||||||
|
function : "Q1";
|
||||||
|
}
|
||||||
|
pin (RD) {
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (SE) {
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
pin (SI) {
|
||||||
|
direction : input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
tests/liberty/retention.lib.filtered.ok
Normal file
42
tests/liberty/retention.lib.filtered.ok
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
library(retention) {
|
||||||
|
cell("retention_cell") {
|
||||||
|
ff(Q1, QN1) {
|
||||||
|
clocked_on : "CK" ;
|
||||||
|
next_state : "(D * !SE + SI * SE)" ;
|
||||||
|
clear : "(((!B2B) * !Q2) + !RD)" ;
|
||||||
|
preset : "((!B2B) * Q2)" ;
|
||||||
|
clear_preset_var1 : "L" ;
|
||||||
|
clear_preset_var2 : "H" ;
|
||||||
|
}
|
||||||
|
latch(Q2, QN2) {
|
||||||
|
enable : "B1" ;
|
||||||
|
data_in : "Q1" ;
|
||||||
|
}
|
||||||
|
pin(B1) {
|
||||||
|
direction : input ;
|
||||||
|
}
|
||||||
|
pin(B2B) {
|
||||||
|
direction : input ;
|
||||||
|
}
|
||||||
|
pin(CK) {
|
||||||
|
clock : true ;
|
||||||
|
direction : input ;
|
||||||
|
}
|
||||||
|
pin(D) {
|
||||||
|
direction : input ;
|
||||||
|
}
|
||||||
|
pin(Q) {
|
||||||
|
direction : output ;
|
||||||
|
function : "Q1" ;
|
||||||
|
}
|
||||||
|
pin(RD) {
|
||||||
|
direction : input ;
|
||||||
|
}
|
||||||
|
pin(SE) {
|
||||||
|
direction : input ;
|
||||||
|
}
|
||||||
|
pin(SI) {
|
||||||
|
direction : input ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
tests/liberty/retention.lib.verilogsim.ok
Normal file
44
tests/liberty/retention.lib.verilogsim.ok
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
module retention_cell (B1, B2B, CK, D, Q, RD, SE, SI);
|
||||||
|
reg Q1, QN1;
|
||||||
|
wire Q1_clear, Q1_preset;
|
||||||
|
reg Q2, QN2;
|
||||||
|
input B1;
|
||||||
|
input B2B;
|
||||||
|
input CK;
|
||||||
|
input D;
|
||||||
|
output Q;
|
||||||
|
assign Q = Q1; // "Q1"
|
||||||
|
input RD;
|
||||||
|
input SE;
|
||||||
|
input SI;
|
||||||
|
always @(posedge CK, posedge Q1_clear, posedge Q1_preset) begin
|
||||||
|
if (Q1_clear) begin
|
||||||
|
Q1 <= 0;
|
||||||
|
end
|
||||||
|
else if (Q1_preset) begin
|
||||||
|
Q1 <= 1;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
Q1 <= ((D&(~SE))|(SI&SE));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
always @(posedge CK, posedge Q1_clear, posedge Q1_preset) begin
|
||||||
|
if (Q1_clear) begin
|
||||||
|
QN1 <= 1;
|
||||||
|
end
|
||||||
|
else if (Q1_preset) begin
|
||||||
|
QN1 <= 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
QN1 <= ~(((D&(~SE))|(SI&SE)));
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assign Q1_clear = (((~B2B)&(~Q2))|(~RD));
|
||||||
|
assign Q1_preset = ((~B2B)&Q2);
|
||||||
|
always @* begin
|
||||||
|
if (B1) begin
|
||||||
|
Q2 <= Q1;
|
||||||
|
QN2 <= ~(Q1);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -4,8 +4,9 @@ module DFF (D, CK, Q);
|
||||||
input CK;
|
input CK;
|
||||||
output Q;
|
output Q;
|
||||||
always @(posedge CK) begin
|
always @(posedge CK) begin
|
||||||
// "D"
|
|
||||||
IQ <= D;
|
IQ <= D;
|
||||||
|
end
|
||||||
|
always @(posedge CK) begin
|
||||||
IQN <= ~(D);
|
IQN <= ~(D);
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ module dff1 (D, CLK, Q);
|
||||||
output Q;
|
output Q;
|
||||||
assign Q = IQ; // IQ
|
assign Q = IQ; // IQ
|
||||||
always @(posedge CLK) begin
|
always @(posedge CLK) begin
|
||||||
// !D
|
|
||||||
IQ <= (~D);
|
IQ <= (~D);
|
||||||
|
end
|
||||||
|
always @(posedge CLK) begin
|
||||||
IQN <= ~((~D));
|
IQN <= ~((~D));
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
@ -17,8 +18,9 @@ module dff2 (D, CLK, Q);
|
||||||
output Q;
|
output Q;
|
||||||
assign Q = IQ; // "IQ"
|
assign Q = IQ; // "IQ"
|
||||||
always @(posedge CLK) begin
|
always @(posedge CLK) begin
|
||||||
// D '
|
|
||||||
IQ <= (~D);
|
IQ <= (~D);
|
||||||
|
end
|
||||||
|
always @(posedge CLK) begin
|
||||||
IQN <= ~((~D));
|
IQN <= ~((~D));
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
@ -32,8 +34,9 @@ module dffe (D, EN, CLK, Q, QN);
|
||||||
output QN;
|
output QN;
|
||||||
assign QN = IQN; // "IQN"
|
assign QN = IQN; // "IQN"
|
||||||
always @(negedge CLK) begin
|
always @(negedge CLK) begin
|
||||||
// ( D & EN ) | ( IQ & ! EN )
|
|
||||||
IQ <= ((D&EN)|(IQ&(~EN)));
|
IQ <= ((D&EN)|(IQ&(~EN)));
|
||||||
|
end
|
||||||
|
always @(negedge CLK) begin
|
||||||
IQN <= ~(((D&EN)|(IQ&(~EN))));
|
IQN <= ~(((D&EN)|(IQ&(~EN))));
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pyosys import libyosys as ys
|
from pyosys import libyosys as ys
|
||||||
|
|
||||||
|
|
||||||
__file_dir__ = Path(__file__).absolute().parent
|
__file_dir__ = Path(__file__).absolute().parent
|
||||||
add_sub = __file_dir__.parent / "arch" / "common" / "add_sub.v"
|
add_sub = __file_dir__.parent / "arch" / "common" / "add_sub.v"
|
||||||
|
|
||||||
28
tests/pyosys/test_sigspec_it.py
Normal file
28
tests/pyosys/test_sigspec_it.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
from pyosys import libyosys as ys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
__file_dir__ = Path(__file__).absolute().parent
|
||||||
|
|
||||||
|
def _dump_sigbit(bit):
|
||||||
|
if bit.is_wire():
|
||||||
|
if bit.wire.width == 1:
|
||||||
|
return bit.wire.name.str()
|
||||||
|
else:
|
||||||
|
return f"{bit.wire.name} [{bit.offset}]"
|
||||||
|
else:
|
||||||
|
if bit.data == ys.State.S1:
|
||||||
|
return 1
|
||||||
|
elif bit.data == ys.State.S0:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
assert "unknown constants not supported"
|
||||||
|
|
||||||
|
d = ys.Design()
|
||||||
|
|
||||||
|
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
|
||||||
|
ys.run_pass(f"hierarchy -top spm", d)
|
||||||
|
module = d.module(r"\spm")
|
||||||
|
for conn_from, conn_to in module.connections_:
|
||||||
|
for bit_from, bit_to in zip(conn_from, conn_to):
|
||||||
|
print(f"assign {_dump_sigbit(bit_from)} = {_dump_sigbit(bit_to)};")
|
||||||
|
|
||||||
|
|
@ -38,3 +38,20 @@ module foo(
|
||||||
assign b = bb;
|
assign b = bb;
|
||||||
assign y = a + bb;
|
assign y = a + bb;
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
module set_param #(
|
||||||
|
parameter [3:0] VALUE = 1'bx
|
||||||
|
) (
|
||||||
|
output logic [3:0] out
|
||||||
|
);
|
||||||
|
assign out = VALUE;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module use_param (
|
||||||
|
output logic [3:0] a, b, c, d
|
||||||
|
);
|
||||||
|
set_param #($signed(1)) spa (a);
|
||||||
|
set_param #('1) spb (b);
|
||||||
|
set_param #(1.1) spc (c);
|
||||||
|
set_param #(1'b1) spd (d);
|
||||||
|
endmodule
|
||||||
|
|
|
||||||
70
tests/sdc/alu_sub.sdc
Normal file
70
tests/sdc/alu_sub.sdc
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
###############################################################################
|
||||||
|
# Created by write_sdc
|
||||||
|
# Fri Oct 3 11:26:00 2025
|
||||||
|
###############################################################################
|
||||||
|
current_design wrapper
|
||||||
|
###############################################################################
|
||||||
|
# Timing Constraints
|
||||||
|
###############################################################################
|
||||||
|
create_clock -name this_clk -period 1.0000 [get_ports {clk}]
|
||||||
|
create_clock -name that_clk -period 2.0000
|
||||||
|
create_clock -name another_clk -period 2.0000 \
|
||||||
|
[list [get_ports {A[0]}]\
|
||||||
|
[get_ports {A[1]}]\
|
||||||
|
[get_ports {A[2]}]\
|
||||||
|
[get_ports {A[3]}]\
|
||||||
|
[get_ports {A[4]}]\
|
||||||
|
[get_ports {A[5]}]\
|
||||||
|
[get_ports {A[6]}]\
|
||||||
|
[get_ports {A[7]}]\
|
||||||
|
[get_ports {B[0]}]\
|
||||||
|
[get_ports {B[1]}]\
|
||||||
|
[get_ports {B[2]}]\
|
||||||
|
[get_ports {B[3]}]\
|
||||||
|
[get_ports {B[4]}]\
|
||||||
|
[get_ports {B[5]}]\
|
||||||
|
[get_ports {B[6]}]\
|
||||||
|
[get_ports {B[7]}]]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[0]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[0]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[1]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[1]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[2]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[2]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[3]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[3]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[4]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[4]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[5]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[5]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[6]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[6]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {A[7]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {A[7]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[0]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[0]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[1]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[1]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[2]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[2]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[3]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[3]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[4]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[4]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[5]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[5]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[6]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[6]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -rise -min -add_delay [get_ports {B[7]}]
|
||||||
|
set_input_delay 1.0000 -clock [get_clocks {this_clk}] -fall -min -add_delay [get_ports {B[7]}]
|
||||||
|
group_path -name operation_group\
|
||||||
|
-through [list [get_nets {alu/operation[0]}]\
|
||||||
|
[get_nets {alu/operation[1]}]\
|
||||||
|
[get_nets {alu/operation[2]}]\
|
||||||
|
[get_nets {alu/operation[3]}]]
|
||||||
|
###############################################################################
|
||||||
|
# Environment
|
||||||
|
###############################################################################
|
||||||
|
###############################################################################
|
||||||
|
# Design Rules
|
||||||
|
###############################################################################
|
||||||
62
tests/sdc/alu_sub.v
Normal file
62
tests/sdc/alu_sub.v
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
module adder(
|
||||||
|
input [7:0] a, input [7:0] b, output [7:0] y
|
||||||
|
);
|
||||||
|
assign y = a + b;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module wrapper(
|
||||||
|
input clk,
|
||||||
|
input [7:0] A,
|
||||||
|
input [7:0] B,
|
||||||
|
input [3:0] op,
|
||||||
|
output reg [7:0] result
|
||||||
|
);
|
||||||
|
wire CF, ZF, SF;
|
||||||
|
alu alu(
|
||||||
|
.clk(clk),
|
||||||
|
.A(A),
|
||||||
|
.B(B),
|
||||||
|
.operation(op),
|
||||||
|
.result(result),
|
||||||
|
.CF(CF),
|
||||||
|
.ZF(ZF),
|
||||||
|
.SF(SF)
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module alu(
|
||||||
|
input clk,
|
||||||
|
input [7:0] A,
|
||||||
|
input [7:0] B,
|
||||||
|
input [3:0] operation,
|
||||||
|
output reg [7:0] result,
|
||||||
|
output reg CF,
|
||||||
|
output reg ZF,
|
||||||
|
output reg SF
|
||||||
|
);
|
||||||
|
|
||||||
|
localparam ALU_OP_ADD /* verilator public_flat */ = 4'b0000;
|
||||||
|
localparam ALU_OP_SUB /* verilator public_flat */ = 4'b0001;
|
||||||
|
|
||||||
|
reg [8:0] tmp;
|
||||||
|
reg [7:0] added;
|
||||||
|
|
||||||
|
adder adder(.a(A), .b(B), .y(added));
|
||||||
|
|
||||||
|
always @(posedge clk)
|
||||||
|
begin
|
||||||
|
case (operation)
|
||||||
|
ALU_OP_ADD :
|
||||||
|
tmp = added;
|
||||||
|
ALU_OP_SUB :
|
||||||
|
tmp = A - B;
|
||||||
|
endcase
|
||||||
|
|
||||||
|
CF <= tmp[8];
|
||||||
|
ZF <= tmp[7:0] == 0;
|
||||||
|
SF <= tmp[7];
|
||||||
|
|
||||||
|
result <= tmp[7:0];
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
14
tests/sdc/alu_sub.ys
Normal file
14
tests/sdc/alu_sub.ys
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
read_verilog alu_sub.v
|
||||||
|
proc
|
||||||
|
hierarchy -auto-top
|
||||||
|
|
||||||
|
select -assert-mod-count 1 adder
|
||||||
|
select -assert-mod-count 1 wrapper
|
||||||
|
select -assert-mod-count 1 alu
|
||||||
|
|
||||||
|
sdc -keep_hierarchy alu_sub.sdc
|
||||||
|
flatten
|
||||||
|
|
||||||
|
select -assert-mod-count 0 adder
|
||||||
|
select -assert-mod-count 1 wrapper
|
||||||
|
select -assert-mod-count 1 alu
|
||||||
1
tests/sdc/get_foo.sdc
Normal file
1
tests/sdc/get_foo.sdc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
get_foo -bar 1
|
||||||
4
tests/sdc/run-test.sh
Executable file
4
tests/sdc/run-test.sh
Executable file
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eu
|
||||||
|
source ../gen-tests-makefile.sh
|
||||||
|
generate_mk --yosys-scripts --bash
|
||||||
2
tests/sdc/side-effects.sdc
Normal file
2
tests/sdc/side-effects.sdc
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
puts "This should print something:"
|
||||||
|
puts [get_ports {A[0]}]
|
||||||
4
tests/sdc/side-effects.sh
Executable file
4
tests/sdc/side-effects.sh
Executable file
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
../../yosys -p 'read_verilog alu_sub.v; proc; hierarchy -auto-top; sdc side-effects.sdc' | grep 'This should print something:
|
||||||
|
YOSYS_SDC_MAGIC_NODE_0'
|
||||||
5
tests/sdc/unknown-getter.sh
Executable file
5
tests/sdc/unknown-getter.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
! ../../yosys -p 'read_verilog alu_sub.v; proc; hierarchy -auto-top; sdc get_foo.sdc' 2>&1 | grep 'Unknown getter'
|
||||||
2
tests/techmap/bug5495.abc
Normal file
2
tests/techmap/bug5495.abc
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
fraig_store; fraig_restore
|
||||||
12
tests/techmap/bug5495.sh
Executable file
12
tests/techmap/bug5495.sh
Executable file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if ! which timeout ; then
|
||||||
|
echo "No 'timeout', skipping test"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! timeout 5 ../../yosys bug5495.v -p 'hierarchy; techmap; abc -script bug5495.abc' ; then
|
||||||
|
echo "Yosys failed to complete"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
7
tests/techmap/bug5495.v
Normal file
7
tests/techmap/bug5495.v
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
module simple(I1, I2, O);
|
||||||
|
input wire I1;
|
||||||
|
input wire I2;
|
||||||
|
output wire O;
|
||||||
|
|
||||||
|
assign O = I1 | I2;
|
||||||
|
endmodule
|
||||||
|
|
@ -361,6 +361,36 @@ namespace RTLIL {
|
||||||
EXPECT_FALSE(Const().is_onehot(&pos));
|
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 :
|
class WireRtlVsHdlIndexConversionTest :
|
||||||
public KernelRtlilTest,
|
public KernelRtlilTest,
|
||||||
public testing::WithParamInterface<std::tuple<bool, int, int>>
|
public testing::WithParamInterface<std::tuple<bool, int, int>>
|
||||||
|
|
|
||||||
26
tests/various/bug3515.v
Normal file
26
tests/various/bug3515.v
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Triple AND GATE
|
||||||
|
module mod_74x08_3 (
|
||||||
|
input A_1,
|
||||||
|
input B_1,
|
||||||
|
input A_2,
|
||||||
|
input B_2,
|
||||||
|
input A_3,
|
||||||
|
input B_3,
|
||||||
|
output Y_1,
|
||||||
|
output Y_2,
|
||||||
|
output Y_3);
|
||||||
|
|
||||||
|
assign Y_1 = A_1 & B_1;
|
||||||
|
assign Y_2 = A_2 & B_2;
|
||||||
|
assign Y_3 = A_3 & B_3;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// OR GATE
|
||||||
|
module mod_74x32_1 (
|
||||||
|
input A_1,
|
||||||
|
input B_1,
|
||||||
|
output Y_1);
|
||||||
|
|
||||||
|
assign Y_1 = A_1 | B_1;
|
||||||
|
endmodule
|
||||||
31
tests/various/bug3515.ys
Normal file
31
tests/various/bug3515.ys
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# base case is able to map
|
||||||
|
read_verilog << EOF
|
||||||
|
module and_x3 (
|
||||||
|
input a, b, c, d,
|
||||||
|
output reg y
|
||||||
|
);
|
||||||
|
|
||||||
|
assign y = (a&b)&(c&d);
|
||||||
|
endmodule
|
||||||
|
EOF
|
||||||
|
hierarchy -top and_x3
|
||||||
|
opt
|
||||||
|
extract -map ./bug3515.v
|
||||||
|
select -assert-count 1 t:mod_74x08_3
|
||||||
|
|
||||||
|
# more needles than haystacks; not able to map
|
||||||
|
design -reset
|
||||||
|
read_verilog << EOF
|
||||||
|
module mod_and_or (
|
||||||
|
input a, b, c, d,
|
||||||
|
output reg y
|
||||||
|
);
|
||||||
|
|
||||||
|
assign y = (a&b)|(c&d);
|
||||||
|
endmodule
|
||||||
|
EOF
|
||||||
|
hierarchy -top mod_and_or
|
||||||
|
opt
|
||||||
|
extract -map ./bug3515.v
|
||||||
|
select -assert-count 2 t:$and
|
||||||
|
|
||||||
121
tests/various/fsm-arst.ys
Normal file
121
tests/various/fsm-arst.ys
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
read_verilog << EOT
|
||||||
|
module non_self_rs_fsm (
|
||||||
|
input wire clk,
|
||||||
|
input wire reset,
|
||||||
|
output wire s1
|
||||||
|
);
|
||||||
|
localparam [7:0] RST = 8'b10010010;
|
||||||
|
localparam [7:0] S1 = 8'b01001000;
|
||||||
|
localparam [7:0] S2 = 8'b11000111;
|
||||||
|
|
||||||
|
reg [7:0] current_state, next_state;
|
||||||
|
always @(posedge clk or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
current_state <= RST;
|
||||||
|
end else begin
|
||||||
|
current_state <= next_state;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(*) begin
|
||||||
|
next_state = current_state;
|
||||||
|
|
||||||
|
case (current_state)
|
||||||
|
RST: next_state = S1;
|
||||||
|
S1: next_state = S2;
|
||||||
|
S2: next_state = S1;
|
||||||
|
default: next_state = RST;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
assign s1 = next_state == S1;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module semi_self_rs_fsm (
|
||||||
|
input wire clk,
|
||||||
|
input wire test,
|
||||||
|
output wire s1
|
||||||
|
);
|
||||||
|
localparam [7:0] RST = 8'b10010010;
|
||||||
|
localparam [7:0] S1 = 8'b01001000;
|
||||||
|
localparam [7:0] S2 = 8'b11000111;
|
||||||
|
|
||||||
|
reg [7:0] current_state, next_state;
|
||||||
|
reg [1:0] reset_test;
|
||||||
|
|
||||||
|
wire reset = (test || (reset_test == 2));
|
||||||
|
|
||||||
|
always @(posedge clk or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
current_state <= RST;
|
||||||
|
reset_test <= 0;
|
||||||
|
end else begin
|
||||||
|
current_state <= next_state;
|
||||||
|
if (current_state == S2)
|
||||||
|
reset_test = reset_test + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
always @(*) begin
|
||||||
|
next_state = current_state;
|
||||||
|
|
||||||
|
case (current_state)
|
||||||
|
RST: next_state = S1;
|
||||||
|
S2: next_state = S1;
|
||||||
|
S1: next_state = S2;
|
||||||
|
|
||||||
|
default: next_state = RST;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
assign s1 = next_state == S1;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module self_rs_fsm (
|
||||||
|
input wire clk,
|
||||||
|
output wire s1
|
||||||
|
);
|
||||||
|
localparam [7:0] RST = 8'b10010010;
|
||||||
|
localparam [7:0] S1 = 8'b01001000;
|
||||||
|
localparam [7:0] S2 = 8'b11000111;
|
||||||
|
|
||||||
|
reg [7:0] next_state;
|
||||||
|
wire reset = next_state == S1;
|
||||||
|
|
||||||
|
always @(posedge clk or posedge reset) begin
|
||||||
|
if (reset) begin
|
||||||
|
next_state <= RST;
|
||||||
|
end else begin
|
||||||
|
case (next_state)
|
||||||
|
RST: next_state = S1;
|
||||||
|
S1: next_state = S2;
|
||||||
|
S2: next_state = S1;
|
||||||
|
default: next_state = RST;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
assign s1 = next_state == S1;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
EOT
|
||||||
|
|
||||||
|
proc
|
||||||
|
opt_expr
|
||||||
|
opt_clean
|
||||||
|
check
|
||||||
|
opt -nodffe -nosdff
|
||||||
|
|
||||||
|
fsm_detect
|
||||||
|
fsm_extract
|
||||||
|
|
||||||
|
cd non_self_rs_fsm
|
||||||
|
select -assert-count 1 t:$fsm
|
||||||
|
|
||||||
|
cd semi_self_rs_fsm
|
||||||
|
select -assert-count 1 t:$fsm
|
||||||
|
|
||||||
|
cd self_rs_fsm
|
||||||
|
select -assert-none t:$fsm
|
||||||
1
tests/verilog/.gitignore
vendored
1
tests/verilog/.gitignore
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
/const_arst.v
|
/const_arst.v
|
||||||
/const_sr.v
|
/const_sr.v
|
||||||
/doubleslash.v
|
/doubleslash.v
|
||||||
|
/reset_auto_counter.v
|
||||||
/roundtrip_proc_1.v
|
/roundtrip_proc_1.v
|
||||||
/roundtrip_proc_2.v
|
/roundtrip_proc_2.v
|
||||||
/assign_to_reg.v
|
/assign_to_reg.v
|
||||||
|
|
|
||||||
17
tests/verilog/reset_auto_counter.ys
Normal file
17
tests/verilog/reset_auto_counter.ys
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
read_verilog -sv <<EOT
|
||||||
|
module arithmetic (
|
||||||
|
input logic [7:0] _0_,
|
||||||
|
input logic [7:0] _1_,
|
||||||
|
output logic [7:0] _2_,
|
||||||
|
);
|
||||||
|
assign _2_ = _0_ + _1_;
|
||||||
|
|
||||||
|
endmodule : arithmetic
|
||||||
|
EOT
|
||||||
|
|
||||||
|
hierarchy
|
||||||
|
techmap
|
||||||
|
write_verilog reset_auto_counter.v
|
||||||
|
! ! grep -qE '_0+0_' reset_auto_counter.v
|
||||||
|
! ! grep -qE '_0+1_' reset_auto_counter.v
|
||||||
|
! ! grep -qE '_0+2_' reset_auto_counter.v
|
||||||
|
|
@ -5,7 +5,20 @@ module pass_through(
|
||||||
assign out = inp;
|
assign out = inp;
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
module set_param #(
|
||||||
|
parameter logic [63:0] VALUE
|
||||||
|
) (
|
||||||
|
output logic [63:0] out
|
||||||
|
);
|
||||||
|
assign out = VALUE;
|
||||||
|
endmodule
|
||||||
|
|
||||||
module top;
|
module top;
|
||||||
|
localparam logic [63:0]
|
||||||
|
l01 = '0,
|
||||||
|
l02 = '1,
|
||||||
|
l03 = 'x,
|
||||||
|
l04 = 'z;
|
||||||
logic [63:0]
|
logic [63:0]
|
||||||
o01, o02, o03, o04,
|
o01, o02, o03, o04,
|
||||||
o05, o06, o07, o08,
|
o05, o06, o07, o08,
|
||||||
|
|
@ -23,6 +36,10 @@ module top;
|
||||||
pass_through pt10('1, o10);
|
pass_through pt10('1, o10);
|
||||||
pass_through pt11('x, o11);
|
pass_through pt11('x, o11);
|
||||||
pass_through pt12('z, o12);
|
pass_through pt12('z, o12);
|
||||||
|
set_param #('0) sp13(o13);
|
||||||
|
set_param #('1) sp14(o14);
|
||||||
|
set_param #('x) sp15(o15);
|
||||||
|
set_param #('z) sp16(o16);
|
||||||
always @* begin
|
always @* begin
|
||||||
assert (o01 === {64 {1'b0}});
|
assert (o01 === {64 {1'b0}});
|
||||||
assert (o02 === {64 {1'b1}});
|
assert (o02 === {64 {1'b1}});
|
||||||
|
|
@ -36,5 +53,13 @@ module top;
|
||||||
assert (o10 === {64 {1'b1}});
|
assert (o10 === {64 {1'b1}});
|
||||||
assert (o11 === {64 {1'bx}});
|
assert (o11 === {64 {1'bx}});
|
||||||
assert (o12 === {64 {1'bz}});
|
assert (o12 === {64 {1'bz}});
|
||||||
|
assert (l01 === {64 {1'b0}});
|
||||||
|
assert (l02 === {64 {1'b1}});
|
||||||
|
assert (l03 === {64 {1'bx}});
|
||||||
|
assert (l04 === {64 {1'bz}});
|
||||||
|
assert (o13 === {64 {1'b0}});
|
||||||
|
assert (o14 === {64 {1'b1}});
|
||||||
|
assert (o15 === {64 {1'bx}});
|
||||||
|
assert (o16 === {64 {1'bz}});
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
read_verilog -sv unbased_unsized.sv
|
read_verilog -sv unbased_unsized.sv
|
||||||
hierarchy
|
hierarchy -top top
|
||||||
proc
|
proc
|
||||||
flatten
|
flatten
|
||||||
opt -full
|
opt -full
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue