3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-15 13:28:59 +00:00

Merge branch 'master' into krys/docs

Fix failing verific tests
This commit is contained in:
Krystine Sherwin 2023-11-01 13:17:31 +13:00
commit 8fad77bd0f
No known key found for this signature in database
57 changed files with 2186 additions and 1394 deletions

1
.gitignore vendored
View file

@ -4,6 +4,7 @@
*.gch *.gch
*.gcda *.gcda
*.gcno *.gcno
*~
__pycache__ __pycache__
/.cproject /.cproject
/.project /.project

View file

@ -2,9 +2,33 @@
List of major changes and improvements between releases List of major changes and improvements between releases
======================================================= =======================================================
Yosys 0.33 .. Yosys 0.34-dev Yosys 0.34 .. Yosys 0.35-dev
-------------------------- --------------------------
Yosys 0.33 .. Yosys 0.34
--------------------------
* New commands and options
- Added option "-assert" to "sim" pass.
- Added option "-noinitstate" to "sim" pass.
- Added option "-dont_use" to "abc" pass.
- Added "dft_tag" pass to create tagging logic for data flow tracking.
- Added "future" pass to resolve future sampled value functions.
- Added "booth" pass to map $mul cells to Booth multipliers.
- Added option "-booth" to "synth" pass.
* SystemVerilog
- Added support for assignments within expressions, e.g., `x[y++] = z;` or
`x = (y *= 2) - 1;`.
* Verific support
- "src" attribute contain full location info.
- module parameters are kept after import.
- accurate access order semantics in memory inference.
- better "bind" support for mixed language projects.
* Various
- "show" command displays dot instead of box for wire aliases.
Yosys 0.32 .. Yosys 0.33 Yosys 0.32 .. Yosys 0.33
-------------------------- --------------------------
* Various * Various

View file

@ -141,7 +141,7 @@ LDLIBS += -lrt
endif endif
endif endif
YOSYS_VER := 0.33+34 YOSYS_VER := 0.34+55
# Note: We arrange for .gitcommit to contain the (short) commit hash in # Note: We arrange for .gitcommit to contain the (short) commit hash in
# tarballs generated with git-archive(1) using .gitattributes. The git repo # tarballs generated with git-archive(1) using .gitattributes. The git repo
@ -157,7 +157,7 @@ endif
OBJS = kernel/version_$(GIT_REV).o OBJS = kernel/version_$(GIT_REV).o
bumpversion: bumpversion:
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 2584903.. | wc -l`/;" Makefile sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 4a1b559.. | wc -l`/;" Makefile
# set 'ABCREV = default' to use abc/ as it is # set 'ABCREV = default' to use abc/ as it is
# #
@ -165,7 +165,7 @@ bumpversion:
# is just a symlink to your actual ABC working directory, as 'make mrproper' # is just a symlink to your actual ABC working directory, as 'make mrproper'
# will remove the 'abc' directory and you do not want to accidentally # will remove the 'abc' directory and you do not want to accidentally
# delete your work on ABC.. # delete your work on ABC..
ABCREV = daad9ed ABCREV = 896e5e7
ABCPULL = 1 ABCPULL = 1
ABCURL ?= https://github.com/YosysHQ/abc ABCURL ?= https://github.com/YosysHQ/abc
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q)
@ -357,7 +357,7 @@ CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
LDLIBS := $(filter-out -lrt,$(LDLIBS)) LDLIBS := $(filter-out -lrt,$(LDLIBS))
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
ABCMKARGS += LIBS="-lpthread -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)" ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)"
EXE = .exe EXE = .exe
else ifeq ($(CONFIG),msys2-64) else ifeq ($(CONFIG),msys2-64)
@ -368,7 +368,7 @@ CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
LDLIBS := $(filter-out -lrt,$(LDLIBS)) LDLIBS := $(filter-out -lrt,$(LDLIBS))
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
ABCMKARGS += LIBS="-lpthread -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)" ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)"
EXE = .exe EXE = .exe
else ifneq ($(CONFIG),none) else ifneq ($(CONFIG),none)

View file

@ -592,6 +592,8 @@ from SystemVerilog:
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
ports are inputs or outputs are supported. ports are inputs or outputs are supported.
- Assignments within expressions are supported.
Building the documentation Building the documentation
========================== ==========================

View file

@ -1019,14 +1019,14 @@ struct metadata {
// In debug mode, using the wrong .as_*() function will assert. // In debug mode, using the wrong .as_*() function will assert.
// In release mode, using the wrong .as_*() function will safely return a default value. // In release mode, using the wrong .as_*() function will safely return a default value.
const unsigned uint_value = 0; const uint64_t uint_value = 0;
const signed sint_value = 0; const int64_t sint_value = 0;
const std::string string_value = ""; const std::string string_value = "";
const double double_value = 0.0; const double double_value = 0.0;
metadata() : value_type(MISSING) {} metadata() : value_type(MISSING) {}
metadata(unsigned value) : value_type(UINT), uint_value(value) {} metadata(uint64_t value) : value_type(UINT), uint_value(value) {}
metadata(signed value) : value_type(SINT), sint_value(value) {} metadata(int64_t value) : value_type(SINT), sint_value(value) {}
metadata(const std::string &value) : value_type(STRING), string_value(value) {} metadata(const std::string &value) : value_type(STRING), string_value(value) {}
metadata(const char *value) : value_type(STRING), string_value(value) {} metadata(const char *value) : value_type(STRING), string_value(value) {}
metadata(double value) : value_type(DOUBLE), double_value(value) {} metadata(double value) : value_type(DOUBLE), double_value(value) {}
@ -1034,12 +1034,12 @@ struct metadata {
metadata(const metadata &) = default; metadata(const metadata &) = default;
metadata &operator=(const metadata &) = delete; metadata &operator=(const metadata &) = delete;
unsigned as_uint() const { uint64_t as_uint() const {
assert(value_type == UINT); assert(value_type == UINT);
return uint_value; return uint_value;
} }
signed as_sint() const { int64_t as_sint() const {
assert(value_type == SINT); assert(value_type == SINT);
return sint_value; return sint_value;
} }
@ -1068,6 +1068,9 @@ using debug_outline = ::_cxxrtl_outline;
// //
// To avoid violating strict aliasing rules, this structure has to be a subclass of the one used // To avoid violating strict aliasing rules, this structure has to be a subclass of the one used
// in the C API, or it would not be possible to cast between the pointers to these. // in the C API, or it would not be possible to cast between the pointers to these.
//
// The `attrs` member cannot be owned by this structure because a `cxxrtl_object` can be created
// from external C code.
struct debug_item : ::cxxrtl_object { struct debug_item : ::cxxrtl_object {
// Object types. // Object types.
enum : uint32_t { enum : uint32_t {
@ -1103,6 +1106,7 @@ struct debug_item : ::cxxrtl_object {
curr = item.data; curr = item.data;
next = item.data; next = item.data;
outline = nullptr; outline = nullptr;
attrs = nullptr;
} }
template<size_t Bits> template<size_t Bits>
@ -1118,6 +1122,7 @@ struct debug_item : ::cxxrtl_object {
curr = const_cast<chunk_t*>(item.data); curr = const_cast<chunk_t*>(item.data);
next = nullptr; next = nullptr;
outline = nullptr; outline = nullptr;
attrs = nullptr;
} }
template<size_t Bits> template<size_t Bits>
@ -1134,6 +1139,7 @@ struct debug_item : ::cxxrtl_object {
curr = item.curr.data; curr = item.curr.data;
next = item.next.data; next = item.next.data;
outline = nullptr; outline = nullptr;
attrs = nullptr;
} }
template<size_t Width> template<size_t Width>
@ -1149,6 +1155,7 @@ struct debug_item : ::cxxrtl_object {
curr = item.data ? item.data[0].data : nullptr; curr = item.data ? item.data[0].data : nullptr;
next = nullptr; next = nullptr;
outline = nullptr; outline = nullptr;
attrs = nullptr;
} }
template<size_t Bits> template<size_t Bits>
@ -1164,6 +1171,7 @@ struct debug_item : ::cxxrtl_object {
curr = const_cast<chunk_t*>(item.data); curr = const_cast<chunk_t*>(item.data);
next = nullptr; next = nullptr;
outline = nullptr; outline = nullptr;
attrs = nullptr;
} }
template<size_t Bits> template<size_t Bits>
@ -1180,6 +1188,7 @@ struct debug_item : ::cxxrtl_object {
curr = const_cast<chunk_t*>(item.curr.data); curr = const_cast<chunk_t*>(item.curr.data);
next = nullptr; next = nullptr;
outline = nullptr; outline = nullptr;
attrs = nullptr;
} }
template<size_t Bits> template<size_t Bits>
@ -1195,6 +1204,7 @@ struct debug_item : ::cxxrtl_object {
curr = const_cast<chunk_t*>(item.data); curr = const_cast<chunk_t*>(item.data);
next = nullptr; next = nullptr;
outline = &group; outline = &group;
attrs = nullptr;
} }
template<size_t Bits, class IntegerT> template<size_t Bits, class IntegerT>
@ -1215,10 +1225,28 @@ struct debug_item : ::cxxrtl_object {
}; };
static_assert(std::is_standard_layout<debug_item>::value, "debug_item is not compatible with C layout"); static_assert(std::is_standard_layout<debug_item>::value, "debug_item is not compatible with C layout");
} // namespace cxxrtl
typedef struct _cxxrtl_attr_set {
cxxrtl::metadata_map map;
} *cxxrtl_attr_set;
namespace cxxrtl {
// Representation of an attribute set in the C++ interface.
using debug_attrs = ::_cxxrtl_attr_set;
struct debug_items { struct debug_items {
std::map<std::string, std::vector<debug_item>> table; std::map<std::string, std::vector<debug_item>> table;
std::map<std::string, std::unique_ptr<debug_attrs>> attrs_table;
void add(const std::string &name, debug_item &&item) { void add(const std::string &name, debug_item &&item, metadata_map &&item_attrs = {}) {
std::unique_ptr<debug_attrs> &attrs = attrs_table[name];
if (attrs.get() == nullptr)
attrs = std::unique_ptr<debug_attrs>(new debug_attrs);
for (auto attr : item_attrs)
attrs->map.insert(attr);
item.attrs = attrs.get();
std::vector<debug_item> &parts = table[name]; std::vector<debug_item> &parts = table[name];
parts.emplace_back(item); parts.emplace_back(item);
std::sort(parts.begin(), parts.end(), std::sort(parts.begin(), parts.end(),
@ -1246,6 +1274,10 @@ struct debug_items {
const debug_item &operator [](const std::string &name) const { const debug_item &operator [](const std::string &name) const {
return at(name); return at(name);
} }
const metadata_map &attrs(const std::string &name) const {
return attrs_table.at(name)->map;
}
}; };
// Tag class to disambiguate the default constructor used by the toplevel module that calls reset(), // Tag class to disambiguate the default constructor used by the toplevel module that calls reset(),

View file

@ -2120,6 +2120,46 @@ struct CxxrtlWorker {
dec_indent(); dec_indent();
} }
void dump_metadata_map(const dict<RTLIL::IdString, RTLIL::Const> &metadata_map)
{
if (metadata_map.empty()) {
f << "metadata_map()";
return;
}
f << "metadata_map({\n";
inc_indent();
for (auto metadata_item : metadata_map) {
if (!metadata_item.first.isPublic())
continue;
if (metadata_item.second.size() > 64 && (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) == 0) {
f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */";
continue;
}
f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", ";
// In Yosys, a real is a type of string.
if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) {
f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint;
} else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) {
f << escape_cxx_string(metadata_item.second.decode_string());
} else if (metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED) {
f << "INT64_C(" << metadata_item.second.as_int(/*is_signed=*/true) << ")";
} else {
f << "UINT64_C(" << metadata_item.second.as_int(/*is_signed=*/false) << ")";
}
f << " },\n";
}
dec_indent();
f << indent << "})";
}
void dump_debug_attrs(const RTLIL::AttrObject *object)
{
dict<RTLIL::IdString, RTLIL::Const> attributes = object->attributes;
// Inherently necessary to get access to the object, so a waste of space to emit.
attributes.erase(ID::hdlname);
dump_metadata_map(attributes);
}
void dump_debug_info_method(RTLIL::Module *module) void dump_debug_info_method(RTLIL::Module *module)
{ {
size_t count_public_wires = 0; size_t count_public_wires = 0;
@ -2205,7 +2245,9 @@ struct CxxrtlWorker {
} }
f << "debug_item::" << flag; f << "debug_item::" << flag;
} }
f << "));\n"; f << "), ";
dump_debug_attrs(wire);
f << ");\n";
count_member_wires++; count_member_wires++;
break; break;
} }
@ -2220,7 +2262,9 @@ struct CxxrtlWorker {
f << "debug_eval_outline"; f << "debug_eval_outline";
else else
f << "debug_alias()"; f << "debug_alias()";
f << ", " << mangle(aliasee) << ", " << wire->start_offset << "));\n"; f << ", " << mangle(aliasee) << ", " << wire->start_offset << "), ";
dump_debug_attrs(aliasee);
f << ");\n";
count_alias_wires++; count_alias_wires++;
break; break;
} }
@ -2230,14 +2274,18 @@ struct CxxrtlWorker {
dump_const(debug_wire_type.sig_subst.as_const()); dump_const(debug_wire_type.sig_subst.as_const());
f << ";\n"; f << ";\n";
f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire)); f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "));\n"; f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "), ";
dump_debug_attrs(wire);
f << ");\n";
count_const_wires++; count_const_wires++;
break; break;
} }
case WireType::OUTLINE: { case WireType::OUTLINE: {
// Localized or inlined, but rematerializable wire // Localized or inlined, but rematerializable wire
f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire)); f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "));\n"; f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "), ";
dump_debug_attrs(wire);
f << ");\n";
count_inline_wires++; count_inline_wires++;
break; break;
} }
@ -2254,7 +2302,13 @@ struct CxxrtlWorker {
continue; continue;
f << indent << "items.add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)); f << indent << "items.add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem));
f << ", debug_item(" << mangle(&mem) << ", "; f << ", debug_item(" << mangle(&mem) << ", ";
f << mem.start_offset << "));\n"; f << mem.start_offset << "), ";
if (mem.packed) {
dump_debug_attrs(mem.cell);
} else {
dump_debug_attrs(mem.mem);
}
f << ");\n";
} }
for (auto cell : module->cells()) { for (auto cell : module->cells()) {
if (is_internal_cell(cell->type)) if (is_internal_cell(cell->type))
@ -2282,33 +2336,6 @@ struct CxxrtlWorker {
} }
} }
void dump_metadata_map(const dict<RTLIL::IdString, RTLIL::Const> &metadata_map)
{
if (metadata_map.empty()) {
f << "metadata_map()";
return;
}
f << "metadata_map({\n";
inc_indent();
for (auto metadata_item : metadata_map) {
if (!metadata_item.first.begins_with("\\"))
continue;
f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", ";
if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) {
f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint;
} else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) {
f << escape_cxx_string(metadata_item.second.decode_string());
} else {
f << metadata_item.second.as_int(/*is_signed=*/metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED);
if (!(metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED))
f << "u";
}
f << " },\n";
}
dec_indent();
f << indent << "})";
}
void dump_module_intf(RTLIL::Module *module) void dump_module_intf(RTLIL::Module *module)
{ {
dump_attrs(module); dump_attrs(module);

View file

@ -90,3 +90,46 @@ void cxxrtl_enum(cxxrtl_handle handle, void *data,
void cxxrtl_outline_eval(cxxrtl_outline outline) { void cxxrtl_outline_eval(cxxrtl_outline outline) {
outline->eval(); outline->eval();
} }
int cxxrtl_attr_type(cxxrtl_attr_set attrs_, const char *name) {
auto attrs = (cxxrtl::metadata_map*)attrs_;
if (!attrs->count(name))
return CXXRTL_ATTR_NONE;
switch (attrs->at(name).value_type) {
case cxxrtl::metadata::UINT:
return CXXRTL_ATTR_UNSIGNED_INT;
case cxxrtl::metadata::SINT:
return CXXRTL_ATTR_SIGNED_INT;
case cxxrtl::metadata::STRING:
return CXXRTL_ATTR_STRING;
case cxxrtl::metadata::DOUBLE:
return CXXRTL_ATTR_DOUBLE;
default:
// Present unsupported attribute type the same way as no attribute at all.
return CXXRTL_ATTR_NONE;
}
}
uint64_t cxxrtl_attr_get_unsigned_int(cxxrtl_attr_set attrs_, const char *name) {
auto &attrs = *(cxxrtl::metadata_map*)attrs_;
assert(attrs.count(name) && attrs.at(name).value_type == cxxrtl::metadata::UINT);
return attrs[name].as_uint();
}
int64_t cxxrtl_attr_get_signed_int(cxxrtl_attr_set attrs_, const char *name) {
auto &attrs = *(cxxrtl::metadata_map*)attrs_;
assert(attrs.count(name) && attrs.at(name).value_type == cxxrtl::metadata::SINT);
return attrs[name].as_sint();
}
const char *cxxrtl_attr_get_string(cxxrtl_attr_set attrs_, const char *name) {
auto &attrs = *(cxxrtl::metadata_map*)attrs_;
assert(attrs.count(name) && attrs.at(name).value_type == cxxrtl::metadata::STRING);
return attrs[name].as_string().c_str();
}
double cxxrtl_attr_get_double(cxxrtl_attr_set attrs_, const char *name) {
auto &attrs = *(cxxrtl::metadata_map*)attrs_;
assert(attrs.count(name) && attrs.at(name).value_type == cxxrtl::metadata::DOUBLE);
return attrs[name].as_double();
}

View file

@ -249,6 +249,15 @@ struct cxxrtl_object {
// this field to NULL. // this field to NULL.
struct _cxxrtl_outline *outline; struct _cxxrtl_outline *outline;
// Opaque reference to an attribute set.
//
// See the documentation of `cxxrtl_attr_set` for details. When creating a `cxxrtl_object`, set
// this field to NULL.
//
// The lifetime of the pointers returned by `cxxrtl_attr_*` family of functions is the same as
// the lifetime of this structure.
struct _cxxrtl_attr_set *attrs;
// More description fields may be added in the future, but the existing ones will never change. // More description fields may be added in the future, but the existing ones will never change.
}; };
@ -304,6 +313,62 @@ typedef struct _cxxrtl_outline *cxxrtl_outline;
// re-evaluated, otherwise the bits read from that object are meaningless. // re-evaluated, otherwise the bits read from that object are meaningless.
void cxxrtl_outline_eval(cxxrtl_outline outline); void cxxrtl_outline_eval(cxxrtl_outline outline);
// Opaque reference to an attribute set.
//
// An attribute set is a map between attribute names (always strings) and values (which may have
// several different types). To find out the type of an attribute, use `cxxrtl_attr_type`, and
// to retrieve the value of an attribute, use `cxxrtl_attr_as_string`.
typedef struct _cxxrtl_attr_set *cxxrtl_attr_set;
// Type of an attribute.
enum cxxrtl_attr_type {
// Attribute is not present.
CXXRTL_ATTR_NONE = 0,
// Attribute has an unsigned integer value.
CXXRTL_ATTR_UNSIGNED_INT = 1,
// Attribute has an unsigned integer value.
CXXRTL_ATTR_SIGNED_INT = 2,
// Attribute has a string value.
CXXRTL_ATTR_STRING = 3,
// Attribute has a double precision floating point value.
CXXRTL_ATTR_DOUBLE = 4,
// More attribute types may be defined in the future, but the existing values will never change.
};
// Determine the presence and type of an attribute in an attribute set.
//
// This function returns one of the possible `cxxrtl_attr_type` values.
int cxxrtl_attr_type(cxxrtl_attr_set attrs, const char *name);
// Retrieve an unsigned integer valued attribute from an attribute set.
//
// This function asserts that `cxxrtl_attr_type(attrs, name) == CXXRTL_ATTR_UNSIGNED_INT`.
// If assertions are disabled, returns 0 if the attribute is missing or has an incorrect type.
uint64_t cxxrtl_attr_get_unsigned_int(cxxrtl_attr_set attrs, const char *name);
// Retrieve a signed integer valued attribute from an attribute set.
//
// This function asserts that `cxxrtl_attr_type(attrs, name) == CXXRTL_ATTR_SIGNED_INT`.
// If assertions are disabled, returns 0 if the attribute is missing or has an incorrect type.
int64_t cxxrtl_attr_get_signed_int(cxxrtl_attr_set attrs, const char *name);
// Retrieve a string valued attribute from an attribute set. The returned string is zero-terminated.
//
// This function asserts that `cxxrtl_attr_type(attrs, name) == CXXRTL_ATTR_STRING`. If assertions
// are disabled, returns NULL if the attribute is missing or has an incorrect type.
const char *cxxrtl_attr_get_string(cxxrtl_attr_set attrs, const char *name);
// Retrieve a double precision floating point valued attribute from an attribute set.
//
// This function asserts that `cxxrtl_attr_type(attrs, name) == CXXRTL_ATTR_DOUBLE`. If assertions
// are disabled, returns NULL if the attribute is missing or has an incorrect type.
double cxxrtl_attr_get_double(cxxrtl_attr_set attrs, const char *name);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -2008,6 +2008,11 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw
dump_case_body(f, indent + " ", *it); dump_case_body(f, indent + " ", *it);
} }
if (sw->cases.empty()) {
// Verilog does not allow empty cases.
f << stringf("%s default: ;\n", indent.c_str());
}
f << stringf("%s" "endcase\n", indent.c_str()); f << stringf("%s" "endcase\n", indent.c_str());
} }

