aboutsummaryrefslogtreecommitdiffstats
path: root/frontends
diff options
context:
space:
mode:
Diffstat (limited to 'frontends')
-rw-r--r--frontends/ast/ast.cc138
-rw-r--r--frontends/ast/ast.h29
-rw-r--r--frontends/ast/genrtlil.cc47
-rw-r--r--frontends/ast/simplify.cc495
-rw-r--r--frontends/json/jsonparse.cc34
-rw-r--r--frontends/rtlil/rtlil_parser.y2
-rw-r--r--frontends/verific/Makefile.inc2
-rw-r--r--frontends/verific/verific.cc636
-rw-r--r--frontends/verific/verific.h4
-rw-r--r--frontends/verific/verificsva.cc12
-rw-r--r--frontends/verilog/preproc.cc10
-rw-r--r--frontends/verilog/verilog_lexer.l15
-rw-r--r--frontends/verilog/verilog_parser.y30
13 files changed, 1212 insertions, 242 deletions
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 4fbc238b0..6097f02f5 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -344,7 +344,7 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
}
if (!multirange_swapped.empty()) {
fprintf(f, " multirange_swapped=[");
- for (auto v : multirange_swapped)
+ for (bool v : multirange_swapped)
fprintf(f, " %d", v);
fprintf(f, " ]");
}
@@ -854,7 +854,7 @@ RTLIL::Const AstNode::bitsAsConst(int width)
return bitsAsConst(width, is_signed);
}
-RTLIL::Const AstNode::asAttrConst()
+RTLIL::Const AstNode::asAttrConst() const
{
log_assert(type == AST_CONSTANT);
@@ -869,8 +869,17 @@ RTLIL::Const AstNode::asAttrConst()
return val;
}
-RTLIL::Const AstNode::asParaConst()
+RTLIL::Const AstNode::asParaConst() const
{
+ if (type == AST_REALVALUE)
+ {
+ AstNode *strnode = AstNode::mkconst_str(stringf("%f", realvalue));
+ RTLIL::Const val = strnode->asAttrConst();
+ val.flags |= RTLIL::CONST_FLAG_REAL;
+ delete strnode;
+ return val;
+ }
+
RTLIL::Const val = asAttrConst();
if (is_signed)
val.flags |= RTLIL::CONST_FLAG_SIGNED;
@@ -983,8 +992,7 @@ static bool param_has_no_default(const AstNode *param) {
(children.size() == 1 && children[0]->type == AST_RANGE);
}
-// create and add a new AstModule from an AST_MODULE AST node
-static void process_module(RTLIL::Design *design, AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false)
+static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false)
{
log_assert(current_scope.empty());
log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE);
@@ -1044,8 +1052,11 @@ static void process_module(RTLIL::Design *design, AstNode *ast, bool defer, AstN
}
}
- // TODO(zachjs): make design available to simplify() in the future
+ // 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, false, 0, -1, false, false)) { }
+ set_simplify_design_context(nullptr);
if (flag_dump_ast2) {
log("Dumping AST after simplification:\n");
@@ -1172,6 +1183,9 @@ static void process_module(RTLIL::Design *design, AstNode *ast, bool defer, AstN
continue;
module->attributes[attr.first] = attr.second->asAttrConst();
}
+ for (const AstNode *node : ast->children)
+ if (node->type == AST_PARAMETER)
+ current_module->avail_parameters(node->str);
}
if (ast->type == AST_INTERFACE)
@@ -1197,6 +1211,42 @@ static void process_module(RTLIL::Design *design, AstNode *ast, bool defer, AstN
}
design->add(current_module);
+ return current_module;
+}
+
+RTLIL::Module *
+AST_INTERNAL::process_and_replace_module(RTLIL::Design *design,
+ RTLIL::Module *old_module,
+ AstNode *new_ast,
+ AstNode *original_ast)
+{
+ // The old module will be deleted. Rename and mark for deletion, using
+ // a static counter to make sure we get a unique name.
+ static unsigned counter;
+ std::ostringstream new_name;
+ new_name << old_module->name.str()
+ << "_before_process_and_replace_module_"
+ << counter;
+ ++counter;
+
+ design->rename(old_module, new_name.str());
+ old_module->set_bool_attribute(ID::to_delete);
+
+ // Check if the module was the top module. If it was, we need to remove
+ // the top attribute and put it on the new module.
+ bool is_top = false;
+ if (old_module->get_bool_attribute(ID::initial_top)) {
+ old_module->attributes.erase(ID::initial_top);
+ is_top = true;
+ }
+
+ // Generate RTLIL from AST for the new module and add to the design:
+ RTLIL::Module* new_module = process_module(design, new_ast, false, original_ast);
+
+ if (is_top)
+ new_module->set_bool_attribute(ID::top);
+
+ return new_module;
}
// renames identifiers in tasks and functions within a package
@@ -1410,13 +1460,32 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule
}
}
+// AstModules may contain cells marked with ID::reprocess_after, which indicates
+// that it should be reprocessed once the specified module has been elaborated.
+bool AstModule::reprocess_if_necessary(RTLIL::Design *design)
+{
+ for (const RTLIL::Cell *cell : cells())
+ {
+ std::string modname = cell->get_string_attribute(ID::reprocess_after);
+ if (modname.empty())
+ continue;
+ if (design->module(modname) || design->module("$abstract" + modname)) {
+ log("Reprocessing module %s because instantiated module %s has become available.\n",
+ log_id(name), log_id(modname));
+ loadconfig();
+ process_and_replace_module(design, this, ast, NULL);
+ return true;
+ }
+ }
+ return false;
+}
+
// When an interface instance is found in a module, the whole RTLIL for the module will be rederived again
// from AST. The interface members are copied into the AST module with the prefix of the interface.
-void AstModule::reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module*> &local_interfaces)
+void AstModule::expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module*> &local_interfaces)
{
loadconfig();
- bool is_top = false;
AstNode *new_ast = ast->clone();
for (auto &intf : local_interfaces) {
std::string intfname = intf.first.str();
@@ -1473,28 +1542,15 @@ void AstModule::reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdStri
}
}
- // The old module will be deleted. Rename and mark for deletion:
- std::string original_name = this->name.str();
- std::string changed_name = original_name + "_before_replacing_local_interfaces";
- design->rename(this, changed_name);
- this->set_bool_attribute(ID::to_delete);
+ // Generate RTLIL from AST for the new module and add to the design,
+ // renaming this module to move it out of the way.
+ RTLIL::Module* new_module =
+ process_and_replace_module(design, this, new_ast, ast_before_replacing_interface_ports);
- // Check if the module was the top module. If it was, we need to remove the top attribute and put it on the
- // new module.
- if (this->get_bool_attribute(ID::initial_top)) {
- this->attributes.erase(ID::initial_top);
- is_top = true;
- }
-
- // Generate RTLIL from AST for the new module and add to the design:
- process_module(design, new_ast, false, ast_before_replacing_interface_ports);
- delete(new_ast);
- RTLIL::Module* mod = design->module(original_name);
- if (is_top)
- mod->set_bool_attribute(ID::top);
+ delete new_ast;
// Set the attribute "interfaces_replaced_in_module" so that it does not happen again.
- mod->set_bool_attribute(ID::interfaces_replaced_in_module);
+ new_module->set_bool_attribute(ID::interfaces_replaced_in_module);
}
// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
@@ -1628,6 +1684,17 @@ static std::string serialize_param_value(const RTLIL::Const &val) {
return res;
}
+std::string AST::derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> &parameters) {
+ std::string para_info;
+ for (const auto &elem : parameters)
+ para_info += stringf("%s=%s", elem.first.c_str(), serialize_param_value(elem.second).c_str());
+
+ if (para_info.size() > 60)
+ return "$paramod$" + sha1(para_info) + stripped_name;
+ else
+ return "$paramod" + stripped_name + para_info;
+}
+
// create a new parametric module (when needed) and return the name of the generated module
std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, AstNode **new_ast_out, bool quiet)
{
@@ -1636,9 +1703,8 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
if (stripped_name.compare(0, 9, "$abstract") == 0)
stripped_name = stripped_name.substr(9);
- std::string para_info;
-
int para_counter = 0;
+ std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters;
for (const auto child : ast->children) {
if (child->type != AST_PARAMETER)
continue;
@@ -1647,25 +1713,21 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id
if (it != parameters.end()) {
if (!quiet)
log("Parameter %s = %s\n", child->str.c_str(), log_signal(it->second));
- para_info += stringf("%s=%s", child->str.c_str(), serialize_param_value(it->second).c_str());
+ named_parameters.emplace_back(child->str, it->second);
continue;
}
it = parameters.find(stringf("$%d", para_counter));
if (it != parameters.end()) {
if (!quiet)
log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(it->second));
- para_info += stringf("%s=%s", child->str.c_str(), serialize_param_value(it->second).c_str());
+ named_parameters.emplace_back(child->str, it->second);
continue;
}
}
- std::string modname;
- if (parameters.size() == 0)
- modname = stripped_name;
- else if (para_info.size() > 60)
- modname = "$paramod$" + sha1(para_info) + stripped_name;
- else
- modname = "$paramod" + stripped_name + para_info;
+ std::string modname = stripped_name;
+ if (parameters.size()) // not named_parameters to cover hierarchical defparams
+ modname = derived_module_name(stripped_name, named_parameters);
if (design->has(modname))
return modname;
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 63104bca4..80497c131 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -262,11 +262,13 @@ namespace AST
void mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes);
void meminfo(int &mem_width, int &mem_size, int &addr_bits);
bool detect_latch(const std::string &var);
+ const RTLIL::Module* lookup_cell_module();
// additional functionality for evaluating constant functions
struct varinfo_t {
RTLIL::Const val;
int offset;
+ bool range_swapped;
bool is_signed;
AstNode *arg = nullptr;
bool explicitly_sized;
@@ -313,8 +315,8 @@ namespace AST
RTLIL::Const bitsAsConst(int width, bool is_signed);
RTLIL::Const bitsAsConst(int width = -1);
RTLIL::Const bitsAsUnsizedConst(int width);
- RTLIL::Const asAttrConst();
- RTLIL::Const asParaConst();
+ RTLIL::Const asAttrConst() const;
+ RTLIL::Const asParaConst() const;
uint64_t asInt(bool is_signed);
bool bits_only_01() const;
bool asBool() const;
@@ -348,7 +350,8 @@ namespace AST
RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, bool mayfail) override;
RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail) override;
std::string derive_common(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> &parameters, AstNode **new_ast_out, bool quiet = false);
- void reprocess_module(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override;
+ void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces) override;
+ bool reprocess_if_necessary(RTLIL::Design *design) override;
RTLIL::Module *clone() const override;
void loadconfig() const;
};
@@ -377,6 +380,14 @@ namespace AST
// struct helper exposed from simplify for genrtlil
AstNode *make_struct_member_range(AstNode *node, AstNode *member_node);
+
+ // generate standard $paramod... derived module name; parameters should be
+ // in the order they are declared in the instantiated module
+ std::string derived_module_name(std::string stripped_name, const std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> &parameters);
+
+ // used to provide simplify() access to the current design for looking up
+ // modules, ports, wires, etc.
+ void set_simplify_design_context(const RTLIL::Design *design);
}
namespace AST_INTERNAL
@@ -395,6 +406,18 @@ namespace AST_INTERNAL
extern dict<std::string, pool<int>> current_memwr_visible;
struct LookaheadRewriter;
struct ProcessGenerator;
+
+ // Create and add a new AstModule from new_ast, then use it to replace
+ // old_module in design, renaming old_module to move it out of the way.
+ // Return the new module.
+ //
+ // If original_ast is not null, it will be used as the AST node for the
+ // new module. Otherwise, new_ast will be used.
+ RTLIL::Module *
+ process_and_replace_module(RTLIL::Design *design,
+ RTLIL::Module *old_module,
+ AST::AstNode *new_ast,
+ AST::AstNode *original_ast = nullptr);
}
YOSYS_NAMESPACE_END
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index c82664b98..020b4e5e8 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -877,7 +877,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
if (children.size() > 1)
range = children[1];
- } else if (id_ast->type == AST_STRUCT_ITEM) {
+ } else if (id_ast->type == AST_STRUCT_ITEM || id_ast->type == AST_STRUCT) {
AstNode *tmp_range = make_struct_member_range(this, id_ast);
this_width = tmp_range->range_left - tmp_range->range_right + 1;
delete tmp_range;
@@ -932,7 +932,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (children.at(0)->type != AST_CONSTANT)
log_file_error(filename, location.first_line, "Static cast with non constant expression!\n");
children.at(1)->detectSignWidthWorker(width_hint, sign_hint);
- width_hint = children.at(0)->bitsAsConst().as_int();
+ this_width = children.at(0)->bitsAsConst().as_int();
+ width_hint = max(width_hint, this_width);
if (width_hint <= 0)
log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n");
break;
@@ -1087,6 +1088,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
}
break;
}
+ if (str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") {
+ width_hint = 32;
+ sign_hint = true;
+ break;
+ }
if (current_scope.count(str))
{
// This width detection is needed for function calls which are
@@ -1126,8 +1132,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
// everything should have been handled above -> print error if not.
default:
+ AstNode *current_scope_ast = current_ast_mod == nullptr ? current_ast : current_ast_mod;
for (auto f : log_files)
- current_ast_mod->dumpAst(f, "verilog-ast> ");
+ current_scope_ast->dumpAst(f, "verilog-ast> ");
log_file_error(filename, location.first_line, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str());
}
@@ -1524,13 +1531,20 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// changing the size of signal can be done directly using RTLIL::SigSpec
case AST_CAST_SIZE: {
RTLIL::SigSpec size = children[0]->genRTLIL();
- RTLIL::SigSpec sig = children[1]->genRTLIL();
if (!size.is_fully_const())
log_file_error(filename, location.first_line, "Static cast with non constant expression!\n");
int width = size.as_int();
if (width <= 0)
log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n");
- sig.extend_u0(width, sign_hint);
+ // determine the *signedness* of the expression
+ int sub_width_hint = -1;
+ bool sub_sign_hint = true;
+ children[1]->detectSignWidth(sub_width_hint, sub_sign_hint);
+ // generate the signal given the *cast's* size and the
+ // *expression's* signedness
+ RTLIL::SigSpec sig = children[1]->genWidthRTLIL(width, sub_sign_hint);
+ // context may effect this node's signedness, but not that of the
+ // casted expression
is_signed = sign_hint;
return sig;
}
@@ -1917,21 +1931,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
continue;
}
if (child->type == AST_PARASET) {
- int extra_const_flags = 0;
IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str;
- if (child->children[0]->type == AST_REALVALUE) {
+ const AstNode *value = child->children[0];
+ if (value->type == AST_REALVALUE)
log_file_warning(filename, location.first_line, "Replacing floating point parameter %s.%s = %f with string.\n",
- log_id(cell), log_id(paraname), child->children[0]->realvalue);
- extra_const_flags = RTLIL::CONST_FLAG_REAL;
- auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue));
- strnode->cloneInto(child->children[0]);
- delete strnode;
- }
- if (child->children[0]->type != AST_CONSTANT)
+ log_id(cell), log_id(paraname), value->realvalue);
+ else if (value->type != AST_CONSTANT)
log_file_error(filename, location.first_line, "Parameter %s.%s with non-constant value!\n",
log_id(cell), log_id(paraname));
- cell->parameters[paraname] = child->children[0]->asParaConst();
- cell->parameters[paraname].flags |= extra_const_flags;
+ cell->parameters[paraname] = value->asParaConst();
continue;
}
if (child->type == AST_ARGUMENT) {
@@ -1948,7 +1956,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (sig.is_wire()) {
// if the resulting SigSpec is a wire, its
// signedness should match that of the AstNode
- log_assert(arg->is_signed == sig.as_wire()->is_signed);
+ if (arg->type == AST_IDENTIFIER && arg->id2ast && arg->id2ast->is_signed && !arg->is_signed)
+ // fully-sliced signed wire will be resolved
+ // once the module becomes available
+ log_assert(attributes.count(ID::reprocess_after));
+ else
+ log_assert(arg->is_signed == sig.as_wire()->is_signed);
} else if (arg->is_signed) {
// non-trivial signed nodes are indirected through
// signed wires to enable sign extension
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index f713cf8e1..4d7c4f522 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -307,6 +307,10 @@ static int size_packed_struct(AstNode *snode, int base_offset)
if (node->type == AST_STRUCT || node->type == AST_UNION) {
// embedded struct or union
width = size_packed_struct(node, base_offset + offset);
+ // set range of struct
+ node->range_right = base_offset + offset;
+ node->range_left = base_offset + offset + width - 1;
+ node->range_valid = true;
}
else {
log_assert(node->type == AST_STRUCT_ITEM);
@@ -493,14 +497,12 @@ static void add_members_to_scope(AstNode *snode, std::string name)
// in case later referenced in assignments
log_assert(snode->type==AST_STRUCT || snode->type==AST_UNION);
for (auto *node : snode->children) {
+ auto member_name = name + "." + node->str;
+ current_scope[member_name] = node;
if (node->type != AST_STRUCT_ITEM) {
// embedded struct or union
add_members_to_scope(node, name + "." + node->str);
}
- else {
- auto member_name = name + "." + node->str;
- current_scope[member_name] = node;
- }
}
}
@@ -564,6 +566,237 @@ static std::string prefix_id(const std::string &prefix, const std::string &str)
return prefix + str;
}
+// direct access to this global should be limited to the following two functions
+static const RTLIL::Design *simplify_design_context = nullptr;
+
+void AST::set_simplify_design_context(const RTLIL::Design *design)
+{
+ log_assert(!simplify_design_context || !design);
+ simplify_design_context = design;
+}
+
+// lookup the module with the given name in the current design context
+static const RTLIL::Module* lookup_module(const std::string &name)
+{
+ return simplify_design_context->module(name);
+}
+
+const RTLIL::Module* AstNode::lookup_cell_module()
+{
+ log_assert(type == AST_CELL);
+
+ auto reprocess_after = [this] (const std::string &modname) {
+ if (!attributes.count(ID::reprocess_after))
+ attributes[ID::reprocess_after] = AstNode::mkconst_str(modname);
+ };
+
+ const AstNode *celltype = nullptr;
+ for (const AstNode *child : children)
+ if (child->type == AST_CELLTYPE) {
+ celltype = child;
+ break;
+ }
+ log_assert(celltype != nullptr);
+
+ const RTLIL::Module *module = lookup_module(celltype->str);
+ if (!module)
+ module = lookup_module("$abstract" + celltype->str);
+ if (!module) {
+ if (celltype->str.at(0) != '$')
+ reprocess_after(celltype->str);
+ return nullptr;
+ }
+
+ // build a mapping from true param name to param value
+ size_t para_counter = 0;
+ dict<RTLIL::IdString, RTLIL::Const> cell_params_map;
+ for (AstNode *child : children) {
+ if (child->type != AST_PARASET)
+ continue;
+
+ if (child->str.empty() && para_counter >= module->avail_parameters.size())
+ return nullptr; // let hierarchy handle this error
+ IdString paraname = child->str.empty() ? module->avail_parameters[para_counter++] : child->str;
+
+ const AstNode *value = child->children[0];
+ if (value->type != AST_REALVALUE && value->type != AST_CONSTANT)
+ return nullptr; // let genrtlil handle this error
+ cell_params_map[paraname] = value->asParaConst();
+ }
+
+ // put the parameters in order and generate the derived module name
+ std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters;
+ for (RTLIL::IdString param : module->avail_parameters) {
+ auto it = cell_params_map.find(param);
+ if (it != cell_params_map.end())
+ named_parameters.emplace_back(it->first, it->second);
+ }
+ std::string modname = celltype->str;
+ if (cell_params_map.size()) // not named_parameters to cover hierarchical defparams
+ modname = derived_module_name(celltype->str, named_parameters);
+
+ // try to find the resolved module
+ module = lookup_module(modname);
+ if (!module) {
+ reprocess_after(modname);
+ return nullptr;
+ }
+ return module;
+}
+
+// returns whether an expression contains an unbased unsized literal; does not
+// check the literal exists in a self-determined context
+static bool contains_unbased_unsized(const AstNode *node)
+{
+ if (node->type == AST_CONSTANT)
+ return node->is_unsized;
+ for (const AstNode *child : node->children)
+ if (contains_unbased_unsized(child))
+ return true;
+ return false;
+}
+
+// adds a wire to the current module with the given name that matches the
+// dimensions of the given wire reference
+void add_wire_for_ref(const RTLIL::Wire *ref, const std::string &str)
+{
+ AstNode *left = AstNode::mkconst_int(ref->width - 1 + ref->start_offset, true);
+ AstNode *right = AstNode::mkconst_int(ref->start_offset, true);
+ if (ref->upto)
+ std::swap(left, right);
+ AstNode *range = new AstNode(AST_RANGE, left, right);
+
+ AstNode *wire = new AstNode(AST_WIRE, range);
+ wire->is_signed = ref->is_signed;
+ wire->is_logic = true;
+ wire->str = str;
+
+ current_ast_mod->children.push_back(wire);
+ current_scope[str] = wire;
+}
+
+enum class IdentUsage {
+ NotReferenced, // target variable is neither read or written in the block
+ Assigned, // target variable is always assigned before use
+ SyncRequired, // target variable may be used before it has been assigned
+};
+
+// determines whether a local variable a block is always assigned before it is
+// used, meaning the nosync attribute can automatically be added to that
+// variable
+static IdentUsage always_asgn_before_use(const AstNode *node, const std::string &target)
+{
+ // This variable has been referenced before it has necessarily been assigned
+ // a value in this procedure.
+ if (node->type == AST_IDENTIFIER && node->str == target)
+ return IdentUsage::SyncRequired;
+
+ // For case statements (which are also used for if/else), we check each
+ // possible branch. If the variable is assigned in all branches, then it is
+ // assigned, and a sync isn't required. If it used before assignment in any
+ // branch, then a sync is required.
+ if (node->type == AST_CASE) {
+ bool all_defined = true;
+ bool any_used = false;
+ bool has_default = false;
+ for (const AstNode *child : node->children) {
+ if (child->type == AST_COND && child->children.at(0)->type == AST_DEFAULT)
+ has_default = true;
+ IdentUsage nested = always_asgn_before_use(child, target);
+ if (nested != IdentUsage::Assigned && child->type == AST_COND)
+ all_defined = false;
+ if (nested == IdentUsage::SyncRequired)
+ any_used = true;
+ }
+ if (any_used)
+ return IdentUsage::SyncRequired;
+ else if (all_defined && has_default)
+ return IdentUsage::Assigned;
+ else
+ return IdentUsage::NotReferenced;
+ }
+
+ // Check if this is an assignment to the target variable. For simplicity, we
+ // don't analyze sub-ranges of the variable.
+ if (node->type == AST_ASSIGN_EQ) {
+ const AstNode *ident = node->children.at(0);
+ if (ident->type == AST_IDENTIFIER && ident->str == target)
+ return IdentUsage::Assigned;
+ }
+
+ for (const AstNode *child : node->children) {
+ IdentUsage nested = always_asgn_before_use(child, target);
+ if (nested != IdentUsage::NotReferenced)
+ return nested;
+ }
+ return IdentUsage::NotReferenced;
+}
+
+static const std::string auto_nosync_prefix = "\\AutoNosync";
+
+// mark a local variable in an always_comb block for automatic nosync
+// consideration
+static void mark_auto_nosync(AstNode *block, const AstNode *wire)
+{
+ log_assert(block->type == AST_BLOCK);
+ log_assert(wire->type == AST_WIRE);
+ block->attributes[auto_nosync_prefix + wire->str] = AstNode::mkconst_int(1,
+ false);
+}
+
+// block names can be prefixed with an explicit scope during elaboration
+static bool is_autonamed_block(const std::string &str) {
+ size_t last_dot = str.rfind('.');
+ // unprefixed names: autonamed if the first char is a dollar sign
+ if (last_dot == std::string::npos)
+ return str.at(0) == '$'; // e.g., `$fordecl_block$1`
+ // prefixed names: autonamed if the final chunk begins with a dollar sign
+ return str.rfind(".$") == last_dot; // e.g., `\foo.bar.$fordecl_block$1`
+}
+
+// check a procedural block for auto-nosync markings, remove them, and add
+// nosync to local variables as necessary
+static void check_auto_nosync(AstNode *node)
+{
+ std::vector<RTLIL::IdString> attrs_to_drop;
+ for (const auto& elem : node->attributes) {
+ // skip attributes that don't begin with the prefix
+ if (elem.first.compare(0, auto_nosync_prefix.size(),
+ auto_nosync_prefix.c_str()))
+ continue;
+
+ // delete and remove the attribute once we're done iterating
+ attrs_to_drop.push_back(elem.first);
+
+ // find the wire based on the attribute
+ std::string wire_name = elem.first.substr(auto_nosync_prefix.size());
+ auto it = current_scope.find(wire_name);
+ if (it == current_scope.end())
+ continue;
+
+ // analyze the usage of the local variable in this block
+ IdentUsage ident_usage = always_asgn_before_use(node, wire_name);
+ if (ident_usage != IdentUsage::Assigned)
+ continue;
+
+ // mark the wire with `nosync`
+ AstNode *wire = it->second;
+ log_assert(wire->type == AST_WIRE);
+ wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
+ }
+
+ // remove the attributes we've "consumed"
+ for (const RTLIL::IdString &str : attrs_to_drop) {
+ auto it = node->attributes.find(str);
+ delete it->second;
+ node->attributes.erase(it);
+ }
+
+ // check local variables in any nested blocks
+ for (AstNode *child : node->children)
+ check_auto_nosync(child);
+}
+
// convert the AST into a simpler AST that has all parameters substituted by their
// values, unrolled for-loops, expanded generate blocks, etc. when this function
// is done with an AST it can be converted into RTLIL using genRTLIL().
@@ -871,6 +1104,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
}
+
+ for (AstNode *child : children)
+ if (child->type == AST_ALWAYS &&
+ child->attributes.count(ID::always_comb))
+ check_auto_nosync(child);
}
// create name resolution entries for all objects with names
@@ -920,19 +1158,110 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
- if (type == AST_ARGUMENT)
- {
- if (children.size() == 1 && children[0]->type == AST_CONSTANT)
- {
- // HACK: For port bindings using unbased unsized literals, mark them
- // signed so they sign-extend. The hierarchy will still incorrectly
- // generate a warning complaining about resizing the expression.
- // This also doesn't handle the complex of something like a ternary
- // expression bound to a port, where the actual size of the port is
- // needed to resolve the expression correctly.
- AstNode *arg = children[0];
- if (arg->is_unsized)
- arg->is_signed = true;
+ if (type == AST_CELL) {
+ bool lookup_suggested = false;
+
+ for (AstNode *child : children) {
+ // simplify any parameters to constants
+ if (child->type == AST_PARASET)
+ while (child->simplify(true, false, false, 1, -1, false, true)) { }
+
+ // look for patterns which _may_ indicate ambiguity requiring
+ // resolution of the underlying module
+ if (child->type == AST_ARGUMENT) {
+ if (child->children.size() != 1)
+ continue;
+ const AstNode *value = child->children[0];
+ if (value->type == AST_IDENTIFIER) {
+ const AstNode *elem = value->id2ast;
+ if (elem == nullptr) {
+ if (current_scope.count(value->str))
+ elem = current_scope.at(value->str);
+ else
+ continue;
+ }
+ if (elem->type == AST_MEMORY)
+ // need to determine is the is a read or wire
+ lookup_suggested = true;
+ else if (elem->type == AST_WIRE && elem->is_signed && !value->children.empty())
+ // this may be a fully sliced signed wire which needs
+ // to be indirected to produce an unsigned connection
+ lookup_suggested = true;
+ }
+ else if (contains_unbased_unsized(value))
+ // unbased unsized literals extend to width of the context
+ lookup_suggested = true;
+ }
+ }
+
+ const RTLIL::Module *module = nullptr;
+ if (lookup_suggested)
+ module = lookup_cell_module();
+ if (module) {
+ size_t port_counter = 0;
+ for (AstNode *child : children) {
+ if (child->type != AST_ARGUMENT)
+ continue;
+
+ // determine the full name of port this argument is connected to
+ RTLIL::IdString port_name;
+ if (child->str.size())
+ port_name = child->str;
+ else {
+ if (port_counter >= module->ports.size())
+ log_file_error(filename, location.first_line,
+ "Cell instance has more ports than the module!\n");
+ port_name = module->ports[port_counter++];
+ }
+
+ // find the port's wire in the underlying module
+ const RTLIL::Wire *ref = module->wire(port_name);
+ if (ref == nullptr)
+ log_file_error(filename, location.first_line,
+ "Cell instance refers to port %s which does not exist in module %s!.\n",
+ log_id(port_name), log_id(module->name));
+
+ // select the argument, if present
+ log_assert(child->children.size() <= 1);
+ if (child->children.empty())
+ continue;
+ AstNode *arg = child->children[0];
+
+ // plain identifiers never need indirection; this also prevents
+ // adding infinite levels of indirection
+ if (arg->type == AST_IDENTIFIER && arg->children.empty())
+ continue;
+
+ // only add indirection for standard inputs or outputs
+ if (ref->port_input == ref->port_output)
+ continue;
+
+ did_something = true;
+
+ // create the indirection wire
+ std::stringstream sstr;
+ sstr << "$indirect$" << ref->name.c_str() << "$" << filename << ":" << location.first_line << "$" << (autoidx++);
+ std::string tmp_str = sstr.str();
+ add_wire_for_ref(ref, tmp_str);
+
+ AstNode *asgn = new AstNode(AST_ASSIGN);
+ current_ast_mod->children.push_back(asgn);
+
+ AstNode *ident = new AstNode(AST_IDENTIFIER);
+ ident->str = tmp_str;
+ child->children[0] = ident->clone();
+
+ if (ref->port_input && !ref->port_output) {
+ asgn->children.push_back(ident);
+ asgn->children.push_back(arg);
+ } else {
+ log_assert(!ref->port_input && ref->port_output);
+ asgn->children.push_back(arg);
+ asgn->children.push_back(ident);
+ }
+ }
+
+
}
}
@@ -1024,6 +1353,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
case AST_PARAMETER:
case AST_LOCALPARAM:
+ // if parameter is implicit type which is the typename of a struct or union,
+ // save information about struct in wiretype attribute
+ if (children[0]->type == AST_IDENTIFIER && current_scope.count(children[0]->str) > 0) {
+ auto item_node = current_scope[children[0]->str];
+ if (item_node->type == AST_STRUCT || item_node->type == AST_UNION) {
+ attributes[ID::wiretype] = item_node->clone();
+ size_packed_struct(attributes[ID::wiretype], 0);
+ add_members_to_scope(attributes[ID::wiretype], str);
+ }
+ }
while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true)
did_something = true;
children[0]->detectSignWidth(width_hint, sign_hint);
@@ -1189,11 +1528,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (const_fold && type == AST_CASE)
{
- int width_hint;
- bool sign_hint;
detectSignWidth(width_hint, sign_hint);
while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { }
if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) {
+ children[0]->is_signed = sign_hint;
RTLIL::Const case_expr = children[0]->bitsAsConst(width_hint, sign_hint);
std::vector<AstNode*> new_children;
new_children.push_back(children[0]);
@@ -1413,6 +1751,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) {
// replace with wire representing the packed structure
newNode = make_packed_struct(template_node, str);
+ newNode->attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
// add original input/output attribute to resolved wire
newNode->is_input = this->is_input;
newNode->is_output = this->is_output;
@@ -1461,18 +1800,33 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (is_custom_type) {
log_assert(children.size() == 2);
log_assert(children[1]->type == AST_WIRETYPE);
- if (!current_scope.count(children[1]->str))
- log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str());
- AstNode *resolved_type_node = current_scope.at(children[1]->str);
+ auto type_name = children[1]->str;
+ if (!current_scope.count(type_name)) {
+ log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str());
+ }
+ AstNode *resolved_type_node = current_scope.at(type_name);
if (resolved_type_node->type != AST_TYPEDEF)
- log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[1]->str.c_str());
+ log_file_error(filename, location.first_line, "`%s' does not name a type\n", type_name.c_str());
log_assert(resolved_type_node->children.size() == 1);
AstNode *template_node = resolved_type_node->children[0];
- delete children[1];
- children.pop_back();
// Ensure typedef itself is fully simplified
- while(template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+ while (template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+
+ if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) {
+ // replace with wire representing the packed structure
+ newNode = make_packed_struct(template_node, str);
+ newNode->attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
+ newNode->type = type;
+ current_scope[str] = this;
+ // copy param value, it needs to be 1st value
+ delete children[1];
+ children.pop_back();
+ newNode->children.insert(newNode->children.begin(), children[0]->clone());
+ goto apply_newNode;
+ }
+ delete children[1];
+ children.pop_back();
if (template_node->type == AST_MEMORY)
log_file_error(filename, location.first_line, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str());
@@ -1687,7 +2041,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (name_has_dot(str, sname)) {
if (current_scope.count(str) > 0) {
auto item_node = current_scope[str];
- if (item_node->type == AST_STRUCT_ITEM) {
+ if (item_node->type == AST_STRUCT_ITEM || item_node->type == AST_STRUCT) {
// structure member, rewrite this node to reference the packed struct wire
auto range = make_struct_member_range(this, item_node);
newNode = new AstNode(AST_IDENTIFIER, range);
@@ -2010,6 +2364,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
{
expand_genblock(str + ".");
+ // if this is an autonamed block is in an always_comb
+ if (current_always && current_always->attributes.count(ID::always_comb)
+ && is_autonamed_block(str))
+ // track local variables in this block so we can consider adding
+ // nosync once the block has been fully elaborated
+ for (AstNode *child : children)
+ if (child->type == AST_WIRE &&
+ !child->attributes.count(ID::nosync))
+ mark_auto_nosync(this, child);
+
std::vector<AstNode*> new_children;
for (size_t i = 0; i < children.size(); i++)
if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) {
@@ -2363,6 +2727,18 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
current_ast_mod->children.push_back(wire_data);
+ int shamt_width_hint = -1;
+ bool shamt_sign_hint = true;
+ shift_expr->detectSignWidth(shamt_width_hint, shamt_sign_hint);
+
+ AstNode *wire_sel = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(shamt_width_hint-1, true), mkconst_int(0, true)));
+ wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", filename.c_str(), location.first_line, autoidx++);
+ wire_sel->attributes[ID::nosync] = AstNode::mkconst_int(1, false);
+ wire_sel->is_logic = true;
+ wire_sel->is_signed = shamt_sign_hint;
+ while (wire_sel->simplify(true, false, false, 1, -1, false, false)) { }
+ current_ast_mod->children.push_back(wire_sel);
+
did_something = true;
newNode = new AstNode(AST_BLOCK);
@@ -2379,39 +2755,44 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
ref_data->id2ast = wire_data;
ref_data->was_checked = true;
+ AstNode *ref_sel = new AstNode(AST_IDENTIFIER);
+ ref_sel->str = wire_sel->str;
+ ref_sel->id2ast = wire_sel;
+ ref_sel->was_checked = true;
+
AstNode *old_data = lvalue->clone();
if (type == AST_ASSIGN_LE)
old_data->lookahead = true;
- AstNode *shamt = shift_expr;
+ AstNode *s = new AstNode(AST_ASSIGN_EQ, ref_sel->clone(), shift_expr);
+ newNode->children.push_back(s);
- int shamt_width_hint = 0;
- bool shamt_sign_hint = true;
- shamt->detectSignWidth(shamt_width_hint, shamt_sign_hint);
+ AstNode *shamt = ref_sel;
+ // convert to signed while preserving the sign and value
+ shamt = new AstNode(AST_CAST_SIZE, mkconst_int(shamt_width_hint + 1, true), shamt);
+ shamt = new AstNode(AST_TO_SIGNED, shamt);
+
+ // offset the shift amount by the lower bound of the dimension
int start_bit = children[0]->id2ast->range_right;
- bool use_shift = shamt_sign_hint;
+ shamt = new AstNode(AST_SUB, shamt, mkconst_int(start_bit, true));
- if (start_bit != 0) {
- shamt = new AstNode(AST_SUB, shamt, mkconst_int(start_bit, true));
- use_shift = true;
- }
+ // reflect the shift amount if the dimension is swapped
+ if (children[0]->id2ast->range_swapped)
+ shamt = new AstNode(AST_SUB, mkconst_int(source_width - result_width, true), shamt);
+
+ // AST_SHIFT uses negative amounts for shifting left
+ shamt = new AstNode(AST_NEG, shamt);
AstNode *t;
t = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false);
- if (use_shift)
- t = new AstNode(AST_SHIFT, t, new AstNode(AST_NEG, shamt->clone()));
- else
- t = new AstNode(AST_SHIFT_LEFT, t, shamt->clone());
+ t = new AstNode(AST_SHIFT, t, shamt->clone());
t = new AstNode(AST_ASSIGN_EQ, ref_mask->clone(), t);
newNode->children.push_back(t);
t = new AstNode(AST_BIT_AND, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), children[1]->clone());
- if (use_shift)
- t = new AstNode(AST_SHIFT, t, new AstNode(AST_NEG, shamt));
- else
- t = new AstNode(AST_SHIFT_LEFT, t, shamt);
+ t = new AstNode(AST_SHIFT, t, shamt);
t = new AstNode(AST_ASSIGN_EQ, ref_data->clone(), t);
newNode->children.push_back(t);
@@ -4045,7 +4426,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
// prefix is carried forward, but resolution of their children is deferred
void AstNode::expand_genblock(const std::string &prefix)
{
- if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) {
+ if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE || type == AST_PREFIX) {
log_assert(!str.empty());
// search starting in the innermost scope and then stepping outward
@@ -4131,10 +4512,15 @@ void AstNode::expand_genblock(const std::string &prefix)
for (size_t i = 0; i < children.size(); i++) {
AstNode *child = children[i];
- // AST_PREFIX member names should not be prefixed; a nested AST_PREFIX
- // still needs to recursed-into
- if (type == AST_PREFIX && i == 1 && child->type == AST_IDENTIFIER)
+ // AST_PREFIX member names should not be prefixed; we recurse into them
+ // as normal to ensure indices and ranges are properly resolved, and
+ // then restore the previous string
+ if (type == AST_PREFIX && i == 1) {
+ std::string backup_scope_name = child->str;
+ child->expand_genblock(prefix);
+ child->str = backup_scope_name;
continue;
+ }
// functions/tasks may reference wires, constants, etc. in this scope
if (child->type == AST_FUNCTION || child->type == AST_TASK)
continue;
@@ -4788,6 +5174,8 @@ bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia
width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width);
}
offset -= variables.at(str).offset;
+ if (variables.at(str).range_swapped)
+ offset = -offset;
std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits;
std::vector<RTLIL::State> new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width);
AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed);
@@ -4845,7 +5233,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
log_file_error(filename, location.first_line, "Incompatible re-declaration of constant function wire %s.\n", stmt->str.c_str());
}
variable.val = RTLIL::Const(RTLIL::State::Sx, width);
- variable.offset = min(stmt->range_left, stmt->range_right);
+ variable.offset = stmt->range_swapped ? stmt->range_left : stmt->range_right;
+ variable.range_swapped = stmt->range_swapped;
variable.is_signed = stmt->is_signed;
variable.explicitly_sized = stmt->children.size() &&
stmt->children.back()->type == AST_RANGE;
@@ -4930,8 +5319,12 @@ AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed)
int width = std::abs(range->range_left - range->range_right) + 1;
varinfo_t &v = variables[stmt->children.at(0)->str];
RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size());
- for (int i = 0; i < width; i++)
- v.val.bits.at(i+offset-v.offset) = r.bits.at(i);
+ for (int i = 0; i < width; i++) {
+ int index = i + offset - v.offset;
+ if (v.range_swapped)
+ index = -index;
+ v.val.bits.at(index) = r.bits.at(i);
+ }
}
delete block->children.front();
diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc
index 50c25abda..1aab81015 100644
--- a/frontends/json/jsonparse.cc
+++ b/frontends/json/jsonparse.cc
@@ -60,10 +60,38 @@ struct JsonNode
break;
if (ch == '\\') {
- int ch = f.get();
+ ch = f.get();
- if (ch == EOF)
- log_error("Unexpected EOF in JSON string.\n");
+ switch (ch) {
+ case EOF: log_error("Unexpected EOF in JSON string.\n"); break;
+ case '"':
+ case '/':
+ case '\\': break;
+ case 'b': ch = '\b'; break;
+ case 'f': ch = '\f'; break;
+ case 'n': ch = '\n'; break;
+ case 'r': ch = '\r'; break;
+ case 't': ch = '\t'; break;
+ case 'u':
+ int val = 0;
+ for (int i = 0; i < 4; i++) {
+ ch = f.get();
+ val <<= 4;
+ if (ch >= '0' && '9' >= ch) {
+ val += ch - '0';
+ } else if (ch >= 'A' && 'F' >= ch) {
+ val += 10 + ch - 'A';
+ } else if (ch >= 'a' && 'f' >= ch) {
+ val += 10 + ch - 'a';
+ } else
+ log_error("Unexpected non-digit character in \\uXXXX sequence: %c.\n", ch);
+ }
+ if (val < 128)
+ ch = val;
+ else
+ log_error("Unsupported \\uXXXX sequence in JSON string: %04X.\n", val);
+ break;
+ }
}
data_string += ch;
diff --git a/frontends/rtlil/rtlil_parser.y b/frontends/rtlil/rtlil_parser.y
index 67aeb10e0..7d99b2c42 100644
--- a/frontends/rtlil/rtlil_parser.y
+++ b/frontends/rtlil/rtlil_parser.y
@@ -22,6 +22,8 @@
*
*/
+%require "3.0"
+
%{
#include <list>
#include "frontends/rtlil/rtlil_frontend.h"
diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc
index 972f4f9f1..c82428613 100644
--- a/frontends/verific/Makefile.inc
+++ b/frontends/verific/Makefile.inc
@@ -10,9 +10,11 @@ EXTRA_TARGETS += share/verific
share/verific:
$(P) rm -rf share/verific.new
$(Q) mkdir -p share/verific.new
+ifneq ($(DISABLE_VERIFIC_VHDL),1)
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008
+endif
$(Q) chmod -R a+rX share/verific.new
$(Q) mv share/verific.new share/verific
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
index 99094f099..328593099 100644
--- a/frontends/verific/verific.cc
+++ b/frontends/verific/verific.cc
@@ -43,13 +43,16 @@ USING_YOSYS_NAMESPACE
#endif
#include "veri_file.h"
-#include "vhdl_file.h"
#include "hier_tree.h"
#include "VeriModule.h"
#include "VeriWrite.h"
-#include "VhdlUnits.h"
#include "VeriLibrary.h"
+#ifdef VERIFIC_VHDL_SUPPORT
+#include "vhdl_file.h"
+#include "VhdlUnits.h"
+#endif
+
#ifdef YOSYSHQ_VERIFIC_EXTENSIONS
#include "InitialAssertions.h"
#endif
@@ -166,7 +169,10 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
FOREACH_ATTRIBUTE(obj, mi, attr) {
if (attr->Key()[0] == ' ' || attr->Value() == nullptr)
continue;
- attributes[RTLIL::escape_id(attr->Key())] = RTLIL::Const(std::string(attr->Value()));
+ std::string val = std::string(attr->Value());
+ if (val.size()>1 && val[0]=='\"' && val.back()=='\"')
+ val = val.substr(1,val.size()-2);
+ attributes[RTLIL::escape_id(attr->Key())] = RTLIL::Const(val);
}
if (nl) {
@@ -175,8 +181,10 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
return;
if (!type_range->IsTypeEnum())
return;
+#ifdef VERIFIC_VHDL_SUPPORT
if (nl->IsFromVhdl() && strcmp(type_range->GetTypeName(), "STD_LOGIC") == 0)
return;
+#endif
auto type_name = type_range->GetTypeName();
if (!type_name)
return;
@@ -193,7 +201,7 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
p = nullptr;
else
for (auto q = p+2; *q != '\0'; q++)
- if (*q != '0' && *q != '1') {
+ if (*q != '0' && *q != '1' && *q != 'x' && *q != 'z') {
p = nullptr;
break;
}
@@ -202,6 +210,7 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
log_error("Expected TypeRange value '%s' to be of form <decimal>'b<binary>.\n", v);
attributes.emplace(stringf("\\enum_value_%s", p+2), RTLIL::escape_id(k));
}
+#ifdef VERIFIC_VHDL_SUPPORT
else if (nl->IsFromVhdl()) {
// Expect "<binary>" or plain <binary>
auto p = v;
@@ -237,6 +246,7 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
if (p == nullptr)
log_error("Expected TypeRange value '%s' to be of form \"<binary>\" or <binary>.\n", v);
}
+#endif
}
}
}
@@ -371,7 +381,7 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr
return true;
}
- if (inst->Type() == PRIM_TRI) {
+ if ((inst->Type() == PRIM_TRI) || (inst->Type() == PRIM_BUFIF1)) {
module->addMuxGate(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
return true;
}
@@ -410,6 +420,42 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr
return true;
}
+ if (inst->Type() == PRIM_DLATCHRS)
+ {
+ if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
+ module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ else
+ module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetSet()), net_map_at(inst->GetReset()),
+ net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DFF)
+ {
+ VerificClocking clocking(this, inst->GetClock());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ if (inst->GetAsyncCond()->IsGnd())
+ clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ else
+ clocking.addAldff(inst_name, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()),
+ net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DLATCH)
+ {
+ if (inst->GetAsyncCond()->IsGnd()) {
+ module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ } else {
+ RTLIL::SigSpec sig_set = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()));
+ RTLIL::SigSpec sig_clr = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), module->Not(NEW_ID, net_map_at(inst->GetAsyncVal())));
+ module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_clr, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ }
+ return true;
+ }
+
return false;
}
@@ -471,7 +517,7 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
return true;
}
- if (inst->Type() == PRIM_TRI) {
+ if ((inst->Type() == PRIM_TRI) || (inst->Type() == PRIM_BUFIF1)) {
cell = module->addMux(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
import_attributes(cell->attributes, inst);
return true;
@@ -520,6 +566,34 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
return true;
}
+ if (inst->Type() == PRIM_DFF)
+ {
+ VerificClocking clocking(this, inst->GetClock());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ if (inst->GetAsyncCond()->IsGnd())
+ cell = clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ else
+ cell = clocking.addAldff(inst_name, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()),
+ net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DLATCH)
+ {
+ if (inst->GetAsyncCond()->IsGnd()) {
+ cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ } else {
+ RTLIL::SigSpec sig_set = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()));
+ RTLIL::SigSpec sig_clr = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), module->Not(NEW_ID, net_map_at(inst->GetAsyncVal())));
+ cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_clr, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ }
+ import_attributes(cell->attributes, inst);
+ return true;
+ }
+
#define IN operatorInput(inst)
#define IN1 operatorInput1(inst)
#define IN2 operatorInput2(inst)
@@ -727,28 +801,14 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
}
if (inst->Type() == OPER_NTO1MUX) {
- cell = module->addShr(inst_name, IN2, IN1, net_map_at(inst->GetOutput()));
+ cell = module->addBmux(inst_name, IN2, IN1, net_map_at(inst->GetOutput()));
import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_NTO1MUX)
{
- SigSpec data = IN2, out = OUT;
-
- int wordsize_bits = ceil_log2(GetSize(out));
- int wordsize = 1 << wordsize_bits;
-
- SigSpec sel = {IN1, SigSpec(State::S0, wordsize_bits)};
-
- SigSpec padded_data;
- for (int i = 0; i < GetSize(data); i += GetSize(out)) {
- SigSpec d = data.extract(i, GetSize(out));
- d.extend_u0(wordsize);
- padded_data.append(d);
- }
-
- cell = module->addShr(inst_name, padded_data, sel, out);
+ cell = module->addBmux(inst_name, IN2, IN1, OUT);
import_attributes(cell->attributes, inst);
return true;
}
@@ -792,6 +852,74 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
return true;
}
+ if (inst->Type() == OPER_WIDE_DLATCHRS)
+ {
+ RTLIL::SigSpec sig_set = operatorInport(inst, "set");
+ RTLIL::SigSpec sig_reset = operatorInport(inst, "reset");
+
+ if (sig_set.is_fully_const() && !sig_set.as_bool() && sig_reset.is_fully_const() && !sig_reset.as_bool())
+ cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), IN, OUT);
+ else
+ cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_reset, IN, OUT);
+ import_attributes(cell->attributes, inst);
+
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_DFF)
+ {
+ VerificClocking clocking(this, inst->GetClock());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ RTLIL::SigSpec sig_d = IN;
+ RTLIL::SigSpec sig_q = OUT;
+ RTLIL::SigSpec sig_adata = IN1;
+ RTLIL::SigSpec sig_acond = IN2;
+
+ if (sig_acond.is_fully_const() && !sig_acond.as_bool()) {
+ cell = clocking.addDff(inst_name, sig_d, sig_q);
+ import_attributes(cell->attributes, inst);
+ } else {
+ int offset = 0, width = 0;
+ for (offset = 0; offset < GetSize(sig_acond); offset += width) {
+ for (width = 1; offset+width < GetSize(sig_acond); width++)
+ if (sig_acond[offset] != sig_acond[offset+width]) break;
+ cell = clocking.addAldff(module->uniquify(inst_name), sig_acond[offset], sig_adata.extract(offset, width),
+ sig_d.extract(offset, width), sig_q.extract(offset, width));
+ import_attributes(cell->attributes, inst);
+ }
+ }
+
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_DLATCH)
+ {
+ RTLIL::SigSpec sig_d = IN;
+ RTLIL::SigSpec sig_q = OUT;
+ RTLIL::SigSpec sig_adata = IN1;
+ RTLIL::SigSpec sig_acond = IN2;
+
+ if (sig_acond.is_fully_const() && !sig_acond.as_bool()) {
+ cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), sig_d, sig_q);
+ import_attributes(cell->attributes, inst);
+ } else {
+ int offset = 0, width = 0;
+ for (offset = 0; offset < GetSize(sig_acond); offset += width) {
+ for (width = 1; offset+width < GetSize(sig_acond); width++)
+ if (sig_acond[offset] != sig_acond[offset+width]) break;
+ RTLIL::SigSpec sig_set = module->Mux(NEW_ID, RTLIL::SigSpec(0, width), sig_adata.extract(offset, width), sig_acond[offset]);
+ RTLIL::SigSpec sig_clr = module->Mux(NEW_ID, RTLIL::SigSpec(0, width), module->Not(NEW_ID, sig_adata.extract(offset, width)), sig_acond[offset]);
+ cell = module->addDlatchsr(module->uniquify(inst_name), net_map_at(inst->GetControl()), sig_set, sig_clr,
+ sig_d.extract(offset, width), sig_q.extract(offset, width));
+ import_attributes(cell->attributes, inst);
+ }
+ }
+
+ return true;
+ }
+
#undef IN
#undef IN1
#undef IN2
@@ -859,6 +987,7 @@ void VerificImporter::merge_past_ffs(pool<RTLIL::Cell*> &candidates)
for (auto cell : candidates)
{
+ if (cell->type != ID($dff)) continue;
SigBit clock = cell->getPort(ID::CLK);
bool clock_pol = cell->getParam(ID::CLK_POLARITY).as_bool();
database[make_pair(clock, int(clock_pol))].insert(cell);
@@ -883,7 +1012,7 @@ static std::string sha1_if_contain_spaces(std::string str)
return str;
}
-void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo, bool norename)
+void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::map<std::string,Netlist*> &nl_todo, bool norename)
{
std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name();
std::string module_name = netlist_name;
@@ -917,6 +1046,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
} else {
log("Importing module %s.\n", RTLIL::id2cstr(module->name));
}
+ import_attributes(module->attributes, nl, nl);
SetIter si;
MapIter mi, mi2;
@@ -965,18 +1095,28 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex());
import_attributes(wire->attributes, portbus, nl);
- if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN)
+ bool portbus_input = portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN;
+ if (portbus_input)
wire->port_input = true;
if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_OUT)
wire->port_output = true;
for (int i = portbus->LeftIndex();; i += portbus->IsUp() ? +1 : -1) {
if (portbus->ElementAtIndex(i) && portbus->ElementAtIndex(i)->GetNet()) {
+ bool bit_input = portbus_input;
+ if (portbus->GetDir() == DIR_NONE) {
+ Port *p = portbus->ElementAtIndex(i);
+ bit_input = p->GetDir() == DIR_INOUT || p->GetDir() == DIR_IN;
+ if (bit_input)
+ wire->port_input = true;
+ if (p->GetDir() == DIR_INOUT || p->GetDir() == DIR_OUT)
+ wire->port_output = true;
+ }
net = portbus->ElementAtIndex(i)->GetNet();
RTLIL::SigBit bit(wire, i - wire->start_offset);
if (net_map.count(net) == 0)
net_map[net] = bit;
- else if (wire->port_input)
+ else if (bit_input)
module->connect(net_map_at(net), bit);
else
module->connect(bit, net_map_at(net));
@@ -1003,7 +1143,6 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
module->memories[memory->name] = memory;
int number_of_bits = net->Size();
- number_of_bits = 1 << ceil_log2(number_of_bits);
int bits_in_word = number_of_bits;
FOREACH_PORTREF_OF_NET(net, si, pr) {
if (pr->GetInst()->Type() == OPER_READ_PORT) {
@@ -1388,23 +1527,45 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
log_assert(inst->Input1Size() == inst->OutputSize());
- SigSpec sig_d, sig_q, sig_o;
- sig_q = module->addWire(new_verific_id(inst), inst->Input1Size());
+ unsigned width = inst->Input1Size();
+
+ SigSpec sig_d, sig_dx, sig_qx, sig_o, sig_ox;
+ sig_dx = module->addWire(new_verific_id(inst), width * 2);
+ sig_qx = module->addWire(new_verific_id(inst), width * 2);
+ sig_ox = module->addWire(new_verific_id(inst), width * 2);
- for (int i = int(inst->Input1Size())-1; i >= 0; i--){
+ for (int i = int(width)-1; i >= 0; i--){
sig_d.append(net_map_at(inst->GetInput1Bit(i)));
sig_o.append(net_map_at(inst->GetOutputBit(i)));
}
if (verific_verbose) {
+ for (unsigned i = 0; i < width; i++) {
+ log(" NEX with A=%s, B=0, Y=%s.\n",
+ log_signal(sig_d[i]), log_signal(sig_dx[i]));
+ log(" EQX with A=%s, B=1, Y=%s.\n",
+ log_signal(sig_d[i]), log_signal(sig_dx[i + width]));
+ }
log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg",
- log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig));
+ log_signal(sig_dx), log_signal(sig_qx), log_signal(clocking.clock_sig));
log(" XNOR with A=%s, B=%s, Y=%s.\n",
- log_signal(sig_d), log_signal(sig_q), log_signal(sig_o));
+ log_signal(sig_dx), log_signal(sig_qx), log_signal(sig_ox));
+ log(" AND with A=%s, B=%s, Y=%s.\n",
+ log_signal(sig_ox.extract(0, width)), log_signal(sig_ox.extract(width, width)), log_signal(sig_o));
+ }
+
+ for (unsigned i = 0; i < width; i++) {
+ module->addNex(new_verific_id(inst), sig_d[i], State::S0, sig_dx[i]);
+ module->addEqx(new_verific_id(inst), sig_d[i], State::S1, sig_dx[i + width]);
}
- clocking.addDff(new_verific_id(inst), sig_d, sig_q);
- module->addXnor(new_verific_id(inst), sig_d, sig_q, sig_o);
+ Const qx_init = Const(State::S1, width);
+ qx_init.bits.resize(2 * width, State::S0);
+
+ clocking.addDff(new_verific_id(inst), sig_dx, sig_qx, qx_init);
+ module->addXnor(new_verific_id(inst), sig_dx, sig_qx, sig_ox);
+
+ module->addAnd(new_verific_id(inst), sig_ox.extract(0, width), sig_ox.extract(width, width), sig_o);
if (!mode_keep)
continue;
@@ -1418,17 +1579,25 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
SigSpec sig_d = net_map_at(inst->GetInput1());
SigSpec sig_o = net_map_at(inst->GetOutput());
- SigSpec sig_q = module->addWire(new_verific_id(inst));
+ SigSpec sig_dx = module->addWire(new_verific_id(inst), 2);
+ SigSpec sig_qx = module->addWire(new_verific_id(inst), 2);
if (verific_verbose) {
+ log(" NEX with A=%s, B=0, Y=%s.\n",
+ log_signal(sig_d), log_signal(sig_dx[0]));
+ log(" EQX with A=%s, B=1, Y=%s.\n",
+ log_signal(sig_d), log_signal(sig_dx[1]));
log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg",
- log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig));
- log(" XNOR with A=%s, B=%s, Y=%s.\n",
- log_signal(sig_d), log_signal(sig_q), log_signal(sig_o));
+ log_signal(sig_dx), log_signal(sig_qx), log_signal(clocking.clock_sig));
+ log(" EQ with A=%s, B=%s, Y=%s.\n",
+ log_signal(sig_dx), log_signal(sig_qx), log_signal(sig_o));
}
- clocking.addDff(new_verific_id(inst), sig_d, sig_q);
- module->addXnor(new_verific_id(inst), sig_d, sig_q, sig_o);
+ module->addNex(new_verific_id(inst), sig_d, State::S0, sig_dx[0]);
+ module->addEqx(new_verific_id(inst), sig_d, State::S1, sig_dx[1]);
+
+ clocking.addDff(new_verific_id(inst), sig_dx, sig_qx, Const(1, 2));
+ module->addEq(new_verific_id(inst), sig_dx, sig_qx, sig_o);
if (!mode_keep)
continue;
@@ -1462,13 +1631,20 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
SigBit sig_d = net_map_at(inst->GetInput1());
SigBit sig_o = net_map_at(inst->GetOutput());
SigBit sig_q = module->addWire(new_verific_id(inst));
+ SigBit sig_d_no_x = module->addWire(new_verific_id(inst));
- if (verific_verbose)
+ if (verific_verbose) {
+ log(" EQX with A=%s, B=%d, Y=%s.\n",
+ log_signal(sig_d), inst->Type() == PRIM_SVA_ROSE, log_signal(sig_d_no_x));
log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg",
- log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig));
+ log_signal(sig_d_no_x), log_signal(sig_q), log_signal(clocking.clock_sig));
+ log(" EQ with A={%s, %s}, B={0, 1}, Y=%s.\n",
+ log_signal(sig_q), log_signal(sig_d_no_x), log_signal(sig_o));
+ }
- clocking.addDff(new_verific_id(inst), sig_d, sig_q);
- module->addEq(new_verific_id(inst), {sig_q, sig_d}, Const(inst->Type() == PRIM_SVA_ROSE ? 1 : 2, 2), sig_o);
+ module->addEqx(new_verific_id(inst), sig_d, inst->Type() == PRIM_SVA_ROSE ? State::S1 : State::S0, sig_d_no_x);
+ clocking.addDff(new_verific_id(inst), sig_d_no_x, sig_q, State::S0);
+ module->addEq(new_verific_id(inst), {sig_q, sig_d_no_x}, Const(1, 2), sig_o);
if (!mode_keep)
continue;
@@ -1521,10 +1697,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
}
import_verific_cells:
- nl_todo.insert(inst->View());
-
std::string inst_type = inst->View()->Owner()->Name();
+ nl_todo[inst_type] = inst->View();
+
if (inst->View()->IsOperator() || inst->View()->IsPrimitive()) {
inst_type = "$verific$" + inst_type;
} else {
@@ -1734,15 +1910,19 @@ VerificClocking::VerificClocking(VerificImporter *importer, Net *net, bool sva_a
if (inst_mux == nullptr || inst_mux->Type() != PRIM_MUX)
break;
- if (!inst_mux->GetInput1()->IsPwr())
+ bool pwr1 = inst_mux->GetInput1()->IsPwr();
+ bool pwr2 = inst_mux->GetInput2()->IsPwr();
+
+ if (!pwr1 && !pwr2)
break;
- Net *sva_net = inst_mux->GetInput2();
+ Net *sva_net = pwr1 ? inst_mux->GetInput2() : inst_mux->GetInput1();
if (!verific_is_sva_net(importer, sva_net))
break;
body_net = sva_net;
cond_net = inst_mux->GetControl();
+ cond_pol = pwr1;
} while (0);
clock_net = net;
@@ -1757,30 +1937,62 @@ Cell *VerificClocking::addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const
{
log_assert(GetSize(sig_d) == GetSize(sig_q));
- if (GetSize(init_value) != 0) {
- log_assert(GetSize(sig_q) == GetSize(init_value));
- if (sig_q.is_wire()) {
- sig_q.as_wire()->attributes[ID::init] = init_value;
+ auto set_init_attribute = [&](SigSpec &s) {
+ if (GetSize(init_value) == 0)
+ return;
+ log_assert(GetSize(s) == GetSize(init_value));
+ if (s.is_wire()) {
+ s.as_wire()->attributes[ID::init] = init_value;
} else {
- Wire *w = module->addWire(NEW_ID, GetSize(sig_q));
+ Wire *w = module->addWire(NEW_ID, GetSize(s));
w->attributes[ID::init] = init_value;
- module->connect(sig_q, w);
- sig_q = w;
+ module->connect(s, w);
+ s = w;
}
- }
+ };
if (enable_sig != State::S1)
sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig);
if (disable_sig != State::S0) {
- log_assert(gclk == false);
log_assert(GetSize(sig_q) == GetSize(init_value));
+
+ if (gclk) {
+ Wire *pre_d = module->addWire(NEW_ID, GetSize(sig_d));
+ Wire *post_q_w = module->addWire(NEW_ID, GetSize(sig_q));
+
+ Const initval(State::Sx, GetSize(sig_q));
+ int offset = 0;
+ for (auto c : sig_q.chunks()) {
+ if (c.wire && c.wire->attributes.count(ID::init)) {
+ Const val = c.wire->attributes.at(ID::init);
+ for (int i = 0; i < GetSize(c); i++)
+ initval[offset+i] = val[c.offset+i];
+ }
+ offset += GetSize(c);
+ }
+
+ if (!initval.is_fully_undef())
+ post_q_w->attributes[ID::init] = initval;
+
+ module->addMux(NEW_ID, sig_d, init_value, disable_sig, pre_d);
+ module->addMux(NEW_ID, post_q_w, init_value, disable_sig, sig_q);
+
+ SigSpec post_q(post_q_w);
+ set_init_attribute(post_q);
+ return module->addFf(name, pre_d, post_q);
+ }
+
+ set_init_attribute(sig_q);
return module->addAdff(name, clock_sig, disable_sig, sig_d, sig_q, init_value, posedge);
}
- if (gclk)
+ if (gclk) {
+ set_init_attribute(sig_q);
return module->addFf(name, sig_d, sig_q);
+ }
+ set_init_attribute(sig_q);
return module->addDff(name, clock_sig, sig_d, sig_q, posedge);
}
@@ -1789,6 +2001,7 @@ Cell *VerificClocking::addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec s
log_assert(gclk == false);
log_assert(disable_sig == State::S0);
+ // FIXME: Adffe
if (enable_sig != State::S1)
sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig);
@@ -1800,12 +2013,48 @@ Cell *VerificClocking::addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::Si
log_assert(gclk == false);
log_assert(disable_sig == State::S0);
+ // FIXME: Dffsre
if (enable_sig != State::S1)
sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig);
return module->addDffsr(name, clock_sig, sig_set, sig_clr, sig_d, sig_q, posedge);
}
+Cell *VerificClocking::addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL::SigSpec sig_adata, SigSpec sig_d, SigSpec sig_q)
+{
+ log_assert(disable_sig == State::S0);
+
+ // FIXME: Aldffe
+ if (enable_sig != State::S1)
+ sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig);
+
+ if (gclk) {
+ Wire *pre_d = module->addWire(NEW_ID, GetSize(sig_d));
+ Wire *post_q = module->addWire(NEW_ID, GetSize(sig_q));
+
+ Const initval(State::Sx, GetSize(sig_q));
+ int offset = 0;
+ for (auto c : sig_q.chunks()) {
+ if (c.wire && c.wire->attributes.count(ID::init)) {
+ Const val = c.wire->attributes.at(ID::init);
+ for (int i = 0; i < GetSize(c); i++)
+ initval[offset+i] = val[c.offset+i];
+ }
+ offset += GetSize(c);
+ }
+
+ if (!initval.is_fully_undef())
+ post_q->attributes[ID::init] = initval;
+
+ module->addMux(NEW_ID, sig_d, sig_adata, sig_aload, pre_d);
+ module->addMux(NEW_ID, post_q, sig_adata, sig_aload, sig_q);
+
+ return module->addFf(name, pre_d, post_q);
+ }
+
+ return module->addAldff(name, clock_sig, sig_aload, sig_d, sig_q, sig_adata, posedge);
+}
+
// ==================================================================
struct VerificExtNets
@@ -1834,7 +2083,7 @@ struct VerificExtNets
string name = stringf("___extnets_%d", portname_cnt++);
Port *new_port = new Port(name.c_str(), drive_up ? DIR_OUT : DIR_IN);
nl->Add(new_port);
- net->Connect(new_port);
+ nl->Buf(net)->Connect(new_port);
// create new Net in up Netlist
Net *new_net = final_net;
@@ -1950,13 +2199,15 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
{
verific_sva_fsm_limit = 16;
- std::set<Netlist*> nl_todo, nl_done;
+ std::map<std::string,Netlist*> nl_todo, nl_done;
- VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1);
VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1);
Array *netlists = NULL;
Array veri_libs, vhdl_libs;
+#ifdef VERIFIC_VHDL_SUPPORT
+ VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1);
if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
+#endif
if (veri_lib) veri_libs.InsertLast(veri_lib);
Map verific_params(STRING_HASH);
@@ -1987,12 +2238,13 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
}
}
+#ifdef VERIFIC_VHDL_SUPPORT
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);
}
@@ -2000,10 +2252,10 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
int i;
FOREACH_ARRAY_ITEM(netlists, i, nl) {
- if (top.empty() && nl->CellBaseName() != top)
+ if (!top.empty() && nl->CellBaseName() != top)
continue;
nl->AddAtt(new Att(" \\top", NULL));
- nl_todo.insert(nl);
+ nl_todo.emplace(nl->CellBaseName(), nl);
}
delete netlists;
@@ -2012,25 +2264,32 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par
log_error("%s\n", verific_error_msg.c_str());
for (auto nl : nl_todo)
- nl->ChangePortBusStructures(1 /* hierarchical */);
+ nl.second->ChangePortBusStructures(1 /* hierarchical */);
VerificExtNets worker;
for (auto nl : nl_todo)
- worker.run(nl);
+ worker.run(nl.second);
while (!nl_todo.empty()) {
- Netlist *nl = *nl_todo.begin();
- if (nl_done.count(nl) == 0) {
+ auto it = nl_todo.begin();
+ Netlist *nl = it->second;
+ 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() == top);
}
- nl_todo.erase(nl);
- nl_done.insert(nl);
+ nl_todo.erase(it);
}
+ hier_tree::DeleteHierarchicalTree();
veri_file::Reset();
+#ifdef VERIFIC_VHDL_SUPPORT
vhdl_file::Reset();
+#endif
Libset::Reset();
+ Message::Reset();
+ RuntimeFlags::DeleteAllFlags();
+ LineFile::DeleteAllLineFiles();
verific_incdirs.clear();
verific_libdirs.clear();
verific_import_pending = false;
@@ -2072,7 +2331,7 @@ struct VerificPass : public Pass {
log("\n");
log("Additional -D<macro>[=<value>] options may be added after the option indicating\n");
log("the language version (and before file names) to set additional verilog defines.\n");
- log("The macros SYNTHESIS and VERIFIC are defined implicitly.\n");
+ log("The macros YOSYS, SYNTHESIS, and VERIFIC are defined implicitly.\n");
log("\n");
log("\n");
log(" verific -formal <verilog-file>..\n");
@@ -2080,34 +2339,43 @@ struct VerificPass : public Pass {
log("Like -sv, but define FORMAL instead of SYNTHESIS.\n");
log("\n");
log("\n");
+#ifdef VERIFIC_VHDL_SUPPORT
log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} <vhdl-file>..\n");
log("\n");
log("Load the specified VHDL files into Verific.\n");
log("\n");
log("\n");
- log(" verific {-f|-F} <command-file>\n");
+#endif
+ log(" verific {-f|-F} [-vlog95|-vlog2k|-sv2005|-sv2009|\n");
+ log(" -sv2012|-sv|-formal] <command-file>\n");
log("\n");
log("Load and execute the specified command file.\n");
- log("\n");
- log("Command file parser supports following commands:\n");
- log(" +define - defines macro\n");
- log(" -u - upper case all identifier (makes Verilog parser case insensitive)\n");
- log(" -v - register library name (file)\n");
- log(" -y - register library name (directory)\n");
- log(" +incdir - specify include dir\n");
- log(" +libext - specify library extension\n");
- log(" +liborder - add library in ordered list\n");
- log(" +librescan - unresolved modules will be always searched starting with the first\n");
- log(" library specified by -y/-v options.\n");
- log(" -f/-file - nested -f option\n");
- log(" -F - nested -F option\n");
- log("\n");
- log(" parse mode:\n");
+ log("Override verilog parsing mode can be set.\n");
+ log("The macros YOSYS, SYNTHESIS/FORMAL, and VERIFIC are defined implicitly.\n");
+ log("\n");
+ log("Command file parser supports following commands in file:\n");
+ log(" +define+<MACRO>=<VALUE> - defines macro\n");
+ log(" -u - upper case all identifier (makes Verilog parser\n");
+ log(" case insensitive)\n");
+ log(" -v <filepath> - register library name (file)\n");
+ log(" -y <filepath> - register library name (directory)\n");
+ log(" +incdir+<filepath> - specify include dir\n");
+ log(" +libext+<filepath> - specify library extension\n");
+ log(" +liborder+<id> - add library in ordered list\n");
+ log(" +librescan - unresolved modules will be always searched\n");
+ log(" starting with the first library specified\n");
+ log(" by -y/-v options.\n");
+ log(" -f/-file <filepath> - nested -f option\n");
+ log(" -F <filepath> - nested -F option (relative path)\n");
+ log(" parse files:\n");
+ log(" <filepath>\n");
+ log(" +systemverilogext+<filepath>\n");
+ log(" +verilog1995ext+<filepath>\n");
+ log(" +verilog2001ext+<filepath>\n");
+ log("\n");
+ log(" analysis mode:\n");
log(" -ams\n");
- log(" +systemverilogext\n");
log(" +v2k\n");
- log(" +verilog1995ext\n");
- log(" +verilog2001ext\n");
log(" -sverilog\n");
log("\n");
log("\n");
@@ -2244,8 +2512,8 @@ struct VerificPass : public Pass {
log(" Parameter can also contain comma separated list of file locations.\n");
log("\n");
log(" -blfile <file>\n");
- log(" Do not run application on locations specified in file, they can represent filename\n");
- log(" or filename and location in file.\n");
+ log(" Do not run application on locations specified in file, they can\n");
+ log(" represent filename or filename and location in file.\n");
log("\n");
log("Applications:\n");
log("\n");
@@ -2282,6 +2550,13 @@ struct VerificPass : public Pass {
log(" WARNING: Templates only available in commercial build.\n");
log("\n");
#endif
+ log("\n");
+ log("\n");
+ log(" verific -cfg [<name> [<value>]]\n");
+ log("\n");
+ log("Get/set Verific runtime flags.\n");
+ log("\n");
+ log("\n");
log("Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n");
log("https://www.yosyshq.com/\n");
log("\n");
@@ -2310,24 +2585,33 @@ struct VerificPass : public Pass {
Message::SetConsoleOutput(0);
Message::RegisterCallBackMsg(msg_func);
+ RuntimeFlags::SetVar("db_preserve_user_instances", 1);
RuntimeFlags::SetVar("db_preserve_user_nets", 1);
+ RuntimeFlags::SetVar("db_preserve_x", 1);
+
RuntimeFlags::SetVar("db_allow_external_nets", 1);
RuntimeFlags::SetVar("db_infer_wide_operators", 1);
+ RuntimeFlags::SetVar("db_infer_set_reset_registers", 0);
RuntimeFlags::SetVar("veri_extract_dualport_rams", 0);
RuntimeFlags::SetVar("veri_extract_multiport_rams", 1);
+ RuntimeFlags::SetVar("veri_allow_any_ram_in_loop", 1);
+#ifdef VERIFIC_VHDL_SUPPORT
RuntimeFlags::SetVar("vhdl_extract_dualport_rams", 0);
RuntimeFlags::SetVar("vhdl_extract_multiport_rams", 1);
+ RuntimeFlags::SetVar("vhdl_allow_any_ram_in_loop", 1);
RuntimeFlags::SetVar("vhdl_support_variable_slice", 1);
RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0);
- RuntimeFlags::SetVar("veri_preserve_assignments", 1);
RuntimeFlags::SetVar("vhdl_preserve_assignments", 1);
-
- RuntimeFlags::SetVar("veri_preserve_comments",1);
- //RuntimeFlags::SetVar("vhdl_preserve_comments",1);
+ //RuntimeFlags::SetVar("vhdl_preserve_comments", 1);
+ RuntimeFlags::SetVar("vhdl_preserve_drivers", 1);
+#endif
+ RuntimeFlags::SetVar("veri_preserve_assignments", 1);
+ RuntimeFlags::SetVar("veri_preserve_comments", 1);
+ RuntimeFlags::SetVar("veri_preserve_drivers", 1);
// Workaround for VIPER #13851
RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1);
@@ -2338,6 +2622,8 @@ struct VerificPass : public Pass {
// https://github.com/YosysHQ/yosys/issues/1055
RuntimeFlags::SetVar("veri_elaborate_top_level_modules_having_interface_ports", 1) ;
+ RuntimeFlags::SetVar("verific_produce_verbose_syntax_error_message", 1);
+
#ifndef DB_PRESERVE_INITIAL_VALUE
# warning Verific was built without DB_PRESERVE_INITIAL_VALUE.
#endif
@@ -2436,14 +2722,54 @@ struct VerificPass : public Pass {
if (GetSize(args) > argidx && (args[argidx] == "-f" || args[argidx] == "-F"))
{
- unsigned verilog_mode = veri_file::VERILOG_95; // default recommended by Verific
+ unsigned verilog_mode = veri_file::UNDEFINED;
+ bool is_formal = false;
+ const char* filename = nullptr;
Verific::veri_file::f_file_flags flags = (args[argidx] == "-f") ? veri_file::F_FILE_NONE : veri_file::F_FILE_CAPITAL;
- Array *file_names = veri_file::ProcessFFile(args[++argidx].c_str(), flags, verilog_mode);
+ for (argidx++; argidx < GetSize(args); argidx++) {
+ if (args[argidx] == "-vlog95") {
+ verilog_mode = veri_file::VERILOG_95;
+ continue;
+ } else if (args[argidx] == "-vlog2k") {
+ verilog_mode = veri_file::VERILOG_2K;
+ continue;
+ } else if (args[argidx] == "-sv2005") {
+ verilog_mode = veri_file::SYSTEM_VERILOG_2005;
+ continue;
+ } else if (args[argidx] == "-sv2009") {
+ verilog_mode = veri_file::SYSTEM_VERILOG_2009;
+ continue;
+ } else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal") {
+ verilog_mode = veri_file::SYSTEM_VERILOG;
+ if (args[argidx] == "-formal") is_formal = true;
+ continue;
+ } else if (args[argidx].compare(0, 1, "-") == 0) {
+ cmd_error(args, argidx, "unknown option");
+ goto check_error;
+ }
+
+ if (!filename) {
+ filename = args[argidx].c_str();
+ continue;
+ } else {
+ log_cmd_error("Only one filename can be specified.\n");
+ }
+ }
+ if (!filename)
+ log_cmd_error("Filname must be specified.\n");
+
+ unsigned analysis_mode = verilog_mode; // keep default as provided by user if not defined in file
+ Array *file_names = veri_file::ProcessFFile(filename, flags, analysis_mode);
+ if (analysis_mode != verilog_mode)
+ log_warning("Provided verilog mode differs from one specified in file.\n");
+
+ veri_file::DefineMacro("YOSYS");
veri_file::DefineMacro("VERIFIC");
+ veri_file::DefineMacro(is_formal ? "FORMAL" : "SYNTHESIS");
- if (!veri_file::AnalyzeMultipleFiles(file_names, verilog_mode, work.c_str(), veri_file::MFCU)) {
+ if (!veri_file::AnalyzeMultipleFiles(file_names, analysis_mode, work.c_str(), veri_file::MFCU)) {
verific_error_msg.clear();
log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n");
}
@@ -2472,6 +2798,7 @@ struct VerificPass : public Pass {
else
log_abort();
+ veri_file::DefineMacro("YOSYS");
veri_file::DefineMacro("VERIFIC");
veri_file::DefineMacro(args[argidx] == "-formal" ? "FORMAL" : "SYNTHESIS");
@@ -2509,6 +2836,7 @@ struct VerificPass : public Pass {
goto check_error;
}
+#ifdef VERIFIC_VHDL_SUPPORT
if (GetSize(args) > argidx && args[argidx] == "-vhdl87") {
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str());
for (argidx++; argidx < GetSize(args); argidx++)
@@ -2544,6 +2872,7 @@ struct VerificPass : public Pass {
verific_import_pending = true;
goto check_error;
}
+#endif
#ifdef YOSYSHQ_VERIFIC_FORMALAPPS
if (argidx < GetSize(args) && args[argidx] == "-app")
@@ -2646,10 +2975,12 @@ struct VerificPass : public Pass {
const char* module = nullptr;
bool mode_vhdl = false;
for (argidx++; argidx < GetSize(args); argidx++) {
+#ifdef VERIFIC_VHDL_SUPPORT
if (args[argidx] == "-vhdl") {
mode_vhdl = true;
continue;
}
+#endif
if (args[argidx] == "-verilog") {
mode_vhdl = false;
continue;
@@ -2676,7 +3007,11 @@ struct VerificPass : public Pass {
log_cmd_error("Filname must be specified.\n");
if (mode_vhdl)
+#ifdef VERIFIC_VHDL_SUPPORT
vhdl_file::PrettyPrint(filename, module, work.c_str());
+#else
+ goto check_error;
+#endif
else
veri_file::PrettyPrint(filename, module, work.c_str());
goto check_error;
@@ -2697,7 +3032,7 @@ struct VerificPass : public Pass {
if (!(argidx+1 < GetSize(args)))
cmd_error(args, argidx+1, "No top module specified.\n");
generator->setLogger([](std::string msg) { log("%s",msg.c_str()); } );
-
+
std::string module = args[++argidx];
VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
VeriModule *veri_module = veri_lib ? veri_lib->GetModule(module.c_str(), 1) : nullptr;
@@ -2769,7 +3104,7 @@ struct VerificPass : public Pass {
#endif
if (GetSize(args) > argidx && args[argidx] == "-import")
{
- std::set<Netlist*> nl_todo, nl_done;
+ std::map<std::string,Netlist*> nl_todo, nl_done;
bool mode_all = false, mode_gates = false, mode_keep = false;
bool mode_nosva = false, mode_names = false, mode_verific = false;
bool mode_autocover = false, mode_fullinit = false;
@@ -2858,11 +3193,13 @@ struct VerificPass : public Pass {
{
log("Running hier_tree::ElaborateAll().\n");
- VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1);
Array veri_libs, vhdl_libs;
+#ifdef VERIFIC_VHDL_SUPPORT
+ VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
+#endif
if (veri_lib) veri_libs.InsertLast(veri_lib);
Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &parameters);
@@ -2870,7 +3207,7 @@ struct VerificPass : public Pass {
int i;
FOREACH_ARRAY_ITEM(netlists, i, nl)
- nl_todo.insert(nl);
+ nl_todo.emplace(nl->CellBaseName(), nl);
delete netlists;
}
else
@@ -2879,7 +3216,9 @@ struct VerificPass : public Pass {
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 veri_modules, vhdl_units;
for (; argidx < GetSize(args); argidx++)
@@ -2893,14 +3232,14 @@ struct VerificPass : public Pass {
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);
}
@@ -2920,8 +3259,10 @@ struct VerificPass : public Pass {
int i;
FOREACH_ARRAY_ITEM(netlists, i, nl) {
+ if (!top_mod_names.count(nl->CellBaseName()))
+ continue;
nl->AddAtt(new Att(" \\top", NULL));
- nl_todo.insert(nl);
+ nl_todo.emplace(nl->CellBaseName(), nl);
}
delete netlists;
}
@@ -2931,17 +3272,17 @@ struct VerificPass : public Pass {
if (flatten) {
for (auto nl : nl_todo)
- nl->Flatten();
+ nl.second->Flatten();
}
if (extnets) {
VerificExtNets worker;
for (auto nl : nl_todo)
- worker.run(nl);
+ worker.run(nl.second);
}
for (auto nl : nl_todo)
- nl->ChangePortBusStructures(1 /* hierarchical */);
+ nl.second->ChangePortBusStructures(1 /* hierarchical */);
if (!dumpfile.empty()) {
VeriWrite veri_writer;
@@ -2949,25 +3290,91 @@ struct VerificPass : public Pass {
}
while (!nl_todo.empty()) {
- Netlist *nl = *nl_todo.begin();
- if (nl_done.count(nl) == 0) {
+ auto it = nl_todo.begin();
+ Netlist *nl = it->second;
+ if (nl_done.count(it->first) == 0) {
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()));
}
- nl_todo.erase(nl);
- nl_done.insert(nl);
+ nl_todo.erase(it);
}
+ hier_tree::DeleteHierarchicalTree();
veri_file::Reset();
+#ifdef VERIFIC_VHDL_SUPPORT
vhdl_file::Reset();
+#endif
Libset::Reset();
+ Message::Reset();
+ RuntimeFlags::DeleteAllFlags();
+ LineFile::DeleteAllLineFiles();
verific_incdirs.clear();
verific_libdirs.clear();
verific_import_pending = false;
goto check_error;
}
+ if (argidx < GetSize(args) && args[argidx] == "-cfg")
+ {
+ if (argidx+1 == GetSize(args)) {
+ MapIter mi;
+ const char *k, *s;
+ unsigned long v;
+ pool<std::string> lines;
+ FOREACH_MAP_ITEM(RuntimeFlags::GetVarMap(), mi, &k, &v) {
+ lines.insert(stringf("%s %lu", k, v));
+ }
+ FOREACH_MAP_ITEM(RuntimeFlags::GetStringVarMap(), mi, &k, &s) {
+ if (s == nullptr)
+ lines.insert(stringf("%s NULL", k));
+ else
+ lines.insert(stringf("%s \"%s\"", k, s));
+ }
+ lines.sort();
+ for (auto &line : lines)
+ log("verific -cfg %s\n", line.c_str());
+ goto check_error;
+ }
+
+ if (argidx+2 == GetSize(args)) {
+ const char *k = args[argidx+1].c_str();
+ if (RuntimeFlags::HasUnsignedVar(k)) {
+ log("verific -cfg %s %lu\n", k, RuntimeFlags::GetVar(k));
+ goto check_error;
+ }
+ if (RuntimeFlags::HasStringVar(k)) {
+ const char *s = RuntimeFlags::GetStringVar(k);
+ if (s == nullptr)
+ log("verific -cfg %s NULL\n", k);
+ else
+ log("verific -cfg %s \"%s\"\n", k, s);
+ goto check_error;
+ }
+ log_cmd_error("Can't find Verific Runtime flag '%s'.\n", k);
+ }
+
+ if (argidx+3 == GetSize(args)) {
+ const auto &k = args[argidx+1], &v = args[argidx+2];
+ if (v == "NULL") {
+ RuntimeFlags::SetStringVar(k.c_str(), nullptr);
+ goto check_error;
+ }
+ if (v[0] == '"') {
+ std::string s = v.substr(1, GetSize(v)-2);
+ RuntimeFlags::SetStringVar(k.c_str(), v.c_str());
+ goto check_error;
+ }
+ char *endptr;
+ unsigned long n = strtol(v.c_str(), &endptr, 0);
+ if (*endptr == 0) {
+ RuntimeFlags::SetVar(k.c_str(), n);
+ goto check_error;
+ }
+ }
+ }
+
cmd_error(args, argidx, "Missing or unsupported mode parameter.\n");
check_error:
@@ -3003,11 +3410,13 @@ struct ReadPass : public Pass {
log("the language version (and before file names) to set additional verilog defines.\n");
log("\n");
log("\n");
+#ifdef VERIFIC_VHDL_SUPPORT
log(" read {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} <vhdl-file>..\n");
log("\n");
log("Load the specified VHDL files. (Requires Verific.)\n");
log("\n");
log("\n");
+#endif
log(" read {-f|-F} <command-file>\n");
log("\n");
log("Load and execute the specified command file. (Requires Verific.)\n");
@@ -3090,6 +3499,7 @@ struct ReadPass : public Pass {
return;
}
+#ifdef VERIFIC_VHDL_SUPPORT
if (args[1] == "-vhdl87" || args[1] == "-vhdl93" || args[1] == "-vhdl2k" || args[1] == "-vhdl2008" || args[1] == "-vhdl") {
if (use_verific) {
args[0] = "verific";
@@ -3099,7 +3509,7 @@ struct ReadPass : public Pass {
}
return;
}
-
+#endif
if (args[1] == "-f" || args[1] == "-F") {
if (use_verific) {
args[0] = "verific";
diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h
index f79d8042a..695c04f3b 100644
--- a/frontends/verific/verific.h
+++ b/frontends/verific/verific.h
@@ -44,12 +44,14 @@ struct VerificClocking {
SigBit disable_sig = State::S0;
bool posedge = true;
bool gclk = false;
+ bool cond_pol = true;
VerificClocking() { }
VerificClocking(VerificImporter *importer, Verific::Net *net, bool sva_at_only = false);
RTLIL::Cell *addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const init_value = Const());
RTLIL::Cell *addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec sig_d, SigSpec sig_q, Const arst_value);
RTLIL::Cell *addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, SigSpec sig_d, SigSpec sig_q);
+ RTLIL::Cell *addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL::SigSpec sig_adata, SigSpec sig_d, SigSpec sig_q);
bool property_matches_sequence(const VerificClocking &seq) const {
if (clock_net != seq.clock_net)
@@ -93,7 +95,7 @@ struct VerificImporter
void merge_past_ffs_clock(pool<RTLIL::Cell*> &candidates, SigBit clock, bool clock_pol);
void merge_past_ffs(pool<RTLIL::Cell*> &candidates);
- void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set<Verific::Netlist*> &nl_todo, bool norename = false);
+ void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::map<std::string,Verific::Netlist*> &nl_todo, bool norename = false);
};
void verific_import_sva_assert(VerificImporter *importer, Verific::Instance *inst);
diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc
index 1bbdcf016..12bac2a3d 100644
--- a/frontends/verific/verificsva.cc
+++ b/frontends/verific/verificsva.cc
@@ -1522,10 +1522,13 @@ struct VerificSvaImporter
if (inst == nullptr)
return false;
- if (clocking.cond_net != nullptr)
+ if (clocking.cond_net != nullptr) {
trig = importer->net_map_at(clocking.cond_net);
- else
+ if (!clocking.cond_pol)
+ trig = module->Not(NEW_ID, trig);
+ } else {
trig = State::S1;
+ }
if (inst->Type() == PRIM_SVA_S_EVENTUALLY || inst->Type() == PRIM_SVA_EVENTUALLY)
{
@@ -1587,8 +1590,11 @@ struct VerificSvaImporter
SigBit trig = State::S1;
- if (clocking.cond_net != nullptr)
+ if (clocking.cond_net != nullptr) {
trig = importer->net_map_at(clocking.cond_net);
+ if (!clocking.cond_pol)
+ trig = module->Not(NEW_ID, trig);
+ }
if (inst == nullptr)
{
diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc
index 17f567587..883531e78 100644
--- a/frontends/verilog/preproc.cc
+++ b/frontends/verilog/preproc.cc
@@ -142,6 +142,16 @@ static std::string next_token(bool pass_newline = false)
return_char(ch);
}
}
+ else if (ch == '\\')
+ {
+ while ((ch = next_char()) != 0) {
+ if (ch < 33 || ch > 126) {
+ return_char(ch);
+ break;
+ }
+ token += ch;
+ }
+ }
else if (ch == '/')
{
if ((ch = next_char()) != 0) {
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index 89c1aa895..958809319 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -128,6 +128,11 @@ static bool isUserType(std::string &s)
%x IMPORT_DPI
%x BASED_CONST
+UNSIGNED_NUMBER [0-9][0-9_]*
+FIXED_POINT_NUMBER_DEC [0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)?
+FIXED_POINT_NUMBER_NO_DEC [0-9][0-9_]*[eE][-+]?[0-9_]+
+TIME_SCALE_SUFFIX [munpf]?s
+
%%
// Initialise comment_caller to something to avoid a "maybe undefined"
// warning from GCC.
@@ -297,7 +302,7 @@ static bool isUserType(std::string &s)
"union" { SV_KEYWORD(TOK_UNION); }
"packed" { SV_KEYWORD(TOK_PACKED); }
-[0-9][0-9_]* {
+{UNSIGNED_NUMBER} {
yylval->string = new std::string(yytext);
return TOK_CONSTVAL;
}
@@ -319,12 +324,12 @@ static bool isUserType(std::string &s)
return TOK_BASED_CONSTVAL;
}
-[0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)? {
+{FIXED_POINT_NUMBER_DEC} {
yylval->string = new std::string(yytext);
return TOK_REALVAL;
}
-[0-9][0-9_]*[eE][-+]?[0-9_]+ {
+{FIXED_POINT_NUMBER_NO_DEC} {
yylval->string = new std::string(yytext);
return TOK_REALVAL;
}
@@ -574,6 +579,10 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
return TOK_SPECIFY_AND;
}
+{UNSIGNED_NUMBER}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; }
+{FIXED_POINT_NUMBER_DEC}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; }
+{FIXED_POINT_NUMBER_NO_DEC}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; }
+
<INITIAL,BASED_CONST>"/*" { comment_caller=YY_START; BEGIN(COMMENT); }
<COMMENT>. /* ignore comment body */
<COMMENT>\n /* ignore comment body */
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index 8d0ba4cf6..c533b0c40 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -33,6 +33,8 @@
*
*/
+%require "3.0"
+
%{
#include <list>
#include <stack>
@@ -367,7 +369,7 @@ static void rewriteGenForDeclInit(AstNode *loop)
%token TOK_BIT_OR_ASSIGN TOK_BIT_AND_ASSIGN TOK_BIT_XOR_ASSIGN TOK_ADD_ASSIGN
%token TOK_SUB_ASSIGN TOK_DIV_ASSIGN TOK_MOD_ASSIGN TOK_MUL_ASSIGN
%token TOK_SHL_ASSIGN TOK_SHR_ASSIGN TOK_SSHL_ASSIGN TOK_SSHR_ASSIGN
-%token TOK_BIND
+%token TOK_BIND TOK_TIME_SCALE
%type <ast> range range_or_multirange non_opt_range non_opt_multirange
%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type
@@ -777,6 +779,9 @@ non_opt_delay:
'#' TOK_ID { delete $2; } |
'#' TOK_CONSTVAL { delete $2; } |
'#' TOK_REALVAL { delete $2; } |
+ // our `expr` doesn't have time_scale, so we need the parenthesized variant
+ '#' TOK_TIME_SCALE |
+ '#' '(' TOK_TIME_SCALE ')' |
'#' '(' mintypmax_expr ')' |
'#' '(' mintypmax_expr ',' mintypmax_expr ')' |
'#' '(' mintypmax_expr ',' mintypmax_expr ',' mintypmax_expr ')';
@@ -832,16 +837,10 @@ opt_wire_type_token:
wire_type_token | %empty;
wire_type_token:
- TOK_WOR {
- astbuf3->is_wor = true;
- } |
- TOK_WAND {
- astbuf3->is_wand = true;
+ // nets
+ net_type {
} |
- // wires
- TOK_WIRE {
- } |
- TOK_WIRE logic_type {
+ net_type logic_type {
} |
// regs
TOK_REG {
@@ -868,6 +867,15 @@ wire_type_token:
astbuf3->range_right = 0;
};
+net_type:
+ TOK_WOR {
+ astbuf3->is_wor = true;
+ } |
+ TOK_WAND {
+ astbuf3->is_wand = true;
+ } |
+ TOK_WIRE;
+
logic_type:
TOK_LOGIC {
} |
@@ -2674,6 +2682,7 @@ for_initialization:
AstNode *node = new AstNode(AST_ASSIGN_EQ, ident, $3);
ast_stack.back()->children.push_back(node);
SET_AST_NODE_LOC(node, @1, @3);
+ delete $1;
} |
non_io_wire_type range TOK_ID {
frontend_verilog_yyerror("For loop variable declaration is missing initialization!");
@@ -2973,6 +2982,7 @@ rvalue:
hierarchical_id '[' expr ']' '.' rvalue {
$$ = new AstNode(AST_PREFIX, $3, $6);
$$->str = *$1;
+ SET_AST_NODE_LOC($$, @1, @6);
delete $1;
} |
hierarchical_id range {