Merge remote-tracking branch 'origin/master' into xc7mux

This commit is contained in:
Eddie Hung 2019-05-28 09:30:53 -07:00
commit ba9513b325
17 changed files with 428 additions and 110 deletions

3
.gitignore vendored
View File

@ -4,6 +4,7 @@
*.gch
*.gcda
*.gcno
__pycache__
/.cproject
/.project
/.settings
@ -28,6 +29,8 @@
/yosys-smtbmc-script.py
/yosys-filterlib
/yosys-filterlib.exe
/kernel/*.pyh
/kernel/python_wrappers.cc
/kernel/version_*.cc
/share
/yosys-win32-mxebin-*

View File

@ -2,6 +2,7 @@
CONFIG := clang
# CONFIG := gcc
# CONFIG := gcc-4.8
# CONFIG := afl-gcc
# CONFIG := emcc
# CONFIG := mxe
# CONFIG := msys2
@ -21,12 +22,6 @@ ENABLE_PROTOBUF := 0
# python wrappers
ENABLE_PYOSYS := 0
PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)"
PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi)
PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"")
PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
PYTHON_PREFIX := `$(PYTHON_EXECUTABLE)-config --prefix`
PYTHON_DESTDIR := $(PYTHON_PREFIX)/lib/python$(PYTHON_VERSION)/site-packages
# other configuration flags
ENABLE_GCOV := 0
@ -90,6 +85,9 @@ PLUGIN_LDFLAGS += -undefined dynamic_lookup
# homebrew search paths
ifneq ($(shell which brew),)
BREW_PREFIX := $(shell brew --prefix)/opt
$(info $$BREW_PREFIX is [${BREW_PREFIX}])
CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost
LDFLAGS += -L$(BREW_PREFIX)/boost/lib
CXXFLAGS += -I$(BREW_PREFIX)/readline/include
LDFLAGS += -L$(BREW_PREFIX)/readline/lib
PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH)
@ -139,6 +137,21 @@ $(info $(subst $$--$$,$(newline),$(shell sed 's,^,[Makefile.conf] ,; s,$$,$$--$$
include Makefile.conf
endif
ifeq ($(ENABLE_PYOSYS),1)
PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)"
PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi)
PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"")
PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
PYTHON_PREFIX := $(shell $(PYTHON_EXECUTABLE)-config --prefix)
PYTHON_DESTDIR := $(PYTHON_PREFIX)/lib/python$(PYTHON_VERSION)/site-packages
# Reload Makefile.conf to override python specific variables if defined
ifneq ($(wildcard Makefile.conf),)
include Makefile.conf
endif
endif
ifeq ($(CONFIG),clang)
CXX = clang
LD = clang++
@ -186,6 +199,12 @@ LD = gcc-4.8
CXXFLAGS += -std=c++11 -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
else ifeq ($(CONFIG),afl-gcc)
CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
LD = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
CXXFLAGS += -std=c++11 -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
else ifeq ($(CONFIG),cygwin)
CXX = gcc
LD = gcc
@ -273,30 +292,51 @@ endif
ifeq ($(ENABLE_PYOSYS),1)
#Detect name of boost_python library. Some distros usbe boost_python-py<version>, other boost_python<version>, some only use the major version number, some a concatenation of major and minor version numbers
ifeq ($(OS), Darwin)
BOOST_PYTHON_LIB ?= $(shell \
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
echo ""; fi; fi; fi; fi;)
else
BOOST_PYTHON_LIB ?= $(shell \
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \
if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
echo ""; fi; fi; fi; fi;)
endif
ifeq ($(BOOST_PYTHON_LIB),)
$(error BOOST_PYTHON_LIB could not be detected. Please define manualy)
endif
ifeq ($(OS), Darwin)
ifeq ($(PYTHON_MAJOR_VERSION),3)
LDLIBS += `$(PYTHON_EXECUTABLE)-config --libs` $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
CXXFLAGS += `$(PYTHON_EXECUTABLE)-config --includes` -D WITH_PYTHON
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
else
LDLIBS += `$(PYTHON_EXECUTABLE)-config --libs` $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
CXXFLAGS += `$(PYTHON_EXECUTABLE)-config --includes` -D WITH_PYTHON
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
endif
else
ifeq ($(PYTHON_MAJOR_VERSION),3)
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
else
LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
endif
endif
ifeq ($(ENABLE_PYOSYS),1)
PY_WRAPPER_FILE = kernel/python_wrappers
OBJS += $(PY_WRAPPER_FILE).o
PY_GEN_SCRIPT= py_wrap_generator
PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()")
endif
endif
ifeq ($(ENABLE_READLINE),1)
CXXFLAGS += -DYOSYS_ENABLE_READLINE
@ -541,7 +581,11 @@ yosys$(EXE): $(OBJS)
$(P) $(LD) -o yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS)
libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
ifeq ($(OS), Darwin)
$(P) $(LD) -o libyosys.so -shared -Wl,-install_name,libyosys.so $(LDFLAGS) $^ $(LDLIBS)
else
$(P) $(LD) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LDFLAGS) $^ $(LDLIBS)
endif
%.o: %.cc
$(Q) mkdir -p $(dir $@)
@ -551,9 +595,11 @@ libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
$(Q) mkdir -p $(dir $@)
$(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) -x c++ -o $@ -E -P -
ifeq ($(ENABLE_PYOSYS),1)
$(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES)
$(Q) mkdir -p $(dir $@)
$(P) python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")"
endif
%.o: %.cpp
$(Q) mkdir -p $(dir $@)
@ -788,6 +834,9 @@ config-gcc-static: clean
config-gcc-4.8: clean
echo 'CONFIG := gcc-4.8' > Makefile.conf
config-afl-gcc: clean
echo 'CONFIG := afl-gcc' > Makefile.conf
config-emcc: clean
echo 'CONFIG := emcc' > Makefile.conf
echo 'ENABLE_TCL := 0' >> Makefile.conf
@ -834,5 +883,5 @@ echo-git-rev:
-include techlibs/*/*.d
.PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator coverage vcxsrc mxebin
.PHONY: config-clean config-clang config-gcc config-gcc-static config-gcc-4.8 config-gprof config-sudo
.PHONY: config-clean config-clang config-gcc config-gcc-static config-gcc-4.8 config-afl-gcc config-gprof config-sudo