View file

@ -229,6 +229,10 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
id2ast = NULL; id2ast = NULL;
basic_prep = false; basic_prep = false;
lookahead = false; lookahead = false;
in_lvalue_from_above = false;
in_param_from_above = false;
in_lvalue = false;
in_param = false;
if (child1) if (child1)
children.push_back(child1); children.push_back(child1);
@ -238,6 +242,8 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
children.push_back(child3); children.push_back(child3);
if (child4) if (child4)
children.push_back(child4); children.push_back(child4);
fixup_hierarchy_flags();
} }
// create a (deep recursive) copy of a node // create a (deep recursive) copy of a node
@ -249,6 +255,10 @@ AstNode *AstNode::clone() const
it = it->clone(); it = it->clone();
for (auto &it : that->attributes) for (auto &it : that->attributes)
it.second = it.second->clone(); it.second = it.second->clone();
that->set_in_lvalue_flag(false);
that->set_in_param_flag(false);
that->fixup_hierarchy_flags(); // fixup to set flags on cloned children
return that; return that;
} }
@ -256,10 +266,13 @@ AstNode *AstNode::clone() const
void AstNode::cloneInto(AstNode *other) const void AstNode::cloneInto(AstNode *other) const
{ {
AstNode *tmp = clone(); AstNode *tmp = clone();
tmp->in_lvalue_from_above = other->in_lvalue_from_above;
tmp->in_param_from_above = other->in_param_from_above;
other->delete_children(); other->delete_children();
*other = *tmp; *other = *tmp;
tmp->children.clear(); tmp->children.clear();
tmp->attributes.clear(); tmp->attributes.clear();
other->fixup_hierarchy_flags();
delete tmp; delete tmp;
} }
@ -351,6 +364,10 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
if (is_enum) { if (is_enum) {
fprintf(f, " type=enum"); fprintf(f, " type=enum");
} }
if (in_lvalue)
fprintf(f, " in_lvalue");
if (in_param)
fprintf(f, " in_param");
fprintf(f, "\n"); fprintf(f, "\n");
for (auto &it : attributes) { for (auto &it : attributes) {
@ -1061,7 +1078,7 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d
// simplify this module or interface using the current design as context // simplify this module or interface using the current design as context
// for lookup up ports and wires within cells // for lookup up ports and wires within cells
set_simplify_design_context(design); set_simplify_design_context(design);
while (ast->simplify(!flag_noopt, false, 0, -1, false, false)) { } while (ast->simplify(!flag_noopt, 0, -1, false)) { }
set_simplify_design_context(nullptr); set_simplify_design_context(nullptr);
if (flag_dump_ast2) { if (flag_dump_ast2) {
@ -1091,7 +1108,7 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d
ast->attributes.erase(ID::whitebox); ast->attributes.erase(ID::whitebox);
} }
AstNode *n = ast->attributes.at(ID::lib_whitebox); AstNode *n = ast->attributes.at(ID::lib_whitebox);
ast->attributes[ID::whitebox] = n; ast->set_attribute(ID::whitebox, n);
ast->attributes.erase(ID::lib_whitebox); ast->attributes.erase(ID::lib_whitebox);
} }
} }
@ -1150,7 +1167,7 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d
ast->children.swap(new_children); ast->children.swap(new_children);
if (ast->attributes.count(ID::blackbox) == 0) { if (ast->attributes.count(ID::blackbox) == 0) {
ast->attributes[ID::blackbox] = AstNode::mkconst_int(1, false); ast->set_attribute(ID::blackbox, AstNode::mkconst_int(1, false));
} }
} }
@ -1298,6 +1315,8 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
flag_pwires = pwires; flag_pwires = pwires;
flag_autowire = autowire; flag_autowire = autowire;
ast->fixup_hierarchy_flags(true);
log_assert(current_ast->type == AST_DESIGN); log_assert(current_ast->type == AST_DESIGN);
for (AstNode *child : current_ast->children) for (AstNode *child : current_ast->children)
{ {
@ -1361,7 +1380,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
} }
else if (child->type == AST_PACKAGE) { else if (child->type == AST_PACKAGE) {
// process enum/other declarations // process enum/other declarations
child->simplify(true, false, 1, -1, false, false); child->simplify(true, 1, -1, false);
rename_in_package_stmts(child); rename_in_package_stmts(child);
design->verilog_packages.push_back(child->clone()); design->verilog_packages.push_back(child->clone());
current_scope.clear(); current_scope.clear();
@ -1748,7 +1767,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
AstNode *new_ast = ast->clone(); AstNode *new_ast = ast->clone();
if (!new_ast->attributes.count(ID::hdlname)) if (!new_ast->attributes.count(ID::hdlname))
new_ast->attributes[ID::hdlname] = AstNode::mkconst_str(stripped_name); new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(stripped_name));
para_counter = 0; para_counter = 0;
for (auto child : new_ast->children) { for (auto child : new_ast->children) {
@ -1795,6 +1814,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
new_ast->children.push_back(defparam); new_ast->children.push_back(defparam);
} }
new_ast->fixup_hierarchy_flags(true);
(*new_ast_out) = new_ast; (*new_ast_out) = new_ast;
return modname; return modname;
} }

View file

@ -221,6 +221,13 @@ namespace AST
std::string filename; std::string filename;
AstSrcLocType location; AstSrcLocType location;
// are we embedded in an lvalue, param?
// (see fixup_hierarchy_flags)
bool in_lvalue;
bool in_param;
bool in_lvalue_from_above;
bool in_param_from_above;
// creating and deleting nodes // creating and deleting nodes
AstNode(AstNodeType type = AST_NONE, AstNode *child1 = nullptr, AstNode *child2 = nullptr, AstNode *child3 = nullptr, AstNode *child4 = nullptr); AstNode(AstNodeType type = AST_NONE, AstNode *child1 = nullptr, AstNode *child2 = nullptr, AstNode *child3 = nullptr, AstNode *child4 = nullptr);
AstNode *clone() const; AstNode *clone() const;
@ -251,7 +258,7 @@ namespace AST
// simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc. // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc.
// it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL() // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL()
bool simplify(bool const_fold, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param); bool simplify(bool const_fold, int stage, int width_hint, bool sign_hint);
void replace_result_wire_name_in_function(const std::string &from, const std::string &to); void replace_result_wire_name_in_function(const std::string &from, const std::string &to);
AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init); AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init);
void expand_genblock(const std::string &prefix); void expand_genblock(const std::string &prefix);
@ -343,6 +350,24 @@ namespace AST
// to evaluate widths of dynamic ranges) // to evaluate widths of dynamic ranges)
AstNode *clone_at_zero(); AstNode *clone_at_zero();
void set_attribute(RTLIL::IdString key, AstNode *node)
{
attributes[key] = node;
node->set_in_param_flag(true);
}
// helper to set in_lvalue/in_param flags from the hierarchy context (the actual flag
// can be overridden based on the intrinsic properties of this node, i.e. based on its type)
void set_in_lvalue_flag(bool flag, bool no_descend = false);
void set_in_param_flag(bool flag, bool no_descend = false);
// fix up the hierarchy flags (in_lvalue/in_param) of this node and its children
//
// to keep the flags in sync, fixup_hierarchy_flags(true) needs to be called once after
// parsing the AST to walk the full tree, then plain fixup_hierarchy_flags() performs
// localized fixups after modifying children/attributes of a particular node
void fixup_hierarchy_flags(bool force_descend = false);
// helper to print errors from simplify/genrtlil code // helper to print errors from simplify/genrtlil code
[[noreturn]] void input_error(const char *format, ...) const YS_ATTRIBUTE(format(printf, 2, 3)); [[noreturn]] void input_error(const char *format, ...) const YS_ATTRIBUTE(format(printf, 2, 3));
}; };

View file

