3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-06-26 02:30:37 +00:00

Merge branch 'YosysHQ:main' into master

This commit is contained in:
Eder Monteiro 2025-01-22 11:52:31 -03:00 committed by GitHub
commit 2df3a55221
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
96 changed files with 1950 additions and 1073 deletions

View file

@ -8,7 +8,7 @@ runs:
shell: bash
run: |
sudo apt-get update
sudo apt-get install gperf build-essential bison flex libreadline-dev gawk tcl-dev libffi-dev git graphviz xdot pkg-config python3 libboost-system-dev libboost-python-dev libboost-filesystem-dev zlib1g-dev
sudo apt-get install gperf build-essential bison flex libreadline-dev gawk tcl-dev libffi-dev git graphviz xdot pkg-config python3 libboost-system-dev libboost-python-dev libboost-filesystem-dev zlib1g-dev libbz2-dev
- name: Install macOS Dependencies
if: runner.os == 'macOS'

View file

@ -30,18 +30,15 @@ jobs:
- ubuntu-latest
compiler:
# oldest supported
- 'clang-14'
- 'clang-10'
- 'gcc-10'
# newest, make sure to update maximum standard step to match
- 'clang-18'
- 'clang-19'
- 'gcc-13'
include:
# macOS
- os: macos-13
compiler: 'clang'
# oldest clang not available on ubuntu-latest
- os: ubuntu-20.04
compiler: 'clang-10'
fail-fast: false
steps:
- name: Checkout Yosys
@ -72,7 +69,7 @@ jobs:
# maximum standard, only on newest compilers
- name: Build C++20
if: ${{ matrix.compiler == 'clang-18' || matrix.compiler == 'gcc-13' }}
if: ${{ matrix.compiler == 'clang-19' || matrix.compiler == 'gcc-13' }}
shell: bash
run: |
make config-$CC_SHORT

View file

@ -2,9 +2,21 @@
List of major changes and improvements between releases
=======================================================
Yosys 0.48 .. Yosys 0.49-dev
Yosys 0.49 .. Yosys 0.50-dev
--------------------------
Yosys 0.48 .. Yosys 0.49
--------------------------
* Various
- "$scopeinfo" cells are now part of JSON export by default.
- Added option to specify hierarchical separator for "flatten".
- Improved "wreduce" to handle more cases of operator size reduction.
- Updated hashing interface, see docs/source/yosys_internals/hashing.rst
for breaking API changes.
* New commands and options
- Added "-noscopeinfo" option to "json" and "write_json" pass.
Yosys 0.47 .. Yosys 0.48
--------------------------
* Various

View file

@ -1,6 +1,6 @@
ISC License
Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com>
Copyright (C) 2012 - 2025 Claire Xenia Wolf <claire@yosyshq.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above

217
Makefile
View file

@ -2,9 +2,7 @@
CONFIG := none
# CONFIG := clang
# CONFIG := gcc
# CONFIG := afl-gcc
# CONFIG := wasi
# CONFIG := mxe
# CONFIG := msys2-32
# CONFIG := msys2-64
@ -155,7 +153,7 @@ ifeq ($(OS), Haiku)
CXXFLAGS += -D_DEFAULT_SOURCE
endif
YOSYS_VER := 0.48+0
YOSYS_VER := 0.49+1
# Note: We arrange for .gitcommit to contain the (short) commit hash in
# tarballs generated with git-archive(1) using .gitattributes. The git repo
@ -171,7 +169,7 @@ endif
OBJS = kernel/version_$(GIT_REV).o
bumpversion:
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline aaa5347.. | wc -l`/;" Makefile
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 427b5a2.. | wc -l`/;" Makefile
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
@ -265,16 +263,6 @@ ifeq ($(DISABLE_ABC_THREADS),1)
ABCMKARGS += "ABC_USE_NO_PTHREADS=1"
endif
else ifeq ($(CONFIG),afl-gcc)
CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL)
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
else ifeq ($(CONFIG),cygwin)
CXX = g++
CXXFLAGS += -std=gnu++11 $(OPT_LEVEL)
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
else ifeq ($(CONFIG),wasi)
ifeq ($(WASI_SDK),)
CXX = clang++
@ -302,18 +290,6 @@ LINK_ABC := 1
DISABLE_ABC_THREADS := 1
endif
else ifeq ($(CONFIG),mxe)
PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config
CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++
CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -Wno-attributes
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s
LIBS := $(filter-out -lrt,$(LIBS))
ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
# TODO: Try to solve pthread linking issue in more appropriate way
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" LINKFLAGS="-Wl,--allow-multiple-definition" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc"
EXE = .exe
else ifeq ($(CONFIG),msys2-32)
CXX = i686-w64-mingw32-g++
CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
@ -340,7 +316,7 @@ ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)"
LTOFLAGS =
else
$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, mxe, msys2-32, msys2-64, none)
$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, msys2-32, msys2-64, none)
endif
@ -392,9 +368,6 @@ ifeq ($(LINK_TERMCAP),1)
LIBS += -ltermcap
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap"
endif
ifeq ($(CONFIG),mxe)
LIBS += -ltermcap
endif
else
ifeq ($(ENABLE_EDITLINE),1)
CXXFLAGS += -DYOSYS_ENABLE_EDITLINE
@ -443,17 +416,12 @@ TCL_INCLUDE ?= /usr/include/$(TCL_VERSION)
TCL_LIBS ?= -l$(TCL_VERSION)
endif
ifeq ($(CONFIG),mxe)
CXXFLAGS += -DYOSYS_ENABLE_TCL
LIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv
else
CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL
LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS))
ifneq (,$(findstring TCL_WITH_EXTERNAL_TOMMATH,$(CXXFLAGS)))
LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libtommath || echo)
endif
endif
endif
ifeq ($(ENABLE_GCOV),1)
CXXFLAGS += --coverage
@ -842,71 +810,101 @@ else
ABCOPT=""
endif
# When YOSYS_NOVERIFIC is set as a make variable, also export it to the
# enviornment, so that `YOSYS_NOVERIFIC=1 make test` _and_
# `make test YOSYS_NOVERIFIC=1` will run with verific disabled.
ifeq ($(YOSYS_NOVERIFIC),1)
export YOSYS_NOVERIFIC
# Tests that generate .mk with tests/gen-tests-makefile.sh
MK_TEST_DIRS =
MK_TEST_DIRS += tests/arch/anlogic
MK_TEST_DIRS += tests/arch/ecp5
MK_TEST_DIRS += tests/arch/efinix
MK_TEST_DIRS += tests/arch/gatemate
MK_TEST_DIRS += tests/arch/gowin
MK_TEST_DIRS += tests/arch/ice40
MK_TEST_DIRS += tests/arch/intel_alm
MK_TEST_DIRS += tests/arch/machxo2
MK_TEST_DIRS += tests/arch/microchip
MK_TEST_DIRS += tests/arch/nanoxplore
MK_TEST_DIRS += tests/arch/nexus
MK_TEST_DIRS += tests/arch/quicklogic/pp3
MK_TEST_DIRS += tests/arch/quicklogic/qlf_k6n10f
MK_TEST_DIRS += tests/arch/xilinx
MK_TEST_DIRS += tests/opt
MK_TEST_DIRS += tests/sat
MK_TEST_DIRS += tests/sim
MK_TEST_DIRS += tests/svtypes
MK_TEST_DIRS += tests/techmap
MK_TEST_DIRS += tests/various
ifeq ($(ENABLE_VERIFIC),1)
ifneq ($(YOSYS_NOVERIFIC),1)
MK_TEST_DIRS += tests/verific
endif
endif
MK_TEST_DIRS += tests/verilog
# Tests that don't generate .mk
SH_TEST_DIRS =
SH_TEST_DIRS += tests/simple
SH_TEST_DIRS += tests/simple_abc9
SH_TEST_DIRS += tests/hana
SH_TEST_DIRS += tests/asicworld
# SH_TEST_DIRS += tests/realmath
SH_TEST_DIRS += tests/share
SH_TEST_DIRS += tests/opt_share
SH_TEST_DIRS += tests/fsm
SH_TEST_DIRS += tests/memlib
SH_TEST_DIRS += tests/bram
SH_TEST_DIRS += tests/svinterfaces
SH_TEST_DIRS += tests/xprop
SH_TEST_DIRS += tests/select
SH_TEST_DIRS += tests/proc
SH_TEST_DIRS += tests/blif
SH_TEST_DIRS += tests/arch
SH_TEST_DIRS += tests/rpc
SH_TEST_DIRS += tests/memfile
SH_TEST_DIRS += tests/fmt
SH_TEST_DIRS += tests/cxxrtl
ifeq ($(ENABLE_FUNCTIONAL_TESTS),1)
SH_TEST_DIRS += tests/functional
endif
test: $(TARGETS) $(EXTRA_TARGETS)
ifeq ($(ENABLE_VERIFIC),1)
ifeq ($(YOSYS_NOVERIFIC),1)
@echo
@echo "Running tests without verific support due to YOSYS_NOVERIFIC=1"
@echo
else
+cd tests/verific && bash run-test.sh $(SEEDOPT)
endif
endif
+cd tests/simple && bash run-test.sh $(SEEDOPT)
+cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT)
+cd tests/hana && bash run-test.sh $(SEEDOPT)
+cd tests/asicworld && bash run-test.sh $(SEEDOPT)
# +cd tests/realmath && bash run-test.sh $(SEEDOPT)
+cd tests/share && bash run-test.sh $(SEEDOPT)
+cd tests/opt_share && bash run-test.sh $(SEEDOPT)
+cd tests/fsm && bash run-test.sh $(SEEDOPT)
+cd tests/techmap && bash run-test.sh
+cd tests/memories && bash run-test.sh $(ABCOPT) $(SEEDOPT)
+cd tests/memlib && bash run-test.sh $(SEEDOPT)
+cd tests/bram && bash run-test.sh $(SEEDOPT)
+cd tests/various && bash run-test.sh
+cd tests/select && bash run-test.sh
+cd tests/sat && bash run-test.sh
+cd tests/sim && bash run-test.sh
+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
+cd tests/svtypes && bash run-test.sh $(SEEDOPT)
+cd tests/proc && bash run-test.sh
+cd tests/blif && bash run-test.sh
+cd tests/opt && bash run-test.sh
+cd tests/aiger && bash run-test.sh $(ABCOPT)
+cd tests/arch && bash run-test.sh
+cd tests/arch/ice40 && bash run-test.sh $(SEEDOPT)
+cd tests/arch/xilinx && bash run-test.sh $(SEEDOPT)
+cd tests/arch/ecp5 && bash run-test.sh $(SEEDOPT)
+cd tests/arch/machxo2 && bash run-test.sh $(SEEDOPT)
+cd tests/arch/efinix && bash run-test.sh $(SEEDOPT)
+cd tests/arch/anlogic && bash run-test.sh $(SEEDOPT)
+cd tests/arch/gowin && bash run-test.sh $(SEEDOPT)
+cd tests/arch/intel_alm && bash run-test.sh $(SEEDOPT)
+cd tests/arch/nanoxplore && bash run-test.sh $(SEEDOPT)
+cd tests/arch/nexus && bash run-test.sh $(SEEDOPT)
+cd tests/arch/quicklogic/pp3 && bash run-test.sh $(SEEDOPT)
+cd tests/arch/quicklogic/qlf_k6n10f && bash run-test.sh $(SEEDOPT)
+cd tests/arch/gatemate && bash run-test.sh $(SEEDOPT)
+cd tests/arch/microchip && bash run-test.sh $(SEEDOPT)
+cd tests/rpc && bash run-test.sh
+cd tests/memfile && bash run-test.sh
+cd tests/verilog && bash run-test.sh
+cd tests/xprop && bash run-test.sh $(SEEDOPT)
+cd tests/fmt && bash run-test.sh
+cd tests/cxxrtl && bash run-test.sh
ifeq ($(ENABLE_FUNCTIONAL_TESTS),1)
+cd tests/functional && bash run-test.sh
endif
# Tests that don't generate .mk and need special args
SH_ABC_TEST_DIRS =
SH_ABC_TEST_DIRS += tests/memories
SH_ABC_TEST_DIRS += tests/aiger
SH_ABC_TEST_DIRS += tests/alumacc
# seed-tests/ is a dummy string, not a directory
.PHONY: seed-tests
seed-tests: $(SH_TEST_DIRS:%=seed-tests/%)
.PHONY: seed-tests/%
seed-tests/%: %/run-test.sh $(TARGETS) $(EXTRA_TARGETS)
+cd $* && bash run-test.sh $(SEEDOPT)
+@echo "...passed tests in $*"
# abcopt-tests/ is a dummy string, not a directory
.PHONY: abcopt-tests
abcopt-tests: $(SH_ABC_TEST_DIRS:%=abcopt-tests/%)
abcopt-tests/%: %/run-test.sh $(TARGETS) $(EXTRA_TARGETS)
+cd $* && bash run-test.sh $(ABCOPT) $(SEEDOPT)
+@echo "...passed tests in $*"
# makefile-tests/ is a dummy string, not a directory
.PHONY: makefile-tests
makefile-tests: $(MK_TEST_DIRS:%=makefile-tests/%)
# this target actually emits .mk files
%.mk:
+cd $(dir $*) && bash run-test.sh
# this one spawns submake on each
makefile-tests/%: %/run-test.mk $(TARGETS) $(EXTRA_TARGETS)
$(MAKE) -C $* -f run-test.mk
+@echo "...passed tests in $*"
test: makefile-tests abcopt-tests seed-tests
@echo ""
@echo " Passed \"make test\"."
ifeq ($(ENABLE_VERIFIC),1)
ifeq ($(YOSYS_NOVERIFIC),1)
@echo " Ran tests without verific support due to YOSYS_NOVERIFIC=1."
endif
endif
@echo ""
VALGRIND ?= valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all
@ -1101,19 +1099,6 @@ vcxsrc: $(GENFILES) $(EXTRA_TARGETS)
zip -r yosys-win32-vcxsrc-$(YOSYS_VER).zip yosys-win32-vcxsrc-$(YOSYS_VER)/
rm -f srcfiles.txt kernel/version.cc
ifeq ($(CONFIG),mxe)
mxebin: $(TARGETS) $(EXTRA_TARGETS)
rm -rf yosys-win32-mxebin-$(YOSYS_VER){,.zip}
mkdir -p yosys-win32-mxebin-$(YOSYS_VER)
cp -r $(PROGRAM_PREFIX)yosys.exe share/ yosys-win32-mxebin-$(YOSYS_VER)/
ifeq ($(ENABLE_ABC),1)
cp -r $(PROGRAM_PREFIX)yosys-abc.exe abc/lib/x86/pthreadVC2.dll yosys-win32-mxebin-$(YOSYS_VER)/
endif
echo -en 'This is Yosys $(YOSYS_VER) for Win32.\r\n' > yosys-win32-mxebin-$(YOSYS_VER)/readme.txt
echo -en 'Documentation at https://yosyshq.net/yosys/.\r\n' >> yosys-win32-mxebin-$(YOSYS_VER)/readme.txt
zip -r yosys-win32-mxebin-$(YOSYS_VER).zip yosys-win32-mxebin-$(YOSYS_VER)/
endif
config-clean: clean
rm -f Makefile.conf
@ -1129,9 +1114,6 @@ config-gcc-static: clean
echo 'ENABLE_READLINE := 0' >> Makefile.conf
echo 'ENABLE_TCL := 0' >> Makefile.conf
config-afl-gcc: clean
echo 'CONFIG := afl-gcc' > Makefile.conf
config-wasi: clean
echo 'CONFIG := wasi' > Makefile.conf
echo 'ENABLE_TCL := 0' >> Makefile.conf
@ -1140,10 +1122,6 @@ config-wasi: clean
echo 'ENABLE_READLINE := 0' >> Makefile.conf
echo 'ENABLE_ZLIB := 0' >> Makefile.conf
config-mxe: clean
echo 'CONFIG := mxe' > Makefile.conf
echo 'ENABLE_PLUGINS := 0' >> Makefile.conf
config-msys2-32: clean
echo 'CONFIG := msys2-32' > Makefile.conf
echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf
@ -1152,9 +1130,6 @@ config-msys2-64: clean
echo 'CONFIG := msys2-64' > Makefile.conf
echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf
config-cygwin: clean
echo 'CONFIG := cygwin' > Makefile.conf
config-gcov: clean
echo 'CONFIG := gcc' > Makefile.conf
echo 'ENABLE_GCOV := 1' >> Makefile.conf
@ -1183,5 +1158,5 @@ echo-cxx:
-include kernel/*.d
-include techlibs/*/*.d
.PHONY: all top-all abc test install install-abc docs clean mrproper qtcreator coverage vcxsrc mxebin
.PHONY: config-clean config-clang config-gcc config-gcc-static config-afl-gcc config-gprof config-sudo
.PHONY: all top-all abc test install install-abc docs clean mrproper qtcreator coverage vcxsrc
.PHONY: config-clean config-clang config-gcc config-gcc-static config-gprof config-sudo

View file

