Update yosys to latest version + add simulation in fpga_flow
This commit is contained in:
parent
555570c15e
commit
1018134726
|
@ -17,11 +17,11 @@ cd ${pwd_path}/scripts
|
|||
|
||||
# SRAM FPGA
|
||||
# TT case
|
||||
perl fpga_flow.pl -conf ${config_file} -benchmark ${bench_txt} -rpt ${rpt_file} -N 10 -K 6 -ace_d 0.5 -power -remove_designs -multi_thread 1 -vpr_fpga_x2p_rename_illegal_port -vpr_fpga_verilog -vpr_fpga_verilog_dir $verilog_path -vpr_fpga_bitstream_generator -vpr_fpga_verilog_print_autocheck_top_testbench -vpr_fpga_verilog_include_timing -vpr_fpga_verilog_include_signal_init -vpr_fpga_verilog_formal_verification_top_netlist -fix_route_chan_width-vpr_fpga_verilog_include_icarus_simulator
|
||||
perl fpga_flow.pl -conf ${config_file} -benchmark ${bench_txt} -rpt ${rpt_file} -N 10 -K 6 -ace_d 0.5 -power -remove_designs -multi_thread 1 -vpr_fpga_x2p_rename_illegal_port -vpr_fpga_verilog -vpr_fpga_verilog_dir $verilog_path -vpr_fpga_bitstream_generator -vpr_fpga_verilog_print_autocheck_top_testbench -vpr_fpga_verilog_include_timing -vpr_fpga_verilog_include_signal_init -vpr_fpga_verilog_formal_verification_top_netlist -fix_route_chan_width-vpr_fpga_verilog_include_icarus_simulator -end_flow_with_test
|
||||
|
||||
rm -rf ${pwd_path}/results
|
||||
rm -rf $verilog_path
|
||||
cd ${pwd_path}
|
||||
|
||||
echo "Netlists successfully generated"
|
||||
echo "Netlists successfully generated and simulated"
|
||||
exit 0
|
||||
|
|
|
@ -90,8 +90,6 @@ my @sctgy;
|
|||
"vpr_power_tags"
|
||||
);
|
||||
|
||||
my $clock_name = "avoid_clk_option";
|
||||
|
||||
# ----------Subrountines------------#
|
||||
# Print TABs and strings
|
||||
sub tab_print($ $ $)
|
||||
|
@ -352,11 +350,13 @@ sub opts_read()
|
|||
&read_opt_into_hash("vpr_fpga_spice_leakage_only","off","off");
|
||||
&read_opt_into_hash("vpr_fpga_spice_parasitic_net_estimation_off","off","off");
|
||||
&read_opt_into_hash("vpr_fpga_spice_testbench_load_extraction_off","off","off");
|
||||
|
||||
# FPGA-Verilog options
|
||||
# Read Opt into Hash(opt_ptr) : "opt_name","with_val","mandatory"
|
||||
&read_opt_into_hash("vpr_fpga_verilog","off","off");
|
||||
&read_opt_into_hash("vpr_fpga_verilog_print_top_tb","off","off");
|
||||
&read_opt_into_hash("vpr_fpga_verilog_print_input_blif_tb","off","off");
|
||||
&read_opt_into_hash("vpr_fpga_bitstream_generator","off","off");
|
||||
# AA add for update
|
||||
&read_opt_into_hash("vpr_fpga_verilog_print_autocheck_top_testbench","off","off");
|
||||
&read_opt_into_hash("vpr_fpga_verilog_dir","on","off");
|
||||
&read_opt_into_hash("vpr_fpga_verilog_print_modelsim_autodeck","on","off");
|
||||
|
@ -370,6 +370,9 @@ sub opts_read()
|
|||
&read_opt_into_hash("vpr_fpga_verilog_print_sdc_analysis","off","off");
|
||||
&read_opt_into_hash("vpr_fpga_verilog_print_user_defined_template","off","off");
|
||||
|
||||
# Regression test option
|
||||
&read_opt_into_hash("end_flow_with_test","off","off");
|
||||
|
||||
&print_opts();
|
||||
|
||||
return 1;
|
||||
|
@ -685,7 +688,8 @@ sub run_yosys_fpgamap($ $ $ $) {
|
|||
close($YOSYS_CMD_FH);
|
||||
#
|
||||
# Create a local copy for the commands
|
||||
system("./$yosys_name -s $cmd_log > $log");
|
||||
|
||||
system("./$yosys_name $cmd_log > $log");
|
||||
|
||||
if (!(-e $blif_out)) {
|
||||
die "ERROR: Fail Yosys for benchmark $bm.\n";
|
||||
|
@ -1221,7 +1225,7 @@ sub run_ace($ $ $ $) {
|
|||
|
||||
print "Entering $ace_dir\n";
|
||||
chdir $ace_dir;
|
||||
system("./$ace_name -b $mpack_vpr_blif -o $act_file -n $ace_new_blif -c $clock_name $ace_customized_opts >> $log");
|
||||
system("./$ace_name -b $mpack_vpr_blif -o $act_file -n $ace_new_blif -c clk $ace_customized_opts >> $log");
|
||||
|
||||
if (!(-e $ace_new_blif)) {
|
||||
die "ERROR: Fail ACE for benchmark $mpack_vpr_blif.\n";
|
||||
|
@ -1232,6 +1236,65 @@ sub run_ace($ $ $ $) {
|
|||
chdir $cwd;
|
||||
}
|
||||
|
||||
# Run Icarus Verilog Simulation
|
||||
sub run_icarus_verilog($ $ $ $ $)
|
||||
{
|
||||
my ($log_file, $compiled_file, $tb_top, $netlists_path, $include_netlists) = @_;
|
||||
|
||||
# Compile and launch simulation
|
||||
system("iverilog -o $compiled_file $netlists_path$include_netlists -s $tb_top");
|
||||
system("vvp $compiled_file >> $log_file"); # no -j option but could be added to speed-up the process
|
||||
|
||||
# Checking simulation results
|
||||
open(F, $log_file);
|
||||
my @lines=<F>;
|
||||
close F;
|
||||
my $keyword = "Succeed";
|
||||
my $results = grep($keyword, @lines);
|
||||
if($results >= 1){
|
||||
print "\nVerification succeed!\n\n";
|
||||
} else {
|
||||
my $keyword = "Failed";
|
||||
my $results = grep($keyword, @lines);
|
||||
if($results >= 1){
|
||||
print "\nVerification failed\n\n";
|
||||
} else {
|
||||
die "\nERROR: Simulation didn't start\n\n";
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# Run netlists verification using Icarus Simulator
|
||||
sub run_netlists_verification($)
|
||||
{
|
||||
my ($benchmark) = @_;
|
||||
my $log_file = "$benchmark"."_sim.log";
|
||||
my $compiled_file = "compiled_"."$benchmark";
|
||||
my $include_netlists = "$benchmark"."_include_netlists.v";
|
||||
my $tb_top_formal = "$benchmark"."_top_formal_verification_random_tb";
|
||||
my $tb_top_autochecked = "$benchmark"."_autocheck_top_tb";
|
||||
my $netlists_path = "$opt_ptr->{vpr_fpga_verilog_dir_val}"."/SRC/";
|
||||
|
||||
system("rm -f $log_file");
|
||||
system("rm -f $compiled_file");
|
||||
|
||||
if("on" eq $opt_ptr->{vpr_fpga_verilog_include_icarus_simulator}){
|
||||
if("on" eq $opt_ptr->{vpr_fpga_verilog_print_autocheck_top_testbench}){
|
||||
if("on" eq $opt_ptr->{vpr_fpga_verilog_formal_verification_top_netlist}){ # Preprogramed FPGA netlist chosen if available to speed-up the process
|
||||
&run_icarus_verilog($log_file, $compiled_file, $tb_top_formal, $netlists_path, $include_netlists);
|
||||
} else {
|
||||
&run_icarus_verilog($log_file, $compiled_file, $tb_top_autochecked, $netlists_path, $include_netlists);
|
||||
}
|
||||
} else {
|
||||
die "ERROR: Cannot run netlist verification without \"-vpr_fpga_verilog_print_autocheck_top_testbench\" token.\n";
|
||||
}
|
||||
} else {
|
||||
die "ERROR: Cannot run netlist verification without \"-vpr_fpga_verilog_include_icarus_simulator\" token.\n";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub run_std_vpr($ $ $ $ $ $ $ $ $)
|
||||
{
|
||||
my ($blif,$bm,$arch,$net,$place,$route,$fix_chan_width,$log,$act_file) = @_;
|
||||
|
@ -1367,7 +1430,7 @@ sub run_std_vpr($ $ $ $ $ $ $ $ $)
|
|||
if ("on" eq $opt_ptr->{vpr_max_router_iteration}) {
|
||||
$other_opt .= "--max_router_iterations $opt_ptr->{vpr_max_router_iteration_val} ";
|
||||
}
|
||||
|
||||
print "\n\n./$vpr_name $arch $blif --net_file $net --place_file $place --route_file $route --full_stats --nodisp $power_opts $packer_opts $chan_width_opt $vpr_spice_opts $other_opt > $log\n\n";
|
||||
system("./$vpr_name $arch $blif --net_file $net --place_file $place --route_file $route --full_stats --nodisp $power_opts $packer_opts $chan_width_opt $vpr_spice_opts $other_opt > $log");
|
||||
|
||||
chdir $cwd;
|
||||
|
@ -1813,6 +1876,10 @@ sub run_yosys_vpr_flow($ $ $ $ $)
|
|||
|
||||
&run_vpr_in_flow($tag, $benchmark, $benchmark_file, $corrected_ace_blif, $vpr_arch, $act_file, $vpr_net, $vpr_place, $vpr_route, $vpr_log, $vpr_reroute_log, $parse_results);
|
||||
|
||||
if("on" eq $opt_ptr->{end_flow_with_test}) {
|
||||
&run_netlists_verification($benchmark);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ Yosys 0.8 .. Yosys 0.8-dev
|
|||
- Added "gate2lut.v" techmap rule
|
||||
- Added "rename -src"
|
||||
- Added "equiv_opt" pass
|
||||
- "synth_xilinx" to now infer hard shift registers, using new "shregmap -tech xilinx"
|
||||
|
||||
|
||||
Yosys 0.7 .. Yosys 0.8
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
#CONFIG := clang
|
||||
CONFIG := gcc
|
||||
# CONFIG := clang
|
||||
CONFIG := gcc
|
||||
# CONFIG := gcc-4.8
|
||||
# CONFIG := emcc
|
||||
# CONFIG := mxe
|
||||
|
@ -10,6 +10,7 @@
|
|||
# features (the more the better)
|
||||
ENABLE_TCL := 1
|
||||
ENABLE_ABC := 1
|
||||
ENABLE_GLOB := 1
|
||||
ENABLE_PLUGINS := 1
|
||||
ENABLE_READLINE := 1
|
||||
ENABLE_EDITLINE := 0
|
||||
|
@ -18,6 +19,15 @@ ENABLE_COVER := 1
|
|||
ENABLE_LIBYOSYS := 0
|
||||
ENABLE_PROTOBUF := 0
|
||||
|
||||
# python wrappers
|
||||
ENABLE_PYOSYS := 0
|
||||
PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)"
|
||||
PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi)
|
||||
PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"")
|
||||
PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
|
||||
PYTHON_PREFIX := `$(PYTHON_EXECUTABLE)-config --prefix`
|
||||
PYTHON_DESTDIR := $(PYTHON_PREFIX)/lib/python$(PYTHON_VERSION)/site-packages
|
||||
|
||||
# other configuration flags
|
||||
ENABLE_GCOV := 0
|
||||
ENABLE_GPROF := 0
|
||||
|
@ -100,7 +110,7 @@ LDFLAGS += -rdynamic
|
|||
LDLIBS += -lrt
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. | wc -l; })
|
||||
YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. 2> /dev/null | wc -l; })
|
||||
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
||||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
|
@ -110,7 +120,7 @@ OBJS = kernel/version_$(GIT_REV).o
|
|||
# is just a symlink to your actual ABC working directory, as 'make mrproper'
|
||||
# will remove the 'abc' directory and you do not want to accidentally
|
||||
# delete your work on ABC..
|
||||
ABCREV = default
|
||||
ABCREV = 3709744
|
||||
ABCPULL = 1
|
||||
ABCURL ?= https://github.com/berkeley-abc/abc
|
||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
|
||||
|
@ -260,6 +270,34 @@ ifeq ($(ENABLE_LIBYOSYS),1)
|
|||
TARGETS += libyosys.so
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_PYOSYS),1)
|
||||
|
||||
#Detect name of boost_python library. Some distros usbe boost_python-py<version>, other boost_python<version>, some only use the major version number, some a concatenation of major and minor version numbers
|
||||
BOOST_PYTHON_LIB ?= $(shell \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \
|
||||
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
|
||||
echo ""; fi; fi; fi; fi;)
|
||||
|
||||
ifeq ($(BOOST_PYTHON_LIB),)
|
||||
$(error BOOST_PYTHON_LIB could not be detected. Please define manualy)
|
||||
endif
|
||||
|
||||
ifeq ($(PYTHON_MAJOR_VERSION),3)
|
||||
LDLIBS += `$(PYTHON_EXECUTABLE)-config --libs` $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
CXXFLAGS += `$(PYTHON_EXECUTABLE)-config --includes` -D WITH_PYTHON
|
||||
else
|
||||
LDLIBS += `$(PYTHON_EXECUTABLE)-config --libs` $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
|
||||
CXXFLAGS += `$(PYTHON_EXECUTABLE)-config --includes` -D WITH_PYTHON
|
||||
endif
|
||||
|
||||
PY_WRAPPER_FILE = kernel/python_wrappers
|
||||
OBJS += $(PY_WRAPPER_FILE).o
|
||||
PY_GEN_SCRIPT= py_wrap_generator
|
||||
PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()")
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_READLINE),1)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_READLINE
|
||||
ifeq ($(OS), FreeBSD)
|
||||
|
@ -298,6 +336,10 @@ LDLIBS += -ldl
|
|||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_GLOB),1)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_GLOB
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_TCL),1)
|
||||
TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')")
|
||||
ifeq ($(OS), FreeBSD)
|
||||
|
@ -505,6 +547,14 @@ libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
|
|||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
||||
%.pyh: %.h
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) -x c++ -o $@ -E -P -
|
||||
|
||||
$(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES)
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")"
|
||||
|
||||
%.o: %.cpp
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
|
||||
|
@ -570,7 +620,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
|
|||
+cd tests/simple && 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/realmath && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/share && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/fsm && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/techmap && bash run-test.sh
|
||||
|
@ -580,6 +630,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
|
|||
+cd tests/sat && bash run-test.sh
|
||||
+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/opt && bash run-test.sh
|
||||
+cd tests/aiger && bash run-test.sh
|
||||
@echo ""
|
||||
@echo " Passed \"make test\"."
|
||||
@echo ""
|
||||
|
@ -629,9 +680,14 @@ endif
|
|||
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR)
|
||||
$(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/.
|
||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)/
|
||||
$(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so
|
||||
$(INSTALL_SUDO) ldconfig
|
||||
ifeq ($(ENABLE_PYOSYS),1)
|
||||
$(INSTALL_SUDO) mkdir -p $(PYTHON_DESTDIR)/pyosys
|
||||
$(INSTALL_SUDO) cp libyosys.so $(PYTHON_DESTDIR)/pyosys/
|
||||
$(INSTALL_SUDO) cp misc/__init__.py $(PYTHON_DESTDIR)/pyosys/
|
||||
endif
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
|
@ -639,6 +695,11 @@ uninstall:
|
|||
$(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR)
|
||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
|
||||
ifeq ($(ENABLE_PYOSYS),1)
|
||||
$(INSTALL_SUDO) rm -vf $(PYTHON_DESTDIR)/pyosys/libyosys.so
|
||||
$(INSTALL_SUDO) rm -vf $(PYTHON_DESTDIR)/pyosys/__init__.py
|
||||
$(INSTALL_SUDO) rmdir $(PYTHON_DESTDIR)/pyosys
|
||||
endif
|
||||
endif
|
||||
|
||||
update-manual: $(TARGETS) $(EXTRA_TARGETS)
|
||||
|
@ -651,8 +712,9 @@ manual: $(TARGETS) $(EXTRA_TARGETS)
|
|||
|
||||
clean:
|
||||
rm -rf share
|
||||
rm -rf kernel/*.pyh
|
||||
if test -d manual; then cd manual && sh clean.sh; fi
|
||||
rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS)
|
||||
rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS) $(PY_WRAP_INCLUDES) $(PY_WRAPPER_FILE).cc
|
||||
rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a
|
||||
rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d
|
||||
rm -rf tests/asicworld/*.out tests/asicworld/*.log
|
||||
|
|
|
@ -34,11 +34,24 @@ compatible license that is similar in terms to the MIT license
|
|||
or the 2-clause BSD license).
|
||||
|
||||
|
||||
Web Site
|
||||
========
|
||||
Web Site and Other Resources
|
||||
============================
|
||||
|
||||
More information and documentation can be found on the Yosys web site:
|
||||
http://www.clifford.at/yosys/
|
||||
- http://www.clifford.at/yosys/
|
||||
|
||||
The "Documentation" page on the web site contains links to more resources,
|
||||
including a manual that even describes some of the Yosys internals:
|
||||
- http://www.clifford.at/yosys/documentation.html
|
||||
|
||||
The file `CodingReadme` in this directory contains additional information
|
||||
for people interested in using the Yosys C++ APIs.
|
||||
|
||||
Users interested in formal verification might want to use the formal verification
|
||||
front-end for Yosys, SymbiYosys:
|
||||
- https://symbiyosys.readthedocs.io/en/latest/
|
||||
- https://github.com/YosysHQ/SymbiYosys
|
||||
|
||||
|
||||
Setup
|
||||
======
|
||||
|
@ -53,25 +66,26 @@ prerequisites for building yosys:
|
|||
|
||||
$ sudo apt-get install build-essential clang bison flex \
|
||||
libreadline-dev gawk tcl-dev libffi-dev git \
|
||||
graphviz xdot pkg-config python3
|
||||
graphviz xdot pkg-config python3 libboost-system-dev \
|
||||
libboost-python-dev libboost-filesystem-dev
|
||||
|
||||
Similarily, on Mac OS X MacPorts or Homebrew can be used to install dependencies:
|
||||
|
||||
$ brew tap Homebrew/bundle && brew bundle
|
||||
$ sudo port install bison flex readline gawk libffi \
|
||||
git graphviz pkgconfig python36
|
||||
git graphviz pkgconfig python36 boost
|
||||
|
||||
On FreeBSD use the following command to install all prerequisites:
|
||||
|
||||
# pkg install bison flex readline gawk libffi\
|
||||
git graphviz pkgconfig python3 python36 tcl-wrapper
|
||||
git graphviz pkgconfig python3 python36 tcl-wrapper boost-libs
|
||||
|
||||
On FreeBSD system use gmake instead of make. To run tests use:
|
||||
% MAKE=gmake CC=cc gmake test
|
||||
|
||||
For Cygwin use the following command to install all prerequisites, or select these additional packages:
|
||||
|
||||
setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel
|
||||
setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel,boost-build
|
||||
|
||||
There are also pre-compiled Yosys binary packages for Ubuntu and Win32 as well
|
||||
as a source distribution for Visual Studio. Visit the Yosys download page for
|
||||
|
@ -92,12 +106,15 @@ Makefile.
|
|||
To build Yosys simply type 'make' in this directory.
|
||||
|
||||
$ make
|
||||
$ make test
|
||||
$ sudo make install
|
||||
|
||||
Note that this also downloads, builds and installs ABC (using yosys-abc
|
||||
as executable name).
|
||||
|
||||
Tests are located in the tests subdirectory and can be executed using the test target. Note that you need gawk as well as a recent version of iverilog (i.e. build from git). Then, execute tests via:
|
||||
|
||||
$ make test
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
|
@ -242,11 +259,7 @@ for them:
|
|||
|
||||
- The ``tri``, ``triand``, ``trior``, ``wand`` and ``wor`` net types
|
||||
|
||||
- The ``config`` keyword and library map files
|
||||
|
||||
- The ``disable``, ``primitive`` and ``specify`` statements
|
||||
|
||||
- Latched logic (is synthesized as logic with feedback loops)
|
||||
- The ``config`` and ``disable`` keywords and library map files
|
||||
|
||||
|
||||
Verilog Attributes and non-standard features
|
||||
|
@ -294,7 +307,25 @@ Verilog Attributes and non-standard features
|
|||
that have the same ports as the real thing but do not contain information
|
||||
on the internal configuration. This modules are only used by the synthesis
|
||||
passes to identify input and output ports of cells. The Verilog backend
|
||||
also does not output blackbox modules on default.
|
||||
also does not output blackbox modules on default. ``read_verilog``, unless
|
||||
called with ``-noblackbox`` will automatically set the blackbox attribute
|
||||
on any empty module it reads.
|
||||
|
||||
- The ``noblackbox`` attribute set on an empty module prevents ``read_verilog``
|
||||
from automatically setting the blackbox attribute on the module.
|
||||
|
||||
- The ``whitebox`` attribute on modules triggers the same behavior as
|
||||
``blackbox``, but is for whitebox modules, i.e. library modules that
|
||||
contain a behavioral model of the cell type.
|
||||
|
||||
- The ``lib_whitebox`` attribute overwrites ``whitebox`` when ``read_verilog``
|
||||
is run in `-lib` mode. Otherwise it's automatically removed.
|
||||
|
||||
- The ``dynports`` attribute is used by the Verilog front-end to mark modules
|
||||
that have ports with a width that depends on a parameter.
|
||||
|
||||
- The ``hdlname`` attribute is used by some passes to document the original
|
||||
(HDL) name of a module when renaming a module.
|
||||
|
||||
- The ``keep`` attribute on cells and wires is used to mark objects that should
|
||||
never be removed by the optimizer. This is used for example for cells that
|
||||
|
@ -335,7 +366,7 @@ Verilog Attributes and non-standard features
|
|||
|
||||
- When defining a macro with `define, all text between triple double quotes
|
||||
is interpreted as macro body, even if it contains unescaped newlines. The
|
||||
tipple double quotes are removed from the macro body. For example:
|
||||
triple double quotes are removed from the macro body. For example:
|
||||
|
||||
`define MY_MACRO(a, b) """
|
||||
assign a = 23;
|
||||
|
@ -385,9 +416,15 @@ Verilog Attributes and non-standard features
|
|||
expressions as <size>. If the expression is not a simple identifier, it
|
||||
must be put in parentheses. Examples: ``WIDTH'd42``, ``(4+2)'b101010``
|
||||
|
||||
- The system tasks ``$finish`` and ``$display`` are supported in initial blocks
|
||||
in an unconditional context (only if/case statements on parameters
|
||||
and constant values). The intended use for this is synthesis-time DRC.
|
||||
- The system tasks ``$finish``, ``$stop`` and ``$display`` are supported in
|
||||
initial blocks in an unconditional context (only if/case statements on
|
||||
expressions over parameters and constant values are allowed). The intended
|
||||
use for this is synthesis-time DRC.
|
||||
|
||||
- There is limited support for converting specify .. endspecify statements to
|
||||
special ``$specify2``, ``$specify3``, and ``$specrule`` cells, for use in
|
||||
blackboxes and whiteboxes. Use ``read_verilog -specify`` to enable this
|
||||
functionality. (By default specify .. endspecify blocks are ignored.)
|
||||
|
||||
|
||||
Non-standard or SystemVerilog features for formal verification
|
||||
|
@ -422,7 +459,7 @@ Non-standard or SystemVerilog features for formal verification
|
|||
supported in any clocked block.
|
||||
|
||||
- The syntax ``@($global_clock)`` can be used to create FFs that have no
|
||||
explicit clock input ($ff cells). The same can be achieved by using
|
||||
explicit clock input (``$ff`` cells). The same can be achieved by using
|
||||
``@(posedge <netname>)`` or ``@(negedge <netname>)`` when ``<netname>``
|
||||
is marked with the ``(* gclk *)`` Verilog attribute.
|
||||
|
||||
|
@ -435,7 +472,7 @@ from SystemVerilog:
|
|||
|
||||
- The ``assert`` statement from SystemVerilog is supported in its most basic
|
||||
form. In module context: ``assert property (<expression>);`` and within an
|
||||
always block: ``assert(<expression>);``. It is transformed to a $assert cell.
|
||||
always block: ``assert(<expression>);``. It is transformed to an ``$assert`` cell.
|
||||
|
||||
- The ``assume``, ``restrict``, and ``cover`` statements from SystemVerilog are
|
||||
also supported. The same limitations as with the ``assert`` statement apply.
|
||||
|
|
|
@ -140,7 +140,7 @@ struct BlifDumper
|
|||
return "subckt";
|
||||
if (!design->modules_.count(RTLIL::escape_id(cell_type)))
|
||||
return "gate";
|
||||
if (design->modules_.at(RTLIL::escape_id(cell_type))->get_bool_attribute("\\blackbox"))
|
||||
if (design->modules_.at(RTLIL::escape_id(cell_type))->get_blackbox_attribute())
|
||||
return "gate";
|
||||
return "subckt";
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ struct BlifDumper
|
|||
}
|
||||
f << stringf("\n");
|
||||
|
||||
if (module->get_bool_attribute("\\blackbox")) {
|
||||
if (module->get_blackbox_attribute()) {
|
||||
f << stringf(".blackbox\n");
|
||||
f << stringf(".end\n");
|
||||
return;
|
||||
|
@ -640,7 +640,7 @@ struct BlifBackend : public Backend {
|
|||
for (auto module_it : design->modules_)
|
||||
{
|
||||
RTLIL::Module *module = module_it.second;
|
||||
if (module->get_bool_attribute("\\blackbox") && !config.blackbox_mode)
|
||||
if (module->get_blackbox_attribute() && !config.blackbox_mode)
|
||||
continue;
|
||||
|
||||
if (module->processes.size() != 0)
|
||||
|
|
|
@ -340,7 +340,7 @@ struct BtorWorker
|
|||
if (cell->type == "$lt") btor_op = "lt";
|
||||
if (cell->type == "$le") btor_op = "lte";
|
||||
if (cell->type.in("$eq", "$eqx")) btor_op = "eq";
|
||||
if (cell->type.in("$ne", "$nex")) btor_op = "ne";
|
||||
if (cell->type.in("$ne", "$nex")) btor_op = "neq";
|
||||
if (cell->type == "$ge") btor_op = "gte";
|
||||
if (cell->type == "$gt") btor_op = "gt";
|
||||
log_assert(!btor_op.empty());
|
||||
|
@ -615,6 +615,7 @@ struct BtorWorker
|
|||
{
|
||||
int abits = cell->getParam("\\ABITS").as_int();
|
||||
int width = cell->getParam("\\WIDTH").as_int();
|
||||
int nwords = cell->getParam("\\SIZE").as_int();
|
||||
int rdports = cell->getParam("\\RD_PORTS").as_int();
|
||||
int wrports = cell->getParam("\\WR_PORTS").as_int();
|
||||
|
||||
|
@ -641,6 +642,52 @@ struct BtorWorker
|
|||
int data_sid = get_bv_sid(width);
|
||||
int bool_sid = get_bv_sid(1);
|
||||
int sid = get_mem_sid(abits, width);
|
||||
|
||||
Const initdata = cell->getParam("\\INIT");
|
||||
initdata.exts(nwords*width);
|
||||
int nid_init_val = -1;
|
||||
|
||||
if (!initdata.is_fully_undef())
|
||||
{
|
||||
bool constword = true;
|
||||
Const firstword = initdata.extract(0, width);
|
||||
|
||||
for (int i = 1; i < nwords; i++) {
|
||||
Const thisword = initdata.extract(i*width, width);
|
||||
if (thisword != firstword) {
|
||||
constword = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (constword)
|
||||
{
|
||||
if (verbose)
|
||||
btorf("; initval = %s\n", log_signal(firstword));
|
||||
nid_init_val = get_sig_nid(firstword);
|
||||
}
|
||||
else
|
||||
{
|
||||
int nid_init_val = next_nid++;
|
||||
btorf("%d state %d\n", nid_init_val, sid);
|
||||
|
||||
for (int i = 0; i < nwords; i++) {
|
||||
Const thisword = initdata.extract(i*width, width);
|
||||
if (thisword.is_fully_undef())
|
||||
continue;
|
||||
Const thisaddr(i, abits);
|
||||
int nid_thisword = get_sig_nid(thisword);
|
||||
int nid_thisaddr = get_sig_nid(thisaddr);
|
||||
int last_nid_init_val = nid_init_val;
|
||||
nid_init_val = next_nid++;
|
||||
if (verbose)
|
||||
btorf("; initval[%d] = %s\n", i, log_signal(thisword));
|
||||
btorf("%d write %d %d %d %d\n", nid_init_val, sid, last_nid_init_val, nid_thisaddr, nid_thisword);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int nid = next_nid++;
|
||||
int nid_head = nid;
|
||||
|
||||
|
@ -649,6 +696,12 @@ struct BtorWorker
|
|||
else
|
||||
btorf("%d state %d %s\n", nid, sid, log_id(cell));
|
||||
|
||||
if (nid_init_val >= 0)
|
||||
{
|
||||
int nid_init = next_nid++;
|
||||
btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val);
|
||||
}
|
||||
|
||||
if (asyncwr)
|
||||
{
|
||||
for (int port = 0; port < wrports; port++)
|
||||
|
@ -932,9 +985,8 @@ struct BtorWorker
|
|||
|
||||
btorf_push(stringf("output %s", log_id(wire)));
|
||||
|
||||
int sid = get_bv_sid(GetSize(wire));
|
||||
int nid = get_sig_nid(wire);
|
||||
btorf("%d output %d %d %s\n", next_nid++, sid, nid, log_id(wire));
|
||||
btorf("%d output %d %s\n", next_nid++, nid, log_id(wire));
|
||||
|
||||
btorf_pop(stringf("output %s", log_id(wire)));
|
||||
}
|
||||
|
|
|
@ -106,6 +106,10 @@ struct EdifBackend : public Backend {
|
|||
log(" if the design contains constant nets. use \"hilomap\" to map to custom\n");
|
||||
log(" constant drivers first)\n");
|
||||
log("\n");
|
||||
log(" -gndvccy\n");
|
||||
log(" create \"GND\" and \"VCC\" cells with \"Y\" outputs. (the default is \"G\"\n");
|
||||
log(" for \"GND\" and \"P\" for \"VCC\".)\n");
|
||||
log("\n");
|
||||
log(" -attrprop\n");
|
||||
log(" create EDIF properties for cell attributes\n");
|
||||
log("\n");
|
||||
|
@ -126,7 +130,7 @@ struct EdifBackend : public Backend {
|
|||
bool port_rename = false;
|
||||
bool attr_properties = false;
|
||||
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
|
||||
bool nogndvcc = false;
|
||||
bool nogndvcc = false, gndvccy = false;
|
||||
CellTypes ct(design);
|
||||
EdifNames edif_names;
|
||||
|
||||
|
@ -141,6 +145,10 @@ struct EdifBackend : public Backend {
|
|||
nogndvcc = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-gndvccy") {
|
||||
gndvccy = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-attrprop") {
|
||||
attr_properties = true;
|
||||
continue;
|
||||
|
@ -170,7 +178,7 @@ struct EdifBackend : public Backend {
|
|||
for (auto module_it : design->modules_)
|
||||
{
|
||||
RTLIL::Module *module = module_it.second;
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
if (module->get_blackbox_attribute())
|
||||
continue;
|
||||
|
||||
if (top_module_name.empty())
|
||||
|
@ -184,7 +192,7 @@ struct EdifBackend : public Backend {
|
|||
for (auto cell_it : module->cells_)
|
||||
{
|
||||
RTLIL::Cell *cell = cell_it.second;
|
||||
if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
|
||||
if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_blackbox_attribute()) {
|
||||
lib_cell_ports[cell->type];
|
||||
for (auto p : cell->connections())
|
||||
lib_cell_ports[cell->type][p.first] = GetSize(p.second);
|
||||
|
@ -211,7 +219,7 @@ struct EdifBackend : public Backend {
|
|||
*f << stringf(" (cellType GENERIC)\n");
|
||||
*f << stringf(" (view VIEW_NETLIST\n");
|
||||
*f << stringf(" (viewType NETLIST)\n");
|
||||
*f << stringf(" (interface (port G (direction OUTPUT)))\n");
|
||||
*f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'G');
|
||||
*f << stringf(" )\n");
|
||||
*f << stringf(" )\n");
|
||||
|
||||
|
@ -219,7 +227,7 @@ struct EdifBackend : public Backend {
|
|||
*f << stringf(" (cellType GENERIC)\n");
|
||||
*f << stringf(" (view VIEW_NETLIST\n");
|
||||
*f << stringf(" (viewType NETLIST)\n");
|
||||
*f << stringf(" (interface (port P (direction OUTPUT)))\n");
|
||||
*f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'P');
|
||||
*f << stringf(" )\n");
|
||||
*f << stringf(" )\n");
|
||||
}
|
||||
|
@ -294,7 +302,7 @@ struct EdifBackend : public Backend {
|
|||
*f << stringf(" (technology (numberDefinition))\n");
|
||||
for (auto module : sorted_modules)
|
||||
{
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
if (module->get_blackbox_attribute())
|
||||
continue;
|
||||
|
||||
SigMap sigmap(module);
|
||||
|
@ -420,9 +428,9 @@ struct EdifBackend : public Backend {
|
|||
if (nogndvcc)
|
||||
log_error("Design contains constant nodes (map with \"hilomap\" first).\n");
|
||||
if (sig == RTLIL::State::S0)
|
||||
*f << stringf(" (portRef G (instanceRef GND))\n");
|
||||
*f << stringf(" (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G');
|
||||
if (sig == RTLIL::State::S1)
|
||||
*f << stringf(" (portRef P (instanceRef VCC))\n");
|
||||
*f << stringf(" (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P');
|
||||
}
|
||||
*f << stringf(" ))\n");
|
||||
}
|
||||
|
|
|
@ -23,7 +23,11 @@
|
|||
#include "kernel/celltypes.h"
|
||||
#include "kernel/cellaigs.h"
|
||||
#include "kernel/log.h"
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
@ -37,6 +41,7 @@ static const FDirection FD_NODIRECTION = 0x0;
|
|||
static const FDirection FD_IN = 0x1;
|
||||
static const FDirection FD_OUT = 0x2;
|
||||
static const FDirection FD_INOUT = 0x3;
|
||||
static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20; // For historic reasons, this is actually one greater than the maximum allowed shift width
|
||||
|
||||
// Get a port direction with respect to a specific module.
|
||||
FDirection getPortFDirection(IdString id, Module *module)
|
||||
|
@ -101,6 +106,125 @@ struct FirrtlWorker
|
|||
RTLIL::Design *design;
|
||||
std::string indent;
|
||||
|
||||
// Define read/write ports and memories.
|
||||
// We'll collect their definitions and emit the corresponding FIRRTL definitions at the appropriate point in module construction.
|
||||
// For the moment, we don't handle $readmemh or $readmemb.
|
||||
// These will be part of a subsequent PR.
|
||||
struct read_port {
|
||||
string name;
|
||||
bool clk_enable;
|
||||
bool clk_parity;
|
||||
bool transparent;
|
||||
RTLIL::SigSpec clk;
|
||||
RTLIL::SigSpec ena;
|
||||
RTLIL::SigSpec addr;
|
||||
read_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr) : name(name), clk_enable(clk_enable), clk_parity(clk_parity), transparent(transparent), clk(clk), ena(ena), addr(addr) {
|
||||
// Current (3/13/2019) conventions:
|
||||
// generate a constant 0 for clock and a constant 1 for enable if they are undefined.
|
||||
if (!clk.is_fully_def())
|
||||
this->clk = SigSpec(RTLIL::Const(0, 1));
|
||||
if (!ena.is_fully_def())
|
||||
this->ena = SigSpec(RTLIL::Const(1, 1));
|
||||
}
|
||||
string gen_read(const char * indent) {
|
||||
string addr_expr = make_expr(addr);
|
||||
string ena_expr = make_expr(ena);
|
||||
string clk_expr = make_expr(clk);
|
||||
string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str());
|
||||
string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str());
|
||||
string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str());
|
||||
return addr_str + ena_str + clk_str;
|
||||
}
|
||||
};
|
||||
struct write_port : read_port {
|
||||
RTLIL::SigSpec mask;
|
||||
write_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr, RTLIL::SigSpec mask) : read_port(name, clk_enable, clk_parity, transparent, clk, ena, addr), mask(mask) {
|
||||
if (!clk.is_fully_def())
|
||||
this->clk = SigSpec(RTLIL::Const(0));
|
||||
if (!ena.is_fully_def())
|
||||
this->ena = SigSpec(RTLIL::Const(0));
|
||||
if (!mask.is_fully_def())
|
||||
this->ena = SigSpec(RTLIL::Const(1));
|
||||
}
|
||||
string gen_read(const char * /* indent */) {
|
||||
log_error("gen_read called on write_port: %s\n", name.c_str());
|
||||
return stringf("gen_read called on write_port: %s\n", name.c_str());
|
||||
}
|
||||
string gen_write(const char * indent) {
|
||||
string addr_expr = make_expr(addr);
|
||||
string ena_expr = make_expr(ena);
|
||||
string clk_expr = make_expr(clk);
|
||||
string mask_expr = make_expr(mask);
|
||||
string mask_str = stringf("%s%s.mask <= %s\n", indent, name.c_str(), mask_expr.c_str());
|
||||
string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str());
|
||||
string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str());
|
||||
string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str());
|
||||
return addr_str + ena_str + clk_str + mask_str;
|
||||
}
|
||||
};
|
||||
/* Memories defined within this module. */
|
||||
struct memory {
|
||||
Cell *pCell; // for error reporting
|
||||
string name; // memory name
|
||||
int abits; // number of address bits
|
||||
int size; // size (in units) of the memory
|
||||
int width; // size (in bits) of each element
|
||||
int read_latency;
|
||||
int write_latency;
|
||||
vector<read_port> read_ports;
|
||||
vector<write_port> write_ports;
|
||||
std::string init_file;
|
||||
std::string init_file_srcFileSpec;
|
||||
string srcLine;
|
||||
memory(Cell *pCell, string name, int abits, int size, int width) : pCell(pCell), name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {
|
||||
// Provide defaults for abits or size if one (but not the other) is specified.
|
||||
if (this->abits == 0 && this->size != 0) {
|
||||
this->abits = ceil_log2(this->size);
|
||||
} else if (this->abits != 0 && this->size == 0) {
|
||||
this->size = 1 << this->abits;
|
||||
}
|
||||
// Sanity-check this construction.
|
||||
if (this->name == "") {
|
||||
log_error("Nameless memory%s\n", this->atLine());
|
||||
}
|
||||
if (this->abits == 0 && this->size == 0) {
|
||||
log_error("Memory %s has zero address bits and size%s\n", this->name.c_str(), this->atLine());
|
||||
}
|
||||
if (this->width == 0) {
|
||||
log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine());
|
||||
}
|
||||
}
|
||||
// We need a default constructor for the dict insert.
|
||||
memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){}
|
||||
|
||||
const char *atLine() {
|
||||
if (srcLine == "") {
|
||||
if (pCell) {
|
||||
auto p = pCell->attributes.find("\\src");
|
||||
srcLine = " at " + p->second.decode_string();
|
||||
}
|
||||
}
|
||||
return srcLine.c_str();
|
||||
}
|
||||
void add_memory_read_port(read_port &rp) {
|
||||
read_ports.push_back(rp);
|
||||
}
|
||||
void add_memory_write_port(write_port &wp) {
|
||||
write_ports.push_back(wp);
|
||||
}
|
||||
void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) {
|
||||
this->init_file = init_file;
|
||||
this->init_file_srcFileSpec = init_file_srcFileSpec;
|
||||
}
|
||||
|
||||
};
|
||||
dict<string, memory> memories;
|
||||
|
||||
void register_memory(memory &m)
|
||||
{
|
||||
memories[m.name] = m;
|
||||
}
|
||||
|
||||
void register_reverse_wire_map(string id, SigSpec sig)
|
||||
{
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
|
@ -111,7 +235,7 @@ struct FirrtlWorker
|
|||
{
|
||||
}
|
||||
|
||||
string make_expr(const SigSpec &sig)
|
||||
static string make_expr(const SigSpec &sig)
|
||||
{
|
||||
string expr;
|
||||
|
||||
|
@ -160,11 +284,9 @@ struct FirrtlWorker
|
|||
|
||||
std::string fid(RTLIL::IdString internal_id)
|
||||
{
|
||||
const char *str = internal_id.c_str();
|
||||
return *str == '\\' ? str + 1 : str;
|
||||
return make_id(internal_id);
|
||||
}
|
||||
|
||||
|
||||
std::string cellname(RTLIL::Cell *cell)
|
||||
{
|
||||
return fid(cell->name).c_str();
|
||||
|
@ -173,6 +295,26 @@ struct FirrtlWorker
|
|||
void process_instance(RTLIL::Cell *cell, vector<string> &wire_exprs)
|
||||
{
|
||||
std::string cell_type = fid(cell->type);
|
||||
std::string instanceOf;
|
||||
// If this is a parameterized module, its parent module is encoded in the cell type
|
||||
if (cell->type.substr(0, 8) == "$paramod")
|
||||
{
|
||||
std::string::iterator it;
|
||||
for (it = cell_type.begin(); it < cell_type.end(); it++)
|
||||
{
|
||||
switch (*it) {
|
||||
case '\\': /* FALL_THROUGH */
|
||||
case '=': /* FALL_THROUGH */
|
||||
case '\'': /* FALL_THROUGH */
|
||||
case '$': instanceOf.append("_"); break;
|
||||
default: instanceOf.append(1, *it); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instanceOf = cell_type;
|
||||
}
|
||||
|
||||
std::string cell_name = cellname(cell);
|
||||
std::string cell_name_comment;
|
||||
|
@ -182,41 +324,75 @@ struct FirrtlWorker
|
|||
cell_name_comment = "";
|
||||
// Find the module corresponding to this instance.
|
||||
auto instModule = design->module(cell->type);
|
||||
wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), cell_type.c_str()));
|
||||
// If there is no instance for this, just return.
|
||||
if (instModule == NULL)
|
||||
{
|
||||
log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str());
|
||||
return;
|
||||
}
|
||||
wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str()));
|
||||
|
||||
for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
|
||||
if (it->second.size() > 0) {
|
||||
const SigSpec &secondSig = it->second;
|
||||
const std::string firstName = cell_name + "." + make_id(it->first);
|
||||
const std::string secondName = make_expr(secondSig);
|
||||
const std::string secondExpr = make_expr(secondSig);
|
||||
// Find the direction for this port.
|
||||
FDirection dir = getPortFDirection(it->first, instModule);
|
||||
std::string source, sink;
|
||||
std::string sourceExpr, sinkExpr;
|
||||
const SigSpec *sinkSig = nullptr;
|
||||
switch (dir) {
|
||||
case FD_INOUT:
|
||||
log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", log_id(cell_type), log_signal(it->second));
|
||||
log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second));
|
||||
/* FALLTHRU */
|
||||
case FD_OUT:
|
||||
source = firstName;
|
||||
sink = secondName;
|
||||
sourceExpr = firstName;
|
||||
sinkExpr = secondExpr;
|
||||
sinkSig = &secondSig;
|
||||
break;
|
||||
case FD_NODIRECTION:
|
||||
log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", log_id(cell_type), log_signal(it->second));
|
||||
/* FALL_THROUGH */
|
||||
log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second));
|
||||
/* FALLTHRU */
|
||||
case FD_IN:
|
||||
source = secondName;
|
||||
sink = firstName;
|
||||
sourceExpr = secondExpr;
|
||||
sinkExpr = firstName;
|
||||
break;
|
||||
default:
|
||||
log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", log_id(cell_type), log_signal(it->second), dir);
|
||||
log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type.c_str(), log_signal(it->second), dir);
|
||||
break;
|
||||
}
|
||||
wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sink.c_str(), source.c_str()));
|
||||
// Check for subfield assignment.
|
||||
std::string bitsString = "bits(";
|
||||
if (sinkExpr.substr(0, bitsString.length()) == bitsString ) {
|
||||
if (sinkSig == nullptr)
|
||||
log_error("Unknown subfield %s.%s\n", cell_type.c_str(), sinkExpr.c_str());
|
||||
// Don't generate the assignment here.
|
||||
// Add the source and sink to the "reverse_wire_map" and we'll output the assignment
|
||||
// as part of the coalesced subfield assignments for this wire.
|
||||
register_reverse_wire_map(sourceExpr, *sinkSig);
|
||||
} else {
|
||||
wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
wire_exprs.push_back(stringf("\n"));
|
||||
|
||||
}
|
||||
|
||||
// Given an expression for a shift amount, and a maximum width,
|
||||
// generate the FIRRTL expression for equivalent dynamic shift taking into account FIRRTL shift semantics.
|
||||
std::string gen_dshl(const string b_expr, const int b_padded_width)
|
||||
{
|
||||
string result = b_expr;
|
||||
if (b_padded_width >= FIRRTL_MAX_DSH_WIDTH_ERROR) {
|
||||
int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1;
|
||||
string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<<max_shift_width_bits) - 1);
|
||||
// Deal with the difference in semantics between FIRRTL and verilog
|
||||
result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr.c_str(), max_shift_string.c_str(), max_shift_string.c_str(), b_expr.c_str(), max_shift_width_bits - 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
f << stringf(" module %s:\n", make_id(module->name));
|
||||
|
@ -225,6 +401,12 @@ struct FirrtlWorker
|
|||
for (auto wire : module->wires())
|
||||
{
|
||||
const auto wireName = make_id(wire->name);
|
||||
// If a wire has initial data, issue a warning since FIRRTL doesn't currently support it.
|
||||
if (wire->attributes.count("\\init")) {
|
||||
log_warning("Initial value (%s) for (%s.%s) not supported\n",
|
||||
wire->attributes.at("\\init").as_string().c_str(),
|
||||
log_id(module), log_id(wire));
|
||||
}
|
||||
if (wire->port_id)
|
||||
{
|
||||
if (wire->port_input && wire->port_output)
|
||||
|
@ -240,7 +422,8 @@ struct FirrtlWorker
|
|||
|
||||
for (auto cell : module->cells())
|
||||
{
|
||||
// Is this cell is a module instance?
|
||||
bool extract_y_bits = false; // Assume no extraction of final bits will be required.
|
||||
// Is this cell is a module instance?
|
||||
if (cell->type[0] != '$')
|
||||
{
|
||||
process_instance(cell, wire_exprs);
|
||||
|
@ -264,21 +447,23 @@ struct FirrtlWorker
|
|||
}
|
||||
|
||||
string primop;
|
||||
bool always_uint = false;
|
||||
bool always_uint = false;
|
||||
if (cell->type == "$not") primop = "not";
|
||||
if (cell->type == "$neg") primop = "neg";
|
||||
if (cell->type == "$logic_not") {
|
||||
else if (cell->type == "$neg") {
|
||||
primop = "neg";
|
||||
is_signed = true; // Result of "neg" is signed (an SInt).
|
||||
} else if (cell->type == "$logic_not") {
|
||||
primop = "eq";
|
||||
a_expr = stringf("%s, UInt(0)", a_expr.c_str());
|
||||
}
|
||||
if (cell->type == "$reduce_and") primop = "andr";
|
||||
if (cell->type == "$reduce_or") primop = "orr";
|
||||
if (cell->type == "$reduce_xor") primop = "xorr";
|
||||
if (cell->type == "$reduce_xnor") {
|
||||
else if (cell->type == "$reduce_and") primop = "andr";
|
||||
else if (cell->type == "$reduce_or") primop = "orr";
|
||||
else if (cell->type == "$reduce_xor") primop = "xorr";
|
||||
else if (cell->type == "$reduce_xnor") {
|
||||
primop = "not";
|
||||
a_expr = stringf("xorr(%s)", a_expr.c_str());
|
||||
}
|
||||
if (cell->type == "$reduce_bool") {
|
||||
else if (cell->type == "$reduce_bool") {
|
||||
primop = "neq";
|
||||
// Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand.
|
||||
bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool();
|
||||
|
@ -297,14 +482,15 @@ struct FirrtlWorker
|
|||
continue;
|
||||
}
|
||||
if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$and", "$or", "$eq", "$eqx",
|
||||
"$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
|
||||
"$logic_and", "$logic_or"))
|
||||
"$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
|
||||
"$logic_and", "$logic_or"))
|
||||
{
|
||||
string y_id = make_id(cell->name);
|
||||
bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
|
||||
int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
|
||||
string a_expr = make_expr(cell->getPort("\\A"));
|
||||
string b_expr = make_expr(cell->getPort("\\B"));
|
||||
int b_padded_width = cell->parameters.at("\\B_WIDTH").as_int();
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
|
||||
|
||||
if (cell->parameters.at("\\A_SIGNED").as_bool()) {
|
||||
|
@ -315,67 +501,95 @@ struct FirrtlWorker
|
|||
if (cell->parameters.at("\\B_SIGNED").as_bool()) {
|
||||
b_expr = "asSInt(" + b_expr + ")";
|
||||
}
|
||||
b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
|
||||
if (b_padded_width < y_width) {
|
||||
auto b_sig = cell->getPort("\\B");
|
||||
b_padded_width = y_width;
|
||||
}
|
||||
}
|
||||
|
||||
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
|
||||
auto a_sig = cell->getPort("\\A");
|
||||
|
||||
if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type == "$shr")) {
|
||||
a_expr = "asUInt(" + a_expr + ")";
|
||||
}
|
||||
|
||||
string primop;
|
||||
bool always_uint = false;
|
||||
bool always_uint = false;
|
||||
if (cell->type == "$add") primop = "add";
|
||||
if (cell->type == "$sub") primop = "sub";
|
||||
if (cell->type == "$mul") primop = "mul";
|
||||
if (cell->type == "$div") primop = "div";
|
||||
if (cell->type == "$mod") primop = "rem";
|
||||
if (cell->type == "$and") {
|
||||
else if (cell->type == "$sub") primop = "sub";
|
||||
else if (cell->type == "$mul") primop = "mul";
|
||||
else if (cell->type == "$div") primop = "div";
|
||||
else if (cell->type == "$mod") primop = "rem";
|
||||
else if (cell->type == "$and") {
|
||||
primop = "and";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$or" ) {
|
||||
else if (cell->type == "$or" ) {
|
||||
primop = "or";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$xor") {
|
||||
else if (cell->type == "$xor") {
|
||||
primop = "xor";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$eq") | (cell->type == "$eqx")) {
|
||||
else if ((cell->type == "$eq") | (cell->type == "$eqx")) {
|
||||
primop = "eq";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$ne") | (cell->type == "$nex")) {
|
||||
else if ((cell->type == "$ne") | (cell->type == "$nex")) {
|
||||
primop = "neq";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$gt") {
|
||||
else if (cell->type == "$gt") {
|
||||
primop = "gt";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$ge") {
|
||||
else if (cell->type == "$ge") {
|
||||
primop = "geq";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$lt") {
|
||||
else if (cell->type == "$lt") {
|
||||
primop = "lt";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$le") {
|
||||
else if (cell->type == "$le") {
|
||||
primop = "leq";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$shl") | (cell->type == "$sshl")) primop = "dshl";
|
||||
if ((cell->type == "$shr") | (cell->type == "$sshr")) primop = "dshr";
|
||||
if ((cell->type == "$logic_and")) {
|
||||
else if ((cell->type == "$shl") | (cell->type == "$sshl")) {
|
||||
// FIRRTL will widen the result (y) by the amount of the shift.
|
||||
// We'll need to offset this by extracting the un-widened portion as Verilog would do.
|
||||
extract_y_bits = true;
|
||||
// Is the shift amount constant?
|
||||
auto b_sig = cell->getPort("\\B");
|
||||
if (b_sig.is_fully_const()) {
|
||||
primop = "shl";
|
||||
b_expr = std::to_string(b_sig.as_int());
|
||||
} else {
|
||||
primop = "dshl";
|
||||
// Convert from FIRRTL left shift semantics.
|
||||
b_expr = gen_dshl(b_expr, b_padded_width);
|
||||
}
|
||||
}
|
||||
else if ((cell->type == "$shr") | (cell->type == "$sshr")) {
|
||||
// We don't need to extract a specific range of bits.
|
||||
extract_y_bits = false;
|
||||
// Is the shift amount constant?
|
||||
auto b_sig = cell->getPort("\\B");
|
||||
if (b_sig.is_fully_const()) {
|
||||
primop = "shr";
|
||||
b_expr = std::to_string(b_sig.as_int());
|
||||
} else {
|
||||
primop = "dshr";
|
||||
}
|
||||
}
|
||||
else if ((cell->type == "$logic_and")) {
|
||||
primop = "and";
|
||||
a_expr = "neq(" + a_expr + ", UInt(0))";
|
||||
b_expr = "neq(" + b_expr + ", UInt(0))";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$logic_or")) {
|
||||
else if ((cell->type == "$logic_or")) {
|
||||
primop = "or";
|
||||
a_expr = "neq(" + a_expr + ", UInt(0))";
|
||||
b_expr = "neq(" + b_expr + ", UInt(0))";
|
||||
|
@ -388,6 +602,11 @@ struct FirrtlWorker
|
|||
|
||||
string expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str());
|
||||
|
||||
// Deal with FIRRTL's "shift widens" semantics
|
||||
if (extract_y_bits) {
|
||||
expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1);
|
||||
}
|
||||
|
||||
if ((is_signed && !always_uint) || cell->type.in("$sub"))
|
||||
expr = stringf("asUInt(%s)", expr.c_str());
|
||||
|
||||
|
@ -420,6 +639,7 @@ struct FirrtlWorker
|
|||
int abits = cell->parameters.at("\\ABITS").as_int();
|
||||
int width = cell->parameters.at("\\WIDTH").as_int();
|
||||
int size = cell->parameters.at("\\SIZE").as_int();
|
||||
memory m(cell, mem_id, abits, size, width);
|
||||
int rd_ports = cell->parameters.at("\\RD_PORTS").as_int();
|
||||
int wr_ports = cell->parameters.at("\\WR_PORTS").as_int();
|
||||
|
||||
|
@ -436,33 +656,24 @@ struct FirrtlWorker
|
|||
if (offset != 0)
|
||||
log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(cell));
|
||||
|
||||
cell_exprs.push_back(stringf(" mem %s:\n", mem_id.c_str()));
|
||||
cell_exprs.push_back(stringf(" data-type => UInt<%d>\n", width));
|
||||
cell_exprs.push_back(stringf(" depth => %d\n", size));
|
||||
|
||||
for (int i = 0; i < rd_ports; i++)
|
||||
cell_exprs.push_back(stringf(" reader => r%d\n", i));
|
||||
|
||||
for (int i = 0; i < wr_ports; i++)
|
||||
cell_exprs.push_back(stringf(" writer => w%d\n", i));
|
||||
|
||||
cell_exprs.push_back(stringf(" read-latency => 0\n"));
|
||||
cell_exprs.push_back(stringf(" write-latency => 1\n"));
|
||||
cell_exprs.push_back(stringf(" read-under-write => undefined\n"));
|
||||
|
||||
for (int i = 0; i < rd_ports; i++)
|
||||
{
|
||||
if (rd_clk_enable[i] != State::S0)
|
||||
log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
|
||||
|
||||
SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(i*abits, abits);
|
||||
SigSpec data_sig = cell->getPort("\\RD_DATA").extract(i*width, width);
|
||||
string addr_expr = make_expr(cell->getPort("\\RD_ADDR").extract(i*abits, abits));
|
||||
|
||||
cell_exprs.push_back(stringf(" %s.r%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str()));
|
||||
cell_exprs.push_back(stringf(" %s.r%d.en <= UInt<1>(1)\n", mem_id.c_str(), i));
|
||||
cell_exprs.push_back(stringf(" %s.r%d.clk <= asClock(UInt<1>(0))\n", mem_id.c_str(), i));
|
||||
|
||||
register_reverse_wire_map(stringf("%s.r%d.data", mem_id.c_str(), i), data_sig);
|
||||
string addr_expr = make_expr(addr_sig);
|
||||
string name(stringf("%s.r%d", m.name.c_str(), i));
|
||||
bool clk_enable = false;
|
||||
bool clk_parity = true;
|
||||
bool transparency = false;
|
||||
SigSpec ena_sig = RTLIL::SigSpec(RTLIL::State::S1, 1);
|
||||
SigSpec clk_sig = RTLIL::SigSpec(RTLIL::State::S0, 1);
|
||||
read_port rp(name, clk_enable, clk_parity, transparency, clk_sig, ena_sig, addr_sig);
|
||||
m.add_memory_read_port(rp);
|
||||
cell_exprs.push_back(rp.gen_read(indent.c_str()));
|
||||
register_reverse_wire_map(stringf("%s.data", name.c_str()), data_sig);
|
||||
}
|
||||
|
||||
for (int i = 0; i < wr_ports; i++)
|
||||
|
@ -473,9 +684,16 @@ struct FirrtlWorker
|
|||
if (wr_clk_polarity[i] != State::S1)
|
||||
log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
|
||||
|
||||
string addr_expr = make_expr(cell->getPort("\\WR_ADDR").extract(i*abits, abits));
|
||||
string data_expr = make_expr(cell->getPort("\\WR_DATA").extract(i*width, width));
|
||||
string clk_expr = make_expr(cell->getPort("\\WR_CLK").extract(i));
|
||||
string name(stringf("%s.w%d", m.name.c_str(), i));
|
||||
bool clk_enable = true;
|
||||
bool clk_parity = true;
|
||||
bool transparency = false;
|
||||
SigSpec addr_sig =cell->getPort("\\WR_ADDR").extract(i*abits, abits);
|
||||
string addr_expr = make_expr(addr_sig);
|
||||
SigSpec data_sig =cell->getPort("\\WR_DATA").extract(i*width, width);
|
||||
string data_expr = make_expr(data_sig);
|
||||
SigSpec clk_sig = cell->getPort("\\WR_CLK").extract(i);
|
||||
string clk_expr = make_expr(clk_sig);
|
||||
|
||||
SigSpec wen_sig = cell->getPort("\\WR_EN").extract(i*width, width);
|
||||
string wen_expr = make_expr(wen_sig[0]);
|
||||
|
@ -484,13 +702,57 @@ struct FirrtlWorker
|
|||
if (wen_sig[0] != wen_sig[i])
|
||||
log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
|
||||
|
||||
cell_exprs.push_back(stringf(" %s.w%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str()));
|
||||
cell_exprs.push_back(stringf(" %s.w%d.data <= %s\n", mem_id.c_str(), i, data_expr.c_str()));
|
||||
cell_exprs.push_back(stringf(" %s.w%d.en <= %s\n", mem_id.c_str(), i, wen_expr.c_str()));
|
||||
cell_exprs.push_back(stringf(" %s.w%d.mask <= UInt<1>(1)\n", mem_id.c_str(), i));
|
||||
cell_exprs.push_back(stringf(" %s.w%d.clk <= asClock(%s)\n", mem_id.c_str(), i, clk_expr.c_str()));
|
||||
SigSpec mask_sig = RTLIL::SigSpec(RTLIL::State::S1, 1);
|
||||
write_port wp(name, clk_enable, clk_parity, transparency, clk_sig, wen_sig[0], addr_sig, mask_sig);
|
||||
m.add_memory_write_port(wp);
|
||||
cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), name.c_str(), data_expr.c_str()));
|
||||
cell_exprs.push_back(wp.gen_write(indent.c_str()));
|
||||
}
|
||||
register_memory(m);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$memwr", "$memrd", "$meminit"))
|
||||
{
|
||||
std::string cell_type = fid(cell->type);
|
||||
std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string());
|
||||
int abits = cell->parameters.at("\\ABITS").as_int();
|
||||
int width = cell->parameters.at("\\WIDTH").as_int();
|
||||
memory *mp = nullptr;
|
||||
if (cell->type == "$meminit" ) {
|
||||
log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str());
|
||||
} else {
|
||||
// It's a $memwr or $memrd. Remember the read/write port parameters for the eventual FIRRTL memory definition.
|
||||
auto addrSig = cell->getPort("\\ADDR");
|
||||
auto dataSig = cell->getPort("\\DATA");
|
||||
auto enableSig = cell->getPort("\\EN");
|
||||
auto clockSig = cell->getPort("\\CLK");
|
||||
Const clk_enable = cell->parameters.at("\\CLK_ENABLE");
|
||||
Const clk_polarity = cell->parameters.at("\\CLK_POLARITY");
|
||||
|
||||
// Do we already have an entry for this memory?
|
||||
if (memories.count(mem_id) == 0) {
|
||||
memory m(cell, mem_id, abits, 0, width);
|
||||
register_memory(m);
|
||||
}
|
||||
mp = &memories.at(mem_id);
|
||||
int portNum = 0;
|
||||
bool transparency = false;
|
||||
string data_expr = make_expr(dataSig);
|
||||
if (cell->type.in("$memwr")) {
|
||||
portNum = (int) mp->write_ports.size();
|
||||
write_port wp(stringf("%s.w%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig, dataSig);
|
||||
mp->add_memory_write_port(wp);
|
||||
cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), wp.name.c_str(), data_expr.c_str()));
|
||||
cell_exprs.push_back(wp.gen_write(indent.c_str()));
|
||||
} else if (cell->type.in("$memrd")) {
|
||||
portNum = (int) mp->read_ports.size();
|
||||
read_port rp(stringf("%s.r%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig);
|
||||
mp->add_memory_read_port(rp);
|
||||
cell_exprs.push_back(rp.gen_read(indent.c_str()));
|
||||
register_reverse_wire_map(stringf("%s.data", rp.name.c_str()), dataSig);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -513,7 +775,65 @@ struct FirrtlWorker
|
|||
continue;
|
||||
}
|
||||
|
||||
log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
// This may be a parameterized module - paramod.
|
||||
if (cell->type.substr(0, 8) == "$paramod")
|
||||
{
|
||||
process_instance(cell, wire_exprs);
|
||||
continue;
|
||||
}
|
||||
if (cell->type == "$shiftx") {
|
||||
// assign y = a[b +: y_width];
|
||||
// We'll extract the correct bits as part of the primop.
|
||||
|
||||
string y_id = make_id(cell->name);
|
||||
int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
|
||||
string a_expr = make_expr(cell->getPort("\\A"));
|
||||
// Get the initial bit selector
|
||||
string b_expr = make_expr(cell->getPort("\\B"));
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
|
||||
|
||||
if (cell->getParam("\\B_SIGNED").as_bool()) {
|
||||
// Use validif to constrain the selection (test the sign bit)
|
||||
auto b_string = b_expr.c_str();
|
||||
int b_sign = cell->parameters.at("\\B_WIDTH").as_int() - 1;
|
||||
b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string);
|
||||
}
|
||||
string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str());
|
||||
|
||||
cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
|
||||
register_reverse_wire_map(y_id, cell->getPort("\\Y"));
|
||||
continue;
|
||||
}
|
||||
if (cell->type == "$shift") {
|
||||
// assign y = a >> b;
|
||||
// where b may be negative
|
||||
|
||||
string y_id = make_id(cell->name);
|
||||
int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
|
||||
string a_expr = make_expr(cell->getPort("\\A"));
|
||||
string b_expr = make_expr(cell->getPort("\\B"));
|
||||
auto b_string = b_expr.c_str();
|
||||
int b_padded_width = cell->parameters.at("\\B_WIDTH").as_int();
|
||||
string expr;
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
|
||||
|
||||
if (cell->getParam("\\B_SIGNED").as_bool()) {
|
||||
// We generate a left or right shift based on the sign of b.
|
||||
std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr.c_str(), gen_dshl(b_expr, b_padded_width).c_str(), y_width);
|
||||
std::string dshr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
|
||||
expr = stringf("mux(%s < 0, %s, %s)",
|
||||
b_string,
|
||||
dshl.c_str(),
|
||||
dshr.c_str()
|
||||
);
|
||||
} else {
|
||||
expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
|
||||
}
|
||||
cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
|
||||
register_reverse_wire_map(y_id, cell->getPort("\\Y"));
|
||||
continue;
|
||||
}
|
||||
log_warning("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
}
|
||||
|
||||
for (auto conn : module->connections())
|
||||
|
@ -610,6 +930,24 @@ struct FirrtlWorker
|
|||
|
||||
f << stringf("\n");
|
||||
|
||||
// If we have any memory definitions, output them.
|
||||
for (auto kv : memories) {
|
||||
memory &m = kv.second;
|
||||
f << stringf(" mem %s:\n", m.name.c_str());
|
||||
f << stringf(" data-type => UInt<%d>\n", m.width);
|
||||
f << stringf(" depth => %d\n", m.size);
|
||||
for (int i = 0; i < (int) m.read_ports.size(); i += 1) {
|
||||
f << stringf(" reader => r%d\n", i);
|
||||
}
|
||||
for (int i = 0; i < (int) m.write_ports.size(); i += 1) {
|
||||
f << stringf(" writer => w%d\n", i);
|
||||
}
|
||||
f << stringf(" read-latency => %d\n", m.read_latency);
|
||||
f << stringf(" write-latency => %d\n", m.write_latency);
|
||||
f << stringf(" read-under-write => undefined\n");
|
||||
}
|
||||
f << stringf("\n");
|
||||
|
||||
for (auto str : cell_exprs)
|
||||
f << str;
|
||||
|
||||
|
@ -629,38 +967,53 @@ struct FirrtlBackend : public Backend {
|
|||
log(" write_firrtl [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write a FIRRTL netlist of the current design.\n");
|
||||
log("The following commands are executed by this command:\n");
|
||||
log(" pmuxtree\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-aig") {
|
||||
// aig_mode = true;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
size_t argidx = args.size(); // We aren't expecting any arguments.
|
||||
|
||||
// If we weren't explicitly passed a filename, use the last argument (if it isn't a flag).
|
||||
if (filename == "") {
|
||||
if (argidx > 0 && args[argidx - 1][0] != '-') {
|
||||
// extra_args and friends need to see this argument.
|
||||
argidx -= 1;
|
||||
filename = args[argidx];
|
||||
}
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
if (!design->full_selection())
|
||||
log_cmd_error("This command only operates on fully selected designs!\n");
|
||||
|
||||
log_header(design, "Executing FIRRTL backend.\n");
|
||||
log_push();
|
||||
|
||||
Module *top = design->top_module();
|
||||
|
||||
if (top == nullptr)
|
||||
log_error("No top module found!\n");
|
||||
Pass::call(design, stringf("pmuxtree"));
|
||||
|
||||
namecache.clear();
|
||||
autoid_counter = 0;
|
||||
|
||||
// Get the top module, or a reasonable facsimile - we need something for the circuit name.
|
||||
Module *top = design->top_module();
|
||||
Module *last = nullptr;
|
||||
// Generate module and wire names.
|
||||
for (auto module : design->modules()) {
|
||||
make_id(module->name);
|
||||
last = module;
|
||||
if (top == nullptr && module->get_bool_attribute("\\top")) {
|
||||
top = module;
|
||||
}
|
||||
for (auto wire : module->wires())
|
||||
if (wire->port_id)
|
||||
make_id(wire->name);
|
||||
}
|
||||
|
||||
if (top == nullptr)
|
||||
top = last;
|
||||
|
||||
*f << stringf("circuit %s:\n", make_id(top->name));
|
||||
|
||||
for (auto module : design->modules())
|
||||
|
|
|
@ -160,7 +160,10 @@ void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::
|
|||
}
|
||||
f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str());
|
||||
for (auto &it : cell->parameters) {
|
||||
f << stringf("%s parameter%s %s ", indent.c_str(), (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", it.first.c_str());
|
||||
f << stringf("%s parameter%s%s %s ", indent.c_str(),
|
||||
(it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "",
|
||||
(it.second.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "",
|
||||
it.first.c_str());
|
||||
dump_const(f, it.second);
|
||||
f << stringf("\n");
|
||||
}
|
||||
|
@ -204,7 +207,7 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const
|
|||
f << stringf("%s case ", indent.c_str());
|
||||
for (size_t i = 0; i < (*it)->compare.size(); i++) {
|
||||
if (i > 0)
|
||||
f << stringf(", ");
|
||||
f << stringf(" , ");
|
||||
dump_sigspec(f, (*it)->compare[i]);
|
||||
}
|
||||
f << stringf("\n");
|
||||
|
|
|
@ -127,7 +127,7 @@ struct IntersynthBackend : public Backend {
|
|||
RTLIL::Module *module = module_it.second;
|
||||
SigMap sigmap(module);
|
||||
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
if (module->get_blackbox_attribute())
|
||||
continue;
|
||||
if (module->memories.size() == 0 && module->processes.size() == 0 && module->cells_.size() == 0)
|
||||
continue;
|
||||
|
|
|
@ -48,7 +48,7 @@ struct ProtobufDesignSerializer
|
|||
|
||||
ProtobufDesignSerializer(bool use_selection, bool aig_mode) :
|
||||
aig_mode_(aig_mode), use_selection_(use_selection) { }
|
||||
|
||||
|
||||
string get_name(IdString name)
|
||||
{
|
||||
return RTLIL::unescape_id(name);
|
||||
|
@ -60,7 +60,7 @@ struct ProtobufDesignSerializer
|
|||
{
|
||||
for (auto ¶m : parameters) {
|
||||
std::string key = get_name(param.first);
|
||||
|
||||
|
||||
|
||||
yosys::pb::Parameter pb_param;
|
||||
|
||||
|
@ -207,7 +207,7 @@ struct ProtobufDesignSerializer
|
|||
(*models)[aig.name] = pb_model;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void serialize_design(yosys::pb::Design *pb, Design *design)
|
||||
{
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
|
|
|
@ -3,14 +3,30 @@ OBJS += backends/smt2/smt2.o
|
|||
|
||||
ifneq ($(CONFIG),mxe)
|
||||
ifneq ($(CONFIG),emcc)
|
||||
|
||||
# MSYS targets support yosys-smtbmc, but require a launcher script
|
||||
ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64))
|
||||
TARGETS += yosys-smtbmc.exe yosys-smtbmc-script.py
|
||||
# Needed to find the Python interpreter for yosys-smtbmc scripts.
|
||||
# Override if necessary, it is only used for msys2 targets.
|
||||
PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3)
|
||||
|
||||
yosys-smtbmc-script.py: backends/smt2/smtbmc.py
|
||||
$(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' \
|
||||
-e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@
|
||||
|
||||
yosys-smtbmc.exe: misc/launcher.c yosys-smtbmc-script.py
|
||||
$(P) gcc -DGUI=0 -O -s -o $@ $<
|
||||
# Other targets
|
||||
else
|
||||
TARGETS += yosys-smtbmc
|
||||
|
||||
yosys-smtbmc: backends/smt2/smtbmc.py
|
||||
$(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
|
||||
$(Q) chmod +x $@.new
|
||||
$(Q) mv $@.new $@
|
||||
endif
|
||||
|
||||
$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py))
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
|
@ -416,6 +416,7 @@ struct Smt2Worker
|
|||
for (char ch : expr) {
|
||||
if (ch == 'A') processed_expr += get_bv(sig_a);
|
||||
else if (ch == 'B') processed_expr += get_bv(sig_b);
|
||||
else if (ch == 'P') processed_expr += get_bv(cell->getPort("\\B"));
|
||||
else if (ch == 'L') processed_expr += is_signed ? "a" : "l";
|
||||
else if (ch == 'U') processed_expr += is_signed ? "s" : "u";
|
||||
else processed_expr += ch;
|
||||
|
@ -554,7 +555,7 @@ struct Smt2Worker
|
|||
|
||||
if (cell->type.in("$shift", "$shiftx")) {
|
||||
if (cell->getParam("\\B_SIGNED").as_bool()) {
|
||||
return export_bvop(cell, stringf("(ite (bvsge B #b%0*d) "
|
||||
return export_bvop(cell, stringf("(ite (bvsge P #b%0*d) "
|
||||
"(bvlshr A B) (bvlshr A (bvneg B)))",
|
||||
GetSize(cell->getPort("\\B")), 0), 's');
|
||||
} else {
|
||||
|
@ -887,8 +888,8 @@ struct Smt2Worker
|
|||
|
||||
string name_a = get_bool(cell->getPort("\\A"));
|
||||
string name_en = get_bool(cell->getPort("\\EN"));
|
||||
decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id,
|
||||
cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell)));
|
||||
string infostr = (cell->name[0] == '$' && cell->attributes.count("\\src")) ? cell->attributes.at("\\src").decode_string() : get_id(cell);
|
||||
decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, infostr.c_str()));
|
||||
|
||||
if (cell->type == "$cover")
|
||||
decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n",
|
||||
|
@ -1103,20 +1104,27 @@ struct Smt2Worker
|
|||
break;
|
||||
|
||||
Const initword = init_data.extract(i*width, width, State::Sx);
|
||||
Const initmask = initword;
|
||||
bool gen_init_constr = false;
|
||||
|
||||
for (auto bit : initword.bits)
|
||||
if (bit == State::S0 || bit == State::S1)
|
||||
for (int k = 0; k < GetSize(initword); k++) {
|
||||
if (initword[k] == State::S0 || initword[k] == State::S1) {
|
||||
gen_init_constr = true;
|
||||
initmask[k] = State::S1;
|
||||
} else {
|
||||
initmask[k] = State::S0;
|
||||
initword[k] = State::S0;
|
||||
}
|
||||
}
|
||||
|
||||
if (gen_init_constr)
|
||||
{
|
||||
if (statebv)
|
||||
/* FIXME */;
|
||||
else
|
||||
init_list.push_back(stringf("(= (select (|%s#%d#0| state) #b%s) #b%s) ; %s[%d]",
|
||||
init_list.push_back(stringf("(= (bvand (select (|%s#%d#0| state) #b%s) #b%s) #b%s) ; %s[%d]",
|
||||
get_id(module), arrayid, Const(i, abits).as_string().c_str(),
|
||||
initword.as_string().c_str(), get_id(cell), i));
|
||||
initmask.as_string().c_str(), initword.as_string().c_str(), get_id(cell), i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1535,7 +1543,7 @@ struct Smt2Backend : public Backend {
|
|||
|
||||
for (auto module : sorted_modules)
|
||||
{
|
||||
if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn())
|
||||
if (module->get_blackbox_attribute() || module->has_memories_warn() || module->has_processes_warn())
|
||||
continue;
|
||||
|
||||
log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module));
|
||||
|
|
|
@ -1484,11 +1484,11 @@ else: # not tempind, covermode
|
|||
smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
|
||||
smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
|
||||
smt_assert_consequent(get_constr_expr(constr_assumes, i))
|
||||
print_msg("Re-solving with appended steps..")
|
||||
if smt_check_sat() == "unsat":
|
||||
print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
|
||||
retstatus = False
|
||||
break
|
||||
print_msg("Re-solving with appended steps..")
|
||||
if smt_check_sat() == "unsat":
|
||||
print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
|
||||
retstatus = False
|
||||
break
|
||||
print_anyconsts(step)
|
||||
for i in range(step, last_check_step+1):
|
||||
print_failed_asserts(i)
|
||||
|
|
|
@ -784,7 +784,7 @@ class SmtIo:
|
|||
|
||||
def get_path(self, mod, path):
|
||||
assert mod in self.modinfo
|
||||
path = path.split(".")
|
||||
path = path.replace("\\", "/").split(".")
|
||||
|
||||
for i in range(len(path)-1):
|
||||
first = ".".join(path[0:i+1])
|
||||
|
|
|
@ -739,7 +739,7 @@ struct SmvBackend : public Backend {
|
|||
pool<Module*> modules;
|
||||
|
||||
for (auto module : design->modules())
|
||||
if (!module->get_bool_attribute("\\blackbox") && !module->has_memories_warn() && !module->has_processes_warn())
|
||||
if (!module->get_blackbox_attribute() && !module->has_memories_warn() && !module->has_processes_warn())
|
||||
modules.insert(module);
|
||||
|
||||
if (template_f.is_open())
|
||||
|
|
|
@ -212,7 +212,7 @@ struct SpiceBackend : public Backend {
|
|||
for (auto module_it : design->modules_)
|
||||
{
|
||||
RTLIL::Module *module = module_it.second;
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
if (module->get_blackbox_attribute())
|
||||
continue;
|
||||
|
||||
if (module->processes.size() != 0)
|
||||
|
|
|
@ -67,7 +67,7 @@ struct TableBackend : public Backend {
|
|||
|
||||
for (auto module : design->modules())
|
||||
{
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
if (module->get_blackbox_attribute())
|
||||
continue;
|
||||
|
||||
SigMap sigmap(module);
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal;
|
||||
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal, siminit;
|
||||
int auto_name_counter, auto_name_offset, auto_name_digits;
|
||||
std::map<RTLIL::IdString, int> auto_name_map;
|
||||
std::set<RTLIL::IdString> reg_wires, reg_ct;
|
||||
|
@ -126,6 +126,33 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true)
|
|||
break;
|
||||
}
|
||||
|
||||
const pool<string> keywords = {
|
||||
// IEEE 1800-2017 Annex B
|
||||
"accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before",
|
||||
"begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle",
|
||||
"checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint",
|
||||
"cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker",
|
||||
"endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage",
|
||||
"endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually",
|
||||
"expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function",
|
||||
"generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies",
|
||||
"import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface",
|
||||
"intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint",
|
||||
"macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor",
|
||||
"noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive",
|
||||
"priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent",
|
||||
"pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat",
|
||||
"restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until",
|
||||
"s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify",
|
||||
"specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on",
|
||||
"sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1",
|
||||
"tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with",
|
||||
"untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while",
|
||||
"wildcard", "wire", "with", "within", "wor", "xnor", "xor",
|
||||
};
|
||||
if (keywords.count(str))
|
||||
do_escape = true;
|
||||
|
||||
if (do_escape)
|
||||
return "\\" + std::string(str) + " ";
|
||||
return std::string(str);
|
||||
|
@ -156,10 +183,15 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name)
|
|||
return true;
|
||||
}
|
||||
|
||||
void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false)
|
||||
void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool escape_comment = false)
|
||||
{
|
||||
bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
|
||||
if (width < 0)
|
||||
width = data.bits.size() - offset;
|
||||
if (width == 0) {
|
||||
f << "\"\"";
|
||||
return;
|
||||
}
|
||||
if (nostr)
|
||||
goto dump_hex;
|
||||
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
|
||||
|
@ -244,7 +276,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
|
|||
}
|
||||
}
|
||||
} else {
|
||||
f << stringf("\"");
|
||||
if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0)
|
||||
f << stringf("\"");
|
||||
std::string str = data.decode_string();
|
||||
for (size_t i = 0; i < str.size(); i++) {
|
||||
if (str[i] == '\n')
|
||||
|
@ -262,7 +295,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
|
|||
else
|
||||
f << str[i];
|
||||
}
|
||||
f << stringf("\"");
|
||||
if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0)
|
||||
f << stringf("\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,6 +347,10 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima
|
|||
|
||||
void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
|
||||
{
|
||||
if (GetSize(sig) == 0) {
|
||||
f << "\"\"";
|
||||
return;
|
||||
}
|
||||
if (sig.is_chunk()) {
|
||||
dump_sigchunk(f, sig.as_chunk());
|
||||
} else {
|
||||
|
@ -338,7 +376,7 @@ void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString,
|
|||
else if (modattr && (it->second == Const(1, 1) || it->second == Const(1)))
|
||||
f << stringf(" 1 ");
|
||||
else
|
||||
dump_const(f, it->second, -1, 0, false, false, attr2comment);
|
||||
dump_const(f, it->second, -1, 0, false, attr2comment);
|
||||
f << stringf(" %s%c", attr2comment ? "*/" : "*)", term);
|
||||
}
|
||||
}
|
||||
|
@ -709,11 +747,14 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
|
||||
if (cell->type == "$shiftx")
|
||||
{
|
||||
std::string temp_id = next_auto_id();
|
||||
f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort("\\A"))-1, temp_id.c_str());
|
||||
dump_sigspec(f, cell->getPort("\\A"));
|
||||
f << stringf(";\n");
|
||||
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->getPort("\\Y"));
|
||||
f << stringf(" = ");
|
||||
dump_sigspec(f, cell->getPort("\\A"));
|
||||
f << stringf("[");
|
||||
f << stringf(" = %s[", temp_id.c_str());
|
||||
if (cell->getParam("\\B_SIGNED").as_bool())
|
||||
f << stringf("$signed(");
|
||||
dump_sigspec(f, cell->getPort("\\B"));
|
||||
|
@ -786,6 +827,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$tribuf")
|
||||
{
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->getPort("\\Y"));
|
||||
f << stringf(" = ");
|
||||
dump_sigspec(f, cell->getPort("\\EN"));
|
||||
f << stringf(" ? ");
|
||||
dump_sigspec(f, cell->getPort("\\A"));
|
||||
f << stringf(" : %d'bz;\n", cell->parameters.at("\\WIDTH").as_int());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$slice")
|
||||
{
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
|
@ -1023,43 +1076,46 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
use_rd_clk = cell->parameters["\\RD_CLK_ENABLE"].extract(i).as_bool();
|
||||
rd_clk_posedge = cell->parameters["\\RD_CLK_POLARITY"].extract(i).as_bool();
|
||||
rd_transparent = cell->parameters["\\RD_TRANSPARENT"].extract(i).as_bool();
|
||||
if (use_rd_clk)
|
||||
{
|
||||
std::ostringstream os;
|
||||
dump_sigspec(os, sig_rd_clk);
|
||||
clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str());
|
||||
if( clk_to_lof_body.count(clk_domain_str) == 0 )
|
||||
clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
|
||||
}
|
||||
if (use_rd_clk && !rd_transparent)
|
||||
{
|
||||
// for clocked read ports make something like:
|
||||
// reg [..] temp_id;
|
||||
// always @(posedge clk)
|
||||
// if (rd_en) temp_id <= array_reg[r_addr];
|
||||
// assign r_data = temp_id;
|
||||
std::string temp_id = next_auto_id();
|
||||
lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) );
|
||||
{
|
||||
std::ostringstream os;
|
||||
if (sig_rd_en != RTLIL::SigBit(true))
|
||||
dump_sigspec(os, sig_rd_clk);
|
||||
clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str());
|
||||
if( clk_to_lof_body.count(clk_domain_str) == 0 )
|
||||
clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
|
||||
}
|
||||
if (!rd_transparent)
|
||||
{
|
||||
// for clocked read ports make something like:
|
||||
// reg [..] temp_id;
|
||||
// always @(posedge clk)
|
||||
// if (rd_en) temp_id <= array_reg[r_addr];
|
||||
// assign r_data = temp_id;
|
||||
std::string temp_id = next_auto_id();
|
||||
lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) );
|
||||
{
|
||||
os << stringf("if (");
|
||||
dump_sigspec(os, sig_rd_en);
|
||||
os << stringf(") ");
|
||||
std::ostringstream os;
|
||||
if (sig_rd_en != RTLIL::SigBit(true))
|
||||
{
|
||||
os << stringf("if (");
|
||||
dump_sigspec(os, sig_rd_en);
|
||||
os << stringf(") ");
|
||||
}
|
||||
os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str());
|
||||
dump_sigspec(os, sig_rd_addr);
|
||||
os << stringf("];\n");
|
||||
clk_to_lof_body[clk_domain_str].push_back(os.str());
|
||||
}
|
||||
{
|
||||
std::ostringstream os;
|
||||
dump_sigspec(os, sig_rd_data);
|
||||
std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
|
||||
clk_to_lof_body[""].push_back(line);
|
||||
}
|
||||
os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str());
|
||||
dump_sigspec(os, sig_rd_addr);
|
||||
os << stringf("];\n");
|
||||
clk_to_lof_body[clk_domain_str].push_back(os.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream os;
|
||||
dump_sigspec(os, sig_rd_data);
|
||||
std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
|
||||
clk_to_lof_body[""].push_back(line);
|
||||
}
|
||||
} else {
|
||||
if (rd_transparent) {
|
||||
// for rd-transparent read-ports make something like:
|
||||
// reg [..] temp_id;
|
||||
// always @(posedge clk)
|
||||
|
@ -1079,15 +1135,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), temp_id.c_str());
|
||||
clk_to_lof_body[""].push_back(line);
|
||||
}
|
||||
} else {
|
||||
// for non-clocked read-ports make something like:
|
||||
// assign r_data = array_reg[r_addr];
|
||||
std::ostringstream os, os2;
|
||||
dump_sigspec(os, sig_rd_data);
|
||||
dump_sigspec(os2, sig_rd_addr);
|
||||
std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
|
||||
clk_to_lof_body[""].push_back(line);
|
||||
}
|
||||
} else {
|
||||
// for non-clocked read-ports make something like:
|
||||
// assign r_data = array_reg[r_addr];
|
||||
std::ostringstream os, os2;
|
||||
dump_sigspec(os, sig_rd_data);
|
||||
dump_sigspec(os2, sig_rd_addr);
|
||||
std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
|
||||
clk_to_lof_body[""].push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1189,6 +1245,118 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (cell->type.in("$assert", "$assume", "$cover"))
|
||||
{
|
||||
f << stringf("%s" "always @* if (", indent.c_str());
|
||||
dump_sigspec(f, cell->getPort("\\EN"));
|
||||
f << stringf(") %s(", cell->type.c_str()+1);
|
||||
dump_sigspec(f, cell->getPort("\\A"));
|
||||
f << stringf(");\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type.in("$specify2", "$specify3"))
|
||||
{
|
||||
f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str());
|
||||
|
||||
SigSpec en = cell->getPort("\\EN");
|
||||
if (en != State::S1) {
|
||||
f << stringf("if (");
|
||||
dump_sigspec(f, cell->getPort("\\EN"));
|
||||
f << stringf(") ");
|
||||
}
|
||||
|
||||
f << "(";
|
||||
if (cell->type == "$specify3" && cell->getParam("\\EDGE_EN").as_bool())
|
||||
f << (cell->getParam("\\EDGE_POL").as_bool() ? "posedge ": "negedge ");
|
||||
|
||||
dump_sigspec(f, cell->getPort("\\SRC"));
|
||||
|
||||
f << " ";
|
||||
if (cell->getParam("\\SRC_DST_PEN").as_bool())
|
||||
f << (cell->getParam("\\SRC_DST_POL").as_bool() ? "+": "-");
|
||||
f << (cell->getParam("\\FULL").as_bool() ? "*> ": "=> ");
|
||||
|
||||
if (cell->type == "$specify3") {
|
||||
f << "(";
|
||||
dump_sigspec(f, cell->getPort("\\DST"));
|
||||
f << " ";
|
||||
if (cell->getParam("\\DAT_DST_PEN").as_bool())
|
||||
f << (cell->getParam("\\DAT_DST_POL").as_bool() ? "+": "-");
|
||||
f << ": ";
|
||||
dump_sigspec(f, cell->getPort("\\DAT"));
|
||||
f << ")";
|
||||
} else {
|
||||
dump_sigspec(f, cell->getPort("\\DST"));
|
||||
}
|
||||
|
||||
bool bak_decimal = decimal;
|
||||
decimal = 1;
|
||||
|
||||
f << ") = (";
|
||||
dump_const(f, cell->getParam("\\T_RISE_MIN"));
|
||||
f << ":";
|
||||
dump_const(f, cell->getParam("\\T_RISE_TYP"));
|
||||
f << ":";
|
||||
dump_const(f, cell->getParam("\\T_RISE_MAX"));
|
||||
f << ", ";
|
||||
dump_const(f, cell->getParam("\\T_FALL_MIN"));
|
||||
f << ":";
|
||||
dump_const(f, cell->getParam("\\T_FALL_TYP"));
|
||||
f << ":";
|
||||
dump_const(f, cell->getParam("\\T_FALL_MAX"));
|
||||
f << ");\n";
|
||||
|
||||
decimal = bak_decimal;
|
||||
|
||||
f << stringf("%s" "endspecify\n", indent.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$specrule")
|
||||
{
|
||||
f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str());
|
||||
|
||||
string spec_type = cell->getParam("\\TYPE").decode_string();
|
||||
f << stringf("%s(", spec_type.c_str());
|
||||
|
||||
if (cell->getParam("\\SRC_PEN").as_bool())
|
||||
f << (cell->getParam("\\SRC_POL").as_bool() ? "posedge ": "negedge ");
|
||||
dump_sigspec(f, cell->getPort("\\SRC"));
|
||||
|
||||
if (cell->getPort("\\SRC_EN") != State::S1) {
|
||||
f << " &&& ";
|
||||
dump_sigspec(f, cell->getPort("\\SRC_EN"));
|
||||
}
|
||||
|
||||
f << ", ";
|
||||
if (cell->getParam("\\DST_PEN").as_bool())
|
||||
f << (cell->getParam("\\DST_POL").as_bool() ? "posedge ": "negedge ");
|
||||
dump_sigspec(f, cell->getPort("\\DST"));
|
||||
|
||||
if (cell->getPort("\\DST_EN") != State::S1) {
|
||||
f << " &&& ";
|
||||
dump_sigspec(f, cell->getPort("\\DST_EN"));
|
||||
}
|
||||
|
||||
bool bak_decimal = decimal;
|
||||
decimal = 1;
|
||||
|
||||
f << ", ";
|
||||
dump_const(f, cell->getParam("\\T_LIMIT"));
|
||||
|
||||
if (spec_type == "$setuphold" || spec_type == "$recrem" || spec_type == "$fullskew") {
|
||||
f << ", ";
|
||||
dump_const(f, cell->getParam("\\T_LIMIT2"));
|
||||
}
|
||||
|
||||
f << ");\n";
|
||||
decimal = bak_decimal;
|
||||
|
||||
f << stringf("%s" "endspecify\n", indent.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_
|
||||
// FIXME: $sr, $dlatch, $memrd, $memwr, $fsm
|
||||
|
||||
|
@ -1211,8 +1379,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
if (it != cell->parameters.begin())
|
||||
f << stringf(",");
|
||||
f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
|
||||
bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
|
||||
dump_const(f, it->second, -1, 0, false, is_signed);
|
||||
dump_const(f, it->second);
|
||||
f << stringf(")");
|
||||
}
|
||||
f << stringf("\n%s" ")", indent.c_str());
|
||||
|
@ -1259,12 +1426,20 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
if (defparam && cell->parameters.size() > 0) {
|
||||
for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) {
|
||||
f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str());
|
||||
bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
|
||||
dump_const(f, it->second, -1, 0, false, is_signed);
|
||||
dump_const(f, it->second);
|
||||
f << stringf(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (siminit && reg_ct.count(cell->type) && cell->hasPort("\\Q")) {
|
||||
std::stringstream ss;
|
||||
dump_reg_init(ss, cell->getPort("\\Q"));
|
||||
if (!ss.str().empty()) {
|
||||
f << stringf("%sinitial %s.Q", indent.c_str(), cell_name.c_str());
|
||||
f << ss.str();
|
||||
f << ";\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
|
||||
|
@ -1443,7 +1618,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
|
|||
SigSpec sig = active_sigmap(wire);
|
||||
Const val = wire->attributes.at("\\init");
|
||||
for (int i = 0; i < GetSize(sig) && i < GetSize(val); i++)
|
||||
active_initdata[sig[i]] = val.bits.at(i);
|
||||
if (val[i] == State::S0 || val[i] == State::S1)
|
||||
active_initdata[sig[i]] = val[i];
|
||||
}
|
||||
|
||||
if (!module->processes.empty())
|
||||
|
@ -1553,6 +1729,10 @@ struct VerilogBackend : public Backend {
|
|||
log(" without this option all internal cells are converted to Verilog\n");
|
||||
log(" expressions.\n");
|
||||
log("\n");
|
||||
log(" -siminit\n");
|
||||
log(" add initial statements with hierarchical refs to initialize FFs when\n");
|
||||
log(" in -noexpr mode.\n");
|
||||
log("\n");
|
||||
log(" -nodec\n");
|
||||
log(" 32-bit constant values are by default dumped as decimal numbers,\n");
|
||||
log(" not bit pattern. This option deactivates this feature and instead\n");
|
||||
|
@ -1609,11 +1789,14 @@ struct VerilogBackend : public Backend {
|
|||
nostr = false;
|
||||
defparam = false;
|
||||
decimal = false;
|
||||
siminit = false;
|
||||
auto_prefix = "";
|
||||
|
||||
bool blackboxes = false;
|
||||
bool selected = false;
|
||||
|
||||
auto_name_map.clear();
|
||||
reg_wires.clear();
|
||||
reg_ct.clear();
|
||||
|
||||
reg_ct.insert("$dff");
|
||||
|
@ -1685,6 +1868,10 @@ struct VerilogBackend : public Backend {
|
|||
decimal = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-siminit") {
|
||||
siminit = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-blackboxes") {
|
||||
blackboxes = true;
|
||||
continue;
|
||||
|
@ -1705,7 +1892,7 @@ struct VerilogBackend : public Backend {
|
|||
|
||||
*f << stringf("/* Generated by %s */\n", yosys_version_str);
|
||||
for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
|
||||
if (it->second->get_bool_attribute("\\blackbox") != blackboxes)
|
||||
if (it->second->get_blackbox_attribute() != blackboxes)
|
||||
continue;
|
||||
if (selected && !design->selected_whole_module(it->first)) {
|
||||
if (design->selected_module(it->first))
|
||||
|
@ -1716,6 +1903,8 @@ struct VerilogBackend : public Backend {
|
|||
dump_module(*f, "", it->second);
|
||||
}
|
||||
|
||||
auto_name_map.clear();
|
||||
reg_wires.clear();
|
||||
reg_ct.clear();
|
||||
}
|
||||
} VerilogBackend;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
demo.bit
|
||||
demo_phy.area
|
||||
full.v
|
||||
*.log
|
||||
*.h
|
||||
*.tde
|
||||
*.svf
|
|
@ -0,0 +1,12 @@
|
|||
LED Blink project for Anlogic Lichee Tang board.
|
||||
|
||||
Follow the install instructions for the Tang Dynasty IDE from given link below.
|
||||
|
||||
https://tang.sipeed.com/en/getting-started/installing-td-ide/linux/
|
||||
|
||||
|
||||
set TD_HOME env variable to the full path to the TD <TD Install Directory> as follow.
|
||||
|
||||
export TD_HOME=<TD Install Directory>
|
||||
|
||||
then run "bash build.sh" in this directory.
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
yosys demo.ys
|
||||
$TD_HOME/bin/td build.tcl
|
|
@ -0,0 +1,11 @@
|
|||
import_device eagle_s20.db -package BG256
|
||||
read_verilog full.v -top demo
|
||||
read_adc demo.adc
|
||||
optimize_rtl
|
||||
map_macro
|
||||
map
|
||||
pack
|
||||
place
|
||||
route
|
||||
report_area -io_info -file demo_phy.area
|
||||
bitgen -bit demo.bit -version 0X0000 -svf demo.svf -svf_comment_on -g ucode:00000000000000000000000000000000
|
|
@ -0,0 +1,2 @@
|
|||
set_pin_assignment {CLK_IN} { LOCATION = K14; } ##24MHZ
|
||||
set_pin_assignment {R_LED} { LOCATION = R3; } ##R_LED
|
|
@ -0,0 +1,18 @@
|
|||
module demo (
|
||||
input wire CLK_IN,
|
||||
output wire R_LED
|
||||
);
|
||||
parameter time1 = 30'd12_000_000;
|
||||
reg led_state;
|
||||
reg [29:0] count;
|
||||
|
||||
always @(posedge CLK_IN)begin
|
||||
if(count == time1)begin
|
||||
count<= 30'd0;
|
||||
led_state <= ~led_state;
|
||||
end
|
||||
else
|
||||
count <= count + 1'b1;
|
||||
end
|
||||
assign R_LED = led_state;
|
||||
endmodule
|
|
@ -0,0 +1,3 @@
|
|||
read_verilog demo.v
|
||||
synth_anlogic -top demo
|
||||
write_verilog full.v
|
|
@ -1,3 +1,4 @@
|
|||
/netlist.edn
|
||||
/netlist.v
|
||||
/work
|
||||
/netlist.vm
|
||||
/example.stp
|
||||
/proj
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# Add placement constraints here
|
||||
|
||||
set_io clk -pinname H16 -fixed yes -DIRECTION INPUT
|
||||
|
||||
set_io SW1 -pinname H12 -fixed yes -DIRECTION INPUT
|
||||
set_io SW2 -pinname H13 -fixed yes -DIRECTION INPUT
|
||||
|
||||
set_io LED1 -pinname J16 -fixed yes -DIRECTION OUTPUT
|
||||
set_io LED2 -pinname M16 -fixed yes -DIRECTION OUTPUT
|
||||
set_io LED3 -pinname K16 -fixed yes -DIRECTION OUTPUT
|
||||
set_io LED4 -pinname N16 -fixed yes -DIRECTION OUTPUT
|
||||
|
||||
set_io AA -pinname L12 -fixed yes -DIRECTION OUTPUT
|
||||
set_io AB -pinname L13 -fixed yes -DIRECTION OUTPUT
|
||||
set_io AC -pinname M13 -fixed yes -DIRECTION OUTPUT
|
||||
set_io AD -pinname N15 -fixed yes -DIRECTION OUTPUT
|
||||
set_io AE -pinname L11 -fixed yes -DIRECTION OUTPUT
|
||||
set_io AF -pinname L14 -fixed yes -DIRECTION OUTPUT
|
||||
set_io AG -pinname N14 -fixed yes -DIRECTION OUTPUT
|
||||
set_io CA -pinname M15 -fixed yes -DIRECTION OUTPUT
|
|
@ -0,0 +1,2 @@
|
|||
# Add timing constraints here
|
||||
create_clock -period 10.000 -waveform {0.000 5.000} [get_ports {clk}]
|
|
@ -1,22 +1,64 @@
|
|||
module top (
|
||||
module example (
|
||||
input clk,
|
||||
input SW1,
|
||||
input SW2,
|
||||
output LED1,
|
||||
output LED2,
|
||||
output LED3,
|
||||
output LED4,
|
||||
output LED5
|
||||
|
||||
output AA, AB, AC, AD,
|
||||
output AE, AF, AG, CA
|
||||
);
|
||||
|
||||
localparam BITS = 5;
|
||||
localparam BITS = 8;
|
||||
localparam LOG2DELAY = 22;
|
||||
|
||||
reg [BITS+LOG2DELAY-1:0] counter = 0;
|
||||
reg [BITS-1:0] outcnt;
|
||||
|
||||
always @(posedge clk) begin
|
||||
counter <= counter + 1;
|
||||
counter <= counter + SW1 + SW2 + 1;
|
||||
outcnt <= counter >> LOG2DELAY;
|
||||
end
|
||||
|
||||
assign {LED1, LED2, LED3, LED4, LED5} = outcnt ^ (outcnt >> 1);
|
||||
assign {LED1, LED2, LED3, LED4} = outcnt ^ (outcnt >> 1);
|
||||
|
||||
// assign CA = counter[10];
|
||||
// seg7enc seg7encinst (
|
||||
// .seg({AA, AB, AC, AD, AE, AF, AG}),
|
||||
// .dat(CA ? outcnt[3:0] : outcnt[7:4])
|
||||
// );
|
||||
|
||||
assign {AA, AB, AC, AD, AE, AF, AG} = ~(7'b 100_0000 >> outcnt[6:4]);
|
||||
assign CA = outcnt[7];
|
||||
endmodule
|
||||
|
||||
module seg7enc (
|
||||
input [3:0] dat,
|
||||
output [6:0] seg
|
||||
);
|
||||
reg [6:0] seg_inv;
|
||||
always @* begin
|
||||
seg_inv = 0;
|
||||
case (dat)
|
||||
4'h0: seg_inv = 7'b 0111111;
|
||||
4'h1: seg_inv = 7'b 0000110;
|
||||
4'h2: seg_inv = 7'b 1011011;
|
||||
4'h3: seg_inv = 7'b 1001111;
|
||||
4'h4: seg_inv = 7'b 1100110;
|
||||
4'h5: seg_inv = 7'b 1101101;
|
||||
4'h6: seg_inv = 7'b 1111101;
|
||||
4'h7: seg_inv = 7'b 0000111;
|
||||
4'h8: seg_inv = 7'b 1111111;
|
||||
4'h9: seg_inv = 7'b 1101111;
|
||||
4'hA: seg_inv = 7'b 1110111;
|
||||
4'hB: seg_inv = 7'b 1111100;
|
||||
4'hC: seg_inv = 7'b 0111001;
|
||||
4'hD: seg_inv = 7'b 1011110;
|
||||
4'hE: seg_inv = 7'b 1111001;
|
||||
4'hF: seg_inv = 7'b 1110001;
|
||||
endcase
|
||||
end
|
||||
assign seg = ~seg_inv;
|
||||
endmodule
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
read_verilog example.v
|
||||
synth_sf2 -top top -edif netlist.edn
|
||||
write_verilog netlist.v
|
|
@ -1,35 +1,57 @@
|
|||
# Run with "libero SCRIPT:libero.tcl"
|
||||
|
||||
file delete -force proj
|
||||
|
||||
new_project \
|
||||
-name top \
|
||||
-location work \
|
||||
-name example \
|
||||
-location proj \
|
||||
-block_mode 0 \
|
||||
-hdl "VERILOG" \
|
||||
-family IGLOO2 \
|
||||
-die PA4MGL500 \
|
||||
-package tq144 \
|
||||
-speed -1 \
|
||||
-hdl VERILOG
|
||||
-die PA4MGL2500 \
|
||||
-package vf256 \
|
||||
-speed -1
|
||||
|
||||
# import_files -edif "[pwd]/netlist.edn"
|
||||
import_files -hdl_source {netlist.vm}
|
||||
import_files -sdc {example.sdc}
|
||||
import_files -io_pdc {example.pdc}
|
||||
build_design_hierarchy
|
||||
set_option -synth 0
|
||||
|
||||
import_files -hdl_source "[pwd]/netlist.v"
|
||||
set_root top
|
||||
organize_tool_files -tool PLACEROUTE \
|
||||
-file {proj/constraint/example.sdc} \
|
||||
-file {proj/constraint/io/example.pdc} \
|
||||
-input_type constraint
|
||||
|
||||
save_project
|
||||
organize_tool_files -tool VERIFYTIMING \
|
||||
-file {proj/constraint/example.sdc} \
|
||||
-input_type constraint
|
||||
|
||||
puts "**> SYNTHESIZE"
|
||||
run_tool -name {SYNTHESIZE}
|
||||
puts "<** SYNTHESIZE"
|
||||
configure_tool -name PLACEROUTE \
|
||||
-params TDPR:true \
|
||||
-params PDPR:false \
|
||||
-params EFFORT_LEVEL:false \
|
||||
-params REPAIR_MIN_DELAY:false
|
||||
|
||||
puts ""
|
||||
puts "**> COMPILE"
|
||||
run_tool -name {COMPILE}
|
||||
puts "<** COMPILE"
|
||||
|
||||
puts ""
|
||||
puts "**> PLACEROUTE"
|
||||
run_tool -name {PLACEROUTE}
|
||||
puts "<** PLACEROUTE"
|
||||
|
||||
# puts "**> export_bitstream"
|
||||
# export_bitstream_file -trusted_facility_file 1 -trusted_facility_file_components {FABRIC}
|
||||
# puts "<** export_bitstream"
|
||||
puts ""
|
||||
puts "**> VERIFYTIMING"
|
||||
run_tool -name {VERIFYTIMING}
|
||||
puts "<** VERIFYTIMING"
|
||||
|
||||
puts ""
|
||||
puts "**> BITSTREAM"
|
||||
export_bitstream_file -trusted_facility_file 1 -trusted_facility_file_components {FABRIC}
|
||||
puts "<** BITSTREAM"
|
||||
|
||||
puts ""
|
||||
exit 0
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
rm -rf work
|
||||
yosys example.ys
|
||||
LM_LICENSE_FILE=1702@`hostname` /opt/microsemi/Libero_SoC_v11.9/Libero/bin/libero SCRIPT:libero.tcl
|
||||
yosys -p 'synth_sf2 -top example -edif netlist.edn -vlog netlist.vm' example.v
|
||||
export LM_LICENSE_FILE=${LM_LICENSE_FILE:-1702@localhost}
|
||||
/opt/microsemi/Libero_SoC_v12.0/Libero/bin/libero SCRIPT:libero.tcl
|
||||
cp proj/designer/example/export/example.stp .
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
out/**
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
from pyosys import libyosys as ys
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
class CellStatsPass(ys.Pass):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("cell_stats", "Shows cell stats as plot")
|
||||
|
||||
def py_help(self):
|
||||
ys.log("This pass uses the matplotlib library to display cell stats\n")
|
||||
|
||||
def py_execute(self, args, design):
|
||||
ys.log_header(design, "Plotting cell stats\n")
|
||||
cell_stats = {}
|
||||
for module in design.selected_whole_modules_warn():
|
||||
for cell in module.selected_cells():
|
||||
if cell.type.str() in cell_stats:
|
||||
cell_stats[cell.type.str()] += 1
|
||||
else:
|
||||
cell_stats[cell.type.str()] = 1
|
||||
plt.bar(range(len(cell_stats)), height = list(cell_stats.values()),align='center')
|
||||
plt.xticks(range(len(cell_stats)), list(cell_stats.keys()))
|
||||
plt.show()
|
||||
|
||||
def py_clear_flags(self):
|
||||
ys.log("Clear Flags - CellStatsPass\n")
|
||||
|
||||
p = CellStatsPass()
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
from pyosys import libyosys as ys
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
design = ys.Design()
|
||||
ys.run_pass("read_verilog ../../tests/simple/fiedler-cooley.v", design);
|
||||
ys.run_pass("prep", design)
|
||||
ys.run_pass("opt -full", design)
|
||||
|
||||
cell_stats = {}
|
||||
for module in design.selected_whole_modules_warn():
|
||||
for cell in module.selected_cells():
|
||||
if cell.type.str() in cell_stats:
|
||||
cell_stats[cell.type.str()] += 1
|
||||
else:
|
||||
cell_stats[cell.type.str()] = 1
|
||||
plt.bar(range(len(cell_stats)), height = list(cell_stats.values()),align='center')
|
||||
plt.xticks(range(len(cell_stats)), list(cell_stats.keys()))
|
||||
plt.show()
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += frontends/aiger/aigerparse.o
|
||||
|
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
* Eddie Hung <eddie@fpgeh.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
// [[CITE]] The AIGER And-Inverter Graph (AIG) Format Version 20071012
|
||||
// Armin Biere. The AIGER And-Inverter Graph (AIG) Format Version 20071012. Technical Report 07/1, October 2011, FMV Reports Series, Institute for Formal Models and Verification, Johannes Kepler University, Altenbergerstr. 69, 4040 Linz, Austria.
|
||||
// http://fmv.jku.at/papers/Biere-FMV-TR-07-1.pdf
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
#include <array>
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "aigerparse.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name)
|
||||
: design(design), f(f), clk_name(clk_name)
|
||||
{
|
||||
module = new RTLIL::Module;
|
||||
module->name = module_name;
|
||||
if (design->module(module->name))
|
||||
log_error("Duplicate definition of module %s!\n", log_id(module->name));
|
||||
}
|
||||
|
||||
void AigerReader::parse_aiger()
|
||||
{
|
||||
std::string header;
|
||||
f >> header;
|
||||
if (header != "aag" && header != "aig")
|
||||
log_error("Unsupported AIGER file!\n");
|
||||
|
||||
// Parse rest of header
|
||||
if (!(f >> M >> I >> L >> O >> A))
|
||||
log_error("Invalid AIGER header\n");
|
||||
|
||||
// Optional values
|
||||
B = C = J = F = 0;
|
||||
if (f.peek() != ' ') goto end_of_header;
|
||||
if (!(f >> B)) log_error("Invalid AIGER header\n");
|
||||
if (f.peek() != ' ') goto end_of_header;
|
||||
if (!(f >> C)) log_error("Invalid AIGER header\n");
|
||||
if (f.peek() != ' ') goto end_of_header;
|
||||
if (!(f >> J)) log_error("Invalid AIGER header\n");
|
||||
if (f.peek() != ' ') goto end_of_header;
|
||||
if (!(f >> F)) log_error("Invalid AIGER header\n");
|
||||
end_of_header:
|
||||
|
||||
std::string line;
|
||||
std::getline(f, line); // Ignore up to start of next line, as standard
|
||||
// says anything that follows could be used for
|
||||
// optional sections
|
||||
|
||||
log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F);
|
||||
|
||||
line_count = 1;
|
||||
|
||||
if (header == "aag")
|
||||
parse_aiger_ascii();
|
||||
else if (header == "aig")
|
||||
parse_aiger_binary();
|
||||
else
|
||||
log_abort();
|
||||
|
||||
// Parse footer (symbol table, comments, etc.)
|
||||
unsigned l1;
|
||||
std::string s;
|
||||
for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) {
|
||||
if (c == 'i' || c == 'l' || c == 'o') {
|
||||
f.ignore(1);
|
||||
if (!(f >> l1 >> s))
|
||||
log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count);
|
||||
|
||||
if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size()))
|
||||
log_error("Line %u has invalid symbol position!\n", line_count);
|
||||
|
||||
RTLIL::Wire* wire;
|
||||
if (c == 'i') wire = inputs[l1];
|
||||
else if (c == 'l') wire = latches[l1];
|
||||
else if (c == 'o') wire = outputs[l1];
|
||||
else log_abort();
|
||||
|
||||
module->rename(wire, stringf("\\%s", s.c_str()));
|
||||
}
|
||||
else if (c == 'b' || c == 'j' || c == 'f') {
|
||||
// TODO
|
||||
}
|
||||
else if (c == 'c') {
|
||||
f.ignore(1);
|
||||
if (f.peek() == '\n')
|
||||
break;
|
||||
// Else constraint (TODO)
|
||||
}
|
||||
else
|
||||
log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
}
|
||||
|
||||
module->fixup_ports();
|
||||
design->add(module);
|
||||
}
|
||||
|
||||
static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal)
|
||||
{
|
||||
const unsigned variable = literal >> 1;
|
||||
const bool invert = literal & 1;
|
||||
RTLIL::IdString wire_name(stringf("\\n%d%s", variable, invert ? "_inv" : "")); // FIXME: is "_inv" the right suffix?
|
||||
RTLIL::Wire *wire = module->wire(wire_name);
|
||||
if (wire) return wire;
|
||||
log_debug("Creating %s\n", wire_name.c_str());
|
||||
wire = module->addWire(wire_name);
|
||||
if (!invert) return wire;
|
||||
RTLIL::IdString wire_inv_name(stringf("\\n%d", variable));
|
||||
RTLIL::Wire *wire_inv = module->wire(wire_inv_name);
|
||||
if (wire_inv) {
|
||||
if (module->cell(wire_inv_name)) return wire;
|
||||
}
|
||||
else {
|
||||
log_debug("Creating %s\n", wire_inv_name.c_str());
|
||||
wire_inv = module->addWire(wire_inv_name);
|
||||
}
|
||||
|
||||
log_debug("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str());
|
||||
module->addNotGate(stringf("\\n%d_not", variable), wire_inv, wire); // FIXME: is "_not" the right suffix?
|
||||
|
||||
return wire;
|
||||
}
|
||||
|
||||
void AigerReader::parse_aiger_ascii()
|
||||
{
|
||||
std::string line;
|
||||
std::stringstream ss;
|
||||
|
||||
unsigned l1, l2, l3;
|
||||
|
||||
// Parse inputs
|
||||
for (unsigned i = 0; i < I; ++i, ++line_count) {
|
||||
if (!(f >> l1))
|
||||
log_error("Line %u cannot be interpreted as an input!\n", line_count);
|
||||
log_debug("%d is an input\n", l1);
|
||||
log_assert(!(l1 & 1)); // TODO: Inputs can't be inverted?
|
||||
RTLIL::Wire *wire = createWireIfNotExists(module, l1);
|
||||
wire->port_input = true;
|
||||
inputs.push_back(wire);
|
||||
}
|
||||
|
||||
// Parse latches
|
||||
RTLIL::Wire *clk_wire = nullptr;
|
||||
if (L > 0) {
|
||||
clk_wire = module->wire(clk_name);
|
||||
log_assert(!clk_wire);
|
||||
log_debug("Creating %s\n", clk_name.c_str());
|
||||
clk_wire = module->addWire(clk_name);
|
||||
clk_wire->port_input = true;
|
||||
}
|
||||
for (unsigned i = 0; i < L; ++i, ++line_count) {
|
||||
if (!(f >> l1 >> l2))
|
||||
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
|
||||
log_debug("%d %d is a latch\n", l1, l2);
|
||||
log_assert(!(l1 & 1)); // TODO: Latch outputs can't be inverted?
|
||||
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
|
||||
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
|
||||
|
||||
module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire);
|
||||
|
||||
// Reset logic is optional in AIGER 1.9
|
||||
if (f.peek() == ' ') {
|
||||
if (!(f >> l3))
|
||||
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
|
||||
|
||||
if (l3 == 0 || l3 == 1)
|
||||
q_wire->attributes["\\init"] = RTLIL::Const(l3);
|
||||
else if (l3 == l1) {
|
||||
//q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx);
|
||||
}
|
||||
else
|
||||
log_error("Line %u has invalid reset literal for latch!\n", line_count);
|
||||
}
|
||||
else {
|
||||
// AIGER latches are assumed to be initialized to zero
|
||||
q_wire->attributes["\\init"] = RTLIL::Const(0);
|
||||
}
|
||||
latches.push_back(q_wire);
|
||||
}
|
||||
|
||||
// Parse outputs
|
||||
for (unsigned i = 0; i < O; ++i, ++line_count) {
|
||||
if (!(f >> l1))
|
||||
log_error("Line %u cannot be interpreted as an output!\n", line_count);
|
||||
|
||||
log_debug("%d is an output\n", l1);
|
||||
RTLIL::Wire *wire = createWireIfNotExists(module, l1);
|
||||
wire->port_output = true;
|
||||
outputs.push_back(wire);
|
||||
}
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse bad state properties
|
||||
for (unsigned i = 0; i < B; ++i, ++line_count)
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse invariant constraints
|
||||
for (unsigned i = 0; i < C; ++i, ++line_count)
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse justice properties
|
||||
for (unsigned i = 0; i < J; ++i, ++line_count)
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse fairness constraints
|
||||
for (unsigned i = 0; i < F; ++i, ++line_count)
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// Parse AND
|
||||
for (unsigned i = 0; i < A; ++i) {
|
||||
if (!(f >> l1 >> l2 >> l3))
|
||||
log_error("Line %u cannot be interpreted as an AND!\n", line_count);
|
||||
|
||||
log_debug("%d %d %d is an AND\n", l1, l2, l3);
|
||||
log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted?
|
||||
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
|
||||
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
|
||||
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
|
||||
module->addAndGate(NEW_ID, i1_wire, i2_wire, o_wire);
|
||||
}
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
}
|
||||
|
||||
static unsigned parse_next_delta_literal(std::istream &f, unsigned ref)
|
||||
{
|
||||
unsigned x = 0, i = 0;
|
||||
unsigned char ch;
|
||||
while ((ch = f.get()) & 0x80)
|
||||
x |= (ch & 0x7f) << (7 * i++);
|
||||
return ref - (x | (ch << (7 * i)));
|
||||
}
|
||||
|
||||
void AigerReader::parse_aiger_binary()
|
||||
{
|
||||
unsigned l1, l2, l3;
|
||||
std::string line;
|
||||
|
||||
// Parse inputs
|
||||
for (unsigned i = 1; i <= I; ++i) {
|
||||
RTLIL::Wire *wire = createWireIfNotExists(module, i << 1);
|
||||
wire->port_input = true;
|
||||
inputs.push_back(wire);
|
||||
}
|
||||
|
||||
// Parse latches
|
||||
RTLIL::Wire *clk_wire = nullptr;
|
||||
if (L > 0) {
|
||||
clk_wire = module->wire(clk_name);
|
||||
log_assert(!clk_wire);
|
||||
log_debug("Creating %s\n", clk_name.c_str());
|
||||
clk_wire = module->addWire(clk_name);
|
||||
clk_wire->port_input = true;
|
||||
}
|
||||
l1 = (I+1) * 2;
|
||||
for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
|
||||
if (!(f >> l2))
|
||||
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
|
||||
log_debug("%d %d is a latch\n", l1, l2);
|
||||
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
|
||||
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
|
||||
|
||||
module->addDff(NEW_ID, clk_wire, d_wire, q_wire);
|
||||
|
||||
// Reset logic is optional in AIGER 1.9
|
||||
if (f.peek() == ' ') {
|
||||
if (!(f >> l3))
|
||||
log_error("Line %u cannot be interpreted as a latch!\n", line_count);
|
||||
|
||||
if (l3 == 0 || l3 == 1)
|
||||
q_wire->attributes["\\init"] = RTLIL::Const(l3);
|
||||
else if (l3 == l1) {
|
||||
//q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx);
|
||||
}
|
||||
else
|
||||
log_error("Line %u has invalid reset literal for latch!\n", line_count);
|
||||
}
|
||||
else {
|
||||
// AIGER latches are assumed to be initialized to zero
|
||||
q_wire->attributes["\\init"] = RTLIL::Const(0);
|
||||
}
|
||||
latches.push_back(q_wire);
|
||||
}
|
||||
|
||||
// Parse outputs
|
||||
for (unsigned i = 0; i < O; ++i, ++line_count) {
|
||||
if (!(f >> l1))
|
||||
log_error("Line %u cannot be interpreted as an output!\n", line_count);
|
||||
|
||||
log_debug("%d is an output\n", l1);
|
||||
RTLIL::Wire *wire = createWireIfNotExists(module, l1);
|
||||
wire->port_output = true;
|
||||
outputs.push_back(wire);
|
||||
}
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse bad state properties
|
||||
for (unsigned i = 0; i < B; ++i, ++line_count)
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse invariant constraints
|
||||
for (unsigned i = 0; i < C; ++i, ++line_count)
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse justice properties
|
||||
for (unsigned i = 0; i < J; ++i, ++line_count)
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// TODO: Parse fairness constraints
|
||||
for (unsigned i = 0; i < F; ++i, ++line_count)
|
||||
std::getline(f, line); // Ignore up to start of next line
|
||||
|
||||
// Parse AND
|
||||
l1 = (I+L+1) << 1;
|
||||
for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) {
|
||||
l2 = parse_next_delta_literal(f, l1);
|
||||
l3 = parse_next_delta_literal(f, l2);
|
||||
|
||||
log_debug("%d %d %d is an AND\n", l1, l2, l3);
|
||||
log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted?
|
||||
RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
|
||||
RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
|
||||
RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
|
||||
|
||||
RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$_AND_");
|
||||
and_cell->setPort("\\A", i1_wire);
|
||||
and_cell->setPort("\\B", i2_wire);
|
||||
and_cell->setPort("\\Y", o_wire);
|
||||
}
|
||||
}
|
||||
|
||||
struct AigerFrontend : public Frontend {
|
||||
AigerFrontend() : Frontend("aiger", "read AIGER file") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" read_aiger [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Load module from an AIGER file into the current design.\n");
|
||||
log("\n");
|
||||
log(" -module_name <module_name>\n");
|
||||
log(" Name of module to be created (default: <filename>)"
|
||||
#ifdef _WIN32
|
||||
"top" // FIXME
|
||||
#else
|
||||
"<filename>"
|
||||
#endif
|
||||
")\n");
|
||||
log("\n");
|
||||
log(" -clk_name <wire_name>\n");
|
||||
log(" AIGER latches to be transformed into posedge DFFs clocked by wire of");
|
||||
log(" this name (default: clk)\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing AIGER frontend.\n");
|
||||
|
||||
RTLIL::IdString clk_name = "\\clk";
|
||||
RTLIL::IdString module_name;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-module_name" && argidx+1 < args.size()) {
|
||||
module_name = RTLIL::escape_id(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
if (arg == "-clk_name" && argidx+1 < args.size()) {
|
||||
clk_name = RTLIL::escape_id(args[++argidx]);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
if (module_name.empty()) {
|
||||
#ifdef _WIN32
|
||||
module_name = "top"; // FIXME: basename equivalent on Win32?
|
||||
#else
|
||||
char* bn = strdup(filename.c_str());
|
||||
module_name = RTLIL::escape_id(bn);
|
||||
free(bn);
|
||||
#endif
|
||||
}
|
||||
|
||||
AigerReader reader(design, *f, module_name, clk_name);
|
||||
reader.parse_aiger();
|
||||
}
|
||||
} AigerFrontend;
|
||||
|
||||
YOSYS_NAMESPACE_END
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
* Eddie Hung <eddie@fpgeh.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ABC_AIGERPARSE
|
||||
#define ABC_AIGERPARSE
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct AigerReader
|
||||
{
|
||||
RTLIL::Design *design;
|
||||
std::istream &f;
|
||||
RTLIL::IdString clk_name;
|
||||
RTLIL::Module *module;
|
||||
|
||||
unsigned M, I, L, O, A;
|
||||
unsigned B, C, J, F; // Optional in AIGER 1.9
|
||||
unsigned line_count;
|
||||
|
||||
std::vector<RTLIL::Wire*> inputs;
|
||||
std::vector<RTLIL::Wire*> latches;
|
||||
std::vector<RTLIL::Wire*> outputs;
|
||||
|
||||
AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name);
|
||||
void parse_aiger();
|
||||
void parse_aiger_ascii();
|
||||
void parse_aiger_binary();
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
|
@ -45,8 +45,8 @@ namespace AST {
|
|||
|
||||
// instantiate global variables (private API)
|
||||
namespace AST_INTERNAL {
|
||||
bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;
|
||||
bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_autowire;
|
||||
AstNode *current_ast, *current_ast_mod;
|
||||
std::map<std::string, AstNode*> current_scope;
|
||||
const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL;
|
||||
|
@ -431,9 +431,12 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
|
|||
break;
|
||||
|
||||
case AST_RANGE:
|
||||
if (range_valid)
|
||||
fprintf(f, "[%d:%d]", range_left, range_right);
|
||||
else {
|
||||
if (range_valid) {
|
||||
if (range_swapped)
|
||||
fprintf(f, "[%d:%d]", range_right, range_left);
|
||||
else
|
||||
fprintf(f, "[%d:%d]", range_left, range_right);
|
||||
} else {
|
||||
for (auto child : children) {
|
||||
fprintf(f, "%c", first ? '[' : ':');
|
||||
child->dumpVlog(f, "");
|
||||
|
@ -562,7 +565,8 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
|
|||
|
||||
case AST_CONCAT:
|
||||
fprintf(f, "{");
|
||||
for (auto child : children) {
|
||||
for (int i = GetSize(children)-1; i >= 0; i--) {
|
||||
auto child = children[i];
|
||||
if (!first)
|
||||
fprintf(f, ", ");
|
||||
child->dumpVlog(f, "");
|
||||
|
@ -926,28 +930,106 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
|
|||
ast_before_simplify = ast->clone();
|
||||
|
||||
if (flag_dump_ast1) {
|
||||
log("Dumping Verilog AST before simplification:\n");
|
||||
log("Dumping AST before simplification:\n");
|
||||
ast->dumpAst(NULL, " ");
|
||||
log("--- END OF AST DUMP ---\n");
|
||||
}
|
||||
if (flag_dump_vlog1) {
|
||||
log("Dumping Verilog AST before simplification:\n");
|
||||
ast->dumpVlog(NULL, " ");
|
||||
log("--- END OF AST DUMP ---\n");
|
||||
}
|
||||
|
||||
if (!defer)
|
||||
{
|
||||
bool blackbox_module = flag_lib;
|
||||
|
||||
if (!blackbox_module && !flag_noblackbox) {
|
||||
blackbox_module = true;
|
||||
for (auto child : ast->children) {
|
||||
if (child->type == AST_WIRE && (child->is_input || child->is_output))
|
||||
continue;
|
||||
if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM)
|
||||
continue;
|
||||
if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE &&
|
||||
(child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule"))
|
||||
continue;
|
||||
blackbox_module = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { }
|
||||
|
||||
if (flag_dump_ast2) {
|
||||
log("Dumping Verilog AST after simplification:\n");
|
||||
log("Dumping AST after simplification:\n");
|
||||
ast->dumpAst(NULL, " ");
|
||||
log("--- END OF AST DUMP ---\n");
|
||||
}
|
||||
|
||||
if (flag_dump_vlog) {
|
||||
log("Dumping Verilog AST (as requested by dump_vlog option):\n");
|
||||
if (flag_dump_vlog2) {
|
||||
log("Dumping Verilog AST after simplification:\n");
|
||||
ast->dumpVlog(NULL, " ");
|
||||
log("--- END OF AST DUMP ---\n");
|
||||
}
|
||||
|
||||
if (flag_lib) {
|
||||
if (flag_nowb && ast->attributes.count("\\whitebox")) {
|
||||
delete ast->attributes.at("\\whitebox");
|
||||
ast->attributes.erase("\\whitebox");
|
||||
}
|
||||
|
||||
if (ast->attributes.count("\\lib_whitebox")) {
|
||||
if (!flag_lib || flag_nowb) {
|
||||
delete ast->attributes.at("\\lib_whitebox");
|
||||
ast->attributes.erase("\\lib_whitebox");
|
||||
} else {
|
||||
if (ast->attributes.count("\\whitebox")) {
|
||||
delete ast->attributes.at("\\whitebox");
|
||||
ast->attributes.erase("\\whitebox");
|
||||
}
|
||||
AstNode *n = ast->attributes.at("\\lib_whitebox");
|
||||
ast->attributes["\\whitebox"] = n;
|
||||
ast->attributes.erase("\\lib_whitebox");
|
||||
}
|
||||
}
|
||||
|
||||
if (!blackbox_module && ast->attributes.count("\\blackbox")) {
|
||||
AstNode *n = ast->attributes.at("\\blackbox");
|
||||
if (n->type != AST_CONSTANT)
|
||||
log_file_error(ast->filename, ast->linenum, "Got blackbox attribute with non-constant value!\n");
|
||||
blackbox_module = n->asBool();
|
||||
}
|
||||
|
||||
if (blackbox_module && ast->attributes.count("\\whitebox")) {
|
||||
AstNode *n = ast->attributes.at("\\whitebox");
|
||||
if (n->type != AST_CONSTANT)
|
||||
log_file_error(ast->filename, ast->linenum, "Got whitebox attribute with non-constant value!\n");
|
||||
blackbox_module = !n->asBool();
|
||||
}
|
||||
|
||||
if (ast->attributes.count("\\noblackbox")) {
|
||||
if (blackbox_module) {
|
||||
AstNode *n = ast->attributes.at("\\noblackbox");
|
||||
if (n->type != AST_CONSTANT)
|
||||
log_file_error(ast->filename, ast->linenum, "Got noblackbox attribute with non-constant value!\n");
|
||||
blackbox_module = !n->asBool();
|
||||
}
|
||||
delete ast->attributes.at("\\noblackbox");
|
||||
ast->attributes.erase("\\noblackbox");
|
||||
}
|
||||
|
||||
if (blackbox_module)
|
||||
{
|
||||
if (ast->attributes.count("\\whitebox")) {
|
||||
delete ast->attributes.at("\\whitebox");
|
||||
ast->attributes.erase("\\whitebox");
|
||||
}
|
||||
|
||||
if (ast->attributes.count("\\lib_whitebox")) {
|
||||
delete ast->attributes.at("\\lib_whitebox");
|
||||
ast->attributes.erase("\\lib_whitebox");
|
||||
}
|
||||
|
||||
std::vector<AstNode*> new_children;
|
||||
for (auto child : ast->children) {
|
||||
if (child->type == AST_WIRE && (child->is_input || child->is_output)) {
|
||||
|
@ -956,12 +1038,19 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
|
|||
child->delete_children();
|
||||
child->children.push_back(AstNode::mkconst_int(0, false, 0));
|
||||
new_children.push_back(child);
|
||||
} else if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE &&
|
||||
(child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule")) {
|
||||
new_children.push_back(child);
|
||||
} else {
|
||||
delete child;
|
||||
}
|
||||
}
|
||||
|
||||
ast->children.swap(new_children);
|
||||
ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false);
|
||||
|
||||
if (ast->attributes.count("\\blackbox") == 0) {
|
||||
ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false);
|
||||
}
|
||||
}
|
||||
|
||||
ignoreThisSignalsInInitial = RTLIL::SigSpec();
|
||||
|
@ -1000,7 +1089,9 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
|
|||
current_module->nomeminit = flag_nomeminit;
|
||||
current_module->nomem2reg = flag_nomem2reg;
|
||||
current_module->mem2reg = flag_mem2reg;
|
||||
current_module->noblackbox = flag_noblackbox;
|
||||
current_module->lib = flag_lib;
|
||||
current_module->nowb = flag_nowb;
|
||||
current_module->noopt = flag_noopt;
|
||||
current_module->icells = flag_icells;
|
||||
current_module->autowire = flag_autowire;
|
||||
|
@ -1016,20 +1107,23 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
|
|||
}
|
||||
|
||||
// create AstModule instances for all modules in the AST tree and add them to 'design'
|
||||
void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog, bool dump_rtlil,
|
||||
bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire)
|
||||
void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil,
|
||||
bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire)
|
||||
{
|
||||
current_ast = ast;
|
||||
flag_dump_ast1 = dump_ast1;
|
||||
flag_dump_ast2 = dump_ast2;
|
||||
flag_no_dump_ptr = no_dump_ptr;
|
||||
flag_dump_vlog = dump_vlog;
|
||||
flag_dump_vlog1 = dump_vlog1;
|
||||
flag_dump_vlog2 = dump_vlog2;
|
||||
flag_dump_rtlil = dump_rtlil;
|
||||
flag_nolatches = nolatches;
|
||||
flag_nomeminit = nomeminit;
|
||||
flag_nomem2reg = nomem2reg;
|
||||
flag_mem2reg = mem2reg;
|
||||
flag_noblackbox = noblackbox;
|
||||
flag_lib = lib;
|
||||
flag_nowb = nowb;
|
||||
flag_noopt = noopt;
|
||||
flag_icells = icells;
|
||||
flag_autowire = autowire;
|
||||
|
@ -1357,12 +1451,15 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
|
|||
current_ast = NULL;
|
||||
flag_dump_ast1 = false;
|
||||
flag_dump_ast2 = false;
|
||||
flag_dump_vlog = false;
|
||||
flag_dump_vlog1 = false;
|
||||
flag_dump_vlog2 = false;
|
||||
flag_nolatches = nolatches;
|
||||
flag_nomeminit = nomeminit;
|
||||
flag_nomem2reg = nomem2reg;
|
||||
flag_mem2reg = mem2reg;
|
||||
flag_noblackbox = noblackbox;
|
||||
flag_lib = lib;
|
||||
flag_nowb = nowb;
|
||||
flag_noopt = noopt;
|
||||
flag_icells = icells;
|
||||
flag_autowire = autowire;
|
||||
|
|
|
@ -214,6 +214,8 @@ namespace AST
|
|||
MEM2REG_FL_SET_ASYNC = 0x00000800,
|
||||
MEM2REG_FL_EQ2 = 0x00001000,
|
||||
MEM2REG_FL_CMPLX_LHS = 0x00002000,
|
||||
MEM2REG_FL_CONST_LHS = 0x00004000,
|
||||
MEM2REG_FL_VAR_LHS = 0x00008000,
|
||||
|
||||
/* proc flags */
|
||||
MEM2REG_FL_EQ1 = 0x01000000,
|
||||
|
@ -237,6 +239,7 @@ namespace AST
|
|||
bool has_const_only_constructs(bool &recommend_const_eval);
|
||||
void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall);
|
||||
AstNode *eval_const_function(AstNode *fcall);
|
||||
bool is_simple_const_expr();
|
||||
|
||||
// create a human-readable text representation of the AST (for debugging)
|
||||
void dumpAst(FILE *f, std::string indent) const;
|
||||
|
@ -279,14 +282,14 @@ namespace AST
|
|||
};
|
||||
|
||||
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
|
||||
void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog, bool dump_rtlil, bool nolatches, bool nomeminit,
|
||||
bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire);
|
||||
void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit,
|
||||
bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire);
|
||||
|
||||
// parametric modules are supported directly by the AST library
|
||||
// therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
|
||||
struct AstModule : RTLIL::Module {
|
||||
AstNode *ast;
|
||||
bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire;
|
||||
bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, autowire;
|
||||
~AstModule() YS_OVERRIDE;
|
||||
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
|
||||
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE;
|
||||
|
|
|
@ -525,7 +525,16 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
}
|
||||
|
||||
if (last_generated_case != NULL && ast->get_bool_attribute("\\full_case") && default_case == NULL) {
|
||||
#if 0
|
||||
// this is a valid transformation, but as optimization it is premature.
|
||||
// better: add a default case that assigns 'x' to everything, and let later
|
||||
// optimizations take care of the rest
|
||||
last_generated_case->compare.clear();
|
||||
#else
|
||||
default_case = new RTLIL::CaseRule;
|
||||
addChunkActions(default_case->actions, this_case_eq_ltemp, SigSpec(State::Sx, GetSize(this_case_eq_rvalue)));
|
||||
sw->cases.push_back(default_case);
|
||||
#endif
|
||||
} else {
|
||||
if (default_case == NULL) {
|
||||
default_case = new RTLIL::CaseRule;
|
||||
|
@ -544,7 +553,11 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
break;
|
||||
|
||||
case AST_WIRE:
|
||||
log_file_error(ast->filename, ast->linenum, "Found wire declaration in block without label!\n");
|
||||
log_file_error(ast->filename, ast->linenum, "Found reg declaration in block without label!\n");
|
||||
break;
|
||||
|
||||
case AST_ASSIGN:
|
||||
log_file_error(ast->filename, ast->linenum, "Found continous assignment in always/initial block!\n");
|
||||
break;
|
||||
|
||||
case AST_PARAMETER:
|
||||
|
@ -632,6 +645,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
if (!id_ast->children[0]->range_valid)
|
||||
log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str());
|
||||
this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
|
||||
if (children.size() > 1)
|
||||
range = children[1];
|
||||
} else
|
||||
log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str());
|
||||
if (range) {
|
||||
|
@ -644,7 +659,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
|
||||
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||
this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
||||
this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
|
||||
delete left_at_zero_ast;
|
||||
delete right_at_zero_ast;
|
||||
} else
|
||||
|
@ -792,7 +807,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
// everything should have been handled above -> print error if not.
|
||||
default:
|
||||
for (auto f : log_files)
|
||||
current_ast->dumpAst(f, "verilog-ast> ");
|
||||
current_ast_mod->dumpAst(f, "verilog-ast> ");
|
||||
log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str());
|
||||
}
|
||||
|
||||
|
@ -942,16 +957,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
|
||||
// simply return the corresponding RTLIL::SigSpec for an AST_CONSTANT node
|
||||
case AST_CONSTANT:
|
||||
case AST_REALVALUE:
|
||||
{
|
||||
if (width_hint < 0)
|
||||
detectSignWidth(width_hint, sign_hint);
|
||||
|
||||
is_signed = sign_hint;
|
||||
return RTLIL::SigSpec(bitsAsConst());
|
||||
}
|
||||
|
||||
case AST_REALVALUE:
|
||||
{
|
||||
if (type == AST_CONSTANT)
|
||||
return RTLIL::SigSpec(bitsAsConst());
|
||||
|
||||
RTLIL::SigSpec sig = realAsConst(width_hint);
|
||||
log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", realvalue, log_signal(sig));
|
||||
return sig;
|
||||
|
@ -1035,7 +1049,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
|
||||
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||
int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
||||
int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
|
||||
AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?
|
||||
children[0]->children[1]->clone() : children[0]->children[0]->clone());
|
||||
fake_ast->children[0]->delete_children();
|
||||
|
@ -1410,10 +1424,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
if (GetSize(en) != 1)
|
||||
en = current_module->ReduceBool(NEW_ID, en);
|
||||
|
||||
std::stringstream sstr;
|
||||
sstr << celltype << "$" << filename << ":" << linenum << "$" << (autoidx++);
|
||||
IdString cellname;
|
||||
if (str.empty()) {
|
||||
std::stringstream sstr;
|
||||
sstr << celltype << "$" << filename << ":" << linenum << "$" << (autoidx++);
|
||||
cellname = sstr.str();
|
||||
} else {
|
||||
cellname = str;
|
||||
}
|
||||
|
||||
RTLIL::Cell *cell = current_module->addCell(sstr.str(), celltype);
|
||||
RTLIL::Cell *cell = current_module->addCell(cellname, celltype);
|
||||
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
||||
|
||||
for (auto &attr : attributes) {
|
||||
|
@ -1472,10 +1492,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
continue;
|
||||
}
|
||||
if (child->type == AST_PARASET) {
|
||||
int extra_const_flags = 0;
|
||||
IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str;
|
||||
if (child->children[0]->type == AST_REALVALUE) {
|
||||
log_file_warning(filename, linenum, "Replacing floating point parameter %s.%s = %f with string.\n",
|
||||
log_id(cell), log_id(paraname), child->children[0]->realvalue);
|
||||
extra_const_flags = RTLIL::CONST_FLAG_REAL;
|
||||
auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue));
|
||||
strnode->cloneInto(child->children[0]);
|
||||
delete strnode;
|
||||
|
@ -1484,6 +1506,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
log_file_error(filename, linenum, "Parameter %s.%s with non-constant value!\n",
|
||||
log_id(cell), log_id(paraname));
|
||||
cell->parameters[paraname] = child->children[0]->asParaConst();
|
||||
cell->parameters[paraname].flags |= extra_const_flags;
|
||||
continue;
|
||||
}
|
||||
if (child->type == AST_ARGUMENT) {
|
||||
|
@ -1503,9 +1526,29 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
}
|
||||
for (auto &attr : attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
log_file_error(filename, linenum, "Attribute `%s' with non-constant value.\n", attr.first.c_str());
|
||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
if (cell->type.in("$specify2", "$specify3")) {
|
||||
int src_width = GetSize(cell->getPort("\\SRC"));
|
||||
int dst_width = GetSize(cell->getPort("\\DST"));
|
||||
bool full = cell->getParam("\\FULL").as_bool();
|
||||
if (!full && src_width != dst_width)
|
||||
log_file_error(filename, linenum, "Parallel specify SRC width does not match DST width.\n");
|
||||
if (cell->type == "$specify3") {
|
||||
int dat_width = GetSize(cell->getPort("\\DAT"));
|
||||
if (dat_width != dst_width)
|
||||
log_file_error(filename, linenum, "Specify DAT width does not match DST width.\n");
|
||||
}
|
||||
cell->setParam("\\SRC_WIDTH", Const(src_width));
|
||||
cell->setParam("\\DST_WIDTH", Const(dst_width));
|
||||
}
|
||||
if (cell->type == "$specrule") {
|
||||
int src_width = GetSize(cell->getPort("\\SRC"));
|
||||
int dst_width = GetSize(cell->getPort("\\DST"));
|
||||
cell->setParam("\\SRC_WIDTH", Const(src_width));
|
||||
cell->setParam("\\DST_WIDTH", Const(dst_width));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1566,7 +1609,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
// everything should have been handled above -> print error if not.
|
||||
default:
|
||||
for (auto f : log_files)
|
||||
current_ast->dumpAst(f, "verilog-ast> ");
|
||||
current_ast_mod->dumpAst(f, "verilog-ast> ");
|
||||
type_name = type2str(type);
|
||||
log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str());
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ using namespace AST_INTERNAL;
|
|||
bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param)
|
||||
{
|
||||
static int recursion_counter = 0;
|
||||
static pair<string, int> last_blocking_assignment_warn;
|
||||
static bool deep_recursion_warning = false;
|
||||
|
||||
if (recursion_counter++ == 1000 && deep_recursion_warning) {
|
||||
|
@ -72,7 +71,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
if (stage == 0)
|
||||
{
|
||||
log_assert(type == AST_MODULE || type == AST_INTERFACE);
|
||||
last_blocking_assignment_warn = pair<string, int>();
|
||||
|
||||
deep_recursion_warning = true;
|
||||
while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { }
|
||||
|
@ -113,6 +111,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS)
|
||||
goto verbose_activate;
|
||||
|
||||
if ((memflags & AstNode::MEM2REG_FL_CONST_LHS) && !(memflags & AstNode::MEM2REG_FL_VAR_LHS))
|
||||
goto verbose_activate;
|
||||
|
||||
// log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags));
|
||||
continue;
|
||||
|
||||
|
@ -137,9 +138,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
int mem_width, mem_size, addr_bits;
|
||||
node->meminfo(mem_width, mem_size, addr_bits);
|
||||
|
||||
int data_range_left = node->children[0]->range_left;
|
||||
int data_range_right = node->children[0]->range_right;
|
||||
|
||||
if (node->children[0]->range_swapped)
|
||||
std::swap(data_range_left, data_range_right);
|
||||
|
||||
for (int i = 0; i < mem_size; i++) {
|
||||
AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE,
|
||||
mkconst_int(mem_width-1, true), mkconst_int(0, true)));
|
||||
mkconst_int(data_range_left, true), mkconst_int(data_range_right, true)));
|
||||
reg->str = stringf("%s[%d]", node->str.c_str(), i);
|
||||
reg->is_reg = true;
|
||||
reg->is_signed = node->is_signed;
|
||||
|
@ -325,6 +332,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
for (size_t i = 0; i < children.size(); i++) {
|
||||
AstNode *node = children[i];
|
||||
if (node->type == AST_WIRE) {
|
||||
if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
|
||||
for (auto c : node->children[0]->children) {
|
||||
if (!c->is_simple_const_expr()) {
|
||||
if (attributes.count("\\dynports"))
|
||||
delete attributes.at("\\dynports");
|
||||
attributes["\\dynports"] = AstNode::mkconst_int(1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this_wire_scope.count(node->str) > 0) {
|
||||
AstNode *first_node = this_wire_scope[node->str];
|
||||
if (first_node->is_input && node->is_reg)
|
||||
|
@ -642,6 +658,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
// (iterate by index as e.g. auto wires can add new children in the process)
|
||||
for (size_t i = 0; i < children.size(); i++) {
|
||||
bool did_something_here = true;
|
||||
bool backup_flag_autowire = flag_autowire;
|
||||
if ((type == AST_GENFOR || type == AST_FOR) && i >= 3)
|
||||
break;
|
||||
if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1)
|
||||
|
@ -652,6 +669,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
break;
|
||||
if (type == AST_PREFIX && i >= 1)
|
||||
break;
|
||||
if (type == AST_DEFPARAM && i == 0)
|
||||
flag_autowire = true;
|
||||
while (did_something_here && i < children.size()) {
|
||||
bool const_fold_here = const_fold, in_lvalue_here = in_lvalue;
|
||||
int width_hint_here = width_hint;
|
||||
|
@ -686,6 +705,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
children.erase(children.begin() + (i--));
|
||||
did_something = true;
|
||||
}
|
||||
flag_autowire = backup_flag_autowire;
|
||||
}
|
||||
for (auto &attr : attributes) {
|
||||
while (attr.second->simplify(true, false, false, stage, -1, false, true))
|
||||
|
@ -934,12 +954,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
}
|
||||
}
|
||||
if (current_scope.count(str) == 0) {
|
||||
// log_warning("Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str());
|
||||
AstNode *auto_wire = new AstNode(AST_AUTOWIRE);
|
||||
auto_wire->str = str;
|
||||
current_ast_mod->children.push_back(auto_wire);
|
||||
current_scope[str] = auto_wire;
|
||||
did_something = true;
|
||||
if (flag_autowire || str == "\\$global_clock") {
|
||||
AstNode *auto_wire = new AstNode(AST_AUTOWIRE);
|
||||
auto_wire->str = str;
|
||||
current_ast_mod->children.push_back(auto_wire);
|
||||
current_scope[str] = auto_wire;
|
||||
did_something = true;
|
||||
} else {
|
||||
log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
|
||||
}
|
||||
}
|
||||
if (id2ast != current_scope[str]) {
|
||||
id2ast = current_scope[str];
|
||||
|
@ -959,6 +982,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
int data_range_left = id2ast->children[0]->range_left;
|
||||
int data_range_right = id2ast->children[0]->range_right;
|
||||
|
||||
if (id2ast->children[0]->range_swapped)
|
||||
std::swap(data_range_left, data_range_right);
|
||||
|
||||
std::stringstream sstr;
|
||||
sstr << "$mem2bits$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++);
|
||||
std::string wire_id = sstr.str();
|
||||
|
@ -1004,7 +1030,26 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
log_file_error(filename, linenum, "While loops are only allowed in constant functions!\n");
|
||||
|
||||
if (type == AST_REPEAT)
|
||||
log_file_error(filename, linenum, "Repeat loops are only allowed in constant functions!\n");
|
||||
{
|
||||
AstNode *count = children[0];
|
||||
AstNode *body = children[1];
|
||||
|
||||
// eval count expression
|
||||
while (count->simplify(true, false, false, stage, 32, true, false)) { }
|
||||
|
||||
if (count->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Repeat loops outside must have constant repeat counts!\n");
|
||||
|
||||
// convert to a block with the body repeated n times
|
||||
type = AST_BLOCK;
|
||||
children.clear();
|
||||
for (int i = 0; i < count->bitsAsConst().as_int(); i++)
|
||||
children.insert(children.begin(), body->clone());
|
||||
|
||||
delete count;
|
||||
delete body;
|
||||
did_something = true;
|
||||
}
|
||||
|
||||
// unroll for loops and generate-for blocks
|
||||
if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0)
|
||||
|
@ -1040,7 +1085,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
|
||||
// eval 1st expression
|
||||
AstNode *varbuf = init_ast->children[1]->clone();
|
||||
while (varbuf->simplify(true, false, false, stage, 32, true, false)) { }
|
||||
{
|
||||
int expr_width_hint = -1;
|
||||
bool expr_sign_hint = true;
|
||||
varbuf->detectSignWidth(expr_width_hint, expr_sign_hint);
|
||||
while (varbuf->simplify(true, false, false, stage, 32, true, false)) { }
|
||||
}
|
||||
|
||||
if (varbuf->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Right hand side of 1st expression of generate for-loop is not constant!\n");
|
||||
|
@ -1062,7 +1112,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
{
|
||||
// eval 2nd expression
|
||||
AstNode *buf = while_ast->clone();
|
||||
while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
|
||||
{
|
||||
int expr_width_hint = -1;
|
||||
bool expr_sign_hint = true;
|
||||
buf->detectSignWidth(expr_width_hint, expr_sign_hint);
|
||||
while (buf->simplify(true, false, false, stage, expr_width_hint, expr_sign_hint, false)) { }
|
||||
}
|
||||
|
||||
if (buf->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "2nd expression of generate for-loop is not constant!\n");
|
||||
|
@ -1103,7 +1158,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
|
||||
// eval 3rd expression
|
||||
buf = next_ast->children[1]->clone();
|
||||
while (buf->simplify(true, false, false, stage, 32, true, false)) { }
|
||||
{
|
||||
int expr_width_hint = -1;
|
||||
bool expr_sign_hint = true;
|
||||
buf->detectSignWidth(expr_width_hint, expr_sign_hint);
|
||||
while (buf->simplify(true, false, false, stage, expr_width_hint, expr_sign_hint, true)) { }
|
||||
}
|
||||
|
||||
if (buf->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Right hand side of 3rd expression of generate for-loop is not constant!\n");
|
||||
|
@ -1112,6 +1172,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
|
|||
varbuf->children[0] = buf;
|
||||
}
|
||||
|
||||
if (type == AST_FOR) {
|
||||
AstNode *buf = next_ast->clone();
|
||||
delete buf->children[1];
|
||||
buf->children[1] = varbuf->children[0]->clone();
|
||||
current_block->children.insert(current_block->children.begin() + current_block_idx++, buf);
|
||||
}
|
||||
|
||||
current_scope[varbuf->str] = backup_scope_varbuf;
|
||||
delete varbuf;
|
||||
delete_children();
|
||||
|
@ -1492,6 +1559,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
newNode->children.push_back(assign_en);
|
||||
|
||||
AstNode *assertnode = new AstNode(type);
|
||||
assertnode->str = str;
|
||||
assertnode->children.push_back(new AstNode(AST_IDENTIFIER));
|
||||
assertnode->children.push_back(new AstNode(AST_IDENTIFIER));
|
||||
assertnode->children[0]->str = id_check;
|
||||
|
@ -1537,6 +1605,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
current_scope[wire_tmp->str] = wire_tmp;
|
||||
wire_tmp->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
|
||||
while (wire_tmp->simplify(true, false, false, 1, -1, false, false)) { }
|
||||
wire_tmp->is_logic = true;
|
||||
|
||||
AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER);
|
||||
wire_tmp_id->str = wire_tmp->str;
|
||||
|
@ -1572,14 +1641,6 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
|
||||
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN";
|
||||
|
||||
if (type == AST_ASSIGN_EQ) {
|
||||
pair<string, int> this_blocking_assignment_warn(filename, linenum);
|
||||
if (this_blocking_assignment_warn != last_blocking_assignment_warn)
|
||||
log_warning("Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n",
|
||||
filename.c_str(), linenum);
|
||||
last_blocking_assignment_warn = this_blocking_assignment_warn;
|
||||
}
|
||||
|
||||
int mem_width, mem_size, addr_bits;
|
||||
bool mem_signed = children[0]->id2ast->is_signed;
|
||||
children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits);
|
||||
|
@ -1689,7 +1750,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
|
||||
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||
int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
||||
int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
|
||||
|
||||
assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
|
||||
new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
|
||||
|
@ -1778,7 +1839,7 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
|
||||
if (str == "\\$past")
|
||||
{
|
||||
if (width_hint <= 0)
|
||||
if (width_hint < 0)
|
||||
goto replace_fcall_later;
|
||||
|
||||
int num_steps = 1;
|
||||
|
@ -2162,6 +2223,8 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
}
|
||||
|
||||
newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init);
|
||||
delete node_filename;
|
||||
delete node_memory;
|
||||
goto apply_newNode;
|
||||
}
|
||||
|
||||
|
@ -2203,6 +2266,8 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
std::map<std::string, std::string> replace_rules;
|
||||
vector<AstNode*> added_mod_children;
|
||||
dict<std::string, AstNode*> wire_cache;
|
||||
vector<AstNode*> new_stmts;
|
||||
vector<AstNode*> output_assignments;
|
||||
|
||||
if (current_block == NULL)
|
||||
{
|
||||
|
@ -2327,8 +2392,8 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
wire->port_id = 0;
|
||||
wire->is_input = false;
|
||||
wire->is_output = false;
|
||||
if (!child->is_output)
|
||||
wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
|
||||
wire->is_reg = true;
|
||||
wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
|
||||
wire_cache[child->str] = wire;
|
||||
|
||||
current_ast_mod->children.push_back(wire);
|
||||
|
@ -2350,13 +2415,10 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
new AstNode(AST_ASSIGN_EQ, wire_id, arg) :
|
||||
new AstNode(AST_ASSIGN_EQ, arg, wire_id);
|
||||
assign->children[0]->was_checked = true;
|
||||
|
||||
for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
|
||||
if (*it != current_block_child)
|
||||
continue;
|
||||
current_block->children.insert(it, assign);
|
||||
break;
|
||||
}
|
||||
if (child->is_input)
|
||||
new_stmts.push_back(assign);
|
||||
else
|
||||
output_assignments.push_back(assign);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2370,15 +2432,19 @@ skip_dynamic_range_lvalue_expansion:;
|
|||
{
|
||||
AstNode *stmt = child->clone();
|
||||
stmt->replace_ids(prefix, replace_rules);
|
||||
|
||||
for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
|
||||
if (*it != current_block_child)
|
||||
continue;
|
||||
current_block->children.insert(it, stmt);
|
||||
break;
|
||||
}
|
||||
new_stmts.push_back(stmt);
|
||||
}
|
||||
|
||||
new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end());
|
||||
|
||||
for (auto it = current_block->children.begin(); ; it++) {
|
||||
log_assert(it != current_block->children.end());
|
||||
if (*it == current_block_child) {
|
||||
current_block->children.insert(it, new_stmts.begin(), new_stmts.end());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
replace_fcall_with_id:
|
||||
if (type == AST_FCALL) {
|
||||
delete_children();
|
||||
|
@ -2848,7 +2914,11 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
|
|||
|
||||
for (size_t i = 0; i < children.size(); i++) {
|
||||
AstNode *child = children[i];
|
||||
if (child->type != AST_FUNCTION && child->type != AST_TASK && child->type != AST_PREFIX)
|
||||
// AST_PREFIX member names should not be prefixed; a nested AST_PREFIX
|
||||
// still needs to recursed-into
|
||||
if (type == AST_PREFIX && i == 1 && child->type == AST_IDENTIFIER)
|
||||
continue;
|
||||
if (child->type != AST_FUNCTION && child->type != AST_TASK)
|
||||
child->expand_genblock(index_var, prefix, name_map);
|
||||
}
|
||||
|
||||
|
@ -2903,7 +2973,7 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
|
|||
dict<AstNode*, uint32_t> &mem2reg_candidates, dict<AstNode*, uint32_t> &proc_flags, uint32_t &flags)
|
||||
{
|
||||
uint32_t children_flags = 0;
|
||||
int ignore_children_counter = 0;
|
||||
int lhs_children_counter = 0;
|
||||
|
||||
if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)
|
||||
{
|
||||
|
@ -2929,6 +2999,16 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
|
|||
proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1;
|
||||
}
|
||||
|
||||
// for proper (non-init) writes: remember if this is a constant index or not
|
||||
if ((flags & MEM2REG_FL_INIT) == 0) {
|
||||
if (children[0]->children.size() && children[0]->children[0]->type == AST_RANGE && children[0]->children[0]->children.size()) {
|
||||
if (children[0]->children[0]->children[0]->type == AST_CONSTANT)
|
||||
mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CONST_LHS;
|
||||
else
|
||||
mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_VAR_LHS;
|
||||
}
|
||||
}
|
||||
|
||||
// remember where this is
|
||||
if (flags & MEM2REG_FL_INIT) {
|
||||
if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT))
|
||||
|
@ -2941,7 +3021,7 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
|
|||
}
|
||||
}
|
||||
|
||||
ignore_children_counter = 1;
|
||||
lhs_children_counter = 1;
|
||||
}
|
||||
|
||||
if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY)
|
||||
|
@ -2984,12 +3064,23 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
|
|||
log_assert((flags & ~0x000000ff) == 0);
|
||||
|
||||
for (auto child : children)
|
||||
if (ignore_children_counter > 0)
|
||||
ignore_children_counter--;
|
||||
else if (proc_flags_p)
|
||||
{
|
||||
if (lhs_children_counter > 0) {
|
||||
lhs_children_counter--;
|
||||
if (child->children.size() && child->children[0]->type == AST_RANGE && child->children[0]->children.size()) {
|
||||
for (auto c : child->children[0]->children) {
|
||||
if (proc_flags_p)
|
||||
c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags);
|
||||
else
|
||||
c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags);
|
||||
}
|
||||
}
|
||||
} else
|
||||
if (proc_flags_p)
|
||||
child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags);
|
||||
else
|
||||
child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags);
|
||||
}
|
||||
|
||||
flags &= ~children_flags | backup_flags;
|
||||
|
||||
|
@ -3041,6 +3132,39 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
|
|||
if (type == AST_FUNCTION || type == AST_TASK)
|
||||
return false;
|
||||
|
||||
if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast))
|
||||
{
|
||||
log_assert(children[0]->type == AST_CONSTANT);
|
||||
log_assert(children[1]->type == AST_CONSTANT);
|
||||
log_assert(children[2]->type == AST_CONSTANT);
|
||||
|
||||
int cursor = children[0]->asInt(false);
|
||||
Const data = children[1]->bitsAsConst();
|
||||
int length = children[2]->asInt(false);
|
||||
|
||||
if (length != 0)
|
||||
{
|
||||
AstNode *block = new AstNode(AST_INITIAL, new AstNode(AST_BLOCK));
|
||||
mod->children.push_back(block);
|
||||
block = block->children[0];
|
||||
|
||||
int wordsz = GetSize(data) / length;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false))), mkconst_bits(data.extract(i*wordsz, wordsz).bits, false)));
|
||||
block->children.back()->children[0]->str = str;
|
||||
block->children.back()->children[0]->id2ast = id2ast;
|
||||
block->children.back()->children[0]->was_checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
AstNode *newNode = new AstNode(AST_NONE);
|
||||
newNode->cloneInto(this);
|
||||
delete newNode;
|
||||
|
||||
did_something = true;
|
||||
}
|
||||
|
||||
if (type == AST_ASSIGN && block == NULL && children[0]->mem2reg_check(mem2reg_set))
|
||||
{
|
||||
if (async_block == NULL) {
|
||||
|
@ -3270,6 +3394,16 @@ bool AstNode::has_const_only_constructs(bool &recommend_const_eval)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool AstNode::is_simple_const_expr()
|
||||
{
|
||||
if (type == AST_IDENTIFIER)
|
||||
return false;
|
||||
for (auto child : children)
|
||||
if (!child->is_simple_const_expr())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// helper function for AstNode::eval_const_function()
|
||||
void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall)
|
||||
{
|
||||
|
|
|
@ -584,7 +584,7 @@ struct BlifFrontend : public Frontend {
|
|||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" read_blif [filename]\n");
|
||||
log(" read_blif [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Load modules from a BLIF file into the current design.\n");
|
||||
log("\n");
|
||||
|
|
|
@ -47,16 +47,20 @@ struct IlangFrontend : public Frontend {
|
|||
log(" -nooverwrite\n");
|
||||
log(" ignore re-definitions of modules. (the default behavior is to\n");
|
||||
log(" create an error message if the existing module is not a blackbox\n");
|
||||
log(" module, and overwrite the existing module if it is a blackbox module.)\n");
|
||||
log(" module, and overwrite the existing module if it is a blackbox module.)\n");
|
||||
log("\n");
|
||||
log(" -overwrite\n");
|
||||
log(" overwrite existing modules with the same name\n");
|
||||
log("\n");
|
||||
log(" -lib\n");
|
||||
log(" only create empty blackbox modules\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
ILANG_FRONTEND::flag_nooverwrite = false;
|
||||
ILANG_FRONTEND::flag_overwrite = false;
|
||||
ILANG_FRONTEND::flag_lib = false;
|
||||
|
||||
log_header(design, "Executing ILANG frontend.\n");
|
||||
|
||||
|
@ -73,6 +77,10 @@ struct IlangFrontend : public Frontend {
|
|||
ILANG_FRONTEND::flag_overwrite = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-lib") {
|
||||
ILANG_FRONTEND::flag_lib = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace ILANG_FRONTEND {
|
|||
extern RTLIL::Design *current_design;
|
||||
extern bool flag_nooverwrite;
|
||||
extern bool flag_overwrite;
|
||||
extern bool flag_lib;
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
@ -53,6 +53,7 @@ USING_YOSYS_NAMESPACE
|
|||
"attribute" { return TOK_ATTRIBUTE; }
|
||||
"parameter" { return TOK_PARAMETER; }
|
||||
"signed" { return TOK_SIGNED; }
|
||||
"real" { return TOK_REAL; }
|
||||
"wire" { return TOK_WIRE; }
|
||||
"memory" { return TOK_MEMORY; }
|
||||
"width" { return TOK_WIDTH; }
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace ILANG_FRONTEND {
|
|||
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
|
||||
std::vector<RTLIL::CaseRule*> case_stack;
|
||||
dict<RTLIL::IdString, RTLIL::Const> attrbuf;
|
||||
bool flag_nooverwrite, flag_overwrite;
|
||||
bool flag_nooverwrite, flag_overwrite, flag_lib;
|
||||
bool delete_current_module;
|
||||
}
|
||||
using namespace ILANG_FRONTEND;
|
||||
|
@ -45,7 +45,16 @@ YOSYS_NAMESPACE_END
|
|||
USING_YOSYS_NAMESPACE
|
||||
%}
|
||||
|
||||
%name-prefix "rtlil_frontend_ilang_yy"
|
||||
%define api.prefix {rtlil_frontend_ilang_yy}
|
||||
|
||||
/* The union is defined in the header, so we need to provide all the
|
||||
* includes it requires
|
||||
*/
|
||||
%code requires {
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "frontends/ilang/ilang_frontend.h"
|
||||
}
|
||||
|
||||
%union {
|
||||
char *string;
|
||||
|
@ -61,7 +70,7 @@ USING_YOSYS_NAMESPACE
|
|||
%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC
|
||||
%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT
|
||||
%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
|
||||
%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_UPTO
|
||||
%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_REAL TOK_UPTO
|
||||
|
||||
%type <rsigspec> sigspec_list_reversed
|
||||
%type <sigspec> sigspec sigspec_list
|
||||
|
@ -98,7 +107,7 @@ module:
|
|||
delete_current_module = false;
|
||||
if (current_design->has($2)) {
|
||||
RTLIL::Module *existing_mod = current_design->module($2);
|
||||
if (!flag_overwrite && attrbuf.count("\\blackbox") && attrbuf.at("\\blackbox").as_bool()) {
|
||||
if (!flag_overwrite && (flag_lib || (attrbuf.count("\\blackbox") && attrbuf.at("\\blackbox").as_bool()))) {
|
||||
log("Ignoring blackbox re-definition of module %s.\n", $2);
|
||||
delete_current_module = true;
|
||||
} else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) {
|
||||
|
@ -124,6 +133,8 @@ module:
|
|||
current_module->fixup_ports();
|
||||
if (delete_current_module)
|
||||
delete current_module;
|
||||
else if (flag_lib)
|
||||
current_module->makeblackbox();
|
||||
current_module = nullptr;
|
||||
} EOL;
|
||||
|
||||
|
@ -239,6 +250,12 @@ cell_body:
|
|||
free($4);
|
||||
delete $5;
|
||||
} |
|
||||
cell_body TOK_PARAMETER TOK_REAL TOK_ID constant EOL {
|
||||
current_cell->parameters[$4] = *$5;
|
||||
current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_REAL;
|
||||
free($4);
|
||||
delete $5;
|
||||
} |
|
||||
cell_body TOK_CONNECT TOK_ID sigspec EOL {
|
||||
if (current_cell->hasPort($3))
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell port %s.", $3).c_str());
|
||||
|
@ -443,4 +460,3 @@ conn_stmt:
|
|||
delete $2;
|
||||
delete $3;
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ Then run in the following command in this directory:
|
|||
|
||||
sby -f example.sby
|
||||
|
||||
This will generate approximately one page of text outpout. The last lines
|
||||
This will generate approximately one page of text output. The last lines
|
||||
should be something like this:
|
||||
|
||||
SBY [example] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:00 (0)
|
||||
|
|
|
@ -46,7 +46,7 @@ USING_YOSYS_NAMESPACE
|
|||
#include "VeriModule.h"
|
||||
#include "VeriWrite.h"
|
||||
#include "VhdlUnits.h"
|
||||
#include "Message.h"
|
||||
#include "VeriLibrary.h"
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
|
@ -776,13 +776,14 @@ void VerificImporter::merge_past_ffs(pool<RTLIL::Cell*> &candidates)
|
|||
|
||||
void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo)
|
||||
{
|
||||
std::string module_name = nl->IsOperator() ? std::string("$verific$") + nl->Owner()->Name() : RTLIL::escape_id(nl->Owner()->Name());
|
||||
std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name();
|
||||
std::string module_name = nl->IsOperator() ? "$verific$" + netlist_name : RTLIL::escape_id(netlist_name);
|
||||
|
||||
netlist = nl;
|
||||
|
||||
if (design->has(module_name)) {
|
||||
if (!nl->IsOperator() && !is_blackbox(nl))
|
||||
log_cmd_error("Re-definition of module `%s'.\n", nl->Owner()->Name());
|
||||
log_cmd_error("Re-definition of module `%s'.\n", netlist_name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1619,30 +1620,35 @@ struct VerificExtNets
|
|||
int portname_cnt = 0;
|
||||
|
||||
// a map from Net to the same Net one level up in the design hierarchy
|
||||
std::map<Net*, Net*> net_level_up;
|
||||
std::map<Net*, Net*> net_level_up_drive_up;
|
||||
std::map<Net*, Net*> net_level_up_drive_down;
|
||||
|
||||
Net *get_net_level_up(Net *net)
|
||||
Net *route_up(Net *net, bool drive_up, Net *final_net = nullptr)
|
||||
{
|
||||
auto &net_level_up = drive_up ? net_level_up_drive_up : net_level_up_drive_down;
|
||||
|
||||
if (net_level_up.count(net) == 0)
|
||||
{
|
||||
Netlist *nl = net->Owner();
|
||||
|
||||
// Simply return if Netlist is not unique
|
||||
if (nl->NumOfRefs() != 1)
|
||||
return net;
|
||||
log_assert(nl->NumOfRefs() == 1);
|
||||
|
||||
Instance *up_inst = (Instance*)nl->GetReferences()->GetLast();
|
||||
Netlist *up_nl = up_inst->Owner();
|
||||
|
||||
// create new Port
|
||||
string name = stringf("___extnets_%d", portname_cnt++);
|
||||
Port *new_port = new Port(name.c_str(), DIR_OUT);
|
||||
Port *new_port = new Port(name.c_str(), drive_up ? DIR_OUT : DIR_IN);
|
||||
nl->Add(new_port);
|
||||
net->Connect(new_port);
|
||||
|
||||
// create new Net in up Netlist
|
||||
Net *new_net = new Net(name.c_str());
|
||||
up_nl->Add(new_net);
|
||||
Net *new_net = final_net;
|
||||
if (new_net == nullptr || new_net->Owner() != up_nl) {
|
||||
new_net = new Net(name.c_str());
|
||||
up_nl->Add(new_net);
|
||||
}
|
||||
up_inst->Connect(new_port, new_net);
|
||||
|
||||
net_level_up[net] = new_net;
|
||||
|
@ -1651,6 +1657,39 @@ struct VerificExtNets
|
|||
return net_level_up.at(net);
|
||||
}
|
||||
|
||||
Net *route_up(Net *net, bool drive_up, Netlist *dest, Net *final_net = nullptr)
|
||||
{
|
||||
while (net->Owner() != dest)
|
||||
net = route_up(net, drive_up, final_net);
|
||||
if (final_net != nullptr)
|
||||
log_assert(net == final_net);
|
||||
return net;
|
||||
}
|
||||
|
||||
Netlist *find_common_ancestor(Netlist *A, Netlist *B)
|
||||
{
|
||||
std::set<Netlist*> ancestors_of_A;
|
||||
|
||||
Netlist *cursor = A;
|
||||
while (1) {
|
||||
ancestors_of_A.insert(cursor);
|
||||
if (cursor->NumOfRefs() != 1)
|
||||
break;
|
||||
cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner();
|
||||
}
|
||||
|
||||
cursor = B;
|
||||
while (1) {
|
||||
if (ancestors_of_A.count(cursor))
|
||||
return cursor;
|
||||
if (cursor->NumOfRefs() != 1)
|
||||
break;
|
||||
cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner();
|
||||
}
|
||||
|
||||
log_error("No common ancestor found between %s and %s.\n", get_full_netlist_name(A).c_str(), get_full_netlist_name(B).c_str());
|
||||
}
|
||||
|
||||
void run(Netlist *nl)
|
||||
{
|
||||
MapIter mi, mi2;
|
||||
|
@ -1674,19 +1713,37 @@ struct VerificExtNets
|
|||
if (verific_verbose)
|
||||
log("Fixing external net reference on port %s.%s.%s:\n", get_full_netlist_name(nl).c_str(), inst->Name(), port->Name());
|
||||
|
||||
while (net->IsExternalTo(nl))
|
||||
{
|
||||
Net *newnet = get_net_level_up(net);
|
||||
if (newnet == net) break;
|
||||
Netlist *ext_nl = net->Owner();
|
||||
|
||||
if (verific_verbose)
|
||||
log(" external net owner: %s\n", get_full_netlist_name(ext_nl).c_str());
|
||||
|
||||
Netlist *ca_nl = find_common_ancestor(nl, ext_nl);
|
||||
|
||||
if (verific_verbose)
|
||||
log(" common ancestor: %s\n", get_full_netlist_name(ca_nl).c_str());
|
||||
|
||||
Net *ca_net = route_up(net, !port->IsOutput(), ca_nl);
|
||||
Net *new_net = ca_net;
|
||||
|
||||
if (ca_nl != nl)
|
||||
{
|
||||
if (verific_verbose)
|
||||
log(" external net: %s.%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name());
|
||||
net = newnet;
|
||||
log(" net in common ancestor: %s\n", ca_net->Name());
|
||||
|
||||
string name = stringf("___extnets_%d", portname_cnt++);
|
||||
new_net = new Net(name.c_str());
|
||||
nl->Add(new_net);
|
||||
|
||||
Net *n = route_up(new_net, port->IsOutput(), ca_nl, ca_net);
|
||||
log_assert(n == ca_net);
|
||||
}
|
||||
|
||||
if (verific_verbose)
|
||||
log(" final net: %s.%s%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name(), net->IsExternalTo(nl) ? " (external)" : "");
|
||||
todo_connect.push_back(tuple<Instance*, Port*, Net*>(inst, port, net));
|
||||
log(" new local net: %s\n", new_net->Name());
|
||||
|
||||
log_assert(!new_net->IsExternalTo(nl));
|
||||
todo_connect.push_back(tuple<Instance*, Port*, Net*>(inst, port, new_net));
|
||||
}
|
||||
|
||||
for (auto it : todo_connect) {
|
||||
|
@ -1696,32 +1753,64 @@ struct VerificExtNets
|
|||
}
|
||||
};
|
||||
|
||||
void verific_import(Design *design, std::string top)
|
||||
void verific_import(Design *design, const std::map<std::string,std::string> ¶meters, std::string top)
|
||||
{
|
||||
verific_sva_fsm_limit = 16;
|
||||
|
||||
std::set<Netlist*> nl_todo, nl_done;
|
||||
|
||||
{
|
||||
VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1);
|
||||
VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1);
|
||||
VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1);
|
||||
VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1);
|
||||
Array *netlists = NULL;
|
||||
Array veri_libs, vhdl_libs;
|
||||
if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
|
||||
if (veri_lib) veri_libs.InsertLast(veri_lib);
|
||||
|
||||
Array veri_libs, vhdl_libs;
|
||||
if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
|
||||
if (veri_lib) veri_libs.InsertLast(veri_lib);
|
||||
Map verific_params(STRING_HASH);
|
||||
for (const auto &i : parameters)
|
||||
verific_params.Insert(i.first.c_str(), i.second.c_str());
|
||||
|
||||
Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs);
|
||||
Netlist *nl;
|
||||
int i;
|
||||
if (top.empty()) {
|
||||
netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params);
|
||||
}
|
||||
else {
|
||||
Array veri_modules, vhdl_units;
|
||||
|
||||
FOREACH_ARRAY_ITEM(netlists, i, nl) {
|
||||
if (top.empty() || nl->Owner()->Name() == top)
|
||||
nl_todo.insert(nl);
|
||||
if (veri_lib) {
|
||||
VeriModule *veri_module = veri_lib->GetModule(top.c_str(), 1);
|
||||
if (veri_module) {
|
||||
veri_modules.InsertLast(veri_module);
|
||||
}
|
||||
|
||||
// Also elaborate all root modules since they may contain bind statements
|
||||
MapIter mi;
|
||||
FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) {
|
||||
if (!veri_module->IsRootModule()) continue;
|
||||
veri_modules.InsertLast(veri_module);
|
||||
}
|
||||
}
|
||||
|
||||
delete netlists;
|
||||
if (vhdl_lib) {
|
||||
VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(top.c_str());
|
||||
if (vhdl_unit)
|
||||
vhdl_units.InsertLast(vhdl_unit);
|
||||
}
|
||||
|
||||
netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &verific_params);
|
||||
}
|
||||
|
||||
Netlist *nl;
|
||||
int i;
|
||||
|
||||
FOREACH_ARRAY_ITEM(netlists, i, nl) {
|
||||
if (top.empty() && nl->CellBaseName() != top)
|
||||
continue;
|
||||
nl->AddAtt(new Att(" \\top", NULL));
|
||||
nl_todo.insert(nl);
|
||||
}
|
||||
|
||||
delete netlists;
|
||||
|
||||
if (!verific_error_msg.empty())
|
||||
log_error("%s\n", verific_error_msg.c_str());
|
||||
|
||||
|
@ -1855,6 +1944,13 @@ struct VerificPass : public Pass {
|
|||
log(" -autocover\n");
|
||||
log(" Generate automatic cover statements for all asserts\n");
|
||||
log("\n");
|
||||
log(" -chparam name value \n");
|
||||
log(" Elaborate the specified top modules (all modules when -all given) using\n");
|
||||
log(" this parameter value. Modules on which this parameter does not exist will\n");
|
||||
log(" cause Verific to produce a VERI-1928 or VHDL-1676 message. This option\n");
|
||||
log(" can be specified multiple times to override multiple parameters.\n");
|
||||
log(" String values must be passed in double quotes (\").\n");
|
||||
log("\n");
|
||||
log(" -v, -vv\n");
|
||||
log(" Verbose log messages. (-vv is even more verbose than -v.)\n");
|
||||
log("\n");
|
||||
|
@ -1920,6 +2016,10 @@ struct VerificPass : public Pass {
|
|||
// WARNING: instantiating unknown module 'XYZ' (VERI-1063)
|
||||
Message::SetMessageType("VERI-1063", VERIFIC_ERROR);
|
||||
|
||||
#ifndef DB_PRESERVE_INITIAL_VALUE
|
||||
# warning Verific was built without DB_PRESERVE_INITIAL_VALUE.
|
||||
#endif
|
||||
|
||||
set_verific_global_flags = false;
|
||||
}
|
||||
|
||||
|
@ -2105,6 +2205,7 @@ struct VerificPass : public Pass {
|
|||
bool mode_autocover = false;
|
||||
bool flatten = false, extnets = false;
|
||||
string dumpfile;
|
||||
Map parameters(STRING_HASH);
|
||||
|
||||
for (argidx++; argidx < GetSize(args); argidx++) {
|
||||
if (args[argidx] == "-all") {
|
||||
|
@ -2143,6 +2244,15 @@ struct VerificPass : public Pass {
|
|||
mode_autocover = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-chparam" && argidx+2 < GetSize(args)) {
|
||||
const std::string &key = args[++argidx];
|
||||
const std::string &value = args[++argidx];
|
||||
unsigned new_insertion = parameters.Insert(key.c_str(), value.c_str(),
|
||||
1 /* force_overwrite */);
|
||||
if (!new_insertion)
|
||||
log_warning_noprefix("-chparam %s already specified: overwriting.\n", key.c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-V") {
|
||||
mode_verific = true;
|
||||
continue;
|
||||
|
@ -2176,7 +2286,7 @@ struct VerificPass : public Pass {
|
|||
if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
|
||||
if (veri_lib) veri_libs.InsertLast(veri_lib);
|
||||
|
||||
Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs);
|
||||
Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, ¶meters);
|
||||
Netlist *nl;
|
||||
int i;
|
||||
|
||||
|
@ -2193,12 +2303,22 @@ struct VerificPass : public Pass {
|
|||
for (; argidx < GetSize(args); argidx++)
|
||||
{
|
||||
const char *name = args[argidx].c_str();
|
||||
VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
|
||||
|
||||
VeriModule *veri_module = veri_file::GetModule(name);
|
||||
if (veri_module) {
|
||||
log("Adding Verilog module '%s' to elaboration queue.\n", name);
|
||||
veri_modules.InsertLast(veri_module);
|
||||
continue;
|
||||
if (veri_lib) {
|
||||
VeriModule *veri_module = veri_lib->GetModule(name, 1);
|
||||
if (veri_module) {
|
||||
log("Adding Verilog module '%s' to elaboration queue.\n", name);
|
||||
veri_modules.InsertLast(veri_module);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Also elaborate all root modules since they may contain bind statements
|
||||
MapIter mi;
|
||||
FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) {
|
||||
if (!veri_module->IsRootModule()) continue;
|
||||
veri_modules.InsertLast(veri_module);
|
||||
}
|
||||
}
|
||||
|
||||
VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
|
||||
|
@ -2213,12 +2333,14 @@ struct VerificPass : public Pass {
|
|||
}
|
||||
|
||||
log("Running hier_tree::Elaborate().\n");
|
||||
Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units);
|
||||
Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, ¶meters);
|
||||
Netlist *nl;
|
||||
int i;
|
||||
|
||||
FOREACH_ARRAY_ITEM(netlists, i, nl)
|
||||
FOREACH_ARRAY_ITEM(netlists, i, nl) {
|
||||
nl->AddAtt(new Att(" \\top", NULL));
|
||||
nl_todo.insert(nl);
|
||||
}
|
||||
delete netlists;
|
||||
}
|
||||
|
||||
|
@ -2309,21 +2431,43 @@ struct ReadPass : public Pass {
|
|||
log("\n");
|
||||
log("Add directory to global Verilog/SystemVerilog include directories.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" read -verific\n");
|
||||
log(" read -noverific\n");
|
||||
log("\n");
|
||||
log("Subsequent calls to 'read' will either use or not use Verific. Calling 'read'\n");
|
||||
log("with -verific will result in an error on Yosys binaries that are built without\n");
|
||||
log("Verific support. The default is to use Verific if it is available.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
if (args.size() < 2)
|
||||
#ifdef YOSYS_ENABLE_VERIFIC
|
||||
static bool verific_available = !check_noverific_env();
|
||||
#else
|
||||
static bool verific_available = false;
|
||||
#endif
|
||||
static bool use_verific = verific_available;
|
||||
|
||||
if (args.size() < 2 || args[1][0] != '-')
|
||||
log_cmd_error("Missing mode parameter.\n");
|
||||
|
||||
if (args[1] == "-verific" || args[1] == "-noverific") {
|
||||
if (args.size() != 2)
|
||||
log_cmd_error("Additional arguments to -verific/-noverific.\n");
|
||||
if (args[1] == "-verific") {
|
||||
if (!verific_available)
|
||||
log_cmd_error("This version of Yosys is built without Verific support.\n");
|
||||
use_verific = true;
|
||||
} else {
|
||||
use_verific = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() < 3)
|
||||
log_cmd_error("Missing file name parameter.\n");
|
||||
|
||||
#ifdef YOSYS_ENABLE_VERIFIC
|
||||
bool use_verific = !check_noverific_env();
|
||||
#else
|
||||
bool use_verific = false;
|
||||
#endif
|
||||
|
||||
if (args[1] == "-vlog95" || args[1] == "-vlog2k") {
|
||||
if (use_verific) {
|
||||
args[0] = "verific";
|
||||
|
|
|
@ -26,7 +26,7 @@ YOSYS_NAMESPACE_BEGIN
|
|||
extern int verific_verbose;
|
||||
|
||||
extern bool verific_import_pending;
|
||||
extern void verific_import(Design *design, std::string top = std::string());
|
||||
extern void verific_import(Design *design, const std::map<std::string,std::string> ¶meters, std::string top = std::string());
|
||||
|
||||
extern pool<int> verific_sva_prims;
|
||||
|
||||
|
|
|
@ -1666,7 +1666,20 @@ struct VerificSvaImporter
|
|||
log(" importing SVA property at root cell %s (%s) at %s:%d.\n", root->Name(), root->View()->Owner()->Name(),
|
||||
LineFile::GetFileName(root->Linefile()), LineFile::GetLineNo(root->Linefile()));
|
||||
|
||||
RTLIL::IdString root_name = module->uniquify(importer->mode_names || root->IsUserDeclared() ? RTLIL::escape_id(root->Name()) : NEW_ID);
|
||||
bool is_user_declared = root->IsUserDeclared();
|
||||
|
||||
// FIXME
|
||||
if (!is_user_declared) {
|
||||
const char *name = root->Name();
|
||||
for (int i = 0; name[i]; i++) {
|
||||
if (i ? (name[i] < '0' || name[i] > '9') : (name[i] != 'i')) {
|
||||
is_user_declared = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RTLIL::IdString root_name = module->uniquify(importer->mode_names || is_user_declared ? RTLIL::escape_id(root->Name()) : NEW_ID);
|
||||
|
||||
// parse SVA sequence into trigger signal
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l
|
|||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) flex -o frontends/verilog/verilog_lexer.cc $<
|
||||
|
||||
frontends/verilog/verilog_parser.tab.o: CXXFLAGS += -DYYMAXDEPTH=10000000
|
||||
|
||||
OBJS += frontends/verilog/verilog_parser.tab.o
|
||||
OBJS += frontends/verilog/verilog_lexer.o
|
||||
OBJS += frontends/verilog/preproc.o
|
||||
|
|
|
@ -81,6 +81,9 @@ struct VerilogFrontend : public Frontend {
|
|||
log(" -assert-assumes\n");
|
||||
log(" treat all assume() statements like assert() statements\n");
|
||||
log("\n");
|
||||
log(" -debug\n");
|
||||
log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2 -yydebug\n");
|
||||
log("\n");
|
||||
log(" -dump_ast1\n");
|
||||
log(" dump abstract syntax tree (before simplification)\n");
|
||||
log("\n");
|
||||
|
@ -90,7 +93,10 @@ struct VerilogFrontend : public Frontend {
|
|||
log(" -no_dump_ptr\n");
|
||||
log(" do not include hex memory addresses in dump (easier to diff dumps)\n");
|
||||
log("\n");
|
||||
log(" -dump_vlog\n");
|
||||
log(" -dump_vlog1\n");
|
||||
log(" dump ast as Verilog code (before simplification)\n");
|
||||
log("\n");
|
||||
log(" -dump_vlog2\n");
|
||||
log(" dump ast as Verilog code (after simplification)\n");
|
||||
log("\n");
|
||||
log(" -dump_rtlil\n");
|
||||
|
@ -139,8 +145,21 @@ struct VerilogFrontend : public Frontend {
|
|||
log(" -nodpi\n");
|
||||
log(" disable DPI-C support\n");
|
||||
log("\n");
|
||||
log(" -noblackbox\n");
|
||||
log(" do not automatically add a (* blackbox *) attribute to an\n");
|
||||
log(" empty module.\n");
|
||||
log("\n");
|
||||
log(" -lib\n");
|
||||
log(" only create empty blackbox modules. This implies -DBLACKBOX.\n");
|
||||
log(" modules with the (* whitebox *) attribute will be preserved.\n");
|
||||
log(" (* lib_whitebox *) will be treated like (* whitebox *).\n");
|
||||
log("\n");
|
||||
log(" -nowb\n");
|
||||
log(" delete (* whitebox *) and (* lib_whitebox *) attributes from\n");
|
||||
log(" all modules.\n");
|
||||
log("\n");
|
||||
log(" -specify\n");
|
||||
log(" parse and import specify blocks\n");
|
||||
log("\n");
|
||||
log(" -noopt\n");
|
||||
log(" don't perform basic optimizations (such as const folding) in the\n");
|
||||
|
@ -197,7 +216,8 @@ struct VerilogFrontend : public Frontend {
|
|||
bool flag_dump_ast1 = false;
|
||||
bool flag_dump_ast2 = false;
|
||||
bool flag_no_dump_ptr = false;
|
||||
bool flag_dump_vlog = false;
|
||||
bool flag_dump_vlog1 = false;
|
||||
bool flag_dump_vlog2 = false;
|
||||
bool flag_dump_rtlil = false;
|
||||
bool flag_nolatches = false;
|
||||
bool flag_nomeminit = false;
|
||||
|
@ -211,6 +231,8 @@ struct VerilogFrontend : public Frontend {
|
|||
bool flag_nooverwrite = false;
|
||||
bool flag_overwrite = false;
|
||||
bool flag_defer = false;
|
||||
bool flag_noblackbox = false;
|
||||
bool flag_nowb = false;
|
||||
std::map<std::string, std::string> defines_map;
|
||||
std::list<std::string> include_dirs;
|
||||
std::list<std::string> attributes;
|
||||
|
@ -221,10 +243,9 @@ struct VerilogFrontend : public Frontend {
|
|||
norestrict_mode = false;
|
||||
assume_asserts_mode = false;
|
||||
lib_mode = false;
|
||||
specify_mode = false;
|
||||
default_nettype_wire = true;
|
||||
|
||||
log_header(design, "Executing Verilog-2005 frontend.\n");
|
||||
|
||||
args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end());
|
||||
|
||||
size_t argidx;
|
||||
|
@ -258,6 +279,14 @@ struct VerilogFrontend : public Frontend {
|
|||
assert_assumes_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-debug") {
|
||||
flag_dump_ast1 = true;
|
||||
flag_dump_ast2 = true;
|
||||
flag_dump_vlog1 = true;
|
||||
flag_dump_vlog2 = true;
|
||||
frontend_verilog_yydebug = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-dump_ast1") {
|
||||
flag_dump_ast1 = true;
|
||||
continue;
|
||||
|
@ -270,8 +299,12 @@ struct VerilogFrontend : public Frontend {
|
|||
flag_no_dump_ptr = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-dump_vlog") {
|
||||
flag_dump_vlog = true;
|
||||
if (arg == "-dump_vlog1") {
|
||||
flag_dump_vlog1 = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-dump_vlog2") {
|
||||
flag_dump_vlog2 = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-dump_rtlil") {
|
||||
|
@ -310,11 +343,23 @@ struct VerilogFrontend : public Frontend {
|
|||
flag_nodpi = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-noblackbox") {
|
||||
flag_noblackbox = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-lib") {
|
||||
lib_mode = true;
|
||||
defines_map["BLACKBOX"] = string();
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nowb") {
|
||||
flag_nowb = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-specify") {
|
||||
specify_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-noopt") {
|
||||
flag_noopt = true;
|
||||
continue;
|
||||
|
@ -376,6 +421,8 @@ struct VerilogFrontend : public Frontend {
|
|||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str());
|
||||
|
||||
log("Parsing %s%s input from `%s' to AST representation.\n",
|
||||
formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str());
|
||||
|
||||
|
@ -410,7 +457,8 @@ struct VerilogFrontend : public Frontend {
|
|||
if (flag_nodpi)
|
||||
error_on_dpi_function(current_ast);
|
||||
|
||||
AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire);
|
||||
AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches,
|
||||
flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire);
|
||||
|
||||
if (!flag_nopp)
|
||||
delete lexin;
|
||||
|
|
|
@ -72,6 +72,9 @@ namespace VERILOG_FRONTEND
|
|||
// running in -lib mode
|
||||
extern bool lib_mode;
|
||||
|
||||
// running in -specify mode
|
||||
extern bool specify_mode;
|
||||
|
||||
// lexer input stream
|
||||
extern std::istream *lexin;
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ YOSYS_NAMESPACE_END
|
|||
"endfunction" { return TOK_ENDFUNCTION; }
|
||||
"task" { return TOK_TASK; }
|
||||
"endtask" { return TOK_ENDTASK; }
|
||||
"specify" { return TOK_SPECIFY; }
|
||||
"specify" { return specify_mode ? TOK_SPECIFY : TOK_IGNORED_SPECIFY; }
|
||||
"endspecify" { return TOK_ENDSPECIFY; }
|
||||
"specparam" { return TOK_SPECPARAM; }
|
||||
"package" { SV_KEYWORD(TOK_PACKAGE); }
|
||||
|
@ -189,6 +189,14 @@ YOSYS_NAMESPACE_END
|
|||
"always_ff" { SV_KEYWORD(TOK_ALWAYS); }
|
||||
"always_latch" { SV_KEYWORD(TOK_ALWAYS); }
|
||||
|
||||
/* use special token for labels on assert, assume, cover, and restrict because it's insanley complex
|
||||
to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some
|
||||
global state.. its a mess) */
|
||||
[a-zA-Z_$][a-zA-Z0-9_$]*/[ \t\r\n]*:[ \t\r\n]*(assert|assume|cover|restrict)[^a-zA-Z0-9_$\.] {
|
||||
frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
|
||||
return TOK_SVA_LABEL;
|
||||
}
|
||||
|
||||
"assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); }
|
||||
"assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); }
|
||||
"cover" { if (formal_mode) return TOK_COVER; SV_KEYWORD(TOK_COVER); }
|
||||
|
@ -198,7 +206,9 @@ YOSYS_NAMESPACE_END
|
|||
"const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); }
|
||||
"checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); }
|
||||
"endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); }
|
||||
"final" { SV_KEYWORD(TOK_FINAL); }
|
||||
"logic" { SV_KEYWORD(TOK_LOGIC); }
|
||||
"var" { SV_KEYWORD(TOK_VAR); }
|
||||
"bit" { SV_KEYWORD(TOK_REG); }
|
||||
|
||||
"eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
|
||||
|
@ -293,6 +303,12 @@ supply1 { return TOK_SUPPLY1; }
|
|||
return TOK_ID;
|
||||
}
|
||||
|
||||
"$"(setup|hold|setuphold|removal|recovery|recrem|skew|timeskew|fullskew|nochange) {
|
||||
if (!specify_mode) REJECT;
|
||||
frontend_verilog_yylval.string = new std::string(yytext);
|
||||
return TOK_ID;
|
||||
}
|
||||
|
||||
"$signed" { return TOK_TO_SIGNED; }
|
||||
"$unsigned" { return TOK_TO_UNSIGNED; }
|
||||
|
||||
|
@ -303,7 +319,7 @@ supply1 { return TOK_SUPPLY1; }
|
|||
|
||||
[a-zA-Z_$][a-zA-Z0-9_$\.]* {
|
||||
frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
|
||||
return TOK_ID;
|
||||
return TOK_ID;
|
||||
}
|
||||
|
||||
"/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" {
|
||||
|
@ -403,6 +419,17 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
|
|||
"+:" { return TOK_POS_INDEXED; }
|
||||
"-:" { return TOK_NEG_INDEXED; }
|
||||
|
||||
[-+]?[=*]> {
|
||||
if (!specify_mode) REJECT;
|
||||
frontend_verilog_yylval.string = new std::string(yytext);
|
||||
return TOK_SPECIFY_OPER;
|
||||
}
|
||||
|
||||
"&&&" {
|
||||
if (!specify_mode) REJECT;
|
||||
return TOK_SPECIFY_AND;
|
||||
}
|
||||
|
||||
"/*" { BEGIN(COMMENT); }
|
||||
<COMMENT>. /* ignore comment body */
|
||||
<COMMENT>\n /* ignore comment body */
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace VERILOG_FRONTEND {
|
|||
std::vector<char> case_type_stack;
|
||||
bool do_not_require_port_stubs;
|
||||
bool default_nettype_wire;
|
||||
bool sv_mode, formal_mode, lib_mode;
|
||||
bool sv_mode, formal_mode, lib_mode, specify_mode;
|
||||
bool noassert_mode, noassume_mode, norestrict_mode;
|
||||
bool assume_asserts_mode, assert_assumes_mode;
|
||||
bool current_wire_rand, current_wire_const;
|
||||
|
@ -94,42 +94,77 @@ static void free_attr(std::map<std::string, AstNode*> *al)
|
|||
delete al;
|
||||
}
|
||||
|
||||
struct specify_target {
|
||||
char polarity_op;
|
||||
AstNode *dst, *dat;
|
||||
};
|
||||
|
||||
struct specify_triple {
|
||||
AstNode *t_min, *t_avg, *t_max;
|
||||
};
|
||||
|
||||
struct specify_rise_fall {
|
||||
specify_triple rise;
|
||||
specify_triple fall;
|
||||
};
|
||||
|
||||
%}
|
||||
|
||||
%name-prefix "frontend_verilog_yy"
|
||||
%define api.prefix {frontend_verilog_yy}
|
||||
|
||||
/* The union is defined in the header, so we need to provide all the
|
||||
* includes it requires
|
||||
*/
|
||||
%code requires {
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "frontends/verilog/verilog_frontend.h"
|
||||
}
|
||||
|
||||
%union {
|
||||
std::string *string;
|
||||
struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast;
|
||||
std::map<std::string, YOSYS_NAMESPACE_PREFIX AST::AstNode*> *al;
|
||||
struct specify_target *specify_target_ptr;
|
||||
struct specify_triple *specify_triple_ptr;
|
||||
struct specify_rise_fall *specify_rise_fall_ptr;
|
||||
bool boolean;
|
||||
char ch;
|
||||
}
|
||||
|
||||
%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE
|
||||
%token <string> TOK_SVA_LABEL TOK_SPECIFY_OPER
|
||||
%token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL
|
||||
%token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END
|
||||
%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM
|
||||
%token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP
|
||||
%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT
|
||||
%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR
|
||||
%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC
|
||||
%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
|
||||
%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
|
||||
%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC
|
||||
%token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT
|
||||
%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK TOK_SPECIFY TOK_ENDSPECIFY TOK_SPECPARAM
|
||||
%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK TOK_SPECIFY
|
||||
%token TOK_IGNORED_SPECIFY TOK_ENDSPECIFY TOK_SPECPARAM TOK_SPECIFY_AND
|
||||
%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL
|
||||
%token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE
|
||||
%token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED
|
||||
%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_ASSUME
|
||||
%token TOK_RESTRICT TOK_COVER TOK_PROPERTY TOK_ENUM TOK_TYPEDEF
|
||||
%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_PROPERTY TOK_ENUM TOK_TYPEDEF
|
||||
%token TOK_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY
|
||||
%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY
|
||||
|
||||
%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int
|
||||
%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
|
||||
%type <string> opt_label tok_prim_wrapper hierarchical_id
|
||||
%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id
|
||||
%type <boolean> opt_signed opt_property unique_case_attr
|
||||
%type <al> attr case_attr
|
||||
|
||||
%type <specify_target_ptr> specify_target
|
||||
%type <specify_triple_ptr> specify_triple
|
||||
%type <specify_rise_fall_ptr> specify_rise_fall
|
||||
%type <ast> specify_if specify_condition specify_opt_arg
|
||||
%type <ch> specify_edge
|
||||
|
||||
// operator precedence from low to high
|
||||
%left OP_LOR
|
||||
%left OP_LAND
|
||||
|
@ -456,6 +491,9 @@ wire_type_token:
|
|||
TOK_LOGIC {
|
||||
astbuf3->is_logic = true;
|
||||
} |
|
||||
TOK_VAR {
|
||||
astbuf3->is_logic = true;
|
||||
} |
|
||||
TOK_INTEGER {
|
||||
astbuf3->is_reg = true;
|
||||
astbuf3->range_left = 31;
|
||||
|
@ -539,7 +577,7 @@ module_body:
|
|||
|
||||
module_body_stmt:
|
||||
task_func_decl | specify_block |param_decl | localparam_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
|
||||
always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl;
|
||||
always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
|
||||
|
||||
checker_decl:
|
||||
TOK_CHECKER TOK_ID ';' {
|
||||
|
@ -697,15 +735,254 @@ task_func_body:
|
|||
task_func_body behavioral_stmt |
|
||||
/* empty */;
|
||||
|
||||
specify_block:
|
||||
TOK_SPECIFY specify_item_opt TOK_ENDSPECIFY |
|
||||
TOK_SPECIFY TOK_ENDSPECIFY ;
|
||||
/*************************** specify parser ***************************/
|
||||
|
||||
specify_item_opt:
|
||||
specify_item_opt specify_item |
|
||||
specify_item ;
|
||||
specify_block:
|
||||
TOK_SPECIFY specify_item_list TOK_ENDSPECIFY;
|
||||
|
||||
specify_item_list:
|
||||
specify_item specify_item_list |
|
||||
/* empty */;
|
||||
|
||||
specify_item:
|
||||
specify_if '(' specify_edge expr TOK_SPECIFY_OPER specify_target ')' '=' specify_rise_fall ';' {
|
||||
AstNode *en_expr = $1;
|
||||
char specify_edge = $3;
|
||||
AstNode *src_expr = $4;
|
||||
string *oper = $5;
|
||||
specify_target *target = $6;
|
||||
specify_rise_fall *timing = $9;
|
||||
|
||||
if (specify_edge != 0 && target->dat == nullptr)
|
||||
frontend_verilog_yyerror("Found specify edge but no data spec.\n");
|
||||
|
||||
AstNode *cell = new AstNode(AST_CELL);
|
||||
ast_stack.back()->children.push_back(cell);
|
||||
cell->str = stringf("$specify$%d", autoidx++);
|
||||
cell->children.push_back(new AstNode(AST_CELLTYPE));
|
||||
cell->children.back()->str = target->dat ? "$specify3" : "$specify2";
|
||||
|
||||
char oper_polarity = 0;
|
||||
char oper_type = oper->at(0);
|
||||
|
||||
if (oper->size() == 3) {
|
||||
oper_polarity = oper->at(0);
|
||||
oper_type = oper->at(1);
|
||||
}
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_type == '*', false, 1)));
|
||||
cell->children.back()->str = "\\FULL";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_polarity != 0, false, 1)));
|
||||
cell->children.back()->str = "\\SRC_DST_PEN";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_polarity == '+', false, 1)));
|
||||
cell->children.back()->str = "\\SRC_DST_POL";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_min));
|
||||
cell->children.back()->str = "\\T_RISE_MIN";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_avg));
|
||||
cell->children.back()->str = "\\T_RISE_TYP";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_max));
|
||||
cell->children.back()->str = "\\T_RISE_MAX";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_min));
|
||||
cell->children.back()->str = "\\T_FALL_MIN";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_avg));
|
||||
cell->children.back()->str = "\\T_FALL_TYP";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_max));
|
||||
cell->children.back()->str = "\\T_FALL_MAX";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_ARGUMENT, en_expr ? en_expr : AstNode::mkconst_int(1, false, 1)));
|
||||
cell->children.back()->str = "\\EN";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr));
|
||||
cell->children.back()->str = "\\SRC";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_ARGUMENT, target->dst));
|
||||
cell->children.back()->str = "\\DST";
|
||||
|
||||
if (target->dat)
|
||||
{
|
||||
cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(specify_edge != 0, false, 1)));
|
||||
cell->children.back()->str = "\\EDGE_EN";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(specify_edge == 'p', false, 1)));
|
||||
cell->children.back()->str = "\\EDGE_POL";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(target->polarity_op != 0, false, 1)));
|
||||
cell->children.back()->str = "\\DAT_DST_PEN";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(target->polarity_op == '+', false, 1)));
|
||||
cell->children.back()->str = "\\DAT_DST_POL";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_ARGUMENT, target->dat));
|
||||
cell->children.back()->str = "\\DAT";
|
||||
}
|
||||
|
||||
delete oper;
|
||||
delete target;
|
||||
delete timing;
|
||||
} |
|
||||
TOK_ID '(' specify_edge expr specify_condition ',' specify_edge expr specify_condition ',' expr specify_opt_arg ')' ';' {
|
||||
if (*$1 != "$setup" && *$1 != "$hold" && *$1 != "$setuphold" && *$1 != "$removal" && *$1 != "$recovery" &&
|
||||
*$1 != "$recrem" && *$1 != "$skew" && *$1 != "$timeskew" && *$1 != "$fullskew" && *$1 != "$nochange")
|
||||
frontend_verilog_yyerror("Unsupported specify rule type: %s\n", $1->c_str());
|
||||
|
||||
AstNode *src_pen = AstNode::mkconst_int($3 != 0, false, 1);
|
||||
AstNode *src_pol = AstNode::mkconst_int($3 == 'p', false, 1);
|
||||
AstNode *src_expr = $4, *src_en = $5 ? $5 : AstNode::mkconst_int(1, false, 1);
|
||||
|
||||
AstNode *dst_pen = AstNode::mkconst_int($7 != 0, false, 1);
|
||||
AstNode *dst_pol = AstNode::mkconst_int($7 == 'p', false, 1);
|
||||
AstNode *dst_expr = $8, *dst_en = $9 ? $9 : AstNode::mkconst_int(1, false, 1);
|
||||
|
||||
AstNode *limit = $11;
|
||||
AstNode *limit2 = $12;
|
||||
|
||||
AstNode *cell = new AstNode(AST_CELL);
|
||||
ast_stack.back()->children.push_back(cell);
|
||||
cell->str = stringf("$specify$%d", autoidx++);
|
||||
cell->children.push_back(new AstNode(AST_CELLTYPE));
|
||||
cell->children.back()->str = "$specrule";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_str(*$1)));
|
||||
cell->children.back()->str = "\\TYPE";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, limit));
|
||||
cell->children.back()->str = "\\T_LIMIT";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, limit2 ? limit2 : AstNode::mkconst_int(0, true)));
|
||||
cell->children.back()->str = "\\T_LIMIT2";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, src_pen));
|
||||
cell->children.back()->str = "\\SRC_PEN";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, src_pol));
|
||||
cell->children.back()->str = "\\SRC_POL";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, dst_pen));
|
||||
cell->children.back()->str = "\\DST_PEN";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_PARASET, dst_pol));
|
||||
cell->children.back()->str = "\\DST_POL";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_ARGUMENT, src_en));
|
||||
cell->children.back()->str = "\\SRC_EN";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr));
|
||||
cell->children.back()->str = "\\SRC";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_ARGUMENT, dst_en));
|
||||
cell->children.back()->str = "\\DST_EN";
|
||||
|
||||
cell->children.push_back(new AstNode(AST_ARGUMENT, dst_expr));
|
||||
cell->children.back()->str = "\\DST";
|
||||
|
||||
delete $1;
|
||||
};
|
||||
|
||||
specify_opt_arg:
|
||||
',' expr {
|
||||
$$ = $2;
|
||||
} |
|
||||
/* empty */ {
|
||||
$$ = nullptr;
|
||||
};
|
||||
|
||||
specify_if:
|
||||
TOK_IF '(' expr ')' {
|
||||
$$ = $3;
|
||||
} |
|
||||
/* empty */ {
|
||||
$$ = nullptr;
|
||||
};
|
||||
|
||||
specify_condition:
|
||||
TOK_SPECIFY_AND expr {
|
||||
$$ = $2;
|
||||
} |
|
||||
/* empty */ {
|
||||
$$ = nullptr;
|
||||
};
|
||||
|
||||
specify_target:
|
||||
expr {
|
||||
$$ = new specify_target;
|
||||
$$->polarity_op = 0;
|
||||
$$->dst = $1;
|
||||
$$->dat = nullptr;
|
||||
} |
|
||||
'(' expr ':' expr ')'{
|
||||
$$ = new specify_target;
|
||||
$$->polarity_op = 0;
|
||||
$$->dst = $2;
|
||||
$$->dat = $4;
|
||||
} |
|
||||
'(' expr TOK_NEG_INDEXED expr ')'{
|
||||
$$ = new specify_target;
|
||||
$$->polarity_op = '-';
|
||||
$$->dst = $2;
|
||||
$$->dat = $4;
|
||||
} |
|
||||
'(' expr TOK_POS_INDEXED expr ')'{
|
||||
$$ = new specify_target;
|
||||
$$->polarity_op = '+';
|
||||
$$->dst = $2;
|
||||
$$->dat = $4;
|
||||
};
|
||||
|
||||
specify_edge:
|
||||
TOK_POSEDGE { $$ = 'p'; } |
|
||||
TOK_NEGEDGE { $$ = 'n'; } |
|
||||
{ $$ = 0; };
|
||||
|
||||
specify_rise_fall:
|
||||
specify_triple {
|
||||
$$ = new specify_rise_fall;
|
||||
$$->rise = *$1;
|
||||
$$->fall.t_min = $1->t_min->clone();
|
||||
$$->fall.t_avg = $1->t_avg->clone();
|
||||
$$->fall.t_max = $1->t_max->clone();
|
||||
delete $1;
|
||||
} |
|
||||
'(' specify_triple ',' specify_triple ')' {
|
||||
$$ = new specify_rise_fall;
|
||||
$$->rise = *$2;
|
||||
$$->fall = *$4;
|
||||
delete $2;
|
||||
delete $4;
|
||||
};
|
||||
|
||||
specify_triple:
|
||||
expr {
|
||||
$$ = new specify_triple;
|
||||
$$->t_min = $1;
|
||||
$$->t_avg = $1->clone();
|
||||
$$->t_max = $1->clone();
|
||||
} |
|
||||
expr ':' expr ':' expr {
|
||||
$$ = new specify_triple;
|
||||
$$->t_min = $1;
|
||||
$$->t_avg = $3;
|
||||
$$->t_max = $5;
|
||||
};
|
||||
|
||||
/******************** ignored specify parser **************************/
|
||||
|
||||
ignored_specify_block:
|
||||
TOK_IGNORED_SPECIFY ignored_specify_item_opt TOK_ENDSPECIFY |
|
||||
TOK_IGNORED_SPECIFY TOK_ENDSPECIFY ;
|
||||
|
||||
ignored_specify_item_opt:
|
||||
ignored_specify_item_opt ignored_specify_item |
|
||||
ignored_specify_item ;
|
||||
|
||||
ignored_specify_item:
|
||||
specparam_declaration
|
||||
// | pulsestyle_declaration
|
||||
// | showcancelled_declaration
|
||||
|
@ -721,13 +998,13 @@ specparam_declaration:
|
|||
// and the 'non_opt_range' rule allows index ranges not allowed by 1364-2005
|
||||
// exxxxtending this for SV specparam would change this anyhow
|
||||
specparam_range:
|
||||
'[' constant_expression ':' constant_expression ']' ;
|
||||
'[' ignspec_constant_expression ':' ignspec_constant_expression ']' ;
|
||||
|
||||
list_of_specparam_assignments:
|
||||
specparam_assignment | list_of_specparam_assignments ',' specparam_assignment;
|
||||
|
||||
specparam_assignment:
|
||||
TOK_ID '=' constant_mintypmax_expression ;
|
||||
ignspec_id '=' constant_mintypmax_expression ;
|
||||
|
||||
/*
|
||||
pulsestyle_declaration :
|
||||
|
@ -802,19 +1079,19 @@ opt_polarity_operator :
|
|||
|
||||
// Good enough for the time being
|
||||
specify_input_terminal_descriptor :
|
||||
TOK_ID ;
|
||||
ignspec_id ;
|
||||
|
||||
// Good enough for the time being
|
||||
specify_output_terminal_descriptor :
|
||||
TOK_ID ;
|
||||
ignspec_id ;
|
||||
|
||||
system_timing_declaration :
|
||||
TOK_ID '(' system_timing_args ')' ';' ;
|
||||
ignspec_id '(' system_timing_args ')' ';' ;
|
||||
|
||||
system_timing_arg :
|
||||
TOK_POSEDGE TOK_ID |
|
||||
TOK_NEGEDGE TOK_ID |
|
||||
expr ;
|
||||
TOK_POSEDGE ignspec_id |
|
||||
TOK_NEGEDGE ignspec_id |
|
||||
ignspec_expr ;
|
||||
|
||||
system_timing_args :
|
||||
system_timing_arg |
|
||||
|
@ -871,19 +1148,27 @@ tzx_path_delay_expression :
|
|||
*/
|
||||
|
||||
path_delay_expression :
|
||||
constant_expression;
|
||||
ignspec_constant_expression;
|
||||
|
||||
constant_mintypmax_expression :
|
||||
constant_expression
|
||||
| constant_expression ':' constant_expression ':' constant_expression
|
||||
ignspec_constant_expression
|
||||
| ignspec_constant_expression ':' ignspec_constant_expression ':' ignspec_constant_expression
|
||||
;
|
||||
|
||||
// for the time being this is OK, but we may write our own expr here.
|
||||
// as I'm not sure it is legal to use a full expr here (probably not)
|
||||
// On the other hand, other rules requiring constant expressions also use 'expr'
|
||||
// (such as param assignment), so we may leave this as-is, perhaps adding runtime checks for constant-ness
|
||||
constant_expression:
|
||||
expr ;
|
||||
ignspec_constant_expression:
|
||||
expr { delete $1; };
|
||||
|
||||
ignspec_expr:
|
||||
expr { delete $1; };
|
||||
|
||||
ignspec_id:
|
||||
TOK_ID { delete $1; };
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
param_signed:
|
||||
TOK_SIGNED {
|
||||
|
@ -917,7 +1202,7 @@ param_range:
|
|||
};
|
||||
|
||||
param_decl:
|
||||
TOK_PARAMETER {
|
||||
attr TOK_PARAMETER {
|
||||
astbuf1 = new AstNode(AST_PARAMETER);
|
||||
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
||||
} param_signed param_integer param_real param_range param_decl_list ';' {
|
||||
|
@ -925,7 +1210,7 @@ param_decl:
|
|||
};
|
||||
|
||||
localparam_decl:
|
||||
TOK_LOCALPARAM {
|
||||
attr TOK_LOCALPARAM {
|
||||
astbuf1 = new AstNode(AST_LOCALPARAM);
|
||||
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
|
||||
} param_signed param_integer param_real param_range param_decl_list ';' {
|
||||
|
@ -1329,17 +1614,25 @@ opt_label:
|
|||
$$ = NULL;
|
||||
};
|
||||
|
||||
opt_sva_label:
|
||||
TOK_SVA_LABEL ':' {
|
||||
$$ = $1;
|
||||
} |
|
||||
/* empty */ {
|
||||
$$ = NULL;
|
||||
};
|
||||
|
||||
opt_property:
|
||||
TOK_PROPERTY {
|
||||
$$ = true;
|
||||
} |
|
||||
TOK_FINAL {
|
||||
$$ = false;
|
||||
} |
|
||||
/* empty */ {
|
||||
$$ = false;
|
||||
};
|
||||
|
||||
opt_stmt_label:
|
||||
TOK_ID ':' | /* empty */;
|
||||
|
||||
modport_stmt:
|
||||
TOK_MODPORT TOK_ID {
|
||||
AstNode *modport = new AstNode(AST_MODPORT);
|
||||
|
@ -1376,83 +1669,164 @@ modport_type_token:
|
|||
TOK_INPUT {current_modport_input = 1; current_modport_output = 0;} | TOK_OUTPUT {current_modport_input = 0; current_modport_output = 1;}
|
||||
|
||||
assert:
|
||||
opt_stmt_label TOK_ASSERT opt_property '(' expr ')' ';' {
|
||||
if (noassert_mode)
|
||||
opt_sva_label TOK_ASSERT opt_property '(' expr ')' ';' {
|
||||
if (noassert_mode) {
|
||||
delete $5;
|
||||
else
|
||||
ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5));
|
||||
} else {
|
||||
AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5);
|
||||
if ($1 != nullptr)
|
||||
node->str = *$1;
|
||||
ast_stack.back()->children.push_back(node);
|
||||
}
|
||||
if ($1 != nullptr)
|
||||
delete $1;
|
||||
} |
|
||||
opt_stmt_label TOK_ASSUME opt_property '(' expr ')' ';' {
|
||||
if (noassume_mode)
|
||||
opt_sva_label TOK_ASSUME opt_property '(' expr ')' ';' {
|
||||
if (noassume_mode) {
|
||||
delete $5;
|
||||
else
|
||||
ast_stack.back()->children.push_back(new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5));
|
||||
} else {
|
||||
AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5);
|
||||
if ($1 != nullptr)
|
||||
node->str = *$1;
|
||||
ast_stack.back()->children.push_back(node);
|
||||
}
|
||||
if ($1 != nullptr)
|
||||
delete $1;
|
||||
} |
|
||||
opt_stmt_label TOK_ASSERT opt_property '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
if (noassert_mode)
|
||||
opt_sva_label TOK_ASSERT opt_property '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
if (noassert_mode) {
|
||||
delete $6;
|
||||
else
|
||||
ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6));
|
||||
} else {
|
||||
AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6);
|
||||
if ($1 != nullptr)
|
||||
node->str = *$1;
|
||||
ast_stack.back()->children.push_back(node);
|
||||
}
|
||||
if ($1 != nullptr)
|
||||
delete $1;
|
||||
} |
|
||||
opt_stmt_label TOK_ASSUME opt_property '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
if (noassume_mode)
|
||||
opt_sva_label TOK_ASSUME opt_property '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
if (noassume_mode) {
|
||||
delete $6;
|
||||
else
|
||||
ast_stack.back()->children.push_back(new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6));
|
||||
} else {
|
||||
AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6);
|
||||
if ($1 != nullptr)
|
||||
node->str = *$1;
|
||||
ast_stack.back()->children.push_back(node);
|
||||
}
|
||||
if ($1 != nullptr)
|
||||
delete $1;
|
||||
} |
|
||||
opt_stmt_label TOK_COVER opt_property '(' expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_COVER, $5));
|
||||
opt_sva_label TOK_COVER opt_property '(' expr ')' ';' {
|
||||
AstNode *node = new AstNode(AST_COVER, $5);
|
||||
if ($1 != nullptr) {
|
||||
node->str = *$1;
|
||||
delete $1;
|
||||
}
|
||||
ast_stack.back()->children.push_back(node);
|
||||
} |
|
||||
opt_stmt_label TOK_COVER opt_property '(' ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_COVER, AstNode::mkconst_int(1, false)));
|
||||
opt_sva_label TOK_COVER opt_property '(' ')' ';' {
|
||||
AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false));
|
||||
if ($1 != nullptr) {
|
||||
node->str = *$1;
|
||||
delete $1;
|
||||
}
|
||||
ast_stack.back()->children.push_back(node);
|
||||
} |
|
||||
opt_stmt_label TOK_COVER ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_COVER, AstNode::mkconst_int(1, false)));
|
||||
opt_sva_label TOK_COVER ';' {
|
||||
AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false));
|
||||
if ($1 != nullptr) {
|
||||
node->str = *$1;
|
||||
delete $1;
|
||||
}
|
||||
ast_stack.back()->children.push_back(node);
|
||||
} |
|
||||
opt_stmt_label TOK_RESTRICT opt_property '(' expr ')' ';' {
|
||||
if (norestrict_mode)
|
||||
opt_sva_label TOK_RESTRICT opt_property '(' expr ')' ';' {
|
||||
if (norestrict_mode) {
|
||||
delete $5;
|
||||
else
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5));
|
||||
} else {
|
||||
AstNode *node = new AstNode(AST_ASSUME, $5);
|
||||
if ($1 != nullptr)
|
||||
node->str = *$1;
|
||||
ast_stack.back()->children.push_back(node);
|
||||
}
|
||||
if (!$3)
|
||||
log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n");
|
||||
if ($1 != nullptr)
|
||||
delete $1;
|
||||
} |
|
||||
opt_stmt_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
if (norestrict_mode)
|
||||
opt_sva_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
if (norestrict_mode) {
|
||||
delete $6;
|
||||
else
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6));
|
||||
} else {
|
||||
AstNode *node = new AstNode(AST_FAIR, $6);
|
||||
if ($1 != nullptr)
|
||||
node->str = *$1;
|
||||
ast_stack.back()->children.push_back(node);
|
||||
}
|
||||
if (!$3)
|
||||
log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n");
|
||||
if ($1 != nullptr)
|
||||
delete $1;
|
||||
};
|
||||
|
||||
assert_property:
|
||||
TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $4));
|
||||
opt_sva_label TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5));
|
||||
if ($1 != nullptr) {
|
||||
ast_stack.back()->children.back()->str = *$1;
|
||||
delete $1;
|
||||
}
|
||||
} |
|
||||
TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4));
|
||||
opt_sva_label TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5));
|
||||
if ($1 != nullptr) {
|
||||
ast_stack.back()->children.back()->str = *$1;
|
||||
delete $1;
|
||||
}
|
||||
} |
|
||||
TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $5));
|
||||
opt_sva_label TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6));
|
||||
if ($1 != nullptr) {
|
||||
ast_stack.back()->children.back()->str = *$1;
|
||||
delete $1;
|
||||
}
|
||||
} |
|
||||
TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $5));
|
||||
opt_sva_label TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6));
|
||||
if ($1 != nullptr) {
|
||||
ast_stack.back()->children.back()->str = *$1;
|
||||
delete $1;
|
||||
}
|
||||
} |
|
||||
TOK_COVER TOK_PROPERTY '(' expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_COVER, $4));
|
||||
opt_sva_label TOK_COVER TOK_PROPERTY '(' expr ')' ';' {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_COVER, $5));
|
||||
if ($1 != nullptr) {
|
||||
ast_stack.back()->children.back()->str = *$1;
|
||||
delete $1;
|
||||
}
|
||||
} |
|
||||
TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' {
|
||||
if (norestrict_mode)
|
||||
delete $4;
|
||||
else
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4));
|
||||
} |
|
||||
TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
if (norestrict_mode)
|
||||
opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' {
|
||||
if (norestrict_mode) {
|
||||
delete $5;
|
||||
else
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $5));
|
||||
} else {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5));
|
||||
if ($1 != nullptr) {
|
||||
ast_stack.back()->children.back()->str = *$1;
|
||||
delete $1;
|
||||
}
|
||||
}
|
||||
} |
|
||||
opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
|
||||
if (norestrict_mode) {
|
||||
delete $6;
|
||||
} else {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6));
|
||||
if ($1 != nullptr) {
|
||||
ast_stack.back()->children.back()->str = *$1;
|
||||
delete $1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
simple_behavioral_stmt:
|
||||
|
@ -1670,6 +2044,11 @@ case_expr_list:
|
|||
TOK_DEFAULT {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_DEFAULT));
|
||||
} |
|
||||
TOK_SVA_LABEL {
|
||||
ast_stack.back()->children.push_back(new AstNode(AST_IDENTIFIER));
|
||||
ast_stack.back()->children.back()->str = *$1;
|
||||
delete $1;
|
||||
} |
|
||||
expr {
|
||||
ast_stack.back()->children.push_back($1);
|
||||
} |
|
||||
|
@ -2048,4 +2427,3 @@ concat_list:
|
|||
$$ = $3;
|
||||
$$->children.push_back($1);
|
||||
};
|
||||
|
||||
|
|
|
@ -453,7 +453,7 @@ Aig::Aig(Cell *cell)
|
|||
int B = mk.inport("\\B");
|
||||
int C = mk.inport("\\C");
|
||||
int D = mk.inport("\\D");
|
||||
int Y = mk.nand_gate(mk.nor_gate(A, B), mk.nor_gate(C, D));
|
||||
int Y = mk.nand_gate(mk.or_gate(A, B), mk.or_gate(C, D));
|
||||
mk.outport(Y, "\\Y");
|
||||
goto optimize;
|
||||
}
|
||||
|
|
|
@ -81,6 +81,32 @@ struct CellTypes
|
|||
}
|
||||
|
||||
void setup_internals()
|
||||
{
|
||||
setup_internals_eval();
|
||||
|
||||
IdString A = "\\A", B = "\\B", EN = "\\EN", Y = "\\Y";
|
||||
IdString SRC = "\\SRC", DST = "\\DST", DAT = "\\DAT";
|
||||
IdString EN_SRC = "\\EN_SRC", EN_DST = "\\EN_DST";
|
||||
|
||||
setup_type("$tribuf", {A, EN}, {Y}, true);
|
||||
|
||||
setup_type("$assert", {A, EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$assume", {A, EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$live", {A, EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$fair", {A, EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$cover", {A, EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$initstate", pool<RTLIL::IdString>(), {Y}, true);
|
||||
setup_type("$anyconst", pool<RTLIL::IdString>(), {Y}, true);
|
||||
setup_type("$anyseq", pool<RTLIL::IdString>(), {Y}, true);
|
||||
setup_type("$allconst", pool<RTLIL::IdString>(), {Y}, true);
|
||||
setup_type("$allseq", pool<RTLIL::IdString>(), {Y}, true);
|
||||
setup_type("$equiv", {A, B}, {Y}, true);
|
||||
setup_type("$specify2", {EN, SRC, DST}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$specify3", {EN, SRC, DST, DAT}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$specrule", {EN_SRC, EN_DST, SRC, DST}, pool<RTLIL::IdString>(), true);
|
||||
}
|
||||
|
||||
void setup_internals_eval()
|
||||
{
|
||||
std::vector<RTLIL::IdString> unary_ops = {
|
||||
"$not", "$pos", "$neg",
|
||||
|
@ -111,20 +137,6 @@ struct CellTypes
|
|||
setup_type("$lcu", {P, G, CI}, {CO}, true);
|
||||
setup_type("$alu", {A, B, CI, BI}, {X, Y, CO}, true);
|
||||
setup_type("$fa", {A, B, C}, {X, Y}, true);
|
||||
|
||||
setup_type("$tribuf", {A, EN}, {Y}, true);
|
||||
|
||||
setup_type("$assert", {A, EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$assume", {A, EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$live", {A, EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$fair", {A, EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$cover", {A, EN}, pool<RTLIL::IdString>(), true);
|
||||
setup_type("$initstate", pool<RTLIL::IdString>(), {Y}, true);
|
||||
setup_type("$anyconst", pool<RTLIL::IdString>(), {Y}, true);
|
||||
setup_type("$anyseq", pool<RTLIL::IdString>(), {Y}, true);
|
||||
setup_type("$allconst", pool<RTLIL::IdString>(), {Y}, true);
|
||||
setup_type("$allseq", pool<RTLIL::IdString>(), {Y}, true);
|
||||
setup_type("$equiv", {A, B}, {Y}, true);
|
||||
}
|
||||
|
||||
void setup_internals_mem()
|
||||
|
@ -153,6 +165,15 @@ struct CellTypes
|
|||
}
|
||||
|
||||
void setup_stdcells()
|
||||
{
|
||||
setup_stdcells_eval();
|
||||
|
||||
IdString A = "\\A", E = "\\E", Y = "\\Y";
|
||||
|
||||
setup_type("$_TBUF_", {A, E}, {Y}, true);
|
||||
}
|
||||
|
||||
void setup_stdcells_eval()
|
||||
{
|
||||
IdString A = "\\A", B = "\\B", C = "\\C", D = "\\D";
|
||||
IdString E = "\\E", F = "\\F", G = "\\G", H = "\\H";
|
||||
|
@ -179,7 +200,6 @@ struct CellTypes
|
|||
setup_type("$_OAI3_", {A, B, C}, {Y}, true);
|
||||
setup_type("$_AOI4_", {A, B, C, D}, {Y}, true);
|
||||
setup_type("$_OAI4_", {A, B, C, D}, {Y}, true);
|
||||
setup_type("$_TBUF_", {A, E}, {Y}, true);
|
||||
}
|
||||
|
||||
void setup_stdcells_mem()
|
||||
|
@ -449,7 +469,7 @@ struct CellTypes
|
|||
if (cell->type == "$_AOI4_")
|
||||
return eval_not(const_or(const_and(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1));
|
||||
if (cell->type == "$_OAI4_")
|
||||
return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1));
|
||||
return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_or(arg3, arg4, false, false, 1), false, false, 1));
|
||||
|
||||
log_assert(arg4.bits.size() == 0);
|
||||
return eval(cell, arg1, arg2, arg3, errp);
|
||||
|
|
|
@ -26,7 +26,7 @@ YOSYS_NAMESPACE_BEGIN
|
|||
|
||||
int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr);
|
||||
|
||||
int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> ¶meters = dict<RTLIL::IdString, RTLIL::Const>(),
|
||||
inline int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> ¶meters = dict<RTLIL::IdString, RTLIL::Const>(),
|
||||
RTLIL::Design *design = nullptr, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr)
|
||||
{
|
||||
static dict<RTLIL::IdString, int> gate_cost = {
|
||||
|
@ -76,7 +76,7 @@ int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const
|
|||
return 1;
|
||||
}
|
||||
|
||||
int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache)
|
||||
inline int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache)
|
||||
{
|
||||
return get_cell_cost(cell->type, cell->parameters, cell->module->design, mod_cost_cache);
|
||||
}
|
||||
|
|
|
@ -110,6 +110,10 @@ int main(int argc, char **argv)
|
|||
log_error_stderr = true;
|
||||
yosys_banner();
|
||||
yosys_setup();
|
||||
#ifdef WITH_PYTHON
|
||||
PyRun_SimpleString(("sys.path.append(\""+proc_self_dirname()+"\")").c_str());
|
||||
PyRun_SimpleString(("sys.path.append(\""+proc_share_dirname()+"plugins\")").c_str());
|
||||
#endif
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
|
@ -179,6 +183,7 @@ int main(int argc, char **argv)
|
|||
{
|
||||
std::string frontend_command = "auto";
|
||||
std::string backend_command = "auto";
|
||||
std::vector<std::string> vlog_defines;
|
||||
std::vector<std::string> passes_commands;
|
||||
std::vector<std::string> plugin_filenames;
|
||||
std::string output_filename = "";
|
||||
|
@ -268,7 +273,10 @@ int main(int argc, char **argv)
|
|||
printf(" -A\n");
|
||||
printf(" will call abort() at the end of the script. for debugging\n");
|
||||
printf("\n");
|
||||
printf(" -D <header_id>[:<filename>]\n");
|
||||
printf(" -D <macro>[=<value>]\n");
|
||||
printf(" set the specified Verilog define (via \"read -define\")\n");
|
||||
printf("\n");
|
||||
printf(" -P <header_id>[:<filename>]\n");
|
||||
printf(" dump the design when printing the specified log header to a file.\n");
|
||||
printf(" yosys_dump_<header_id>.il is used as filename if none is specified.\n");
|
||||
printf(" Use 'ALL' as <header_id> to dump at every header.\n");
|
||||
|
@ -287,6 +295,9 @@ int main(int argc, char **argv)
|
|||
printf(" -E <depsfile>\n");
|
||||
printf(" write a Makefile dependencies file with in- and output file names\n");
|
||||
printf("\n");
|
||||
printf(" -g\n");
|
||||
printf(" globally enable debug log messages\n");
|
||||
printf("\n");
|
||||
printf(" -V\n");
|
||||
printf(" print version information and exit\n");
|
||||
printf("\n");
|
||||
|
@ -307,7 +318,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:E:")) != -1)
|
||||
while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
|
@ -332,6 +343,9 @@ int main(int argc, char **argv)
|
|||
case 'S':
|
||||
passes_commands.push_back("synth");
|
||||
break;
|
||||
case 'g':
|
||||
log_force_debug++;
|
||||
break;
|
||||
case 'm':
|
||||
plugin_filenames.push_back(optarg);
|
||||
break;
|
||||
|
@ -408,6 +422,9 @@ int main(int argc, char **argv)
|
|||
std::regex_constants::egrep));
|
||||
break;
|
||||
case 'D':
|
||||
vlog_defines.push_back(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
{
|
||||
auto args = split_tokens(optarg, ":");
|
||||
if (!args.empty() && args[0] == "ALL") {
|
||||
|
@ -462,6 +479,10 @@ int main(int argc, char **argv)
|
|||
#endif
|
||||
|
||||
yosys_setup();
|
||||
#ifdef WITH_PYTHON
|
||||
PyRun_SimpleString(("sys.path.append(\""+proc_self_dirname()+"\")").c_str());
|
||||
PyRun_SimpleString(("sys.path.append(\""+proc_share_dirname()+"plugins\")").c_str());
|
||||
#endif
|
||||
log_error_atexit = yosys_atexit;
|
||||
|
||||
for (auto &fn : plugin_filenames)
|
||||
|
@ -473,6 +494,13 @@ int main(int argc, char **argv)
|
|||
shell(yosys_design);
|
||||
}
|
||||
|
||||
if (!vlog_defines.empty()) {
|
||||
std::string vdef_cmd = "read -define";
|
||||
for (auto vdef : vlog_defines)
|
||||
vdef_cmd += " " + vdef;
|
||||
run_pass(vdef_cmd);
|
||||
}
|
||||
|
||||
while (optind < argc)
|
||||
run_frontend(argv[optind++], frontend_command, output_filename == "-" ? &backend_command : NULL);
|
||||
|
||||
|
@ -501,13 +529,13 @@ int main(int argc, char **argv)
|
|||
log_error("Can't open dependencies file for writing: %s\n", strerror(errno));
|
||||
bool first = true;
|
||||
for (auto fn : yosys_output_files) {
|
||||
fprintf(f, "%s%s", first ? "" : " ", fn.c_str());
|
||||
fprintf(f, "%s%s", first ? "" : " ", escape_filename_spaces(fn).c_str());
|
||||
first = false;
|
||||
}
|
||||
fprintf(f, ":");
|
||||
for (auto fn : yosys_input_files) {
|
||||
if (yosys_output_files.count(fn) == 0)
|
||||
fprintf(f, " %s", fn.c_str());
|
||||
fprintf(f, " %s", escape_filename_spaces(fn).c_str());
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
|
|
@ -557,9 +557,11 @@ public:
|
|||
void clear() { hashtable.clear(); entries.clear(); }
|
||||
|
||||
iterator begin() { return iterator(this, int(entries.size())-1); }
|
||||
iterator element(int n) { return iterator(this, int(entries.size())-1-n); }
|
||||
iterator end() { return iterator(nullptr, -1); }
|
||||
|
||||
const_iterator begin() const { return const_iterator(this, int(entries.size())-1); }
|
||||
const_iterator element(int n) const { return const_iterator(this, int(entries.size())-1-n); }
|
||||
const_iterator end() const { return const_iterator(nullptr, -1); }
|
||||
};
|
||||
|
||||
|
@ -881,9 +883,11 @@ public:
|
|||
void clear() { hashtable.clear(); entries.clear(); }
|
||||
|
||||
iterator begin() { return iterator(this, int(entries.size())-1); }
|
||||
iterator element(int n) { return iterator(this, int(entries.size())-1-n); }
|
||||
iterator end() { return iterator(nullptr, -1); }
|
||||
|
||||
const_iterator begin() const { return const_iterator(this, int(entries.size())-1); }
|
||||
const_iterator element(int n) const { return const_iterator(this, int(entries.size())-1-n); }
|
||||
const_iterator end() const { return const_iterator(nullptr, -1); }
|
||||
};
|
||||
|
||||
|
@ -952,6 +956,7 @@ public:
|
|||
void clear() { database.clear(); }
|
||||
|
||||
const_iterator begin() const { return database.begin(); }
|
||||
const_iterator element(int n) const { return database.element(n); }
|
||||
const_iterator end() const { return database.end(); }
|
||||
};
|
||||
|
||||
|
@ -1051,6 +1056,7 @@ public:
|
|||
void clear() { database.clear(); parents.clear(); }
|
||||
|
||||
const_iterator begin() const { return database.begin(); }
|
||||
const_iterator element(int n) const { return database.element(n); }
|
||||
const_iterator end() const { return database.end(); }
|
||||
};
|
||||
|
||||
|
|
|
@ -56,6 +56,10 @@ int log_verbose_level;
|
|||
string log_last_error;
|
||||
void (*log_error_atexit)() = NULL;
|
||||
|
||||
int log_make_debug = 0;
|
||||
int log_force_debug = 0;
|
||||
int log_debug_suppressed = 0;
|
||||
|
||||
vector<int> header_count;
|
||||
pool<RTLIL::IdString> log_id_cache;
|
||||
vector<shared_str> string_buf;
|
||||
|
@ -92,6 +96,9 @@ void logv(const char *format, va_list ap)
|
|||
format++;
|
||||
}
|
||||
|
||||
if (log_make_debug && !ys_debug(1))
|
||||
return;
|
||||
|
||||
std::string str = vstringf(format, ap);
|
||||
|
||||
if (str.empty())
|
||||
|
@ -196,7 +203,11 @@ void logv_header(RTLIL::Design *design, const char *format, va_list ap)
|
|||
if (log_hdump.count(header_id) && design != nullptr)
|
||||
for (auto &filename : log_hdump.at(header_id)) {
|
||||
log("Dumping current design to '%s'.\n", filename.c_str());
|
||||
if (yosys_xtrace)
|
||||
IdString::xtrace_db_dump();
|
||||
Pass::call(design, {"dump", "-o", filename});
|
||||
if (yosys_xtrace)
|
||||
log("#X# -- end of dump --\n");
|
||||
}
|
||||
|
||||
if (pop_errfile)
|
||||
|
|
|
@ -64,6 +64,10 @@ extern int log_verbose_level;
|
|||
extern string log_last_error;
|
||||
extern void (*log_error_atexit)();
|
||||
|
||||
extern int log_make_debug;
|
||||
extern int log_force_debug;
|
||||
extern int log_debug_suppressed;
|
||||
|
||||
void logv(const char *format, va_list ap);
|
||||
void logv_header(RTLIL::Design *design, const char *format, va_list ap);
|
||||
void logv_warning(const char *format, va_list ap);
|
||||
|
@ -82,6 +86,45 @@ YS_NORETURN void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf,
|
|||
void log_file_error(const string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4), noreturn);
|
||||
YS_NORETURN void log_cmd_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn);
|
||||
|
||||
#ifndef NDEBUG
|
||||
static inline bool ys_debug(int n = 0) { if (log_force_debug) return true; log_debug_suppressed += n; return false; }
|
||||
# define log_debug(...) do { if (ys_debug(1)) log(__VA_ARGS__); } while (0)
|
||||
#else
|
||||
static inline bool ys_debug(int n = 0) { return false; }
|
||||
# define log_debug(_fmt, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
static inline void log_suppressed() {
|
||||
if (log_debug_suppressed && !log_make_debug) {
|
||||
log("<suppressed ~%d debug messages>\n", log_debug_suppressed);
|
||||
log_debug_suppressed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct LogMakeDebugHdl {
|
||||
bool status = false;
|
||||
LogMakeDebugHdl(bool start_on = false) {
|
||||
if (start_on)
|
||||
on();
|
||||
}
|
||||
~LogMakeDebugHdl() {
|
||||
off();
|
||||
}
|
||||
void on() {
|
||||
if (status) return;
|
||||
status=true;
|
||||
log_make_debug++;
|
||||
}
|
||||
void off_silent() {
|
||||
if (!status) return;
|
||||
status=false;
|
||||
log_make_debug--;
|
||||
}
|
||||
void off() {
|
||||
off_silent();
|
||||
}
|
||||
};
|
||||
|
||||
void log_spacer();
|
||||
void log_push();
|
||||
void log_pop();
|
||||
|
@ -94,7 +137,9 @@ const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true);
|
|||
const char *log_const(const RTLIL::Const &value, bool autoint = true);
|
||||
const char *log_id(RTLIL::IdString id);
|
||||
|
||||
template<typename T> static inline const char *log_id(T *obj) {
|
||||
template<typename T> static inline const char *log_id(T *obj, const char *nullstr = nullptr) {
|
||||
if (nullstr && obj == nullptr)
|
||||
return nullstr;
|
||||
return log_id(obj->name);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,9 @@ Pass::pre_post_exec_state_t Pass::pre_execute()
|
|||
|
||||
void Pass::post_execute(Pass::pre_post_exec_state_t state)
|
||||
{
|
||||
IdString::checkpoint();
|
||||
log_suppressed();
|
||||
|
||||
int64_t time_ns = PerformanceTimer::query() - state.begin_ns;
|
||||
runtime_ns += time_ns;
|
||||
current_pass = state.parent_pass;
|
||||
|
|
|
@ -33,6 +33,8 @@ std::vector<int> RTLIL::IdString::global_refcount_storage_;
|
|||
std::vector<char*> RTLIL::IdString::global_id_storage_;
|
||||
dict<char*, int, hash_cstr_ops> RTLIL::IdString::global_id_index_;
|
||||
std::vector<int> RTLIL::IdString::global_free_idx_list_;
|
||||
int RTLIL::IdString::last_created_idx_[8];
|
||||
int RTLIL::IdString::last_created_idx_ptr_;
|
||||
|
||||
RTLIL::Const::Const()
|
||||
{
|
||||
|
@ -74,6 +76,13 @@ RTLIL::Const::Const(const std::vector<bool> &bits)
|
|||
this->bits.push_back(b ? RTLIL::S1 : RTLIL::S0);
|
||||
}
|
||||
|
||||
RTLIL::Const::Const(const RTLIL::Const &c)
|
||||
{
|
||||
flags = c.flags;
|
||||
for (auto b : c.bits)
|
||||
this->bits.push_back(b);
|
||||
}
|
||||
|
||||
bool RTLIL::Const::operator <(const RTLIL::Const &other) const
|
||||
{
|
||||
if (bits.size() != other.bits.size())
|
||||
|
@ -205,16 +214,23 @@ bool RTLIL::Const::is_fully_undef() const
|
|||
return true;
|
||||
}
|
||||
|
||||
void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id)
|
||||
void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value)
|
||||
{
|
||||
attributes[id] = RTLIL::Const(1);
|
||||
if (value)
|
||||
attributes[id] = RTLIL::Const(1);
|
||||
else {
|
||||
const auto it = attributes.find(id);
|
||||
if (it != attributes.end())
|
||||
attributes.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const
|
||||
{
|
||||
if (attributes.count(id) == 0)
|
||||
const auto it = attributes.find(id);
|
||||
if (it == attributes.end())
|
||||
return false;
|
||||
return attributes.at(id).as_bool();
|
||||
return it->second.as_bool();
|
||||
}
|
||||
|
||||
void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<string> &data)
|
||||
|
@ -358,6 +374,10 @@ RTLIL::Design::Design()
|
|||
|
||||
refcount_modules_ = 0;
|
||||
selection_stack.push_back(RTLIL::Selection());
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
RTLIL::Design::get_all_designs()->insert(std::pair<unsigned int, RTLIL::Design*>(hashidx_, this));
|
||||
#endif
|
||||
}
|
||||
|
||||
RTLIL::Design::~Design()
|
||||
|
@ -368,8 +388,19 @@ RTLIL::Design::~Design()
|
|||
delete n;
|
||||
for (auto n : verilog_globals)
|
||||
delete n;
|
||||
#ifdef WITH_PYTHON
|
||||
RTLIL::Design::get_all_designs()->erase(hashidx_);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Design*> all_designs;
|
||||
std::map<unsigned int, RTLIL::Design*> *RTLIL::Design::get_all_designs(void)
|
||||
{
|
||||
return &all_designs;
|
||||
}
|
||||
#endif
|
||||
|
||||
RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules()
|
||||
{
|
||||
return RTLIL::ObjRange<RTLIL::Module*>(&modules_, &refcount_modules_);
|
||||
|
@ -587,7 +618,7 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_modules() const
|
|||
std::vector<RTLIL::Module*> result;
|
||||
result.reserve(modules_.size());
|
||||
for (auto &it : modules_)
|
||||
if (selected_module(it.first) && !it.second->get_bool_attribute("\\blackbox"))
|
||||
if (selected_module(it.first) && !it.second->get_blackbox_attribute())
|
||||
result.push_back(it.second);
|
||||
return result;
|
||||
}
|
||||
|
@ -597,7 +628,7 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules() const
|
|||
std::vector<RTLIL::Module*> result;
|
||||
result.reserve(modules_.size());
|
||||
for (auto &it : modules_)
|
||||
if (selected_whole_module(it.first) && !it.second->get_bool_attribute("\\blackbox"))
|
||||
if (selected_whole_module(it.first) && !it.second->get_blackbox_attribute())
|
||||
result.push_back(it.second);
|
||||
return result;
|
||||
}
|
||||
|
@ -607,7 +638,7 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules_warn() const
|
|||
std::vector<RTLIL::Module*> result;
|
||||
result.reserve(modules_.size());
|
||||
for (auto &it : modules_)
|
||||
if (it.second->get_bool_attribute("\\blackbox"))
|
||||
if (it.second->get_blackbox_attribute())
|
||||
continue;
|
||||
else if (selected_whole_module(it.first))
|
||||
result.push_back(it.second);
|
||||
|
@ -625,6 +656,10 @@ RTLIL::Module::Module()
|
|||
design = nullptr;
|
||||
refcount_wires_ = 0;
|
||||
refcount_cells_ = 0;
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
RTLIL::Module::get_all_modules()->insert(std::pair<unsigned int, RTLIL::Module*>(hashidx_, this));
|
||||
#endif
|
||||
}
|
||||
|
||||
RTLIL::Module::~Module()
|
||||
|
@ -637,6 +672,41 @@ RTLIL::Module::~Module()
|
|||
delete it->second;
|
||||
for (auto it = processes.begin(); it != processes.end(); ++it)
|
||||
delete it->second;
|
||||
#ifdef WITH_PYTHON
|
||||
RTLIL::Module::get_all_modules()->erase(hashidx_);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Module*> all_modules;
|
||||
std::map<unsigned int, RTLIL::Module*> *RTLIL::Module::get_all_modules(void)
|
||||
{
|
||||
return &all_modules;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RTLIL::Module::makeblackbox()
|
||||
{
|
||||
pool<RTLIL::Wire*> delwires;
|
||||
|
||||
for (auto it = wires_.begin(); it != wires_.end(); ++it)
|
||||
if (!it->second->port_input && !it->second->port_output)
|
||||
delwires.insert(it->second);
|
||||
|
||||
for (auto it = memories.begin(); it != memories.end(); ++it)
|
||||
delete it->second;
|
||||
memories.clear();
|
||||
|
||||
for (auto it = cells_.begin(); it != cells_.end(); ++it)
|
||||
delete it->second;
|
||||
cells_.clear();
|
||||
|
||||
for (auto it = processes.begin(); it != processes.end(); ++it)
|
||||
delete it->second;
|
||||
processes.clear();
|
||||
|
||||
remove(delwires);
|
||||
set_bool_attribute("\\blackbox");
|
||||
}
|
||||
|
||||
void RTLIL::Module::reprocess_module(RTLIL::Design *, dict<RTLIL::IdString, RTLIL::Module *>)
|
||||
|
@ -758,7 +828,7 @@ namespace {
|
|||
|
||||
void check()
|
||||
{
|
||||
if (cell->type.substr(0, 1) != "$" || cell->type.substr(0, 3) == "$__" || cell->type.substr(0, 8) == "$paramod" ||
|
||||
if (cell->type.substr(0, 1) != "$" || cell->type.substr(0, 3) == "$__" || cell->type.substr(0, 8) == "$paramod" || cell->type.substr(0,10) == "$fmcombine" ||
|
||||
cell->type.substr(0, 9) == "$verific$" || cell->type.substr(0, 7) == "$array:" || cell->type.substr(0, 8) == "$extern:")
|
||||
return;
|
||||
|
||||
|
@ -1128,6 +1198,46 @@ namespace {
|
|||
return;
|
||||
}
|
||||
|
||||
if (cell->type.in("$specify2", "$specify3")) {
|
||||
param_bool("\\FULL");
|
||||
param_bool("\\SRC_DST_PEN");
|
||||
param_bool("\\SRC_DST_POL");
|
||||
param("\\T_RISE_MIN");
|
||||
param("\\T_RISE_TYP");
|
||||
param("\\T_RISE_MAX");
|
||||
param("\\T_FALL_MIN");
|
||||
param("\\T_FALL_TYP");
|
||||
param("\\T_FALL_MAX");
|
||||
port("\\EN", 1);
|
||||
port("\\SRC", param("\\SRC_WIDTH"));
|
||||
port("\\DST", param("\\DST_WIDTH"));
|
||||
if (cell->type == "$specify3") {
|
||||
param_bool("\\EDGE_EN");
|
||||
param_bool("\\EDGE_POL");
|
||||
param_bool("\\DAT_DST_PEN");
|
||||
param_bool("\\DAT_DST_POL");
|
||||
port("\\DAT", param("\\DST_WIDTH"));
|
||||
}
|
||||
check_expected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type == "$specrule") {
|
||||
param("\\TYPE");
|
||||
param_bool("\\SRC_PEN");
|
||||
param_bool("\\SRC_POL");
|
||||
param_bool("\\DST_PEN");
|
||||
param_bool("\\DST_POL");
|
||||
param("\\T_LIMIT");
|
||||
param("\\T_LIMIT2");
|
||||
port("\\SRC_EN", 1);
|
||||
port("\\DST_EN", 1);
|
||||
port("\\SRC", param("\\SRC_WIDTH"));
|
||||
port("\\DST", param("\\DST_WIDTH"));
|
||||
check_expected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type == "$_BUF_") { check_gate("AY"); return; }
|
||||
if (cell->type == "$_NOT_") { check_gate("AY"); return; }
|
||||
if (cell->type == "$_AND_") { check_gate("ABY"); return; }
|
||||
|
@ -1404,7 +1514,10 @@ void RTLIL::Module::add(RTLIL::Cell *cell)
|
|||
cell->module = this;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
|
||||
{
|
||||
log_assert(refcount_wires_ == 0);
|
||||
|
||||
struct DeleteWireWorker
|
||||
{
|
||||
RTLIL::Module *module;
|
||||
|
@ -1419,17 +1532,29 @@ namespace {
|
|||
}
|
||||
sig = chunks;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
|
||||
{
|
||||
log_assert(refcount_wires_ == 0);
|
||||
void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) {
|
||||
log_assert(GetSize(lhs) == GetSize(rhs));
|
||||
RTLIL::SigSpec new_lhs, new_rhs;
|
||||
for (int i = 0; i < GetSize(lhs); i++) {
|
||||
RTLIL::SigBit lhs_bit = lhs[i];
|
||||
if (lhs_bit.wire != nullptr && wires_p->count(lhs_bit.wire))
|
||||
continue;
|
||||
RTLIL::SigBit rhs_bit = rhs[i];
|
||||
if (rhs_bit.wire != nullptr && wires_p->count(rhs_bit.wire))
|
||||
continue;
|
||||
new_lhs.append(lhs_bit);
|
||||
new_rhs.append(rhs_bit);
|
||||
}
|
||||
lhs = new_lhs;
|
||||
rhs = new_rhs;
|
||||
}
|
||||
};
|
||||
|
||||
DeleteWireWorker delete_wire_worker;
|
||||
delete_wire_worker.module = this;
|
||||
delete_wire_worker.wires_p = &wires;
|
||||
rewrite_sigspecs(delete_wire_worker);
|
||||
rewrite_sigspecs2(delete_wire_worker);
|
||||
|
||||
for (auto &it : wires) {
|
||||
log_assert(wires_.count(it->name) != 0);
|
||||
|
@ -2200,8 +2325,27 @@ RTLIL::Wire::Wire()
|
|||
port_input = false;
|
||||
port_output = false;
|
||||
upto = false;
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
RTLIL::Wire::get_all_wires()->insert(std::pair<unsigned int, RTLIL::Wire*>(hashidx_, this));
|
||||
#endif
|
||||
}
|
||||
|
||||
RTLIL::Wire::~Wire()
|
||||
{
|
||||
#ifdef WITH_PYTHON
|
||||
RTLIL::Wire::get_all_wires()->erase(hashidx_);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Wire*> all_wires;
|
||||
std::map<unsigned int, RTLIL::Wire*> *RTLIL::Wire::get_all_wires(void)
|
||||
{
|
||||
return &all_wires;
|
||||
}
|
||||
#endif
|
||||
|
||||
RTLIL::Memory::Memory()
|
||||
{
|
||||
static unsigned int hashidx_count = 123456789;
|
||||
|
@ -2211,6 +2355,9 @@ RTLIL::Memory::Memory()
|
|||
width = 1;
|
||||
start_offset = 0;
|
||||
size = 0;
|
||||
#ifdef WITH_PYTHON
|
||||
RTLIL::Memory::get_all_memorys()->insert(std::pair<unsigned int, RTLIL::Memory*>(hashidx_, this));
|
||||
#endif
|
||||
}
|
||||
|
||||
RTLIL::Cell::Cell() : module(nullptr)
|
||||
|
@ -2221,8 +2368,27 @@ RTLIL::Cell::Cell() : module(nullptr)
|
|||
|
||||
// log("#memtrace# %p\n", this);
|
||||
memhasher();
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
RTLIL::Cell::get_all_cells()->insert(std::pair<unsigned int, RTLIL::Cell*>(hashidx_, this));
|
||||
#endif
|
||||
}
|
||||
|
||||
RTLIL::Cell::~Cell()
|
||||
{
|
||||
#ifdef WITH_PYTHON
|
||||
RTLIL::Cell::get_all_cells()->erase(hashidx_);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Cell*> all_cells;
|
||||
std::map<unsigned int, RTLIL::Cell*> *RTLIL::Cell::get_all_cells(void)
|
||||
{
|
||||
return &all_cells;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool RTLIL::Cell::hasPort(RTLIL::IdString portname) const
|
||||
{
|
||||
return connections_.count(portname) != 0;
|
||||
|
@ -2358,7 +2524,7 @@ void RTLIL::Cell::check()
|
|||
|
||||
void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
|
||||
{
|
||||
if (type.substr(0, 1) != "$" || type.substr(0, 2) == "$_" || type.substr(0, 8) == "$paramod" ||
|
||||
if (type.substr(0, 1) != "$" || type.substr(0, 2) == "$_" || type.substr(0, 8) == "$paramod" || type.substr(0,10) == "$fmcombine" ||
|
||||
type.substr(0, 9) == "$verific$" || type.substr(0, 7) == "$array:" || type.substr(0, 8) == "$extern:")
|
||||
return;
|
||||
|
||||
|
@ -2410,6 +2576,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
|
|||
if (connections_.count("\\Y"))
|
||||
parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]);
|
||||
|
||||
if (connections_.count("\\Q"))
|
||||
parameters["\\WIDTH"] = GetSize(connections_["\\Q"]);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
|
@ -2479,6 +2648,14 @@ RTLIL::SigChunk::SigChunk(RTLIL::SigBit bit)
|
|||
width = 1;
|
||||
}
|
||||
|
||||
RTLIL::SigChunk::SigChunk(const RTLIL::SigChunk &sigchunk) : data(sigchunk.data)
|
||||
{
|
||||
wire = sigchunk.wire;
|
||||
data = sigchunk.data;
|
||||
width = sigchunk.width;
|
||||
offset = sigchunk.offset;
|
||||
}
|
||||
|
||||
RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const
|
||||
{
|
||||
RTLIL::SigChunk ret;
|
||||
|
@ -3232,7 +3409,7 @@ void RTLIL::SigSpec::extend_u0(int width, bool is_signed)
|
|||
remove(width, width_ - width);
|
||||
|
||||
if (width_ < width) {
|
||||
RTLIL::SigBit padding = width_ > 0 ? (*this)[width_ - 1] : RTLIL::State::S0;
|
||||
RTLIL::SigBit padding = width_ > 0 ? (*this)[width_ - 1] : RTLIL::State::Sx;
|
||||
if (!is_signed)
|
||||
padding = RTLIL::State::S0;
|
||||
while (width_ < width)
|
||||
|
@ -3338,7 +3515,7 @@ bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const
|
|||
pack();
|
||||
other.pack();
|
||||
|
||||
if (chunks_.size() != chunks_.size())
|
||||
if (chunks_.size() != other.chunks_.size())
|
||||
return false;
|
||||
|
||||
updhash();
|
||||
|
@ -3863,5 +4040,15 @@ RTLIL::Process *RTLIL::Process::clone() const
|
|||
return new_proc;
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
RTLIL::Memory::~Memory()
|
||||
{
|
||||
RTLIL::Memory::get_all_memorys()->erase(hashidx_);
|
||||
}
|
||||
static std::map<unsigned int, RTLIL::Memory*> all_memorys;
|
||||
std::map<unsigned int, RTLIL::Memory*> *RTLIL::Memory::get_all_memorys(void)
|
||||
{
|
||||
return &all_memorys;
|
||||
}
|
||||
#endif
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace RTLIL
|
|||
CONST_FLAG_NONE = 0,
|
||||
CONST_FLAG_STRING = 1,
|
||||
CONST_FLAG_SIGNED = 2, // only used for parameters
|
||||
CONST_FLAG_REAL = 4 // unused -- to be used for parameters
|
||||
CONST_FLAG_REAL = 4 // only used for parameters
|
||||
};
|
||||
|
||||
struct Const;
|
||||
|
@ -76,6 +76,9 @@ namespace RTLIL
|
|||
|
||||
struct IdString
|
||||
{
|
||||
#undef YOSYS_XTRACE_GET_PUT
|
||||
#undef YOSYS_SORT_ID_FREE_LIST
|
||||
|
||||
// the global id string cache
|
||||
|
||||
static struct destruct_guard_t {
|
||||
|
@ -89,9 +92,43 @@ namespace RTLIL
|
|||
static dict<char*, int, hash_cstr_ops> global_id_index_;
|
||||
static std::vector<int> global_free_idx_list_;
|
||||
|
||||
static int last_created_idx_ptr_;
|
||||
static int last_created_idx_[8];
|
||||
|
||||
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 void checkpoint()
|
||||
{
|
||||
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;
|
||||
}
|
||||
#ifdef YOSYS_SORT_ID_FREE_LIST
|
||||
std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater<int>());
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int get_reference(int idx)
|
||||
{
|
||||
global_refcount_storage_.at(idx)++;
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -107,6 +144,11 @@ namespace RTLIL
|
|||
auto it = global_id_index_.find((char*)p);
|
||||
if (it != global_id_index_.end()) {
|
||||
global_refcount_storage_.at(it->second)++;
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -124,16 +166,22 @@ namespace RTLIL
|
|||
global_refcount_storage_.at(idx)++;
|
||||
|
||||
// Avoid Create->Delete->Create pattern
|
||||
static IdString last_created_id;
|
||||
put_reference(last_created_id.index_);
|
||||
last_created_id.index_ = idx;
|
||||
get_reference(last_created_id.index_);
|
||||
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;
|
||||
|
||||
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
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
@ -144,6 +192,12 @@ namespace RTLIL
|
|||
if (!destruct_guard.ok)
|
||||
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
|
||||
|
||||
log_assert(global_refcount_storage_.at(idx) > 0);
|
||||
|
||||
if (--global_refcount_storage_.at(idx) != 0)
|
||||
|
@ -463,6 +517,8 @@ struct RTLIL::Const
|
|||
Const(RTLIL::State bit, int width = 1);
|
||||
Const(const std::vector<RTLIL::State> &bits) : bits(bits) { flags = CONST_FLAG_NONE; }
|
||||
Const(const std::vector<bool> &bits);
|
||||
Const(const RTLIL::Const &c);
|
||||
RTLIL::Const &operator =(const RTLIL::Const &other) = default;
|
||||
|
||||
bool operator <(const RTLIL::Const &other) const;
|
||||
bool operator ==(const RTLIL::Const &other) const;
|
||||
|
@ -492,6 +548,14 @@ struct RTLIL::Const
|
|||
return ret;
|
||||
}
|
||||
|
||||
void extu(int width) {
|
||||
bits.resize(width, RTLIL::State::S0);
|
||||
}
|
||||
|
||||
void exts(int width) {
|
||||
bits.resize(width, bits.empty() ? RTLIL::State::Sx : bits.back());
|
||||
}
|
||||
|
||||
inline unsigned int hash() const {
|
||||
unsigned int h = mkhash_init;
|
||||
for (auto b : bits)
|
||||
|
@ -504,9 +568,13 @@ struct RTLIL::AttrObject
|
|||
{
|
||||
dict<RTLIL::IdString, RTLIL::Const> attributes;
|
||||
|
||||
void set_bool_attribute(RTLIL::IdString id);
|
||||
void set_bool_attribute(RTLIL::IdString id, bool value=true);
|
||||
bool get_bool_attribute(RTLIL::IdString id) const;
|
||||
|
||||
bool get_blackbox_attribute(bool ignore_wb=false) const {
|
||||
return get_bool_attribute("\\blackbox") || (!ignore_wb && get_bool_attribute("\\whitebox"));
|
||||
}
|
||||
|
||||
void set_strpool_attribute(RTLIL::IdString id, const pool<string> &data);
|
||||
void add_strpool_attribute(RTLIL::IdString id, const pool<string> &data);
|
||||
pool<string> get_strpool_attribute(RTLIL::IdString id) const;
|
||||
|
@ -529,6 +597,8 @@ struct RTLIL::SigChunk
|
|||
SigChunk(int val, int width = 32);
|
||||
SigChunk(RTLIL::State bit, int width = 1);
|
||||
SigChunk(RTLIL::SigBit bit);
|
||||
SigChunk(const RTLIL::SigChunk &sigchunk);
|
||||
RTLIL::SigChunk &operator =(const RTLIL::SigChunk &other) = default;
|
||||
|
||||
RTLIL::SigChunk extract(int offset, int length) const;
|
||||
|
||||
|
@ -553,6 +623,8 @@ struct RTLIL::SigBit
|
|||
SigBit(const RTLIL::SigChunk &chunk);
|
||||
SigBit(const RTLIL::SigChunk &chunk, int index);
|
||||
SigBit(const RTLIL::SigSpec &sig);
|
||||
SigBit(const RTLIL::SigBit &sigbit);
|
||||
RTLIL::SigBit &operator =(const RTLIL::SigBit &other) = default;
|
||||
|
||||
bool operator <(const RTLIL::SigBit &other) const;
|
||||
bool operator ==(const RTLIL::SigBit &other) const;
|
||||
|
@ -874,9 +946,13 @@ struct RTLIL::Design
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<RTLIL::Module*> selected_modules() const;
|
||||
std::vector<RTLIL::Module*> selected_whole_modules() const;
|
||||
std::vector<RTLIL::Module*> selected_whole_modules_warn() const;
|
||||
#ifdef WITH_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Design*> *get_all_designs(void);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct RTLIL::Module : public RTLIL::AttrObject
|
||||
|
@ -914,6 +990,7 @@ public:
|
|||
virtual void sort();
|
||||
virtual void check();
|
||||
virtual void optimize();
|
||||
virtual void makeblackbox();
|
||||
|
||||
void connect(const RTLIL::SigSig &conn);
|
||||
void connect(const RTLIL::SigSpec &lhs, const RTLIL::SigSpec &rhs);
|
||||
|
@ -924,6 +1001,7 @@ public:
|
|||
void fixup_ports();
|
||||
|
||||
template<typename T> void rewrite_sigspecs(T &functor);
|
||||
template<typename T> void rewrite_sigspecs2(T &functor);
|
||||
void cloneInto(RTLIL::Module *new_mod) const;
|
||||
virtual RTLIL::Module *clone() const;
|
||||
|
||||
|
@ -1132,6 +1210,10 @@ public:
|
|||
RTLIL::SigSpec Allconst (RTLIL::IdString name, int width = 1, const std::string &src = "");
|
||||
RTLIL::SigSpec Allseq (RTLIL::IdString name, int width = 1, const std::string &src = "");
|
||||
RTLIL::SigSpec Initstate (RTLIL::IdString name, const std::string &src = "");
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Module*> *get_all_modules(void);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct RTLIL::Wire : public RTLIL::AttrObject
|
||||
|
@ -1143,7 +1225,7 @@ protected:
|
|||
// use module->addWire() and module->remove() to create or destroy wires
|
||||
friend struct RTLIL::Module;
|
||||
Wire();
|
||||
~Wire() { };
|
||||
~Wire();
|
||||
|
||||
public:
|
||||
// do not simply copy wires
|
||||
|
@ -1154,6 +1236,10 @@ public:
|
|||
RTLIL::IdString name;
|
||||
int width, start_offset, port_id;
|
||||
bool port_input, port_output, upto;
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Wire*> *get_all_wires(void);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct RTLIL::Memory : public RTLIL::AttrObject
|
||||
|
@ -1165,6 +1251,10 @@ struct RTLIL::Memory : public RTLIL::AttrObject
|
|||
|
||||
RTLIL::IdString name;
|
||||
int width, start_offset, size;
|
||||
#ifdef WITH_PYTHON
|
||||
~Memory();
|
||||
static std::map<unsigned int, RTLIL::Memory*> *get_all_memorys(void);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct RTLIL::Cell : public RTLIL::AttrObject
|
||||
|
@ -1176,6 +1266,7 @@ protected:
|
|||
// use module->addCell() and module->remove() to create or destroy cells
|
||||
friend struct RTLIL::Module;
|
||||
Cell();
|
||||
~Cell();
|
||||
|
||||
public:
|
||||
// do not simply copy cells
|
||||
|
@ -1216,6 +1307,11 @@ public:
|
|||
}
|
||||
|
||||
template<typename T> void rewrite_sigspecs(T &functor);
|
||||
template<typename T> void rewrite_sigspecs2(T &functor);
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
static std::map<unsigned int, RTLIL::Cell*> *get_all_cells(void);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct RTLIL::CaseRule
|
||||
|
@ -1230,6 +1326,7 @@ struct RTLIL::CaseRule
|
|||
bool empty() const;
|
||||
|
||||
template<typename T> void rewrite_sigspecs(T &functor);
|
||||
template<typename T> void rewrite_sigspecs2(T &functor);
|
||||
RTLIL::CaseRule *clone() const;
|
||||
};
|
||||
|
||||
|
@ -1243,6 +1340,7 @@ struct RTLIL::SwitchRule : public RTLIL::AttrObject
|
|||
bool empty() const;
|
||||
|
||||
template<typename T> void rewrite_sigspecs(T &functor);
|
||||
template<typename T> void rewrite_sigspecs2(T &functor);
|
||||
RTLIL::SwitchRule *clone() const;
|
||||
};
|
||||
|
||||
|
@ -1253,6 +1351,7 @@ struct RTLIL::SyncRule
|
|||
std::vector<RTLIL::SigSig> actions;
|
||||
|
||||
template<typename T> void rewrite_sigspecs(T &functor);
|
||||
template<typename T> void rewrite_sigspecs2(T &functor);
|
||||
RTLIL::SyncRule *clone() const;
|
||||
};
|
||||
|
||||
|
@ -1265,6 +1364,7 @@ struct RTLIL::Process : public RTLIL::AttrObject
|
|||
~Process();
|
||||
|
||||
template<typename T> void rewrite_sigspecs(T &functor);
|
||||
template<typename T> void rewrite_sigspecs2(T &functor);
|
||||
RTLIL::Process *clone() const;
|
||||
};
|
||||
|
||||
|
@ -1276,13 +1376,14 @@ inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire) : wire(wire), offset(0) { log_as
|
|||
inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire, int offset) : wire(wire), offset(offset) { log_assert(wire != nullptr); }
|
||||
inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk) : wire(chunk.wire) { log_assert(chunk.width == 1); if (wire) offset = chunk.offset; else data = chunk.data[0]; }
|
||||
inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk, int index) : wire(chunk.wire) { if (wire) offset = chunk.offset + index; else data = chunk.data[index]; }
|
||||
inline RTLIL::SigBit::SigBit(const RTLIL::SigBit &sigbit) : wire(sigbit.wire), data(sigbit.data){if(wire) offset = sigbit.offset;}
|
||||
|
||||
inline bool RTLIL::SigBit::operator<(const RTLIL::SigBit &other) const {
|
||||
if (wire == other.wire)
|
||||
return wire ? (offset < other.offset) : (data < other.data);
|
||||
if (wire != nullptr && other.wire != nullptr)
|
||||
return wire->name < other.wire->name;
|
||||
return wire < other.wire;
|
||||
return (wire != nullptr) < (other.wire != nullptr);
|
||||
}
|
||||
|
||||
inline bool RTLIL::SigBit::operator==(const RTLIL::SigBit &other) const {
|
||||
|
@ -1325,12 +1426,30 @@ void RTLIL::Module::rewrite_sigspecs(T &functor)
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::Module::rewrite_sigspecs2(T &functor)
|
||||
{
|
||||
for (auto &it : cells_)
|
||||
it.second->rewrite_sigspecs2(functor);
|
||||
for (auto &it : processes)
|
||||
it.second->rewrite_sigspecs2(functor);
|
||||
for (auto &it : connections_) {
|
||||
functor(it.first, it.second);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::Cell::rewrite_sigspecs(T &functor) {
|
||||
for (auto &it : connections_)
|
||||
functor(it.second);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::Cell::rewrite_sigspecs2(T &functor) {
|
||||
for (auto &it : connections_)
|
||||
functor(it.second);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::CaseRule::rewrite_sigspecs(T &functor) {
|
||||
for (auto &it : compare)
|
||||
|
@ -1343,6 +1462,17 @@ void RTLIL::CaseRule::rewrite_sigspecs(T &functor) {
|
|||
it->rewrite_sigspecs(functor);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::CaseRule::rewrite_sigspecs2(T &functor) {
|
||||
for (auto &it : compare)
|
||||
functor(it);
|
||||
for (auto &it : actions) {
|
||||
functor(it.first, it.second);
|
||||
}
|
||||
for (auto it : switches)
|
||||
it->rewrite_sigspecs2(functor);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::SwitchRule::rewrite_sigspecs(T &functor)
|
||||
{
|
||||
|
@ -1351,6 +1481,14 @@ void RTLIL::SwitchRule::rewrite_sigspecs(T &functor)
|
|||
it->rewrite_sigspecs(functor);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::SwitchRule::rewrite_sigspecs2(T &functor)
|
||||
{
|
||||
functor(signal);
|
||||
for (auto it : cases)
|
||||
it->rewrite_sigspecs2(functor);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::SyncRule::rewrite_sigspecs(T &functor)
|
||||
{
|
||||
|
@ -1361,6 +1499,15 @@ void RTLIL::SyncRule::rewrite_sigspecs(T &functor)
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::SyncRule::rewrite_sigspecs2(T &functor)
|
||||
{
|
||||
functor(signal);
|
||||
for (auto &it : actions) {
|
||||
functor(it.first, it.second);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::Process::rewrite_sigspecs(T &functor)
|
||||
{
|
||||
|
@ -1369,6 +1516,14 @@ void RTLIL::Process::rewrite_sigspecs(T &functor)
|
|||
it->rewrite_sigspecs(functor);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RTLIL::Process::rewrite_sigspecs2(T &functor)
|
||||
{
|
||||
root_case.rewrite_sigspecs2(functor);
|
||||
for (auto it : syncs)
|
||||
it->rewrite_sigspecs2(functor);
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32)
|
||||
# include <windows.h>
|
||||
# include <io.h>
|
||||
#elif defined(__APPLE__)
|
||||
|
@ -41,13 +41,15 @@
|
|||
# include <unistd.h>
|
||||
# include <dirent.h>
|
||||
# include <sys/stat.h>
|
||||
# include <glob.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# include <dirent.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/wait.h>
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && defined(YOSYS_ENABLE_GLOB)
|
||||
# include <glob.h>
|
||||
#endif
|
||||
|
||||
|
@ -55,6 +57,16 @@
|
|||
# include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
# define INIT_MODULE PyInit_libyosys
|
||||
extern "C" PyObject* INIT_MODULE();
|
||||
#else
|
||||
# define INIT_MODULE initlibyosys
|
||||
extern "C" void INIT_MODULE();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
@ -139,14 +151,16 @@ void yosys_banner()
|
|||
|
||||
int ceil_log2(int x)
|
||||
{
|
||||
#if defined(__GNUC__)
|
||||
return x > 1 ? (8*sizeof(int)) - __builtin_clz(x-1) : 0;
|
||||
#else
|
||||
if (x <= 0)
|
||||
return 0;
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
if (((x-1) >> i) == 0)
|
||||
return i;
|
||||
|
||||
log_abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string stringf(const char *fmt, ...)
|
||||
|
@ -216,12 +230,18 @@ std::string next_token(std::string &text, const char *sep, bool long_strings)
|
|||
|
||||
if (long_strings && pos_begin != text.size() && text[pos_begin] == '"') {
|
||||
string sep_string = sep;
|
||||
for (size_t i = pos_begin+1; i < text.size(); i++)
|
||||
for (size_t i = pos_begin+1; i < text.size(); i++) {
|
||||
if (text[i] == '"' && (i+1 == text.size() || sep_string.find(text[i+1]) != std::string::npos)) {
|
||||
std::string token = text.substr(pos_begin, i-pos_begin+1);
|
||||
text = text.substr(i+1);
|
||||
return token;
|
||||
}
|
||||
if (i+1 < text.size() && text[i] == '"' && text[i+1] == ';' && (i+2 == text.size() || sep_string.find(text[i+2]) != std::string::npos)) {
|
||||
std::string token = text.substr(pos_begin, i-pos_begin+1);
|
||||
text = text.substr(i+2);
|
||||
return token + ";";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t pos_end = text.find_first_of(sep, pos_begin);
|
||||
|
@ -464,26 +484,61 @@ void remove_directory(std::string dirname)
|
|||
#endif
|
||||
}
|
||||
|
||||
std::string escape_filename_spaces(const std::string& filename)
|
||||
{
|
||||
std::string out;
|
||||
out.reserve(filename.size());
|
||||
for (auto c : filename)
|
||||
{
|
||||
if (c == ' ')
|
||||
out += "\\ ";
|
||||
else
|
||||
out.push_back(c);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int GetSize(RTLIL::Wire *wire)
|
||||
{
|
||||
return wire->width;
|
||||
}
|
||||
|
||||
bool already_setup = false;
|
||||
|
||||
void yosys_setup()
|
||||
{
|
||||
if(already_setup)
|
||||
return;
|
||||
already_setup = true;
|
||||
// if there are already IdString objects then we have a global initialization order bug
|
||||
IdString empty_id;
|
||||
log_assert(empty_id.index_ == 0);
|
||||
IdString::get_reference(empty_id.index_);
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
PyImport_AppendInittab((char*)"libyosys", INIT_MODULE);
|
||||
Py_Initialize();
|
||||
PyRun_SimpleString("import sys");
|
||||
#endif
|
||||
|
||||
Pass::init_register();
|
||||
yosys_design = new RTLIL::Design;
|
||||
yosys_celltypes.setup();
|
||||
log_push();
|
||||
}
|
||||
|
||||
bool yosys_already_setup()
|
||||
{
|
||||
return already_setup;
|
||||
}
|
||||
|
||||
bool already_shutdown = false;
|
||||
|
||||
void yosys_shutdown()
|
||||
{
|
||||
if(already_shutdown)
|
||||
return;
|
||||
already_shutdown = true;
|
||||
log_pop();
|
||||
|
||||
delete yosys_design;
|
||||
|
@ -511,9 +566,16 @@ void yosys_shutdown()
|
|||
dlclose(it.second);
|
||||
|
||||
loaded_plugins.clear();
|
||||
#ifdef WITH_PYTHON
|
||||
loaded_python_plugins.clear();
|
||||
#endif
|
||||
loaded_plugin_aliases.clear();
|
||||
#endif
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
Py_Finalize();
|
||||
#endif
|
||||
|
||||
IdString empty_id;
|
||||
IdString::put_reference(empty_id.index_);
|
||||
}
|
||||
|
@ -564,7 +626,7 @@ std::vector<std::string> glob_filename(const std::string &filename_pattern)
|
|||
{
|
||||
std::vector<std::string> results;
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) || !defined(YOSYS_ENABLE_GLOB)
|
||||
results.push_back(filename_pattern);
|
||||
#else
|
||||
glob_t globbuf;
|
||||
|
|
|
@ -66,6 +66,10 @@
|
|||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
#include <Python.h>
|
||||
#endif
|
||||
|
||||
#ifndef _YOSYS_
|
||||
# error It looks like you are trying to build Yosys without the config defines set. \
|
||||
When building Yosys with a custom make system, make sure you set all the \
|
||||
|
@ -115,6 +119,7 @@ extern const char *Tcl_GetStringResult(Tcl_Interp *interp);
|
|||
# define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#define YOSYS_NAMESPACE Yosys
|
||||
#define PRIVATE_NAMESPACE_BEGIN namespace {
|
||||
#define PRIVATE_NAMESPACE_END }
|
||||
#define YOSYS_NAMESPACE_BEGIN namespace Yosys {
|
||||
|
@ -239,7 +244,7 @@ extern bool memhasher_active;
|
|||
inline void memhasher() { if (memhasher_active) memhasher_do(); }
|
||||
|
||||
void yosys_banner();
|
||||
int ceil_log2(int x);
|
||||
int ceil_log2(int x) YS_ATTRIBUTE(const);
|
||||
std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
||||
std::string vstringf(const char *fmt, va_list ap);
|
||||
int readsome(std::istream &f, char *s, int n);
|
||||
|
@ -252,6 +257,7 @@ std::string make_temp_dir(std::string template_str = "/tmp/yosys_XXXXXX");
|
|||
bool check_file_exists(std::string filename, bool is_exec = false);
|
||||
bool is_absolute_path(std::string filename);
|
||||
void remove_directory(std::string dirname);
|
||||
std::string escape_filename_spaces(const std::string& filename);
|
||||
|
||||
template<typename T> int GetSize(const T &obj) { return obj.size(); }
|
||||
int GetSize(RTLIL::Wire *wire);
|
||||
|
@ -276,6 +282,11 @@ namespace hashlib {
|
|||
}
|
||||
|
||||
void yosys_setup();
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
bool yosys_already_setup();
|
||||
#endif
|
||||
|
||||
void yosys_shutdown();
|
||||
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
|
@ -317,6 +328,9 @@ extern std::vector<RTLIL::Design*> pushed_designs;
|
|||
|
||||
// from passes/cmds/pluginc.cc
|
||||
extern std::map<std::string, void*> loaded_plugins;
|
||||
#ifdef WITH_PYTHON
|
||||
extern std::map<std::string, void*> loaded_python_plugins;
|
||||
#endif
|
||||
extern std::map<std::string, std::string> loaded_plugin_aliases;
|
||||
void load_plugin(std::string filename, std::vector<std::string> aliases);
|
||||
|
||||
|
|
|
@ -320,12 +320,10 @@ class SubCircuit::SolverWorker
|
|||
|
||||
static int numberOfPermutations(const std::vector<std::string> &list)
|
||||
{
|
||||
int numPermutations = 1;
|
||||
for (int i = 0; i < int(list.size()); i++) {
|
||||
assert(numPermutations < maxPermutationsLimit);
|
||||
numPermutations *= i+1;
|
||||
}
|
||||
return numPermutations;
|
||||
constexpr size_t mappedPermutationsSize = 10;
|
||||
constexpr int mappedPermutations[mappedPermutationsSize] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
|
||||
assert(list.size() < mappedPermutationsSize);
|
||||
return mappedPermutations[list.size()];
|
||||
}
|
||||
|
||||
static void permutateVectorToMap(std::map<std::string, std::string> &map, const std::vector<std::string> &list, int idx)
|
||||
|
|
|
@ -119,6 +119,12 @@ than one bit from \B{S} is set the output is undefined. Cells of this type are u
|
|||
``parallel cases'' (defined by using the {\tt parallel\_case} attribute or detected by
|
||||
an optimization).
|
||||
|
||||
The {\tt \$tribuf} cell is used to implement tristate logic. Cells of this type have a \B{WIDTH}
|
||||
parameter and inputs \B{A} and \B{EN} and an output \B{Y}. The \B{A} input and \B{Y} output are
|
||||
\B{WIDTH} bits wide, and the \B{EN} input is one bit wide. When \B{EN} is 0, the output \B{Y}
|
||||
is not driven. When \B{EN} is 1, the value from \B{A} input is sent to the \B{Y} output. Therefore,
|
||||
the {\tt \$tribuf} cell implements the function \lstinline[language=Verilog]; Y = EN ? A : 'bz;.
|
||||
|
||||
Behavioural code with cascaded {\tt if-then-else}- and {\tt case}-statements
|
||||
usually results in trees of multiplexer cells. Many passes (from various
|
||||
optimizations to FSM extraction) heavily depend on these multiplexer trees to
|
||||
|
@ -398,9 +404,15 @@ Verilog & Cell Type \\
|
|||
\hline
|
||||
\lstinline[language=Verilog]; Y = ~A; & {\tt \$\_NOT\_} \\
|
||||
\lstinline[language=Verilog]; Y = A & B; & {\tt \$\_AND\_} \\
|
||||
\lstinline[language=Verilog]; Y = ~(A & B); & {\tt \$\_NAND\_} \\
|
||||
\lstinline[language=Verilog]; Y = A & ~B; & {\tt \$\_ANDNOT\_} \\
|
||||
\lstinline[language=Verilog]; Y = A | B; & {\tt \$\_OR\_} \\
|
||||
\lstinline[language=Verilog]; Y = ~(A | B); & {\tt \$\_NOR\_} \\
|
||||
\lstinline[language=Verilog]; Y = A | ~B; & {\tt \$\_ORNOT\_} \\
|
||||
\lstinline[language=Verilog]; Y = A ^ B; & {\tt \$\_XOR\_} \\
|
||||
\lstinline[language=Verilog]; Y = ~(A ^ B); & {\tt \$\_XNOR\_} \\
|
||||
\lstinline[language=Verilog]; Y = S ? B : A; & {\tt \$\_MUX\_} \\
|
||||
\lstinline[language=Verilog]; Y = EN ? A : 'bz; & {\tt \$\_TBUF\_} \\
|
||||
\hline
|
||||
\lstinline[language=Verilog]; always @(negedge C) Q <= D; & {\tt \$\_DFF\_N\_} \\
|
||||
\lstinline[language=Verilog]; always @(posedge C) Q <= D; & {\tt \$\_DFF\_P\_} \\
|
||||
|
@ -423,9 +435,10 @@ $ClkEdge$ & $RstLvl$ & $RstVal$ & Cell Type \\
|
|||
\end{table}
|
||||
|
||||
Table~\ref{tab:CellLib_gates} lists all cell types used for gate level logic. The cell types
|
||||
{\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_OR\_}, {\tt \$\_XOR\_} and {\tt \$\_MUX\_}
|
||||
are used to model combinatorial logic. The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_}
|
||||
represent d-type flip-flops.
|
||||
{\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_NAND\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_OR\_}, {\tt \$\_NOR\_},
|
||||
{\tt \$\_ORNOT\_}, {\tt \$\_XOR\_}, {\tt \$\_XNOR\_} and {\tt \$\_MUX\_} are used to model combinatorial logic.
|
||||
The cell type {\tt \$\_TBUF\_} is used to model tristate logic.
|
||||
The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_} represent d-type flip-flops.
|
||||
|
||||
The cell types {\tt \$\_DFF\_NN0\_}, {\tt \$\_DFF\_NN1\_}, {\tt \$\_DFF\_NP0\_}, {\tt \$\_DFF\_NP1\_},
|
||||
{\tt \$\_DFF\_PN0\_}, {\tt \$\_DFF\_PN1\_}, {\tt \$\_DFF\_PP0\_} and {\tt \$\_DFF\_PP1\_} implement
|
||||
|
@ -452,6 +465,10 @@ Add information about {\tt \$assert}, {\tt \$assume}, {\tt \$live}, {\tt \$fair}
|
|||
{\tt \$initstate}, {\tt \$anyconst}, {\tt \$anyseq}, {\tt \$allconst}, {\tt \$allseq} cells.
|
||||
\end{fixme}
|
||||
|
||||
\begin{fixme}
|
||||
Add information about {\tt \$specify2}, {\tt \$specify3}, and {\tt \$specrule} cells.
|
||||
\end{fixme}
|
||||
|
||||
\begin{fixme}
|
||||
Add information about {\tt \$slice} and {\tt \$concat} cells.
|
||||
\end{fixme}
|
||||
|
@ -477,7 +494,6 @@ Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLA
|
|||
\end{fixme}
|
||||
|
||||
\begin{fixme}
|
||||
Add information about {\tt \$\_NAND\_}, {\tt \$\_NOR\_}, {\tt \$\_XNOR\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_ORNOT\_},
|
||||
{\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells.
|
||||
Add information about {\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells.
|
||||
\end{fixme}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import os
|
||||
import sys
|
||||
sys.setdlopenflags(os.RTLD_NOW | os.RTLD_GLOBAL)
|
||||
|
||||
__all__ = ["libyosys"]
|
|
@ -0,0 +1,358 @@
|
|||
/* This file comes from the PyPA Setuptools repository, commit 16e452a:
|
||||
https://github.com/pypa/setuptools
|
||||
Modifications include this comment and inline inclusion of the LICENSE text. */
|
||||
|
||||
/* Copyright (C) 2016 Jason R Coombs <jaraco@jaraco.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE. */
|
||||
|
||||
/* Setuptools Script Launcher for Windows
|
||||
|
||||
This is a stub executable for Windows that functions somewhat like
|
||||
Effbot's "exemaker", in that it runs a script with the same name but
|
||||
a .py extension, using information from a #! line. It differs in that
|
||||
it spawns the actual Python executable, rather than attempting to
|
||||
hook into the Python DLL. This means that the script will run with
|
||||
sys.executable set to the Python executable, where exemaker ends up with
|
||||
sys.executable pointing to itself. (Which means it won't work if you try
|
||||
to run another Python process using sys.executable.)
|
||||
|
||||
To build/rebuild with mingw32, do this in the setuptools project directory:
|
||||
|
||||
gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c
|
||||
gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c
|
||||
|
||||
To build for Windows RT, install both Visual Studio Express for Windows 8
|
||||
and for Windows Desktop (both freeware), create "win32" application using
|
||||
"Windows Desktop" version, create new "ARM" target via
|
||||
"Configuration Manager" menu and modify ".vcxproj" file by adding
|
||||
"<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>" tag
|
||||
as child of "PropertyGroup" tags that has "Debug|ARM" and "Release|ARM"
|
||||
properties.
|
||||
|
||||
It links to msvcrt.dll, but this shouldn't be a problem since it doesn't
|
||||
actually run Python in the same process. Note that using 'exec' instead
|
||||
of 'spawn' doesn't work, because on Windows this leads to the Python
|
||||
executable running in the *background*, attached to the same console
|
||||
window, meaning you get a command prompt back *before* Python even finishes
|
||||
starting. So, we have to use spawnv() and wait for Python to exit before
|
||||
continuing. :(
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int child_pid=0;
|
||||
|
||||
int fail(char *format, char *data) {
|
||||
/* Print error message to stderr and return 2 */
|
||||
fprintf(stderr, format, data);
|
||||
return 2;
|
||||
}
|
||||
|
||||
char *quoted(char *data) {
|
||||
int i, ln = strlen(data), nb;
|
||||
|
||||
/* We allocate twice as much space as needed to deal with worse-case
|
||||
of having to escape everything. */
|
||||
char *result = calloc(ln*2+3, sizeof(char));
|
||||
char *presult = result;
|
||||
|
||||
*presult++ = '"';
|
||||
for (nb=0, i=0; i < ln; i++)
|
||||
{
|
||||
if (data[i] == '\\')
|
||||
nb += 1;
|
||||
else if (data[i] == '"')
|
||||
{
|
||||
for (; nb > 0; nb--)
|
||||
*presult++ = '\\';
|
||||
*presult++ = '\\';
|
||||
}
|
||||
else
|
||||
nb = 0;
|
||||
*presult++ = data[i];
|
||||
}
|
||||
|
||||
for (; nb > 0; nb--) /* Deal w trailing slashes */
|
||||
*presult++ = '\\';
|
||||
|
||||
*presult++ = '"';
|
||||
*presult++ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
char *loadable_exe(char *exename) {
|
||||
/* HINSTANCE hPython; DLL handle for python executable */
|
||||
char *result;
|
||||
|
||||
/* hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
if (!hPython) return NULL; */
|
||||
|
||||
/* Return the absolute filename for spawnv */
|
||||
result = calloc(MAX_PATH, sizeof(char));
|
||||
strncpy(result, exename, MAX_PATH);
|
||||
/*if (result) GetModuleFileNameA(hPython, result, MAX_PATH);
|
||||
|
||||
FreeLibrary(hPython); */
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char *find_exe(char *exename, char *script) {
|
||||
char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
|
||||
char path[_MAX_PATH], c, *result;
|
||||
|
||||
/* convert slashes to backslashes for uniform search below */
|
||||
result = exename;
|
||||
while (c = *result++) if (c=='/') result[-1] = '\\';
|
||||
|
||||
_splitpath(exename, drive, dir, fname, ext);
|
||||
if (drive[0] || dir[0]=='\\') {
|
||||
return loadable_exe(exename); /* absolute path, use directly */
|
||||
}
|
||||
/* Use the script's parent directory, which should be the Python home
|
||||
(This should only be used for bdist_wininst-installed scripts, because
|
||||
easy_install-ed scripts use the absolute path to python[w].exe
|
||||
*/
|
||||
_splitpath(script, drive, dir, fname, ext);
|
||||
result = dir + strlen(dir) -1;
|
||||
if (*result == '\\') result--;
|
||||
while (*result != '\\' && result>=dir) *result-- = 0;
|
||||
_makepath(path, drive, dir, exename, NULL);
|
||||
return loadable_exe(path);
|
||||
}
|
||||
|
||||
|
||||
char **parse_argv(char *cmdline, int *argc)
|
||||
{
|
||||
/* Parse a command line in-place using MS C rules */
|
||||
|
||||
char **result = calloc(strlen(cmdline), sizeof(char *));
|
||||
char *output = cmdline;
|
||||
char c;
|
||||
int nb = 0;
|
||||
int iq = 0;
|
||||
*argc = 0;
|
||||
|
||||
result[0] = output;
|
||||
while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
|
||||
|
||||
do {
|
||||
c = *cmdline++;
|
||||
if (!c || (isspace(c) && !iq)) {
|
||||
while (nb) {*output++ = '\\'; nb--; }
|
||||
*output++ = 0;
|
||||
result[++*argc] = output;
|
||||
if (!c) return result;
|
||||
while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
|
||||
if (!*cmdline) return result; /* avoid empty arg if trailing ws */
|
||||
continue;
|
||||
}
|
||||
if (c == '\\')
|
||||
++nb; /* count \'s */
|
||||
else {
|
||||
if (c == '"') {
|
||||
if (!(nb & 1)) { iq = !iq; c = 0; } /* skip " unless odd # of \ */
|
||||
nb = nb >> 1; /* cut \'s in half */
|
||||
}
|
||||
while (nb) {*output++ = '\\'; nb--; }
|
||||
if (c) *output++ = c;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
void pass_control_to_child(DWORD control_type) {
|
||||
/*
|
||||
* distribute-issue207
|
||||
* passes the control event to child process (Python)
|
||||
*/
|
||||
if (!child_pid) {
|
||||
return;
|
||||
}
|
||||
GenerateConsoleCtrlEvent(child_pid,0);
|
||||
}
|
||||
|
||||
BOOL control_handler(DWORD control_type) {
|
||||
/*
|
||||
* distribute-issue207
|
||||
* control event handler callback function
|
||||
*/
|
||||
switch (control_type) {
|
||||
case CTRL_C_EVENT:
|
||||
pass_control_to_child(0);
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int create_and_wait_for_subprocess(char* command) {
|
||||
/*
|
||||
* distribute-issue207
|
||||
* launches child process (Python)
|
||||
*/
|
||||
DWORD return_value = 0;
|
||||
LPSTR commandline = command;
|
||||
STARTUPINFOA s_info;
|
||||
PROCESS_INFORMATION p_info;
|
||||
ZeroMemory(&p_info, sizeof(p_info));
|
||||
ZeroMemory(&s_info, sizeof(s_info));
|
||||
s_info.cb = sizeof(STARTUPINFO);
|
||||
// set-up control handler callback funciotn
|
||||
SetConsoleCtrlHandler((PHANDLER_ROUTINE) control_handler, TRUE);
|
||||
if (!CreateProcessA(NULL, commandline, NULL, NULL, TRUE, 0, NULL, NULL, &s_info, &p_info)) {
|
||||
fprintf(stderr, "failed to create process.\n");
|
||||
return 0;
|
||||
}
|
||||
child_pid = p_info.dwProcessId;
|
||||
// wait for Python to exit
|
||||
WaitForSingleObject(p_info.hProcess, INFINITE);
|
||||
if (!GetExitCodeProcess(p_info.hProcess, &return_value)) {
|
||||
fprintf(stderr, "failed to get exit code from process.\n");
|
||||
return 0;
|
||||
}
|
||||
return return_value;
|
||||
}
|
||||
|
||||
char* join_executable_and_args(char *executable, char **args, int argc)
|
||||
{
|
||||
/*
|
||||
* distribute-issue207
|
||||
* CreateProcess needs a long string of the executable and command-line arguments,
|
||||
* so we need to convert it from the args that was built
|
||||
*/
|
||||
int len,counter;
|
||||
char* cmdline;
|
||||
|
||||
len=strlen(executable)+2;
|
||||
for (counter=1; counter<argc; counter++) {
|
||||
len+=strlen(args[counter])+1;
|
||||
}
|
||||
|
||||
cmdline = (char*)calloc(len, sizeof(char));
|
||||
sprintf(cmdline, "%s", executable);
|
||||
len=strlen(executable);
|
||||
for (counter=1; counter<argc; counter++) {
|
||||
sprintf(cmdline+len, " %s", args[counter]);
|
||||
len+=strlen(args[counter])+1;
|
||||
}
|
||||
return cmdline;
|
||||
}
|
||||
|
||||
int run(int argc, char **argv, int is_gui) {
|
||||
|
||||
char python[256]; /* python executable's filename*/
|
||||
char *pyopt; /* Python option */
|
||||
char script[256]; /* the script's filename */
|
||||
|
||||
int scriptf; /* file descriptor for script file */
|
||||
|
||||
char **newargs, **newargsp, **parsedargs; /* argument array for exec */
|
||||
char *ptr, *end; /* working pointers for string manipulation */
|
||||
char *cmdline;
|
||||
int i, parsedargc; /* loop counter */
|
||||
|
||||
/* compute script name from our .exe name*/
|
||||
GetModuleFileNameA(NULL, script, sizeof(script));
|
||||
end = script + strlen(script);
|
||||
while( end>script && *end != '.')
|
||||
*end-- = '\0';
|
||||
*end-- = '\0';
|
||||
strcat(script, (GUI ? "-script.pyw" : "-script.py"));
|
||||
|
||||
/* figure out the target python executable */
|
||||
|
||||
scriptf = open(script, O_RDONLY);
|
||||
if (scriptf == -1) {
|
||||
return fail("Cannot open %s\n", script);
|
||||
}
|
||||
end = python + read(scriptf, python, sizeof(python));
|
||||
close(scriptf);
|
||||
|
||||
ptr = python-1;
|
||||
while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {;}
|
||||
|
||||
*ptr-- = '\0';
|
||||
|
||||
if (strncmp(python, "#!", 2)) {
|
||||
/* default to python.exe if no #! header */
|
||||
strcpy(python, "#!python.exe");
|
||||
}
|
||||
|
||||
parsedargs = parse_argv(python+2, &parsedargc);
|
||||
|
||||
/* Using spawnv() can fail strangely if you e.g. find the Cygwin
|
||||
Python, so we'll make sure Windows can find and load it */
|
||||
|
||||
ptr = find_exe(parsedargs[0], script);
|
||||
if (!ptr) {
|
||||
return fail("Cannot find Python executable %s\n", parsedargs[0]);
|
||||
}
|
||||
|
||||
/* printf("Python executable: %s\n", ptr); */
|
||||
|
||||
/* Argument array needs to be
|
||||
parsedargc + argc, plus 1 for null sentinel */
|
||||
|
||||
newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *));
|
||||
newargsp = newargs;
|
||||
|
||||
*newargsp++ = quoted(ptr);
|
||||
for (i = 1; i<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]);
|
||||
|
||||
*newargsp++ = quoted(script);
|
||||
for (i = 1; i < argc; i++) *newargsp++ = quoted(argv[i]);
|
||||
|
||||
*newargsp++ = NULL;
|
||||
|
||||
/* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */
|
||||
|
||||
if (is_gui) {
|
||||
/* Use exec, we don't need to wait for the GUI to finish */
|
||||
execv(ptr, (const char * const *)(newargs));
|
||||
return fail("Could not exec %s", ptr); /* shouldn't get here! */
|
||||
}
|
||||
|
||||
/*
|
||||
* distribute-issue207: using CreateProcessA instead of spawnv
|
||||
*/
|
||||
cmdline = join_executable_and_args(ptr, newargs, parsedargc + argc);
|
||||
return create_and_wait_for_subprocess(cmdline);
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) {
|
||||
return run(__argc, __argv, GUI);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return run(argc, argv, GUI);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,12 @@
|
|||
//
|
||||
// yosys -- Yosys Open SYnthesis Suite
|
||||
//
|
||||
//
|
||||
// Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||
//
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
|
@ -73,7 +73,7 @@ message Module {
|
|||
BitVector bits = 2;
|
||||
}
|
||||
map<string, Port> port = 2;
|
||||
|
||||
|
||||
// Named cells in this module.
|
||||
message Cell {
|
||||
// Set to true when the name of this cell is automatically created and
|
||||
|
@ -129,7 +129,7 @@ message Model {
|
|||
TYPE_FALSE = 6;
|
||||
};
|
||||
Type type = 1;
|
||||
|
||||
|
||||
message Port {
|
||||
// Name of port.
|
||||
string portname = 1;
|
||||
|
@ -148,7 +148,7 @@ message Model {
|
|||
// Set for AND, NAND.
|
||||
Gate gate = 3;
|
||||
}
|
||||
|
||||
|
||||
// Set when the node drives given output port(s).
|
||||
message OutPort {
|
||||
// Name of port.
|
||||
|
|
|
@ -71,7 +71,7 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n
|
|||
RTLIL::Module *mod = design->modules_.at(it.second->type);
|
||||
if (!design->selected_whole_module(mod->name))
|
||||
continue;
|
||||
if (mod->get_bool_attribute("\\blackbox"))
|
||||
if (mod->get_blackbox_attribute())
|
||||
continue;
|
||||
if (it.second->hasPort(name))
|
||||
continue;
|
||||
|
|
|
@ -128,7 +128,7 @@ struct BugpointPass : public Pass {
|
|||
{
|
||||
for (auto &it : design_copy->modules_)
|
||||
{
|
||||
if (it.second->get_bool_attribute("\\blackbox"))
|
||||
if (it.second->get_blackbox_attribute())
|
||||
continue;
|
||||
|
||||
if (index++ == seed)
|
||||
|
@ -143,7 +143,7 @@ struct BugpointPass : public Pass {
|
|||
{
|
||||
for (auto mod : design_copy->modules())
|
||||
{
|
||||
if (mod->get_bool_attribute("\\blackbox"))
|
||||
if (mod->get_blackbox_attribute())
|
||||
continue;
|
||||
|
||||
for (auto wire : mod->wires())
|
||||
|
@ -168,7 +168,7 @@ struct BugpointPass : public Pass {
|
|||
{
|
||||
for (auto mod : design_copy->modules())
|
||||
{
|
||||
if (mod->get_bool_attribute("\\blackbox"))
|
||||
if (mod->get_blackbox_attribute())
|
||||
continue;
|
||||
|
||||
for (auto &it : mod->cells_)
|
||||
|
@ -186,7 +186,7 @@ struct BugpointPass : public Pass {
|
|||
{
|
||||
for (auto mod : design_copy->modules())
|
||||
{
|
||||
if (mod->get_bool_attribute("\\blackbox"))
|
||||
if (mod->get_blackbox_attribute())
|
||||
continue;
|
||||
|
||||
for (auto cell : mod->cells())
|
||||
|
@ -281,6 +281,9 @@ struct BugpointPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (script.empty())
|
||||
log_cmd_error("Missing -script option.\n");
|
||||
|
||||
if (!has_part)
|
||||
{
|
||||
modules = true;
|
||||
|
@ -298,7 +301,7 @@ struct BugpointPass : public Pass {
|
|||
if (!check_logfile(grep))
|
||||
log_cmd_error("The provided grep string is not found in the log file!\n");
|
||||
|
||||
int seed = 0, crashing_seed = seed;
|
||||
int seed = 0;
|
||||
bool found_something = false, stage2 = false;
|
||||
while (true)
|
||||
{
|
||||
|
@ -324,7 +327,6 @@ struct BugpointPass : public Pass {
|
|||
if (crashing_design != design)
|
||||
delete crashing_design;
|
||||
crashing_design = simplified;
|
||||
crashing_seed = seed;
|
||||
found_something = true;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -98,21 +98,23 @@ struct CoverPass : public Pass {
|
|||
}
|
||||
if ((args[argidx] == "-o" || args[argidx] == "-a" || args[argidx] == "-d") && argidx+1 < args.size()) {
|
||||
const char *open_mode = args[argidx] == "-a" ? "a+" : "w";
|
||||
std::string filename = args[++argidx];
|
||||
const std::string &filename = args[++argidx];
|
||||
FILE *f = nullptr;
|
||||
if (args[argidx-1] == "-d") {
|
||||
#ifdef _WIN32
|
||||
log_cmd_error("The 'cover -d' option is not supported on win32.\n");
|
||||
#else
|
||||
char filename_buffer[4096];
|
||||
snprintf(filename_buffer, 4096, "%s/yosys_cover_%d_XXXXXX.txt", filename.c_str(), getpid());
|
||||
filename = mkstemps(filename_buffer, 4);
|
||||
f = fdopen(mkstemps(filename_buffer, 4), "w");
|
||||
#endif
|
||||
} else {
|
||||
f = fopen(filename.c_str(), open_mode);
|
||||
}
|
||||
FILE *f = fopen(filename.c_str(), open_mode);
|
||||
if (f == NULL) {
|
||||
for (auto f : out_files)
|
||||
fclose(f);
|
||||
log_cmd_error("Can't create file %s.\n", args[argidx].c_str());
|
||||
log_cmd_error("Can't create file %s%s.\n", args[argidx-1] == "-d" ? "in directory " : "", args[argidx].c_str());
|
||||
}
|
||||
out_files.push_back(f);
|
||||
continue;
|
||||
|
|
|
@ -23,9 +23,18 @@
|
|||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
# include <boost/algorithm/string/predicate.hpp>
|
||||
# include <Python.h>
|
||||
# include <boost/filesystem.hpp>
|
||||
#endif
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
std::map<std::string, void*> loaded_plugins;
|
||||
#ifdef WITH_PYTHON
|
||||
std::map<std::string, void*> loaded_python_plugins;
|
||||
#endif
|
||||
std::map<std::string, std::string> loaded_plugin_aliases;
|
||||
|
||||
#ifdef YOSYS_ENABLE_PLUGINS
|
||||
|
@ -36,7 +45,35 @@ void load_plugin(std::string filename, std::vector<std::string> aliases)
|
|||
if (filename.find('/') == std::string::npos)
|
||||
filename = "./" + filename;
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
if (!loaded_plugins.count(filename) && !loaded_python_plugins.count(filename)) {
|
||||
#else
|
||||
if (!loaded_plugins.count(filename)) {
|
||||
#endif
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
|
||||
boost::filesystem::path full_path(filename);
|
||||
|
||||
if(strcmp(full_path.extension().c_str(), ".py") == 0)
|
||||
{
|
||||
std::string path(full_path.parent_path().c_str());
|
||||
filename = full_path.filename().c_str();
|
||||
filename = filename.substr(0,filename.size()-3);
|
||||
PyRun_SimpleString(("sys.path.insert(0,\""+path+"\")").c_str());
|
||||
PyErr_Print();
|
||||
PyObject *module_p = PyImport_ImportModule(filename.c_str());
|
||||
if(module_p == NULL)
|
||||
{
|
||||
PyErr_Print();
|
||||
log_cmd_error("Can't load python module `%s'\n", full_path.filename().c_str());
|
||||
return;
|
||||
}
|
||||
loaded_python_plugins[orig_filename] = module_p;
|
||||
Pass::init_register();
|
||||
} else {
|
||||
#endif
|
||||
|
||||
void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL);
|
||||
if (hdl == NULL && orig_filename.find('/') == std::string::npos)
|
||||
hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL);
|
||||
|
@ -44,6 +81,10 @@ void load_plugin(std::string filename, std::vector<std::string> aliases)
|
|||
log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror());
|
||||
loaded_plugins[orig_filename] = hdl;
|
||||
Pass::init_register();
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (auto &alias : aliases)
|
||||
|
@ -107,7 +148,11 @@ struct PluginPass : public Pass {
|
|||
if (list_mode)
|
||||
{
|
||||
log("\n");
|
||||
#ifdef WITH_PYTHON
|
||||
if (loaded_plugins.empty() and loaded_python_plugins.empty())
|
||||
#else
|
||||
if (loaded_plugins.empty())
|
||||
#endif
|
||||
log("No plugins loaded.\n");
|
||||
else
|
||||
log("Loaded plugins:\n");
|
||||
|
@ -115,6 +160,11 @@ struct PluginPass : public Pass {
|
|||
for (auto &it : loaded_plugins)
|
||||
log(" %s\n", it.first.c_str());
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
for (auto &it : loaded_python_plugins)
|
||||
log(" %s\n", it.first.c_str());
|
||||
#endif
|
||||
|
||||
if (!loaded_plugin_aliases.empty()) {
|
||||
log("\n");
|
||||
int max_alias_len = 1;
|
||||
|
|
|
@ -291,7 +291,7 @@ struct QwpWorker
|
|||
// gaussian elimination
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
if (config.verbose && ((i+1) % (N/15)) == 0)
|
||||
if (config.verbose && N > 15 && ((i+1) % (N/15)) == 0)
|
||||
log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N);
|
||||
|
||||
// find best row
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name)
|
||||
static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name, bool flag_output)
|
||||
{
|
||||
from_name = RTLIL::escape_id(from_name);
|
||||
to_name = RTLIL::escape_id(to_name);
|
||||
|
@ -37,13 +37,18 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
|
|||
Wire *w = it.second;
|
||||
log("Renaming wire %s to %s in module %s.\n", log_id(w), log_id(to_name), log_id(module));
|
||||
module->rename(w, to_name);
|
||||
if (w->port_id)
|
||||
if (w->port_id || flag_output) {
|
||||
if (flag_output)
|
||||
w->port_output = true;
|
||||
module->fixup_ports();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &it : module->cells_)
|
||||
if (it.first == from_name) {
|
||||
if (flag_output)
|
||||
log_cmd_error("Called with -output but the specified object is a cell.\n");
|
||||
log("Renaming cell %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module));
|
||||
module->rename(it.second, to_name);
|
||||
return;
|
||||
|
@ -108,15 +113,26 @@ struct RenamePass : public Pass {
|
|||
log("Rename the specified object. Note that selection patterns are not supported\n");
|
||||
log("by this command.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -output old_name new_name\n");
|
||||
log("\n");
|
||||
log("Like above, but also make the wire an output. This will fail if the object is\n");
|
||||
log("not a wire.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -src [selection]\n");
|
||||
log("\n");
|
||||
log("Assign names auto-generated from the src attribute to all selected wires and\n");
|
||||
log("cells with private names.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -wire [selection]\n");
|
||||
log("\n");
|
||||
log("Assign auto-generated names based on the wires they drive to all selected\n");
|
||||
log("cells with private names. Ignores cells driving privatly named wires.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -enumerate [-pattern <pattern>] [selection]\n");
|
||||
log("\n");
|
||||
log("Assign short auto-generated names to all selected wires and cells with private\n");
|
||||
|
@ -124,11 +140,13 @@ struct RenamePass : public Pass {
|
|||
log("The character %% in the pattern is replaced with a integer number. The default\n");
|
||||
log("pattern is '_%%_'.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -hide [selection]\n");
|
||||
log("\n");
|
||||
log("Assign private names (the ones with $-prefix) to all selected wires and cells\n");
|
||||
log("with public names. This ignores all selected ports.\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log(" rename -top new_name\n");
|
||||
log("\n");
|
||||
log("Rename top module.\n");
|
||||
|
@ -142,6 +160,7 @@ struct RenamePass : public Pass {
|
|||
bool flag_enumerate = false;
|
||||
bool flag_hide = false;
|
||||
bool flag_top = false;
|
||||
bool flag_output = false;
|
||||
bool got_mode = false;
|
||||
|
||||
size_t argidx;
|
||||
|
@ -153,6 +172,11 @@ struct RenamePass : public Pass {
|
|||
got_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-output" && !got_mode) {
|
||||
flag_output = true;
|
||||
got_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-wire" && !got_mode) {
|
||||
flag_wire = true;
|
||||
got_mode = true;
|
||||
|
@ -322,10 +346,12 @@ struct RenamePass : public Pass {
|
|||
if (!design->selected_active_module.empty())
|
||||
{
|
||||
if (design->modules_.count(design->selected_active_module) > 0)
|
||||
rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name);
|
||||
rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name, flag_output);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flag_output)
|
||||
log_cmd_error("Mode -output requires that there is an active module selected.\n");
|
||||
for (auto &mod : design->modules_) {
|
||||
if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) {
|
||||
to_name = RTLIL::escape_id(to_name);
|
||||
|
|
|
@ -128,6 +128,45 @@ struct SetattrPass : public Pass {
|
|||
}
|
||||
} SetattrPass;
|
||||
|
||||
struct WbflipPass : public Pass {
|
||||
WbflipPass() : Pass("wbflip", "flip the whitebox attribute") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" wbflip [selection]\n");
|
||||
log("\n");
|
||||
log("Flip the whitebox attribute on selected cells. I.e. if it's set, unset it, and\n");
|
||||
log("vice-versa. Blackbox cells are not effected by this command.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
std::string arg = args[argidx];
|
||||
// if (arg == "-mod") {
|
||||
// flag_mod = true;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (Module *module : design->modules())
|
||||
{
|
||||
if (!design->selected(module))
|
||||
continue;
|
||||
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
continue;
|
||||
|
||||
module->set_bool_attribute("\\whitebox", !module->get_bool_attribute("\\whitebox"));
|
||||
}
|
||||
}
|
||||
} WbflipPass;
|
||||
|
||||
struct SetparamPass : public Pass {
|
||||
SetparamPass() : Pass("setparam", "set/unset parameters on objects") { }
|
||||
void help() YS_OVERRIDE
|
||||
|
|
|
@ -143,6 +143,9 @@ struct SetundefPass : public Pass {
|
|||
log(" -init\n");
|
||||
log(" also create/update init values for flip-flops\n");
|
||||
log("\n");
|
||||
log(" -params\n");
|
||||
log(" replace undef in cell parameters\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
|
@ -150,6 +153,7 @@ struct SetundefPass : public Pass {
|
|||
bool undriven_mode = false;
|
||||
bool expose_mode = false;
|
||||
bool init_mode = false;
|
||||
bool params_mode = false;
|
||||
SetundefWorker worker;
|
||||
|
||||
log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n");
|
||||
|
@ -199,6 +203,10 @@ struct SetundefPass : public Pass {
|
|||
init_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-params") {
|
||||
params_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {
|
||||
got_value = true;
|
||||
worker.next_bit_mode = MODE_RANDOM;
|
||||
|
@ -228,6 +236,18 @@ struct SetundefPass : public Pass {
|
|||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
if (params_mode)
|
||||
{
|
||||
for (auto *cell : module->selected_cells()) {
|
||||
for (auto ¶meter : cell->parameters) {
|
||||
for (auto &bit : parameter.second.bits) {
|
||||
if (bit > RTLIL::State::S1)
|
||||
bit = worker.next_bit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (undriven_mode)
|
||||
{
|
||||
if (!module->processes.empty())
|
||||
|
|
|
@ -237,15 +237,34 @@ struct ShowWorker
|
|||
int idx = single_idx_count++;
|
||||
for (int rep, i = int(sig.chunks().size())-1; i >= 0; i -= rep) {
|
||||
const RTLIL::SigChunk &c = sig.chunks().at(i);
|
||||
net = gen_signode_simple(c, false);
|
||||
log_assert(!net.empty());
|
||||
if (!driver && c.wire == nullptr) {
|
||||
RTLIL::State s1 = c.data.front();
|
||||
for (auto s2 : c.data)
|
||||
if (s1 != s2)
|
||||
goto not_const_stream;
|
||||
net.clear();
|
||||
} else {
|
||||
not_const_stream:
|
||||
net = gen_signode_simple(c, false);
|
||||
log_assert(!net.empty());
|
||||
}
|
||||
for (rep = 1; i-rep >= 0 && c == sig.chunks().at(i-rep); rep++) {}
|
||||
std::string repinfo = rep > 1 ? stringf("%dx ", rep) : "";
|
||||
if (driver) {
|
||||
log_assert(!net.empty());
|
||||
label_string += stringf("<s%d> %d:%d - %s%d:%d |", i, pos, pos-c.width+1, repinfo.c_str(), c.offset+c.width-1, c.offset);
|
||||
net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i));
|
||||
net_conn_map[net].bits = rep*c.width;
|
||||
net_conn_map[net].color = nextColor(c, net_conn_map[net].color);
|
||||
} else
|
||||
if (net.empty()) {
|
||||
log_assert(rep == 1);
|
||||
label_string += stringf("%c -> %d:%d |",
|
||||
c.data.front() == State::S0 ? '0' :
|
||||
c.data.front() == State::S1 ? '1' :
|
||||
c.data.front() == State::Sx ? 'X' :
|
||||
c.data.front() == State::Sz ? 'Z' : '?',
|
||||
pos, pos-rep*c.width+1);
|
||||
} else {
|
||||
label_string += stringf("<s%d> %s%d:%d - %d:%d |", i, repinfo.c_str(), c.offset+c.width-1, c.offset, pos, pos-rep*c.width+1);
|
||||
net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i));
|
||||
|
@ -555,7 +574,7 @@ struct ShowWorker
|
|||
if (!design->selected_module(module->name))
|
||||
continue;
|
||||
if (design->selected_whole_module(module->name)) {
|
||||
if (module->get_bool_attribute("\\blackbox")) {
|
||||
if (module->get_blackbox_attribute()) {
|
||||
// log("Skipping blackbox module %s.\n", id2cstr(module->name));
|
||||
continue;
|
||||
} else
|
||||
|
@ -771,7 +790,7 @@ struct ShowPass : public Pass {
|
|||
if (format != "ps" && format != "dot") {
|
||||
int modcount = 0;
|
||||
for (auto &mod_it : design->modules_) {
|
||||
if (mod_it.second->get_bool_attribute("\\blackbox"))
|
||||
if (mod_it.second->get_blackbox_attribute())
|
||||
continue;
|
||||
if (mod_it.second->cells_.empty() && mod_it.second->connections().empty())
|
||||
continue;
|
||||
|
|
|
@ -37,7 +37,9 @@ struct statdata_t
|
|||
STAT_INT_MEMBERS
|
||||
#undef X
|
||||
double area;
|
||||
string tech;
|
||||
|
||||
std::map<RTLIL::IdString, int> techinfo;
|
||||
std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type;
|
||||
std::set<RTLIL::IdString> unknown_cell_area;
|
||||
|
||||
|
@ -70,8 +72,10 @@ struct statdata_t
|
|||
#undef X
|
||||
}
|
||||
|
||||
statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area)
|
||||
statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area, string techname)
|
||||
{
|
||||
tech = techname;
|
||||
|
||||
#define X(_name) _name = 0;
|
||||
STAT_NUMERIC_MEMBERS
|
||||
#undef X
|
||||
|
@ -153,7 +157,8 @@ struct statdata_t
|
|||
log(" Number of processes: %6d\n", num_processes);
|
||||
log(" Number of cells: %6d\n", num_cells);
|
||||
for (auto &it : num_cells_by_type)
|
||||
log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second);
|
||||
if (it.second)
|
||||
log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second);
|
||||
|
||||
if (!unknown_cell_area.empty()) {
|
||||
log("\n");
|
||||
|
@ -165,6 +170,59 @@ struct statdata_t
|
|||
log("\n");
|
||||
log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area);
|
||||
}
|
||||
|
||||
if (tech == "xilinx")
|
||||
{
|
||||
int lut6_cnt = num_cells_by_type["\\LUT6"];
|
||||
int lut5_cnt = num_cells_by_type["\\LUT5"];
|
||||
int lut4_cnt = num_cells_by_type["\\LUT4"];
|
||||
int lut3_cnt = num_cells_by_type["\\LUT3"];
|
||||
int lut2_cnt = num_cells_by_type["\\LUT2"];
|
||||
int lut1_cnt = num_cells_by_type["\\LUT1"];
|
||||
int lc_cnt = 0;
|
||||
|
||||
lc_cnt += lut6_cnt;
|
||||
|
||||
lc_cnt += lut5_cnt;
|
||||
if (lut1_cnt) {
|
||||
int cnt = std::min(lut5_cnt, lut1_cnt);
|
||||
lut5_cnt -= cnt;
|
||||
lut1_cnt -= cnt;
|
||||
}
|
||||
|
||||
lc_cnt += lut4_cnt;
|
||||
if (lut1_cnt) {
|
||||
int cnt = std::min(lut4_cnt, lut1_cnt);
|
||||
lut4_cnt -= cnt;
|
||||
lut1_cnt -= cnt;
|
||||
}
|
||||
if (lut2_cnt) {
|
||||
int cnt = std::min(lut4_cnt, lut2_cnt);
|
||||
lut4_cnt -= cnt;
|
||||
lut2_cnt -= cnt;
|
||||
}
|
||||
|
||||
lc_cnt += lut3_cnt;
|
||||
if (lut1_cnt) {
|
||||
int cnt = std::min(lut3_cnt, lut1_cnt);
|
||||
lut3_cnt -= cnt;
|
||||
lut1_cnt -= cnt;
|
||||
}
|
||||
if (lut2_cnt) {
|
||||
int cnt = std::min(lut3_cnt, lut2_cnt);
|
||||
lut3_cnt -= cnt;
|
||||
lut2_cnt -= cnt;
|
||||
}
|
||||
if (lut3_cnt) {
|
||||
int cnt = (lut3_cnt + 1) / 2;
|
||||
lut3_cnt -= cnt;
|
||||
}
|
||||
|
||||
lc_cnt += (lut2_cnt + lut1_cnt + 1) / 2;
|
||||
|
||||
log("\n");
|
||||
log(" Estimated number of LCs: %10d\n", lc_cnt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -226,6 +284,10 @@ struct StatPass : public Pass {
|
|||
log(" -liberty <liberty_file>\n");
|
||||
log(" use cell area information from the provided liberty file\n");
|
||||
log("\n");
|
||||
log(" -tech <technology>\n");
|
||||
log(" print area estemate for the specified technology. Corrently supported\n");
|
||||
log(" calues for <technology>: xilinx\n");
|
||||
log("\n");
|
||||
log(" -width\n");
|
||||
log(" annotate internal cell types with their word width.\n");
|
||||
log(" e.g. $add_8 for an 8 bit wide $add cell.\n");
|
||||
|
@ -239,6 +301,7 @@ struct StatPass : public Pass {
|
|||
RTLIL::Module *top_mod = NULL;
|
||||
std::map<RTLIL::IdString, statdata_t> mod_stat;
|
||||
dict<IdString, double> cell_area;
|
||||
string techname;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
|
@ -253,6 +316,10 @@ struct StatPass : public Pass {
|
|||
read_liberty_cellarea(cell_area, liberty_file);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-tech" && argidx+1 < args.size()) {
|
||||
techname = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-top" && argidx+1 < args.size()) {
|
||||
if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0)
|
||||
log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str());
|
||||
|
@ -263,13 +330,16 @@ struct StatPass : public Pass {
|
|||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
if (techname != "" && techname != "xilinx")
|
||||
log_cmd_error("Unsupported technology: '%s'\n", techname.c_str());
|
||||
|
||||
for (auto mod : design->selected_modules())
|
||||
{
|
||||
if (!top_mod && design->full_selection())
|
||||
if (mod->get_bool_attribute("\\top"))
|
||||
top_mod = mod;
|
||||
|
||||
statdata_t data(design, mod, width_mode, cell_area);
|
||||
statdata_t data(design, mod, width_mode, cell_area, techname);
|
||||
mod_stat[mod->name] = data;
|
||||
|
||||
log("\n");
|
||||
|
|
|
@ -94,4 +94,38 @@ struct TracePass : public Pass {
|
|||
}
|
||||
} TracePass;
|
||||
|
||||
struct DebugPass : public Pass {
|
||||
DebugPass() : Pass("debug", "run command with debug log messages enabled") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" debug cmd\n");
|
||||
log("\n");
|
||||
log("Execute the specified command with debug log messages enabled\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// .. parse options ..
|
||||
break;
|
||||
}
|
||||
|
||||
log_force_debug++;
|
||||
|
||||
try {
|
||||
std::vector<std::string> new_args(args.begin() + argidx, args.end());
|
||||
Pass::call(design, new_args);
|
||||
} catch (...) {
|
||||
log_force_debug--;
|
||||
throw;
|
||||
}
|
||||
|
||||
log_force_debug--;
|
||||
}
|
||||
} DebugPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
|
@ -44,7 +44,10 @@ struct EquivOptPass:public ScriptPass
|
|||
log(" useful for handling architecture-specific primitives.\n");
|
||||
log("\n");
|
||||
log(" -assert\n");
|
||||
log(" produce an error if the circuits are not equivalent\n");
|
||||
log(" produce an error if the circuits are not equivalent.\n");
|
||||
log("\n");
|
||||
log(" -undef\n");
|
||||
log(" enable modelling of undef states during equiv_induct.\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this verification command:\n");
|
||||
help_script();
|
||||
|
@ -52,13 +55,14 @@ struct EquivOptPass:public ScriptPass
|
|||
}
|
||||
|
||||
std::string command, techmap_opts;
|
||||
bool assert;
|
||||
bool assert, undef;
|
||||
|
||||
void clear_flags() YS_OVERRIDE
|
||||
{
|
||||
command = "";
|
||||
techmap_opts = "";
|
||||
assert = false;
|
||||
undef = false;
|
||||
}
|
||||
|
||||
void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
|
||||
|
@ -84,6 +88,10 @@ struct EquivOptPass:public ScriptPass
|
|||
assert = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-undef") {
|
||||
undef = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -134,12 +142,17 @@ struct EquivOptPass:public ScriptPass
|
|||
opts = " -map <filename> ...";
|
||||
else
|
||||
opts = techmap_opts;
|
||||
run("techmap -D EQUIV -autoproc" + opts);
|
||||
run("techmap -wb -D EQUIV -autoproc" + opts);
|
||||
}
|
||||
|
||||
if (check_label("prove")) {
|
||||
run("equiv_make gold gate equiv");
|
||||
run("equiv_induct equiv");
|
||||
if (help_mode)
|
||||
run("equiv_induct [-undef] equiv");
|
||||
else if (undef)
|
||||
run("equiv_induct -undef equiv");
|
||||
else
|
||||
run("equiv_induct equiv");
|
||||
if (help_mode)
|
||||
run("equiv_status [-assert] equiv");
|
||||
else if (assert)
|
||||
|
|
|
@ -72,7 +72,8 @@ struct FsmOpt
|
|||
|
||||
new_transition_table.swap(fsm_data.transition_table);
|
||||
new_state_table.swap(fsm_data.state_table);
|
||||
fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state);
|
||||
if (fsm_data.reset_state != -1)
|
||||
fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -140,6 +140,23 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
|
|||
}
|
||||
}
|
||||
|
||||
// Return the "basic" type for an array item.
|
||||
std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
|
||||
std::string basicType = celltype;
|
||||
if (celltype.substr(0, 7) == "$array:") {
|
||||
int pos_idx = celltype.find_first_of(':');
|
||||
int pos_num = celltype.find_first_of(':', pos_idx + 1);
|
||||
int pos_type = celltype.find_first_of(':', pos_num + 1);
|
||||
basicType = celltype.substr(pos_type + 1);
|
||||
if (pos != nullptr) {
|
||||
pos[0] = pos_idx;
|
||||
pos[1] = pos_num;
|
||||
pos[2] = pos_type;
|
||||
}
|
||||
}
|
||||
return basicType;
|
||||
}
|
||||
|
||||
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)
|
||||
{
|
||||
bool did_something = false;
|
||||
|
@ -178,9 +195,11 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
|
|||
std::vector<RTLIL::SigSpec> connections_to_add_signal;
|
||||
|
||||
if (cell->type.substr(0, 7) == "$array:") {
|
||||
int pos_idx = cell->type.str().find_first_of(':');
|
||||
int pos_num = cell->type.str().find_first_of(':', pos_idx + 1);
|
||||
int pos_type = cell->type.str().find_first_of(':', pos_num + 1);
|
||||
int pos[3];
|
||||
basic_cell_type(cell->type.str(), pos);
|
||||
int pos_idx = pos[0];
|
||||
int pos_num = pos[1];
|
||||
int pos_type = pos[2];
|
||||
int idx = atoi(cell->type.str().substr(pos_idx + 1, pos_num).c_str());
|
||||
int num = atoi(cell->type.str().substr(pos_num + 1, pos_type).c_str());
|
||||
array_cells[cell] = std::pair<int, int>(idx, num);
|
||||
|
@ -327,9 +346,9 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
|
|||
}
|
||||
RTLIL::Module *mod = design->modules_[cell->type];
|
||||
|
||||
if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
|
||||
if (design->modules_.at(cell->type)->get_blackbox_attribute()) {
|
||||
if (flag_simcheck)
|
||||
log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox module.\n",
|
||||
log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n",
|
||||
cell->type.c_str(), module->name.c_str(), cell->name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
@ -432,17 +451,14 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::
|
|||
|
||||
if (indent == 0)
|
||||
log("Top module: %s\n", mod->name.c_str());
|
||||
else if (!mod->get_bool_attribute("\\blackbox"))
|
||||
else if (!mod->get_blackbox_attribute())
|
||||
log("Used module: %*s%s\n", indent, "", mod->name.c_str());
|
||||
used.insert(mod);
|
||||
|
||||
for (auto cell : mod->cells()) {
|
||||
std::string celltype = cell->type.str();
|
||||
if (celltype.substr(0, 7) == "$array:") {
|
||||
int pos_idx = celltype.find_first_of(':');
|
||||
int pos_num = celltype.find_first_of(':', pos_idx + 1);
|
||||
int pos_type = celltype.find_first_of(':', pos_num + 1);
|
||||
celltype = celltype.substr(pos_type + 1);
|
||||
celltype = basic_cell_type(celltype);
|
||||
}
|
||||
if (design->module(celltype))
|
||||
hierarchy_worker(design, used, design->module(celltype), indent+4);
|
||||
|
@ -475,7 +491,7 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
|
|||
|
||||
int del_counter = 0;
|
||||
for (auto mod : del_modules) {
|
||||
if (!purge_lib && mod->get_bool_attribute("\\blackbox"))
|
||||
if (!purge_lib && mod->get_blackbox_attribute())
|
||||
continue;
|
||||
log("Removing unused module `%s'.\n", mod->name.c_str());
|
||||
design->modules_.erase(mod->name);
|
||||
|
@ -502,9 +518,19 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)
|
|||
if (db.count(module) == 0) {
|
||||
int score = 0;
|
||||
db[module] = 0;
|
||||
for (auto cell : module->cells())
|
||||
if (design->module(cell->type))
|
||||
score = max(score, find_top_mod_score(design, design->module(cell->type), db) + 1);
|
||||
for (auto cell : module->cells()) {
|
||||
std::string celltype = cell->type.str();
|
||||
// Is this an array instance
|
||||
if (celltype.substr(0, 7) == "$array:") {
|
||||
celltype = basic_cell_type(celltype);
|
||||
}
|
||||
// Is this cell a module instance?
|
||||
auto instModule = design->module(celltype);
|
||||
// If there is no instance for this, issue a warning.
|
||||
if (instModule != nullptr) {
|
||||
score = max(score, find_top_mod_score(design, instModule, db) + 1);
|
||||
}
|
||||
}
|
||||
db[module] = score;
|
||||
}
|
||||
return db.at(module);
|
||||
|
@ -544,7 +570,7 @@ struct HierarchyPass : public Pass {
|
|||
log("\n");
|
||||
log(" -simcheck\n");
|
||||
log(" like -check, but also throw an error if blackbox modules are\n");
|
||||
log(" instantiated, and throw an error if the design has no top module\n");
|
||||
log(" instantiated, and throw an error if the design has no top module.\n");
|
||||
log("\n");
|
||||
log(" -purge_lib\n");
|
||||
log(" by default the hierarchy command will not remove library (blackbox)\n");
|
||||
|
@ -557,20 +583,20 @@ struct HierarchyPass : public Pass {
|
|||
log("\n");
|
||||
log(" -keep_positionals\n");
|
||||
log(" per default this pass also converts positional arguments in cells\n");
|
||||
log(" to arguments using port names. this option disables this behavior.\n");
|
||||
log(" to arguments using port names. This option disables this behavior.\n");
|
||||
log("\n");
|
||||
log(" -keep_portwidths\n");
|
||||
log(" per default this pass adjusts the port width on cells that are\n");
|
||||
log(" module instances when the width does not match the module port. this\n");
|
||||
log(" module instances when the width does not match the module port. This\n");
|
||||
log(" option disables this behavior.\n");
|
||||
log("\n");
|
||||
log(" -nokeep_asserts\n");
|
||||
log(" per default this pass sets the \"keep\" attribute on all modules\n");
|
||||
log(" that directly or indirectly contain one or more $assert cells. this\n");
|
||||
log(" option disables this behavior.\n");
|
||||
log(" that directly or indirectly contain one or more formal properties.\n");
|
||||
log(" This option disables this behavior.\n");
|
||||
log("\n");
|
||||
log(" -top <module>\n");
|
||||
log(" use the specified top module to built a design hierarchy. modules\n");
|
||||
log(" use the specified top module to build the design hierarchy. Modules\n");
|
||||
log(" outside this tree (unused modules) are removed.\n");
|
||||
log("\n");
|
||||
log(" when the -top option is used, the 'top' attribute will be set on the\n");
|
||||
|
@ -580,6 +606,12 @@ struct HierarchyPass : public Pass {
|
|||
log(" -auto-top\n");
|
||||
log(" automatically determine the top of the design hierarchy and mark it.\n");
|
||||
log("\n");
|
||||
log(" -chparam name value \n");
|
||||
log(" elaborate the top module using this parameter value. Modules on which\n");
|
||||
log(" this parameter does not exist may cause a warning message to be output.\n");
|
||||
log(" This option can be specified multiple times to override multiple\n");
|
||||
log(" parameters. String values must be passed in double quotes (\").\n");
|
||||
log("\n");
|
||||
log("In -generate mode this pass generates blackbox modules for the given cell\n");
|
||||
log("types (wildcards supported). For this the design is searched for cells that\n");
|
||||
log("match the given types and then the given port declarations are used to\n");
|
||||
|
@ -615,6 +647,7 @@ struct HierarchyPass : public Pass {
|
|||
bool nokeep_asserts = false;
|
||||
std::vector<std::string> generate_cells;
|
||||
std::vector<generate_port_decl_t> generate_ports;
|
||||
std::map<std::string, std::string> parameters;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
|
@ -689,28 +722,61 @@ struct HierarchyPass : public Pass {
|
|||
if (args[argidx] == "-top") {
|
||||
if (++argidx >= args.size())
|
||||
log_cmd_error("Option -top requires an additional argument!\n");
|
||||
top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL;
|
||||
if (top_mod == NULL && design->modules_.count("$abstract" + RTLIL::escape_id(args[argidx]))) {
|
||||
dict<RTLIL::IdString, RTLIL::Const> empty_parameters;
|
||||
design->modules_.at("$abstract" + RTLIL::escape_id(args[argidx]))->derive(design, empty_parameters);
|
||||
top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL;
|
||||
}
|
||||
if (top_mod == NULL)
|
||||
load_top_mod = args[argidx];
|
||||
load_top_mod = args[argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-auto-top") {
|
||||
auto_top_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-chparam" && argidx+2 < args.size()) {
|
||||
const std::string &key = args[++argidx];
|
||||
const std::string &value = args[++argidx];
|
||||
auto r = parameters.emplace(key, value);
|
||||
if (!r.second) {
|
||||
log_warning("-chparam %s already specified: overwriting.\n", key.c_str());
|
||||
r.first->second = value;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design, false);
|
||||
|
||||
if (!load_top_mod.empty()) {
|
||||
if (!load_top_mod.empty())
|
||||
{
|
||||
IdString top_name = RTLIL::escape_id(load_top_mod);
|
||||
IdString abstract_id = "$abstract" + RTLIL::escape_id(load_top_mod);
|
||||
top_mod = design->module(top_name);
|
||||
|
||||
dict<RTLIL::IdString, RTLIL::Const> top_parameters;
|
||||
for (auto ¶ : parameters) {
|
||||
SigSpec sig_value;
|
||||
if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second))
|
||||
log_cmd_error("Can't decode value '%s'!\n", para.second.c_str());
|
||||
top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const();
|
||||
}
|
||||
|
||||
if (top_mod == nullptr && design->module(abstract_id))
|
||||
top_mod = design->module(design->module(abstract_id)->derive(design, top_parameters));
|
||||
else if (top_mod != nullptr && !top_parameters.empty())
|
||||
top_mod = design->module(top_mod->derive(design, top_parameters));
|
||||
|
||||
if (top_mod != nullptr && top_mod->name != top_name) {
|
||||
Module *m = top_mod->clone();
|
||||
m->name = top_name;
|
||||
Module *old_mod = design->module(top_name);
|
||||
if (old_mod)
|
||||
design->remove(old_mod);
|
||||
design->add(m);
|
||||
top_mod = m;
|
||||
}
|
||||
}
|
||||
|
||||
if (top_mod == nullptr && !load_top_mod.empty()) {
|
||||
#ifdef YOSYS_ENABLE_VERIFIC
|
||||
if (verific_import_pending) {
|
||||
verific_import(design, load_top_mod);
|
||||
verific_import(design, parameters, load_top_mod);
|
||||
top_mod = design->module(RTLIL::escape_id(load_top_mod));
|
||||
}
|
||||
#endif
|
||||
|
@ -719,7 +785,7 @@ struct HierarchyPass : public Pass {
|
|||
} else {
|
||||
#ifdef YOSYS_ENABLE_VERIFIC
|
||||
if (verific_import_pending)
|
||||
verific_import(design);
|
||||
verific_import(design, parameters);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -820,7 +886,7 @@ struct HierarchyPass : public Pass {
|
|||
std::map<RTLIL::Module*, bool> cache;
|
||||
for (auto mod : design->modules())
|
||||
if (set_keep_assert(cache, mod)) {
|
||||
log("Module %s directly or indirectly contains $assert cells -> setting \"keep\" attribute.\n", log_id(mod));
|
||||
log("Module %s directly or indirectly contains formal properties -> setting \"keep\" attribute.\n", log_id(mod));
|
||||
mod->set_bool_attribute("\\keep");
|
||||
}
|
||||
}
|
||||
|
@ -884,7 +950,7 @@ struct HierarchyPass : public Pass {
|
|||
if (m == nullptr)
|
||||
continue;
|
||||
|
||||
if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty()) {
|
||||
if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
|
||||
IdString new_m_name = m->derive(design, cell->parameters, true);
|
||||
if (new_m_name.empty())
|
||||
continue;
|
||||
|
|
|
@ -75,7 +75,7 @@ struct UniquifyPass : public Pass {
|
|||
if (tmod == nullptr)
|
||||
continue;
|
||||
|
||||
if (tmod->get_bool_attribute("\\blackbox"))
|
||||
if (tmod->get_blackbox_attribute())
|
||||
continue;
|
||||
|
||||
if (tmod->get_bool_attribute("\\unique") && newname == tmod->name)
|
||||
|
@ -87,6 +87,8 @@ struct UniquifyPass : public Pass {
|
|||
smod->name = newname;
|
||||
cell->type = newname;
|
||||
smod->set_bool_attribute("\\unique");
|
||||
if (smod->attributes.count("\\hdlname") == 0)
|
||||
smod->attributes["\\hdlname"] = string(log_id(tmod->name));
|
||||
design->add(smod);
|
||||
|
||||
did_something = true;
|
||||
|
|
|
@ -542,7 +542,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
|
|||
}
|
||||
|
||||
// assign write ports
|
||||
|
||||
pair<SigBit, bool> wr_clkdom;
|
||||
for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++)
|
||||
{
|
||||
bool clken = wr_clken[cell_port_i] == State::S1;
|
||||
|
@ -552,7 +552,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
|
|||
pair<SigBit, bool> clkdom(clksig, clkpol);
|
||||
if (!clken)
|
||||
clkdom = pair<SigBit, bool>(State::S1, false);
|
||||
|
||||
wr_clkdom = clkdom;
|
||||
log(" Write port #%d is in clock domain %s%s.\n",
|
||||
cell_port_i, clkdom.second ? "" : "!",
|
||||
clken ? log_signal(clkdom.first) : "~async~");
|
||||
|
@ -641,6 +641,7 @@ grow_read_ports:;
|
|||
pi.sig_data = SigSpec();
|
||||
pi.sig_en = SigSpec();
|
||||
pi.make_outreg = false;
|
||||
pi.make_transp = false;
|
||||
}
|
||||
new_portinfos.push_back(pi);
|
||||
if (pi.dupidx == dup_count-1) {
|
||||
|
@ -718,7 +719,13 @@ grow_read_ports:;
|
|||
if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
|
||||
if (match.make_transp && wr_ports <= 1) {
|
||||
pi.make_transp = true;
|
||||
enable_make_transp = true;
|
||||
if (pi.clocks != 0) {
|
||||
if (wr_ports == 1 && wr_clkdom != clkdom) {
|
||||
log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
goto skip_bram_rport;
|
||||
}
|
||||
enable_make_transp = true;
|
||||
}
|
||||
} else {
|
||||
log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
goto skip_bram_rport;
|
||||
|
@ -737,7 +744,8 @@ grow_read_ports:;
|
|||
if (clken) {
|
||||
clock_domains[pi.clocks] = clkdom;
|
||||
clock_polarities[pi.clkpol] = clkdom.second;
|
||||
read_transp[pi.transp] = transp;
|
||||
if (!pi.make_transp)
|
||||
read_transp[pi.transp] = transp;
|
||||
pi.sig_clock = clkdom.first;
|
||||
pi.sig_en = rd_en[cell_port_i];
|
||||
pi.effective_clkpol = clkdom.second;
|
||||
|
@ -913,17 +921,18 @@ grow_read_ports:;
|
|||
} else {
|
||||
SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
|
||||
c->setPort(stringf("\\%sDATA", pf), bram_dout);
|
||||
|
||||
if (pi.make_outreg) {
|
||||
if (pi.make_outreg && pi.make_transp) {
|
||||
log(" Moving output register to address for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
SigSpec sig_addr_q = module->addWire(NEW_ID, bram.abits);
|
||||
module->addDff(NEW_ID, pi.sig_clock, sig_addr, sig_addr_q, pi.effective_clkpol);
|
||||
c->setPort(stringf("\\%sADDR", pf), sig_addr_q);
|
||||
} else if (pi.make_outreg) {
|
||||
SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits);
|
||||
if (!pi.sig_en.empty())
|
||||
bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en);
|
||||
module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol);
|
||||
bram_dout = bram_dout_q;
|
||||
}
|
||||
|
||||
if (pi.make_transp)
|
||||
{
|
||||
} else if (pi.make_transp) {
|
||||
log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
|
||||
|
||||
SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits),
|
||||
|
@ -949,6 +958,8 @@ grow_read_ports:;
|
|||
SigSpec addr_ok_q = addr_ok;
|
||||
if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
|
||||
addr_ok_q = module->addWire(NEW_ID);
|
||||
if (!pi.sig_en.empty())
|
||||
addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en);
|
||||
module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,5 +13,6 @@ OBJS += passes/opt/wreduce.o
|
|||
OBJS += passes/opt/opt_demorgan.o
|
||||
OBJS += passes/opt/rmports.o
|
||||
OBJS += passes/opt/opt_lut.o
|
||||
OBJS += passes/opt/pmux2shiftx.o
|
||||
endif
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ struct keep_cache_t
|
|||
|
||||
bool query(Cell *cell)
|
||||
{
|
||||
if (cell->type.in("$memwr", "$meminit", "$assert", "$assume", "$live", "$fair", "$cover"))
|
||||
if (cell->type.in("$memwr", "$meminit", "$assert", "$assume", "$live", "$fair", "$cover", "$specify2", "$specify3", "$specrule"))
|
||||
return true;
|
||||
|
||||
if (cell->has_keep_attr())
|
||||
|
@ -85,22 +85,34 @@ void rmunused_module_cells(Module *module, bool verbose)
|
|||
{
|
||||
SigMap sigmap(module);
|
||||
pool<Cell*> queue, unused;
|
||||
pool<SigBit> used_raw_bits;
|
||||
dict<SigBit, pool<Cell*>> wire2driver;
|
||||
dict<SigBit, vector<string>> driver_driver_logs;
|
||||
|
||||
SigMap raw_sigmap;
|
||||
for (auto &it : module->connections_) {
|
||||
for (int i = 0; i < GetSize(it.second); i++) {
|
||||
if (it.second[i].wire != nullptr)
|
||||
raw_sigmap.add(it.first[i], it.second[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : module->cells_) {
|
||||
Cell *cell = it.second;
|
||||
for (auto &it2 : cell->connections()) {
|
||||
if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first))
|
||||
for (auto raw_bit : it2.second) {
|
||||
if (raw_bit.wire == nullptr)
|
||||
continue;
|
||||
auto bit = sigmap(raw_bit);
|
||||
if (bit.wire == nullptr)
|
||||
log_warning("Driver-driver conflict for %s between cell %s.%s and constant %s in %s: Resolved using constant.\n",
|
||||
log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module));
|
||||
if (bit.wire != nullptr)
|
||||
wire2driver[bit].insert(cell);
|
||||
}
|
||||
if (ct_all.cell_known(cell->type) && !ct_all.cell_output(cell->type, it2.first))
|
||||
continue;
|
||||
for (auto raw_bit : it2.second) {
|
||||
if (raw_bit.wire == nullptr)
|
||||
continue;
|
||||
auto bit = sigmap(raw_bit);
|
||||
if (bit.wire == nullptr)
|
||||
driver_driver_logs[raw_sigmap(raw_bit)].push_back(stringf("Driver-driver conflict "
|
||||
"for %s between cell %s.%s and constant %s in %s: Resolved using constant.",
|
||||
log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module)));
|
||||
if (bit.wire != nullptr)
|
||||
wire2driver[bit].insert(cell);
|
||||
}
|
||||
}
|
||||
if (keep_cache.query(cell))
|
||||
queue.insert(cell);
|
||||
|
@ -114,6 +126,8 @@ void rmunused_module_cells(Module *module, bool verbose)
|
|||
for (auto bit : sigmap(wire))
|
||||
for (auto c : wire2driver[bit])
|
||||
queue.insert(c), unused.erase(c);
|
||||
for (auto raw_bit : SigSpec(wire))
|
||||
used_raw_bits.insert(raw_sigmap(raw_bit));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,11 +151,27 @@ void rmunused_module_cells(Module *module, bool verbose)
|
|||
|
||||
for (auto cell : unused) {
|
||||
if (verbose)
|
||||
log(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
|
||||
log_debug(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
|
||||
module->design->scratchpad_set_bool("opt.did_something", true);
|
||||
module->remove(cell);
|
||||
count_rm_cells++;
|
||||
}
|
||||
|
||||
for (auto &it : module->cells_) {
|
||||
Cell *cell = it.second;
|
||||
for (auto &it2 : cell->connections()) {
|
||||
if (ct_all.cell_known(cell->type) && !ct_all.cell_input(cell->type, it2.first))
|
||||
continue;
|
||||
for (auto raw_bit : raw_sigmap(it2.second))
|
||||
used_raw_bits.insert(raw_bit);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it : driver_driver_logs) {
|
||||
if (used_raw_bits.count(it.first))
|
||||
for (auto msg : it.second)
|
||||
log_warning("%s\n", msg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int count_nontrivial_wire_attrs(RTLIL::Wire *w)
|
||||
|
@ -202,7 +232,7 @@ bool check_public_name(RTLIL::IdString id)
|
|||
return true;
|
||||
}
|
||||
|
||||
void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
|
||||
bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
|
||||
{
|
||||
SigPool register_signals;
|
||||
SigPool connected_signals;
|
||||
|
@ -245,11 +275,13 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
|||
module->connections_.clear();
|
||||
|
||||
SigPool used_signals;
|
||||
SigPool raw_used_signals;
|
||||
SigPool used_signals_nodrivers;
|
||||
for (auto &it : module->cells_) {
|
||||
RTLIL::Cell *cell = it.second;
|
||||
for (auto &it2 : cell->connections_) {
|
||||
assign_map.apply(it2.second);
|
||||
raw_used_signals.add(it2.second);
|
||||
used_signals.add(it2.second);
|
||||
if (!ct_all.cell_output(cell->type, it2.first))
|
||||
used_signals_nodrivers.add(it2.second);
|
||||
|
@ -259,6 +291,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
|||
RTLIL::Wire *wire = it.second;
|
||||
if (wire->port_id > 0) {
|
||||
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
|
||||
raw_used_signals.add(sig);
|
||||
assign_map.apply(sig);
|
||||
used_signals.add(sig);
|
||||
if (!wire->port_input)
|
||||
|
@ -271,72 +304,102 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<RTLIL::Wire*> maybe_del_wires;
|
||||
pool<RTLIL::Wire*> del_wires_queue;
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) {
|
||||
RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1;
|
||||
assign_map.apply(s2);
|
||||
if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) {
|
||||
maybe_del_wires.push_back(wire);
|
||||
} else {
|
||||
log_assert(GetSize(s1) == GetSize(s2));
|
||||
RTLIL::SigSig new_conn;
|
||||
for (int i = 0; i < GetSize(s1); i++)
|
||||
if (s1[i] != s2[i]) {
|
||||
new_conn.first.append_bit(s1[i]);
|
||||
new_conn.second.append_bit(s2[i]);
|
||||
}
|
||||
if (new_conn.first.size() > 0) {
|
||||
used_signals.add(new_conn.first);
|
||||
used_signals.add(new_conn.second);
|
||||
module->connect(new_conn);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!used_signals.check_any(RTLIL::SigSpec(wire)))
|
||||
maybe_del_wires.push_back(wire);
|
||||
SigSpec s1 = SigSpec(wire), s2 = assign_map(s1);
|
||||
log_assert(GetSize(s1) == GetSize(s2));
|
||||
|
||||
Const initval;
|
||||
if (wire->attributes.count("\\init"))
|
||||
initval = wire->attributes.at("\\init");
|
||||
if (GetSize(initval) != GetSize(wire))
|
||||
initval.bits.resize(GetSize(wire), State::Sx);
|
||||
if (initval.is_fully_undef())
|
||||
wire->attributes.erase("\\init");
|
||||
|
||||
if (GetSize(wire) == 0) {
|
||||
// delete zero-width wires
|
||||
goto delete_this_wire;
|
||||
} else
|
||||
if (wire->port_id != 0 || wire->get_bool_attribute("\\keep") || !initval.is_fully_undef()) {
|
||||
// do not delete anything with "keep" or module ports or initialized wires
|
||||
} else
|
||||
if (!purge_mode && check_public_name(wire->name)) {
|
||||
// do not get rid of public names unless in purge mode
|
||||
} else
|
||||
if (!raw_used_signals.check_any(s1)) {
|
||||
// delete wires that aren't used by anything directly
|
||||
goto delete_this_wire;
|
||||
} else
|
||||
if (!used_signals.check_any(s2)) {
|
||||
// delete wires that aren't used by anything indirectly, even though other wires may alias it
|
||||
goto delete_this_wire;
|
||||
}
|
||||
|
||||
RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire));
|
||||
if (!used_signals_nodrivers.check_any(sig)) {
|
||||
std::string unused_bits;
|
||||
for (int i = 0; i < GetSize(sig); i++) {
|
||||
if (sig[i].wire == NULL)
|
||||
continue;
|
||||
if (!used_signals_nodrivers.check(sig[i])) {
|
||||
if (!unused_bits.empty())
|
||||
unused_bits += " ";
|
||||
unused_bits += stringf("%d", i);
|
||||
if (0)
|
||||
{
|
||||
delete_this_wire:
|
||||
del_wires_queue.insert(wire);
|
||||
}
|
||||
else
|
||||
{
|
||||
RTLIL::SigSig new_conn;
|
||||
for (int i = 0; i < GetSize(s1); i++)
|
||||
if (s1[i] != s2[i]) {
|
||||
if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
|
||||
s2[i] = initval[i];
|
||||
initval[i] = State::Sx;
|
||||
}
|
||||
new_conn.first.append_bit(s1[i]);
|
||||
new_conn.second.append_bit(s2[i]);
|
||||
}
|
||||
if (new_conn.first.size() > 0) {
|
||||
if (initval.is_fully_undef())
|
||||
wire->attributes.erase("\\init");
|
||||
else
|
||||
wire->attributes.at("\\init") = initval;
|
||||
used_signals.add(new_conn.first);
|
||||
used_signals.add(new_conn.second);
|
||||
module->connect(new_conn);
|
||||
}
|
||||
if (unused_bits.empty() || wire->port_id != 0)
|
||||
|
||||
if (!used_signals_nodrivers.check_all(s2)) {
|
||||
std::string unused_bits;
|
||||
for (int i = 0; i < GetSize(s2); i++) {
|
||||
if (s2[i].wire == NULL)
|
||||
continue;
|
||||
if (!used_signals_nodrivers.check(s2[i])) {
|
||||
if (!unused_bits.empty())
|
||||
unused_bits += " ";
|
||||
unused_bits += stringf("%d", i);
|
||||
}
|
||||
}
|
||||
if (unused_bits.empty() || wire->port_id != 0)
|
||||
wire->attributes.erase("\\unused_bits");
|
||||
else
|
||||
wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits);
|
||||
} else {
|
||||
wire->attributes.erase("\\unused_bits");
|
||||
else
|
||||
wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits);
|
||||
} else {
|
||||
wire->attributes.erase("\\unused_bits");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int del_temp_wires_count = 0;
|
||||
for (auto wire : del_wires_queue) {
|
||||
if (ys_debug() || (check_public_name(wire->name) && verbose))
|
||||
log_debug(" removing unused non-port wire %s.\n", wire->name.c_str());
|
||||
else
|
||||
del_temp_wires_count++;
|
||||
}
|
||||
|
||||
pool<RTLIL::Wire*> del_wires;
|
||||
module->remove(del_wires_queue);
|
||||
count_rm_wires += GetSize(del_wires_queue);
|
||||
|
||||
int del_wires_count = 0;
|
||||
for (auto wire : maybe_del_wires)
|
||||
if (!used_signals.check_any(RTLIL::SigSpec(wire))) {
|
||||
if (check_public_name(wire->name) && verbose) {
|
||||
log(" removing unused non-port wire %s.\n", wire->name.c_str());
|
||||
}
|
||||
del_wires.insert(wire);
|
||||
del_wires_count++;
|
||||
}
|
||||
if (verbose && del_temp_wires_count)
|
||||
log_debug(" removed %d unused temporary wires.\n", del_temp_wires_count);
|
||||
|
||||
module->remove(del_wires);
|
||||
count_rm_wires += del_wires.size();
|
||||
|
||||
if (verbose && del_wires_count > 0)
|
||||
log(" removed %d unused temporary wires.\n", del_wires_count);
|
||||
return !del_wires_queue.empty();
|
||||
}
|
||||
|
||||
bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
|
||||
|
@ -399,7 +462,7 @@ bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
|
|||
}
|
||||
|
||||
if (verbose)
|
||||
log(" removing redundant init attribute on %s.\n", log_id(wire));
|
||||
log_debug(" removing redundant init attribute on %s.\n", log_id(wire));
|
||||
|
||||
wire->attributes.erase("\\init");
|
||||
did_something = true;
|
||||
|
@ -426,7 +489,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
|
|||
}
|
||||
for (auto cell : delcells) {
|
||||
if (verbose)
|
||||
log(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
|
||||
log_debug(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
|
||||
log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A")));
|
||||
module->remove(cell);
|
||||
}
|
||||
|
@ -434,10 +497,10 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
|
|||
module->design->scratchpad_set_bool("opt.did_something", true);
|
||||
|
||||
rmunused_module_cells(module, verbose);
|
||||
rmunused_module_signals(module, purge_mode, verbose);
|
||||
while (rmunused_module_signals(module, purge_mode, verbose)) { }
|
||||
|
||||
if (rminit && rmunused_module_init(module, purge_mode, verbose))
|
||||
rmunused_module_signals(module, purge_mode, verbose);
|
||||
while (rmunused_module_signals(module, purge_mode, verbose)) { }
|
||||
}
|
||||
|
||||
struct OptCleanPass : public Pass {
|
||||
|
@ -483,6 +546,9 @@ struct OptCleanPass : public Pass {
|
|||
|
||||
ct_all.setup(design);
|
||||
|
||||
count_rm_cells = 0;
|
||||
count_rm_wires = 0;
|
||||
|
||||
for (auto module : design->selected_whole_modules_warn()) {
|
||||
if (module->has_processes_warn())
|
||||
continue;
|
||||
|
@ -548,9 +614,10 @@ struct CleanPass : public Pass {
|
|||
for (auto module : design->selected_whole_modules()) {
|
||||
if (module->has_processes())
|
||||
continue;
|
||||
rmunused_module(module, purge_mode, false, false);
|
||||
rmunused_module(module, purge_mode, ys_debug(), false);
|
||||
}
|
||||
|
||||
log_suppressed();
|
||||
if (count_rm_cells > 0 || count_rm_wires > 0)
|
||||
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
|
||||
|
||||
|
|
|
@ -39,6 +39,9 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
|
|||
SigPool used_signals;
|
||||
SigPool all_signals;
|
||||
|
||||
dict<SigBit, pair<Wire*, State>> initbits;
|
||||
pool<Wire*> revisit_initwires;
|
||||
|
||||
for (auto cell : module->cells())
|
||||
for (auto &conn : cell->connections()) {
|
||||
if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first))
|
||||
|
@ -48,9 +51,17 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
|
|||
}
|
||||
|
||||
for (auto wire : module->wires()) {
|
||||
if (wire->attributes.count("\\init")) {
|
||||
SigSpec sig = sigmap(wire);
|
||||
Const initval = wire->attributes.at("\\init");
|
||||
for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) {
|
||||
if (initval[i] == State::S0 || initval[i] == State::S1)
|
||||
initbits[sig[i]] = make_pair(wire, initval[i]);
|
||||
}
|
||||
}
|
||||
if (wire->port_input)
|
||||
driven_signals.add(sigmap(wire));
|
||||
if (wire->port_output)
|
||||
if (wire->port_output || wire->get_bool_attribute("\\keep"))
|
||||
used_signals.add(sigmap(wire));
|
||||
all_signals.add(sigmap(wire));
|
||||
}
|
||||
|
@ -67,10 +78,43 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
|
|||
if (sig.size() == 0)
|
||||
continue;
|
||||
|
||||
log("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c));
|
||||
module->connect(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width)));
|
||||
Const val(RTLIL::State::Sx, GetSize(sig));
|
||||
for (int i = 0; i < GetSize(sig); i++) {
|
||||
SigBit bit = sigmap(sig[i]);
|
||||
auto cursor = initbits.find(bit);
|
||||
if (cursor != initbits.end()) {
|
||||
revisit_initwires.insert(cursor->second.first);
|
||||
val[i] = cursor->second.second;
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("Setting undriven signal in %s to constant: %s = %s\n", log_id(module), log_signal(sig), log_signal(val));
|
||||
module->connect(sig, val);
|
||||
did_something = true;
|
||||
}
|
||||
|
||||
if (!revisit_initwires.empty())
|
||||
{
|
||||
SigMap sm2(module);
|
||||
|
||||
for (auto wire : revisit_initwires) {
|
||||
SigSpec sig = sm2(wire);
|
||||
Const initval = wire->attributes.at("\\init");
|
||||
for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) {
|
||||
if (SigBit(initval[i]) == sig[i])
|
||||
initval[i] = State::Sx;
|
||||
}
|
||||
if (initval.is_fully_undef()) {
|
||||
log_debug("Removing init attribute from %s/%s.\n", log_id(module), log_id(wire));
|
||||
wire->attributes.erase("\\init");
|
||||
did_something = true;
|
||||
} else if (initval != wire->attributes.at("\\init")) {
|
||||
log_debug("Updating init attribute on %s/%s: %s\n", log_id(module), log_id(wire), log_signal(initval));
|
||||
wire->attributes["\\init"] = initval;
|
||||
did_something = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val)
|
||||
|
@ -78,7 +122,7 @@ void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell,
|
|||
RTLIL::SigSpec Y = cell->getPort(out_port);
|
||||
out_val.extend_u0(Y.size(), false);
|
||||
|
||||
log("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
|
||||
log_debug("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
|
||||
cell->type.c_str(), cell->name.c_str(), info.c_str(),
|
||||
module->name.c_str(), log_signal(Y), log_signal(out_val));
|
||||
// log_cell(cell);
|
||||
|
@ -134,7 +178,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
|
|||
if (GetSize(grouped_bits[i]) == GetSize(bits_y))
|
||||
return false;
|
||||
|
||||
log("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
|
||||
log_id(cell->type), log_id(cell), log_id(module));
|
||||
|
||||
for (int i = 0; i < GRP_N; i++)
|
||||
|
@ -155,6 +199,13 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
|
|||
new_b.append_bit(it.first.second);
|
||||
}
|
||||
|
||||
if (cell->type.in("$and", "$or") && i == GRP_CONST_A) {
|
||||
log_debug(" Direct Connection: %s (%s with %s)\n", log_signal(new_b), log_id(cell->type), log_signal(new_a));
|
||||
module->connect(new_y, new_b);
|
||||
module->connect(new_conn);
|
||||
continue;
|
||||
}
|
||||
|
||||
RTLIL::Cell *c = module->addCell(NEW_ID, cell->type);
|
||||
|
||||
c->setPort("\\A", new_a);
|
||||
|
@ -173,10 +224,10 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
|
|||
|
||||
module->connect(new_conn);
|
||||
|
||||
log(" New cell `%s': A=%s", log_id(c), log_signal(new_a));
|
||||
log_debug(" New cell `%s': A=%s", log_id(c), log_signal(new_a));
|
||||
if (b_name == "\\B")
|
||||
log(", B=%s", log_signal(new_b));
|
||||
log("\n");
|
||||
log_debug(", B=%s", log_signal(new_b));
|
||||
log_debug("\n");
|
||||
}
|
||||
|
||||
cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str());
|
||||
|
@ -190,7 +241,7 @@ void handle_polarity_inv(Cell *cell, IdString port, IdString param, const SigMap
|
|||
{
|
||||
SigSpec sig = assign_map(cell->getPort(port));
|
||||
if (invert_map.count(sig)) {
|
||||
log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
|
||||
log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
|
||||
log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
|
||||
log_signal(sig), log_signal(invert_map.at(sig)));
|
||||
cell->setPort(port, (invert_map.at(sig)));
|
||||
|
@ -219,7 +270,7 @@ void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdStrin
|
|||
if (cell->type.in(type1, type2)) {
|
||||
SigSpec sig = assign_map(cell->getPort(port));
|
||||
if (invert_map.count(sig)) {
|
||||
log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
|
||||
log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
|
||||
log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
|
||||
log_signal(sig), log_signal(invert_map.at(sig)));
|
||||
cell->setPort(port, (invert_map.at(sig)));
|
||||
|
@ -448,9 +499,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
{
|
||||
if (cell->type == "$reduce_xnor") {
|
||||
cover("opt.opt_expr.reduce_xnor_not");
|
||||
log("Replacing %s cell `%s' in module `%s' with $not cell.\n",
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with $not cell.\n",
|
||||
log_id(cell->type), log_id(cell->name), log_id(module));
|
||||
cell->type = "$not";
|
||||
did_something = true;
|
||||
} else {
|
||||
cover("opt.opt_expr.unary_buffer");
|
||||
replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A"));
|
||||
|
@ -481,7 +533,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if (GetSize(new_sig_a) < GetSize(sig_a)) {
|
||||
cover_list("opt.opt_expr.fine.neutral_A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_and", "$reduce_bool", cell->type.str());
|
||||
log("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
|
||||
log_debug("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
|
||||
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_sig_a));
|
||||
cell->setPort("\\A", new_sig_a);
|
||||
cell->parameters.at("\\A_WIDTH") = GetSize(new_sig_a);
|
||||
|
@ -504,7 +556,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if (GetSize(new_sig_b) < GetSize(sig_b)) {
|
||||
cover_list("opt.opt_expr.fine.neutral_B", "$logic_and", "$logic_or", cell->type.str());
|
||||
log("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
|
||||
log_debug("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
|
||||
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_sig_b));
|
||||
cell->setPort("\\B", new_sig_b);
|
||||
cell->parameters.at("\\B_WIDTH") = GetSize(new_sig_b);
|
||||
|
@ -530,7 +582,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
|
||||
cover("opt.opt_expr.fine.$reduce_and");
|
||||
log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
|
||||
log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
|
||||
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
|
||||
cell->setPort("\\A", sig_a = new_a);
|
||||
cell->parameters.at("\\A_WIDTH") = 1;
|
||||
|
@ -556,7 +608,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
|
||||
cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str());
|
||||
log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
|
||||
log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
|
||||
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
|
||||
cell->setPort("\\A", sig_a = new_a);
|
||||
cell->parameters.at("\\A_WIDTH") = 1;
|
||||
|
@ -582,7 +634,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) {
|
||||
cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell->type.str());
|
||||
log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
|
||||
log_debug("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
|
||||
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b));
|
||||
cell->setPort("\\B", sig_b = new_b);
|
||||
cell->parameters.at("\\B_WIDTH") = 1;
|
||||
|
@ -633,7 +685,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) {
|
||||
cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str());
|
||||
log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
RTLIL::SigSpec tmp = cell->getPort("\\A");
|
||||
cell->setPort("\\A", cell->getPort("\\B"));
|
||||
cell->setPort("\\B", tmp);
|
||||
|
@ -743,7 +795,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
ACTION_DO("\\Y", cell->getPort("\\A"));
|
||||
if (input == State::S0 && !a.is_fully_undef()) {
|
||||
cover("opt.opt_expr.action_" S__LINE__);
|
||||
log("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
|
||||
log_debug("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
|
||||
cell->type.c_str(), cell->name.c_str(), module->name.c_str());
|
||||
cell->setPort("\\A", SigSpec(State::Sx, GetSize(a)));
|
||||
did_something = true;
|
||||
|
@ -815,7 +867,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
ACTION_DO("\\Y", cell->getPort("\\A"));
|
||||
} else {
|
||||
cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str());
|
||||
log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
cell->type = "$not";
|
||||
cell->parameters.erase("\\B_WIDTH");
|
||||
cell->parameters.erase("\\B_SIGNED");
|
||||
|
@ -830,7 +882,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
(assign_map(cell->getPort("\\A")).is_fully_zero() || assign_map(cell->getPort("\\B")).is_fully_zero()))
|
||||
{
|
||||
cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
|
||||
log("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
|
||||
log_id(module), "$eq" ? "$logic_not" : "$reduce_bool");
|
||||
cell->type = cell->type == "$eq" ? "$logic_not" : "$reduce_bool";
|
||||
if (assign_map(cell->getPort("\\A")).is_fully_zero()) {
|
||||
|
@ -869,7 +921,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str());
|
||||
|
||||
log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
|
||||
log_debug("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
|
||||
log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y));
|
||||
|
||||
module->connect(cell->getPort("\\Y"), sig_y);
|
||||
|
@ -932,7 +984,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
if (identity_wrt_b)
|
||||
cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
|
||||
|
||||
log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
|
||||
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B');
|
||||
|
||||
if (!identity_wrt_a) {
|
||||
|
@ -962,7 +1014,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") &&
|
||||
cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) {
|
||||
cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str());
|
||||
log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
cell->setPort("\\A", cell->getPort("\\S"));
|
||||
cell->unsetPort("\\B");
|
||||
cell->unsetPort("\\S");
|
||||
|
@ -981,7 +1033,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) {
|
||||
cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str());
|
||||
log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
cell->setPort("\\A", cell->getPort("\\S"));
|
||||
cell->unsetPort("\\S");
|
||||
if (cell->type == "$mux") {
|
||||
|
@ -1001,7 +1053,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
|
||||
cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str());
|
||||
log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
log_debug("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
|
||||
cell->setPort("\\B", cell->getPort("\\S"));
|
||||
cell->unsetPort("\\S");
|
||||
if (cell->type == "$mux") {
|
||||
|
@ -1054,7 +1106,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
}
|
||||
if (cell->getPort("\\S").size() != new_s.size()) {
|
||||
cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str());
|
||||
log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
|
||||
log_debug("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
|
||||
GetSize(cell->getPort("\\S")) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module));
|
||||
cell->setPort("\\A", new_a);
|
||||
cell->setPort("\\B", new_b);
|
||||
|
@ -1172,7 +1224,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
{
|
||||
cover("opt.opt_expr.mul_shift.zero");
|
||||
|
||||
log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
|
||||
log_debug("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
|
||||
cell->name.c_str(), module->name.c_str());
|
||||
|
||||
module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
|
||||
|
@ -1190,7 +1242,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
else
|
||||
cover("opt.opt_expr.mul_shift.unswapped");
|
||||
|
||||
log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
|
||||
log_debug("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
|
||||
a_val, cell->name.c_str(), module->name.c_str(), i);
|
||||
|
||||
if (!swapped_ab) {
|
||||
|
@ -1230,7 +1282,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
{
|
||||
cover("opt.opt_expr.divmod_zero");
|
||||
|
||||
log("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
|
||||
log_debug("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
|
||||
cell->name.c_str(), module->name.c_str());
|
||||
|
||||
module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(State::Sx, sig_y.size())));
|
||||
|
@ -1247,7 +1299,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
{
|
||||
cover("opt.opt_expr.div_shift");
|
||||
|
||||
log("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
|
||||
log_debug("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
|
||||
b_val, cell->name.c_str(), module->name.c_str(), i);
|
||||
|
||||
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
|
||||
|
@ -1265,7 +1317,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
{
|
||||
cover("opt.opt_expr.mod_mask");
|
||||
|
||||
log("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
|
||||
log_debug("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
|
||||
b_val, cell->name.c_str(), module->name.c_str());
|
||||
|
||||
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
|
||||
|
@ -1335,7 +1387,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
SigSpec y_sig = cell->getPort("\\Y");
|
||||
Const y_value(cell->type.in("$eq", "$eqx") ? 0 : 1, GetSize(y_sig));
|
||||
|
||||
log("Replacing cell `%s' in module `%s' with constant driver %s.\n",
|
||||
log_debug("Replacing cell `%s' in module `%s' with constant driver %s.\n",
|
||||
log_id(cell), log_id(module), log_signal(y_value));
|
||||
|
||||
module->connect(y_sig, y_value);
|
||||
|
@ -1347,7 +1399,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if (redundant_bits)
|
||||
{
|
||||
log("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
|
||||
log_debug("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
|
||||
redundant_bits, log_id(cell->type), log_id(cell), log_id(module));
|
||||
|
||||
cell->setPort("\\A", sig_a);
|
||||
|
@ -1486,7 +1538,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
|||
|
||||
if (replace || remove)
|
||||
{
|
||||
log("Replacing %s cell `%s' (implementing %s) with %s.\n",
|
||||
log_debug("Replacing %s cell `%s' (implementing %s) with %s.\n",
|
||||
log_id(cell->type), log_id(cell), condition.c_str(), replacement.c_str());
|
||||
if (replace)
|
||||
module->connect(cell->getPort("\\Y"), replace_sig);
|
||||
|
@ -1592,8 +1644,14 @@ struct OptExprPass : public Pass {
|
|||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
if (undriven)
|
||||
log("Optimizing module %s.\n", log_id(module));
|
||||
|
||||
if (undriven) {
|
||||
did_something = false;
|
||||
replace_undriven(design, module);
|
||||
if (did_something)
|
||||
design->scratchpad_set_bool("opt.did_something", true);
|
||||
}
|
||||
|
||||
do {
|
||||
do {
|
||||
|
@ -1603,7 +1661,11 @@ struct OptExprPass : public Pass {
|
|||
design->scratchpad_set_bool("opt.did_something", true);
|
||||
} while (did_something);
|
||||
replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc, clkinv);
|
||||
if (did_something)
|
||||
design->scratchpad_set_bool("opt.did_something", true);
|
||||
} while (did_something);
|
||||
|
||||
log_suppressed();
|
||||
}
|
||||
|
||||
log_pop();
|
||||
|
|
|
@ -315,17 +315,17 @@ struct OptMergeWorker
|
|||
{
|
||||
if (sharemap.count(cell) > 0) {
|
||||
did_something = true;
|
||||
log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
|
||||
log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
|
||||
for (auto &it : cell->connections()) {
|
||||
if (cell->output(it.first)) {
|
||||
RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first);
|
||||
log(" Redirecting output %s: %s = %s\n", it.first.c_str(),
|
||||
log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(),
|
||||
log_signal(it.second), log_signal(other_sig));
|
||||
module->connect(RTLIL::SigSig(it.second, other_sig));
|
||||
assign_map.add(it.second, other_sig);
|
||||
}
|
||||
}
|
||||
log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
|
||||
log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
|
||||
#ifdef USE_CELL_HASH_CACHE
|
||||
cell_hash_cache.erase(cell);
|
||||
#endif
|
||||
|
@ -336,6 +336,8 @@ struct OptMergeWorker
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_suppressed();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -181,20 +181,29 @@ struct OptMuxtreeWorker
|
|||
|
||||
for (int mux_idx = 0; mux_idx < GetSize(root_muxes); mux_idx++)
|
||||
if (root_muxes.at(mux_idx)) {
|
||||
log(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : "");
|
||||
log_debug(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : "");
|
||||
root_mux_rerun.erase(mux_idx);
|
||||
eval_root_mux(mux_idx);
|
||||
if (glob_abort_cnt == 0) {
|
||||
log(" Giving up (too many iterations)\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (!root_mux_rerun.empty()) {
|
||||
int mux_idx = *root_mux_rerun.begin();
|
||||
log(" Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell));
|
||||
log_debug(" Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell));
|
||||
log_assert(root_enable_muxes.at(mux_idx));
|
||||
root_mux_rerun.erase(mux_idx);
|
||||
eval_root_mux(mux_idx);
|
||||
if (glob_abort_cnt == 0) {
|
||||
log(" Giving up (too many iterations)\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log(" Analyzing evaluation results.\n");
|
||||
log_assert(glob_abort_cnt > 0);
|
||||
|
||||
for (auto &mi : mux2info)
|
||||
{
|
||||
|
@ -326,7 +335,7 @@ struct OptMuxtreeWorker
|
|||
if (abort_count == 0) {
|
||||
root_mux_rerun.insert(m);
|
||||
root_enable_muxes.at(m) = true;
|
||||
log(" Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell));
|
||||
log_debug(" Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell));
|
||||
} else
|
||||
eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1);
|
||||
} else
|
||||
|
@ -397,10 +406,8 @@ struct OptMuxtreeWorker
|
|||
|
||||
void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
|
||||
{
|
||||
if (glob_abort_cnt == 0) {
|
||||
log(" Giving up (too many iterations)\n");
|
||||
if (glob_abort_cnt == 0)
|
||||
return;
|
||||
}
|
||||
glob_abort_cnt--;
|
||||
|
||||
muxinfo_t &muxinfo = mux2info[mux_idx];
|
||||
|
@ -454,6 +461,7 @@ struct OptMuxtreeWorker
|
|||
|
||||
void eval_root_mux(int mux_idx)
|
||||
{
|
||||
log_assert(glob_abort_cnt > 0);
|
||||
knowledge_t knowledge;
|
||||
knowledge.known_inactive.resize(GetSize(bit2info));
|
||||
knowledge.known_active.resize(GetSize(bit2info));
|
||||
|
|
|
@ -174,8 +174,6 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
|
|||
cell->unsetParam("\\CLR_POLARITY");
|
||||
cell->unsetPort("\\SET");
|
||||
cell->unsetPort("\\CLR");
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -186,11 +184,12 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
|
|||
cell->unsetParam("\\CLR_POLARITY");
|
||||
cell->unsetPort("\\SET");
|
||||
cell->unsetPort("\\CLR");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
if (!hasreset)
|
||||
{
|
||||
IdString new_type;
|
||||
|
||||
|
@ -207,8 +206,10 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
|
|||
cell->unsetPort("\\S");
|
||||
cell->unsetPort("\\R");
|
||||
|
||||
return did_something;
|
||||
return true;
|
||||
}
|
||||
|
||||
return did_something;
|
||||
}
|
||||
|
||||
bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
|
||||
|
|
|
@ -0,0 +1,852 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct OnehotDatabase
|
||||
{
|
||||
Module *module;
|
||||
const SigMap &sigmap;
|
||||
bool verbose = false;
|
||||
bool initialized = false;
|
||||
|
||||
pool<SigBit> init_ones;
|
||||
dict<SigSpec, pool<SigSpec>> sig_sources_db;
|
||||
dict<SigSpec, bool> sig_onehot_cache;
|
||||
pool<SigSpec> recursion_guard;
|
||||
|
||||
OnehotDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
|
||||
{
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
log_assert(!initialized);
|
||||
initialized = true;
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
auto it = wire->attributes.find("\\init");
|
||||
if (it == wire->attributes.end())
|
||||
continue;
|
||||
|
||||
auto &val = it->second;
|
||||
int width = std::max(GetSize(wire), GetSize(val));
|
||||
|
||||
for (int i = 0; i < width; i++)
|
||||
if (val[i] == State::S1)
|
||||
init_ones.insert(sigmap(SigBit(wire, i)));
|
||||
}
|
||||
|
||||
for (auto cell : module->cells())
|
||||
{
|
||||
vector<SigSpec> inputs;
|
||||
SigSpec output;
|
||||
|
||||
if (cell->type.in("$adff", "$dff", "$dffe", "$dlatch", "$ff"))
|
||||
{
|
||||
output = cell->getPort("\\Q");
|
||||
if (cell->type == "$adff")
|
||||
inputs.push_back(cell->getParam("\\ARST_VALUE"));
|
||||
inputs.push_back(cell->getPort("\\D"));
|
||||
}
|
||||
|
||||
if (cell->type.in("$mux", "$pmux"))
|
||||
{
|
||||
output = cell->getPort("\\Y");
|
||||
inputs.push_back(cell->getPort("\\A"));
|
||||
SigSpec B = cell->getPort("\\B");
|
||||
for (int i = 0; i < GetSize(B); i += GetSize(output))
|
||||
inputs.push_back(B.extract(i, GetSize(output)));
|
||||
}
|
||||
|
||||
if (!output.empty())
|
||||
{
|
||||
output = sigmap(output);
|
||||
auto &srcs = sig_sources_db[output];
|
||||
for (auto src : inputs) {
|
||||
while (!src.empty() && src[GetSize(src)-1] == State::S0)
|
||||
src.remove(GetSize(src)-1);
|
||||
srcs.insert(sigmap(src));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void query_worker(const SigSpec &sig, bool &retval, bool &cache, int indent)
|
||||
{
|
||||
if (verbose)
|
||||
log("%*s %s\n", indent, "", log_signal(sig));
|
||||
log_assert(retval);
|
||||
|
||||
if (recursion_guard.count(sig)) {
|
||||
if (verbose)
|
||||
log("%*s - recursion\n", indent, "");
|
||||
cache = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = sig_onehot_cache.find(sig);
|
||||
if (it != sig_onehot_cache.end()) {
|
||||
if (verbose)
|
||||
log("%*s - cached (%s)\n", indent, "", it->second ? "true" : "false");
|
||||
if (!it->second)
|
||||
retval = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool found_init_ones = false;
|
||||
for (auto bit : sig) {
|
||||
if (init_ones.count(bit)) {
|
||||
if (found_init_ones) {
|
||||
if (verbose)
|
||||
log("%*s - non-onehot init value\n", indent, "");
|
||||
retval = false;
|
||||
break;
|
||||
}
|
||||
found_init_ones = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval)
|
||||
{
|
||||
if (sig.is_fully_const())
|
||||
{
|
||||
bool found_ones = false;
|
||||
for (auto bit : sig) {
|
||||
if (bit == State::S1) {
|
||||
if (found_ones) {
|
||||
if (verbose)
|
||||
log("%*s - non-onehot constant\n", indent, "");
|
||||
retval = false;
|
||||
break;
|
||||
}
|
||||
found_ones = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto srcs = sig_sources_db.find(sig);
|
||||
if (srcs == sig_sources_db.end()) {
|
||||
if (verbose)
|
||||
log("%*s - no sources for non-const signal\n", indent, "");
|
||||
retval = false;
|
||||
} else {
|
||||
for (auto &src : srcs->second) {
|
||||
bool child_cache = true;
|
||||
recursion_guard.insert(sig);
|
||||
query_worker(src, retval, child_cache, indent+4);
|
||||
recursion_guard.erase(sig);
|
||||
if (!child_cache)
|
||||
cache = false;
|
||||
if (!retval)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// it is always safe to cache a negative result
|
||||
if (cache || !retval)
|
||||
sig_onehot_cache[sig] = retval;
|
||||
}
|
||||
|
||||
bool query(const SigSpec &sig)
|
||||
{
|
||||
bool retval = true;
|
||||
bool cache = true;
|
||||
|
||||
if (verbose)
|
||||
log("** ONEHOT QUERY START (%s)\n", log_signal(sig));
|
||||
|
||||
if (!initialized)
|
||||
initialize();
|
||||
|
||||
query_worker(sig, retval, cache, 3);
|
||||
|
||||
if (verbose)
|
||||
log("** ONEHOT QUERY RESULT = %s\n", retval ? "true" : "false");
|
||||
|
||||
// it is always safe to cache the root result of a query
|
||||
if (!cache)
|
||||
sig_onehot_cache[sig] = retval;
|
||||
|
||||
return retval;
|
||||
}
|
||||
};
|
||||
|
||||
struct Pmux2ShiftxPass : public Pass {
|
||||
Pmux2ShiftxPass() : Pass("pmux2shiftx", "transform $pmux cells to $shiftx cells") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" pmux2shiftx [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass transforms $pmux cells to $shiftx cells.\n");
|
||||
log("\n");
|
||||
log(" -v, -vv\n");
|
||||
log(" verbose output\n");
|
||||
log("\n");
|
||||
log(" -min_density <percentage>\n");
|
||||
log(" specifies the minimum density for the shifter\n");
|
||||
log(" default: 50\n");
|
||||
log("\n");
|
||||
log(" -min_choices <int>\n");
|
||||
log(" specified the minimum number of choices for a control signal\n");
|
||||
log(" default: 3\n");
|
||||
log("\n");
|
||||
log(" -onehot ignore|pmux|shiftx\n");
|
||||
log(" select strategy for one-hot encoded control signals\n");
|
||||
log(" default: pmux\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
int min_density = 50;
|
||||
int min_choices = 3;
|
||||
bool allow_onehot = false;
|
||||
bool optimize_onehot = true;
|
||||
bool verbose = false;
|
||||
bool verbose_onehot = false;
|
||||
|
||||
log_header(design, "Executing PMUX2SHIFTX pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-min_density" && argidx+1 < args.size()) {
|
||||
min_density = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-min_choices" && argidx+1 < args.size()) {
|
||||
min_choices = atoi(args[++argidx].c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "ignore") {
|
||||
argidx++;
|
||||
allow_onehot = false;
|
||||
optimize_onehot = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "pmux") {
|
||||
argidx++;
|
||||
allow_onehot = false;
|
||||
optimize_onehot = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "shiftx") {
|
||||
argidx++;
|
||||
allow_onehot = true;
|
||||
optimize_onehot = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-v") {
|
||||
verbose = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-vv") {
|
||||
verbose = true;
|
||||
verbose_onehot = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
OnehotDatabase onehot_db(module, sigmap);
|
||||
onehot_db.verbose = verbose_onehot;
|
||||
|
||||
dict<SigBit, pair<SigSpec, Const>> eqdb;
|
||||
|
||||
for (auto cell : module->cells())
|
||||
{
|
||||
if (cell->type == "$eq")
|
||||
{
|
||||
dict<SigBit, State> bits;
|
||||
|
||||
SigSpec A = sigmap(cell->getPort("\\A"));
|
||||
SigSpec B = sigmap(cell->getPort("\\B"));
|
||||
|
||||
int a_width = cell->getParam("\\A_WIDTH").as_int();
|
||||
int b_width = cell->getParam("\\B_WIDTH").as_int();
|
||||
|
||||
if (a_width < b_width) {
|
||||
bool a_signed = cell->getParam("\\A_SIGNED").as_int();
|
||||
A.extend_u0(b_width, a_signed);
|
||||
}
|
||||
|
||||
if (b_width < a_width) {
|
||||
bool b_signed = cell->getParam("\\B_SIGNED").as_int();
|
||||
B.extend_u0(a_width, b_signed);
|
||||
}
|
||||
|
||||
for (int i = 0; i < GetSize(A); i++) {
|
||||
SigBit a_bit = A[i], b_bit = B[i];
|
||||
if (b_bit.wire && !a_bit.wire) {
|
||||
std::swap(a_bit, b_bit);
|
||||
}
|
||||
if (!a_bit.wire || b_bit.wire)
|
||||
goto next_cell;
|
||||
if (bits.count(a_bit))
|
||||
goto next_cell;
|
||||
bits[a_bit] = b_bit.data;
|
||||
}
|
||||
|
||||
if (GetSize(bits) > 20)
|
||||
goto next_cell;
|
||||
|
||||
bits.sort();
|
||||
pair<SigSpec, Const> entry;
|
||||
|
||||
for (auto it : bits) {
|
||||
entry.first.append_bit(it.first);
|
||||
entry.second.bits.push_back(it.second);
|
||||
}
|
||||
|
||||
eqdb[sigmap(cell->getPort("\\Y")[0])] = entry;
|
||||
goto next_cell;
|
||||
}
|
||||
|
||||
if (cell->type == "$logic_not")
|
||||
{
|
||||
dict<SigBit, State> bits;
|
||||
|
||||
SigSpec A = sigmap(cell->getPort("\\A"));
|
||||
|
||||
for (int i = 0; i < GetSize(A); i++)
|
||||
bits[A[i]] = State::S0;
|
||||
|
||||
bits.sort();
|
||||
pair<SigSpec, Const> entry;
|
||||
|
||||
for (auto it : bits) {
|
||||
entry.first.append_bit(it.first);
|
||||
entry.second.bits.push_back(it.second);
|
||||
}
|
||||
|
||||
eqdb[sigmap(cell->getPort("\\Y")[0])] = entry;
|
||||
goto next_cell;
|
||||
}
|
||||
next_cell:;
|
||||
}
|
||||
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
if (cell->type != "$pmux")
|
||||
continue;
|
||||
|
||||
string src = cell->get_src_attribute();
|
||||
int width = cell->getParam("\\WIDTH").as_int();
|
||||
int width_bits = ceil_log2(width);
|
||||
int extwidth = width;
|
||||
|
||||
while (extwidth & (extwidth-1))
|
||||
extwidth++;
|
||||
|
||||
dict<SigSpec, pool<int>> seldb;
|
||||
|
||||
SigSpec A = cell->getPort("\\A");
|
||||
SigSpec B = cell->getPort("\\B");
|
||||
SigSpec S = sigmap(cell->getPort("\\S"));
|
||||
for (int i = 0; i < GetSize(S); i++)
|
||||
{
|
||||
if (!eqdb.count(S[i]))
|
||||
continue;
|
||||
|
||||
auto &entry = eqdb.at(S[i]);
|
||||
seldb[entry.first].insert(i);
|
||||
}
|
||||
|
||||
if (seldb.empty())
|
||||
continue;
|
||||
|
||||
bool printed_pmux_header = false;
|
||||
|
||||
if (verbose) {
|
||||
printed_pmux_header = true;
|
||||
log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell));
|
||||
log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits);
|
||||
}
|
||||
|
||||
SigSpec updated_S = cell->getPort("\\S");
|
||||
SigSpec updated_B = cell->getPort("\\B");
|
||||
|
||||
while (!seldb.empty())
|
||||
{
|
||||
// pick the largest entry in seldb
|
||||
SigSpec sig = seldb.begin()->first;
|
||||
for (auto &it : seldb) {
|
||||
if (GetSize(sig) < GetSize(it.first))
|
||||
sig = it.first;
|
||||
else if (GetSize(seldb.at(sig)) < GetSize(it.second))
|
||||
sig = it.first;
|
||||
}
|
||||
|
||||
// find the relevant choices
|
||||
bool is_onehot = GetSize(sig) > 2;
|
||||
dict<Const, int> choices;
|
||||
for (int i : seldb.at(sig)) {
|
||||
Const val = eqdb.at(S[i]).second;
|
||||
int onebits = 0;
|
||||
for (auto b : val.bits)
|
||||
if (b == State::S1)
|
||||
onebits++;
|
||||
if (onebits > 1)
|
||||
is_onehot = false;
|
||||
choices[val] = i;
|
||||
}
|
||||
|
||||
bool full_pmux = GetSize(choices) == GetSize(S);
|
||||
|
||||
// TBD: also find choices that are using signals that are subsets of the bits in "sig"
|
||||
|
||||
if (!verbose)
|
||||
{
|
||||
if (is_onehot && !allow_onehot && !optimize_onehot) {
|
||||
seldb.erase(sig);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GetSize(choices) < min_choices) {
|
||||
seldb.erase(sig);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!printed_pmux_header) {
|
||||
printed_pmux_header = true;
|
||||
log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell));
|
||||
log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits);
|
||||
}
|
||||
|
||||
log(" checking ctrl signal %s\n", log_signal(sig));
|
||||
|
||||
auto print_choices = [&]() {
|
||||
log(" table of choices:\n");
|
||||
for (auto &it : choices)
|
||||
log(" %3d: %s: %s\n", it.second, log_signal(it.first),
|
||||
log_signal(B.extract(it.second*width, width)));
|
||||
};
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
if (is_onehot && !allow_onehot && !optimize_onehot) {
|
||||
print_choices();
|
||||
log(" ignoring one-hot encoding.\n");
|
||||
seldb.erase(sig);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GetSize(choices) < min_choices) {
|
||||
print_choices();
|
||||
log(" insufficient choices.\n");
|
||||
seldb.erase(sig);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_onehot && optimize_onehot)
|
||||
{
|
||||
print_choices();
|
||||
if (!onehot_db.query(sig))
|
||||
{
|
||||
log(" failed to detect onehot driver. do not optimize.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
log(" optimizing one-hot encoding.\n");
|
||||
for (auto &it : choices)
|
||||
{
|
||||
const Const &val = it.first;
|
||||
int index = -1;
|
||||
|
||||
for (int i = 0; i < GetSize(val); i++)
|
||||
if (val[i] == State::S1) {
|
||||
log_assert(index < 0);
|
||||
index = i;
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
log(" %3d: zero encoding.\n", it.second);
|
||||
continue;
|
||||
}
|
||||
|
||||
SigBit new_ctrl = sig[index];
|
||||
log(" %3d: new crtl signal is %s.\n", it.second, log_signal(new_ctrl));
|
||||
updated_S[it.second] = new_ctrl;
|
||||
}
|
||||
}
|
||||
seldb.erase(sig);
|
||||
continue;
|
||||
}
|
||||
|
||||
// find the best permutation
|
||||
vector<int> perm_new_from_old(GetSize(sig));
|
||||
Const perm_xormask(State::S0, GetSize(sig));
|
||||
{
|
||||
vector<int> values(GetSize(choices));
|
||||
vector<bool> used_src_columns(GetSize(sig));
|
||||
vector<vector<bool>> columns(GetSize(sig), vector<bool>(GetSize(values)));
|
||||
|
||||
for (int i = 0; i < GetSize(choices); i++) {
|
||||
Const val = choices.element(i)->first;
|
||||
for (int k = 0; k < GetSize(val); k++)
|
||||
if (val[k] == State::S1)
|
||||
columns[k][i] = true;
|
||||
}
|
||||
|
||||
for (int dst_col = GetSize(sig)-1; dst_col >= 0; dst_col--)
|
||||
{
|
||||
int best_src_col = -1;
|
||||
bool best_inv = false;
|
||||
int best_maxval = 0;
|
||||
int best_delta = 0;
|
||||
|
||||
// find best src column for this dst column
|
||||
for (int src_col = 0; src_col < GetSize(sig); src_col++)
|
||||
{
|
||||
if (used_src_columns[src_col])
|
||||
continue;
|
||||
|
||||
int this_maxval = 0;
|
||||
int this_minval = 1 << 30;
|
||||
|
||||
int this_inv_maxval = 0;
|
||||
int this_inv_minval = 1 << 30;
|
||||
|
||||
for (int i = 0; i < GetSize(values); i++)
|
||||
{
|
||||
int val = values[i];
|
||||
int inv_val = val;
|
||||
|
||||
if (columns[src_col][i])
|
||||
val |= 1 << dst_col;
|
||||
else
|
||||
inv_val |= 1 << dst_col;
|
||||
|
||||
this_maxval = std::max(this_maxval, val);
|
||||
this_minval = std::min(this_minval, val);
|
||||
|
||||
this_inv_maxval = std::max(this_inv_maxval, inv_val);
|
||||
this_inv_minval = std::min(this_inv_minval, inv_val);
|
||||
}
|
||||
|
||||
int this_delta = this_maxval - this_minval;
|
||||
int this_inv_delta = this_maxval - this_minval;
|
||||
bool this_inv = false;
|
||||
|
||||
if (this_delta != this_inv_delta)
|
||||
this_inv = this_inv_delta < this_delta;
|
||||
else if (this_maxval != this_inv_maxval)
|
||||
this_inv = this_inv_maxval < this_maxval;
|
||||
|
||||
if (this_inv) {
|
||||
this_delta = this_inv_delta;
|
||||
this_maxval = this_inv_maxval;
|
||||
this_minval = this_inv_minval;
|
||||
}
|
||||
|
||||
bool this_is_better = false;
|
||||
|
||||
if (best_src_col < 0)
|
||||
this_is_better = true;
|
||||
else if (this_delta != best_delta)
|
||||
this_is_better = this_delta < best_delta;
|
||||
else if (this_maxval != best_maxval)
|
||||
this_is_better = this_maxval < best_maxval;
|
||||
else
|
||||
this_is_better = sig[best_src_col] < sig[src_col];
|
||||
|
||||
if (this_is_better) {
|
||||
best_src_col = src_col;
|
||||
best_inv = this_inv;
|
||||
best_maxval = this_maxval;
|
||||
best_delta = this_delta;
|
||||
}
|
||||
}
|
||||
|
||||
used_src_columns[best_src_col] = true;
|
||||
perm_new_from_old[dst_col] = best_src_col;
|
||||
perm_xormask[dst_col] = best_inv ? State::S1 : State::S0;
|
||||
}
|
||||
}
|
||||
|
||||
// permutated sig
|
||||
SigSpec perm_sig(State::S0, GetSize(sig));
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
perm_sig[i] = sig[perm_new_from_old[i]];
|
||||
|
||||
log(" best permutation: %s\n", log_signal(perm_sig));
|
||||
log(" best xor mask: %s\n", log_signal(perm_xormask));
|
||||
|
||||
// permutated choices
|
||||
int min_choice = 1 << 30;
|
||||
int max_choice = -1;
|
||||
dict<Const, int> perm_choices;
|
||||
|
||||
for (auto &it : choices)
|
||||
{
|
||||
Const &old_c = it.first;
|
||||
Const new_c(State::S0, GetSize(old_c));
|
||||
|
||||
for (int i = 0; i < GetSize(old_c); i++)
|
||||
new_c[i] = old_c[perm_new_from_old[i]];
|
||||
|
||||
Const new_c_before_xor = new_c;
|
||||
new_c = const_xor(new_c, perm_xormask, false, false, GetSize(new_c));
|
||||
|
||||
perm_choices[new_c] = it.second;
|
||||
|
||||
min_choice = std::min(min_choice, new_c.as_int());
|
||||
max_choice = std::max(max_choice, new_c.as_int());
|
||||
|
||||
log(" %3d: %s -> %s -> %s: %s\n", it.second, log_signal(old_c), log_signal(new_c_before_xor),
|
||||
log_signal(new_c), log_signal(B.extract(it.second*width, width)));
|
||||
}
|
||||
|
||||
int range_density = 100*GetSize(choices) / (max_choice-min_choice+1);
|
||||
int absolute_density = 100*GetSize(choices) / (max_choice+1);
|
||||
|
||||
log(" choices: %d\n", GetSize(choices));
|
||||
log(" min choice: %d\n", min_choice);
|
||||
log(" max choice: %d\n", max_choice);
|
||||
log(" range density: %d%%\n", range_density);
|
||||
log(" absolute density: %d%%\n", absolute_density);
|
||||
|
||||
if (full_pmux) {
|
||||
int full_density = 100*GetSize(choices) / (1 << GetSize(sig));
|
||||
log(" full density: %d%%\n", full_density);
|
||||
if (full_density < min_density) {
|
||||
full_pmux = false;
|
||||
} else {
|
||||
min_choice = 0;
|
||||
max_choice = (1 << GetSize(sig))-1;
|
||||
log(" update to full case.\n");
|
||||
log(" new min choice: %d\n", min_choice);
|
||||
log(" new max choice: %d\n", max_choice);
|
||||
}
|
||||
}
|
||||
|
||||
bool full_case = (min_choice == 0) && (max_choice == (1 << GetSize(sig))-1) && (full_pmux || max_choice+1 == GetSize(choices));
|
||||
log(" full case: %s\n", full_case ? "true" : "false");
|
||||
|
||||
// check density percentages
|
||||
Const offset(State::S0, GetSize(sig));
|
||||
if (absolute_density < min_density && range_density >= min_density)
|
||||
{
|
||||
offset = Const(min_choice, GetSize(sig));
|
||||
log(" offset: %s\n", log_signal(offset));
|
||||
|
||||
min_choice -= offset.as_int();
|
||||
max_choice -= offset.as_int();
|
||||
|
||||
dict<Const, int> new_perm_choices;
|
||||
for (auto &it : perm_choices)
|
||||
new_perm_choices[const_sub(it.first, offset, false, false, GetSize(sig))] = it.second;
|
||||
perm_choices.swap(new_perm_choices);
|
||||
} else
|
||||
if (absolute_density < min_density) {
|
||||
log(" insufficient density.\n");
|
||||
seldb.erase(sig);
|
||||
continue;
|
||||
}
|
||||
|
||||
// creat cmp signal
|
||||
SigSpec cmp = perm_sig;
|
||||
if (perm_xormask.as_bool())
|
||||
cmp = module->Xor(NEW_ID, cmp, perm_xormask, false, src);
|
||||
if (offset.as_bool())
|
||||
cmp = module->Sub(NEW_ID, cmp, offset, false, src);
|
||||
|
||||
// create enable signal
|
||||
SigBit en = State::S1;
|
||||
if (!full_case) {
|
||||
Const enable_mask(State::S0, max_choice+1);
|
||||
for (auto &it : perm_choices)
|
||||
enable_mask[it.first.as_int()] = State::S1;
|
||||
en = module->addWire(NEW_ID);
|
||||
module->addShift(NEW_ID, enable_mask, cmp, en, false, src);
|
||||
}
|
||||
|
||||
// create data signal
|
||||
SigSpec data(State::Sx, (max_choice+1)*extwidth);
|
||||
if (full_pmux) {
|
||||
for (int i = 0; i <= max_choice; i++)
|
||||
data.replace(i*extwidth, A);
|
||||
}
|
||||
for (auto &it : perm_choices) {
|
||||
int position = it.first.as_int()*extwidth;
|
||||
int data_index = it.second;
|
||||
data.replace(position, B.extract(data_index*width, width));
|
||||
updated_S[data_index] = State::S0;
|
||||
updated_B.replace(data_index*width, SigSpec(State::Sx, width));
|
||||
}
|
||||
|
||||
// create shiftx cell
|
||||
SigSpec shifted_cmp = {cmp, SigSpec(State::S0, width_bits)};
|
||||
SigSpec outsig = module->addWire(NEW_ID, width);
|
||||
Cell *c = module->addShiftx(NEW_ID, data, shifted_cmp, outsig, false, src);
|
||||
updated_S.append(en);
|
||||
updated_B.append(outsig);
|
||||
log(" created $shiftx cell %s.\n", log_id(c));
|
||||
|
||||
// remove this sig and continue with the next block
|
||||
seldb.erase(sig);
|
||||
}
|
||||
|
||||
// update $pmux cell
|
||||
cell->setPort("\\S", updated_S);
|
||||
cell->setPort("\\B", updated_B);
|
||||
cell->setParam("\\S_WIDTH", GetSize(updated_S));
|
||||
}
|
||||
}
|
||||
}
|
||||
} Pmux2ShiftxPass;
|
||||
|
||||
struct OnehotPass : public Pass {
|
||||
OnehotPass() : Pass("onehot", "optimize $eq cells for onehot signals") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" onehot [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass optimizes $eq cells that compare one-hot signals against constants\n");
|
||||
log("\n");
|
||||
log(" -v, -vv\n");
|
||||
log(" verbose output\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool verbose = false;
|
||||
bool verbose_onehot = false;
|
||||
|
||||
log_header(design, "Executing ONEHOT pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-v") {
|
||||
verbose = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-vv") {
|
||||
verbose = true;
|
||||
verbose_onehot = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
SigMap sigmap(module);
|
||||
OnehotDatabase onehot_db(module, sigmap);
|
||||
onehot_db.verbose = verbose_onehot;
|
||||
|
||||
for (auto cell : module->selected_cells())
|
||||
{
|
||||
if (cell->type != "$eq")
|
||||
continue;
|
||||
|
||||
SigSpec A = sigmap(cell->getPort("\\A"));
|
||||
SigSpec B = sigmap(cell->getPort("\\B"));
|
||||
|
||||
int a_width = cell->getParam("\\A_WIDTH").as_int();
|
||||
int b_width = cell->getParam("\\B_WIDTH").as_int();
|
||||
|
||||
if (a_width < b_width) {
|
||||
bool a_signed = cell->getParam("\\A_SIGNED").as_int();
|
||||
A.extend_u0(b_width, a_signed);
|
||||
}
|
||||
|
||||
if (b_width < a_width) {
|
||||
bool b_signed = cell->getParam("\\B_SIGNED").as_int();
|
||||
B.extend_u0(a_width, b_signed);
|
||||
}
|
||||
|
||||
if (A.is_fully_const())
|
||||
std::swap(A, B);
|
||||
|
||||
if (!B.is_fully_const())
|
||||
continue;
|
||||
|
||||
if (verbose)
|
||||
log("Checking $eq(%s, %s) cell %s/%s.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell));
|
||||
|
||||
if (!onehot_db.query(A)) {
|
||||
if (verbose)
|
||||
log(" onehot driver test on %s failed.\n", log_signal(A));
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = -1;
|
||||
bool not_onehot = false;
|
||||
|
||||
for (int i = 0; i < GetSize(B); i++) {
|
||||
if (B[i] != State::S1)
|
||||
continue;
|
||||
if (index >= 0)
|
||||
not_onehot = true;
|
||||
index = i;
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
if (verbose)
|
||||
log(" not optimizing the zero pattern.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
SigSpec Y = cell->getPort("\\Y");
|
||||
|
||||
if (not_onehot)
|
||||
{
|
||||
if (verbose)
|
||||
log(" replacing with constant 0 driver.\n");
|
||||
else
|
||||
log("Replacing one-hot $eq(%s, %s) cell %s/%s with constant 0 driver.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell));
|
||||
module->connect(Y, SigSpec(1, GetSize(Y)));
|
||||
}
|
||||
else
|
||||
{
|
||||
SigSpec sig = A[index];
|
||||
if (verbose)
|
||||
log(" replacing with signal %s.\n", log_signal(sig));
|
||||
else
|
||||
log("Replacing one-hot $eq(%s, %s) cell %s/%s with signal %s.\n",log_signal(A), log_signal(B), log_id(module), log_id(cell), log_signal(sig));
|
||||
sig.extend_u0(GetSize(Y));
|
||||
module->connect(Y, sig);
|
||||
}
|
||||
|
||||
module->remove(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
} OnehotPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -29,6 +29,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
struct WreduceConfig
|
||||
{
|
||||
pool<IdString> supported_cell_types;
|
||||
bool keepdc = false;
|
||||
|
||||
WreduceConfig()
|
||||
{
|
||||
|
@ -38,7 +39,8 @@ struct WreduceConfig
|
|||
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
|
||||
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
|
||||
"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
|
||||
"$mux", "$pmux"
|
||||
"$mux", "$pmux",
|
||||
"$dff", "$adff"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -52,6 +54,8 @@ struct WreduceWorker
|
|||
std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
|
||||
std::set<SigBit> work_queue_bits;
|
||||
pool<SigBit> keep_bits;
|
||||
dict<SigBit, State> init_bits;
|
||||
pool<SigBit> remove_init_bits;
|
||||
|
||||
WreduceWorker(WreduceConfig *config, Module *module) :
|
||||
config(config), module(module), mi(module) { }
|
||||
|
@ -79,7 +83,7 @@ struct WreduceWorker
|
|||
|
||||
SigBit ref = sig_a[i];
|
||||
for (int k = 0; k < GetSize(sig_s); k++) {
|
||||
if (ref != Sx && sig_b[k*GetSize(sig_a) + i] != Sx && ref != sig_b[k*GetSize(sig_a) + i])
|
||||
if ((config->keepdc || (ref != Sx && sig_b[k*GetSize(sig_a) + i] != Sx)) && ref != sig_b[k*GetSize(sig_a) + i])
|
||||
goto no_match_ab;
|
||||
if (sig_b[k*GetSize(sig_a) + i] != Sx)
|
||||
ref = sig_b[k*GetSize(sig_a) + i];
|
||||
|
@ -134,6 +138,93 @@ struct WreduceWorker
|
|||
module->connect(sig_y.extract(n_kept, n_removed), sig_removed);
|
||||
}
|
||||
|
||||
void run_cell_dff(Cell *cell)
|
||||
{
|
||||
// Reduce size of FF if inputs are just sign/zero extended or output bit is not used
|
||||
|
||||
SigSpec sig_d = mi.sigmap(cell->getPort("\\D"));
|
||||
SigSpec sig_q = mi.sigmap(cell->getPort("\\Q"));
|
||||
Const initval;
|
||||
|
||||
int width_before = GetSize(sig_q);
|
||||
|
||||
if (width_before == 0)
|
||||
return;
|
||||
|
||||
bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
|
||||
bool sign_ext = !zero_ext;
|
||||
|
||||
for (int i = 0; i < GetSize(sig_q); i++) {
|
||||
SigBit bit = sig_q[i];
|
||||
if (init_bits.count(bit))
|
||||
initval.bits.push_back(init_bits.at(bit));
|
||||
else
|
||||
initval.bits.push_back(State::Sx);
|
||||
}
|
||||
|
||||
for (int i = GetSize(sig_q)-1; i >= 0; i--)
|
||||
{
|
||||
if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx)) {
|
||||
module->connect(sig_q[i], State::S0);
|
||||
remove_init_bits.insert(sig_q[i]);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1]) {
|
||||
module->connect(sig_q[i], sig_q[i-1]);
|
||||
remove_init_bits.insert(sig_q[i]);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto info = mi.query(sig_q[i]);
|
||||
if (info == nullptr)
|
||||
return;
|
||||
if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
|
||||
remove_init_bits.insert(sig_q[i]);
|
||||
sig_d.remove(i);
|
||||
sig_q.remove(i);
|
||||
zero_ext = false;
|
||||
sign_ext = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (width_before == GetSize(sig_q))
|
||||
return;
|
||||
|
||||
if (GetSize(sig_q) == 0) {
|
||||
log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
|
||||
module->remove(cell);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Removed top %d bits (of %d) from FF cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
|
||||
log_id(module), log_id(cell), log_id(cell->type));
|
||||
|
||||
for (auto bit : sig_d)
|
||||
work_queue_bits.insert(bit);
|
||||
|
||||
for (auto bit : sig_q)
|
||||
work_queue_bits.insert(bit);
|
||||
|
||||
// Narrow ARST_VALUE parameter to new size.
|
||||
if (cell->parameters.count("\\ARST_VALUE")) {
|
||||
Const arst_value = cell->getParam("\\ARST_VALUE");
|
||||
arst_value.bits.resize(GetSize(sig_q));
|
||||
cell->setParam("\\ARST_VALUE", arst_value);
|
||||
}
|
||||
|
||||
cell->setPort("\\D", sig_d);
|
||||
cell->setPort("\\Q", sig_q);
|
||||
cell->fixup_parameters();
|
||||
}
|
||||
|
||||
void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something)
|
||||
{
|
||||
port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool();
|
||||
|
@ -176,6 +267,9 @@ struct WreduceWorker
|
|||
if (cell->type.in("$mux", "$pmux"))
|
||||
return run_cell_mux(cell);
|
||||
|
||||
if (cell->type.in("$dff", "$adff"))
|
||||
return run_cell_dff(cell);
|
||||
|
||||
SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
|
||||
|
||||
if (sig.has_const())
|
||||
|
@ -300,10 +394,21 @@ struct WreduceWorker
|
|||
|
||||
void run()
|
||||
{
|
||||
for (auto w : module->wires())
|
||||
// create a copy as mi.sigmap will be updated as we process the module
|
||||
SigMap init_attr_sigmap = mi.sigmap;
|
||||
|
||||
for (auto w : module->wires()) {
|
||||
if (w->get_bool_attribute("\\keep"))
|
||||
for (auto bit : mi.sigmap(w))
|
||||
keep_bits.insert(bit);
|
||||
if (w->attributes.count("\\init")) {
|
||||
Const initval = w->attributes.at("\\init");
|
||||
SigSpec initsig = init_attr_sigmap(w);
|
||||
int width = std::min(GetSize(initval), GetSize(initsig));
|
||||
for (int i = 0; i < width; i++)
|
||||
init_bits[initsig[i]] = initval[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (auto c : module->selected_cells())
|
||||
work_queue_cells.insert(c);
|
||||
|
@ -351,6 +456,22 @@ struct WreduceWorker
|
|||
module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
|
||||
module->swap_names(w, nw);
|
||||
}
|
||||
|
||||
if (!remove_init_bits.empty()) {
|
||||
for (auto w : module->wires()) {
|
||||
if (w->attributes.count("\\init")) {
|
||||
Const initval = w->attributes.at("\\init");
|
||||
Const new_initval(State::Sx, GetSize(w));
|
||||
SigSpec initsig = init_attr_sigmap(w);
|
||||
int width = std::min(GetSize(initval), GetSize(initsig));
|
||||
for (int i = 0; i < width; i++) {
|
||||
if (!remove_init_bits.count(initsig[i]))
|
||||
new_initval[i] = initval[i];
|
||||
}
|
||||
w->attributes.at("\\init") = new_initval;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -375,6 +496,9 @@ struct WreducePass : public Pass {
|
|||
log(" Do not change the width of memory address ports. Use this options in\n");
|
||||
log(" flows that use the 'memory_memx' pass.\n");
|
||||
log("\n");
|
||||
log(" -keepdc\n");
|
||||
log(" Do not optimize explicit don't-care values.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
|
||||
{
|
||||
|
@ -389,6 +513,10 @@ struct WreducePass : public Pass {
|
|||
opt_memx = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-keepdc") {
|
||||
config.keepdc = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -411,6 +539,42 @@ struct WreducePass : public Pass {
|
|||
module->connect(sig, Const(0, GetSize(sig)));
|
||||
}
|
||||
}
|
||||
|
||||
if (c->type.in("$div", "$mod", "$pow"))
|
||||
{
|
||||
SigSpec A = c->getPort("\\A");
|
||||
int original_a_width = GetSize(A);
|
||||
if (c->getParam("\\A_SIGNED").as_bool()) {
|
||||
while (GetSize(A) > 1 && A[GetSize(A)-1] == State::S0 && A[GetSize(A)-2] == State::S0)
|
||||
A.remove(GetSize(A)-1, 1);
|
||||
} else {
|
||||
while (GetSize(A) > 0 && A[GetSize(A)-1] == State::S0)
|
||||
A.remove(GetSize(A)-1, 1);
|
||||
}
|
||||
if (original_a_width != GetSize(A)) {
|
||||
log("Removed top %d bits (of %d) from port A of cell %s.%s (%s).\n",
|
||||
original_a_width-GetSize(A), original_a_width, log_id(module), log_id(c), log_id(c->type));
|
||||
c->setPort("\\A", A);
|
||||
c->setParam("\\A_WIDTH", GetSize(A));
|
||||
}
|
||||
|
||||
SigSpec B = c->getPort("\\B");
|
||||
int original_b_width = GetSize(B);
|
||||
if (c->getParam("\\B_SIGNED").as_bool()) {
|
||||
while (GetSize(B) > 1 && B[GetSize(B)-1] == State::S0 && B[GetSize(B)-2] == State::S0)
|
||||
B.remove(GetSize(B)-1, 1);
|
||||
} else {
|
||||
while (GetSize(B) > 0 && B[GetSize(B)-1] == State::S0)
|
||||
B.remove(GetSize(B)-1, 1);
|
||||
}
|
||||
if (original_b_width != GetSize(B)) {
|
||||
log("Removed top %d bits (of %d) from port B of cell %s.%s (%s).\n",
|
||||
original_b_width-GetSize(B), original_b_width, log_id(module), log_id(c), log_id(c->type));
|
||||
c->setPort("\\B", B);
|
||||
c->setParam("\\B_WIDTH", GetSize(B));
|
||||
}
|
||||
}
|
||||
|
||||
if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) {
|
||||
IdString memid = c->getParam("\\MEMID").decode_string();
|
||||
RTLIL::Memory *mem = module->memories.at(memid);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue