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
*.gcda
*.gcno
*~
__pycache__
/.cproject
/.project

View File

@ -2,9 +2,33 @@
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
--------------------------
* Various

View File

@ -141,7 +141,7 @@ LDLIBS += -lrt
endif
endif
YOSYS_VER := 0.33+34
YOSYS_VER := 0.34+55
# Note: We arrange for .gitcommit to contain the (short) commit hash in
# tarballs generated with git-archive(1) using .gitattributes. The git repo
@ -157,7 +157,7 @@ endif
OBJS = kernel/version_$(GIT_REV).o
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
#
@ -165,7 +165,7 @@ bumpversion:
# is just a symlink to your actual ABC working directory, as 'make mrproper'
# will remove the 'abc' directory and you do not want to accidentally
# delete your work on ABC..
ABCREV = daad9ed
ABCREV = 896e5e7
ABCPULL = 1
ABCURL ?= https://github.com/YosysHQ/abc
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
LDLIBS := $(filter-out -lrt,$(LDLIBS))
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
else ifeq ($(CONFIG),msys2-64)
@ -368,7 +368,7 @@ CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
LDLIBS := $(filter-out -lrt,$(LDLIBS))
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
else ifneq ($(CONFIG),none)

View File

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

View File

@ -1019,14 +1019,14 @@ struct metadata {
// In debug mode, using the wrong .as_*() function will assert.
// In release mode, using the wrong .as_*() function will safely return a default value.
const unsigned uint_value = 0;
const signed sint_value = 0;
const uint64_t uint_value = 0;
const int64_t sint_value = 0;
const std::string string_value = "";
const double double_value = 0.0;
metadata() : value_type(MISSING) {}
metadata(unsigned value) : value_type(UINT), uint_value(value) {}
metadata(signed value) : value_type(SINT), sint_value(value) {}
metadata(uint64_t value) : value_type(UINT), uint_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 char *value) : value_type(STRING), string_value(value) {}
metadata(double value) : value_type(DOUBLE), double_value(value) {}
@ -1034,12 +1034,12 @@ struct metadata {
metadata(const metadata &) = default;
metadata &operator=(const metadata &) = delete;
unsigned as_uint() const {
uint64_t as_uint() const {
assert(value_type == UINT);
return uint_value;
}
signed as_sint() const {
int64_t as_sint() const {
assert(value_type == SINT);
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
// 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 {
// Object types.
enum : uint32_t {
@ -1103,6 +1106,7 @@ struct debug_item : ::cxxrtl_object {
curr = item.data;
next = item.data;
outline = nullptr;
attrs = nullptr;
}
template<size_t Bits>
@ -1118,6 +1122,7 @@ struct debug_item : ::cxxrtl_object {
curr = const_cast<chunk_t*>(item.data);
next = nullptr;
outline = nullptr;
attrs = nullptr;
}
template<size_t Bits>
@ -1134,6 +1139,7 @@ struct debug_item : ::cxxrtl_object {
curr = item.curr.data;
next = item.next.data;
outline = nullptr;
attrs = nullptr;
}
template<size_t Width>
@ -1149,6 +1155,7 @@ struct debug_item : ::cxxrtl_object {
curr = item.data ? item.data[0].data : nullptr;
next = nullptr;
outline = nullptr;
attrs = nullptr;
}
template<size_t Bits>
@ -1164,6 +1171,7 @@ struct debug_item : ::cxxrtl_object {
curr = const_cast<chunk_t*>(item.data);
next = nullptr;
outline = nullptr;
attrs = nullptr;
}
template<size_t Bits>
@ -1180,6 +1188,7 @@ struct debug_item : ::cxxrtl_object {
curr = const_cast<chunk_t*>(item.curr.data);
next = nullptr;
outline = nullptr;
attrs = nullptr;
}
template<size_t Bits>
@ -1195,6 +1204,7 @@ struct debug_item : ::cxxrtl_object {
curr = const_cast<chunk_t*>(item.data);
next = nullptr;
outline = &group;
attrs = nullptr;
}
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");
} // 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 {
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];
parts.emplace_back(item);
std::sort(parts.begin(), parts.end(),
@ -1246,6 +1274,10 @@ struct debug_items {
const debug_item &operator [](const std::string &name) const {
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(),

View File

@ -2120,6 +2120,46 @@ struct CxxrtlWorker {
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)
{
size_t count_public_wires = 0;
@ -2205,7 +2245,9 @@ struct CxxrtlWorker {
}
f << "debug_item::" << flag;
}
f << "));\n";
f << "), ";
dump_debug_attrs(wire);
f << ");\n";
count_member_wires++;
break;
}
@ -2220,7 +2262,9 @@ struct CxxrtlWorker {
f << "debug_eval_outline";
else
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++;
break;
}
@ -2230,14 +2274,18 @@ struct CxxrtlWorker {
dump_const(debug_wire_type.sig_subst.as_const());
f << ";\n";
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++;
break;
}
case WireType::OUTLINE: {
// Localized or inlined, but rematerializable 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++;
break;
}
@ -2254,7 +2302,13 @@ struct CxxrtlWorker {
continue;
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 << 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()) {
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)
{
dump_attrs(module);

View File

@ -90,3 +90,46 @@ void cxxrtl_enum(cxxrtl_handle handle, void *data,
void cxxrtl_outline_eval(cxxrtl_outline outline) {
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.
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.
};
@ -304,6 +313,62 @@ typedef struct _cxxrtl_outline *cxxrtl_outline;
// re-evaluated, otherwise the bits read from that object are meaningless.
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
}
#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);
}
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());
}

View File

@ -229,6 +229,10 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
id2ast = NULL;
basic_prep = false;
lookahead = false;
in_lvalue_from_above = false;
in_param_from_above = false;
in_lvalue = false;
in_param = false;
if (child1)
children.push_back(child1);
@ -238,6 +242,8 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
children.push_back(child3);
if (child4)
children.push_back(child4);
fixup_hierarchy_flags();
}
// create a (deep recursive) copy of a node
@ -249,6 +255,10 @@ AstNode *AstNode::clone() const
it = it->clone();
for (auto &it : that->attributes)
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;
}
@ -256,10 +266,13 @@ AstNode *AstNode::clone() const
void AstNode::cloneInto(AstNode *other) const
{
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 = *tmp;
tmp->children.clear();
tmp->attributes.clear();
other->fixup_hierarchy_flags();
delete tmp;
}
@ -351,6 +364,10 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
if (is_enum) {
fprintf(f, " type=enum");
}
if (in_lvalue)
fprintf(f, " in_lvalue");
if (in_param)
fprintf(f, " in_param");
fprintf(f, "\n");
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
// for lookup up ports and wires within cells
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);
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);
}
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);
}
}
@ -1150,7 +1167,7 @@ static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool d
ast->children.swap(new_children);
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_autowire = autowire;
ast->fixup_hierarchy_flags(true);
log_assert(current_ast->type == AST_DESIGN);
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) {
// process enum/other declarations
child->simplify(true, false, 1, -1, false, false);
child->simplify(true, 1, -1, false);
rename_in_package_stmts(child);
design->verilog_packages.push_back(child->clone());
current_scope.clear();
@ -1748,7 +1767,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
AstNode *new_ast = ast->clone();
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;
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->fixup_hierarchy_flags(true);
(*new_ast_out) = new_ast;
return modname;
}

View File

@ -221,6 +221,13 @@ namespace AST
std::string filename;
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
AstNode(AstNodeType type = AST_NONE, AstNode *child1 = nullptr, AstNode *child2 = nullptr, AstNode *child3 = nullptr, AstNode *child4 = nullptr);
AstNode *clone() const;
@ -251,7 +258,7 @@ namespace AST
// 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()
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);
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);
@ -343,6 +350,24 @@ namespace AST
// to evaluate widths of dynamic ranges)
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
[[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);
for (auto c : node->id2ast->children)
wire->children.push_back(c->clone());
wire->fixup_hierarchy_flags();
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;
while (wire->simplify(true, false, 1, -1, false, false)) { }
while (wire->simplify(true, 1, -1, false)) { }
current_ast_mod->children.push_back(wire);
lookaheadids[node->str] = make_pair(node->id2ast, wire);
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;
} else {
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)
this_width = id_ast->children[0]->bits.size();
else
@ -970,8 +971,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
else if (!range->range_valid) {
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();
while (left_at_zero_ast->simplify(true, false, 1, -1, false, false)) { }
while (right_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, 1, -1, false)) { }
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());
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;
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)
input_error("Left operand of tobits expression is not constant!\n");
children[1]->detectSignWidthWorker(sub_width_hint, sign_hint);
@ -1009,7 +1010,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
break;
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)
input_error("Static cast with non constant expression!\n");
children.at(1)->detectSignWidthWorker(width_hint, sign_hint);
@ -1031,7 +1032,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
break;
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)
input_error("Left operand of replicate expression is not constant!\n");
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:
// Prefix nodes always resolve to identifiers in generate loops, so we
// 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);
detectSignWidthWorker(width_hint, sign_hint, found_real);
break;
@ -1151,7 +1152,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
case AST_FCALL:
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") {
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)
input_error("System function %s called with non-const argument!\n",
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);
AstNode *left = range->children.at(0)->clone();
AstNode *right = range->children.at(1)->clone();
while (left->simplify(true, false, 1, -1, false, true)) { }
while (right->simplify(true, false, 1, -1, false, true)) { }
left->set_in_param_flag(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)
input_error("Function %s has non-constant width!",
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) {
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();
while (left_at_zero_ast->simplify(true, false, 1, -1, false, false)) { }
while (right_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, 1, -1, false)) { }
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());
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());
fake_ast->children[0]->delete_children();
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;
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
#include "vhdl_file.h"
#include "VhdlUnits.h"
#include "NameSpace.h"
#endif
#ifdef VERIFIC_EDIF_SUPPORT
@ -265,7 +266,7 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
Att *attr;
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) {
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));
}
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;
MapIter mi, mi2;
MapIter mi2;
Port *port;
PortBus *portbus;
Net *net;
@ -2488,51 +2504,71 @@ std::string verific_import(Design *design, const std::map<std::string,std::strin
for (const auto &i : parameters)
verific_params.Insert(i.first.c_str(), i.second.c_str());
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
VerificExtensions::ElaborateAndRewrite("work", &verific_params);
verific_error_msg.clear();
#endif
if (top.empty()) {
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
VerificExtensions::ElaborateAndRewrite("work", &verific_params);
verific_error_msg.clear();
#endif
netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params);
}
else {
Array veri_modules, vhdl_units;
if (veri_lib) {
VeriModule *veri_module = veri_lib->GetModule(top.c_str(), 1);
if (veri_module) {
veri_modules.InsertLast(veri_module);
if (veri_module->IsConfiguration()) {
VeriConfiguration *cfg = (VeriConfiguration*)veri_module;
VeriName *module_name = (VeriName*)cfg->GetTopModuleNames()->GetLast();
VeriLibrary *lib = veri_module->GetLibrary() ;
if (module_name && module_name->IsHierName()) {
VeriName *prefix = module_name->GetPrefix() ;
const char *lib_name = (prefix) ? prefix->GetName() : 0 ;
if (!Strings::compare("work", lib_name)) lib = veri_file::GetLibrary(lib_name, 1) ;
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
for (int static_elaborate = 1; static_elaborate >= 0; static_elaborate--)
#endif
{
Array veri_modules, vhdl_units;
if (veri_lib) {
VeriModule *veri_module = veri_lib->GetModule(top.c_str(), 1);
if (veri_module) {
veri_modules.InsertLast(veri_module);
if (veri_module->IsConfiguration()) {
VeriConfiguration *cfg = (VeriConfiguration*)veri_module;
VeriName *module_name = (VeriName*)cfg->GetTopModuleNames()->GetLast();
VeriLibrary *lib = veri_module->GetLibrary() ;
if (module_name && module_name->IsHierName()) {
VeriName *prefix = module_name->GetPrefix() ;
const char *lib_name = (prefix) ? prefix->GetName() : 0 ;
if (!Strings::compare("work", lib_name)) lib = veri_file::GetLibrary(lib_name, 1) ;
}
if (lib && module_name)
top = lib->GetModule(module_name->GetName(), 1)->GetName();
}
}
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
if (!static_elaborate)
#endif
{
// Also elaborate all root modules since they may contain bind statements
MapIter mi;
FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) {
if (!veri_module->IsRootModule()) continue;
veri_modules.InsertLast(veri_module);
}
if (lib && module_name)
top = lib->GetModule(module_name->GetName(), 1)->GetName();
}
}
// Also elaborate all root modules since they may contain bind statements
MapIter mi;
FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) {
if (!veri_module->IsRootModule()) continue;
veri_modules.InsertLast(veri_module);
}
}
#ifdef VERIFIC_VHDL_SUPPORT
if (vhdl_lib) {
VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(top.c_str());
if (vhdl_unit)
vhdl_units.InsertLast(vhdl_unit);
}
if (vhdl_lib) {
VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(top.c_str());
if (vhdl_unit)
vhdl_units.InsertLast(vhdl_unit);
}
#endif
netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &verific_params);
#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);
}
}
Netlist *nl;
@ -2545,7 +2581,7 @@ std::string verific_import(Design *design, const std::map<std::string,std::strin
continue;
nl->AddAtt(new Att(" \\top", NULL));
nl_todo.emplace(nl->CellBaseName(), nl);
cell_name = nl->Owner()->Name();
cell_name = nl->CellBaseName();
}
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) {
VerificImporter importer(false, false, false, false, false, false, false);
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);
}
@ -3056,6 +3092,7 @@ struct VerificPass : public Pass {
int argidx = 1;
std::string work = "work";
bool is_work_set = false;
(void)is_work_set;
veri_file::RegisterCallBackVerificStream(&verific_read_cb);
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::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++)
{
if (args[i] == "-work" && i+1 < GetSize(args)) {
@ -3141,7 +3191,7 @@ struct VerificPass : public Pass {
continue;
}
if (args[i] == "-L" && i+1 < GetSize(args)) {
if (args[++i] == "work")
if (args[++i] == work)
veri_file::RemoveAllLOptions();
continue;
}
@ -3574,15 +3624,16 @@ struct VerificPass : public Pass {
std::set<std::string> top_mod_names;
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
VerificExtensions::ElaborateAndRewrite(work, &parameters);
verific_error_msg.clear();
#endif
if (!ppfile.empty())
veri_file::PrettyPrint(ppfile.c_str(), nullptr, work.c_str());
if (mode_all)
{
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
VerificExtensions::ElaborateAndRewrite(work, &parameters);
verific_error_msg.clear();
#endif
if (!ppfile.empty())
veri_file::PrettyPrint(ppfile.c_str(), nullptr, work.c_str());
log("Running hier_tree::ElaborateAll().\n");
VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1);
@ -3607,67 +3658,93 @@ struct VerificPass : public Pass {
if (argidx == GetSize(args))
cmd_error(args, argidx, "No top module specified.\n");
VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
#ifdef VERIFIC_VHDL_SUPPORT
VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
#endif
Array *netlists = nullptr;
Array veri_modules, vhdl_units;
for (; argidx < GetSize(args); argidx++)
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
for (int static_elaborate = 1; static_elaborate >= 0; static_elaborate--)
#endif
{
const char *name = args[argidx].c_str();
top_mod_names.insert(name);
VeriModule *veri_module = veri_lib ? veri_lib->GetModule(name, 1) : nullptr;
if (veri_module) {
if (veri_module->IsConfiguration()) {
log("Adding Verilog configuration '%s' to elaboration queue.\n", name);
veri_modules.InsertLast(veri_module);
top_mod_names.erase(name);
VeriConfiguration *cfg = (VeriConfiguration*)veri_module;
VeriName *module_name;
int i;
FOREACH_ARRAY_ITEM(cfg->GetTopModuleNames(), i, module_name) {
VeriLibrary *lib = veri_module->GetLibrary() ;
if (module_name && module_name->IsHierName()) {
VeriName *prefix = module_name->GetPrefix() ;
const char *lib_name = (prefix) ? prefix->GetName() : 0 ;
if (!Strings::compare("work", lib_name)) lib = veri_file::GetLibrary(lib_name, 1) ;
}
if (lib && module_name)
top_mod_names.insert(lib->GetModule(module_name->GetName(), 1)->GetName());
}
} else {
log("Adding Verilog module '%s' to elaboration queue.\n", name);
veri_modules.InsertLast(veri_module);
}
continue;
}
VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
#ifdef VERIFIC_VHDL_SUPPORT
VhdlDesignUnit *vhdl_unit = vhdl_lib ? vhdl_lib->GetPrimUnit(name) : nullptr;
if (vhdl_unit) {
log("Adding VHDL unit '%s' to elaboration queue.\n", name);
vhdl_units.InsertLast(vhdl_unit);
VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
#endif
Array veri_modules, vhdl_units;
for (int i = argidx; i < GetSize(args); i++)
{
const char *name = args[i].c_str();
top_mod_names.insert(name);
VeriModule *veri_module = veri_lib ? veri_lib->GetModule(name, 1) : nullptr;
if (veri_module) {
if (veri_module->IsConfiguration()) {
log("Adding Verilog configuration '%s' to elaboration queue.\n", name);
veri_modules.InsertLast(veri_module);
top_mod_names.erase(name);
VeriConfiguration *cfg = (VeriConfiguration*)veri_module;
VeriName *module_name;
int i;
FOREACH_ARRAY_ITEM(cfg->GetTopModuleNames(), i, module_name) {
VeriLibrary *lib = veri_module->GetLibrary() ;
if (module_name && module_name->IsHierName()) {
VeriName *prefix = module_name->GetPrefix() ;
const char *lib_name = (prefix) ? prefix->GetName() : 0 ;
if (work != lib_name) lib = veri_file::GetLibrary(lib_name, 1) ;
}
if (lib && module_name)
top_mod_names.insert(lib->GetModule(module_name->GetName(), 1)->GetName());
}
} else {
log("Adding Verilog module '%s' to elaboration queue.\n", name);
veri_modules.InsertLast(veri_module);
}
continue;
}
#ifdef VERIFIC_VHDL_SUPPORT
VhdlDesignUnit *vhdl_unit = vhdl_lib ? vhdl_lib->GetPrimUnit(name) : nullptr;
if (vhdl_unit) {
log("Adding VHDL unit '%s' to elaboration queue.\n", name);
vhdl_units.InsertLast(vhdl_unit);
continue;
}
#endif
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
log_error("Can't find module/unit '%s'.\n", name);
}
if (veri_lib) {
// Also elaborate all root modules since they may contain bind statements
MapIter mi;
VeriModule *veri_module;
FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) {
if (!veri_module->IsRootModule()) continue;
veri_modules.InsertLast(veri_module);
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) {
// Also elaborate all root modules since they may contain bind statements
MapIter mi;
VeriModule *veri_module;
FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) {
if (!veri_module->IsRootModule()) continue;
veri_modules.InsertLast(veri_module);
}
}
}
log("Running hier_tree::Elaborate().\n");
netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &parameters);
}
log("Running hier_tree::Elaborate().\n");
Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &parameters);
Netlist *nl;
int i;
@ -3732,7 +3809,7 @@ struct VerificPass : public Pass {
VerificImporter importer(mode_gates, mode_keep, mode_nosva,
mode_names, mode_verific, mode_autocover, mode_fullinit);
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);
}