@ -47,7 +47,7 @@ struct Scheduler {
struct Vertex {
T *data;
Vertex *prev, *next;
pool<Vertex*, hash_ptr_ops> preds, succs;
pool<Vertex*> preds, succs;
Vertex() : data(NULL), prev(this), next(this) {}
Vertex(T *data) : data(data), prev(NULL), next(NULL) {}
@ -300,10 +300,10 @@ struct FlowGraph {
};
std::vector<Node*> nodes;
dict<const RTLIL::Wire*, pool<Node*, hash_ptr_ops>> wire_comb_defs, wire_sync_defs, wire_uses;
dict<Node*, pool<const RTLIL::Wire*>, hash_ptr_ops> node_comb_defs, node_sync_defs, node_uses;
dict<const RTLIL::Wire*, pool<Node*>> wire_comb_defs, wire_sync_defs, wire_uses;
dict<Node*, pool<const RTLIL::Wire*>> node_comb_defs, node_sync_defs, node_uses;
dict<const RTLIL::Wire*, bool> wire_def_inlinable;
dict<const RTLIL::Wire*, dict<Node*, bool, hash_ptr_ops>> wire_use_inlinable;
dict<const RTLIL::Wire*, dict<Node*, bool>> wire_use_inlinable;
dict<RTLIL::SigBit, bool> bit_has_state;
~FlowGraph()
@ -365,7 +365,7 @@ struct FlowGraph {
return false;
}
bool is_inlinable(const RTLIL::Wire *wire, const pool<Node*, hash_ptr_ops> &nodes) const
bool is_inlinable(const RTLIL::Wire *wire, const pool<Node*> &nodes) const
{
// Can the wire be inlined, knowing that the given nodes are reachable?
if (nodes.size() != 1)
@ -3080,7 +3080,7 @@ struct CxxrtlWorker {
// without feedback arcs can generally be evaluated in a single pass, i.e. it always requires only
// a single delta cycle.
Scheduler<FlowGraph::Node> scheduler;
dict<FlowGraph::Node*, Scheduler<FlowGraph::Node>::Vertex*, hash_ptr_ops> node_vertex_map;
dict<FlowGraph::Node*, Scheduler<FlowGraph::Node>::Vertex*> node_vertex_map;
for (auto node : flow.nodes)
node_vertex_map[node] = scheduler.add(node);
for (auto node_comb_def : flow.node_comb_defs) {
@ -3095,7 +3095,7 @@ struct CxxrtlWorker {
// Find out whether the order includes any feedback arcs.
std::vector<FlowGraph::Node*> node_order;
pool<FlowGraph::Node*, hash_ptr_ops> evaluated_nodes;
pool<FlowGraph::Node*> evaluated_nodes;
pool<const RTLIL::Wire*> feedback_wires;
for (auto vertex : scheduler.schedule()) {
auto node = vertex->data;
@ -3139,7 +3139,7 @@ struct CxxrtlWorker {
}
// Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users.
pool<FlowGraph::Node*, hash_ptr_ops> worklist;
pool<FlowGraph::Node*> worklist;
for (auto node : flow.nodes) {
if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type))
worklist.insert(node); // node evaluates a submodule
@ -3159,8 +3159,8 @@ struct CxxrtlWorker {
worklist.insert(node); // node drives public wires
}
}
dict<const RTLIL::Wire*, pool<FlowGraph::Node*, hash_ptr_ops>> live_wires;
pool<FlowGraph::Node*, hash_ptr_ops> live_nodes;
dict<const RTLIL::Wire*, pool<FlowGraph::Node*>> live_wires;
pool<FlowGraph::Node*> live_nodes;
while (!worklist.empty()) {
auto node = worklist.pop();
live_nodes.insert(node);
@ -3290,15 +3290,15 @@ struct CxxrtlWorker {
// Discover nodes reachable from primary outputs (i.e. outlines) up until primary inputs (i.e. members)
// and collect reachable wire users.
pool<FlowGraph::Node*, hash_ptr_ops> worklist;
pool<FlowGraph::Node*> worklist;
for (auto node : flow.nodes) {
if (flow.node_comb_defs.count(node))
for (auto wire : flow.node_comb_defs[node])
if (debug_wire_types[wire].is_outline())
worklist.insert(node); // node drives outline
}
dict<const RTLIL::Wire*, pool<FlowGraph::Node*, hash_ptr_ops>> debug_live_wires;
pool<FlowGraph::Node*, hash_ptr_ops> debug_live_nodes;
dict<const RTLIL::Wire*, pool<FlowGraph::Node*>> debug_live_wires;
pool<FlowGraph::Node*> debug_live_nodes;
while (!worklist.empty()) {
auto node = worklist.pop();
debug_live_nodes.insert(node);

View file

@ -34,6 +34,7 @@ struct JsonWriter
bool use_selection;
bool aig_mode;
bool compat_int_mode;
bool scopeinfo_mode;
Design *design;
Module *module;
@ -43,9 +44,9 @@ struct JsonWriter
dict<SigBit, string> sigids;
pool<Aig> aig_models;
JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode) :
JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode, bool scopeinfo_mode) :
f(f), use_selection(use_selection), aig_mode(aig_mode),
compat_int_mode(compat_int_mode) { }
compat_int_mode(compat_int_mode), scopeinfo_mode(scopeinfo_mode) { }
string get_string(string str)
{
@ -192,9 +193,7 @@ struct JsonWriter
for (auto c : module->cells()) {
if (use_selection && !module->selected(c))
continue;
// Eventually we will want to emit $scopeinfo, but currently this
// will break JSON netlist consumers like nextpnr
if (c->type == ID($scopeinfo))
if (!scopeinfo_mode && c->type == ID($scopeinfo))
continue;
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(c->name).c_str());
@ -356,6 +355,9 @@ struct JsonBackend : public Backend {
log(" -selected\n");
log(" output only select module\n");
log("\n");
log(" -noscopeinfo\n");
log(" don't include $scopeinfo cells in the output\n");
log("\n");
log("\n");
log("The general syntax of the JSON output created by this command is as follows:\n");
log("\n");
@ -601,6 +603,7 @@ struct JsonBackend : public Backend {
bool aig_mode = false;
bool compat_int_mode = false;
bool use_selection = false;
bool scopeinfo_mode = true;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@ -617,13 +620,17 @@ struct JsonBackend : public Backend {
use_selection = true;
continue;
}
if (args[argidx] == "-noscopeinfo") {
scopeinfo_mode = false;
continue;
}
break;
}
extra_args(f, filename, args, argidx);
log_header(design, "Executing JSON backend.\n");
JsonWriter json_writer(*f, use_selection, aig_mode, compat_int_mode);
JsonWriter json_writer(*f, use_selection, aig_mode, compat_int_mode, scopeinfo_mode);
json_writer.write_design(design);
}
} JsonBackend;
@ -648,6 +655,9 @@ struct JsonPass : public Pass {
log(" emit 32-bit or smaller fully-defined parameter values directly\n");
log(" as JSON numbers (for compatibility with old parsers)\n");
log("\n");
log(" -noscopeinfo\n");
log(" don't include $scopeinfo cells in the output\n");
log("\n");
log("See 'help write_json' for a description of the JSON format used.\n");
log("\n");
}
@ -656,6 +666,7 @@ struct JsonPass : public Pass {
std::string filename;
bool aig_mode = false;
bool compat_int_mode = false;
bool scopeinfo_mode = true;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@ -672,6 +683,10 @@ struct JsonPass : public Pass {
compat_int_mode = true;
continue;
}
if (args[argidx] == "-noscopeinfo") {
scopeinfo_mode = false;
continue;
}
break;
}
extra_args(args, argidx, design);
@ -693,7 +708,7 @@ struct JsonPass : public Pass {
f = &buf;
}
JsonWriter json_writer(*f, true, aig_mode, compat_int_mode);
JsonWriter json_writer(*f, true, aig_mode, compat_int_mode, scopeinfo_mode);
json_writer.write_design(design);
if (!empty) {

View file

@ -1208,7 +1208,7 @@ class SmtOpts:
def helpmsg(self):
return """
-s <solver>
set SMT solver: z3, yices, boolector, bitwuzla, cvc4, mathsat, dummy
set SMT solver: z3, yices, boolector, bitwuzla, cvc4, cvc5, mathsat, dummy
default: yices
-S <opt>

View file

@ -5,12 +5,35 @@ import os
project = 'YosysHQ Yosys'
author = 'YosysHQ GmbH'
copyright ='2024 YosysHQ GmbH'
yosys_ver = "0.48"
copyright ='2025 YosysHQ GmbH'
yosys_ver = "0.49"
# select HTML theme
html_theme = 'furo-ys'
html_css_files = ['custom.css']
html_theme_options: dict[str] = {
"source_repository": "https://github.com/YosysHQ/yosys/",
"source_branch": "main",
"source_directory": "docs/source/",
}
# try to fix the readthedocs detection
html_context: dict[str] = {
"READTHEDOCS": True,
"display_github": True,
"github_user": "YosysHQ",
"github_repo": "yosys",
"slug": "yosys",
}
# override source_branch if not main
git_slug = os.getenv("READTHEDOCS_VERSION_NAME")
if git_slug not in [None, "latest", "stable"]:
html_theme_options["source_branch"] = git_slug
# edit only works on branches, not tags
if os.getenv("READTHEDOCS_VERSION_TYPE", "branch") != "branch":
html_theme_options["top_of_page_buttons"] = ["view"]
# These folders are copied to the documentation's HTML output
html_static_path = ['_static', "_images"]

View file

@ -7,17 +7,6 @@ see :doc:`/introduction`. For a quick guide on how to get started using Yosys,
check out :doc:`/getting_started/index`. For the complete list of commands
available, go to :ref:`commandindex`.
.. note::
This documentation recently went through a major restructure. If you're
looking for something from the previous version and can't find it here,
please `let us know`_. Documentation from before the restructure can still
be found by switching to `version 0.36`_ or earlier. Note that the previous
theme does not include a version switcher.
.. _let us know: https://github.com/YosysHQ/yosys/issues/new/choose
.. _version 0.36: https://yosyshq.readthedocs.io/projects/yosys/en/0.36/
.. todo:: look into command ref improvements
- Search bar with live drop down suggestions for matching on title /

View file

@ -0,0 +1,166 @@
Hashing and associative data structures in Yosys
------------------------------------------------
Container classes based on hashing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yosys uses ``dict<K, T>`` and ``pool<T>`` as main container classes.
``dict<K, T>`` is essentially a replacement for ``std::unordered_map<K, T>``
and ``pool<T>`` is a replacement for ``std::unordered_set<T>``.
The main characteristics are:
* ``dict<K, T>`` and ``pool<T>`` are about 2x faster than the std containers
(though this claim hasn't been verified for over 10 years)
* references to elements in a ``dict<K, T>`` or ``pool<T>`` are invalidated by
insert and remove operations (similar to ``std::vector<T>`` on ``push_back()``).
* some iterators are invalidated by ``erase()``. specifically, iterators
that have not passed the erased element yet are invalidated. (``erase()``
itself returns valid iterator to the next element.)
* no iterators are invalidated by ``insert()``. elements are inserted at
``begin()``. i.e. only a new iterator that starts at ``begin()`` will see the
inserted elements.
* the method ``.count(key, iterator)`` is like ``.count(key)`` but only
considers elements that can be reached via the iterator.
* iterators can be compared. ``it1 < it2`` means that the position of ``t2``
can be reached via ``t1`` but not vice versa.
* the method ``.sort()`` can be used to sort the elements in the container
the container stays sorted until elements are added or removed.
* ``dict<K, T>`` and ``pool<T>`` will have the same order of iteration across
all compilers, standard libraries and architectures.
In addition to ``dict<K, T>`` and ``pool<T>`` there is also an ``idict<K>`` that
creates a bijective map from ``K`` to the integers. For example:
::
idict<string, 42> si;
log("%d\n", si("hello")); // will print 42
log("%d\n", si("world")); // will print 43
log("%d\n", si.at("world")); // will print 43
log("%d\n", si.at("dummy")); // will throw exception
log("%s\n", si[42].c_str())); // will print hello
log("%s\n", si[43].c_str())); // will print world
log("%s\n", si[44].c_str())); // will throw exception
It is not possible to remove elements from an idict.
Finally ``mfp<K>`` implements a merge-find set data structure (aka. disjoint-set
or union-find) over the type ``K`` ("mfp" = merge-find-promote).
The hash function
~~~~~~~~~~~~~~~~~
The hash function generally used in Yosys is the XOR version of DJB2:
::
state = ((state << 5) + state) ^ value
This is an old-school hash designed to hash ASCII characters. Yosys doesn't hash
a lot of ASCII text, but it still happens to be a local optimum due to factors
described later.
Hash function quality is multi-faceted and highly dependent on what is being
hashed. Yosys isn't concerned by any cryptographic qualities, instead the goal
is minimizing total hashing collision risk given the data patterns within Yosys.
In general, a good hash function typically folds values into a state accumulator
with a mathematical function that is fast to compute and has some beneficial
properties. One of these is the avalanche property, which demands that a small
change such as flipping a bit or incrementing by one in the input produces a
large, unpredictable change in the output. Additionally, the bit independence
criterion states that any pair of output bits should change independently when
any single input bit is inverted. These properties are important for avoiding
hash collision on data patterns like the hash of a sequence not colliding with
its permutation, not losing from the state the information added by hashing
preceding elements, etc.
DJB2 lacks these properties. Instead, since Yosys hashes large numbers of data
structures composed of incrementing integer IDs, Yosys abuses the predictability
of DJB2 to get lower hash collisions, with regular nature of the hashes
surviving through the interaction with the "modulo prime" operations in the
associative data structures. For example, some most common objects in Yosys are
interned ``IdString``\ s of incrementing indices or ``SigBit``\ s with bit
offsets into wire (represented by its unique ``IdString`` name) as the typical
case. This is what makes DJB2 a local optimum. Additionally, the ADD version of
DJB2 (like above but with addition instead of XOR) is used to this end for some
types, abandoning the general pattern of folding values into a state value.
Making a type hashable
~~~~~~~~~~~~~~~~~~~~~~
Let's first take a look at the external interface on a simplified level.
Generally, to get the hash for ``T obj``, you would call the utility function
``run_hash<T>(const T& obj)``, corresponding to ``hash_ops<T>::hash(obj)``,
the default implementation of which uses ``hash_ops<T>::hash_into(Hasher(), obj)``.
``Hasher`` is the class actually implementing the hash function, hiding its
initialized internal state, and passing it out on ``hash_t yield()`` with
perhaps some finalization steps.
``hash_ops<T>`` is the star of the show. By default it pulls the ``Hasher h``
through a ``Hasher T::hash_into(Hasher h)`` method. That's the method you have to
implement to make a record (class or struct) type easily hashable with Yosys
hashlib associative data structures.
``hash_ops<T>`` is specialized for built-in types like ``int`` or ``bool`` and
treats pointers the same as integers, so it doesn't dereference pointers. Since
many RTLIL data structures like ``RTLIL::Wire`` carry their own unique index
``Hasher::hash_t hashidx_;``, there are specializations for ``hash_ops<Wire*>``
and others in ``kernel/hashlib.h`` that actually dereference the pointers and
call ``hash_into`` on the instances pointed to.
``hash_ops<T>`` is also specialized for simple compound types like
``std::pair<U>`` by calling hash_into in sequence on its members. For flexible
size containers like ``std::vector<U>`` the size of the container is hashed
first. That is also how implementing hashing for a custom record data type
should be - unless there is strong reason to do otherwise, call ``h.eat(m)`` on
the ``Hasher h`` you have received for each member in sequence and ``return
h;``.
The ``hash_ops<T>::hash(obj)`` method is not indended to be called when
context of implementing the hashing for a record or other compound type.
When writing it, you should connect it to ``hash_ops<T>::hash_into(Hasher h)``
as shown below. If you have a strong reason to do so, and you have
to create a special implementation for top-level hashing, look at how
``hash_ops<RTLIL::SigBit>::hash(...)`` is implemented in ``kernel/rtlil.h``.
Porting plugins from the legacy interface
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Previously, the interface to implement hashing on custom types was just
``unsigned int T::hash() const``. This meant hashes for members were computed
independently and then ad-hoc combined with the hash function with some xorshift
operations thrown in to mix bits together somewhat. A plugin can stay compatible
with both versions prior and after the break by implementing both interfaces
based on the existance and value of `YS_HASHING_VERSION`.
.. code-block:: cpp
:caption: Example hash compatibility wrapper
:name: hash_plugin_compat
#ifndef YS_HASHING_VERSION
unsigned int T::hash() const {
return mkhash(a, b);
}
#elif YS_HASHING_VERSION == 1
Hasher T::hash_into(Hasher h) const {
h.eat(a);
h.eat(b);
return h;
}
Hasher T::hash() const {
Hasher h;
h.eat(*this);
return h;
}
#else
#error "Unsupported hashing interface"
#endif
Feel free to contact Yosys maintainers with related issues.

View file

@ -39,3 +39,4 @@ as reference to implement a similar system in any language.
extending_yosys/index
techmap
verilog
hashing

View file

@ -90,7 +90,7 @@ struct ScopeinfoExamplePass : public Pass {
// Shuffle wires so this example produces more interesting outputs
std::sort(wires.begin(), wires.end(), [](Wire *a, Wire *b) {
return mkhash_xorshift(a->name.hash() * 0x2c9277b5) < mkhash_xorshift(b->name.hash() * 0x2c9277b5);
return mkhash_xorshift(run_hash(a->name) * 0x2c9277b5) < mkhash_xorshift(run_hash(b->name) * 0x2c9277b5);
});
ModuleHdlnameIndex index(module);

View file

@ -14,15 +14,15 @@
};
# TODO: don't override src when ./abc is empty
# which happens when the command used is `nix build` and not `nix build ?submodules=1`
abc-verifier = pkgs.abc-verifier.overrideAttrs(x: y: {src = ./abc;});
abc-verifier = pkgs.abc-verifier;
yosys = pkgs.clangStdenv.mkDerivation {
name = "yosys";
src = ./. ;
buildInputs = with pkgs; [ clang bison flex libffi tcl readline python3 llvmPackages.libcxxClang zlib git pkg-configUpstream llvmPackages.bintools ];
buildInputs = with pkgs; [ clang bison flex libffi tcl readline python3 zlib git pkg-configUpstream llvmPackages.bintools ];
checkInputs = with pkgs; [ gtest ];
propagatedBuildInputs = [ abc-verifier ];
preConfigure = "make config-clang";
checkTarget = "test";
checkTarget = "unit-test";
installPhase = ''
make install PREFIX=$out ABCEXTERNAL=yosys-abc
ln -s ${abc-verifier}/bin/abc $out/bin/yosys-abc
@ -41,7 +41,7 @@
packages.default = yosys;
defaultPackage = yosys;
devShell = pkgs.mkShell {
buildInputs = with pkgs; [ clang llvmPackages.bintools bison flex libffi tcl readline python3 llvmPackages.libcxxClang zlib git gtest abc-verifier ];
buildInputs = with pkgs; [ clang llvmPackages.bintools gcc bison flex libffi tcl readline python3 zlib git gtest abc-verifier verilog boost python3Packages.boost ];
};
}
);

View file

@ -177,7 +177,7 @@ namespace AST
{
// for dict<> and pool<>
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
// this nodes type
AstNodeType type;

View file

@ -407,7 +407,7 @@ static const std::string verific_unescape(const char *value)
}
#endif
void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj, Netlist *nl)
void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj, Netlist *nl, int wire_width_hint)
{
if (!obj)
return;
@ -433,10 +433,18 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
auto type_range = nl->GetTypeRange(obj->Name());
if (!type_range)
return;
if (type_range->IsTypeScalar()) {
if (nl->IsFromVhdl() && type_range->IsTypeScalar()) {
const long long bottom_bound = type_range->GetScalarRangeLeftBound();
const long long top_bound = type_range->GetScalarRangeRightBound();
const unsigned bit_width = type_range->NumElements();
int bit_width = type_range->LeftRangeBound()+1;
if (bit_width <= 0) { // VHDL null range
if (wire_width_hint >= 0)
bit_width = wire_width_hint;
else
bit_width = 64; //fallback, currently largest integer width that verific will allow (in vhdl2019 mode)
} else {
if (wire_width_hint >= 0) log_assert(bit_width == wire_width_hint);
}
RTLIL::Const bottom_const(bottom_bound, bit_width);
RTLIL::Const top_const(top_bound, bit_width);
if (bottom_bound < 0 || top_bound < 0) {
@ -611,7 +619,7 @@ RTLIL::SigSpec VerificImporter::operatorInportCase(Instance *inst, const char *p
}
}
RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool<Net*, hash_ptr_ops> *any_all_nets)
RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool<Net*> *any_all_nets)
{
RTLIL::SigSpec sig;
RTLIL::Wire *dummy_wire = NULL;
@ -1456,7 +1464,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
log("Importing module %s.\n", RTLIL::id2cstr(module->name));
}
import_attributes(module->attributes, nl, nl);
module->set_string_attribute(ID::hdlname, nl->CellBaseName());
if (module->name.isPublic())
module->set_string_attribute(ID::hdlname, nl->CellBaseName());
module->set_string_attribute(ID(library), nl->Owner()->Owner()->Name());
#ifdef VERIFIC_VHDL_SUPPORT
if (nl->IsFromVhdl()) {
@ -1499,7 +1508,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
log(" importing port %s.\n", port->Name());
RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(port->Name()));
import_attributes(wire->attributes, port, nl);
import_attributes(wire->attributes, port, nl, 1);
wire->port_id = nl->IndexOf(port) + 1;
@ -1527,11 +1536,11 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(portbus->Name()), portbus->Size());
wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex());
wire->upto = portbus->IsUp();
import_attributes(wire->attributes, portbus, nl);
import_attributes(wire->attributes, portbus, nl, portbus->Size());
SetIter si ;
Port *port ;
FOREACH_PORT_OF_PORTBUS(portbus, si, port) {
import_attributes(wire->attributes, port->GetNet(), nl);
import_attributes(wire->attributes, port->GetNet(), nl, portbus->Size());
break;
}
bool portbus_input = portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN;
@ -1568,9 +1577,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
module->fixup_ports();
dict<Net*, char, hash_ptr_ops> init_nets;
pool<Net*, hash_ptr_ops> anyconst_nets, anyseq_nets;
pool<Net*, hash_ptr_ops> allconst_nets, allseq_nets;
dict<Net*, char> init_nets;
pool<Net*> anyconst_nets, anyseq_nets;
pool<Net*> allconst_nets, allseq_nets;
any_all_nets.clear();
FOREACH_NET_OF_NETLIST(nl, mi, net)
@ -1693,7 +1702,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
log(" importing net %s as %s.\n", net->Name(), log_id(wire_name));
RTLIL::Wire *wire = module->addWire(wire_name);
import_attributes(wire->attributes, net, nl);
import_attributes(wire->attributes, net, nl, 1);
net_map[net] = wire;
}
@ -1722,10 +1731,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
MapIter mibus;
FOREACH_NET_OF_NETBUS(netbus, mibus, net) {
if (net)
import_attributes(wire->attributes, net, nl);
import_attributes(wire->attributes, net, nl, netbus->Size());
break;
}
import_attributes(wire->attributes, netbus, nl);
import_attributes(wire->attributes, netbus, nl, netbus->Size());
RTLIL::Const initval = Const(State::Sx, GetSize(wire));
bool initval_valid = false;
@ -1833,10 +1842,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
module->connect(net_map_at(net), module->Anyseq(new_verific_id(net)));
#ifdef VERIFIC_SYSTEMVERILOG_SUPPORT
pool<Instance*, hash_ptr_ops> sva_asserts;
pool<Instance*, hash_ptr_ops> sva_assumes;
pool<Instance*, hash_ptr_ops> sva_covers;
pool<Instance*, hash_ptr_ops> sva_triggers;
pool<Instance*> sva_asserts;
pool<Instance*> sva_assumes;
pool<Instance*> sva_covers;
pool<Instance*> sva_triggers;
#endif
pool<RTLIL::Cell*> past_ffs;

View file

@ -71,7 +71,7 @@ struct VerificImporter
std::map<Verific::Net*, RTLIL::SigBit> net_map;
std::map<Verific::Net*, Verific::Net*> sva_posedge_map;
pool<Verific::Net*, hash_ptr_ops> any_all_nets;
pool<Verific::Net*> any_all_nets;
bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific;
bool mode_autocover, mode_fullinit;
@ -81,7 +81,7 @@ struct VerificImporter
RTLIL::SigBit net_map_at(Verific::Net *net);
RTLIL::IdString new_verific_id(Verific::DesignObj *obj);
void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr);
void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr, int wire_width_hint = -1);
RTLIL::SigBit netToSigBit(Verific::Net *net);
RTLIL::SigSpec operatorInput(Verific::Instance *inst);
@ -89,7 +89,7 @@ struct VerificImporter
RTLIL::SigSpec operatorInput2(Verific::Instance *inst);
RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname);
RTLIL::SigSpec operatorInportCase(Verific::Instance *inst, const char *portname);
RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool<Verific::Net*, hash_ptr_ops> *any_all_nets = nullptr);
RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool<Verific::Net*> *any_all_nets = nullptr);
bool import_netlist_instance_gates(Verific::Instance *inst, RTLIL::IdString inst_name);
bool import_netlist_instance_cells(Verific::Instance *inst, RTLIL::IdString inst_name);

View file

@ -1051,7 +1051,7 @@ struct VerificSvaImporter
msg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile());
}
dict<Net*, bool, hash_ptr_ops> check_expression_cache;
dict<Net*, bool> check_expression_cache;
bool check_expression(Net *net, bool raise_error = false)
{

View file

@ -37,57 +37,15 @@ And then executed using the following command:
Yosys Data Structures
---------------------
Here is a short list of data structures that you should make yourself familiar
with before you write C++ code for Yosys. The following data structures are all
defined when "kernel/yosys.h" is included and USING_YOSYS_NAMESPACE is used.
1. Container classes based on hashing
1. Yosys Container Classes
Yosys heavily relies on custom container data structures such as dict or pool
defined in kernel/hashlib.h.
dict<K, T> is essentially a replacement for std::unordered_map<K, T>
and pool<T> is a replacement for std::unordered_set<T>. Please refer to
docs/source/yosys_internals/hashing.rst for more information on those.
Yosys uses dict<K, T> and pool<T> as main container classes. dict<K, T> is
essentially a replacement for std::unordered_map<K, T> and pool<T> is a
replacement for std::unordered_set<T>. The main characteristics are:
- dict<K, T> and pool<T> are about 2x faster than the std containers
- references to elements in a dict<K, T> or pool<T> are invalidated by
insert and remove operations (similar to std::vector<T> on push_back()).
- some iterators are invalidated by erase(). specifically, iterators
that have not passed the erased element yet are invalidated. (erase()
itself returns valid iterator to the next element.)
- no iterators are invalidated by insert(). elements are inserted at
begin(). i.e. only a new iterator that starts at begin() will see the
inserted elements.
- the method .count(key, iterator) is like .count(key) but only
considers elements that can be reached via the iterator.
- iterators can be compared. it1 < it2 means that the position of t2
can be reached via t1 but not vice versa.
- the method .sort() can be used to sort the elements in the container
the container stays sorted until elements are added or removed.
- dict<K, T> and pool<T> will have the same order of iteration across
all compilers, standard libraries and architectures.
In addition to dict<K, T> and pool<T> there is also an idict<K> that
creates a bijective map from K to the integers. For example:
idict<string, 42> si;
log("%d\n", si("hello")); // will print 42
log("%d\n", si("world")); // will print 43
log("%d\n", si.at("world")); // will print 43
log("%d\n", si.at("dummy")); // will throw exception
log("%s\n", si[42].c_str())); // will print hello
log("%s\n", si[43].c_str())); // will print world
log("%s\n", si[44].c_str())); // will throw exception
It is not possible to remove elements from an idict.
Finally mfp<K> implements a merge-find set data structure (aka. disjoint-set or
union-find) over the type K ("mfp" = merge-find-promote).
Otherwise, Yosys makes use of the following:
2. Standard STL data types

View file

@ -30,7 +30,7 @@ struct BitPatternPool
int width;
struct bits_t {
std::vector<RTLIL::State> bitdata;
mutable unsigned int cached_hash;
mutable Hasher::hash_t cached_hash;
bits_t(int width = 0) : bitdata(width), cached_hash(0) { }
RTLIL::State &operator[](int index) {
return bitdata[index];
@ -39,14 +39,15 @@ struct BitPatternPool
return bitdata[index];
}
bool operator==(const bits_t &other) const {
if (hash() != other.hash())
if (run_hash(*this) != run_hash(other))
return false;
return bitdata == other.bitdata;
}
unsigned int hash() const {
[[nodiscard]] Hasher hash_into(Hasher h) const {
if (!cached_hash)
cached_hash = hash_ops<std::vector<RTLIL::State>>::hash(bitdata);
return cached_hash;
cached_hash = run_hash(bitdata);
h.eat(cached_hash);
return h;
}
};
pool<bits_t> database;

View file

@ -39,13 +39,13 @@ bool AigNode::operator==(const AigNode &other) const
return true;
}
unsigned int AigNode::hash() const
Hasher AigNode::hash_into(Hasher h) const
{
unsigned int h = mkhash_init;
h = mkhash(portname.hash(), portbit);
h = mkhash(h, inverter);
h = mkhash(h, left_parent);
h = mkhash(h, right_parent);
h.eat(portname);
h.eat(portbit);
h.eat(inverter);
h.eat(left_parent);
h.eat(right_parent);
return h;
}
@ -54,9 +54,10 @@ bool Aig::operator==(const Aig &other) const
return name == other.name;
}
unsigned int Aig::hash() const
Hasher Aig::hash_into(Hasher h) const
{
return hash_ops<std::string>::hash(name);
h.eat(name);
return h;
}
struct AigMaker

View file

@ -34,7 +34,7 @@ struct AigNode
AigNode();
bool operator==(const AigNode &other) const;
unsigned int hash() const;
[[nodiscard]] Hasher hash_into(Hasher h) const;
};
struct Aig
@ -44,7 +44,7 @@ struct Aig
Aig(Cell *cell);
bool operator==(const Aig &other) const;
unsigned int hash() const;
[[nodiscard]] Hasher hash_into(Hasher h) const;
};
YOSYS_NAMESPACE_END

View file

@ -315,9 +315,6 @@ struct ConstEval
Macc macc;
macc.from_cell(cell);
if (!eval(macc.bit_ports, undef, cell))
return false;
for (auto &port : macc.ports) {
if (!eval(port.in_a, undef, cell))
return false;

View file

@ -18,6 +18,7 @@
*/
#include "kernel/yosys.h"
#include "kernel/hashlib.h"
#include "libs/sha1/sha1.h"
#include "libs/cxxopts/include/cxxopts.hpp"
#include <iostream>
@ -282,6 +283,8 @@ int main(int argc, char **argv)
("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging")
("autoidx", "start counting autoidx up from <seed>, similar effect to --hash-seed",
cxxopts::value<uint64_t>(), "<idx>")
("hash-seed", "mix up hashing values with <seed>, for extreme optimization and testing",
cxxopts::value<uint64_t>(), "<seed>")
("A,abort", "will call abort() at the end of the script. for debugging")
("x,experimental", "do not print warnings for the experimental <feature>",
cxxopts::value<std::vector<std::string>>(), "<feature>")
@ -437,6 +440,10 @@ int main(int argc, char **argv)
int idx = result["autoidx"].as<uint64_t>();
autoidx = idx;
}
if (result.count("hash-seed")) {
int seed = result["hash-seed"].as<uint64_t>();
Hasher::set_fudge((Hasher::hash_t)seed);
}
if (log_errfile == NULL) {
log_files.push_back(stdout);

View file

@ -74,10 +74,8 @@ struct DriveBitWire
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(wire->name.hash(), offset);
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
operator SigBit() const
{
@ -107,10 +105,8 @@ struct DriveBitPort
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
};
@ -133,10 +129,7 @@ struct DriveBitMarker
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(marker, offset);
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
};
@ -171,10 +164,7 @@ public:
return multiple_ == other.multiple_;
}
unsigned int hash() const
{
return multiple_.hash();
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
};
struct DriveBit
@ -362,35 +352,7 @@ public:
return *this;
}
unsigned int hash() const
{
unsigned int inner = 0;
switch (type_)
{
case DriveType::NONE:
inner = 0;
break;
case DriveType::CONSTANT:
inner = constant_;
break;
case DriveType::WIRE:
inner = wire_.hash();
break;
case DriveType::PORT:
inner = port_.hash();
break;
case DriveType::MARKER:
inner = marker_.hash();
break;
case DriveType::MULTIPLE:
inner = multiple_.hash();
break;
default:
log_abort();
break;
}
return mkhash((unsigned int)type_, inner);
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
bool operator==(const DriveBit &other) const
{
@ -511,10 +473,7 @@ struct DriveChunkWire
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(wire->name.hash(), width), offset);
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
explicit operator SigChunk() const
{
@ -572,10 +531,7 @@ struct DriveChunkPort
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(mkhash(cell->name.hash(), port.hash()), width), offset);
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
};
@ -616,10 +572,7 @@ struct DriveChunkMarker
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(marker, width), offset);
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
};
struct DriveChunkMultiple
@ -659,10 +612,7 @@ public:
return false; // TODO implement, canonicalize order
}
unsigned int hash() const
{
return mkhash(width_, multiple_.hash());
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
};
struct DriveChunk
@ -913,35 +863,7 @@ public:
bool try_append(DriveBit const &bit);
bool try_append(DriveChunk const &chunk);
unsigned int hash() const
{
unsigned int inner = 0;
switch (type_)
{
case DriveType::NONE:
inner = 0;
break;
case DriveType::CONSTANT:
inner = constant_.hash();
break;
case DriveType::WIRE:
inner = wire_.hash();
break;
case DriveType::PORT:
inner = port_.hash();
break;
case DriveType::MARKER:
inner = marker_.hash();
break;
case DriveType::MULTIPLE:
inner = multiple_.hash();
break;
default:
log_abort();
break;
}
return mkhash((unsigned int)type_, inner);
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
bool operator==(const DriveChunk &other) const
{
@ -1144,17 +1066,20 @@ public:
DriveSpec &operator=(DriveBitMarker const &bit) { return *this = DriveBit(bit); }
DriveSpec &operator=(DriveBitMultiple const &bit) { return *this = DriveBit(bit); }
unsigned int hash() const {
if (hash_ != 0) return hash_;
void updhash() const {
if (hash_ != 0)
return;
pack();
hash_ = hash_ops<std::vector<DriveChunk>>().hash(chunks_);
hash_ = run_hash(chunks_);
hash_ |= (hash_ == 0);
return hash_;
}
[[nodiscard]] Hasher hash_into(Hasher h) const;
bool operator==(DriveSpec const &other) const {
if (size() != other.size() || hash() != other.hash())
updhash();
other.updhash();
if (size() != other.size() || hash_ != other.hash_)
return false;
return chunks() == other.chunks();
}
@ -1187,7 +1112,7 @@ private:
bool operator==(const DriveBitId &other) const { return id == other.id; }
bool operator!=(const DriveBitId &other) const { return id != other.id; }
bool operator<(const DriveBitId &other) const { return id < other.id; }
unsigned int hash() const { return id; }
[[nodiscard]] Hasher hash_into(Hasher h) const;
};
// Essentially a dict<DriveBitId, pool<DriveBitId>> but using less memory
// and fewer allocations
@ -1333,6 +1258,131 @@ private:
}
};
inline Hasher DriveBitWire::hash_into(Hasher h) const
{
h.eat(wire->name);
h.eat(offset);
return h;
}
inline Hasher DriveBitPort::hash_into(Hasher h) const
{
h.eat(cell->name);
h.eat(port);
h.eat(offset);
return h;
}
inline Hasher DriveBitMarker::hash_into(Hasher h) const
{
h.eat(marker);
h.eat(offset);
return h;
}
inline Hasher DriveBitMultiple::hash_into(Hasher h) const
{
h.eat(multiple_);
return h;
}
inline Hasher DriveBit::hash_into(Hasher h) const
{
switch (type_) {
case DriveType::NONE:
h.eat(0);
break;
case DriveType::CONSTANT:
h.eat(constant_);
break;
case DriveType::WIRE:
h.eat(wire_);
break;
case DriveType::PORT:
h.eat(port_);
break;
case DriveType::MARKER:
h.eat(marker_);
break;
case DriveType::MULTIPLE:
h.eat(multiple_);
break;
}
h.eat(type_);
return h;
}
inline Hasher DriveChunkWire::hash_into(Hasher h) const
{
h.eat(wire->name);
h.eat(width);
h.eat(offset);
return h;
}
inline Hasher DriveChunkPort::hash_into(Hasher h) const
{
h.eat(cell->name);
h.eat(port);
h.eat(width);
h.eat(offset);
return h;
}
inline Hasher DriveChunkMarker::hash_into(Hasher h) const
{
h.eat(marker);
h.eat(width);
h.eat(offset);
return h;
}
inline Hasher DriveChunkMultiple::hash_into(Hasher h) const
{
h.eat(width_);
h.eat(multiple_);
return h;
}
inline Hasher DriveChunk::hash_into(Hasher h) const
{
switch (type_) {
case DriveType::NONE:
h.eat(0);
break;
case DriveType::CONSTANT:
h.eat(constant_);
break;
case DriveType::WIRE:
h.eat(wire_);
break;
case DriveType::PORT:
h.eat(port_);
break;
case DriveType::MARKER:
h.eat(marker_);
break;
case DriveType::MULTIPLE:
h.eat(multiple_);
break;
}
h.eat(type_);
return h;
}
inline Hasher DriveSpec::hash_into(Hasher h) const
{
updhash();
h.eat(hash_);
return h;
}
inline Hasher DriverMap::DriveBitId::hash_into(Hasher h) const
{
h.eat(id);
return h;
}
YOSYS_NAMESPACE_END
#endif

View file

@ -151,7 +151,7 @@ namespace Functional {
// returns the data width of a bitvector sort, errors out for other sorts
int data_width() const { return std::get<1>(_v).second; }
bool operator==(Sort const& other) const { return _v == other._v; }
unsigned int hash() const { return mkhash(_v); }
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(_v); return h; }
};
class IR;
class Factory;
@ -225,8 +225,10 @@ namespace Functional {
const RTLIL::Const &as_const() const { return std::get<RTLIL::Const>(_extra); }
std::pair<IdString, IdString> as_idstring_pair() const { return std::get<std::pair<IdString, IdString>>(_extra); }
int as_int() const { return std::get<int>(_extra); }
int hash() const {
return mkhash((unsigned int) _fn, mkhash(_extra));
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat((unsigned int) _fn);
h.eat(_extra);
return h;
}
bool operator==(NodeData const &other) const {
return _fn == other._fn && _extra == other._extra;

View file

@ -17,27 +17,52 @@
#include <string>
#include <variant>
#include <vector>
#include <type_traits>
#include <stdint.h>
#define YS_HASHING_VERSION 1
namespace hashlib {
/**
* HASHING
*
* Also refer to docs/source/yosys_internals/hashing.rst
*
* The Hasher knows how to hash 32 and 64-bit integers. That's it.
* In the future, it could be expanded to do vectors with SIMD.
*
* The Hasher doesn't know how to hash common standard containers
* and compositions. However, hashlib provides centralized wrappers.
*
* Hashlib doesn't know how to hash silly Yosys-specific types.
* Hashlib doesn't depend on Yosys and can be used standalone.
* Please don't use hashlib standalone for new projects.
* Never directly include kernel/hashlib.h in Yosys code.
* Instead include kernel/yosys_common.h
*
* The hash_ops type is now always left to its default value, derived
* from templated functions through SFINAE. Providing custom ops is
* still supported.
*
* HASH TABLES
*
* We implement associative data structures with separate chaining.
* Linked lists use integers into the indirection hashtable array
* instead of pointers.
*/
const int hashtable_size_trigger = 2;
const int hashtable_size_factor = 3;
// The XOR version of DJB2
inline unsigned int mkhash(unsigned int a, unsigned int b) {
return ((a << 5) + a) ^ b;
}
namespace legacy {
inline uint32_t djb2_add(uint32_t a, uint32_t b) {
return ((a << 5) + a) + b;
}
};
// traditionally 5381 is used as starting value for the djb2 hash
const unsigned int mkhash_init = 5381;
// The ADD version of DJB2
// (use this version for cache locality in b)
inline unsigned int mkhash_add(unsigned int a, unsigned int b) {
return ((a << 5) + a) + b;
}
template<typename T>
struct hash_ops;
inline unsigned int mkhash_xorshift(unsigned int a) {
if (sizeof(a) == 4) {
@ -53,72 +78,113 @@ inline unsigned int mkhash_xorshift(unsigned int a) {
return a;
}
template<typename T> struct hash_ops {
class HasherDJB32 {
public:
using hash_t = uint32_t;
HasherDJB32() {
// traditionally 5381 is used as starting value for the djb2 hash
state = 5381;
}
static void set_fudge(hash_t f) {
fudge = f;
}
private:
uint32_t state;
static uint32_t fudge;
// The XOR version of DJB2
[[nodiscard]]
static uint32_t djb2_xor(uint32_t a, uint32_t b) {
uint32_t hash = ((a << 5) + a) ^ b;
return hash;
}
public:
void hash32(uint32_t i) {
state = djb2_xor(i, state);
state = mkhash_xorshift(fudge ^ state);
return;
}
void hash64(uint64_t i) {
state = djb2_xor((uint32_t)(i & 0xFFFFFFFFULL), state);
state = djb2_xor((uint32_t)(i >> 32ULL), state);
state = mkhash_xorshift(fudge ^ state);
return;
}
[[nodiscard]]
hash_t yield() {
return (hash_t)state;
}
template<typename T>
void eat(T&& t) {
*this = hash_ops<std::remove_cv_t<std::remove_reference_t<T>>>::hash_into(std::forward<T>(t), *this);
}
template<typename T>
void eat(const T& t) {
*this = hash_ops<T>::hash_into(t, *this);
}
void commutative_eat(hash_t t) {
state ^= t;
}
void force(hash_t new_state) {
state = new_state;
}
};
using Hasher = HasherDJB32;
// Boilerplate compressor for trivially implementing
// top-level hash method with hash_into
#define HASH_TOP_LOOP_FST [[nodiscard]] static inline Hasher hash
#define HASH_TOP_LOOP_SND { \
Hasher h; \
h = hash_into(a, h); \
return h; \
}
template<typename T>
struct hash_ops {
static inline bool cmp(const T &a, const T &b) {
return a == b;
}
static inline unsigned int hash(const T &a) {
return a.hash();
}
};
struct hash_int_ops {
template<typename T>
static inline bool cmp(T a, T b) {
return a == b;
}
};
template<> struct hash_ops<bool> : hash_int_ops
{
static inline unsigned int hash(bool a) {
return a ? 1 : 0;
}
};
template<> struct hash_ops<int32_t> : hash_int_ops
{
static inline unsigned int hash(int32_t a) {
return a;
}
};
template<> struct hash_ops<int64_t> : hash_int_ops
{
static inline unsigned int hash(int64_t a) {
return mkhash((unsigned int)(a), (unsigned int)(a >> 32));
}
};
template<> struct hash_ops<uint32_t> : hash_int_ops
{
static inline unsigned int hash(uint32_t a) {
return a;
}
};
template<> struct hash_ops<uint64_t> : hash_int_ops
{
static inline unsigned int hash(uint64_t a) {
return mkhash((unsigned int)(a), (unsigned int)(a >> 32));
}
};
template<> struct hash_ops<std::string> {
static inline bool cmp(const std::string &a, const std::string &b) {
return a == b;
}
static inline unsigned int hash(const std::string &a) {
unsigned int v = 0;
for (auto c : a)
v = mkhash(v, c);
return v;
[[nodiscard]] static inline Hasher hash_into(const T &a, Hasher h) {
if constexpr (std::is_integral_v<T>) {
static_assert(sizeof(T) <= sizeof(uint64_t));
if (sizeof(T) == sizeof(uint64_t))
h.hash64(a);
else
h.hash32(a);
return h;
} else if constexpr (std::is_enum_v<T>) {
using u_type = std::underlying_type_t<T>;
return hash_ops<u_type>::hash_into((u_type) a, h);
} else if constexpr (std::is_pointer_v<T>) {
return hash_ops<uintptr_t>::hash_into((uintptr_t) a, h);
} else if constexpr (std::is_same_v<T, std::string>) {
for (auto c : a)
h.hash32(c);
return h;
} else {
return a.hash_into(h);
}
}
HASH_TOP_LOOP_FST (const T &a) HASH_TOP_LOOP_SND
};
template<typename P, typename Q> struct hash_ops<std::pair<P, Q>> {
static inline bool cmp(std::pair<P, Q> a, std::pair<P, Q> b) {
return a == b;
}
static inline unsigned int hash(std::pair<P, Q> a) {
return mkhash(hash_ops<P>::hash(a.first), hash_ops<Q>::hash(a.second));
[[nodiscard]] static inline Hasher hash_into(std::pair<P, Q> a, Hasher h) {
h = hash_ops<P>::hash_into(a.first, h);
h = hash_ops<Q>::hash_into(a.second, h);
return h;
}
HASH_TOP_LOOP_FST (std::pair<P, Q> a) HASH_TOP_LOOP_SND
};
template<typename... T> struct hash_ops<std::tuple<T...>> {
@ -126,50 +192,66 @@ template<typename... T> struct hash_ops<std::tuple<T...>> {
return a == b;
}
template<size_t I = 0>
static inline typename std::enable_if<I == sizeof...(T), unsigned int>::type hash(std::tuple<T...>) {
return mkhash_init;
static inline typename std::enable_if<I == sizeof...(T), Hasher>::type hash_into(std::tuple<T...>, Hasher h) {
return h;
}
template<size_t I = 0>
static inline typename std::enable_if<I != sizeof...(T), unsigned int>::type hash(std::tuple<T...> a) {
static inline typename std::enable_if<I != sizeof...(T), Hasher>::type hash_into(std::tuple<T...> a, Hasher h) {
typedef hash_ops<typename std::tuple_element<I, std::tuple<T...>>::type> element_ops_t;
return mkhash(hash<I+1>(a), element_ops_t::hash(std::get<I>(a)));
h = hash_into<I+1>(a, h);
h = element_ops_t::hash_into(std::get<I>(a), h);
return h;
}
HASH_TOP_LOOP_FST (std::tuple<T...> a) HASH_TOP_LOOP_SND
};
template<typename T> struct hash_ops<std::vector<T>> {
static inline bool cmp(std::vector<T> a, std::vector<T> b) {
return a == b;
}
static inline unsigned int hash(std::vector<T> a) {
unsigned int h = mkhash_init;
[[nodiscard]] static inline Hasher hash_into(std::vector<T> a, Hasher h) {
h.eat((uint32_t)a.size());
for (auto k : a)
h = mkhash(h, hash_ops<T>::hash(k));
h.eat(k);
return h;
}
HASH_TOP_LOOP_FST (std::vector<T> a) HASH_TOP_LOOP_SND
};
template<typename T, size_t N> struct hash_ops<std::array<T, N>> {
static inline bool cmp(std::array<T, N> a, std::array<T, N> b) {
return a == b;
}
[[nodiscard]] static inline Hasher hash_into(std::array<T, N> a, Hasher h) {
for (const auto& k : a)
h = hash_ops<T>::hash_into(k, h);
return h;
}
HASH_TOP_LOOP_FST (std::array<T, N> a) HASH_TOP_LOOP_SND
};
struct hash_cstr_ops {
static inline bool cmp(const char *a, const char *b) {
for (int i = 0; a[i] || b[i]; i++)
if (a[i] != b[i])
return false;
return true;
return strcmp(a, b) == 0;
}
static inline unsigned int hash(const char *a) {
unsigned int hash = mkhash_init;
[[nodiscard]] static inline Hasher hash_into(const char *a, Hasher h) {
while (*a)
hash = mkhash(hash, *(a++));
return hash;
h.hash32(*(a++));
return h;
}
HASH_TOP_LOOP_FST (const char *a) HASH_TOP_LOOP_SND
};
template <> struct hash_ops<char*> : hash_cstr_ops {};
struct hash_ptr_ops {
static inline bool cmp(const void *a, const void *b) {
return a == b;
}
static inline unsigned int hash(const void *a) {
return (uintptr_t)a;
[[nodiscard]] static inline Hasher hash_into(const void *a, Hasher h) {
return hash_ops<uintptr_t>::hash_into((uintptr_t)a, h);
}
HASH_TOP_LOOP_FST (const void *a) HASH_TOP_LOOP_SND
};
struct hash_obj_ops {
@ -177,22 +259,42 @@ struct hash_obj_ops {
return a == b;
}
template<typename T>
static inline unsigned int hash(const T *a) {
return a ? a->hash() : 0;
[[nodiscard]] static inline Hasher hash_into(const T *a, Hasher h) {
if (a)
h = a->hash_into(h);
else
h.eat(0);
return h;
}
template<typename T>
HASH_TOP_LOOP_FST (const T *a) HASH_TOP_LOOP_SND
};
/**
* If you find yourself using this function, think hard
* about if it's the right thing to do. Mixing finalized
* hashes together with XORs or worse can destroy
* desirable qualities of the hash function
*/
template<typename T>
[[nodiscard]]
Hasher::hash_t run_hash(const T& obj) {
return hash_ops<T>::hash(obj).yield();
}
/** Refer to docs/source/yosys_internals/hashing.rst */
template<typename T>
[[nodiscard]]
[[deprecated]]
inline unsigned int mkhash(const T &v) {
return hash_ops<T>().hash(v);
return (unsigned int) run_hash<T>(v);
}
template<> struct hash_ops<std::monostate> {
static inline bool cmp(std::monostate a, std::monostate b) {
return a == b;
}
static inline unsigned int hash(std::monostate) {
return mkhash_init;
[[nodiscard]] static inline Hasher hash_into(std::monostate, Hasher h) {
return h;
}
};
@ -200,9 +302,10 @@ template<typename... T> struct hash_ops<std::variant<T...>> {
static inline bool cmp(std::variant<T...> a, std::variant<T...> b) {
return a == b;
}
static inline unsigned int hash(std::variant<T...> a) {
unsigned int h = std::visit([](const auto &v) { return mkhash(v); }, a);
return mkhash(a.index(), h);
[[nodiscard]] static inline Hasher hash_into(std::variant<T...> a, Hasher h) {
std::visit([& h](const auto &v) { h.eat(v); }, a);
h.eat(a.index());
return h;
}
};
@ -210,18 +313,19 @@ template<typename T> struct hash_ops<std::optional<T>> {
static inline bool cmp(std::optional<T> a, std::optional<T> b) {
return a == b;
}
static inline unsigned int hash(std::optional<T> a) {
[[nodiscard]] static inline Hasher hash_into(std::optional<T> a, Hasher h) {
if(a.has_value())
return mkhash(*a);
h.eat(*a);
else
return 0;
h.eat(0);
return h;
}
};
inline int hashtable_size(int min_size)
inline unsigned int hashtable_size(unsigned int min_size)
{
// Primes as generated by https://oeis.org/A175953
static std::vector<int> zero_and_some_primes = {
static std::vector<unsigned int> zero_and_some_primes = {
0, 23, 29, 37, 47, 59, 79, 101, 127, 163, 211, 269, 337, 431, 541, 677,
853, 1069, 1361, 1709, 2137, 2677, 3347, 4201, 5261, 6577, 8231, 10289,
12889, 16127, 20161, 25219, 31531, 39419, 49277, 61603, 77017, 96281,
@ -231,13 +335,13 @@ inline int hashtable_size(int min_size)
25499291, 31874149, 39842687, 49803361, 62254207, 77817767, 97272239,
121590311, 151987889, 189984863, 237481091, 296851369, 371064217,
463830313, 579787991, 724735009, 905918777, 1132398479, 1415498113,
1769372713
1769372713, 2211715897, 2764644887, 3455806139
};
for (auto p : zero_and_some_primes)
if (p >= min_size) return p;
if (sizeof(int) == 4)
if (sizeof(unsigned int) == 4)
throw std::length_error("hash table exceeded maximum size.\nDesign is likely too large for yosys to handle, if possible try not to flatten the design.");
for (auto p : zero_and_some_primes)
@ -252,8 +356,7 @@ template<typename K, typename OPS = hash_ops<K>> class pool;
template<typename K, typename OPS = hash_ops<K>> class mfp;
template<typename K, typename T, typename OPS>
class dict
{
class dict {
struct entry_t
{
std::pair<K, T> udata;
@ -277,11 +380,11 @@ class dict
}
#endif
int do_hash(const K &key) const
Hasher::hash_t do_hash(const K &key) const
{
unsigned int hash = 0;
Hasher::hash_t hash = 0;
if (!hashtable.empty())
hash = ops.hash(key) % (unsigned int)(hashtable.size());
hash = ops.hash(key).yield() % (unsigned int)(hashtable.size());
return hash;
}
@ -292,13 +395,13 @@ class dict
for (int i = 0; i < int(entries.size()); i++) {
do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size()));
int hash = do_hash(entries[i].udata.first);
Hasher::hash_t hash = do_hash(entries[i].udata.first);
entries[i].next = hashtable[hash];
hashtable[hash] = i;
}
}
int do_erase(int index, int hash)
int do_erase(int index, Hasher::hash_t hash)
{
do_assert(index < int(entries.size()));
if (hashtable.empty() || index < 0)
@ -321,7 +424,7 @@ class dict
if (index != back_idx)
{
int back_hash = do_hash(entries[back_idx].udata.first);
Hasher::hash_t back_hash = do_hash(entries[back_idx].udata.first);
k = hashtable[back_hash];
do_assert(0 <= k && k < int(entries.size()));
@ -347,7 +450,7 @@ class dict
return 1;
}
int do_lookup(const K &key, int &hash) const
int do_lookup(const K &key, Hasher::hash_t &hash) const
{
if (hashtable.empty())
return -1;
@ -367,7 +470,7 @@ class dict
return index;
}
int do_insert(const K &key, int &hash)
int do_insert(const K &key, Hasher::hash_t &hash)
{
if (hashtable.empty()) {
entries.emplace_back(std::pair<K, T>(key, T()), -1);
@ -380,7 +483,7 @@ class dict
return entries.size() - 1;
}
int do_insert(const std::pair<K, T> &value, int &hash)
int do_insert(const std::pair<K, T> &value, Hasher::hash_t &hash)
{
if (hashtable.empty()) {
entries.emplace_back(value, -1);
@ -393,7 +496,7 @@ class dict
return entries.size() - 1;
}
int do_insert(std::pair<K, T> &&rvalue, int &hash)
int do_insert(std::pair<K, T> &&rvalue, Hasher::hash_t &hash)
{
if (hashtable.empty()) {
auto key = rvalue.first;
@ -505,7 +608,7 @@ public:
std::pair<iterator, bool> insert(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -515,7 +618,7 @@ public:
std::pair<iterator, bool> insert(const std::pair<K, T> &value)
{
int hash = do_hash(value.first);
Hasher::hash_t hash = do_hash(value.first);
int i = do_lookup(value.first, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -525,7 +628,7 @@ public:
std::pair<iterator, bool> insert(std::pair<K, T> &&rvalue)
{
int hash = do_hash(rvalue.first);
Hasher::hash_t hash = do_hash(rvalue.first);
int i = do_lookup(rvalue.first, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -535,7 +638,7 @@ public:
std::pair<iterator, bool> emplace(K const &key, T const &value)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -545,7 +648,7 @@ public:
std::pair<iterator, bool> emplace(K const &key, T &&rvalue)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -555,7 +658,7 @@ public:
std::pair<iterator, bool> emplace(K &&rkey, T const &value)
{
int hash = do_hash(rkey);
Hasher::hash_t hash = do_hash(rkey);
int i = do_lookup(rkey, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -565,7 +668,7 @@ public:
std::pair<iterator, bool> emplace(K &&rkey, T &&rvalue)
{
int hash = do_hash(rkey);
Hasher::hash_t hash = do_hash(rkey);
int i = do_lookup(rkey, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -575,35 +678,35 @@ public:
int erase(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int index = do_lookup(key, hash);
return do_erase(index, hash);
}
iterator erase(iterator it)
{
int hash = do_hash(it->first);
Hasher::hash_t hash = do_hash(it->first);
do_erase(it.index, hash);
return ++it;
}
int count(const K &key) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
return i < 0 ? 0 : 1;
}
int count(const K &key, const_iterator it) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
return i < 0 || i > it.index ? 0 : 1;
}
iterator find(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
return end();
@ -612,7 +715,7 @@ public:
const_iterator find(const K &key) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
return end();
@ -621,7 +724,7 @@ public:
T& at(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
throw std::out_of_range("dict::at()");
@ -630,7 +733,7 @@ public:
const T& at(const K &key) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
throw std::out_of_range("dict::at()");
@ -639,7 +742,7 @@ public:
const T& at(const K &key, const T &defval) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
return defval;
@ -648,7 +751,7 @@ public:
T& operator[](const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
i = do_insert(std::pair<K, T>(key, T()), hash);
@ -683,12 +786,14 @@ public:
return !operator==(other);
}
unsigned int hash() const {
unsigned int h = mkhash_init;
for (auto &entry : entries) {
h ^= hash_ops<K>::hash(entry.udata.first);
h ^= hash_ops<T>::hash(entry.udata.second);
[[nodiscard]] Hasher hash_into(Hasher h) const {
for (auto &it : entries) {
Hasher entry_hash;
entry_hash.eat(it.udata.first);
entry_hash.eat(it.udata.second);
h.commutative_eat(entry_hash.yield());
}
h.eat(entries.size());
return h;
}
@ -734,11 +839,11 @@ protected:
}
#endif
int do_hash(const K &key) const
Hasher::hash_t do_hash(const K &key) const
{
unsigned int hash = 0;
Hasher::hash_t hash = 0;
if (!hashtable.empty())
hash = ops.hash(key) % (unsigned int)(hashtable.size());
hash = ops.hash(key).yield() % (unsigned int)(hashtable.size());
return hash;
}
@ -749,13 +854,13 @@ protected:
for (int i = 0; i < int(entries.size()); i++) {
do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size()));
int hash = do_hash(entries[i].udata);
Hasher::hash_t hash = do_hash(entries[i].udata);
entries[i].next = hashtable[hash];
hashtable[hash] = i;
}
}
int do_erase(int index, int hash)
int do_erase(int index, Hasher::hash_t hash)
{
do_assert(index < int(entries.size()));
if (hashtable.empty() || index < 0)
@ -776,7 +881,7 @@ protected:
if (index != back_idx)
{
int back_hash = do_hash(entries[back_idx].udata);
Hasher::hash_t back_hash = do_hash(entries[back_idx].udata);
k = hashtable[back_hash];
if (k == back_idx) {
@ -800,7 +905,7 @@ protected:
return 1;
}
int do_lookup(const K &key, int &hash) const
int do_lookup(const K &key, Hasher::hash_t &hash) const
{
if (hashtable.empty())
return -1;
@ -820,7 +925,7 @@ protected:
return index;
}
int do_insert(const K &value, int &hash)
int do_insert(const K &value, Hasher::hash_t &hash)
{
if (hashtable.empty()) {
entries.emplace_back(value, -1);
@ -833,7 +938,7 @@ protected:
return entries.size() - 1;
}
int do_insert(K &&rvalue, int &hash)
int do_insert(K &&rvalue, Hasher::hash_t &hash)
{
if (hashtable.empty()) {
entries.emplace_back(std::forward<K>(rvalue), -1);
@ -940,7 +1045,7 @@ public:
std::pair<iterator, bool> insert(const K &value)
{
int hash = do_hash(value);
Hasher::hash_t hash = do_hash(value);
int i = do_lookup(value, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -950,7 +1055,7 @@ public:
std::pair<iterator, bool> insert(K &&rvalue)
{
int hash = do_hash(rvalue);
Hasher::hash_t hash = do_hash(rvalue);
int i = do_lookup(rvalue, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -966,35 +1071,35 @@ public:
int erase(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int index = do_lookup(key, hash);
return do_erase(index, hash);
}
iterator erase(iterator it)
{
int hash = do_hash(*it);
Hasher::hash_t hash = do_hash(*it);
do_erase(it.index, hash);
return ++it;
}
int count(const K &key) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
return i < 0 ? 0 : 1;
}
int count(const K &key, const_iterator it) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
return i < 0 || i > it.index ? 0 : 1;
}
iterator find(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
return end();
@ -1003,7 +1108,7 @@ public:
const_iterator find(const K &key) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
return end();
@ -1012,7 +1117,7 @@ public:
bool operator[](const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
return i >= 0;
}
@ -1051,11 +1156,12 @@ public:
return !operator==(other);
}
unsigned int hash() const {
unsigned int hashval = mkhash_init;
for (auto &it : entries)
hashval ^= ops.hash(it.udata);
return hashval;
[[nodiscard]] Hasher hash_into(Hasher h) const {
for (auto &it : entries) {
h.commutative_eat(ops.hash(it.udata).yield());
}
h.eat(entries.size());
return h;
}
void reserve(size_t n) { entries.reserve(n); }
@ -1105,7 +1211,7 @@ public:
int operator()(const K &key)
{
int hash = database.do_hash(key);
Hasher::hash_t hash = database.do_hash(key);
int i = database.do_lookup(key, hash);
if (i < 0)
i = database.do_insert(key, hash);
@ -1114,7 +1220,7 @@ public:
int at(const K &key) const
{
int hash = database.do_hash(key);
Hasher::hash_t hash = database.do_hash(key);
int i = database.do_lookup(key, hash);
if (i < 0)
throw std::out_of_range("idict::at()");
@ -1123,7 +1229,7 @@ public:
int at(const K &key, int defval) const
{
int hash = database.do_hash(key);
Hasher::hash_t hash = database.do_hash(key);
int i = database.do_lookup(key, hash);
if (i < 0)
return defval;
@ -1132,7 +1238,7 @@ public:
int count(const K &key) const
{
int hash = database.do_hash(key);
Hasher::hash_t hash = database.do_hash(key);
int i = database.do_lookup(key, hash);
return i < 0 ? 0 : 1;
}
@ -1176,7 +1282,7 @@ class mfp
mutable std::vector<int> parents;
public:
typedef typename idict<K, 0, OPS>::const_iterator const_iterator;
typedef typename idict<K, 0>::const_iterator const_iterator;
constexpr mfp()
{

View file

@ -363,13 +363,13 @@ void log_dump_val_worker(RTLIL::IdString v);
void log_dump_val_worker(RTLIL::SigSpec v);
void log_dump_val_worker(RTLIL::State v);
template<typename K, typename T, typename OPS> static inline void log_dump_val_worker(dict<K, T, OPS> &v);
template<typename K, typename OPS> static inline void log_dump_val_worker(pool<K, OPS> &v);
template<typename K, typename T> static inline void log_dump_val_worker(dict<K, T> &v);
template<typename K> static inline void log_dump_val_worker(pool<K> &v);
template<typename K> static inline void log_dump_val_worker(std::vector<K> &v);
template<typename T> static inline void log_dump_val_worker(T *ptr);
template<typename K, typename T, typename OPS>
static inline void log_dump_val_worker(dict<K, T, OPS> &v) {
template<typename K, typename T>
static inline void log_dump_val_worker(dict<K, T> &v) {
log("{");
bool first = true;
for (auto &it : v) {
@ -382,8 +382,8 @@ static inline void log_dump_val_worker(dict<K, T, OPS> &v) {
log(" }");
}
template<typename K, typename OPS>
static inline void log_dump_val_worker(pool<K, OPS> &v) {
template<typename K>
static inline void log_dump_val_worker(pool<K> &v) {
log("{");
bool first = true;
for (auto &it : v) {

View file

@ -30,14 +30,11 @@ struct Macc
RTLIL::SigSpec in_a, in_b;
bool is_signed, do_subtract;
};
std::vector<port_t> ports;
RTLIL::SigSpec bit_ports;
void optimize(int width)
{
std::vector<port_t> new_ports;
RTLIL::SigSpec new_bit_ports;
RTLIL::Const off(0, width);
for (auto &port : ports)
@ -48,11 +45,6 @@ struct Macc
if (GetSize(port.in_a) < GetSize(port.in_b))
std::swap(port.in_a, port.in_b);
if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) {
bit_ports.append(port.in_a);
continue;
}
if (port.in_a.is_fully_const() && port.in_b.is_fully_const()) {
RTLIL::Const v = port.in_a.as_const();
if (GetSize(port.in_b))
@ -79,12 +71,6 @@ struct Macc
new_ports.push_back(port);
}
for (auto &bit : bit_ports)
if (bit == State::S1)
off = const_add(off, RTLIL::Const(1, width), false, false, width);
else if (bit != State::S0)
new_bit_ports.append(bit);
if (off.as_bool()) {
port_t port;
port.in_a = off;
@ -94,7 +80,6 @@ struct Macc
}
new_ports.swap(ports);
bit_ports = new_bit_ports;
}
void from_cell(RTLIL::Cell *cell)
@ -102,7 +87,6 @@ struct Macc
RTLIL::SigSpec port_a = cell->getPort(ID::A);
ports.clear();
bit_ports = cell->getPort(ID::B);
auto config_bits = cell->getParam(ID::CONFIG);
int config_cursor = 0;
@ -145,6 +129,9 @@ struct Macc
ports.push_back(this_port);
}
for (auto bit : cell->getPort(ID::B))
ports.push_back(port_t{{bit}, {}, false, false});
log_assert(config_cursor == config_width);
log_assert(port_a_cursor == GetSize(port_a));
}
@ -190,11 +177,11 @@ struct Macc
}
cell->setPort(ID::A, port_a);
cell->setPort(ID::B, bit_ports);
cell->setPort(ID::B, {});
cell->setParam(ID::CONFIG, config_bits);
cell->setParam(ID::CONFIG_WIDTH, GetSize(config_bits));
cell->setParam(ID::A_WIDTH, GetSize(port_a));
cell->setParam(ID::B_WIDTH, GetSize(bit_ports));
cell->setParam(ID::B_WIDTH, 0);
}
bool eval(RTLIL::Const &result) const
@ -219,19 +206,12 @@ struct Macc
result = const_add(result, summand, port.is_signed, port.is_signed, GetSize(result));
}
for (auto bit : bit_ports) {
if (bit.wire)
return false;
result = const_add(result, bit.data, false, false, GetSize(result));
}
return true;
}
bool is_simple_product()
{
return bit_ports.empty() &&
ports.size() == 1 &&
return ports.size() == 1 &&
!ports[0].in_b.empty() &&
!ports[0].do_subtract;
}

View file

@ -48,8 +48,11 @@ struct ModIndex : public RTLIL::Monitor
return cell == other.cell && port == other.port && offset == other.offset;
}
unsigned int hash() const {
return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(cell->name);
h.eat(port);
h.eat(offset);
return h;
}
};
@ -304,6 +307,7 @@ struct ModWalker
RTLIL::Cell *cell;
RTLIL::IdString port;
int offset;
PortBit(Cell* c, IdString p, int o) : cell(c), port(p), offset(o) {}
bool operator<(const PortBit &other) const {
if (cell != other.cell)
@ -317,8 +321,11 @@ struct ModWalker
return cell == other.cell && port == other.port && offset == other.offset;
}
unsigned int hash() const {
return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(cell->name);
h.eat(port);
h.eat(offset);
return h;
}
};
@ -355,7 +362,7 @@ struct ModWalker
{
for (int i = 0; i < int(bits.size()); i++)
if (bits[i].wire != NULL) {
PortBit pbit = { cell, port, i };
PortBit pbit {cell, port, i};
if (is_output) {
signal_drivers[bits[i]].insert(pbit);
cell_outputs[cell].insert(bits[i]);

View file

@ -1221,7 +1221,7 @@ struct LicensePass : public Pass {
log(" | |\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | |\n");
log(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Copyright (C) 2012 - 2025 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | |\n");
log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
log(" | purpose with or without fee is hereby granted, provided that the above |\n");

View file

@ -35,7 +35,7 @@ YOSYS_NAMESPACE_BEGIN
bool RTLIL::IdString::destruct_guard_ok = false;
RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard;
std::vector<char*> RTLIL::IdString::global_id_storage_;
dict<char*, int, hash_cstr_ops> RTLIL::IdString::global_id_index_;
dict<char*, int> RTLIL::IdString::global_id_index_;
#ifndef YOSYS_NO_IDS_REFCNT
std::vector<int> RTLIL::IdString::global_refcount_storage_;
std::vector<int> RTLIL::IdString::global_free_idx_list_;
@ -4476,17 +4476,17 @@ void RTLIL::SigSpec::updhash() const
cover("kernel.rtlil.sigspec.hash");
that->pack();
that->hash_ = mkhash_init;
Hasher h;
for (auto &c : that->chunks_)
if (c.wire == NULL) {
for (auto &v : c.data)
that->hash_ = mkhash(that->hash_, v);
h.eat(v);
} else {
that->hash_ = mkhash(that->hash_, c.wire->name.index_);
that->hash_ = mkhash(that->hash_, c.offset);
that->hash_ = mkhash(that->hash_, c.width);
h.eat(c.wire->name.index_);
h.eat(c.offset);
h.eat(c.width);
}
that->hash_ = h.yield();
if (that->hash_ == 0)
that->hash_ = 1;
}

View file

@ -76,329 +76,357 @@ namespace RTLIL
struct SyncRule;
struct Process;
struct Binding;
struct IdString;
typedef std::pair<SigSpec, SigSpec> SigSig;
};
struct IdString
struct RTLIL::IdString
{
#undef YOSYS_XTRACE_GET_PUT
#undef YOSYS_SORT_ID_FREE_LIST
#undef YOSYS_USE_STICKY_IDS
#undef YOSYS_NO_IDS_REFCNT
// the global id string cache
static bool destruct_guard_ok; // POD, will be initialized to zero
static struct destruct_guard_t {
destruct_guard_t() { destruct_guard_ok = true; }
~destruct_guard_t() { destruct_guard_ok = false; }
} destruct_guard;
static std::vector<char*> global_id_storage_;
static dict<char*, int> global_id_index_;
#ifndef YOSYS_NO_IDS_REFCNT
static std::vector<int> global_refcount_storage_;
static std::vector<int> global_free_idx_list_;
#endif
#ifdef YOSYS_USE_STICKY_IDS
static int last_created_idx_ptr_;
static int last_created_idx_[8];
#endif
static inline void xtrace_db_dump()
{
#undef YOSYS_XTRACE_GET_PUT
#undef YOSYS_SORT_ID_FREE_LIST
#undef YOSYS_USE_STICKY_IDS
#undef YOSYS_NO_IDS_REFCNT
// the global id string cache
static bool destruct_guard_ok; // POD, will be initialized to zero
static struct destruct_guard_t {
destruct_guard_t() { destruct_guard_ok = true; }
~destruct_guard_t() { destruct_guard_ok = false; }
} destruct_guard;
static std::vector<char*> global_id_storage_;
static dict<char*, int, hash_cstr_ops> global_id_index_;
#ifndef YOSYS_NO_IDS_REFCNT
static std::vector<int> global_refcount_storage_;
static std::vector<int> global_free_idx_list_;
#ifdef YOSYS_XTRACE_GET_PUT
for (int idx = 0; idx < GetSize(global_id_storage_); idx++)
{
if (global_id_storage_.at(idx) == nullptr)
log("#X# DB-DUMP index %d: FREE\n", idx);
else
log("#X# DB-DUMP index %d: '%s' (ref %d)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx));
}
#endif
}
static inline void checkpoint()
{
#ifdef YOSYS_USE_STICKY_IDS
static int last_created_idx_ptr_;
static int last_created_idx_[8];
last_created_idx_ptr_ = 0;
for (int i = 0; i < 8; i++) {
if (last_created_idx_[i])
put_reference(last_created_idx_[i]);
last_created_idx_[i] = 0;
}
#endif
#ifdef YOSYS_SORT_ID_FREE_LIST
std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater<int>());
#endif
}
static inline void xtrace_db_dump()
{
#ifdef YOSYS_XTRACE_GET_PUT
for (int idx = 0; idx < GetSize(global_id_storage_); idx++)
{
if (global_id_storage_.at(idx) == nullptr)
log("#X# DB-DUMP index %d: FREE\n", idx);
else
log("#X# DB-DUMP index %d: '%s' (ref %d)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx));
}
#endif
static inline int get_reference(int idx)
{
if (idx) {
#ifndef YOSYS_NO_IDS_REFCNT
global_refcount_storage_[idx]++;
#endif
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace)
log("#X# GET-BY-INDEX '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
#endif
}
return idx;
}
static int get_reference(const char *p)
{
log_assert(destruct_guard_ok);
if (!p[0])
return 0;
auto it = global_id_index_.find((char*)p);
if (it != global_id_index_.end()) {
#ifndef YOSYS_NO_IDS_REFCNT
global_refcount_storage_.at(it->second)++;
#endif
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace)
log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(it->second), it->second, global_refcount_storage_.at(it->second));
#endif
return it->second;
}
static inline void checkpoint()
{
#ifdef YOSYS_USE_STICKY_IDS
last_created_idx_ptr_ = 0;
for (int i = 0; i < 8; i++) {
if (last_created_idx_[i])
put_reference(last_created_idx_[i]);
last_created_idx_[i] = 0;
}
#endif
#ifdef YOSYS_SORT_ID_FREE_LIST
std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater<int>());
#endif
}
log_assert(p[0] == '$' || p[0] == '\\');
log_assert(p[1] != 0);
for (const char *c = p; *c; c++)
if ((unsigned)*c <= (unsigned)' ')
log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p);
static inline int get_reference(int idx)
{
if (idx) {
#ifndef YOSYS_NO_IDS_REFCNT
global_refcount_storage_[idx]++;
#endif
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace)
log("#X# GET-BY-INDEX '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
#endif
}
return idx;
}
static int get_reference(const char *p)
{
log_assert(destruct_guard_ok);
if (!p[0])
return 0;
auto it = global_id_index_.find((char*)p);
if (it != global_id_index_.end()) {
#ifndef YOSYS_NO_IDS_REFCNT
global_refcount_storage_.at(it->second)++;
#endif
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace)
log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(it->second), it->second, global_refcount_storage_.at(it->second));
#endif
return it->second;
}
log_assert(p[0] == '$' || p[0] == '\\');
log_assert(p[1] != 0);
for (const char *c = p; *c; c++)
if ((unsigned)*c <= (unsigned)' ')
log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p);
#ifndef YOSYS_NO_IDS_REFCNT
if (global_free_idx_list_.empty()) {
if (global_id_storage_.empty()) {
global_refcount_storage_.push_back(0);
global_id_storage_.push_back((char*)"");
global_id_index_[global_id_storage_.back()] = 0;
}
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();
global_id_storage_.at(idx) = strdup(p);
global_id_index_[global_id_storage_.at(idx)] = idx;
global_refcount_storage_.at(idx)++;
#else
#ifndef YOSYS_NO_IDS_REFCNT
if (global_free_idx_list_.empty()) {
if (global_id_storage_.empty()) {
global_refcount_storage_.push_back(0);
global_id_storage_.push_back((char*)"");
global_id_index_[global_id_storage_.back()] = 0;
}
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", p, idx);
log_backtrace("-X- ", yosys_xtrace-1);
}
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace)
log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\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;
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);
}
#ifndef YOSYS_NO_IDS_REFCNT
static inline void put_reference(int idx)
{
// put_reference() may be called from destructors after the destructor of
// global_refcount_storage_ has been run. in this case we simply do nothing.
if (!destruct_guard_ok || !idx)
return;
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace) {
log("#X# PUT '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
}
#endif
int &refcount = global_refcount_storage_[idx];
if (--refcount > 0)
return;
log_assert(refcount == 0);
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);
}
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);
}
int idx = global_free_idx_list_.back();
global_free_idx_list_.pop_back();
global_id_storage_.at(idx) = strdup(p);
global_id_index_[global_id_storage_.at(idx)] = idx;
global_refcount_storage_.at(idx)++;
#else
static inline void put_reference(int) { }
if (global_id_storage_.empty()) {
global_id_storage_.push_back((char*)"");
global_id_index_[global_id_storage_.back()] = 0;
}
int idx = global_id_storage_.size();
global_id_storage_.push_back(strdup(p));
global_id_index_[global_id_storage_.back()] = idx;
#endif
// the actual IdString object is just is a single int
int index_;
inline IdString() : index_(0) { }
inline IdString(const char *str) : index_(get_reference(str)) { }
inline IdString(const IdString &str) : index_(get_reference(str.index_)) { }
inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; }
inline IdString(const std::string &str) : index_(get_reference(str.c_str())) { }
inline ~IdString() { put_reference(index_); }
inline void operator=(const IdString &rhs) {
put_reference(index_);
index_ = get_reference(rhs.index_);
if (yosys_xtrace) {
log("#X# New IdString '%s' with index %d.\n", p, idx);
log_backtrace("-X- ", yosys_xtrace-1);
}
inline void operator=(const char *rhs) {
IdString id(rhs);
*this = id;
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace)
log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\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
static inline void put_reference(int idx)
{
// put_reference() may be called from destructors after the destructor of
// global_refcount_storage_ has been run. in this case we simply do nothing.
if (!destruct_guard_ok || !idx)
return;
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace) {
log("#X# PUT '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
}
#endif
int &refcount = global_refcount_storage_[idx];
if (--refcount > 0)
return;
log_assert(refcount == 0);
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);
}
inline void operator=(const std::string &rhs) {
IdString id(rhs);
*this = id;
}
inline const char *c_str() const {
return global_id_storage_.at(index_);
}
inline std::string str() const {
return std::string(global_id_storage_.at(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_; }
// The methods below are just convenience functions for better compatibility with std::string.
bool operator==(const std::string &rhs) const { return c_str() == rhs; }
bool operator!=(const std::string &rhs) const { return c_str() != rhs; }
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 {
const char *p = c_str();
#ifndef NDEBUG
for (; i != 0; i--, p++)
log_assert(*p != 0);
return *p;
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
return *(p + i);
static inline void put_reference(int) { }
#endif
// the actual IdString object is just is a single int
int index_;
inline IdString() : index_(0) { }
inline IdString(const char *str) : index_(get_reference(str)) { }
inline IdString(const IdString &str) : index_(get_reference(str.index_)) { }
inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; }
inline IdString(const std::string &str) : index_(get_reference(str.c_str())) { }
inline ~IdString() { put_reference(index_); }
inline void operator=(const IdString &rhs) {
put_reference(index_);
index_ = get_reference(rhs.index_);
}
inline void operator=(const char *rhs) {
IdString id(rhs);
*this = id;
}
inline void operator=(const std::string &rhs) {
IdString id(rhs);
*this = id;
}
inline const char *c_str() const {
return global_id_storage_.at(index_);
}
inline std::string str() const {
return std::string(global_id_storage_.at(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_; }
// The methods below are just convenience functions for better compatibility with std::string.
bool operator==(const std::string &rhs) const { return c_str() == rhs; }
bool operator!=(const std::string &rhs) const { return c_str() != rhs; }
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 {
const char *p = c_str();
#ifndef NDEBUG
for (; i != 0; i--, p++)
log_assert(*p != 0);
return *p;
#else
return *(p + i);
#endif
}
std::string substr(size_t pos = 0, size_t len = std::string::npos) const {
if (len == std::string::npos || len >= strlen(c_str() + pos))
return std::string(c_str() + pos);
else
return std::string(c_str() + pos, len);
}
int compare(size_t pos, size_t len, const char* s) const {
return strncmp(c_str()+pos, s, len);
}
bool begins_with(const char* prefix) const {
size_t len = strlen(prefix);
if (size() < len) return false;
return compare(0, len, prefix) == 0;
}
bool ends_with(const char* suffix) const {
size_t len = strlen(suffix);
if (size() < len) return false;
return compare(size()-len, len, suffix) == 0;
}
bool contains(const char* str) const {
return strstr(c_str(), str);
}
size_t size() const {
return strlen(c_str());
}
bool empty() const {
return c_str()[0] == 0;
}
void clear() {
*this = IdString();
}
[[nodiscard]] Hasher hash_into(Hasher h) const { return hash_ops<int>::hash_into(index_, h); }
[[nodiscard]] Hasher hash_top() const {
Hasher h;
h.force((Hasher::hash_t) index_);
return h;
}
// The following is a helper key_compare class. Instead of for example std::set<Cell*>
// use std::set<Cell*, IdString::compare_ptr_by_name<Cell>> if the order of cells in the
// set has an influence on the algorithm.
template<typename T> struct compare_ptr_by_name {
bool operator()(const T *a, const T *b) const {
return (a == nullptr || b == nullptr) ? (a < b) : (a->name < b->name);
}
std::string substr(size_t pos = 0, size_t len = std::string::npos) const {
if (len == std::string::npos || len >= strlen(c_str() + pos))
return std::string(c_str() + pos);
else
return std::string(c_str() + pos, len);
}
int compare(size_t pos, size_t len, const char* s) const {
return strncmp(c_str()+pos, s, len);
}
bool begins_with(const char* prefix) const {
size_t len = strlen(prefix);
if (size() < len) return false;
return compare(0, len, prefix) == 0;
}
bool ends_with(const char* suffix) const {
size_t len = strlen(suffix);
if (size() < len) return false;
return compare(size()-len, len, suffix) == 0;
}
bool contains(const char* str) const {
return strstr(c_str(), str);
}
size_t size() const {
return strlen(c_str());
}
bool empty() const {
return c_str()[0] == 0;
}
void clear() {
*this = IdString();
}
unsigned int hash() const {
return index_;
}
// The following is a helper key_compare class. Instead of for example std::set<Cell*>
// use std::set<Cell*, IdString::compare_ptr_by_name<Cell>> if the order of cells in the
// set has an influence on the algorithm.
template<typename T> struct compare_ptr_by_name {
bool operator()(const T *a, const T *b) const {
return (a == nullptr || b == nullptr) ? (a < b) : (a->name < b->name);
}
};
// often one needs to check if a given IdString is part of a list (for example a list
// of cell types). the following functions helps with that.
template<typename... Args>
bool in(Args... args) const {
// Credit: https://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html
bool result = false;
(void) std::initializer_list<int>{ (result = result || in(args), 0)... };
return result;
}
bool in(const IdString &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 pool<IdString> &rhs) const { return rhs.count(*this) != 0; }
bool isPublic() const { return begins_with("\\"); }
};
// often one needs to check if a given IdString is part of a list (for example a list
// of cell types). the following functions helps with that.
template<typename... Args>
bool in(Args... args) const {
return (... || in(args));
}
bool in(const IdString &rhs) const { return *this == rhs; }
bool in(const char *rhs) const { return *this == rhs; }
bool in(const std::string &rhs) const { return *this == rhs; }
inline bool in(const pool<IdString> &rhs) const;
inline bool in(const pool<IdString> &&rhs) const;
bool isPublic() const { return begins_with("\\"); }
};
namespace hashlib {
template <>
struct hash_ops<RTLIL::IdString> {
static inline bool cmp(const RTLIL::IdString &a, const RTLIL::IdString &b) {
return a == b;
}
[[nodiscard]] static inline Hasher hash(const RTLIL::IdString id) {
return id.hash_top();
}
[[nodiscard]] static inline Hasher hash_into(const RTLIL::IdString id, Hasher h) {
return id.hash_into(h);
}
};
};
/**
* How to not use these methods:
* 1. if(celltype.in({...})) -> if(celltype.in(...))
* 2. pool<IdString> p; ... a.in(p) -> (bool)p.count(a)
*/
[[deprecated]]
inline bool RTLIL::IdString::in(const pool<IdString> &rhs) const { return rhs.count(*this) != 0; }
[[deprecated]]
inline bool RTLIL::IdString::in(const pool<IdString> &&rhs) const { return rhs.count(*this) != 0; }
namespace RTLIL {
namespace ID {
#define X(_id) extern IdString _id;
#include "kernel/constids.inc"
#undef X
};
extern dict<std::string, std::string> constpad;
const pool<IdString> &builtin_ff_cell_types();
@ -796,11 +824,10 @@ public:
bv.resize(width, bv.empty() ? RTLIL::State::Sx : bv.back());
}
inline unsigned int hash() const {
unsigned int h = mkhash_init;
for (State b : *this)
h = mkhash(h, b);
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(size());
for (auto b : *this)
h.eat(b);
return h;
}
};
@ -890,7 +917,23 @@ struct RTLIL::SigBit
bool operator <(const RTLIL::SigBit &other) const;
bool operator ==(const RTLIL::SigBit &other) const;
bool operator !=(const RTLIL::SigBit &other) const;
unsigned int hash() const;
[[nodiscard]] Hasher hash_into(Hasher h) const;
[[nodiscard]] Hasher hash_top() const;
};
namespace hashlib {
template <>
struct hash_ops<RTLIL::SigBit> {
static inline bool cmp(const RTLIL::SigBit &a, const RTLIL::SigBit &b) {
return a == b;
}
[[nodiscard]] static inline Hasher hash(const RTLIL::SigBit sb) {
return sb.hash_top();
}
[[nodiscard]] static inline Hasher hash_into(const RTLIL::SigBit sb, Hasher h) {
return sb.hash_into(h);
}
};
};
struct RTLIL::SigSpecIterator
@ -931,7 +974,7 @@ struct RTLIL::SigSpec
{
private:
int width_;
unsigned long hash_;
Hasher::hash_t hash_;
std::vector<RTLIL::SigChunk> chunks_; // LSB at index 0
std::vector<RTLIL::SigBit> bits_; // LSB at index 0
@ -972,11 +1015,6 @@ public:
SigSpec(const std::set<RTLIL::SigBit> &bits);
explicit SigSpec(bool bit);
size_t get_hash() const {
if (!hash_) hash();
return hash_;
}
inline const std::vector<RTLIL::SigChunk> &chunks() const { pack(); return chunks_; }
inline const std::vector<RTLIL::SigBit> &bits() const { inline_unpack(); return bits_; }
@ -1083,7 +1121,7 @@ public:
operator std::vector<RTLIL::SigBit>() const { return bits(); }
const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < width_ ? (*this)[offset] : defval; }
unsigned int hash() const { if (!hash_) updhash(); return hash_; };
[[nodiscard]] Hasher hash_into(Hasher h) const { if (!hash_) updhash(); h.eat(hash_); return h; }
#ifndef NDEBUG
void check(Module *mod = nullptr) const;
@ -1124,8 +1162,8 @@ struct RTLIL::Selection
struct RTLIL::Monitor
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
Monitor() {
static unsigned int hashidx_count = 123456789;
@ -1147,8 +1185,8 @@ struct define_map_t;
struct RTLIL::Design
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
pool<RTLIL::Monitor*> monitors;
dict<std::string, std::string> scratchpad;
@ -1252,8 +1290,8 @@ struct RTLIL::Design
struct RTLIL::Module : public RTLIL::AttrObject
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected:
void add(RTLIL::Wire *wire);
@ -1607,8 +1645,8 @@ void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire);
struct RTLIL::Wire : public RTLIL::AttrObject
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected:
// use module->addWire() and module->remove() to create or destroy wires
@ -1646,8 +1684,8 @@ inline int GetSize(RTLIL::Wire *wire) {
struct RTLIL::Memory : public RTLIL::AttrObject
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
Memory();
@ -1661,8 +1699,8 @@ struct RTLIL::Memory : public RTLIL::AttrObject
struct RTLIL::Cell : public RTLIL::AttrObject
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected:
// use module->addCell() and module->remove() to create or destroy cells
@ -1771,8 +1809,8 @@ struct RTLIL::SyncRule
struct RTLIL::Process : public RTLIL::AttrObject
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected:
// use module->addProcess() and module->remove() to create or destroy processes
@ -1816,10 +1854,25 @@ inline bool RTLIL::SigBit::operator!=(const RTLIL::SigBit &other) const {
return (wire != other.wire) || (wire ? (offset != other.offset) : (data != other.data));
}
inline unsigned int RTLIL::SigBit::hash() const {
if (wire)
return mkhash_add(wire->name.hash(), offset);
return data;
inline Hasher RTLIL::SigBit::hash_into(Hasher h) const {
if (wire) {
h.eat(offset);
h.eat(wire->name);
return h;
}
h.eat(data);
return h;
}
inline Hasher RTLIL::SigBit::hash_top() const {
Hasher h;
if (wire) {
h.force(hashlib::legacy::djb2_add(wire->name.index_, offset));
return h;
}
h.force(data);
return h;
}
inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const {

View file

@ -743,7 +743,6 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
if (cell->type == ID($macc))
{
std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
Macc macc;
@ -785,12 +784,6 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
}
}
for (int i = 0; i < GetSize(b); i++) {
std::vector<int> val(GetSize(y), ez->CONST_FALSE);
val.at(0) = b.at(i);
tmp = ez->vec_add(tmp, val);
}
if (model_undef)
{
std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);

View file

@ -169,8 +169,11 @@ public:
return !(*this == other);
}
int hash() const {
return mkhash(scope_name.hash(), hash_ptr_ops::hash(target));
[[nodiscard]] Hasher hash_into(Hasher h) const
{
h.eat(scope_name);
h.eat(target);
return h;
}
bool valid() const {
@ -322,7 +325,7 @@ struct ModuleItem {
Cell *cell() const { return type == Type::Cell ? static_cast<Cell *>(ptr) : nullptr; }
bool operator==(const ModuleItem &other) const { return ptr == other.ptr && type == other.type; }
unsigned int hash() const { return (uintptr_t)ptr; }
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(ptr); return h; }
};
static inline void log_dump_val_worker(typename IdTree<ModuleItem>::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); }
@ -334,12 +337,14 @@ template<typename O>
std::vector<IdString> parse_hdlname(const O* object)
{
std::vector<IdString> path;
if (!object->name.isPublic())
return path;
for (auto const &item : object->get_hdlname_attribute())
path.push_back("\\" + item);
if (path.empty())
if (path.empty() && object->name.isPublic())
path.push_back(object->name);
if (!path.empty() && !(object->name.isPublic() || object->name.begins_with("$paramod") || object->name.begins_with("$abstract"))) {
path.pop_back();
path.push_back(object->name);
}
return path;
}
@ -348,17 +353,22 @@ std::pair<std::vector<IdString>, IdString> parse_scopename(const O* object)
{
std::vector<IdString> path;
IdString trailing = object->name;
if (object->name.isPublic()) {
if (object->name.isPublic() || object->name.begins_with("$paramod") || object->name.begins_with("$abstract")) {
for (auto const &item : object->get_hdlname_attribute())
path.push_back("\\" + item);
if (!path.empty()) {
trailing = path.back();
path.pop_back();
}
} else if (object->has_attribute(ID::hdlname)) {
for (auto const &item : object->get_hdlname_attribute())
path.push_back("\\" + item);
if (!path.empty()) {
path.pop_back();
}
} else {
for (auto const &item : split_tokens(object->get_string_attribute(ID(scopename)), " "))
path.push_back("\\" + item);
}
return {path, trailing};
}

View file

@ -29,7 +29,11 @@ struct SigPool
struct bitDef_t : public std::pair<RTLIL::Wire*, int> {
bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { }
bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { }
unsigned int hash() const { return first->name.hash() + second; }
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(first->name);
h.eat(second);
return h;
}
};
pool<bitDef_t> bits;
@ -143,7 +147,11 @@ struct SigSet
struct bitDef_t : public std::pair<RTLIL::Wire*, int> {
bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { }
bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { }
unsigned int hash() const { return first->name.hash() + second; }
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(first->name);
h.eat(second);
return h;
}
};
dict<bitDef_t, std::set<T, Compare>> bits;

View file

@ -36,7 +36,6 @@ struct TimingInfo
explicit NameBit(const RTLIL::SigBit &b) : name(b.wire->name), offset(b.offset) {}
bool operator==(const NameBit& nb) const { return nb.name == name && nb.offset == offset; }
bool operator!=(const NameBit& nb) const { return !operator==(nb); }
unsigned int hash() const { return mkhash_add(name.hash(), offset); }
std::optional<SigBit> get_connection(RTLIL::Cell *cell) {
if (!cell->hasPort(name))
return {};
@ -45,6 +44,11 @@ struct TimingInfo
return {};
return port[offset];
}
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(name);
h.eat(offset);
return h;
}
};
struct BitBit
{
@ -52,7 +56,11 @@ struct TimingInfo
BitBit(const NameBit &first, const NameBit &second) : first(first), second(second) {}
BitBit(const SigBit &first, const SigBit &second) : first(first), second(second) {}
bool operator==(const BitBit& bb) const { return bb.first == first && bb.second == second; }
unsigned int hash() const { return mkhash_add(first.hash(), second.hash()); }
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(first);
h.eat(second);
return h;
}
};
struct ModuleTiming

View file

@ -31,17 +31,17 @@ YOSYS_NAMESPACE_BEGIN
// A map-like container, but you can save and restore the state
// ------------------------------------------------
template<typename Key, typename T, typename OPS = hash_ops<Key>>
template<typename Key, typename T>
struct stackmap
{
private:
std::vector<dict<Key, T*, OPS>> backup_state;
dict<Key, T, OPS> current_state;
std::vector<dict<Key, T*>> backup_state;
dict<Key, T> current_state;
static T empty_tuple;
public:
stackmap() { }
stackmap(const dict<Key, T, OPS> &other) : current_state(other) { }
stackmap(const dict<Key, T> &other) : current_state(other) { }
template<typename Other>
void operator=(const Other &other)
@ -94,7 +94,7 @@ public:
current_state.erase(k);
}
const dict<Key, T, OPS> &stdmap()
const dict<Key, T> &stdmap()
{
return current_state;
}
@ -128,7 +128,7 @@ public:
// A simple class for topological sorting
// ------------------------------------------------
template <typename T, typename C = std::less<T>, typename OPS = hash_ops<T>> class TopoSort
template <typename T, typename C = std::less<T>> class TopoSort
{
public:
// We use this ordering of the edges in the adjacency matrix for

View file

@ -93,6 +93,7 @@ std::set<std::string> yosys_input_files, yosys_output_files;
bool memhasher_active = false;
uint32_t memhasher_rng = 123456;
std::vector<void*> memhasher_store;
uint32_t Hasher::fudge = 0;
std::string yosys_share_dirname;
std::string yosys_abc_executable;
@ -140,7 +141,7 @@ void yosys_banner()
log("\n");
log(" /----------------------------------------------------------------------------\\\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Copyright (C) 2012 - 2025 Claire Xenia Wolf <claire@yosyshq.com> |\n");
log(" | Distributed under an ISC-like license, type \"license\" to see terms |\n");
log(" \\----------------------------------------------------------------------------/\n");
log(" %s\n", yosys_version_str);

View file

@ -134,8 +134,7 @@ YOSYS_NAMESPACE_BEGIN
// Note: All headers included in hashlib.h must be included
// outside of YOSYS_NAMESPACE before this or bad things will happen.
#ifdef HASHLIB_H
# undef HASHLIB_H
# include "kernel/hashlib.h"
# error "You've probably included hashlib.h under two namespace paths. Bad idea."
#else
# include "kernel/hashlib.h"
# undef HASHLIB_H
@ -153,6 +152,15 @@ using std::get;
using std::min;
using std::max;
using hashlib::Hasher;
using hashlib::run_hash;
using hashlib::hash_ops;
using hashlib::mkhash_xorshift;
using hashlib::dict;
using hashlib::idict;
using hashlib::pool;
using hashlib::mfp;
// A primitive shared string implementation that does not
// move its .c_str() when the object is copied or moved.
struct shared_str {
@ -163,22 +171,12 @@ struct shared_str {
const char *c_str() const { return content->c_str(); }
const string &str() const { return *content; }
bool operator==(const shared_str &other) const { return *content == *other.content; }
unsigned int hash() const { return hashlib::hash_ops<std::string>::hash(*content); }
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(*content);
return h;
}
};
using hashlib::mkhash;
using hashlib::mkhash_init;
using hashlib::mkhash_add;
using hashlib::mkhash_xorshift;
using hashlib::hash_ops;
using hashlib::hash_cstr_ops;
using hashlib::hash_ptr_ops;
using hashlib::hash_obj_ops;
using hashlib::dict;
using hashlib::idict;
using hashlib::pool;
using hashlib::mfp;
namespace RTLIL {
struct IdString;
struct Const;
@ -347,10 +345,6 @@ RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std:
static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); return id; })()
namespace ID = RTLIL::ID;
namespace hashlib {
template<> struct hash_ops<RTLIL::State> : hash_ops<int> {};
}
YOSYS_NAMESPACE_END

View file

@ -35,7 +35,10 @@ struct IdPath : public std::vector<RTLIL::IdString>
bool has_address() const { int tmp; return get_address(tmp); };
bool get_address(int &addr) const;
int hash() const { return hashlib::hash_ops<std::vector<RTLIL::IdString>>::hash(*this); }
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(static_cast<const std::vector<RTLIL::IdString>&&>(*this));
return h;
}
};
struct WitnessHierarchyItem {

View file

@ -24,6 +24,7 @@
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <limits>
namespace json11 {

View file

@ -855,7 +855,11 @@ class WClass:
if self.hash_id != None:
text += "\n\t\tunsigned int get_hash_py()"
text += "\n\t\t{"
text += "\n\t\t\treturn get_cpp_obj()->" + self.hash_id + ";"
suffix = f"->{self.hash_id}" if self.hash_id else f"->{self.hash_id}"
if self.hash_id == "":
text += f"\n\t\t\treturn run_hash(*(get_cpp_obj()));"
else:
text += f"\n\t\t\treturn run_hash(get_cpp_obj()->{self.hash_id});"
text += "\n\t\t}"
text += "\n\t};\n"
@ -956,7 +960,7 @@ class WClass:
sources = [
Source("kernel/celltypes",[
WClass("CellType", link_types.pointer, None, None, "type.hash()", True),
WClass("CellType", link_types.pointer, None, None, "type", True),
WClass("CellTypes", link_types.pointer, None, None, None, True)
]
),
@ -970,23 +974,23 @@ sources = [
]
),
Source("kernel/rtlil",[
WClass("IdString", link_types.ref_copy, None, "str()", "hash()"),
WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"),
WClass("IdString", link_types.ref_copy, None, "str()", ""),
WClass("Const", link_types.ref_copy, None, "as_string()", ""),
WClass("AttrObject", link_types.ref_copy, None, None, None),
WClass("Selection", link_types.ref_copy, None, None, None),
WClass("Monitor", link_types.derive, None, None, None),
WClass("CaseRule",link_types.ref_copy, None, None, None, True),
WClass("SwitchRule",link_types.ref_copy, None, None, None, True),
WClass("SyncRule", link_types.ref_copy, None, None, None, True),
WClass("Process", link_types.ref_copy, None, "name.c_str()", "name.hash()"),
WClass("Process", link_types.ref_copy, None, "name.c_str()", "name"),
WClass("SigChunk", link_types.ref_copy, None, None, None),
WClass("SigBit", link_types.ref_copy, None, None, "hash()"),
WClass("SigSpec", link_types.ref_copy, None, None, "hash()"),
WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "hash()")
WClass("SigBit", link_types.ref_copy, None, None, ""),
WClass("SigSpec", link_types.ref_copy, None, None, ""),
WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "")
]
),
#Source("kernel/satgen",[

View file

@ -47,7 +47,7 @@ struct DftTagWorker {
bool operator<(const tag_set &other) const { return index < other.index; }
bool operator==(const tag_set &other) const { return index == other.index; }
unsigned int hash() const { return hash_ops<int>::hash(index); }
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(index); return h; }
bool empty() const { return index == 0; }
};

View file

@ -52,8 +52,10 @@ struct ExampleDtPass : public Pass
return name == other.name && parameters == other.parameters;
}
unsigned int hash() const {
return mkhash(name.hash(), parameters.hash());
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(name);
h.eat(parameters);
return h;
}
};

View file

@ -20,7 +20,6 @@
#include "kernel/register.h"
#include "kernel/rtlil.h"
#include "kernel/log.h"
#include "kernel/hashlib.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

View file

@ -70,13 +70,13 @@ struct GraphNode {
pool<IdString> names_;
dict<int, uint8_t> tags_;
pool<GraphNode*, hash_ptr_ops> upstream_;
pool<GraphNode*, hash_ptr_ops> downstream_;
pool<GraphNode*> upstream_;
pool<GraphNode*> downstream_;
pool<IdString> &names() { return get()->names_; }
dict<int, uint8_t> &tags() { return get()->tags_; }
pool<GraphNode*, hash_ptr_ops> &upstream() { return get()->upstream_; }
pool<GraphNode*, hash_ptr_ops> &downstream() { return get()->downstream_; }
pool<GraphNode*> &upstream() { return get()->upstream_; }
pool<GraphNode*> &downstream() { return get()->downstream_; }
uint8_t tag(int index) {
return tags().at(index, 0);
@ -154,8 +154,8 @@ struct Graph {
nodes.push_back(n);
n->index = GetSize(nodes);
pool<GraphNode*, hash_ptr_ops> new_upstream;
pool<GraphNode*, hash_ptr_ops> new_downstream;
pool<GraphNode*> new_upstream;
pool<GraphNode*> new_downstream;
for (auto g : n->upstream()) {
if (n != (g = g->get()))
@ -302,7 +302,7 @@ struct Graph {
}
}
pool<GraphNode*, hash_ptr_ops> excluded;
pool<GraphNode*> excluded;
for (auto grp : config.groups)
{
@ -348,7 +348,7 @@ struct Graph {
excluded.insert(g->get());
dict<Cell*, GraphNode*> cell_nodes;
dict<SigBit, pool<GraphNode*, hash_ptr_ops>> sig_users;
dict<SigBit, pool<GraphNode*>> sig_users;
for (auto cell : module->selected_cells()) {
auto g = new GraphNode;
@ -483,8 +483,8 @@ struct Graph {
{
header("Any nodes with identical connections");
typedef pair<pool<GraphNode*, hash_ptr_ops>, pool<GraphNode*, hash_ptr_ops>> node_conn_t;
dict<node_conn_t, pool<GraphNode*, hash_ptr_ops>> nodes_by_conn;
typedef pair<pool<GraphNode*>, pool<GraphNode*>> node_conn_t;
dict<node_conn_t, pool<GraphNode*>> nodes_by_conn;
for (auto g : term ? term_nodes : nonterm_nodes) {
auto &entry = nodes_by_conn[node_conn_t(g->upstream(), g->downstream())];
for (auto n : entry)
@ -506,8 +506,8 @@ struct Graph {
header("Sibblings with identical tags");
for (auto g : nonterm_nodes) {
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) {
dict<std::vector<int>, pool<GraphNode*, hash_ptr_ops>> nodes_by_tags;
auto process_conns = [&](const pool<GraphNode*> &stream) {
dict<std::vector<int>, pool<GraphNode*>> nodes_by_tags;
for (auto n : stream) {
if (n->terminal) continue;
std::vector<int> key;
@ -556,7 +556,7 @@ struct Graph {
if (!term) {
header("Sibblings with similar tags (strict)");
for (auto g : nonterm_nodes) {
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) {
auto process_conns = [&](const pool<GraphNode*> &stream) {
std::vector<GraphNode*> nodes;
for (auto n : stream)
if (!n->terminal) nodes.push_back(n);
@ -585,7 +585,7 @@ struct Graph {
if (!term) {
header("Sibblings with similar tags (non-strict)");
for (auto g : nonterm_nodes) {
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) {
auto process_conns = [&](const pool<GraphNode*> &stream) {
std::vector<GraphNode*> nodes;
for (auto n : stream)
if (!n->terminal) nodes.push_back(n);
@ -603,7 +603,7 @@ struct Graph {
{
header("Any nodes with identical fan-in or fan-out");
dict<pool<GraphNode*, hash_ptr_ops>, pool<GraphNode*, hash_ptr_ops>> nodes_by_conn[2];
dict<pool<GraphNode*>, pool<GraphNode*>> nodes_by_conn[2];
for (auto g : term ? term_nodes : nonterm_nodes) {
auto &up_entry = nodes_by_conn[0][g->upstream()];
auto &down_entry = nodes_by_conn[1][g->downstream()];
@ -629,7 +629,7 @@ struct Graph {
if (!term) {
header("Sibblings with similar tags (lax)");
for (auto g : nonterm_nodes) {
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) {
auto process_conns = [&](const pool<GraphNode*> &stream) {
std::vector<GraphNode*> nodes;
for (auto n : stream)
if (!n->terminal) nodes.push_back(n);
@ -720,9 +720,9 @@ struct VizWorker
fprintf(f, "digraph \"%s\" {\n", log_id(module));
fprintf(f, " rankdir = LR;\n");
dict<GraphNode*, std::vector<std::vector<std::string>>, hash_ptr_ops> extra_lines;
dict<GraphNode*, GraphNode*, hash_ptr_ops> bypass_nodes;
pool<GraphNode*, hash_ptr_ops> bypass_candidates;
dict<GraphNode*, std::vector<std::vector<std::string>>> extra_lines;
dict<GraphNode*, GraphNode*> bypass_nodes;
pool<GraphNode*> bypass_candidates;
auto bypass = [&](GraphNode *g, GraphNode *n) {
log_assert(g->terminal);

View file

@ -46,11 +46,11 @@ struct EquivStructWorker
parameters == other.parameters && port_sizes == other.port_sizes;
}
unsigned int hash() const {
unsigned int h = mkhash_init;
h = mkhash(h, mkhash(type));
h = mkhash(h, mkhash(parameters));
h = mkhash(h, mkhash(connections));
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(type);
h.eat(parameters);
h.eat(port_sizes);
h.eat(connections);
return h;
}
};

View file

@ -377,6 +377,13 @@ static void extract_fsm(RTLIL::Wire *wire)
fsm_cell->setPort(ID::CTRL_OUT, ctrl_out);
fsm_cell->parameters[ID::NAME] = RTLIL::Const(wire->name.str());
fsm_cell->attributes = wire->attributes;
if(fsm_cell->attributes.count(ID::hdlname)) {
auto hdlname = fsm_cell->get_hdlname_attribute();
hdlname.pop_back();
fsm_cell->set_hdlname_attribute(hdlname);
fsm_cell->set_string_attribute(ID(scopename), fsm_cell->get_string_attribute(ID::hdlname));
fsm_cell->attributes.erase(ID::hdlname);
}
fsm_data.copy_to_cell(fsm_cell);
// rename original state wire
@ -385,6 +392,13 @@ static void extract_fsm(RTLIL::Wire *wire)
wire->attributes.erase(ID::fsm_encoding);
wire->name = stringf("$fsm$oldstate%s", wire->name.c_str());
module->wires_[wire->name] = wire;
if(wire->attributes.count(ID::hdlname)) {
auto hdlname = wire->get_hdlname_attribute();
hdlname.pop_back();
wire->set_hdlname_attribute(hdlname);
wire->set_string_attribute(ID(scopename), wire->get_string_attribute(ID::hdlname));
wire->attributes.erase(ID::hdlname);
}
// unconnect control outputs from old drivers

View file

@ -17,6 +17,7 @@
*
*/
#include <inttypes.h>
#include "kernel/yosys.h"
#include "kernel/cost.h"
@ -66,7 +67,7 @@ struct ThresholdHierarchyKeeping {
}
if (size > threshold) {
log("Keeping %s (estimated size above threshold: %llu > %llu).\n", log_id(module), size, threshold);
log("Keeping %s (estimated size above threshold: %" PRIu64 " > %" PRIu64 ").\n", log_id(module), size, threshold);
module->set_bool_attribute(ID::keep_hierarchy);
size = 0;
}

View file

@ -117,7 +117,7 @@ struct ShareWorker
static int bits_macc(const Macc &m, int width)
{
int bits = GetSize(m.bit_ports);
int bits = 0;
for (auto &p : m.ports)
bits += bits_macc_port(p, width);
return bits;
@ -1255,7 +1255,6 @@ struct ShareWorker
qcsat.max_cell_count = 100;
}
pool<RTLIL::Cell*> sat_cells;
std::set<RTLIL::SigBit> bits_queue;
std::vector<int> cell_active, other_cell_active;
@ -1298,8 +1297,8 @@ struct ShareWorker
qcsat.ez->assume(qcsat.ez->AND(sub1, sub2));
log(" Size of SAT problem: %d cells, %d variables, %d clauses\n",
GetSize(sat_cells), qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses());
log(" Size of SAT problem: %d variables, %d clauses\n",
qcsat.ez->numCnfVariables(), qcsat.ez->numCnfClauses());
if (qcsat.ez->solve(sat_model, sat_model_values)) {
log(" According to the SAT solver this pair of cells can not be shared.\n");

View file

@ -263,6 +263,19 @@ struct WreduceWorker
}
}
int reduced_opsize(const SigSpec &inp, bool signed_)
{
int size = GetSize(inp);
if (signed_) {
while (size >= 2 && inp[size - 1] == inp[size - 2])
size--;
} else {
while (size >= 1 && inp[size - 1] == State::S0)
size--;
}
return size;
}
void run_cell(Cell *cell)
{
bool did_something = false;
@ -295,6 +308,45 @@ struct WreduceWorker
bool port_a_signed = false;
bool port_b_signed = false;
// For some operations if the output is no wider than either of the inputs
// we are free to choose the signedness of the operands
if (cell->type.in(ID($mul), ID($add), ID($sub)) &&
max_port_a_size == GetSize(sig) &&
max_port_b_size == GetSize(sig)) {
SigSpec sig_a = mi.sigmap(cell->getPort(ID::A)), sig_b = mi.sigmap(cell->getPort(ID::B));
// Remove top bits from sig_a and sig_b which are not visible on the output
sig_a.extend_u0(max_port_a_size);
sig_b.extend_u0(max_port_b_size);
int signed_cost, unsigned_cost;
if (cell->type == ID($mul)) {
signed_cost = reduced_opsize(sig_a, true) * reduced_opsize(sig_b, true);
unsigned_cost = reduced_opsize(sig_a, false) * reduced_opsize(sig_b, false);
} else {
signed_cost = max(reduced_opsize(sig_a, true), reduced_opsize(sig_b, true));
unsigned_cost = max(reduced_opsize(sig_a, false), reduced_opsize(sig_b, false));
}
if (!port_a_signed && !port_b_signed && signed_cost < unsigned_cost) {
log("Converting cell %s.%s (%s) from unsigned to signed.\n",
log_id(module), log_id(cell), log_id(cell->type));
cell->setParam(ID::A_SIGNED, 1);
cell->setParam(ID::B_SIGNED, 1);
port_a_signed = true;
port_b_signed = true;
did_something = true;
} else if (port_a_signed && port_b_signed && unsigned_cost < signed_cost) {
log("Converting cell %s.%s (%s) from signed to unsigned.\n",
log_id(module), log_id(cell), log_id(cell->type));
cell->setParam(ID::A_SIGNED, 0);
cell->setParam(ID::B_SIGNED, 0);
port_a_signed = false;
port_b_signed = false;
did_something = true;
}
}
if (max_port_a_size >= 0 && cell->type != ID($shiftx))
run_reduce_inport(cell, 'A', max_port_a_size, port_a_signed, did_something);

View file

@ -127,11 +127,10 @@ struct proc_dlatch_db_t
return signal == other.signal && match == other.match && children == other.children;
}
unsigned int hash() const {
unsigned int h = mkhash_init;
mkhash(h, signal.hash());
mkhash(h, match.hash());
for (auto i : children) mkhash(h, i);
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(signal);
h.eat(match);
h.eat(children);
return h;
}
};

View file

@ -108,8 +108,8 @@ struct SigSnippets
struct SnippetSwCache
{
dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>, hash_ptr_ops> full_case_bits_cache;
dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache;
dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>> full_case_bits_cache;
dict<RTLIL::SwitchRule*, pool<int>> cache;
const SigSnippets *snippets;
int current_snippet;
@ -318,7 +318,7 @@ const pool<SigBit> &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRul
return swcache.full_case_bits_cache.at(sw);
}
RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara,
RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool> &swpara,
RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode)
{
RTLIL::SigSpec result = defval;
@ -421,7 +421,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode)
swcache.snippets = &sigsnip;
swcache.insert(&proc->root_case);
dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara;
dict<RTLIL::SwitchRule*, bool> swpara;
int cnt = 0;
for (int idx : sigsnip.snippets)

View file

@ -176,7 +176,7 @@ struct coverdb_t
struct mutate_queue_t
{
pool<mutate_t*, hash_ptr_ops> db;
pool<mutate_t*> db;
mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
mutate_t *m = nullptr;

View file

@ -46,9 +46,11 @@ struct IdBit {
bool operator==(const IdBit &other) const { return name == other.name && bit == other.bit; };
bool operator!=(const IdBit &other) const { return name != other.name || bit != other.bit; };
unsigned hash() const
[[nodiscard]] Hasher hash_into(Hasher h) const
{
return mkhash_add(name.hash(), bit);
h.eat(name);
h.eat(bit);
return h;
}
IdString name;
@ -62,9 +64,11 @@ struct InvBit {
bool operator==(const InvBit &other) const { return bit == other.bit && inverted == other.inverted; };
bool operator!=(const InvBit &other) const { return bit != other.bit || inverted != other.inverted; };
unsigned hash() const
[[nodiscard]] Hasher hash_into(Hasher h) const
{
return mkhash(bit.hash(), inverted);
h.eat(bit);
h.eat(inverted);
return h;
}
IdBit bit;

View file

@ -161,7 +161,7 @@ struct SimInstance
pool<SigBit> dirty_bits;
pool<Cell*> dirty_cells;
pool<IdString> dirty_memories;
pool<SimInstance*, hash_ptr_ops> dirty_children;
pool<SimInstance*> dirty_children;
struct ff_state_t
{

View file

@ -1411,6 +1411,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
module->connect(conn);
}
cell_stats.sort();
for (auto &it : cell_stats)
log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
int in_wires = 0, out_wires = 0;

View file

@ -111,7 +111,7 @@ struct AlumaccWorker
dict<RTLIL::SigBit, int> bit_users;
dict<RTLIL::SigSpec, maccnode_t*> sig_macc;
dict<RTLIL::SigSig, pool<alunode_t*, hash_ptr_ops>> sig_alu;
dict<RTLIL::SigSig, pool<alunode_t*>> sig_alu;
int macc_counter, alu_counter;
AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module)
@ -226,7 +226,7 @@ struct AlumaccWorker
{
while (1)
{
pool<maccnode_t*, hash_ptr_ops> delete_nodes;
pool<maccnode_t*> delete_nodes;
for (auto &it : sig_macc)
{
@ -278,12 +278,12 @@ struct AlumaccWorker
void macc_to_alu()
{
pool<maccnode_t*, hash_ptr_ops> delete_nodes;
pool<maccnode_t*> delete_nodes;
for (auto &it : sig_macc)
{
auto n = it.second;
RTLIL::SigSpec A, B, C = n->macc.bit_ports;
RTLIL::SigSpec A, B, C;
bool a_signed = false, b_signed = false;
bool subtract_b = false;
alunode_t *alunode;

View file

@ -233,10 +233,9 @@ struct ClockgatePass : public Pass {
SigBit ce_bit;
bool pol_clk;
bool pol_ce;
unsigned int hash() const {
[[nodiscard]] Hasher hash_into(Hasher h) const {
auto t = std::make_tuple(clk_bit, ce_bit, pol_clk, pol_ce);
unsigned int h = mkhash_init;
h = mkhash(h, hash_ops<decltype(t)>::hash(t));
h.eat(t);
return h;
}
bool operator==(const ClkNetInfo& other) const {

View file

@ -28,22 +28,22 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
IdString concat_name(RTLIL::Cell *cell, IdString object_name)
IdString concat_name(RTLIL::Cell *cell, IdString object_name, const std::string &separator = ".")
{
if (object_name[0] == '\\')
return stringf("%s.%s", cell->name.c_str(), object_name.c_str() + 1);
return stringf("%s%s%s", cell->name.c_str(), separator.c_str(), object_name.c_str() + 1);
else {
std::string object_name_str = object_name.str();
if (object_name_str.substr(0, 8) == "$flatten")
object_name_str.erase(0, 8);
return stringf("$flatten%s.%s", cell->name.c_str(), object_name_str.c_str());
return stringf("$flatten%s%s%s", cell->name.c_str(), separator.c_str(), object_name_str.c_str());
}
}
template<class T>
IdString map_name(RTLIL::Cell *cell, T *object)
IdString map_name(RTLIL::Cell *cell, T *object, const std::string &separator = ".")
{
return cell->module->uniquify(concat_name(cell, object->name));
return cell->module->uniquify(concat_name(cell, object->name, separator));
}
void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr)
@ -60,6 +60,7 @@ struct FlattenWorker
bool ignore_wb = false;
bool create_scopeinfo = true;
bool create_scopename = false;
std::string separator = ".";
template<class T>
void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name)
@ -107,13 +108,13 @@ struct FlattenWorker
}
}
void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells)
void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, SigMap &sigmap, std::vector<RTLIL::Cell*> &new_cells, const std::string &separator)
{
// Copy the contents of the flattened cell
dict<IdString, IdString> memory_map;
for (auto &tpl_memory_it : tpl->memories) {
RTLIL::Memory *new_memory = module->addMemory(map_name(cell, tpl_memory_it.second), tpl_memory_it.second);
RTLIL::Memory *new_memory = module->addMemory(map_name(cell, tpl_memory_it.second, separator), tpl_memory_it.second);
map_attributes(cell, new_memory, tpl_memory_it.second->name);
memory_map[tpl_memory_it.first] = new_memory->name;
design->select(module, new_memory);
@ -127,7 +128,7 @@ struct FlattenWorker
RTLIL::Wire *new_wire = nullptr;
if (tpl_wire->name[0] == '\\') {
RTLIL::Wire *hier_wire = module->wire(concat_name(cell, tpl_wire->name));
RTLIL::Wire *hier_wire = module->wire(concat_name(cell, tpl_wire->name, separator));
if (hier_wire != nullptr && hier_wire->get_bool_attribute(ID::hierconn)) {
hier_wire->attributes.erase(ID::hierconn);
if (GetSize(hier_wire) < GetSize(tpl_wire)) {
@ -139,7 +140,7 @@ struct FlattenWorker
}
}
if (new_wire == nullptr) {
new_wire = module->addWire(map_name(cell, tpl_wire), tpl_wire);
new_wire = module->addWire(map_name(cell, tpl_wire, separator), tpl_wire);
new_wire->port_input = new_wire->port_output = false;
new_wire->port_id = false;
}
@ -150,7 +151,7 @@ struct FlattenWorker
}
for (auto &tpl_proc_it : tpl->processes) {
RTLIL::Process *new_proc = module->addProcess(map_name(cell, tpl_proc_it.second), tpl_proc_it.second);
RTLIL::Process *new_proc = module->addProcess(map_name(cell, tpl_proc_it.second, separator), tpl_proc_it.second);
map_attributes(cell, new_proc, tpl_proc_it.second->name);
for (auto new_proc_sync : new_proc->syncs)
for (auto &memwr_action : new_proc_sync->mem_write_actions)
@ -161,14 +162,14 @@ struct FlattenWorker
}
for (auto tpl_cell : tpl->cells()) {
RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell), tpl_cell);
RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell, separator), tpl_cell);
map_attributes(cell, new_cell, tpl_cell->name);
if (new_cell->has_memid()) {
IdString memid = new_cell->getParam(ID::MEMID).decode_string();
new_cell->setParam(ID::MEMID, Const(memory_map.at(memid).str()));
} else if (new_cell->is_mem_cell()) {
IdString memid = new_cell->getParam(ID::MEMID).decode_string();
new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid).str()));
new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid, separator).str()));
}
auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); };
new_cell->rewrite_sigspecs(rewriter);
@ -279,7 +280,7 @@ struct FlattenWorker
module->rename(scopeinfo, cell_name);
}
void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules)
void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules, const std::string &separator)
{
if (!design->selected(module) || module->get_blackbox_attribute(ignore_wb))
return;
@ -308,7 +309,7 @@ struct FlattenWorker
// If a design is fully selected and has a top module defined, topological sorting ensures that all cells
// added during flattening are black boxes, and flattening is finished in one pass. However, when flattening
// individual modules, this isn't the case, and the newly added cells might have to be flattened further.
flatten_cell(design, module, cell, tpl, sigmap, worklist);
flatten_cell(design, module, cell, tpl, sigmap, worklist, separator);
}
}
};
@ -345,6 +346,9 @@ struct FlattenPass : public Pass {
log(" with a public name the enclosing scope can be found via their\n");
log(" 'hdlname' attribute.\n");
log("\n");
log(" -separator <char>\n");
log(" Use this separator char instead of '.' when concatenating design levels.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
@ -353,6 +357,9 @@ struct FlattenPass : public Pass {
FlattenWorker worker;
if (design->scratchpad.count("flatten.separator"))
worker.separator = design->scratchpad_get_string("flatten.separator");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-wb") {
@ -367,6 +374,10 @@ struct FlattenPass : public Pass {
worker.create_scopename = true;
continue;
}
if (args[argidx] == "-separator" && argidx + 1 < args.size()) {
worker.separator = args[++argidx];
continue;
}
break;
}
extra_args(args, argidx, design);
@ -401,7 +412,7 @@ struct FlattenPass : public Pass {
log_error("Cannot flatten a design containing recursive instantiations.\n");
for (auto module : topo_modules.sorted)
worker.flatten_module(design, module, used_modules);
worker.flatten_module(design, module, used_modules, worker.separator);
if (top != nullptr)
for (auto module : design->modules().to_vector())

View file

@ -250,9 +250,11 @@ struct FlowGraph
{
return !(*this == other);
}
unsigned int hash() const
[[nodiscard]] Hasher hash_into(Hasher h) const
{
return hash_ops<pair<RTLIL::SigBit, int>>::hash({node, is_bottom});
std::pair<RTLIL::SigBit, int> p = {node, is_bottom};
h.eat(p);
return h;
}
static NodePrime top(RTLIL::SigBit node)

View file

@ -286,19 +286,23 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap)
log(" %s %s * %s (%dx%d bits, %s)\n", port.do_subtract ? "sub" : "add", log_signal(port.in_a), log_signal(port.in_b),
GetSize(port.in_a), GetSize(port.in_b), port.is_signed ? "signed" : "unsigned");
if (GetSize(macc.bit_ports) != 0)
log(" add bits %s (%d bits)\n", log_signal(macc.bit_ports), GetSize(macc.bit_ports));
if (unmap)
{
typedef std::pair<RTLIL::SigSpec, bool> summand_t;
std::vector<summand_t> summands;
RTLIL::SigSpec bit_ports;
for (auto &port : macc.ports) {
summand_t this_summand;
if (GetSize(port.in_b)) {
this_summand.first = module->addWire(NEW_ID, width);
module->addMul(NEW_ID, port.in_a, port.in_b, this_summand.first, port.is_signed);
} else if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) {
// Mimic old 'bit_ports' treatment in case it's relevant for performance,
// i.e. defer single-bit summands to be the last ones
bit_ports.append(port.in_a);
continue;
} else if (GetSize(port.in_a) != width) {
this_summand.first = module->addWire(NEW_ID, width);
module->addPos(NEW_ID, port.in_a, this_summand.first, port.is_signed);
@ -309,7 +313,7 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap)
summands.push_back(this_summand);
}
for (auto &bit : macc.bit_ports)
for (auto &bit : bit_ports)
summands.push_back(summand_t(bit, false));
if (GetSize(summands) == 0)
@ -346,14 +350,20 @@ void maccmap(RTLIL::Module *module, RTLIL::Cell *cell, bool unmap)
else
{
MaccmapWorker worker(module, width);
RTLIL::SigSpec bit_ports;
for (auto &port : macc.ports)
if (GetSize(port.in_b) == 0)
for (auto &port : macc.ports) {
// Mimic old 'bit_ports' treatment in case it's relevant for performance,
// i.e. defer single-bit summands to be the last ones
if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract)
bit_ports.append(port.in_a);
else if (GetSize(port.in_b) == 0)
worker.add(port.in_a, port.is_signed, port.do_subtract);
else
worker.add(port.in_a, port.in_b, port.is_signed, port.do_subtract);
}
for (auto &bit : macc.bit_ports)
for (auto bit : bit_ports)
worker.add(bit, 0);
module->connect(cell->getPort(ID::Y), worker.synth());

View file

@ -201,18 +201,19 @@ static RTLIL::Cell* create_gold_module(RTLIL::Design *design, RTLIL::IdString ce
this_port.do_subtract = xorshift32(2) == 1;
macc.ports.push_back(this_port);
}
wire = module->addWire(ID::B);
wire->width = xorshift32(mulbits_a ? xorshift32(4)+1 : xorshift32(16)+1);
wire->port_input = true;
macc.bit_ports = wire;
// Macc::to_cell sets the input ports
macc.to_cell(cell);
wire = module->addWire(ID::Y);
wire->width = width;
wire->port_output = true;
cell->setPort(ID::Y, wire);
macc.to_cell(cell);
// override the B input (macc helpers always sets an empty vector)
wire = module->addWire(ID::B);
wire->width = xorshift32(mulbits_a ? xorshift32(4)+1 : xorshift32(16)+1);
wire->port_input = true;
cell->setPort(ID::B, wire);
}
if (cell_type == ID($lut))

View file

@ -53,7 +53,7 @@ struct QlDspSimdPass : public Pass {
DspConfig(const DspConfig &ref) = default;
DspConfig(DspConfig &&ref) = default;
unsigned int hash() const { return connections.hash(); }
[[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(connections); return h; }
bool operator==(const DspConfig &ref) const { return connections == ref.connections; }
};

View file

@ -1,10 +1,9 @@
import sys
from datetime import datetime, timezone
def generate(filename):
with open(filename, "w") as f:
f.write("// **AUTOGENERATED FILE** **DO NOT EDIT**\n")
f.write(f"// Generated by {sys.argv[0]} at {datetime.now(timezone.utc)}\n")
f.write(f"// Generated by {sys.argv[0]}\n")
f.write("`timescale 1ns /10ps\n")
for a_width in [1,2,4,9,18,36]:

View file

@ -0,0 +1,50 @@
# We don't set the B port on $macc cells anymore,
# test compatibility with older RTLIL files which can
# have the B port populated
read_verilog <<EOF
module gold(input signed [2:0] a1, input signed [2:0] b1,
input [1:0] a2, input [3:0] b2, input c, input d, output signed [3:0] y);
wire signed [3:0] ab1;
assign ab1 = a1 * b1;
assign y = ab1 + a2*b2 + c + d + 1;
endmodule
EOF
prep
# test the model for $macc including the retired B parameter
# matches the gold module
read_rtlil <<EOF
attribute \src "<<EOF:1.1-4.10"
module \gate
wire width 3 input 1 signed \a1
wire width 2 input 3 \a2
wire width 3 input 2 signed \b1
wire width 4 input 4 \b2
wire input 5 \c
wire input 6 \d
wire width 4 output 7 signed \y
cell $macc $1
parameter \A_WIDTH 12
parameter \B_WIDTH 3
parameter \CONFIG 20'01010000011011010011
parameter \CONFIG_WIDTH 20
parameter \Y_WIDTH 4
connect \A { \a2 \b2 \b1 \a1 }
connect \B { \d \c 1'1 }
connect \Y \y
end
end
EOF
design -save gold_gate
equiv_make gold gate equiv
equiv_induct equiv
equiv_status -assert equiv
design -load gold_gate
maccmap gate
equiv_make gold gate equiv
equiv_induct equiv
equiv_status -assert equiv

View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -e
for x in *.ys; do
echo "Running $x.."
../../yosys -ql ${x%.ys}.log $x
done

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -2,4 +2,4 @@
set -eu
python3 mem_gen.py
source ../../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash
generate_mk --yosys-scripts --bash

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -4,7 +4,7 @@ YOSYS_BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"/../ >/dev/null 2>&1 && pwd)
# $ generate_target target_name test_command
generate_target() {
target_name=$1
target_name=$(basename $PWD)-$1
test_command=$2
echo "all: $target_name"
echo ".PHONY: $target_name"
@ -105,7 +105,6 @@ generate_tests() {
fi
}
run_tests() {
generate_mk() {
generate_tests "$@" > run-test.mk
exec ${MAKE:-make} -f run-test.mk
}

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../gen-tests-makefile.sh
run_tests --yosys-scripts
generate_mk --yosys-scripts

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../gen-tests-makefile.sh
run_tests --yosys-scripts
generate_mk --yosys-scripts

View file

@ -9,4 +9,4 @@ find tb/* -name tb*.v | while read name; do
iverilog -o tb/$test_name.out $name $verilog_name
./tb/$test_name.out -fst
done
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
generate_mk --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../gen-tests-makefile.sh
run_tests --yosys-scripts --prove-sv
generate_mk --yosys-scripts --prove-sv

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../gen-tests-makefile.sh
run_tests --yosys-scripts --tcl-scripts --bash --yosys-args "-e 'select out of bounds'"
generate_mk --yosys-scripts --tcl-scripts --bash --yosys-args "-e 'select out of bounds'"

View file

@ -0,0 +1,38 @@
read_verilog <<EOT
module top(input in, output out);
wire [1:0] w1, w2;
f1_test u1 (.in(in), .out(w1[0]));
f2_test u2 (.in(w1), .out(w2));
f3_test u3 (.in(w2[0]), .out(out));
endmodule
module f1_test(input in, output out);
assign out = in;
endmodule
module f2_test(input [1:0] in, output [1:0] out);
assign out[0] = in[0];
assign out[1] = in[1];
endmodule
module f3_test(input in, output [1:0] out);
assign out[0] = in;
assign out[1] = in;
endmodule
EOT
hierarchy -top top
flatten -scopename
prep
write_json json_scopeinfo.out
!grep -qF '$scopeinfo' json_scopeinfo.out
write_json -noscopeinfo json_scopeinfo.out
!grep -qvF '$scopeinfo' json_scopeinfo.out
json -o json_scopeinfo.out
!grep -qF '$scopeinfo' json_scopeinfo.out
json -noscopeinfo -o json_scopeinfo.out
!grep -qvF '$scopeinfo' json_scopeinfo.out

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../gen-tests-makefile.sh
run_tests --yosys-scripts --bash
generate_mk --yosys-scripts --bash

22
tests/various/wreduce2.ys Normal file
View file

@ -0,0 +1,22 @@
read_verilog <<EOF
module top(a, b, y);
parameter awidth = 6;
parameter bwidth = 8;
parameter ywidth = 14;
input [awidth-1:0] a;
input [bwidth-1:0] b;
output [ywidth-1:0] y;
wire [ywidth-1:0] aext = {{(ywidth-awidth){a[awidth-1]}}, a};
wire [ywidth-1:0] bext = {{(ywidth-bwidth){b[bwidth-1]}}, b};
assign y = aext*bext;
endmodule
EOF
opt_clean
wreduce
select -assert-count 1 t:$mul
select -assert-count 1 t:$mul r:A_SIGNED=1 r:B_SIGNED=1 %i %i
select -assert-count 1 t:$mul r:A_WIDTH=6 r:B_WIDTH=8 %i %i

45
tests/verific/bounds.sv Normal file
View file

@ -0,0 +1,45 @@
typedef enum {IDLE, RUN, STOP} state_t;
typedef struct {
logic [7:0] field1;
int field2;
} my_struct_t;
// Submodule to handle the interface ports
module submodule (
my_ifc i_ifc,
my_ifc o_ifc
);
// Connect the interface signals
assign o_ifc.data = i_ifc.data;
endmodule
module test (
input i_a,
output o_a,
input [0:0] i_b,
output [0:0] o_b,
input [3:0] i_c,
output [3:0] o_c,
input logic i_d,
output logic o_d,
input bit [7:0] i_e,
output bit [7:0] o_e,
input int i_f,
output int o_f,
input state_t i_h,
output state_t o_h,
input my_struct_t i_i,
output my_struct_t o_i
);
assign o_a = i_a;
assign o_b = i_b;
assign o_c = i_c;
assign o_d = i_d;
assign o_e = i_e;
assign o_f = i_f;
assign o_h = i_h;
assign o_i = i_i;
endmodule

View file

@ -2,17 +2,108 @@ library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity work is
entity test is
Port (
a : in INTEGER range -5 to 10;
b : out INTEGER range -6 to 11
);
end entity work;
-- BIT type
bit_in : in BIT;
bit_out : out BIT;
architecture Behavioral of work is
-- BIT_VECTOR type
bit_vector_in : in BIT_VECTOR(3 downto 0);
bit_vector_out : out BIT_VECTOR(3 downto 0);
-- BIT_VECTOR type with to index
bit_vector_in_to : in BIT_VECTOR(0 to 3);
bit_vector_out_to : out BIT_VECTOR(0 to 3);
-- STD_ULOGIC type
std_ulogic_in : in STD_ULOGIC;
std_ulogic_out : out STD_ULOGIC;
-- STD_ULOGIC_VECTOR type
std_ulogic_vector_in : in STD_ULOGIC_VECTOR(3 downto 0);
std_ulogic_vector_out : out STD_ULOGIC_VECTOR(3 downto 0);
-- STD_ULOGIC_VECTOR type with to index
std_ulogic_vector_in_to : in STD_ULOGIC_VECTOR(0 to 3);
std_ulogic_vector_out_to : out STD_ULOGIC_VECTOR(0 to 3);
-- STD_LOGIC type
std_logic_in : in STD_LOGIC;
std_logic_out : out STD_LOGIC;
-- STD_LOGIC_VECTOR type
std_logic_vector_in : in STD_LOGIC_VECTOR(3 downto 0);
std_logic_vector_out : out STD_LOGIC_VECTOR(3 downto 0);
-- STD_LOGIC_VECTOR type with to index
std_logic_vector_in_to : in STD_LOGIC_VECTOR(0 to 3);
std_logic_vector_out_to : out STD_LOGIC_VECTOR(0 to 3);
-- SIGNED type
signed_in : in SIGNED(3 downto 0);
signed_out : out SIGNED(3 downto 0);
-- SIGNED type with to index
signed_in_to : in SIGNED(0 to 3);
signed_out_to : out SIGNED(0 to 3);
-- UNSIGNED type
unsigned_in : in UNSIGNED(3 downto 0);
unsigned_out : out UNSIGNED(3 downto 0);
-- UNSIGNED type with to index
unsigned_in_to : in UNSIGNED(0 to 3);
unsigned_out_to : out UNSIGNED(0 to 3);
-- INTEGER type without range
integer_in : in INTEGER;
integer_out : out INTEGER;
-- INTEGER type with range
integer_with_range_in : in INTEGER range -5 to 10;
integer_with_range_out : out INTEGER range -6 to 10;
-- INTEGER type with single value range
integer_single_value_in : in INTEGER range 5 to 5;
integer_single_value_out : out INTEGER range 5 to 5;
-- INTEGER type with null range
integer_null_range_in : in INTEGER range 7 to -1;
integer_null_range_out : out INTEGER range 0 to -1;
-- NATURAL type
natural_in : in NATURAL;
natural_out : out NATURAL;
-- POSITIVE type
positive_in : in POSITIVE;
positive_out : out POSITIVE
);
end entity test;
architecture Behavioral of test is
signal integer_with_range : INTEGER range -1 to 100;
begin
process(a)
begin
b <= a;
end process;
bit_out <= bit_in;
bit_vector_out <= bit_vector_in;
bit_vector_out_to <= bit_vector_in_to;
std_ulogic_out <= std_ulogic_in;
std_ulogic_vector_out <= std_ulogic_vector_in;
std_ulogic_vector_out_to <= std_ulogic_vector_in_to;
std_logic_out <= std_logic_in;
std_logic_vector_out <= std_logic_vector_in;
std_logic_vector_out_to <= std_logic_vector_in_to;
signed_out <= signed_in;
signed_out_to <= signed_in_to;
unsigned_out <= unsigned_in;
unsigned_out_to <= unsigned_in_to;
integer_with_range_out <= integer_with_range_in;
integer_out <= integer_in;
integer_single_value_out <= integer_single_value_in;
integer_null_range_out <= integer_null_range_in;
natural_out <= natural_in;
positive_out <= positive_in;
integer_with_range <= 42;
end architecture Behavioral;

View file

@ -1,6 +1,168 @@
read -vhdl bounds.vhd
verific -import work
select -assert-count 1 a:bottom_bound=5'bs11011
select -assert-count 1 a:top_bound=5'bs01010
select -assert-count 1 a:bottom_bound=5'bs11010
select -assert-count 1 a:top_bound=5'bs01011
hierarchy -top test
# bit: not a scalar type
select -assert-count 0 w:bit_in a:bottom_bound %i
select -assert-count 0 w:bit_in a:top_bound %i
select -assert-count 0 w:bit_out a:bottom_bound %i
select -assert-count 0 w:bit_out a:top_bound %i
# bit_vector: not a scalar type
select -assert-count 0 w:bit_vector_in a:bottom_bound %i
select -assert-count 0 w:bit_vector_in a:top_bound %i
select -assert-count 0 w:bit_vector_out a:bottom_bound %i
select -assert-count 0 w:bit_vector_out a:top_bound %i
# bit_vector with to index: not a scalar type
select -assert-count 0 w:bit_vector_in_to a:bottom_bound %i
select -assert-count 0 w:bit_vector_in_to a:top_bound %i
select -assert-count 0 w:bit_vector_out_to a:bottom_bound %i
select -assert-count 0 w:bit_vector_out_to a:top_bound %i
# std_ulogic: not a scalar type
select -assert-count 0 w:std_ulogic_in a:bottom_bound %i
select -assert-count 0 w:std_ulogic_in a:top_bound %i
select -assert-count 0 w:std_ulogic_out a:bottom_bound %i
select -assert-count 0 w:std_ulogic_out a:top_bound %i
# std_ulogic_vector: not a scalar type
select -assert-count 0 w:std_ulogic_vector_in a:bottom_bound %i
select -assert-count 0 w:std_ulogic_vector_in a:top_bound %i
select -assert-count 0 w:std_ulogic_vector_out a:bottom_bound %i
select -assert-count 0 w:std_ulogic_vector_out a:top_bound %i
# std_ulogic_vector with to index: not a scalar type
select -assert-count 0 w:std_ulogic_vector_in_to a:bottom_bound %i
select -assert-count 0 w:std_ulogic_vector_in_to a:top_bound %i
select -assert-count 0 w:std_ulogic_vector_out_to a:bottom_bound %i
select -assert-count 0 w:std_ulogic_vector_out_to a:top_bound %i
# std_logic: not a scalar type
select -assert-count 0 w:std_logic_in a:bottom_bound %i
select -assert-count 0 w:std_logic_in a:top_bound %i
select -assert-count 0 w:std_logic_out a:bottom_bound %i
select -assert-count 0 w:std_logic_out a:top_bound %i
# std_logic_vector: not a scalar type
select -assert-count 0 w:std_logic_vector_in a:bottom_bound %i
select -assert-count 0 w:std_logic_vector_in a:top_bound %i
select -assert-count 0 w:std_logic_vector_out a:bottom_bound %i
select -assert-count 0 w:std_logic_vector_out a:top_bound %i
# std_logic_vector with to index: not a scalar type
select -assert-count 0 w:std_logic_vector_in_to a:bottom_bound %i
select -assert-count 0 w:std_logic_vector_in_to a:top_bound %i
select -assert-count 0 w:std_logic_vector_out_to a:bottom_bound %i
select -assert-count 0 w:std_logic_vector_out_to a:top_bound %i
# signed: not a scalar type
select -assert-count 0 w:signed_in a:bottom_bound %i
select -assert-count 0 w:signed_in a:top_bound %i
select -assert-count 0 w:signed_out a:bottom_bound %i
select -assert-count 0 w:signed_out a:top_bound %i
# signed with to index: not a scalar type
select -assert-count 0 w:signed_in_to a:bottom_bound %i
select -assert-count 0 w:signed_in_to a:top_bound %i
select -assert-count 0 w:signed_out_to a:bottom_bound %i
select -assert-count 0 w:signed_out_to a:top_bound %i
# unsigned: not a scalar type
select -assert-count 0 w:unsigned_in a:bottom_bound %i
select -assert-count 0 w:unsigned_in a:top_bound %i
select -assert-count 0 w:unsigned_out a:bottom_bound %i
select -assert-count 0 w:unsigned_out a:top_bound %i
# unsigned with to index: not a scalar type
select -assert-count 0 w:unsigned_in_to a:bottom_bound %i
select -assert-count 0 w:unsigned_in_to a:top_bound %i
select -assert-count 0 w:unsigned_out_to a:bottom_bound %i
select -assert-count 0 w:unsigned_out_to a:top_bound %i
# integer: scalar type
select -assert-count 1 w:integer_in a:bottom_bound=32'b10000000000000000000000000000000 %i
select -assert-count 1 w:integer_in a:top_bound=32'b01111111111111111111111111111111 %i
select -assert-count 1 w:integer_out a:bottom_bound=32'b10000000000000000000000000000000 %i
select -assert-count 1 w:integer_out a:top_bound=32'b01111111111111111111111111111111 %i
# integer with range: scalar type
select -assert-count 1 w:integer_with_range_in a:bottom_bound=5'bs11011 %i
select -assert-count 1 w:integer_with_range_in a:top_bound=5'bs01010 %i
select -assert-count 1 w:integer_with_range_out a:bottom_bound=5'bs11010 %i
select -assert-count 1 w:integer_with_range_out a:top_bound=5'bs01010 %i
# integer with single value range: scalar type
select -assert-count 1 w:integer_single_value_in a:bottom_bound=3'bs101 %i
select -assert-count 1 w:integer_single_value_in a:top_bound=3'bs101 %i
select -assert-count 1 w:integer_single_value_out a:bottom_bound=3'bs101 %i
select -assert-count 1 w:integer_single_value_out a:top_bound=3'bs101 %i
# integer with null range: scalar type
select -assert-count 1 w:integer_null_range_in a:bottom_bound=4'bs0111 %i
select -assert-count 1 w:integer_null_range_in a:top_bound=4'bs1111 %i
select -assert-count 1 w:integer_null_range_out a:bottom_bound=1'bs0 %i
select -assert-count 1 w:integer_null_range_out a:top_bound=1'bs1 %i
# natural: scalar type
select -assert-count 1 w:natural_in a:bottom_bound=31'b0000000000000000000000000000000 %i
select -assert-count 1 w:natural_in a:top_bound=31'b1111111111111111111111111111111 %i
select -assert-count 1 w:natural_out a:bottom_bound=31'b0000000000000000000000000000000 %i
select -assert-count 1 w:natural_out a:top_bound=31'b1111111111111111111111111111111 %i
# positive: scalar type
select -assert-count 1 w:positive_in a:bottom_bound=31'b0000000000000000000000000000001 %i
select -assert-count 1 w:positive_in a:top_bound=31'b1111111111111111111111111111111 %i
select -assert-count 1 w:positive_out a:bottom_bound=31'b0000000000000000000000000000001 %i
select -assert-count 1 w:positive_out a:top_bound=31'b1111111111111111111111111111111 %i
design -reset
read -vhdl2019 bounds.vhd
hierarchy -top test
## integer size changed in VHDL 2019
# integer: scalar type
select -assert-count 1 w:integer_in a:bottom_bound=64'b1000000000000000000000000000000000000000000000000000000000000000 %i
select -assert-count 1 w:integer_in a:top_bound=64'b0111111111111111111111111111111111111111111111111111111111111111 %i
select -assert-count 1 w:integer_out a:bottom_bound=64'b1000000000000000000000000000000000000000000000000000000000000000 %i
select -assert-count 1 w:integer_out a:top_bound=64'b0111111111111111111111111111111111111111111111111111111111111111 %i
# natural: scalar type
select -assert-count 1 w:natural_in a:bottom_bound=63'b000000000000000000000000000000000000000000000000000000000000000 %i
select -assert-count 1 w:natural_in a:top_bound=63'b111111111111111111111111111111111111111111111111111111111111111 %i
select -assert-count 1 w:natural_out a:bottom_bound=63'b000000000000000000000000000000000000000000000000000000000000000 %i
select -assert-count 1 w:natural_out a:top_bound=63'b111111111111111111111111111111111111111111111111111111111111111 %i
# positive: scalar type
select -assert-count 1 w:positive_in a:bottom_bound=63'b000000000000000000000000000000000000000000000000000000000000001 %i
select -assert-count 1 w:positive_in a:top_bound=63'b111111111111111111111111111111111111111111111111111111111111111 %i
select -assert-count 1 w:positive_out a:bottom_bound=63'b000000000000000000000000000000000000000000000000000000000000001 %i
select -assert-count 1 w:positive_out a:top_bound=63'b111111111111111111111111111111111111111111111111111111111111111 %i
## ranged integer sizes should be unaffected
# integer with range: scalar type
select -assert-count 1 w:integer_with_range_in a:bottom_bound=5'bs11011 %i
select -assert-count 1 w:integer_with_range_in a:top_bound=5'bs01010 %i
select -assert-count 1 w:integer_with_range_out a:bottom_bound=5'bs11010 %i
select -assert-count 1 w:integer_with_range_out a:top_bound=5'bs01010 %i
# integer with single value range: scalar type
select -assert-count 1 w:integer_single_value_in a:bottom_bound=3'bs101 %i
select -assert-count 1 w:integer_single_value_in a:top_bound=3'bs101 %i
select -assert-count 1 w:integer_single_value_out a:bottom_bound=3'bs101 %i
select -assert-count 1 w:integer_single_value_out a:top_bound=3'bs101 %i
# integer with null range: scalar type
select -assert-count 1 w:integer_null_range_in a:bottom_bound=4'bs0111 %i
select -assert-count 1 w:integer_null_range_in a:top_bound=4'bs1111 %i
select -assert-count 1 w:integer_null_range_out a:bottom_bound=1'bs0 %i
select -assert-count 1 w:integer_null_range_out a:top_bound=1'bs1 %i
design -reset
read -sv bounds.sv
hierarchy -top test
## bounds should not be generated for SV
select -assert-count none a:bottom_bound
select -assert-count none a:top_bound

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../gen-tests-makefile.sh
run_tests --yosys-scripts --bash
generate_mk --yosys-scripts --bash

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
set -eu
source ../gen-tests-makefile.sh
run_tests --yosys-scripts --bash
generate_mk --yosys-scripts --bash