View File

@ -257,7 +257,7 @@ for them:
- Non-synthesizable language features as defined in
IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002
- The ``tri``, ``triand``, ``trior``, ``wand`` and ``wor`` net types
- The ``tri``, ``triand`` and ``trior`` net types
- The ``config`` and ``disable`` keywords and library map files

View File

@ -194,6 +194,9 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
is_logic = false;
is_signed = false;
is_string = false;
is_wand = false;
is_wor = false;
is_unsized = false;
was_checked = false;
range_valid = false;
range_swapped = false;
@ -722,7 +725,7 @@ AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width)
}
// create an AST node for a constant (using a bit vector as value)
AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed)
AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed, bool is_unsized)
{
AstNode *node = new AstNode(AST_CONSTANT);
node->is_signed = is_signed;
@ -736,9 +739,15 @@ AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signe
node->range_valid = true;
node->range_left = node->bits.size()-1;
node->range_right = 0;
node->is_unsized = is_unsized;
return node;
}
AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed)
{
return mkconst_bits(v, is_signed, false);
}
// create an AST node for a constant (using a string in bit vector form as value)
AstNode *AstNode::mkconst_str(const std::vector<RTLIL::State> &v)
{
@ -775,6 +784,14 @@ bool AstNode::bits_only_01() const
return true;
}
RTLIL::Const AstNode::bitsAsUnsizedConst(int width)
{
RTLIL::State extbit = bits.back();
while (width > int(bits.size()))
bits.push_back(extbit);
return RTLIL::Const(bits);
}
RTLIL::Const AstNode::bitsAsConst(int width, bool is_signed)
{
std::vector<RTLIL::State> bits = this->bits;

View File

@ -173,7 +173,7 @@ namespace AST
// node content - most of it is unused in most node types
std::string str;
std::vector<RTLIL::State> bits;
bool is_input, is_output, is_reg, is_logic, is_signed, is_string, range_valid, range_swapped, was_checked;
bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized;
int port_id, range_left, range_right;
uint32_t integer;
double realvalue;
@ -262,6 +262,7 @@ namespace AST
// helper functions for creating AST nodes for constants
static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32);
static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed, bool is_unsized);
static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed);
static AstNode *mkconst_str(const std::vector<RTLIL::State> &v);
static AstNode *mkconst_str(const std::string &str);
@ -269,6 +270,7 @@ namespace AST
// helper function for creating sign-extended const objects
RTLIL::Const bitsAsConst(int width, bool is_signed);
RTLIL::Const bitsAsConst(int width = -1);
RTLIL::Const bitsAsUnsizedConst(int width);
RTLIL::Const asAttrConst();
RTLIL::Const asParaConst();
uint64_t asInt(bool is_signed);

