diff options
147 files changed, 5652 insertions, 791 deletions
diff --git a/.gitignore b/.gitignore index 484faf419..93e28cd6c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ /yosys-abc /yosys-abc.exe /yosys-config +/yosys-smtbmc /yosys-filterlib /yosys-filterlib.exe /kernel/version_*.cc diff --git a/CodingReadme b/CodingReadme index f93d22050..5d5d9b32c 100644 --- a/CodingReadme +++ b/CodingReadme @@ -93,6 +93,9 @@ creates a bijective map from K to the integers. For example: It is not possible to remove elements from an idict. +Finally mfp<K> implements a merge-find set data structure (aka. disjoint-set or +union–find) over the type K ("mfp" = merge-find-promote). + 2. Standard STL data types In Yosys we use std::vector<T> and std::string whenever applicable. When @@ -342,10 +345,10 @@ Then with default config setting: ./yosys -p 'synth; show' tests/simple/fiedler-cooley.v ./yosys -p 'synth_xilinx -top up3down5; show' tests/simple/fiedler-cooley.v - cd ~yosys/techlibs/cmos + cd ~yosys/examples/cmos bash testbench.sh - cd ~yosys/techlibs/xilinx/example_basys3 + cd ~yosys/examples/basys3 bash run.sh @@ -18,11 +18,11 @@ ENABLE_LIBYOSYS := 0 ENABLE_GPROF := 0 ENABLE_NDEBUG := 0 -DESTDIR := /usr/local +PREFIX ?= /usr/local INSTALL_SUDO := -TARGET_BINDIR := $(DESTDIR)/bin -TARGET_DATDIR := $(DESTDIR)/share/yosys +TARGET_BINDIR := $(DESTDIR)$(PREFIX)/bin +TARGET_DATDIR := $(DESTDIR)$(PREFIX)/share/yosys EXE = OBJS = @@ -39,8 +39,8 @@ all: top-all YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST))) VPATH := $(YOSYS_SRC) -CXXFLAGS = -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(DESTDIR)/include -LDFLAGS = -L$(DESTDIR)/lib +CXXFLAGS += -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(DESTDIR)$(PREFIX)/include +LDFLAGS += -L$(DESTDIR)$(PREFIX)/lib LDLIBS = -lstdc++ -lm SED = sed BISON = bison @@ -179,7 +179,7 @@ endif ifeq ($(ENABLE_VERIFIC),1) VERIFIC_DIR ?= /usr/local/src/verific_lib_eval -VERIFIC_COMPONENTS ?= verilog vhdl database util containers +VERIFIC_COMPONENTS ?= verilog vhdl database util containers sdf CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) endif @@ -300,18 +300,18 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS)) endif yosys$(EXE): $(OBJS) - $(P) $(CXX) -o yosys$(EXE) $(LDFLAGS) $^ $(LDLIBS) + $(P) $(CXX) -o yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) $(P) $(CXX) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LDFLAGS) $^ $(LDLIBS) %.o: %.cc $(Q) mkdir -p $(dir $@) - $(P) $(CXX) -o $@ -c $(CXXFLAGS) $< + $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< %.o: %.cpp $(Q) mkdir -p $(dir $@) - $(P) $(CXX) -o $@ -c $(CXXFLAGS) $< + $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile $(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc @@ -319,9 +319,9 @@ kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile $(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1` $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS)))\"; }" > kernel/version_$(GIT_REV).cc yosys-config: misc/yosys-config.in - $(P) $(SED) -e 's,@CXXFLAGS@,$(subst -I. -I"$(YOSYS_SRC)",-I"$(TARGET_DATDIR)/include",$(CXXFLAGS)),;' \ - -e 's,@CXX@,$(CXX),;' -e 's,@LDFLAGS@,$(LDFLAGS),;' -e 's,@LDLIBS@,$(LDLIBS),;' \ - -e 's,@BINDIR@,$(TARGET_BINDIR),;' -e 's,@DATDIR@,$(TARGET_DATDIR),;' < $< > yosys-config + $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(TARGET_DATDIR)/include",$(CXXFLAGS))#;' \ + -e 's#@CXX@#$(CXX)#;' -e 's#@LDFLAGS@#$(LDFLAGS)#;' -e 's#@LDLIBS@#$(LDLIBS)#;' \ + -e 's#@BINDIR@#$(TARGET_BINDIR)#;' -e 's#@DATDIR@#$(TARGET_DATDIR)#;' < $< > yosys-config $(Q) chmod +x yosys-config abc/abc-$(ABCREV)$(EXE): @@ -378,20 +378,20 @@ vloghtb: $(TARGETS) $(EXTRA_TARGETS) @echo "" install: $(TARGETS) $(EXTRA_TARGETS) - $(INSTALL_SUDO) mkdir -p $(DESTDIR)/bin - $(INSTALL_SUDO) install $(TARGETS) $(DESTDIR)/bin/ - $(INSTALL_SUDO) mkdir -p $(DESTDIR)/share/yosys - $(INSTALL_SUDO) cp -r share/. $(DESTDIR)/share/yosys/. + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(PREFIX)/bin + $(INSTALL_SUDO) install $(TARGETS) $(DESTDIR)$(PREFIX)/bin/ + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(PREFIX)/share/yosys + $(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(PREFIX)/share/yosys/. ifeq ($(ENABLE_LIBYOSYS),1) - $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)/lib/ + $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(PREFIX)/lib/ $(INSTALL_SUDO) ldconfig endif uninstall: - $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)/bin/,$(notdir $(TARGETS))) - $(INSTALL_SUDO) rm -rvf $(DESTDIR)/share/yosys/ + $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(PREFIX)/bin/,$(notdir $(TARGETS))) + $(INSTALL_SUDO) rm -rvf $(DESTDIR)$(PREFIX)/share/yosys/ ifeq ($(ENABLE_LIBYOSYS),1) - $(INSTALL_SUDO) rm -vf $(DESTDIR)/lib/libyosys.so + $(INSTALL_SUDO) rm -vf $(DESTDIR)$(PREFIX)/lib/libyosys.so endif update-manual: $(TARGETS) $(EXTRA_TARGETS) @@ -56,7 +56,7 @@ For example on Ubuntu Linux 14.04 LTS the following commands will install all prerequisites for building yosys: $ yosys_deps="build-essential clang bison flex libreadline-dev gawk - tcl-dev libffi-dev git mercurial graphviz xdot pkg-config python" + tcl-dev libffi-dev git mercurial graphviz xdot pkg-config python3" $ sudo apt-get install $yosys_deps There are also pre-compiled Yosys binary packages for Ubuntu and Win32 as well @@ -190,15 +190,13 @@ for the given cell library: clean If you do not have a liberty file but want to test this synthesis script, -you can use the file techlibs/cmos/cmos_cells.lib from the yosys sources. +you can use the file examples/cmos/cmos_cells.lib from the yosys sources. -Various more complex liberty files (for testing) can be found here: +Liberty file downloads for and information about free and open ASIC standard +cell libraries can be found here: - http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/.. - ../cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib - ../cadence/lib/ami035/signalstorm/osu035_stdcells.lib - ../cadence/lib/tsmc018/signalstorm/osu018_stdcells.lib - ../cadence/lib/ami05/signalstorm/osu05_stdcells.lib + http://www.vlsitechnology.org/html/libraries.html + http://www.vlsitechnology.org/synopsys/vsclib013.lib The command "synth" provides a good default synthesis script (see "help synth"). If possible a synthesis script should borrow from "synth". For example: @@ -367,6 +365,10 @@ 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 and unconditional context (only if/case statements on parameters + and constant values). The intended use for this is synthesis-time DRC. + Supported features from SystemVerilog ===================================== diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc index 4e0a393a8..eacda2734 100644 --- a/backends/smt2/Makefile.inc +++ b/backends/smt2/Makefile.inc @@ -1,3 +1,16 @@ OBJS += backends/smt2/smt2.o +ifneq ($(CONFIG),mxe) +ifneq ($(CONFIG),emcc) +TARGETS += yosys-smtbmc + +yosys-smtbmc: backends/smt2/smtbmc.py + $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new + $(Q) chmod +x $@.new + $(Q) mv $@.new $@ + +$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py)) +endif +endif + diff --git a/backends/smt2/example.v b/backends/smt2/example.v new file mode 100644 index 000000000..b195266eb --- /dev/null +++ b/backends/smt2/example.v @@ -0,0 +1,11 @@ +module main(input clk); + reg [3:0] counter = 0; + always @(posedge clk) begin + if (counter == 10) + counter <= 0; + else + counter <= counter + 1; + end + assert property (counter != 15); + // assert property (counter <= 10); +endmodule diff --git a/backends/smt2/example.ys b/backends/smt2/example.ys new file mode 100644 index 000000000..6fccb344f --- /dev/null +++ b/backends/smt2/example.ys @@ -0,0 +1,3 @@ +read_verilog -formal example.v +hierarchy; proc; opt; memory -nordff -nomap; opt -fast +write_smt2 -bv -mem -wires example.smt2 diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index 1e00ac718..c852921ee 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -32,7 +32,7 @@ struct Smt2Worker CellTypes ct; SigMap sigmap; RTLIL::Module *module; - bool bvmode, memmode, regsmode, verbose; + bool bvmode, memmode, regsmode, wiresmode, verbose; int idcounter; std::vector<std::string> decls, trans; @@ -44,8 +44,9 @@ struct Smt2Worker std::map<Cell*, int> memarrays; std::map<int, int> bvsizes; - Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool regsmode, bool verbose) : - ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), regsmode(regsmode), verbose(verbose), idcounter(0) + Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool regsmode, bool wiresmode, bool verbose) : + ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), + regsmode(regsmode), wiresmode(wiresmode), verbose(verbose), idcounter(0) { decls.push_back(stringf("(declare-sort |%s_s| 0)\n", log_id(module))); @@ -132,7 +133,7 @@ struct Smt2Worker std::string get_bool(RTLIL::SigSpec sig, const char *state_name = "state") { - return get_bool(sig.to_single_sigbit(), state_name); + return get_bool(sig.as_bit(), state_name); } std::string get_bv(RTLIL::SigSpec sig, const char *state_name = "state") @@ -215,7 +216,7 @@ struct Smt2Worker void export_gate(RTLIL::Cell *cell, std::string expr) { - RTLIL::SigBit bit = sigmap(cell->getPort("\\Y").to_single_sigbit()); + RTLIL::SigBit bit = sigmap(cell->getPort("\\Y").as_bit()); std::string processed_expr; for (char ch : expr) { @@ -243,8 +244,8 @@ struct Smt2Worker int width = GetSize(sig_y); if (type == 's' || type == 'd' || type == 'b') { - width = std::max(width, GetSize(cell->getPort("\\A"))); - width = std::max(width, GetSize(cell->getPort("\\B"))); + width = max(width, GetSize(cell->getPort("\\A"))); + width = max(width, GetSize(cell->getPort("\\B"))); } if (cell->hasPort("\\A")) { @@ -488,7 +489,7 @@ struct Smt2Worker for (auto bit : SigSpec(wire)) if (reg_bits.count(bit)) is_register = true; - if (wire->port_id || is_register || wire->get_bool_attribute("\\keep")) { + if (wire->port_id || is_register || wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\')) { RTLIL::SigSpec sig = sigmap(wire); if (wire->port_input) decls.push_back(stringf("; yosys-smt2-input %s %d\n", log_id(wire), wire->width)); @@ -496,6 +497,8 @@ struct Smt2Worker decls.push_back(stringf("; yosys-smt2-output %s %d\n", log_id(wire), wire->width)); if (is_register) decls.push_back(stringf("; yosys-smt2-register %s %d\n", log_id(wire), wire->width)); + if (wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\')) + decls.push_back(stringf("; yosys-smt2-wire %s %d\n", log_id(wire), wire->width)); if (bvmode && GetSize(sig) > 1) { decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n", log_id(module), log_id(wire), log_id(module), GetSize(sig), get_bv(sig).c_str())); @@ -628,6 +631,7 @@ struct Smt2Worker for (auto it : decls) f << it; + f << stringf("; yosys-smt2-module %s\n", log_id(module)); f << stringf("(define-fun |%s_t| ((state |%s_s|) (next_state |%s_s|)) Bool ", log_id(module), log_id(module), log_id(module)); if (GetSize(trans) > 1) { f << "(and\n"; @@ -693,6 +697,9 @@ struct Smt2Backend : public Backend { log(" -regs\n"); log(" also create '<mod>_n' functions for all registers.\n"); log("\n"); + log(" -wires\n"); + log(" also create '<mod>_n' functions for all public wires.\n"); + log("\n"); log(" -tpl <template_file>\n"); log(" use the given template file. the line containing only the token '%%%%'\n"); log(" is replaced with the regular output of this command.\n"); @@ -749,7 +756,7 @@ struct Smt2Backend : public Backend { virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { std::ifstream template_f; - bool bvmode = false, memmode = false, regsmode = false, verbose = false; + bool bvmode = false, memmode = false, regsmode = false, wiresmode = false, verbose = false; log_header("Executing SMT2 backend.\n"); @@ -775,6 +782,10 @@ struct Smt2Backend : public Backend { regsmode = true; continue; } + if (args[argidx] == "-wires") { + wiresmode = true; + continue; + } if (args[argidx] == "-verbose") { verbose = true; continue; @@ -804,7 +815,7 @@ struct Smt2Backend : public Backend { log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module)); - Smt2Worker worker(module, bvmode, memmode, regsmode, verbose); + Smt2Worker worker(module, bvmode, memmode, regsmode, wiresmode, verbose); worker.run(); worker.write(*f); } diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py new file mode 100644 index 000000000..db0bce4bb --- /dev/null +++ b/backends/smt2/smtbmc.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# +# 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. +# + +import os, sys, getopt, re +##yosys-sys-path## +from smtio import smtio, smtopts, mkvcd + +skip_steps = 0 +num_steps = 20 +vcdfile = None +tempind = False +assume_skipped = None +topmod = None +so = smtopts() + + +def usage(): + print(""" +yosys-smtbmc [options] <yosys_smt2_output> + + -t <num_steps>, -t <skip_steps>:<num_steps> + default: skip_steps=0, num_steps=20 + + -u <start_step> + assume asserts in skipped steps in BMC + + -c <vcd_filename> + write counter-example to this VCD file + (hint: use 'write_smt2 -wires' for maximum + coverage of signals in generated VCD file) + + -i + instead of BMC run temporal induction + + -m <module_name> + name of the top module +""" + so.helpmsg()) + sys.exit(1) + + +try: + opts, args = getopt.getopt(sys.argv[1:], so.optstr + "t:u:c:im:") +except: + usage() + +for o, a in opts: + if o == "-t": + match = re.match(r"(\d+):(.*)", a) + if match: + skip_steps = int(match.group(1)) + num_steps = int(match.group(2)) + else: + num_steps = int(a) + elif o == "-u": + assume_skipped = int(a) + elif o == "-c": + vcdfile = a + elif o == "-i": + tempind = True + elif o == "-m": + topmod = a + elif so.handle(o, a): + pass + else: + usage() + +if len(args) != 1: + usage() + + +smt = smtio(opts=so) + +print("%s Solver: %s" % (smt.timestamp(), so.solver)) +smt.setup("QF_AUFBV") + +debug_nets = set() +debug_nets_re = re.compile(r"^; yosys-smt2-(input|output|register|wire) (\S+) (\d+)") + +with open(args[0], "r") as f: + for line in f: + match = debug_nets_re.match(line) + if match: + debug_nets.add(match.group(2)) + if line.startswith("; yosys-smt2-module") and topmod is None: + topmod = line.split()[2] + smt.write(line) + +assert topmod is not None + + +def write_vcd_model(steps): + print("%s Writing model to VCD file." % smt.timestamp()) + + vcd = mkvcd(open(vcdfile, "w")) + for netname in sorted(debug_nets): + width = len(smt.get_net_bin(topmod, netname, "s0")) + vcd.add_net(netname, width) + + for i in range(steps): + vcd.set_time(i) + for netname in debug_nets: + vcd.set_net(netname, smt.get_net_bin(topmod, netname, "s%d" % i)) + + vcd.set_time(steps) + + +if tempind: + retstatus = False + for step in range(num_steps, -1, -1): + smt.write("(declare-fun s%d () %s_s)" % (step, topmod)) + smt.write("(assert (%s_u s%d))" % (topmod, step)) + + if step == num_steps: + smt.write("(assert (not (%s_a s%d)))" % (topmod, step)) + + else: + smt.write("(assert (%s_t s%d s%d))" % (topmod, step, step+1)) + smt.write("(assert (%s_a s%d))" % (topmod, step)) + + if step > num_steps-skip_steps: + print("%s Skipping induction in step %d.." % (smt.timestamp(), step)) + continue + + print("%s Trying induction in step %d.." % (smt.timestamp(), step)) + + if smt.check_sat() == "sat": + if step == 0: + print("%s Temporal induction failed!" % smt.timestamp()) + if vcdfile is not None: + write_vcd_model(num_steps+1) + + else: + print("%s Temporal induction successful." % smt.timestamp()) + retstatus = True + break + + +else: # not tempind + retstatus = True + for step in range(num_steps+1): + smt.write("(declare-fun s%d () %s_s)" % (step, topmod)) + smt.write("(assert (%s_u s%d))" % (topmod, step)) + + if step == 0: + smt.write("(assert (%s_i s0))" % (topmod)) + + else: + smt.write("(assert (%s_t s%d s%d))" % (topmod, step-1, step)) + + if step < skip_steps: + if assume_skipped is not None and step >= assume_skipped: + print("%s Skipping step %d (and assuming pass).." % (smt.timestamp(), step)) + smt.write("(assert (%s_a s%d))" % (topmod, step)) + else: + print("%s Skipping step %d.." % (smt.timestamp(), step)) + continue + + print("%s Checking asserts in step %d.." % (smt.timestamp(), step)) + smt.write("(push 1)") + + smt.write("(assert (not (%s_a s%d)))" % (topmod, step)) + + if smt.check_sat() == "sat": + print("%s BMC failed!" % smt.timestamp()) + if vcdfile is not None: + write_vcd_model(step+1) + retstatus = False + break + + else: # unsat + smt.write("(pop 1)") + smt.write("(assert (%s_a s%d))" % (topmod, step)) + + +smt.write("(exit)") +smt.wait() + +print("%s Status: %s" % (smt.timestamp(), "PASSED" if retstatus else "FAILED (!)")) +sys.exit(0 if retstatus else 1) + diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py new file mode 100644 index 000000000..6e8bded77 --- /dev/null +++ b/backends/smt2/smtio.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +# +# 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. +# + +import sys +import subprocess +from select import select +from time import time + +class smtio: + def __init__(self, solver=None, debug_print=None, debug_file=None, timeinfo=None, opts=None): + if opts is not None: + self.solver = opts.solver + self.debug_print = opts.debug_print + self.debug_file = opts.debug_file + self.timeinfo = opts.timeinfo + + else: + self.solver = "z3" + self.debug_print = False + self.debug_file = None + self.timeinfo = True + + if solver is not None: + self.solver = solver + + if debug_print is not None: + self.debug_print = debug_print + + if debug_file is not None: + self.debug_file = debug_file + + if timeinfo is not None: + self.timeinfo = timeinfo + + if self.solver == "yices": + popen_vargs = ['yices-smt2', '--incremental'] + + if self.solver == "z3": + popen_vargs = ['z3', '-smt2', '-in'] + + if self.solver == "cvc4": + popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2'] + + if self.solver == "mathsat": + popen_vargs = ['mathsat'] + + self.p = subprocess.Popen(popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + self.start_time = time() + + def setup(self, logic="ALL", info=None): + self.write("(set-logic %s)" % logic) + if info is not None: + self.write("(set-info :source |%s|)" % info) + self.write("(set-info :smt-lib-version 2.5)") + self.write("(set-info :category \"industrial\")") + + def timestamp(self): + secs = int(time() - self.start_time) + return "## %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60) + + def write(self, stmt): + stmt = stmt.strip() + if self.debug_print: + print("> %s" % stmt) + if self.debug_file: + print(stmt, file=self.debug_file) + self.debug_file.flush() + self.p.stdin.write(bytes(stmt + "\n", "ascii")) + self.p.stdin.flush() + + def read(self): + stmt = [] + count_brackets = 0 + + while True: + line = self.p.stdout.readline().decode("ascii").strip() + count_brackets += line.count("(") + count_brackets -= line.count(")") + stmt.append(line) + if self.debug_print: + print("< %s" % line) + if count_brackets == 0: + break + if not self.p.poll(): + print("SMT Solver terminated unexpectedly: %s" % "".join(stmt)) + sys.exit(1) + + stmt = "".join(stmt) + if stmt.startswith("(error"): + print("SMT Solver Error: %s" % stmt, file=sys.stderr) + sys.exit(1) + + return stmt + + def check_sat(self): + if self.debug_print: + print("> (check-sat)") + if self.debug_file: + print("; running check-sat..", file=self.debug_file) + self.debug_file.flush() + + self.p.stdin.write(bytes("(check-sat)\n", "ascii")) + self.p.stdin.flush() + + if self.timeinfo: + i = 0 + s = "/-\|" + + count = 0 + num_bs = 0 + while select([self.p.stdout], [], [], 0.1) == ([], [], []): + count += 1 + + if count < 25: + continue + + if count % 10 == 0 or count == 25: + secs = count // 10 + + if secs < 60: + m = "(%d seconds)" % secs + elif secs < 60*60: + m = "(%d seconds -- %d:%02d)" % (secs, secs // 60, secs % 60) + else: + m = "(%d seconds -- %d:%02d:%02d)" % (secs, secs // (60*60), (secs // 60) % 60, secs % 60) + + print("%s %s %c" % ("\b \b" * num_bs, m, s[i]), end="", file=sys.stderr) + num_bs = len(m) + 3 + + else: + print("\b" + s[i], end="", file=sys.stderr) + + sys.stderr.flush() + i = (i + 1) % len(s) + + if num_bs != 0: + print("\b \b" * num_bs, end="", file=sys.stderr) + sys.stderr.flush() + + result = self.read() + if self.debug_file: + print("(set-info :status %s)" % result, file=self.debug_file) + print("(check-sat)", file=self.debug_file) + self.debug_file.flush() + return result + + def parse(self, stmt): + def worker(stmt): + if stmt[0] == '(': + expr = [] + cursor = 1 + while stmt[cursor] != ')': + el, le = worker(stmt[cursor:]) + expr.append(el) + cursor += le + return expr, cursor+1 + + if stmt[0] == '|': + expr = "|" + cursor = 1 + while stmt[cursor] != '|': + expr += stmt[cursor] + cursor += 1 + expr += "|" + return expr, cursor+1 + + if stmt[0] in [" ", "\t", "\r", "\n"]: + el, le = worker(stmt[1:]) + return el, le+1 + + expr = "" + cursor = 0 + while stmt[cursor] not in ["(", ")", "|", " ", "\t", "\r", "\n"]: + expr += stmt[cursor] + cursor += 1 + return expr, cursor + return worker(stmt)[0] + + def bv2hex(self, v): + h = "" + v = bv2bin(v) + while len(v) > 0: + d = 0 + if len(v) > 0 and v[-1] == "1": d += 1 + if len(v) > 1 and v[-2] == "1": d += 2 + if len(v) > 2 and v[-3] == "1": d += 4 + if len(v) > 3 and v[-4] == "1": d += 8 + h = hex(d)[2:] + h + if len(v) < 4: break + v = v[:-4] + return h + + def bv2bin(self, v): + if v == "true": return "1" + if v == "false": return "0" + if v.startswith("#b"): + return v[2:] + if v.startswith("#x"): + digits = [] + for d in v[2:]: + if d == "0": digits.append("0000") + if d == "1": digits.append("0001") + if d == "2": digits.append("0010") + if d == "3": digits.append("0011") + if d == "4": digits.append("0100") + if d == "5": digits.append("0101") + if d == "6": digits.append("0110") + if d == "7": digits.append("0111") + if d == "8": digits.append("1000") + if d == "9": digits.append("1001") + if d in ("a", "A"): digits.append("1010") + if d in ("b", "B"): digits.append("1011") + if d in ("c", "C"): digits.append("1100") + if d in ("d", "D"): digits.append("1101") + if d in ("e", "E"): digits.append("1110") + if d in ("f", "F"): digits.append("1111") + return "".join(digits) + assert False + + def get(self, expr): + self.write("(get-value (%s))" % (expr)) + return self.parse(self.read())[0][1] + + def get_net(self, mod_name, net_name, state_name): + return self.get("(|%s_n %s| %s)" % (mod_name, net_name, state_name)) + + def get_net_bool(self, mod_name, net_name, state_name): + v = self.get_net(mod_name, net_name, state_name) + assert v in ["true", "false"] + return 1 if v == "true" else 0 + + def get_net_hex(self, mod_name, net_name, state_name): + return self.bv2hex(self.get_net(mod_name, net_name, state_name)) + + def get_net_bin(self, mod_name, net_name, state_name): + return self.bv2bin(self.get_net(mod_name, net_name, state_name)) + + def wait(self): + self.p.wait() + + +class smtopts: + def __init__(self): + self.optstr = "s:d:vp" + self.solver = "z3" + self.debug_print = False + self.debug_file = None + self.timeinfo = True + + def handle(self, o, a): + if o == "-s": + self.solver = a + elif o == "-v": + self.debug_print = True + elif o == "-p": + self.timeinfo = True + elif o == "-d": + self.debug_file = open(a, "w") + else: + return False + return True + + def helpmsg(self): + return """ + -s <solver> + set SMT solver: z3, cvc4, yices, mathsat + default: z3 + + -v + enable debug output + + -p + disable timer display during solving + + -d <filename> + write smt2 statements to file +""" + + +class mkvcd: + def __init__(self, f): + self.f = f + self.t = -1 + self.nets = dict() + + def add_net(self, name, width): + assert self.t == -1 + key = "n%d" % len(self.nets) + self.nets[name] = (key, width) + + def set_net(self, name, bits): + assert name in self.nets + assert self.t >= 0 + print("b%s %s" % (bits, self.nets[name][0]), file=self.f) + + def set_time(self, t): + assert t >= self.t + if t != self.t: + if self.t == -1: + print("$var event 1 ! smt_clock $end", file=self.f) + for name in sorted(self.nets): + key, width = self.nets[name] + print("$var wire %d %s %s $end" % (width, key, name), file=self.f) + print("$enddefinitions $end", file=self.f) + self.t = t + assert self.t >= 0 + print("#%d" % self.t, file=self.f) + print("1!", file=self.f) + diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc index fdf022c5a..b29a88ac2 100644 --- a/backends/smv/smv.cc +++ b/backends/smv/smv.cc @@ -244,13 +244,13 @@ struct SmvWorker int width_y = GetSize(cell->getPort("\\Y")); int shift_b_width = GetSize(sig_b); - int width_ay = std::max(GetSize(sig_a), width_y); + int width_ay = max(GetSize(sig_a), width_y); int width = width_ay; for (int i = 1, j = 0;; i <<= 1, j++) if (width_ay < i) { width = i-1; - shift_b_width = std::min(shift_b_width, j); + shift_b_width = min(shift_b_width, j); break; } @@ -361,8 +361,8 @@ struct SmvWorker if (cell->type.in("$div", "$mod")) { int width_y = GetSize(cell->getPort("\\Y")); - int width = std::max(width_y, GetSize(cell->getPort("\\A"))); - width = std::max(width, GetSize(cell->getPort("\\B"))); + int width = max(width_y, GetSize(cell->getPort("\\A"))); + width = max(width, GetSize(cell->getPort("\\B"))); string expr_a, expr_b, op; if (cell->type == "$div") op = "/"; @@ -384,7 +384,7 @@ struct SmvWorker if (cell->type.in("$eq", "$ne", "$eqx", "$nex", "$lt", "$le", "$ge", "$gt")) { - int width = std::max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B"))); + int width = max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B"))); string expr_a, expr_b, op; if (cell->type == "$eq") op = "="; diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index c04389f63..10f10e3cd 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -165,12 +165,13 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o log_assert(i < (int)data.bits.size()); if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1) goto dump_bits; - if (data.bits[i] == RTLIL::S1 && (i - offset) == 31) - goto dump_bits; if (data.bits[i] == RTLIL::S1) val |= 1 << (i - offset); } - f << stringf("32'%sd %d", set_signed ? "s" : "", val); + if (set_signed && val < 0) + f << stringf("-32'sd %u", -val); + else + f << stringf("32'%sd %u", set_signed ? "s" : "", val); } else { dump_bits: f << stringf("%d'%sb", width, set_signed ? "s" : ""); @@ -805,15 +806,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // initial begin // memid[0] <= ... // end - int mem_val; f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size-1, 0); if (use_init) { f << stringf("%s" "initial begin\n", indent.c_str()); for (int i=0; i<size; i++) { - mem_val = cell->parameters["\\INIT"].extract(i*width, width).as_int(); - f << stringf("%s" " %s[%d] <= %d'd%d;\n", indent.c_str(), mem_id.c_str(), i, width, mem_val); + f << stringf("%s" " %s[%d] <= ", indent.c_str(), mem_id.c_str(), i); + dump_const(f, cell->parameters["\\INIT"].extract(i*width, width)); + f << stringf(";\n"); } f << stringf("%s" "end\n", indent.c_str()); } @@ -884,7 +885,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::ostringstream os; dump_sigspec(os, sig_rd_data); 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[clk_domain_str].push_back(line); + clk_to_lof_body[""].push_back(line); } } else { // for non-clocked read-ports make something like: diff --git a/techlibs/xilinx/example_basys3/README b/examples/basys3/README index 85b6eab10..0ce717294 100644 --- a/techlibs/xilinx/example_basys3/README +++ b/examples/basys3/README @@ -2,6 +2,9 @@ A simple example design, based on the Digilent BASYS3 board =========================================================== +This example uses Yosys for synthesis and Xilinx Vivado +for place&route and bit-stream creation. + Running Yosys: yosys run_yosys.ys diff --git a/techlibs/xilinx/example_basys3/example.v b/examples/basys3/example.v index 2b01a22a8..2b01a22a8 100644 --- a/techlibs/xilinx/example_basys3/example.v +++ b/examples/basys3/example.v diff --git a/techlibs/xilinx/example_basys3/example.xdc b/examples/basys3/example.xdc index c1fd0e925..c1fd0e925 100644 --- a/techlibs/xilinx/example_basys3/example.xdc +++ b/examples/basys3/example.xdc diff --git a/techlibs/xilinx/example_basys3/run.sh b/examples/basys3/run.sh index 10f059103..10f059103 100644 --- a/techlibs/xilinx/example_basys3/run.sh +++ b/examples/basys3/run.sh diff --git a/techlibs/xilinx/example_basys3/run_prog.tcl b/examples/basys3/run_prog.tcl index d711af840..d711af840 100644 --- a/techlibs/xilinx/example_basys3/run_prog.tcl +++ b/examples/basys3/run_prog.tcl diff --git a/techlibs/xilinx/example_basys3/run_vivado.tcl b/examples/basys3/run_vivado.tcl index c3b6a610e..c3b6a610e 100644 --- a/techlibs/xilinx/example_basys3/run_vivado.tcl +++ b/examples/basys3/run_vivado.tcl diff --git a/techlibs/xilinx/example_basys3/run_yosys.ys b/examples/basys3/run_yosys.ys index 4541826d3..4541826d3 100644 --- a/techlibs/xilinx/example_basys3/run_yosys.ys +++ b/examples/basys3/run_yosys.ys diff --git a/techlibs/cmos/cmos_cells.lib b/examples/cmos/cmos_cells.lib index 1b0bf8457..1b0bf8457 100644 --- a/techlibs/cmos/cmos_cells.lib +++ b/examples/cmos/cmos_cells.lib diff --git a/techlibs/cmos/cmos_cells.sp b/examples/cmos/cmos_cells.sp index 673b20d08..673b20d08 100644 --- a/techlibs/cmos/cmos_cells.sp +++ b/examples/cmos/cmos_cells.sp diff --git a/techlibs/cmos/cmos_cells.v b/examples/cmos/cmos_cells.v index 27278facb..27278facb 100644 --- a/techlibs/cmos/cmos_cells.v +++ b/examples/cmos/cmos_cells.v diff --git a/techlibs/cmos/counter.v b/examples/cmos/counter.v index f21658724..f21658724 100644 --- a/techlibs/cmos/counter.v +++ b/examples/cmos/counter.v diff --git a/techlibs/cmos/counter.ys b/examples/cmos/counter.ys index a784f3465..a784f3465 100644 --- a/techlibs/cmos/counter.ys +++ b/examples/cmos/counter.ys diff --git a/techlibs/cmos/testbench.sh b/examples/cmos/testbench.sh index 061704b64..061704b64 100644 --- a/techlibs/cmos/testbench.sh +++ b/examples/cmos/testbench.sh diff --git a/techlibs/cmos/testbench.sp b/examples/cmos/testbench.sp index 95d2f67cd..95d2f67cd 100644 --- a/techlibs/cmos/testbench.sp +++ b/examples/cmos/testbench.sp diff --git a/misc/example.cc b/examples/cxx-api/demomain.cc index 2e35bcd46..a64593306 100644 --- a/misc/example.cc +++ b/examples/cxx-api/demomain.cc @@ -1,5 +1,5 @@ // Note: Set ENABLE_LIBYOSYS=1 in Makefile or Makefile.conf to build libyosys.so -// yosys-config --exec --cxx -o example --cxxflags --ldflags example.cc -lyosys -lstdc++ +// yosys-config --exec --cxx -o demomain --cxxflags --ldflags demomain.cc -lyosys -lstdc++ #include <kernel/yosys.h> diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index a45859157..3e163bae7 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -699,7 +699,7 @@ AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signe for (size_t i = 0; i < 32; i++) { if (i < node->bits.size()) node->integer |= (node->bits[i] == RTLIL::S1) << i; - else if (is_signed) + else if (is_signed && !node->bits.empty()) node->integer |= (node->bits.back() == RTLIL::S1) << i; } node->range_valid = true; @@ -831,7 +831,7 @@ double AstNode::asReal(bool is_signed) { RTLIL::Const val(bits); - bool is_negative = is_signed && val.bits.back() == RTLIL::State::S1; + bool is_negative = is_signed && !val.bits.empty() && val.bits.back() == RTLIL::State::S1; if (is_negative) val = const_neg(val, val, false, false, val.bits.size()); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index a2655e9a5..87e8379e9 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -520,6 +520,11 @@ struct AST_INTERNAL::ProcessGenerator log_error("Found wire declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum); break; + case AST_PARAMETER: + case AST_LOCALPARAM: + log_error("Found parameter declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum); + break; + case AST_TCALL: case AST_FOR: break; @@ -547,14 +552,14 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun switch (type) { case AST_CONSTANT: - width_hint = std::max(width_hint, int(bits.size())); + width_hint = max(width_hint, int(bits.size())); if (!is_signed) sign_hint = false; break; case AST_REALVALUE: *found_real = true; - width_hint = std::max(width_hint, 32); + width_hint = max(width_hint, 32); break; case AST_IDENTIFIER: @@ -617,7 +622,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun this_width = range->range_left - range->range_right + 1; sign_hint = false; } - width_hint = std::max(width_hint, this_width); + width_hint = max(width_hint, this_width); if (!id_ast->is_signed) sign_hint = false; break; @@ -627,7 +632,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (children[0]->type != AST_CONSTANT) log_error("Left operand of tobits expression is not constant at %s:%d!\n", filename.c_str(), linenum); children[1]->detectSignWidthWorker(sub_width_hint, sign_hint); - width_hint = std::max(width_hint, children[0]->bitsAsConst().as_int()); + width_hint = max(width_hint, children[0]->bitsAsConst().as_int()); break; case AST_TO_SIGNED: @@ -646,7 +651,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun child->detectSignWidthWorker(sub_width_hint, sub_sign_hint); this_width += sub_width_hint; } - width_hint = std::max(width_hint, this_width); + width_hint = max(width_hint, this_width); sign_hint = false; break; @@ -655,7 +660,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (children[0]->type != AST_CONSTANT) log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum); children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint); - width_hint = std::max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint); + width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint); sign_hint = false; break; @@ -678,7 +683,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_REDUCE_XOR: case AST_REDUCE_XNOR: case AST_REDUCE_BOOL: - width_hint = std::max(width_hint, 1); + width_hint = max(width_hint, 1); sign_hint = false; break; @@ -698,7 +703,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_NEX: case AST_GE: case AST_GT: - width_hint = std::max(width_hint, 1); + width_hint = max(width_hint, 1); sign_hint = false; break; @@ -714,7 +719,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_LOGIC_AND: case AST_LOGIC_OR: case AST_LOGIC_NOT: - width_hint = std::max(width_hint, 1); + width_hint = max(width_hint, 1); sign_hint = false; break; @@ -729,7 +734,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (!id2ast->children[0]->range_valid) log_error("Failed to detect with of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1; - width_hint = std::max(width_hint, this_width); + width_hint = max(width_hint, this_width); break; // everything should have been handled above -> print error if not. @@ -1054,7 +1059,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) detectSignWidth(width_hint, sign_hint); RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint); - int width = std::max(left.size(), right.size()); + int width = max(left.size(), right.size()); if (width_hint > 0) width = width_hint; is_signed = children[0]->is_signed && children[1]->is_signed; @@ -1068,7 +1073,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (0) { case AST_REDUCE_XNOR: type_name = "$reduce_xnor"; } { RTLIL::SigSpec arg = children[0]->genRTLIL(); - RTLIL::SigSpec sig = uniop2rtlil(this, type_name, std::max(width_hint, 1), arg); + RTLIL::SigSpec sig = uniop2rtlil(this, type_name, max(width_hint, 1), arg); return sig; } @@ -1077,7 +1082,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (0) { case AST_REDUCE_BOOL: type_name = "$reduce_bool"; } { RTLIL::SigSpec arg = children[0]->genRTLIL(); - RTLIL::SigSpec sig = arg.size() > 1 ? uniop2rtlil(this, type_name, std::max(width_hint, 1), arg) : arg; + RTLIL::SigSpec sig = arg.size() > 1 ? uniop2rtlil(this, type_name, max(width_hint, 1), arg) : arg; return sig; } @@ -1123,7 +1128,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (0) { case AST_GE: type_name = "$ge"; } if (0) { case AST_GT: type_name = "$gt"; } { - int width = std::max(width_hint, 1); + int width = max(width_hint, 1); width_hint = -1, sign_hint = true; children[0]->detectSignWidthWorker(width_hint, sign_hint); children[1]->detectSignWidthWorker(width_hint, sign_hint); @@ -1145,7 +1150,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint); #if 0 - int width = std::max(left.size(), right.size()); + int width = max(left.size(), right.size()); if (width > width_hint && width_hint > 0) width = width_hint; if (width < width_hint) { @@ -1154,10 +1159,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (type == AST_SUB && (!children[0]->is_signed || !children[1]->is_signed)) width = width_hint; if (type == AST_MUL) - width = std::min(left.size() + right.size(), width_hint); + width = min(left.size() + right.size(), width_hint); } #else - int width = std::max(std::max(left.size(), right.size()), width_hint); + int width = max(max(left.size(), right.size()), width_hint); #endif is_signed = children[0]->is_signed && children[1]->is_signed; return binop2rtlil(this, type_name, width, left, right); @@ -1169,14 +1174,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { RTLIL::SigSpec left = children[0]->genRTLIL(); RTLIL::SigSpec right = children[1]->genRTLIL(); - return binop2rtlil(this, type_name, std::max(width_hint, 1), left, right); + return binop2rtlil(this, type_name, max(width_hint, 1), left, right); } // generate cells for unary operations: $logic_not case AST_LOGIC_NOT: { RTLIL::SigSpec arg = children[0]->genRTLIL(); - return uniop2rtlil(this, "$logic_not", std::max(width_hint, 1), arg); + return uniop2rtlil(this, "$logic_not", max(width_hint, 1), arg); } // generate multiplexer for ternary operator (aka ?:-operator) @@ -1192,7 +1197,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (cond.size() > 1) cond = uniop2rtlil(this, "$reduce_bool", 1, cond, false); - int width = std::max(val1.size(), val2.size()); + int width = max(val1.size(), val2.size()); is_signed = children[1]->is_signed && children[2]->is_signed; widthExtend(this, val1, width, is_signed); widthExtend(this, val2, width, is_signed); @@ -1220,6 +1225,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) id2ast->meminfo(mem_width, mem_size, addr_bits); cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); + cell->setPort("\\EN", RTLIL::SigSpec(RTLIL::State::Sx, 1)); cell->setPort("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); cell->setPort("\\DATA", RTLIL::SigSpec(wire)); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 5eb173c9a..2264d896a 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -174,13 +174,113 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } // deactivate all calls to non-synthesis system tasks - if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$strobe" || str == "$monitor" || str == "$time" || str == "$stop" || str == "$finish" || + // note that $display and $finish are used for synthesis-time DRC so they're not in this list + if ((type == AST_FCALL || type == AST_TCALL) && (str == "$strobe" || str == "$monitor" || str == "$time" || str == "$stop" || str == "$dumpfile" || str == "$dumpvars" || str == "$dumpon" || str == "$dumpoff" || str == "$dumpall")) { log_warning("Ignoring call to system %s %s at %s:%d.\n", type == AST_FCALL ? "function" : "task", str.c_str(), filename.c_str(), linenum); delete_children(); str = std::string(); } + if ((type == AST_TCALL) && (str == "$display" || str == "$write") && (!current_always || current_always->type != AST_INITIAL)) { + log_warning("System task `%s' outside initial block is unsupported at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + delete_children(); + str = std::string(); + } + + // print messages if this a call to $display() or $write() + // This code implements only a small subset of Verilog-2005 $display() format specifiers, + // but should be good enough for most uses + if ((type == AST_TCALL) && ((str == "$display") || (str == "$write"))) + { + size_t nargs = GetSize(children); + if(nargs < 1) + log_error("System task `%s' got %d arguments, expected >= 1 at %s:%d.\n", + str.c_str(), int(children.size()), filename.c_str(), linenum); + + // First argument is the format string + AstNode *node_string = children[0]->clone(); + while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_string->type != AST_CONSTANT) + log_error("Failed to evaluate system task `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + std::string sformat = node_string->bitsAsConst().decode_string(); + + // Other arguments are placeholders. Process the string as we go through it + std::string sout; + size_t next_arg = 1; + for(size_t i=0; i<sformat.length(); i++) + { + // format specifier + if(sformat[i] == '%') + { + // If there's no next character, that's a problem + if(i+1 >= sformat.length()) + log_error("System task `%s' called with `%%' at end of string at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + char cformat = sformat[++i]; + + // %% is special, does not need a matching argument + if(cformat == '%') + { + sout += '%'; + continue; + } + + // If we're out of arguments, that's a problem! + if(next_arg >= nargs) + log_error("System task `%s' called with more format specifiers than arguments at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + // Simplify the argument + AstNode *node_arg = children[next_arg ++]->clone(); + while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_arg->type != AST_CONSTANT) + log_error("Failed to evaluate system task `%s' with non-constant argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + // Everything from here on depends on the format specifier + switch(cformat) + { + case 's': + case 'S': + sout += node_arg->bitsAsConst().decode_string(); + break; + + case 'd': + case 'D': + { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int()); + sout += tmp; + } + break; + + case 'x': + case 'X': + { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int()); + sout += tmp; + } + break; + + default: + log_error("System task `%s' called with invalid format specifier at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + break; + } + } + + // not a format specifier + else + sout += sformat[i]; + } + + // Finally, print the message (only include a \n for $display, not for $write) + log("%s", sout.c_str()); + if(str == "$display") + log("\n"); + delete_children(); + str = std::string(); + } + // activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.) if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX) const_fold = true; @@ -298,7 +398,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; children[0]->detectSignWidth(backup_width_hint, backup_sign_hint); children[1]->detectSignWidth(width_hint, sign_hint); - width_hint = std::max(width_hint, backup_width_hint); + width_hint = max(width_hint, backup_width_hint); child_0_is_self_determined = true; break; @@ -312,7 +412,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; if (!children[1]->range_valid) log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum); - width_hint = std::max(width_hint, children[1]->range_left - children[1]->range_right + 1); + width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1); } break; @@ -552,6 +652,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // dumpAst(NULL, "> "); log_error("Index in generate block prefix syntax at %s:%d is not constant!\n", filename.c_str(), linenum); } + if (children[1]->type == AST_PREFIX) + children[1]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param); log_assert(children[1]->type == AST_IDENTIFIER); newNode = children[1]->clone(); const char *second_part = children[1]->str.c_str(); @@ -631,8 +733,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, for (auto range : children[1]->children) { if (!range->range_valid) log_error("Non-constant range on memory decl at %s:%d.\n", filename.c_str(), linenum); - multirange_dimensions.push_back(std::min(range->range_left, range->range_right)); - multirange_dimensions.push_back(std::max(range->range_left, range->range_right) - std::min(range->range_left, range->range_right) + 1); + multirange_dimensions.push_back(min(range->range_left, range->range_right)); + multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1); total_size *= multirange_dimensions.back(); } delete children[1]; @@ -658,10 +760,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (i == 0) index_expr = new_index_expr; else - index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i-1], true)), new_index_expr); + index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i+1], true)), new_index_expr); } - for (int i = GetSize(id2ast->multirange_dimensions)/1; i < GetSize(children[0]->children); i++) + for (int i = GetSize(id2ast->multirange_dimensions)/2; i < GetSize(children[0]->children); i++) children.push_back(children[0]->children[i]->clone()); delete children[0]; @@ -911,7 +1013,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, std::vector<AstNode*> new_children; for (size_t i = 0; i < children.size(); i++) - if (children[i]->type == AST_WIRE) { + if (children[i]->type == AST_WIRE || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) { children[i]->simplify(false, false, false, stage, -1, false, false); current_ast_mod->children.push_back(children[i]); current_scope[children[i]->str] = children[i]; @@ -1067,7 +1169,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, log_error("Non-constant array range on cell array at %s:%d.\n", filename.c_str(), linenum); newNode = new AstNode(AST_GENBLOCK); - int num = std::max(children.at(0)->range_left, children.at(0)->range_right) - std::min(children.at(0)->range_left, children.at(0)->range_right) + 1; + int num = max(children.at(0)->range_left, children.at(0)->range_right) - min(children.at(0)->range_left, children.at(0)->range_right) + 1; for (int i = 0; i < num; i++) { int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i; @@ -1274,7 +1376,7 @@ skip_dynamic_range_lvalue_expansion:; // found right-hand side identifier for memory -> replace with memory read port if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type == AST_MEMORY && !in_lvalue && - children[0]->type == AST_RANGE && children[0]->children.size() == 1) { + children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) { newNode = new AstNode(AST_MEMRD, children[0]->children[0]->clone()); newNode->str = str; newNode->id2ast = id2ast; @@ -1569,7 +1671,17 @@ skip_dynamic_range_lvalue_expansion:; if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION) log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum); } - if (type == AST_TCALL) { + + if (type == AST_TCALL) + { + if (str == "$finish") + { + if (!current_always || current_always->type != AST_INITIAL) + log_error("System task `$finish' outside initial block is unsupported at %s:%d.\n", filename.c_str(), linenum); + + log_error("System task `$finish' executed at %s:%d.\n", filename.c_str(), linenum); + } + if (str == "\\$readmemh" || str == "\\$readmemb") { if (GetSize(children) < 2 || GetSize(children) > 4) @@ -1606,12 +1718,19 @@ skip_dynamic_range_lvalue_expansion:; bool unconditional_init = false; if (current_always->type == AST_INITIAL) { + pool<AstNode*> queue; log_assert(current_always->children[0]->type == AST_BLOCK); - for (auto n : current_always->children[0]->children) - if (n == this) { - unconditional_init = true; - break; + queue.insert(current_always->children[0]); + while (!unconditional_init && !queue.empty()) { + pool<AstNode*> next_queue; + for (auto n : queue) + for (auto c : n->children) { + if (c == this) + unconditional_init = true; + next_queue.insert(c); } + next_queue.swap(queue); + } } newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init); @@ -1654,6 +1773,8 @@ skip_dynamic_range_lvalue_expansion:; size_t arg_count = 0; std::map<std::string, std::string> replace_rules; + vector<AstNode*> added_mod_children; + dict<std::string, AstNode*> wire_cache; if (current_block == NULL) { @@ -1746,17 +1867,39 @@ skip_dynamic_range_lvalue_expansion:; } for (auto child : decl->children) - if (child->type == AST_WIRE) + if (child->type == AST_WIRE || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) { - AstNode *wire = child->clone(); - wire->str = prefix + wire->str; - wire->port_id = 0; - wire->is_input = false; - wire->is_output = false; - current_ast_mod->children.push_back(wire); - while (wire->simplify(true, false, false, 1, -1, false, false)) { } + AstNode *wire = nullptr; + + if (wire_cache.count(child->str)) + { + wire = wire_cache.at(child->str); + if (wire->children.empty()) { + for (auto c : child->children) + wire->children.push_back(c->clone()); + } else { + if (!child->children.empty()) + log_error("Incompatible re-declaration of wire %s at %s:%d.\n", child->str.c_str(), filename.c_str(), linenum); + } + } + else + { + wire = child->clone(); + wire->str = prefix + wire->str; + wire->port_id = 0; + wire->is_input = false; + wire->is_output = false; + wire_cache[child->str] = wire; + + current_ast_mod->children.push_back(wire); + added_mod_children.push_back(wire); + } + + if (child->type == AST_WIRE) + while (wire->simplify(true, false, false, 1, -1, false, false)) { } replace_rules[child->str] = wire->str; + current_scope[wire->str] = wire; if ((child->is_input || child->is_output) && arg_count < children.size()) { @@ -1776,8 +1919,13 @@ skip_dynamic_range_lvalue_expansion:; } } + for (auto child : added_mod_children) { + child->replace_ids(prefix, replace_rules); + while (child->simplify(true, false, false, 1, -1, false, false)) { } + } + for (auto child : decl->children) - if (child->type != AST_WIRE) + if (child->type != AST_WIRE && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) { AstNode *stmt = child->clone(); stmt->replace_ids(prefix, replace_rules); @@ -1924,7 +2072,7 @@ skip_dynamic_range_lvalue_expansion:; if (0) { case AST_GE: const_func = RTLIL::const_ge; } if (0) { case AST_GT: const_func = RTLIL::const_gt; } if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { - int cmp_width = std::max(children[0]->bits.size(), children[1]->bits.size()); + int cmp_width = max(children[0]->bits.size(), children[1]->bits.size()); bool cmp_signed = children[0]->is_signed && children[1]->is_signed; RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed), children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1); @@ -2099,6 +2247,9 @@ static void replace_result_wire_name_in_function(AstNode *node, std::string &fro // replace a readmem[bh] TCALL ast node with a block of memory assignments AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init) { + int mem_width, mem_size, addr_bits; + memory->meminfo(mem_width, mem_size, addr_bits); + AstNode *block = new AstNode(AST_BLOCK); AstNode *meminit = nullptr; @@ -2114,13 +2265,13 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m log_assert(GetSize(memory->children) == 2 && memory->children[1]->type == AST_RANGE && memory->children[1]->range_valid); int range_left = memory->children[1]->range_left, range_right = memory->children[1]->range_right; - int range_min = std::min(range_left, range_right), range_max = std::max(range_left, range_right); + int range_min = min(range_left, range_right), range_max = max(range_left, range_right); if (start_addr < 0) start_addr = range_min; if (finish_addr < 0) - finish_addr = range_max; + finish_addr = range_max + 1; bool in_comment = false; int increment = start_addr <= finish_addr ? +1 : -1; @@ -2160,7 +2311,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m continue; } - AstNode *value = VERILOG_FRONTEND::const2ast((is_readmemh ? "'h" : "'b") + token); + AstNode *value = VERILOG_FRONTEND::const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token); if (unconditional_init) { @@ -2196,12 +2347,12 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m block->children.back()->children[0]->id2ast = memory; } - if ((cursor == finish_addr) || (increment > 0 && cursor >= range_max) || (increment < 0 && cursor <= range_min)) + if ((cursor == finish_addr) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min)) break; cursor += increment; } - if ((cursor == finish_addr) || (increment > 0 && cursor >= range_max) || (increment < 0 && cursor <= range_min)) + if ((cursor == finish_addr) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min)) break; } @@ -2598,7 +2749,7 @@ void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) if (mem_size < 0) mem_size *= -1; - mem_size += std::min(children[1]->range_left, children[1]->range_right) + 1; + mem_size += min(children[1]->range_left, children[1]->range_right) + 1; addr_bits = 1; while ((1 << addr_bits) < mem_size) @@ -2634,8 +2785,8 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia if (!children.at(0)->range_valid) log_error("Non-constant range in %s:%d (called from %s:%d).\n", filename.c_str(), linenum, fcall->filename.c_str(), fcall->linenum); - offset = std::min(children.at(0)->range_left, children.at(0)->range_right); - width = std::min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); + offset = min(children.at(0)->range_left, children.at(0)->range_right); + width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); } offset -= variables.at(str).offset; std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits; @@ -2675,7 +2826,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) log_error("Can't determine size of variable %s in %s:%d (called from %s:%d).\n", child->str.c_str(), child->filename.c_str(), child->linenum, fcall->filename.c_str(), fcall->linenum); variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1); - variables[child->str].offset = std::min(child->range_left, child->range_right); + variables[child->str].offset = min(child->range_left, child->range_right); variables[child->str].is_signed = child->is_signed; if (child->is_input && argidx < fcall->children.size()) variables[child->str].val = fcall->children.at(argidx++)->bitsAsConst(variables[child->str].val.bits.size()); @@ -2706,6 +2857,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_ASSIGN_EQ) { + if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 && + stmt->children.at(0)->children.at(0)->type == AST_RANGE) + stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall); stmt->children.at(1)->replace_variables(variables, fcall); while (stmt->simplify(true, false, false, 1, -1, false, true)) { } @@ -2731,7 +2885,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (!range->range_valid) log_error("Non-constant range in %s:%d (called from %s:%d).\n", range->filename.c_str(), range->linenum, fcall->filename.c_str(), fcall->linenum); - int offset = std::min(range->range_left, range->range_right); + int offset = min(range->range_left, range->range_right); int width = std::abs(range->range_left - range->range_right) + 1; varinfo_t &v = variables[stmt->children.at(0)->str]; RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size()); diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc index 4bf050741..9f2e08df0 100644 --- a/frontends/blif/blifparse.cc +++ b/frontends/blif/blifparse.cc @@ -133,7 +133,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name) if (v[0] == '"') { std::string str(v+1); if (str.back() == '"') - str.pop_back(); + str.resize(str.size()-1); const_v = Const(str); } else { int n = strlen(v); diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index 6090fabe5..cc31c8642 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -50,6 +50,7 @@ USING_YOSYS_NAMESPACE int integer; YOSYS_NAMESPACE_PREFIX RTLIL::Const *data; YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec *sigspec; + std::vector<YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec> *rsigspec; } %token <string> TOK_ID TOK_VALUE TOK_STRING @@ -60,6 +61,7 @@ USING_YOSYS_NAMESPACE %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 +%type <rsigspec> sigspec_list_reversed %type <sigspec> sigspec sigspec_list %type <integer> sync_type %type <data> constant @@ -121,7 +123,7 @@ attr_stmt: autoidx_stmt: TOK_AUTOIDX TOK_INT EOL { - autoidx = std::max(autoidx, $2); + autoidx = max(autoidx, $2); }; wire_stmt: @@ -274,8 +276,8 @@ compare_list: /* empty */; case_body: - switch_stmt case_body | - assign_stmt case_body | + case_body switch_stmt | + case_body assign_stmt | /* empty */; assign_stmt: @@ -389,16 +391,20 @@ sigspec: $$ = $2; }; -sigspec_list: - sigspec_list sigspec { - $$ = new RTLIL::SigSpec; - $$->append(*$2); - $$->append(*$1); - delete $1; +sigspec_list_reversed: + sigspec_list_reversed sigspec { + $$->push_back(*$2); delete $2; } | /* empty */ { + $$ = new std::vector<RTLIL::SigSpec>; + }; + +sigspec_list: sigspec_list_reversed { $$ = new RTLIL::SigSpec; + for (auto it = $1->rbegin(); it != $1->rend(); it++) + $$->append(*it); + delete $1; }; conn_stmt: diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc index 13f242c4b..68ef9aed1 100644 --- a/frontends/verific/Makefile.inc +++ b/frontends/verific/Makefile.inc @@ -8,8 +8,9 @@ EXTRA_TARGETS += share/verific share/verific: $(P) rm -rf share/verific.new $(Q) mkdir -p share/verific.new - $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs share/verific.new/vhdl_vdbs_1993 - $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008 share/verific.new/vhdl_vdbs_2008 + $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987 + $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993 + $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008 $(Q) mv share/verific.new share/verific endif diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 897a7f328..45cd4f3fc 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -314,6 +314,16 @@ static bool import_netlist_instance_cells(RTLIL::Module *module, std::map<Net*, return true; } + if (inst->Type() == PRIM_DLATCHRS) + { + if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd()) + module->addDlatch(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetControl()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput())); + else + module->addDlatchsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetControl()), net_map.at(inst->GetSet()), net_map.at(inst->GetReset()), + net_map.at(inst->GetInput()), net_map.at(inst->GetOutput())); + return true; + } + #define IN operatorInput(inst, net_map) #define IN1 operatorInput1(inst, net_map) #define IN2 operatorInput2(inst, net_map) @@ -541,7 +551,7 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* // log(" importing portbus %s.\n", portbus->Name()); RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(portbus->Name()), portbus->Size()); - wire->start_offset = std::min(portbus->LeftIndex(), portbus->RightIndex()); + wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex()); import_attributes(wire->attributes, portbus); if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN) @@ -580,11 +590,11 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* int bits_in_word = number_of_bits; FOREACH_PORTREF_OF_NET(net, si, pr) { if (pr->GetInst()->Type() == OPER_READ_PORT) { - bits_in_word = std::min<int>(bits_in_word, pr->GetInst()->OutputSize()); + bits_in_word = min<int>(bits_in_word, pr->GetInst()->OutputSize()); continue; } if (pr->GetInst()->Type() == OPER_WRITE_PORT || pr->GetInst()->Type() == OPER_CLOCKED_WRITE_PORT) { - bits_in_word = std::min<int>(bits_in_word, pr->GetInst()->Input2Size()); + bits_in_word = min<int>(bits_in_word, pr->GetInst()->Input2Size()); continue; } log_error("Verific RamNet %s is connected to unsupported instance type %s (%s).\n", @@ -630,7 +640,7 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* RTLIL::IdString wire_name = module->uniquify(RTLIL::escape_id(netbus->Name())); RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size()); - wire->start_offset = std::min(netbus->LeftIndex(), netbus->RightIndex()); + wire->start_offset = min(netbus->LeftIndex(), netbus->RightIndex()); import_attributes(wire->attributes, netbus); for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) { @@ -692,7 +702,8 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* cell->parameters["\\TRANSPARENT"] = false; cell->parameters["\\ABITS"] = GetSize(addr); cell->parameters["\\WIDTH"] = GetSize(data); - cell->setPort("\\CLK", RTLIL::State::S0); + cell->setPort("\\CLK", RTLIL::State::Sx); + cell->setPort("\\EN", RTLIL::State::Sx); cell->setPort("\\ADDR", addr); cell->setPort("\\DATA", data); continue; @@ -744,6 +755,8 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), inst->IsOperator() ? std::string("$verific$") + inst->View()->Owner()->Name() : RTLIL::escape_id(inst->View()->Owner()->Name())); + dict<IdString, vector<SigBit>> cell_port_conns; + FOREACH_PORTREF_OF_INST(inst, mi2, pr) { // log(" .%s(%s)\n", pr->GetPort()->Name(), pr->GetNet()->Name()); const char *port_name = pr->GetPort()->Name(); @@ -751,18 +764,18 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* if (pr->GetPort()->Bus()) { port_name = pr->GetPort()->Bus()->Name(); port_offset = pr->GetPort()->Bus()->IndexOf(pr->GetPort()) - - std::min(pr->GetPort()->Bus()->LeftIndex(), pr->GetPort()->Bus()->RightIndex()); - } - RTLIL::SigSpec conn; - if (cell->hasPort(RTLIL::escape_id(port_name))) - conn = cell->getPort(RTLIL::escape_id(port_name)); - while (GetSize(conn) <= port_offset) { - if (pr->GetPort()->GetDir() != DIR_IN) - conn.append(module->addWire(NEW_ID, port_offset - GetSize(conn))); - conn.append(RTLIL::State::Sz); + min(pr->GetPort()->Bus()->LeftIndex(), pr->GetPort()->Bus()->RightIndex()); } - conn.replace(port_offset, net_map.at(pr->GetNet())); - cell->setPort(RTLIL::escape_id(port_name), conn); + IdString port_name_id = RTLIL::escape_id(port_name); + auto &sigvec = cell_port_conns[port_name_id]; + if (GetSize(sigvec) <= port_offset) + sigvec.resize(port_offset+1, State::Sz); + sigvec[port_offset] = net_map.at(pr->GetNet()); + } + + for (auto &it : cell_port_conns) { + // log(" .%s(%s)\n", log_id(it.first), log_signal(it.second)); + cell->setPort(it.first, it.second); } } } @@ -840,7 +853,7 @@ struct VerificPass : public Pass { } if (args.size() > 1 && args[1] == "-vhdl87") { - vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str()); + vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str()); for (size_t argidx = 2; argidx < args.size(); argidx++) if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_87)) log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", args[argidx].c_str()); @@ -917,10 +930,12 @@ struct VerificPass : public Pass { for (; argidx < args.size(); argidx++) { if (veri_file::GetModule(args[argidx].c_str())) { + log("Running veri_file::Elaborate(\"%s\").\n", args[argidx].c_str()); if (!veri_file::Elaborate(args[argidx].c_str())) log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str()); nl_todo.insert(Netlist::PresentDesign()); } else { + log("Running vhdl_file::Elaborate(\"%s\").\n", args[argidx].c_str()); if (!vhdl_file::Elaborate(args[argidx].c_str())) log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str()); nl_todo.insert(Netlist::PresentDesign()); diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index ebecb92f2..4a58357bf 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -96,44 +96,54 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le if (base == 10 && GetSize(digits) == 1 && digits.front() >= 0xf0) base = 2; + data.clear(); + if (base == 10) { - data.clear(); - if (len_in_bits < 0) { - while (!digits.empty()) - data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); - while (data.size() < 32) - data.push_back(RTLIL::S0); - } else { - for (int i = 0; i < len_in_bits; i++) - data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + while (!digits.empty()) + data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + } else { + int bits_per_digit = my_ilog2(base-1); + for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) { + if (*it > (base-1) && *it < 0xf0) + log_error("Digit larger than %d used in in base-%d constant at %s:%d.\n", + base-1, base, current_filename.c_str(), get_line_num()); + for (int i = 0; i < bits_per_digit; i++) { + int bitmask = 1 << i; + if (*it == 0xf0) + data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx); + else if (*it == 0xf1) + data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz); + else if (*it == 0xf2) + data.push_back(RTLIL::Sa); + else + data.push_back((*it & bitmask) ? RTLIL::S1 : RTLIL::S0); + } } - return; } - int bits_per_digit = my_ilog2(base-1); - if (len_in_bits < 0) - len_in_bits = std::max<int>(digits.size() * bits_per_digit, 32); + int len = GetSize(data); + RTLIL::State msb = data.empty() ? RTLIL::S0 : data.back(); - data.clear(); - data.resize(len_in_bits); - - for (int i = 0; i < len_in_bits; i++) { - int bitmask = 1 << (i % bits_per_digit); - int digitidx = digits.size() - (i / bits_per_digit) - 1; - if (digitidx < 0) { - if (i > 0 && (data[i-1] == RTLIL::Sz || data[i-1] == RTLIL::Sx || data[i-1] == RTLIL::Sa)) - data[i] = data[i-1]; - else - data[i] = RTLIL::S0; - } else if (digits[digitidx] == 0xf0) - data[i] = case_type == 'x' ? RTLIL::Sa : RTLIL::Sx; - else if (digits[digitidx] == 0xf1) - data[i] = case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz; - else if (digits[digitidx] == 0xf2) - data[i] = RTLIL::Sa; - else - data[i] = (digits[digitidx] & bitmask) ? RTLIL::S1 : RTLIL::S0; + if (len_in_bits < 0) { + if (len < 32) + data.resize(32, msb == RTLIL::S0 || msb == RTLIL::S1 ? RTLIL::S0 : msb); + return; + } + + for (len = len - 1; len >= 0; len--) + if (data[len] == RTLIL::S1) + break; + if (msb == RTLIL::S0 || msb == RTLIL::S1) { + len += 1; + data.resize(len_in_bits, RTLIL::S0); + } else { + len += 2; + data.resize(len_in_bits, msb); } + + if (len > len_in_bits) + log_warning("Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n", + len_in_bits, len, current_filename.c_str(), get_line_num()); } // convert the Verilog code for a constant to an AST node @@ -142,7 +152,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn if (warn_z) { AstNode *ret = const2ast(code, case_type); if (std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end()) - log_warning("Yosys does not support tri-state logic at the moment. (%s:%d)\n", + log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n", current_filename.c_str(), get_line_num()); return ret; } @@ -220,8 +230,6 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn if (len_in_bits < 0) { if (is_signed && data.back() == RTLIL::S1) data.push_back(RTLIL::S0); - while (data.size() < 32) - data.push_back(RTLIL::S0); } return AstNode::mkconst_bits(data, is_signed); } diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index fb8a7b95f..997920b89 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -39,6 +39,7 @@ #include <string.h> YOSYS_NAMESPACE_BEGIN +using namespace VERILOG_FRONTEND; static std::list<std::string> output_code; static std::list<std::string> input_buffer; @@ -222,7 +223,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons input_file(f, filename); defines_map["YOSYS"] = "1"; - defines_map["SYNTHESIS"] = "1"; + defines_map[formal_mode ? "FORMAL" : "SYNTHESIS"] = "1"; while (!input_buffer.empty()) { diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 727ee3d1c..cd8b586c4 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -39,6 +39,14 @@ using namespace VERILOG_FRONTEND; static std::vector<std::string> verilog_defaults; static std::list<std::vector<std::string>> verilog_defaults_stack; +static void error_on_dpi_function(AST::AstNode *node) +{ + if (node->type == AST::AST_DPI_FUNCTION) + log_error("Found DPI function %s at %s:%d.\n", node->str.c_str(), node->filename.c_str(), node->linenum); + for (auto child : node->children) + error_on_dpi_function(child); +} + struct VerilogFrontend : public Frontend { VerilogFrontend() : Frontend("verilog", "read modules from Verilog file") { } virtual void help() @@ -55,8 +63,8 @@ struct VerilogFrontend : public Frontend { log(" of SystemVerilog is supported)\n"); log("\n"); log(" -formal\n"); - log(" enable support for assert() and assume() statements\n"); - log(" (assert support is also enabled with -sv)\n"); + log(" enable support for assert() and assume() from SystemVerilog\n"); + log(" replace the implicit -D SYNTHESIS with -D FORMAL\n"); log("\n"); log(" -dump_ast1\n"); log(" dump abstract syntax tree (before simplification)\n"); @@ -107,6 +115,9 @@ struct VerilogFrontend : public Frontend { log(" -nopp\n"); log(" do not run the pre-processor\n"); log("\n"); + log(" -nodpi\n"); + log(" disable DPI-C support\n"); + log("\n"); log(" -lib\n"); log(" only create empty blackbox modules. This implies -DBLACKBOX.\n"); log("\n"); @@ -160,6 +171,7 @@ struct VerilogFrontend : public Frontend { bool flag_mem2reg = false; bool flag_ppdump = false; bool flag_nopp = false; + bool flag_nodpi = false; bool flag_lib = false; bool flag_noopt = false; bool flag_icells = false; @@ -229,6 +241,10 @@ struct VerilogFrontend : public Frontend { flag_nopp = true; continue; } + if (arg == "-nodpi") { + flag_nodpi = true; + continue; + } if (arg == "-lib") { flag_lib = true; defines_map["BLACKBOX"] = string(); @@ -320,6 +336,9 @@ struct VerilogFrontend : public Frontend { child->attributes[attr] = AST::AstNode::mkconst_int(1, false); } + if (flag_nodpi) + error_on_dpi_function(current_ast); + AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_ignore_redef, flag_defer, default_nettype_wire); if (!flag_nopp) diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index a72593583..69a8ddaad 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -170,7 +170,7 @@ YOSYS_NAMESPACE_END "always_latch" { SV_KEYWORD(TOK_ALWAYS); } "assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); } -"assume" { if (formal_mode) return TOK_ASSUME; return TOK_ID; } +"assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); } "property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); } "logic" { SV_KEYWORD(TOK_REG); } "bit" { SV_KEYWORD(TOK_REG); } @@ -248,7 +248,7 @@ and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { supply0 { return TOK_SUPPLY0; } supply1 { return TOK_SUPPLY1; } -"$"(display|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { +"$"(display|write|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { frontend_verilog_yylval.string = new std::string(yytext); return TOK_ID; } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 708ac7627..863fee599 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -153,6 +153,8 @@ design: module design | defattr design | task_func_decl design | + param_decl design | + localparam_decl design | /* empty */; attr: @@ -708,6 +710,8 @@ wire_name_and_opt_assign: wire_name: TOK_ID range_or_multirange { + if (astbuf1 == nullptr) + frontend_verilog_yyerror("Syntax error."); AstNode *node = astbuf1->clone(); node->str = *$1; append_attr_clone(node, albuf); @@ -755,7 +759,7 @@ assign_expr_list: assign_expr | assign_expr_list ',' assign_expr; assign_expr: - expr '=' expr { + lvalue '=' expr { ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3)); }; @@ -959,7 +963,7 @@ simple_behavioral_stmt: // this production creates the obligatory if-else shift/reduce conflict behavioral_stmt: - defattr | assert | wire_decl | + defattr | assert | wire_decl | param_decl | localparam_decl | non_opt_delay behavioral_stmt | simple_behavioral_stmt ';' | ';' | hierarchical_id attr { diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index 288571d99..894a95ed1 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -30,7 +30,7 @@ struct BitPatternPool int width; struct bits_t { std::vector<RTLIL::State> bitdata; - unsigned int cached_hash; + mutable unsigned int cached_hash; bits_t(int width = 0) : bitdata(width), cached_hash(0) { } RTLIL::State &operator[](int index) { return bitdata[index]; @@ -45,7 +45,7 @@ struct BitPatternPool } unsigned int hash() const { if (!cached_hash) - ((bits_t*)this)->cached_hash = hash_ops<std::vector<RTLIL::State>>::hash(bitdata); + cached_hash = hash_ops<std::vector<RTLIL::State>>::hash(bitdata); return cached_hash; } }; diff --git a/kernel/calc.cc b/kernel/calc.cc index 32c06c189..a24fa2abf 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -154,7 +154,7 @@ static RTLIL::Const logic_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL: RTLIL::Const arg1, RTLIL::Const arg2, bool signed1, bool signed2, int result_len = -1) { if (result_len < 0) - result_len = std::max(arg1.bits.size(), arg2.bits.size()); + result_len = max(arg1.bits.size(), arg2.bits.size()); extend_u0(arg1, result_len, signed1); extend_u0(arg2, result_len, signed2); @@ -310,7 +310,7 @@ RTLIL::Const RTLIL::const_shl(const RTLIL::Const &arg1, const RTLIL::Const &arg2 RTLIL::Const RTLIL::const_shr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len) { RTLIL::Const arg1_ext = arg1; - extend_u0(arg1_ext, std::max(result_len, GetSize(arg1)), signed1); + extend_u0(arg1_ext, max(result_len, GetSize(arg1)), signed1); return const_shift_worker(arg1_ext, arg2, false, +1, result_len); } @@ -389,7 +389,7 @@ RTLIL::Const RTLIL::const_eq(const RTLIL::Const &arg1, const RTLIL::Const &arg2, RTLIL::Const arg2_ext = arg2; RTLIL::Const result(RTLIL::State::S0, result_len); - int width = std::max(arg1_ext.bits.size(), arg2_ext.bits.size()); + int width = max(arg1_ext.bits.size(), arg2_ext.bits.size()); extend_u0(arg1_ext, width, signed1 && signed2); extend_u0(arg2_ext, width, signed1 && signed2); @@ -423,7 +423,7 @@ RTLIL::Const RTLIL::const_eqx(const RTLIL::Const &arg1, const RTLIL::Const &arg2 RTLIL::Const arg2_ext = arg2; RTLIL::Const result(RTLIL::State::S0, result_len); - int width = std::max(arg1_ext.bits.size(), arg2_ext.bits.size()); + int width = max(arg1_ext.bits.size(), arg2_ext.bits.size()); extend_u0(arg1_ext, width, signed1 && signed2); extend_u0(arg2_ext, width, signed1 && signed2); @@ -472,21 +472,21 @@ RTLIL::Const RTLIL::const_add(const RTLIL::Const &arg1, const RTLIL::Const &arg2 { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) + const2big(arg2, signed2, undef_bit_pos); - return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); } RTLIL::Const RTLIL::const_sub(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) - const2big(arg2, signed2, undef_bit_pos); - return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); } RTLIL::Const RTLIL::const_mul(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) * const2big(arg2, signed2, undef_bit_pos); - return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); + return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) @@ -499,7 +499,7 @@ RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2 bool result_neg = (a.getSign() == BigInteger::negative) != (b.getSign() == BigInteger::negative); a = a.getSign() == BigInteger::negative ? -a : a; b = b.getSign() == BigInteger::negative ? -b : b; - return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); + return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) @@ -512,7 +512,7 @@ RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2 bool result_neg = a.getSign() == BigInteger::negative; a = a.getSign() == BigInteger::negative ? -a : a; b = b.getSign() == BigInteger::negative ? -b : b; - return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); + return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) @@ -563,7 +563,7 @@ RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2 y *= -1; } - return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); + return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_pos(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index be2e7bbb8..41f81355d 100644 --- a/kernel/cellaigs.cc +++ b/kernel/cellaigs.cc @@ -392,7 +392,7 @@ Aig::Aig(Cell *cell) if (cell->type.in("$eq", "$ne")) { - int width = std::max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B"))); + int width = max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B"))); vector<int> A = mk.inport_vec("\\A", width); vector<int> B = mk.inport_vec("\\B", width); int Y = mk.bool_node(false); diff --git a/kernel/celltypes.h b/kernel/celltypes.h index f36833446..40fdca36e 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -122,7 +122,7 @@ struct CellTypes void setup_internals_mem() { IdString SET = "\\SET", CLR = "\\CLR", CLK = "\\CLK", ARST = "\\ARST", EN = "\\EN"; - IdString Q = "\\Q", D = "\\D", ADDR = "\\ADDR", DATA = "\\DATA"; + IdString Q = "\\Q", D = "\\D", ADDR = "\\ADDR", DATA = "\\DATA", RD_EN = "\\RD_EN"; IdString RD_CLK = "\\RD_CLK", RD_ADDR = "\\RD_ADDR", WR_CLK = "\\WR_CLK", WR_EN = "\\WR_EN"; IdString WR_ADDR = "\\WR_ADDR", WR_DATA = "\\WR_DATA", RD_DATA = "\\RD_DATA"; IdString CTRL_IN = "\\CTRL_IN", CTRL_OUT = "\\CTRL_OUT"; @@ -135,10 +135,10 @@ struct CellTypes setup_type("$dlatch", {EN, D}, {Q}); setup_type("$dlatchsr", {EN, SET, CLR, D}, {Q}); - setup_type("$memrd", {CLK, ADDR}, {DATA}); + setup_type("$memrd", {CLK, EN, ADDR}, {DATA}); setup_type("$memwr", {CLK, EN, ADDR, DATA}, pool<RTLIL::IdString>()); setup_type("$meminit", {ADDR, DATA}, pool<RTLIL::IdString>()); - setup_type("$mem", {RD_CLK, RD_ADDR, WR_CLK, WR_EN, WR_ADDR, WR_DATA}, {RD_DATA}); + setup_type("$mem", {RD_CLK, RD_EN, RD_ADDR, WR_CLK, WR_EN, WR_ADDR, WR_DATA}, {RD_DATA}); setup_type("$fsm", {CLK, ARST, CTRL_IN}, {CTRL_OUT}); } diff --git a/kernel/driver.cc b/kernel/driver.cc index 95835951e..02e332f90 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -404,7 +404,7 @@ int main(int argc, char **argv) log("%s\n", yosys_version_str); int64_t total_ns = 0; - std::set<std::tuple<int64_t, int, std::string>> timedat; + std::set<tuple<int64_t, int, std::string>> timedat; for (auto &it : pass_register) if (it.second->call_counter) { diff --git a/kernel/hashlib.h b/kernel/hashlib.h index f94945eca..f015bf4dd 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -63,18 +63,21 @@ struct hash_int_ops { static inline bool cmp(T a, T b) { return a == b; } +}; + +template<> struct hash_ops<int32_t> : hash_int_ops +{ static inline unsigned int hash(int32_t a) { return a; } +}; +template<> struct hash_ops<int64_t> : hash_int_ops +{ static inline unsigned int hash(int64_t a) { return mkhash(a, a >> 32); } }; -template<> struct hash_ops<int> : hash_int_ops {}; -template<> struct hash_ops<long> : hash_int_ops {}; -template<> struct hash_ops<long long> : hash_int_ops {}; - template<> struct hash_ops<std::string> { static inline bool cmp(const std::string &a, const std::string &b) { return a == b; @@ -118,10 +121,9 @@ template<typename T> struct hash_ops<std::vector<T>> { return a == b; } static inline unsigned int hash(std::vector<T> a) { - hash_ops<T> t_ops; unsigned int h = mkhash_init; for (auto k : a) - h = mkhash(h, t_ops.hash(k)); + h = mkhash(h, hash_ops<T>::hash(k)); return h; } }; @@ -160,6 +162,11 @@ struct hash_obj_ops { } }; +template<typename T> +inline unsigned int mkhash(const T &v) { + return hash_ops<T>().hash(v); +} + inline int hashtable_size(int min_size) { static std::vector<int> zero_and_some_primes = { @@ -188,6 +195,7 @@ inline int hashtable_size(int min_size) template<typename K, typename T, typename OPS = hash_ops<K>> class dict; template<typename K, int offset = 0, typename OPS = hash_ops<K>> class idict; template<typename K, typename OPS = hash_ops<K>> class pool; +template<typename K, typename OPS = hash_ops<K>> class mfp; template<typename K, typename T, typename OPS> class dict @@ -498,6 +506,15 @@ public: return entries[i].udata.second; } + T at(const K &key, const T &defval) const + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i < 0) + return defval; + return entries[i].udata.second; + } + T& operator[](const K &key) { int hash = do_hash(key); @@ -888,6 +905,15 @@ public: return i + offset; } + int at(const K &key, int defval) const + { + int hash = database.do_hash(key); + int i = database.do_lookup(key, hash); + if (i < 0) + return defval; + return i + offset; + } + int count(const K &key) const { int hash = database.do_hash(key); @@ -907,6 +933,108 @@ public: return database.entries.at(index - offset).udata; } + void swap(idict &other) + { + database.swap(other.database); + } + + size_t size() const { return database.size(); } + bool empty() const { return database.empty(); } + void clear() { database.clear(); } + + const_iterator begin() const { return database.begin(); } + const_iterator end() const { return database.end(); } +}; + +template<typename K, typename OPS> +class mfp +{ + mutable idict<K, 0, OPS> database; + mutable std::vector<int> parents; + +public: + typedef typename idict<K, 0, OPS>::const_iterator const_iterator; + + int operator()(const K &key) const + { + int i = database(key); + parents.resize(database.size(), -1); + return i; + } + + const K &operator[](int index) const + { + return database[index]; + } + + int ifind(int i) const + { + int p = i, k = i; + + while (parents[p] != -1) + p = parents[p]; + + while (k != p) { + int next_k = parents[k]; + parents[k] = p; + k = next_k; + } + + return p; + } + + void imerge(int i, int j) + { + i = ifind(i); + j = ifind(j); + + if (i != j) + parents[i] = j; + } + + void ipromote(int i) + { + int k = i; + + while (k != -1) { + int next_k = parents[k]; + parents[k] = i; + k = next_k; + } + + parents[i] = -1; + } + + int lookup(const K &a) const + { + return ifind((*this)(a)); + } + + const K &find(const K &a) const + { + return (*this)[ifind((*this)(a))]; + } + + void merge(const K &a, const K &b) + { + imerge((*this)(a), (*this)(b)); + } + + void promote(const K &a) + { + ipromote((*this)(a)); + } + + void swap(mfp &other) + { + database.swap(other.database); + parents.swap(other.parents); + } + + size_t size() const { return database.size(); } + bool empty() const { return database.empty(); } + void clear() { database.clear(); parents.clear(); } + const_iterator begin() const { return database.begin(); } const_iterator end() const { return database.end(); } }; diff --git a/kernel/log.h b/kernel/log.h index 8cf471465..28baf9886 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -29,6 +29,11 @@ # include <sys/resource.h> #endif +#if defined(_MSC_VER) +// At least this is not in MSVC++ 2013. +# define __PRETTY_FUNCTION__ __FUNCTION__ +#endif + // from libs/sha1/sha1.h class SHA1; diff --git a/kernel/macc.h b/kernel/macc.h index 7efd02281..286ce567f 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -158,8 +158,8 @@ struct Macc int max_size = 0, num_bits = 0; for (auto &port : ports) { - max_size = std::max(max_size, GetSize(port.in_a)); - max_size = std::max(max_size, GetSize(port.in_b)); + max_size = max(max_size, GetSize(port.in_a)); + max_size = max(max_size, GetSize(port.in_b)); } while (max_size) diff --git a/kernel/modtools.h b/kernel/modtools.h index 44c1bde12..1480ec71f 100644 --- a/kernel/modtools.h +++ b/kernel/modtools.h @@ -226,7 +226,7 @@ struct ModIndex : public RTLIL::Monitor auto_reload_module = true; } - ModIndex(RTLIL::Module *_m) : module(_m) + ModIndex(RTLIL::Module *_m) : sigmap(_m), module(_m) { auto_reload_counter = 0; auto_reload_module = true; @@ -274,6 +274,27 @@ struct ModIndex : public RTLIL::Monitor return empty_result_set; return info->ports; } + + void dump_db() + { + log("--- ModIndex Dump ---\n"); + + if (auto_reload_module) { + log("AUTO-RELOAD\n"); + reload_module(); + } + + for (auto &it : database) { + log("BIT %s:\n", log_signal(it.first)); + if (it.second.is_input) + log(" PRIMARY INPUT\n"); + if (it.second.is_output) + log(" PRIMARY OUTPUT\n"); + for (auto &port : it.second.ports) + log(" PORT: %s.%s[%d] (%s)\n", log_id(port.cell), + log_id(port.port), port.offset, log_id(port.cell->type)); + } + } }; struct ModWalker diff --git a/kernel/register.cc b/kernel/register.cc index 179d064fd..49a67324d 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -209,7 +209,7 @@ void Pass::call(RTLIL::Design *design, std::string command) void Pass::call(RTLIL::Design *design, std::vector<std::string> args) { - if (args.size() == 0 || args[0][0] == '#') + if (args.size() == 0 || args[0][0] == '#' || args[0][0] == ':') return; if (echo_mode) { @@ -534,14 +534,28 @@ void Backend::backend_call(RTLIL::Design *design, std::ostream *f, std::string f design->check(); } +static struct CellHelpMessages { + dict<string, string> cell_help, cell_code; + CellHelpMessages() { +#include "techlibs/common/simlib_help.inc" +#include "techlibs/common/simcells_help.inc" + cell_help.sort(); + cell_code.sort(); + } +} cell_help_messages; + struct HelpPass : public Pass { HelpPass() : Pass("help", "display help messages") { } virtual void help() { log("\n"); - log(" help ............. list all commands\n"); - log(" help <command> ... print help message for given command\n"); - log(" help -all ........ print complete command reference\n"); + log(" help ................ list all commands\n"); + log(" help <command> ...... print help message for given command\n"); + log(" help -all ........... print complete command reference\n"); + log("\n"); + log(" help -cells .......... list all cell types\n"); + log(" help <celltype> ..... print help message for given cell type\n"); + log(" help <celltype>+ .... print verilog code for given cell type\n"); log("\n"); } void escape_tex(std::string &tex) @@ -609,6 +623,7 @@ struct HelpPass : public Pass { log(" %-20s %s\n", it.first.c_str(), it.second->short_help.c_str()); log("\n"); log("Type 'help <command>' for more information on a command.\n"); + log("Type 'help -cells' for a list of all cell types.\n"); log("\n"); return; } @@ -624,6 +639,18 @@ struct HelpPass : public Pass { it.second->help(); } } + else if (args[1] == "-cells") { + log("\n"); + for (auto &it : cell_help_messages.cell_help) { + string line = split_tokens(it.second, "\n").at(0); + string cell_name = next_token(line); + log(" %-15s %s\n", cell_name.c_str(), line.c_str()); + } + log("\n"); + log("Type 'help <cell_type>' for more information on a cell type.\n"); + log("\n"); + return; + } // this option is undocumented as it is for internal use only else if (args[1] == "-write-tex-command-reference-manual") { FILE *f = fopen("command-reference-manual.tex", "wt"); @@ -649,10 +676,20 @@ struct HelpPass : public Pass { } fclose(f); } - else if (pass_register.count(args[1]) == 0) - log("No such command: %s\n", args[1].c_str()); - else + else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); + } + else if (cell_help_messages.cell_help.count(args[1])) { + log("%s", cell_help_messages.cell_help.at(args[1]).c_str()); + log("Run 'help %s+' to display the Verilog model for this cell type.\n", args[1].c_str()); + log("\n"); + } + else if (cell_help_messages.cell_code.count(args[1])) { + log("\n"); + log("%s", cell_help_messages.cell_code.at(args[1]).c_str()); + } + else + log("No such command or cell type: %s\n", args[1].c_str()); return; } diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 560ef4b64..4403bcfdc 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -947,6 +947,7 @@ namespace { param_bool("\\CLK_POLARITY"); param_bool("\\TRANSPARENT"); port("\\CLK", 1); + port("\\EN", 1); port("\\ADDR", param("\\ABITS")); port("\\DATA", param("\\WIDTH")); check_expected(); @@ -980,12 +981,13 @@ namespace { param("\\SIZE"); param("\\OFFSET"); param("\\INIT"); - param_bits("\\RD_CLK_ENABLE", std::max(1, param("\\RD_PORTS"))); - param_bits("\\RD_CLK_POLARITY", std::max(1, param("\\RD_PORTS"))); - param_bits("\\RD_TRANSPARENT", std::max(1, param("\\RD_PORTS"))); - param_bits("\\WR_CLK_ENABLE", std::max(1, param("\\WR_PORTS"))); - param_bits("\\WR_CLK_POLARITY", std::max(1, param("\\WR_PORTS"))); + param_bits("\\RD_CLK_ENABLE", max(1, param("\\RD_PORTS"))); + param_bits("\\RD_CLK_POLARITY", max(1, param("\\RD_PORTS"))); + param_bits("\\RD_TRANSPARENT", max(1, param("\\RD_PORTS"))); + param_bits("\\WR_CLK_ENABLE", max(1, param("\\WR_PORTS"))); + param_bits("\\WR_CLK_POLARITY", max(1, param("\\WR_PORTS"))); port("\\RD_CLK", param("\\RD_PORTS")); + port("\\RD_EN", param("\\RD_PORTS")); port("\\RD_ADDR", param("\\RD_PORTS") * param("\\ABITS")); port("\\RD_DATA", param("\\RD_PORTS") * param("\\WIDTH")); port("\\WR_CLK", param("\\WR_PORTS")); @@ -1446,6 +1448,19 @@ void RTLIL::Module::connect(const RTLIL::SigSig &conn) for (auto mon : design->monitors) mon->notify_connect(this, conn); + // ignore all attempts to assign constants to other constants + if (conn.first.has_const()) { + RTLIL::SigSig new_conn; + for (int i = 0; i < GetSize(conn.first); i++) + if (conn.first[i].wire) { + new_conn.first.append(conn.first[i]); + new_conn.second.append(conn.second[i]); + } + if (GetSize(new_conn.first)) + connect(new_conn); + return; + } + if (yosys_xtrace) { log("#X# Connect (SigSig) in %s: %s = %s (%d bits)\n", log_id(this), log_signal(conn.first), log_signal(conn.second), GetSize(conn.first)); log_backtrace("-X- ", yosys_xtrace-1); @@ -1586,10 +1601,10 @@ DEF_METHOD(LogicNot, 1, "$logic_not") add ## _func(name, sig_a, sig_b, sig_y, is_signed); \ return sig_y; \ } -DEF_METHOD(And, std::max(sig_a.size(), sig_b.size()), "$and") -DEF_METHOD(Or, std::max(sig_a.size(), sig_b.size()), "$or") -DEF_METHOD(Xor, std::max(sig_a.size(), sig_b.size()), "$xor") -DEF_METHOD(Xnor, std::max(sig_a.size(), sig_b.size()), "$xnor") +DEF_METHOD(And, max(sig_a.size(), sig_b.size()), "$and") +DEF_METHOD(Or, max(sig_a.size(), sig_b.size()), "$or") +DEF_METHOD(Xor, max(sig_a.size(), sig_b.size()), "$xor") +DEF_METHOD(Xnor, max(sig_a.size(), sig_b.size()), "$xnor") DEF_METHOD(Shl, sig_a.size(), "$shl") DEF_METHOD(Shr, sig_a.size(), "$shr") DEF_METHOD(Sshl, sig_a.size(), "$sshl") @@ -1604,11 +1619,11 @@ DEF_METHOD(Eqx, 1, "$eqx") DEF_METHOD(Nex, 1, "$nex") DEF_METHOD(Ge, 1, "$ge") DEF_METHOD(Gt, 1, "$gt") -DEF_METHOD(Add, std::max(sig_a.size(), sig_b.size()), "$add") -DEF_METHOD(Sub, std::max(sig_a.size(), sig_b.size()), "$sub") -DEF_METHOD(Mul, std::max(sig_a.size(), sig_b.size()), "$mul") -DEF_METHOD(Div, std::max(sig_a.size(), sig_b.size()), "$div") -DEF_METHOD(Mod, std::max(sig_a.size(), sig_b.size()), "$mod") +DEF_METHOD(Add, max(sig_a.size(), sig_b.size()), "$add") +DEF_METHOD(Sub, max(sig_a.size(), sig_b.size()), "$sub") +DEF_METHOD(Mul, max(sig_a.size(), sig_b.size()), "$mul") +DEF_METHOD(Div, max(sig_a.size(), sig_b.size()), "$div") +DEF_METHOD(Mod, max(sig_a.size(), sig_b.size()), "$mod") DEF_METHOD(LogicAnd, 1, "$logic_and") DEF_METHOD(LogicOr, 1, "$logic_or") #undef DEF_METHOD @@ -1740,13 +1755,13 @@ RTLIL::Cell* RTLIL::Module::addConcat(RTLIL::IdString name, RTLIL::SigSpec sig_a return cell; } -RTLIL::Cell* RTLIL::Module::addLut(RTLIL::IdString name, RTLIL::SigSpec sig_i, RTLIL::SigSpec sig_o, RTLIL::Const lut) +RTLIL::Cell* RTLIL::Module::addLut(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const lut) { RTLIL::Cell *cell = addCell(name, "$lut"); cell->parameters["\\LUT"] = lut; - cell->parameters["\\WIDTH"] = sig_i.size(); - cell->setPort("\\A", sig_i); - cell->setPort("\\Y", sig_o); + cell->parameters["\\WIDTH"] = sig_a.size(); + cell->setPort("\\A", sig_a); + cell->setPort("\\Y", sig_y); return cell; } @@ -3182,6 +3197,17 @@ RTLIL::SigChunk RTLIL::SigSpec::as_chunk() const return chunks_[0]; } +RTLIL::SigBit RTLIL::SigSpec::as_bit() const +{ + cover("kernel.rtlil.sigspec.as_bit"); + + log_assert(width_ == 1); + if (packed()) + return RTLIL::SigBit(*chunks_.begin()); + else + return bits_[0]; +} + bool RTLIL::SigSpec::match(std::string pattern) const { cover("kernel.rtlil.sigspec.match"); @@ -3269,18 +3295,6 @@ dict<RTLIL::SigBit, RTLIL::SigBit> RTLIL::SigSpec::to_sigbit_dict(const RTLIL::S return new_map; } -RTLIL::SigBit RTLIL::SigSpec::to_single_sigbit() const -{ - cover("kernel.rtlil.sigspec.to_single_sigbit"); - - pack(); - log_assert(width_ == 1); - for (auto &c : chunks_) - if (c.width) - return RTLIL::SigBit(c); - log_abort(); -} - static void sigspec_parse_split(std::vector<std::string> &tokens, const std::string &text, char sep) { size_t start = 0, end = 0; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 21b42cdbe..7b669536e 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -460,7 +460,7 @@ struct RTLIL::Const Const(std::string str); Const(int val, int width = 32); Const(RTLIL::State bit, int width = 1); - Const(const std::vector<RTLIL::State> &bits) : bits(bits) { flags = CONST_FLAG_NONE; }; + Const(const std::vector<RTLIL::State> &bits) : bits(bits) { flags = CONST_FLAG_NONE; } Const(const std::vector<bool> &bits); bool operator <(const RTLIL::Const &other) const; @@ -476,7 +476,7 @@ struct RTLIL::Const inline int size() const { return bits.size(); } inline RTLIL::State &operator[](int index) { return bits.at(index); } - inline const RTLIL::State &operator[](int index) const { return bits.at(index); }; + inline const RTLIL::State &operator[](int index) const { return bits.at(index); } inline RTLIL::Const extract(int offset, int len = 1, RTLIL::State padding = RTLIL::State::S0) const { RTLIL::Const ret; @@ -690,6 +690,7 @@ public: bool is_wire() const; bool is_chunk() const; + inline bool is_bit() const { return width_ == 1; } bool is_fully_const() const; bool is_fully_zero() const; @@ -704,6 +705,7 @@ public: RTLIL::Const as_const() const; RTLIL::Wire *as_wire() const; RTLIL::SigChunk as_chunk() const; + RTLIL::SigBit as_bit() const; bool match(std::string pattern) const; @@ -712,7 +714,6 @@ public: std::vector<RTLIL::SigBit> to_sigbit_vector() const; std::map<RTLIL::SigBit, RTLIL::SigBit> to_sigbit_map(const RTLIL::SigSpec &other) const; dict<RTLIL::SigBit, RTLIL::SigBit> to_sigbit_dict(const RTLIL::SigSpec &other) const; - RTLIL::SigBit to_single_sigbit() const; static bool parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str); static bool parse_sel(RTLIL::SigSpec &sig, RTLIL::Design *design, RTLIL::Module *module, std::string str); @@ -998,7 +999,7 @@ public: RTLIL::Cell* addSlice (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const offset); RTLIL::Cell* addConcat (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y); - RTLIL::Cell* addLut (RTLIL::IdString name, RTLIL::SigSpec sig_i, RTLIL::SigSpec sig_o, RTLIL::Const lut); + RTLIL::Cell* addLut (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const lut); RTLIL::Cell* addTribuf (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_y); RTLIL::Cell* addAssert (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en); RTLIL::Cell* addEquiv (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y); diff --git a/kernel/satgen.h b/kernel/satgen.h index 7b0994441..d44d61f16 100644 --- a/kernel/satgen.h +++ b/kernel/satgen.h @@ -980,7 +980,7 @@ struct SatGen div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE); } } else { - int copy_a_bits = std::min(cell->getPort("\\A").size(), cell->getPort("\\B").size()); + int copy_a_bits = min(cell->getPort("\\A").size(), cell->getPort("\\B").size()); div_zero_result.insert(div_zero_result.end(), a.begin(), a.begin() + copy_a_bits); if (cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()) div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), div_zero_result.back()); diff --git a/kernel/sigtools.h b/kernel/sigtools.h index 7082ace4c..83ff470d2 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -222,18 +222,7 @@ struct SigSet struct SigMap { - struct bitDef_t : public std::pair<RTLIL::Wire*, int> { - bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { } - bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { } - unsigned int hash() const { return first->name.hash() + second; } - }; - - struct shared_bit_data_t { - RTLIL::SigBit map_to; - std::set<bitDef_t> bits; - }; - - dict<bitDef_t, shared_bit_data_t*> bits; + mfp<SigBit> database; SigMap(RTLIL::Module *module = NULL) { @@ -241,45 +230,14 @@ struct SigMap set(module); } - SigMap(const SigMap &other) - { - copy(other); - } - - const SigMap &operator=(const SigMap &other) - { - copy(other); - return *this; - } - - void copy(const SigMap &other) - { - clear(); - for (auto &bit : other.bits) { - bits[bit.first] = new shared_bit_data_t; - bits[bit.first]->map_to = bit.second->map_to; - bits[bit.first]->bits = bit.second->bits; - } - } - void swap(SigMap &other) { - bits.swap(other.bits); - } - - ~SigMap() - { - clear(); + database.swap(other.database); } void clear() { - std::set<shared_bit_data_t*> all_bd_ptr; - for (auto &it : bits) - all_bd_ptr.insert(it.second); - for (auto bd_ptr : all_bd_ptr) - delete bd_ptr; - bits.clear(); + database.clear(); } void set(RTLIL::Module *module) @@ -289,118 +247,49 @@ struct SigMap add(it.first, it.second); } - // internal helper function - void register_bit(const RTLIL::SigBit &bit) - { - if (bit.wire && bits.count(bit) == 0) { - shared_bit_data_t *bd = new shared_bit_data_t; - bd->map_to = bit; - bd->bits.insert(bit); - bits[bit] = bd; - } - } - - // internal helper function - void unregister_bit(const RTLIL::SigBit &bit) - { - if (bit.wire && bits.count(bit) > 0) { - shared_bit_data_t *bd = bits[bit]; - bd->bits.erase(bit); - if (bd->bits.size() == 0) - delete bd; - bits.erase(bit); - } - } - - // internal helper function - void merge_bit(const RTLIL::SigBit &bit1, const RTLIL::SigBit &bit2) - { - log_assert(bit1.wire != NULL && bit2.wire != NULL); - - shared_bit_data_t *bd1 = bits[bit1]; - shared_bit_data_t *bd2 = bits[bit2]; - log_assert(bd1 != NULL && bd2 != NULL); - - if (bd1 == bd2) - return; - - if (bd1->bits.size() < bd2->bits.size()) - { - for (auto &bit : bd1->bits) - bits[bit] = bd2; - bd2->bits.insert(bd1->bits.begin(), bd1->bits.end()); - delete bd1; - } - else - { - bd1->map_to = bd2->map_to; - for (auto &bit : bd2->bits) - bits[bit] = bd1; - bd1->bits.insert(bd2->bits.begin(), bd2->bits.end()); - delete bd2; - } - } - - // internal helper function - void set_bit(const RTLIL::SigBit &bit1, const RTLIL::SigBit &bit2) - { - log_assert(bit1.wire != NULL); - log_assert(bits.count(bit1) > 0); - bits[bit1]->map_to = bit2; - } - - // internal helper function - void map_bit(RTLIL::SigBit &bit) const - { - if (bit.wire && bits.count(bit) > 0) - bit = bits.at(bit)->map_to; - } - void add(RTLIL::SigSpec from, RTLIL::SigSpec to) { log_assert(GetSize(from) == GetSize(to)); for (int i = 0; i < GetSize(from); i++) { - RTLIL::SigBit &bf = from[i]; - RTLIL::SigBit &bt = to[i]; + int bfi = database.lookup(from[i]); + int bti = database.lookup(to[i]); - if (bf.wire == NULL) - continue; + const RTLIL::SigBit &bf = database[bfi]; + const RTLIL::SigBit &bt = database[bti]; - register_bit(bf); - register_bit(bt); + if (bf.wire || bt.wire) + { + database.imerge(bfi, bti); - if (bt.wire != NULL) - merge_bit(bf, bt); - else - set_bit(bf, bt); + if (bf.wire == nullptr) + database.ipromote(bfi); + + if (bt.wire == nullptr) + database.ipromote(bti); + } } } void add(RTLIL::SigSpec sig) { for (auto &bit : sig) { - register_bit(bit); - set_bit(bit, bit); + RTLIL::SigBit b = database.find(bit); + if (b.wire != nullptr) + database.promote(bit); } } - void del(RTLIL::SigSpec sig) - { - for (auto &bit : sig) - unregister_bit(bit); - } - void apply(RTLIL::SigBit &bit) const { - map_bit(bit); + bit = database.find(bit); } void apply(RTLIL::SigSpec &sig) const { for (auto &bit : sig) - map_bit(bit); + apply(bit); } RTLIL::SigBit operator()(RTLIL::SigBit bit) const @@ -417,10 +306,19 @@ struct SigMap RTLIL::SigSpec operator()(RTLIL::Wire *wire) const { - RTLIL::SigSpec sig(wire); + SigSpec sig(wire); apply(sig); return sig; } + + RTLIL::SigSpec allbits() const + { + RTLIL::SigSpec sig; + for (auto &bit : database) + if (bit.wire != nullptr) + sig.append(bit); + return sig; + } }; YOSYS_NAMESPACE_END diff --git a/kernel/yosys.h b/kernel/yosys.h index 6aacd4d54..92fa6ac19 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -41,6 +41,7 @@ #include <map> #include <set> +#include <tuple> #include <vector> #include <string> #include <algorithm> @@ -138,8 +139,14 @@ YOSYS_NAMESPACE_BEGIN using std::vector; using std::string; +using std::tuple; using std::pair; +using std::make_tuple; +using std::make_pair; +using std::min; +using std::max; + // A primitive shared string implementation that does not // move its .c_str() when the object is copied or moved. struct shared_str { @@ -164,6 +171,7 @@ using hashlib::hash_obj_ops; using hashlib::dict; using hashlib::idict; using hashlib::pool; +using hashlib::mfp; namespace RTLIL { struct IdString; @@ -242,6 +250,8 @@ YOSYS_NAMESPACE_END YOSYS_NAMESPACE_BEGIN using RTLIL::State; +using RTLIL::SigChunk; +using RTLIL::SigSig; namespace hashlib { template<> struct hash_ops<RTLIL::State> : hash_ops<int> {}; diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index 9f9ec2e2b..c648eb1fe 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -220,8 +220,9 @@ cell is created. Having individual cells for read and write ports has the advant consolidated using resource sharing passes. In some cases this drastically reduces the number of required ports on the memory cell. -The {\tt \$memrd} cells have a clock input \B{CLK}, an address input \B{ADDR} and a data output -\B{DATA}. They also have the following parameters: +The {\tt \$memrd} cells have a clock input \B{CLK}, an enable input \B{EN}, an +address input \B{ADDR}, and a data output \B{DATA}. They also have the +following parameters: \begin{itemize} \item \B{MEMID} \\ @@ -322,6 +323,9 @@ The {\tt \$mem} cell has the following ports: \item \B{RD\_CLK} \\ This input is \B{RD\_PORTS} bits wide, containing all clock signals for the read ports. +\item \B{RD\_EN} \\ +This input is \B{RD\_PORTS} bits wide, containing all enable signals for the read ports. + \item \B{RD\_ADDR} \\ This input is \B{RD\_PORTS}*\B{ABITS} bits wide, containing all address signals for the read ports. diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index e4b40c41c..01ada7739 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -14,6 +14,7 @@ OBJS += passes/cmds/setattr.o OBJS += passes/cmds/copy.o OBJS += passes/cmds/splice.o OBJS += passes/cmds/scc.o +OBJS += passes/cmds/torder.o OBJS += passes/cmds/logcmd.o OBJS += passes/cmds/tee.o OBJS += passes/cmds/write_file.o @@ -22,4 +23,6 @@ OBJS += passes/cmds/cover.o OBJS += passes/cmds/trace.o OBJS += passes/cmds/plugin.o OBJS += passes/cmds/check.o +OBJS += passes/cmds/qwp.o +OBJS += passes/cmds/edgetypes.o diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc new file mode 100644 index 000000000..7b75a009f --- /dev/null +++ b/passes/cmds/edgetypes.cc @@ -0,0 +1,106 @@ +/* + * 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 EdgetypePass : public Pass { + EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" edgetypes [options] [selection]\n"); + log("\n"); + log("This command lists all unique types of 'edges' found in the selection. An 'edge'\n"); + log("is a 4-tuple of source and sink cell type and port name.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-ltr") { + // config.ltr = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + pool<string> edge_cache; + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + dict<SigBit, pool<tuple<IdString, IdString, int>>> bit_sources, bit_sinks; + pool<std::pair<IdString, IdString>> multibit_ports; + + for (auto cell : module->selected_cells()) + for (auto conn : cell->connections()) + { + IdString cell_type = cell->type; + IdString port_name = conn.first; + SigSpec sig = sigmap(conn.second); + + if (GetSize(sig) > 1) + multibit_ports.insert(std::pair<IdString, IdString>(cell_type, port_name)); + + for (int i = 0; i < GetSize(sig); i++) { + if (cell->output(port_name)) + bit_sources[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i)); + if (cell->input(port_name)) + bit_sinks[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i)); + } + } + + for (auto &it : bit_sources) + for (auto &source : it.second) + for (auto &sink : bit_sinks[it.first]) + { + auto source_cell_type = std::get<0>(source); + auto source_port_name = std::get<1>(source); + auto source_bit_index = std::get<2>(source); + + auto sink_cell_type = std::get<0>(sink); + auto sink_port_name = std::get<1>(sink); + auto sink_bit_index = std::get<2>(sink); + + string source_str = multibit_ports.count(std::pair<IdString, IdString>(source_cell_type, source_port_name)) ? + stringf("%s.%s[%d]", log_id(source_cell_type), log_id(source_port_name), source_bit_index) : + stringf("%s.%s", log_id(source_cell_type), log_id(source_port_name)); + + string sink_str = multibit_ports.count(std::pair<IdString, IdString>(sink_cell_type, sink_port_name)) ? + stringf("%s.%s[%d]", log_id(sink_cell_type), log_id(sink_port_name), sink_bit_index) : + stringf("%s.%s", log_id(sink_cell_type), log_id(sink_port_name)); + + edge_cache.insert(source_str + " " + sink_str); + } + } + + edge_cache.sort(); + for (auto &str : edge_cache) + log("%s\n", str.c_str()); + } +} EdgetypePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index f7c65bbde..e2d80d9bf 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -115,7 +115,7 @@ struct PluginPass : public Pass { log("\n"); int max_alias_len = 1; for (auto &it : loaded_plugin_aliases) - max_alias_len = std::max(max_alias_len, GetSize(it.first)); + max_alias_len = max(max_alias_len, GetSize(it.first)); for (auto &it : loaded_plugin_aliases) log("Alias: %-*s %s\n", max_alias_len, it.first.c_str(), it.second.c_str()); } diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc new file mode 100644 index 000000000..8ec815a72 --- /dev/null +++ b/passes/cmds/qwp.cc @@ -0,0 +1,840 @@ +/* + * 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" + +#undef LOG_MATRICES +#undef PYPLOT_EDGES + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static uint32_t xorshift32_state; + +static double xorshift32() +{ + xorshift32_state ^= xorshift32_state << 13; + xorshift32_state ^= xorshift32_state >> 17; + xorshift32_state ^= xorshift32_state << 5; + return (xorshift32_state % 1000000) / 1e6; +} + +struct QwpConfig +{ + bool ltr; + bool alpha; + double grid; + + std::ofstream dump_file; + + QwpConfig() { + ltr = false; + alpha = false; + grid = 1.0 / 16; + } +}; + +struct QwpWorker +{ + QwpConfig &config; + Module *module; + char direction; + + struct Node { + Cell *cell; + bool tied, alt_tied; + + // pos = position in current direction + // alt_pos = position in the other direction + double pos, alt_pos; + + Node() { + cell = nullptr; + tied = false; + pos = xorshift32(); + alt_tied = false; + alt_pos = xorshift32(); + } + + void tie(double v) { + tied = true; + pos = v; + } + + void alt_tie(double v) { + alt_tied = true; + alt_pos = v; + } + + void swap_alt() { + std::swap(tied, alt_tied); + std::swap(pos, alt_pos); + } + + void proj_left(double midpos) { + cell = nullptr; + tie(pos > midpos ? midpos : pos); + } + + void proj_right(double midpos) { + cell = nullptr; + tie(pos < midpos ? midpos : pos); + } + }; + + vector<Node> nodes; + dict<pair<int, int>, double> edges; + dict<Cell*, int> cell_to_node; + + // worker state variables + double midpos; + double radius; + double alt_midpos; + double alt_radius; + + QwpWorker(QwpConfig &config, Module *module, char direction = 'x') : config(config), module(module), direction(direction) + { + log_assert(direction == 'x' || direction == 'y'); + } + + void load_module() + { + log_assert(direction == 'x'); + + SigMap sigmap(module); + dict<SigBit, pool<int>> bits_to_nodes; + + if (config.ltr || config.alpha) + { + dict<Wire*, double> alpha_inputs, alpha_outputs; + + if (config.alpha) + { + dict<string, Wire*> alpha_order; + + for (auto wire : module->wires()) { + if (wire->port_input || wire->port_output) + alpha_order[wire->name.str()] = wire; + } + + alpha_order.sort(); + + for (auto &it : alpha_order) { + if (it.second->port_input) { + int idx = GetSize(alpha_inputs); + alpha_inputs[it.second] = idx + 0.5; + } + if (it.second->port_output) { + int idx = GetSize(alpha_outputs); + alpha_outputs[it.second] = idx + 0.5; + } + } + } + + for (auto wire : module->wires()) + { + if (!wire->port_input && !wire->port_output) + continue; + + int idx = GetSize(nodes); + nodes.push_back(Node()); + + if (config.ltr) { + if (wire->port_input) + nodes[idx].tie(0.0); + else + nodes[idx].tie(1.0); + } + + if (config.alpha) { + if (wire->port_input) + nodes[idx].alt_tie(alpha_inputs.at(wire) / GetSize(alpha_inputs)); + else + nodes[idx].alt_tie(alpha_outputs.at(wire) / GetSize(alpha_outputs)); + } + + for (auto bit : sigmap(wire)) + bits_to_nodes[bit].insert(idx); + } + } + + for (auto cell : module->selected_cells()) + { + log_assert(cell_to_node.count(cell) == 0); + int idx = GetSize(nodes); + nodes.push_back(Node()); + + cell_to_node[cell] = GetSize(nodes); + nodes[idx].cell = cell; + + for (auto &conn : cell->connections()) + for (auto bit : sigmap(conn.second)) + bits_to_nodes[bit].insert(idx); + } + + for (auto &it : bits_to_nodes) + { + if (GetSize(it.second) > 100) + continue; + + for (int idx1 : it.second) + for (int idx2 : it.second) + if (idx1 < idx2) + edges[pair<int, int>(idx1, idx2)] += 1.0 / GetSize(it.second); + } + } + + void solve(bool alt_mode = false) + { + // A := constraint_matrix + // y := constraint_rhs_vector + // + // AA = A' * A + // Ay = A' * y + // + // M := [AA Ay] + + // Row major order + int N = GetSize(nodes), N1 = N+1; + vector<double> M(N * N1); + + // Edge constraints: + // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 -weight 0 ... 0 0], y[i] := 0 + // + // i.e. nonzero columns in A[i,:] at the two node indices. + for (auto &edge : edges) + { + int idx1 = edge.first.first; + int idx2 = edge.first.second; + double weight = edge.second * (1.0 + xorshift32() * 1e-3); + + M[idx1 + idx1*N1] += weight * weight; + M[idx2 + idx2*N1] += weight * weight; + + M[idx1 + idx2*N1] += -weight * weight; + M[idx2 + idx1*N1] += -weight * weight; + } + + // Node constraints: + // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 0], y[i] := weight * pos + // + // i.e. nonzero column in A[i,:] at the node index + // + // "tied" nodes have a large weight, pinning them in position. Untied + // nodes have a small weight, giving then a tiny preference to stay at + // the current position, making sure that AA never is singular. + for (int idx = 0; idx < GetSize(nodes); idx++) + { + auto &node = nodes[idx]; + double rhs = (alt_mode ? node.alt_pos : node.pos); + + double weight = 1e-3; + if (alt_mode ? node.alt_tied : node.tied) + weight = 1e3; + weight *= (1.0 + xorshift32() * 1e-3); + + M[idx + idx*N1] += weight * weight; + M[N + idx*N1] += rhs * weight * weight; + } + +#ifdef LOG_MATRICES + log("\n"); + for (int i = 0; i < N; i++) { + for (int j = 0; j < N+1; j++) + log(" %10.2e", M[(N+1)*i + j]); + log("\n"); + } +#endif + + // Solve "AA*x = Ay" + // (least squares fit for "A*x = y") + // + // Using gaussian elimination to get M := [Id x] + + vector<int> pivot_cache; + vector<int> queue; + + for (int i = 0; i < N; i++) + queue.push_back(i); + + // gaussian elimination + for (int i = 0; i < N; i++) + { + // find best row + int best_row = queue.front(); + int best_row_queue_idx = 0; + double best_row_absval = 0; + + for (int k = 0; k < GetSize(queue); k++) { + int row = queue[k]; + double absval = fabs(M[i + row*N1]); + if (absval > best_row_absval) { + best_row = row; + best_row_queue_idx = k; + best_row_absval = absval; + } + } + + int row = best_row; + pivot_cache.push_back(row); + + queue[best_row_queue_idx] = queue.back(); + queue.pop_back(); + + // normalize row + for (int k = i+1; k < N1; k++) + M[k + row*N1] /= M[i + row*N1]; + M[i + row*N1] = 1.0; + + // elimination + for (int other_row : queue) { + double d = M[i + other_row*N1]; + for (int k = i+1; k < N1; k++) + M[k + other_row*N1] -= d*M[k + row*N1]; + M[i + other_row*N1] = 0.0; + } + } + + log_assert(queue.empty()); + log_assert(GetSize(pivot_cache) == N); + + // back substitution + for (int i = N-1; i >= 0; i--) + for (int j = i+1; j < N; j++) + { + int row = pivot_cache[i]; + int other_row = pivot_cache[j]; + M[N + row*N1] -= M[j + row*N1] * M[N + other_row*N1]; + M[j + row*N1] = 0.0; + } + +#ifdef LOG_MATRICES + log("\n"); + for (int i = 0; i < N; i++) { + for (int j = 0; j < N+1; j++) + log(" %10.2e", M[(N+1)*i + j]); + log("\n"); + } +#endif + + // update node positions + for (int i = 0; i < N; i++) + { + double v = M[(N+1)*i + N]; + double c = alt_mode ? alt_midpos : midpos; + double r = alt_mode ? alt_radius : radius; + + if (std::isfinite(v)) { + v = min(v, c+r); + v = max(v, c-r); + } else { + v = c; + } + + if (alt_mode) { + if (!nodes[i].alt_tied) + nodes[i].alt_pos = v; + } else { + if (!nodes[i].tied) + nodes[i].pos = v; + } + } + } + + void log_cell_coordinates(int indent, bool log_all_nodes = false) + { + for (auto &node : nodes) + { + if (node.cell == nullptr && !log_all_nodes) + continue; + + for (int i = 0; i < indent; i++) + log(" "); + + if (direction == 'x') + log("X=%.2f, Y=%.2f", node.pos, node.alt_pos); + else + log("X=%.2f, Y=%.2f", node.alt_pos, node.pos); + + if (node.tied) + log(" [%c-tied]", direction); + + if (node.alt_tied) + log(" [%c-tied]", direction == 'x' ? 'y' : 'x'); + + if (node.cell != nullptr) + log(" %s (%s)", log_id(node.cell), log_id(node.cell->type)); + else + log(" (none)"); + + log("\n"); + } + } + + void dump_svg(const pool<int> *green_nodes = nullptr, double median = -1) + { + double x_center = direction == 'x' ? midpos : alt_midpos; + double y_center = direction == 'y' ? midpos : alt_midpos; + + double x_radius = direction == 'x' ? radius : alt_radius; + double y_radius = direction == 'y' ? radius : alt_radius; + + config.dump_file << stringf("<svg height=\"240\" width=\"470\">\n"); + config.dump_file << stringf("<rect x=\"0\" y=\"0\" width=\"470\" height=\"240\" style=\"fill:rgb(250,250,200);\" />\n"); + config.dump_file << stringf("<rect x=\"20\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n"); + config.dump_file << stringf("<rect x=\"250\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n"); + + double win_x = 250 + 200 * (direction == 'x' ? midpos - radius : alt_midpos - alt_radius); + double win_y = 20 + 200 * (direction == 'y' ? midpos - radius : alt_midpos - alt_radius); + + double win_w = 200 * (direction == 'x' ? 2*radius : 2*alt_radius); + double win_h = 200 * (direction == 'y' ? 2*radius : 2*alt_radius); + + config.dump_file << stringf("<rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\" " + "style=\"stroke:rgb(0,0,0);stroke-width:1;fill:none\" />\n", win_x, win_y, win_w, win_h); + + if (median >= 0) + { + double x1 = 20.0, x2 = 220.0, y1 = 20.0, y2 = 220.0; + + if (direction == 'x') + x1 = x2 = 120 + 100 * (median - x_center) / x_radius; + else + y1 = y2 = 120 + 100 * (median - y_center) / y_radius; + + config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" " + "style=\"stroke:rgb(150,0,150);stroke-width:1\" />\n", x1, y1, x2, y2); + } + + for (auto &edge : edges) + { + auto &node1 = nodes[edge.first.first]; + auto &node2 = nodes[edge.first.second]; + + double x1 = direction == 'x' ? node1.pos : node1.alt_pos; + double y1 = direction == 'y' ? node1.pos : node1.alt_pos; + + double x2 = direction == 'x' ? node2.pos : node2.alt_pos; + double y2 = direction == 'y' ? node2.pos : node2.alt_pos; + + x1 = 120 + 100 * (x1 - x_center) / x_radius; + y1 = 120 + 100 * (y1 - y_center) / y_radius; + + x2 = 120 + 100 * (x2 - x_center) / x_radius; + y2 = 120 + 100 * (y2 - y_center) / y_radius; + + config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" " + "style=\"stroke:rgb(0,0,0);stroke-width:1\" />\n", x1, y1, x2, y2); + } + + for (int i = 0; i < GetSize(nodes); i++) + { + auto &node = nodes[i]; + + double x = direction == 'x' ? node.pos : node.alt_pos; + double y = direction == 'y' ? node.pos : node.alt_pos; + + x = 120 + 100 * (x - x_center) / x_radius; + y = 120 + 100 * (y - y_center) / y_radius; + + const char *color = node.cell == nullptr ? "blue" : "red"; + + if (green_nodes != nullptr && green_nodes->count(i)) + color = "green"; + + config.dump_file << stringf("<circle cx=\"%.2f\" cy=\"%.2f\" r=\"3\" fill=\"%s\"/>\n", x, y, color); + } + + config.dump_file << stringf("</svg>\n"); + } + + void run_worker(int indent) + { + int count_cells = 0; + + for (auto &node : nodes) + if (node.cell != nullptr) + count_cells++; + + for (int i = 0; i < indent; i++) + log(" "); + + string range_str; + + if (direction == 'x') + range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f", + midpos - radius, midpos + radius, + alt_midpos - alt_radius, alt_midpos + alt_radius); + else + range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f", + alt_midpos - alt_radius, alt_midpos + alt_radius, + midpos - radius, midpos + radius); + + log("%c-qwp on %s with %d cells, %d nodes, and %d edges.\n", direction, + range_str.c_str(), count_cells, GetSize(nodes), GetSize(edges)); + + solve(); + solve(true); + + // detect median position and check for break condition + + vector<pair<double, int>> sorted_pos; + for (int i = 0; i < GetSize(nodes); i++) + if (nodes[i].cell != nullptr) + sorted_pos.push_back(pair<double, int>(nodes[i].pos, i)); + + std::sort(sorted_pos.begin(), sorted_pos.end()); + int median_sidx = GetSize(sorted_pos)/2; + double median = sorted_pos[median_sidx].first; + + double left_scale = radius / (median - (midpos - radius)); + double right_scale = radius / ((midpos + radius) - median); + + if (config.dump_file.is_open()) + { + config.dump_file << stringf("<h4>LSQ %c-Solution for %s:</h4>\n", direction, range_str.c_str()); + + pool<int> green_nodes; + for (int i = 0; i < median_sidx; i++) + green_nodes.insert(sorted_pos[i].second); + + dump_svg(&green_nodes, median); + } + + for (auto &node : nodes) + { + double rel_pos = node.pos - median; + if (rel_pos < 0) { + node.pos = midpos + left_scale*rel_pos; + if (std::isfinite(node.pos)) { + node.pos = min(node.pos, midpos); + node.pos = max(node.pos, midpos - radius); + } else + node.pos = midpos - radius/2; + } else { + node.pos = midpos + right_scale*rel_pos; + if (std::isfinite(node.pos)) { + node.pos = max(node.pos, midpos); + node.pos = min(node.pos, midpos + radius); + } else + node.pos = midpos + radius/2; + } + } + + if (GetSize(sorted_pos) < 2 || (2*radius <= config.grid && 2*alt_radius <= config.grid)) { + log_cell_coordinates(indent + 1); + return; + } + + // create child workers + + char child_direction = direction == 'x' ? 'y' : 'x'; + + QwpWorker left_worker(config, module, child_direction); + QwpWorker right_worker(config, module, child_direction); + + // duplicate nodes into child workers + + dict<int, int> left_nodes, right_nodes; + + for (int k = 0; k < GetSize(sorted_pos); k++) + { + int i = sorted_pos[k].second; + + if (k < median_sidx) { + left_nodes[i] = GetSize(left_worker.nodes); + left_worker.nodes.push_back(nodes[i]); + if (left_worker.nodes.back().pos > midpos) + left_worker.nodes.back().pos = midpos; + left_worker.nodes.back().swap_alt(); + } else { + right_nodes[i] = GetSize(right_worker.nodes); + right_worker.nodes.push_back(nodes[i]); + if (right_worker.nodes.back().pos < midpos) + right_worker.nodes.back().pos = midpos; + right_worker.nodes.back().swap_alt(); + } + } + + // duplicate edges into child workers, project nodes as needed + + for (auto &edge : edges) + { + int idx1 = edge.first.first; + int idx2 = edge.first.second; + double weight = edge.second; + + if (nodes[idx1].cell == nullptr && nodes[idx2].cell == nullptr) + continue; + + int left_idx1 = left_nodes.count(idx1) ? left_nodes.at(idx1) : -1; + int left_idx2 = left_nodes.count(idx2) ? left_nodes.at(idx2) : -1; + + int right_idx1 = right_nodes.count(idx1) ? right_nodes.at(idx1) : -1; + int right_idx2 = right_nodes.count(idx2) ? right_nodes.at(idx2) : -1; + + if (left_idx1 >= 0 && left_worker.nodes[left_idx1].cell && left_idx2 < 0) { + left_idx2 = left_nodes[idx2] = GetSize(left_worker.nodes); + left_worker.nodes.push_back(nodes[idx2]); + left_worker.nodes.back().proj_left(midpos); + left_worker.nodes.back().swap_alt(); + } else + if (left_idx2 >= 0 && left_worker.nodes[left_idx2].cell && left_idx1 < 0) { + left_idx1 = left_nodes[idx1] = GetSize(left_worker.nodes); + left_worker.nodes.push_back(nodes[idx1]); + left_worker.nodes.back().proj_left(midpos); + left_worker.nodes.back().swap_alt(); + } + + if (right_idx1 >= 0 && right_worker.nodes[right_idx1].cell && right_idx2 < 0) { + right_idx2 = right_nodes[idx2] = GetSize(right_worker.nodes); + right_worker.nodes.push_back(nodes[idx2]); + right_worker.nodes.back().proj_right(midpos); + right_worker.nodes.back().swap_alt(); + } else + if (right_idx2 >= 0 && right_worker.nodes[right_idx2].cell && right_idx1 < 0) { + right_idx1 = right_nodes[idx1] = GetSize(right_worker.nodes); + right_worker.nodes.push_back(nodes[idx1]); + right_worker.nodes.back().proj_right(midpos); + right_worker.nodes.back().swap_alt(); + } + + if (left_idx1 >= 0 && left_idx2 >= 0) + left_worker.edges[pair<int, int>(left_idx1, left_idx2)] += weight; + + if (right_idx1 >= 0 && right_idx2 >= 0) + right_worker.edges[pair<int, int>(right_idx1, right_idx2)] += weight; + } + + // run child workers + + left_worker.midpos = right_worker.midpos = alt_midpos; + left_worker.radius = right_worker.radius = alt_radius; + + left_worker.alt_midpos = midpos - radius/2; + right_worker.alt_midpos = midpos + radius/2; + left_worker.alt_radius = right_worker.alt_radius = radius/2; + + left_worker.run_worker(indent+1); + right_worker.run_worker(indent+1); + + // re-integrate results + + for (auto &it : left_nodes) + if (left_worker.nodes[it.second].cell != nullptr) { + nodes[it.first].pos = left_worker.nodes[it.second].alt_pos; + nodes[it.first].alt_pos = left_worker.nodes[it.second].pos; + } + + for (auto &it : right_nodes) + if (right_worker.nodes[it.second].cell != nullptr) { + nodes[it.first].pos = right_worker.nodes[it.second].alt_pos; + nodes[it.first].alt_pos = right_worker.nodes[it.second].pos; + } + + if (config.dump_file.is_open()) { + config.dump_file << stringf("<h4>Final %c-Solution for %s:</h4>\n", direction, range_str.c_str()); + dump_svg(); + } + } + + void histogram(const vector<double> &values) + { + if (values.empty()) { + log("no data\n"); + return; + } + + double min_value = values.front(); + double max_value = values.front(); + + for (auto &v : values) { + min_value = min(min_value, v); + max_value = max(max_value, v); + } + + if (fabs(max_value - min_value) < 0.001) { + log("all values in range %f .. %f\n", min_value, max_value); + return; + } + + vector<int> buckets(60); + int max_bucket_val = 0; + + for (auto &v : values) { + int idx = min(int(GetSize(buckets) * (v - min_value) / (max_value - min_value)), GetSize(buckets)-1); + max_bucket_val = max(max_bucket_val, ++buckets.at(idx)); + } + + for (int i = 4; i >= 0; i--) { + for (int k = 0; k < GetSize(buckets); k++) { + int v = 10 * buckets[k] / max_bucket_val; + if (v >= 2*i+1) + log(v == 2*i+1 ? "." : ":"); + else + log(i == 0 ? (buckets[k] > 0 ? "," : "_") : " "); + } + log("\n"); + } + log("%-30f%30f\n", min_value, max_value); + } + + void run() + { + log("\n"); + log("Running qwp on module %s..\n", log_id(module)); + + if (config.dump_file.is_open()) + config.dump_file << stringf("<h3>QWP protocol for module %s:</h3>\n", log_id(module)); + + load_module(); + + midpos = 0.5; + radius = 0.5; + alt_midpos = 0.5; + alt_radius = 0.5; + run_worker(1); + + for (auto &node : nodes) + if (node.cell != nullptr) + node.cell->attributes["\\qwp_position"] = stringf("%f %f", node.pos, node.alt_pos); + + vector<double> edge_lengths; + vector<double> weighted_edge_lengths; + + double total_edge_length = 0; + double total_weighted_edge_length = 0; + + for (auto &edge : edges) + { + auto &node1 = nodes[edge.first.first]; + auto &node2 = nodes[edge.first.second]; + + double distance = sqrt(pow(node1.pos - node2.pos, 2) + pow(node1.alt_pos - node2.alt_pos, 2)); + double weighted_distance = distance * edge.second; + + edge_lengths.push_back(distance); + weighted_edge_lengths.push_back(weighted_distance); + + total_edge_length += distance; + total_weighted_edge_length += weighted_distance; + } + + log("\n"); + log("Summary for module %s:\n", log_id(module)); + log("Number of edges: %d\n", GetSize(edges)); + log("Total edge length: %f\n", total_edge_length); + log("Total weighted edge length: %f\n", total_weighted_edge_length); + + log("\n"); + log("Histogram over edge lengths:\n"); + histogram(edge_lengths); + + log("\n"); + log("Histogram over weighted edge lengths:\n"); + histogram(weighted_edge_lengths); + } +}; + +struct QwpPass : public Pass { + QwpPass() : Pass("qwp", "quadratic wirelength placer") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" qwp [options] [selection]\n"); + log("\n"); + log("This command runs quadratic wirelength placement on the selected modules and\n"); + log("annotates the cells in the design with 'qwp_position' attributes.\n"); + log("\n"); + log(" -ltr\n"); + log(" Add left-to-right constraints: constrain all inputs on the left border\n"); + log(" outputs to the right border.\n"); + log("\n"); + log(" -alpha\n"); + log(" Add constraints for inputs/outputs to be placed in alphanumerical\n"); + log(" order along the y-axis (top-to-bottom).\n"); + log("\n"); + log(" -grid N\n"); + log(" Number of grid divisions in x- and y-direction. (default=16)\n"); + log("\n"); + log(" -dump <html_file_name>\n"); + log(" Dump a protocol of the placement algorithm to the html file.\n"); + log("\n"); + log("Note: This implementation of a quadratic wirelength placer uses exact\n"); + log("dense matrix operations. It is only a toy-placer for small circuits.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + QwpConfig config; + xorshift32_state = 123456789; + + log_header("Executing QWP pass (quadratic wirelength placer).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-ltr") { + config.ltr = true; + continue; + } + if (args[argidx] == "-alpha") { + config.alpha = true; + continue; + } + if (args[argidx] == "-grid" && argidx+1 < args.size()) { + config.grid = 1.0 / atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-dump" && argidx+1 < args.size()) { + config.dump_file.open(args[++argidx], std::ofstream::trunc); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + QwpWorker worker(config, module); + worker.run(); + +#ifdef PYPLOT_EDGES + log("\n"); + log("plt.figure(figsize=(10, 10));\n"); + + for (auto &edge : worker.edges) { + log("plt.plot([%.2f, %.2f], [%.2f, %.2f], \"r-\");\n", + worker.nodes[edge.first.first].pos, + worker.nodes[edge.first.second].pos, + worker.nodes[edge.first.first].alt_pos, + worker.nodes[edge.first.second].alt_pos); + } + + for (auto &node : worker.nodes) { + const char *style = node.cell != nullptr ? "ko" : "ks"; + log("plt.plot([%.2f], [%.2f], \"%s\");\n", node.pos, node.alt_pos, style); + } +#endif + } + } +} QwpPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index 43a43b4fc..532026f26 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -69,10 +69,10 @@ struct SccWorker for (auto nextCell : cellToNextCell[cell]) if (cellLabels.count(nextCell) == 0) { run(nextCell, depth+1, maxDepth); - cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second); + cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second); } else if (cellsOnStack.count(nextCell) > 0 && (maxDepth < 0 || cellDepth[nextCell] + maxDepth > depth)) { - cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second); + cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second); } if (cellLabels[cell].first == cellLabels[cell].second) diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 3035e7301..3b03d6802 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -552,7 +552,7 @@ struct ShowWorker continue; if (design->selected_whole_module(module->name)) { if (module->get_bool_attribute("\\blackbox")) { - log("Skipping blackbox module %s.\n", id2cstr(module->name)); + // log("Skipping blackbox module %s.\n", id2cstr(module->name)); continue; } else if (module->cells_.empty() && module->connections().empty() && module->processes.empty()) { diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc index 4ce2ec11c..2556fb740 100644 --- a/passes/cmds/splice.cc +++ b/passes/cmds/splice.cc @@ -64,7 +64,7 @@ struct SpliceWorker return sliced_signals_cache.at(sig); int offset = 0; - int p = driven_bits_map.at(sig.extract(0, 1).to_single_sigbit()) - 1; + int p = driven_bits_map.at(sig.extract(0, 1).as_bit()) - 1; while (driven_bits.at(p) != RTLIL::State::Sm) p--, offset++; diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index 3cd857f41..a64c48791 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -50,7 +50,7 @@ struct SplitnetsWorker new_wire_name += format.substr(1, 1); RTLIL::Wire *new_wire = module->addWire(module->uniquify(new_wire_name), width); - new_wire->port_id = wire->port_id; + new_wire->port_id = wire->port_id ? wire->port_id + offset : 0; new_wire->port_input = wire->port_input; new_wire->port_output = wire->port_output; @@ -130,14 +130,24 @@ struct SplitnetsPass : public Pass { } extra_args(args, argidx, design); - for (auto &mod_it : design->modules_) + for (auto module : design->selected_modules()) { - RTLIL::Module *module = mod_it.second; - if (!design->selected(module)) - continue; - SplitnetsWorker worker; + if (flag_ports) + { + int normalized_port_factor = 0; + + for (auto wire : module->wires()) + if (wire->port_id != 0) { + normalized_port_factor = max(normalized_port_factor, wire->port_id+1); + normalized_port_factor = max(normalized_port_factor, GetSize(wire)+1); + } + + for (auto wire : module->wires()) + wire->port_id *= normalized_port_factor; + } + if (flag_driver) { CellTypes ct(design); @@ -194,7 +204,8 @@ struct SplitnetsPass : public Pass { delete_wires.insert(it.first); module->remove(delete_wires); - module->fixup_ports(); + if (flag_ports) + module->fixup_ports(); } } } SplitnetsPass; diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 0aa76467f..af56a9e20 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -110,7 +110,7 @@ struct statdata_t int width_a = it.second->hasPort("\\A") ? GetSize(it.second->getPort("\\A")) : 0; int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0; int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0; - cell_type = stringf("%s_%d", cell_type.c_str(), std::max<int>({width_a, width_b, width_y})); + cell_type = stringf("%s_%d", cell_type.c_str(), max<int>({width_a, width_b, width_y})); } else if (cell_type.in("$mux", "$pmux")) cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Y"))); @@ -223,7 +223,7 @@ struct StatPass : public Pass { data.log_data(); } - if (top_mod != NULL) + if (top_mod != NULL && GetSize(mod_stat) > 1) { log("\n"); log("=== design hierarchy ===\n"); diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc new file mode 100644 index 000000000..50317c023 --- /dev/null +++ b/passes/cmds/torder.cc @@ -0,0 +1,123 @@ +/* + * 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/celltypes.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct TorderPass : public Pass { + TorderPass() : Pass("torder", "print cells in topological order") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" torder [options] [selection]\n"); + log("\n"); + log("This command prints the selected cells in topological order.\n"); + log("\n"); + log(" -stop <cell_type> <cell_port>\n"); + log(" do not use the specified cell port in topological sorting\n"); + log("\n"); + log(" -noautostop\n"); + log(" by default Q outputs of internal FF cells and memory read port outputs\n"); + log(" are not used in topological sorting. this option deactivates that.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + bool noautostop = false; + dict<IdString, pool<IdString>> stop_db; + + log_header("Executing TORDER pass (print cells in topological order).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-stop" && argidx+2 < args.size()) { + IdString cell_type = RTLIL::escape_id(args[++argidx]); + IdString cell_port = RTLIL::escape_id(args[++argidx]); + stop_db[cell_type].insert(cell_port); + continue; + } + if (args[argidx] == "-noautostop") { + noautostop = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + log("module %s\n", log_id(module)); + + SigMap sigmap(module); + dict<SigBit, pool<IdString>> bit_drivers, bit_users; + TopoSort<IdString, RTLIL::sort_by_id_str> toposort; + + for (auto cell : module->selected_cells()) + for (auto conn : cell->connections()) + { + if (stop_db.count(cell->type) && stop_db.at(cell->type).count(conn.first)) + continue; + + if (!noautostop && yosys_celltypes.cell_known(cell->type)) { + if (conn.first.in("\\Q", "\\CTRL_OUT", "\\RD_DATA")) + continue; + if (cell->type == "$memrd" && conn.first == "\\DATA") + continue; + } + + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + bit_users[bit].insert(cell->name); + + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + bit_drivers[bit].insert(cell->name); + + toposort.node(cell->name); + } + + for (auto &it : bit_users) + if (bit_drivers.count(it.first)) + for (auto driver_cell : bit_drivers.at(it.first)) + for (auto user_cell : it.second) + toposort.edge(driver_cell, user_cell); + + toposort.analyze_loops = true; + toposort.sort(); + + for (auto &it : toposort.loops) { + log(" loop"); + for (auto cell : it) + log(" %s", log_id(cell)); + log("\n"); + } + + for (auto cell : toposort.sorted) + log(" cell %s\n", log_id(cell)); + } + } +} TorderPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc index 548eaca3b..dd7b3be02 100644 --- a/passes/equiv/Makefile.inc +++ b/passes/equiv/Makefile.inc @@ -6,4 +6,7 @@ OBJS += passes/equiv/equiv_status.o OBJS += passes/equiv/equiv_add.o OBJS += passes/equiv/equiv_remove.o OBJS += passes/equiv/equiv_induct.o +OBJS += passes/equiv/equiv_struct.o +OBJS += passes/equiv/equiv_purge.o +OBJS += passes/equiv/equiv_mark.o diff --git a/passes/equiv/equiv_add.cc b/passes/equiv/equiv_add.cc index 4ce750b1d..9ae490fe7 100644 --- a/passes/equiv/equiv_add.cc +++ b/passes/equiv/equiv_add.cc @@ -33,56 +33,117 @@ struct EquivAddPass : public Pass { log("\n"); log("This command adds an $equiv cell for the specified signals.\n"); log("\n"); + log("\n"); + log(" equiv_add -cell gold_cell gate_cell\n"); + log("\n"); + log("This command adds $equiv cells for the ports of the specified cells.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, Design *design) { - if (GetSize(args) != 3) - cmd_error(args, GetSize(args)-1, "Invalid number of arguments."); - if (design->selected_active_module.empty()) log_cmd_error("This command must be executed in module context!\n"); Module *module = design->module(design->selected_active_module); log_assert(module != nullptr); - SigSpec gold_signal, gate_signal; + if (GetSize(args) == 4 && args[1] == "-cell") + { + Cell *gold_cell = module->cell(RTLIL::escape_id(args[2])); + Cell *gate_cell = module->cell(RTLIL::escape_id(args[3])); - if (!SigSpec::parse(gate_signal, module, args[2])) - log_cmd_error("Error in gate signal: %s\n", args[2].c_str()); + if (gold_cell == nullptr) + log_cmd_error("Can't find gold cell '%s'.\n", args[2].c_str()); - if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1])) - log_cmd_error("Error in gold signal: %s\n", args[1].c_str()); + if (gate_cell == nullptr) + log_cmd_error("Can't find gate cell '%s'.\n", args[3].c_str()); - log_assert(GetSize(gold_signal) == GetSize(gate_signal)); - SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal)); + for (auto conn : gold_cell->connections()) + { + auto port = conn.first; + SigSpec gold_sig = gold_cell->getPort(port); + SigSpec gate_sig = gate_cell->getPort(port); + int width = min(GetSize(gold_sig), GetSize(gate_sig)); - SigMap sigmap(module); - sigmap.apply(gold_signal); - sigmap.apply(gate_signal); + if (gold_cell->input(port) && gate_cell->input(port)) + { + SigSpec combined_sig = module->addWire(NEW_ID, width); - dict<SigBit, SigBit> to_equiv_bits; - pool<Cell*> added_equiv_cells; + for (int i = 0; i < width; i++) { + module->addEquiv(NEW_ID, gold_sig[i], gate_sig[i], combined_sig[i]); + gold_sig[i] = gate_sig[i] = combined_sig[i]; + } - for (int i = 0; i < GetSize(gold_signal); i++) { - Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]); - equiv_cell->set_bool_attribute("\\keep"); - to_equiv_bits[gold_signal[i]] = equiv_signal[i]; - to_equiv_bits[gate_signal[i]] = equiv_signal[i]; - added_equiv_cells.insert(equiv_cell); + gold_cell->setPort(port, gold_sig); + gate_cell->setPort(port, gate_sig); + continue; + } + + if (gold_cell->output(port) && gate_cell->output(port)) + { + SigSpec new_gold_wire = module->addWire(NEW_ID, width); + SigSpec new_gate_wire = module->addWire(NEW_ID, width); + SigSig gg_conn; + + for (int i = 0; i < width; i++) { + module->addEquiv(NEW_ID, new_gold_wire[i], new_gold_wire[i], gold_sig[i]); + gg_conn.first.append(gate_sig[i]); + gg_conn.second.append(gold_sig[i]); + gold_sig[i] = new_gold_wire[i]; + gate_sig[i] = new_gate_wire[i]; + } + + module->connect(gg_conn); + gold_cell->setPort(port, gold_sig); + gate_cell->setPort(port, gate_sig); + continue; + } + } } + else + { + if (GetSize(args) != 3) + cmd_error(args, GetSize(args)-1, "Invalid number of arguments."); - for (auto cell : module->cells()) - for (auto conn : cell->connections()) - if (!added_equiv_cells.count(cell) && cell->input(conn.first)) { - SigSpec new_sig; - for (auto bit : conn.second) - if (to_equiv_bits.count(sigmap(bit))) - new_sig.append(to_equiv_bits.at(sigmap(bit))); - else - new_sig.append(bit); - if (conn.second != new_sig) - cell->setPort(conn.first, new_sig); + SigSpec gold_signal, gate_signal; + + if (!SigSpec::parse(gate_signal, module, args[2])) + log_cmd_error("Error in gate signal: %s\n", args[2].c_str()); + + if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1])) + log_cmd_error("Error in gold signal: %s\n", args[1].c_str()); + + log_assert(GetSize(gold_signal) == GetSize(gate_signal)); + SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal)); + + SigMap sigmap(module); + sigmap.apply(gold_signal); + sigmap.apply(gate_signal); + + dict<SigBit, SigBit> to_equiv_bits; + pool<Cell*> added_equiv_cells; + + for (int i = 0; i < GetSize(gold_signal); i++) { + Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]); + equiv_cell->set_bool_attribute("\\keep"); + to_equiv_bits[gold_signal[i]] = equiv_signal[i]; + to_equiv_bits[gate_signal[i]] = equiv_signal[i]; + added_equiv_cells.insert(equiv_cell); } + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) + if (!added_equiv_cells.count(cell) && cell->input(conn.first)) { + SigSpec new_sig; + for (auto bit : conn.second) + if (to_equiv_bits.count(sigmap(bit))) + new_sig.append(to_equiv_bits.at(sigmap(bit))); + else + new_sig.append(bit); + if (conn.second != new_sig) + cell->setPort(conn.first, new_sig); + } + } } } EquivAddPass; diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc index a536fe308..cdb951ec9 100644 --- a/passes/equiv/equiv_induct.cc +++ b/passes/equiv/equiv_induct.cc @@ -59,8 +59,8 @@ struct EquivInductWorker cell_warn_cache.insert(cell); } if (cell->type == "$equiv") { - SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit(); - SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit(); + SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit(); + SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit(); if (bit_a != bit_b) { int ez_a = satgen.importSigBit(bit_a, step); int ez_b = satgen.importSigBit(bit_b, step); @@ -137,8 +137,8 @@ struct EquivInductWorker for (auto cell : workset) { - SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit(); - SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit(); + SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit(); + SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit(); log(" Trying to prove $equiv for %s:", log_signal(sigmap(cell->getPort("\\Y")))); diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index c001fdbfe..8b063c542 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -280,7 +280,7 @@ struct EquivMakeWorker for (auto c : cells_list) for (auto &conn : c->connections()) - if (ct.cell_input(c->type, conn.first)) { + if (!ct.cell_output(c->type, conn.first)) { SigSpec old_sig = assign_map(conn.second); SigSpec new_sig = rd_signal_map(old_sig); if (old_sig != new_sig) { diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc new file mode 100644 index 000000000..3e9819d1a --- /dev/null +++ b/passes/equiv/equiv_mark.cc @@ -0,0 +1,239 @@ +/* + * 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 EquivMarkWorker +{ + Module *module; + SigMap sigmap; + + // cache for traversing signal flow graph + dict<SigBit, pool<IdString>> up_bit2cells; + dict<IdString, pool<SigBit>> up_cell2bits; + pool<IdString> edge_cells, equiv_cells; + + // graph traversal state + pool<SigBit> queue, visited; + + // assigned regions + dict<IdString, int> cell_regions; + dict<SigBit, int> bit_regions; + int next_region; + + // merge-find + mfp<int> region_mf; + + EquivMarkWorker(Module *module) : module(module), sigmap(module) + { + for (auto cell : module->cells()) + { + if (cell->type == "$equiv") + equiv_cells.insert(cell->name); + + for (auto &port : cell->connections()) + { + if (cell->input(port.first)) + for (auto bit : sigmap(port.second)) + up_cell2bits[cell->name].insert(bit); + + if (cell->output(port.first)) + for (auto bit : sigmap(port.second)) + up_bit2cells[bit].insert(cell->name); + } + } + + next_region = 0; + } + + void mark() + { + while (!queue.empty()) + { + pool<IdString> cells; + + for (auto &bit : queue) + { + // log_assert(bit_regions.count(bit) == 0); + bit_regions[bit] = next_region; + visited.insert(bit); + + for (auto cell : up_bit2cells[bit]) + if (edge_cells.count(cell) == 0) + cells.insert(cell); + } + + queue.clear(); + + for (auto cell : cells) + { + if (next_region == 0 && equiv_cells.count(cell)) + continue; + + if (cell_regions.count(cell)) { + if (cell_regions.at(cell) != 0) + region_mf.merge(cell_regions.at(cell), next_region); + continue; + } + + cell_regions[cell] = next_region; + + for (auto bit : up_cell2bits[cell]) + if (visited.count(bit) == 0) + queue.insert(bit); + } + } + + next_region++; + } + + void run() + { + log("Running equiv_mark on module %s:\n", log_id(module)); + + // marking region 0 + + for (auto wire : module->wires()) + if (wire->port_id > 0) + for (auto bit : sigmap(wire)) + queue.insert(bit); + + for (auto cell_name : equiv_cells) + { + auto cell = module->cell(cell_name); + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + + if (sig_a == sig_b) { + for (auto bit : sig_a) + queue.insert(bit); + edge_cells.insert(cell_name); + cell_regions[cell_name] = 0; + } + } + + mark(); + + // marking unsolved regions + + for (auto cell : module->cells()) + { + if (cell_regions.count(cell->name) || cell->type != "$equiv") + continue; + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + + log_assert(sig_a != sig_b); + + for (auto bit : sig_a) + queue.insert(bit); + + for (auto bit : sig_b) + queue.insert(bit); + + cell_regions[cell->name] = next_region; + mark(); + } + + // setting attributes + + dict<int, int> final_region_map; + int next_final_region = 0; + + dict<int, int> region_cell_count; + dict<int, int> region_wire_count; + + for (int i = 0; i < next_region; i++) { + int r = region_mf.find(i); + if (final_region_map.count(r) == 0) + final_region_map[r] = next_final_region++; + final_region_map[i] = final_region_map[r]; + } + + for (auto cell : module->cells()) + { + if (cell_regions.count(cell->name)) { + int r = final_region_map.at(cell_regions.at(cell->name)); + cell->attributes["\\equiv_region"] = Const(r); + region_cell_count[r]++; + } else + cell->attributes.erase("\\equiv_region"); + } + + for (auto wire : module->wires()) + { + pool<int> regions; + for (auto bit : sigmap(wire)) + if (bit_regions.count(bit)) + regions.insert(region_mf.find(bit_regions.at(bit))); + + if (GetSize(regions) == 1) { + int r = final_region_map.at(*regions.begin()); + wire->attributes["\\equiv_region"] = Const(r); + region_wire_count[r]++; + } else + wire->attributes.erase("\\equiv_region"); + } + + for (int i = 0; i < next_final_region; i++) + log(" region %d: %d cells, %d wires\n", i, region_wire_count[i], region_cell_count[i]); + } +}; + +struct EquivMarkPass : public Pass { + EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_mark [options] [selection]\n"); + log("\n"); + log("This command marks the regions in an equivalence checking module. Region 0 is\n"); + log("the proven part of the circuit. Regions with higher numbers are connected\n"); + log("unproven subcricuits. The integer attribute 'equiv_region' is set on all\n"); + log("wires and cells.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + log_header("Executing EQUIV_MARK pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-foobar") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) { + EquivMarkWorker worker(module); + worker.run(); + } + } +} EquivMarkPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc index 34318dec2..982176c44 100644 --- a/passes/equiv/equiv_miter.cc +++ b/passes/equiv/equiv_miter.cc @@ -156,7 +156,7 @@ struct EquivMiterWorker struct RewriteSigSpecWorker { RTLIL::Module * mod; void operator()(SigSpec &sig) { - vector<RTLIL::SigChunk> chunks = sig.chunks(); + vector<SigChunk> chunks = sig.chunks(); for (auto &c : chunks) if (c.wire != NULL) c.wire = mod->wires_.at(c.wire->name); diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc new file mode 100644 index 000000000..f4141ad4d --- /dev/null +++ b/passes/equiv/equiv_purge.cc @@ -0,0 +1,210 @@ +/* + * 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 EquivPurgeWorker +{ + Module *module; + SigMap sigmap; + int name_cnt; + + EquivPurgeWorker(Module *module) : module(module), sigmap(module), name_cnt(0) { } + + SigSpec make_output(SigSpec sig, IdString cellname) + { + if (sig.is_wire()) { + Wire *wire = sig.as_wire(); + if (wire->name[0] == '\\') { + if (!wire->port_output) { + log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname)); + wire->port_output = true; + } + return wire; + } + } + + while (1) + { + IdString name = stringf("\\equiv_%d", name_cnt++); + if (module->count_id(name)) + continue; + + Wire *wire = module->addWire(name, GetSize(sig)); + wire->port_output = true; + module->connect(wire, sig); + log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname)); + return wire; + } + } + + SigSpec make_input(SigSpec sig) + { + if (sig.is_wire()) { + Wire *wire = sig.as_wire(); + if (wire->name[0] == '\\') { + if (!wire->port_output) { + log(" Module input: %s\n", log_signal(wire)); + wire->port_input = true; + } + return module->addWire(NEW_ID, GetSize(sig)); + } + } + + while (1) + { + IdString name = stringf("\\equiv_%d", name_cnt++); + if (module->count_id(name)) + continue; + + Wire *wire = module->addWire(name, GetSize(sig)); + wire->port_input = true; + module->connect(sig, wire); + log(" Module input: %s\n", log_signal(wire)); + return module->addWire(NEW_ID, GetSize(sig)); + } + } + + void run() + { + log("Running equiv_purge on module %s:\n", log_id(module)); + + for (auto wire : module->wires()) { + wire->port_input = false; + wire->port_output = false; + } + + pool<SigBit> queue, visited; + + // cache for traversing signal flow graph + dict<SigBit, pool<IdString>> up_bit2cells; + dict<IdString, pool<SigBit>> up_cell2bits; + + for (auto cell : module->cells()) + { + if (cell->type != "$equiv") { + for (auto &port : cell->connections()) { + if (cell->input(port.first)) + for (auto bit : sigmap(port.second)) + up_cell2bits[cell->name].insert(bit); + if (cell->output(port.first)) + for (auto bit : sigmap(port.second)) + up_bit2cells[bit].insert(cell->name); + } + continue; + } + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + if (sig_a == sig_b) + continue; + + for (auto bit : sig_a) + queue.insert(bit); + + for (auto bit : sig_b) + queue.insert(bit); + + for (auto bit : sig_y) + visited.insert(bit); + + cell->setPort("\\Y", make_output(sig_y, cell->name)); + } + + SigSpec srcsig; + SigMap rewrite_sigmap(module); + + while (!queue.empty()) + { + pool<SigBit> next_queue; + + for (auto bit : queue) + visited.insert(bit); + + for (auto bit : queue) + { + auto &cells = up_bit2cells[bit]; + + if (cells.empty()) { + srcsig.append(bit); + } else { + for (auto cell : cells) + for (auto bit : up_cell2bits[cell]) + if (visited.count(bit) == 0) + next_queue.insert(bit); + } + } + + next_queue.swap(queue); + } + + srcsig.sort_and_unify(); + + for (SigChunk chunk : srcsig.chunks()) + if (chunk.wire != nullptr) + rewrite_sigmap.add(chunk, make_input(chunk)); + + for (auto cell : module->cells()) + if (cell->type == "$equiv") + cell->setPort("\\Y", rewrite_sigmap(sigmap(cell->getPort("\\Y")))); + + module->fixup_ports(); + } +}; + +struct EquivPurgePass : public Pass { + EquivPurgePass() : Pass("equiv_purge", "purge equivalence checking module") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_purge [options] [selection]\n"); + log("\n"); + log("This command removes the proven part of an equivalence checking module, leaving\n"); + log("only the unproven segments in the design. This will also remove and add module\n"); + log("ports as needed.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + log_header("Executing EQUIV_PURGE pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-foobar") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) { + EquivPurgeWorker worker(module); + worker.run(); + } + } +} EquivPurgePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc index 1f52a6321..fa22dc621 100644 --- a/passes/equiv/equiv_simple.cc +++ b/passes/equiv/equiv_simple.cc @@ -89,8 +89,8 @@ struct EquivSimpleWorker bool run_cell() { - SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).to_single_sigbit(); - SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).to_single_sigbit(); + SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).as_bit(); + SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).as_bit(); int ez_context = ez->frozen_literal(); if (satgen.model_undef) @@ -314,7 +314,7 @@ struct EquivSimplePass : public Pass { for (auto cell : module->selected_cells()) if (cell->type == "$equiv" && cell->getPort("\\A") != cell->getPort("\\B")) { - auto bit = sigmap(cell->getPort("\\Y").to_single_sigbit()); + auto bit = sigmap(cell->getPort("\\Y").as_bit()); auto bit_group = bit; if (!nogroup && bit_group.wire) bit_group.offset = 0; diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc new file mode 100644 index 000000000..9f962d7ec --- /dev/null +++ b/passes/equiv/equiv_struct.cc @@ -0,0 +1,336 @@ +/* + * 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 EquivStructWorker +{ + Module *module; + SigMap sigmap; + SigMap equiv_bits; + bool mode_fwd; + bool mode_icells; + int merge_count; + + struct merge_key_t + { + IdString type; + vector<pair<IdString, Const>> parameters; + vector<pair<IdString, int>> port_sizes; + vector<tuple<IdString, int, SigBit>> connections; + + bool operator==(const merge_key_t &other) const { + return type == other.type && connections == other.connections && + parameters == other.parameters && port_sizes == other.port_sizes; + } + + unsigned int hash() const { + unsigned int h = mkhash_init; + h = mkhash(h, mkhash(type)); + h = mkhash(h, mkhash(parameters)); + h = mkhash(h, mkhash(connections)); + return h; + } + }; + + dict<merge_key_t, pool<IdString>> merge_cache; + pool<merge_key_t> fwd_merge_cache, bwd_merge_cache; + + void merge_cell_pair(Cell *cell_a, Cell *cell_b) + { + SigMap merged_map; + merge_count++; + + SigSpec inputs_a, inputs_b; + vector<string> input_names; + + for (auto &port_a : cell_a->connections()) + { + SigSpec bits_a = sigmap(port_a.second); + SigSpec bits_b = sigmap(cell_b->getPort(port_a.first)); + + log_assert(GetSize(bits_a) == GetSize(bits_b)); + + if (!cell_a->output(port_a.first)) + for (int i = 0; i < GetSize(bits_a); i++) + if (bits_a[i] != bits_b[i]) { + inputs_a.append(bits_a[i]); + inputs_b.append(bits_b[i]); + input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() : + stringf("%s[%d]", log_id(port_a.first), i)); + } + } + + for (int i = 0; i < GetSize(inputs_a); i++) { + SigBit bit_a = inputs_a[i], bit_b = inputs_b[i]; + SigBit bit_y = module->addWire(NEW_ID); + log(" New $equiv for input %s: A: %s, B: %s, Y: %s\n", + input_names[i].c_str(), log_signal(bit_a), log_signal(bit_b), log_signal(bit_y)); + module->addEquiv(NEW_ID, bit_a, bit_b, bit_y); + merged_map.add(bit_a, bit_y); + merged_map.add(bit_b, bit_y); + } + + std::vector<IdString> outport_names, inport_names; + + for (auto &port_a : cell_a->connections()) + if (cell_a->output(port_a.first)) + outport_names.push_back(port_a.first); + else + inport_names.push_back(port_a.first); + + for (auto &pn : inport_names) + cell_a->setPort(pn, merged_map(sigmap(cell_a->getPort(pn)))); + + for (auto &pn : outport_names) { + SigSpec sig_a = cell_a->getPort(pn); + SigSpec sig_b = cell_b->getPort(pn); + module->connect(sig_b, sig_a); + } + + auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged"); + merged_attr.insert(log_id(cell_b)); + cell_a->add_strpool_attribute("\\equiv_merged", merged_attr); + module->remove(cell_b); + } + + EquivStructWorker(Module *module, bool mode_fwd, bool mode_icells) : + module(module), sigmap(module), equiv_bits(module), + mode_fwd(mode_fwd), mode_icells(mode_icells), merge_count(0) + { + log(" Starting new iteration.\n"); + + pool<SigBit> equiv_inputs; + pool<IdString> cells; + + for (auto cell : module->selected_cells()) + if (cell->type == "$equiv") { + SigBit sig_a = sigmap(cell->getPort("\\A").as_bit()); + SigBit sig_b = sigmap(cell->getPort("\\B").as_bit()); + equiv_bits.add(sig_b, sig_a); + equiv_inputs.insert(sig_a); + equiv_inputs.insert(sig_b); + cells.insert(cell->name); + } else { + if (mode_icells || module->design->module(cell->type)) + cells.insert(cell->name); + } + + for (auto cell : module->selected_cells()) + if (cell->type == "$equiv") { + SigBit sig_a = sigmap(cell->getPort("\\A").as_bit()); + SigBit sig_b = sigmap(cell->getPort("\\B").as_bit()); + SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit()); + if (sig_a == sig_b && equiv_inputs.count(sig_y)) { + log(" Purging redundant $equiv cell %s.\n", log_id(cell)); + module->remove(cell); + merge_count++; + } + } + + if (merge_count > 0) + return; + + for (auto cell_name : cells) + { + merge_key_t key; + vector<tuple<IdString, int, SigBit>> fwd_connections; + + Cell *cell = module->cell(cell_name); + key.type = cell->type; + + for (auto &it : cell->parameters) + key.parameters.push_back(it); + std::sort(key.parameters.begin(), key.parameters.end()); + + for (auto &it : cell->connections()) + key.port_sizes.push_back(make_pair(it.first, GetSize(it.second))); + std::sort(key.port_sizes.begin(), key.port_sizes.end()); + + for (auto &conn : cell->connections()) + { + if (cell->input(conn.first)) { + SigSpec sig = sigmap(conn.second); + for (int i = 0; i < GetSize(sig); i++) + fwd_connections.push_back(make_tuple(conn.first, i, sig[i])); + } + + if (cell->output(conn.first)) { + SigSpec sig = equiv_bits(conn.second); + for (int i = 0; i < GetSize(sig); i++) { + key.connections.clear(); + key.connections.push_back(make_tuple(conn.first, i, sig[i])); + + if (merge_cache.count(key)) + bwd_merge_cache.insert(key); + merge_cache[key].insert(cell_name); + } + } + } + + std::sort(fwd_connections.begin(), fwd_connections.end()); + key.connections.swap(fwd_connections); + + if (merge_cache.count(key)) + fwd_merge_cache.insert(key); + merge_cache[key].insert(cell_name); + } + + for (int phase = 0; phase < 2; phase++) + { + auto &queue = phase ? bwd_merge_cache : fwd_merge_cache; + + for (auto &key : queue) + { + const char *strategy = nullptr; + vector<Cell*> gold_cells, gate_cells, other_cells; + vector<pair<Cell*, Cell*>> cell_pairs; + + for (auto cell_name : merge_cache[key]) { + Cell *c = module->cell(cell_name); + if (c != nullptr) { + string n = cell_name.str(); + if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gold") + gold_cells.push_back(c); + else if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gate") + gate_cells.push_back(c); + else + other_cells.push_back(c); + } + } + + if (GetSize(gold_cells) > 1 || GetSize(gate_cells) > 1 || GetSize(other_cells) > 1) + { + strategy = "deduplicate"; + for (int i = 0; i+1 < GetSize(gold_cells); i += 2) + cell_pairs.push_back(make_pair(gold_cells[i], gold_cells[i+1])); + for (int i = 0; i+1 < GetSize(gate_cells); i += 2) + cell_pairs.push_back(make_pair(gate_cells[i], gate_cells[i+1])); + for (int i = 0; i+1 < GetSize(other_cells); i += 2) + cell_pairs.push_back(make_pair(other_cells[i], other_cells[i+1])); + goto run_strategy; + } + + if (GetSize(gold_cells) == 1 && GetSize(gate_cells) == 1) + { + strategy = "gold-gate-pairs"; + cell_pairs.push_back(make_pair(gold_cells[0], gate_cells[0])); + goto run_strategy; + } + + if (GetSize(gold_cells) == 1 && GetSize(other_cells) == 1) + { + strategy = "gold-guess"; + cell_pairs.push_back(make_pair(gold_cells[0], other_cells[0])); + goto run_strategy; + } + + if (GetSize(other_cells) == 1 && GetSize(gate_cells) == 1) + { + strategy = "gate-guess"; + cell_pairs.push_back(make_pair(other_cells[0], gate_cells[0])); + goto run_strategy; + } + + log_assert(GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells) < 2); + continue; + + run_strategy: + log(" %s merging %d cells (from group of %d) using strategy %s:\n", phase ? "Bwd" : "Fwd", + 2*GetSize(cell_pairs), GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells), strategy); + for (auto it : cell_pairs) { + log(" Merging cells %s and %s.\n", log_id(it.first), log_id(it.second)); + merge_cell_pair(it.first, it.second); + } + } + + if (merge_count > 0) + return; + } + + log(" Nothing to merge.\n"); + } +}; + +struct EquivStructPass : public Pass { + EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_struct [options] [selection]\n"); + log("\n"); + log("This command adds additional $equiv cells based on the assumption that the\n"); + log("gold and gate circuit are structurally equivalent. Note that this can introduce\n"); + log("bad $equiv cells in cases where the netlists are not structurally equivalent,\n"); + log("for example when analyzing circuits with cells with commutative inputs. This\n"); + log("command will also de-duplicate gates.\n"); + log("\n"); + log(" -fwd\n"); + log(" by default this command performans forward sweeps until nothing can\n"); + log(" be merged by forwards sweeps, the backward sweeps until forward\n"); + log(" sweeps are effective again. with this option set only forward sweeps\n"); + log(" are performed.\n"); + log("\n"); + log(" -icells\n"); + log(" by default, the internal RTL and gate cell types are ignored. add\n"); + log(" this option to also process those cell types with this command.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + bool mode_icells = false; + bool mode_fwd = false; + + log_header("Executing EQUIV_STRUCT pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-fwd") { + mode_fwd = true; + continue; + } + if (args[argidx] == "-icells") { + mode_icells = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + int module_merge_count = 0; + log("Running equiv_struct on module %s:\n", log_id(module)); + while (1) { + EquivStructWorker worker(module, mode_fwd, mode_icells); + if (worker.merge_count == 0) + break; + module_merge_count += worker.merge_count; + } + if (module_merge_count) + log(" Performed a total of %d merges in module %s.\n", module_merge_count, log_id(module)); + } + } +} EquivStructPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index 7a621b567..740113e35 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -34,7 +34,7 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user; static std::set<RTLIL::Cell*> muxtree_cells; static SigPool sig_at_port; -static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, SigPool &recursion_monitor) +static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor) { if (sig_at_port.check_any(assign_map(sig))) return false; @@ -42,31 +42,39 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, Sig if (sig.is_fully_const() || old_sig == sig) return true; - if (recursion_monitor.check_any(sig)) { - log_warning("logic loop in mux tree at signal %s in module %s.\n", - log_signal(sig), RTLIL::id2cstr(module->name)); - return false; - } - - recursion_monitor.add(sig); - std::set<sig2driver_entry_t> cellport_list; sig2driver.find(sig, cellport_list); - for (auto &cellport : cellport_list) { + for (auto &cellport : cellport_list) + { if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") return false; + + if (recursion_monitor.count(cellport.first)) { + log_warning("logic loop in mux tree at signal %s in module %s.\n", + log_signal(sig), RTLIL::id2cstr(module->name)); + return false; + } + + recursion_monitor.insert(cellport.first); + RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A")); RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B")); - if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) + + if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) { + recursion_monitor.erase(cellport.first); return false; + } + for (int i = 0; i < sig_b.size(); i += sig_a.size()) - if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) + if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) { + recursion_monitor.erase(cellport.first); return false; + } + + recursion_monitor.erase(cellport.first); muxtree_cells.insert(cellport.first); } - recursion_monitor.del(sig); - return true; } @@ -81,6 +89,8 @@ static bool check_state_users(RTLIL::SigSpec sig) RTLIL::Cell *cell = cellport.first; if (muxtree_cells.count(cell) > 0) continue; + if (cell->type == "$logic_not" && assign_map(cell->getPort("\\A")) == sig) + continue; if (cellport.second != "\\A" && cellport.second != "\\B") return false; if (!cell->hasPort("\\A") || !cell->hasPort("\\B") || !cell->hasPort("\\Y")) @@ -100,6 +110,8 @@ static bool check_state_users(RTLIL::SigSpec sig) static void detect_fsm(RTLIL::Wire *wire) { + if (wire->attributes.count("\\init") > 0) + return; if (wire->attributes.count("\\fsm_encoding") > 0 || wire->width <= 1) return; if (sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire)))) @@ -111,7 +123,7 @@ static void detect_fsm(RTLIL::Wire *wire) if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q") continue; muxtree_cells.clear(); - SigPool recursion_monitor; + pool<Cell*> recursion_monitor; RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q")); RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D")); if (sig_q == RTLIL::SigSpec(wire) && check_state_mux_tree(sig_q, sig_d, recursion_monitor) && check_state_users(sig_q)) { diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc index 7322368cd..a7cc95ffa 100644 --- a/passes/fsm/fsm_opt.cc +++ b/passes/fsm/fsm_opt.cc @@ -78,7 +78,7 @@ struct FsmOpt bool signal_is_unused(RTLIL::SigSpec sig) { - RTLIL::SigBit bit = sig.to_single_sigbit(); + RTLIL::SigBit bit = sig.as_bit(); if (bit.wire == NULL || bit.wire->attributes.count("\\unused_bits") == 0) return false; diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h index 1b98ccbac..68222769a 100644 --- a/passes/fsm/fsmdata.h +++ b/passes/fsm/fsmdata.h @@ -39,7 +39,7 @@ struct FsmData int state_num_log2 = 0; for (int i = state_table.size(); i > 0; i = i >> 1) state_num_log2++; - state_num_log2 = std::max(state_num_log2, 1); + state_num_log2 = max(state_num_log2, 1); cell->parameters["\\STATE_BITS"] = RTLIL::Const(state_bits); cell->parameters["\\STATE_NUM"] = RTLIL::Const(state_table.size()); diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index 99aa1e116..1fb669c11 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -1,4 +1,5 @@ OBJS += passes/hierarchy/hierarchy.o +OBJS += passes/hierarchy/singleton.o OBJS += passes/hierarchy/submod.o diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 598fe9396..fcc30d175 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -66,7 +66,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, for (auto &conn : i2.second->connections()) { if (conn.first[0] != '$') portnames.insert(conn.first); - portwidths[conn.first] = std::max(portwidths[conn.first], conn.second.size()); + portwidths[conn.first] = max(portwidths[conn.first], conn.second.size()); } for (auto ¶ : i2.second->parameters) parameters.insert(para.first); @@ -84,8 +84,8 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, for (auto &decl : portdecls) if (decl.index > 0) { - portwidths[decl.portname] = std::max(portwidths[decl.portname], 1); - portwidths[decl.portname] = std::max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]); + portwidths[decl.portname] = max(portwidths[decl.portname], 1); + portwidths[decl.portname] = max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]); log(" port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::id2cstr(decl.portname)); if (indices.count(decl.index) > ports.size()) log_error("Port index (%d) exceeds number of found ports (%d).\n", decl.index, int(ports.size())); @@ -106,7 +106,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, log_assert(!indices.empty()); indices.erase(d.index); ports[d.index-1] = d; - portwidths[d.portname] = std::max(portwidths[d.portname], 1); + portwidths[d.portname] = max(portwidths[d.portname], 1); log(" port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::id2cstr(d.portname)); goto found_matching_decl; } @@ -327,7 +327,7 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db) db[module] = 0; for (auto cell : module->cells()) if (design->module(cell->type)) - db[module] = std::max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1); + db[module] = max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1); } return db.at(module); } diff --git a/passes/hierarchy/singleton.cc b/passes/hierarchy/singleton.cc new file mode 100644 index 000000000..5715c0eb1 --- /dev/null +++ b/passes/hierarchy/singleton.cc @@ -0,0 +1,101 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct SingletonPass : public Pass { + SingletonPass() : Pass("singleton", "create singleton modules") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" singleton [selection]\n"); + log("\n"); + log("By default, a module that is instantiated by several other modules is only\n"); + log("kept once in the design. This preserves the original modularity of the design\n"); + log("and reduces the overall size of the design in memory. But it prevents certain\n"); + log("optimizations and other operations on the design. This pass creates singleton\n"); + log("modules for all selected cells. The created modules are marked with the\n"); + log("'singleton' attribute.\n"); + log("\n"); + log("This commands only operates on modules that by themself have the 'singleton'\n"); + log("attribute set (the 'top' module is a singleton implicitly).\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("Executing SINGLETON pass (creating singleton modules).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-check") { + // flag_check = true; + // continue; + // } + } + extra_args(args, argidx, design); + + bool did_something = true; + int singleton_cnt = 0; + + while (did_something) + { + did_something = false; + + for (auto module : design->selected_modules()) + { + if (!module->get_bool_attribute("\\singleton") && !module->get_bool_attribute("\\top")) + continue; + + for (auto cell : module->selected_cells()) + { + auto tmod = design->module(cell->type); + + if (tmod == nullptr) + continue; + + if (tmod->get_bool_attribute("\\blackbox")) + continue; + + if (tmod->get_bool_attribute("\\singleton")) + continue; + + cell->type = module->name.str() + "." + log_id(cell->name); + log("Creating singleton '%s'.\n", log_id(cell->type)); + + auto smod = tmod->clone(); + smod->name = cell->type; + smod->set_bool_attribute("\\singleton"); + design->add(smod); + + did_something = true; + singleton_cnt++; + } + } + } + + log("Created %d singleton modules.\n", singleton_cnt); + } +} SingletonPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index 824d6a6e8..f2d9b5847 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -112,15 +112,15 @@ struct rules_t if (ports[i] != other.ports[i]) log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i); if (wrmode[i] != other.wrmode[i]) - variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[1]; + variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i]; if (enable[i] != other.enable[i]) - variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[1]; + variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[i]; if (transp[i] != other.transp[i]) - variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[1]; + variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[i]; if (clocks[i] != other.clocks[i]) - variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[1]; + variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[i]; if (clkpol[i] != other.clkpol[i]) - variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[1]; + variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i]; } } }; @@ -387,9 +387,9 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, if (pi.clkpol > 1) clkpol_wr_ports.insert(pi.clkpol); } - clocks_max = std::max(clocks_max, pi.clocks); - clkpol_max = std::max(clkpol_max, pi.clkpol); - transp_max = std::max(transp_max, pi.transp); + clocks_max = max(clocks_max, pi.clocks); + clkpol_max = max(clkpol_max, pi.clkpol); + transp_max = max(transp_max, pi.transp); } log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant); @@ -429,11 +429,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, rd_clkpol.extend_u0(rd_ports); rd_transp.extend_u0(rd_ports); + SigSpec rd_en = cell->getPort("\\RD_EN"); SigSpec rd_clk = cell->getPort("\\RD_CLK"); SigSpec rd_data = cell->getPort("\\RD_DATA"); SigSpec rd_addr = cell->getPort("\\RD_ADDR"); - if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0) + if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0) { int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable; log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size); @@ -688,6 +689,10 @@ grow_read_ports:; log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } + if (rd_en[cell_port_i] != State::S1 && pi.enable == 0) { + log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + goto skip_bram_rport; + } skip_bram_rport_clkcheck: if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) { if (match.make_transp && wr_ports <= 1) { @@ -713,6 +718,7 @@ grow_read_ports:; clock_polarities[pi.clkpol] = clkdom.second; read_transp[pi.transp] = transp; pi.sig_clock = clkdom.first; + pi.sig_en = rd_en[cell_port_i]; pi.effective_clkpol = clkdom.second; } @@ -853,8 +859,11 @@ grow_read_ports:; if (pi.enable) { SigSpec sig_en = pi.sig_en; - sig_en.extend_u0((grid_d+1) * pi.enable); - sig_en = sig_en.extract(grid_d * pi.enable, pi.enable); + + if (pi.wrmode == 1) { + sig_en.extend_u0((grid_d+1) * pi.enable); + sig_en = sig_en.extract(grid_d * pi.enable, pi.enable); + } if (!addr_ok.empty()) sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok); @@ -886,6 +895,8 @@ grow_read_ports:; 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; } @@ -966,7 +977,7 @@ void handle_cell(Cell *cell, const rules_t &rules) log("\n"); pool<pair<IdString, int>> failed_brams; - dict<pair<int, int>, std::tuple<int, int, int>> best_rule_cache; + dict<pair<int, int>, tuple<int, int, int>> best_rule_cache; for (int i = 0; i < GetSize(rules.matches); i++) { @@ -1067,7 +1078,7 @@ void handle_cell(Cell *cell, const rules_t &rules) } log(" Storing for later selection.\n"); - best_rule_cache[pair<int, int>(i, vi)] = std::tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]); + best_rule_cache[pair<int, int>(i, vi)] = tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]); next_match_rule: if (or_next_if_better || best_rule_cache.empty()) @@ -1126,7 +1137,7 @@ struct MemoryBramPass : public Pass { log(" groups 2 # number of port groups\n"); log(" ports 1 1 # number of ports in each group\n"); log(" wrmode 1 0 # set to '1' if this groups is write ports\n"); - log(" enable 4 0 # number of enable bits (for write ports)\n"); + log(" enable 4 1 # number of enable bits\n"); log(" transp 0 2 # transparent (for read ports)\n"); log(" clocks 1 2 # clock configuration\n"); log(" clkpol 2 2 # clock polarity configuration\n"); diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index 91b5759eb..5c0acb3e5 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc @@ -57,13 +57,14 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory) SigSpec sig_rd_transparent; SigSpec sig_rd_addr; SigSpec sig_rd_data; + SigSpec sig_rd_en; std::vector<Cell*> memcells; for (auto &cell_it : module->cells_) { Cell *cell = cell_it.second; if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string()) { - addr_bits = std::max(addr_bits, cell->getParam("\\ABITS").as_int()); + addr_bits = max(addr_bits, cell->getParam("\\ABITS").as_int()); memcells.push_back(cell); } } @@ -139,22 +140,27 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory) SigSpec transparent = SigSpec(cell->parameters["\\TRANSPARENT"]); SigSpec addr = sigmap(cell->getPort("\\ADDR")); SigSpec data = sigmap(cell->getPort("\\DATA")); + SigSpec en = sigmap(cell->getPort("\\EN")); - clk.extend_u0(1, false); - clk_enable.extend_u0(1, false); - clk_polarity.extend_u0(1, false); - transparent.extend_u0(1, false); - addr.extend_u0(addr_bits, false); - data.extend_u0(memory->width, false); - - sig_rd_clk.append(clk); - sig_rd_clk_enable.append(clk_enable); - sig_rd_clk_polarity.append(clk_polarity); - sig_rd_transparent.append(transparent); - sig_rd_addr.append(addr); - sig_rd_data.append(data); - - rd_ports++; + if (!en.is_fully_zero()) + { + clk.extend_u0(1, false); + clk_enable.extend_u0(1, false); + clk_polarity.extend_u0(1, false); + transparent.extend_u0(1, false); + addr.extend_u0(addr_bits, false); + data.extend_u0(memory->width, false); + + sig_rd_clk.append(clk); + sig_rd_clk_enable.append(clk_enable); + sig_rd_clk_polarity.append(clk_polarity); + sig_rd_transparent.append(transparent); + sig_rd_addr.append(addr); + sig_rd_data.append(data); + sig_rd_en.append(en); + + rd_ports++; + } continue; } } @@ -203,6 +209,7 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory) mem->setPort("\\RD_CLK", sig_rd_clk); mem->setPort("\\RD_ADDR", sig_rd_addr); mem->setPort("\\RD_DATA", sig_rd_data); + mem->setPort("\\RD_EN", sig_rd_en); for (auto c : memcells) module->remove(c); diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index 5584f27c3..2eec0207b 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -31,6 +31,8 @@ struct MemoryDffWorker vector<Cell*> dff_cells; dict<SigBit, SigBit> invbits; dict<SigBit, int> sigbit_users_count; + dict<SigSpec, Cell*> mux_cells_a, mux_cells_b; + pool<Cell*> forward_merged_dffs, candidate_dffs; MemoryDffWorker(Module *module) : module(module), sigmap(module) { } @@ -45,6 +47,9 @@ struct MemoryDffWorker for (auto cell : dff_cells) { + if (after && forward_merged_dffs.count(cell)) + continue; + SigSpec this_clk = cell->getPort("\\CLK"); bool this_clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool(); @@ -70,6 +75,7 @@ struct MemoryDffWorker bit = d; clk = this_clk; clk_polarity = this_clk_polarity; + candidate_dffs.insert(cell); goto replaced_this_bit; } @@ -86,6 +92,7 @@ struct MemoryDffWorker RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx); bool clk_polarity = 0; + candidate_dffs.clear(); RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) { @@ -105,13 +112,18 @@ struct MemoryDffWorker return; } - if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { + if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) + { + for (auto cell : candidate_dffs) + forward_merged_dffs.insert(cell); + cell->setPort("\\CLK", clk); cell->setPort("\\ADDR", sig_addr); cell->setPort("\\DATA", sig_data); cell->setPort("\\EN", sig_en); cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + log("merged $dff to cell.\n"); return; } @@ -150,16 +162,44 @@ struct MemoryDffWorker if (sigbit_users_count[bit] > 1) goto skip_ff_after_read_merging; - if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx)) + if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data)) { - disconnect_dff(sig_data); - cell->setPort("\\CLK", clk_data); - cell->setPort("\\DATA", sig_data); - cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); - cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); - cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); - log("merged data $dff to cell.\n"); - return; + bool enable_invert = mux_cells_a.count(sig_data); + Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data); + SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A")); + + sig_data = sigmap(mux->getPort("\\Y")); + for (auto bit : sig_data) + if (sigbit_users_count[bit] > 1) + goto skip_ff_after_read_merging; + + if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q) + { + disconnect_dff(sig_data); + cell->setPort("\\CLK", clk_data); + cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S")); + cell->setPort("\\DATA", sig_data); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); + log("merged data $dff with rd enable to cell.\n"); + return; + } + } + else + { + if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx)) + { + disconnect_dff(sig_data); + cell->setPort("\\CLK", clk_data); + cell->setPort("\\EN", State::S1); + cell->setPort("\\DATA", sig_data); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); + log("merged data $dff to cell.\n"); + return; + } } skip_ff_after_read_merging:; @@ -169,6 +209,7 @@ struct MemoryDffWorker clk_addr != RTLIL::SigSpec(RTLIL::State::Sx)) { cell->setPort("\\CLK", clk_addr); + cell->setPort("\\EN", State::S1); cell->setPort("\\ADDR", sig_addr); cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); @@ -191,6 +232,10 @@ struct MemoryDffWorker for (auto cell : module->cells()) { if (cell->type == "$dff") dff_cells.push_back(cell); + if (cell->type == "$mux") { + mux_cells_a[sigmap(cell->getPort("\\A"))] = cell; + mux_cells_b[sigmap(cell->getPort("\\B"))] = cell; + } if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) { SigSpec sig_a = cell->getPort("\\A"); SigSpec sig_y = cell->getPort("\\Y"); diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index 524fa8d2b..0b8ccb363 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -200,34 +200,43 @@ struct MemoryMapWorker if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1) { + RTLIL::Cell *dff_cell = nullptr; + if (cell->parameters["\\RD_TRANSPARENT"].bits[i] == RTLIL::State::S1) { - RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); - c->parameters["\\WIDTH"] = RTLIL::Const(mem_abits); - c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); - c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); - c->setPort("\\D", rd_addr); + dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); + dff_cell->parameters["\\WIDTH"] = RTLIL::Const(mem_abits); + dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); + dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); + dff_cell->setPort("\\D", rd_addr); count_dff++; RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$q"), mem_abits); - c->setPort("\\Q", RTLIL::SigSpec(w)); + dff_cell->setPort("\\Q", RTLIL::SigSpec(w)); rd_addr = RTLIL::SigSpec(w); } else { - RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); - c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; - c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); - c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); - c->setPort("\\Q", rd_signals.back()); + dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); + dff_cell->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; + dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); + dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); + dff_cell->setPort("\\Q", rd_signals.back()); count_dff++; RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$d"), mem_width); rd_signals.clear(); rd_signals.push_back(RTLIL::SigSpec(w)); - c->setPort("\\D", rd_signals.back()); + dff_cell->setPort("\\D", rd_signals.back()); + } + + SigBit en_bit = cell->getPort("\\RD_EN").extract(i); + if (en_bit != State::S1) { + SigSpec new_d = module->Mux(genid(cell->name, "$rdenmux", i), + dff_cell->getPort("\\Q"), dff_cell->getPort("\\D"), en_bit); + dff_cell->setPort("\\D", new_d); } } diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc index b8f27025a..3a6fd0b44 100644 --- a/passes/memory/memory_share.cc +++ b/passes/memory/memory_share.cc @@ -210,7 +210,7 @@ struct MemoryShareWorker if (non_feedback_nets.count(sig_data[i])) goto not_pure_feedback_port; - async_rd_bits[sig_addr].resize(std::max(async_rd_bits.size(), sig_data.size())); + async_rd_bits[sig_addr].resize(max(async_rd_bits.size(), sig_data.size())); for (int i = 0; i < int(sig_data.size()); i++) async_rd_bits[sig_addr][i].insert(sig_data[i]); diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc index a497362bf..60724da77 100644 --- a/passes/memory/memory_unpack.cc +++ b/passes/memory/memory_unpack.cc @@ -57,6 +57,7 @@ void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) cell->parameters["\\CLK_POLARITY"] = RTLIL::SigSpec(memory->parameters.at("\\RD_CLK_POLARITY")).extract(i, 1).as_const(); cell->parameters["\\TRANSPARENT"] = RTLIL::SigSpec(memory->parameters.at("\\RD_TRANSPARENT")).extract(i, 1).as_const(); cell->setPort("\\CLK", memory->getPort("\\RD_CLK").extract(i, 1)); + cell->setPort("\\EN", memory->getPort("\\RD_EN").extract(i, 1)); cell->setPort("\\ADDR", memory->getPort("\\RD_ADDR").extract(i*abits, abits)); cell->setPort("\\DATA", memory->getPort("\\RD_DATA").extract(i*mem->width, mem->width)); } diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_const.cc index 32a804260..0fcacf0a8 100644 --- a/passes/opt/opt_const.cc +++ b/passes/opt/opt_const.cc @@ -228,6 +228,37 @@ void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdStrin } } +bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative) +{ + bool all_bits_one = true; + bool last_bit_one = true; + + if (GetSize(value.bits) < 1) + return false; + + if (GetSize(value.bits) == 1) { + if (value.bits[0] != State::S1) + return false; + if (is_signed) + is_negative = true; + return true; + } + + for (int i = 0; i < GetSize(value.bits); i++) { + if (value.bits[i] != State::S1) + all_bits_one = false; + if (value.bits[i] != (i ? State::S0 : State::S1)) + last_bit_one = false; + } + + if (all_bits_one && is_signed) { + is_negative = true; + return true; + } + + return last_bit_one; +} + void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv) { if (!design->selected(module)) @@ -249,6 +280,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\A").size() == 1 && cell->getPort("\\Y").size() == 1) invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\A")); + if ((cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == SigSpec(State::S1) && cell->getPort("\\B") == SigSpec(State::S0)) + invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\S")); if (ct_combinational.cell_known(cell->type)) for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = assign_map(conn.second); @@ -556,7 +589,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec b = cell->getPort("\\B"); if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) { - int width = std::max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); + int width = max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); a.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()); b.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()); } @@ -682,6 +715,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons { bool identity_wrt_a = false; bool identity_wrt_b = false; + bool arith_inverse = false; if (cell->type == "$add" || cell->type == "$sub" || cell->type == "$or" || cell->type == "$xor") { @@ -708,10 +742,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); - if (a.is_fully_const() && a.size() <= 32 && a.as_int() == 1) + if (a.is_fully_const() && is_one_or_minus_one(a.as_const(), cell->getParam("\\A_SIGNED").as_bool(), arith_inverse)) identity_wrt_b = true; - - if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1) + else + if (b.is_fully_const() && is_one_or_minus_one(b.as_const(), cell->getParam("\\B_SIGNED").as_bool(), arith_inverse)) identity_wrt_a = true; } @@ -739,7 +773,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED"); } - cell->type = "$pos"; + cell->type = arith_inverse ? "$neg" : "$pos"; cell->unsetPort("\\B"); cell->parameters.erase("\\B_WIDTH"); cell->parameters.erase("\\B_SIGNED"); diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 15d59202e..905a01627 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -136,7 +136,7 @@ struct OptMuxtreeWorker } } for (auto wire : module->wires()) { - if (wire->port_output) + if (wire->port_output || wire->get_bool_attribute("\\keep")) for (int idx : sig2bits(RTLIL::SigSpec(wire))) bit2info[idx].seen_non_mux = true; } diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 2ecbb31a8..e1b184af3 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -28,6 +28,15 @@ PRIVATE_NAMESPACE_BEGIN SigMap assign_map, dff_init_map; SigSet<RTLIL::Cell*> mux_drivers; +dict<SigBit, pool<SigBit>> init_attributes; + +void remove_init_attr(SigSpec sig) +{ + for (auto bit : assign_map(sig)) + if (init_attributes.count(bit)) + for (auto wbit : init_attributes.at(bit)) + wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx; +} bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) { @@ -52,6 +61,7 @@ bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) delete_dlatch: log("Removing %s (%s) from module %s.\n", dlatch->name.c_str(), dlatch->type.c_str(), mod->name.c_str()); + remove_init_attr(dlatch->getPort("\\Q")); mod->remove(dlatch); return true; } @@ -145,7 +155,6 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) } if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) { - log_dump(sig_q, sig_d); mod->connect(sig_q, sig_d); goto delete_dff; } @@ -162,6 +171,7 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) delete_dff: log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str()); + remove_init_attr(dff->getPort("\\Q")); mod->remove(dff); return true; } @@ -193,8 +203,14 @@ struct OptRmdffPass : public Pass { assign_map.set(mod_it.second); dff_init_map.set(mod_it.second); for (auto &it : mod_it.second->wires_) - if (it.second->attributes.count("\\init") != 0) + if (it.second->attributes.count("\\init") != 0) { dff_init_map.add(it.second, it.second->attributes.at("\\init")); + for (int i = 0; i < GetSize(it.second); i++) { + SigBit wire_bit(it.second, i), mapped_bit = assign_map(wire_bit); + if (mapped_bit.wire) + init_attributes[mapped_bit].insert(wire_bit); + } + } mux_drivers.clear(); std::vector<RTLIL::IdString> dff_list; diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 2c39708bb..1d9b1006e 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -113,8 +113,8 @@ struct ShareWorker static int bits_macc_port(const Macc::port_t &p, int width) { if (GetSize(p.in_a) == 0 || GetSize(p.in_b) == 0) - return std::min(std::max(GetSize(p.in_a), GetSize(p.in_b)), width); - return std::min(GetSize(p.in_a), width) * std::min(GetSize(p.in_b), width) / 2; + return min(max(GetSize(p.in_a), GetSize(p.in_b)), width); + return min(GetSize(p.in_a), width) * min(GetSize(p.in_b), width) / 2; } static int bits_macc(const Macc &m, int width) @@ -224,13 +224,13 @@ struct ShareWorker supermacc->ports.push_back(p); } - int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * std::max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); + int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); - for (int i = 0; i < std::min(GetSize(p1.in_a), GetSize(p2.in_a)); i++) + for (int i = 0; i < min(GetSize(p1.in_a), GetSize(p2.in_a)); i++) if (p1.in_a[i] == p2.in_a[i] && score > 0) score--; - for (int i = 0; i < std::min(GetSize(p1.in_b), GetSize(p2.in_b)); i++) + for (int i = 0; i < min(GetSize(p1.in_b), GetSize(p2.in_b)); i++) if (p1.in_b[i] == p2.in_b[i] && score > 0) score--; @@ -243,7 +243,7 @@ struct ShareWorker Macc m1(c1), m2(c2), supermacc; int w1 = GetSize(c1->getPort("\\Y")), w2 = GetSize(c2->getPort("\\Y")); - int width = std::max(w1, w2); + int width = max(w1, w2); m1.optimize(w1); m2.optimize(w2); @@ -419,8 +419,8 @@ struct ShareWorker int a2_width = c2->parameters.at("\\A_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -438,9 +438,9 @@ struct ShareWorker int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; - if (std::max(b1_width, b2_width) > 2 * std::min(b1_width, b2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false; + if (max(b1_width, b2_width) > 2 * min(b1_width, b2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -458,15 +458,15 @@ struct ShareWorker int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - int min1_width = std::min(a1_width, b1_width); - int max1_width = std::max(a1_width, b1_width); + int min1_width = min(a1_width, b1_width); + int max1_width = max(a1_width, b1_width); - int min2_width = std::min(a2_width, b2_width); - int max2_width = std::max(a2_width, b2_width); + int min2_width = min(a2_width, b2_width); + int max2_width = max(a2_width, b2_width); - if (std::max(min1_width, min2_width) > 2 * std::min(min1_width, min2_width)) return false; - if (std::max(max1_width, max2_width) > 2 * std::min(max1_width, max2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(min1_width, min2_width) > 2 * min(min1_width, min2_width)) return false; + if (max(max1_width, max2_width) > 2 * min(max1_width, max2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -475,7 +475,7 @@ struct ShareWorker if (c1->type == "$macc") { if (!config.opt_aggressive) - if (share_macc(c1, c2) > 2 * std::min(bits_macc(c1), bits_macc(c2))) return false; + if (share_macc(c1, c2) > 2 * min(bits_macc(c1), bits_macc(c2))) return false; return true; } @@ -532,8 +532,8 @@ struct ShareWorker RTLIL::SigSpec a2 = c2->getPort("\\A"); RTLIL::SigSpec y2 = c2->getPort("\\Y"); - int a_width = std::max(a1.size(), a2.size()); - int y_width = std::max(y1.size(), y2.size()); + int a_width = max(a1.size(), a2.size()); + int y_width = max(y1.size(), y2.size()); a1.extend_u0(a_width, a_signed); a2.extend_u0(a_width, a_signed); @@ -563,11 +563,11 @@ struct ShareWorker if (config.generic_cbin_ops.count(c1->type)) { - int score_unflipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) + - std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()); + int score_unflipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) + + max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()); - int score_flipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) + - std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()); + int score_flipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) + + max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()); if (score_flipped < score_unflipped) { @@ -630,13 +630,13 @@ struct ShareWorker RTLIL::SigSpec b2 = c2->getPort("\\B"); RTLIL::SigSpec y2 = c2->getPort("\\Y"); - int a_width = std::max(a1.size(), a2.size()); - int b_width = std::max(b1.size(), b2.size()); - int y_width = std::max(y1.size(), y2.size()); + int a_width = max(a1.size(), a2.size()); + int b_width = max(b1.size(), b2.size()); + int y_width = max(y1.size(), y2.size()); if (c1->type == "$shr" && a_signed) { - a_width = std::max(y_width, a_width); + a_width = max(y_width, a_width); if (a1.size() < y1.size()) a1.extend_u0(y1.size(), true); if (a2.size() < y2.size()) a2.extend_u0(y2.size(), true); @@ -708,6 +708,10 @@ struct ShareWorker if (c1->type == "$memrd") { RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); + RTLIL::SigSpec addr1 = c1->getPort("\\ADDR"); + RTLIL::SigSpec addr2 = c2->getPort("\\ADDR"); + if (addr1 != addr2) + supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act)); supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA"))); supercell_aux.insert(supercell); return supercell; diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 70a40e960..4f08da675 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -37,7 +37,7 @@ struct WreduceConfig "$and", "$or", "$xor", "$xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", - "$add", "$sub", // "$mul", "$div", "$mod", "$pow", + "$add", "$sub", "$mul", // "$div", "$mod", "$pow", "$mux", "$pmux" }); } @@ -51,6 +51,7 @@ struct WreduceWorker std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells; std::set<SigBit> work_queue_bits; + pool<SigBit> keep_bits; WreduceWorker(WreduceConfig *config, Module *module) : config(config), module(module), mi(module) { } @@ -65,10 +66,13 @@ struct WreduceWorker SigSpec sig_y = mi.sigmap(cell->getPort("\\Y")); std::vector<SigBit> bits_removed; + if (sig_y.has_const()) + return; + for (int i = GetSize(sig_y)-1; i >= 0; i--) { auto info = mi.query(sig_y[i]); - if (!info->is_output && GetSize(info->ports) <= 1) { + if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_y[i]))) { bits_removed.push_back(Sx); continue; } @@ -172,6 +176,11 @@ struct WreduceWorker if (cell->type.in("$mux", "$pmux")) return run_cell_mux(cell); + SigSpec sig = mi.sigmap(cell->getPort("\\Y")); + + if (sig.has_const()) + return; + // Reduce size of ports A and B based on constant input bits and size of output port @@ -179,8 +188,8 @@ struct WreduceWorker int max_port_b_size = cell->hasPort("\\B") ? GetSize(cell->getPort("\\B")) : -1; if (cell->type.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) { - max_port_a_size = std::min(max_port_a_size, GetSize(cell->getPort("\\Y"))); - max_port_b_size = std::min(max_port_b_size, GetSize(cell->getPort("\\Y"))); + max_port_a_size = min(max_port_a_size, GetSize(sig)); + max_port_b_size = min(max_port_b_size, GetSize(sig)); } bool port_a_signed = false; @@ -192,10 +201,33 @@ struct WreduceWorker if (max_port_b_size >= 0) run_reduce_inport(cell, 'B', max_port_b_size, port_b_signed, did_something); + if (cell->hasPort("\\A") && cell->hasPort("\\B") && port_a_signed && port_b_signed) { + SigSpec sig_a = mi.sigmap(cell->getPort("\\A")), sig_b = mi.sigmap(cell->getPort("\\B")); + if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0 && + GetSize(sig_b) > 0 && sig_b[GetSize(sig_b)-1] == State::S0) { + log("Converting cell %s.%s (%s) from signed to unsigned.\n", + log_id(module), log_id(cell), log_id(cell->type)); + cell->setParam("\\A_SIGNED", 0); + cell->setParam("\\B_SIGNED", 0); + port_a_signed = false; + port_b_signed = false; + did_something = true; + } + } + + if (cell->hasPort("\\A") && !cell->hasPort("\\B") && port_a_signed) { + SigSpec sig_a = mi.sigmap(cell->getPort("\\A")); + if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0) { + log("Converting cell %s.%s (%s) from signed to unsigned.\n", + log_id(module), log_id(cell), log_id(cell->type)); + cell->setParam("\\A_SIGNED", 0); + port_a_signed = false; + did_something = true; + } + } - // Reduce size of port Y based on sizes for A and B and unused bits in Y - SigSpec sig = mi.sigmap(cell->getPort("\\Y")); + // Reduce size of port Y based on sizes for A and B and unused bits in Y int bits_removed = 0; if (port_a_signed && cell->type == "$shr") { @@ -221,7 +253,7 @@ struct WreduceWorker if (cell->hasPort("\\A")) a_size = GetSize(cell->getPort("\\A")); if (cell->hasPort("\\B")) b_size = GetSize(cell->getPort("\\B")); - int max_y_size = std::max(a_size, b_size); + int max_y_size = max(a_size, b_size); if (cell->type == "$add") max_y_size++; @@ -265,6 +297,11 @@ struct WreduceWorker void run() { + for (auto w : module->wires()) + if (w->get_bool_attribute("\\keep")) + for (auto bit : mi.sigmap(w)) + keep_bits.insert(bit); + for (auto c : module->selected_cells()) work_queue_cells.insert(c); @@ -348,14 +385,16 @@ struct WreducePass : public Pass { continue; for (auto c : module->selected_cells()) - if (c->type.in({"$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", + if (c->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", - "$logic_not", "$logic_and", "$logic_or"}) && GetSize(c->getPort("\\Y")) > 1) { + "$logic_not", "$logic_and", "$logic_or") && GetSize(c->getPort("\\Y")) > 1) { SigSpec sig = c->getPort("\\Y"); - c->setPort("\\Y", sig[0]); - c->setParam("\\Y_WIDTH", 1); - sig.remove(0); - module->connect(sig, Const(0, GetSize(sig))); + if (!sig.has_const()) { + c->setPort("\\Y", sig[0]); + c->setParam("\\Y_WIDTH", 1); + sig.remove(0); + module->connect(sig, Const(0, GetSize(sig))); + } } WreduceWorker worker(&config, module); diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 904d92114..943e8c562 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -27,35 +27,121 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs) +struct SigSnippets { - for (auto &action : cs->actions) { - if (action.first.size()) - return action.first; - } + idict<SigSpec> sigidx; + dict<SigBit, int> bit2snippet; + pool<int> snippets; - for (auto sw : cs->switches) - for (auto cs2 : sw->cases) { - RTLIL::SigSpec sig = find_any_lvalue(cs2); - if (sig.size()) - return sig; + void insert(SigSpec sig) + { + if (sig.empty()) + return; + + int key = sigidx(sig); + if (snippets.count(key)) + return; + + SigSpec new_sig; + + for (int i = 0; i < GetSize(sig); i++) + { + int other_key = bit2snippet.at(sig[i], -1); + + if (other_key < 0) { + new_sig.append(sig[i]); + continue; + } + + if (!new_sig.empty()) { + int new_key = sigidx(new_sig); + snippets.insert(new_key); + for (auto bit : new_sig) + bit2snippet[bit] = new_key; + new_sig = SigSpec(); + } + + SigSpec other_sig = sigidx[other_key]; + int k = 0, n = 1; + + while (other_sig[k] != sig[i]) { + k++; + log_assert(k < GetSize(other_sig)); + } + + while (i+n < GetSize(sig) && k+n < GetSize(other_sig) && sig[i+n] == other_sig[k+n]) + n++; + + SigSpec sig1 = other_sig.extract(0, k); + SigSpec sig2 = other_sig.extract(k, n); + SigSpec sig3 = other_sig.extract(k+n, GetSize(other_sig)-k-n); + + for (auto bit : other_sig) + bit2snippet.erase(bit); + snippets.erase(other_key); + + insert(sig1); + insert(sig2); + insert(sig3); + + i += n-1; + } + + if (!new_sig.empty()) { + int new_key = sigidx(new_sig); + snippets.insert(new_key); + for (auto bit : new_sig) + bit2snippet[bit] = new_key; + } } - return RTLIL::SigSpec(); -} + void insert(const RTLIL::CaseRule *cs) + { + for (auto &action : cs->actions) + insert(action.first); -void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig) + for (auto sw : cs->switches) + for (auto cs2 : sw->cases) + insert(cs2); + } +}; + +struct SnippetSwCache { - for (auto &action : cs->actions) { - RTLIL::SigSpec lvalue = action.first.extract(sig); - if (lvalue.size()) - sig = lvalue; + dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache; + const SigSnippets *snippets; + int current_snippet; + + bool check(RTLIL::SwitchRule *sw) + { + return cache[sw].count(current_snippet) != 0; } - for (auto sw : cs->switches) - for (auto cs2 : sw->cases) - extract_core_signal(cs2, sig); -} + void insert(const RTLIL::CaseRule *cs, vector<RTLIL::SwitchRule*> &sw_stack) + { + for (auto &action : cs->actions) + for (auto bit : action.first) { + int sn = snippets->bit2snippet.at(bit, -1); + if (sn < 0) + continue; + for (auto sw : sw_stack) + cache[sw].insert(sn); + } + + for (auto sw : cs->switches) { + sw_stack.push_back(sw); + for (auto cs2 : sw->cases) + insert(cs2, sw_stack); + sw_stack.pop_back(); + } + } + + void insert(const RTLIL::CaseRule *cs) + { + vector<RTLIL::SwitchRule*> sw_stack; + insert(cs, sw_stack); + } +}; RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw) { @@ -179,7 +265,8 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size(); } -RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval) +RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara, + RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval) { RTLIL::SigSpec result = defval; @@ -190,9 +277,37 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (auto sw : cs->switches) { + if (!swcache.check(sw)) + continue; + // detect groups of parallel cases std::vector<int> pgroups(sw->cases.size()); + bool is_simple_parallel_case = true; + if (!sw->get_bool_attribute("\\parallel_case")) { + if (!swpara.count(sw)) { + pool<Const> case_values; + for (size_t i = 0; i < sw->cases.size(); i++) { + RTLIL::CaseRule *cs2 = sw->cases[i]; + for (auto pat : cs2->compare) { + if (!pat.is_fully_def()) + goto not_simple_parallel_case; + Const cpat = pat.as_const(); + if (case_values.count(cpat)) + goto not_simple_parallel_case; + case_values.insert(cpat); + } + } + if (0) + not_simple_parallel_case: + is_simple_parallel_case = false; + swpara[sw] = is_simple_parallel_case; + } else { + is_simple_parallel_case = swpara.at(sw); + } + } + + if (!is_simple_parallel_case) { BitPatternPool pool(sw->signal.size()); bool extra_group_for_next_case = false; for (size_t i = 0; i < sw->cases.size(); i++) { @@ -225,7 +340,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (size_t i = 0; i < sw->cases.size(); i++) { int case_idx = sw->cases.size() - i - 1; RTLIL::CaseRule *cs2 = sw->cases[case_idx]; - RTLIL::SigSpec value = signal_to_mux_tree(mod, cs2, sig, initial_val); + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val); if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw); else @@ -238,24 +353,26 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) { - bool first = true; - while (1) - { - RTLIL::SigSpec sig = find_any_lvalue(&proc->root_case); + log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); - if (sig.size() == 0) - break; + SigSnippets sigsnip; + sigsnip.insert(&proc->root_case); - if (first) { - log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); - first = false; - } + SnippetSwCache swcache; + swcache.snippets = &sigsnip; + swcache.insert(&proc->root_case); + + dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara; - extract_core_signal(&proc->root_case, sig); + int cnt = 0; + for (int idx : sigsnip.snippets) + { + swcache.current_snippet = idx; + RTLIL::SigSpec sig = sigsnip.sigidx[idx]; - log(" creating decoder for signal `%s'.\n", log_signal(sig)); + log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig)); - RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size())); + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size())); mod->connect(RTLIL::SigSig(sig, value)); } } diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc index 522664032..614a1bd31 100644 --- a/passes/sat/eval.cc +++ b/passes/sat/eval.cc @@ -568,7 +568,7 @@ struct EvalPass : public Pass { if (tab_column_width.size() < row.size()) tab_column_width.resize(row.size()); for (size_t i = 0; i < row.size(); i++) - tab_column_width[i] = std::max(tab_column_width[i], int(row[i].size())); + tab_column_width[i] = max(tab_column_width[i], int(row[i].size())); } log("\n"); diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index ca784890b..ebdf2ed5d 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -116,7 +116,7 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De info.cell = it.second; if (info.cell->type == "$dff") { - info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit(); info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool(); std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector(); std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->getPort("\\Q")).to_sigbit_vector(); @@ -128,8 +128,8 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De } if (info.cell->type == "$adff") { - info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit(); - info.bit_arst = sigmap(info.cell->getPort("\\ARST")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit(); + info.bit_arst = sigmap(info.cell->getPort("\\ARST")).as_bit(); info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool(); info.arst_polarity = info.cell->parameters.at("\\ARST_POLARITY").as_bool(); std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector(); @@ -144,21 +144,21 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De } if (info.cell->type == "$_DFF_N_" || info.cell->type == "$_DFF_P_") { - info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit(); info.clk_polarity = info.cell->type == "$_DFF_P_"; - info.bit_d = sigmap(info.cell->getPort("\\D")).to_single_sigbit(); - bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info; + info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit(); + bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info; continue; } if (info.cell->type.size() == 10 && info.cell->type.substr(0, 6) == "$_DFF_") { - info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit(); - info.bit_arst = sigmap(info.cell->getPort("\\R")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit(); + info.bit_arst = sigmap(info.cell->getPort("\\R")).as_bit(); info.clk_polarity = info.cell->type[6] == 'P'; info.arst_polarity = info.cell->type[7] == 'P'; info.arst_value = info.cell->type[0] == '1' ? RTLIL::State::S1 : RTLIL::State::S0; - info.bit_d = sigmap(info.cell->getPort("\\D")).to_single_sigbit(); - bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info; + info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit(); + bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info; continue; } } diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index e0d11243b..373b80488 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -265,7 +265,7 @@ struct PerformReduction } int max_child_depth = 0; for (auto &bit : drv.second) - max_child_depth = std::max(register_cone_worker(celldone, sigdepth, bit), max_child_depth); + max_child_depth = max(register_cone_worker(celldone, sigdepth, bit), max_child_depth); sigdepth[out] = max_child_depth + 1; } else { pi_bits.push_back(out); diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index c83286924..2e9c6d2f9 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -606,8 +606,8 @@ struct SatHelper int maxModelWidth = 10; for (auto &info : modelInfo) { - maxModelName = std::max(maxModelName, int(info.description.size())); - maxModelWidth = std::max(maxModelWidth, info.width); + maxModelName = max(maxModelName, int(info.description.size())); + maxModelWidth = max(maxModelWidth, info.width); } log("\n"); @@ -781,9 +781,9 @@ struct SatHelper wavedata[info.description].first = info.width; wavedata[info.description].second[info.timestep] = value; - mintime = std::min(mintime, info.timestep); - maxtime = std::max(maxtime, info.timestep); - maxwidth = std::max(maxwidth, info.width); + mintime = min(mintime, info.timestep); + maxtime = max(maxtime, info.timestep); + maxwidth = max(maxwidth, info.width); } fprintf(f, "{ \"signal\": ["); @@ -936,6 +936,9 @@ struct SatPass : public Pass { log(" -show-inputs, -show-outputs, -show-ports\n"); log(" add all module (input/output) ports to the list of shown signals\n"); log("\n"); + log(" -show-regs, -show-public, -show-all\n"); + log(" show all registers, show signals with 'public' names, show all signals\n"); + log("\n"); log(" -ignore_div_by_zero\n"); log(" ignore all solutions that involve a division by zero\n"); log("\n"); @@ -1064,6 +1067,7 @@ struct SatPass : public Pass { bool verify = false, fail_on_timeout = false, enable_undef = false, set_def_inputs = false; bool ignore_div_by_zero = false, set_init_undef = false, set_init_zero = false, max_undef = false; bool tempinduct = false, prove_asserts = false, show_inputs = false, show_outputs = false; + bool show_regs = false, show_public = false, show_all = false; bool ignore_unknown_cells = false, falsify = false, tempinduct_def = false, set_init_def = false; bool tempinduct_baseonly = false, tempinduct_inductonly = false, set_assumes = false; int tempinduct_skip = 0, stepsize = 1; @@ -1112,7 +1116,7 @@ struct SatPass : public Pass { continue; } if (args[argidx] == "-stepsize" && argidx+1 < args.size()) { - stepsize = std::max(1, atoi(args[++argidx].c_str())); + stepsize = max(1, atoi(args[++argidx].c_str())); continue; } if (args[argidx] == "-ignore_div_by_zero") { @@ -1272,6 +1276,18 @@ struct SatPass : public Pass { show_outputs = true; continue; } + if (args[argidx] == "-show-regs") { + show_regs = true; + continue; + } + if (args[argidx] == "-show-public") { + show_public = true; + continue; + } + if (args[argidx] == "-show-all") { + show_all = true; + continue; + } if (args[argidx] == "-ignore_unknown_cells") { ignore_unknown_cells = true; continue; @@ -1331,6 +1347,29 @@ struct SatPass : public Pass { shows.push_back(it.second->name.str()); } + if (show_regs) { + pool<Wire*> reg_wires; + for (auto cell : module->cells()) { + if (cell->type == "$dff" || cell->type.substr(0, 6) == "$_DFF_") + for (auto bit : cell->getPort("\\Q")) + if (bit.wire) + reg_wires.insert(bit.wire); + } + for (auto wire : reg_wires) + shows.push_back(wire->name.str()); + } + + if (show_public) { + for (auto wire : module->wires()) + if (wire->name[0] == '\\') + shows.push_back(wire->name.str()); + } + + if (show_all) { + for (auto wire : module->wires()) + shows.push_back(wire->name.str()); + } + if (tempinduct) { if (loopcount > 0 || max_undef) diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index af4816a72..674cb77cb 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -20,6 +20,8 @@ OBJS += passes/techmap/pmuxtree.o OBJS += passes/techmap/muxcover.o OBJS += passes/techmap/aigmap.o OBJS += passes/techmap/tribuf.o +OBJS += passes/techmap/lut2mux.o +OBJS += passes/techmap/nlutmap.o endif GENFILES += passes/techmap/techmap.inc diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index d1e629b5a..5c5148590 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -100,6 +100,7 @@ SigMap assign_map; RTLIL::Module *module; std::vector<gate_t> signal_list; std::map<RTLIL::SigBit, int> signal_map; +pool<std::string> enabled_gates; bool clk_polarity, en_polarity; RTLIL::SigSpec clk_sig, en_sig; @@ -837,17 +838,28 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fprintf(f, "GATE ONE 1 Y=CONST1;\n"); fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_")); fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_")); - fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_")); - fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_")); - fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_")); - fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_")); - fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_")); - fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_")); - fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_")); - fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_")); - fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_")); - fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_")); - fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_")); + if (enabled_gates.empty() || enabled_gates.count("AND")) + fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_")); + if (enabled_gates.empty() || enabled_gates.count("NAND")) + fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_")); + if (enabled_gates.empty() || enabled_gates.count("OR")) + fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_")); + if (enabled_gates.empty() || enabled_gates.count("NOR")) + fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_")); + if (enabled_gates.empty() || enabled_gates.count("XOR")) + fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_")); + if (enabled_gates.empty() || enabled_gates.count("XNOR")) + fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_")); + if (enabled_gates.empty() || enabled_gates.count("AOI3")) + fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_")); + if (enabled_gates.empty() || enabled_gates.count("OAI3")) + fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_")); + if (enabled_gates.empty() || enabled_gates.count("AOI4")) + fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_")); + if (enabled_gates.empty() || enabled_gates.count("OAI4")) + fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_")); + if (enabled_gates.empty() || enabled_gates.count("MUX")) + fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_")); if (map_mux4) fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*get_cell_cost("$_MUX_")); if (map_mux8) @@ -1075,6 +1087,12 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin design->select(module, cell); continue; } + if (c->type == "$lut" && GetSize(c->getPort("\\A")) == 1 && c->getParam("\\LUT").as_int() == 2) { + SigSpec my_a = module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]; + SigSpec my_y = module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]; + module->connect(my_y, my_a); + continue; + } RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; cell->parameters = c->parameters; @@ -1224,6 +1242,11 @@ struct AbcPass : public Pass { // log(" try to extract 4-input, 8-input, and/or 16-input muxes\n"); // log(" (ignored when used with -liberty or -lut)\n"); // log("\n"); + log(" -g type1,type2,...\n"); + log(" Map the the specified list of gate types. Supported gates types are:\n"); + log(" AND, NAND, OR, NOR, XOR, XNOR, MUX, AOI3, OAI3, AOI4, OAI4.\n"); + log(" (The NOT gate is always added to this list automatically.)\n"); + log("\n"); log(" -dff\n"); log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n"); log(" clock domains are automatically partitioned in clock domains and each\n"); @@ -1274,6 +1297,7 @@ struct AbcPass : public Pass { map_mux4 = false; map_mux8 = false; map_mux16 = false; + enabled_gates.clear(); #ifdef _WIN32 if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) @@ -1338,6 +1362,25 @@ struct AbcPass : public Pass { map_mux16 = true; continue; } + if (arg == "-g" && argidx+1 < args.size()) { + for (auto g : split_tokens(args[++argidx], ",")) { + if (g == "AND") goto ok_gate; + if (g == "NAND") goto ok_gate; + if (g == "OR") goto ok_gate; + if (g == "NOR") goto ok_gate; + if (g == "XOR") goto ok_gate; + if (g == "XNOR") goto ok_gate; + if (g == "MUX") goto ok_gate; + if (g == "AOI3") goto ok_gate; + if (g == "OAI3") goto ok_gate; + if (g == "AOI4") goto ok_gate; + if (g == "OAI4") goto ok_gate; + cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str())); + ok_gate: + enabled_gates.insert(g); + } + continue; + } if (arg == "-fast") { fast_mode = true; continue; @@ -1393,7 +1436,7 @@ struct AbcPass : public Pass { std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; - typedef std::tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; + typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells; std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse; diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index 54c9bc1aa..3c7ff4b92 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -40,7 +40,7 @@ struct AlumaccWorker { std::vector<RTLIL::Cell*> cells; RTLIL::SigSpec a, b, c, y; - std::vector<std::tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp; + std::vector<tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp; bool is_signed, invert_b; RTLIL::Cell *alu_cell; @@ -98,9 +98,9 @@ struct AlumaccWorker } }; - std::map<RTLIL::SigBit, int> bit_users; - std::map<RTLIL::SigSpec, maccnode_t*> sig_macc; - std::map<RTLIL::SigSig, std::set<alunode_t*>> sig_alu; + dict<RTLIL::SigBit, int> bit_users; + dict<RTLIL::SigSpec, maccnode_t*> sig_macc; + dict<RTLIL::SigSig, pool<alunode_t*, hash_ptr_ops>> sig_alu; int macc_counter, alu_counter; AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module) @@ -138,7 +138,7 @@ struct AlumaccWorker n->users = 0; for (auto bit : n->y) - n->users = std::max(n->users, bit_users.at(bit) - 1); + n->users = max(n->users, bit_users.at(bit) - 1); if (cell->type.in("$pos", "$neg")) { @@ -215,7 +215,7 @@ struct AlumaccWorker { while (1) { - std::set<maccnode_t*> delete_nodes; + pool<maccnode_t*, hash_ptr_ops> delete_nodes; for (auto &it : sig_macc) { @@ -267,7 +267,7 @@ struct AlumaccWorker void macc_to_alu() { - std::set<maccnode_t*> delete_nodes; + pool<maccnode_t*, hash_ptr_ops> delete_nodes; for (auto &it : sig_macc) { @@ -409,7 +409,7 @@ struct AlumaccWorker n->a = A; n->b = B; n->c = RTLIL::S1; - n->y = module->addWire(NEW_ID, std::max(GetSize(A), GetSize(B))); + n->y = module->addWire(NEW_ID, max(GetSize(A), GetSize(B))); n->is_signed = is_signed; n->invert_b = true; sig_alu[RTLIL::SigSig(A, B)].insert(n); diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index 2215c18e5..e0273f439 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -63,16 +63,26 @@ struct DffinitPass : public Pass { SigMap sigmap(module); dict<SigBit, State> init_bits; pool<SigBit> cleanup_bits; + pool<SigBit> used_bits; - for (auto wire : module->selected_wires()) + for (auto wire : module->selected_wires()) { if (wire->attributes.count("\\init")) { Const value = wire->attributes.at("\\init"); - for (int i = 0; i < std::min(GetSize(value), GetSize(wire)); i++) + for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) init_bits[sigmap(SigBit(wire, i))] = value[i]; } + if (wire->port_output) + for (auto bit : sigmap(wire)) + used_bits.insert(bit); + } for (auto cell : module->selected_cells()) { + for (auto it : cell->connections()) + if (!cell->known() || cell->input(it.first)) + for (auto bit : sigmap(it.second)) + used_bits.insert(bit); + if (ff_types.count(cell->type) == 0) continue; @@ -90,7 +100,7 @@ struct DffinitPass : public Pass { for (int i = 0; i < GetSize(sig); i++) { if (init_bits.count(sig[i]) == 0) continue; - while (GetSize(value.bits) < i) + while (GetSize(value.bits) <= i) value.bits.push_back(State::S0); value.bits[i] = init_bits.at(sig[i]); cleanup_bits.insert(sig[i]); @@ -104,11 +114,15 @@ struct DffinitPass : public Pass { for (auto wire : module->selected_wires()) if (wire->attributes.count("\\init")) { - Const value = wire->attributes.at("\\init"); + Const &value = wire->attributes.at("\\init"); bool do_cleanup = true; - for (int i = 0; i < std::min(GetSize(value), GetSize(wire)); i++) - if (cleanup_bits.count(sigmap(SigBit(wire, i))) == 0) + for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) { + SigBit bit = sigmap(SigBit(wire, i)); + if (cleanup_bits.count(bit) || !used_bits.count(bit)) + value[i] = State::Sx; + else if (value[i] != State::Sx) do_cleanup = false; + } if (do_cleanup) { log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire)); wire->attributes.erase("\\init"); diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index 64131c1a8..693651bfd 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -491,6 +491,7 @@ struct DfflibmapPass : public Pass { std::string arg = args[argidx]; if (arg == "-liberty" && argidx+1 < args.size()) { liberty_file = args[++argidx]; + rewrite_filename(liberty_file); continue; } if (arg == "-prepare") { diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc index 68a7fc1f6..fc73177ce 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -130,7 +130,7 @@ public: RTLIL::SigSpec needleSig = conn.second; RTLIL::SigSpec haystackSig = haystackCell->getPort(portMapping.at(conn.first.str())); - for (int i = 0; i < std::min(needleSig.size(), haystackSig.size()); i++) { + for (int i = 0; i < min(needleSig.size(), haystackSig.size()); i++) { RTLIL::Wire *needleWire = needleSig[i].wire, *haystackWire = haystackSig[i].wire; if (needleWire != lastNeedleWire || haystackWire != lastHaystackWire) if (!compareAttributes(wire_attr, needleWire ? needleWire->attributes : emptyAttr, haystackWire ? haystackWire->attributes : emptyAttr)) @@ -737,7 +737,7 @@ struct ExtractPass : public Pass { RTLIL::Cell *newCell = newMod->addCell(cell->name, cell->type); newCell->parameters = cell->parameters; for (auto &conn : cell->connections()) { - std::vector<RTLIL::SigChunk> chunks = sigmap(conn.second); + std::vector<SigChunk> chunks = sigmap(conn.second); for (auto &chunk : chunks) if (chunk.wire != NULL) chunk.wire = newMod->wires_.at(chunk.wire->name); diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index 0e0a2adc9..9dab40ca8 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -45,10 +45,10 @@ struct IopadmapPass : public Pass { log("the resulting cells to more sophisticated PAD cells.\n"); log("\n"); log(" -inpad <celltype> <portname>[:<portname>]\n"); - log(" Map module input ports to the given cell type with\n"); - log(" the given port name. if a 2nd portname is given, the\n"); + log(" Map module input ports to the given cell type with the\n"); + log(" given output port name. if a 2nd portname is given, the\n"); log(" signal is passed through the pad call, using the 2nd\n"); - log(" portname as output.\n"); + log(" portname as input.\n"); log("\n"); log(" -outpad <celltype> <portname>[:<portname>]\n"); log(" -inoutpad <celltype> <portname>[:<portname>]\n"); diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc new file mode 100644 index 000000000..fe55e499a --- /dev/null +++ b/passes/techmap/lut2mux.cc @@ -0,0 +1,93 @@ +/* + * 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 + +int lut2mux(Cell *cell) +{ + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_y = cell->getPort("\\Y"); + Const lut = cell->getParam("\\LUT"); + int count = 1; + + if (GetSize(sig_a) == 1) + { + cell->module->addMuxGate(NEW_ID, lut[0], lut[1], sig_a, sig_y); + } + else + { + SigSpec sig_a_hi = sig_a[GetSize(sig_a)-1]; + SigSpec sig_a_lo = sig_a.extract(0, GetSize(sig_a)-1); + SigSpec sig_y1 = cell->module->addWire(NEW_ID); + SigSpec sig_y2 = cell->module->addWire(NEW_ID); + + Const lut1 = lut.extract(0, GetSize(lut)/2); + Const lut2 = lut.extract(GetSize(lut)/2, GetSize(lut)/2); + + count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y1, lut1)); + count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y2, lut2)); + + cell->module->addMuxGate(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y); + } + + cell->module->remove(cell); + return count; +} + +struct Lut2muxPass : public Pass { + Lut2muxPass() : Pass("lut2mux", "convert $lut to $_MUX_") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" lut2mux [options] [selection]\n"); + log("\n"); + log("This pass converts $lut cells to $_MUX_ gates.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("Executing LUT2MUX pass (mapping to constant drivers).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-v") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) { + if (cell->type == "$lut") { + IdString cell_name = cell->name; + int count = lut2mux(cell); + log("Converted %s.%s to %d MUX cells.\n", log_id(module), log_id(cell_name), count); + } + } + } +} Lut2muxPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index dad1c06ac..d5b8fe804 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -134,7 +134,7 @@ struct MaccmapWorker } return retval; #else - return std::max(n - 1, 0); + return max(n - 1, 0); #endif } diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc index b250c5680..514c3365f 100644 --- a/passes/techmap/muxcover.cc +++ b/passes/techmap/muxcover.cc @@ -49,8 +49,8 @@ struct MuxcoverWorker vector<tree_t> tree_list; - dict<std::tuple<SigBit, SigBit, SigBit>, std::tuple<SigBit, pool<SigBit>, bool>> decode_mux_cache; - dict<SigBit, std::tuple<SigBit, SigBit, SigBit>> decode_mux_reverse_cache; + dict<tuple<SigBit, SigBit, SigBit>, tuple<SigBit, pool<SigBit>, bool>> decode_mux_cache; + dict<SigBit, tuple<SigBit, SigBit, SigBit>> decode_mux_reverse_cache; int decode_mux_counter; bool use_mux4; @@ -142,7 +142,7 @@ struct MuxcoverWorker if (A == B) return 0; - std::tuple<SigBit, SigBit, SigBit> key(A, B, sel); + tuple<SigBit, SigBit, SigBit> key(A, B, sel); if (decode_mux_cache.count(key) == 0) { auto &entry = decode_mux_cache[key]; std::get<0>(entry) = module->addWire(NEW_ID); diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc new file mode 100644 index 000000000..7ece40059 --- /dev/null +++ b/passes/techmap/nlutmap.cc @@ -0,0 +1,173 @@ +/* + * 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 NlutmapConfig +{ + vector<int> luts; +}; + +struct NlutmapWorker +{ + const NlutmapConfig &config; + pool<Cell*> mapped_cells; + Module *module; + + NlutmapWorker(const NlutmapConfig &config, Module *module) : + config(config), module(module) + { + } + + RTLIL::Selection get_selection() + { + RTLIL::Selection sel(false); + for (auto cell : module->cells()) + if (!mapped_cells.count(cell)) + sel.select(module, cell); + return sel; + } + + void run_abc(int lut_size) + { + Pass::call_on_selection(module->design, get_selection(), "lut2mux"); + + if (lut_size > 0) + Pass::call_on_selection(module->design, get_selection(), stringf("abc -lut 1:%d", lut_size)); + else + Pass::call_on_selection(module->design, get_selection(), "abc"); + + Pass::call_on_module(module->design, module, "opt_clean"); + } + + void run() + { + vector<int> available_luts = config.luts; + + while (!available_luts.empty()) + { + int n_luts = available_luts.back(); + int lut_size = GetSize(available_luts); + available_luts.pop_back(); + + if (n_luts == 0) + continue; + + run_abc(lut_size); + + SigMap sigmap(module); + dict<Cell*, int> candidate_ratings; + dict<SigBit, int> bit_lut_count; + + for (auto cell : module->cells()) + { + if (cell->type != "$lut" || mapped_cells.count(cell)) + continue; + + if (GetSize(cell->getPort("\\A")) == lut_size) + candidate_ratings[cell] = 0; + + for (auto &conn : cell->connections()) + for (auto bit : sigmap(conn.second)) + bit_lut_count[bit]++; + } + + for (auto &cand : candidate_ratings) + { + for (auto &conn : cand.first->connections()) + for (auto bit : sigmap(conn.second)) + cand.second -= bit_lut_count[bit]; + } + + vector<pair<int, IdString>> rated_candidates; + + for (auto &cand : candidate_ratings) + rated_candidates.push_back(pair<int, IdString>(cand.second, cand.first->name)); + + std::sort(rated_candidates.begin(), rated_candidates.end()); + + while (n_luts > 0 && !rated_candidates.empty()) { + mapped_cells.insert(module->cell(rated_candidates.back().second)); + rated_candidates.pop_back(); + n_luts--; + } + + if (!available_luts.empty()) + available_luts.back() += n_luts; + } + + run_abc(0); + } +}; + +struct NlutmapPass : public Pass { + NlutmapPass() : Pass("nlutmap", "map to LUTs of different sizes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" nlutmap [options] [selection]\n"); + log("\n"); + log("This pass uses successive calls to 'abc' to map to an architecture. That\n"); + log("provides a small number of differently sized LUTs.\n"); + log("\n"); + log(" -luts N_1,N_2,N_3,...\n"); + log(" The number of LUTs with 1, 2, 3, ... inputs that are\n"); + log(" available in the target architecture.\n"); + log("\n"); + log("Excess logic that does not fit into the specified LUTs is mapped back\n"); + log("to generic logic gates ($_AND_, etc.).\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + NlutmapConfig config; + + log_header("Executing NLUTMAP pass (mapping to constant drivers).\n"); + log_push(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-luts" && argidx+1 < args.size()) { + vector<string> tokens = split_tokens(args[++argidx], ","); + config.luts.clear(); + for (auto &token : tokens) + config.luts.push_back(atoi(token.c_str())); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) + { + NlutmapWorker worker(config, module); + worker.run(); + } + + log_pop(); + } +} NlutmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index 0fb5b3741..f6ac3964b 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -247,7 +247,7 @@ void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell) bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool(); bool is_ne = cell->type == "$ne" || cell->type == "$nex"; - RTLIL::SigSpec xor_out = module->addWire(NEW_ID, std::max(GetSize(sig_a), GetSize(sig_b))); + RTLIL::SigSpec xor_out = module->addWire(NEW_ID, max(GetSize(sig_a), GetSize(sig_b))); RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed); xor_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); simplemap_bitop(module, xor_cell); @@ -293,7 +293,7 @@ void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell) RTLIL::Cell *gate = module->addCell(NEW_ID, "$_TBUF_"); gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a[i]); - gate->setPort("\\E", sig_e[i]); + gate->setPort("\\E", sig_e); gate->setPort("\\Y", sig_y[i]); } } diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index 30a6d784f..19b2bda9c 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -49,7 +49,7 @@ void apply_prefix(std::string prefix, std::string &id) void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module) { - std::vector<RTLIL::SigChunk> chunks = sig; + vector<SigChunk> chunks = sig; for (auto &chunk : chunks) if (chunk.wire != NULL) { std::string wire_name = chunk.wire->name.str(); @@ -257,7 +257,7 @@ struct TechmapWorker } else { SigSpec sig_tpl = w, sig_tpl_pf = w, sig_mod = it.second; apply_prefix(cell->name.str(), sig_tpl_pf, module); - for (int i = 0; i < GetSize(sig_tpl); i++) { + for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) { if (tpl_written_bits.count(tpl_sigmap(sig_tpl[i]))) { c.first.append(sig_mod[i]); c.second.append(sig_tpl_pf[i]); diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index abac62231..a8fcac9bc 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -256,7 +256,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, case 2: n = xorshift32(GetSize(sig)); m = xorshift32(GetSize(sig)); - for (int i = std::min(n, m); i < std::max(n, m); i++) + for (int i = min(n, m); i < max(n, m); i++) sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0; break; } @@ -552,6 +552,9 @@ struct TestCellPass : public Pass { log(" -nosat\n"); log(" do not check SAT model or run SAT equivalence checking\n"); log("\n"); + log(" -noeval\n"); + log(" do not check const-eval models\n"); + log("\n"); log(" -v\n"); log(" print additional debug information to the console\n"); log("\n"); @@ -570,6 +573,7 @@ struct TestCellPass : public Pass { bool verbose = false; bool constmode = false; bool nosat = false; + bool noeval = false; int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -619,6 +623,10 @@ struct TestCellPass : public Pass { nosat = true; continue; } + if (args[argidx] == "-noeval") { + noeval = true; + continue; + } if (args[argidx] == "-v") { verbose = true; continue; @@ -772,7 +780,8 @@ struct TestCellPass : public Pass { Backend::backend_call(design, &vlog_file, "<test_cell -vlog>", "verilog -selected -noexpr"); uut_names.push_back(uut_name); } - run_eval_test(design, verbose, nosat, uut_name, vlog_file); + if (!noeval) + run_eval_test(design, verbose, nosat, uut_name, vlog_file); } delete design; } diff --git a/techlibs/common/.gitignore b/techlibs/common/.gitignore new file mode 100644 index 000000000..0a1e7b68d --- /dev/null +++ b/techlibs/common/.gitignore @@ -0,0 +1,2 @@ +simlib_help.inc +simcells_help.inc diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index f222a0289..236d6c551 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -1,8 +1,24 @@ ifneq ($(SMALL),1) OBJS += techlibs/common/synth.o +OBJS += techlibs/common/prep.o endif +GENFILES += techlibs/common/simlib_help.inc +GENFILES += techlibs/common/simcells_help.inc + +techlibs/common/simlib_help.inc: techlibs/common/cellhelp.py techlibs/common/simlib.v + $(Q) mkdir -p techlibs/common + $(P) python3 $^ > $@.new + $(Q) mv $@.new $@ + +techlibs/common/simcells_help.inc: techlibs/common/cellhelp.py techlibs/common/simcells.v + $(Q) mkdir -p techlibs/common + $(P) python3 $^ > $@.new + $(Q) mv $@.new $@ + +kernel/register.o: techlibs/common/simlib_help.inc techlibs/common/simcells_help.inc + $(eval $(call add_share_file,share,techlibs/common/simlib.v)) $(eval $(call add_share_file,share,techlibs/common/simcells.v)) $(eval $(call add_share_file,share,techlibs/common/techmap.v)) diff --git a/techlibs/common/cellhelp.py b/techlibs/common/cellhelp.py new file mode 100644 index 000000000..5c44cb802 --- /dev/null +++ b/techlibs/common/cellhelp.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import fileinput +import json + +current_help_msg = [] +current_module_code = [] +current_module_name = None +current_module_signature = None + +def print_current_cell(): + print("cell_help[\"%s\"] = %s;" % (current_module_name, "\n".join([json.dumps(line) for line in current_help_msg]))) + print("cell_code[\"%s+\"] = %s;" % (current_module_name, "\n".join([json.dumps(line) for line in current_module_code]))) + +for line in fileinput.input(): + if line.startswith("//-"): + current_help_msg.append(line[4:] if len(line) > 4 else "\n") + if line.startswith("module "): + current_module_name = line.split()[1].strip("\\") + current_module_signature = " ".join(line.replace("\\", "").replace(";", "").split()[1:]) + current_module_code = [] + elif not line.startswith("endmodule"): + line = " " + line + current_module_code.append(line.replace("\t", " ")) + if line.startswith("endmodule"): + if len(current_help_msg) == 0: + current_help_msg.append("\n") + current_help_msg.append(" %s\n" % current_module_signature) + current_help_msg.append("\n") + current_help_msg.append("No help message for this cell type found.\n") + current_help_msg.append("\n") + print_current_cell() + current_help_msg = [] + diff --git a/techlibs/common/prep.cc b/techlibs/common/prep.cc new file mode 100644 index 000000000..ebd329746 --- /dev/null +++ b/techlibs/common/prep.cc @@ -0,0 +1,156 @@ +/* + * 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/register.h" +#include "kernel/celltypes.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool check_label(bool &active, std::string run_from, std::string run_to, std::string label) +{ + if (!run_from.empty() && run_from == run_to) { + active = (label == run_from); + } else { + if (label == run_from) + active = true; + if (label == run_to) + active = false; + } + return active; +} + +struct PrepPass : public Pass { + PrepPass() : Pass("prep", "generic synthesis script") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" prep [options]\n"); + log("\n"); + log("This command runs a conservative RTL synthesis. A typical application for this\n"); + log("is the preparation stage of a verification flow. This command does not operate\n"); + log("on partly selected designs.\n"); + log("\n"); + log(" -top <module>\n"); + log(" use the specified module as top module (default='top')\n"); + log("\n"); + log(" -nordff\n"); + log(" passed to 'memory_dff'. prohibits merging of FFs into memory read ports\n"); + log("\n"); + log(" -run <from_label>[:<to_label>]\n"); + log(" only run the commands between the labels (see below). an empty\n"); + log(" from label is synonymous to 'begin', and empty to label is\n"); + log(" synonymous to the end of the command list.\n"); + log("\n"); + log("\n"); + log("The following commands are executed by this synthesis command:\n"); + log("\n"); + log(" begin:\n"); + log(" hierarchy -check [-top <top>]\n"); + log("\n"); + log(" prep:\n"); + log(" proc\n"); + log(" opt_clean\n"); + log(" check\n"); + log(" opt -keepdc\n"); + log(" wreduce\n"); + log(" memory_dff [-nordff]\n"); + log(" opt_clean\n"); + log(" memory_collect\n"); + log(" opt -keepdc -fast\n"); + log("\n"); + log(" check:\n"); + log(" stat\n"); + log(" check\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + std::string top_module, memory_opts; + std::string run_from, run_to; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-top" && argidx+1 < args.size()) { + top_module = args[++argidx]; + continue; + } + if (args[argidx] == "-run" && argidx+1 < args.size()) { + size_t pos = args[argidx+1].find(':'); + if (pos == std::string::npos) { + run_from = args[++argidx]; + run_to = args[argidx]; + } else { + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos+1); + } + continue; + } + if (args[argidx] == "-nordff") { + memory_opts += " -nordff"; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!design->full_selection()) + log_cmd_error("This comannd only operates on fully selected designs!\n"); + + bool active = run_from.empty(); + + log_header("Executing PREP pass.\n"); + log_push(); + + if (check_label(active, run_from, run_to, "begin")) + { + if (top_module.empty()) + Pass::call(design, stringf("hierarchy -check")); + else + Pass::call(design, stringf("hierarchy -check -top %s", top_module.c_str())); + } + + if (check_label(active, run_from, run_to, "coarse")) + { + Pass::call(design, "proc"); + Pass::call(design, "opt_clean"); + Pass::call(design, "check"); + Pass::call(design, "opt -keepdc"); + Pass::call(design, "wreduce"); + Pass::call(design, "memory_dff" + memory_opts); + Pass::call(design, "opt_clean"); + Pass::call(design, "memory_collect"); + Pass::call(design, "opt -keepdc -fast"); + } + + if (check_label(active, run_from, run_to, "check")) + { + Pass::call(design, "stat"); + Pass::call(design, "check"); + } + + log_pop(); + } +} PrepPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/common/simcells.v b/techlibs/common/simcells.v index 3b7d55c6e..26de2d4fa 100644 --- a/techlibs/common/simcells.v +++ b/techlibs/common/simcells.v @@ -25,60 +25,184 @@ * */ -module \$_BUF_ (A, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_BUF_ (A, Y) +//- +//- A buffer. This cell type is always optimized away by the opt_clean pass. +//- +//- Truth table: A | Y +//- ---+--- +//- 0 | 0 +//- 1 | 1 +//- +module \$_BUF_ (A, Y); input A; output Y; assign Y = A; endmodule -module \$_NOT_ (A, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_NOT_ (A, Y) +//- +//- An inverter gate. +//- +//- Truth table: A | Y +//- ---+--- +//- 0 | 1 +//- 1 | 0 +//- +module \$_NOT_ (A, Y); input A; output Y; assign Y = ~A; endmodule -module \$_AND_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_AND_ (A, B, Y) +//- +//- A 2-input AND gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 0 +//- 0 1 | 0 +//- 1 0 | 0 +//- 1 1 | 1 +//- +module \$_AND_ (A, B, Y); input A, B; output Y; assign Y = A & B; endmodule -module \$_NAND_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_NAND_ (A, B, Y) +//- +//- A 2-input NAND gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 1 +//- 0 1 | 1 +//- 1 0 | 1 +//- 1 1 | 0 +//- +module \$_NAND_ (A, B, Y); input A, B; output Y; assign Y = ~(A & B); endmodule -module \$_OR_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_OR_ (A, B, Y) +//- +//- A 2-input OR gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 0 +//- 0 1 | 1 +//- 1 0 | 1 +//- 1 1 | 1 +//- +module \$_OR_ (A, B, Y); input A, B; output Y; assign Y = A | B; endmodule -module \$_NOR_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_NOR_ (A, B, Y) +//- +//- A 2-input NOR gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 1 +//- 0 1 | 0 +//- 1 0 | 0 +//- 1 1 | 0 +//- +module \$_NOR_ (A, B, Y); input A, B; output Y; assign Y = ~(A | B); endmodule -module \$_XOR_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_XOR_ (A, B, Y) +//- +//- A 2-input XOR gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 0 +//- 0 1 | 1 +//- 1 0 | 1 +//- 1 1 | 0 +//- +module \$_XOR_ (A, B, Y); input A, B; output Y; assign Y = A ^ B; endmodule -module \$_XNOR_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_XNOR_ (A, B, Y) +//- +//- A 2-input XNOR gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 1 +//- 0 1 | 0 +//- 1 0 | 0 +//- 1 1 | 1 +//- +module \$_XNOR_ (A, B, Y); input A, B; output Y; assign Y = ~(A ^ B); endmodule +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_MUX_ (A, B, S, Y) +//- +//- A 2-input MUX gate. +//- +//- Truth table: A B S | Y +//- -------+--- +//- a - 0 | a +//- - b 1 | b +//- module \$_MUX_ (A, B, S, Y); input A, B, S; output Y; assign Y = S ? B : A; endmodule +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_MUX4_ (A, B, C, D, S, T, Y) +//- +//- A 4-input MUX gate. +//- +//- Truth table: A B C D S T | Y +//- -------------+--- +//- a - - - 0 0 | a +//- - b - - 1 0 | b +//- - - c - 0 1 | c +//- - - - d 1 1 | d +//- module \$_MUX4_ (A, B, C, D, S, T, Y); input A, B, C, D, S, T; output Y; @@ -86,6 +210,23 @@ assign Y = T ? (S ? D : C) : (S ? B : A); endmodule +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y) +//- +//- An 8-input MUX gate. +//- +//- Truth table: A B C D E F G H S T U | Y +//- -----------------------+--- +//- a - - - - - - - 0 0 0 | a +//- - b - - - - - - 1 0 0 | b +//- - - c - - - - - 0 1 0 | c +//- - - - d - - - - 1 1 0 | d +//- - - - - e - - - 0 0 1 | e +//- - - - - - f - - 1 0 1 | f +//- - - - - - - g - 0 1 1 | g +//- - - - - - - - h 1 1 1 | h +//- module \$_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y); input A, B, C, D, E, F, G, H, S, T, U; output Y; @@ -95,6 +236,31 @@ assign Y = U ? T ? (S ? H : G) : (S ? B : A); endmodule +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y) +//- +//- A 16-input MUX gate. +//- +//- Truth table: A B C D E F G H I J K L M N O P S T U V | Y +//- -----------------------------------------+--- +//- a - - - - - - - - - - - - - - - 0 0 0 0 | a +//- - b - - - - - - - - - - - - - - 1 0 0 0 | b +//- - - c - - - - - - - - - - - - - 0 1 0 0 | c +//- - - - d - - - - - - - - - - - - 1 1 0 0 | d +//- - - - - e - - - - - - - - - - - 0 0 1 0 | e +//- - - - - - f - - - - - - - - - - 1 0 1 0 | f +//- - - - - - - g - - - - - - - - - 0 1 1 0 | g +//- - - - - - - - h - - - - - - - - 1 1 1 0 | h +//- - - - - - - - - i - - - - - - - 0 0 0 1 | i +//- - - - - - - - - - j - - - - - - 1 0 0 1 | j +//- - - - - - - - - - - k - - - - - 0 1 0 1 | k +//- - - - - - - - - - - - l - - - - 1 1 0 1 | l +//- - - - - - - - - - - - - m - - - 0 0 1 1 | m +//- - - - - - - - - - - - - - n - - 1 0 1 1 | n +//- - - - - - - - - - - - - - - o - 0 1 1 1 | o +//- - - - - - - - - - - - - - - - p 1 1 1 1 | p +//- module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y); input A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V; output Y; @@ -108,37 +274,145 @@ assign Y = V ? U ? T ? (S ? P : O) : (S ? B : A); endmodule -module \$_AOI3_ (A, B, C, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_AOI3_ (A, B, C, Y) +//- +//- A 3-input And-Or-Invert gate. +//- +//- Truth table: A B C | Y +//- -------+--- +//- 0 0 0 | 1 +//- 0 0 1 | 0 +//- 0 1 0 | 1 +//- 0 1 1 | 0 +//- 1 0 0 | 1 +//- 1 0 1 | 0 +//- 1 1 0 | 0 +//- 1 1 1 | 0 +//- +module \$_AOI3_ (A, B, C, Y); input A, B, C; output Y; assign Y = ~((A & B) | C); endmodule -module \$_OAI3_ (A, B, C, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_OAI3_ (A, B, C, Y) +//- +//- A 3-input Or-And-Invert gate. +//- +//- Truth table: A B C | Y +//- -------+--- +//- 0 0 0 | 1 +//- 0 0 1 | 1 +//- 0 1 0 | 1 +//- 0 1 1 | 0 +//- 1 0 0 | 1 +//- 1 0 1 | 0 +//- 1 1 0 | 1 +//- 1 1 1 | 0 +//- +module \$_OAI3_ (A, B, C, Y); input A, B, C; output Y; assign Y = ~((A | B) & C); endmodule -module \$_AOI4_ (A, B, C, D, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_AOI4_ (A, B, C, Y) +//- +//- A 4-input And-Or-Invert gate. +//- +//- Truth table: A B C D | Y +//- ---------+--- +//- 0 0 0 0 | 1 +//- 0 0 0 1 | 1 +//- 0 0 1 0 | 1 +//- 0 0 1 1 | 0 +//- 0 1 0 0 | 1 +//- 0 1 0 1 | 1 +//- 0 1 1 0 | 1 +//- 0 1 1 1 | 0 +//- 1 0 0 0 | 1 +//- 1 0 0 1 | 1 +//- 1 0 1 0 | 1 +//- 1 0 1 1 | 0 +//- 1 1 0 0 | 0 +//- 1 1 0 1 | 0 +//- 1 1 1 0 | 0 +//- 1 1 1 1 | 0 +//- +module \$_AOI4_ (A, B, C, D, Y); input A, B, C, D; output Y; assign Y = ~((A & B) | (C & D)); endmodule -module \$_OAI4_ (A, B, C, D, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_OAI4_ (A, B, C, Y) +//- +//- A 4-input Or-And-Invert gate. +//- +//- Truth table: A B C D | Y +//- ---------+--- +//- 0 0 0 0 | 1 +//- 0 0 0 1 | 1 +//- 0 0 1 0 | 1 +//- 0 0 1 1 | 1 +//- 0 1 0 0 | 1 +//- 0 1 0 1 | 0 +//- 0 1 1 0 | 0 +//- 0 1 1 1 | 0 +//- 1 0 0 0 | 1 +//- 1 0 0 1 | 0 +//- 1 0 1 0 | 0 +//- 1 0 1 1 | 0 +//- 1 1 0 0 | 1 +//- 1 1 0 1 | 0 +//- 1 1 1 0 | 0 +//- 1 1 1 1 | 0 +//- +module \$_OAI4_ (A, B, C, D, Y); input A, B, C, D; output Y; assign Y = ~((A | B) & (C | D)); endmodule +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_TBUF_ (A, E, Y) +//- +//- A tri-state buffer. +//- +//- Truth table: A E | Y +//- -----+--- +//- a 1 | a +//- - 0 | z +//- module \$_TBUF_ (A, E, Y); input A, E; output Y; assign Y = E ? A : 1'bz; endmodule -module \$_SR_NN_ (S, R, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_SR_NN_ (S, R, Q) +//- +//- A set-reset latch with negative polarity SET and RESET. +//- +//- Truth table: S R | Q +//- -----+--- +//- 0 0 | x +//- 0 1 | 1 +//- 1 0 | 0 +//- 1 1 | y +//- +module \$_SR_NN_ (S, R, Q); input S, R; output reg Q; always @(negedge S, negedge R) begin @@ -149,7 +423,20 @@ always @(negedge S, negedge R) begin end endmodule -module \$_SR_NP_ (S, R, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_SR_NP_ (S, R, Q) +//- +//- A set-reset latch with negative polarity SET and positive polarioty RESET. +//- +//- Truth table: S R | Q +//- -----+--- +//- 0 1 | x +//- 0 0 | 1 +//- 1 1 | 0 +//- 1 0 | y +//- +module \$_SR_NP_ (S, R, Q); input S, R; output reg Q; always @(negedge S, posedge R) begin @@ -160,7 +447,20 @@ always @(negedge S, posedge R) begin end endmodule -module \$_SR_PN_ (S, R, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_SR_PN_ (S, R, Q) +//- +//- A set-reset latch with positive polarity SET and negative polarioty RESET. +//- +//- Truth table: S R | Q +//- -----+--- +//- 1 0 | x +//- 1 1 | 1 +//- 0 0 | 0 +//- 0 1 | y +//- +module \$_SR_PN_ (S, R, Q); input S, R; output reg Q; always @(posedge S, negedge R) begin @@ -171,7 +471,20 @@ always @(posedge S, negedge R) begin end endmodule -module \$_SR_PP_ (S, R, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_SR_PP_ (S, R, Q) +//- +//- A set-reset latch with positive polarity SET and RESET. +//- +//- Truth table: S R | Q +//- -----+--- +//- 1 1 | x +//- 1 0 | 1 +//- 0 1 | 0 +//- 0 0 | y +//- +module \$_SR_PP_ (S, R, Q); input S, R; output reg Q; always @(posedge S, posedge R) begin @@ -182,7 +495,18 @@ always @(posedge S, posedge R) begin end endmodule -module \$_DFF_N_ (D, Q, C); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_N_ (D, C, Q) +//- +//- A negative edge D-type flip-flop. +//- +//- Truth table: D C | Q +//- -----+--- +//- d \ | d +//- - - | q +//- +module \$_DFF_N_ (D, C, Q); input D, C; output reg Q; always @(negedge C) begin @@ -190,7 +514,18 @@ always @(negedge C) begin end endmodule -module \$_DFF_P_ (D, Q, C); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_P_ (D, C, Q) +//- +//- A positive edge D-type flip-flop. +//- +//- Truth table: D C | Q +//- -----+--- +//- d / | d +//- - - | q +//- +module \$_DFF_P_ (D, C, Q); input D, C; output reg Q; always @(posedge C) begin @@ -198,7 +533,18 @@ always @(posedge C) begin end endmodule -module \$_DFFE_NN_ (D, Q, C, E); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFE_NN_ (D, C, E, Q) +//- +//- A negative edge D-type flip-flop with negative polarity enable. +//- +//- Truth table: D C E | Q +//- -------+--- +//- d \ 0 | d +//- - - - | q +//- +module \$_DFFE_NN_ (D, C, E, Q); input D, C, E; output reg Q; always @(negedge C) begin @@ -206,7 +552,18 @@ always @(negedge C) begin end endmodule -module \$_DFFE_NP_ (D, Q, C, E); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFE_NP_ (D, C, E, Q) +//- +//- A negative edge D-type flip-flop with positive polarity enable. +//- +//- Truth table: D C E | Q +//- -------+--- +//- d \ 1 | d +//- - - - | q +//- +module \$_DFFE_NP_ (D, C, E, Q); input D, C, E; output reg Q; always @(negedge C) begin @@ -214,7 +571,18 @@ always @(negedge C) begin end endmodule -module \$_DFFE_PN_ (D, Q, C, E); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFE_PN_ (D, C, E, Q) +//- +//- A positive edge D-type flip-flop with negative polarity enable. +//- +//- Truth table: D C E | Q +//- -------+--- +//- d / 0 | d +//- - - - | q +//- +module \$_DFFE_PN_ (D, C, E, Q); input D, C, E; output reg Q; always @(posedge C) begin @@ -222,7 +590,18 @@ always @(posedge C) begin end endmodule -module \$_DFFE_PP_ (D, Q, C, E); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFE_PP_ (D, C, E, Q) +//- +//- A positive edge D-type flip-flop with positive polarity enable. +//- +//- Truth table: D C E | Q +//- -------+--- +//- d / 1 | d +//- - - - | q +//- +module \$_DFFE_PP_ (D, C, E, Q); input D, C, E; output reg Q; always @(posedge C) begin @@ -230,7 +609,19 @@ always @(posedge C) begin end endmodule -module \$_DFF_NN0_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_NN0_ (D, C, R, Q) +//- +//- A negative edge D-type flip-flop with negative polarity reset. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 0 | 0 +//- d \ - | d +//- - - - | q +//- +module \$_DFF_NN0_ (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or negedge R) begin @@ -241,7 +632,19 @@ always @(negedge C or negedge R) begin end endmodule -module \$_DFF_NN1_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_NN1_ (D, C, R, Q) +//- +//- A negative edge D-type flip-flop with negative polarity set. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 0 | 1 +//- d \ - | d +//- - - - | q +//- +module \$_DFF_NN1_ (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or negedge R) begin @@ -252,7 +655,19 @@ always @(negedge C or negedge R) begin end endmodule -module \$_DFF_NP0_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_NP0_ (D, C, R, Q) +//- +//- A negative edge D-type flip-flop with positive polarity reset. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 1 | 0 +//- d \ - | d +//- - - - | q +//- +module \$_DFF_NP0_ (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or posedge R) begin @@ -263,7 +678,19 @@ always @(negedge C or posedge R) begin end endmodule -module \$_DFF_NP1_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_NP1_ (D, C, R, Q) +//- +//- A negative edge D-type flip-flop with positive polarity set. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 1 | 1 +//- d \ - | d +//- - - - | q +//- +module \$_DFF_NP1_ (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or posedge R) begin @@ -274,7 +701,19 @@ always @(negedge C or posedge R) begin end endmodule -module \$_DFF_PN0_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_PN0_ (D, C, R, Q) +//- +//- A positive edge D-type flip-flop with negative polarity reset. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 0 | 0 +//- d / - | d +//- - - - | q +//- +module \$_DFF_PN0_ (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or negedge R) begin @@ -285,7 +724,19 @@ always @(posedge C or negedge R) begin end endmodule -module \$_DFF_PN1_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_PN1_ (D, C, R, Q) +//- +//- A positive edge D-type flip-flop with negative polarity set. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 0 | 1 +//- d / - | d +//- - - - | q +//- +module \$_DFF_PN1_ (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or negedge R) begin @@ -296,7 +747,19 @@ always @(posedge C or negedge R) begin end endmodule -module \$_DFF_PP0_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_PP0_ (D, C, R, Q) +//- +//- A positive edge D-type flip-flop with positive polarity reset. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 1 | 0 +//- d / - | d +//- - - - | q +//- +module \$_DFF_PP0_ (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or posedge R) begin @@ -307,7 +770,19 @@ always @(posedge C or posedge R) begin end endmodule -module \$_DFF_PP1_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_PP1_ (D, C, R, Q) +//- +//- A positive edge D-type flip-flop with positive polarity set. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 1 | 1 +//- d / - | d +//- - - - | q +//- +module \$_DFF_PP1_ (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or posedge R) begin @@ -318,7 +793,7 @@ always @(posedge C or posedge R) begin end endmodule -module \$_DFFSR_NNN_ (C, S, R, D, Q); +module \$_DFFSR_NNN_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(negedge C, negedge S, negedge R) begin @@ -331,7 +806,7 @@ always @(negedge C, negedge S, negedge R) begin end endmodule -module \$_DFFSR_NNP_ (C, S, R, D, Q); +module \$_DFFSR_NNP_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(negedge C, negedge S, posedge R) begin @@ -344,7 +819,7 @@ always @(negedge C, negedge S, posedge R) begin end endmodule -module \$_DFFSR_NPN_ (C, S, R, D, Q); +module \$_DFFSR_NPN_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(negedge C, posedge S, negedge R) begin @@ -357,7 +832,7 @@ always @(negedge C, posedge S, negedge R) begin end endmodule -module \$_DFFSR_NPP_ (C, S, R, D, Q); +module \$_DFFSR_NPP_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(negedge C, posedge S, posedge R) begin @@ -370,7 +845,7 @@ always @(negedge C, posedge S, posedge R) begin end endmodule -module \$_DFFSR_PNN_ (C, S, R, D, Q); +module \$_DFFSR_PNN_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(posedge C, negedge S, negedge R) begin @@ -383,7 +858,7 @@ always @(posedge C, negedge S, negedge R) begin end endmodule -module \$_DFFSR_PNP_ (C, S, R, D, Q); +module \$_DFFSR_PNP_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(posedge C, negedge S, posedge R) begin @@ -396,7 +871,7 @@ always @(posedge C, negedge S, posedge R) begin end endmodule -module \$_DFFSR_PPN_ (C, S, R, D, Q); +module \$_DFFSR_PPN_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(posedge C, posedge S, negedge R) begin @@ -409,7 +884,7 @@ always @(posedge C, posedge S, negedge R) begin end endmodule -module \$_DFFSR_PPP_ (C, S, R, D, Q); +module \$_DFFSR_PPP_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(posedge C, posedge S, posedge R) begin @@ -422,7 +897,7 @@ always @(posedge C, posedge S, posedge R) begin end endmodule -module \$_DLATCH_N_ (E, D, Q); +module \$_DLATCH_N_ (E, D, Q); input E, D; output reg Q; always @* begin @@ -431,7 +906,7 @@ always @* begin end endmodule -module \$_DLATCH_P_ (E, D, Q); +module \$_DLATCH_P_ (E, D, Q); input E, D; output reg Q; always @* begin @@ -440,7 +915,7 @@ always @* begin end endmodule -module \$_DLATCHSR_NNN_ (E, S, R, D, Q); +module \$_DLATCHSR_NNN_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -453,7 +928,7 @@ always @* begin end endmodule -module \$_DLATCHSR_NNP_ (E, S, R, D, Q); +module \$_DLATCHSR_NNP_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -466,7 +941,7 @@ always @* begin end endmodule -module \$_DLATCHSR_NPN_ (E, S, R, D, Q); +module \$_DLATCHSR_NPN_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -479,7 +954,7 @@ always @* begin end endmodule -module \$_DLATCHSR_NPP_ (E, S, R, D, Q); +module \$_DLATCHSR_NPP_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -492,7 +967,7 @@ always @* begin end endmodule -module \$_DLATCHSR_PNN_ (E, S, R, D, Q); +module \$_DLATCHSR_PNN_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -505,7 +980,7 @@ always @* begin end endmodule -module \$_DLATCHSR_PNP_ (E, S, R, D, Q); +module \$_DLATCHSR_PNP_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -518,7 +993,7 @@ always @* begin end endmodule -module \$_DLATCHSR_PPN_ (E, S, R, D, Q); +module \$_DLATCHSR_PPN_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -531,7 +1006,7 @@ always @* begin end endmodule -module \$_DLATCHSR_PPP_ (E, S, R, D, Q); +module \$_DLATCHSR_PPP_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 2a56b3a1e..a2dc466d4 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -1494,7 +1494,7 @@ endmodule // -------------------------------------------------------- `ifndef SIMLIB_NOMEM -module \$memrd (CLK, ADDR, DATA); +module \$memrd (CLK, EN, ADDR, DATA); parameter MEMID = ""; parameter ABITS = 8; @@ -1504,7 +1504,7 @@ parameter CLK_ENABLE = 0; parameter CLK_POLARITY = 0; parameter TRANSPARENT = 0; -input CLK; +input CLK, EN; input [ABITS-1:0] ADDR; output [WIDTH-1:0] DATA; @@ -1568,7 +1568,7 @@ endmodule // -------------------------------------------------------- -module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); +module \$mem (RD_CLK, RD_EN, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); parameter MEMID = ""; parameter signed SIZE = 4; @@ -1587,6 +1587,7 @@ parameter WR_CLK_ENABLE = 1'b1; parameter WR_CLK_POLARITY = 1'b1; input [RD_PORTS-1:0] RD_CLK; +input [RD_PORTS-1:0] RD_EN; input [RD_PORTS*ABITS-1:0] RD_ADDR; output reg [RD_PORTS*WIDTH-1:0] RD_DATA; @@ -1626,7 +1627,7 @@ always @(RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA) begin #`SIMLIB_MEMDELAY; `endif for (i = 0; i < RD_PORTS; i = i+1) begin - if ((!RD_TRANSPARENT[i] && RD_CLK_ENABLE[i]) && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) begin + if (!RD_TRANSPARENT[i] && RD_CLK_ENABLE[i] && RD_EN[i] && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) begin // $display("Read from %s: addr=%b data=%b", MEMID, RD_ADDR[i*ABITS +: ABITS], memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]); RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]; end diff --git a/techlibs/greenpak4/Makefile.inc b/techlibs/greenpak4/Makefile.inc new file mode 100644 index 000000000..5808e7bdf --- /dev/null +++ b/techlibs/greenpak4/Makefile.inc @@ -0,0 +1,6 @@ + +OBJS += techlibs/greenpak4/synth_greenpak4.o + +$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/cells_map.v)) +$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/cells_sim.v)) +$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/gp_dff.lib)) diff --git a/techlibs/greenpak4/cells_map.v b/techlibs/greenpak4/cells_map.v new file mode 100644 index 000000000..667d853da --- /dev/null +++ b/techlibs/greenpak4/cells_map.v @@ -0,0 +1,48 @@ +module \$_DFF_P_ (input D, C, output Q); + GP_DFF _TECHMAP_REPLACE_ ( + .D(D), + .Q(Q), + .CLK(C), + .nRSTZ(1'b1), + .nSETZ(1'b1) + ); +endmodule + +module \$_DFFSR_PNN_ (input C, S, R, D, output Q); + GP_DFF _TECHMAP_REPLACE_ ( + .D(D), + .Q(Q), + .CLK(C), + .nRSTZ(R), + .nSETZ(S) + ); +endmodule + +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + + input [WIDTH-1:0] A; + output Y; + + generate + if (WIDTH == 1) begin + GP_2LUT #(.INIT({2'b00, LUT})) _TECHMAP_REPLACE_ (.OUT(Y), + .IN0(A[0]), .IN1(1'b0)); + end else + if (WIDTH == 2) begin + GP_2LUT #(.INIT(LUT)) _TECHMAP_REPLACE_ (.OUT(Y), + .IN0(A[0]), .IN1(A[1])); + end else + if (WIDTH == 3) begin + GP_3LUT #(.INIT(LUT)) _TECHMAP_REPLACE_ (.OUT(Y), + .IN0(A[0]), .IN1(A[1]), .IN2(A[2])); + end else + if (WIDTH == 4) begin + GP_4LUT #(.INIT(LUT)) _TECHMAP_REPLACE_ (.OUT(Y), + .IN0(A[0]), .IN1(A[1]), .IN2(A[2]), .IN3(A[3])); + end else begin + wire _TECHMAP_FAIL_ = 1; + end + endgenerate +endmodule diff --git a/techlibs/greenpak4/cells_sim.v b/techlibs/greenpak4/cells_sim.v new file mode 100644 index 000000000..d9ddaaccf --- /dev/null +++ b/techlibs/greenpak4/cells_sim.v @@ -0,0 +1,25 @@ +module GP_DFF(input D, CLK, nRSTZ, nSETZ, output reg Q); + always @(posedge CLK, negedge nRSTZ, negedge nSETZ) begin + if (!nRSTZ) + Q <= 1'b0; + else if (!nSETZ) + Q <= 1'b1; + else + Q <= D; + end +endmodule + +module GP_2LUT(input IN0, IN1, output OUT); + parameter [3:0] INIT = 0; + assign OUT = INIT[{IN1, IN0}]; +endmodule + +module GP_3LUT(input IN0, IN1, IN2, output OUT); + parameter [7:0] INIT = 0; + assign OUT = INIT[{IN2, IN1, IN0}]; +endmodule + +module GP_4LUT(input IN0, IN1, IN2, IN3, output OUT); + parameter [15:0] INIT = 0; + assign OUT = INIT[{IN3, IN2, IN1, IN0}]; +endmodule diff --git a/techlibs/greenpak4/gp_dff.lib b/techlibs/greenpak4/gp_dff.lib new file mode 100644 index 000000000..9e2e46cb4 --- /dev/null +++ b/techlibs/greenpak4/gp_dff.lib @@ -0,0 +1,26 @@ +library(gp_dff) { + cell(GP_DFF_NOSR) { + area: 1; + ff("IQ", "IQN") { clocked_on: CLK; + next_state: D; } + pin(CLK) { direction: input; + clock: true; } + pin(D) { direction: input; } + pin(Q) { direction: output; + function: "IQ"; } + } + cell(GP_DFF_SR) { + area: 1; + ff("IQ", "IQN") { clocked_on: CLK; + next_state: D; + preset: "nSETZ'"; + clear: "nRSTZ'"; } + pin(CLK) { direction: input; + clock: true; } + pin(D) { direction: input; } + pin(Q) { direction: output; + function: "IQ"; } + pin(nRSTZ) { direction: input; } + pin(nSETZ) { direction: input; } + } +} diff --git a/techlibs/greenpak4/synth_greenpak4.cc b/techlibs/greenpak4/synth_greenpak4.cc new file mode 100644 index 000000000..15b53d623 --- /dev/null +++ b/techlibs/greenpak4/synth_greenpak4.cc @@ -0,0 +1,233 @@ +/* + * 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/register.h" +#include "kernel/celltypes.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool check_label(bool &active, std::string run_from, std::string run_to, std::string label) +{ + if (label == run_from) + active = true; + if (label == run_to) + active = false; + return active; +} + +struct SynthGreenPAK4Pass : public Pass { + SynthGreenPAK4Pass() : Pass("synth_greenpak4", "synthesis for GreenPAK4 FPGAs") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_greenpak4 [options]\n"); + log("\n"); + log("This command runs synthesis for GreenPAK4 FPGAs. This work is experimental.\n"); + log("\n"); + log(" -top <module>\n"); + log(" use the specified module as top module (default='top')\n"); + log("\n"); + log(" -blif <file>\n"); + log(" write the design to the specified BLIF file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -edif <file>\n"); + log(" write the design to the specified edif file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -run <from_label>:<to_label>\n"); + log(" only run the commands between the labels (see below). an empty\n"); + log(" from label is synonymous to 'begin', and empty to label is\n"); + log(" synonymous to the end of the command list.\n"); + log("\n"); + log(" -noflatten\n"); + log(" do not flatten design before synthesis\n"); + log("\n"); + log(" -retime\n"); + log(" run 'abc' with -dff option\n"); + log("\n"); + log("\n"); + log("The following commands are executed by this synthesis command:\n"); + log("\n"); + log(" begin:\n"); + log(" read_verilog -lib +/greenpak4/cells_sim.v\n"); + log(" hierarchy -check -top <top>\n"); + log("\n"); + log(" flatten: (unless -noflatten)\n"); + log(" proc\n"); + log(" flatten\n"); + log(" tribuf -logic\n"); + log("\n"); + log(" coarse:\n"); + log(" synth -run coarse\n"); + log("\n"); + log(" fine:\n"); + log(" opt -fast -mux_undef -undriven -fine\n"); + log(" memory_map\n"); + log(" opt -undriven -fine\n"); + log(" techmap\n"); + log(" dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib\n"); + log(" opt -fast\n"); + log(" abc -dff (only if -retime)\n"); + log("\n"); + log(" map_luts:\n"); + log(" nlutmap -luts 0,8,16,2\n"); + log(" clean\n"); + log("\n"); + log(" map_cells:\n"); + log(" techmap -map +/greenpak4/cells_map.v\n"); + log(" clean\n"); + log("\n"); + log(" check:\n"); + log(" hierarchy -check\n"); + log(" stat\n"); + log(" check -noinit\n"); + log("\n"); + log(" blif:\n"); + log(" write_blif -gates -attr -param <file-name>\n"); + log("\n"); + log(" edif:\n"); + log(" write_edif <file-name>\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + std::string top_opt = "-auto-top"; + std::string run_from, run_to; + std::string blif_file, edif_file; + bool flatten = true; + bool retime = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-top" && argidx+1 < args.size()) { + top_opt = "-top " + args[++argidx]; + continue; + } + if (args[argidx] == "-blif" && argidx+1 < args.size()) { + blif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-edif" && argidx+1 < args.size()) { + edif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-run" && argidx+1 < args.size()) { + size_t pos = args[argidx+1].find(':'); + if (pos == std::string::npos) + break; + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos+1); + continue; + } + if (args[argidx] == "-flatten") { + flatten = true; + continue; + } + if (args[argidx] == "-noflatten") { + flatten = false; + continue; + } + if (args[argidx] == "-retime") { + retime = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!design->full_selection()) + log_cmd_error("This comannd only operates on fully selected designs!\n"); + + bool active = run_from.empty(); + + log_header("Executing SYNTH_GREENPAK4 pass.\n"); + log_push(); + + if (check_label(active, run_from, run_to, "begin")) + { + Pass::call(design, "read_verilog -lib +/greenpak4/cells_sim.v"); + Pass::call(design, stringf("hierarchy -check %s", top_opt.c_str())); + } + + if (flatten && check_label(active, run_from, run_to, "flatten")) + { + Pass::call(design, "proc"); + Pass::call(design, "flatten"); + Pass::call(design, "tribuf -logic"); + } + + if (check_label(active, run_from, run_to, "coarse")) + { + Pass::call(design, "synth -run coarse"); + } + + if (check_label(active, run_from, run_to, "fine")) + { + Pass::call(design, "opt -fast -mux_undef -undriven -fine"); + Pass::call(design, "memory_map"); + Pass::call(design, "opt -undriven -fine"); + Pass::call(design, "techmap"); + Pass::call(design, "dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib"); + Pass::call(design, "opt -fast"); + if (retime) + Pass::call(design, "abc -dff"); + } + + if (check_label(active, run_from, run_to, "map_luts")) + { + Pass::call(design, "nlutmap -luts 0,8,16,2"); + Pass::call(design, "clean"); + } + + if (check_label(active, run_from, run_to, "map_cells")) + { + Pass::call(design, "techmap -map +/greenpak4/cells_map.v"); + Pass::call(design, "clean"); + } + + if (check_label(active, run_from, run_to, "check")) + { + Pass::call(design, "hierarchy -check"); + Pass::call(design, "stat"); + Pass::call(design, "check -noinit"); + } + + if (check_label(active, run_from, run_to, "blif")) + { + if (!blif_file.empty()) + Pass::call(design, stringf("write_blif -gates -attr -param %s", blif_file.c_str())); + } + + if (check_label(active, run_from, run_to, "edif")) + { + if (!edif_file.empty()) + Pass::call(design, stringf("write_edif %s", edif_file.c_str())); + } + + log_pop(); + } +} SynthGreenPAK4Pass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/ice40/Makefile.inc b/techlibs/ice40/Makefile.inc index ed495519a..83009d176 100644 --- a/techlibs/ice40/Makefile.inc +++ b/techlibs/ice40/Makefile.inc @@ -1,6 +1,7 @@ OBJS += techlibs/ice40/synth_ice40.o OBJS += techlibs/ice40/ice40_ffssr.o +OBJS += techlibs/ice40/ice40_ffinit.o OBJS += techlibs/ice40/ice40_opt.o GENFILES += techlibs/ice40/brams_init1.vh @@ -12,7 +13,7 @@ EXTRA_OBJS += techlibs/ice40/brams_init.mk techlibs/ice40/brams_init.mk: techlibs/ice40/brams_init.py $(Q) mkdir -p techlibs/ice40 - $(P) python $< + $(P) python3 $< $(Q) touch techlibs/ice40/brams_init.mk techlibs/ice40/brams_init1.vh: techlibs/ice40/brams_init.mk diff --git a/techlibs/ice40/brams.txt b/techlibs/ice40/brams.txt index 05131b227..03d596111 100644 --- a/techlibs/ice40/brams.txt +++ b/techlibs/ice40/brams.txt @@ -5,7 +5,7 @@ bram $__ICE40_RAM4K_M0 groups 2 ports 1 1 wrmode 0 1 - enable 0 16 + enable 1 16 transp 0 0 clocks 2 3 clkpol 2 3 @@ -22,7 +22,7 @@ bram $__ICE40_RAM4K_M123 groups 2 ports 1 1 wrmode 0 1 - enable 0 1 + enable 1 1 transp 0 0 clocks 2 3 clkpol 2 3 diff --git a/techlibs/ice40/brams_init.py b/techlibs/ice40/brams_init.py index 93eb9846b..4a1485110 100644 --- a/techlibs/ice40/brams_init.py +++ b/techlibs/ice40/brams_init.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 def write_init_vh(filename, initbits): with open(filename, "w") as f: diff --git a/techlibs/ice40/brams_map.v b/techlibs/ice40/brams_map.v index 8c5c7e812..19a61d73b 100644 --- a/techlibs/ice40/brams_map.v +++ b/techlibs/ice40/brams_map.v @@ -90,7 +90,7 @@ module \$__ICE40_RAM4K ( .RCLKE(RCLKE), .RE (RE ), .RADDR(RADDR), - .WCLK (WCLK ), + .WCLKN(WCLK ), .WCLKE(WCLKE), .WE (WE ), .WADDR(WADDR), @@ -119,7 +119,7 @@ module \$__ICE40_RAM4K ( .INIT_F(INIT_F) ) _TECHMAP_REPLACE_ ( .RDATA(RDATA), - .RCLK (RCLK ), + .RCLKN(RCLK ), .RCLKE(RCLKE), .RE (RE ), .RADDR(RADDR), @@ -152,11 +152,11 @@ module \$__ICE40_RAM4K ( .INIT_F(INIT_F) ) _TECHMAP_REPLACE_ ( .RDATA(RDATA), - .RCLK (RCLK ), + .RCLKN(RCLK ), .RCLKE(RCLKE), .RE (RE ), .RADDR(RADDR), - .WCLK (WCLK ), + .WCLKN(WCLK ), .WCLKE(WCLKE), .WE (WE ), .WADDR(WADDR), @@ -168,7 +168,7 @@ module \$__ICE40_RAM4K ( endmodule -module \$__ICE40_RAM4K_M0 (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); +module \$__ICE40_RAM4K_M0 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter [0:0] CLKPOL2 = 1; parameter [0:0] CLKPOL3 = 1; @@ -179,6 +179,7 @@ module \$__ICE40_RAM4K_M0 (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); input [7:0] A1ADDR; output [15:0] A1DATA; + input A1EN; input [7:0] B1ADDR; input [15:0] B1DATA; @@ -212,18 +213,18 @@ module \$__ICE40_RAM4K_M0 (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .RDATA(A1DATA), .RADDR(A1ADDR_11), .RCLK(CLK2), - .RCLKE(1'b1), + .RCLKE(A1EN), .RE(1'b1), .WDATA(B1DATA), .WADDR(B1ADDR_11), .MASK(~B1EN), .WCLK(CLK3), - .WCLKE(1'b1), - .WE(|B1EN) + .WCLKE(|B1EN), + .WE(1'b1) ); endmodule -module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); +module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CFG_ABITS = 9; parameter CFG_DBITS = 8; @@ -242,6 +243,7 @@ module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); input [CFG_ABITS-1:0] A1ADDR; output [CFG_DBITS-1:0] A1DATA; + input A1EN; input [CFG_ABITS-1:0] B1ADDR; input [CFG_DBITS-1:0] B1DATA; @@ -297,13 +299,13 @@ module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .RDATA(A1DATA_16), .RADDR(A1ADDR_11), .RCLK(CLK2), - .RCLKE(1'b1), + .RCLKE(A1EN), .RE(1'b1), .WDATA(B1DATA_16), .WADDR(B1ADDR_11), .WCLK(CLK3), - .WCLKE(1'b1), - .WE(|B1EN) + .WCLKE(|B1EN), + .WE(1'b1) ); endmodule diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index f94040245..f23218c00 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -58,8 +58,8 @@ module SB_IO ( generate if (PIN_TYPE[5:4] == 2'b01) assign PACKAGE_PIN = dout; - if (PIN_TYPE[5:4] == 2'b10) assign PACKAGE_PIN = outena_q ? dout : 1'bz; - if (PIN_TYPE[5:4] == 2'b11) assign PACKAGE_PIN = OUTPUT_ENABLE ? dout : 1'bz; + if (PIN_TYPE[5:4] == 2'b10) assign PACKAGE_PIN = OUTPUT_ENABLE ? dout : 1'bz; + if (PIN_TYPE[5:4] == 2'b11) assign PACKAGE_PIN = outena_q ? dout : 1'bz; endgenerate `endif endmodule @@ -473,7 +473,7 @@ endmodule module SB_RAM40_4KNR ( output [15:0] RDATA, - input RCLK, RCLKE, RE, + input RCLKN, RCLKE, RE, input [10:0] RADDR, input WCLK, WCLKE, WE, input [10:0] WADDR, @@ -520,7 +520,7 @@ module SB_RAM40_4KNR ( .INIT_F (INIT_F ) ) RAM ( .RDATA(RDATA), - .RCLK (~RCLK), + .RCLK (~RCLKN), .RCLKE(RCLKE), .RE (RE ), .RADDR(RADDR), @@ -537,7 +537,7 @@ module SB_RAM40_4KNW ( output [15:0] RDATA, input RCLK, RCLKE, RE, input [10:0] RADDR, - input WCLK, WCLKE, WE, + input WCLKN, WCLKE, WE, input [10:0] WADDR, input [15:0] MASK, WDATA ); @@ -586,7 +586,7 @@ module SB_RAM40_4KNW ( .RCLKE(RCLKE), .RE (RE ), .RADDR(RADDR), - .WCLK (~WCLK), + .WCLK (~WCLKN), .WCLKE(WCLKE), .WE (WE ), .WADDR(WADDR), @@ -597,9 +597,9 @@ endmodule module SB_RAM40_4KNRNW ( output [15:0] RDATA, - input RCLK, RCLKE, RE, + input RCLKN, RCLKE, RE, input [10:0] RADDR, - input WCLK, WCLKE, WE, + input WCLKN, WCLKE, WE, input [10:0] WADDR, input [15:0] MASK, WDATA ); @@ -644,11 +644,11 @@ module SB_RAM40_4KNRNW ( .INIT_F (INIT_F ) ) RAM ( .RDATA(RDATA), - .RCLK (~RCLK), + .RCLK (~RCLKN), .RCLKE(RCLKE), .RE (RE ), .RADDR(RADDR), - .WCLK (~WCLK), + .WCLK (~WCLKN), .WCLKE(WCLKE), .WE (WE ), .WADDR(WADDR), diff --git a/techlibs/ice40/ice40_ffinit.cc b/techlibs/ice40/ice40_ffinit.cc new file mode 100644 index 000000000..50400be80 --- /dev/null +++ b/techlibs/ice40/ice40_ffinit.cc @@ -0,0 +1,142 @@ +/* + * 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 Ice40FfinitPass : public Pass { + Ice40FfinitPass() : Pass("ice40_ffinit", "iCE40: handle FF init values") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" ice40_ffinit [options] [selection]\n"); + log("\n"); + log("Remove zero init values for FF output signals. Add inverters to implement\n"); + log("nonzero init values.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("Executing ICE40_FFINIT pass (implement FF init values).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + log("Handling FF init values in %s.\n", log_id(module)); + + SigMap sigmap(module); + pool<Wire*> init_wires; + dict<SigBit, State> initbits; + pool<SigBit> handled_initbits; + + for (auto wire : module->selected_wires()) + { + if (wire->attributes.count("\\init") == 0) + continue; + + SigSpec wirebits = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + init_wires.insert(wire); + + for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++) + { + SigBit bit = wirebits[i]; + State val = initval[i]; + + if (val != State::S0 && val != State::S1) + continue; + + if (initbits.count(bit)) { + if (initbits.at(bit) != val) + log_error("Conflicting init values for signal %s.\n", log_signal(bit)); + continue; + } + + initbits[bit] = val; + } + } + + for (auto cell : module->selected_cells()) + { + if (!cell->type.in("\\SB_DFF", "\\SB_DFFE", "\\SB_DFFN", "\\SB_DFFNE")) + continue; + + SigBit sig_d = sigmap(cell->getPort("\\D")); + SigBit sig_q = sigmap(cell->getPort("\\Q")); + + if (!initbits.count(sig_q)) + continue; + + State val = initbits.at(sig_q); + handled_initbits.insert(sig_q); + + log("FF init value for cell %s (%s): %s = %c\n", log_id(cell), log_id(cell->type), + log_signal(sig_q), val != State::S0 ? '1' : '0'); + + if (val == State::S0) + continue; + + Wire *new_sig_d = module->addWire(NEW_ID); + Wire *new_sig_q = module->addWire(NEW_ID); + + module->addNotGate(NEW_ID, sig_d, new_sig_d); + module->addNotGate(NEW_ID, new_sig_q, sig_q); + + cell->setPort("\\D", new_sig_d); + cell->setPort("\\Q", new_sig_q); + } + + for (auto wire : init_wires) + { + if (wire->attributes.count("\\init") == 0) + continue; + + SigSpec wirebits = sigmap(wire); + Const &initval = wire->attributes.at("\\init"); + bool remove_attribute = true; + + for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++) { + if (handled_initbits.count(wirebits[i])) + wirebits[i] = State::Sx; + else + remove_attribute = false; + } + + if (remove_attribute) + wire->attributes.erase("\\init"); + } + } + } +} Ice40FfinitPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index 788835f1d..75cab7bda 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -104,6 +104,7 @@ struct SynthIce40Pass : public Pass { log(" techmap -map +/ice40/cells_map.v\n"); log(" opt_const -mux_undef\n"); log(" simplemap\n"); + log(" ice40_ffinit\n"); log(" ice40_ffssr\n"); log(" ice40_opt -full\n"); log("\n"); @@ -236,6 +237,7 @@ struct SynthIce40Pass : public Pass { Pass::call(design, "techmap -map +/ice40/cells_map.v"); Pass::call(design, "opt_const -mux_undef"); Pass::call(design, "simplemap"); + Pass::call(design, "ice40_ffinit"); Pass::call(design, "ice40_ffssr"); Pass::call(design, "ice40_opt -full"); } diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc index cca41f917..ccf88ec7e 100644 --- a/techlibs/xilinx/Makefile.inc +++ b/techlibs/xilinx/Makefile.inc @@ -11,7 +11,7 @@ EXTRA_OBJS += techlibs/xilinx/brams_init.mk techlibs/xilinx/brams_init.mk: techlibs/xilinx/brams_init.py $(Q) mkdir -p techlibs/xilinx - $(P) python $< + $(P) python3 $< $(Q) touch $@ techlibs/xilinx/brams_init_36.vh: techlibs/xilinx/brams_init.mk diff --git a/techlibs/xilinx/brams.txt b/techlibs/xilinx/brams.txt index 894e714c6..f1161114e 100644 --- a/techlibs/xilinx/brams.txt +++ b/techlibs/xilinx/brams.txt @@ -6,7 +6,7 @@ bram $__XILINX_RAMB36_SDP groups 2 ports 1 1 wrmode 0 1 - enable 0 8 + enable 1 8 transp 0 0 clocks 2 3 clkpol 2 3 @@ -19,7 +19,7 @@ bram $__XILINX_RAMB18_SDP groups 2 ports 1 1 wrmode 0 1 - enable 0 4 + enable 1 4 transp 0 0 clocks 2 3 clkpol 2 3 @@ -42,9 +42,9 @@ bram $__XILINX_RAMB36_TDP groups 2 ports 1 1 wrmode 0 1 - enable 0 4 @a10d36 - enable 0 2 @a11d18 - enable 0 1 @a12d9 @a13d4 @a14d2 @a15d1 + enable 1 4 @a10d36 + enable 1 2 @a11d18 + enable 1 1 @a12d9 @a13d4 @a14d2 @a15d1 transp 0 0 clocks 2 3 clkpol 2 3 @@ -65,8 +65,8 @@ bram $__XILINX_RAMB18_TDP groups 2 ports 1 1 wrmode 0 1 - enable 0 2 @a10d18 - enable 0 1 @a11d9 @a12d4 @a13d2 @a14d1 + enable 1 2 @a10d18 + enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1 transp 0 0 clocks 2 3 clkpol 2 3 diff --git a/techlibs/xilinx/brams_init.py b/techlibs/xilinx/brams_init.py index eac829ddf..e787b1f76 100644 --- a/techlibs/xilinx/brams_init.py +++ b/techlibs/xilinx/brams_init.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 with open("techlibs/xilinx/brams_init_18.vh", "w") as f: for i in range(8): diff --git a/techlibs/xilinx/brams_map.v b/techlibs/xilinx/brams_map.v index cbfd4e1eb..7ea49158d 100644 --- a/techlibs/xilinx/brams_map.v +++ b/techlibs/xilinx/brams_map.v @@ -1,4 +1,4 @@ -module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); +module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CLKPOL2 = 1; parameter CLKPOL3 = 1; parameter [36863:0] INIT = 36864'bx; @@ -8,6 +8,7 @@ module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); input [8:0] A1ADDR; output [71:0] A1DATA; + input A1EN; input [8:0] B1ADDR; input [71:0] B1DATA; @@ -47,7 +48,7 @@ module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .ADDRARDADDR(A1ADDR_16), .CLKARDCLK(CLK2), - .ENARDEN(|1), + .ENARDEN(A1EN), .REGCEAREGCE(|1), .RSTRAMARSTRAM(|0), .RSTREGARSTREG(|0), @@ -65,7 +66,7 @@ endmodule // ------------------------------------------------------------------------ -module \$__XILINX_RAMB18_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); +module \$__XILINX_RAMB18_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CLKPOL2 = 1; parameter CLKPOL3 = 1; parameter [18431:0] INIT = 18432'bx; @@ -75,6 +76,7 @@ module \$__XILINX_RAMB18_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); input [8:0] A1ADDR; output [35:0] A1DATA; + input A1EN; input [8:0] B1ADDR; input [35:0] B1DATA; @@ -111,7 +113,7 @@ module \$__XILINX_RAMB18_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .ADDRARDADDR(A1ADDR_14), .CLKARDCLK(CLK2), - .ENARDEN(|1), + .ENARDEN(A1EN), .REGCEAREGCE(|1), .RSTRAMARSTRAM(|0), .RSTREGARSTREG(|0), @@ -129,7 +131,7 @@ endmodule // ------------------------------------------------------------------------ -module \$__XILINX_RAMB36_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); +module \$__XILINX_RAMB36_TDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CFG_ABITS = 10; parameter CFG_DBITS = 36; parameter CFG_ENABLE_B = 4; @@ -143,6 +145,7 @@ module \$__XILINX_RAMB36_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); input [CFG_ABITS-1:0] A1ADDR; output [CFG_DBITS-1:0] A1DATA; + input A1EN; input [CFG_ABITS-1:0] B1ADDR; input [CFG_DBITS-1:0] B1DATA; @@ -181,7 +184,7 @@ module \$__XILINX_RAMB36_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .DOPADOP(DOP[3:0]), .ADDRARDADDR(A1ADDR_16), .CLKARDCLK(CLK2), - .ENARDEN(|1), + .ENARDEN(A1EN), .REGCEAREGCE(|1), .RSTRAMARSTRAM(|0), .RSTREGARSTREG(|0), @@ -219,7 +222,7 @@ module \$__XILINX_RAMB36_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .DOPADOP(DOP[3:0]), .ADDRARDADDR(A1ADDR_16), .CLKARDCLK(CLK2), - .ENARDEN(|1), + .ENARDEN(A1EN), .REGCEAREGCE(|1), .RSTRAMARSTRAM(|0), .RSTREGARSTREG(|0), @@ -242,7 +245,7 @@ endmodule // ------------------------------------------------------------------------ -module \$__XILINX_RAMB18_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); +module \$__XILINX_RAMB18_TDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CFG_ABITS = 10; parameter CFG_DBITS = 18; parameter CFG_ENABLE_B = 2; @@ -256,6 +259,7 @@ module \$__XILINX_RAMB18_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); input [CFG_ABITS-1:0] A1ADDR; output [CFG_DBITS-1:0] A1DATA; + input A1EN; input [CFG_ABITS-1:0] B1ADDR; input [CFG_DBITS-1:0] B1DATA; @@ -294,7 +298,7 @@ module \$__XILINX_RAMB18_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .DOPADOP(DOP), .ADDRARDADDR(A1ADDR_14), .CLKARDCLK(CLK2), - .ENARDEN(|1), + .ENARDEN(A1EN), .REGCEAREGCE(|1), .RSTRAMARSTRAM(|0), .RSTREGARSTREG(|0), @@ -332,7 +336,7 @@ module \$__XILINX_RAMB18_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .DOPADOP(DOP), .ADDRARDADDR(A1ADDR_14), .CLKARDCLK(CLK2), - .ENARDEN(|1), + .ENARDEN(A1EN), .REGCEAREGCE(|1), .RSTRAMARSTRAM(|0), .RSTREGARSTREG(|0), diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index b3d4c214f..fbcc96014 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -79,7 +79,6 @@ struct SynthXilinxPass : public Pass { log("\n"); log(" coarse:\n"); log(" synth -run coarse\n"); - log(" dff2dffe\n"); log("\n"); log(" bram:\n"); log(" memory_bram -rules +/xilinx/brams.txt\n"); @@ -92,6 +91,7 @@ struct SynthXilinxPass : public Pass { log(" fine:\n"); log(" opt -fast -full\n"); log(" memory_map\n"); + log(" dff2dffe\n"); log(" opt -full\n"); log(" techmap -map +/techmap.v -map +/xilinx/arith_map.v\n"); log(" opt -fast\n"); @@ -178,7 +178,6 @@ struct SynthXilinxPass : public Pass { if (check_label(active, run_from, run_to, "coarse")) { Pass::call(design, "synth -run coarse"); - Pass::call(design, "dff2dffe"); } if (check_label(active, run_from, run_to, "bram")) @@ -197,6 +196,7 @@ struct SynthXilinxPass : public Pass { { Pass::call(design, "opt -fast -full"); Pass::call(design, "memory_map"); + Pass::call(design, "dff2dffe"); Pass::call(design, "opt -full"); Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/arith_map.v"); Pass::call(design, "opt -fast"); @@ -204,7 +204,7 @@ struct SynthXilinxPass : public Pass { if (check_label(active, run_from, run_to, "map_luts")) { - Pass::call(design, "abc -lut 5:8" + string(retime ? " -dff" : "")); + Pass::call(design, "abc -lut 6:8" + string(retime ? " -dff" : "")); Pass::call(design, "clean"); } diff --git a/tests/bram/generate.py b/tests/bram/generate.py index 766157eb3..05a7ed027 100644 --- a/tests/bram/generate.py +++ b/tests/bram/generate.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 import os import sys diff --git a/tests/bram/run-test.sh b/tests/bram/run-test.sh index d617187ec..f0bf0131e 100755 --- a/tests/bram/run-test.sh +++ b/tests/bram/run-test.sh @@ -8,7 +8,7 @@ rm -rf temp mkdir -p temp echo "generating tests.." -python generate.py +python3 generate.py { echo -n "all:" diff --git a/tests/fsm/generate.py b/tests/fsm/generate.py index fb5695ff6..8757d4741 100644 --- a/tests/fsm/generate.py +++ b/tests/fsm/generate.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 import sys import random diff --git a/tests/fsm/run-test.sh b/tests/fsm/run-test.sh index 57c2a5b12..423892334 100755 --- a/tests/fsm/run-test.sh +++ b/tests/fsm/run-test.sh @@ -8,7 +8,7 @@ set -e rm -rf temp mkdir -p temp echo "generating tests.." -python generate.py +python3 generate.py { all_targets="all_targets:" diff --git a/tests/realmath/generate.py b/tests/realmath/generate.py index aee211185..19d01c7c6 100644 --- a/tests/realmath/generate.py +++ b/tests/realmath/generate.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 import sys import random diff --git a/tests/realmath/run-test.sh b/tests/realmath/run-test.sh index 8419688c9..f1ec5476b 100755 --- a/tests/realmath/run-test.sh +++ b/tests/realmath/run-test.sh @@ -4,7 +4,7 @@ set -e rm -rf temp mkdir -p temp echo "generating tests.." -python generate.py +python3 generate.py cd temp echo "running tests.." diff --git a/tests/share/generate.py b/tests/share/generate.py index 271dd9c40..01a19a8d9 100644 --- a/tests/share/generate.py +++ b/tests/share/generate.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 import sys import random diff --git a/tests/share/run-test.sh b/tests/share/run-test.sh index 6e880677c..18dbbc279 100755 --- a/tests/share/run-test.sh +++ b/tests/share/run-test.sh @@ -8,7 +8,7 @@ set -e rm -rf temp mkdir -p temp echo "generating tests.." -python generate.py +python3 generate.py echo "running tests.." for i in $( ls temp/*.ys | sed 's,[^0-9],,g; s,^0*\(.\),\1,g;' ); do diff --git a/tests/simple/graphtest.v b/tests/simple/graphtest.v new file mode 100644 index 000000000..74788dbbe --- /dev/null +++ b/tests/simple/graphtest.v @@ -0,0 +1,34 @@ +module graphtest (A,B,X,Y,Z); + +input [3:0] A; +input [3:0] B; +output reg [3:0] X; +output [9:0] Y; +output [7:0] Z; + +wire [4:0] t; + +assign t[4] = 1'b0; // Constant connects to wire +assign t[2:0] = A[2:0] & { 2'b10, B[3]}; // Concatenation of intermediate wire +assign t[3] = A[2] ^ B[3]; // Bitwise-XOR + +// assign Y[2:0] = 3'b111; +// assign Y[6:3] = A; +// assign Y[9:7] = t[0:2]; +assign Y = {3'b111, A, t[2:0]}; // Direct assignment of concatenation + +assign Z[0] = 1'b0; // Constant connects to PO +assign Z[1] = t[3]; // Intermediate sig connects to PO +assign Z[3:2] = A[2:1]; // PI connects to PO +assign Z[7:4] = {1'b0, B[2:0]}; // Concat of CV and PI connect to PO + +always @* begin + if (A == 4'b1111) begin // All-Const at port (eq) + X = B; + end + else begin + X = 4'b0000; // All-Const at port (mux) + end +end + +endmodule diff --git a/tests/simple/memory.v b/tests/simple/memory.v index 67f89cd75..d58ed9d1a 100644 --- a/tests/simple/memory.v +++ b/tests/simple/memory.v @@ -228,3 +228,18 @@ module memtest09 ( end endmodule +// ---------------------------------------------------------- + +module memtest10(input clk, input [5:0] din, output [5:0] dout); + reg [5:0] queue [0:3]; + integer i; + + always @(posedge clk) begin + queue[0] <= din; + for (i = 1; i < 4; i=i+1) begin + queue[i] <= queue[i-1]; + end + end + + assign dout = queue[3]; +endmodule diff --git a/tests/simple/task_func.v b/tests/simple/task_func.v index 9b8e26e51..fa50c1d5c 100644 --- a/tests/simple/task_func.v +++ b/tests/simple/task_func.v @@ -68,7 +68,7 @@ endmodule // ------------------------------------------------------------------- -module task_func_test03( input [7:0] din_a, input [7:0] din_b, output [7:0] dout_a); +module task_func_test03(input [7:0] din_a, input [7:0] din_b, output [7:0] dout_a); assign dout_a = test(din_a,din_b); function [7:0] test; input [7:0] a; @@ -80,3 +80,43 @@ module task_func_test03( input [7:0] din_a, input [7:0] din_b, output [7:0] dout end endfunction endmodule + +// ------------------------------------------------------------------- + +module task_func_test04(input [7:0] in, output [7:0] out1, out2, out3, out4); + parameter p = 23; + parameter px = 42; + function [7:0] test1; + input [7:0] i; + parameter p = 42; + begin + test1 = i + p; + end + endfunction + function [7:0] test2; + input [7:0] i; + parameter p2 = p+42; + begin + test2 = i + p2; + end + endfunction + function [7:0] test3; + input [7:0] i; + begin + test3 = i + p; + end + endfunction + function [7:0] test4; + input [7:0] i; + parameter px = p + 13; + parameter p3 = px - 37; + parameter p4 = p3 ^ px; + begin + test4 = i + p4; + end + endfunction + assign out1 = test1(in); + assign out2 = test2(in); + assign out3 = test3(in); + assign out4 = test4(in); +endmodule diff --git a/tests/simple/wreduce.v b/tests/simple/wreduce.v new file mode 100644 index 000000000..ba5484385 --- /dev/null +++ b/tests/simple/wreduce.v @@ -0,0 +1,9 @@ +module wreduce_test0(input [7:0] a, b, output [15:0] x, y, z); + assign x = -$signed({1'b0, a}); + assign y = $signed({1'b0, a}) + $signed({1'b0, b}); + assign z = x ^ y; +endmodule + +module wreduce_test1(input [31:0] a, b, output [7:0] x, y, z, w); + assign x = a - b, y = a * b, z = a >> b, w = a << b; +endmodule diff --git a/tests/techmap/mem_simple_4x1_map.v b/tests/techmap/mem_simple_4x1_map.v index 868f5d00c..762e2938e 100644 --- a/tests/techmap/mem_simple_4x1_map.v +++ b/tests/techmap/mem_simple_4x1_map.v @@ -1,5 +1,5 @@ -module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); +module \$mem (RD_CLK, RD_EN, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); parameter MEMID = ""; parameter SIZE = 256; parameter OFFSET = 0; @@ -17,6 +17,7 @@ module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); parameter WR_CLK_POLARITY = 1'b1; input [RD_PORTS-1:0] RD_CLK; + input [RD_PORTS-1:0] RD_EN; input [RD_PORTS*ABITS-1:0] RD_ADDR; output reg [RD_PORTS*WIDTH-1:0] RD_DATA; @@ -30,6 +31,8 @@ module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); parameter _TECHMAP_CONNMAP_RD_CLK_ = 0; parameter _TECHMAP_CONNMAP_WR_CLK_ = 0; + parameter _TECHMAP_CONSTVAL_RD_EN_ = 0; + parameter _TECHMAP_BITS_CONNMAP_ = 0; parameter _TECHMAP_CONNMAP_WR_EN_ = 0; @@ -46,6 +49,10 @@ module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); if (RD_PORTS > 1 || WR_PORTS > 1) _TECHMAP_FAIL_ <= 1; + // read enable must be constant high + if (_TECHMAP_CONSTVAL_RD_EN_[0] !== 1'b1) + _TECHMAP_FAIL_ <= 1; + // we expect positive read clock and non-transparent reads if (RD_TRANSPARENT || !RD_CLK_ENABLE || !RD_CLK_POLARITY) _TECHMAP_FAIL_ <= 1; diff --git a/tests/tools/txt2tikztiming.py b/tests/tools/txt2tikztiming.py index cfefe339f..9c6cd3a19 100755 --- a/tests/tools/txt2tikztiming.py +++ b/tests/tools/txt2tikztiming.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 import argparse import fileinput |