@ -176,10 +176,11 @@ struct AST_INTERNAL::LookaheadRewriter
AstNode *wire = new AstNode(AST_WIRE); AstNode *wire = new AstNode(AST_WIRE);
for (auto c : node->id2ast->children) for (auto c : node->id2ast->children)
wire->children.push_back(c->clone()); wire->children.push_back(c->clone());
wire->fixup_hierarchy_flags();
wire->str = stringf("$lookahead%s$%d", node->str.c_str(), autoidx++); wire->str = stringf("$lookahead%s$%d", node->str.c_str(), autoidx++);
wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false); wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
wire->is_logic = true; wire->is_logic = true;
while (wire->simplify(true, false, 1, -1, false, false)) { } while (wire->simplify(true, 1, -1, false)) { }
current_ast_mod->children.push_back(wire); current_ast_mod->children.push_back(wire);
lookaheadids[node->str] = make_pair(node->id2ast, wire); lookaheadids[node->str] = make_pair(node->id2ast, wire);
wire->genRTLIL(); wire->genRTLIL();
@ -926,7 +927,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1; this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1;
} else { } else {
if (id_ast->children[0]->type != AST_CONSTANT) if (id_ast->children[0]->type != AST_CONSTANT)
while (id_ast->simplify(true, false, 1, -1, false, true)) { } while (id_ast->simplify(true, 1, -1, false)) { }
if (id_ast->children[0]->type == AST_CONSTANT) if (id_ast->children[0]->type == AST_CONSTANT)
this_width = id_ast->children[0]->bits.size(); this_width = id_ast->children[0]->bits.size();
else else
@ -970,8 +971,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
else if (!range->range_valid) { else if (!range->range_valid) {
AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero(); AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero();
AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone(); AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone();
while (left_at_zero_ast->simplify(true, false, 1, -1, false, false)) { } while (left_at_zero_ast->simplify(true, 1, -1, false)) { }
while (right_at_zero_ast->simplify(true, false, 1, -1, false, false)) { } while (right_at_zero_ast->simplify(true, 1, -1, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
@ -987,7 +988,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
break; break;
case AST_TO_BITS: case AST_TO_BITS:
while (children[0]->simplify(true, false, 1, -1, false, false) == true) { } while (children[0]->simplify(true, 1, -1, false) == true) { }
if (children[0]->type != AST_CONSTANT) if (children[0]->type != AST_CONSTANT)
input_error("Left operand of tobits expression is not constant!\n"); input_error("Left operand of tobits expression is not constant!\n");
children[1]->detectSignWidthWorker(sub_width_hint, sign_hint); children[1]->detectSignWidthWorker(sub_width_hint, sign_hint);
@ -1009,7 +1010,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
break; break;
case AST_CAST_SIZE: case AST_CAST_SIZE:
while (children.at(0)->simplify(true, false, 1, -1, false, false)) { } while (children.at(0)->simplify(true, 1, -1, false)) { }
if (children.at(0)->type != AST_CONSTANT) if (children.at(0)->type != AST_CONSTANT)
input_error("Static cast with non constant expression!\n"); input_error("Static cast with non constant expression!\n");
children.at(1)->detectSignWidthWorker(width_hint, sign_hint); children.at(1)->detectSignWidthWorker(width_hint, sign_hint);
@ -1031,7 +1032,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
break; break;
case AST_REPLICATE: case AST_REPLICATE:
while (children[0]->simplify(true, false, 1, -1, false, true) == true) { } while (children[0]->simplify(true, 1, -1, false) == true) { }
if (children[0]->type != AST_CONSTANT) if (children[0]->type != AST_CONSTANT)
input_error("Left operand of replicate expression is not constant!\n"); input_error("Left operand of replicate expression is not constant!\n");
children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint); children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
@ -1143,7 +1144,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
case AST_PREFIX: case AST_PREFIX:
// Prefix nodes always resolve to identifiers in generate loops, so we // Prefix nodes always resolve to identifiers in generate loops, so we
// can simply perform the resolution to determine the sign and width. // can simply perform the resolution to determine the sign and width.
simplify(true, false, 1, -1, false, false); simplify(true, 1, -1, false);
log_assert(type == AST_IDENTIFIER); log_assert(type == AST_IDENTIFIER);
detectSignWidthWorker(width_hint, sign_hint, found_real); detectSignWidthWorker(width_hint, sign_hint, found_real);
break; break;
@ -1151,7 +1152,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
case AST_FCALL: case AST_FCALL:
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") {
if (GetSize(children) == 1) { if (GetSize(children) == 1) {
while (children[0]->simplify(true, false, 1, -1, false, true) == true) { } while (children[0]->simplify(true, 1, -1, false) == true) { }
if (children[0]->type != AST_CONSTANT) if (children[0]->type != AST_CONSTANT)
input_error("System function %s called with non-const argument!\n", input_error("System function %s called with non-const argument!\n",
RTLIL::unescape_id(str).c_str()); RTLIL::unescape_id(str).c_str());
@ -1198,8 +1199,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
log_assert(range->type == AST_RANGE && range->children.size() == 2); log_assert(range->type == AST_RANGE && range->children.size() == 2);
AstNode *left = range->children.at(0)->clone(); AstNode *left = range->children.at(0)->clone();
AstNode *right = range->children.at(1)->clone(); AstNode *right = range->children.at(1)->clone();
while (left->simplify(true, false, 1, -1, false, true)) { } left->set_in_param_flag(true);
while (right->simplify(true, false, 1, -1, false, true)) { } right->set_in_param_flag(true);
while (left->simplify(true, 1, -1, false)) { }
while (right->simplify(true, 1, -1, false)) { }
if (left->type != AST_CONSTANT || right->type != AST_CONSTANT) if (left->type != AST_CONSTANT || right->type != AST_CONSTANT)
input_error("Function %s has non-constant width!", input_error("Function %s has non-constant width!",
RTLIL::unescape_id(str).c_str()); RTLIL::unescape_id(str).c_str());
@ -1543,8 +1546,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (!children[0]->range_valid) { if (!children[0]->range_valid) {
AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero(); AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero();
AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone(); AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone();
while (left_at_zero_ast->simplify(true, false, 1, -1, false, false)) { } while (left_at_zero_ast->simplify(true, 1, -1, false)) { }
while (right_at_zero_ast->simplify(true, false, 1, -1, false, false)) { } while (right_at_zero_ast->simplify(true, 1, -1, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
@ -1552,7 +1555,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
children[0]->children[1]->clone() : children[0]->children[0]->clone()); children[0]->children[1]->clone() : children[0]->children[0]->clone());
fake_ast->children[0]->delete_children(); fake_ast->children[0]->delete_children();
if (member_node) if (member_node)
fake_ast->children[0]->attributes[ID::wiretype] = member_node->clone(); fake_ast->children[0]->set_attribute(ID::wiretype, member_node->clone());
int fake_ast_width = 0; int fake_ast_width = 0;
bool fake_ast_sign = true; bool fake_ast_sign = true;

File diff suppressed because it is too large Load diff

View file

@ -52,6 +52,7 @@ USING_YOSYS_NAMESPACE
#ifdef VERIFIC_VHDL_SUPPORT #ifdef VERIFIC_VHDL_SUPPORT
#include "vhdl_file.h" #include "vhdl_file.h"
#include "VhdlUnits.h" #include "VhdlUnits.h"
#include "NameSpace.h"
#endif #endif
#ifdef VERIFIC_EDIF_SUPPORT #ifdef VERIFIC_EDIF_SUPPORT
@ -265,7 +266,7 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
Att *attr; Att *attr;
if (obj->Linefile()) if (obj->Linefile())
attributes[ID::src] = stringf("%s:%d", LineFile::GetFileName(obj->Linefile()), LineFile::GetLineNo(obj->Linefile())); attributes[ID::src] = stringf("%s:%d.%d-%d.%d", LineFile::GetFileName(obj->Linefile()), obj->Linefile()->GetLeftLine(), obj->Linefile()->GetLeftCol(), obj->Linefile()->GetRightLine(), obj->Linefile()->GetRightCol());
FOREACH_ATTRIBUTE(obj, mi, attr) { FOREACH_ATTRIBUTE(obj, mi, attr) {
if (attr->Key()[0] == ' ' || attr->Value() == nullptr) if (attr->Key()[0] == ' ' || attr->Value() == nullptr)
@ -1275,9 +1276,24 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
log("Importing module %s.\n", RTLIL::id2cstr(module->name)); log("Importing module %s.\n", RTLIL::id2cstr(module->name));
} }
import_attributes(module->attributes, nl, nl); import_attributes(module->attributes, nl, nl);
module->set_string_attribute(ID::hdlname, nl->CellBaseName());
#ifdef VERIFIC_VHDL_SUPPORT
if (nl->IsFromVhdl()) {
NameSpace name_space(0);
char *architecture_name = name_space.ReName(nl->Name()) ;
module->set_string_attribute(ID(architecture), (architecture_name) ? architecture_name : nl->Name());
}
#endif
const char *param_name ;
const char *param_value ;
MapIter mi;
FOREACH_PARAMETER_OF_NETLIST(nl, mi, param_name, param_value) {
module->avail_parameters(RTLIL::escape_id(param_name));
module->parameter_default_values[RTLIL::escape_id(param_name)] = verific_const(param_value);
}
SetIter si; SetIter si;
MapIter mi, mi2; MapIter mi2;
Port *port; Port *port;
PortBus *portbus; PortBus *portbus;
Net *net; Net *net;
@ -2488,15 +2504,20 @@ std::string verific_import(Design *design, const std::map<std::string,std::strin
for (const auto &i : parameters) for (const auto &i : parameters)
verific_params.Insert(i.first.c_str(), i.second.c_str()); verific_params.Insert(i.first.c_str(), i.second.c_str());
if (top.empty()) {
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS #ifdef YOSYSHQ_VERIFIC_EXTENSIONS
VerificExtensions::ElaborateAndRewrite("work", &verific_params); VerificExtensions::ElaborateAndRewrite("work", &verific_params);
verific_error_msg.clear(); verific_error_msg.clear();
#endif #endif
if (top.empty()) {
netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params); netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params);
} }
else { else {
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
for (int static_elaborate = 1; static_elaborate >= 0; static_elaborate--)
#endif
{
Array veri_modules, vhdl_units; Array veri_modules, vhdl_units;
if (veri_lib) { if (veri_lib) {
@ -2517,6 +2538,10 @@ std::string verific_import(Design *design, const std::map<std::string,std::strin
} }
} }
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
if (!static_elaborate)
#endif
{
// Also elaborate all root modules since they may contain bind statements // Also elaborate all root modules since they may contain bind statements
MapIter mi; MapIter mi;
FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) {
@ -2524,6 +2549,7 @@ std::string verific_import(Design *design, const std::map<std::string,std::strin
veri_modules.InsertLast(veri_module); veri_modules.InsertLast(veri_module);
} }
} }
}
#ifdef VERIFIC_VHDL_SUPPORT #ifdef VERIFIC_VHDL_SUPPORT
if (vhdl_lib) { if (vhdl_lib) {
@ -2532,8 +2558,18 @@ std::string verific_import(Design *design, const std::map<std::string,std::strin
vhdl_units.InsertLast(vhdl_unit); vhdl_units.InsertLast(vhdl_unit);
} }
#endif #endif
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
if (static_elaborate) {
VerificExtensions::ElaborateAndRewrite("work", &veri_modules, &vhdl_units, &verific_params);
verific_error_msg.clear();
continue;
}
#endif
netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &verific_params); netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &verific_params);
} }
}
Netlist *nl; Netlist *nl;
int i; int i;
@ -2545,7 +2581,7 @@ std::string verific_import(Design *design, const std::map<std::string,std::strin
continue; continue;
nl->AddAtt(new Att(" \\top", NULL)); nl->AddAtt(new Att(" \\top", NULL));
nl_todo.emplace(nl->CellBaseName(), nl); nl_todo.emplace(nl->CellBaseName(), nl);
cell_name = nl->Owner()->Name(); cell_name = nl->CellBaseName();
} }
if (top.empty()) cell_name = top; if (top.empty()) cell_name = top;
@ -2567,7 +2603,7 @@ std::string verific_import(Design *design, const std::map<std::string,std::strin
if (nl_done.count(it->first) == 0) { if (nl_done.count(it->first) == 0) {
VerificImporter importer(false, false, false, false, false, false, false); VerificImporter importer(false, false, false, false, false, false, false);
nl_done[it->first] = it->second; nl_done[it->first] = it->second;
importer.import_netlist(design, nl, nl_todo, nl->Owner()->Name() == cell_name); importer.import_netlist(design, nl, nl_todo, nl->CellBaseName() == cell_name);
} }
nl_todo.erase(it); nl_todo.erase(it);
} }
@ -3056,6 +3092,7 @@ struct VerificPass : public Pass {
int argidx = 1; int argidx = 1;
std::string work = "work"; std::string work = "work";
bool is_work_set = false; bool is_work_set = false;
(void)is_work_set;
veri_file::RegisterCallBackVerificStream(&verific_read_cb); veri_file::RegisterCallBackVerificStream(&verific_read_cb);
if (GetSize(args) > argidx && (args[argidx] == "-set-error" || args[argidx] == "-set-warning" || if (GetSize(args) > argidx && (args[argidx] == "-set-error" || args[argidx] == "-set-warning" ||
@ -3133,7 +3170,20 @@ struct VerificPass : public Pass {
} }
veri_file::RemoveAllLOptions(); veri_file::RemoveAllLOptions();
veri_file::AddLOption("work"); for (int i = argidx; i < GetSize(args); i++)
{
if (args[i] == "-work" && i+1 < GetSize(args)) {
work = args[++i];
is_work_set = true;
continue;
}
if (args[i] == "-L" && i+1 < GetSize(args)) {
++i;
continue;
}
break;
}
veri_file::AddLOption(work.c_str());
for (int i = argidx; i < GetSize(args); i++) for (int i = argidx; i < GetSize(args); i++)
{ {
if (args[i] == "-work" && i+1 < GetSize(args)) { if (args[i] == "-work" && i+1 < GetSize(args)) {
@ -3141,7 +3191,7 @@ struct VerificPass : public Pass {
continue; continue;
} }
if (args[i] == "-L" && i+1 < GetSize(args)) { if (args[i] == "-L" && i+1 < GetSize(args)) {
if (args[++i] == "work") if (args[++i] == work)
veri_file::RemoveAllLOptions(); veri_file::RemoveAllLOptions();
continue; continue;
} }
@ -3574,6 +3624,9 @@ struct VerificPass : public Pass {
std::set<std::string> top_mod_names; std::set<std::string> top_mod_names;
if (mode_all)
{
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS #ifdef YOSYSHQ_VERIFIC_EXTENSIONS
VerificExtensions::ElaborateAndRewrite(work, &parameters); VerificExtensions::ElaborateAndRewrite(work, &parameters);
verific_error_msg.clear(); verific_error_msg.clear();
@ -3581,8 +3634,6 @@ struct VerificPass : public Pass {
if (!ppfile.empty()) if (!ppfile.empty())
veri_file::PrettyPrint(ppfile.c_str(), nullptr, work.c_str()); veri_file::PrettyPrint(ppfile.c_str(), nullptr, work.c_str());
if (mode_all)
{
log("Running hier_tree::ElaborateAll().\n"); log("Running hier_tree::ElaborateAll().\n");
VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1); VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1);
@ -3607,15 +3658,22 @@ struct VerificPass : public Pass {
if (argidx == GetSize(args)) if (argidx == GetSize(args))
cmd_error(args, argidx, "No top module specified.\n"); cmd_error(args, argidx, "No top module specified.\n");
Array *netlists = nullptr;
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
for (int static_elaborate = 1; static_elaborate >= 0; static_elaborate--)
#endif
{
VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
#ifdef VERIFIC_VHDL_SUPPORT #ifdef VERIFIC_VHDL_SUPPORT
VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
#endif #endif
Array veri_modules, vhdl_units; Array veri_modules, vhdl_units;
for (; argidx < GetSize(args); argidx++) for (int i = argidx; i < GetSize(args); i++)
{ {
const char *name = args[argidx].c_str(); const char *name = args[i].c_str();
top_mod_names.insert(name); top_mod_names.insert(name);
VeriModule *veri_module = veri_lib ? veri_lib->GetModule(name, 1) : nullptr; VeriModule *veri_module = veri_lib ? veri_lib->GetModule(name, 1) : nullptr;
@ -3634,7 +3692,7 @@ struct VerificPass : public Pass {
if (module_name && module_name->IsHierName()) { if (module_name && module_name->IsHierName()) {
VeriName *prefix = module_name->GetPrefix() ; VeriName *prefix = module_name->GetPrefix() ;
const char *lib_name = (prefix) ? prefix->GetName() : 0 ; const char *lib_name = (prefix) ? prefix->GetName() : 0 ;
if (!Strings::compare("work", lib_name)) lib = veri_file::GetLibrary(lib_name, 1) ; if (work != lib_name) lib = veri_file::GetLibrary(lib_name, 1) ;
} }
if (lib && module_name) if (lib && module_name)
top_mod_names.insert(lib->GetModule(module_name->GetName(), 1)->GetName()); top_mod_names.insert(lib->GetModule(module_name->GetName(), 1)->GetName());
@ -3656,6 +3714,22 @@ struct VerificPass : public Pass {
log_error("Can't find module/unit '%s'.\n", name); log_error("Can't find module/unit '%s'.\n", name);
} }
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
if (static_elaborate) {
VerificExtensions::ElaborateAndRewrite(work, &veri_modules, &vhdl_units, &parameters);
verific_error_msg.clear();
#endif
if (!ppfile.empty())
veri_file::PrettyPrint(ppfile.c_str(), nullptr, work.c_str());
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
continue;
}
#endif
const char *lib_name = nullptr;
SetIter si;
FOREACH_SET_ITEM(veri_file::GetAllLOptions(), si, &lib_name) {
VeriLibrary* veri_lib = veri_file::GetLibrary(lib_name, 0);
if (veri_lib) { if (veri_lib) {
// Also elaborate all root modules since they may contain bind statements // Also elaborate all root modules since they may contain bind statements
MapIter mi; MapIter mi;
@ -3665,9 +3739,12 @@ struct VerificPass : public Pass {
veri_modules.InsertLast(veri_module); veri_modules.InsertLast(veri_module);
} }
} }
}
log("Running hier_tree::Elaborate().\n"); log("Running hier_tree::Elaborate().\n");
Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &parameters); netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &parameters);
}
Netlist *nl; Netlist *nl;
int i; int i;
@ -3732,7 +3809,7 @@ struct VerificPass : public Pass {
VerificImporter importer(mode_gates, mode_keep, mode_nosva, VerificImporter importer(mode_gates, mode_keep, mode_nosva,
mode_names, mode_verific, mode_autocover, mode_fullinit); mode_names, mode_verific, mode_autocover, mode_fullinit);
nl_done[it->first] = it->second; nl_done[it->first] = it->second;
importer.import_netlist(design, nl, nl_todo, top_mod_names.count(nl->Owner()->Name())); importer.import_netlist(design, nl, nl_todo, top_mod_names.count(nl->CellBaseName()));
} }
nl_todo.erase(it); nl_todo.erase(it);
} }

View file

@ -292,6 +292,65 @@ static void rewriteGenForDeclInit(AstNode *loop)
substitute(incr); substitute(incr);
} }
static void ensureAsgnExprAllowed()
{
if (!sv_mode)
frontend_verilog_yyerror("Assignments within expressions are only supported in SystemVerilog mode.");
if (ast_stack.back()->type != AST_BLOCK)
frontend_verilog_yyerror("Assignments within expressions are only permitted within procedures.");
}
// add a pre/post-increment/decrement statement
static const AstNode *addIncOrDecStmt(dict<IdString, AstNode*> *stmt_attr, AstNode *lhs,
dict<IdString, AstNode*> *op_attr, AST::AstNodeType op,
YYLTYPE begin, YYLTYPE end)
{
AstNode *one = AstNode::mkconst_int(1, true);
AstNode *rhs = new AstNode(op, lhs->clone(), one);
if (op_attr != nullptr)
append_attr(rhs, op_attr);
AstNode *stmt = new AstNode(AST_ASSIGN_EQ, lhs, rhs);
SET_AST_NODE_LOC(stmt, begin, end);
if (stmt_attr != nullptr)
append_attr(stmt, stmt_attr);
ast_stack.back()->children.push_back(stmt);
return stmt;
}
// create a pre/post-increment/decrement expression, and add the corresponding statement
static AstNode *addIncOrDecExpr(AstNode *lhs, dict<IdString, AstNode*> *attr, AST::AstNodeType op, YYLTYPE begin, YYLTYPE end, bool undo)
{
ensureAsgnExprAllowed();
const AstNode *stmt = addIncOrDecStmt(nullptr, lhs, attr, op, begin, end);
log_assert(stmt->type == AST_ASSIGN_EQ);
AstNode *expr = stmt->children[0]->clone();
if (undo) {
AstNode *minus_one = AstNode::mkconst_int(-1, true, 1);
expr = new AstNode(op, expr, minus_one);
}
SET_AST_NODE_LOC(expr, begin, end);
return expr;
}
// add a binary operator assignment statement, e.g., a += b
static const AstNode *addAsgnBinopStmt(dict<IdString, AstNode*> *attr, AstNode *lhs, AST::AstNodeType op, AstNode *rhs, YYLTYPE begin, YYLTYPE end)
{
SET_AST_NODE_LOC(rhs, end, end);
if (op == AST_SHIFT_LEFT || op == AST_SHIFT_RIGHT ||
op == AST_SHIFT_SLEFT || op == AST_SHIFT_SRIGHT) {
rhs = new AstNode(AST_TO_UNSIGNED, rhs);
SET_AST_NODE_LOC(rhs, end, end);
}
rhs = new AstNode(op, lhs->clone(), rhs);
AstNode *stmt = new AstNode(AST_ASSIGN_EQ, lhs, rhs);
SET_AST_NODE_LOC(rhs, begin, end);
SET_AST_NODE_LOC(stmt, begin, end);
ast_stack.back()->children.push_back(stmt);
if (attr != nullptr)
append_attr(stmt, attr);
return lhs;
}
%} %}
%define api.prefix {frontend_verilog_yy} %define api.prefix {frontend_verilog_yy}
@ -358,7 +417,7 @@ static void rewriteGenForDeclInit(AstNode *loop)
%type <integer> integer_atom_type integer_vector_type %type <integer> integer_atom_type integer_vector_type
%type <al> attr case_attr %type <al> attr case_attr
%type <ast> struct_union %type <ast> struct_union
%type <ast_node_type> asgn_binop %type <ast_node_type> asgn_binop inc_or_dec_op
%type <ast> genvar_identifier %type <ast> genvar_identifier
%type <specify_target_ptr> specify_target %type <specify_target_ptr> specify_target
@ -2610,17 +2669,11 @@ simple_behavioral_stmt:
SET_AST_NODE_LOC(node, @2, @5); SET_AST_NODE_LOC(node, @2, @5);
append_attr(node, $1); append_attr(node, $1);
} | } |
attr lvalue TOK_INCREMENT { attr lvalue attr inc_or_dec_op {
AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, new AstNode(AST_ADD, $2->clone(), AstNode::mkconst_int(1, true))); addIncOrDecStmt($1, $2, $3, $4, @1, @4);
ast_stack.back()->children.push_back(node);
SET_AST_NODE_LOC(node, @2, @3);
append_attr(node, $1);
} | } |
attr lvalue TOK_DECREMENT { attr inc_or_dec_op attr lvalue {
AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, new AstNode(AST_SUB, $2->clone(), AstNode::mkconst_int(1, true))); addIncOrDecStmt($1, $4, $3, $2, @1, @4);
ast_stack.back()->children.push_back(node);
SET_AST_NODE_LOC(node, @2, @3);
append_attr(node, $1);
} | } |
attr lvalue OP_LE delay expr { attr lvalue OP_LE delay expr {
AstNode *node = new AstNode(AST_ASSIGN_LE, $2, $5); AstNode *node = new AstNode(AST_ASSIGN_LE, $2, $5);
@ -2629,18 +2682,7 @@ simple_behavioral_stmt:
append_attr(node, $1); append_attr(node, $1);
} | } |
attr lvalue asgn_binop delay expr { attr lvalue asgn_binop delay expr {
AstNode *expr_node = $5; addAsgnBinopStmt($1, $2, $3, $5, @2, @5);
if ($3 == AST_SHIFT_LEFT || $3 == AST_SHIFT_RIGHT ||
$3 == AST_SHIFT_SLEFT || $3 == AST_SHIFT_SRIGHT) {
expr_node = new AstNode(AST_TO_UNSIGNED, expr_node);
SET_AST_NODE_LOC(expr_node, @5, @5);
}
AstNode *op_node = new AstNode($3, $2->clone(), expr_node);
AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, op_node);
SET_AST_NODE_LOC(op_node, @2, @5);
SET_AST_NODE_LOC(node, @2, @5);
ast_stack.back()->children.push_back(node);
append_attr(node, $1);
}; };
asgn_binop: asgn_binop:
@ -2657,6 +2699,12 @@ asgn_binop:
TOK_SSHL_ASSIGN { $$ = AST_SHIFT_SLEFT; } | TOK_SSHL_ASSIGN { $$ = AST_SHIFT_SLEFT; } |
TOK_SSHR_ASSIGN { $$ = AST_SHIFT_SRIGHT; } ; TOK_SSHR_ASSIGN { $$ = AST_SHIFT_SRIGHT; } ;
inc_or_dec_op:
// NOTE: These should only be permitted in SV mode, but Yosys has
// allowed them in all modes since support for them was added in 2017.
TOK_INCREMENT { $$ = AST_ADD; } |
TOK_DECREMENT { $$ = AST_SUB; } ;
for_initialization: for_initialization:
TOK_ID '=' expr { TOK_ID '=' expr {
AstNode *ident = new AstNode(AST_IDENTIFIER); AstNode *ident = new AstNode(AST_IDENTIFIER);
@ -3149,6 +3197,14 @@ expr:
$$->children.push_back($6); $$->children.push_back($6);
SET_AST_NODE_LOC($$, @1, @$); SET_AST_NODE_LOC($$, @1, @$);
append_attr($$, $3); append_attr($$, $3);
} |
inc_or_dec_op attr rvalue {
$$ = addIncOrDecExpr($3, $2, $1, @1, @3, false);
} |
// TODO: Attributes are allowed in the middle here, but they create some
// non-trivial conflicts that don't seem worth solving for now.
rvalue inc_or_dec_op {
$$ = addIncOrDecExpr($1, nullptr, $2, @1, @2, true);
}; };
basic_expr: basic_expr:
@ -3436,6 +3492,17 @@ basic_expr:
frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode.");
$$ = new AstNode(AST_CAST_SIZE, $1, $4); $$ = new AstNode(AST_CAST_SIZE, $1, $4);
SET_AST_NODE_LOC($$, @1, @4); SET_AST_NODE_LOC($$, @1, @4);
} |
'(' expr '=' expr ')' {
ensureAsgnExprAllowed();
AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, $4);
ast_stack.back()->children.push_back(node);
SET_AST_NODE_LOC(node, @2, @4);
$$ = $2->clone();
} |
'(' expr asgn_binop expr ')' {
ensureAsgnExprAllowed();
$$ = addAsgnBinopStmt(nullptr, $2, $3, $4, @2, @4)-> clone();
}; };
concat_list: concat_list:

View file

@ -90,6 +90,12 @@ template<> struct hash_ops<uint32_t> : hash_int_ops
return a; return a;
} }
}; };
template<> struct hash_ops<uint64_t> : hash_int_ops
{
static inline unsigned int hash(uint64_t a) {
return mkhash((unsigned int)(a), (unsigned int)(a >> 32));
}
};
template<> struct hash_ops<std::string> { template<> struct hash_ops<std::string> {
static inline bool cmp(const std::string &a, const std::string &b) { static inline bool cmp(const std::string &a, const std::string &b) {
@ -988,7 +994,7 @@ public:
return !operator==(other); return !operator==(other);
} }
bool hash() const { unsigned int hash() const {
unsigned int hashval = mkhash_init; unsigned int hashval = mkhash_init;
for (auto &it : entries) for (auto &it : entries)
hashval ^= ops.hash(it.udata); hashval ^= ops.hash(it.udata);

View file

@ -313,18 +313,33 @@ RTLIL::Const RTLIL::Const::from_string(const std::string &str)
std::string RTLIL::Const::decode_string() const std::string RTLIL::Const::decode_string() const
{ {
std::string string; const int n = GetSize(bits);
string.reserve(GetSize(bits)/8); const int n_over_8 = n / 8;
for (int i = 0; i < GetSize(bits); i += 8) { std::string s;
s.reserve(n_over_8);
int i = n_over_8 * 8;
if (i < n) {
char ch = 0; char ch = 0;
for (int j = 0; j < 8 && i + j < int (bits.size()); j++) for (int j = 0; j < (n - i); j++) {
if (bits[i + j] == RTLIL::State::S1) if (bits[i + j] == RTLIL::State::S1) {
ch |= 1 << j; ch |= 1 << j;
if (ch != 0)
string.append({ch});
} }
std::reverse(string.begin(), string.end()); }
return string; if (ch != 0)
s.append({ch});
}
i -= 8;
for (; i >= 0; i -= 8) {
char ch = 0;
for (int j = 0; j < 8; j++) {
if (bits[i + j] == RTLIL::State::S1) {
ch |= 1 << j;
}
}
if (ch != 0)
s.append({ch});
}
return s;
} }
bool RTLIL::Const::is_fully_zero() const bool RTLIL::Const::is_fully_zero() const
@ -2677,6 +2692,19 @@ RTLIL::Cell* RTLIL::Module::addPow(RTLIL::IdString name, const RTLIL::SigSpec &s
return cell; return cell;
} }
RTLIL::Cell* RTLIL::Module::addFa(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_c, const RTLIL::SigSpec &sig_x, const RTLIL::SigSpec &sig_y, const std::string &src)
{
RTLIL::Cell *cell = addCell(name, ID($fa));
cell->parameters[ID::WIDTH] = sig_a.size();
cell->setPort(ID::A, sig_a);
cell->setPort(ID::B, sig_b);
cell->setPort(ID::C, sig_c);
cell->setPort(ID::X, sig_x);
cell->setPort(ID::Y, sig_y);
cell->set_src_attribute(src);
return cell;
}
RTLIL::Cell* RTLIL::Module::addSlice(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, RTLIL::Const offset, const std::string &src) RTLIL::Cell* RTLIL::Module::addSlice(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, RTLIL::Const offset, const std::string &src)
{ {
RTLIL::Cell *cell = addCell(name, ID($slice)); RTLIL::Cell *cell = addCell(name, ID($slice));
@ -4031,13 +4059,17 @@ void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec
unpack(); unpack();
other->unpack(); other->unpack();
dict<RTLIL::SigBit, int> pattern_to_with;
for (int i = 0; i < GetSize(pattern.bits_); i++) { for (int i = 0; i < GetSize(pattern.bits_); i++) {
if (pattern.bits_[i].wire != NULL) { if (pattern.bits_[i].wire != NULL) {
pattern_to_with.emplace(pattern.bits_[i], i);
}
}
for (int j = 0; j < GetSize(bits_); j++) { for (int j = 0; j < GetSize(bits_); j++) {
if (bits_[j] == pattern.bits_[i]) { auto it = pattern_to_with.find(bits_[j]);
other->bits_[j] = with.bits_[i]; if (it != pattern_to_with.end()) {
} other->bits_[j] = with.bits_[it->second];
}
} }
} }

View file

@ -309,9 +309,13 @@ namespace RTLIL
char operator[](size_t i) const { char operator[](size_t i) const {
const char *p = c_str(); const char *p = c_str();
#ifndef NDEBUG
for (; i != 0; i--, p++) for (; i != 0; i--, p++)
log_assert(*p != 0); log_assert(*p != 0);
return *p; return *p;
#else
return *(p + i);
#endif
} }
std::string substr(size_t pos = 0, size_t len = std::string::npos) const { std::string substr(size_t pos = 0, size_t len = std::string::npos) const {
@ -1298,6 +1302,8 @@ public:
RTLIL::Cell* addModFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); RTLIL::Cell* addModFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addPow (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool a_signed = false, bool b_signed = false, const std::string &src = ""); RTLIL::Cell* addPow (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool a_signed = false, bool b_signed = false, const std::string &src = "");
RTLIL::Cell* addFa (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_c, const RTLIL::SigSpec &sig_x, const RTLIL::SigSpec &sig_y, const std::string &src = "");
RTLIL::Cell* addLogicNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); RTLIL::Cell* addLogicNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addLogicAnd (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); RTLIL::Cell* addLogicAnd (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addLogicOr (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); RTLIL::Cell* addLogicOr (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");

View file

@ -128,41 +128,103 @@ public:
// A simple class for topological sorting // A simple class for topological sorting
// ------------------------------------------------ // ------------------------------------------------
template<typename T, typename C = std::less<T>> template <typename T, typename C = std::less<T>, typename OPS = hash_ops<T>> class TopoSort
struct TopoSort
{ {
bool analyze_loops, found_loops; public:
std::map<T, std::set<T, C>, C> database; // We use this ordering of the edges in the adjacency matrix for
std::set<std::set<T, C>> loops; // exact compatibility with an older implementation.
std::vector<T> sorted; struct IndirectCmp {
IndirectCmp(const std::vector<T> &nodes) : node_cmp_(), nodes_(nodes) {}
bool operator()(int a, int b) const
{
log_assert(static_cast<size_t>(a) < nodes_.size());
log_assert(static_cast<size_t>(b) < nodes_.size());
return node_cmp_(nodes_[a], nodes_[b]);
}
const C node_cmp_;
const std::vector<T> &nodes_;
};
TopoSort() bool analyze_loops;
std::map<T, int, C> node_to_index;
std::vector<std::set<int, IndirectCmp>> edges;
std::vector<T> sorted;
std::set<std::set<T, C>> loops;
TopoSort() : indirect_cmp(nodes)
{ {
analyze_loops = true; analyze_loops = true;
found_loops = false; found_loops = false;
} }
void node(T n) int node(T n)
{ {
if (database.count(n) == 0) auto rv = node_to_index.emplace(n, static_cast<int>(nodes.size()));
database[n] = std::set<T, C>(); if (rv.second) {
nodes.push_back(n);
edges.push_back(std::set<int, IndirectCmp>(indirect_cmp));
}
return rv.first->second;
} }
void edge(T left, T right) void edge(int l_index, int r_index) { edges[r_index].insert(l_index); }
void edge(T left, T right) { edge(node(left), node(right)); }
bool has_node(const T &node) { return node_to_index.find(node) != node_to_index.end(); }
bool sort()
{ {
node(left); log_assert(GetSize(node_to_index) == GetSize(edges));
database[right].insert(left); log_assert(GetSize(nodes) == GetSize(edges));
loops.clear();
sorted.clear();
found_loops = false;
std::vector<bool> marked_cells(edges.size(), false);
std::vector<bool> active_cells(edges.size(), false);
std::vector<int> active_stack;
sorted.reserve(edges.size());
for (const auto &it : node_to_index)
sort_worker(it.second, marked_cells, active_cells, active_stack);
log_assert(GetSize(sorted) == GetSize(nodes));
return !found_loops;
} }
void sort_worker(const T &n, std::set<T, C> &marked_cells, std::set<T, C> &active_cells, std::vector<T> &active_stack) // Build the more expensive representation of edges for
// a few passes that use it directly.
std::map<T, std::set<T, C>, C> get_database()
{ {
if (active_cells.count(n)) { std::map<T, std::set<T, C>, C> database;
for (size_t i = 0; i < nodes.size(); ++i) {
std::set<T, C> converted_edge_set;
for (int other_node : edges[i]) {
converted_edge_set.insert(nodes[other_node]);
}
database.emplace(nodes[i], converted_edge_set);
}
return database;
}
private:
bool found_loops;
std::vector<T> nodes;
const IndirectCmp indirect_cmp;
void sort_worker(const int root_index, std::vector<bool> &marked_cells, std::vector<bool> &active_cells, std::vector<int> &active_stack)
{
if (active_cells[root_index]) {
found_loops = true; found_loops = true;
if (analyze_loops) { if (analyze_loops) {
std::set<T, C> loop; std::set<T, C> loop;
for (int i = GetSize(active_stack) - 1; i >= 0; i--) { for (int i = GetSize(active_stack) - 1; i >= 0; i--) {
loop.insert(active_stack[i]); const int index = active_stack[i];
if (active_stack[i] == n) loop.insert(nodes[index]);
if (index == root_index)
break; break;
} }
loops.insert(loop); loops.insert(loop);
@ -170,42 +232,24 @@ struct TopoSort
return; return;
} }
if (marked_cells.count(n)) if (marked_cells[root_index])
return; return;
if (!database.at(n).empty()) if (!edges[root_index].empty()) {
{
if (analyze_loops) if (analyze_loops)
active_stack.push_back(n); active_stack.push_back(root_index);
active_cells.insert(n); active_cells[root_index] = true;
for (auto &left_n : database.at(n)) for (int left_n : edges[root_index])
sort_worker(left_n, marked_cells, active_cells, active_stack); sort_worker(left_n, marked_cells, active_cells, active_stack);
if (analyze_loops) if (analyze_loops)
active_stack.pop_back(); active_stack.pop_back();
active_cells.erase(n); active_cells[root_index] = false;
} }
marked_cells.insert(n); marked_cells[root_index] = true;
sorted.push_back(n); sorted.push_back(nodes[root_index]);
}
bool sort()
{
loops.clear();
sorted.clear();
found_loops = false;
std::set<T, C> marked_cells;
std::set<T, C> active_cells;
std::vector<T> active_stack;
for (auto &it : database)
sort_worker(it.first, marked_cells, active_cells, active_stack);
log_assert(GetSize(sorted) == GetSize(database));
return !found_loops;
} }
}; };

View file

@ -175,48 +175,6 @@ int ceil_log2(int x)
#endif #endif
} }
std::string stringf(const char *fmt, ...)
{
std::string string;
va_list ap;
va_start(ap, fmt);
string = vstringf(fmt, ap);
va_end(ap);
return string;
}
std::string vstringf(const char *fmt, va_list ap)
{
std::string string;
char *str = NULL;
#if defined(_WIN32 )|| defined(__CYGWIN__)
int sz = 64, rc;
while (1) {
va_list apc;
va_copy(apc, ap);
str = (char*)realloc(str, sz);
rc = vsnprintf(str, sz, fmt, apc);
va_end(apc);
if (rc >= 0 && rc < sz)
break;
sz *= 2;
}
#else
if (vasprintf(&str, fmt, ap) < 0)
str = NULL;
#endif
if (str != NULL) {
string = str;
free(str);
}
return string;
}
int readsome(std::istream &f, char *s, int n) int readsome(std::istream &f, char *s, int n)
{ {
int rc = int(f.readsome(s, n)); int rc = int(f.readsome(s, n));
@ -1395,8 +1353,12 @@ void shell(RTLIL::Design *design)
if ((command = fgets(command_buffer, 4096, stdin)) == NULL) if ((command = fgets(command_buffer, 4096, stdin)) == NULL)
break; break;
#endif #endif
if (command[strspn(command, " \t\r\n")] == 0) if (command[strspn(command, " \t\r\n")] == 0) {
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
free(command);
#endif
continue; continue;
}
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) #if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
add_history(command); add_history(command);
#endif #endif
@ -1418,10 +1380,17 @@ void shell(RTLIL::Design *design)
log_reset_stack(); log_reset_stack();
} }
design->check(); design->check();
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
if (command)
free(command);
#endif
} }
if (command == NULL) if (command == NULL)
printf("exit\n"); printf("exit\n");
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
else
free(command);
#endif
recursion_counter--; recursion_counter--;
log_cmd_error_throw = false; log_cmd_error_throw = false;
} }

View file

@ -272,8 +272,64 @@ inline void memhasher() { if (memhasher_active) memhasher_do(); }
void yosys_banner(); void yosys_banner();
int ceil_log2(int x) YS_ATTRIBUTE(const); int ceil_log2(int x) YS_ATTRIBUTE(const);
inline std::string vstringf(const char *fmt, va_list ap)
{
// For the common case of strings shorter than 128, save a heap
// allocation by using a stack allocated buffer.
const int kBufSize = 128;
char buf[kBufSize];
buf[0] = '\0';
va_list apc;
va_copy(apc, ap);
int n = vsnprintf(buf, kBufSize, fmt, apc);
va_end(apc);
if (n < kBufSize)
return std::string(buf);
std::string string;
char *str = NULL;
#if defined(_WIN32 )|| defined(__CYGWIN__)
int sz = 2 * kBufSize, rc;
while (1) {
va_copy(apc, ap);
str = (char*)realloc(str, sz);
rc = vsnprintf(str, sz, fmt, apc);
va_end(apc);
if (rc >= 0 && rc < sz)
break;
sz *= 2;
}
if (str != NULL) {
string = str;
free(str);
}
return string;
#else
if (vasprintf(&str, fmt, ap) < 0)
str = NULL;
if (str != NULL) {
string = str;
free(str);
}
return string;
#endif
}
std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2)); std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2));
std::string vstringf(const char *fmt, va_list ap);
inline std::string stringf(const char *fmt, ...)
{
std::string string;
va_list ap;
va_start(ap, fmt);
string = vstringf(fmt, ap);
va_end(ap);
return string;
}
int readsome(std::istream &f, char *s, int n); int readsome(std::istream &f, char *s, int n);
std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false); std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false);
std::vector<std::string> split_tokens(const std::string &text, const char *sep = " \t\r\n"); std::vector<std::string> split_tokens(const std::string &text, const char *sep = " \t\r\n");