View File

@ -904,7 +904,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (!range_valid)
log_file_error(filename, linenum, "Signal `%s' with non-constant width!\n", str.c_str());
log_assert(range_left >= range_right || (range_left == -1 && range_right == 0));
if (!(range_left >= range_right || (range_left == -1 && range_right == 0)))
log_file_error(filename, linenum, "Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1);
RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1);
wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
@ -919,6 +920,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
wire->attributes[attr.first] = attr.second->asAttrConst();
}
if (is_wand) wire->set_bool_attribute("\\wand");
if (is_wor) wire->set_bool_attribute("\\wor");
}
break;
@ -963,8 +967,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
detectSignWidth(width_hint, sign_hint);
is_signed = sign_hint;
if (type == AST_CONSTANT)
return RTLIL::SigSpec(bitsAsConst());
if (type == AST_CONSTANT) {
if (is_unsized) {
return RTLIL::SigSpec(bitsAsUnsizedConst(width_hint));
} else {
return RTLIL::SigSpec(bitsAsConst());
}
}
RTLIL::SigSpec sig = realAsConst(width_hint);
log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", realvalue, log_signal(sig));

View File

@ -71,7 +71,7 @@ static int my_ilog2(int x)
}
// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type)
static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized)
{
// all digits in string (MSB at index 0)
std::vector<uint8_t> digits;
@ -129,6 +129,9 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
return;
}
if (is_unsized && (len > len_in_bits))
log_file_error(current_filename, get_line_num(), "Unsized constant must have width of 1 bit, but have %d bits!\n", len);
for (len = len - 1; len >= 0; len--)
if (data[len] == RTLIL::S1)
break;
@ -186,7 +189,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
// Simple base-10 integer
if (*endptr == 0) {
std::vector<RTLIL::State> data;
my_strtobin(data, str, -1, 10, case_type);
my_strtobin(data, str, -1, 10, case_type, false);
if (data.back() == RTLIL::S1)
data.push_back(RTLIL::S0);
return AstNode::mkconst_bits(data, true);
@ -201,6 +204,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
{
std::vector<RTLIL::State> data;
bool is_signed = false;
bool is_unsized = false;
if (*(endptr+1) == 's') {
is_signed = true;
endptr++;
@ -209,28 +213,34 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
{
case 'b':
case 'B':
my_strtobin(data, endptr+2, len_in_bits, 2, case_type);
my_strtobin(data, endptr+2, len_in_bits, 2, case_type, false);
break;
case 'o':
case 'O':
my_strtobin(data, endptr+2, len_in_bits, 8, case_type);
my_strtobin(data, endptr+2, len_in_bits, 8, case_type, false);
break;
case 'd':
case 'D':
my_strtobin(data, endptr+2, len_in_bits, 10, case_type);
my_strtobin(data, endptr+2, len_in_bits, 10, case_type, false);
break;
case 'h':
case 'H':
my_strtobin(data, endptr+2, len_in_bits, 16, case_type);
my_strtobin(data, endptr+2, len_in_bits, 16, case_type, false);
break;
default:
return NULL;
char next_char = char(tolower(*(endptr+1)));
if (next_char == '0' || next_char == '1' || next_char == 'x' || next_char == 'z') {
my_strtobin(data, endptr+1, 1, 2, case_type, true);
is_unsized = true;
} else {
return NULL;
}
}
if (len_in_bits < 0) {
if (is_signed && data.back() == RTLIL::S1)
data.push_back(RTLIL::S0);
}
return AstNode::mkconst_bits(data, is_signed);
return AstNode::mkconst_bits(data, is_signed, is_unsized);
}
return NULL;

View File

@ -218,6 +218,8 @@ YOSYS_NAMESPACE_END
"output" { return TOK_OUTPUT; }
"inout" { return TOK_INOUT; }
"wire" { return TOK_WIRE; }
"wor" { return TOK_WOR; }
"wand" { return TOK_WAND; }
"reg" { return TOK_REG; }
"integer" { return TOK_INTEGER; }
"signed" { return TOK_SIGNED; }
@ -232,7 +234,7 @@ YOSYS_NAMESPACE_END
return TOK_CONSTVAL;
}
[0-9]*[ \t]*\'s?[bodhBODH][ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
[0-9]*[ \t]*\'s?[bodhBODH]*[ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
frontend_verilog_yylval.string = new std::string(yytext);
return TOK_CONSTVAL;
}

