summaryrefslogtreecommitdiff
path: root/frontends
diff options
context:
space:
mode:
Diffstat (limited to 'frontends')
-rw-r--r--frontends/ast/ast.cc94
-rw-r--r--frontends/ast/ast.h33
-rw-r--r--frontends/ast/genrtlil.cc209
-rw-r--r--frontends/ast/simplify.cc482
-rw-r--r--frontends/blif/blifparse.cc159
-rw-r--r--frontends/blif/blifparse.h3
-rw-r--r--frontends/ilang/.gitignore2
-rw-r--r--frontends/ilang/Makefile.inc7
-rw-r--r--frontends/ilang/ilang_frontend.cc4
-rw-r--r--frontends/ilang/ilang_lexer.l2
-rw-r--r--frontends/json/Makefile.inc3
-rw-r--r--frontends/json/jsonparse.cc541
-rw-r--r--frontends/liberty/liberty.cc73
-rw-r--r--frontends/verific/Makefile.inc3
-rw-r--r--frontends/verific/README62
-rw-r--r--frontends/verific/build_amd64.txt30
-rw-r--r--frontends/verific/example.sby16
-rw-r--r--frontends/verific/example.sv18
-rw-r--r--frontends/verific/test_navre.ys18
-rw-r--r--frontends/verific/verific.cc1870
-rw-r--r--frontends/verific/verific.h106
-rw-r--r--frontends/verific/verificsva.cc1814
-rw-r--r--frontends/verilog/.gitignore2
-rw-r--r--frontends/verilog/Makefile.inc7
-rw-r--r--frontends/verilog/const2ast.cc8
-rw-r--r--frontends/verilog/preproc.cc189
-rw-r--r--frontends/verilog/verilog_frontend.cc121
-rw-r--r--frontends/verilog/verilog_frontend.h3
-rw-r--r--frontends/verilog/verilog_lexer.l55
-rw-r--r--frontends/verilog/verilog_parser.y440
-rw-r--r--frontends/vhdl2verilog/Makefile.inc1
-rw-r--r--frontends/vhdl2verilog/vhdl2verilog.cc183
32 files changed, 5586 insertions, 972 deletions
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 92513a24..e79be953 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -44,7 +44,7 @@ namespace AST {
// instanciate global variables (private API)
namespace AST_INTERNAL {
- bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
+ bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;
AstNode *current_ast, *current_ast_mod;
std::map<std::string, AstNode*> current_scope;
@@ -84,6 +84,9 @@ std::string AST::type2str(AstNodeType type)
X(AST_PREFIX)
X(AST_ASSERT)
X(AST_ASSUME)
+ X(AST_LIVE)
+ X(AST_FAIR)
+ X(AST_COVER)
X(AST_FCALL)
X(AST_TO_BITS)
X(AST_TO_SIGNED)
@@ -168,8 +171,8 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id)
AstNode *attr = attributes.at(id);
if (attr->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- id.c_str(), attr->filename.c_str(), attr->linenum);
+ log_file_error(attr->filename, attr->linenum, "Attribute `%s' with non-constant value!\n",
+ id.c_str());
return attr->integer != 0;
}
@@ -188,8 +191,10 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
is_input = false;
is_output = false;
is_reg = false;
+ is_logic = false;
is_signed = false;
is_string = false;
+ was_checked = false;
range_valid = false;
range_swapped = false;
port_id = 0;
@@ -209,7 +214,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
}
// create a (deep recursive) copy of a node
-AstNode *AstNode::clone()
+AstNode *AstNode::clone() const
{
AstNode *that = new AstNode;
*that = *this;
@@ -221,7 +226,7 @@ AstNode *AstNode::clone()
}
// create a (deep recursive) copy of a node use 'other' as target root node
-void AstNode::cloneInto(AstNode *other)
+void AstNode::cloneInto(AstNode *other) const
{
AstNode *tmp = clone();
other->delete_children();
@@ -251,7 +256,7 @@ AstNode::~AstNode()
// create a nice text representation of the node
// (traverse tree by recursion, use 'other' pointer for diffing two AST trees)
-void AstNode::dumpAst(FILE *f, std::string indent)
+void AstNode::dumpAst(FILE *f, std::string indent) const
{
if (f == NULL) {
for (auto f : log_files)
@@ -262,10 +267,12 @@ void AstNode::dumpAst(FILE *f, std::string indent)
std::string type_name = type2str(type);
fprintf(f, "%s%s <%s:%d>", indent.c_str(), type_name.c_str(), filename.c_str(), linenum);
- if (id2ast)
- fprintf(f, " [%p -> %p]", this, id2ast);
- else
- fprintf(f, " [%p]", this);
+ if (!flag_no_dump_ptr) {
+ if (id2ast)
+ fprintf(f, " [%p -> %p]", this, id2ast);
+ else
+ fprintf(f, " [%p]", this);
+ }
if (!str.empty())
fprintf(f, " str='%s'", str.c_str());
@@ -282,7 +289,9 @@ void AstNode::dumpAst(FILE *f, std::string indent)
fprintf(f, " input");
if (is_output)
fprintf(f, " output");
- if (is_reg)
+ if (is_logic)
+ fprintf(f, " logic");
+ if (is_reg) // this is an AST dump, not Verilog - if we see "logic reg" that's fine.
fprintf(f, " reg");
if (is_signed)
fprintf(f, " signed");
@@ -330,7 +339,7 @@ static std::string id2vl(std::string txt)
}
// dump AST node as Verilog pseudo-code
-void AstNode::dumpVlog(FILE *f, std::string indent)
+void AstNode::dumpVlog(FILE *f, std::string indent) const
{
bool first = true;
std::string txt;
@@ -649,6 +658,8 @@ bool AstNode::operator==(const AstNode &other) const
return false;
if (is_output != other.is_output)
return false;
+ if (is_logic != other.is_logic)
+ return false;
if (is_reg != other.is_reg)
return false;
if (is_signed != other.is_signed)
@@ -752,7 +763,7 @@ AstNode *AstNode::mkconst_str(const std::string &str)
return node;
}
-bool AstNode::bits_only_01()
+bool AstNode::bits_only_01() const
{
for (auto bit : bits)
if (bit != RTLIL::S0 && bit != RTLIL::S1)
@@ -803,7 +814,7 @@ RTLIL::Const AstNode::asParaConst()
return val;
}
-bool AstNode::asBool()
+bool AstNode::asBool() const
{
log_assert(type == AST_CONSTANT);
for (auto &bit : bits)
@@ -812,7 +823,7 @@ bool AstNode::asBool()
return false;
}
-int AstNode::isConst()
+int AstNode::isConst() const
{
if (type == AST_CONSTANT)
return 1;
@@ -952,8 +963,8 @@ static AstModule* process_module(AstNode *ast, bool defer)
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), ast->filename.c_str(), ast->linenum);
+ log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
current_module->attributes[attr.first] = attr.second->asAttrConst();
}
for (size_t i = 0; i < ast->children.size(); i++) {
@@ -999,12 +1010,13 @@ static AstModule* process_module(AstNode *ast, bool defer)
}
// create AstModule instances for all modules in the AST tree and add them to 'design'
-void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool dump_rtlil,
- bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire)
+void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog, bool dump_rtlil,
+ bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire)
{
current_ast = ast;
flag_dump_ast1 = dump_ast1;
flag_dump_ast2 = dump_ast2;
+ flag_no_dump_ptr = no_dump_ptr;
flag_dump_vlog = dump_vlog;
flag_dump_rtlil = dump_rtlil;
flag_nolatches = nolatches;
@@ -1016,14 +1028,12 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
flag_icells = icells;
flag_autowire = autowire;
- std::vector<AstNode*> global_decls;
-
log_assert(current_ast->type == AST_DESIGN);
for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++)
{
if ((*it)->type == AST_MODULE)
{
- for (auto n : global_decls)
+ for (auto n : design->verilog_globals)
(*it)->children.push_back(n->clone());
for (auto n : design->verilog_packages){
@@ -1041,12 +1051,20 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
(*it)->str = "$abstract" + (*it)->str;
if (design->has((*it)->str)) {
- if (!ignore_redef)
- log_error("Re-definition of module `%s' at %s:%d!\n",
+ RTLIL::Module *existing_mod = design->module((*it)->str);
+ if (!nooverwrite && !overwrite && !existing_mod->get_bool_attribute("\\blackbox")) {
+ log_file_error((*it)->filename, (*it)->linenum, "Re-definition of module `%s'!\n",
+ (*it)->str.c_str());
+ } else if (nooverwrite) {
+ log("Ignoring re-definition of module `%s' at %s:%d.\n",
(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
- log("Ignoring re-definition of module `%s' at %s:%d!\n",
- (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
- continue;
+ continue;
+ } else {
+ log("Replacing existing%s module `%s' at %s:%d.\n",
+ existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "",
+ (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
+ design->remove(existing_mod);
+ }
}
design->add(process_module(*it, defer));
@@ -1054,7 +1072,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
else if ((*it)->type == AST_PACKAGE)
design->verilog_packages.push_back((*it)->clone());
else
- global_decls.push_back(*it);
+ design->verilog_globals.push_back((*it)->clone());
}
}
@@ -1066,7 +1084,7 @@ AstModule::~AstModule()
}
// create a new parametric module (when needed) and return the name of the generated module
-RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters)
+RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool)
{
std::string stripped_name = name.str();
@@ -1105,7 +1123,10 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
rewrite_parameter:
para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
delete child->children.at(0);
- child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0);
+ if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0)
+ child->children[0] = AstNode::mkconst_str(parameters[para_id].decode_string());
+ else
+ child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0);
parameters.erase(para_id);
continue;
}
@@ -1115,8 +1136,16 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
goto rewrite_parameter;
}
}
- if (parameters.size() > 0)
- log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), stripped_name.c_str());
+
+ for (auto param : parameters) {
+ AstNode *defparam = new AstNode(AST_DEFPARAM, new AstNode(AST_IDENTIFIER));
+ defparam->children[0]->str = param.first.str();
+ if ((param.second.flags & RTLIL::CONST_FLAG_STRING) != 0)
+ defparam->children.push_back(AstNode::mkconst_str(param.second.decode_string()));
+ else
+ defparam->children.push_back(AstNode::mkconst_bits(param.second.bits, (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0));
+ new_ast->children.push_back(defparam);
+ }
std::string modname;
@@ -1177,4 +1206,3 @@ void AST::use_internal_line_num()
}
YOSYS_NAMESPACE_END
-
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index cd6e264e..7e97bdb3 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -1,4 +1,4 @@
-/*
+/* -*- c++ -*-
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
@@ -65,6 +65,9 @@ namespace AST
AST_PREFIX,
AST_ASSERT,
AST_ASSUME,
+ AST_LIVE,
+ AST_FAIR,
+ AST_COVER,
AST_FCALL,
AST_TO_BITS,
@@ -165,7 +168,7 @@ namespace AST
// node content - most of it is unused in most node types
std::string str;
std::vector<RTLIL::State> bits;
- bool is_input, is_output, is_reg, is_signed, is_string, range_valid, range_swapped;
+ bool is_input, is_output, is_reg, is_logic, is_signed, is_string, range_valid, range_swapped, was_checked;
int port_id, range_left, range_right;
uint32_t integer;
double realvalue;
@@ -187,8 +190,8 @@ namespace AST
// creating and deleting nodes
AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL);
- AstNode *clone();
- void cloneInto(AstNode *other);
+ AstNode *clone() const;
+ void cloneInto(AstNode *other) const;
void delete_children();
~AstNode();
@@ -231,8 +234,8 @@ namespace AST
AstNode *eval_const_function(AstNode *fcall);
// create a human-readable text representation of the AST (for debugging)
- void dumpAst(FILE *f, std::string indent);
- void dumpVlog(FILE *f, std::string indent);
+ void dumpAst(FILE *f, std::string indent) const;
+ void dumpVlog(FILE *f, std::string indent) const;
// used by genRTLIL() for detecting expression width and sign
void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL);
@@ -261,27 +264,27 @@ namespace AST
RTLIL::Const asAttrConst();
RTLIL::Const asParaConst();
uint64_t asInt(bool is_signed);
- bool bits_only_01();
- bool asBool();
+ bool bits_only_01() const;
+ bool asBool() const;
// helper functions for real valued const eval
- int isConst(); // return '1' for AST_CONSTANT and '2' for AST_REALVALUE
+ int isConst() const; // return '1' for AST_CONSTANT and '2' for AST_REALVALUE
double asReal(bool is_signed);
RTLIL::Const realAsConst(int width);
};
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
- void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool dump_rtlil, bool nolatches, bool nomeminit,
- bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire);
+ void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog, bool dump_rtlil, bool nolatches, bool nomeminit,
+ bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire);
// parametric modules are supported directly by the AST library
// therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
struct AstModule : RTLIL::Module {
AstNode *ast;
bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire;
- virtual ~AstModule();
- virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters);
- virtual RTLIL::Module *clone() const;
+ ~AstModule() YS_OVERRIDE;
+ RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
+ RTLIL::Module *clone() const YS_OVERRIDE;
};
// this must be set by the language frontend before parsing the sources
@@ -302,7 +305,7 @@ namespace AST
namespace AST_INTERNAL
{
// internal state variables
- extern bool flag_dump_ast1, flag_dump_ast2, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
+ extern bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;
extern AST::AstNode *current_ast, *current_ast_mod;
extern std::map<std::string, AST::AstNode*> current_scope;
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index db8d7409..0f7e910f 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -55,8 +55,8 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_wi
if (gen_attributes)
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), that->filename.c_str(), that->linenum);
+ log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -89,8 +89,8 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s
if (that != NULL)
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), that->filename.c_str(), that->linenum);
+ log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -117,8 +117,8 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_wi
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), that->filename.c_str(), that->linenum);
+ log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -152,8 +152,8 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), that->filename.c_str(), that->linenum);
+ log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -207,8 +207,8 @@ struct AST_INTERNAL::ProcessGenerator
proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->linenum, autoidx++);
for (auto &attr : always->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), always->filename.c_str(), always->linenum);
+ log_file_error(always->filename, always->linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
proc->attributes[attr.first] = attr.second->asAttrConst();
}
current_module->processes[proc->name] = proc;
@@ -223,16 +223,22 @@ struct AST_INTERNAL::ProcessGenerator
bool found_global_syncs = false;
bool found_anyedge_syncs = false;
for (auto child : always->children)
+ {
+ if ((child->type == AST_POSEDGE || child->type == AST_NEGEDGE) && GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER &&
+ child->children.at(0)->id2ast && child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute("\\gclk")) {
+ found_global_syncs = true;
+ }
if (child->type == AST_EDGE) {
if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->str == "\\$global_clock")
found_global_syncs = true;
else
found_anyedge_syncs = true;
}
+ }
if (found_anyedge_syncs) {
if (found_global_syncs)
- log_error("Found non-synthesizable event list at %s:%d!\n", always->filename.c_str(), always->linenum);
+ log_file_error(always->filename, always->linenum, "Found non-synthesizable event list!\n");
log("Note: Assuming pure combinatorial block at %s:%d in\n", always->filename.c_str(), always->linenum);
log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n");
log("use of @* instead of @(...) for better match of synthesis and simulation.\n");
@@ -242,14 +248,17 @@ struct AST_INTERNAL::ProcessGenerator
bool found_clocked_sync = false;
for (auto child : always->children)
if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) {
+ if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->id2ast &&
+ child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute("\\gclk"))
+ continue;
found_clocked_sync = true;
if (found_global_syncs || found_anyedge_syncs)
- log_error("Found non-synthesizable event list at %s:%d!\n", always->filename.c_str(), always->linenum);
+ log_file_error(always->filename, always->linenum, "Found non-synthesizable event list!\n");
RTLIL::SyncRule *syncrule = new RTLIL::SyncRule;
syncrule->type = child->type == AST_POSEDGE ? RTLIL::STp : RTLIL::STn;
syncrule->signal = child->children[0]->genRTLIL();
if (GetSize(syncrule->signal) != 1)
- log_error("Found posedge/negedge event on a signal that is not 1 bit wide at %s:%d!\n", always->filename.c_str(), always->linenum);
+ log_file_error(always->filename, always->linenum, "Found posedge/negedge event on a signal that is not 1 bit wide!\n");
addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true);
proc->syncs.push_back(syncrule);
}
@@ -471,8 +480,8 @@ struct AST_INTERNAL::ProcessGenerator
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), ast->filename.c_str(), ast->linenum);
+ log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
sw->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -540,12 +549,12 @@ struct AST_INTERNAL::ProcessGenerator
break;
case AST_WIRE:
- log_error("Found wire declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum);
+ log_file_error(ast->filename, ast->linenum, "Found wire declaration in block without label!\n");
break;
case AST_PARAMETER:
case AST_LOCALPARAM:
- log_error("Found parameter declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum);
+ log_file_error(ast->filename, ast->linenum, "Found parameter declaration in block without label!\n");
break;
case AST_NONE:
@@ -554,6 +563,8 @@ struct AST_INTERNAL::ProcessGenerator
break;
default:
+ // ast->dumpAst(NULL, "ast> ");
+ // current_ast_mod->dumpAst(NULL, "mod> ");
log_abort();
}
}
@@ -591,7 +602,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (id_ast == NULL && current_scope.count(str))
id_ast = current_scope.at(str);
if (!id_ast)
- log_error("Failed to resolve identifier %s for width detection at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to resolve identifier %s for width detection!\n", str.c_str());
if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM) {
if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) {
this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1;
@@ -601,7 +612,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (id_ast->children[0]->type == AST_CONSTANT)
this_width = id_ast->children[0]->bits.size();
else
- log_error("Failed to detect width for parameter %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to detect width for parameter %s!\n", str.c_str());
if (children.size() != 0)
range = children[0];
} else if (id_ast->type == AST_WIRE || id_ast->type == AST_AUTOWIRE) {
@@ -613,7 +624,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
// log("---\n");
// id_ast->dumpAst(NULL, "decl> ");
// dumpAst(NULL, "ref> ");
- log_error("Failed to detect width of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to detect width of signal access `%s'!\n", str.c_str());
}
} else {
this_width = id_ast->range_left - id_ast->range_right + 1;
@@ -624,10 +635,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
this_width = 32;
} else if (id_ast->type == AST_MEMORY) {
if (!id_ast->children[0]->range_valid)
- log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str());
this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
} else
- log_error("Failed to detect width for identifier %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str());
if (range) {
if (range->children.size() == 1)
this_width = 1;
@@ -637,8 +648,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
- log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n",
+ str.c_str());
this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
delete left_at_zero_ast;
delete right_at_zero_ast;
@@ -654,7 +665,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
case AST_TO_BITS:
while (children[0]->simplify(true, false, false, 1, -1, false, false) == true) { }
if (children[0]->type != AST_CONSTANT)
- log_error("Left operand of tobits expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Left operand of tobits expression is not constant!\n");
children[1]->detectSignWidthWorker(sub_width_hint, sign_hint);
width_hint = max(width_hint, children[0]->bitsAsConst().as_int());
break;
@@ -682,7 +693,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
case AST_REPLICATE:
while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
if (children[0]->type != AST_CONSTANT)
- log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Left operand of replicate expression is not constant!\n");
children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint);
sign_hint = false;
@@ -756,18 +767,18 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (!id2ast->is_signed)
sign_hint = false;
if (!id2ast->children[0]->range_valid)
- log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str());
this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1;
width_hint = max(width_hint, this_width);
break;
case AST_FCALL:
- if (str == "\\$anyconst" || str == "\\$anyseq") {
+ if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") {
if (GetSize(children) == 1) {
while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
if (children[0]->type != AST_CONSTANT)
- log_error("System function %s called with non-const argument at %s:%d!\n",
- RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s called with non-const argument!\n",
+ RTLIL::unescape_id(str).c_str());
width_hint = max(width_hint, int(children[0]->asInt(true)));
}
break;
@@ -788,8 +799,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
default:
for (auto f : log_files)
current_ast->dumpAst(f, "verilog-ast> ");
- log_error("Don't know how to detect sign and width for %s node at %s:%d!\n",
- type2str(type).c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n",
+ type2str(type).c_str());
}
if (*found_real)
@@ -852,11 +863,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// create an RTLIL::Wire for an AST_WIRE node
case AST_WIRE: {
if (current_module->wires_.count(str) != 0)
- log_error("Re-definition of signal `%s' at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Re-definition of signal `%s'!\n",
+ str.c_str());
if (!range_valid)
- log_error("Signal `%s' with non-constant width at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Signal `%s' with non-constant width!\n",
+ str.c_str());
log_assert(range_left >= range_right || (range_left == -1 && range_right == 0));
@@ -870,8 +881,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
wire->attributes[attr.first] = attr.second->asAttrConst();
}
}
@@ -880,16 +891,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// create an RTLIL::Memory for an AST_MEMORY node
case AST_MEMORY: {
if (current_module->memories.count(str) != 0)
- log_error("Re-definition of memory `%s' at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Re-definition of memory `%s'!\n",
+ str.c_str());
log_assert(children.size() >= 2);
log_assert(children[0]->type == AST_RANGE);
log_assert(children[1]->type == AST_RANGE);
if (!children[0]->range_valid || !children[1]->range_valid)
- log_error("Memory `%s' with non-constant width or size at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Memory `%s' with non-constant width or size!\n",
+ str.c_str());
RTLIL::Memory *memory = new RTLIL::Memory;
memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
@@ -906,8 +917,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
memory->attributes[attr.first] = attr.second->asAttrConst();
}
}
@@ -926,8 +937,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_REALVALUE:
{
RTLIL::SigSpec sig = realAsConst(width_hint);
- log_warning("converting real value %e to binary %s at %s:%d.\n",
- realvalue, log_signal(sig), filename.c_str(), linenum);
+ log_file_warning(filename, linenum, "converting real value %e to binary %s.\n",
+ realvalue, log_signal(sig));
return sig;
}
@@ -947,25 +958,25 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
wire->name = str;
if (flag_autowire)
- log_warning("Identifier `%s' is implicitly declared at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_warning(filename, linenum, "Identifier `%s' is implicitly declared.\n", str.c_str());
else
- log_error("Identifier `%s' is implicitly declared at %s:%d and `default_nettype is set to none.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
}
else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) {
if (id2ast->children[0]->type != AST_CONSTANT)
- log_error("Parameter %s does not evaluate to constant value at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n",
+ str.c_str());
chunk = RTLIL::Const(id2ast->children[0]->bits);
goto use_const_chunk;
}
else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE &&
id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0)
- log_error("Identifier `%s' doesn't map to any signal at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Identifier `%s' doesn't map to any signal!\n",
+ str.c_str());
if (id2ast->type == AST_MEMORY)
- log_error("Identifier `%s' does map to an unexpanded memory at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n",
+ str.c_str());
wire = current_module->wires_[str];
chunk.wire = wire;
@@ -983,8 +994,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
- log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n",
+ str.c_str());
int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?
children[0]->children[1]->clone() : children[0]->children[0]->clone());
@@ -1012,11 +1023,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
chunk.offset = (id2ast->range_left - id2ast->range_right + 1) - (chunk.offset + chunk.width);
if (chunk.offset >= source_width || chunk.offset + chunk.width < 0) {
if (chunk.width == 1)
- log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting result bit to undef.\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting result bit to undef.\n",
+ str.c_str());
else
- log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting all %d result bits to undef.\n",
- str.c_str(), filename.c_str(), linenum, chunk.width);
+ log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting all %d result bits to undef.\n",
+ str.c_str(), chunk.width);
chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width);
} else {
if (chunk.width + chunk.offset > source_width) {
@@ -1029,11 +1040,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
chunk.offset += add_undef_bits_lsb;
}
if (add_undef_bits_lsb)
- log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting %d LSB bits to undef.\n",
- str.c_str(), filename.c_str(), linenum, add_undef_bits_lsb);
+ log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting %d LSB bits to undef.\n",
+ str.c_str(), add_undef_bits_lsb);
if (add_undef_bits_msb)
- log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting %d MSB bits to undef.\n",
- str.c_str(), filename.c_str(), linenum, add_undef_bits_msb);
+ log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting %d MSB bits to undef.\n",
+ str.c_str(), add_undef_bits_msb);
}
}
}
@@ -1072,7 +1083,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
RTLIL::SigSpec left = children[0]->genRTLIL();
RTLIL::SigSpec right = children[1]->genRTLIL();
if (!left.is_fully_const())
- log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Left operand of replicate expression is not constant!\n");
int count = left.as_int();
RTLIL::SigSpec sig;
for (int i = 0; i < count; i++)
@@ -1289,6 +1300,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0);
cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
+ if (!sign_hint)
+ is_signed = false;
+
return RTLIL::SigSpec(wire);
}
@@ -1308,7 +1322,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
int num_words = 1;
if (type == AST_MEMINIT) {
if (children[2]->type != AST_CONSTANT)
- log_error("Memory init with non-constant word count at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Memory init with non-constant word count!\n");
num_words = int(children[2]->asInt(false));
cell->parameters["\\WORDS"] = RTLIL::Const(num_words);
}
@@ -1336,9 +1350,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// generate $assert cells
case AST_ASSERT:
case AST_ASSUME:
+ case AST_LIVE:
+ case AST_FAIR:
+ case AST_COVER:
{
- const char *celltype = "$assert";
+ const char *celltype = nullptr;
+ if (type == AST_ASSERT) celltype = "$assert";
if (type == AST_ASSUME) celltype = "$assume";
+ if (type == AST_LIVE) celltype = "$live";
+ if (type == AST_FAIR) celltype = "$fair";
+ if (type == AST_COVER) celltype = "$cover";
log_assert(children.size() == 2);
@@ -1358,8 +1379,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -1380,10 +1401,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
new_left.append(left[i]);
new_right.append(right[i]);
}
- log_warning("Ignoring assignment to constant bits at %s:%d:\n"
- " old assignment: %s = %s\n new assignment: %s = %s.\n",
- filename.c_str(), linenum, log_signal(left), log_signal(right),
- log_signal(new_left), log_signal(new_right));
+ log_file_warning(filename, linenum, "Ignoring assignment to constant bits:\n"
+ " old assignment: %s = %s\n new assignment: %s = %s.\n",
+ log_signal(left), log_signal(right),
+ log_signal(new_left), log_signal(new_right));
left = new_left;
right = new_right;
}
@@ -1397,8 +1418,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
int port_counter = 0, para_counter = 0;
if (current_module->count_id(str) != 0)
- log_error("Re-definition of cell `%s' at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Re-definition of cell `%s'!\n", str.c_str());
RTLIL::Cell *cell = current_module->addCell(str, "");
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
@@ -1414,16 +1434,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (child->type == AST_PARASET) {
IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str;
if (child->children[0]->type == AST_REALVALUE) {
- log_warning("Replacing floating point parameter %s.%s = %f with string at %s:%d.\n",
- log_id(cell), log_id(paraname), child->children[0]->realvalue,
- filename.c_str(), linenum);
+ log_file_warning(filename, linenum, "Replacing floating point parameter %s.%s = %f with string.\n",
+ log_id(cell), log_id(paraname), child->children[0]->realvalue);
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_error("Parameter %s.%s with non-constant value at %s:%d!\n",
- log_id(cell), log_id(paraname), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Parameter %s.%s with non-constant value!\n",
+ log_id(cell), log_id(paraname));
cell->parameters[paraname] = child->children[0]->asParaConst();
continue;
}
@@ -1444,8 +1463,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
}
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
- log_error("Attribute `%s' with non-constant value at %s:%d!\n",
- attr.first.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n",
+ attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
}
@@ -1466,30 +1485,37 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
} break;
case AST_FCALL: {
- if (str == "\\$anyconst" || str == "\\$anyseq")
+ if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq")
{
string myid = stringf("%s$%d", str.c_str() + 1, autoidx++);
int width = width_hint;
if (GetSize(children) > 1)
- log_error("System function %s got %d arguments, expected 1 or 0 at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), GetSize(children), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 0.\n",
+ RTLIL::unescape_id(str).c_str(), GetSize(children));
if (GetSize(children) == 1) {
if (children[0]->type != AST_CONSTANT)
- log_error("System function %s called with non-const argument at %s:%d!\n",
- RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s called with non-const argument!\n",
+ RTLIL::unescape_id(str).c_str());
width = children[0]->asInt(true);
}
if (width <= 0)
- log_error("Failed to detect width of %s at %s:%d!\n",
- RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to detect width of %s!\n",
+ RTLIL::unescape_id(str).c_str());
Cell *cell = current_module->addCell(myid, str.substr(1));
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
cell->parameters["\\WIDTH"] = width;
+ if (attributes.count("\\reg")) {
+ auto &attr = attributes.at("\\reg");
+ if (attr->type != AST_CONSTANT)
+ log_file_error(filename, linenum, "Attribute `reg' with non-constant value!\n");
+ cell->attributes["\\reg"] = attr->asAttrConst();
+ }
+
Wire *wire = current_module->addWire(myid + "_wire", width);
wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
cell->setPort("\\Y", wire);
@@ -1504,8 +1530,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
for (auto f : log_files)
current_ast->dumpAst(f, "verilog-ast> ");
type_name = type2str(type);
- log_error("Don't know how to generate RTLIL code for %s node at %s:%d!\n",
- type_name.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n",
+ type_name.c_str());
}
return RTLIL::SigSpec();
@@ -1535,4 +1561,3 @@ RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL
}
YOSYS_NAMESPACE_END
-
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 9d5c75fe..04c429f7 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -177,13 +177,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// note that $display, $finish, and $stop are used for synthesis-time DRC so they're not in this list
if ((type == AST_FCALL || type == AST_TCALL) && (str == "$strobe" || str == "$monitor" || str == "$time" ||
str == "$dumpfile" || str == "$dumpvars" || str == "$dumpon" || str == "$dumpoff" || str == "$dumpall")) {
- log_warning("Ignoring call to system %s %s at %s:%d.\n", type == AST_FCALL ? "function" : "task", str.c_str(), filename.c_str(), linenum);
+ log_file_warning(filename, linenum, "Ignoring call to system %s %s.\n", type == AST_FCALL ? "function" : "task", str.c_str());
delete_children();
str = std::string();
}
if ((type == AST_TCALL) && (str == "$display" || str == "$write") && (!current_always || current_always->type != AST_INITIAL)) {
- log_warning("System task `%s' outside initial block is unsupported at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_warning(filename, linenum, "System task `%s' outside initial block is unsupported.\n", str.c_str());
delete_children();
str = std::string();
}
@@ -195,14 +195,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
{
int nargs = GetSize(children);
if (nargs < 1)
- log_error("System task `%s' got %d arguments, expected >= 1 at %s:%d.\n",
- str.c_str(), int(children.size()), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System task `%s' got %d arguments, expected >= 1.\n",
+ str.c_str(), int(children.size()));
// First argument is the format string
AstNode *node_string = children[0];
while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (node_string->type != AST_CONSTANT)
- log_error("Failed to evaluate system task `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant 1st argument.\n", str.c_str());
std::string sformat = node_string->bitsAsConst().decode_string();
// Other arguments are placeholders. Process the string as we go through it
@@ -215,7 +215,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
{
// If there's no next character, that's a problem
if (i+1 >= sformat.length())
- log_error("System task `%s' called with `%%' at end of string at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System task `%s' called with `%%' at end of string.\n", str.c_str());
char cformat = sformat[++i];
@@ -239,13 +239,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
case 'x':
case 'X':
if (next_arg >= GetSize(children))
- log_error("Missing argument for %%%c format specifier in system task `%s' at %s:%d.\n",
- cformat, str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n",
+ cformat, str.c_str());
node_arg = children[next_arg++];
while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (node_arg->type != AST_CONSTANT)
- log_error("Failed to evaluate system task `%s' with non-constant argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str());
break;
case 'm':
@@ -253,7 +253,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
break;
default:
- log_error("System task `%s' called with invalid/unsupported format specifier at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str());
break;
}
@@ -327,6 +327,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (node->type == AST_WIRE) {
if (this_wire_scope.count(node->str) > 0) {
AstNode *first_node = this_wire_scope[node->str];
+ if (first_node->is_input && node->is_reg)
+ goto wires_are_incompatible;
if (!node->is_input && !node->is_output && node->is_reg && node->children.size() == 0)
goto wires_are_compatible;
if (first_node->children.size() == 0 && node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
@@ -361,6 +363,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
first_node->is_output = true;
if (node->is_reg)
first_node->is_reg = true;
+ if (node->is_logic)
+ first_node->is_logic = true;
if (node->is_signed)
first_node->is_signed = true;
for (auto &it : node->attributes) {
@@ -374,7 +378,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
continue;
wires_are_incompatible:
if (stage > 1)
- log_error("Incompatible re-declaration of wire %s at %s:%d.\n", node->str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Incompatible re-declaration of wire %s.\n", node->str.c_str());
continue;
}
this_wire_scope[node->str] = node;
@@ -387,7 +391,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
for (size_t i = 0; i < children.size(); i++) {
AstNode *node = children[i];
- if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE)
+ if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY)
while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM))
did_something = true;
}
@@ -401,13 +405,20 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_ALWAYS || type == AST_INITIAL)
{
+ if (current_always != nullptr)
+ log_file_error(filename, linenum, "Invalid nesting of always blocks and/or initializations.\n");
+
current_always = this;
current_always_clocked = false;
if (type == AST_ALWAYS)
- for (auto child : children)
+ for (auto child : children) {
if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE)
current_always_clocked = true;
+ if (child->type == AST_EDGE && GetSize(child->children) == 1 &&
+ child->children[0]->type == AST_IDENTIFIER && child->children[0]->str == "\\$global_clock")
+ current_always_clocked = true;
+ }
}
int backup_width_hint = width_hint;
@@ -433,6 +444,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
children[1]->detectSignWidth(width_hint, sign_hint);
width_hint = max(width_hint, backup_width_hint);
child_0_is_self_determined = true;
+ // test only once, before optimizations and memory mappings but after assignment LHS was mapped to an identifier
+ if (children[0]->id2ast && !children[0]->was_checked) {
+ if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->id2ast->is_logic)
+ children[0]->id2ast->is_reg = true; // if logic type is used in a block asignment
+ if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && !children[0]->id2ast->is_reg)
+ log_warning("wire '%s' is assigned in a block at %s:%d.\n", children[0]->str.c_str(), filename.c_str(), linenum);
+ if (type == AST_ASSIGN && children[0]->id2ast->is_reg)
+ log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.\n", children[0]->str.c_str(), filename.c_str(), linenum);
+ children[0]->was_checked = true;
+ }
break;
case AST_PARAMETER:
@@ -444,7 +465,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, true) == true)
did_something = true;
if (!children[1]->range_valid)
- log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Non-constant width range on parameter decl.\n");
width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1);
}
break;
@@ -685,26 +706,47 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
current_scope.clear();
// convert defparam nodes to cell parameters
- if (type == AST_DEFPARAM && !str.empty()) {
- size_t pos = str.rfind('.');
+ if (type == AST_DEFPARAM && !children.empty())
+ {
+ if (children[0]->type != AST_IDENTIFIER)
+ log_file_error(filename, linenum, "Module name in defparam contains non-constant expressions!\n");
+
+ string modname, paramname = children[0]->str;
+
+ size_t pos = paramname.rfind('.');
+
+ while (pos != 0 && pos != std::string::npos)
+ {
+ modname = paramname.substr(0, pos);
+
+ if (current_scope.count(modname))
+ break;
+
+ pos = paramname.rfind('.', pos - 1);
+ }
+
if (pos == std::string::npos)
- log_error("Defparam `%s' does not contain a dot (module/parameter separator) at %s:%d!\n",
- RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
- std::string modname = str.substr(0, pos), paraname = "\\" + str.substr(pos+1);
- if (current_scope.count(modname) == 0 || current_scope.at(modname)->type != AST_CELL)
- log_error("Can't find cell for defparam `%s . %s` at %s:%d!\n", RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paraname).c_str(), filename.c_str(), linenum);
- AstNode *cell = current_scope.at(modname), *paraset = clone();
+ log_file_error(filename, linenum, "Can't find object for defparam `%s`!\n", RTLIL::unescape_id(paramname).c_str());
+
+ paramname = "\\" + paramname.substr(pos+1);
+
+ if (current_scope.at(modname)->type != AST_CELL)
+ log_file_error(filename, linenum, "Defparam argument `%s . %s` does not match a cell!\n",
+ RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paramname).c_str());
+
+ AstNode *paraset = new AstNode(AST_PARASET, children[1]->clone(), GetSize(children) > 2 ? children[2]->clone() : NULL);
+ paraset->str = paramname;
+
+ AstNode *cell = current_scope.at(modname);
cell->children.insert(cell->children.begin() + 1, paraset);
- paraset->type = AST_PARASET;
- paraset->str = paraname;
- str.clear();
+ delete_children();
}
// resolve constant prefixes
if (type == AST_PREFIX) {
if (children[0]->type != AST_CONSTANT) {
// dumpAst(NULL, "> ");
- log_error("Index in generate block prefix syntax at %s:%d is not constant!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Index in generate block prefix syntax is not constant!\n");
}
if (children[1]->type == AST_PREFIX)
children[1]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param);
@@ -720,9 +762,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// evaluate TO_BITS nodes
if (type == AST_TO_BITS) {
if (children[0]->type != AST_CONSTANT)
- log_error("Left operand of to_bits expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Left operand of to_bits expression is not constant!\n");
if (children[1]->type != AST_CONSTANT)
- log_error("Right operand of to_bits expression is not constant at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Right operand of to_bits expression is not constant!\n");
RTLIL::Const new_value = children[1]->bitsAsConst(children[0]->bitsAsConst().as_int(), children[1]->is_signed);
newNode = mkconst_bits(new_value.bits, children[1]->is_signed);
goto apply_newNode;
@@ -786,7 +828,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
multirange_dimensions.clear();
for (auto range : children[1]->children) {
if (!range->range_valid)
- log_error("Non-constant range on memory decl at %s:%d.\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Non-constant range on memory decl.\n");
multirange_dimensions.push_back(min(range->range_left, range->range_right));
multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1);
total_size *= multirange_dimensions.back();
@@ -804,7 +846,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
for (int i = 0; 2*i < GetSize(id2ast->multirange_dimensions); i++)
{
if (GetSize(children[0]->children) < i)
- log_error("Insufficient number of array indices for %s at %s:%d.\n", log_id(str), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Insufficient number of array indices for %s.\n", log_id(str));
AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone();
@@ -833,12 +875,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_PARAMETER || type == AST_LOCALPARAM) {
if (children.size() > 1 && children[1]->type == AST_RANGE) {
if (!children[1]->range_valid)
- log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Non-constant width range on parameter decl.\n");
int width = std::abs(children[1]->range_left - children[1]->range_right) + 1;
if (children[0]->type == AST_REALVALUE) {
RTLIL::Const constvalue = children[0]->realAsConst(width);
- log_warning("converting real value %e to binary %s at %s:%d.\n",
- children[0]->realvalue, log_signal(constvalue), filename.c_str(), linenum);
+ log_file_warning(filename, linenum, "converting real value %e to binary %s.\n",
+ children[0]->realvalue, log_signal(constvalue));
delete children[0];
children[0] = mkconst_bits(constvalue.bits, sign_hint);
did_something = true;
@@ -896,7 +938,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue)
{
if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1)
- log_error("Invalid bit-select on memory access at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Invalid bit-select on memory access!\n");
int mem_width, mem_size, addr_bits;
id2ast->meminfo(mem_width, mem_size, addr_bits);
@@ -921,6 +963,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
AstNode *assign = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), data);
assign->children[0]->str = wire_id;
+ assign->children[0]->was_checked = true;
if (current_block)
{
@@ -945,10 +988,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
if (type == AST_WHILE)
- log_error("While loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "While loops are only allowed in constant functions!\n");
if (type == AST_REPEAT)
- log_error("Repeat loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Repeat loops are only allowed in constant functions!\n");
// unroll for loops and generate-for blocks
if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0)
@@ -963,31 +1006,31 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
body_ast = body_ast->children.at(0);
if (init_ast->type != AST_ASSIGN_EQ)
- log_error("Unsupported 1st expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Unsupported 1st expression of generate for-loop!\n");
if (next_ast->type != AST_ASSIGN_EQ)
- log_error("Unsupported 3rd expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Unsupported 3rd expression of generate for-loop!\n");
if (type == AST_GENFOR) {
if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_GENVAR)
- log_error("Left hand side of 1st expression of generate for-loop at %s:%d is not a gen var!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Left hand side of 1st expression of generate for-loop is not a gen var!\n");
if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_GENVAR)
- log_error("Left hand side of 3rd expression of generate for-loop at %s:%d is not a gen var!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Left hand side of 3rd expression of generate for-loop is not a gen var!\n");
} else {
if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_WIRE)
- log_error("Left hand side of 1st expression of generate for-loop at %s:%d is not a register!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Left hand side of 1st expression of generate for-loop is not a register!\n");
if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_WIRE)
- log_error("Left hand side of 3rd expression of generate for-loop at %s:%d is not a register!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Left hand side of 3rd expression of generate for-loop is not a register!\n");
}
if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast)
- log_error("Incompatible left-hand sides in 1st and 3rd expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Incompatible left-hand sides in 1st and 3rd expression of generate for-loop!\n");
// eval 1st expression
AstNode *varbuf = init_ast->children[1]->clone();
while (varbuf->simplify(true, false, false, stage, 32, true, false)) { }
if (varbuf->type != AST_CONSTANT)
- log_error("Right hand side of 1st expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Right hand side of 1st expression of generate for-loop is not constant!\n");
varbuf = new AstNode(AST_LOCALPARAM, varbuf);
varbuf->str = init_ast->children[0]->str;
@@ -1009,7 +1052,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (buf->type != AST_CONSTANT)
- log_error("2nd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "2nd expression of generate for-loop is not constant!\n");
if (buf->integer == 0) {
delete buf;
@@ -1050,7 +1093,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
while (buf->simplify(true, false, false, stage, 32, true, false)) { }
if (buf->type != AST_CONSTANT)
- log_error("Right hand side of 3rd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Right hand side of 3rd expression of generate for-loop is not constant!\n");
delete varbuf->children[0];
varbuf->children[0] = buf;
@@ -1062,6 +1105,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
did_something = true;
}
+ // check for local objects in unnamed block
+ if (type == AST_BLOCK && str.empty())
+ {
+ 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)
+ log_file_error(children[i]->filename, children[i]->linenum, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n");
+ }
+
// transform block with name
if (type == AST_BLOCK && !str.empty())
{
@@ -1070,7 +1121,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
std::vector<AstNode*> new_children;
for (size_t i = 0; i < children.size(); i++)
- if (children[i]->type == AST_WIRE || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) {
+ if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) {
children[i]->simplify(false, false, false, stage, -1, false, false);
current_ast_mod->children.push_back(children[i]);
current_scope[children[i]->str] = children[i];
@@ -1107,7 +1158,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (buf->type != AST_CONSTANT) {
// for (auto f : log_files)
// dumpAst(f, "verilog-ast> ");
- log_error("Condition for generate if at %s:%d is not constant!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Condition for generate if is not constant!\n");
}
if (buf->asBool() != 0) {
delete buf;
@@ -1148,7 +1199,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (buf->type != AST_CONSTANT) {
// for (auto f : log_files)
// dumpAst(f, "verilog-ast> ");
- log_error("Condition for generate case at %s:%d is not constant!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Condition for generate case is not constant!\n");
}
bool ref_signed = buf->is_signed;
@@ -1182,7 +1233,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (buf->type != AST_CONSTANT) {
// for (auto f : log_files)
// dumpAst(f, "verilog-ast> ");
- log_error("Expression in generate case at %s:%d is not constant!\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Expression in generate case is not constant!\n");
}
bool is_selected = RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool();
@@ -1223,7 +1274,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_CELLARRAY)
{
if (!children.at(0)->range_valid)
- log_error("Non-constant array range on cell array at %s:%d.\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Non-constant array range on cell array.\n");
newNode = new AstNode(AST_GENBLOCK);
int num = max(children.at(0)->range_left, children.at(0)->range_right) - min(children.at(0)->range_left, children.at(0)->range_right) + 1;
@@ -1234,7 +1285,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
newNode->children.push_back(new_cell);
new_cell->str += stringf("[%d]", idx);
if (new_cell->type == AST_PRIMITIVE) {
- log_error("Cell arrays of primitives are currently not supported at %s:%d.\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Cell arrays of primitives are currently not supported.\n");
} else {
log_assert(new_cell->children.at(0)->type == AST_CELLTYPE);
new_cell->children.at(0)->str = stringf("$array:%d:%d:%s", i, num, new_cell->children.at(0)->str.c_str());
@@ -1248,8 +1299,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_PRIMITIVE)
{
if (children.size() < 2)
- log_error("Insufficient number of arguments for primitive `%s' at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Insufficient number of arguments for primitive `%s'!\n",
+ str.c_str());
std::vector<AstNode*> children_list;
for (auto child : children) {
@@ -1264,8 +1315,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (str == "bufif0" || str == "bufif1" || str == "notif0" || str == "notif1")
{
if (children_list.size() != 3)
- log_error("Invalid number of arguments for primitive `%s' at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Invalid number of arguments for primitive `%s'!\n",
+ str.c_str());
std::vector<RTLIL::State> z_const(1, RTLIL::State::Sz);
@@ -1350,8 +1401,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
while (left_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
- log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",
- str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n",
+ str.c_str());
result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
}
did_something = true;
@@ -1370,7 +1421,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
skip_dynamic_range_lvalue_expansion:;
- if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && current_block != NULL)
+ if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && current_block != NULL)
{
std::stringstream sstr;
sstr << "$formal$" << filename << ":" << linenum << "$" << (autoidx++);
@@ -1378,16 +1429,19 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *wire_check = new AstNode(AST_WIRE);
wire_check->str = id_check;
+ wire_check->was_checked = true;
current_ast_mod->children.push_back(wire_check);
current_scope[wire_check->str] = wire_check;
while (wire_check->simplify(true, false, false, 1, -1, false, false)) { }
AstNode *wire_en = new AstNode(AST_WIRE);
wire_en->str = id_en;
+ wire_en->was_checked = true;
current_ast_mod->children.push_back(wire_en);
if (current_always_clocked) {
current_ast_mod->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1)))));
current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en;
+ current_ast_mod->children.back()->children[0]->children[0]->children[0]->was_checked = true;
}
current_scope[wire_en->str] = wire_en;
while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
@@ -1397,9 +1451,11 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bit, false));
assign_check->children[0]->str = id_check;
+ assign_check->children[0]->was_checked = true;
AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, 1));
assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
AstNode *default_signals = new AstNode(AST_BLOCK);
default_signals->children.push_back(assign_check);
@@ -1408,6 +1464,7 @@ skip_dynamic_range_lvalue_expansion:;
assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone()));
assign_check->children[0]->str = id_check;
+ assign_check->children[0]->was_checked = true;
if (current_always == nullptr || current_always->type != AST_INITIAL) {
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1));
@@ -1416,6 +1473,7 @@ skip_dynamic_range_lvalue_expansion:;
assign_en->children[1]->str = "\\$initstate";
}
assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
newNode = new AstNode(AST_BLOCK);
newNode->children.push_back(assign_check);
@@ -1432,7 +1490,7 @@ skip_dynamic_range_lvalue_expansion:;
goto apply_newNode;
}
- if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && children.size() == 1)
+ if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && children.size() == 1)
{
children.push_back(mkconst_int(1, false, 1));
did_something = true;
@@ -1524,12 +1582,14 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
wire_addr->str = id_addr;
+ wire_addr->was_checked = true;
current_ast_mod->children.push_back(wire_addr);
current_scope[wire_addr->str] = wire_addr;
while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
wire_data->str = id_data;
+ wire_data->was_checked = true;
wire_data->is_signed = mem_signed;
current_ast_mod->children.push_back(wire_data);
current_scope[wire_data->str] = wire_data;
@@ -1539,6 +1599,7 @@ skip_dynamic_range_lvalue_expansion:;
if (current_always->type != AST_INITIAL) {
wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
wire_en->str = id_en;
+ wire_en->was_checked = true;
current_ast_mod->children.push_back(wire_en);
current_scope[wire_en->str] = wire_en;
while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
@@ -1554,14 +1615,17 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
assign_addr->children[0]->str = id_addr;
+ assign_addr->children[0]->was_checked = true;
AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
assign_data->children[0]->str = id_data;
+ assign_data->children[0]->was_checked = true;
AstNode *assign_en = nullptr;
if (current_always->type != AST_INITIAL) {
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
}
AstNode *default_signals = new AstNode(AST_BLOCK);
@@ -1573,6 +1637,7 @@ skip_dynamic_range_lvalue_expansion:;
assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
assign_addr->children[0]->str = id_addr;
+ assign_addr->children[0]->was_checked = true;
if (children[0]->children.size() == 2)
{
@@ -1587,12 +1652,14 @@ skip_dynamic_range_lvalue_expansion:;
assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone()));
assign_data->children[0]->str = id_data;
+ assign_data->children[0]->was_checked = true;
if (current_always->type != AST_INITIAL) {
for (int i = 0; i < mem_width; i++)
set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0;
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
}
}
else
@@ -1608,12 +1675,13 @@ skip_dynamic_range_lvalue_expansion:;
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
- log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
assign_data->children[0]->str = id_data;
+ assign_data->children[0]->was_checked = true;
if (current_always->type != AST_INITIAL) {
for (int i = 0; i < mem_width; i++)
@@ -1621,6 +1689,7 @@ skip_dynamic_range_lvalue_expansion:;
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
}
delete left_at_zero_ast;
@@ -1632,10 +1701,12 @@ skip_dynamic_range_lvalue_expansion:;
{
assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone());
assign_data->children[0]->str = id_data;
+ assign_data->children[0]->was_checked = true;
if (current_always->type != AST_INITIAL) {
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
assign_en->children[0]->str = id_en;
+ assign_en->children[0]->was_checked = true;
}
}
@@ -1700,19 +1771,19 @@ skip_dynamic_range_lvalue_expansion:;
int num_steps = 1;
if (GetSize(children) != 1 && GetSize(children) != 2)
- log_error("System function %s got %d arguments, expected 1 or 2 at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 2.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
if (!current_always_clocked)
- log_error("System function %s is only allowed in clocked blocks at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s is only allowed in clocked blocks.\n",
+ RTLIL::unescape_id(str).c_str());
if (GetSize(children) == 2)
{
AstNode *buf = children[1]->clone();
while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (buf->type != AST_CONSTANT)
- log_error("Failed to evaluate system function `%s' with non-constant value at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant value.\n", str.c_str());
num_steps = buf->asInt(true);
delete buf;
@@ -1768,12 +1839,12 @@ skip_dynamic_range_lvalue_expansion:;
if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell")
{
if (GetSize(children) != 1)
- log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
if (!current_always_clocked)
- log_error("System function %s is only allowed in clocked blocks at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s is only allowed in clocked blocks.\n",
+ RTLIL::unescape_id(str).c_str());
AstNode *present = children.at(0)->clone();
AstNode *past = clone();
@@ -1794,23 +1865,8 @@ skip_dynamic_range_lvalue_expansion:;
goto apply_newNode;
}
- if (str == "\\$rose" || str == "\\$fell")
- {
- if (GetSize(children) != 1)
- log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
-
- if (!current_always_clocked)
- log_error("System function %s is only allowed in clocked blocks at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
-
- newNode = new AstNode(AST_EQ, children.at(0)->clone(), clone());
- newNode->children.at(1)->str = "\\$past";
- goto apply_newNode;
- }
-
// $anyconst and $anyseq are mapped in AstNode::genRTLIL()
- if (str == "\\$anyconst" || str == "\\$anyseq") {
+ if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") {
recursion_counter--;
return false;
}
@@ -1818,13 +1874,13 @@ skip_dynamic_range_lvalue_expansion:;
if (str == "\\$clog2")
{
if (children.size() != 1)
- log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
AstNode *buf = children[0]->clone();
while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (buf->type != AST_CONSTANT)
- log_error("Failed to evaluate system function `%s' with non-constant value at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant value.\n", str.c_str());
RTLIL::Const arg_value = buf->bitsAsConst();
if (arg_value.as_bool())
@@ -1840,29 +1896,100 @@ skip_dynamic_range_lvalue_expansion:;
goto apply_newNode;
}
+ if (str == "\\$size" || str == "\\$bits")
+ {
+ if (str == "\\$bits" && children.size() != 1)
+ log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
+
+ if (str == "\\$size" && children.size() != 1 && children.size() != 2)
+ log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 2.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
+
+ int dim = 1;
+ if (str == "\\$size" && children.size() == 2) {
+ AstNode *buf = children[1]->clone();
+ // Evaluate constant expression
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ dim = buf->asInt(false);
+ delete buf;
+ }
+ AstNode *buf = children[0]->clone();
+ int mem_depth = 1;
+ AstNode *id_ast = NULL;
+
+ // Is this needed?
+ //while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ buf->detectSignWidth(width_hint, sign_hint);
+
+ if (buf->type == AST_IDENTIFIER) {
+ id_ast = buf->id2ast;
+ if (id_ast == NULL && current_scope.count(buf->str))
+ id_ast = current_scope.at(buf->str);
+ if (!id_ast)
+ log_file_error(filename, linenum, "Failed to resolve identifier %s for width detection!\n", buf->str.c_str());
+ if (id_ast->type == AST_MEMORY) {
+ // We got here only if the argument is a memory
+ // Otherwise $size() and $bits() return the expression width
+ AstNode *mem_range = id_ast->children[1];
+ if (str == "\\$bits") {
+ if (mem_range->type == AST_RANGE) {
+ if (!mem_range->range_valid)
+ log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", buf->str.c_str());
+ mem_depth = mem_range->range_left - mem_range->range_right + 1;
+ } else
+ log_file_error(filename, linenum, "Unknown memory depth AST type in `%s'!\n", buf->str.c_str());
+ } else {
+ // $size()
+ if (mem_range->type == AST_RANGE) {
+ if (!mem_range->range_valid)
+ log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", buf->str.c_str());
+ int dims;
+ if (id_ast->multirange_dimensions.empty())
+ dims = 1;
+ else
+ dims = GetSize(id_ast->multirange_dimensions)/2;
+ if (dim == 1)
+ width_hint = (dims > 1) ? id_ast->multirange_dimensions[1] : (mem_range->range_left - mem_range->range_right + 1);
+ else if (dim <= dims) {
+ width_hint = id_ast->multirange_dimensions[2*dim-1];
+ } else if ((dim > dims+1) || (dim < 0))
+ log_file_error(filename, linenum, "Dimension %d out of range in `%s', as it only has dimensions 1..%d!\n", dim, buf->str.c_str(), dims+1);
+ } else
+ log_file_error(filename, linenum, "Unknown memory depth AST type in `%s'!\n", buf->str.c_str());
+ }
+ }
+ }
+ delete buf;
+
+ newNode = mkconst_int(width_hint * mem_depth, false);
+ goto apply_newNode;
+ }
+
if (str == "\\$ln" || str == "\\$log10" || str == "\\$exp" || str == "\\$sqrt" || str == "\\$pow" ||
str == "\\$floor" || str == "\\$ceil" || str == "\\$sin" || str == "\\$cos" || str == "\\$tan" ||
str == "\\$asin" || str == "\\$acos" || str == "\\$atan" || str == "\\$atan2" || str == "\\$hypot" ||
- str == "\\$sinh" || str == "\\$cosh" || str == "\\$tanh" || str == "\\$asinh" || str == "\\$acosh" || str == "\\$atanh")
+ str == "\\$sinh" || str == "\\$cosh" || str == "\\$tanh" || str == "\\$asinh" || str == "\\$acosh" || str == "\\$atanh" ||
+ str == "\\$rtoi" || str == "\\$itor")
{
bool func_with_two_arguments = str == "\\$pow" || str == "\\$atan2" || str == "\\$hypot";
double x = 0, y = 0;
if (func_with_two_arguments) {
if (children.size() != 2)
- log_error("System function %s got %d arguments, expected 2 at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s got %d arguments, expected 2.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
} else {
if (children.size() != 1)
- log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
}
if (children.size() >= 1) {
while (children[0]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (!children[0]->isConst())
- log_error("Failed to evaluate system function `%s' with non-constant argument at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant argument.\n",
+ RTLIL::unescape_id(str).c_str());
int child_width_hint = width_hint;
bool child_sign_hint = sign_hint;
children[0]->detectSignWidth(child_width_hint, child_sign_hint);
@@ -1872,37 +1999,42 @@ skip_dynamic_range_lvalue_expansion:;
if (children.size() >= 2) {
while (children[1]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (!children[1]->isConst())
- log_error("Failed to evaluate system function `%s' with non-constant argument at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant argument.\n",
+ RTLIL::unescape_id(str).c_str());
int child_width_hint = width_hint;
bool child_sign_hint = sign_hint;
children[1]->detectSignWidth(child_width_hint, child_sign_hint);
y = children[1]->asReal(child_sign_hint);
}
- newNode = new AstNode(AST_REALVALUE);
- if (str == "\\$ln") newNode->realvalue = ::log(x);
- else if (str == "\\$log10") newNode->realvalue = ::log10(x);
- else if (str == "\\$exp") newNode->realvalue = ::exp(x);
- else if (str == "\\$sqrt") newNode->realvalue = ::sqrt(x);
- else if (str == "\\$pow") newNode->realvalue = ::pow(x, y);
- else if (str == "\\$floor") newNode->realvalue = ::floor(x);
- else if (str == "\\$ceil") newNode->realvalue = ::ceil(x);
- else if (str == "\\$sin") newNode->realvalue = ::sin(x);
- else if (str == "\\$cos") newNode->realvalue = ::cos(x);
- else if (str == "\\$tan") newNode->realvalue = ::tan(x);
- else if (str == "\\$asin") newNode->realvalue = ::asin(x);
- else if (str == "\\$acos") newNode->realvalue = ::acos(x);
- else if (str == "\\$atan") newNode->realvalue = ::atan(x);
- else if (str == "\\$atan2") newNode->realvalue = ::atan2(x, y);
- else if (str == "\\$hypot") newNode->realvalue = ::hypot(x, y);
- else if (str == "\\$sinh") newNode->realvalue = ::sinh(x);
- else if (str == "\\$cosh") newNode->realvalue = ::cosh(x);
- else if (str == "\\$tanh") newNode->realvalue = ::tanh(x);
- else if (str == "\\$asinh") newNode->realvalue = ::asinh(x);
- else if (str == "\\$acosh") newNode->realvalue = ::acosh(x);
- else if (str == "\\$atanh") newNode->realvalue = ::atanh(x);
- else log_abort();
+ if (str == "\\$rtoi") {
+ newNode = AstNode::mkconst_int(x, true);
+ } else {
+ newNode = new AstNode(AST_REALVALUE);
+ if (str == "\\$ln") newNode->realvalue = ::log(x);
+ else if (str == "\\$log10") newNode->realvalue = ::log10(x);
+ else if (str == "\\$exp") newNode->realvalue = ::exp(x);
+ else if (str == "\\$sqrt") newNode->realvalue = ::sqrt(x);
+ else if (str == "\\$pow") newNode->realvalue = ::pow(x, y);
+ else if (str == "\\$floor") newNode->realvalue = ::floor(x);
+ else if (str == "\\$ceil") newNode->realvalue = ::ceil(x);
+ else if (str == "\\$sin") newNode->realvalue = ::sin(x);
+ else if (str == "\\$cos") newNode->realvalue = ::cos(x);
+ else if (str == "\\$tan") newNode->realvalue = ::tan(x);
+ else if (str == "\\$asin") newNode->realvalue = ::asin(x);
+ else if (str == "\\$acos") newNode->realvalue = ::acos(x);
+ else if (str == "\\$atan") newNode->realvalue = ::atan(x);
+ else if (str == "\\$atan2") newNode->realvalue = ::atan2(x, y);
+ else if (str == "\\$hypot") newNode->realvalue = ::hypot(x, y);
+ else if (str == "\\$sinh") newNode->realvalue = ::sinh(x);
+ else if (str == "\\$cosh") newNode->realvalue = ::cosh(x);
+ else if (str == "\\$tanh") newNode->realvalue = ::tanh(x);
+ else if (str == "\\$asinh") newNode->realvalue = ::asinh(x);
+ else if (str == "\\$acosh") newNode->realvalue = ::acosh(x);
+ else if (str == "\\$atanh") newNode->realvalue = ::atanh(x);
+ else if (str == "\\$itor") newNode->realvalue = x;
+ else log_abort();
+ }
goto apply_newNode;
}
@@ -1920,14 +2052,14 @@ skip_dynamic_range_lvalue_expansion:;
for (int i = 2; i < GetSize(dpi_decl->children); i++)
{
if (i-2 >= GetSize(children))
- log_error("Insufficient number of arguments in DPI function call at %s:%d.\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Insufficient number of arguments in DPI function call.\n");
argtypes.push_back(RTLIL::unescape_id(dpi_decl->children.at(i)->str));
args.push_back(children.at(i-2)->clone());
while (args.back()->simplify(true, false, false, stage, -1, false, true)) { }
if (args.back()->type != AST_CONSTANT && args.back()->type != AST_REALVALUE)
- log_error("Failed to evaluate DPI function with non-constant argument at %s:%d.\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate DPI function with non-constant argument.\n");
}
newNode = dpi_call(rtype, fname, argtypes, args);
@@ -1939,7 +2071,7 @@ skip_dynamic_range_lvalue_expansion:;
}
if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION)
- log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Can't resolve function name `%s'.\n", str.c_str());
}
if (type == AST_TCALL)
@@ -1947,26 +2079,26 @@ skip_dynamic_range_lvalue_expansion:;
if (str == "$finish" || str == "$stop")
{
if (!current_always || current_always->type != AST_INITIAL)
- log_error("System task `%s' outside initial block is unsupported at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System task `%s' outside initial block is unsupported.\n", str.c_str());
- log_error("System task `%s' executed at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System task `%s' executed.\n", str.c_str());
}
if (str == "\\$readmemh" || str == "\\$readmemb")
{
if (GetSize(children) < 2 || GetSize(children) > 4)
- log_error("System function %s got %d arguments, expected 2-4 at %s:%d.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "System function %s got %d arguments, expected 2-4.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
AstNode *node_filename = children[0]->clone();
while (node_filename->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (node_filename->type != AST_CONSTANT)
- log_error("Failed to evaluate system function `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str());
AstNode *node_memory = children[1]->clone();
while (node_memory->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (node_memory->type != AST_IDENTIFIER || node_memory->id2ast == nullptr || node_memory->id2ast->type != AST_MEMORY)
- log_error("Failed to evaluate system function `%s' with non-memory 2nd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-memory 2nd argument.\n", str.c_str());
int start_addr = -1, finish_addr = -1;
@@ -1974,7 +2106,7 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *node_addr = children[2]->clone();
while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (node_addr->type != AST_CONSTANT)
- log_error("Failed to evaluate system function `%s' with non-constant 3rd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant 3rd argument.\n", str.c_str());
start_addr = int(node_addr->asInt(false));
}
@@ -1982,7 +2114,7 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *node_addr = children[3]->clone();
while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (node_addr->type != AST_CONSTANT)
- log_error("Failed to evaluate system function `%s' with non-constant 4th argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant 4th argument.\n", str.c_str());
finish_addr = int(node_addr->asInt(false));
}
@@ -2008,7 +2140,7 @@ skip_dynamic_range_lvalue_expansion:;
}
if (current_scope.count(str) == 0 || current_scope[str]->type != AST_TASK)
- log_error("Can't resolve task name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Can't resolve task name `%s'.\n", str.c_str());
}
AstNode *decl = current_scope[str];
@@ -2036,9 +2168,9 @@ skip_dynamic_range_lvalue_expansion:;
}
if (in_param)
- log_error("Non-constant function call in constant expression at %s:%d.\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Non-constant function call in constant expression.\n");
if (require_const_eval)
- log_error("Function %s can only be called with constant arguments at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Function %s can only be called with constant arguments.\n", str.c_str());
}
size_t arg_count = 0;
@@ -2137,7 +2269,7 @@ skip_dynamic_range_lvalue_expansion:;
}
for (auto child : decl->children)
- if (child->type == AST_WIRE || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM)
+ if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM)
{
AstNode *wire = nullptr;
@@ -2147,9 +2279,16 @@ skip_dynamic_range_lvalue_expansion:;
if (wire->children.empty()) {
for (auto c : child->children)
wire->children.push_back(c->clone());
- } else {
- if (!child->children.empty())
- log_error("Incompatible re-declaration of wire %s at %s:%d.\n", child->str.c_str(), filename.c_str(), linenum);
+ } else if (!child->children.empty()) {
+ while (child->simplify(true, false, false, stage, -1, false, false)) { }
+ if (GetSize(child->children) == GetSize(wire->children)) {
+ for (int i = 0; i < GetSize(child->children); i++)
+ if (*child->children.at(i) != *wire->children.at(i))
+ goto tcall_incompatible_wires;
+ } else {
+ tcall_incompatible_wires:
+ log_file_error(filename, linenum, "Incompatible re-declaration of wire %s.\n", child->str.c_str());
+ }
}
}
else
@@ -2197,7 +2336,7 @@ skip_dynamic_range_lvalue_expansion:;
}
for (auto child : decl->children)
- if (child->type != AST_WIRE && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM)
+ if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM)
{
AstNode *stmt = child->clone();
stmt->replace_ids(prefix, replace_rules);
@@ -2533,9 +2672,10 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
std::ifstream f;
f.open(mem_filename.c_str());
+ yosys_input_files.insert(mem_filename);
if (f.fail())
- log_error("Can not open file `%s` for %s at %s:%d.\n", mem_filename.c_str(), str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Can not open file `%s` for %s.\n", mem_filename.c_str(), str.c_str());
log_assert(GetSize(memory->children) == 2 && memory->children[1]->type == AST_RANGE && memory->children[1]->range_valid);
int range_left = memory->children[1]->range_left, range_right = memory->children[1]->range_right;
@@ -2581,7 +2721,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
char *endptr;
cursor = strtol(nptr, &endptr, 16);
if (!*nptr || *endptr)
- log_error("Can not parse address `%s` for %s at %s:%d.\n", nptr, str.c_str(), filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Can not parse address `%s` for %s.\n", nptr, str.c_str());
continue;
}
@@ -2660,7 +2800,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
size_t pos = child->str.rfind('.');
if (pos == std::string::npos)
- pos = child->str[0] == '\\' ? 1 : 0;
+ pos = child->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0;
else
pos = pos + 1;
new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos);
@@ -2837,7 +2977,7 @@ bool AstNode::mem2reg_check(pool<AstNode*> &mem2reg_set)
return false;
if (children.empty() || children[0]->type != AST_RANGE || GetSize(children[0]->children) != 1)
- log_error("Invalid array access at %s:%d.\n", filename.c_str(), linenum);
+ log_file_error(filename, linenum, "Invalid array access.\n");
return true;
}
@@ -2902,6 +3042,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
wire_addr->str = id_addr;
wire_addr->is_reg = true;
+ wire_addr->was_checked = true;
wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
mod->children.push_back(wire_addr);
while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
@@ -2909,6 +3050,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
wire_data->str = id_data;
wire_data->is_reg = true;
+ wire_data->was_checked = true;
wire_data->is_signed = mem_signed;
wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
mod->children.push_back(wire_data);
@@ -2977,6 +3119,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true)));
wire_addr->str = id_addr;
wire_addr->is_reg = true;
+ wire_addr->was_checked = true;
if (block)
wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
mod->children.push_back(wire_addr);
@@ -2985,6 +3128,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
wire_data->str = id_data;
wire_data->is_reg = true;
+ wire_data->was_checked = true;
wire_data->is_signed = mem_signed;
if (block)
wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
@@ -2993,6 +3137,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone());
assign_addr->children[0]->str = id_addr;
+ assign_addr->children[0]->was_checked = true;
AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
case_node->children[0]->str = id_addr;
@@ -3003,6 +3148,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));
AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER));
assign_reg->children[0]->str = id_data;
+ assign_reg->children[0]->was_checked = true;
assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i);
cond_node->children[1]->children.push_back(assign_reg);
case_node->children.push_back(cond_node);
@@ -3015,6 +3161,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK));
AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false));
assign_reg->children[0]->str = id_data;
+ assign_reg->children[0]->was_checked = true;
cond_node->children[1]->children.push_back(assign_reg);
case_node->children.push_back(cond_node);
@@ -3043,6 +3190,8 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
if (bit_part_sel)
children.push_back(bit_part_sel);
+
+ did_something = true;
}
log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0);
@@ -3094,13 +3243,13 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia
int offset = variables.at(str).offset, width = variables.at(str).val.bits.size();
if (!children.empty()) {
if (children.size() != 1 || children.at(0)->type != AST_RANGE)
- log_error("Memory access in constant function is not supported in %s:%d (called from %s:%d).\n",
- filename.c_str(), linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(filename, linenum, "Memory access in constant function is not supported\n%s:%d: ...called from here.\n",
+ fcall->filename.c_str(), fcall->linenum);
children.at(0)->replace_variables(variables, fcall);
while (simplify(true, false, false, 1, -1, false, true)) { }
if (!children.at(0)->range_valid)
- log_error("Non-constant range in %s:%d (called from %s:%d).\n",
- filename.c_str(), linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(filename, linenum, "Non-constant range\n%s:%d: ... called from here.\n",
+ fcall->filename.c_str(), fcall->linenum);
offset = min(children.at(0)->range_left, children.at(0)->range_right);
width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width);
}
@@ -3139,8 +3288,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
{
while (child->simplify(true, false, false, 1, -1, false, true)) { }
if (!child->range_valid)
- log_error("Can't determine size of variable %s in %s:%d (called from %s:%d).\n",
- child->str.c_str(), child->filename.c_str(), child->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(child->filename, child->linenum, "Can't determine size of variable %s\n%s:%d: ... called from here.\n",
+ child->str.c_str(), fcall->filename.c_str(), fcall->linenum);
variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1);
variables[child->str].offset = min(child->range_left, child->range_right);
variables[child->str].is_signed = child->is_signed;
@@ -3183,24 +3332,24 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
continue;
if (stmt->children.at(1)->type != AST_CONSTANT)
- log_error("Non-constant expression in constant function at %s:%d (called from %s:%d). X\n",
- stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here. X\n",
+ fcall->filename.c_str(), fcall->linenum);
if (stmt->children.at(0)->type != AST_IDENTIFIER)
- log_error("Unsupported composite left hand side in constant function at %s:%d (called from %s:%d).\n",
- stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(stmt->filename, stmt->linenum, "Unsupported composite left hand side in constant function\n%s:%d: ... called from here.\n",
+ fcall->filename.c_str(), fcall->linenum);
if (!variables.count(stmt->children.at(0)->str))
- log_error("Assignment to non-local variable in constant function at %s:%d (called from %s:%d).\n",
- stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(stmt->filename, stmt->linenum, "Assignment to non-local variable in constant function\n%s:%d: ... called from here.\n",
+ fcall->filename.c_str(), fcall->linenum);
if (stmt->children.at(0)->children.empty()) {
variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size());
} else {
AstNode *range = stmt->children.at(0)->children.at(0);
if (!range->range_valid)
- log_error("Non-constant range in %s:%d (called from %s:%d).\n",
- range->filename.c_str(), range->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(range->filename, range->linenum, "Non-constant range\n%s:%d: ... called from here.\n",
+ fcall->filename.c_str(), fcall->linenum);
int offset = min(range->range_left, range->range_right);
int width = std::abs(range->range_left - range->range_right) + 1;
varinfo_t &v = variables[stmt->children.at(0)->str];
@@ -3231,8 +3380,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
while (cond->simplify(true, false, false, 1, -1, false, true)) { }
if (cond->type != AST_CONSTANT)
- log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n",
- stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n",
+ fcall->filename.c_str(), fcall->linenum);
if (cond->asBool()) {
block->children.insert(block->children.begin(), stmt->children.at(1)->clone());
@@ -3252,8 +3401,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
while (num->simplify(true, false, false, 1, -1, false, true)) { }
if (num->type != AST_CONSTANT)
- log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n",
- stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n",
+ fcall->filename.c_str(), fcall->linenum);
block->children.erase(block->children.begin());
for (int i = 0; i < num->bitsAsConst().as_int(); i++)
@@ -3290,8 +3439,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
while (cond->simplify(true, false, false, 1, -1, false, true)) { }
if (cond->type != AST_CONSTANT)
- log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n",
- stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n",
+ fcall->filename.c_str(), fcall->linenum);
found_match = cond->asBool();
delete cond;
@@ -3320,8 +3469,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
continue;
}
- log_error("Unsupported language construct in constant function at %s:%d (called from %s:%d).\n",
- stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_file_error(stmt->filename, stmt->linenum, "Unsupported language construct in constant function\n%s:%d: ... called from here.\n",
+ fcall->filename.c_str(), fcall->linenum);
log_abort();
}
@@ -3338,4 +3487,3 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
}
YOSYS_NAMESPACE_END
-
diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc
index 6d4d6087..034b3e70 100644
--- a/frontends/blif/blifparse.cc
+++ b/frontends/blif/blifparse.cc
@@ -55,12 +55,37 @@ static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count,
}
}
-void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean, bool sop_mode)
+static std::pair<RTLIL::IdString, int> wideports_split(std::string name)
+{
+ int pos = -1;
+
+ if (name.empty() || name.back() != ']')
+ goto failed;
+
+ for (int i = 0; i+1 < GetSize(name); i++) {
+ if (name[i] == '[')
+ pos = i;
+ else if (name[i] < '0' || name[i] > '9')
+ pos = -1;
+ else if (i == pos+1 && name[i] == '0' && name[i+1] != ']')
+ pos = -1;
+ }
+
+ if (pos >= 0)
+ return std::pair<RTLIL::IdString, int>("\\" + name.substr(0, pos), atoi(name.c_str() + pos+1)+1);
+
+failed:
+ return std::pair<RTLIL::IdString, int>("\\" + name, 0);
+}
+
+void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean, bool sop_mode, bool wideports)
{
RTLIL::Module *module = nullptr;
RTLIL::Const *lutptr = NULL;
RTLIL::Cell *sopcell = NULL;
+ RTLIL::Cell *lastcell = nullptr;
RTLIL::State lut_default_state = RTLIL::State::Sx;
+ std::string err_reason;
int blif_maxnum = 0, sopmode = -1;
auto blif_wire = [&](const std::string &wire_name) -> Wire*
@@ -96,6 +121,8 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
dict<RTLIL::IdString, RTLIL::Const> *obj_attributes = nullptr;
dict<RTLIL::IdString, RTLIL::Const> *obj_parameters = nullptr;
+ dict<RTLIL::IdString, std::pair<int, bool>> wideports_cache;
+
size_t buffer_size = 4096;
char *buffer = (char*)malloc(buffer_size);
int line_count = 0;
@@ -134,6 +161,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
if (module != nullptr)
goto error;
module = new RTLIL::Module;
+ lastcell = nullptr;
module->name = RTLIL::escape_id(strtok(NULL, " \t\r\n"));
obj_attributes = &module->attributes;
obj_parameters = nullptr;
@@ -148,7 +176,32 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
if (!strcmp(cmd, ".end"))
{
+ for (auto &wp : wideports_cache)
+ {
+ auto name = wp.first;
+ int width = wp.second.first;
+ bool isinput = wp.second.second;
+
+ RTLIL::Wire *wire = module->addWire(name, width);
+ wire->port_input = isinput;
+ wire->port_output = !isinput;
+
+ for (int i = 0; i < width; i++) {
+ RTLIL::IdString other_name = name.str() + stringf("[%d]", i);
+ RTLIL::Wire *other_wire = module->wire(other_name);
+ if (other_wire) {
+ other_wire->port_input = false;
+ other_wire->port_output = false;
+ if (isinput)
+ module->connect(other_wire, SigSpec(wire, i));
+ else
+ module->connect(SigSpec(wire, i), other_wire);
+ }
+ }
+ }
+
module->fixup_ports();
+ wideports_cache.clear();
if (run_clean)
{
@@ -182,14 +235,17 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
}
module = nullptr;
+ lastcell = nullptr;
obj_attributes = nullptr;
obj_parameters = nullptr;
continue;
}
- if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) {
+ if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs"))
+ {
char *p;
- while ((p = strtok(NULL, " \t\r\n")) != NULL) {
+ while ((p = strtok(NULL, " \t\r\n")) != NULL)
+ {
RTLIL::IdString wire_name(stringf("\\%s", p));
RTLIL::Wire *wire = module->wire(wire_name);
if (wire == nullptr)
@@ -198,12 +254,36 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
wire->port_input = true;
else
wire->port_output = true;
+
+ if (wideports) {
+ std::pair<RTLIL::IdString, int> wp = wideports_split(p);
+ if (wp.second > 0) {
+ wideports_cache[wp.first].first = std::max(wideports_cache[wp.first].first, wp.second);
+ wideports_cache[wp.first].second = !strcmp(cmd, ".inputs");
+ }
+ }
}
obj_attributes = nullptr;
obj_parameters = nullptr;
continue;
}
+ if (!strcmp(cmd, ".cname"))
+ {
+ char *p = strtok(NULL, " \t\r\n");
+ if (p == NULL)
+ goto error;
+
+ if(lastcell == nullptr || module == nullptr)
+ {
+ err_reason = stringf("No primative object to attach .cname %s.", p);
+ goto error_with_reason;
+ }
+
+ module->rename(lastcell, p);
+ continue;
+ }
+
if (!strcmp(cmd, ".attr") || !strcmp(cmd, ".param")) {
char *n = strtok(NULL, " \t\r\n");
char *v = strtok(NULL, "\r\n");
@@ -221,12 +301,16 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
const_v.bits[i] = v[n-i-1] != '0' ? State::S1 : State::S0;
}
if (!strcmp(cmd, ".attr")) {
- if (obj_attributes == nullptr)
- goto error;
+ if (obj_attributes == nullptr) {
+ err_reason = stringf("No object to attach .attr too.");
+ goto error_with_reason;
+ }
(*obj_attributes)[id_n] = const_v;
} else {
- if (obj_parameters == nullptr)
- goto error;
+ if (obj_parameters == nullptr) {
+ err_reason = stringf("No object to attach .param too.");
+ goto error_with_reason;
+ }
(*obj_parameters)[id_n] = const_v;
}
continue;
@@ -271,6 +355,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
}
}
+ lastcell = cell;
obj_attributes = &cell->attributes;
obj_parameters = &cell->parameters;
continue;
@@ -285,14 +370,45 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
IdString celltype = RTLIL::escape_id(p);
RTLIL::Cell *cell = module->addCell(NEW_ID, celltype);
- while ((p = strtok(NULL, " \t\r\n")) != NULL) {
+ dict<RTLIL::IdString, dict<int, SigBit>> cell_wideports_cache;
+
+ while ((p = strtok(NULL, " \t\r\n")) != NULL)
+ {
char *q = strchr(p, '=');
if (q == NULL || !q[0])
goto error;
*(q++) = 0;
- cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec());
+
+ if (wideports) {
+ std::pair<RTLIL::IdString, int> wp = wideports_split(p);
+ if (wp.second > 0)
+ cell_wideports_cache[wp.first][wp.second-1] = blif_wire(q);
+ else
+ cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec());
+ } else {
+ cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec());
+ }
+ }
+
+ for (auto &it : cell_wideports_cache)
+ {
+ int width = 0;
+ for (auto &b : it.second)
+ width = std::max(width, b.first + 1);
+
+ SigSpec sig;
+
+ for (int i = 0; i < width; i++) {
+ if (it.second.count(i))
+ sig.append(it.second.at(i));
+ else
+ sig.append(module->addWire(NEW_ID));
+ }
+
+ cell->setPort(it.first, sig);
}
+ lastcell = cell;
obj_attributes = &cell->attributes;
obj_parameters = &cell->parameters;
continue;
@@ -301,7 +417,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
obj_attributes = nullptr;
obj_parameters = nullptr;
- if (!strcmp(cmd, ".barbuf"))
+ if (!strcmp(cmd, ".barbuf") || !strcmp(cmd, ".conn"))
{
char *p = strtok(NULL, " \t\r\n");
if (p == NULL)
@@ -369,6 +485,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
sopcell->setPort("\\A", input_sig);
sopcell->setPort("\\Y", output_sig);
sopmode = -1;
+ lastcell = sopcell;
}
else
{
@@ -379,6 +496,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
cell->setPort("\\Y", output_sig);
lutptr = &cell->parameters.at("\\LUT");
lut_default_state = RTLIL::State::Sx;
+ lastcell = cell;
}
continue;
}
@@ -432,7 +550,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
if (lutptr)
{
- if (input_len > 8)
+ if (input_len > 12)
goto error;
for (int i = 0; i < (1 << input_len); i++) {
@@ -452,13 +570,17 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
}
}
+ return;
+
error:
log_error("Syntax error in line %d!\n", line_count);
+error_with_reason:
+ log_error("Syntax error in line %d: %s\n", line_count, err_reason.c_str());
}
struct BlifFrontend : public Frontend {
BlifFrontend() : Frontend("blif", "read BLIF file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -469,10 +591,15 @@ struct BlifFrontend : public Frontend {
log(" -sop\n");
log(" Create $sop cells instead of $lut cells\n");
log("\n");
+ log(" -wideports\n");
+ log(" Merge ports that match the pattern 'name[int]' into a single\n");
+ log(" multi-bit port 'name'.\n");
+ log("\n");
}
- virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool sop_mode = false;
+ bool wideports = false;
log_header(design, "Executing BLIF frontend.\n");
@@ -483,11 +610,15 @@ struct BlifFrontend : public Frontend {
sop_mode = true;
continue;
}
+ if (arg == "-wideports") {
+ wideports = true;
+ continue;
+ }
break;
}
extra_args(f, filename, args, argidx);
- parse_blif(design, *f, "", true, sop_mode);
+ parse_blif(design, *f, "", true, sop_mode, wideports);
}
} BlifFrontend;
diff --git a/frontends/blif/blifparse.h b/frontends/blif/blifparse.h
index 058087d8..955b6aac 100644
--- a/frontends/blif/blifparse.h
+++ b/frontends/blif/blifparse.h
@@ -24,7 +24,8 @@
YOSYS_NAMESPACE_BEGIN
-extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean = false, bool sop_mode = false);
+extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name,
+ bool run_clean = false, bool sop_mode = false, bool wideports = false);
YOSYS_NAMESPACE_END
diff --git a/frontends/ilang/.gitignore b/frontends/ilang/.gitignore
index 43106a81..f586b33c 100644
--- a/frontends/ilang/.gitignore
+++ b/frontends/ilang/.gitignore
@@ -1,4 +1,4 @@
ilang_lexer.cc
ilang_parser.output
ilang_parser.tab.cc
-ilang_parser.tab.h
+ilang_parser.tab.hh
diff --git a/frontends/ilang/Makefile.inc b/frontends/ilang/Makefile.inc
index e2a476c9..6f1f0e8f 100644
--- a/frontends/ilang/Makefile.inc
+++ b/frontends/ilang/Makefile.inc
@@ -1,15 +1,14 @@
GENFILES += frontends/ilang/ilang_parser.tab.cc
-GENFILES += frontends/ilang/ilang_parser.tab.h
+GENFILES += frontends/ilang/ilang_parser.tab.hh
GENFILES += frontends/ilang/ilang_parser.output
GENFILES += frontends/ilang/ilang_lexer.cc
frontends/ilang/ilang_parser.tab.cc: frontends/ilang/ilang_parser.y
$(Q) mkdir -p $(dir $@)
- $(P) $(BISON) -d -r all -b frontends/ilang/ilang_parser $<
- $(Q) mv frontends/ilang/ilang_parser.tab.c frontends/ilang/ilang_parser.tab.cc
+ $(P) $(BISON) -o $@ -d -r all -b frontends/ilang/ilang_parser $<
-frontends/ilang/ilang_parser.tab.h: frontends/ilang/ilang_parser.tab.cc
+frontends/ilang/ilang_parser.tab.hh: frontends/ilang/ilang_parser.tab.cc
frontends/ilang/ilang_lexer.cc: frontends/ilang/ilang_lexer.l
$(Q) mkdir -p $(dir $@)
diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc
index ed678998..d8783ac1 100644
--- a/frontends/ilang/ilang_frontend.cc
+++ b/frontends/ilang/ilang_frontend.cc
@@ -35,7 +35,7 @@ YOSYS_NAMESPACE_BEGIN
struct IlangFrontend : public Frontend {
IlangFrontend() : Frontend("ilang", "read modules from ilang file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -45,7 +45,7 @@ struct IlangFrontend : public Frontend {
log("representation of a design in yosys's internal format.)\n");
log("\n");
}
- virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing ILANG frontend.\n");
extra_args(f, filename, args, 1);
diff --git a/frontends/ilang/ilang_lexer.l b/frontends/ilang/ilang_lexer.l
index 84238854..d8e01ae4 100644
--- a/frontends/ilang/ilang_lexer.l
+++ b/frontends/ilang/ilang_lexer.l
@@ -30,7 +30,7 @@
#endif
#include "frontends/ilang/ilang_frontend.h"
-#include "ilang_parser.tab.h"
+#include "ilang_parser.tab.hh"
USING_YOSYS_NAMESPACE
diff --git a/frontends/json/Makefile.inc b/frontends/json/Makefile.inc
new file mode 100644
index 00000000..0fe1b372
--- /dev/null
+++ b/frontends/json/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += frontends/json/jsonparse.o
+
diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc
new file mode 100644
index 00000000..82361ea9
--- /dev/null
+++ b/frontends/json/jsonparse.cc
@@ -0,0 +1,541 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct JsonNode
+{
+ char type; // S=String, N=Number, A=Array, D=Dict
+ string data_string;
+ int data_number;
+ vector<JsonNode*> data_array;
+ dict<string, JsonNode*> data_dict;
+ vector<string> data_dict_keys;
+
+ JsonNode(std::istream &f)
+ {
+ type = 0;
+ data_number = 0;
+
+ while (1)
+ {
+ int ch = f.get();
+
+ if (ch == EOF)
+ log_error("Unexpected EOF in JSON file.\n");
+
+ if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
+ continue;
+
+ if (ch == '"')
+ {
+ type = 'S';
+
+ while (1)
+ {
+ ch = f.get();
+
+ if (ch == EOF)
+ log_error("Unexpected EOF in JSON string.\n");
+
+ if (ch == '"')
+ break;
+
+ if (ch == '\\') {
+ int ch = f.get();
+
+ if (ch == EOF)
+ log_error("Unexpected EOF in JSON string.\n");
+ }
+
+ data_string += ch;
+ }
+
+ break;
+ }
+
+ if ('0' <= ch && ch <= '9')
+ {
+ type = 'N';
+ data_number = ch - '0';
+ data_string += ch;
+
+ while (1)
+ {
+ ch = f.get();
+
+ if (ch == EOF)
+ break;
+
+ if (ch == '.')
+ goto parse_real;
+
+ if (ch < '0' || '9' < ch) {
+ f.unget();
+ break;
+ }
+
+ data_number = data_number*10 + (ch - '0');
+ data_string += ch;
+ }
+
+ data_string = "";
+ break;
+
+ parse_real:
+ type = 'S';
+ data_number = 0;
+ data_string += ch;
+
+ while (1)
+ {
+ ch = f.get();
+
+ if (ch == EOF)
+ break;
+
+ if (ch < '0' || '9' < ch) {
+ f.unget();
+ break;
+ }
+
+ data_string += ch;
+ }
+
+ break;
+ }
+
+ if (ch == '[')
+ {
+ type = 'A';
+
+ while (1)
+ {
+ ch = f.get();
+
+ if (ch == EOF)
+ log_error("Unexpected EOF in JSON file.\n");
+
+ if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',')
+ continue;
+
+ if (ch == ']')
+ break;
+
+ f.unget();
+ data_array.push_back(new JsonNode(f));
+ }
+
+ break;
+ }
+
+ if (ch == '{')
+ {
+ type = 'D';
+
+ while (1)
+ {
+ ch = f.get();
+
+ if (ch == EOF)
+ log_error("Unexpected EOF in JSON file.\n");
+
+ if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',')
+ continue;
+
+ if (ch == '}')
+ break;
+
+ f.unget();
+ JsonNode key(f);
+
+ while (1)
+ {
+ ch = f.get();
+
+ if (ch == EOF)
+ log_error("Unexpected EOF in JSON file.\n");
+
+ if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ':')
+ continue;
+
+ f.unget();
+ break;
+ }
+
+ JsonNode *value = new JsonNode(f);
+
+ if (key.type != 'S')
+ log_error("Unexpected non-string key in JSON dict.\n");
+
+ data_dict[key.data_string] = value;
+ data_dict_keys.push_back(key.data_string);
+ }
+
+ break;
+ }
+
+ log_error("Unexpected character in JSON file: '%c'\n", ch);
+ }
+ }
+
+ ~JsonNode()
+ {
+ for (auto it : data_array)
+ delete it;
+ for (auto &it : data_dict)
+ delete it.second;
+ }
+};
+
+void json_parse_attr_param(dict<IdString, Const> &results, JsonNode *node)
+{
+ if (node->type != 'D')
+ log_error("JSON attributes or parameters node is not a dictionary.\n");
+
+ for (auto it : node->data_dict)
+ {
+ IdString key = RTLIL::escape_id(it.first.c_str());
+ JsonNode *value_node = it.second;
+ Const value;
+
+ if (value_node->type == 'S') {
+ string &s = value_node->data_string;
+ if (s.find_first_not_of("01xz") == string::npos)
+ value = Const::from_string(s);
+ else
+ value = Const(s);
+ } else
+ if (value_node->type == 'N') {
+ value = Const(value_node->data_number, 32);
+ } else
+ if (value_node->type == 'A') {
+ log_error("JSON attribute or parameter value is an array.\n");
+ } else
+ if (value_node->type == 'D') {
+ log_error("JSON attribute or parameter value is a dict.\n");
+ } else {
+ log_abort();
+ }
+
+ results[key] = value;
+ }
+}
+
+void json_import(Design *design, string &modname, JsonNode *node)
+{
+ log("Importing module %s from JSON tree.\n", modname.c_str());
+
+ Module *module = new RTLIL::Module;
+ module->name = RTLIL::escape_id(modname.c_str());
+
+ if (design->module(module->name))
+ log_error("Re-definition of module %s.\n", log_id(module->name));
+
+ design->add(module);
+
+ if (node->data_dict.count("attributes"))
+ json_parse_attr_param(module->attributes, node->data_dict.at("attributes"));
+
+ dict<int, SigBit> signal_bits;
+
+ if (node->data_dict.count("ports"))
+ {
+ JsonNode *ports_node = node->data_dict.at("ports");
+
+ if (ports_node->type != 'D')
+ log_error("JSON ports node is not a dictionary.\n");
+
+ for (int port_id = 1; port_id <= GetSize(ports_node->data_dict_keys); port_id++)
+ {
+ IdString port_name = RTLIL::escape_id(ports_node->data_dict_keys[port_id-1].c_str());
+ JsonNode *port_node = ports_node->data_dict.at(ports_node->data_dict_keys[port_id-1]);
+
+ if (port_node->type != 'D')
+ log_error("JSON port node '%s' is not a dictionary.\n", log_id(port_name));
+
+ if (port_node->data_dict.count("direction") == 0)
+ log_error("JSON port node '%s' has no direction attribute.\n", log_id(port_name));
+
+ if (port_node->data_dict.count("bits") == 0)
+ log_error("JSON port node '%s' has no bits attribute.\n", log_id(port_name));
+
+ JsonNode *port_direction_node = port_node->data_dict.at("direction");
+ JsonNode *port_bits_node = port_node->data_dict.at("bits");
+
+ if (port_direction_node->type != 'S')
+ log_error("JSON port node '%s' has non-string direction attribute.\n", log_id(port_name));
+
+ if (port_bits_node->type != 'A')
+ log_error("JSON port node '%s' has non-array bits attribute.\n", log_id(port_name));
+
+ Wire *port_wire = module->wire(port_name);
+
+ if (port_wire == nullptr)
+ port_wire = module->addWire(port_name, GetSize(port_bits_node->data_array));
+
+ if (port_direction_node->data_string == "input") {
+ port_wire->port_input = true;
+ } else
+ if (port_direction_node->data_string == "output") {
+ port_wire->port_output = true;
+ } else
+ if (port_direction_node->data_string == "inout") {
+ port_wire->port_input = true;
+ port_wire->port_output = true;
+ } else
+ log_error("JSON port node '%s' has invalid '%s' direction attribute.\n", log_id(port_name), port_direction_node->data_string.c_str());
+
+ port_wire->port_id = port_id;
+
+ for (int i = 0; i < GetSize(port_bits_node->data_array); i++)
+ {
+ JsonNode *bitval_node = port_bits_node->data_array.at(i);
+ SigBit sigbit(port_wire, i);
+
+ if (bitval_node->type == 'S') {
+ if (bitval_node->data_string == "0")
+ module->connect(sigbit, State::S0);
+ else if (bitval_node->data_string == "1")
+ module->connect(sigbit, State::S1);
+ else if (bitval_node->data_string == "x")
+ module->connect(sigbit, State::Sx);
+ else if (bitval_node->data_string == "z")
+ module->connect(sigbit, State::Sz);
+ else
+ log_error("JSON port node '%s' has invalid '%s' bit string value on bit %d.\n",
+ log_id(port_name), bitval_node->data_string.c_str(), i);
+ } else
+ if (bitval_node->type == 'N') {
+ int bitidx = bitval_node->data_number;
+ if (signal_bits.count(bitidx)) {
+ if (port_wire->port_output) {
+ module->connect(sigbit, signal_bits.at(bitidx));
+ } else {
+ module->connect(signal_bits.at(bitidx), sigbit);
+ signal_bits[bitidx] = sigbit;
+ }
+ } else {
+ signal_bits[bitidx] = sigbit;
+ }
+ } else
+ log_error("JSON port node '%s' has invalid bit value on bit %d.\n", log_id(port_name), i);
+ }
+ }
+
+ module->fixup_ports();
+ }
+
+ if (node->data_dict.count("netnames"))
+ {
+ JsonNode *netnames_node = node->data_dict.at("netnames");
+
+ if (netnames_node->type != 'D')
+ log_error("JSON netnames node is not a dictionary.\n");
+
+ for (auto &net : netnames_node->data_dict)
+ {
+ IdString net_name = RTLIL::escape_id(net.first.c_str());
+ JsonNode *net_node = net.second;
+
+ if (net_node->type != 'D')
+ log_error("JSON netname node '%s' is not a dictionary.\n", log_id(net_name));
+
+ if (net_node->data_dict.count("bits") == 0)
+ log_error("JSON netname node '%s' has no bits attribute.\n", log_id(net_name));
+
+ JsonNode *bits_node = net_node->data_dict.at("bits");
+
+ if (bits_node->type != 'A')
+ log_error("JSON netname node '%s' has non-array bits attribute.\n", log_id(net_name));
+
+ Wire *wire = module->wire(net_name);
+
+ if (wire == nullptr)
+ wire = module->addWire(net_name, GetSize(bits_node->data_array));
+
+ for (int i = 0; i < GetSize(bits_node->data_array); i++)
+ {
+ JsonNode *bitval_node = bits_node->data_array.at(i);
+ SigBit sigbit(wire, i);
+
+ if (bitval_node->type == 'S') {
+ if (bitval_node->data_string == "0")
+ module->connect(sigbit, State::S0);
+ else if (bitval_node->data_string == "1")
+ module->connect(sigbit, State::S1);
+ else if (bitval_node->data_string == "x")
+ module->connect(sigbit, State::Sx);
+ else if (bitval_node->data_string == "z")
+ module->connect(sigbit, State::Sz);
+ else
+ log_error("JSON netname node '%s' has invalid '%s' bit string value on bit %d.\n",
+ log_id(net_name), bitval_node->data_string.c_str(), i);
+ } else
+ if (bitval_node->type == 'N') {
+ int bitidx = bitval_node->data_number;
+ if (signal_bits.count(bitidx)) {
+ if (sigbit != signal_bits.at(bitidx))
+ module->connect(sigbit, signal_bits.at(bitidx));
+ } else {
+ signal_bits[bitidx] = sigbit;
+ }
+ } else
+ log_error("JSON netname node '%s' has invalid bit value on bit %d.\n", log_id(net_name), i);
+ }
+
+ if (net_node->data_dict.count("attributes"))
+ json_parse_attr_param(wire->attributes, net_node->data_dict.at("attributes"));
+ }
+ }
+
+ if (node->data_dict.count("cells"))
+ {
+ JsonNode *cells_node = node->data_dict.at("cells");
+
+ if (cells_node->type != 'D')
+ log_error("JSON cells node is not a dictionary.\n");
+
+ for (auto &cell_node_it : cells_node->data_dict)
+ {
+ IdString cell_name = RTLIL::escape_id(cell_node_it.first.c_str());
+ JsonNode *cell_node = cell_node_it.second;
+
+ if (cell_node->type != 'D')
+ log_error("JSON cells node '%s' is not a dictionary.\n", log_id(cell_name));
+
+ if (cell_node->data_dict.count("type") == 0)
+ log_error("JSON cells node '%s' has no type attribute.\n", log_id(cell_name));
+
+ JsonNode *type_node = cell_node->data_dict.at("type");
+
+ if (type_node->type != 'S')
+ log_error("JSON cells node '%s' has a non-string type.\n", log_id(cell_name));
+
+ IdString cell_type = RTLIL::escape_id(type_node->data_string.c_str());
+
+ Cell *cell = module->addCell(cell_name, cell_type);
+
+ if (cell_node->data_dict.count("connections") == 0)
+ log_error("JSON cells node '%s' has no connections attribute.\n", log_id(cell_name));
+
+ JsonNode *connections_node = cell_node->data_dict.at("connections");
+
+ if (connections_node->type != 'D')
+ log_error("JSON cells node '%s' has non-dictionary connections attribute.\n", log_id(cell_name));
+
+ for (auto &conn_it : connections_node->data_dict)
+ {
+ IdString conn_name = RTLIL::escape_id(conn_it.first.c_str());
+ JsonNode *conn_node = conn_it.second;
+
+ if (conn_node->type != 'A')
+ log_error("JSON cells node '%s' connection '%s' is not an array.\n", log_id(cell_name), log_id(conn_name));
+
+ SigSpec sig;
+
+ for (int i = 0; i < GetSize(conn_node->data_array); i++)
+ {
+ JsonNode *bitval_node = conn_node->data_array.at(i);
+
+ if (bitval_node->type == 'S') {
+ if (bitval_node->data_string == "0")
+ sig.append(State::S0);
+ else if (bitval_node->data_string == "1")
+ sig.append(State::S1);
+ else if (bitval_node->data_string == "x")
+ sig.append(State::Sx);
+ else if (bitval_node->data_string == "z")
+ sig.append(State::Sz);
+ else
+ log_error("JSON cells node '%s' connection '%s' has invalid '%s' bit string value on bit %d.\n",
+ log_id(cell_name), log_id(conn_name), bitval_node->data_string.c_str(), i);
+ } else
+ if (bitval_node->type == 'N') {
+ int bitidx = bitval_node->data_number;
+ if (signal_bits.count(bitidx) == 0)
+ signal_bits[bitidx] = module->addWire(NEW_ID);
+ sig.append(signal_bits.at(bitidx));
+ } else
+ log_error("JSON cells node '%s' connection '%s' has invalid bit value on bit %d.\n",
+ log_id(cell_name), log_id(conn_name), i);
+
+ }
+
+ cell->setPort(conn_name, sig);
+ }
+
+ if (cell_node->data_dict.count("attributes"))
+ json_parse_attr_param(cell->attributes, cell_node->data_dict.at("attributes"));
+
+ if (cell_node->data_dict.count("parameters"))
+ json_parse_attr_param(cell->parameters, cell_node->data_dict.at("parameters"));
+ }
+ }
+}
+
+struct JsonFrontend : public Frontend {
+ JsonFrontend() : Frontend("json", "read JSON file") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" read_json [filename]\n");
+ log("\n");
+ log("Load modules from a JSON file into the current design See \"help write_json\"\n");
+ log("for a description of the file format.\n");
+ log("\n");
+ }
+ void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing JSON frontend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ // std::string arg = args[argidx];
+ // if (arg == "-sop") {
+ // sop_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ JsonNode root(*f);
+
+ if (root.type != 'D')
+ log_error("JSON root node is not a dictionary.\n");
+
+ if (root.data_dict.count("modules") != 0)
+ {
+ JsonNode *modules = root.data_dict.at("modules");
+
+ if (modules->type != 'D')
+ log_error("JSON modules node is not a dictionary.\n");
+
+ for (auto &it : modules->data_dict)
+ json_import(design, it.first, it.second);
+ }
+ }
+} JsonFrontend;
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc
index 4666c818..0a5bd84d 100644
--- a/frontends/liberty/liberty.cc
+++ b/frontends/liberty/liberty.cc
@@ -148,7 +148,7 @@ static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack
}
if (0 <= top && stack[top].type == 2) {
- if (next_token.type == '*' || next_token.type == '&' || next_token.type == 0 || next_token.type == '(')
+ if (next_token.type == '*' || next_token.type == '&' || next_token.type == 0 || next_token.type == '(' || next_token.type == '!')
return false;
stack[top].type = 3;
return true;
@@ -188,7 +188,7 @@ static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr)
}
token_t next_token(0);
- if (*expr == '(' || *expr == ')' || *expr == '\'' || *expr == '!' || *expr == '^' || *expr == '*' || *expr == '+' || *expr == '|')
+ if (*expr == '(' || *expr == ')' || *expr == '\'' || *expr == '!' || *expr == '^' || *expr == '*' || *expr == '+' || *expr == '|' || *expr == '&')
next_token = token_t(*(expr++));
else
next_token = token_t(0, parse_func_identifier(module, expr));
@@ -290,7 +290,7 @@ static void create_ff(RTLIL::Module *module, LibertyAst *node)
log_assert(!cell->type.empty());
}
-static void create_latch(RTLIL::Module *module, LibertyAst *node)
+static bool create_latch(RTLIL::Module *module, LibertyAst *node, bool flag_ignore_miss_data_latch)
{
RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
@@ -309,8 +309,14 @@ static void create_latch(RTLIL::Module *module, LibertyAst *node)
preset_sig = parse_func_expr(module, child->value.c_str());
}
- if (enable_sig.size() == 0 || data_sig.size() == 0)
- log_error("Latch cell %s has no data_in and/or enable attribute.\n", log_id(module->name));
+ if (enable_sig.size() == 0 || data_sig.size() == 0) {
+ if (!flag_ignore_miss_data_latch)
+ log_error("Latch cell %s has no data_in and/or enable attribute.\n", log_id(module->name));
+ else
+ log("Ignored latch cell %s with no data_in and/or enable attribute.\n", log_id(module->name));
+
+ return false;
+ }
for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
{
@@ -399,6 +405,8 @@ static void create_latch(RTLIL::Module *module, LibertyAst *node)
cell->setPort("\\D", data_sig);
cell->setPort("\\Q", iq_sig);
cell->setPort("\\E", enable_sig);
+
+ return true;
}
void parse_type_map(std::map<std::string, std::tuple<int, int, bool>> &type_map, LibertyAst *ast)
@@ -444,7 +452,7 @@ void parse_type_map(std::map<std::string, std::tuple<int, int, bool>> &type_map,
struct LibertyFrontend : public Frontend {
LibertyFrontend() : Frontend("liberty", "read cells from liberty file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -455,9 +463,13 @@ struct LibertyFrontend : public Frontend {
log(" -lib\n");
log(" only create empty blackbox modules\n");
log("\n");
- log(" -ignore_redef\n");
+ log(" -nooverwrite\n");
log(" ignore re-definitions of modules. (the default behavior is to\n");
- log(" create an error message.)\n");
+ log(" create an error message if the existing module is not a blackbox\n");
+ log(" module, and overwrite the existing module if it is a blackbox module.)\n");
+ log("\n");
+ log(" -overwrite\n");
+ log(" overwrite existing modules with the same name\n");
log("\n");
log(" -ignore_miss_func\n");
log(" ignore cells with missing function specification of outputs\n");
@@ -466,16 +478,21 @@ struct LibertyFrontend : public Frontend {
log(" ignore cells with a missing or invalid direction\n");
log(" specification on a pin\n");
log("\n");
+ log(" -ignore_miss_data_latch\n");
+ log(" ignore latches with missing data and/or enable pins\n");
+ log("\n");
log(" -setattr <attribute_name>\n");
log(" set the specified attribute (to the value 1) on all loaded modules\n");
log("\n");
}
- virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool flag_lib = false;
- bool flag_ignore_redef = false;
+ bool flag_nooverwrite = false;
+ bool flag_overwrite = false;
bool flag_ignore_miss_func = false;
bool flag_ignore_miss_dir = false;
+ bool flag_ignore_miss_data_latch = false;
std::vector<std::string> attributes;
log_header(design, "Executing Liberty frontend.\n");
@@ -487,8 +504,14 @@ struct LibertyFrontend : public Frontend {
flag_lib = true;
continue;
}
- if (arg == "-ignore_redef") {
- flag_ignore_redef = true;
+ if (arg == "-ignore_redef" || arg == "-nooverwrite") {
+ flag_nooverwrite = true;
+ flag_overwrite = false;
+ continue;
+ }
+ if (arg == "-overwrite") {
+ flag_nooverwrite = false;
+ flag_overwrite = true;
continue;
}
if (arg == "-ignore_miss_func") {
@@ -499,6 +522,10 @@ struct LibertyFrontend : public Frontend {
flag_ignore_miss_dir = true;
continue;
}
+ if (arg == "-ignore_miss_data_latch") {
+ flag_ignore_miss_data_latch = true;
+ continue;
+ }
if (arg == "-setattr" && argidx+1 < args.size()) {
attributes.push_back(RTLIL::escape_id(args[++argidx]));
continue;
@@ -521,9 +548,16 @@ struct LibertyFrontend : public Frontend {
std::string cell_name = RTLIL::escape_id(cell->args.at(0));
if (design->has(cell_name)) {
- if (flag_ignore_redef)
+ Module *existing_mod = design->module(cell_name);
+ if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) {
+ log_error("Re-definition of of cell/module %s!\n", log_id(cell_name));
+ } else if (flag_nooverwrite) {
+ log("Ignoring re-definition of module %s.\n", log_id(cell_name));
continue;
- log_error("Duplicate definition of cell/module %s.\n", RTLIL::unescape_id(cell_name).c_str());
+ } else {
+ log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "", log_id(cell_name));
+ design->remove(existing_mod);
+ }
}
// log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name).c_str());
@@ -566,6 +600,12 @@ struct LibertyFrontend : public Frontend {
LibertyAst *dir = node->find("direction");
+ if (dir == nullptr) {
+ LibertyAst *pin = node->find("pin");
+ if (pin != nullptr)
+ dir = pin->find("direction");
+ }
+
if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name));
@@ -600,7 +640,10 @@ struct LibertyFrontend : public Frontend {
if (node->id == "ff" && node->args.size() == 2)
create_ff(module, node);
if (node->id == "latch" && node->args.size() == 2)
- create_latch(module, node);
+ if (!create_latch(module, node, flag_ignore_miss_data_latch)) {
+ delete module;
+ goto skip_cell;
+ }
}
if (node->id == "pin" && node->args.size() == 1)
diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc
index 68ef9aed..972f4f9f 100644
--- a/frontends/verific/Makefile.inc
+++ b/frontends/verific/Makefile.inc
@@ -3,6 +3,8 @@ OBJS += frontends/verific/verific.o
ifeq ($(ENABLE_VERIFIC),1)
+OBJS += frontends/verific/verificsva.o
+
EXTRA_TARGETS += share/verific
share/verific:
@@ -11,6 +13,7 @@ share/verific:
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993
$(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008
+ $(Q) chmod -R a+rX share/verific.new
$(Q) mv share/verific.new share/verific
endif
diff --git a/frontends/verific/README b/frontends/verific/README
new file mode 100644
index 00000000..b4c436a3
--- /dev/null
+++ b/frontends/verific/README
@@ -0,0 +1,62 @@
+
+
+This directory contains Verific bindings for Yosys.
+See http://www.verific.com/ for details.
+
+
+Building Yosys with the 32 bit Verific eval library on amd64:
+=============================================================
+
+1.) Use a Makefile.conf like the following one:
+
+--snip--
+CONFIG := gcc
+ENABLE_TCL := 0
+ENABLE_PLUGINS := 0
+ENABLE_VERIFIC := 1
+CXXFLAGS += -m32
+LDFLAGS += -m32
+VERIFIC_DIR = /usr/local/src/verific_lib_eval
+--snap--
+
+
+2.) Install the necessary multilib packages
+
+Hint: On debian/ubuntu the multilib packages have names such as
+libreadline-dev:i386 or lib32readline6-dev, depending on the
+exact version of debian/ubuntu you are working with.
+
+
+3.) Build and test
+
+make -j8
+./yosys -p 'verific -sv frontends/verific/example.sv; verific -import top'
+
+
+Verific Features that should be enabled in your Verific library
+===============================================================
+
+database/DBCompileFlags.h:
+ DB_PRESERVE_INITIAL_VALUE
+
+
+Testing Verific+Yosys+SymbiYosys for formal verification
+========================================================
+
+Install Yosys+Verific, SymbiYosys, and Yices2. Install instructions:
+http://symbiyosys.readthedocs.io/en/latest/quickstart.html#installing
+
+Then run in the following command in this directory:
+
+ sby -f example.sby
+
+This will generate approximately one page of text outpout. The last lines
+should be something like this:
+
+ SBY [example] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:00 (0)
+ SBY [example] summary: Elapsed process time [H:MM:SS (secs)]: 0:00:00 (0)
+ SBY [example] summary: engine_0 (smtbmc yices) returned PASS for basecase
+ SBY [example] summary: engine_0 (smtbmc yices) returned PASS for induction
+ SBY [example] summary: successful proof by k-induction.
+ SBY [example] DONE (PASS, rc=0)
+
diff --git a/frontends/verific/build_amd64.txt b/frontends/verific/build_amd64.txt
deleted file mode 100644
index d6952820..00000000
--- a/frontends/verific/build_amd64.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-
-Notes on building yosys with verific support on amd64 when you
-only have the i386 eval version of Verific:
-
-
-1.) Use a Makefile.conf like the following one:
-
---snip--
-CONFIG := clang
-ENABLE_TCL := 0
-ENABLE_PLUGINS := 0
-ENABLE_VERIFIC := 1
-CXXFLAGS += -m32
-LDFLAGS += -m32
-VERIFIC_DIR = /usr/local/src/verific_lib_eval
---snap--
-
-
-2.) Install the necessary multilib packages
-
-Hint: On debian/ubuntu the multilib packages have names such as
-libreadline-dev:i386 or lib32readline6-dev, depending on the
-exact version of debian/ubuntu you are working with.
-
-
-3.) Build and test
-
-make -j8
-./yosys frontends/verific/test_navre.ys
-
diff --git a/frontends/verific/example.sby b/frontends/verific/example.sby
new file mode 100644
index 00000000..ffbf33ca
--- /dev/null
+++ b/frontends/verific/example.sby
@@ -0,0 +1,16 @@
+# Simple SymbiYosys example job utilizing Verific
+
+[options]
+mode prove
+depth 10
+
+[engines]
+smtbmc yices
+
+[script]
+verific -sv example.sv
+verific -import top
+prep -top top
+
+[files]
+example.sv
diff --git a/frontends/verific/example.sv b/frontends/verific/example.sv
new file mode 100644
index 00000000..21a5d42c
--- /dev/null
+++ b/frontends/verific/example.sv
@@ -0,0 +1,18 @@
+module top (
+ input clk, rst,
+ output reg [3:0] cnt
+);
+ initial cnt = 0;
+
+ always @(posedge clk) begin
+ if (rst)
+ cnt <= 0;
+ else
+ cnt <= cnt + 4'd 1;
+ end
+
+ always @(posedge clk) begin
+ assume (cnt != 10);
+ assert (cnt != 15);
+ end
+endmodule
diff --git a/frontends/verific/test_navre.ys b/frontends/verific/test_navre.ys
deleted file mode 100644
index a56b725a..00000000
--- a/frontends/verific/test_navre.ys
+++ /dev/null
@@ -1,18 +0,0 @@
-verific -vlog2k ../yosys-bigsim/softusb_navre/rtl/softusb_navre.v
-verific -import softusb_navre
-
-memory softusb_navre
-flatten softusb_navre
-rename softusb_navre gate
-
-read_verilog ../yosys-bigsim/softusb_navre/rtl/softusb_navre.v
-cd softusb_navre; proc; opt; memory; opt; cd ..
-rename softusb_navre gold
-
-expose -dff -shared gold gate
-miter -equiv -ignore_gold_x -make_assert -make_outputs -make_outcmp gold gate miter
-
-cd miter
-flatten; opt -undriven
-sat -verify -maxsteps 5 -set-init-undef -set-def-inputs -prove-asserts -tempinduct-def \
- -seq 1 -set-at 1 in_rst 1 # -show-inputs -show-outputs
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
index 7dd36a74..1dd6d7e2 100644
--- a/frontends/verific/verific.cc
+++ b/frontends/verific/verific.cc
@@ -29,29 +29,47 @@
# include <dirent.h>
#endif
+#include "frontends/verific/verific.h"
+
USING_YOSYS_NAMESPACE
#ifdef YOSYS_ENABLE_VERIFIC
+#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Woverloaded-virtual"
+#endif
#include "veri_file.h"
#include "vhdl_file.h"
+#include "hier_tree.h"
#include "VeriModule.h"
+#include "VeriWrite.h"
#include "VhdlUnits.h"
-#include "DataBase.h"
#include "Message.h"
+#ifdef __clang__
#pragma clang diagnostic pop
+#endif
#ifdef VERIFIC_NAMESPACE
-using namespace Verific ;
+using namespace Verific;
+#endif
+
#endif
-static void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefile, const char *msg, va_list args)
+#ifdef YOSYS_ENABLE_VERIFIC
+YOSYS_NAMESPACE_BEGIN
+
+int verific_verbose;
+bool verific_import_pending;
+string verific_error_msg;
+
+vector<string> verific_incdirs, verific_libdirs;
+
+void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefile, const char *msg, va_list args)
{
- log("VERIFIC-%s [%s] ",
+ string message_prefix = stringf("VERIFIC-%s [%s] ",
msg_type == VERIFIC_NONE ? "NONE" :
msg_type == VERIFIC_ERROR ? "ERROR" :
msg_type == VERIFIC_WARNING ? "WARNING" :
@@ -59,13 +77,47 @@ static void msg_func(msg_type_t msg_type, const char *message_id, linefile_type
msg_type == VERIFIC_INFO ? "INFO" :
msg_type == VERIFIC_COMMENT ? "COMMENT" :
msg_type == VERIFIC_PROGRAM_ERROR ? "PROGRAM_ERROR" : "UNKNOWN", message_id);
- if (linefile)
- log("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile));
- logv(msg, args);
- log("\n");
+
+ string message = linefile ? stringf("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile)) : "";
+ message += vstringf(msg, args);
+
+ if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR)
+ log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str());
+ else
+ log("%s%s\n", message_prefix.c_str(), message.c_str());
+
+ if (verific_error_msg.empty() && (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_PROGRAM_ERROR))
+ verific_error_msg = message;
}
-static void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj)
+string get_full_netlist_name(Netlist *nl)
+{
+ if (nl->NumOfRefs() == 1) {
+ Instance *inst = (Instance*)nl->GetReferences()->GetLast();
+ return get_full_netlist_name(inst->Owner()) + "." + inst->Name();
+ }
+
+ return nl->CellBaseName();
+}
+
+// ==================================================================
+
+VerificImporter::VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_names, bool mode_verific, bool mode_autocover) :
+ mode_gates(mode_gates), mode_keep(mode_keep), mode_nosva(mode_nosva),
+ mode_names(mode_names), mode_verific(mode_verific), mode_autocover(mode_autocover)
+{
+}
+
+RTLIL::SigBit VerificImporter::net_map_at(Net *net)
+{
+ if (net->IsExternalTo(netlist))
+ log_error("Found external reference to '%s.%s' in netlist '%s', please use -flatten or -extnets.\n",
+ get_full_netlist_name(net->Owner()).c_str(), net->Name(), get_full_netlist_name(netlist).c_str());
+
+ return net_map.at(net);
+}
+
+void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj)
{
MapIter mi;
Att *attr;
@@ -74,44 +126,47 @@ static void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, D
attributes["\\src"] = stringf("%s:%d", LineFile::GetFileName(obj->Linefile()), LineFile::GetLineNo(obj->Linefile()));
// FIXME: Parse numeric attributes
- FOREACH_ATTRIBUTE(obj, mi, attr)
+ 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()));
+ }
}
-static RTLIL::SigSpec operatorInput(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map)
+RTLIL::SigSpec VerificImporter::operatorInput(Instance *inst)
{
RTLIL::SigSpec sig;
for (int i = int(inst->InputSize())-1; i >= 0; i--)
if (inst->GetInputBit(i))
- sig.append(net_map.at(inst->GetInputBit(i)));
+ sig.append(net_map_at(inst->GetInputBit(i)));
else
sig.append(RTLIL::State::Sz);
return sig;
}
-static RTLIL::SigSpec operatorInput1(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map)
+RTLIL::SigSpec VerificImporter::operatorInput1(Instance *inst)
{
RTLIL::SigSpec sig;
for (int i = int(inst->Input1Size())-1; i >= 0; i--)
if (inst->GetInput1Bit(i))
- sig.append(net_map.at(inst->GetInput1Bit(i)));
+ sig.append(net_map_at(inst->GetInput1Bit(i)));
else
sig.append(RTLIL::State::Sz);
return sig;
}
-static RTLIL::SigSpec operatorInput2(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map)
+RTLIL::SigSpec VerificImporter::operatorInput2(Instance *inst)
{
RTLIL::SigSpec sig;
for (int i = int(inst->Input2Size())-1; i >= 0; i--)
if (inst->GetInput2Bit(i))
- sig.append(net_map.at(inst->GetInput2Bit(i)));
+ sig.append(net_map_at(inst->GetInput2Bit(i)));
else
sig.append(RTLIL::State::Sz);
return sig;
}
-static RTLIL::SigSpec operatorInport(Instance *inst, const char *portname, std::map<Net*, RTLIL::SigBit> &net_map)
+RTLIL::SigSpec VerificImporter::operatorInport(Instance *inst, const char *portname)
{
PortBus *portbus = inst->View()->GetPortBus(portname);
if (portbus) {
@@ -124,7 +179,7 @@ static RTLIL::SigSpec operatorInport(Instance *inst, const char *portname, std::
else if (net->IsPwr())
sig.append(RTLIL::State::S1);
else
- sig.append(net_map.at(net));
+ sig.append(net_map_at(net));
} else
sig.append(RTLIL::State::Sz);
}
@@ -133,17 +188,17 @@ static RTLIL::SigSpec operatorInport(Instance *inst, const char *portname, std::
Port *port = inst->View()->GetPort(portname);
log_assert(port != NULL);
Net *net = inst->GetNet(port);
- return net_map.at(net);
+ return net_map_at(net);
}
}
-static RTLIL::SigSpec operatorOutput(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map, RTLIL::Module *module)
+RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool<Net*, hash_ptr_ops> *any_all_nets)
{
RTLIL::SigSpec sig;
RTLIL::Wire *dummy_wire = NULL;
for (int i = int(inst->OutputSize())-1; i >= 0; i--)
- if (inst->GetOutputBit(i)) {
- sig.append(net_map.at(inst->GetOutputBit(i)));
+ if (inst->GetOutputBit(i) && (!any_all_nets || !any_all_nets->count(inst->GetOutputBit(i)))) {
+ sig.append(net_map_at(inst->GetOutputBit(i)));
dummy_wire = NULL;
} else {
if (dummy_wire == NULL)
@@ -155,72 +210,74 @@ static RTLIL::SigSpec operatorOutput(Instance *inst, std::map<Net*, RTLIL::SigBi
return sig;
}
-static bool import_netlist_instance_gates(RTLIL::Module *module, std::map<Net*, RTLIL::SigBit> &net_map, Instance *inst)
+bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdString inst_name)
{
if (inst->Type() == PRIM_AND) {
- module->addAndGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ module->addAndGate(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_NAND) {
RTLIL::SigSpec tmp = module->addWire(NEW_ID);
- module->addAndGate(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), tmp);
- module->addNotGate(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ module->addAndGate(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
+ module->addNotGate(inst_name, tmp, net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_OR) {
- module->addOrGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ module->addOrGate(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_NOR) {
RTLIL::SigSpec tmp = module->addWire(NEW_ID);
- module->addOrGate(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), tmp);
- module->addNotGate(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ module->addOrGate(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
+ module->addNotGate(inst_name, tmp, net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_XOR) {
- module->addXorGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ module->addXorGate(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_XNOR) {
- module->addXnorGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ module->addXnorGate(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_BUF) {
- module->addBufGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ auto outnet = inst->GetOutput();
+ if (!any_all_nets.count(outnet))
+ module->addBufGate(inst_name, net_map_at(inst->GetInput()), net_map_at(outnet));
return true;
}
if (inst->Type() == PRIM_INV) {
- module->addNotGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ module->addNotGate(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_MUX) {
- module->addMuxGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ module->addMuxGate(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_TRI) {
- module->addMuxGate(RTLIL::escape_id(inst->Name()), RTLIL::State::Sz, net_map.at(inst->GetInput()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ module->addMuxGate(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_FADD)
{
- RTLIL::SigSpec a = net_map.at(inst->GetInput1()), b = net_map.at(inst->GetInput2()), c = net_map.at(inst->GetCin());
- RTLIL::SigSpec x = inst->GetCout() ? net_map.at(inst->GetCout()) : module->addWire(NEW_ID);
- RTLIL::SigSpec y = inst->GetOutput() ? net_map.at(inst->GetOutput()) : module->addWire(NEW_ID);
+ RTLIL::SigSpec a = net_map_at(inst->GetInput1()), b = net_map_at(inst->GetInput2()), c = net_map_at(inst->GetCin());
+ RTLIL::SigSpec x = inst->GetCout() ? net_map_at(inst->GetCout()) : module->addWire(NEW_ID);
+ RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(NEW_ID);
RTLIL::SigSpec tmp1 = module->addWire(NEW_ID);
RTLIL::SigSpec tmp2 = module->addWire(NEW_ID);
RTLIL::SigSpec tmp3 = module->addWire(NEW_ID);
module->addXorGate(NEW_ID, a, b, tmp1);
- module->addXorGate(RTLIL::escape_id(inst->Name()), tmp1, c, y);
+ module->addXorGate(inst_name, tmp1, c, y);
module->addAndGate(NEW_ID, tmp1, c, tmp2);
module->addAndGate(NEW_ID, a, b, tmp3);
module->addOrGate(NEW_ID, tmp2, tmp3, x);
@@ -229,163 +286,168 @@ static bool import_netlist_instance_gates(RTLIL::Module *module, std::map<Net*,
if (inst->Type() == PRIM_DFFRS)
{
+ VerificClocking clocking(this, inst->GetClock());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
- module->addDffGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
else if (inst->GetSet()->IsGnd())
- module->addAdffGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetReset()),
- net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), false);
+ clocking.addAdff(inst_name, net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), State::S0);
else if (inst->GetReset()->IsGnd())
- module->addAdffGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()),
- net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), true);
+ clocking.addAdff(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), State::S1);
else
- module->addDffsrGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()), net_map.at(inst->GetReset()),
- net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ clocking.addDffsr(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetReset()),
+ net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
return true;
}
return false;
}
-static bool import_netlist_instance_cells(RTLIL::Module *module, std::map<Net*, RTLIL::SigBit> &net_map, Instance *inst)
+bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdString inst_name)
{
if (inst->Type() == PRIM_AND) {
- module->addAnd(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ module->addAnd(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_NAND) {
RTLIL::SigSpec tmp = module->addWire(NEW_ID);
- module->addAnd(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), tmp);
- module->addNot(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ module->addAnd(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
+ module->addNot(inst_name, tmp, net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_OR) {
- module->addOr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ module->addOr(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_NOR) {
RTLIL::SigSpec tmp = module->addWire(NEW_ID);
- module->addOr(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), tmp);
- module->addNot(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ module->addOr(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
+ module->addNot(inst_name, tmp, net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_XOR) {
- module->addXor(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ module->addXor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_XNOR) {
- module->addXnor(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ module->addXnor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_INV) {
- module->addNot(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ module->addNot(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_MUX) {
- module->addMux(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ module->addMux(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_TRI) {
- module->addMux(RTLIL::escape_id(inst->Name()), RTLIL::State::Sz, net_map.at(inst->GetInput()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ module->addMux(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == PRIM_FADD)
{
RTLIL::SigSpec a_plus_b = module->addWire(NEW_ID, 2);
- RTLIL::SigSpec y = inst->GetOutput() ? net_map.at(inst->GetOutput()) : module->addWire(NEW_ID);
+ RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(NEW_ID);
if (inst->GetCout())
- y.append(net_map.at(inst->GetCout()));
- module->addAdd(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), a_plus_b);
- module->addAdd(RTLIL::escape_id(inst->Name()), a_plus_b, net_map.at(inst->GetCin()), y);
+ y.append(net_map_at(inst->GetCout()));
+ module->addAdd(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), a_plus_b);
+ module->addAdd(inst_name, a_plus_b, net_map_at(inst->GetCin()), y);
return true;
}
if (inst->Type() == PRIM_DFFRS)
{
+ VerificClocking clocking(this, inst->GetClock());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
- module->addDff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
else if (inst->GetSet()->IsGnd())
- module->addAdff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetReset()),
- net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), RTLIL::State::S0);
+ clocking.addAdff(inst_name, net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S0);
else if (inst->GetReset()->IsGnd())
- module->addAdff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()),
- net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), RTLIL::State::S1);
+ clocking.addAdff(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S1);
else
- module->addDffsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()), net_map.at(inst->GetReset()),
- net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ clocking.addDffsr(inst_name, 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_DLATCHRS)
{
if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
- module->addDlatch(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetControl()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
else
- module->addDlatchsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetControl()), net_map.at(inst->GetSet()), net_map.at(inst->GetReset()),
- net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ 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;
}
- #define IN operatorInput(inst, net_map)
- #define IN1 operatorInput1(inst, net_map)
- #define IN2 operatorInput2(inst, net_map)
- #define OUT operatorOutput(inst, net_map, module)
+ #define IN operatorInput(inst)
+ #define IN1 operatorInput1(inst)
+ #define IN2 operatorInput2(inst)
+ #define OUT operatorOutput(inst)
+ #define FILTERED_OUT operatorOutput(inst, &any_all_nets)
#define SIGNED inst->View()->IsSigned()
if (inst->Type() == OPER_ADDER) {
RTLIL::SigSpec out = OUT;
if (inst->GetCout() != NULL)
- out.append(net_map.at(inst->GetCout()));
+ out.append(net_map_at(inst->GetCout()));
if (inst->GetCin()->IsGnd()) {
- module->addAdd(RTLIL::escape_id(inst->Name()), IN1, IN2, out, SIGNED);
+ module->addAdd(inst_name, IN1, IN2, out, SIGNED);
} else {
RTLIL::SigSpec tmp = module->addWire(NEW_ID, GetSize(out));
module->addAdd(NEW_ID, IN1, IN2, tmp, SIGNED);
- module->addAdd(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetCin()), out, false);
+ module->addAdd(inst_name, tmp, net_map_at(inst->GetCin()), out, false);
}
return true;
}
if (inst->Type() == OPER_MULTIPLIER) {
- module->addMul(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ module->addMul(inst_name, IN1, IN2, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_DIVIDER) {
- module->addDiv(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ module->addDiv(inst_name, IN1, IN2, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_MODULO) {
- module->addMod(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ module->addMod(inst_name, IN1, IN2, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_REMAINDER) {
- module->addMod(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ module->addMod(inst_name, IN1, IN2, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_SHIFT_LEFT) {
- module->addShl(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, false);
+ module->addShl(inst_name, IN1, IN2, OUT, false);
return true;
}
if (inst->Type() == OPER_ENABLED_DECODER) {
RTLIL::SigSpec vec;
- vec.append(net_map.at(inst->GetControl()));
+ vec.append(net_map_at(inst->GetControl()));
for (unsigned i = 1; i < inst->OutputSize(); i++) {
vec.append(RTLIL::State::S0);
}
- module->addShl(RTLIL::escape_id(inst->Name()), vec, IN, OUT, false);
+ module->addShl(inst_name, vec, IN, OUT, false);
return true;
}
@@ -395,7 +457,7 @@ static bool import_netlist_instance_cells(RTLIL::Module *module, std::map<Net*,
for (unsigned i = 1; i < inst->OutputSize(); i++) {
vec.append(RTLIL::State::S0);
}
- module->addShl(RTLIL::escape_id(inst->Name()), vec, IN, OUT, false);
+ module->addShl(inst_name, vec, IN, OUT, false);
return true;
}
@@ -403,112 +465,163 @@ static bool import_netlist_instance_cells(RTLIL::Module *module, std::map<Net*,
Net *net_cin = inst->GetCin();
Net *net_a_msb = inst->GetInput1Bit(0);
if (net_cin->IsGnd())
- module->addShr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, false);
+ module->addShr(inst_name, IN1, IN2, OUT, false);
else if (net_cin == net_a_msb)
- module->addSshr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, true);
+ module->addSshr(inst_name, IN1, IN2, OUT, true);
else
log_error("Can't import Verific OPER_SHIFT_RIGHT instance %s: carry_in is neither 0 nor msb of left input\n", inst->Name());
return true;
}
if (inst->Type() == OPER_REDUCE_AND) {
- module->addReduceAnd(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ module->addReduceAnd(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
return true;
}
if (inst->Type() == OPER_REDUCE_OR) {
- module->addReduceOr(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
return true;
}
if (inst->Type() == OPER_REDUCE_XOR) {
- module->addReduceXor(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ module->addReduceXor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
return true;
}
if (inst->Type() == OPER_REDUCE_XNOR) {
- module->addReduceXnor(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ module->addReduceXnor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_REDUCE_NOR) {
+ SigSpec t = module->ReduceOr(NEW_ID, IN, SIGNED);
+ module->addNot(inst_name, t, net_map_at(inst->GetOutput()));
return true;
}
if (inst->Type() == OPER_LESSTHAN) {
Net *net_cin = inst->GetCin();
if (net_cin->IsGnd())
- module->addLt(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ module->addLt(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
else if (net_cin->IsPwr())
- module->addLe(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ module->addLe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
else
log_error("Can't import Verific OPER_LESSTHAN instance %s: carry_in is neither 0 nor 1\n", inst->Name());
return true;
}
if (inst->Type() == OPER_WIDE_AND) {
- module->addAnd(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ module->addAnd(inst_name, IN1, IN2, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_WIDE_OR) {
- module->addOr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ module->addOr(inst_name, IN1, IN2, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_WIDE_XOR) {
- module->addXor(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ module->addXor(inst_name, IN1, IN2, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_WIDE_XNOR) {
- module->addXnor(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ module->addXnor(inst_name, IN1, IN2, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_WIDE_BUF) {
- module->addPos(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED);
+ module->addPos(inst_name, IN, FILTERED_OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_WIDE_INV) {
- module->addNot(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED);
+ module->addNot(inst_name, IN, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_MINUS) {
- module->addSub(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ module->addSub(inst_name, IN1, IN2, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_UMINUS) {
- module->addNeg(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED);
+ module->addNeg(inst_name, IN, OUT, SIGNED);
return true;
}
if (inst->Type() == OPER_EQUAL) {
- module->addEq(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ module->addEq(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
return true;
}
if (inst->Type() == OPER_NEQUAL) {
- module->addNe(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ module->addNe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
return true;
}
if (inst->Type() == OPER_WIDE_MUX) {
- module->addMux(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetControl()), OUT);
+ module->addMux(inst_name, IN1, IN2, net_map_at(inst->GetControl()), OUT);
+ return true;
+ }
+
+ if (inst->Type() == OPER_NTO1MUX) {
+ module->addShr(inst_name, IN2, IN1, net_map_at(inst->GetOutput()));
+ 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);
+ }
+
+ module->addShr(inst_name, padded_data, sel, out);
+ return true;
+ }
+
+ if (inst->Type() == OPER_SELECTOR)
+ {
+ module->addPmux(inst_name, State::S0, IN2, IN1, net_map_at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_SELECTOR)
+ {
+ SigSpec out = OUT;
+ module->addPmux(inst_name, SigSpec(State::S0, GetSize(out)), IN2, IN1, out);
return true;
}
if (inst->Type() == OPER_WIDE_TRI) {
- module->addMux(RTLIL::escape_id(inst->Name()), RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map.at(inst->GetControl()), OUT);
+ module->addMux(inst_name, RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map_at(inst->GetControl()), OUT);
return true;
}
- if (inst->Type() == OPER_WIDE_DFFRS) {
- RTLIL::SigSpec sig_set = operatorInport(inst, "set", net_map);
- RTLIL::SigSpec sig_reset = operatorInport(inst, "reset", net_map);
+ if (inst->Type() == OPER_WIDE_DFFRS)
+ {
+ VerificClocking clocking(this, inst->GetClock());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ 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())
- module->addDff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), IN, OUT);
+ clocking.addDff(inst_name, IN, OUT);
else
- module->addDffsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), sig_set, sig_reset, IN, OUT);
+ clocking.addDffsr(inst_name, sig_set, sig_reset, IN, OUT);
+
return true;
}
@@ -521,23 +634,95 @@ static bool import_netlist_instance_cells(RTLIL::Module *module, std::map<Net*,
return false;
}
-static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo, bool mode_gates)
+void VerificImporter::merge_past_ffs_clock(pool<RTLIL::Cell*> &candidates, SigBit clock, bool clock_pol)
+{
+ bool keep_running = true;
+ SigMap sigmap;
+
+ while (keep_running)
+ {
+ keep_running = false;
+
+ dict<SigBit, pool<RTLIL::Cell*>> dbits_db;
+ SigSpec dbits;
+
+ for (auto cell : candidates) {
+ SigBit bit = sigmap(cell->getPort("\\D"));
+ dbits_db[bit].insert(cell);
+ dbits.append(bit);
+ }
+
+ dbits.sort_and_unify();
+
+ for (auto chunk : dbits.chunks())
+ {
+ SigSpec sig_d = chunk;
+
+ if (chunk.wire == nullptr || GetSize(sig_d) == 1)
+ continue;
+
+ SigSpec sig_q = module->addWire(NEW_ID, GetSize(sig_d));
+ RTLIL::Cell *new_ff = module->addDff(NEW_ID, clock, sig_d, sig_q, clock_pol);
+
+ if (verific_verbose)
+ log(" merging single-bit past_ffs into new %d-bit ff %s.\n", GetSize(sig_d), log_id(new_ff));
+
+ for (int i = 0; i < GetSize(sig_d); i++)
+ for (auto old_ff : dbits_db[sig_d[i]])
+ {
+ if (verific_verbose)
+ log(" replacing old ff %s on bit %d.\n", log_id(old_ff), i);
+
+ SigBit old_q = old_ff->getPort("\\Q");
+ SigBit new_q = sig_q[i];
+
+ sigmap.add(old_q, new_q);
+ module->connect(old_q, new_q);
+ candidates.erase(old_ff);
+ module->remove(old_ff);
+ keep_running = true;
+ }
+ }
+ }
+}
+
+void VerificImporter::merge_past_ffs(pool<RTLIL::Cell*> &candidates)
+{
+ dict<pair<SigBit, int>, pool<RTLIL::Cell*>> database;
+
+ for (auto cell : candidates)
+ {
+ SigBit clock = cell->getPort("\\CLK");
+ bool clock_pol = cell->getParam("\\CLK_POLARITY").as_bool();
+ database[make_pair(clock, int(clock_pol))].insert(cell);
+ }
+
+ for (auto it : database)
+ merge_past_ffs_clock(it.second, it.first.first, it.first.second);
+}
+
+void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo)
{
std::string module_name = nl->IsOperator() ? std::string("$verific$") + nl->Owner()->Name() : RTLIL::escape_id(nl->Owner()->Name());
+ netlist = nl;
+
if (design->has(module_name)) {
if (!nl->IsOperator())
log_cmd_error("Re-definition of module `%s'.\n", nl->Owner()->Name());
return;
}
- RTLIL::Module *module = new RTLIL::Module;
+ module = new RTLIL::Module;
module->name = module_name;
design->add(module);
- log("Importing module %s.\n", RTLIL::id2cstr(module->name));
-
- std::map<Net*, RTLIL::SigBit> net_map;
+ if (nl->IsBlackBox()) {
+ log("Importing blackbox module %s.\n", RTLIL::id2cstr(module->name));
+ module->set_bool_attribute("\\blackbox");
+ } else {
+ log("Importing module %s.\n", RTLIL::id2cstr(module->name));
+ }
SetIter si;
MapIter mi, mi2;
@@ -553,7 +738,8 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*
if (port->Bus())
continue;
- // log(" importing port %s.\n", port->Name());
+ if (verific_verbose)
+ log(" importing port %s.\n", port->Name());
RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(port->Name()));
import_attributes(wire->attributes, port);
@@ -570,15 +756,16 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*
if (net_map.count(net) == 0)
net_map[net] = wire;
else if (wire->port_input)
- module->connect(net_map.at(net), wire);
+ module->connect(net_map_at(net), wire);
else
- module->connect(wire, net_map.at(net));
+ module->connect(wire, net_map_at(net));
}
}
FOREACH_PORTBUS_OF_NETLIST(nl, mi, portbus)
{
- // log(" importing portbus %s.\n", portbus->Name());
+ if (verific_verbose)
+ log(" importing portbus %s.\n", portbus->Name());
RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(portbus->Name()), portbus->Size());
wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex());
@@ -596,9 +783,9 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*
if (net_map.count(net) == 0)
net_map[net] = bit;
else if (wire->port_input)
- module->connect(net_map.at(net), bit);
+ module->connect(net_map_at(net), bit);
else
- module->connect(bit, net_map.at(net));
+ module->connect(bit, net_map_at(net));
}
if (i == portbus->RightIndex())
break;
@@ -607,6 +794,11 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*
module->fixup_ports();
+ dict<Net*, char, hash_ptr_ops> init_nets;
+ pool<Net*, hash_ptr_ops> anyconst_nets, anyseq_nets;
+ pool<Net*, hash_ptr_ops> allconst_nets, allseq_nets;
+ any_all_nets.clear();
+
FOREACH_NET_OF_NETLIST(nl, mi, net)
{
if (net->IsRamNet())
@@ -633,20 +825,98 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*
memory->width = bits_in_word;
memory->size = number_of_bits / bits_in_word;
+
+ const char *ascii_initdata = net->GetWideInitialValue();
+ if (ascii_initdata) {
+ while (*ascii_initdata != 0 && *ascii_initdata != '\'')
+ ascii_initdata++;
+ if (*ascii_initdata == '\'')
+ ascii_initdata++;
+ if (*ascii_initdata != 0) {
+ log_assert(*ascii_initdata == 'b');
+ ascii_initdata++;
+ }
+ for (int word_idx = 0; word_idx < memory->size; word_idx++) {
+ Const initval = Const(State::Sx, memory->width);
+ bool initval_valid = false;
+ for (int bit_idx = memory->width-1; bit_idx >= 0; bit_idx--) {
+ if (*ascii_initdata == 0)
+ break;
+ if (*ascii_initdata == '0' || *ascii_initdata == '1') {
+ initval[bit_idx] = (*ascii_initdata == '0') ? State::S0 : State::S1;
+ initval_valid = true;
+ }
+ ascii_initdata++;
+ }
+ if (initval_valid) {
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$meminit");
+ cell->parameters["\\WORDS"] = 1;
+ if (net->GetOrigTypeRange()->LeftRangeBound() < net->GetOrigTypeRange()->RightRangeBound())
+ cell->setPort("\\ADDR", word_idx);
+ else
+ cell->setPort("\\ADDR", memory->size - word_idx - 1);
+ cell->setPort("\\DATA", initval);
+ cell->parameters["\\MEMID"] = RTLIL::Const(memory->name.str());
+ cell->parameters["\\ABITS"] = 32;
+ cell->parameters["\\WIDTH"] = memory->width;
+ cell->parameters["\\PRIORITY"] = RTLIL::Const(autoidx-1);
+ }
+ }
+ }
continue;
}
+ if (net->GetInitialValue())
+ init_nets[net] = net->GetInitialValue();
+
+ const char *rand_const_attr = net->GetAttValue(" rand_const");
+ const char *rand_attr = net->GetAttValue(" rand");
+
+ const char *anyconst_attr = net->GetAttValue("anyconst");
+ const char *anyseq_attr = net->GetAttValue("anyseq");
+
+ const char *allconst_attr = net->GetAttValue("allconst");
+ const char *allseq_attr = net->GetAttValue("allseq");
+
+ if (rand_const_attr != nullptr && (!strcmp(rand_const_attr, "1") || !strcmp(rand_const_attr, "'1'"))) {
+ anyconst_nets.insert(net);
+ any_all_nets.insert(net);
+ }
+ else if (rand_attr != nullptr && (!strcmp(rand_attr, "1") || !strcmp(rand_attr, "'1'"))) {
+ anyseq_nets.insert(net);
+ any_all_nets.insert(net);
+ }
+ else if (anyconst_attr != nullptr && (!strcmp(anyconst_attr, "1") || !strcmp(anyconst_attr, "'1'"))) {
+ anyconst_nets.insert(net);
+ any_all_nets.insert(net);
+ }
+ else if (anyseq_attr != nullptr && (!strcmp(anyseq_attr, "1") || !strcmp(anyseq_attr, "'1'"))) {
+ anyseq_nets.insert(net);
+ any_all_nets.insert(net);
+ }
+ else if (allconst_attr != nullptr && (!strcmp(allconst_attr, "1") || !strcmp(allconst_attr, "'1'"))) {
+ allconst_nets.insert(net);
+ any_all_nets.insert(net);
+ }
+ else if (allseq_attr != nullptr && (!strcmp(allseq_attr, "1") || !strcmp(allseq_attr, "'1'"))) {
+ allseq_nets.insert(net);
+ any_all_nets.insert(net);
+ }
+
if (net_map.count(net)) {
- // log(" skipping net %s.\n", net->Name());
+ if (verific_verbose)
+ log(" skipping net %s.\n", net->Name());
continue;
}
if (net->Bus())
continue;
- // log(" importing net %s.\n", net->Name());
+ RTLIL::IdString wire_name = module->uniquify(mode_names || net->IsUserDeclared() ? RTLIL::escape_id(net->Name()) : NEW_ID);
+
+ if (verific_verbose)
+ log(" importing net %s as %s.\n", net->Name(), log_id(wire_name));
- RTLIL::IdString wire_name = module->uniquify(RTLIL::escape_id(net->Name()));
RTLIL::Wire *wire = module->addWire(wire_name);
import_attributes(wire->attributes, net);
@@ -666,81 +936,191 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*
if (found_new_net)
{
- // log(" importing netbus %s.\n", netbus->Name());
+ RTLIL::IdString wire_name = module->uniquify(mode_names || netbus->IsUserDeclared() ? RTLIL::escape_id(netbus->Name()) : NEW_ID);
+
+ if (verific_verbose)
+ log(" importing netbus %s as %s.\n", netbus->Name(), log_id(wire_name));
- RTLIL::IdString wire_name = module->uniquify(RTLIL::escape_id(netbus->Name()));
RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size());
wire->start_offset = min(netbus->LeftIndex(), netbus->RightIndex());
import_attributes(wire->attributes, netbus);
- for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) {
- if (netbus->ElementAtIndex(i)) {
+ RTLIL::Const initval = Const(State::Sx, GetSize(wire));
+ bool initval_valid = false;
+
+ for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1)
+ {
+ if (netbus->ElementAtIndex(i))
+ {
+ int bitidx = i - wire->start_offset;
net = netbus->ElementAtIndex(i);
- RTLIL::SigBit bit(wire, i - wire->start_offset);
+ RTLIL::SigBit bit(wire, bitidx);
+
+ if (init_nets.count(net)) {
+ if (init_nets.at(net) == '0')
+ initval.bits.at(bitidx) = State::S0;
+ if (init_nets.at(net) == '1')
+ initval.bits.at(bitidx) = State::S1;
+ initval_valid = true;
+ init_nets.erase(net);
+ }
+
if (net_map.count(net) == 0)
net_map[net] = bit;
else
- module->connect(bit, net_map.at(net));
+ module->connect(bit, net_map_at(net));
}
+
if (i == netbus->RightIndex())
break;
}
+
+ if (initval_valid)
+ wire->attributes["\\init"] = initval;
}
else
{
- // log(" skipping netbus %s.\n", netbus->Name());
+ if (verific_verbose)
+ log(" skipping netbus %s.\n", netbus->Name());
+ }
+
+ SigSpec anyconst_sig;
+ SigSpec anyseq_sig;
+ SigSpec allconst_sig;
+ SigSpec allseq_sig;
+
+ for (int i = netbus->RightIndex();; i += netbus->IsUp() ? -1 : +1) {
+ net = netbus->ElementAtIndex(i);
+ if (net != nullptr && anyconst_nets.count(net)) {
+ anyconst_sig.append(net_map_at(net));
+ anyconst_nets.erase(net);
+ }
+ if (net != nullptr && anyseq_nets.count(net)) {
+ anyseq_sig.append(net_map_at(net));
+ anyseq_nets.erase(net);
+ }
+ if (net != nullptr && allconst_nets.count(net)) {
+ allconst_sig.append(net_map_at(net));
+ allconst_nets.erase(net);
+ }
+ if (net != nullptr && allseq_nets.count(net)) {
+ allseq_sig.append(net_map_at(net));
+ allseq_nets.erase(net);
+ }
+ if (i == netbus->LeftIndex())
+ break;
}
+
+ if (GetSize(anyconst_sig))
+ module->connect(anyconst_sig, module->Anyconst(NEW_ID, GetSize(anyconst_sig)));
+
+ if (GetSize(anyseq_sig))
+ module->connect(anyseq_sig, module->Anyseq(NEW_ID, GetSize(anyseq_sig)));
+
+ if (GetSize(allconst_sig))
+ module->connect(allconst_sig, module->Allconst(NEW_ID, GetSize(allconst_sig)));
+
+ if (GetSize(allseq_sig))
+ module->connect(allseq_sig, module->Allseq(NEW_ID, GetSize(allseq_sig)));
}
+ for (auto it : init_nets)
+ {
+ Const initval;
+ SigBit bit = net_map_at(it.first);
+ log_assert(bit.wire);
+
+ if (bit.wire->attributes.count("\\init"))
+ initval = bit.wire->attributes.at("\\init");
+
+ while (GetSize(initval) < GetSize(bit.wire))
+ initval.bits.push_back(State::Sx);
+
+ if (it.second == '0')
+ initval.bits.at(bit.offset) = State::S0;
+ if (it.second == '1')
+ initval.bits.at(bit.offset) = State::S1;
+
+ bit.wire->attributes["\\init"] = initval;
+ }
+
+ for (auto net : anyconst_nets)
+ module->connect(net_map_at(net), module->Anyconst(NEW_ID));
+
+ for (auto net : anyseq_nets)
+ module->connect(net_map_at(net), module->Anyseq(NEW_ID));
+
+ pool<Instance*, hash_ptr_ops> sva_asserts;
+ pool<Instance*, hash_ptr_ops> sva_assumes;
+ pool<Instance*, hash_ptr_ops> sva_covers;
+ pool<Instance*, hash_ptr_ops> sva_triggers;
+
+ pool<RTLIL::Cell*> past_ffs;
+
FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst)
{
- // log(" importing cell %s (%s).\n", inst->Name(), inst->View()->Owner()->Name());
+ RTLIL::IdString inst_name = module->uniquify(mode_names || inst->IsUserDeclared() ? RTLIL::escape_id(inst->Name()) : NEW_ID);
+
+ if (verific_verbose)
+ log(" importing cell %s (%s) as %s.\n", inst->Name(), inst->View()->Owner()->Name(), log_id(inst_name));
+
+ if (mode_verific)
+ goto import_verific_cells;
if (inst->Type() == PRIM_PWR) {
- module->connect(net_map.at(inst->GetOutput()), RTLIL::State::S1);
+ module->connect(net_map_at(inst->GetOutput()), RTLIL::State::S1);
continue;
}
if (inst->Type() == PRIM_GND) {
- module->connect(net_map.at(inst->GetOutput()), RTLIL::State::S0);
+ module->connect(net_map_at(inst->GetOutput()), RTLIL::State::S0);
continue;
}
if (inst->Type() == PRIM_BUF) {
- module->addBufGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ auto outnet = inst->GetOutput();
+ if (!any_all_nets.count(outnet))
+ module->addBufGate(inst_name, net_map_at(inst->GetInput()), net_map_at(outnet));
continue;
}
if (inst->Type() == PRIM_X) {
- module->connect(net_map.at(inst->GetOutput()), RTLIL::State::Sx);
+ module->connect(net_map_at(inst->GetOutput()), RTLIL::State::Sx);
continue;
}
if (inst->Type() == PRIM_Z) {
- module->connect(net_map.at(inst->GetOutput()), RTLIL::State::Sz);
+ module->connect(net_map_at(inst->GetOutput()), RTLIL::State::Sz);
continue;
}
if (inst->Type() == OPER_READ_PORT)
{
RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetInput()->Name()));
- if (memory->width != int(inst->OutputSize()))
- log_error("Import of asymetric memories from Verific is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name());
+ int numchunks = int(inst->OutputSize()) / memory->width;
+ int chunksbits = ceil_log2(numchunks);
- RTLIL::SigSpec addr = operatorInput1(inst, net_map);
- RTLIL::SigSpec data = operatorOutput(inst, net_map, module);
+ if ((numchunks * memory->width) != int(inst->OutputSize()) || (numchunks & (numchunks - 1)) != 0)
+ log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name());
- RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), "$memrd");
- cell->parameters["\\MEMID"] = memory->name.str();
- cell->parameters["\\CLK_ENABLE"] = false;
- cell->parameters["\\CLK_POLARITY"] = true;
- cell->parameters["\\TRANSPARENT"] = false;
- cell->parameters["\\ABITS"] = GetSize(addr);
- cell->parameters["\\WIDTH"] = GetSize(data);
- cell->setPort("\\CLK", RTLIL::State::Sx);
- cell->setPort("\\EN", RTLIL::State::Sx);
- cell->setPort("\\ADDR", addr);
- cell->setPort("\\DATA", data);
+ for (int i = 0; i < numchunks; i++)
+ {
+ RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)};
+ RTLIL::SigSpec data = operatorOutput(inst).extract(i * memory->width, memory->width);
+
+ RTLIL::Cell *cell = module->addCell(numchunks == 1 ? inst_name :
+ RTLIL::IdString(stringf("%s_%d", inst_name.c_str(), i)), "$memrd");
+ cell->parameters["\\MEMID"] = memory->name.str();
+ cell->parameters["\\CLK_ENABLE"] = false;
+ cell->parameters["\\CLK_POLARITY"] = true;
+ cell->parameters["\\TRANSPARENT"] = false;
+ cell->parameters["\\ABITS"] = GetSize(addr);
+ cell->parameters["\\WIDTH"] = GetSize(data);
+ cell->setPort("\\CLK", RTLIL::State::Sx);
+ cell->setPort("\\EN", RTLIL::State::Sx);
+ cell->setPort("\\ADDR", addr);
+ cell->setPort("\\DATA", data);
+ }
continue;
}
@@ -748,52 +1128,197 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*
{
RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetOutput()->Name()));
if (memory->width != int(inst->Input2Size()))
- log_error("Import of asymetric memories from Verific is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name());
+ log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name());
- RTLIL::SigSpec addr = operatorInput1(inst, net_map);
- RTLIL::SigSpec data = operatorInput2(inst, net_map);
+ RTLIL::SigSpec addr = operatorInput1(inst);
+ RTLIL::SigSpec data = operatorInput2(inst);
- RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), "$memwr");
+ RTLIL::Cell *cell = module->addCell(inst_name, "$memwr");
cell->parameters["\\MEMID"] = memory->name.str();
cell->parameters["\\CLK_ENABLE"] = false;
cell->parameters["\\CLK_POLARITY"] = true;
cell->parameters["\\PRIORITY"] = 0;
cell->parameters["\\ABITS"] = GetSize(addr);
cell->parameters["\\WIDTH"] = GetSize(data);
- cell->setPort("\\EN", RTLIL::SigSpec(net_map.at(inst->GetControl())).repeat(GetSize(data)));
+ cell->setPort("\\EN", RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data)));
cell->setPort("\\CLK", RTLIL::State::S0);
cell->setPort("\\ADDR", addr);
cell->setPort("\\DATA", data);
if (inst->Type() == OPER_CLOCKED_WRITE_PORT) {
cell->parameters["\\CLK_ENABLE"] = true;
- cell->setPort("\\CLK", net_map.at(inst->GetClock()));
+ cell->setPort("\\CLK", net_map_at(inst->GetClock()));
}
continue;
}
if (!mode_gates) {
- if (import_netlist_instance_cells(module, net_map, inst))
+ if (import_netlist_instance_cells(inst, inst_name))
continue;
- if (inst->IsOperator())
+ if (inst->IsOperator() && !verific_sva_prims.count(inst->Type()))
log_warning("Unsupported Verific operator: %s (fallback to gate level implementation provided by verific)\n", inst->View()->Owner()->Name());
} else {
- if (import_netlist_instance_gates(module, net_map, inst))
+ if (import_netlist_instance_gates(inst, inst_name))
continue;
}
+ if (inst->Type() == PRIM_SVA_ASSERT || inst->Type() == PRIM_SVA_IMMEDIATE_ASSERT)
+ sva_asserts.insert(inst);
+
+ if (inst->Type() == PRIM_SVA_ASSUME || inst->Type() == PRIM_SVA_IMMEDIATE_ASSUME)
+ sva_assumes.insert(inst);
+
+ if (inst->Type() == PRIM_SVA_COVER || inst->Type() == PRIM_SVA_IMMEDIATE_COVER)
+ sva_covers.insert(inst);
+
+ if (inst->Type() == PRIM_SVA_TRIGGERED)
+ sva_triggers.insert(inst);
+
+ if (inst->Type() == OPER_SVA_STABLE)
+ {
+ VerificClocking clocking(this, inst->GetInput2Bit(0));
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ log_assert(inst->Input1Size() == inst->OutputSize());
+
+ SigSpec sig_d, sig_q, sig_o;
+ sig_q = module->addWire(NEW_ID, inst->Input1Size());
+
+ for (int i = int(inst->Input1Size())-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) {
+ 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));
+ }
+
+ clocking.addDff(NEW_ID, sig_d, sig_q);
+ module->addXnor(NEW_ID, sig_d, sig_q, sig_o);
+
+ if (!mode_keep)
+ continue;
+ }
+
+ if (inst->Type() == PRIM_SVA_STABLE)
+ {
+ VerificClocking clocking(this, inst->GetInput2());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ SigSpec sig_d = net_map_at(inst->GetInput1());
+ SigSpec sig_o = net_map_at(inst->GetOutput());
+ SigSpec sig_q = module->addWire(NEW_ID);
+
+ if (verific_verbose) {
+ 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));
+ }
+
+ clocking.addDff(NEW_ID, sig_d, sig_q);
+ module->addXnor(NEW_ID, sig_d, sig_q, sig_o);
+
+ if (!mode_keep)
+ continue;
+ }
+
+ if (inst->Type() == PRIM_SVA_PAST)
+ {
+ VerificClocking clocking(this, inst->GetInput2());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ SigBit sig_d = net_map_at(inst->GetInput1());
+ SigBit sig_q = net_map_at(inst->GetOutput());
+
+ if (verific_verbose)
+ 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));
+
+ past_ffs.insert(clocking.addDff(NEW_ID, sig_d, sig_q));
+
+ if (!mode_keep)
+ continue;
+ }
+
+ if ((inst->Type() == PRIM_SVA_ROSE || inst->Type() == PRIM_SVA_FELL))
+ {
+ VerificClocking clocking(this, inst->GetInput2());
+ log_assert(clocking.disable_sig == State::S0);
+ log_assert(clocking.body_net == nullptr);
+
+ SigBit sig_d = net_map_at(inst->GetInput1());
+ SigBit sig_o = net_map_at(inst->GetOutput());
+ SigBit sig_q = module->addWire(NEW_ID);
+
+ if (verific_verbose)
+ 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));
+
+ clocking.addDff(NEW_ID, sig_d, sig_q);
+ module->addEq(NEW_ID, {sig_q, sig_d}, Const(inst->Type() == PRIM_SVA_ROSE ? 1 : 2, 2), sig_o);
+
+ if (!mode_keep)
+ continue;
+ }
+
+ if (!mode_keep && verific_sva_prims.count(inst->Type())) {
+ if (verific_verbose)
+ log(" skipping SVA cell in non k-mode\n");
+ continue;
+ }
+
+ if (inst->Type() == PRIM_HDL_ASSERTION)
+ {
+ SigBit cond = net_map_at(inst->GetInput());
+
+ if (verific_verbose)
+ log(" assert condition %s.\n", log_signal(cond));
+
+ const char *assume_attr = nullptr; // inst->GetAttValue("assume");
+
+ Cell *cell = nullptr;
+ if (assume_attr != nullptr && !strcmp(assume_attr, "1"))
+ cell = module->addAssume(NEW_ID, cond, State::S1);
+ else
+ cell = module->addAssert(NEW_ID, cond, State::S1);
+
+ import_attributes(cell->attributes, inst);
+ continue;
+ }
+
if (inst->IsPrimitive())
- log_error("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name());
+ {
+ if (!mode_keep)
+ log_error("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name());
+
+ if (!verific_sva_prims.count(inst->Type()))
+ log_warning("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name());
+ }
+ import_verific_cells:
nl_todo.insert(inst->View());
- RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), inst->IsOperator() ?
+ RTLIL::Cell *cell = module->addCell(inst_name, inst->IsOperator() ?
std::string("$verific$") + inst->View()->Owner()->Name() : RTLIL::escape_id(inst->View()->Owner()->Name()));
+ if (inst->IsPrimitive() && mode_keep)
+ cell->attributes["\\keep"] = 1;
+
dict<IdString, vector<SigBit>> cell_port_conns;
+ if (verific_verbose)
+ log(" ports in verific db:\n");
+
FOREACH_PORTREF_OF_INST(inst, mi2, pr) {
- // log(" .%s(%s)\n", pr->GetPort()->Name(), pr->GetNet()->Name());
+ if (verific_verbose)
+ log(" .%s(%s)\n", pr->GetPort()->Name(), pr->GetNet()->Name());
const char *port_name = pr->GetPort()->Name();
int port_offset = 0;
if (pr->GetPort()->Bus()) {
@@ -808,127 +1333,685 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*
for (auto bit : zwires)
sigvec.push_back(bit);
}
- sigvec[port_offset] = net_map.at(pr->GetNet());
+ sigvec[port_offset] = net_map_at(pr->GetNet());
}
+ if (verific_verbose)
+ log(" ports in yosys db:\n");
+
for (auto &it : cell_port_conns) {
- // log(" .%s(%s)\n", log_id(it.first), log_signal(it.second));
+ if (verific_verbose)
+ log(" .%s(%s)\n", log_id(it.first), log_signal(it.second));
cell->setPort(it.first, it.second);
}
}
+
+ if (!mode_nosva)
+ {
+ for (auto inst : sva_asserts) {
+ if (mode_autocover)
+ verific_import_sva_cover(this, inst);
+ verific_import_sva_assert(this, inst);
+ }
+
+ for (auto inst : sva_assumes)
+ verific_import_sva_assume(this, inst);
+
+ for (auto inst : sva_covers)
+ verific_import_sva_cover(this, inst);
+
+ for (auto inst : sva_triggers)
+ verific_import_sva_trigger(this, inst);
+
+ merge_past_ffs(past_ffs);
+ }
+}
+
+// ==================================================================
+
+VerificClocking::VerificClocking(VerificImporter *importer, Net *net, bool sva_at_only)
+{
+ module = importer->module;
+
+ log_assert(importer != nullptr);
+ log_assert(net != nullptr);
+
+ Instance *inst = net->Driver();
+
+ if (inst != nullptr && inst->Type() == PRIM_SVA_AT)
+ {
+ net = inst->GetInput1();
+ body_net = inst->GetInput2();
+
+ inst = net->Driver();
+
+ Instance *body_inst = body_net->Driver();
+ if (body_inst != nullptr && body_inst->Type() == PRIM_SVA_DISABLE_IFF) {
+ disable_net = body_inst->GetInput1();
+ disable_sig = importer->net_map_at(disable_net);
+ body_net = body_inst->GetInput2();
+ }
+ }
+ else
+ {
+ if (sva_at_only)
+ return;
+ }
+
+ // Use while() instead of if() to work around VIPER #13453
+ while (inst != nullptr && inst->Type() == PRIM_SVA_POSEDGE)
+ {
+ net = inst->GetInput();
+ inst = net->Driver();;
+ }
+
+ if (inst != nullptr && inst->Type() == PRIM_INV)
+ {
+ net = inst->GetInput();
+ inst = net->Driver();;
+ posedge = false;
+ }
+
+ // Detect clock-enable circuit
+ do {
+ if (inst == nullptr || inst->Type() != PRIM_AND)
+ break;
+
+ Net *net_dlatch = inst->GetInput1();
+ Instance *inst_dlatch = net_dlatch->Driver();
+
+ if (inst_dlatch == nullptr || inst_dlatch->Type() != PRIM_DLATCHRS)
+ break;
+
+ if (!inst_dlatch->GetSet()->IsGnd() || !inst_dlatch->GetReset()->IsGnd())
+ break;
+
+ Net *net_enable = inst_dlatch->GetInput();
+ Net *net_not_clock = inst_dlatch->GetControl();
+
+ if (net_enable == nullptr || net_not_clock == nullptr)
+ break;
+
+ Instance *inst_not_clock = net_not_clock->Driver();
+
+ if (inst_not_clock == nullptr || inst_not_clock->Type() != PRIM_INV)
+ break;
+
+ Net *net_clock1 = inst_not_clock->GetInput();
+ Net *net_clock2 = inst->GetInput2();
+
+ if (net_clock1 == nullptr || net_clock1 != net_clock2)
+ break;
+
+ enable_net = net_enable;
+ enable_sig = importer->net_map_at(enable_net);
+
+ net = net_clock1;
+ inst = net->Driver();;
+ } while (0);
+
+ // Detect condition expression
+ do {
+ if (body_net == nullptr)
+ break;
+
+ Instance *inst_mux = body_net->Driver();
+
+ if (inst_mux == nullptr || inst_mux->Type() != PRIM_MUX)
+ break;
+
+ if (!inst_mux->GetInput1()->IsPwr())
+ break;
+
+ Net *sva_net = inst_mux->GetInput2();
+ if (!verific_is_sva_net(importer, sva_net))
+ break;
+
+ body_net = sva_net;
+ cond_net = inst_mux->GetControl();
+ } while (0);
+
+ clock_net = net;
+ clock_sig = importer->net_map_at(clock_net);
+
+ const char *gclk_attr = clock_net->GetAttValue("gclk");
+ if (gclk_attr != nullptr && (!strcmp(gclk_attr, "1") || !strcmp(gclk_attr, "'1'")))
+ gclk = true;
}
+Cell *VerificClocking::addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const init_value)
+{
+ 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["\\init"] = init_value;
+ } else {
+ Wire *w = module->addWire(NEW_ID, GetSize(sig_q));
+ w->attributes["\\init"] = init_value;
+ module->connect(sig_q, w);
+ sig_q = 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));
+ return module->addAdff(name, clock_sig, disable_sig, sig_d, sig_q, init_value, posedge);
+ }
+
+ if (gclk)
+ return module->addFf(name, sig_d, sig_q);
+
+ return module->addDff(name, clock_sig, sig_d, sig_q, posedge);
+}
+
+Cell *VerificClocking::addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec sig_d, SigSpec sig_q, Const arst_value)
+{
+ log_assert(gclk == false);
+ log_assert(disable_sig == State::S0);
+
+ if (enable_sig != State::S1)
+ sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig);
+
+ return module->addAdff(name, clock_sig, sig_arst, sig_d, sig_q, arst_value, posedge);
+}
+
+Cell *VerificClocking::addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, SigSpec sig_d, SigSpec sig_q)
+{
+ log_assert(gclk == false);
+ log_assert(disable_sig == State::S0);
+
+ 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);
+}
+
+// ==================================================================
+
+struct VerificExtNets
+{
+ int portname_cnt = 0;
+
+ // a map from Net to the same Net one level up in the design hierarchy
+ std::map<Net*, Net*> net_level_up;
+
+ Net *get_net_level_up(Net *net)
+ {
+ if (net_level_up.count(net) == 0)
+ {
+ Netlist *nl = net->Owner();
+
+ // Simply return if Netlist is not unique
+ if (nl->NumOfRefs() != 1)
+ return net;
+
+ Instance *up_inst = (Instance*)nl->GetReferences()->GetLast();
+ Netlist *up_nl = up_inst->Owner();
+
+ // create new Port
+ string name = stringf("___extnets_%d", portname_cnt++);
+ Port *new_port = new Port(name.c_str(), DIR_OUT);
+ nl->Add(new_port);
+ net->Connect(new_port);
+
+ // create new Net in up Netlist
+ Net *new_net = new Net(name.c_str());
+ up_nl->Add(new_net);
+ up_inst->Connect(new_port, new_net);
+
+ net_level_up[net] = new_net;
+ }
+
+ return net_level_up.at(net);
+ }
+
+ void run(Netlist *nl)
+ {
+ MapIter mi, mi2;
+ Instance *inst;
+ PortRef *pr;
+
+ vector<tuple<Instance*, Port*, Net*>> todo_connect;
+
+ FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst)
+ run(inst->View());
+
+ FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst)
+ FOREACH_PORTREF_OF_INST(inst, mi2, pr)
+ {
+ Port *port = pr->GetPort();
+ Net *net = pr->GetNet();
+
+ if (!net->IsExternalTo(nl))
+ continue;
+
+ if (verific_verbose)
+ log("Fixing external net reference on port %s.%s.%s:\n", get_full_netlist_name(nl).c_str(), inst->Name(), port->Name());
+
+ while (net->IsExternalTo(nl))
+ {
+ Net *newnet = get_net_level_up(net);
+ if (newnet == net) break;
+
+ if (verific_verbose)
+ log(" external net: %s.%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name());
+ net = newnet;
+ }
+
+ if (verific_verbose)
+ log(" final net: %s.%s%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name(), net->IsExternalTo(nl) ? " (external)" : "");
+ todo_connect.push_back(tuple<Instance*, Port*, Net*>(inst, port, net));
+ }
+
+ for (auto it : todo_connect) {
+ get<0>(it)->Disconnect(get<1>(it));
+ get<0>(it)->Connect(get<1>(it), get<2>(it));
+ }
+ }
+};
+
+void verific_import(Design *design, std::string top)
+{
+ std::set<Netlist*> nl_todo, nl_done;
+
+ {
+ VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1);
+ VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1);
+
+ Array veri_libs, vhdl_libs;
+ if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
+ if (veri_lib) veri_libs.InsertLast(veri_lib);
+
+ Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs);
+ Netlist *nl;
+ int i;
+
+ FOREACH_ARRAY_ITEM(netlists, i, nl) {
+ if (top.empty() || nl->Owner()->Name() == top)
+ nl_todo.insert(nl);
+ }
+
+ delete netlists;
+ }
+
+ if (!verific_error_msg.empty())
+ log_error("%s\n", verific_error_msg.c_str());
+
+ VerificExtNets worker;
+ for (auto nl : nl_todo)
+ worker.run(nl);
+
+ while (!nl_todo.empty()) {
+ Netlist *nl = *nl_todo.begin();
+ if (nl_done.count(nl) == 0) {
+ VerificImporter importer(false, false, false, false, false, false);
+ importer.import_netlist(design, nl, nl_todo);
+ }
+ nl_todo.erase(nl);
+ nl_done.insert(nl);
+ }
+
+ veri_file::Reset();
+ vhdl_file::Reset();
+ Libset::Reset();
+ verific_incdirs.clear();
+ verific_libdirs.clear();
+ verific_import_pending = false;
+
+ if (!verific_error_msg.empty())
+ log_error("%s\n", verific_error_msg.c_str());
+}
+
+YOSYS_NAMESPACE_END
#endif /* YOSYS_ENABLE_VERIFIC */
-YOSYS_NAMESPACE_BEGIN
+PRIVATE_NAMESPACE_BEGIN
+
+bool check_noverific_env()
+{
+ const char *e = getenv("YOSYS_NOVERIFIC");
+ if (e == nullptr)
+ return false;
+ if (atoi(e) == 0)
+ return false;
+ return true;
+}
struct VerificPass : public Pass {
VerificPass() : Pass("verific", "load Verilog and VHDL designs using Verific") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv} <verilog-file>..\n");
+ log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv} <verilog-file>..\n");
log("\n");
log("Load the specified Verilog/SystemVerilog files into Verific.\n");
log("\n");
+ log("All files specified in one call to this command are one compilation unit.\n");
+ log("Files passed to different calls to this command are treated as belonging to\n");
+ log("different compilation units.\n");
log("\n");
- log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008} <vhdl-file>..\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("\n");
+ log("\n");
+ log(" verific -formal <verilog-file>..\n");
+ log("\n");
+ log("Like -sv, but define FORMAL instead of SYNTHESIS.\n");
+ log("\n");
+ log("\n");
+ 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 -import [-gates] {-all | <top-module>..}\n");
+ log(" verific -work <libname> {-sv|-vhdl|...} <hdl-file>\n");
+ log("\n");
+ log("Load the specified Verilog/SystemVerilog/VHDL file into the specified library.\n");
+ log("(default library when -work is not present: \"work\")\n");
+ log("\n");
+ log("\n");
+ log(" verific -vlog-incdir <directory>..\n");
+ log("\n");
+ log("Add Verilog include directories.\n");
+ log("\n");
+ log("\n");
+ log(" verific -vlog-libdir <directory>..\n");
+ log("\n");
+ log("Add Verilog library directories. Verific will search in this directories to\n");
+ log("find undefined modules.\n");
+ log("\n");
+ log("\n");
+ log(" verific -vlog-define <macro>[=<value>]..\n");
+ log("\n");
+ log("Add Verilog defines.\n");
+ log("\n");
+ log("\n");
+ log(" verific -vlog-undef <macro>..\n");
+ log("\n");
+ log("Remove Verilog defines previously set with -vlog-define.\n");
+ log("\n");
+ log("\n");
+ log(" verific -set-error <msg_id>..\n");
+ log(" verific -set-warning <msg_id>..\n");
+ log(" verific -set-info <msg_id>..\n");
+ log(" verific -set-ignore <msg_id>..\n");
+ log("\n");
+ log("Set message severity. <msg_id> is the string in square brackets when a message\n");
+ log("is printed, such as VERI-1209.\n");
+ log("\n");
+ log("\n");
+ log(" verific -import [options] <top-module>..\n");
log("\n");
log("Elaborate the design for the specified top modules, import to Yosys and\n");
- log("reset the internal state of Verific. A gate-level netlist is created\n");
- log("when called with -gates.\n");
+ log("reset the internal state of Verific.\n");
+ log("\n");
+ log("Import options:\n");
+ log("\n");
+ log(" -all\n");
+ log(" Elaborate all modules, not just the hierarchy below the given top\n");
+ log(" modules. With this option the list of modules to import is optional.\n");
+ log("\n");
+ log(" -gates\n");
+ log(" Create a gate-level netlist.\n");
+ log("\n");
+ log(" -flatten\n");
+ log(" Flatten the design in Verific before importing.\n");
+ log("\n");
+ log(" -extnets\n");
+ log(" Resolve references to external nets by adding module ports as needed.\n");
+ log("\n");
+ log(" -autocover\n");
+ log(" Generate automatic cover statements for all asserts\n");
+ log("\n");
+ log(" -v, -vv\n");
+ log(" Verbose log messages. (-vv is even more verbose than -v.)\n");
+ log("\n");
+ log("The following additional import options are useful for debugging the Verific\n");
+ log("bindings (for Yosys and/or Verific developers):\n");
+ log("\n");
+ log(" -k\n");
+ log(" Keep going after an unsupported verific primitive is found. The\n");
+ log(" unsupported primitive is added as blockbox module to the design.\n");
+ log(" This will also add all SVA related cells to the design parallel to\n");
+ log(" the checker logic inferred by it.\n");
+ log("\n");
+ log(" -V\n");
+ log(" Import Verific netlist as-is without translating to Yosys cell types. \n");
+ log("\n");
+ log(" -nosva\n");
+ log(" Ignore SVA properties, do not infer checker logic.\n");
+ log("\n");
+ log(" -n\n");
+ log(" Keep all Verific names on instances and nets. By default only\n");
+ log(" user-declared names are preserved.\n");
+ log("\n");
+ log(" -d <dump_file>\n");
+ log(" Dump the Verific netlist as a verilog file.\n");
log("\n");
log("Visit http://verific.com/ for more information on Verific.\n");
log("\n");
}
#ifdef YOSYS_ENABLE_VERIFIC
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
- log_header(design, "Executing VERIFIC (loading Verilog and VHDL designs using Verific).\n");
+ static bool set_verific_global_flags = true;
- Message::SetConsoleOutput(0);
- Message::RegisterCallBackMsg(msg_func);
+ if (check_noverific_env())
+ log_cmd_error("This version of Yosys is built without Verific support.\n");
- if (args.size() > 1 && args[1] == "-vlog95") {
- for (size_t argidx = 2; argidx < args.size(); argidx++)
- if (!veri_file::Analyze(args[argidx].c_str(), veri_file::VERILOG_95))
- log_cmd_error("Reading `%s' in VERILOG_95 mode failed.\n", args[argidx].c_str());
- return;
+ log_header(design, "Executing VERIFIC (loading SystemVerilog and VHDL designs using Verific).\n");
+
+ if (set_verific_global_flags)
+ {
+ Message::SetConsoleOutput(0);
+ Message::RegisterCallBackMsg(msg_func);
+ RuntimeFlags::SetVar("db_preserve_user_nets", 1);
+ RuntimeFlags::SetVar("db_allow_external_nets", 1);
+ RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0);
+ RuntimeFlags::SetVar("veri_extract_dualport_rams", 0);
+ RuntimeFlags::SetVar("veri_extract_multiport_rams", 1);
+ RuntimeFlags::SetVar("db_infer_wide_operators", 1);
+
+ // Workaround for VIPER #13851
+ RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1);
+
+ // WARNING: instantiating unknown module 'XYZ' (VERI-1063)
+ Message::SetMessageType("VERI-1063", VERIFIC_ERROR);
+
+ set_verific_global_flags = false;
}
- if (args.size() > 1 && args[1] == "-vlog2k") {
- for (size_t argidx = 2; argidx < args.size(); argidx++)
- if (!veri_file::Analyze(args[argidx].c_str(), veri_file::VERILOG_2K))
- log_cmd_error("Reading `%s' in VERILOG_2K mode failed.\n", args[argidx].c_str());
- return;
+ verific_verbose = 0;
+
+ const char *release_str = Message::ReleaseString();
+ time_t release_time = Message::ReleaseDate();
+ char *release_tmstr = ctime(&release_time);
+
+ if (release_str == nullptr)
+ release_str = "(no release string)";
+
+ for (char *p = release_tmstr; *p; p++)
+ if (*p == '\n') *p = 0;
+
+ log("Built with Verific %s, released at %s.\n", release_str, release_tmstr);
+
+ int argidx = 1;
+ std::string work = "work";
+
+ if (GetSize(args) > argidx && (args[argidx] == "-set-error" || args[argidx] == "-set-warning" ||
+ args[argidx] == "-set-info" || args[argidx] == "-set-ignore"))
+ {
+ msg_type_t new_type;
+
+ if (args[argidx] == "-set-error")
+ new_type = VERIFIC_ERROR;
+ else if (args[argidx] == "-set-warning")
+ new_type = VERIFIC_WARNING;
+ else if (args[argidx] == "-set-info")
+ new_type = VERIFIC_INFO;
+ else if (args[argidx] == "-set-ignore")
+ new_type = VERIFIC_IGNORE;
+ else
+ log_abort();
+
+ for (argidx++; argidx < GetSize(args); argidx++)
+ Message::SetMessageType(args[argidx].c_str(), new_type);
+
+ goto check_error;
}
- if (args.size() > 1 && args[1] == "-sv2005") {
- for (size_t argidx = 2; argidx < args.size(); argidx++)
- if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG_2005))
- log_cmd_error("Reading `%s' in SYSTEM_VERILOG_2005 mode failed.\n", args[argidx].c_str());
- return;
+ if (GetSize(args) > argidx && args[argidx] == "-vlog-incdir") {
+ for (argidx++; argidx < GetSize(args); argidx++)
+ verific_incdirs.push_back(args[argidx]);
+ goto check_error;
}
- if (args.size() > 1 && args[1] == "-sv2009") {
- for (size_t argidx = 2; argidx < args.size(); argidx++)
- if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG_2009))
- log_cmd_error("Reading `%s' in SYSTEM_VERILOG_2009 mode failed.\n", args[argidx].c_str());
- return;
+ if (GetSize(args) > argidx && args[argidx] == "-vlog-libdir") {
+ for (argidx++; argidx < GetSize(args); argidx++)
+ verific_libdirs.push_back(args[argidx]);
+ goto check_error;
}
- if (args.size() > 1 && args[1] == "-sv") {
- for (size_t argidx = 2; argidx < args.size(); argidx++)
- if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG))
- log_cmd_error("Reading `%s' in SYSTEM_VERILOG mode failed.\n", args[argidx].c_str());
- return;
+ if (GetSize(args) > argidx && args[argidx] == "-vlog-define") {
+ for (argidx++; argidx < GetSize(args); argidx++) {
+ string name = args[argidx];
+ size_t equal = name.find('=');
+ if (equal != std::string::npos) {
+ string value = name.substr(equal+1);
+ name = name.substr(0, equal);
+ veri_file::DefineCmdLineMacro(name.c_str(), value.c_str());
+ } else {
+ veri_file::DefineCmdLineMacro(name.c_str());
+ }
+ }
+ goto check_error;
}
- if (args.size() > 1 && args[1] == "-vhdl87") {
+ if (GetSize(args) > argidx && args[argidx] == "-vlog-undef") {
+ for (argidx++; argidx < GetSize(args); argidx++) {
+ string name = args[argidx];
+ veri_file::UndefineMacro(name.c_str());
+ }
+ goto check_error;
+ }
+
+ for (; argidx < GetSize(args); argidx++)
+ {
+ if (args[argidx] == "-work" && argidx+1 < GetSize(args)) {
+ work = args[++argidx];
+ continue;
+ }
+ break;
+ }
+
+ if (GetSize(args) > argidx && (args[argidx] == "-vlog95" || args[argidx] == "-vlog2k" || args[argidx] == "-sv2005" ||
+ args[argidx] == "-sv2009" || args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal"))
+ {
+ Array file_names;
+ unsigned verilog_mode;
+
+ if (args[argidx] == "-vlog95")
+ verilog_mode = veri_file::VERILOG_95;
+ else if (args[argidx] == "-vlog2k")
+ verilog_mode = veri_file::VERILOG_2K;
+ else if (args[argidx] == "-sv2005")
+ verilog_mode = veri_file::SYSTEM_VERILOG_2005;
+ else if (args[argidx] == "-sv2009")
+ verilog_mode = veri_file::SYSTEM_VERILOG_2009;
+ else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal")
+ verilog_mode = veri_file::SYSTEM_VERILOG;
+ else
+ log_abort();
+
+ veri_file::DefineMacro("VERIFIC");
+ veri_file::DefineMacro(args[argidx] == "-formal" ? "FORMAL" : "SYNTHESIS");
+
+ for (argidx++; argidx < GetSize(args) && GetSize(args[argidx]) >= 2 && args[argidx].substr(0, 2) == "-D"; argidx++) {
+ std::string name = args[argidx].substr(2);
+ if (args[argidx] == "-D") {
+ if (++argidx >= GetSize(args))
+ break;
+ name = args[argidx];
+ }
+ size_t equal = name.find('=');
+ if (equal != std::string::npos) {
+ string value = name.substr(equal+1);
+ name = name.substr(0, equal);
+ veri_file::DefineMacro(name.c_str(), value.c_str());
+ } else {
+ veri_file::DefineMacro(name.c_str());
+ }
+ }
+
+ for (auto &dir : verific_incdirs)
+ veri_file::AddIncludeDir(dir.c_str());
+ for (auto &dir : verific_libdirs)
+ veri_file::AddYDir(dir.c_str());
+
+ while (argidx < GetSize(args))
+ file_names.Insert(args[argidx++].c_str());
+
+ if (!veri_file::AnalyzeMultipleFiles(&file_names, verilog_mode, work.c_str(), veri_file::MFCU))
+ log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n");
+
+ verific_import_pending = true;
+ goto check_error;
+ }
+
+ if (GetSize(args) > argidx && args[argidx] == "-vhdl87") {
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str());
- for (size_t argidx = 2; argidx < args.size(); argidx++)
- if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_87))
+ for (argidx++; argidx < GetSize(args); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_87))
log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", args[argidx].c_str());
- return;
+ verific_import_pending = true;
+ goto check_error;
}
- if (args.size() > 1 && args[1] == "-vhdl93") {
+ if (GetSize(args) > argidx && args[argidx] == "-vhdl93") {
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
- for (size_t argidx = 2; argidx < args.size(); argidx++)
- if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_93))
+ for (argidx++; argidx < GetSize(args); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_93))
log_cmd_error("Reading `%s' in VHDL_93 mode failed.\n", args[argidx].c_str());
- return;
+ verific_import_pending = true;
+ goto check_error;
}
- if (args.size() > 1 && args[1] == "-vhdl2k") {
+ if (GetSize(args) > argidx && args[argidx] == "-vhdl2k") {
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
- for (size_t argidx = 2; argidx < args.size(); argidx++)
- if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_2K))
+ for (argidx++; argidx < GetSize(args); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_2K))
log_cmd_error("Reading `%s' in VHDL_2K mode failed.\n", args[argidx].c_str());
- return;
+ verific_import_pending = true;
+ goto check_error;
}
- if (args.size() > 1 && args[1] == "-vhdl2008") {
+ if (GetSize(args) > argidx && (args[argidx] == "-vhdl2008" || args[argidx] == "-vhdl")) {
vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2008").c_str());
- for (size_t argidx = 2; argidx < args.size(); argidx++)
- if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_2008))
+ for (argidx++; argidx < GetSize(args); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_2008))
log_cmd_error("Reading `%s' in VHDL_2008 mode failed.\n", args[argidx].c_str());
- return;
+ verific_import_pending = true;
+ goto check_error;
}
- if (args.size() > 1 && args[1] == "-import")
+ if (GetSize(args) > argidx && args[argidx] == "-import")
{
std::set<Netlist*> nl_todo, nl_done;
- bool mode_all = false, mode_gates = false;
+ bool mode_all = false, mode_gates = false, mode_keep = false;
+ bool mode_nosva = false, mode_names = false, mode_verific = false;
+ bool mode_autocover = false;
+ bool flatten = false, extnets = false;
+ string dumpfile;
- size_t argidx = 2;
- for (; argidx < args.size(); argidx++) {
+ for (argidx++; argidx < GetSize(args); argidx++) {
if (args[argidx] == "-all") {
mode_all = true;
continue;
@@ -937,69 +2020,342 @@ struct VerificPass : public Pass {
mode_gates = true;
continue;
}
+ if (args[argidx] == "-flatten") {
+ flatten = true;
+ continue;
+ }
+ if (args[argidx] == "-extnets") {
+ extnets = true;
+ continue;
+ }
+ if (args[argidx] == "-k") {
+ mode_keep = true;
+ continue;
+ }
+ if (args[argidx] == "-nosva") {
+ mode_nosva = true;
+ continue;
+ }
+ if (args[argidx] == "-n") {
+ mode_names = true;
+ continue;
+ }
+ if (args[argidx] == "-autocover") {
+ mode_autocover = true;
+ continue;
+ }
+ if (args[argidx] == "-V") {
+ mode_verific = true;
+ continue;
+ }
+ if (args[argidx] == "-v") {
+ verific_verbose = 1;
+ continue;
+ }
+ if (args[argidx] == "-vv") {
+ verific_verbose = 2;
+ continue;
+ }
+ if (args[argidx] == "-d" && argidx+1 < GetSize(args)) {
+ dumpfile = args[++argidx];
+ continue;
+ }
break;
}
- if (argidx > args.size() && args[argidx].substr(0, 1) == "-")
+ if (argidx > GetSize(args) && args[argidx].substr(0, 1) == "-")
cmd_error(args, argidx, "unknown option");
if (mode_all)
{
- if (argidx != args.size())
- log_cmd_error("Got -all and an explicit list of top modules.\n");
-
- MapIter m1, m2, m3;
- VeriModule *mod;
- FOREACH_VERILOG_MODULE(m1, mod)
- args.push_back(mod->Name());
-
- VhdlLibrary *lib;
- VhdlPrimaryUnit *primunit;
- FOREACH_VHDL_LIBRARY(m1, lib)
- FOREACH_VHDL_PRIMARY_UNIT(lib, m2, primunit) {
- if (primunit->IsPackageDecl())
- continue;
- args.push_back(primunit->Name());
+#if 0
+ log("Running veri_file::ElaborateAll().\n");
+ if (!veri_file::ElaborateAll())
+ log_cmd_error("Elaboration of Verilog modules failed.\n");
+
+ log("Running vhdl_file::ElaborateAll().\n");
+ if (!vhdl_file::ElaborateAll())
+ log_cmd_error("Elaboration of VHDL modules failed.\n");
+
+ Library *lib = Netlist::PresentDesign()->Owner()->Owner();
+
+ if (argidx == GetSize(args))
+ {
+ MapIter iter;
+ char *iter_name;
+ Verific::Cell *iter_cell;
+
+ FOREACH_MAP_ITEM(lib->GetCells(), iter, &iter_name, &iter_cell) {
+ if (*iter_name != '$')
+ nl_todo.insert(iter_cell->GetFirstNetlist());
+ }
+ }
+ else
+ {
+ for (; argidx < GetSize(args); argidx++)
+ {
+ Verific::Cell *cell = lib->GetCell(args[argidx].c_str());
+
+ if (cell == nullptr)
+ log_cmd_error("Module not found: %s\n", args[argidx].c_str());
+
+ nl_todo.insert(cell->GetFirstNetlist());
+ cell->GetFirstNetlist()->SetPresentDesign();
+ }
}
+#else
+ 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;
+ if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
+ if (veri_lib) veri_libs.InsertLast(veri_lib);
+
+ Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs);
+ Netlist *nl;
+ int i;
+
+ FOREACH_ARRAY_ITEM(netlists, i, nl)
+ nl_todo.insert(nl);
+ delete netlists;
+#endif
}
else
- if (argidx == args.size())
+ {
+ if (argidx == GetSize(args))
log_cmd_error("No top module specified.\n");
- for (; argidx < args.size(); argidx++) {
- if (veri_file::GetModule(args[argidx].c_str())) {
- log("Running veri_file::Elaborate(\"%s\").\n", args[argidx].c_str());
- if (!veri_file::Elaborate(args[argidx].c_str()))
- log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str());
- nl_todo.insert(Netlist::PresentDesign());
- } else {
- log("Running vhdl_file::Elaborate(\"%s\").\n", args[argidx].c_str());
- if (!vhdl_file::Elaborate(args[argidx].c_str()))
- log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str());
- nl_todo.insert(Netlist::PresentDesign());
+#if 0
+ for (; argidx < GetSize(args); argidx++) {
+ if (veri_file::GetModule(args[argidx].c_str())) {
+ log("Running veri_file::Elaborate(\"%s\").\n", args[argidx].c_str());
+ if (!veri_file::Elaborate(args[argidx].c_str()))
+ log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str());
+ nl_todo.insert(Netlist::PresentDesign());
+ } else {
+ log("Running vhdl_file::Elaborate(\"%s\").\n", args[argidx].c_str());
+ if (!vhdl_file::Elaborate(args[argidx].c_str()))
+ log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str());
+ nl_todo.insert(Netlist::PresentDesign());
+ }
+ }
+#else
+ Array veri_modules, vhdl_units;
+ for (; argidx < GetSize(args); argidx++)
+ {
+ const char *name = args[argidx].c_str();
+
+ VeriModule *veri_module = veri_file::GetModule(name);
+ if (veri_module) {
+ log("Adding Verilog module '%s' to elaboration queue.\n", name);
+ veri_modules.InsertLast(veri_module);
+ continue;
+ }
+
+ VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
+ VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(name);
+ if (vhdl_unit) {
+ log("Adding VHDL unit '%s' to elaboration queue.\n", name);
+ vhdl_units.InsertLast(vhdl_unit);
+ continue;
+ }
+
+ log_error("Can't find module/unit '%s'.\n", name);
}
+
+ log("Running hier_tree::Elaborate().\n");
+ Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units);
+ Netlist *nl;
+ int i;
+
+ FOREACH_ARRAY_ITEM(netlists, i, nl)
+ nl_todo.insert(nl);
+ delete netlists;
+#endif
+ }
+
+ if (!verific_error_msg.empty())
+ goto check_error;
+
+ if (flatten) {
+ for (auto nl : nl_todo)
+ nl->Flatten();
+ }
+
+ if (extnets) {
+ VerificExtNets worker;
+ for (auto nl : nl_todo)
+ worker.run(nl);
+ }
+
+ if (!dumpfile.empty()) {
+ VeriWrite veri_writer;
+ veri_writer.WriteFile(dumpfile.c_str(), Netlist::PresentDesign());
}
while (!nl_todo.empty()) {
Netlist *nl = *nl_todo.begin();
- if (nl_done.count(nl) == 0)
- import_netlist(design, nl, nl_todo, mode_gates);
+ if (nl_done.count(nl) == 0) {
+ VerificImporter importer(mode_gates, mode_keep, mode_nosva,
+ mode_names, mode_verific, mode_autocover);
+ importer.import_netlist(design, nl, nl_todo);
+ }
nl_todo.erase(nl);
nl_done.insert(nl);
}
+ veri_file::Reset();
+ vhdl_file::Reset();
Libset::Reset();
- return;
+ verific_incdirs.clear();
+ verific_libdirs.clear();
+ verific_import_pending = false;
+ goto check_error;
}
log_cmd_error("Missing or unsupported mode parameter.\n");
+
+ check_error:
+ if (!verific_error_msg.empty())
+ log_error("%s\n", verific_error_msg.c_str());
+
}
#else /* YOSYS_ENABLE_VERIFIC */
- virtual void execute(std::vector<std::string>, RTLIL::Design *) {
+ void execute(std::vector<std::string>, RTLIL::Design *) YS_OVERRIDE {
log_cmd_error("This version of Yosys is built without Verific support.\n");
}
#endif
} VerificPass;
-YOSYS_NAMESPACE_END
+struct ReadPass : public Pass {
+ ReadPass() : Pass("read", "load HDL designs") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" read {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv|-formal} <verilog-file>..\n");
+ log("\n");
+ log("Load the specified Verilog/SystemVerilog files. (Full SystemVerilog support\n");
+ log("is only available via Verific.)\n");
+ 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("\n");
+ log("\n");
+ 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");
+ log(" read -define <macro>[=<value>]..\n");
+ log("\n");
+ log("Set global Verilog/SystemVerilog defines.\n");
+ log("\n");
+ log("\n");
+ log(" read -undef <macro>..\n");
+ log("\n");
+ log("Unset global Verilog/SystemVerilog defines.\n");
+ log("\n");
+ log("\n");
+ log(" read -incdir <directory>\n");
+ log("\n");
+ log("Add directory to global Verilog/SystemVerilog include directories.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ if (args.size() < 2)
+ log_cmd_error("Missing mode parameter.\n");
+
+ if (args.size() < 3)
+ log_cmd_error("Missing file name parameter.\n");
+
+#ifdef YOSYS_ENABLE_VERIFIC
+ bool use_verific = !check_noverific_env();
+#else
+ bool use_verific = false;
+#endif
+
+ if (args[1] == "-vlog95" || args[1] == "-vlog2k") {
+ if (use_verific) {
+ args[0] = "verific";
+ } else {
+ args[0] = "read_verilog";
+ args.erase(args.begin()+1, args.begin()+2);
+ }
+ Pass::call(design, args);
+ return;
+ }
+
+ if (args[1] == "-sv2005" || args[1] == "-sv2009" || args[1] == "-sv2012" || args[1] == "-sv" || args[1] == "-formal") {
+ if (use_verific) {
+ args[0] = "verific";
+ } else {
+ args[0] = "read_verilog";
+ if (args[1] == "-formal")
+ args.insert(args.begin()+1, std::string());
+ args[1] = "-sv";
+ }
+ Pass::call(design, args);
+ return;
+ }
+
+ if (args[1] == "-vhdl87" || args[1] == "-vhdl93" || args[1] == "-vhdl2k" || args[1] == "-vhdl2008" || args[1] == "-vhdl") {
+ if (use_verific) {
+ args[0] = "verific";
+ Pass::call(design, args);
+ } else {
+ log_cmd_error("This version of Yosys is built without Verific support.\n");
+ }
+ return;
+ }
+
+ if (args[1] == "-define") {
+ if (use_verific) {
+ args[0] = "verific";
+ args[1] = "-vlog-define";
+ Pass::call(design, args);
+ }
+ args[0] = "verilog_defines";
+ args.erase(args.begin()+1, args.begin()+2);
+ for (int i = 1; i < GetSize(args); i++)
+ args[i] = "-D" + args[i];
+ Pass::call(design, args);
+ return;
+ }
+
+ if (args[1] == "-undef") {
+ if (use_verific) {
+ args[0] = "verific";
+ args[1] = "-vlog-undef";
+ Pass::call(design, args);
+ }
+ args[0] = "verilog_defines";
+ args.erase(args.begin()+1, args.begin()+2);
+ for (int i = 1; i < GetSize(args); i++)
+ args[i] = "-U" + args[i];
+ Pass::call(design, args);
+ return;
+ }
+
+ if (args[1] == "-incdir") {
+ if (use_verific) {
+ args[0] = "verific";
+ args[1] = "-vlog-incdir";
+ Pass::call(design, args);
+ }
+ args[0] = "verilog_defaults";
+ args[1] = "-add";
+ for (int i = 2; i < GetSize(args); i++)
+ args[i] = "-I" + args[i];
+ Pass::call(design, args);
+ return;
+ }
+
+ log_cmd_error("Missing or unsupported mode parameter.\n");
+ }
+} ReadPass;
+PRIVATE_NAMESPACE_END
diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h
new file mode 100644
index 00000000..cbd9314d
--- /dev/null
+++ b/frontends/verific/verific.h
@@ -0,0 +1,106 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifdef YOSYS_ENABLE_VERIFIC
+
+#include "DataBase.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+extern int verific_verbose;
+
+extern bool verific_import_pending;
+extern void verific_import(Design *design, std::string top = std::string());
+
+extern pool<int> verific_sva_prims;
+
+struct VerificImporter;
+
+struct VerificClocking {
+ RTLIL::Module *module = nullptr;
+ Verific::Net *clock_net = nullptr;
+ Verific::Net *enable_net = nullptr;
+ Verific::Net *disable_net = nullptr;
+ Verific::Net *body_net = nullptr;
+ Verific::Net *cond_net = nullptr;
+ SigBit clock_sig = State::Sx;
+ SigBit enable_sig = State::S1;
+ SigBit disable_sig = State::S0;
+ bool posedge = true;
+ bool gclk = false;
+
+ 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);
+
+ bool property_matches_sequence(const VerificClocking &seq) const {
+ if (clock_net != seq.clock_net)
+ return false;
+ if (enable_net != seq.enable_net)
+ return false;
+ if (posedge != seq.posedge)
+ return false;
+ return true;
+ }
+};
+
+struct VerificImporter
+{
+ RTLIL::Module *module;
+ Verific::Netlist *netlist;
+
+ std::map<Verific::Net*, RTLIL::SigBit> net_map;
+ std::map<Verific::Net*, Verific::Net*> sva_posedge_map;
+ pool<Verific::Net*, hash_ptr_ops> any_all_nets;
+
+ bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific;
+ bool mode_autocover;
+
+ VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_names, bool mode_verific, bool mode_autocover);
+
+ RTLIL::SigBit net_map_at(Verific::Net *net);
+
+ void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj);
+
+ RTLIL::SigSpec operatorInput(Verific::Instance *inst);
+ RTLIL::SigSpec operatorInput1(Verific::Instance *inst);
+ RTLIL::SigSpec operatorInput2(Verific::Instance *inst);
+ RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname);
+ RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool<Verific::Net*, hash_ptr_ops> *any_all_nets = nullptr);
+
+ bool import_netlist_instance_gates(Verific::Instance *inst, RTLIL::IdString inst_name);
+ bool import_netlist_instance_cells(Verific::Instance *inst, RTLIL::IdString inst_name);
+
+ 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);
+};
+
+void verific_import_sva_assert(VerificImporter *importer, Verific::Instance *inst);
+void verific_import_sva_assume(VerificImporter *importer, Verific::Instance *inst);
+void verific_import_sva_cover(VerificImporter *importer, Verific::Instance *inst);
+void verific_import_sva_trigger(VerificImporter *importer, Verific::Instance *inst);
+bool verific_is_sva_net(VerificImporter *importer, Verific::Net *net);
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc
new file mode 100644
index 00000000..85b84218
--- /dev/null
+++ b/frontends/verific/verificsva.cc
@@ -0,0 +1,1814 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+
+// Currently supported SVA sequence and property syntax:
+// http://symbiyosys.readthedocs.io/en/latest/verific.html
+//
+// Next gen property syntax:
+// basic_property
+// [antecedent_condition] property
+// [antecedent_condition] always.. property
+// [antecedent_condition] eventually.. basic_property
+// [antecedent_condition] property until.. expression
+// [antecedent_condition] basic_property until.. basic_property (assert/assume only)
+//
+// antecedent_condition:
+// sequence |->
+// sequence |=>
+//
+// basic_property:
+// sequence
+// not basic_property
+// sequence #-# basic_property
+// sequence #=# basic_property
+// basic_property or basic_property (cover only)
+// basic_property and basic_property (assert/assume only)
+// basic_property implies basic_property
+// basic_property iff basic_property
+//
+// sequence:
+// expression
+// sequence ##N sequence
+// sequence ##[*] sequence
+// sequence ##[+] sequence
+// sequence ##[N:M] sequence
+// sequence ##[N:$] sequence
+// expression [*]
+// expression [+]
+// expression [*N]
+// expression [*N:M]
+// expression [*N:$]
+// sequence or sequence
+// sequence and sequence
+// expression throughout sequence
+// sequence intersect sequence
+// sequence within sequence
+// first_match( sequence )
+// expression [=N]
+// expression [=N:M]
+// expression [=N:$]
+// expression [->N]
+// expression [->N:M]
+// expression [->N:$]
+
+
+#include "kernel/yosys.h"
+#include "frontends/verific/verific.h"
+
+USING_YOSYS_NAMESPACE
+
+#ifdef VERIFIC_NAMESPACE
+using namespace Verific;
+#endif
+
+PRIVATE_NAMESPACE_BEGIN
+
+// Non-deterministic FSM
+struct SvaNFsmNode
+{
+ // Edge: Activate the target node if ctrl signal is true, consumes clock cycle
+ // Link: Activate the target node if ctrl signal is true, doesn't consume clock cycle
+ vector<pair<int, SigBit>> edges, links;
+ bool is_cond_node;
+};
+
+// Non-deterministic FSM after resolving links
+struct SvaUFsmNode
+{
+ // Edge: Activate the target node if all bits in ctrl signal are true, consumes clock cycle
+ // Accept: This node functions as an accept node if all bits in ctrl signal are true
+ vector<pair<int, SigSpec>> edges;
+ vector<SigSpec> accept, cond;
+ bool reachable;
+};
+
+// Deterministic FSM
+struct SvaDFsmNode
+{
+ // A DFSM state corresponds to a set of NFSM states. We represent DFSM states as sorted vectors
+ // of NFSM state node ids. Edge/accept controls are constants matched against the ctrl sigspec.
+ SigSpec ctrl;
+ vector<pair<vector<int>, Const>> edges;
+ vector<Const> accept, reject;
+
+ // additional temp data for getReject()
+ Wire *ffoutwire;
+ SigBit statesig;
+ SigSpec nextstate;
+
+ // additional temp data for getDFsm()
+ int outnode;
+};
+
+struct SvaFsm
+{
+ Module *module;
+ VerificClocking clocking;
+
+ SigBit trigger_sig = State::S1, disable_sig;
+ SigBit throughout_sig = State::S1;
+ bool in_cond_mode = false;
+
+ vector<SigBit> disable_stack;
+ vector<SigBit> throughout_stack;
+
+ int startNode, acceptNode, condNode;
+ vector<SvaNFsmNode> nodes;
+
+ vector<SvaUFsmNode> unodes;
+ dict<vector<int>, SvaDFsmNode> dnodes;
+ dict<pair<SigSpec, SigSpec>, SigBit> cond_eq_cache;
+ bool materialized = false;
+
+ SigBit final_accept_sig = State::Sx;
+ SigBit final_reject_sig = State::Sx;
+
+ SvaFsm(const VerificClocking &clking, SigBit trig = State::S1)
+ {
+ module = clking.module;
+ clocking = clking;
+ trigger_sig = trig;
+
+ startNode = createNode();
+ acceptNode = createNode();
+
+ in_cond_mode = true;
+ condNode = createNode();
+ in_cond_mode = false;
+ }
+
+ void pushDisable(SigBit sig)
+ {
+ log_assert(!materialized);
+
+ disable_stack.push_back(disable_sig);
+
+ if (disable_sig == State::S0)
+ disable_sig = sig;
+ else
+ disable_sig = module->Or(NEW_ID, disable_sig, sig);
+ }
+
+ void popDisable()
+ {
+ log_assert(!materialized);
+ log_assert(!disable_stack.empty());
+
+ disable_sig = disable_stack.back();
+ disable_stack.pop_back();
+ }
+
+ void pushThroughout(SigBit sig)
+ {
+ log_assert(!materialized);
+
+ throughout_stack.push_back(throughout_sig);
+
+ if (throughout_sig == State::S1)
+ throughout_sig = sig;
+ else
+ throughout_sig = module->And(NEW_ID, throughout_sig, sig);
+ }
+
+ void popThroughout()
+ {
+ log_assert(!materialized);
+ log_assert(!throughout_stack.empty());
+
+ throughout_sig = throughout_stack.back();
+ throughout_stack.pop_back();
+ }
+
+ int createNode(int link_node = -1)
+ {
+ log_assert(!materialized);
+
+ int idx = GetSize(nodes);
+ nodes.push_back(SvaNFsmNode());
+ nodes.back().is_cond_node = in_cond_mode;
+ if (link_node >= 0)
+ createLink(link_node, idx);
+ return idx;
+ }
+
+ int createStartNode()
+ {
+ return createNode(startNode);
+ }
+
+ void createEdge(int from_node, int to_node, SigBit ctrl = State::S1)
+ {
+ log_assert(!materialized);
+ log_assert(0 <= from_node && from_node < GetSize(nodes));
+ log_assert(0 <= to_node && to_node < GetSize(nodes));
+ log_assert(from_node != acceptNode);
+ log_assert(to_node != acceptNode);
+ log_assert(from_node != condNode);
+ log_assert(to_node != condNode);
+ log_assert(to_node != startNode);
+
+ if (from_node != startNode)
+ log_assert(nodes.at(from_node).is_cond_node == nodes.at(to_node).is_cond_node);
+
+ if (throughout_sig != State::S1) {
+ if (ctrl != State::S1)
+ ctrl = module->And(NEW_ID, throughout_sig, ctrl);
+ else
+ ctrl = throughout_sig;
+ }
+
+ nodes[from_node].edges.push_back(make_pair(to_node, ctrl));
+ }
+
+ void createLink(int from_node, int to_node, SigBit ctrl = State::S1)
+ {
+ log_assert(!materialized);
+ log_assert(0 <= from_node && from_node < GetSize(nodes));
+ log_assert(0 <= to_node && to_node < GetSize(nodes));
+ log_assert(from_node != acceptNode);
+ log_assert(from_node != condNode);
+ log_assert(to_node != startNode);
+
+ if (from_node != startNode)
+ log_assert(nodes.at(from_node).is_cond_node == nodes.at(to_node).is_cond_node);
+
+ if (throughout_sig != State::S1) {
+ if (ctrl != State::S1)
+ ctrl = module->And(NEW_ID, throughout_sig, ctrl);
+ else
+ ctrl = throughout_sig;
+ }
+
+ nodes[from_node].links.push_back(make_pair(to_node, ctrl));
+ }
+
+ void make_link_order(vector<int> &order, int node, int min)
+ {
+ order[node] = std::max(order[node], min);
+ for (auto &it : nodes[node].links)
+ make_link_order(order, it.first, order[node]+1);
+ }
+
+ // ----------------------------------------------------
+ // Generating NFSM circuit to acquire accept signal
+
+ SigBit getAccept()
+ {
+ log_assert(!materialized);
+ materialized = true;
+
+ vector<Wire*> state_wire(GetSize(nodes));
+ vector<SigBit> state_sig(GetSize(nodes));
+ vector<SigBit> next_state_sig(GetSize(nodes));
+
+ // Create state signals
+
+ {
+ SigBit not_disable = State::S1;
+
+ if (disable_sig != State::S0)
+ not_disable = module->Not(NEW_ID, disable_sig);
+
+ for (int i = 0; i < GetSize(nodes); i++)
+ {
+ Wire *w = module->addWire(NEW_ID);
+ state_wire[i] = w;
+ state_sig[i] = w;
+
+ if (i == startNode)
+ state_sig[i] = module->Or(NEW_ID, state_sig[i], trigger_sig);
+
+ if (disable_sig != State::S0)
+ state_sig[i] = module->And(NEW_ID, state_sig[i], not_disable);
+ }
+ }
+
+ // Follow Links
+
+ {
+ vector<int> node_order(GetSize(nodes));
+ vector<vector<int>> order_to_nodes;
+
+ for (int i = 0; i < GetSize(nodes); i++)
+ make_link_order(node_order, i, 0);
+
+ for (int i = 0; i < GetSize(nodes); i++) {
+ if (node_order[i] >= GetSize(order_to_nodes))
+ order_to_nodes.resize(node_order[i]+1);
+ order_to_nodes[node_order[i]].push_back(i);
+ }
+
+ for (int order = 0; order < GetSize(order_to_nodes); order++)
+ for (int node : order_to_nodes[order])
+ {
+ for (auto &it : nodes[node].links)
+ {
+ int target = it.first;
+ SigBit ctrl = state_sig[node];
+
+ if (it.second != State::S1)
+ ctrl = module->And(NEW_ID, ctrl, it.second);
+
+ state_sig[target] = module->Or(NEW_ID, state_sig[target], ctrl);
+ }
+ }
+ }
+
+ // Construct activations
+
+ {
+ vector<SigSpec> activate_sig(GetSize(nodes));
+ vector<SigBit> activate_bit(GetSize(nodes));
+
+ for (int i = 0; i < GetSize(nodes); i++) {
+ for (auto &it : nodes[i].edges)
+ activate_sig[it.first].append(module->And(NEW_ID, state_sig[i], it.second));
+ }
+
+ for (int i = 0; i < GetSize(nodes); i++) {
+ if (GetSize(activate_sig[i]) == 0)
+ next_state_sig[i] = State::S0;
+ else if (GetSize(activate_sig[i]) == 1)
+ next_state_sig[i] = activate_sig[i];
+ else
+ next_state_sig[i] = module->ReduceOr(NEW_ID, activate_sig[i]);
+ }
+ }
+
+ // Create state FFs
+
+ for (int i = 0; i < GetSize(nodes); i++)
+ {
+ if (next_state_sig[i] != State::S0) {
+ clocking.addDff(NEW_ID, next_state_sig[i], state_wire[i], Const(0, 1));
+ } else {
+ module->connect(state_wire[i], State::S0);
+ }
+ }
+
+ final_accept_sig = state_sig[acceptNode];
+ return final_accept_sig;
+ }
+
+ // ----------------------------------------------------
+ // Generating quantifier-based NFSM circuit to acquire reject signal
+
+ SigBit getAnyAllRejectWorker(bool /* allMode */)
+ {
+ // FIXME
+ log_abort();
+ }
+
+ SigBit getAnyReject()
+ {
+ return getAnyAllRejectWorker(false);
+ }
+
+ SigBit getAllReject()
+ {
+ return getAnyAllRejectWorker(true);
+ }
+
+ // ----------------------------------------------------
+ // Generating DFSM circuit to acquire reject signal
+
+ void node_to_unode(int node, int unode, SigSpec ctrl)
+ {
+ if (node == acceptNode)
+ unodes[unode].accept.push_back(ctrl);
+
+ if (node == condNode)
+ unodes[unode].cond.push_back(ctrl);
+
+ for (auto &it : nodes[node].edges) {
+ if (it.second != State::S1) {
+ SigSpec s = {ctrl, it.second};
+ s.sort_and_unify();
+ unodes[unode].edges.push_back(make_pair(it.first, s));
+ } else {
+ unodes[unode].edges.push_back(make_pair(it.first, ctrl));
+ }
+ }
+
+ for (auto &it : nodes[node].links) {
+ if (it.second != State::S1) {
+ SigSpec s = {ctrl, it.second};
+ s.sort_and_unify();
+ node_to_unode(it.first, unode, s);
+ } else {
+ node_to_unode(it.first, unode, ctrl);
+ }
+ }
+ }
+
+ void mark_reachable_unode(int unode)
+ {
+ if (unodes[unode].reachable)
+ return;
+
+ unodes[unode].reachable = true;
+ for (auto &it : unodes[unode].edges)
+ mark_reachable_unode(it.first);
+ }
+
+ void usortint(vector<int> &vec)
+ {
+ vector<int> newvec;
+ std::sort(vec.begin(), vec.end());
+ for (int i = 0; i < GetSize(vec); i++)
+ if (i == GetSize(vec)-1 || vec[i] != vec[i+1])
+ newvec.push_back(vec[i]);
+ vec.swap(newvec);
+ }
+
+ bool cmp_ctrl(const pool<SigBit> &ctrl_bits, const SigSpec &ctrl)
+ {
+ for (int i = 0; i < GetSize(ctrl); i++)
+ if (ctrl_bits.count(ctrl[i]) == 0)
+ return false;
+ return true;
+ }
+
+ void create_dnode(const vector<int> &state, bool firstmatch, bool condaccept)
+ {
+ if (dnodes.count(state) != 0)
+ return;
+
+ SvaDFsmNode dnode;
+ dnodes[state] = SvaDFsmNode();
+
+ for (int unode : state) {
+ log_assert(unodes[unode].reachable);
+ for (auto &it : unodes[unode].edges)
+ dnode.ctrl.append(it.second);
+ for (auto &it : unodes[unode].accept)
+ dnode.ctrl.append(it);
+ for (auto &it : unodes[unode].cond)
+ dnode.ctrl.append(it);
+ }
+
+ dnode.ctrl.sort_and_unify();
+
+ if (GetSize(dnode.ctrl) > 16) {
+ if (verific_verbose >= 2) {
+ log(" detected state explosion in DFSM generation:\n");
+ dump();
+ log(" ctrl signal: %s\n", log_signal(dnode.ctrl));
+ }
+ log_error("SVA DFSM state ctrl signal has %d (>16) bits. Stopping to prevent exponential design size explosion.\n", GetSize(dnode.ctrl));
+ }
+
+ for (int i = 0; i < (1 << GetSize(dnode.ctrl)); i++)
+ {
+ Const ctrl_val(i, GetSize(dnode.ctrl));
+ pool<SigBit> ctrl_bits;
+
+ for (int i = 0; i < GetSize(dnode.ctrl); i++)
+ if (ctrl_val[i] == State::S1)
+ ctrl_bits.insert(dnode.ctrl[i]);
+
+ vector<int> new_state;
+ bool accept = false, cond = false;
+
+ for (int unode : state) {
+ for (auto &it : unodes[unode].accept)
+ if (cmp_ctrl(ctrl_bits, it))
+ accept = true;
+ for (auto &it : unodes[unode].cond)
+ if (cmp_ctrl(ctrl_bits, it))
+ cond = true;
+ }
+
+ bool new_state_cond = false;
+ bool new_state_noncond = false;
+
+ if (accept && condaccept)
+ accept = cond;
+
+ if (!accept || !firstmatch) {
+ for (int unode : state)
+ for (auto &it : unodes[unode].edges)
+ if (cmp_ctrl(ctrl_bits, it.second)) {
+ if (nodes.at(it.first).is_cond_node)
+ new_state_cond = true;
+ else
+ new_state_noncond = true;
+ new_state.push_back(it.first);
+ }
+ }
+
+ if (accept)
+ dnode.accept.push_back(ctrl_val);
+
+ if (condaccept && (!new_state_cond || !new_state_noncond))
+ new_state.clear();
+
+ if (new_state.empty()) {
+ if (!accept)
+ dnode.reject.push_back(ctrl_val);
+ } else {
+ usortint(new_state);
+ dnode.edges.push_back(make_pair(new_state, ctrl_val));
+ create_dnode(new_state, firstmatch, condaccept);
+ }
+ }
+
+ dnodes[state] = dnode;
+ }
+
+ void optimize_cond(vector<Const> &values)
+ {
+ bool did_something = true;
+
+ while (did_something)
+ {
+ did_something = false;
+
+ for (int i = 0; i < GetSize(values); i++)
+ for (int j = 0; j < GetSize(values); j++)
+ {
+ if (i == j)
+ continue;
+
+ log_assert(GetSize(values[i]) == GetSize(values[j]));
+
+ int delta_pos = -1;
+ bool i_within_j = true;
+ bool j_within_i = true;
+
+ for (int k = 0; k < GetSize(values[i]); k++) {
+ if (values[i][k] == State::Sa && values[j][k] != State::Sa) {
+ i_within_j = false;
+ continue;
+ }
+ if (values[i][k] != State::Sa && values[j][k] == State::Sa) {
+ j_within_i = false;
+ continue;
+ }
+ if (values[i][k] == values[j][k])
+ continue;
+ if (delta_pos >= 0)
+ goto next_pair;
+ delta_pos = k;
+ }
+
+ if (delta_pos >= 0 && i_within_j && j_within_i) {
+ did_something = true;
+ values[i][delta_pos] = State::Sa;
+ values[j] = values.back();
+ values.pop_back();
+ goto next_pair;
+ }
+
+ if (delta_pos < 0 && i_within_j) {
+ did_something = true;
+ values[i] = values.back();
+ values.pop_back();
+ goto next_pair;
+ }
+
+ if (delta_pos < 0 && j_within_i) {
+ did_something = true;
+ values[j] = values.back();
+ values.pop_back();
+ goto next_pair;
+ }
+ next_pair:;
+ }
+ }
+ }
+
+ SigBit make_cond_eq(const SigSpec &ctrl, const Const &value, SigBit enable = State::S1)
+ {
+ SigSpec sig_a, sig_b;
+
+ log_assert(GetSize(ctrl) == GetSize(value));
+
+ for (int i = 0; i < GetSize(ctrl); i++)
+ if (value[i] != State::Sa) {
+ sig_a.append(ctrl[i]);
+ sig_b.append(value[i]);
+ }
+
+ if (GetSize(sig_a) == 0)
+ return enable;
+
+ if (enable != State::S1) {
+ sig_a.append(enable);
+ sig_b.append(State::S1);
+ }
+
+ auto key = make_pair(sig_a, sig_b);
+
+ if (cond_eq_cache.count(key) == 0)
+ {
+ if (sig_b == State::S1)
+ cond_eq_cache[key] = sig_a;
+ else if (sig_b == State::S0)
+ cond_eq_cache[key] = module->Not(NEW_ID, sig_a);
+ else
+ cond_eq_cache[key] = module->Eq(NEW_ID, sig_a, sig_b);
+
+ if (verific_verbose >= 2) {
+ log(" Cond: %s := %s == %s\n", log_signal(cond_eq_cache[key]),
+ log_signal(sig_a), log_signal(sig_b));
+ }
+ }
+
+ return cond_eq_cache.at(key);
+ }
+
+ void getFirstAcceptReject(SigBit *accept_p, SigBit *reject_p)
+ {
+ log_assert(!materialized);
+ materialized = true;
+
+ // Create unlinked NFSM
+
+ unodes.resize(GetSize(nodes));
+
+ for (int node = 0; node < GetSize(nodes); node++)
+ node_to_unode(node, node, SigSpec());
+
+ mark_reachable_unode(startNode);
+
+ // Create DFSM
+
+ create_dnode(vector<int>{startNode}, true, false);
+ dnodes.sort();
+
+ // Create DFSM Circuit
+
+ SigSpec accept_sig, reject_sig;
+
+ for (auto &it : dnodes)
+ {
+ SvaDFsmNode &dnode = it.second;
+ dnode.ffoutwire = module->addWire(NEW_ID);
+ dnode.statesig = dnode.ffoutwire;
+
+ if (it.first == vector<int>{startNode})
+ dnode.statesig = module->Or(NEW_ID, dnode.statesig, trigger_sig);
+ }
+
+ for (auto &it : dnodes)
+ {
+ SvaDFsmNode &dnode = it.second;
+ dict<vector<int>, vector<Const>> edge_cond;
+
+ for (auto &edge : dnode.edges)
+ edge_cond[edge.first].push_back(edge.second);
+
+ for (auto &it : edge_cond) {
+ optimize_cond(it.second);
+ for (auto &value : it.second)
+ dnodes.at(it.first).nextstate.append(make_cond_eq(dnode.ctrl, value, dnode.statesig));
+ }
+
+ if (accept_p) {
+ vector<Const> accept_cond = dnode.accept;
+ optimize_cond(accept_cond);
+ for (auto &value : accept_cond)
+ accept_sig.append(make_cond_eq(dnode.ctrl, value, dnode.statesig));
+ }
+
+ if (reject_p) {
+ vector<Const> reject_cond = dnode.reject;
+ optimize_cond(reject_cond);
+ for (auto &value : reject_cond)
+ reject_sig.append(make_cond_eq(dnode.ctrl, value, dnode.statesig));
+ }
+ }
+
+ for (auto &it : dnodes)
+ {
+ SvaDFsmNode &dnode = it.second;
+ if (GetSize(dnode.nextstate) == 0) {
+ module->connect(dnode.ffoutwire, State::S0);
+ } else
+ if (GetSize(dnode.nextstate) == 1) {
+ clocking.addDff(NEW_ID, dnode.nextstate, dnode.ffoutwire, State::S0);
+ } else {
+ SigSpec nextstate = module->ReduceOr(NEW_ID, dnode.nextstate);
+ clocking.addDff(NEW_ID, nextstate, dnode.ffoutwire, State::S0);
+ }
+ }
+
+ if (accept_p)
+ {
+ if (GetSize(accept_sig) == 0)
+ final_accept_sig = State::S0;
+ else if (GetSize(accept_sig) == 1)
+ final_accept_sig = accept_sig;
+ else
+ final_accept_sig = module->ReduceOr(NEW_ID, accept_sig);
+ *accept_p = final_accept_sig;
+ }
+
+ if (reject_p)
+ {
+ if (GetSize(reject_sig) == 0)
+ final_reject_sig = State::S0;
+ else if (GetSize(reject_sig) == 1)
+ final_reject_sig = reject_sig;
+ else
+ final_reject_sig = module->ReduceOr(NEW_ID, reject_sig);
+ *reject_p = final_reject_sig;
+ }
+ }
+
+ SigBit getFirstAccept()
+ {
+ SigBit accept;
+ getFirstAcceptReject(&accept, nullptr);
+ return accept;
+ }
+
+ SigBit getReject()
+ {
+ SigBit reject;
+ getFirstAcceptReject(nullptr, &reject);
+ return reject;
+ }
+
+ void getDFsm(SvaFsm &output_fsm, int output_start_node, int output_accept_node, int output_reject_node = -1, bool firstmatch = true, bool condaccept = false)
+ {
+ log_assert(!materialized);
+ materialized = true;
+
+ // Create unlinked NFSM
+
+ unodes.resize(GetSize(nodes));
+
+ for (int node = 0; node < GetSize(nodes); node++)
+ node_to_unode(node, node, SigSpec());
+
+ mark_reachable_unode(startNode);
+
+ // Create DFSM
+
+ create_dnode(vector<int>{startNode}, firstmatch, condaccept);
+ dnodes.sort();
+
+ // Create DFSM Graph
+
+ for (auto &it : dnodes)
+ {
+ SvaDFsmNode &dnode = it.second;
+ dnode.outnode = output_fsm.createNode();
+
+ if (it.first == vector<int>{startNode})
+ output_fsm.createLink(output_start_node, dnode.outnode);
+
+ if (output_accept_node >= 0) {
+ vector<Const> accept_cond = dnode.accept;
+ optimize_cond(accept_cond);
+ for (auto &value : accept_cond)
+ output_fsm.createLink(it.second.outnode, output_accept_node, make_cond_eq(dnode.ctrl, value));
+ }
+
+ if (output_reject_node >= 0) {
+ vector<Const> reject_cond = dnode.reject;
+ optimize_cond(reject_cond);
+ for (auto &value : reject_cond)
+ output_fsm.createLink(it.second.outnode, output_reject_node, make_cond_eq(dnode.ctrl, value));
+ }
+ }
+
+ for (auto &it : dnodes)
+ {
+ SvaDFsmNode &dnode = it.second;
+ dict<vector<int>, vector<Const>> edge_cond;
+
+ for (auto &edge : dnode.edges)
+ edge_cond[edge.first].push_back(edge.second);
+
+ for (auto &it : edge_cond) {
+ optimize_cond(it.second);
+ for (auto &value : it.second)
+ output_fsm.createEdge(dnode.outnode, dnodes.at(it.first).outnode, make_cond_eq(dnode.ctrl, value));
+ }
+ }
+ }
+
+ // ----------------------------------------------------
+ // State dump for verbose log messages
+
+ void dump_nodes()
+ {
+ if (nodes.empty())
+ return;
+
+ log(" non-deterministic encoding:\n");
+ for (int i = 0; i < GetSize(nodes); i++)
+ {
+ log(" node %d:%s\n", i,
+ i == startNode ? " [start]" :
+ i == acceptNode ? " [accept]" :
+ i == condNode ? " [cond]" : "");
+
+ for (auto &it : nodes[i].edges) {
+ if (it.second != State::S1)
+ log(" egde %s -> %d\n", log_signal(it.second), it.first);
+ else
+ log(" egde -> %d\n", it.first);
+ }
+
+ for (auto &it : nodes[i].links) {
+ if (it.second != State::S1)
+ log(" link %s -> %d\n", log_signal(it.second), it.first);
+ else
+ log(" link -> %d\n", it.first);
+ }
+ }
+ }
+
+ void dump_unodes()
+ {
+ if (unodes.empty())
+ return;
+
+ log(" unlinked non-deterministic encoding:\n");
+ for (int i = 0; i < GetSize(unodes); i++)
+ {
+ if (!unodes[i].reachable)
+ continue;
+
+ log(" unode %d:%s\n", i, i == startNode ? " [start]" : "");
+
+ for (auto &it : unodes[i].edges) {
+ if (!it.second.empty())
+ log(" egde %s -> %d\n", log_signal(it.second), it.first);
+ else
+ log(" egde -> %d\n", it.first);
+ }
+
+ for (auto &ctrl : unodes[i].accept) {
+ if (!ctrl.empty())
+ log(" accept %s\n", log_signal(ctrl));
+ else
+ log(" accept\n");
+ }
+
+ for (auto &ctrl : unodes[i].cond) {
+ if (!ctrl.empty())
+ log(" cond %s\n", log_signal(ctrl));
+ else
+ log(" cond\n");
+ }
+ }
+ }
+
+ void dump_dnodes()
+ {
+ if (dnodes.empty())
+ return;
+
+ log(" deterministic encoding:\n");
+ for (auto &it : dnodes)
+ {
+ log(" dnode {");
+ for (int i = 0; i < GetSize(it.first); i++)
+ log("%s%d", i ? "," : "", it.first[i]);
+ log("}:%s\n", GetSize(it.first) == 1 && it.first[0] == startNode ? " [start]" : "");
+
+ log(" ctrl %s\n", log_signal(it.second.ctrl));
+
+ for (auto &edge : it.second.edges) {
+ log(" edge %s -> {", log_signal(edge.second));
+ for (int i = 0; i < GetSize(edge.first); i++)
+ log("%s%d", i ? "," : "", edge.first[i]);
+ log("}\n");
+ }
+
+ for (auto &value : it.second.accept)
+ log(" accept %s\n", log_signal(value));
+
+ for (auto &value : it.second.reject)
+ log(" reject %s\n", log_signal(value));
+ }
+ }
+
+ void dump()
+ {
+ if (!nodes.empty())
+ log(" number of NFSM states: %d\n", GetSize(nodes));
+
+ if (!unodes.empty()) {
+ int count = 0;
+ for (auto &unode : unodes)
+ if (unode.reachable)
+ count++;
+ log(" number of reachable UFSM states: %d\n", count);
+ }
+
+ if (!dnodes.empty())
+ log(" number of DFSM states: %d\n", GetSize(dnodes));
+
+ if (verific_verbose >= 2) {
+ dump_nodes();
+ dump_unodes();
+ dump_dnodes();
+ }
+
+ if (trigger_sig != State::S1)
+ log(" trigger signal: %s\n", log_signal(trigger_sig));
+
+ if (final_accept_sig != State::Sx)
+ log(" accept signal: %s\n", log_signal(final_accept_sig));
+
+ if (final_reject_sig != State::Sx)
+ log(" reject signal: %s\n", log_signal(final_reject_sig));
+ }
+};
+
+PRIVATE_NAMESPACE_END
+
+YOSYS_NAMESPACE_BEGIN
+
+pool<int> verific_sva_prims = {
+ // Copy&paste from Verific 3.16_484_32_170630 Netlist.h
+ PRIM_SVA_IMMEDIATE_ASSERT, PRIM_SVA_ASSERT, PRIM_SVA_COVER, PRIM_SVA_ASSUME,
+ PRIM_SVA_EXPECT, PRIM_SVA_POSEDGE, PRIM_SVA_NOT, PRIM_SVA_FIRST_MATCH,
+ PRIM_SVA_ENDED, PRIM_SVA_MATCHED, PRIM_SVA_CONSECUTIVE_REPEAT,
+ PRIM_SVA_NON_CONSECUTIVE_REPEAT, PRIM_SVA_GOTO_REPEAT,
+ PRIM_SVA_MATCH_ITEM_TRIGGER, PRIM_SVA_AND, PRIM_SVA_OR, PRIM_SVA_SEQ_AND,
+ PRIM_SVA_SEQ_OR, PRIM_SVA_EVENT_OR, PRIM_SVA_OVERLAPPED_IMPLICATION,
+ PRIM_SVA_NON_OVERLAPPED_IMPLICATION, PRIM_SVA_OVERLAPPED_FOLLOWED_BY,
+ PRIM_SVA_NON_OVERLAPPED_FOLLOWED_BY, PRIM_SVA_INTERSECT, PRIM_SVA_THROUGHOUT,
+ PRIM_SVA_WITHIN, PRIM_SVA_AT, PRIM_SVA_DISABLE_IFF, PRIM_SVA_SAMPLED,
+ PRIM_SVA_ROSE, PRIM_SVA_FELL, PRIM_SVA_STABLE, PRIM_SVA_PAST,
+ PRIM_SVA_MATCH_ITEM_ASSIGN, PRIM_SVA_SEQ_CONCAT, PRIM_SVA_IF,
+ PRIM_SVA_RESTRICT, PRIM_SVA_TRIGGERED, PRIM_SVA_STRONG, PRIM_SVA_WEAK,
+ PRIM_SVA_NEXTTIME, PRIM_SVA_S_NEXTTIME, PRIM_SVA_ALWAYS, PRIM_SVA_S_ALWAYS,
+ PRIM_SVA_S_EVENTUALLY, PRIM_SVA_EVENTUALLY, PRIM_SVA_UNTIL, PRIM_SVA_S_UNTIL,
+ PRIM_SVA_UNTIL_WITH, PRIM_SVA_S_UNTIL_WITH, PRIM_SVA_IMPLIES, PRIM_SVA_IFF,
+ PRIM_SVA_ACCEPT_ON, PRIM_SVA_REJECT_ON, PRIM_SVA_SYNC_ACCEPT_ON,
+ PRIM_SVA_SYNC_REJECT_ON, PRIM_SVA_GLOBAL_CLOCKING_DEF,
+ PRIM_SVA_GLOBAL_CLOCKING_REF, PRIM_SVA_IMMEDIATE_ASSUME,
+ PRIM_SVA_IMMEDIATE_COVER, OPER_SVA_SAMPLED, OPER_SVA_STABLE
+};
+
+struct VerificSvaImporter
+{
+ VerificImporter *importer = nullptr;
+ Module *module = nullptr;
+
+ Netlist *netlist = nullptr;
+ Instance *root = nullptr;
+
+ VerificClocking clocking;
+
+ bool mode_assert = false;
+ bool mode_assume = false;
+ bool mode_cover = false;
+ bool mode_trigger = false;
+
+ Instance *net_to_ast_driver(Net *n)
+ {
+ if (n == nullptr)
+ return nullptr;
+
+ if (n->IsMultipleDriven())
+ return nullptr;
+
+ Instance *inst = n->Driver();
+
+ if (inst == nullptr)
+ return nullptr;
+
+ if (!verific_sva_prims.count(inst->Type()))
+ return nullptr;
+
+ if (inst->Type() == PRIM_SVA_ROSE || inst->Type() == PRIM_SVA_FELL ||
+ inst->Type() == PRIM_SVA_STABLE || inst->Type() == OPER_SVA_STABLE ||
+ inst->Type() == PRIM_SVA_PAST || inst->Type() == PRIM_SVA_TRIGGERED)
+ return nullptr;
+
+ return inst;
+ }
+
+ Instance *get_ast_input(Instance *inst) { return net_to_ast_driver(inst->GetInput()); }
+ Instance *get_ast_input1(Instance *inst) { return net_to_ast_driver(inst->GetInput1()); }
+ Instance *get_ast_input2(Instance *inst) { return net_to_ast_driver(inst->GetInput2()); }
+ Instance *get_ast_input3(Instance *inst) { return net_to_ast_driver(inst->GetInput3()); }
+ Instance *get_ast_control(Instance *inst) { return net_to_ast_driver(inst->GetControl()); }
+
+ // ----------------------------------------------------------
+ // SVA Importer
+
+ struct ParserErrorException {
+ };
+
+ [[noreturn]] void parser_error(std::string errmsg)
+ {
+ if (!importer->mode_keep)
+ log_error("%s", errmsg.c_str());
+ log_warning("%s", errmsg.c_str());
+ throw ParserErrorException();
+ }
+
+ [[noreturn]] void parser_error(std::string errmsg, linefile_type loc)
+ {
+ parser_error(stringf("%s at %s:%d.\n", errmsg.c_str(), LineFile::GetFileName(loc), LineFile::GetLineNo(loc)));
+ }
+
+ [[noreturn]] void parser_error(std::string errmsg, Instance *inst)
+ {
+ parser_error(stringf("%s at %s (%s)", errmsg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile());
+ }
+
+ [[noreturn]] void parser_error(Instance *inst)
+ {
+ parser_error(stringf("Verific SVA primitive %s (%s) is currently unsupported in this context",
+ inst->View()->Owner()->Name(), inst->Name()), inst->Linefile());
+ }
+
+ dict<Net*, bool, hash_ptr_ops> check_expression_cache;
+
+ bool check_expression(Net *net, bool raise_error = false)
+ {
+ while (!check_expression_cache.count(net))
+ {
+ Instance *inst = net_to_ast_driver(net);
+
+ if (inst == nullptr) {
+ check_expression_cache[net] = true;
+ break;
+ }
+
+ if (inst->Type() == PRIM_SVA_AT)
+ {
+ VerificClocking new_clocking(importer, net);
+ log_assert(new_clocking.cond_net == nullptr);
+ if (!clocking.property_matches_sequence(new_clocking))
+ parser_error("Mixed clocking is currently not supported", inst);
+ check_expression_cache[net] = check_expression(new_clocking.body_net, raise_error);
+ break;
+ }
+
+ if (inst->Type() == PRIM_SVA_FIRST_MATCH || inst->Type() == PRIM_SVA_NOT)
+ {
+ check_expression_cache[net] = check_expression(inst->GetInput(), raise_error);
+ break;
+ }
+
+ if (inst->Type() == PRIM_SVA_SEQ_OR || inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_INTERSECT ||
+ inst->Type() == PRIM_SVA_WITHIN || inst->Type() == PRIM_SVA_THROUGHOUT ||
+ inst->Type() == PRIM_SVA_OR || inst->Type() == PRIM_SVA_AND)
+ {
+ check_expression_cache[net] = check_expression(inst->GetInput1(), raise_error) && check_expression(inst->GetInput2(), raise_error);
+ break;
+ }
+
+ if (inst->Type() == PRIM_SVA_SEQ_CONCAT)
+ {
+ const char *sva_low_s = inst->GetAttValue("sva:low");
+ const char *sva_high_s = inst->GetAttValue("sva:high");
+
+ int sva_low = atoi(sva_low_s);
+ int sva_high = atoi(sva_high_s);
+ bool sva_inf = !strcmp(sva_high_s, "$");
+
+ if (sva_low == 0 && sva_high == 0 && !sva_inf)
+ check_expression_cache[net] = check_expression(inst->GetInput1(), raise_error) && check_expression(inst->GetInput2(), raise_error);
+ else
+ check_expression_cache[net] = false;
+ break;
+ }
+
+ check_expression_cache[net] = false;
+ }
+
+ if (raise_error && !check_expression_cache.at(net))
+ parser_error(net_to_ast_driver(net));
+ return check_expression_cache.at(net);
+ }
+
+ SigBit parse_expression(Net *net)
+ {
+ check_expression(net, true);
+
+ Instance *inst = net_to_ast_driver(net);
+
+ if (inst == nullptr) {
+ return importer->net_map_at(net);
+ }
+
+ if (inst->Type() == PRIM_SVA_AT)
+ {
+ VerificClocking new_clocking(importer, net);
+ log_assert(new_clocking.cond_net == nullptr);
+ if (!clocking.property_matches_sequence(new_clocking))
+ parser_error("Mixed clocking is currently not supported", inst);
+ return parse_expression(new_clocking.body_net);
+ }
+
+ if (inst->Type() == PRIM_SVA_FIRST_MATCH)
+ return parse_expression(inst->GetInput());
+
+ if (inst->Type() == PRIM_SVA_NOT)
+ return module->Not(NEW_ID, parse_expression(inst->GetInput()));
+
+ if (inst->Type() == PRIM_SVA_SEQ_OR || inst->Type() == PRIM_SVA_OR)
+ return module->Or(NEW_ID, parse_expression(inst->GetInput1()), parse_expression(inst->GetInput2()));
+
+ if (inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_AND || inst->Type() == PRIM_SVA_INTERSECT ||
+ inst->Type() == PRIM_SVA_WITHIN || inst->Type() == PRIM_SVA_THROUGHOUT || inst->Type() == PRIM_SVA_SEQ_CONCAT)
+ return module->And(NEW_ID, parse_expression(inst->GetInput1()), parse_expression(inst->GetInput2()));
+
+ log_abort();
+ }
+
+ bool check_zero_consecutive_repeat(Net *net)
+ {
+ Instance *inst = net_to_ast_driver(net);
+
+ if (inst == nullptr)
+ return false;
+
+ if (inst->Type() != PRIM_SVA_CONSECUTIVE_REPEAT)
+ return false;
+
+ const char *sva_low_s = inst->GetAttValue("sva:low");
+ int sva_low = atoi(sva_low_s);
+
+ return sva_low == 0;
+ }
+
+ int parse_consecutive_repeat(SvaFsm &fsm, int start_node, Net *net, bool add_pre_delay, bool add_post_delay)
+ {
+ Instance *inst = net_to_ast_driver(net);
+
+ log_assert(inst->Type() == PRIM_SVA_CONSECUTIVE_REPEAT);
+
+ const char *sva_low_s = inst->GetAttValue("sva:low");
+ const char *sva_high_s = inst->GetAttValue("sva:high");
+
+ int sva_low = atoi(sva_low_s);
+ int sva_high = atoi(sva_high_s);
+ bool sva_inf = !strcmp(sva_high_s, "$");
+
+ Net *body_net = inst->GetInput();
+
+ if (add_pre_delay || add_post_delay)
+ log_assert(sva_low == 0);
+
+ if (sva_low == 0) {
+ if (!add_pre_delay && !add_post_delay)
+ parser_error("Possibly zero-length consecutive repeat must follow or precede a delay of at least one cycle", inst);
+ sva_low++;
+ }
+
+ int node = fsm.createNode(start_node);
+ start_node = node;
+
+ if (add_pre_delay) {
+ node = fsm.createNode(start_node);
+ fsm.createEdge(start_node, node);
+ }
+
+ int prev_node = node;
+ node = parse_sequence(fsm, node, body_net);
+
+ for (int i = 1; i < sva_low; i++)
+ {
+ int next_node = fsm.createNode();
+ fsm.createEdge(node, next_node);
+
+ prev_node = node;
+ node = parse_sequence(fsm, next_node, body_net);
+ }
+
+ if (sva_inf)
+ {
+ log_assert(prev_node >= 0);
+ fsm.createEdge(node, prev_node);
+ }
+ else
+ {
+ for (int i = sva_low; i < sva_high; i++)
+ {
+ int next_node = fsm.createNode();
+ fsm.createEdge(node, next_node);
+
+ prev_node = node;
+ node = parse_sequence(fsm, next_node, body_net);
+
+ fsm.createLink(prev_node, node);
+ }
+ }
+
+ if (add_post_delay) {
+ int next_node = fsm.createNode();
+ fsm.createEdge(node, next_node);
+ node = next_node;
+ }
+
+ if (add_pre_delay || add_post_delay)
+ fsm.createLink(start_node, node);
+
+ return node;
+ }
+
+ int parse_sequence(SvaFsm &fsm, int start_node, Net *net)
+ {
+ if (check_expression(net)) {
+ int node = fsm.createNode();
+ fsm.createLink(start_node, node, parse_expression(net));
+ return node;
+ }
+
+ Instance *inst = net_to_ast_driver(net);
+
+ if (inst->Type() == PRIM_SVA_AT)
+ {
+ VerificClocking new_clocking(importer, net);
+ log_assert(new_clocking.cond_net == nullptr);
+ if (!clocking.property_matches_sequence(new_clocking))
+ parser_error("Mixed clocking is currently not supported", inst);
+ return parse_sequence(fsm, start_node, new_clocking.body_net);
+ }
+
+ if (inst->Type() == PRIM_SVA_FIRST_MATCH)
+ {
+ SvaFsm match_fsm(clocking);
+ match_fsm.createLink(parse_sequence(match_fsm, match_fsm.createStartNode(), inst->GetInput()), match_fsm.acceptNode);
+
+ int node = fsm.createNode();
+ match_fsm.getDFsm(fsm, start_node, node);
+
+ if (verific_verbose) {
+ log(" First Match FSM:\n");
+ match_fsm.dump();
+ }
+
+ return node;
+ }
+
+ if (inst->Type() == PRIM_SVA_SEQ_CONCAT)
+ {
+ const char *sva_low_s = inst->GetAttValue("sva:low");
+ const char *sva_high_s = inst->GetAttValue("sva:high");
+
+ int sva_low = atoi(sva_low_s);
+ int sva_high = atoi(sva_high_s);
+ bool sva_inf = !strcmp(sva_high_s, "$");
+
+ int node = -1;
+ bool past_add_delay = false;
+
+ if (check_zero_consecutive_repeat(inst->GetInput1()) && sva_low > 0) {
+ node = parse_consecutive_repeat(fsm, start_node, inst->GetInput1(), false, true);
+ sva_low--, sva_high--;
+ } else {
+ node = parse_sequence(fsm, start_node, inst->GetInput1());
+ }
+
+ if (check_zero_consecutive_repeat(inst->GetInput2()) && sva_low > 0) {
+ past_add_delay = true;
+ sva_low--, sva_high--;
+ }
+
+ for (int i = 0; i < sva_low; i++) {
+ int next_node = fsm.createNode();
+ fsm.createEdge(node, next_node);
+ node = next_node;
+ }
+
+ if (sva_inf)
+ {
+ fsm.createEdge(node, node);
+ }
+ else
+ {
+ for (int i = sva_low; i < sva_high; i++)
+ {
+ int next_node = fsm.createNode();
+ fsm.createEdge(node, next_node);
+ fsm.createLink(node, next_node);
+ node = next_node;
+ }
+ }
+
+ if (past_add_delay)
+ node = parse_consecutive_repeat(fsm, node, inst->GetInput2(), true, false);
+ else
+ node = parse_sequence(fsm, node, inst->GetInput2());
+
+ return node;
+ }
+
+ if (inst->Type() == PRIM_SVA_CONSECUTIVE_REPEAT)
+ {
+ return parse_consecutive_repeat(fsm, start_node, net, false, false);
+ }
+
+ if (inst->Type() == PRIM_SVA_NON_CONSECUTIVE_REPEAT || inst->Type() == PRIM_SVA_GOTO_REPEAT)
+ {
+ const char *sva_low_s = inst->GetAttValue("sva:low");
+ const char *sva_high_s = inst->GetAttValue("sva:high");
+
+ int sva_low = atoi(sva_low_s);
+ int sva_high = atoi(sva_high_s);
+ bool sva_inf = !strcmp(sva_high_s, "$");
+
+ Net *body_net = inst->GetInput();
+ int node = fsm.createNode(start_node);
+
+ SigBit cond = parse_expression(body_net);
+ SigBit not_cond = module->Not(NEW_ID, cond);
+
+ for (int i = 0; i < sva_low; i++)
+ {
+ int wait_node = fsm.createNode();
+ fsm.createEdge(wait_node, wait_node, not_cond);
+
+ if (i == 0)
+ fsm.createLink(node, wait_node);
+ else
+ fsm.createEdge(node, wait_node);
+
+ int next_node = fsm.createNode();
+ fsm.createLink(wait_node, next_node, cond);
+
+ node = next_node;
+ }
+
+ if (sva_inf)
+ {
+ int wait_node = fsm.createNode();
+ fsm.createEdge(wait_node, wait_node, not_cond);
+ fsm.createEdge(node, wait_node);
+ fsm.createLink(wait_node, node, cond);
+ }
+ else
+ {
+ for (int i = sva_low; i < sva_high; i++)
+ {
+ int wait_node = fsm.createNode();
+ fsm.createEdge(wait_node, wait_node, not_cond);
+
+ if (i == 0)
+ fsm.createLink(node, wait_node);
+ else
+ fsm.createEdge(node, wait_node);
+
+ int next_node = fsm.createNode();
+ fsm.createLink(wait_node, next_node, cond);
+
+ fsm.createLink(node, next_node);
+ node = next_node;
+ }
+ }
+
+ if (inst->Type() == PRIM_SVA_NON_CONSECUTIVE_REPEAT)
+ fsm.createEdge(node, node);
+
+ return node;
+ }
+
+ if (inst->Type() == PRIM_SVA_SEQ_OR || inst->Type() == PRIM_SVA_OR)
+ {
+ int node = parse_sequence(fsm, start_node, inst->GetInput1());
+ int node2 = parse_sequence(fsm, start_node, inst->GetInput2());
+ fsm.createLink(node2, node);
+ return node;
+ }
+
+ if (inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_AND)
+ {
+ SvaFsm fsm1(clocking);
+ fsm1.createLink(parse_sequence(fsm1, fsm1.createStartNode(), inst->GetInput1()), fsm1.acceptNode);
+
+ SvaFsm fsm2(clocking);
+ fsm2.createLink(parse_sequence(fsm2, fsm2.createStartNode(), inst->GetInput2()), fsm2.acceptNode);
+
+ SvaFsm combined_fsm(clocking);
+ fsm1.getDFsm(combined_fsm, combined_fsm.createStartNode(), -1, combined_fsm.acceptNode);
+ fsm2.getDFsm(combined_fsm, combined_fsm.createStartNode(), -1, combined_fsm.acceptNode);
+
+ int node = fsm.createNode();
+ combined_fsm.getDFsm(fsm, start_node, -1, node);
+
+ if (verific_verbose)
+ {
+ log(" Left And FSM:\n");
+ fsm1.dump();
+
+ log(" Right And FSM:\n");
+ fsm1.dump();
+
+ log(" Combined And FSM:\n");
+ combined_fsm.dump();
+ }
+
+ return node;
+ }
+
+ if (inst->Type() == PRIM_SVA_INTERSECT || inst->Type() == PRIM_SVA_WITHIN)
+ {
+ SvaFsm intersect_fsm(clocking);
+
+ if (inst->Type() == PRIM_SVA_INTERSECT)
+ {
+ intersect_fsm.createLink(parse_sequence(intersect_fsm, intersect_fsm.createStartNode(), inst->GetInput1()), intersect_fsm.acceptNode);
+ }
+ else
+ {
+ int n = intersect_fsm.createNode();
+ intersect_fsm.createLink(intersect_fsm.createStartNode(), n);
+ intersect_fsm.createEdge(n, n);
+
+ n = parse_sequence(intersect_fsm, n, inst->GetInput1());
+
+ intersect_fsm.createLink(n, intersect_fsm.acceptNode);
+ intersect_fsm.createEdge(n, n);
+ }
+
+ intersect_fsm.in_cond_mode = true;
+ intersect_fsm.createLink(parse_sequence(intersect_fsm, intersect_fsm.createStartNode(), inst->GetInput2()), intersect_fsm.condNode);
+ intersect_fsm.in_cond_mode = false;
+
+ int node = fsm.createNode();
+ intersect_fsm.getDFsm(fsm, start_node, node, -1, false, true);
+
+ if (verific_verbose) {
+ log(" Intersect FSM:\n");
+ intersect_fsm.dump();
+ }
+
+ return node;
+ }
+
+ if (inst->Type() == PRIM_SVA_THROUGHOUT)
+ {
+ SigBit expr = parse_expression(inst->GetInput1());
+
+ fsm.pushThroughout(expr);
+ int node = parse_sequence(fsm, start_node, inst->GetInput2());
+ fsm.popThroughout();
+
+ return node;
+ }
+
+ parser_error(inst);
+ }
+
+ void get_fsm_accept_reject(SvaFsm &fsm, SigBit *accept_p, SigBit *reject_p, bool swap_accept_reject = false)
+ {
+ log_assert(accept_p != nullptr || reject_p != nullptr);
+
+ if (swap_accept_reject)
+ get_fsm_accept_reject(fsm, reject_p, accept_p);
+ else if (reject_p == nullptr)
+ *accept_p = fsm.getAccept();
+ else if (accept_p == nullptr)
+ *reject_p = fsm.getReject();
+ else
+ fsm.getFirstAcceptReject(accept_p, reject_p);
+ }
+
+ bool eventually_property(Net *&net, SigBit &trig)
+ {
+ Instance *inst = net_to_ast_driver(net);
+
+ if (inst == nullptr)
+ return false;
+
+ if (clocking.cond_net != nullptr)
+ trig = importer->net_map_at(clocking.cond_net);
+ else
+ trig = State::S1;
+
+ if (inst->Type() == PRIM_SVA_S_EVENTUALLY || inst->Type() == PRIM_SVA_EVENTUALLY)
+ {
+ if (mode_cover || mode_trigger)
+ parser_error(inst);
+
+ net = inst->GetInput();
+ clocking.cond_net = nullptr;
+
+ return true;
+ }
+
+ if (inst->Type() == PRIM_SVA_OVERLAPPED_IMPLICATION ||
+ inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION)
+ {
+ Net *antecedent_net = inst->GetInput1();
+ Net *consequent_net = inst->GetInput2();
+
+ Instance *consequent_inst = net_to_ast_driver(consequent_net);
+
+ if (consequent_inst == nullptr)
+ return false;
+
+ if (consequent_inst->Type() != PRIM_SVA_S_EVENTUALLY && consequent_inst->Type() != PRIM_SVA_EVENTUALLY)
+ return false;
+
+ if (mode_cover || mode_trigger)
+ parser_error(consequent_inst);
+
+ int node;
+
+ SvaFsm antecedent_fsm(clocking, trig);
+ node = parse_sequence(antecedent_fsm, antecedent_fsm.createStartNode(), antecedent_net);
+ if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) {
+ int next_node = antecedent_fsm.createNode();
+ antecedent_fsm.createEdge(node, next_node);
+ node = next_node;
+ }
+ antecedent_fsm.createLink(node, antecedent_fsm.acceptNode);
+
+ trig = antecedent_fsm.getAccept();
+ net = consequent_inst->GetInput();
+ clocking.cond_net = nullptr;
+
+ if (verific_verbose) {
+ log(" Eventually Antecedent FSM:\n");
+ antecedent_fsm.dump();
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ void parse_property(Net *net, SigBit *accept_p, SigBit *reject_p)
+ {
+ Instance *inst = net_to_ast_driver(net);
+
+ SigBit trig = State::S1;
+
+ if (clocking.cond_net != nullptr)
+ trig = importer->net_map_at(clocking.cond_net);
+
+ if (inst == nullptr)
+ {
+ log_assert(trig == State::S1);
+
+ if (accept_p != nullptr)
+ *accept_p = importer->net_map_at(net);
+ if (reject_p != nullptr)
+ *reject_p = module->Not(NEW_ID, importer->net_map_at(net));
+ }
+ else
+ if (inst->Type() == PRIM_SVA_OVERLAPPED_IMPLICATION ||
+ inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION)
+ {
+ Net *antecedent_net = inst->GetInput1();
+ Net *consequent_net = inst->GetInput2();
+ int node;
+
+ SvaFsm antecedent_fsm(clocking, trig);
+ node = parse_sequence(antecedent_fsm, antecedent_fsm.createStartNode(), antecedent_net);
+ if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) {
+ int next_node = antecedent_fsm.createNode();
+ antecedent_fsm.createEdge(node, next_node);
+ node = next_node;
+ }
+
+ Instance *consequent_inst = net_to_ast_driver(consequent_net);
+
+ if (consequent_inst && (consequent_inst->Type() == PRIM_SVA_UNTIL || consequent_inst->Type() == PRIM_SVA_S_UNTIL ||
+ consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH))
+ {
+ bool until_with = consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH;
+
+ Net *until_net = consequent_inst->GetInput2();
+ consequent_net = consequent_inst->GetInput1();
+ consequent_inst = net_to_ast_driver(consequent_net);
+
+ SigBit until_sig = parse_expression(until_net);
+ SigBit not_until_sig = module->Not(NEW_ID, until_sig);
+ antecedent_fsm.createEdge(node, node, not_until_sig);
+
+ antecedent_fsm.createLink(node, antecedent_fsm.acceptNode, until_with ? State::S1 : not_until_sig);
+ }
+ else
+ {
+ antecedent_fsm.createLink(node, antecedent_fsm.acceptNode);
+ }
+
+ SigBit antecedent_match = antecedent_fsm.getAccept();
+
+ if (verific_verbose) {
+ log(" Antecedent FSM:\n");
+ antecedent_fsm.dump();
+ }
+
+ bool consequent_not = false;
+ if (consequent_inst && consequent_inst->Type() == PRIM_SVA_NOT) {
+ consequent_not = true;
+ consequent_net = consequent_inst->GetInput();
+ consequent_inst = net_to_ast_driver(consequent_net);
+ }
+
+ SvaFsm consequent_fsm(clocking, antecedent_match);
+ node = parse_sequence(consequent_fsm, consequent_fsm.createStartNode(), consequent_net);
+ consequent_fsm.createLink(node, consequent_fsm.acceptNode);
+
+ get_fsm_accept_reject(consequent_fsm, accept_p, reject_p, consequent_not);
+
+ if (verific_verbose) {
+ log(" Consequent FSM:\n");
+ consequent_fsm.dump();
+ }
+ }
+ else
+ {
+ bool prop_not = inst->Type() == PRIM_SVA_NOT;
+ if (prop_not) {
+ net = inst->GetInput();
+ inst = net_to_ast_driver(net);
+ }
+
+ SvaFsm fsm(clocking, trig);
+ int node = parse_sequence(fsm, fsm.createStartNode(), net);
+ fsm.createLink(node, fsm.acceptNode);
+
+ get_fsm_accept_reject(fsm, accept_p, reject_p, prop_not);
+
+ if (verific_verbose) {
+ log(" Sequence FSM:\n");
+ fsm.dump();
+ }
+ }
+ }
+
+ void import()
+ {
+ try
+ {
+ module = importer->module;
+ netlist = root->Owner();
+
+ if (verific_verbose)
+ log(" importing SVA property at root cell %s (%s) at %s:%d.\n", root->Name(), root->View()->Owner()->Name(),
+ LineFile::GetFileName(root->Linefile()), LineFile::GetLineNo(root->Linefile()));
+
+ RTLIL::IdString root_name = module->uniquify(importer->mode_names || root->IsUserDeclared() ? RTLIL::escape_id(root->Name()) : NEW_ID);
+
+ // parse SVA sequence into trigger signal
+
+ clocking = VerificClocking(importer, root->GetInput(), true);
+ SigBit accept_bit = State::S0, reject_bit = State::S0;
+
+ if (clocking.body_net == nullptr)
+ {
+ if (clocking.clock_net != nullptr || clocking.enable_net != nullptr || clocking.disable_net != nullptr || clocking.cond_net != nullptr)
+ parser_error(stringf("Failed to parse SVA clocking"), root);
+
+ if (mode_assert || mode_assume) {
+ reject_bit = module->Not(NEW_ID, parse_expression(root->GetInput()));
+ } else {
+ accept_bit = parse_expression(root->GetInput());
+ }
+ }
+ else
+ {
+ Net *net = clocking.body_net;
+ SigBit trig;
+
+ if (eventually_property(net, trig))
+ {
+ SigBit sig_a, sig_en = trig;
+ parse_property(net, &sig_a, nullptr);
+
+ // add final FF stage
+
+ SigBit sig_a_q, sig_en_q;
+
+ if (clocking.body_net == nullptr) {
+ sig_a_q = sig_a;
+ sig_en_q = sig_en;
+ } else {
+ sig_a_q = module->addWire(NEW_ID);
+ sig_en_q = module->addWire(NEW_ID);
+ clocking.addDff(NEW_ID, sig_a, sig_a_q, State::S0);
+ clocking.addDff(NEW_ID, sig_en, sig_en_q, State::S0);
+ }
+
+ // generate fair/live cell
+
+ RTLIL::Cell *c = nullptr;
+
+ if (mode_assert) c = module->addLive(root_name, sig_a_q, sig_en_q);
+ if (mode_assume) c = module->addFair(root_name, sig_a_q, sig_en_q);
+
+ importer->import_attributes(c->attributes, root);
+
+ return;
+ }
+ else
+ {
+ if (mode_assert || mode_assume) {
+ parse_property(net, nullptr, &reject_bit);
+ } else {
+ parse_property(net, &accept_bit, nullptr);
+ }
+ }
+ }
+
+ if (mode_trigger)
+ {
+ module->connect(importer->net_map_at(root->GetOutput()), accept_bit);
+ }
+ else
+ {
+ SigBit sig_a = module->Not(NEW_ID, reject_bit);
+ SigBit sig_en = module->Or(NEW_ID, accept_bit, reject_bit);
+
+ // add final FF stage
+
+ SigBit sig_a_q, sig_en_q;
+
+ if (clocking.body_net == nullptr) {
+ sig_a_q = sig_a;
+ sig_en_q = sig_en;
+ } else {
+ sig_a_q = module->addWire(NEW_ID);
+ sig_en_q = module->addWire(NEW_ID);
+ clocking.addDff(NEW_ID, sig_a, sig_a_q, State::S0);
+ clocking.addDff(NEW_ID, sig_en, sig_en_q, State::S0);
+ }
+
+ // generate assert/assume/cover cell
+
+ RTLIL::Cell *c = nullptr;
+
+ if (mode_assert) c = module->addAssert(root_name, sig_a_q, sig_en_q);
+ if (mode_assume) c = module->addAssume(root_name, sig_a_q, sig_en_q);
+ if (mode_cover) c = module->addCover(root_name, sig_a_q, sig_en_q);
+
+ importer->import_attributes(c->attributes, root);
+ }
+ }
+ catch (ParserErrorException)
+ {
+ }
+ }
+};
+
+void verific_import_sva_assert(VerificImporter *importer, Instance *inst)
+{
+ VerificSvaImporter worker;
+ worker.importer = importer;
+ worker.root = inst;
+ worker.mode_assert = true;
+ worker.import();
+}
+
+void verific_import_sva_assume(VerificImporter *importer, Instance *inst)
+{
+ VerificSvaImporter worker;
+ worker.importer = importer;
+ worker.root = inst;
+ worker.mode_assume = true;
+ worker.import();
+}
+
+void verific_import_sva_cover(VerificImporter *importer, Instance *inst)
+{
+ VerificSvaImporter worker;
+ worker.importer = importer;
+ worker.root = inst;
+ worker.mode_cover = true;
+ worker.import();
+}
+
+void verific_import_sva_trigger(VerificImporter *importer, Instance *inst)
+{
+ VerificSvaImporter worker;
+ worker.importer = importer;
+ worker.root = inst;
+ worker.mode_trigger = true;
+ worker.import();
+}
+
+bool verific_is_sva_net(VerificImporter *importer, Verific::Net *net)
+{
+ VerificSvaImporter worker;
+ worker.importer = importer;
+ return worker.net_to_ast_driver(net) != nullptr;
+}
+
+YOSYS_NAMESPACE_END
diff --git a/frontends/verilog/.gitignore b/frontends/verilog/.gitignore
index 1d4ae9e5..aadbcdcd 100644
--- a/frontends/verilog/.gitignore
+++ b/frontends/verilog/.gitignore
@@ -1,4 +1,4 @@
verilog_lexer.cc
verilog_parser.output
verilog_parser.tab.cc
-verilog_parser.tab.h
+verilog_parser.tab.hh
diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc
index a06c1d5a..dbaace58 100644
--- a/frontends/verilog/Makefile.inc
+++ b/frontends/verilog/Makefile.inc
@@ -1,15 +1,14 @@
GENFILES += frontends/verilog/verilog_parser.tab.cc
-GENFILES += frontends/verilog/verilog_parser.tab.h
+GENFILES += frontends/verilog/verilog_parser.tab.hh
GENFILES += frontends/verilog/verilog_parser.output
GENFILES += frontends/verilog/verilog_lexer.cc
frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y
$(Q) mkdir -p $(dir $@)
- $(P) $(BISON) -d -r all -b frontends/verilog/verilog_parser $<
- $(Q) mv frontends/verilog/verilog_parser.tab.c frontends/verilog/verilog_parser.tab.cc
+ $(P) $(BISON) -o $@ -d -r all -b frontends/verilog/verilog_parser $<
-frontends/verilog/verilog_parser.tab.h: frontends/verilog/verilog_parser.tab.cc
+frontends/verilog/verilog_parser.tab.hh: frontends/verilog/verilog_parser.tab.cc
frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l
$(Q) mkdir -p $(dir $@)
diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc
index 4a58357b..7848c626 100644
--- a/frontends/verilog/const2ast.cc
+++ b/frontends/verilog/const2ast.cc
@@ -49,8 +49,7 @@ static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
int carry = 0;
for (size_t i = 0; i < digits.size(); i++) {
if (digits[i] >= 10)
- log_error("Invalid use of [a-fxz?] in decimal constant at %s:%d.\n",
- current_filename.c_str(), get_line_num());
+ log_file_error(current_filename, get_line_num(), "Invalid use of [a-fxz?] in decimal constant.\n");
digits[i] += carry * 10;
carry = digits[i] % 2;
digits[i] /= 2;
@@ -105,8 +104,8 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
int bits_per_digit = my_ilog2(base-1);
for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) {
if (*it > (base-1) && *it < 0xf0)
- log_error("Digit larger than %d used in in base-%d constant at %s:%d.\n",
- base-1, base, current_filename.c_str(), get_line_num());
+ log_file_error(current_filename, get_line_num(), "Digit larger than %d used in in base-%d constant.\n",
+ base-1, base);
for (int i = 0; i < bits_per_digit; i++) {
int bitmask = 1 << i;
if (*it == 0xf0)
@@ -238,4 +237,3 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
}
YOSYS_NAMESPACE_END
-
diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc
index 997920b8..dea22ee8 100644
--- a/frontends/verilog/preproc.cc
+++ b/frontends/verilog/preproc.cc
@@ -182,15 +182,20 @@ static std::string next_token(bool pass_newline = false)
{
const char *ok = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789";
if (ch == '`' || strchr(ok, ch) != NULL)
- while ((ch = next_char()) != 0) {
- if (strchr(ok, ch) == NULL) {
- return_char(ch);
- break;
- }
+ {
+ char first = ch;
+ ch = next_char();
+ if (first == '`' && (ch == '"' || ch == '`')) {
token += ch;
- }
+ } else do {
+ if (strchr(ok, ch) == NULL) {
+ return_char(ch);
+ break;
+ }
+ token += ch;
+ } while ((ch = next_char()) != 0);
+ }
}
-
return token;
}
@@ -210,10 +215,69 @@ static void input_file(std::istream &f, std::string filename)
input_buffer.insert(it, "\n`file_pop\n");
}
-std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs)
+
+static bool try_expand_macro(std::set<std::string> &defines_with_args,
+ std::map<std::string, std::string> &defines_map,
+ std::string &tok
+ )
+{
+ if (tok == "`\"") {
+ std::string literal("\"");
+ // Expand string literal
+ while (!input_buffer.empty()) {
+ std::string ntok = next_token();
+ if (ntok == "`\"") {
+ insert_input(literal+"\"");
+ return true;
+ } else if (!try_expand_macro(defines_with_args, defines_map, ntok)) {
+ literal += ntok;
+ }
+ }
+ return false; // error - unmatched `"
+ } else if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) {
+ std::string name = tok.substr(1);
+ // printf("expand: >>%s<< -> >>%s<<\n", name.c_str(), defines_map[name].c_str());
+ std::string skipped_spaces = skip_spaces();
+ tok = next_token(false);
+ if (tok == "(" && defines_with_args.count(name) > 0) {
+ int level = 1;
+ std::vector<std::string> args;
+ args.push_back(std::string());
+ while (1)
+ {
+ skip_spaces();
+ tok = next_token(true);
+ if (tok == ")" || tok == "}" || tok == "]")
+ level--;
+ if (level == 0)
+ break;
+ if (level == 1 && tok == ",")
+ args.push_back(std::string());
+ else
+ args.back() += tok;
+ if (tok == "(" || tok == "{" || tok == "[")
+ level++;
+ }
+ for (int i = 0; i < GetSize(args); i++)
+ defines_map[stringf("macro_%s_arg%d", name.c_str(), i+1)] = args[i];
+ } else {
+ insert_input(tok);
+ insert_input(skipped_spaces);
+ }
+ insert_input(defines_map[name]);
+ return true;
+ } else if (tok == "``") {
+ // Swallow `` in macro expansion
+ return true;
+ } else return false;
+}
+
+std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> &pre_defines_map,
+ dict<std::string, std::pair<std::string, bool>> &global_defines_cache, const std::list<std::string> &include_dirs)
{
std::set<std::string> defines_with_args;
std::map<std::string, std::string> defines_map(pre_defines_map);
+ std::vector<std::string> filename_stack;
int ifdef_fail_level = 0;
bool in_elseif = false;
@@ -222,9 +286,19 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons
input_buffer_charp = 0;
input_file(f, filename);
+
defines_map["YOSYS"] = "1";
defines_map[formal_mode ? "FORMAL" : "SYNTHESIS"] = "1";
+ for (auto &it : pre_defines_map)
+ defines_map[it.first] = it.second;
+
+ for (auto &it : global_defines_cache) {
+ if (it.second.second)
+ defines_with_args.insert(it.first);
+ defines_map[it.first] = it.second.first;
+ }
+
while (!input_buffer.empty())
{
std::string tok = next_token();
@@ -281,6 +355,9 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons
if (tok == "`include") {
skip_spaces();
std::string fn = next_token(true);
+ while (try_expand_macro(defines_with_args, defines_map, fn)) {
+ fn = next_token();
+ }
while (1) {
size_t pos = fn.find('"');
if (pos == std::string::npos)
@@ -292,26 +369,66 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons
}
std::ifstream ff;
ff.clear();
- ff.open(fn.c_str());
- if (ff.fail() && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) {
+ std::string fixed_fn = fn;
+ ff.open(fixed_fn.c_str());
+
+ bool filename_path_sep_found;
+ bool fn_relative;
+#ifdef _WIN32
+ // Both forward and backslash are acceptable separators on Windows.
+ filename_path_sep_found = (filename.find_first_of("/\\") != std::string::npos);
+ // Easier just to invert the check for an absolute path (e.g. C:\ or C:/)
+ fn_relative = !(fn[1] == ':' && (fn[2] == '/' || fn[2] == '\\'));
+#else
+ filename_path_sep_found = (filename.find('/') != std::string::npos);
+ fn_relative = (fn[0] != '/');
+#endif
+
+ if (ff.fail() && fn.size() > 0 && fn_relative && filename_path_sep_found) {
// if the include file was not found, it is not given with an absolute path, and the
// currently read file is given with a path, then try again relative to its directory
ff.clear();
- ff.open(filename.substr(0, filename.rfind('/')+1) + fn);
+#ifdef _WIN32
+ fixed_fn = filename.substr(0, filename.find_last_of("/\\")+1) + fn;
+#else
+ fixed_fn = filename.substr(0, filename.rfind('/')+1) + fn;
+#endif
+ ff.open(fixed_fn);
}
- if (ff.fail() && fn.size() > 0 && fn[0] != '/') {
+ if (ff.fail() && fn.size() > 0 && fn_relative) {
// if the include file was not found and it is not given with an absolute path, then
// search it in the include path
for (auto incdir : include_dirs) {
ff.clear();
- ff.open(incdir + '/' + fn);
+ fixed_fn = incdir + '/' + fn;
+ ff.open(fixed_fn);
if (!ff.fail()) break;
}
}
- if (ff.fail())
+ if (ff.fail()) {
output_code.push_back("`file_notfound " + fn);
- else
- input_file(ff, fn);
+ } else {
+ input_file(ff, fixed_fn);
+ yosys_input_files.insert(fixed_fn);
+ }
+ continue;
+ }
+
+ if (tok == "`file_push") {
+ skip_spaces();
+ std::string fn = next_token(true);
+ if (!fn.empty() && fn.front() == '"' && fn.back() == '"')
+ fn = fn.substr(1, fn.size()-2);
+ output_code.push_back(tok + " \"" + fn + "\"");
+ filename_stack.push_back(filename);
+ filename = fn;
+ continue;
+ }
+
+ if (tok == "`file_pop") {
+ output_code.push_back(tok);
+ filename = filename_stack.back();
+ filename_stack.pop_back();
continue;
}
@@ -379,6 +496,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons
defines_with_args.insert(name);
else
defines_with_args.erase(name);
+ global_defines_cache[name] = std::pair<std::string, bool>(value, state == 2);
continue;
}
@@ -389,6 +507,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons
// printf("undef: >>%s<<\n", name.c_str());
defines_map.erase(name);
defines_with_args.erase(name);
+ global_defines_cache.erase(name);
continue;
}
@@ -401,39 +520,16 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons
continue;
}
- if (tok.size() > 1 && tok[0] == '`' && defines_map.count(tok.substr(1)) > 0) {
- std::string name = tok.substr(1);
- // printf("expand: >>%s<< -> >>%s<<\n", name.c_str(), defines_map[name].c_str());
- std::string skipped_spaces = skip_spaces();
- tok = next_token(false);
- if (tok == "(" && defines_with_args.count(name) > 0) {
- int level = 1;
- std::vector<std::string> args;
- args.push_back(std::string());
- while (1)
- {
- tok = next_token(true);
- if (tok == ")" || tok == "}" || tok == "]")
- level--;
- if (level == 0)
- break;
- if (level == 1 && tok == ",")
- args.push_back(std::string());
- else
- args.back() += tok;
- if (tok == "(" || tok == "{" || tok == "[")
- level++;
- }
- for (int i = 0; i < GetSize(args); i++)
- defines_map[stringf("macro_%s_arg%d", name.c_str(), i+1)] = args[i];
- } else {
- insert_input(tok);
- insert_input(skipped_spaces);
- }
- insert_input(defines_map[name]);
+ if (tok == "`resetall") {
+ defines_map.clear();
+ defines_with_args.clear();
+ global_defines_cache.clear();
continue;
}
+ if (try_expand_macro(defines_with_args, defines_map, tok))
+ continue;
+
output_code.push_back(tok);
}
@@ -449,4 +545,3 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons
}
YOSYS_NAMESPACE_END
-
diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc
index 894723c8..8dcc7c5a 100644
--- a/frontends/verilog/verilog_frontend.cc
+++ b/frontends/verilog/verilog_frontend.cc
@@ -42,14 +42,14 @@ static std::list<std::vector<std::string>> verilog_defaults_stack;
static void error_on_dpi_function(AST::AstNode *node)
{
if (node->type == AST::AST_DPI_FUNCTION)
- log_error("Found DPI function %s at %s:%d.\n", node->str.c_str(), node->filename.c_str(), node->linenum);
+ log_file_error(node->filename, node->linenum, "Found DPI function %s.\n", node->str.c_str());
for (auto child : node->children)
error_on_dpi_function(child);
}
struct VerilogFrontend : public Frontend {
VerilogFrontend() : Frontend("verilog", "read modules from Verilog file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -78,6 +78,9 @@ struct VerilogFrontend : public Frontend {
log(" -dump_ast2\n");
log(" dump abstract syntax tree (after simplification)\n");
log("\n");
+ log(" -no_dump_ptr\n");
+ log(" do not include hex memory addresses in dump (easier to diff dumps)\n");
+ log("\n");
log(" -dump_vlog\n");
log(" dump ast as Verilog code (after simplification)\n");
log("\n");
@@ -137,9 +140,13 @@ struct VerilogFrontend : public Frontend {
log(" -icells\n");
log(" interpret cell types starting with '$' as internal cell types\n");
log("\n");
- log(" -ignore_redef\n");
+ log(" -nooverwrite\n");
log(" ignore re-definitions of modules. (the default behavior is to\n");
- log(" create an error message.)\n");
+ log(" create an error message if the existing module is not a black box\n");
+ log(" module, and overwrite the existing module otherwise.)\n");
+ log("\n");
+ log(" -overwrite\n");
+ log(" overwrite existing modules with the same name\n");
log("\n");
log(" -defer\n");
log(" only read the abstract syntax tree and defer actual compilation\n");
@@ -168,14 +175,19 @@ struct VerilogFrontend : public Frontend {
log("recommended to use a simulator (for example Icarus Verilog) for checking\n");
log("the syntax of the code, rather than to rely on read_verilog for that.\n");
log("\n");
+ log("Depending on if read_verilog is run in -formal mode, either the macro\n");
+ log("SYNTHESIS or FORMAL is defined automatically. In addition, read_verilog\n");
+ log("always defines the macro YOSYS.\n");
+ log("\n");
log("See the Yosys README file for a list of non-standard Verilog features\n");
log("supported by the Yosys Verilog front-end.\n");
log("\n");
}
- virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool flag_dump_ast1 = false;
bool flag_dump_ast2 = false;
+ bool flag_no_dump_ptr = false;
bool flag_dump_vlog = false;
bool flag_dump_rtlil = false;
bool flag_nolatches = false;
@@ -187,7 +199,8 @@ struct VerilogFrontend : public Frontend {
bool flag_nodpi = false;
bool flag_noopt = false;
bool flag_icells = false;
- bool flag_ignore_redef = false;
+ bool flag_nooverwrite = false;
+ bool flag_overwrite = false;
bool flag_defer = false;
std::map<std::string, std::string> defines_map;
std::list<std::string> include_dirs;
@@ -232,6 +245,10 @@ struct VerilogFrontend : public Frontend {
flag_dump_ast2 = true;
continue;
}
+ if (arg == "-no_dump_ptr") {
+ flag_no_dump_ptr = true;
+ continue;
+ }
if (arg == "-dump_vlog") {
flag_dump_vlog = true;
continue;
@@ -285,8 +302,14 @@ struct VerilogFrontend : public Frontend {
flag_icells = true;
continue;
}
- if (arg == "-ignore_redef") {
- flag_ignore_redef = true;
+ if (arg == "-ignore_redef" || arg == "-nooverwrite") {
+ flag_nooverwrite = true;
+ flag_overwrite = false;
+ continue;
+ }
+ if (arg == "-overwrite") {
+ flag_nooverwrite = false;
+ flag_overwrite = true;
continue;
}
if (arg == "-defer") {
@@ -303,10 +326,10 @@ struct VerilogFrontend : public Frontend {
}
if (arg == "-D" && argidx+1 < args.size()) {
std::string name = args[++argidx], value;
- size_t equal = name.find('=', 2);
+ size_t equal = name.find('=');
if (equal != std::string::npos) {
- value = arg.substr(equal+1);
- name = arg.substr(0, equal);
+ value = name.substr(equal+1);
+ name = name.substr(0, equal);
}
defines_map[name] = value;
continue;
@@ -345,7 +368,7 @@ struct VerilogFrontend : public Frontend {
std::string code_after_preproc;
if (!flag_nopp) {
- code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, include_dirs);
+ code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, design->verilog_defines, include_dirs);
if (flag_ppdump)
log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str());
lexin = new std::istringstream(code_after_preproc);
@@ -366,7 +389,7 @@ struct VerilogFrontend : public Frontend {
if (flag_nodpi)
error_on_dpi_function(current_ast);
- AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, flag_noopt, flag_icells, flag_ignore_redef, flag_defer, default_nettype_wire);
+ AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire);
if (!flag_nopp)
delete lexin;
@@ -380,7 +403,7 @@ struct VerilogFrontend : public Frontend {
struct VerilogDefaults : public Pass {
VerilogDefaults() : Pass("verilog_defaults", "set default options for read_verilog") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -401,9 +424,9 @@ struct VerilogDefaults : public Pass {
log("not imply -clear.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design*)
+ void execute(std::vector<std::string> args, RTLIL::Design*) YS_OVERRIDE
{
- if (args.size() == 0)
+ if (args.size() < 2)
cmd_error(args, 1, "Missing argument.");
if (args[1] == "-add") {
@@ -436,6 +459,66 @@ struct VerilogDefaults : public Pass {
}
} VerilogDefaults;
+struct VerilogDefines : public Pass {
+ VerilogDefines() : Pass("verilog_defines", "define and undefine verilog defines") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" verilog_defines [options]\n");
+ log("\n");
+ log("Define and undefine verilog preprocessor macros.\n");
+ log("\n");
+ log(" -Dname[=definition]\n");
+ log(" define the preprocessor symbol 'name' and set its optional value\n");
+ log(" 'definition'\n");
+ log("\n");
+ log(" -Uname[=definition]\n");
+ log(" undefine the preprocessor symbol 'name'\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-D" && argidx+1 < args.size()) {
+ std::string name = args[++argidx], value;
+ size_t equal = name.find('=');
+ if (equal != std::string::npos) {
+ value = name.substr(equal+1);
+ name = name.substr(0, equal);
+ }
+ design->verilog_defines[name] = std::pair<std::string, bool>(value, false);
+ continue;
+ }
+ if (arg.compare(0, 2, "-D") == 0) {
+ size_t equal = arg.find('=', 2);
+ std::string name = arg.substr(2, equal-2);
+ std::string value;
+ if (equal != std::string::npos)
+ value = arg.substr(equal+1);
+ design->verilog_defines[name] = std::pair<std::string, bool>(value, false);
+ continue;
+ }
+ if (arg == "-U" && argidx+1 < args.size()) {
+ std::string name = args[++argidx];
+ design->verilog_defines.erase(name);
+ continue;
+ }
+ if (arg.compare(0, 2, "-U") == 0) {
+ std::string name = arg.substr(2);
+ design->verilog_defines.erase(name);
+ continue;
+ }
+ break;
+ }
+
+ if (args.size() != argidx)
+ cmd_error(args, argidx, "Extra argument.");
+ }
+} VerilogDefines;
+
YOSYS_NAMESPACE_END
// the yyerror function used by bison to report parser errors
@@ -444,13 +527,11 @@ void frontend_verilog_yyerror(char const *fmt, ...)
va_list ap;
char buffer[1024];
char *p = buffer;
- p += snprintf(p, buffer + sizeof(buffer) - p, "Parser error in line %s:%d: ",
- YOSYS_NAMESPACE_PREFIX AST::current_filename.c_str(), frontend_verilog_yyget_lineno());
va_start(ap, fmt);
p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap);
va_end(ap);
p += snprintf(p, buffer + sizeof(buffer) - p, "\n");
- YOSYS_NAMESPACE_PREFIX log_error("%s", buffer);
+ YOSYS_NAMESPACE_PREFIX log_file_error(YOSYS_NAMESPACE_PREFIX AST::current_filename, frontend_verilog_yyget_lineno(),
+ "%s", buffer);
exit(1);
}
-
diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h
index 606ec20a..16edc798 100644
--- a/frontends/verilog/verilog_frontend.h
+++ b/frontends/verilog/verilog_frontend.h
@@ -68,7 +68,8 @@ namespace VERILOG_FRONTEND
}
// the pre-processor
-std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs);
+std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> &pre_defines_map,
+ dict<std::string, std::pair<std::string, bool>> &global_defines_cache, const std::list<std::string> &include_dirs);
YOSYS_NAMESPACE_END
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index 405aeb97..83921bf0 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -42,7 +42,7 @@
#include "kernel/log.h"
#include "frontends/verilog/verilog_frontend.h"
#include "frontends/ast/ast.h"
-#include "verilog_parser.tab.h"
+#include "verilog_parser.tab.hh"
USING_YOSYS_NAMESPACE
using namespace AST;
@@ -145,6 +145,9 @@ YOSYS_NAMESPACE_END
"endfunction" { return TOK_ENDFUNCTION; }
"task" { return TOK_TASK; }
"endtask" { return TOK_ENDTASK; }
+"specify" { return TOK_SPECIFY; }
+"endspecify" { return TOK_ENDSPECIFY; }
+"specparam" { return TOK_SPECPARAM; }
"package" { SV_KEYWORD(TOK_PACKAGE); }
"endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); }
"parameter" { return TOK_PARAMETER; }
@@ -170,17 +173,30 @@ YOSYS_NAMESPACE_END
"endgenerate" { return TOK_ENDGENERATE; }
"while" { return TOK_WHILE; }
"repeat" { return TOK_REPEAT; }
+"automatic" { return TOK_AUTOMATIC; }
+
+"unique" { SV_KEYWORD(TOK_UNIQUE); }
+"unique0" { SV_KEYWORD(TOK_UNIQUE); }
+"priority" { SV_KEYWORD(TOK_PRIORITY); }
"always_comb" { SV_KEYWORD(TOK_ALWAYS); }
"always_ff" { SV_KEYWORD(TOK_ALWAYS); }
"always_latch" { SV_KEYWORD(TOK_ALWAYS); }
-"assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); }
-"assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); }
-"restrict" { if (formal_mode) return TOK_RESTRICT; SV_KEYWORD(TOK_RESTRICT); }
-"property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); }
-"logic" { SV_KEYWORD(TOK_REG); }
-"bit" { SV_KEYWORD(TOK_REG); }
+"assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); }
+"assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); }
+"cover" { if (formal_mode) return TOK_COVER; SV_KEYWORD(TOK_COVER); }
+"restrict" { if (formal_mode) return TOK_RESTRICT; SV_KEYWORD(TOK_RESTRICT); }
+"property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); }
+"rand" { if (formal_mode) return TOK_RAND; SV_KEYWORD(TOK_RAND); }
+"const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); }
+"checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); }
+"endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); }
+"logic" { SV_KEYWORD(TOK_LOGIC); }
+"bit" { SV_KEYWORD(TOK_REG); }
+
+"eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
+"s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
"input" { return TOK_INPUT; }
"output" { return TOK_OUTPUT; }
@@ -192,14 +208,17 @@ YOSYS_NAMESPACE_END
"genvar" { return TOK_GENVAR; }
"real" { return TOK_REAL; }
+"enum" { SV_KEYWORD(TOK_ENUM); }
+"typedef" { SV_KEYWORD(TOK_TYPEDEF); }
+
[0-9][0-9_]* {
frontend_verilog_yylval.string = new std::string(yytext);
- return TOK_CONST;
+ return TOK_CONSTVAL;
}
[0-9]*[ \t]*\'s?[bodhBODH][ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
frontend_verilog_yylval.string = new std::string(yytext);
- return TOK_CONST;
+ return TOK_CONSTVAL;
}
[0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)? {
@@ -222,10 +241,18 @@ YOSYS_NAMESPACE_END
while (yystr[i]) {
if (yystr[i] == '\\' && yystr[i + 1]) {
i++;
- if (yystr[i] == 'n')
+ if (yystr[i] == 'a')
+ yystr[i] = '\a';
+ else if (yystr[i] == 'f')
+ yystr[i] = '\f';
+ else if (yystr[i] == 'n')
yystr[i] = '\n';
+ else if (yystr[i] == 'r')
+ yystr[i] = '\r';
else if (yystr[i] == 't')
yystr[i] = '\t';
+ else if (yystr[i] == 'v')
+ yystr[i] = '\v';
else if ('0' <= yystr[i] && yystr[i] <= '7') {
yystr[i] = yystr[i] - '0';
if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') {
@@ -358,7 +385,9 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
"<<<" { return OP_SSHL; }
">>>" { return OP_SSHR; }
-"::" { SV_KEYWORD(TOK_PACKAGESEP); }
+"::" { return TOK_PACKAGESEP; }
+"++" { return TOK_INCREMENT; }
+"--" { return TOK_DECREMENT; }
"+:" { return TOK_POS_INDEXED; }
"-:" { return TOK_NEG_INDEXED; }
@@ -372,10 +401,6 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
\\[\r\n] /* ignore continuation sequence */
"//"[^\r\n]* /* ignore one-line comments */
-"#"\ *[0-9][0-9_]* /* ignore simulation timings */
-"#"\ *[0-9][0-9_]*\.[0-9][0-9_]* /* ignore simulation timings */
-"#"\ *[$a-zA-Z_\.][$a-zA-Z_0-9\.]* /* ignore simulation timings */
-
. { return *yytext; }
%%
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index 5bbda535..2389d7d3 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -59,6 +59,7 @@ namespace VERILOG_FRONTEND {
bool default_nettype_wire;
bool sv_mode, formal_mode, lib_mode;
bool norestrict_mode, assume_asserts_mode;
+ bool current_wire_rand, current_wire_const;
std::istream *lexin;
}
YOSYS_NAMESPACE_END
@@ -100,27 +101,29 @@ static void free_attr(std::map<std::string, AstNode*> *al)
bool boolean;
}
-%token <string> TOK_STRING TOK_ID TOK_CONST TOK_REALVAL TOK_PRIMITIVE
+%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE
%token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END
%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM
%token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP
-%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG
+%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC
%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
-%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR
+%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC
%token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT
-%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK
+%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK TOK_SPECIFY TOK_ENDSPECIFY TOK_SPECPARAM
%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL
%token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE
%token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED
%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_ASSUME
-%token TOK_RESTRICT TOK_PROPERTY
+%token TOK_RESTRICT TOK_COVER TOK_PROPERTY TOK_ENUM TOK_TYPEDEF
+%token TOK_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY
+%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY
%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int
%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
%type <string> opt_label tok_prim_wrapper hierarchical_id
-%type <boolean> opt_signed
-%type <al> attr
+%type <boolean> opt_signed unique_case_attr
+%type <al> attr case_attr
// operator precedence from low to high
%left OP_LOR
@@ -139,7 +142,9 @@ static void free_attr(std::map<std::string, AstNode*> *al)
%define parse.error verbose
%define parse.lac full
-%expect 2
+%nonassoc FAKE_THEN
+%nonassoc TOK_ELSE
+
%debug
%%
@@ -269,7 +274,13 @@ single_module_para:
if (astbuf1) delete astbuf1;
astbuf1 = new AstNode(AST_PARAMETER);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
- } param_signed param_integer param_range single_param_decl | single_param_decl;
+ } param_signed param_integer param_range single_param_decl |
+ TOK_LOCALPARAM {
+ if (astbuf1) delete astbuf1;
+ astbuf1 = new AstNode(AST_LOCALPARAM);
+ astbuf1->children.push_back(AstNode::mkconst_int(0, true));
+ } param_signed param_integer param_range single_param_decl |
+ single_param_decl;
module_args_opt:
'(' ')' | /* empty */ | '(' module_args optional_comma ')';
@@ -346,6 +357,9 @@ package_body_stmt:
localparam_decl;
non_opt_delay:
+ '#' TOK_ID { delete $2; } |
+ '#' TOK_CONSTVAL { delete $2; } |
+ '#' TOK_REALVAL { delete $2; } |
'#' '(' expr ')' { delete $3; } |
'#' '(' expr ':' expr ':' expr ')' { delete $3; delete $5; delete $7; };
@@ -355,14 +369,17 @@ delay:
wire_type:
{
astbuf3 = new AstNode(AST_WIRE);
+ current_wire_rand = false;
+ current_wire_const = false;
} wire_type_token_list delay {
$$ = astbuf3;
};
wire_type_token_list:
- wire_type_token | wire_type_token_list wire_type_token;
+ wire_type_token | wire_type_token_list wire_type_token |
+ wire_type_token_io ;
-wire_type_token:
+wire_type_token_io:
TOK_INPUT {
astbuf3->is_input = true;
} |
@@ -372,12 +389,17 @@ wire_type_token:
TOK_INOUT {
astbuf3->is_input = true;
astbuf3->is_output = true;
- } |
+ };
+
+wire_type_token:
TOK_WIRE {
} |
TOK_REG {
astbuf3->is_reg = true;
} |
+ TOK_LOGIC {
+ astbuf3->is_logic = true;
+ } |
TOK_INTEGER {
astbuf3->is_reg = true;
astbuf3->range_left = 31;
@@ -392,6 +414,12 @@ wire_type_token:
} |
TOK_SIGNED {
astbuf3->is_signed = true;
+ } |
+ TOK_RAND {
+ current_wire_rand = true;
+ } |
+ TOK_CONST {
+ current_wire_const = true;
};
non_opt_range:
@@ -454,8 +482,19 @@ module_body:
/* empty */;
module_body_stmt:
- task_func_decl | param_decl | localparam_decl | defparam_decl | wire_decl | assign_stmt | cell_stmt |
- always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property;
+ task_func_decl | specify_block |param_decl | localparam_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
+ always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl;
+
+checker_decl:
+ TOK_CHECKER TOK_ID ';' {
+ AstNode *node = new AstNode(AST_GENBLOCK);
+ node->str = *$2;
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } module_body TOK_ENDCHECKER {
+ delete $2;
+ ast_stack.pop_back();
+ };
task_func_decl:
attr TOK_DPI_FUNCTION TOK_ID TOK_ID {
@@ -491,35 +530,36 @@ task_func_decl:
} opt_dpi_function_args ';' {
current_function_or_task = NULL;
} |
- attr TOK_TASK TOK_ID {
+ attr TOK_TASK opt_automatic TOK_ID {
current_function_or_task = new AstNode(AST_TASK);
- current_function_or_task->str = *$3;
+ current_function_or_task->str = *$4;
append_attr(current_function_or_task, $1);
ast_stack.back()->children.push_back(current_function_or_task);
ast_stack.push_back(current_function_or_task);
current_function_or_task_port_id = 1;
- delete $3;
+ delete $4;
} task_func_args_opt ';' task_func_body TOK_ENDTASK {
current_function_or_task = NULL;
ast_stack.pop_back();
} |
- attr TOK_FUNCTION opt_signed range_or_signed_int TOK_ID {
+ attr TOK_FUNCTION opt_automatic opt_signed range_or_signed_int TOK_ID {
current_function_or_task = new AstNode(AST_FUNCTION);
- current_function_or_task->str = *$5;
+ current_function_or_task->str = *$6;
append_attr(current_function_or_task, $1);
ast_stack.back()->children.push_back(current_function_or_task);
ast_stack.push_back(current_function_or_task);
AstNode *outreg = new AstNode(AST_WIRE);
- outreg->str = *$5;
- outreg->is_signed = $3;
- if ($4 != NULL) {
- outreg->children.push_back($4);
- outreg->is_signed = $3 || $4->is_signed;
- $4->is_signed = false;
+ outreg->str = *$6;
+ outreg->is_signed = $4;
+ outreg->is_reg = true;
+ if ($5 != NULL) {
+ outreg->children.push_back($5);
+ outreg->is_signed = $4 || $5->is_signed;
+ $5->is_signed = false;
}
current_function_or_task->children.push_back(outreg);
current_function_or_task_port_id = 1;
- delete $5;
+ delete $6;
} task_func_args_opt ';' task_func_body TOK_ENDFUNCTION {
current_function_or_task = NULL;
ast_stack.pop_back();
@@ -546,6 +586,10 @@ dpi_function_args:
dpi_function_arg |
/* empty */;
+opt_automatic:
+ TOK_AUTOMATIC |
+ /* empty */;
+
opt_signed:
TOK_SIGNED {
$$ = true;
@@ -597,6 +641,194 @@ task_func_body:
task_func_body behavioral_stmt |
/* empty */;
+specify_block:
+ TOK_SPECIFY specify_item_opt TOK_ENDSPECIFY |
+ TOK_SPECIFY TOK_ENDSPECIFY ;
+
+specify_item_opt:
+ specify_item_opt specify_item |
+ specify_item ;
+
+specify_item:
+ specparam_declaration
+ // | pulsestyle_declaration
+ // | showcancelled_declaration
+ | path_declaration
+ | system_timing_declaration
+ ;
+
+specparam_declaration:
+ TOK_SPECPARAM list_of_specparam_assignments ';' |
+ TOK_SPECPARAM specparam_range list_of_specparam_assignments ';' ;
+
+// IEEE 1364-2005 calls this sinmply 'range' but the current 'range' rule allows empty match
+// and the 'non_opt_range' rule allows index ranges not allowed by 1364-2005
+// exxxxtending this for SV specparam would change this anyhow
+specparam_range:
+ '[' constant_expression ':' constant_expression ']' ;
+
+list_of_specparam_assignments:
+ specparam_assignment | list_of_specparam_assignments ',' specparam_assignment;
+
+specparam_assignment:
+ TOK_ID '=' constant_mintypmax_expression ;
+
+/*
+pulsestyle_declaration :
+ ;
+
+showcancelled_declaration :
+ ;
+*/
+
+path_declaration :
+ simple_path_declaration ';'
+ // | edge_sensitive_path_declaration
+ // | state_dependent_path_declaration
+ ;
+
+simple_path_declaration :
+ parallel_path_description '=' path_delay_value |
+ full_path_description '=' path_delay_value
+ ;
+
+path_delay_value :
+ '(' path_delay_expression list_of_path_delay_extra_expressions ')'
+ | path_delay_expression
+ | path_delay_expression list_of_path_delay_extra_expressions
+ ;
+
+list_of_path_delay_extra_expressions :
+/*
+ t_path_delay_expression
+ | trise_path_delay_expression ',' tfall_path_delay_expression
+ | trise_path_delay_expression ',' tfall_path_delay_expression ',' tz_path_delay_expression
+ | t01_path_delay_expression ',' t10_path_delay_expression ',' t0z_path_delay_expression ','
+ tz1_path_delay_expression ',' t1z_path_delay_expression ',' tz0_path_delay_expression
+ | t01_path_delay_expression ',' t10_path_delay_expression ',' t0z_path_delay_expression ','
+ tz1_path_delay_expression ',' t1z_path_delay_expression ',' tz0_path_delay_expression ','
+ t0x_path_delay_expression ',' tx1_path_delay_expression ',' t1x_path_delay_expression ','
+ tx0_path_delay_expression ',' txz_path_delay_expression ',' tzx_path_delay_expression
+*/
+ ',' path_delay_expression
+ | ',' path_delay_expression ',' path_delay_expression
+ | ',' path_delay_expression ',' path_delay_expression ','
+ path_delay_expression ',' path_delay_expression ',' path_delay_expression
+ | ',' path_delay_expression ',' path_delay_expression ','
+ path_delay_expression ',' path_delay_expression ',' path_delay_expression ','
+ path_delay_expression ',' path_delay_expression ',' path_delay_expression ','
+ path_delay_expression ',' path_delay_expression ',' path_delay_expression
+ ;
+
+parallel_path_description :
+ '(' specify_input_terminal_descriptor opt_polarity_operator '=' '>' specify_output_terminal_descriptor ')' ;
+
+full_path_description :
+ '(' list_of_path_inputs '*' '>' list_of_path_outputs ')' ;
+
+// This was broken into 2 rules to solve shift/reduce conflicts
+list_of_path_inputs :
+ specify_input_terminal_descriptor opt_polarity_operator |
+ specify_input_terminal_descriptor more_path_inputs opt_polarity_operator ;
+
+more_path_inputs :
+ ',' specify_input_terminal_descriptor |
+ more_path_inputs ',' specify_input_terminal_descriptor ;
+
+list_of_path_outputs :
+ specify_output_terminal_descriptor |
+ list_of_path_outputs ',' specify_output_terminal_descriptor ;
+
+opt_polarity_operator :
+ '+'
+ | '-'
+ | ;
+
+// Good enough for the time being
+specify_input_terminal_descriptor :
+ TOK_ID ;
+
+// Good enough for the time being
+specify_output_terminal_descriptor :
+ TOK_ID ;
+
+system_timing_declaration :
+ TOK_ID '(' system_timing_args ')' ';' ;
+
+system_timing_arg :
+ TOK_POSEDGE TOK_ID |
+ TOK_NEGEDGE TOK_ID |
+ expr ;
+
+system_timing_args :
+ system_timing_arg |
+ system_timing_args ',' system_timing_arg ;
+
+/*
+t_path_delay_expression :
+ path_delay_expression;
+
+trise_path_delay_expression :
+ path_delay_expression;
+
+tfall_path_delay_expression :
+ path_delay_expression;
+
+tz_path_delay_expression :
+ path_delay_expression;
+
+t01_path_delay_expression :
+ path_delay_expression;
+
+t10_path_delay_expression :
+ path_delay_expression;
+
+t0z_path_delay_expression :
+ path_delay_expression;
+
+tz1_path_delay_expression :
+ path_delay_expression;
+
+t1z_path_delay_expression :
+ path_delay_expression;
+
+tz0_path_delay_expression :
+ path_delay_expression;
+
+t0x_path_delay_expression :
+ path_delay_expression;
+
+tx1_path_delay_expression :
+ path_delay_expression;
+
+t1x_path_delay_expression :
+ path_delay_expression;
+
+tx0_path_delay_expression :
+ path_delay_expression;
+
+txz_path_delay_expression :
+ path_delay_expression;
+
+tzx_path_delay_expression :
+ path_delay_expression;
+*/
+
+path_delay_expression :
+ constant_expression;
+
+constant_mintypmax_expression :
+ constant_expression
+ | constant_expression ':' constant_expression ':' constant_expression
+ ;
+
+// for the time being this is OK, but we may write our own expr here.
+// as I'm not sure it is legal to use a full expr here (probably not)
+// On the other hand, other rules requiring constant expressions also use 'expr'
+// (such as param assignment), so we may leave this as-is, perhaps assing runtime checks for constant-ness
+constant_expression:
+ expr ;
+
param_signed:
TOK_SIGNED {
astbuf1->is_signed = true;
@@ -666,14 +898,13 @@ defparam_decl_list:
single_defparam_decl | defparam_decl_list ',' single_defparam_decl;
single_defparam_decl:
- range hierarchical_id '=' expr {
+ range rvalue '=' expr {
AstNode *node = new AstNode(AST_DEFPARAM);
- node->str = *$2;
+ node->children.push_back($2);
node->children.push_back($4);
if ($1 != NULL)
node->children.push_back($1);
ast_stack.back()->children.push_back(node);
- delete $2;
};
wire_decl:
@@ -731,7 +962,48 @@ wire_name_list:
wire_name_and_opt_assign | wire_name_list ',' wire_name_and_opt_assign;
wire_name_and_opt_assign:
- wire_name |
+ wire_name {
+ bool attr_anyconst = false;
+ bool attr_anyseq = false;
+ bool attr_allconst = false;
+ bool attr_allseq = false;
+ if (ast_stack.back()->children.back()->get_bool_attribute("\\anyconst")) {
+ delete ast_stack.back()->children.back()->attributes.at("\\anyconst");
+ ast_stack.back()->children.back()->attributes.erase("\\anyconst");
+ attr_anyconst = true;
+ }
+ if (ast_stack.back()->children.back()->get_bool_attribute("\\anyseq")) {
+ delete ast_stack.back()->children.back()->attributes.at("\\anyseq");
+ ast_stack.back()->children.back()->attributes.erase("\\anyseq");
+ attr_anyseq = true;
+ }
+ if (ast_stack.back()->children.back()->get_bool_attribute("\\allconst")) {
+ delete ast_stack.back()->children.back()->attributes.at("\\allconst");
+ ast_stack.back()->children.back()->attributes.erase("\\allconst");
+ attr_allconst = true;
+ }
+ if (ast_stack.back()->children.back()->get_bool_attribute("\\allseq")) {
+ delete ast_stack.back()->children.back()->attributes.at("\\allseq");
+ ast_stack.back()->children.back()->attributes.erase("\\allseq");
+ attr_allseq = true;
+ }
+ if (current_wire_rand || attr_anyconst || attr_anyseq || attr_allconst || attr_allseq) {
+ AstNode *wire = new AstNode(AST_IDENTIFIER);
+ AstNode *fcall = new AstNode(AST_FCALL);
+ wire->str = ast_stack.back()->children.back()->str;
+ fcall->str = current_wire_const ? "\\$anyconst" : "\\$anyseq";
+ if (attr_anyconst)
+ fcall->str = "\\$anyconst";
+ if (attr_anyseq)
+ fcall->str = "\\$anyseq";
+ if (attr_allconst)
+ fcall->str = "\\$allconst";
+ if (attr_allseq)
+ fcall->str = "\\$allseq";
+ fcall->attributes["\\reg"] = AstNode::mkconst_str(RTLIL::unescape_id(wire->str));
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, fcall));
+ }
+ } |
wire_name '=' expr {
AstNode *wire = new AstNode(AST_IDENTIFIER);
wire->str = ast_stack.back()->children.back()->str;
@@ -782,6 +1054,7 @@ wire_name:
node->port_id = current_function_or_task_port_id++;
}
ast_stack.back()->children.push_back(node);
+
delete $1;
};
@@ -994,18 +1267,45 @@ opt_label:
$$ = NULL;
};
+opt_property:
+ TOK_PROPERTY | /* empty */;
+
+opt_stmt_label:
+ TOK_ID ':' | /* empty */;
+
assert:
- TOK_ASSERT '(' expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $3));
+ opt_stmt_label TOK_ASSERT opt_property '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5));
+ } |
+ opt_stmt_label TOK_ASSUME opt_property '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5));
+ } |
+ opt_stmt_label TOK_ASSERT opt_property '(' TOK_EVENTUALLY expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6));
+ } |
+ opt_stmt_label TOK_ASSUME opt_property '(' TOK_EVENTUALLY expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6));
} |
- TOK_ASSUME '(' expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $3));
+ opt_stmt_label TOK_COVER opt_property '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_COVER, $5));
} |
- TOK_RESTRICT '(' expr ')' ';' {
+ opt_stmt_label TOK_COVER opt_property '(' ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_COVER, AstNode::mkconst_int(1, false)));
+ } |
+ opt_stmt_label TOK_COVER ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_COVER, AstNode::mkconst_int(1, false)));
+ } |
+ opt_stmt_label TOK_RESTRICT opt_property '(' expr ')' ';' {
if (norestrict_mode)
- delete $3;
+ delete $5;
+ else
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5));
+ } |
+ opt_stmt_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' {
+ if (norestrict_mode)
+ delete $6;
else
- ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $3));
+ ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6));
};
assert_property:
@@ -1015,11 +1315,26 @@ assert_property:
TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' {
ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4));
} |
+ TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $5));
+ } |
+ TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $5));
+ } |
+ TOK_COVER TOK_PROPERTY '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_COVER, $4));
+ } |
TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' {
if (norestrict_mode)
delete $4;
else
ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4));
+ } |
+ TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
+ if (norestrict_mode)
+ delete $5;
+ else
+ ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $5));
};
simple_behavioral_stmt:
@@ -1027,6 +1342,14 @@ simple_behavioral_stmt:
AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $4);
ast_stack.back()->children.push_back(node);
} |
+ lvalue TOK_INCREMENT {
+ AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, new AstNode(AST_ADD, $1->clone(), AstNode::mkconst_int(1, true)));
+ ast_stack.back()->children.push_back(node);
+ } |
+ lvalue TOK_DECREMENT {
+ AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, new AstNode(AST_SUB, $1->clone(), AstNode::mkconst_int(1, true)));
+ ast_stack.back()->children.push_back(node);
+ } |
lvalue OP_LE delay expr {
AstNode *node = new AstNode(AST_ASSIGN_LE, $1, $4);
ast_stack.back()->children.push_back(node);
@@ -1118,7 +1441,7 @@ behavioral_stmt:
ast_stack.pop_back();
ast_stack.pop_back();
} |
- attr case_type '(' expr ')' {
+ case_attr case_type '(' expr ')' {
AstNode *node = new AstNode(AST_CASE, $4);
ast_stack.back()->children.push_back(node);
ast_stack.push_back(node);
@@ -1128,6 +1451,23 @@ behavioral_stmt:
ast_stack.pop_back();
};
+unique_case_attr:
+ /* empty */ {
+ $$ = false;
+ } |
+ TOK_PRIORITY case_attr {
+ $$ = $2;
+ } |
+ TOK_UNIQUE case_attr {
+ $$ = true;
+ };
+
+case_attr:
+ attr unique_case_attr {
+ if ($2) (*$1)["\\parallel_case"] = AstNode::mkconst_int(1, false);
+ $$ = $1;
+ };
+
case_type:
TOK_CASE {
case_type_stack.push_back(0);
@@ -1162,7 +1502,7 @@ optional_else:
ast_stack.back()->children.push_back(cond);
ast_stack.push_back(block);
} behavioral_stmt |
- /* empty */;
+ /* empty */ %prec FAKE_THEN;
case_body:
case_body case_item |
@@ -1229,7 +1569,9 @@ rvalue:
$$ = new AstNode(AST_IDENTIFIER, $2);
$$->str = *$1;
delete $1;
- if ($2 == nullptr && formal_mode && ($$->str == "\\$initstate" || $$->str == "\\$anyconst" || $$->str == "\\$anyseq"))
+ if ($2 == nullptr && ($$->str == "\\$initstate" ||
+ $$->str == "\\$anyconst" || $$->str == "\\$anyseq" ||
+ $$->str == "\\$allconst" || $$->str == "\\$allseq"))
$$->type = AST_FCALL;
} |
hierarchical_id non_opt_multirange {
@@ -1333,7 +1675,7 @@ gen_stmt_or_null:
gen_stmt_block | ';';
opt_gen_else:
- TOK_ELSE gen_stmt_or_null | /* empty */;
+ TOK_ELSE gen_stmt_or_null | /* empty */ %prec FAKE_THEN;
expr:
basic_expr {
@@ -1351,7 +1693,7 @@ basic_expr:
rvalue {
$$ = $1;
} |
- '(' expr ')' TOK_CONST {
+ '(' expr ')' TOK_CONSTVAL {
if ($4->substr(0, 1) != "'")
frontend_verilog_yyerror("Syntax error.");
AstNode *bits = $2;
@@ -1361,7 +1703,7 @@ basic_expr:
$$ = new AstNode(AST_TO_BITS, bits, val);
delete $4;
} |
- hierarchical_id TOK_CONST {
+ hierarchical_id TOK_CONSTVAL {
if ($2->substr(0, 1) != "'")
frontend_verilog_yyerror("Syntax error.");
AstNode *bits = new AstNode(AST_IDENTIFIER);
@@ -1373,14 +1715,14 @@ basic_expr:
delete $1;
delete $2;
} |
- TOK_CONST TOK_CONST {
+ TOK_CONSTVAL TOK_CONSTVAL {
$$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
if ($$ == NULL || (*$2)[0] != '\'')
log_error("Value conversion failed: `%s%s'\n", $1->c_str(), $2->c_str());
delete $1;
delete $2;
} |
- TOK_CONST {
+ TOK_CONSTVAL {
$$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
if ($$ == NULL)
log_error("Value conversion failed: `%s'\n", $1->c_str());
@@ -1441,10 +1783,18 @@ basic_expr:
$$ = new AstNode(AST_BIT_AND, $1, $4);
append_attr($$, $3);
} |
+ basic_expr OP_NAND attr basic_expr {
+ $$ = new AstNode(AST_BIT_NOT, new AstNode(AST_BIT_AND, $1, $4));
+ append_attr($$, $3);
+ } |
basic_expr '|' attr basic_expr {
$$ = new AstNode(AST_BIT_OR, $1, $4);
append_attr($$, $3);
} |
+ basic_expr OP_NOR attr basic_expr {
+ $$ = new AstNode(AST_BIT_NOT, new AstNode(AST_BIT_OR, $1, $4));
+ append_attr($$, $3);
+ } |
basic_expr '^' attr basic_expr {
$$ = new AstNode(AST_BIT_XOR, $1, $4);
append_attr($$, $3);
diff --git a/frontends/vhdl2verilog/Makefile.inc b/frontends/vhdl2verilog/Makefile.inc
deleted file mode 100644
index 003d89c4..00000000
--- a/frontends/vhdl2verilog/Makefile.inc
+++ /dev/null
@@ -1 +0,0 @@
-OBJS += frontends/vhdl2verilog/vhdl2verilog.o
diff --git a/frontends/vhdl2verilog/vhdl2verilog.cc b/frontends/vhdl2verilog/vhdl2verilog.cc
deleted file mode 100644
index 6f9c0e3f..00000000
--- a/frontends/vhdl2verilog/vhdl2verilog.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * yosys -- Yosys Open SYnthesis Suite
- *
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#include "kernel/register.h"
-#include "kernel/sigtools.h"
-#include "kernel/log.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <limits.h>
-
-#ifndef _WIN32
-# include <unistd.h>
-# include <dirent.h>
-#endif
-
-YOSYS_NAMESPACE_BEGIN
-
-struct Vhdl2verilogPass : public Pass {
- Vhdl2verilogPass() : Pass("vhdl2verilog", "importing VHDL designs using vhdl2verilog") { }
- virtual void help()
- {
- // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
- log("\n");
- log(" vhdl2verilog [options] <vhdl-file>..\n");
- log("\n");
- log("This command reads VHDL source files using the 'vhdl2verilog' tool and the\n");
- log("Yosys Verilog frontend.\n");
- log("\n");
- log(" -out <out_file>\n");
- log(" do not import the vhdl2verilog output. instead write it to the\n");
- log(" specified file.\n");
- log("\n");
- log(" -vhdl2verilog_dir <directory>\n");
- log(" do use the specified vhdl2verilog installation. this is the directory\n");
- log(" that contains the setup_env.sh file. when this option is not present,\n");
- log(" it is assumed that vhdl2verilog is in the PATH environment variable.\n");
- log("\n");
- log(" -top <top-entity-name>\n");
- log(" The name of the top entity. This option is mandatory.\n");
- log("\n");
- log("The following options are passed as-is to vhdl2verilog:\n");
- log("\n");
- log(" -arch <architecture_name>\n");
- log(" -unroll_generate\n");
- log(" -nogenericeval\n");
- log(" -nouniquify\n");
- log(" -oldparser\n");
- log(" -suppress <list>\n");
- log(" -quiet\n");
- log(" -nobanner\n");
- log(" -mapfile <file>\n");
- log("\n");
- log("vhdl2verilog can be obtained from:\n");
- log("http://www.edautils.com/vhdl2verilog.html\n");
- log("\n");
- }
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
- {
- log_header(design, "Executing VHDL2VERILOG (importing VHDL designs using vhdl2verilog).\n");
- log_push();
-
- std::string out_file, top_entity;
- std::string vhdl2verilog_dir;
- std::string extra_opts;
-
- size_t argidx;
- for (argidx = 1; argidx < args.size(); argidx++) {
- if (args[argidx] == "-out" && argidx+1 < args.size()) {
- out_file = args[++argidx];
- continue;
- }
- if (args[argidx] == "-top" && argidx+1 < args.size()) {
- top_entity = args[++argidx];
- continue;
- }
- if (args[argidx] == "-vhdl2verilog_dir" && argidx+1 < args.size()) {
- vhdl2verilog_dir = args[++argidx];
- continue;
- }
- if ((args[argidx] == "-arch" || args[argidx] == "-suppress" || args[argidx] == "-mapfile") && argidx+1 < args.size()) {
- if (args[argidx] == "-mapfile" && !args[argidx+1].empty() && args[argidx+1][0] != '/') {
- char pwd[PATH_MAX];
- if (!getcwd(pwd, sizeof(pwd))) {
- log_cmd_error("getcwd failed: %s", strerror(errno));
- log_abort();
- }
- args[argidx+1] = pwd + ("/" + args[argidx+1]);
- }
- extra_opts += std::string(" ") + args[argidx];
- extra_opts += std::string(" '") + args[++argidx] + std::string("'");
- continue;
- }
- if (args[argidx] == "-unroll_generate" || args[argidx] == "-nogenericeval" || args[argidx] == "-nouniquify" ||
- args[argidx] == "-oldparser" || args[argidx] == "-quiet" || args[argidx] == "-nobanner") {
- extra_opts += std::string(" ") + args[argidx];
- continue;
- }
- break;
- }
-
- if (argidx == args.size())
- cmd_error(args, argidx, "Missing filenames.");
- if (args[argidx].substr(0, 1) == "-")
- cmd_error(args, argidx, "Unknown option.");
- if (top_entity.empty())
- log_cmd_error("Missing -top option.\n");
-
- std::string tempdir_name = make_temp_dir("/tmp/yosys-vhdl2verilog-XXXXXX");
- log("Using temp directory %s.\n", tempdir_name.c_str());
-
- if (!out_file.empty() && out_file[0] != '/') {
- char pwd[PATH_MAX];
- if (!getcwd(pwd, sizeof(pwd))) {
- log_cmd_error("getcwd failed: %s", strerror(errno));
- log_abort();
- }
- out_file = pwd + ("/" + out_file);
- }
-
- FILE *f = fopen(stringf("%s/files.list", tempdir_name.c_str()).c_str(), "wt");
- while (argidx < args.size()) {
- std::string file = args[argidx++];
- if (file.empty())
- continue;
- if (file[0] != '/') {
- char pwd[PATH_MAX];
- if (!getcwd(pwd, sizeof(pwd))) {
- log_cmd_error("getcwd failed: %s", strerror(errno));
- log_abort();
- }
- file = pwd + ("/" + file);
- }
- fprintf(f, "%s\n", file.c_str());
- log("Adding '%s' to the file list.\n", file.c_str());
- }
- fclose(f);
-
- std::string command = "exec 2>&1; ";
- if (!vhdl2verilog_dir.empty())
- command += stringf("cd '%s'; . ./setup_env.sh; ", vhdl2verilog_dir.c_str());
- command += stringf("cd '%s'; vhdl2verilog -out '%s' -filelist files.list -top '%s'%s", tempdir_name.c_str(),
- out_file.empty() ? "vhdl2verilog_output.v" : out_file.c_str(), top_entity.c_str(), extra_opts.c_str());
-
- log("Running '%s'..\n", command.c_str());
-
- int ret = run_command(command, [](const std::string &line) { log("%s", line.c_str()); });
- if (ret != 0)
- log_error("Execution of command \"%s\" failed: return code %d.\n", command.c_str(), ret);
-
- if (out_file.empty()) {
- std::ifstream ff;
- ff.open(stringf("%s/vhdl2verilog_output.v", tempdir_name.c_str()).c_str());
- if (ff.fail())
- log_error("Can't open vhdl2verilog output file `vhdl2verilog_output.v'.\n");
- Frontend::frontend_call(design, &ff, stringf("%s/vhdl2verilog_output.v", tempdir_name.c_str()), "verilog");
- }
-
- log_header(design, "Removing temp directory `%s':\n", tempdir_name.c_str());
- remove_directory(tempdir_name);
- log_pop();
- }
-} Vhdl2verilogPass;
-
-YOSYS_NAMESPACE_END
-