View File

@ -292,6 +292,65 @@ static void rewriteGenForDeclInit(AstNode *loop)
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}
@ -358,7 +417,7 @@ static void rewriteGenForDeclInit(AstNode *loop)
%type <integer> integer_atom_type integer_vector_type
%type <al> attr case_attr
%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 <specify_target_ptr> specify_target
@ -2610,17 +2669,11 @@ simple_behavioral_stmt:
SET_AST_NODE_LOC(node, @2, @5);
append_attr(node, $1);
} |
attr lvalue TOK_INCREMENT {
AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, new AstNode(AST_ADD, $2->clone(), AstNode::mkconst_int(1, true)));
ast_stack.back()->children.push_back(node);
SET_AST_NODE_LOC(node, @2, @3);
append_attr(node, $1);
attr lvalue attr inc_or_dec_op {
addIncOrDecStmt($1, $2, $3, $4, @1, @4);
} |
attr lvalue TOK_DECREMENT {
AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, new AstNode(AST_SUB, $2->clone(), AstNode::mkconst_int(1, true)));
ast_stack.back()->children.push_back(node);
SET_AST_NODE_LOC(node, @2, @3);
append_attr(node, $1);
attr inc_or_dec_op attr lvalue {
addIncOrDecStmt($1, $4, $3, $2, @1, @4);
} |
attr lvalue OP_LE delay expr {
AstNode *node = new AstNode(AST_ASSIGN_LE, $2, $5);
@ -2629,18 +2682,7 @@ simple_behavioral_stmt:
append_attr(node, $1);
} |
attr lvalue asgn_binop delay expr {
AstNode *expr_node = $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);
addAsgnBinopStmt($1, $2, $3, $5, @2, @5);
};
asgn_binop:
@ -2657,6 +2699,12 @@ asgn_binop:
TOK_SSHL_ASSIGN { $$ = AST_SHIFT_SLEFT; } |
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:
TOK_ID '=' expr {
AstNode *ident = new AstNode(AST_IDENTIFIER);
@ -3149,6 +3197,14 @@ expr:
$$->children.push_back($6);
SET_AST_NODE_LOC($$, @1, @$);
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:
@ -3436,6 +3492,17 @@ basic_expr:
frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode.");
$$ = new AstNode(AST_CAST_SIZE, $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:

View File

@ -90,6 +90,12 @@ template<> struct hash_ops<uint32_t> : hash_int_ops
return a;
}
};
template<> struct hash_ops<uint64_t> : hash_int_ops
{
static inline unsigned int hash(uint64_t a) {
return mkhash((unsigned int)(a), (unsigned int)(a >> 32));
}
};
template<> struct hash_ops<std::string> {
static inline bool cmp(const std::string &a, const std::string &b) {
@ -988,7 +994,7 @@ public:
return !operator==(other);
}
bool hash() const {
unsigned int hash() const {
unsigned int hashval = mkhash_init;
for (auto &it : entries)
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 string;
string.reserve(GetSize(bits)/8);
for (int i = 0; i < GetSize(bits); i += 8) {
const int n = GetSize(bits);
const int n_over_8 = n / 8;
std::string s;
s.reserve(n_over_8);
int i = n_over_8 * 8;
if (i < n) {
char ch = 0;
for (int j = 0; j < 8 && i + j < int (bits.size()); j++)
if (bits[i + j] == RTLIL::State::S1)
for (int j = 0; j < (n - i); j++) {
if (bits[i + j] == RTLIL::State::S1) {
ch |= 1 << j;
}
}
if (ch != 0)
string.append({ch});
s.append({ch});
}
std::reverse(string.begin(), string.end());
return string;
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
@ -2677,6 +2692,19 @@ RTLIL::Cell* RTLIL::Module::addPow(RTLIL::IdString name, const RTLIL::SigSpec &s
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 *cell = addCell(name, ID($slice));
@ -4031,13 +4059,17 @@ void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec
unpack();
other->unpack();
dict<RTLIL::SigBit, int> pattern_to_with;
for (int i = 0; i < GetSize(pattern.bits_); i++) {
if (pattern.bits_[i].wire != NULL) {
for (int j = 0; j < GetSize(bits_); j++) {
if (bits_[j] == pattern.bits_[i]) {
other->bits_[j] = with.bits_[i];
}
}
pattern_to_with.emplace(pattern.bits_[i], i);
}
}
for (int j = 0; j < GetSize(bits_); j++) {
auto it = pattern_to_with.find(bits_[j]);
if (it != pattern_to_with.end()) {
other->bits_[j] = with.bits_[it->second];
}
}

View File

@ -308,10 +308,14 @@ namespace RTLIL
bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; }
char operator[](size_t i) const {
const char *p = c_str();
const char *p = c_str();
#ifndef NDEBUG
for (; i != 0; i--, p++)
log_assert(*p != 0);
return *p;
#else
return *(p + i);
#endif
}
std::string substr(size_t pos = 0, size_t len = std::string::npos) const {
@ -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* 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* 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 = "");

View File

@ -128,41 +128,103 @@ public:
// A simple class for topological sorting
// ------------------------------------------------
template<typename T, typename C = std::less<T>>
struct TopoSort
template <typename T, typename C = std::less<T>, typename OPS = hash_ops<T>> class TopoSort
{
bool analyze_loops, found_loops;
std::map<T, std::set<T, C>, C> database;
std::set<std::set<T, C>> loops;
std::vector<T> sorted;
public:
// We use this ordering of the edges in the adjacency matrix for
// exact compatibility with an older implementation.
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;
found_loops = false;
}
void node(T n)
int node(T n)
{
if (database.count(n) == 0)
database[n] = std::set<T, C>();
auto rv = node_to_index.emplace(n, static_cast<int>(nodes.size()));
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);
database[right].insert(left);
log_assert(GetSize(node_to_index) == GetSize(edges));
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;
if (analyze_loops) {
std::set<T, C> loop;
for (int i = GetSize(active_stack)-1; i >= 0; i--) {
loop.insert(active_stack[i]);
if (active_stack[i] == n)
for (int i = GetSize(active_stack) - 1; i >= 0; i--) {
const int index = active_stack[i];
loop.insert(nodes[index]);
if (index == root_index)
break;
}
loops.insert(loop);
@ -170,42 +232,24 @@ struct TopoSort
return;
}
if (marked_cells.count(n))
if (marked_cells[root_index])
return;
if (!database.at(n).empty())
{
if (!edges[root_index].empty()) {
if (analyze_loops)
active_stack.push_back(n);
active_cells.insert(n);
active_stack.push_back(root_index);
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);
if (analyze_loops)
active_stack.pop_back();
active_cells.erase(n);
active_cells[root_index] = false;
}
marked_cells.insert(n);
sorted.push_back(n);
}
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;
marked_cells[root_index] = true;
sorted.push_back(nodes[root_index]);
}
};

View File

@ -175,48 +175,6 @@ int ceil_log2(int x)
#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 rc = int(f.readsome(s, n));
@ -1395,8 +1353,12 @@ void shell(RTLIL::Design *design)
if ((command = fgets(command_buffer, 4096, stdin)) == NULL)
break;
#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;
}
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
add_history(command);
#endif
@ -1418,10 +1380,17 @@ void shell(RTLIL::Design *design)
log_reset_stack();
}
design->check();
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
if (command)
free(command);
#endif
}
if (command == NULL)
printf("exit\n");
#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE)
else
free(command);
#endif
recursion_counter--;
log_cmd_error_throw = false;
}