View File

@ -139,7 +139,7 @@ struct specify_rise_fall {
%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM
%token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP
%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR
%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC
%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC
%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC
@ -485,6 +485,12 @@ wire_type_token_io:
wire_type_token:
TOK_WIRE {
} |
TOK_WOR {
astbuf3->is_wor = true;
} |
TOK_WAND {
astbuf3->is_wand = true;
} |
TOK_REG {
astbuf3->is_reg = true;
} |

View File

@ -230,6 +230,9 @@ static void logv_warning_with_prefix(const char *prefix,
}
else
{
int bak_log_make_debug = log_make_debug;
log_make_debug = 0;
for (auto &re : log_werror_regexes)
if (std::regex_search(message, re))
log_error("%s", message.c_str());
@ -254,6 +257,7 @@ static void logv_warning_with_prefix(const char *prefix,
}
log_warnings_count++;
log_make_debug = bak_log_make_debug;
}
}
@ -285,6 +289,9 @@ static void logv_error_with_prefix(const char *prefix,
#ifdef EMSCRIPTEN
auto backup_log_files = log_files;
#endif
int bak_log_make_debug = log_make_debug;
log_make_debug = 0;
log_suppressed();
if (log_errfile != NULL)
log_files.push_back(log_errfile);
@ -298,6 +305,8 @@ static void logv_error_with_prefix(const char *prefix,
log("%s%s", prefix, log_last_error.c_str());
log_flush();
log_make_debug = bak_log_make_debug;
if (log_error_atexit)
log_error_atexit();

View File

@ -607,6 +607,7 @@ struct RTLIL::SigChunk
RTLIL::SigChunk &operator =(const RTLIL::SigChunk &other) = default;
RTLIL::SigChunk extract(int offset, int length) const;
inline int size() const { return width; }
bool operator <(const RTLIL::SigChunk &other) const;
bool operator ==(const RTLIL::SigChunk &other) const;

View File

@ -562,7 +562,8 @@ struct HierarchyPass : public Pass {
log("In parametric designs, a module might exists in several variations with\n");
log("different parameter values. This pass looks at all modules in the current\n");
log("design an re-runs the language frontends for the parametric modules as\n");
log("needed.\n");
log("needed. It also resolves assignments to wired logic data types (wand/wor),\n");
log("resolves positional module parameters, unroll array instances, and more.\n");
log("\n");
log(" -check\n");
log(" also check the design hierarchy. this generates an error when\n");
@ -943,62 +944,178 @@ struct HierarchyPass : public Pass {
std::vector<Module*> design_modules = design->modules();
for (auto module : design_modules)
for (auto cell : module->cells())
{
Module *m = design->module(cell->type);
pool<Wire*> wand_wor_index;
dict<Wire*, SigSpec> wand_map, wor_map;
vector<SigSig> new_connections;
if (m == nullptr)
continue;
if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
IdString new_m_name = m->derive(design, cell->parameters, true);
if (new_m_name.empty())
continue;
if (new_m_name != m->name) {
m = design->module(new_m_name);
blackbox_derivatives.insert(m);
for (auto wire : module->wires())
{
if (wire->get_bool_attribute("\\wand")) {
wand_map[wire] = SigSpec();
wand_wor_index.insert(wire);
}
if (wire->get_bool_attribute("\\wor")) {
wor_map[wire] = SigSpec();
wand_wor_index.insert(wire);
}
}
for (auto &conn : cell->connections())
for (auto &conn : module->connections())
{
Wire *w = m->wire(conn.first);
SigSig new_conn;
int cursor = 0;
if (w == nullptr || w->port_id == 0)
continue;
if (GetSize(conn.second) == 0)
continue;
SigSpec sig = conn.second;
if (!keep_portwidths && GetSize(w) != GetSize(conn.second))
for (auto c : conn.first.chunks())
{
if (GetSize(w) < GetSize(conn.second))
{
int n = GetSize(conn.second) - GetSize(w);
if (!w->port_input && w->port_output)
module->connect(sig.extract(GetSize(w), n), Const(0, n));
sig.remove(GetSize(w), n);
Wire *w = c.wire;
SigSpec rhs = conn.second.extract(cursor, GetSize(c));
if (wand_wor_index.count(w) == 0) {
new_conn.first.append(c);
new_conn.second.append(rhs);
} else {
if (wand_map.count(w)) {
SigSpec sig = SigSpec(State::S1, GetSize(w));
sig.replace(c.offset, rhs);
wand_map.at(w).append(sig);
} else {
SigSpec sig = SigSpec(State::S0, GetSize(w));
sig.replace(c.offset, rhs);
wor_map.at(w).append(sig);
}
}
else
cursor += GetSize(c);
}
new_connections.push_back(new_conn);
}
module->new_connections(new_connections);
for (auto cell : module->cells())
{
if (!cell->known())
continue;
for (auto &conn : cell->connections())
{
if (!cell->output(conn.first))
continue;
SigSpec new_sig;
bool update_port = false;
for (auto c : conn.second.chunks())
{
int n = GetSize(w) - GetSize(conn.second);
if (w->port_input && !w->port_output)
sig.append(Const(0, n));
else
sig.append(module->addWire(NEW_ID, n));
Wire *w = c.wire;
if (wand_wor_index.count(w) == 0) {
new_sig.append(c);
continue;
}
Wire *t = module->addWire(NEW_ID, GetSize(c));
new_sig.append(t);
update_port = true;
if (wand_map.count(w)) {
SigSpec sig = SigSpec(State::S1, GetSize(w));
sig.replace(c.offset, t);
wand_map.at(w).append(sig);
} else {
SigSpec sig = SigSpec(State::S0, GetSize(w));
sig.replace(c.offset, t);
wor_map.at(w).append(sig);
}
}
if (!conn.second.is_fully_const() || !w->port_input || w->port_output)
log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell),
log_id(conn.first), GetSize(conn.second), GetSize(sig));
cell->setPort(conn.first, sig);
if (update_port)
cell->setPort(conn.first, new_sig);
}
}
for (auto w : wand_wor_index)
{
bool wand = wand_map.count(w);
SigSpec sigs = wand ? wand_map.at(w) : wor_map.at(w);
if (GetSize(sigs) == 0)
continue;
if (GetSize(w) == 1) {
if (wand)
module->addReduceAnd(NEW_ID, sigs, w);
else
module->addReduceOr(NEW_ID, sigs, w);
continue;
}
if (w->port_output && !w->port_input && sig.has_const())
log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n",
log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig));
SigSpec s = sigs.extract(0, GetSize(w));
for (int i = GetSize(w); i < GetSize(sigs); i += GetSize(w)) {
if (wand)
s = module->And(NEW_ID, s, sigs.extract(i, GetSize(w)));
else
s = module->Or(NEW_ID, s, sigs.extract(i, GetSize(w)));
}
module->connect(w, s);
}
for (auto cell : module->cells())
{
Module *m = design->module(cell->type);
if (m == nullptr)
continue;
if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
IdString new_m_name = m->derive(design, cell->parameters, true);
if (new_m_name.empty())
continue;
if (new_m_name != m->name) {
m = design->module(new_m_name);
blackbox_derivatives.insert(m);
}
}
for (auto &conn : cell->connections())
{
Wire *w = m->wire(conn.first);
if (w == nullptr || w->port_id == 0)
continue;
if (GetSize(conn.second) == 0)
continue;
SigSpec sig = conn.second;
if (!keep_portwidths && GetSize(w) != GetSize(conn.second))
{
if (GetSize(w) < GetSize(conn.second))
{
int n = GetSize(conn.second) - GetSize(w);
if (!w->port_input && w->port_output)
module->connect(sig.extract(GetSize(w), n), Const(0, n));
sig.remove(GetSize(w), n);
}
else
{
int n = GetSize(w) - GetSize(conn.second);
if (w->port_input && !w->port_output)
sig.append(Const(0, n));
else
sig.append(module->addWire(NEW_ID, n));
}
if (!conn.second.is_fully_const() || !w->port_input || w->port_output)
log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell),
log_id(conn.first), GetSize(conn.second), GetSize(sig));
cell->setPort(conn.first, sig);
}
if (w->port_output && !w->port_input && sig.has_const())
log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n",
log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig));
}
}
}