View file

@ -24,8 +24,8 @@ PRIVATE_NAMESPACE_BEGIN
int autoname_worker(Module *module, const dict<Wire*, int>& wire_score) int autoname_worker(Module *module, const dict<Wire*, int>& wire_score)
{ {
dict<Cell*, pair<int, IdString>> proposed_cell_names; dict<Cell*, pair<int, string>> proposed_cell_names;
dict<Wire*, pair<int, IdString>> proposed_wire_names; dict<Wire*, pair<int, string>> proposed_wire_names;
int best_score = -1; int best_score = -1;
for (auto cell : module->selected_cells()) { for (auto cell : module->selected_cells()) {
@ -36,7 +36,7 @@ int autoname_worker(Module *module, const dict<Wire*, int>& wire_score)
if (bit.wire != nullptr && bit.wire->name[0] != '$') { if (bit.wire != nullptr && bit.wire->name[0] != '$') {
if (suffix.empty()) if (suffix.empty())
suffix = stringf("_%s_%s", log_id(cell->type), log_id(conn.first)); suffix = stringf("_%s_%s", log_id(cell->type), log_id(conn.first));
IdString new_name(bit.wire->name.str() + suffix); string new_name(bit.wire->name.str() + suffix);
int score = wire_score.at(bit.wire); int score = wire_score.at(bit.wire);
if (cell->output(conn.first)) score = 0; if (cell->output(conn.first)) score = 0;
score = 10000*score + new_name.size(); score = 10000*score + new_name.size();
@ -54,7 +54,7 @@ int autoname_worker(Module *module, const dict<Wire*, int>& wire_score)
if (bit.wire != nullptr && bit.wire->name[0] == '$' && !bit.wire->port_id) { if (bit.wire != nullptr && bit.wire->name[0] == '$' && !bit.wire->port_id) {
if (suffix.empty()) if (suffix.empty())
suffix = stringf("_%s", log_id(conn.first)); suffix = stringf("_%s", log_id(conn.first));
IdString new_name(cell->name.str() + suffix); string new_name(cell->name.str() + suffix);
int score = wire_score.at(bit.wire); int score = wire_score.at(bit.wire);
if (cell->output(conn.first)) score = 0; if (cell->output(conn.first)) score = 0;
score = 10000*score + new_name.size(); score = 10000*score + new_name.size();
@ -71,7 +71,7 @@ int autoname_worker(Module *module, const dict<Wire*, int>& wire_score)
for (auto &it : proposed_cell_names) { for (auto &it : proposed_cell_names) {
if (best_score*2 < it.second.first) if (best_score*2 < it.second.first)
continue; continue;
IdString n = module->uniquify(it.second.second); IdString n = module->uniquify(IdString(it.second.second));
log_debug("Rename cell %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n)); log_debug("Rename cell %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n));
module->rename(it.first, n); module->rename(it.first, n);
} }
@ -79,7 +79,7 @@ int autoname_worker(Module *module, const dict<Wire*, int>& wire_score)
for (auto &it : proposed_wire_names) { for (auto &it : proposed_wire_names) {
if (best_score*2 < it.second.first) if (best_score*2 < it.second.first)
continue; continue;
IdString n = module->uniquify(it.second.second); IdString n = module->uniquify(IdString(it.second.second));
log_debug("Rename wire %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n)); log_debug("Rename wire %s in %s to %s.\n", log_id(it.first), log_id(module), log_id(n));
module->rename(it.first, n); module->rename(it.first, n);
} }

View file

@ -405,7 +405,7 @@ struct DftTagWorker {
auto &sig_y = cell->getPort(ID::Y); auto &sig_y = cell->getPort(ID::Y);
auto sig_a = cell->getPort(ID::A); auto sig_a = cell->getPort(ID::A);
auto sig_b = cell->getPort(ID::B); auto sig_b = cell->getPort(ID::B);
if (cell->type.in(ID($and), ID($or))) { if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor))) {
sig_a.extend_u0(GetSize(sig_y), cell->getParam(ID::A_SIGNED).as_bool()); sig_a.extend_u0(GetSize(sig_y), cell->getParam(ID::A_SIGNED).as_bool());
sig_b.extend_u0(GetSize(sig_y), cell->getParam(ID::B_SIGNED).as_bool()); sig_b.extend_u0(GetSize(sig_y), cell->getParam(ID::B_SIGNED).as_bool());
} }
@ -669,12 +669,12 @@ struct DftTagWorker {
auto &sig_y = cell->getPort(ID::Y); auto &sig_y = cell->getPort(ID::Y);
auto sig_a = cell->getPort(ID::A); auto sig_a = cell->getPort(ID::A);
if (cell->type.in(ID($reduce_or), ID($reduce_bool), ID($logic_not)))
sig_a = autoNot(NEW_ID, sig_a);
auto group_sig_a = tag_group_signal(tag, sig_a); auto group_sig_a = tag_group_signal(tag, sig_a);
auto tag_sig_a = tag_signal(tag, sig_a); auto tag_sig_a = tag_signal(tag, sig_a);
if (cell->type.in(ID($reduce_or), ID($reduce_bool), ID($logic_not)))
sig_a = autoNot(NEW_ID, sig_a);
auto filled = autoOr(NEW_ID, sig_a, group_sig_a); auto filled = autoOr(NEW_ID, sig_a, group_sig_a);
auto prop = autoReduceAnd(NEW_ID, filled); auto prop = autoReduceAnd(NEW_ID, filled);

View file

@ -582,7 +582,7 @@ struct GliftPass : public Pass {
for (auto cell : module->selected_cells()) { for (auto cell : module->selected_cells()) {
RTLIL::Module *tpl = design->module(cell->type); RTLIL::Module *tpl = design->module(cell->type);
if (tpl != nullptr) { if (tpl != nullptr) {
if (topo_modules.database.count(tpl) == 0) if (!topo_modules.has_node(tpl))
worklist.push_back(tpl); worklist.push_back(tpl);
topo_modules.edge(tpl, module); topo_modules.edge(tpl, module);
non_top_modules.insert(cell->type); non_top_modules.insert(cell->type);

View file

@ -27,7 +27,6 @@
#include "kernel/log.h" #include "kernel/log.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <set>
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
@ -39,18 +38,18 @@ struct SccWorker
SigMap sigmap; SigMap sigmap;
CellTypes ct, specifyCells; CellTypes ct, specifyCells;
std::set<RTLIL::Cell*> workQueue; pool<RTLIL::Cell*> workQueue;
std::map<RTLIL::Cell*, std::set<RTLIL::Cell*>> cellToNextCell; dict<RTLIL::Cell*, pool<RTLIL::Cell*>> cellToNextCell;
std::map<RTLIL::Cell*, RTLIL::SigSpec> cellToPrevSig, cellToNextSig; dict<RTLIL::Cell*, RTLIL::SigSpec> cellToPrevSig, cellToNextSig;
std::map<RTLIL::Cell*, std::pair<int, int>> cellLabels; dict<RTLIL::Cell*, std::pair<int, int>> cellLabels;
std::map<RTLIL::Cell*, int> cellDepth; dict<RTLIL::Cell*, int> cellDepth;
std::set<RTLIL::Cell*> cellsOnStack; pool<RTLIL::Cell*> cellsOnStack;
std::vector<RTLIL::Cell*> cellStack; std::vector<RTLIL::Cell*> cellStack;
int labelCounter; int labelCounter;
std::map<RTLIL::Cell*, int> cell2scc; dict<RTLIL::Cell*, int> cell2scc;
std::vector<std::set<RTLIL::Cell*>> sccList; std::vector<pool<RTLIL::Cell*>> sccList;
void run(RTLIL::Cell *cell, int depth, int maxDepth) void run(RTLIL::Cell *cell, int depth, int maxDepth)
{ {
@ -85,7 +84,7 @@ struct SccWorker
else else
{ {
log("Found an SCC:"); log("Found an SCC:");
std::set<RTLIL::Cell*> scc; pool<RTLIL::Cell*> scc;
while (cellsOnStack.count(cell) > 0) { while (cellsOnStack.count(cell) > 0) {
RTLIL::Cell *c = cellStack.back(); RTLIL::Cell *c = cellStack.back();
cellStack.pop_back(); cellStack.pop_back();
@ -199,11 +198,11 @@ struct SccWorker
for (auto cell : workQueue) for (auto cell : workQueue)
{ {
cellToNextCell[cell] = sigToNextCells.find(cellToNextSig[cell]); sigToNextCells.find(cellToNextSig[cell], cellToNextCell[cell]);
if (!nofeedbackMode && cellToNextCell[cell].count(cell)) { if (!nofeedbackMode && cellToNextCell[cell].count(cell)) {
log("Found an SCC:"); log("Found an SCC:");
std::set<RTLIL::Cell*> scc; pool<RTLIL::Cell*> scc;
log(" %s", RTLIL::id2cstr(cell->name)); log(" %s", RTLIL::id2cstr(cell->name));
cell2scc[cell] = sccList.size(); cell2scc[cell] = sccList.size();
scc.insert(cell); scc.insert(cell);
@ -231,7 +230,7 @@ struct SccWorker
{ {
for (int i = 0; i < int(sccList.size()); i++) for (int i = 0; i < int(sccList.size()); i++)
{ {
std::set<RTLIL::Cell*> &cells = sccList[i]; pool<RTLIL::Cell*> &cells = sccList[i];
RTLIL::SigSpec prevsig, nextsig, sig; RTLIL::SigSpec prevsig, nextsig, sig;
for (auto cell : cells) { for (auto cell : cells) {
@ -295,7 +294,7 @@ struct SccPass : public Pass {
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override void execute(std::vector<std::string> args, RTLIL::Design *design) override
{ {
std::map<std::string, std::string> setAttr; dict<std::string, std::string> setAttr;
bool allCellTypes = false; bool allCellTypes = false;
bool selectMode = false; bool selectMode = false;
bool nofeedbackMode = false; bool nofeedbackMode = false;

View file

@ -575,7 +575,7 @@ struct ShowWorker
} else { } else {
net_conn_map[right_node].in.insert({stringf("x%d", single_idx_count), GetSize(conn.first)}); net_conn_map[right_node].in.insert({stringf("x%d", single_idx_count), GetSize(conn.first)});
net_conn_map[left_node].out.insert({stringf("x%d", single_idx_count), GetSize(conn.first)}); net_conn_map[left_node].out.insert({stringf("x%d", single_idx_count), GetSize(conn.first)});
fprintf(f, "x%d [shape=box, style=rounded, label=\"BUF\", %s];\n", single_idx_count++, findColor(conn).c_str()); fprintf(f, "x%d [shape=point, %s];\n", single_idx_count++, findColor(conn).c_str());
} }
} }
} }

View file

@ -427,10 +427,16 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
cells.node(cell); cells.node(cell);
} }
for (auto &it_right : cell_to_inbit) // Build the graph for the topological sort.
for (auto &it_sigbit : it_right.second) for (auto &it_right : cell_to_inbit) {
for (auto &it_left : outbit_to_cell[it_sigbit]) const int r_index = cells.node(it_right.first);
cells.edge(it_left, it_right.first); for (auto &it_sigbit : it_right.second) {
for (auto &it_left : outbit_to_cell[it_sigbit]) {
const int l_index = cells.node(it_left);
cells.edge(l_index, r_index);
}
}
}
cells.sort(); cells.sort();

View file

@ -41,7 +41,6 @@ struct OptMergeWorker
CellTypes ct; CellTypes ct;
int total_count; int total_count;
SHA1 checksum;
static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn) static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn)
{ {
@ -78,7 +77,7 @@ struct OptMergeWorker
return str; return str;
} }
std::string hash_cell_parameters_and_connections(const RTLIL::Cell *cell) uint64_t hash_cell_parameters_and_connections(const RTLIL::Cell *cell)
{ {
vector<string> hash_conn_strings; vector<string> hash_conn_strings;
std::string hash_string = cell->type.str() + "\n"; std::string hash_string = cell->type.str() + "\n";
@ -149,8 +148,7 @@ struct OptMergeWorker
for (auto it : hash_conn_strings) for (auto it : hash_conn_strings)
hash_string += it; hash_string += it;
checksum.update(hash_string); return std::hash<std::string>{}(hash_string);
return checksum.final();
} }
bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2)
@ -268,13 +266,13 @@ struct OptMergeWorker
} }
did_something = false; did_something = false;
dict<std::string, RTLIL::Cell*> sharemap; dict<uint64_t, RTLIL::Cell*> sharemap;
for (auto cell : cells) for (auto cell : cells)
{ {
if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known()) if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known())
continue; continue;
auto hash = hash_cell_parameters_and_connections(cell); uint64_t hash = hash_cell_parameters_and_connections(cell);
auto r = sharemap.insert(std::make_pair(hash, cell)); auto r = sharemap.insert(std::make_pair(hash, cell));
if (!r.second) { if (!r.second) {
if (compare_cell_parameters_and_connections(cell, r.first->second)) { if (compare_cell_parameters_and_connections(cell, r.first->second)) {

View file

@ -1032,7 +1032,7 @@ struct ShareWorker
} }
bool found_scc = !toposort.sort(); bool found_scc = !toposort.sort();
topo_cell_drivers = std::move(toposort.database); topo_cell_drivers = toposort.get_database();
if (found_scc && toposort.analyze_loops) if (found_scc && toposort.analyze_loops)
for (auto &loop : toposort.loops) { for (auto &loop : toposort.loops) {

View file

@ -42,7 +42,8 @@ GENFILES += passes/pmgen/peepopt_pm.h
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h)) $(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul_right.pmg
PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftmul_left.pmg
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)

View file

@ -212,7 +212,7 @@ second argument, and the matcher will iterate over those options:
index <SigSpec> port(eq, BA) === bar index <SigSpec> port(eq, BA) === bar
set eq_ab AB set eq_ab AB
set eq_ba BA set eq_ba BA
generate endmatch
Notice how `define` can be used to define additional local variables similar Notice how `define` can be used to define additional local variables similar
to the loop variables defined by `slice` and `choice`. to the loop variables defined by `slice` and `choice`.

View file

@ -24,11 +24,8 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
bool did_something; bool did_something;
dict<SigBit, State> initbits;
pool<SigBit> rminitbits;
#include "passes/pmgen/peepopt_pm.h" #include "passes/pmgen/peepopt_pm.h"
#include "generate.h"
struct PeepoptPass : public Pass { struct PeepoptPass : public Pass {
PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { } PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { }
@ -40,38 +37,29 @@ struct PeepoptPass : public Pass {
log("\n"); log("\n");
log("This pass applies a collection of peephole optimizers to the current design.\n"); log("This pass applies a collection of peephole optimizers to the current design.\n");
log("\n"); log("\n");
log("This pass employs the following rules:\n");
log("\n");
log(" * muldiv - Replace (A*B)/B with A\n");
log("\n");
log(" * shiftmul - Replace A>>(B*C) with A'>>(B<<K) where C and K are constants\n");
log(" and A' is derived from A by appropriately inserting padding\n");
log(" into the signal. (right variant)\n");
log("\n");
log(" Analogously, replace A<<(B*C) with appropriate selection of\n");
log(" output bits from A<<(B<<K). (left variant)\n");
log("\n");
} }
void execute(std::vector<std::string> args, RTLIL::Design *design) override void execute(std::vector<std::string> args, RTLIL::Design *design) override
{ {
std::string genmode;
log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n"); log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n");
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) for (argidx = 1; argidx < args.size(); argidx++)
{ {
if (args[argidx] == "-generate" && argidx+1 < args.size()) {
genmode = args[++argidx];
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
if (!genmode.empty())
{
initbits.clear();
rminitbits.clear();
if (genmode == "shiftmul")
GENERATE_PATTERN(peepopt_pm, shiftmul);
else if (genmode == "muldiv")
GENERATE_PATTERN(peepopt_pm, muldiv);
else
log_abort();
return;
}
for (auto module : design->selected_modules()) for (auto module : design->selected_modules())
{ {
did_something = true; did_something = true;
@ -79,47 +67,14 @@ struct PeepoptPass : public Pass {
while (did_something) while (did_something)
{ {
did_something = false; did_something = false;
initbits.clear();
rminitbits.clear();
peepopt_pm pm(module); peepopt_pm pm(module);
for (auto w : module->wires()) {
auto it = w->attributes.find(ID::init);
if (it != w->attributes.end()) {
SigSpec sig = pm.sigmap(w);
Const val = it->second;
int len = std::min(GetSize(sig), GetSize(val));
for (int i = 0; i < len; i++) {
if (sig[i].wire == nullptr)
continue;
if (val[i] != State::S0 && val[i] != State::S1)
continue;
initbits[sig[i]] = val[i];
}
}
}
pm.setup(module->selected_cells()); pm.setup(module->selected_cells());
pm.run_shiftmul(); pm.run_shiftmul_right();
pm.run_shiftmul_left();
pm.run_muldiv(); pm.run_muldiv();
for (auto w : module->wires()) {
auto it = w->attributes.find(ID::init);
if (it != w->attributes.end()) {
SigSpec sig = pm.sigmap(w);
Const &val = it->second;
int len = std::min(GetSize(sig), GetSize(val));
for (int i = 0; i < len; i++) {
if (rminitbits.count(sig[i]))
val[i] = State::Sx;
}
}
}
initbits.clear();
rminitbits.clear();
} }
} }
} }

View file

@ -1,92 +0,0 @@
pattern shiftmul
//
// Optimize mul+shift pairs that result from expressions such as foo[s*W+:W]
//
state <SigSpec> shamt
match shift
select shift->type.in($shift, $shiftx, $shr)
endmatch
code shamt
shamt = port(shift, \B);
if (shamt.empty())
reject;
if (shamt[GetSize(shamt)-1] == State::S0) {
do {
shamt.remove(GetSize(shamt)-1);
if (shamt.empty())
reject;
} while (shamt[GetSize(shamt)-1] == State::S0);
} else
if (shift->type.in($shift, $shiftx) && param(shift, \B_SIGNED).as_bool()) {
reject;
}
if (GetSize(shamt) > 20)
reject;
endcode
match mul
select mul->type.in($mul)
select port(mul, \A).is_fully_const() || port(mul, \B).is_fully_const()
index <SigSpec> port(mul, \Y) === shamt
filter !param(mul, \A_SIGNED).as_bool()
endmatch
code
{
IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B;
Const const_factor_cnst = port(mul, const_factor_port).as_const();
int const_factor = const_factor_cnst.as_int();
if (GetSize(const_factor_cnst) == 0)
reject;
if (GetSize(const_factor_cnst) > 20)
reject;
if (GetSize(port(shift, \Y)) > const_factor)
reject;
int factor_bits = ceil_log2(const_factor);
SigSpec mul_din = port(mul, const_factor_port == \A ? \B : \A);
if (GetSize(shamt) < factor_bits+GetSize(mul_din))
reject;
did_something = true;
log("shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul));
int new_const_factor = 1 << factor_bits;
SigSpec padding(State::Sx, new_const_factor-const_factor);
SigSpec old_a = port(shift, \A), new_a;
int trunc = 0;
if (GetSize(old_a) % const_factor != 0) {
trunc = const_factor - GetSize(old_a) % const_factor;
old_a.append(SigSpec(State::Sx, trunc));
}
for (int i = 0; i*const_factor < GetSize(old_a); i++) {
SigSpec slice = old_a.extract(i*const_factor, const_factor);
new_a.append(slice);
new_a.append(padding);
}
if (trunc > 0)
new_a.remove(GetSize(new_a)-trunc, trunc);
SigSpec new_b = {mul_din, SigSpec(State::S0, factor_bits)};
if (param(shift, \B_SIGNED).as_bool())
new_b.append(State::S0);
shift->setPort(\A, new_a);
shift->setParam(\A_WIDTH, GetSize(new_a));
shift->setPort(\B, new_b);
shift->setParam(\B_WIDTH, GetSize(new_b));
blacklist(shift);
accept;
}
endcode

View file

@ -0,0 +1,160 @@
pattern shiftmul_left
//
// Optimize mul+shift pairs that result from expressions such as foo[s*W+:W]
//
match shift
select shift->type.in($shift, $shiftx, $shl)
select shift->type.in($shl) || param(shift, \B_SIGNED).as_bool()
filter !port(shift, \B).empty()
endmatch
match neg
if shift->type.in($shift, $shiftx)
select neg->type == $neg
index <SigSpec> port(neg, \Y) === port(shift, \B)
filter !port(shift, \A).empty()
endmatch
// the left shift amount
state <SigSpec> shift_amount
// log2 scale factor in interpreting of shift_amount
// due to zero padding on the shift cell's B port
state <int> log2scale
code shift_amount log2scale
if (neg) {
// case of `$shift`, `$shiftx`
shift_amount = port(neg, \A);
if (!param(neg, \A_SIGNED).as_bool())
shift_amount.append(State::S0);
} else {
// case of `$shl`
shift_amount = port(shift, \B);
if (!param(shift, \B_SIGNED).as_bool())
shift_amount.append(State::S0);
}
// at this point shift_amount is signed, make
// sure we can never go negative
if (shift_amount.bits().back() != State::S0)
reject;
while (shift_amount.bits().back() == State::S0) {
shift_amount.remove(GetSize(shift_amount) - 1);
if (shift_amount.empty()) reject;
}
log2scale = 0;
while (shift_amount[0] == State::S0) {
shift_amount.remove(0);
if (shift_amount.empty()) reject;
log2scale++;
}
if (GetSize(shift_amount) > 20)
reject;
endcode
state <SigSpec> mul_din
state <Const> mul_const
match mul
select mul->type.in($mul)
index <SigSpec> port(mul, \Y) === shift_amount
filter !param(mul, \A_SIGNED).as_bool()
choice <IdString> constport {\A, \B}
filter port(mul, constport).is_fully_const()
define <IdString> varport (constport == \A ? \B : \A)
set mul_const SigSpec({port(mul, constport), SigSpec(State::S0, log2scale)}).as_const()
// get mul_din unmapped (so no `port()` shorthand)
// because we will be using it to set the \A port
// on the shift cell, and we want to stay close
// to the original design
set mul_din mul->getPort(varport)
endmatch
code
{
if (mul_const.empty() || GetSize(mul_const) > 20)
reject;
// make sure there's no overlap in the signal
// selections by the shiftmul pattern
if (GetSize(port(shift, \A)) > mul_const.as_int())
reject;
int factor_bits = ceil_log2(mul_const.as_int());
// make sure the multiplication never wraps around
if (GetSize(shift_amount) < factor_bits + GetSize(mul_din))
reject;
if (neg) {
// make sure the negation never wraps around
if (GetSize(port(shift, \B)) < factor_bits + GetSize(mul_din)
+ log2scale + 1)
reject;
}
did_something = true;
log("left shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul));
int const_factor = mul_const.as_int();
int new_const_factor = 1 << factor_bits;
SigSpec padding(State::Sm, new_const_factor-const_factor);
SigSpec old_y = port(shift, \Y), new_y;
int trunc = 0;
if (GetSize(old_y) % const_factor != 0) {
trunc = const_factor - GetSize(old_y) % const_factor;
old_y.append(SigSpec(State::Sm, trunc));
}
for (int i = 0; i*const_factor < GetSize(old_y); i++) {
SigSpec slice = old_y.extract(i*const_factor, const_factor);
new_y.append(slice);
new_y.append(padding);
}
if (trunc > 0)
new_y.remove(GetSize(new_y)-trunc, trunc);
{
// Now replace occurences of Sm in new_y with bits
// of a dummy wire
int padbits = 0;
for (auto bit : new_y)
if (bit == SigBit(State::Sm))
padbits++;
SigSpec padwire = module->addWire(NEW_ID, padbits);
for (int i = new_y.size() - 1; i >= 0; i--)
if (new_y[i] == SigBit(State::Sm)) {
new_y[i] = padwire.bits().back();
padwire.remove(padwire.size() - 1);
}
}
SigSpec new_b = {mul_din, SigSpec(State::S0, factor_bits)};
shift->setPort(\Y, new_y);
shift->setParam(\Y_WIDTH, GetSize(new_y));
if (shift->type == $shl) {
if (param(shift, \B_SIGNED).as_bool())
new_b.append(State::S0);
shift->setPort(\B, new_b);
shift->setParam(\B_WIDTH, GetSize(new_b));
} else {
SigSpec b_neg = module->addWire(NEW_ID, GetSize(new_b) + 1);
module->addNeg(NEW_ID, new_b, b_neg);
shift->setPort(\B, b_neg);
shift->setParam(\B_WIDTH, GetSize(b_neg));
}
blacklist(shift);
accept;
}
endcode

View file

@ -0,0 +1,113 @@
pattern shiftmul_right
//
// Optimize mul+shift pairs that result from expressions such as foo[s*W+:W]
//
match shift
select shift->type.in($shift, $shiftx, $shr)
filter !port(shift, \B).empty()
endmatch
// the right shift amount
state <SigSpec> shift_amount
// log2 scale factor in interpreting of shift_amount
// due to zero padding on the shift cell's B port
state <int> log2scale
code shift_amount log2scale
shift_amount = port(shift, \B);
if (shift->type.in($shr) || !param(shift, \B_SIGNED).as_bool())
shift_amount.append(State::S0);
// at this point shift_amount is signed, make
// sure we can never go negative
if (shift_amount.bits().back() != State::S0)
reject;
while (shift_amount.bits().back() == State::S0) {
shift_amount.remove(GetSize(shift_amount) - 1);
if (shift_amount.empty()) reject;
}
log2scale = 0;
while (shift_amount[0] == State::S0) {
shift_amount.remove(0);
if (shift_amount.empty()) reject;
log2scale++;
}
if (GetSize(shift_amount) > 20)
reject;
endcode
state <SigSpec> mul_din
state <Const> mul_const
match mul
select mul->type.in($mul)
index <SigSpec> port(mul, \Y) === shift_amount
filter !param(mul, \A_SIGNED).as_bool()
choice <IdString> constport {\A, \B}
filter port(mul, constport).is_fully_const()
define <IdString> varport (constport == \A ? \B : \A)
set mul_const SigSpec({port(mul, constport), SigSpec(State::S0, log2scale)}).as_const()
// get mul_din unmapped (so no `port()` shorthand)
// because we will be using it to set the \A port
// on the shift cell, and we want to stay close
// to the original design
set mul_din mul->getPort(varport)
endmatch
code
{
if (mul_const.empty() || GetSize(mul_const) > 20)
reject;
// make sure there's no overlap in the signal
// selections by the shiftmul pattern
if (GetSize(port(shift, \Y)) > mul_const.as_int())
reject;
int factor_bits = ceil_log2(mul_const.as_int());
// make sure the multiplication never wraps around
if (GetSize(shift_amount) + log2scale < factor_bits + GetSize(mul_din))
reject;
did_something = true;
log("right shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul));
int const_factor = mul_const.as_int();
int new_const_factor = 1 << factor_bits;
SigSpec padding(State::Sx, new_const_factor-const_factor);
SigSpec old_a = port(shift, \A), new_a;
int trunc = 0;
if (GetSize(old_a) % const_factor != 0) {
trunc = const_factor - GetSize(old_a) % const_factor;
old_a.append(SigSpec(State::Sx, trunc));
}
for (int i = 0; i*const_factor < GetSize(old_a); i++) {
SigSpec slice = old_a.extract(i*const_factor, const_factor);
new_a.append(slice);
new_a.append(padding);
}
if (trunc > 0)
new_a.remove(GetSize(new_a)-trunc, trunc);
SigSpec new_b = {mul_din, SigSpec(State::S0, factor_bits)};
if (param(shift, \B_SIGNED).as_bool())
new_b.append(State::S0);
shift->setPort(\A, new_a);
shift->setParam(\A_WIDTH, GetSize(new_a));
shift->setPort(\B, new_b);
shift->setParam(\B_WIDTH, GetSize(new_b));
blacklist(shift);
accept;
}
endcode

View file

@ -29,13 +29,6 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
template<> struct hashlib::hash_ops<uint64_t> : hashlib::hash_int_ops
{
static inline unsigned int hash(uint64_t a) {
return mkhash((unsigned int)(a), (unsigned int)(a >> 32));
}
};
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
// xorshift128 params // xorshift128 params

View file

@ -111,6 +111,7 @@ struct SimShared
int step = 0; int step = 0;
std::vector<TriggeredAssertion> triggered_assertions; std::vector<TriggeredAssertion> triggered_assertions;
bool serious_asserts = false; bool serious_asserts = false;
bool initstate = true;
}; };
void zinit(State &v) void zinit(State &v)
@ -218,9 +219,13 @@ struct SimInstance
log_assert(module); log_assert(module);
if (module->get_blackbox_attribute(true)) if (module->get_blackbox_attribute(true))
log_error("Cannot simulate blackbox module %s (instanced at %s).\n", log_error("Cannot simulate blackbox module %s (instantiated at %s).\n",
log_id(module->name), hiername().c_str()); log_id(module->name), hiername().c_str());
if (module->has_processes())
log_error("Found processes in simulation hierarchy (in module %s at %s). Run 'proc' first.\n",
log_id(module), hiername().c_str());
if (parent) { if (parent) {
log_assert(parent->children.count(instance) == 0); log_assert(parent->children.count(instance) == 0);
parent->children[instance] = this; parent->children[instance] = this;
@ -578,7 +583,7 @@ struct SimInstance
Const data = Const(State::Sx, mem.width << port.wide_log2); Const data = Const(State::Sx, mem.width << port.wide_log2);
if (port.clk_enable) if (port.clk_enable)
log_error("Memory %s.%s has clocked read ports. Run 'memory' with -nordff.\n", log_id(module), log_id(mem.memid)); log_error("Memory %s.%s has clocked read ports. Run 'memory_nordff' to transform the circuit to remove those.\n", log_id(module), log_id(mem.memid));
if (addr.is_fully_def()) { if (addr.is_fully_def()) {
int addr_int = addr.as_int(); int addr_int = addr.as_int();
@ -1356,6 +1361,8 @@ struct SimWorker : SimShared
set_inports(clock, State::Sx); set_inports(clock, State::Sx);
set_inports(clockn, State::Sx); set_inports(clockn, State::Sx);
top->set_initstate_outputs(initstate ? State::S1 : State::S0);
update(false); update(false);
register_output_step(0); register_output_step(0);
@ -1372,6 +1379,9 @@ struct SimWorker : SimShared
update(true); update(true);
register_output_step(10*cycle + 5); register_output_step(10*cycle + 5);
if (cycle == 0)
top->set_initstate_outputs(State::S0);
if (debug) if (debug)
log("\n===== %d =====\n", 10*cycle + 10); log("\n===== %d =====\n", 10*cycle + 10);
else if (verbose) else if (verbose)
@ -1953,7 +1963,7 @@ struct SimWorker : SimShared
if (yw.steps.empty()) { if (yw.steps.empty()) {
log_warning("Yosys witness file `%s` contains no time steps\n", yw.filename.c_str()); log_warning("Yosys witness file `%s` contains no time steps\n", yw.filename.c_str());
} else { } else {
top->set_initstate_outputs(State::S1); top->set_initstate_outputs(initstate ? State::S1 : State::S0);
set_yw_state(yw, hierarchy, 0); set_yw_state(yw, hierarchy, 0);
set_yw_clocks(yw, hierarchy, true); set_yw_clocks(yw, hierarchy, true);
initialize_stable_past(); initialize_stable_past();
@ -2546,6 +2556,9 @@ struct SimPass : public Pass {
log(" -n <integer>\n"); log(" -n <integer>\n");
log(" number of clock cycles to simulate (default: 20)\n"); log(" number of clock cycles to simulate (default: 20)\n");
log("\n"); log("\n");
log(" -noinitstate\n");
log(" do not activate $initstate cells during the first cycle\n");
log("\n");
log(" -a\n"); log(" -a\n");
log(" use all nets in VCD/FST operations, not just those with public names\n"); log(" use all nets in VCD/FST operations, not just those with public names\n");
log("\n"); log("\n");
@ -2646,6 +2659,10 @@ struct SimPass : public Pass {
worker.cycles_set = true; worker.cycles_set = true;
continue; continue;
} }
if (args[argidx] == "-noinitstate") {
worker.initstate = false;
continue;
}
if (args[argidx] == "-rstlen" && argidx+1 < args.size()) { if (args[argidx] == "-rstlen" && argidx+1 < args.size()) {
worker.rstlen = atoi(args[++argidx].c_str()); worker.rstlen = atoi(args[++argidx].c_str());
continue; continue;

File diff suppressed because it is too large Load diff

View file

@ -312,7 +312,7 @@ struct FlattenPass : public Pass {
for (auto cell : module->selected_cells()) { for (auto cell : module->selected_cells()) {
RTLIL::Module *tpl = design->module(cell->type); RTLIL::Module *tpl = design->module(cell->type);
if (tpl != nullptr) { if (tpl != nullptr) {
if (topo_modules.database.count(tpl) == 0) if (!topo_modules.has_node(tpl))
worklist.insert(tpl); worklist.insert(tpl);
topo_modules.edge(tpl, module); topo_modules.edge(tpl, module);
} }

View file

@ -36,7 +36,7 @@ void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) { for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_)); RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::Y, sig_y[i]); gate->setPort(ID::Y, sig_y[i]);
} }
@ -73,7 +73,7 @@ void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) { for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_b[i]); gate->setPort(ID::B, sig_b[i]);
gate->setPort(ID::Y, sig_y[i]); gate->setPort(ID::Y, sig_y[i]);
@ -124,7 +124,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
} }
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_a[i+1]); gate->setPort(ID::B, sig_a[i+1]);
gate->setPort(ID::Y, sig_t[i/2]); gate->setPort(ID::Y, sig_t[i/2]);
@ -137,7 +137,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell)
if (cell->type == ID($reduce_xnor)) { if (cell->type == ID($reduce_xnor)) {
RTLIL::SigSpec sig_t = module->addWire(NEW_ID); RTLIL::SigSpec sig_t = module->addWire(NEW_ID);
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_)); RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, sig_a); gate->setPort(ID::A, sig_a);
gate->setPort(ID::Y, sig_t); gate->setPort(ID::Y, sig_t);
last_output_cell = gate; last_output_cell = gate;
@ -165,7 +165,7 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell
} }
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_OR_)); RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_OR_));
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, sig[i]); gate->setPort(ID::A, sig[i]);
gate->setPort(ID::B, sig[i+1]); gate->setPort(ID::B, sig[i+1]);
gate->setPort(ID::Y, sig_t[i/2]); gate->setPort(ID::Y, sig_t[i/2]);
@ -194,7 +194,7 @@ void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell)
} }
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_)); RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_NOT_));
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, sig_a); gate->setPort(ID::A, sig_a);
gate->setPort(ID::Y, sig_y); gate->setPort(ID::Y, sig_y);
} }
@ -223,7 +223,7 @@ void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell)
log_assert(!gate_type.empty()); log_assert(!gate_type.empty());
RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type);
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, sig_a); gate->setPort(ID::A, sig_a);
gate->setPort(ID::B, sig_b); gate->setPort(ID::B, sig_b);
gate->setPort(ID::Y, sig_y); gate->setPort(ID::Y, sig_y);
@ -239,19 +239,19 @@ void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell)
RTLIL::SigSpec xor_out = module->addWire(NEW_ID, 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); RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed);
xor_cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); xor_cell->attributes[ID::src] = cell->attributes[ID::src];
simplemap_bitop(module, xor_cell); simplemap_bitop(module, xor_cell);
module->remove(xor_cell); module->remove(xor_cell);
RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID); RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID);
RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out); RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out);
reduce_cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); reduce_cell->attributes[ID::src] = cell->attributes[ID::src];
simplemap_reduce(module, reduce_cell); simplemap_reduce(module, reduce_cell);
module->remove(reduce_cell); module->remove(reduce_cell);
if (!is_ne) { if (!is_ne) {
RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y); RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y);
not_cell->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); not_cell->attributes[ID::src] = cell->attributes[ID::src];
simplemap_lognot(module, not_cell); simplemap_lognot(module, not_cell);
module->remove(not_cell); module->remove(not_cell);
} }
@ -265,7 +265,7 @@ void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) { for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_)); RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_b[i]); gate->setPort(ID::B, sig_b[i]);
gate->setPort(ID::S, cell->getPort(ID::S)); gate->setPort(ID::S, cell->getPort(ID::S));
@ -282,7 +282,7 @@ void simplemap_bwmux(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) { for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_)); RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::B, sig_b[i]); gate->setPort(ID::B, sig_b[i]);
gate->setPort(ID::S, sig_s[i]); gate->setPort(ID::S, sig_s[i]);
@ -298,7 +298,7 @@ void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(sig_y); i++) { for (int i = 0; i < GetSize(sig_y); i++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_TBUF_)); RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_TBUF_));
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, sig_a[i]); gate->setPort(ID::A, sig_a[i]);
gate->setPort(ID::E, sig_e); gate->setPort(ID::E, sig_e);
gate->setPort(ID::Y, sig_y[i]); gate->setPort(ID::Y, sig_y[i]);
@ -316,7 +316,7 @@ void simplemap_bmux(RTLIL::Module *module, RTLIL::Cell *cell)
for (int i = 0; i < GetSize(new_data); i += width) { for (int i = 0; i < GetSize(new_data); i += width) {
for (int k = 0; k < width; k++) { for (int k = 0; k < width; k++) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_)); RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, data[i*2+k]); gate->setPort(ID::A, data[i*2+k]);
gate->setPort(ID::B, data[i*2+width+k]); gate->setPort(ID::B, data[i*2+width+k]);
gate->setPort(ID::S, sel[idx]); gate->setPort(ID::S, sel[idx]);
@ -339,7 +339,7 @@ void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell)
SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2); SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2);
for (int i = 0; i < GetSize(lut_data); i += 2) { for (int i = 0; i < GetSize(lut_data); i += 2) {
RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_)); RTLIL::Cell *gate = module->addCell(NEW_ID, ID($_MUX_));
gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->attributes[ID::src] = cell->attributes[ID::src];
gate->setPort(ID::A, lut_data[i]); gate->setPort(ID::A, lut_data[i]);
gate->setPort(ID::B, lut_data[i+1]); gate->setPort(ID::B, lut_data[i+1]);
gate->setPort(ID::S, lut_ctrl[idx]); gate->setPort(ID::S, lut_ctrl[idx]);