View File

@ -272,8 +272,64 @@ inline void memhasher() { if (memhasher_active) memhasher_do(); }
void yosys_banner();
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 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);
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");

View File

@ -24,8 +24,8 @@ PRIVATE_NAMESPACE_BEGIN
int autoname_worker(Module *module, const dict<Wire*, int>& wire_score)
{
dict<Cell*, pair<int, IdString>> proposed_cell_names;
dict<Wire*, pair<int, IdString>> proposed_wire_names;
dict<Cell*, pair<int, string>> proposed_cell_names;
dict<Wire*, pair<int, string>> proposed_wire_names;
int best_score = -1;
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 (suffix.empty())
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);
if (cell->output(conn.first)) score = 0;
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 (suffix.empty())
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);
if (cell->output(conn.first)) score = 0;
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) {
if (best_score*2 < it.second.first)
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));
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) {
if (best_score*2 < it.second.first)
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));
module->rename(it.first, n);
}

View File

@ -405,7 +405,7 @@ struct DftTagWorker {
auto &sig_y = cell->getPort(ID::Y);
auto sig_a = cell->getPort(ID::A);
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_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_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 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 prop = autoReduceAnd(NEW_ID, filled);

View File

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

View File

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

View File

@ -575,7 +575,7 @@ struct ShowWorker
} else {
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)});
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