View File

@ -319,8 +319,9 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
wire->attributes.erase("\\init");
if (GetSize(wire) == 0) {
// delete zero-width wires
goto delete_this_wire;
// delete zero-width wires, unless they are module ports
if (wire->port_id == 0)
goto delete_this_wire;
} else
if (wire->port_id != 0 || wire->get_bool_attribute("\\keep") || !initval.is_fully_undef()) {
// do not delete anything with "keep" or module ports or initialized wires

View File

@ -338,16 +338,6 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx);
}
if (sig_e.size()) {
if (!sig_e.is_fully_const())
return false;
if (sig_e != val_ep) {
if (has_init)
mod->connect(sig_q, val_init);
goto delete_dff;
}
}
if (dff->type.in("$ff", "$dff") && mux_drivers.has(sig_d)) {
std::set<RTLIL::Cell*> muxes;
mux_drivers.find(sig_d, muxes);
@ -365,39 +355,60 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
}
}
// If clock is driven by a constant and (i) no reset signal
// (ii) Q has no initial value
// (iii) initial value is same as reset value
if (!sig_c.empty() && sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) {
if (val_rv.bits.size() == 0)
val_rv = val_init;
// Q is permanently reset value or initial value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
// If D is fully undefined and reset signal present and (i) Q has no initial value
// (ii) initial value is same as reset value
if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) {
// Q is permanently reset value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
// If D is fully undefined and no reset signal and Q has an initial value
if (sig_d.is_fully_undef() && !sig_r.size() && has_init) {
// Q is permanently initial value
mod->connect(sig_q, val_init);
goto delete_dff;
}
// If D is fully constant and (i) no reset signal
// (ii) reset value is same as constant D
// and (a) has no initial value
// (b) initial value same as constant D
if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) {
// Q is permanently D
mod->connect(sig_q, sig_d);
goto delete_dff;
}
// If D input is same as Q output and (i) no reset signal
// (ii) no initial signal
// (iii) initial value is same as reset value
if (sig_d == sig_q && (sig_r.empty() || !has_init || val_init == val_rv)) {
// Q is permanently reset value or initial value
if (sig_r.size())
mod->connect(sig_q, val_rv);
if (has_init)
else if (has_init)
mod->connect(sig_q, val_init);
goto delete_dff;
}
// If reset signal is present, and is fully constant
if (!sig_r.empty() && sig_r.is_fully_const())
{
// If reset value is permanently active or if reset is undefined
if (sig_r == val_rp || sig_r.is_fully_undef()) {
// Q is permanently reset value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
@ -417,6 +428,30 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
dff->unsetPort("\\R");
}
// If enable signal is present, and is fully constant
if (!sig_e.empty() && sig_e.is_fully_const())
{
// If enable value is permanently inactive
if (sig_e != val_ep) {
// Q is permanently initial value
mod->connect(sig_q, val_init);
goto delete_dff;
}
log("Removing unused enable from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
if (dff->type == "$dffe") {
dff->type = "$dff";
dff->unsetPort("\\EN");
dff->unsetParam("\\EN_POLARITY");
return true;
}
log_assert(dff->type.substr(0,7) == "$_DFFE_");
dff->type = stringf("$_DFF_%c_", + dff->type[7]);
dff->unsetPort("\\E");
}
return false;
delete_dff:

36
tests/simple/wandwor.v Normal file
View File

@ -0,0 +1,36 @@
module wandwor_test0 (A, B, C, D, X, Y, Z);
input A, B, C, D;
output wor X;
output wand Y;
output Z;
assign X = A, X = B, Y = C, Y = D;
foo foo_0 (C, D, X);
foo foo_1 (A, B, Y);
foo foo_2 (X, Y, Z);
endmodule
module wandwor_test1 (A, B, C, D, X, Y, Z);
input [3:0] A, B, C, D;
output wor [3:0] X;
output wand [3:0] Y;
output Z;
bar bar_inst (
.I0({A, B}),
.I1({B, A}),
.O({X, Y})
);
assign X = C, X = D;
assign Y = C, Y = D;
assign Z = ^{X,Y};
endmodule
module foo(input I0, I1, output O);
assign O = I0 ^ I1;
endmodule
module bar(input [7:0] I0, I1, output [7:0] O);
assign O = I0 + I1;
endmodule

View File

@ -1,30 +1,50 @@
module opt_rmdff_test (input C, input D, input E, output reg [16:0] Q);
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove0 (.CLK(C), .D(D), .EN(1'b0), .Q(Q[0]));
initial Q[1] = 1'b1;
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove1 (.CLK(C), .D(D), .EN(1'b0), .Q(Q[1]));
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove2 (.CLK(C), .D(D), .EN(1'bx), .Q(Q[2]));
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) keep2 (.CLK(C), .D(D), .EN(1'b1), .Q(Q[2]));
initial Q[3] = 1'b0;
\$dffe #(.WIDTH(1), .CLK_POLARITY(0), .EN_POLARITY(1)) keep3 (.CLK(C), .D(D), .EN(1'b1), .Q(Q[3]));
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove4 (.CLK(C), .D(D), .EN(1'b1), .Q(Q[4]));
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove5 (.CLK(C), .D(D), .EN(1'bx), .Q(Q[5]));
initial Q[6] = 1'b0;
\$dffe #(.WIDTH(1), .CLK_POLARITY(0), .EN_POLARITY(0)) keep6 (.CLK(C), .D(D), .EN(E), .Q(Q[6]));
module opt_rmdff_test (input C, input D, input E, output [29:0] Q);
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove0 (.CLK(C), .D(D), .EN(1'b0), .Q(Q[0])); // EN is never active
(* init = "1'b1" *) wire Q1; assign Q[1] = Q1;
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove1 (.CLK(C), .D(D), .EN(1'b0), .Q(Q1)); // EN is never active
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove2 (.CLK(C), .D(D), .EN(1'bx), .Q(Q[2])); // EN is don't care
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) keep3 (.CLK(C), .D(D), .EN(1'b1), .Q(Q[3])); // EN is always active
(* init = "1'b0" *) wire Q4; assign Q[4] = Q4;
\$dffe #(.WIDTH(1), .CLK_POLARITY(0), .EN_POLARITY(1)) keep4 (.CLK(C), .D(D), .EN(1'b1), .Q(Q4)); // EN is always active
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove5 (.CLK(C), .D(D), .EN(1'b1), .Q(Q[5])); // EN is never active
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove6 (.CLK(C), .D(D), .EN(1'bx), .Q(Q[6])); // EN is don't care
(* init = "1'b0" *) wire Q7; assign Q[7] = Q7;
\$dffe #(.WIDTH(1), .CLK_POLARITY(0), .EN_POLARITY(0)) keep7 (.CLK(C), .D(D), .EN(E), .Q(Q7)); // EN is non constant
\$_DFFE_PP_ remove7 (.C(C), .D(D), .E(1'b0), .Q(Q[7]));
initial Q[8] = 1'b1;
\$_DFFE_PP_ remove8 (.C(C), .D(D), .E(1'b0), .Q(Q[8]));
\$_DFFE_PP_ remove9 (.C(C), .D(D), .E(1'bx), .Q(Q[9]));
\$_DFFE_PP_ keep10 (.C(C), .D(D), .E(1'b1), .Q(Q[10]));
initial Q[11] = 1'b0;
\$_DFFE_PP_ keep11 (.C(C), .D(D), .E(1'b1), .Q(Q[11]));
\$_DFFE_PP_ remove8 (.C(C), .D(D), .E(1'b0), .Q(Q[8])); // EN is never active
(* init = "1'b1" *) wire Q9; assign Q[9] = Q9;
\$_DFFE_PP_ remove9 (.C(C), .D(D), .E(1'b0), .Q(Q9)); // EN is never active
\$_DFFE_PP_ remove10 (.C(C), .D(D), .E(1'bx), .Q(Q[10])); // EN is don't care
\$_DFFE_PP_ keep11 (.C(C), .D(D), .E(1'b1), .Q(Q[11])); // EN is always active
(* init = "1'b0" *) wire Q12; assign Q[12] = Q12;
\$_DFFE_PP_ keep12 (.C(C), .D(D), .E(1'b1), .Q(Q12)); // EN is always active
\$_DFFE_NN_ remove12 (.C(C), .D(D), .E(1'b1), .Q(Q[12]));
initial Q[13] = 1'b1;
\$_DFFE_NN_ remove13 (.C(C), .D(D), .E(1'b1), .Q(Q[13]));
\$_DFFE_NN_ remove14 (.C(C), .D(D), .E(1'bx), .Q(Q[14]));
\$_DFFE_NN_ keep15 (.C(C), .D(D), .E(1'b0), .Q(Q[15]));
initial Q[16] = 1'b0;
\$_DFFE_NN_ keep16 (.C(C), .D(D), .E(1'b0), .Q(Q[16]));
\$_DFFE_NN_ remove13 (.C(C), .D(D), .E(1'b1), .Q(Q[13])); // EN is never active
(* init = "1'b1" *) wire Q14; assign Q[14] = Q14;
\$_DFFE_NN_ remove14 (.C(C), .D(D), .E(1'b1), .Q(Q14)); // EN is never active
\$_DFFE_NN_ remove15 (.C(C), .D(D), .E(1'bx), .Q(Q[15])); // EN is don't care
\$_DFFE_NN_ keep16 (.C(C), .D(D), .E(1'b0), .Q(Q[16])); // EN is always active
(* init = "1'b0" *) wire Q17; assign Q[17] = Q17;
\$_DFFE_NN_ keep17 (.C(C), .D(D), .E(1'b0), .Q(Q17)); // EN is always active
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove18 (.CLK(1'b0), .D(D), .EN(E), .Q(Q[18])); // CLK is constant
(* init = "1'b1" *) wire Q19; assign Q[19] = Q19;
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove19 (.CLK(1'b1), .D(D), .EN(E), .Q(Q19)); // CLK is constant
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove20 (.CLK(C), .D(1'bx), .EN(E), .Q(Q[20])); // D is undriven, Q has no initial value
(* init = "1'b0" *) wire Q21; assign Q[21] = Q21;
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) keep21 (.CLK(C), .D(1'bx), .EN(E), .Q(Q21)); // D is undriven, Q has initial value
//\$dffe #(.WIDTH(1), .CLK_POLARITY(0), .EN_POLARITY(1)) remove22 (.CLK(C), .D(1'b0), .EN(1'b1), .Q(Q[22])); // D is constant, no initial Q value, EN is always active
// // (TODO, Q starts with 1'bx and becomes 1'b0)
(* init = "1'b0" *) wire Q23; assign Q[23] = Q23;
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) noenable23 (.CLK(C), .D(1'b0), .EN(1'b1), .Q(Q23)); // D is constant, initial Q value same as D, EN is always active
(* init = "1'b1" *) wire Q24; assign Q[24] = Q24;
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) keep24 (.CLK(C), .D(1'b0), .EN(1'b0), .Q(Q24)); // D is constant, initial Q value NOT same as D, EN is always active
(* init = "1'b1" *) wire Q25; assign Q[25] = Q25;
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove25 (.CLK(C), .D(1'b0), .EN(1'b1), .Q(Q25)); // D is constant, EN is never active
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove26 (.CLK(C), .D(Q[26]), .EN(1'b1), .Q(Q[26])); // D is Q, EN is always active
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove27 (.CLK(C), .D(Q[27]), .EN(1'b1), .Q(Q[27])); // D is Q, EN is never active, but no initial value
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove28 (.CLK(C), .D(Q[28]), .EN(E), .Q(Q[28])); // EN is nonconst, but no initial value
(* init = "1'b1" *) wire Q29; assign Q[29] = Q29;
\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) keep29 (.CLK(C), .D(Q[29]), .EN(1'b1), .Q(Q29)); // EN is always active, but with initial value
endmodule

View File

@ -2,12 +2,13 @@ read_verilog -icells opt_rmdff.v
prep
design -stash gold
read_verilog -icells opt_rmdff.v
proc
opt_rmdff
select -assert-count 0 c:remove*
select -assert-min 7 c:keep*
select -assert-count 0 t:$dffe 7:$_DFFE_* %u c:noenable* %i
prep
design -stash gate
design -import gold -as gold