View file

@ -197,7 +197,7 @@ module DFFE (output reg Q, input D, CLK, CE);
end end
endmodule // DFFE (positive clock edge; clock enable) endmodule // DFFE (positive clock edge; clock enable)
(* abc9_box, lib_whitebox *) (* abc9_flop, lib_whitebox *)
module DFFS (output reg Q, input D, CLK, SET); module DFFS (output reg Q, input D, CLK, SET);
parameter [0:0] INIT = 1'b1; parameter [0:0] INIT = 1'b1;
initial Q = INIT; initial Q = INIT;
@ -216,7 +216,7 @@ module DFFS (output reg Q, input D, CLK, SET);
end end
endmodule // DFFS (positive clock edge; synchronous set) endmodule // DFFS (positive clock edge; synchronous set)
(* abc9_box, lib_whitebox *) (* abc9_flop, lib_whitebox *)
module DFFSE (output reg Q, input D, CLK, CE, SET); module DFFSE (output reg Q, input D, CLK, CE, SET);
parameter [0:0] INIT = 1'b1; parameter [0:0] INIT = 1'b1;
initial Q = INIT; initial Q = INIT;
@ -282,7 +282,7 @@ module DFFP (output reg Q, input D, CLK, PRESET);
specify specify
(posedge CLK => (Q : D)) = (480, 660); (posedge CLK => (Q : D)) = (480, 660);
(posedge PRESET => (Q : 1'b1)) = (1800, 2679); (PRESET => Q) = (1800, 2679);
$setup(D, posedge CLK, 576); $setup(D, posedge CLK, 576);
endspecify endspecify
@ -301,7 +301,7 @@ module DFFPE (output reg Q, input D, CLK, CE, PRESET);
specify specify
if (CE) (posedge CLK => (Q : D)) = (480, 660); if (CE) (posedge CLK => (Q : D)) = (480, 660);
(posedge PRESET => (Q : 1'b1)) = (1800, 2679); (PRESET => Q) = (1800, 2679);
$setup(D, posedge CLK &&& CE, 576); $setup(D, posedge CLK &&& CE, 576);
$setup(CE, posedge CLK, 63); $setup(CE, posedge CLK, 63);
endspecify endspecify
@ -321,7 +321,7 @@ module DFFC (output reg Q, input D, CLK, CLEAR);
specify specify
(posedge CLK => (Q : D)) = (480, 660); (posedge CLK => (Q : D)) = (480, 660);
(posedge CLEAR => (Q : 1'b0)) = (1800, 2679); (CLEAR => Q) = (1800, 2679);
$setup(D, posedge CLK, 576); $setup(D, posedge CLK, 576);
endspecify endspecify
@ -340,7 +340,7 @@ module DFFCE (output reg Q, input D, CLK, CE, CLEAR);
specify specify
if (CE) (posedge CLK => (Q : D)) = (480, 660); if (CE) (posedge CLK => (Q : D)) = (480, 660);
(posedge CLEAR => (Q : 1'b0)) = (1800, 2679); (CLEAR => Q) = (1800, 2679);
$setup(D, posedge CLK &&& CE, 576); $setup(D, posedge CLK &&& CE, 576);
$setup(CE, posedge CLK, 63); $setup(CE, posedge CLK, 63);
endspecify endspecify
@ -384,7 +384,7 @@ module DFFNE (output reg Q, input D, CLK, CE);
end end
endmodule // DFFNE (negative clock edge; clock enable) endmodule // DFFNE (negative clock edge; clock enable)
(* abc9_box, lib_whitebox *) (* abc9_flop, lib_whitebox *)
module DFFNS (output reg Q, input D, CLK, SET); module DFFNS (output reg Q, input D, CLK, SET);
parameter [0:0] INIT = 1'b1; parameter [0:0] INIT = 1'b1;
initial Q = INIT; initial Q = INIT;
@ -403,7 +403,7 @@ module DFFNS (output reg Q, input D, CLK, SET);
end end
endmodule // DFFNS (negative clock edge; synchronous set) endmodule // DFFNS (negative clock edge; synchronous set)
(* abc9_box, lib_whitebox *) (* abc9_flop, lib_whitebox *)
module DFFNSE (output reg Q, input D, CLK, CE, SET); module DFFNSE (output reg Q, input D, CLK, CE, SET);
parameter [0:0] INIT = 1'b1; parameter [0:0] INIT = 1'b1;
initial Q = INIT; initial Q = INIT;
@ -469,7 +469,7 @@ module DFFNP (output reg Q, input D, CLK, PRESET);
specify specify
(negedge CLK => (Q : D)) = (480, 660); (negedge CLK => (Q : D)) = (480, 660);
(posedge PRESET => (Q : 1'b1)) = (1800, 2679); (PRESET => Q) = (1800, 2679);
$setup(D, negedge CLK, 576); $setup(D, negedge CLK, 576);
endspecify endspecify
@ -488,7 +488,7 @@ module DFFNPE (output reg Q, input D, CLK, CE, PRESET);
specify specify
if (CE) (negedge CLK => (Q : D)) = (480, 660); if (CE) (negedge CLK => (Q : D)) = (480, 660);
(posedge PRESET => (Q : 1'b1)) = (1800, 2679); (PRESET => Q) = (1800, 2679);
$setup(D, negedge CLK &&& CE, 576); $setup(D, negedge CLK &&& CE, 576);
$setup(CE, negedge CLK, 63); $setup(CE, negedge CLK, 63);
endspecify endspecify
@ -508,7 +508,7 @@ module DFFNC (output reg Q, input D, CLK, CLEAR);
specify specify
(negedge CLK => (Q : D)) = (480, 660); (negedge CLK => (Q : D)) = (480, 660);
(posedge CLEAR => (Q : 1'b0)) = (1800, 2679); (CLEAR => Q) = (1800, 2679);
$setup(D, negedge CLK, 576); $setup(D, negedge CLK, 576);
endspecify endspecify
@ -527,7 +527,7 @@ module DFFNCE (output reg Q, input D, CLK, CE, CLEAR);
specify specify
if (CE) (negedge CLK => (Q : D)) = (480, 660); if (CE) (negedge CLK => (Q : D)) = (480, 660);
(posedge CLEAR => (Q : 1'b0)) = (1800, 2679); (CLEAR => Q) = (1800, 2679);
$setup(D, negedge CLK &&& CE, 576); $setup(D, negedge CLK &&& CE, 576);
$setup(CE, negedge CLK, 63); $setup(CE, negedge CLK, 63);
endspecify endspecify
@ -957,7 +957,7 @@ end
endmodule endmodule
(* abc9_flop, lib_whitebox *)
module RAM16S1 (DO, DI, AD, WRE, CLK); module RAM16S1 (DO, DI, AD, WRE, CLK);
parameter INIT_0 = 16'h0000; parameter INIT_0 = 16'h0000;
@ -992,7 +992,7 @@ end
endmodule endmodule
(* abc9_flop, lib_whitebox *)
module RAM16S2 (DO, DI, AD, WRE, CLK); module RAM16S2 (DO, DI, AD, WRE, CLK);
parameter INIT_0 = 16'h0000; parameter INIT_0 = 16'h0000;
@ -1031,7 +1031,7 @@ end
endmodule endmodule
(* abc9_flop, lib_whitebox *)
module RAM16S4 (DO, DI, AD, WRE, CLK); module RAM16S4 (DO, DI, AD, WRE, CLK);
parameter INIT_0 = 16'h0000; parameter INIT_0 = 16'h0000;

View file

@ -7,14 +7,27 @@ hierarchy -top my_dff
proc proc
equiv_opt -async2sync -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v synth_quicklogic # equivalency check equiv_opt -async2sync -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v synth_quicklogic # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd dff # Constrain all select calls below inside the top module cd my_dff # Constrain all select calls below inside the top module
select -assert-none t:* select -assert-count 1 t:ckpad
select -assert-count 1 t:dffepc
select -assert-count 1 t:inpad
select -assert-count 1 t:logic_0
select -assert-count 1 t:logic_1
select -assert-count 1 t:outpad
select -assert-none t:ckpad t:dffepc t:inpad t:logic_0 t:logic_1 t:outpad %% t:* %D
design -load read design -load read
hierarchy -top my_dffe hierarchy -top my_dffe
proc proc
equiv_opt -async2sync -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v synth_quicklogic # equivalency check equiv_opt -async2sync -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v synth_quicklogic # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd dffe # Constrain all select calls below inside the top module cd my_dffe # Constrain all select calls below inside the top module
select -assert-none t:* select -assert-count 1 t:ckpad
select -assert-count 1 t:dffepc
select -assert-count 2 t:inpad
select -assert-count 1 t:logic_0
select -assert-count 1 t:outpad
select -assert-none t:ckpad t:dffepc t:inpad t:logic_0 t:outpad %% t:* %D

View file

@ -8,9 +8,11 @@ module top (
$changed(b) $changed(b)
); );
wire x = 'x;
`ifndef FAIL `ifndef FAIL
assume property ( assume property (
b !== 'x ##1 $changed(b) b !== x ##1 $changed(b)
); );
`endif `endif

View file

@ -1 +1,15 @@
test_cell -s 1694091355 -n 1000 -script booth_map_script.ys_ $mul read_verilog <<EOF
module test(clk, a, b, y);
input wire clk;
input wire [9:0] a;
input wire [6:0] b;
output wire [20:0] y;
assign y = a * b;
endmodule
EOF
booth
sat -verify -set a 0 -set b 0 -prove y 0
design -reset
test_cell -s 1694091355 -n 100 -script booth_map_script.ys_ $mul

View file

@ -0,0 +1,94 @@
verific -sv <<EOF
module top(clk);
input wire clk;
parameter DEPTH_LOG2 = 4;
parameter DEPTH = 2**DEPTH_LOG2;
parameter BYTEWIDTH = 8;
parameter WIDTH = BYTEWIDTH*4;
parameter PRIME1 = 237481091;
parameter PRIME2 = 296851369;
(* ram_style = "block" *)
reg [WIDTH-1:0] mem [DEPTH-1:0];
integer i;
initial begin
for (i = 0; i < DEPTH; i = i + 1) begin
// Make up data by multiplying a large prime with the address,
// then cropping and retaining the lower bits
mem[i] = PRIME1 * i;
end
end
reg [DEPTH_LOG2-1:0] counter = 0;
reg done = 1'b0;
always @(posedge clk) begin
if (!done)
counter = counter + 1;
if (counter == 0)
done = 1;
end
wire [WIDTH-1:0] old_data = PRIME1 * counter;
wire [WIDTH-1:0] new_data = PRIME2 * counter;
reg [WIDTH-1:0] expect_old_data;
reg [WIDTH-1:0] expect_mixed_data;
always @(posedge clk) begin
if (!done) begin
expect_old_data <= mem[counter];
mem[counter][31:24] <= new_data[31:24];
mem[counter][23:16] = new_data[23:16]; // !!! is blocking
mem[counter][15:8] <= new_data[15:8];
mem[counter][7:0] <= new_data[7:0];
expect_mixed_data <= mem[counter];
end
end
reg done_delay1 = 1'b1;
reg [WIDTH-1:0] new_data_delay1 = 1'b1;
reg [WIDTH-1:0] old_data_delay1 = 1'b1;
always @(posedge clk) begin
if (!done_delay1) begin
assert(expect_old_data == old_data_delay1);
assert(expect_mixed_data[31:24] == old_data_delay1[31:24]);
assert(expect_mixed_data[23:16] == new_data_delay1[23:16]);
assert(expect_mixed_data[15:0] == old_data_delay1[15:0]);
end
end
reg [DEPTH_LOG2-1:0] counter_delay1;
always @(posedge clk) begin
counter_delay1 <= counter;
done_delay1 <= done;
new_data_delay1 <= new_data;
old_data_delay1 <= old_data;
end
reg [DEPTH_LOG2-1:0] counter_delay2;
reg done_delay2 = 1'b1;
reg [WIDTH-1:0] new_data_delay2 = 1'b1;
always @(posedge clk) begin
counter_delay2 <= counter_delay1;
done_delay2 <= done_delay1;
new_data_delay2 <= new_data_delay1;
end
always @(posedge clk) begin
if (!done_delay2)
assert(mem[counter_delay2] == new_data_delay2);
end
endmodule
EOF
hierarchy -top top
proc
opt_clean
memory -nomap
select -assert-count 1 t:$mem_v2
sim -assert -clock clk -n 20

View file

@ -0,0 +1,73 @@
module top;
integer x, y, z;
task check;
input integer a, b, c;
assert (x == a);
assert (y == b);
assert (z == c);
endtask
always_comb begin
x = 0; y = 0; z = 0;
check(0, 0, 0);
// post-increment/decrement statements
x++;
check(1, 0, 0);
(* bar *) y (* foo *) ++;
check(1, 1, 0);
z--;
check(1, 1, -1);
(* bar *) z (* foo *) --;
check(1, 1, -2);
// pre-increment/decrement statements are equivalent
++z;
check(1, 1, -1);
(* bar *) ++ (* foo *) z;
check(1, 1, 0);
--x;
check(0, 1, 0);
(* bar *) -- (* foo *) y;
check(0, 0, 0);
// procedural pre-increment/decrement expressions
z = ++x;
check(1, 0, 1);
z = ++ (* foo *) x;
check(2, 0, 2);
y = --x;
check(1, 1, 2);
y = -- (* foo *) x;
// procedural post-increment/decrement expressions
// TODO: support attributes on post-increment/decrement
check(0, 0, 2);
y = x++;
check(1, 0, 2);
y = x--;
check(0, 1, 2);
// procedural assignment expressions
x = (y = (z = 99) + 1) + 1;
check(101, 100, 99);
x = (y *= 2);
check(200, 200, 99);
x = (z >>= 2) * 4;
check(96, 200, 24);
y = (z >>= 1'sb1) * 2; // shift is implicitly cast to unsigned
check(96, 24, 12);
// check width of post-increment expressions
z = (y = 0);
begin
byte w;
w = 0;
x = {1'b1, ++w};
check(257, 0, 0);
assert (w == 1);
x = {2'b10, w++};
check(513, 0, 0);
assert (w == 2);
end
end
endmodule

View file

@ -0,0 +1,3 @@
read_verilog -sv asgn_expr.sv
proc
sat -verify -prove-asserts -show-all

View file

@ -0,0 +1,7 @@
logger -expect error "Assignments within expressions are only permitted within procedures." 1
read_verilog -sv <<EOF
module top;
integer x, y;
assign x = y++;
endmodule
EOF

View file

@ -0,0 +1,7 @@
logger -expect error "Assignments within expressions are only permitted within procedures." 1
read_verilog -sv <<EOF
module top;
integer x;
wire [++x:0] y;
endmodule
EOF

View file

@ -0,0 +1,7 @@
logger -expect error "Assignments within expressions are only permitted within procedures." 1
read_verilog -sv <<EOF
module top;
integer x;
integer y = --x;
endmodule
EOF

View file

@ -0,0 +1,7 @@
logger -expect error "Assignments within expressions are only permitted within procedures." 1
read_verilog -sv <<EOF
module top;
integer x, y;
assign x = (y = 1);
endmodule
EOF

View file

@ -0,0 +1,7 @@
logger -expect error "Assignments within expressions are only permitted within procedures." 1
read_verilog -sv <<EOF
module top;
integer x, y;
assign x = (y += 2);
endmodule
EOF

View file

@ -0,0 +1,7 @@
logger -expect error "Assignments within expressions are only supported in SystemVerilog mode." 1
read_verilog <<EOF
module top;
integer x, y;
initial y = ++x;
endmodule
EOF

View file

@ -0,0 +1,7 @@
logger -expect error "Assignments within expressions are only supported in SystemVerilog mode." 1
read_verilog <<EOF
module top;
integer x, y;
initial y = x++;
endmodule
EOF

View file

@ -0,0 +1,7 @@
logger -expect error "Assignments within expressions are only supported in SystemVerilog mode." 1
read_verilog <<EOF
module top;
integer x, y;
initial y = (x = 1);
endmodule
EOF

View file

@ -0,0 +1,15 @@
read_verilog -sv <<EOF
module top;
integer x, y;
initial y = (x += 1);
endmodule
EOF
design -reset
logger -expect error "syntax error, unexpected TOK_ID" 1
read_verilog <<EOF
module top;
integer x, y;
initial y = (x += 1);
endmodule
EOF

View file

@ -15,7 +15,7 @@ EOT
select -assert-none a:* a:src %d select -assert-none a:* a:src %d
logger -expect error "syntax error, unexpected ATTR_BEGIN" 1 logger -expect error "syntax error, unexpected ';', expecting ATTR_BEGIN or TOK_INCREMENT or TOK_DECREMENT" 1
design -reset design -reset
read_verilog <<EOT read_verilog <<EOT
module top; module top;