@ -424,13 +424,19 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
for (auto &bit : sig)
outbit_to_cell[bit].insert(cell);
}
cells.node(cell);
cells.node(cell);
}
for (auto &it_right : cell_to_inbit)
for (auto &it_sigbit : it_right.second)
for (auto &it_left : outbit_to_cell[it_sigbit])
cells.edge(it_left, it_right.first);
// Build the graph for the topological sort.
for (auto &it_right : cell_to_inbit) {
const int r_index = cells.node(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();

View File

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

View File

@ -1032,7 +1032,7 @@ struct ShareWorker
}
bool found_scc = !toposort.sort();
topo_cell_drivers = std::move(toposort.database);
topo_cell_drivers = toposort.get_database();
if (found_scc && toposort.analyze_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
$(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
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
set eq_ab AB
set eq_ba BA
generate
endmatch
Notice how `define` can be used to define additional local variables similar
to the loop variables defined by `slice` and `choice`.

View File

@ -24,11 +24,8 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
bool did_something;
dict<SigBit, State> initbits;
pool<SigBit> rminitbits;
#include "passes/pmgen/peepopt_pm.h"
#include "generate.h"
struct PeepoptPass : public Pass {
PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { }
@ -40,38 +37,29 @@ struct PeepoptPass : public Pass {
log("\n");
log("This pass applies a collection of peephole optimizers to the current design.\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
{
std::string genmode;
log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
if (args[argidx] == "-generate" && argidx+1 < args.size()) {
genmode = args[++argidx];
continue;
}
break;
}
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())
{
did_something = true;
@ -79,47 +67,14 @@ struct PeepoptPass : public Pass {
while (did_something)
{
did_something = false;
initbits.clear();
rminitbits.clear();
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.run_shiftmul();
pm.run_shiftmul_right();
pm.run_shiftmul_left();
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
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
// xorshift128 params
@ -453,7 +446,7 @@ struct RecoverNamesWorker {
pool<IdString> comb_whiteboxes, buffer_types;
// class -> (gold, (gate, inverted))
dict<equiv_cls_t, std::pair<pool<IdBit>, dict<IdBit, bool>>> cls2bits;
dict<equiv_cls_t, std::pair<pool<IdBit>, dict<IdBit, bool>>> cls2bits;
void analyse_boxes()
{

View File

@ -111,6 +111,7 @@ struct SimShared
int step = 0;
std::vector<TriggeredAssertion> triggered_assertions;
bool serious_asserts = false;
bool initstate = true;
};
void zinit(State &v)
@ -218,9 +219,13 @@ struct SimInstance
log_assert(module);
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());
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) {
log_assert(parent->children.count(instance) == 0);
parent->children[instance] = this;
@ -578,7 +583,7 @@ struct SimInstance
Const data = Const(State::Sx, mem.width << port.wide_log2);
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()) {
int addr_int = addr.as_int();
@ -1356,6 +1361,8 @@ struct SimWorker : SimShared
set_inports(clock, State::Sx);
set_inports(clockn, State::Sx);
top->set_initstate_outputs(initstate ? State::S1 : State::S0);
update(false);
register_output_step(0);
@ -1372,6 +1379,9 @@ struct SimWorker : SimShared
update(true);
register_output_step(10*cycle + 5);
if (cycle == 0)
top->set_initstate_outputs(State::S0);
if (debug)
log("\n===== %d =====\n", 10*cycle + 10);
else if (verbose)
@ -1953,7 +1963,7 @@ struct SimWorker : SimShared
if (yw.steps.empty()) {
log_warning("Yosys witness file `%s` contains no time steps\n", yw.filename.c_str());
} else {
top->set_initstate_outputs(State::S1);
top->set_initstate_outputs(initstate ? State::S1 : State::S0);
set_yw_state(yw, hierarchy, 0);
set_yw_clocks(yw, hierarchy, true);
initialize_stable_past();
@ -2546,6 +2556,9 @@ struct SimPass : public Pass {
log(" -n <integer>\n");
log(" number of clock cycles to simulate (default: 20)\n");
log("\n");
log(" -noinitstate\n");
log(" do not activate $initstate cells during the first cycle\n");
log("\n");
log(" -a\n");
log(" use all nets in VCD/FST operations, not just those with public names\n");
log("\n");
@ -2646,6 +2659,10 @@ struct SimPass : public Pass {
worker.cycles_set = true;
continue;
}
if (args[argidx] == "-noinitstate") {
worker.initstate = false;
continue;
}
if (args[argidx] == "-rstlen" && argidx+1 < args.size()) {
worker.rstlen = atoi(args[++argidx].c_str());
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()) {
RTLIL::Module *tpl = design->module(cell->type);
if (tpl != nullptr) {
if (topo_modules.database.count(tpl) == 0)
if (!topo_modules.has_node(tpl))
worklist.insert(tpl);
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++) {
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::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++) {
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::B, sig_b[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);
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::B, sig_a[i+1]);
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)) {
RTLIL::SigSpec sig_t = module->addWire(NEW_ID);
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::Y, sig_t);
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_));
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::B, sig[i+1]);
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_));
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::Y, sig_y);
}
@ -223,7 +223,7 @@ void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell)
log_assert(!gate_type.empty());
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::B, sig_b);
gate->setPort(ID::Y, sig_y);
@ -239,20 +239,20 @@ 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::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);
module->remove(xor_cell);
RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID);
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);
module->remove(reduce_cell);
if (!is_ne) {
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));
simplemap_lognot(module, not_cell);
not_cell->attributes[ID::src] = cell->attributes[ID::src];
simplemap_lognot(module, 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++) {
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::B, sig_b[i]);
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++) {
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::B, sig_b[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++) {
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::E, sig_e);
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 k = 0; k < width; k++) {
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::B, data[i*2+width+k]);
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);
for (int i = 0; i < GetSize(lut_data); i += 2) {
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::B, lut_data[i+1]);
gate->setPort(ID::S, lut_ctrl[idx]);

View File

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

View File

@ -7,14 +7,27 @@ hierarchy -top my_dff
proc
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)
cd dff # Constrain all select calls below inside the top module
select -assert-none t:*
cd my_dff # Constrain all select calls below inside the top module
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
hierarchy -top my_dffe
proc
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)
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)
);
wire x = 'x;
`ifndef FAIL
assume property (
b !== 'x ##1 $changed(b)
b !== x ##1 $changed(b)
);
`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
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
read_verilog <<EOT
module top;