From 5033b51947a6ef02cb785b5622e993335efa750a Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Thu, 30 Aug 2018 20:46:20 +0200 Subject: New upstream version 0.7+20180830git0b7a184 --- frontends/ast/ast.cc | 94 +- frontends/ast/ast.h | 33 +- frontends/ast/genrtlil.cc | 209 ++-- frontends/ast/simplify.cc | 482 +++++--- frontends/blif/blifparse.cc | 159 ++- frontends/blif/blifparse.h | 3 +- frontends/ilang/.gitignore | 2 +- frontends/ilang/Makefile.inc | 7 +- frontends/ilang/ilang_frontend.cc | 4 +- frontends/ilang/ilang_lexer.l | 2 +- frontends/json/Makefile.inc | 3 + frontends/json/jsonparse.cc | 541 +++++++++ frontends/liberty/liberty.cc | 73 +- frontends/verific/Makefile.inc | 3 + frontends/verific/README | 62 + frontends/verific/build_amd64.txt | 30 - frontends/verific/example.sby | 16 + frontends/verific/example.sv | 18 + frontends/verific/test_navre.ys | 18 - frontends/verific/verific.cc | 1936 +++++++++++++++++++++++++++----- frontends/verific/verific.h | 106 ++ frontends/verific/verificsva.cc | 1814 ++++++++++++++++++++++++++++++ frontends/verilog/.gitignore | 2 +- frontends/verilog/Makefile.inc | 7 +- frontends/verilog/const2ast.cc | 8 +- frontends/verilog/preproc.cc | 189 +++- frontends/verilog/verilog_frontend.cc | 121 +- frontends/verilog/verilog_frontend.h | 3 +- frontends/verilog/verilog_lexer.l | 55 +- frontends/verilog/verilog_parser.y | 440 +++++++- frontends/vhdl2verilog/Makefile.inc | 1 - frontends/vhdl2verilog/vhdl2verilog.cc | 183 --- 32 files changed, 5619 insertions(+), 1005 deletions(-) create mode 100644 frontends/json/Makefile.inc create mode 100644 frontends/json/jsonparse.cc create mode 100644 frontends/verific/README delete mode 100644 frontends/verific/build_amd64.txt create mode 100644 frontends/verific/example.sby create mode 100644 frontends/verific/example.sv delete mode 100644 frontends/verific/test_navre.ys create mode 100644 frontends/verific/verific.h create mode 100644 frontends/verific/verificsva.cc delete mode 100644 frontends/vhdl2verilog/Makefile.inc delete mode 100644 frontends/vhdl2verilog/vhdl2verilog.cc (limited to 'frontends') 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 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 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 parameters) +RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict parameters, bool) { std::string stripped_name = name.str(); @@ -1105,7 +1123,10 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dictstr.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 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 @@ -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 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 parameters); - virtual RTLIL::Module *clone() const; + ~AstModule() YS_OVERRIDE; + RTLIL::IdString derive(RTLIL::Design *design, dict 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 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 dicttype != 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 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 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 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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 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("\\" + name.substr(0, pos), atoi(name.c_str() + pos+1)+1); + +failed: + return std::pair("\\" + 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 *obj_attributes = nullptr; dict *obj_parameters = nullptr; + dict> 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 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> 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 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 args, RTLIL::Design *design) + void execute(std::istream *&f, std::string filename, std::vector 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 args, RTLIL::Design *design) + void execute(std::istream *&f, std::string filename, std::vector 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 + * + * 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 data_array; + dict data_dict; + vector 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 &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 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 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 &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> &type_map, LibertyAst *ast) @@ -444,7 +452,7 @@ void parse_type_map(std::map> &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 \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 args, RTLIL::Design *design) + void execute(std::istream *&f, std::string filename, std::vector 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 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 #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 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; +} + +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); } -static void import_attributes(dict &attributes, DesignObj *obj) +void VerificImporter::import_attributes(dict &attributes, DesignObj *obj) { MapIter mi; Att *attr; @@ -74,44 +126,47 @@ static void import_attributes(dict &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_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_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_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_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_map, RTLIL::Module *module) +RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool *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_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::mapType() == 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_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::mapOutputSize(); 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::mapGetCin(); 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 &nl_todo, bool mode_gates) +void VerificImporter::merge_past_ffs_clock(pool &candidates, SigBit clock, bool clock_pol) +{ + bool keep_running = true; + SigMap sigmap; + + while (keep_running) + { + keep_running = false; + + dict> 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 &candidates) +{ + dict, pool> 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 &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_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::setBus()) 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::setport_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::setport_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::setfixup_ports(); + dict init_nets; + pool anyconst_nets, anyseq_nets; + pool 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::setwidth = 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::setName()); + 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 sva_asserts; + pool sva_assumes; + pool sva_covers; + pool sva_triggers; + + pool 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::setmemories.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> 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::setGetNet()); + 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); } } -} -#endif /* YOSYS_ENABLE_VERIFIC */ + if (!mode_nosva) + { + for (auto inst : sva_asserts) { + if (mode_autocover) + verific_import_sva_cover(this, inst); + verific_import_sva_assert(this, inst); + } -YOSYS_NAMESPACE_BEGIN + for (auto inst : sva_assumes) + verific_import_sva_assume(this, inst); -struct VerificPass : public Pass { - VerificPass() : Pass("verific", "load Verilog and VHDL designs using Verific") { } - virtual void help() - { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv} ..\n"); - log("\n"); - log("Load the specified Verilog/SystemVerilog files into Verific.\n"); - log("\n"); - log("\n"); - log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008} ..\n"); - log("\n"); - log("Load the specified VHDL files into Verific.\n"); - log("\n"); - log("\n"); - log(" verific -import [-gates] {-all | ..}\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("\n"); - log("Visit http://verific.com/ for more information on Verific.\n"); - log("\n"); + 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); } -#ifdef YOSYS_ENABLE_VERIFIC - virtual void execute(std::vector args, RTLIL::Design *design) - { - log_header(design, "Executing VERIFIC (loading Verilog and VHDL designs using Verific).\n"); +} - Message::SetConsoleOutput(0); - Message::RegisterCallBackMsg(msg_func); +// ================================================================== - 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; - } +VerificClocking::VerificClocking(VerificImporter *importer, Net *net, bool sva_at_only) +{ + module = importer->module; - 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; - } + log_assert(importer != nullptr); + log_assert(net != nullptr); - 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; - } + Instance *inst = net->Driver(); - 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 (inst != nullptr && inst->Type() == PRIM_SVA_AT) + { + net = inst->GetInput1(); + body_net = inst->GetInput2(); - 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; - } + inst = net->Driver(); - if (args.size() > 1 && args[1] == "-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)) - log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", args[argidx].c_str()); - return; + 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(); } - - if (args.size() > 1 && args[1] == "-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)) - log_cmd_error("Reading `%s' in VHDL_93 mode failed.\n", args[argidx].c_str()); + } + else + { + if (sva_at_only) return; - } + } - if (args.size() > 1 && args[1] == "-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)) + // 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_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> 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(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 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 */ + +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") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv} ..\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("Additional -D[=] 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 ..\n"); + log("\n"); + log("Like -sv, but define FORMAL instead of SYNTHESIS.\n"); + log("\n"); + log("\n"); + log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} ..\n"); + log("\n"); + log("Load the specified VHDL files into Verific.\n"); + log("\n"); + log("\n"); + log(" verific -work {-sv|-vhdl|...} \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 ..\n"); + log("\n"); + log("Add Verilog include directories.\n"); + log("\n"); + log("\n"); + log(" verific -vlog-libdir ..\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 [=]..\n"); + log("\n"); + log("Add Verilog defines.\n"); + log("\n"); + log("\n"); + log(" verific -vlog-undef ..\n"); + log("\n"); + log("Remove Verilog defines previously set with -vlog-define.\n"); + log("\n"); + log("\n"); + log(" verific -set-error ..\n"); + log(" verific -set-warning ..\n"); + log(" verific -set-info ..\n"); + log(" verific -set-ignore ..\n"); + log("\n"); + log("Set message severity. 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] ..\n"); + log("\n"); + log("Elaborate the design for the specified top modules, import to Yosys and\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 \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 + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + static bool set_verific_global_flags = true; + + if (check_noverific_env()) + log_cmd_error("This version of Yosys is built without Verific support.\n"); + + 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; + } + + 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 (GetSize(args) > argidx && args[argidx] == "-vlog-incdir") { + for (argidx++; argidx < GetSize(args); argidx++) + verific_incdirs.push_back(args[argidx]); + goto check_error; + } + + if (GetSize(args) > argidx && args[argidx] == "-vlog-libdir") { + for (argidx++; argidx < GetSize(args); argidx++) + verific_libdirs.push_back(args[argidx]); + goto check_error; + } + + 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 (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 (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()); + verific_import_pending = true; + goto check_error; + } + + if (GetSize(args) > argidx && args[argidx] == "-vhdl93") { + vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str()); + 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()); + verific_import_pending = true; + goto check_error; + } + + if (GetSize(args) > argidx && args[argidx] == "-vhdl2k") { + vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str()); + 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 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, RTLIL::Design *) { + void execute(std::vector, 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} ..\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[=] 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} ..\n"); + log("\n"); + log("Load the specified VHDL files. (Requires Verific.)\n"); + log("\n"); + log("\n"); + log(" read -define [=]..\n"); + log("\n"); + log("Set global Verilog/SystemVerilog defines.\n"); + log("\n"); + log("\n"); + log(" read -undef ..\n"); + log("\n"); + log("Unset global Verilog/SystemVerilog defines.\n"); + log("\n"); + log("\n"); + log(" read -incdir \n"); + log("\n"); + log("Add directory to global Verilog/SystemVerilog include directories.\n"); + log("\n"); + } + void execute(std::vector 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 + * + * 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 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 net_map; + std::map sva_posedge_map; + pool 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 &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 *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 &candidates, SigBit clock, bool clock_pol); + void merge_past_ffs(pool &candidates); + + void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set &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 + * + * 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> 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> edges; + vector 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, Const>> edges; + vector 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 disable_stack; + vector throughout_stack; + + int startNode, acceptNode, condNode; + vector nodes; + + vector unodes; + dict, SvaDFsmNode> dnodes; + dict, 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 &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 state_wire(GetSize(nodes)); + vector state_sig(GetSize(nodes)); + vector 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 node_order(GetSize(nodes)); + vector> 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 activate_sig(GetSize(nodes)); + vector 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 &vec) + { + vector 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 &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 &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 ctrl_bits; + + for (int i = 0; i < GetSize(dnode.ctrl); i++) + if (ctrl_val[i] == State::S1) + ctrl_bits.insert(dnode.ctrl[i]); + + vector 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 &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{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{startNode}) + dnode.statesig = module->Or(NEW_ID, dnode.statesig, trigger_sig); + } + + for (auto &it : dnodes) + { + SvaDFsmNode &dnode = it.second; + dict, vector> 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 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 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{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{startNode}) + output_fsm.createLink(output_start_node, dnode.outnode); + + if (output_accept_node >= 0) { + vector 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 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> 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 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 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 &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 &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 pre_defines_map, const std::list include_dirs) + +static bool try_expand_macro(std::set &defines_with_args, + std::map &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 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 &pre_defines_map, + dict> &global_defines_cache, const std::list &include_dirs) { std::set defines_with_args; std::map defines_map(pre_defines_map); + std::vector 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(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 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> 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 args, RTLIL::Design *design) + void execute(std::istream *&f, std::string filename, std::vector 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 defines_map; std::list 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 args, RTLIL::Design*) + void execute(std::vector 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 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(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(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 pre_defines_map, const std::list include_dirs); +std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map &pre_defines_map, + dict> &global_defines_cache, const std::list &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 *al) bool boolean; } -%token TOK_STRING TOK_ID TOK_CONST TOK_REALVAL TOK_PRIMITIVE +%token 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 range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int %type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list %type opt_label tok_prim_wrapper hierarchical_id -%type opt_signed -%type attr +%type opt_signed unique_case_attr +%type attr case_attr // operator precedence from low to high %left OP_LOR @@ -139,7 +142,9 @@ static void free_attr(std::map *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 - * - * 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 -#include -#include -#include -#include - -#ifndef _WIN32 -# include -# include -#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] ..\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 \n"); - log(" do not import the vhdl2verilog output. instead write it to the\n"); - log(" specified file.\n"); - log("\n"); - log(" -vhdl2verilog_dir \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 \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 \n"); - log(" -unroll_generate\n"); - log(" -nogenericeval\n"); - log(" -nouniquify\n"); - log(" -oldparser\n"); - log(" -suppress \n"); - log(" -quiet\n"); - log(" -nobanner\n"); - log(" -mapfile \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 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 - -- cgit v1.2.3