diff options
Diffstat (limited to 'frontends')
26 files changed, 4006 insertions, 773 deletions
diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc index 993ead928..91d917c91 100644 --- a/frontends/ast/Makefile.inc +++ b/frontends/ast/Makefile.inc @@ -2,4 +2,5 @@ OBJS += frontends/ast/ast.o OBJS += frontends/ast/simplify.o OBJS += frontends/ast/genrtlil.o +OBJS += frontends/ast/dpicall.o diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 96608ae37..1e43875ae 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -32,7 +32,9 @@ #include <sstream> #include <stdarg.h> -#include <assert.h> +#include <math.h> + +YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; @@ -46,11 +48,10 @@ namespace AST { // instanciate global variables (private API) namespace AST_INTERNAL { - bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells; + bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; AstNode *current_ast, *current_ast_mod; std::map<std::string, AstNode*> current_scope; - RTLIL::SigSpec *genRTLIL_subst_from = NULL; - RTLIL::SigSpec *genRTLIL_subst_to = NULL; + const std::map<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; RTLIL::SigSpec ignoreThisSignalsInInitial; AstNode *current_top_block, *current_block, *current_block_child; AstModule *current_module; @@ -67,6 +68,7 @@ std::string AST::type2str(AstNodeType type) X(AST_MODULE) X(AST_TASK) X(AST_FUNCTION) + X(AST_DPI_FUNCTION) X(AST_WIRE) X(AST_MEMORY) X(AST_AUTOWIRE) @@ -76,7 +78,9 @@ std::string AST::type2str(AstNodeType type) X(AST_PARASET) X(AST_ARGUMENT) X(AST_RANGE) + X(AST_MULTIRANGE) X(AST_CONSTANT) + X(AST_REALVALUE) X(AST_CELLTYPE) X(AST_IDENTIFIER) X(AST_PREFIX) @@ -127,6 +131,7 @@ std::string AST::type2str(AstNodeType type) X(AST_ASSIGN) X(AST_CELL) X(AST_PRIMITIVE) + X(AST_CELLARRAY) X(AST_ALWAYS) X(AST_INITIAL) X(AST_BLOCK) @@ -136,6 +141,8 @@ std::string AST::type2str(AstNodeType type) X(AST_COND) X(AST_DEFAULT) X(AST_FOR) + X(AST_WHILE) + X(AST_REPEAT) X(AST_GENVAR) X(AST_GENFOR) X(AST_GENIF) @@ -177,10 +184,12 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2) is_signed = false; is_string = false; range_valid = false; + range_swapped = false; port_id = 0; range_left = -1; range_right = 0; integer = 0; + realvalue = 0; id2ast = NULL; basic_prep = false; @@ -243,6 +252,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 (!str.empty()) fprintf(f, " str='%s'", str.c_str()); if (!bits.empty()) { @@ -265,9 +280,17 @@ void AstNode::dumpAst(FILE *f, std::string indent) if (port_id > 0) fprintf(f, " port=%d", port_id); if (range_valid || range_left != -1 || range_right != 0) - fprintf(f, " range=[%d:%d]%s", range_left, range_right, range_valid ? "" : "!"); + fprintf(f, " %srange=[%d:%d]%s", range_swapped ? "swapped_" : "", range_left, range_right, range_valid ? "" : "!"); if (integer != 0) fprintf(f, " int=%u", (int)integer); + if (realvalue != 0) + fprintf(f, " real=%e", realvalue); + if (!multirange_dimensions.empty()) { + fprintf(f, " multirange=["); + for (int v : multirange_dimensions) + fprintf(f, " %d", v); + fprintf(f, " ]"); + } fprintf(f, "\n"); for (auto &it : attributes) { @@ -309,7 +332,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) } for (auto &it : attributes) { - fprintf(f, "%s" "(* %s = ", indent.c_str(), id2vl(it.first).c_str()); + fprintf(f, "%s" "(* %s = ", indent.c_str(), id2vl(it.first.str()).c_str()); it.second->dumpVlog(f, ""); fprintf(f, " *)%s", indent.empty() ? "" : "\n"); } @@ -451,6 +474,10 @@ void AstNode::dumpVlog(FILE *f, std::string indent) fprintf(f, "%zd'b %s", bits.size(), RTLIL::Const(bits).as_string().c_str()); break; + case AST_REALVALUE: + fprintf(f, "%e", realvalue); + break; + case AST_BLOCK: if (children.size() == 1) { children[0]->dumpVlog(f, indent); @@ -603,6 +630,8 @@ bool AstNode::operator==(const AstNode &other) const return false; if (range_valid != other.range_valid) return false; + if (range_swapped != other.range_swapped) + return false; if (port_id != other.port_id) return false; if (range_left != other.range_left) @@ -694,6 +723,14 @@ AstNode *AstNode::mkconst_str(const std::string &str) return node; } +bool AstNode::bits_only_01() +{ + for (auto bit : bits) + if (bit != RTLIL::S0 && bit != RTLIL::S1) + return false; + return true; +} + RTLIL::Const AstNode::bitsAsConst(int width, bool is_signed) { std::vector<RTLIL::State> bits = this->bits; @@ -746,11 +783,94 @@ bool AstNode::asBool() return false; } +int AstNode::isConst() +{ + if (type == AST_CONSTANT) + return 1; + if (type == AST_REALVALUE) + return 2; + return 0; +} + +uint64_t AstNode::asInt(bool is_signed) +{ + if (type == AST_CONSTANT) + { + RTLIL::Const v = bitsAsConst(64, is_signed); + uint64_t ret = 0; + + for (int i = 0; i < 64; i++) + if (v.bits.at(i) == RTLIL::State::S1) + ret |= uint64_t(1) << i; + + return ret; + } + + if (type == AST_REALVALUE) + return realvalue; + + log_abort(); +} + +double AstNode::asReal(bool is_signed) +{ + if (type == AST_CONSTANT) + { + RTLIL::Const val(bits); + + bool is_negative = is_signed && val.bits.back() == RTLIL::State::S1; + if (is_negative) + val = const_neg(val, val, false, false, val.bits.size()); + + double v = 0; + for (size_t i = 0; i < val.bits.size(); i++) + // IEEE Std 1800-2012 Par 6.12.2: Individual bits that are x or z in + // the net or the variable shall be treated as zero upon conversion. + if (val.bits.at(i) == RTLIL::State::S1) + v += exp2(i); + if (is_negative) + v *= -1; + + return v; + } + + if (type == AST_REALVALUE) + return realvalue; + + log_abort(); +} + +RTLIL::Const AstNode::realAsConst(int width) +{ + double v = round(realvalue); + RTLIL::Const result; +#ifdef EMSCRIPTEN + if (!isfinite(v)) { +#else + if (!std::isfinite(v)) { +#endif + result.bits = std::vector<RTLIL::State>(width, RTLIL::State::Sx); + } else { + bool is_negative = v < 0; + if (is_negative) + v *= -1; + for (int i = 0; i < width; i++, v /= 2) + result.bits.push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0); + if (is_negative) + result = const_neg(result, result, false, false, result.bits.size()); + } + return result; +} + // create a new AstModule from an AST_MODULE AST node -static AstModule* process_module(AstNode *ast) +static AstModule* process_module(AstNode *ast, bool defer) { - assert(ast->type == AST_MODULE); - log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str()); + log_assert(ast->type == AST_MODULE); + + if (defer) + log("Storing AST representation for module `%s'.\n", ast->str.c_str()); + else + log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str()); current_module = new AstModule; current_module->ast = NULL; @@ -766,60 +886,63 @@ static AstModule* process_module(AstNode *ast) log("--- END OF AST DUMP ---\n"); } - while (ast->simplify(!flag_noopt, false, false, 0, -1, false)) { } + if (!defer) + { + while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { } - if (flag_dump_ast2) { - log("Dumping verilog AST after simplification:\n"); - ast->dumpAst(NULL, " "); - log("--- END OF AST DUMP ---\n"); - } + if (flag_dump_ast2) { + log("Dumping verilog AST after simplification:\n"); + ast->dumpAst(NULL, " "); + log("--- END OF AST DUMP ---\n"); + } - if (flag_dump_vlog) { - log("Dumping verilog AST (as requested by dump_vlog option):\n"); - ast->dumpVlog(NULL, " "); - log("--- END OF AST DUMP ---\n"); - } + if (flag_dump_vlog) { + log("Dumping verilog AST (as requested by dump_vlog option):\n"); + ast->dumpVlog(NULL, " "); + log("--- END OF AST DUMP ---\n"); + } - if (flag_lib) { - std::vector<AstNode*> new_children; - for (auto child : ast->children) { - if (child->type == AST_WIRE && (child->is_input || child->is_output)) - new_children.push_back(child); - else - delete child; + if (flag_lib) { + std::vector<AstNode*> new_children; + for (auto child : ast->children) { + if (child->type == AST_WIRE && (child->is_input || child->is_output)) + new_children.push_back(child); + else + delete child; + } + ast->children.swap(new_children); + ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false); } - ast->children.swap(new_children); - ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false); - } - ignoreThisSignalsInInitial = RTLIL::SigSpec(); + ignoreThisSignalsInInitial = RTLIL::SigSpec(); - 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); - current_module->attributes[attr.first] = attr.second->asAttrConst(); - } - for (size_t i = 0; i < ast->children.size(); i++) { - AstNode *node = ast->children[i]; - if (node->type == AST_WIRE || node->type == AST_MEMORY) - node->genRTLIL(); - } - for (size_t i = 0; i < ast->children.size(); i++) { - AstNode *node = ast->children[i]; - if (node->type != AST_WIRE && node->type != AST_MEMORY && node->type != AST_INITIAL) - node->genRTLIL(); - } + 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); + current_module->attributes[attr.first] = attr.second->asAttrConst(); + } + for (size_t i = 0; i < ast->children.size(); i++) { + AstNode *node = ast->children[i]; + if (node->type == AST_WIRE || node->type == AST_MEMORY) + node->genRTLIL(); + } + for (size_t i = 0; i < ast->children.size(); i++) { + AstNode *node = ast->children[i]; + if (node->type != AST_WIRE && node->type != AST_MEMORY && node->type != AST_INITIAL) + node->genRTLIL(); + } - ignoreThisSignalsInInitial.sort_and_unify(); + ignoreThisSignalsInInitial.sort_and_unify(); - for (size_t i = 0; i < ast->children.size(); i++) { - AstNode *node = ast->children[i]; - if (node->type == AST_INITIAL) - node->genRTLIL(); - } + for (size_t i = 0; i < ast->children.size(); i++) { + AstNode *node = ast->children[i]; + if (node->type == AST_INITIAL) + node->genRTLIL(); + } - ignoreThisSignalsInInitial = RTLIL::SigSpec(); + ignoreThisSignalsInInitial = RTLIL::SigSpec(); + } current_module->ast = ast_before_simplify; current_module->nolatches = flag_nolatches; @@ -828,11 +951,13 @@ static AstModule* process_module(AstNode *ast) current_module->lib = flag_lib; current_module->noopt = flag_noopt; current_module->icells = flag_icells; + current_module->autowire = flag_autowire; + current_module->fixup_ports(); return current_module; } // 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 nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef) +void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire) { current_ast = ast; flag_dump_ast1 = dump_ast1; @@ -844,18 +969,37 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump flag_lib = lib; flag_noopt = noopt; flag_icells = icells; + flag_autowire = autowire; + + std::vector<AstNode*> global_decls; - assert(current_ast->type == AST_DESIGN); - for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) { - if (design->modules.count((*it)->str) != 0) { - if (!ignore_redef) - log_error("Re-definition of module `%s' at %s:%d!\n", + 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) + (*it)->children.push_back(n->clone()); + + if (flag_icells && (*it)->str.substr(0, 2) == "\\$") + (*it)->str = (*it)->str.substr(1); + + if (defer) + (*it)->str = "$abstract" + (*it)->str; + + if (design->has((*it)->str)) { + if (!ignore_redef) + log_error("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); - log_error("Ignoring re-definition of module `%s' at %s:%d!\n", - (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); - continue; + continue; + } + + design->add(process_module(*it, defer)); } - design->modules[(*it)->str] = process_module(*it); + else + global_decls.push_back(*it); } } @@ -869,7 +1013,12 @@ AstModule::~AstModule() // create a new parametric module (when needed) and return the name of the generated module RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters) { - log_header("Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", name.c_str()); + std::string stripped_name = name.str(); + + if (stripped_name.substr(0, 9) == "$abstract") + stripped_name = stripped_name.substr(9); + + log_header("Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str()); current_ast = NULL; flag_dump_ast1 = false; @@ -881,16 +1030,14 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin flag_lib = lib; flag_noopt = noopt; flag_icells = icells; + flag_autowire = autowire; use_internal_line_num(); std::string para_info; - std::vector<unsigned char> hash_data; - hash_data.insert(hash_data.end(), name.begin(), name.end()); - hash_data.push_back(0); - AstNode *new_ast = ast->clone(); int para_counter = 0; + int orig_parameters_n = parameters.size(); for (auto it = new_ast->children.begin(); it != new_ast->children.end(); it++) { AstNode *child = *it; if (child->type != AST_PARAMETER) @@ -903,10 +1050,6 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id]))); delete child->children.at(0); child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0); - hash_data.insert(hash_data.end(), child->str.begin(), child->str.end()); - hash_data.push_back(0); - hash_data.insert(hash_data.end(), parameters[para_id].bits.begin(), parameters[para_id].bits.end()); - hash_data.push_back(0xff); parameters.erase(para_id); continue; } @@ -917,33 +1060,21 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin } } if (parameters.size() > 0) - log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), name.c_str()); + log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), stripped_name.c_str()); std::string modname; - if (para_info.size() > 60) - { - unsigned char hash[20]; - unsigned char *hash_data2 = new unsigned char[hash_data.size()]; - for (size_t i = 0; i < hash_data.size(); i++) - hash_data2[i] = hash_data[i]; - sha1::calc(hash_data2, hash_data.size(), hash); - delete[] hash_data2; - - char hexstring[41]; - sha1::toHexString(hash, hexstring); - - modname = "$paramod$" + std::string(hexstring) + name; - } + if (orig_parameters_n == 0) + modname = stripped_name; + else if (para_info.size() > 60) + modname = "$paramod$" + sha1(para_info) + stripped_name; else - { - modname = "$paramod" + name + para_info; - } + modname = "$paramod" + stripped_name + para_info; - if (design->modules.count(modname) == 0) { + if (!design->has(modname)) { new_ast->str = modname; - design->modules[modname] = process_module(new_ast); - design->modules[modname]->check(); + design->add(process_module(new_ast, false)); + design->module(modname)->check(); } else { log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); } @@ -955,6 +1086,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin RTLIL::Module *AstModule::clone() const { AstModule *new_mod = new AstModule; + new_mod->name = name; cloneInto(new_mod); new_mod->ast = ast->clone(); @@ -964,6 +1096,7 @@ RTLIL::Module *AstModule::clone() const new_mod->lib = lib; new_mod->noopt = noopt; new_mod->icells = icells; + new_mod->autowire = autowire; return new_mod; } @@ -986,3 +1119,5 @@ void AST::use_internal_line_num() get_line_num = &internal_get_line_num; } +YOSYS_NAMESPACE_END + diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 01702c3cf..0a4016736 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -33,6 +33,8 @@ #include <stdint.h> #include <set> +YOSYS_NAMESPACE_BEGIN + namespace AST { // all node types, type2str() must be extended @@ -44,6 +46,7 @@ namespace AST AST_MODULE, AST_TASK, AST_FUNCTION, + AST_DPI_FUNCTION, AST_WIRE, AST_MEMORY, @@ -54,7 +57,9 @@ namespace AST AST_PARASET, AST_ARGUMENT, AST_RANGE, + AST_MULTIRANGE, AST_CONSTANT, + AST_REALVALUE, AST_CELLTYPE, AST_IDENTIFIER, AST_PREFIX, @@ -107,6 +112,7 @@ namespace AST AST_ASSIGN, AST_CELL, AST_PRIMITIVE, + AST_CELLARRAY, AST_ALWAYS, AST_INITIAL, AST_BLOCK, @@ -116,6 +122,8 @@ namespace AST AST_COND, AST_DEFAULT, AST_FOR, + AST_WHILE, + AST_REPEAT, AST_GENVAR, AST_GENFOR, @@ -147,9 +155,13 @@ namespace AST // node content - most of it is unused in most node types std::string str; std::vector<RTLIL::State> bits; - bool is_input, is_output, is_reg, is_signed, is_string, range_valid; + bool is_input, is_output, is_reg, is_signed, is_string, range_valid, range_swapped; int port_id, range_left, range_right; uint32_t integer; + double realvalue; + + // if this is a multirange memory then this vector contains offset and length of each dimension + std::vector<int> multirange_dimensions; // this is set by simplify and used during RTLIL generation AstNode *id2ast; @@ -183,6 +195,7 @@ namespace AST MEM2REG_FL_SET_ELSE = 0x00000400, MEM2REG_FL_SET_ASYNC = 0x00000800, MEM2REG_FL_EQ2 = 0x00001000, + MEM2REG_FL_CMPLX_LHS = 0x00002000, /* proc flags */ MEM2REG_FL_EQ1 = 0x01000000, @@ -190,27 +203,33 @@ namespace AST // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc. // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL() - bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint); + bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param); void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map); - void replace_ids(std::map<std::string, std::string> &rules); + void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules); void mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> &mem2reg_places, std::map<AstNode*, uint32_t> &mem2reg_flags, std::map<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags); void mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block); void meminfo(int &mem_width, int &mem_size, int &addr_bits); + // additional functionality for evaluating constant functions + struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; }; + bool has_const_only_constructs(bool &recommend_const_eval); + void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall); + 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); // used by genRTLIL() for detecting expression width and sign - void detectSignWidthWorker(int &width_hint, bool &sign_hint); - void detectSignWidth(int &width_hint, bool &sign_hint); + void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL); + void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL); // create RTLIL code for this AST node // for expressions the resulting signal vector is returned // all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false); - RTLIL::SigSpec genWidthRTLIL(int width, RTLIL::SigSpec *subst_from = NULL, RTLIL::SigSpec *subst_to = NULL); + RTLIL::SigSpec genWidthRTLIL(int width, const std::map<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL); // compare AST nodes bool operator==(const AstNode &other) const; @@ -228,17 +247,24 @@ namespace AST RTLIL::Const bitsAsConst(int width = -1); RTLIL::Const asAttrConst(); RTLIL::Const asParaConst(); + uint64_t asInt(bool is_signed); + bool bits_only_01(); bool asBool(); + + // helper functions for real valued const eval + int isConst(); // 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 = false, bool dump_ast2 = false, bool dump_vlog = false, bool nolatches = false, bool nomem2reg = false, bool mem2reg = false, bool lib = false, bool noopt = false, bool icells = false, bool ignore_redef = false); + void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire); // parametric modules are supported directly by the AST library // therfore we need our own derivate of RTLIL::Module with overloaded virtual functions struct AstModule : RTLIL::Module { AstNode *ast; - bool nolatches, nomem2reg, mem2reg, lib, noopt, icells; + bool nolatches, nomem2reg, mem2reg, lib, noopt, icells, autowire; virtual ~AstModule(); virtual RTLIL::IdString derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters); virtual RTLIL::Module *clone() const; @@ -254,18 +280,24 @@ namespace AST // set set_line_num and get_line_num to internal dummy functions (done by simplify() and AstModule::derive // to control the filename and linenum properties of new nodes not generated by a frontend parser) void use_internal_line_num(); + + // call a DPI function + AstNode *dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args); } namespace AST_INTERNAL { // internal state variables - extern bool flag_dump_ast1, flag_dump_ast2, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells; + extern bool flag_dump_ast1, flag_dump_ast2, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; extern AST::AstNode *current_ast, *current_ast_mod; extern std::map<std::string, AST::AstNode*> current_scope; - extern RTLIL::SigSpec *genRTLIL_subst_from, *genRTLIL_subst_to, ignoreThisSignalsInInitial; + extern const std::map<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr; + extern RTLIL::SigSpec ignoreThisSignalsInInitial; extern AST::AstNode *current_top_block, *current_block, *current_block_child; extern AST::AstModule *current_module; struct ProcessGenerator; } +YOSYS_NAMESPACE_END + #endif diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc new file mode 100644 index 000000000..2eb104fa0 --- /dev/null +++ b/frontends/ast/dpicall.cc @@ -0,0 +1,140 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "ast.h" + +#ifdef YOSYS_ENABLE_PLUGINS + +#include <dlfcn.h> +#include <ffi.h> + +typedef void (*ffi_fptr) (); + +static ffi_fptr resolve_fn (std::string symbol_name) +{ + if (symbol_name.find(':') != std::string::npos) + { + int pos = symbol_name.find(':'); + std::string plugin_name = symbol_name.substr(0, pos); + std::string real_symbol_name = symbol_name.substr(pos+1); + + while (loaded_plugin_aliases.count(plugin_name)) + plugin_name = loaded_plugin_aliases.at(plugin_name); + + if (loaded_plugins.count(plugin_name) == 0) + log_error("unable to resolve '%s': can't find plugin `%s'\n", symbol_name.c_str(), plugin_name.c_str()); + + void *symbol = dlsym(loaded_plugins.at(plugin_name), real_symbol_name.c_str()); + + if (symbol == nullptr) + log_error("unable to resolve '%s': can't find symbol `%s' in plugin `%s'\n", + symbol_name.c_str(), real_symbol_name.c_str(), plugin_name.c_str()); + + return (ffi_fptr) symbol; + } + + for (auto &it : loaded_plugins) { + void *symbol = dlsym(it.second, symbol_name.c_str()); + if (symbol != nullptr) + return (ffi_fptr) symbol; + } + + void *symbol = dlsym(RTLD_DEFAULT, symbol_name.c_str()); + if (symbol != nullptr) + return (ffi_fptr) symbol; + + log_error("unable to resolve '%s'.\n", symbol_name.c_str()); +} + +AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args) +{ + AST::AstNode *newNode = nullptr; + union { double f64; float f32; int32_t i32; } value_store [args.size() + 1]; + ffi_type *types [args.size() + 1]; + void *values [args.size() + 1]; + ffi_cif cif; + int status; + + log("Calling DPI function `%s' and returning `%s':\n", fname.c_str(), rtype.c_str()); + + log_assert(SIZE(args) == SIZE(argtypes)); + for (int i = 0; i < SIZE(args); i++) { + if (argtypes[i] == "real") { + log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed)); + value_store[i].f64 = args[i]->asReal(args[i]->is_signed); + values[i] = &value_store[i].f64; + types[i] = &ffi_type_double; + } else if (argtypes[i] == "shortreal") { + log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed)); + value_store[i].f32 = args[i]->asReal(args[i]->is_signed); + values[i] = &value_store[i].f32; + types[i] = &ffi_type_double; + } else if (argtypes[i] == "integer") { + log(" arg %d (%s): %lld\n", i, argtypes[i].c_str(), (long long)args[i]->asInt(args[i]->is_signed)); + value_store[i].i32 = args[i]->asInt(args[i]->is_signed); + values[i] = &value_store[i].i32; + types[i] = &ffi_type_sint32; + } else { + log_error("invalid argtype '%s' for argument %d.\n", argtypes[i].c_str(), i); + } + } + + if (rtype == "integer") { + types[args.size()] = &ffi_type_slong; + values[args.size()] = &value_store[args.size()].i32; + } else if (rtype == "shortreal") { + types[args.size()] = &ffi_type_float; + values[args.size()] = &value_store[args.size()].f32; + } else if (rtype == "real") { + types[args.size()] = &ffi_type_double; + values[args.size()] = &value_store[args.size()].f64; + } else { + log_error("invalid rtype '%s'.\n", rtype.c_str()); + } + + if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types)) != FFI_OK) + log_error("ffi_prep_cif failed: status %d.\n", status); + + ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values); + + if (rtype == "real") { + newNode = new AstNode(AST_REALVALUE); + newNode->realvalue = value_store[args.size()].f64; + log(" return realvalue: %g\n", newNode->asReal(true)); + } else if (rtype == "shortreal") { + newNode = new AstNode(AST_REALVALUE); + newNode->realvalue = value_store[args.size()].f32; + log(" return realvalue: %g\n", newNode->asReal(true)); + } else { + newNode = AstNode::mkconst_int(value_store[args.size()].i32, false); + log(" return integer: %lld\n", (long long)newNode->asInt(true)); + } + + return newNode; +} + +#else /* YOSYS_ENABLE_PLUGINS */ + +AST::AstNode *AST::dpi_call(const std::string&, const std::string &fname, const std::vector<std::string>&, const std::vector<AstNode*>&) +{ + log_error("Can't call DPI function `%s': this version of yosys is built without plugin support\n", fname.c_str()); +} + +#endif /* YOSYS_ENABLE_PLUGINS */ + diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 591d027cb..f87a68f67 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -27,14 +27,16 @@ */ #include "kernel/log.h" +#include "kernel/utils.h" #include "libs/sha1/sha1.h" #include "ast.h" #include <sstream> #include <stdarg.h> -#include <assert.h> #include <algorithm> +YOSYS_NAMESPACE_BEGIN + using namespace AST; using namespace AST_INTERNAL; @@ -42,28 +44,13 @@ using namespace AST_INTERNAL; static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true) { std::stringstream sstr; - sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); + sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (autoidx++); - RTLIL::Cell *cell = new RTLIL::Cell; + RTLIL::Cell *cell = current_module->addCell(sstr.str(), type); cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); - cell->name = sstr.str(); - cell->type = type; - current_module->cells[cell->name] = cell; - RTLIL::Wire *wire = new RTLIL::Wire; + RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); - wire->name = cell->name + "_Y"; - wire->width = result_width; - current_module->wires[wire->name] = wire; - - RTLIL::SigChunk chunk; - chunk.wire = wire; - chunk.width = wire->width; - chunk.offset = 0; - - RTLIL::SigSpec sig; - sig.chunks.push_back(chunk); - sig.width = chunk.width; if (gen_attributes) for (auto &attr : that->attributes) { @@ -74,45 +61,30 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_wi } cell->parameters["\\A_SIGNED"] = RTLIL::Const(that->children[0]->is_signed); - cell->parameters["\\A_WIDTH"] = RTLIL::Const(arg.width); - cell->connections["\\A"] = arg; + cell->parameters["\\A_WIDTH"] = RTLIL::Const(arg.size()); + cell->setPort("\\A", arg); cell->parameters["\\Y_WIDTH"] = result_width; - cell->connections["\\Y"] = sig; - return sig; + cell->setPort("\\Y", wire); + return wire; } // helper function for extending bit width (preferred over SigSpec::extend() because of correct undef propagation in ConstEval) static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_signed) { - if (width <= sig.width) { + if (width <= sig.size()) { sig.extend(width, is_signed); return; } std::stringstream sstr; - sstr << "$extend" << "$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); + sstr << "$extend" << "$" << that->filename << ":" << that->linenum << "$" << (autoidx++); - RTLIL::Cell *cell = new RTLIL::Cell; + RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$pos"); cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); - cell->name = sstr.str(); - cell->type = "$pos"; - current_module->cells[cell->name] = cell; - RTLIL::Wire *wire = new RTLIL::Wire; + RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width); wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); - wire->name = cell->name + "_Y"; - wire->width = width; - current_module->wires[wire->name] = wire; - - RTLIL::SigChunk chunk; - chunk.wire = wire; - chunk.width = wire->width; - chunk.offset = 0; - - RTLIL::SigSpec new_sig; - new_sig.chunks.push_back(chunk); - new_sig.width = chunk.width; if (that != NULL) for (auto &attr : that->attributes) { @@ -123,40 +95,25 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s } cell->parameters["\\A_SIGNED"] = RTLIL::Const(is_signed); - cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig.width); - cell->connections["\\A"] = sig; + cell->parameters["\\A_WIDTH"] = RTLIL::Const(sig.size()); + cell->setPort("\\A", sig); cell->parameters["\\Y_WIDTH"] = width; - cell->connections["\\Y"] = new_sig; - sig = new_sig; + cell->setPort("\\Y", wire); + sig = wire; } // helper function for creating RTLIL code for binary operations static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { std::stringstream sstr; - sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); + sstr << type << "$" << that->filename << ":" << that->linenum << "$" << (autoidx++); - RTLIL::Cell *cell = new RTLIL::Cell; + RTLIL::Cell *cell = current_module->addCell(sstr.str(), type); cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); - cell->name = sstr.str(); - cell->type = type; - current_module->cells[cell->name] = cell; - RTLIL::Wire *wire = new RTLIL::Wire; + RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); - wire->name = cell->name + "_Y"; - wire->width = result_width; - current_module->wires[wire->name] = wire; - - RTLIL::SigChunk chunk; - chunk.wire = wire; - chunk.width = wire->width; - chunk.offset = 0; - - RTLIL::SigSpec sig; - sig.chunks.push_back(chunk); - sig.width = chunk.width; for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) @@ -168,45 +125,30 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_wi cell->parameters["\\A_SIGNED"] = RTLIL::Const(that->children[0]->is_signed); cell->parameters["\\B_SIGNED"] = RTLIL::Const(that->children[1]->is_signed); - cell->parameters["\\A_WIDTH"] = RTLIL::Const(left.width); - cell->parameters["\\B_WIDTH"] = RTLIL::Const(right.width); + cell->parameters["\\A_WIDTH"] = RTLIL::Const(left.size()); + cell->parameters["\\B_WIDTH"] = RTLIL::Const(right.size()); - cell->connections["\\A"] = left; - cell->connections["\\B"] = right; + cell->setPort("\\A", left); + cell->setPort("\\B", right); cell->parameters["\\Y_WIDTH"] = result_width; - cell->connections["\\Y"] = sig; - return sig; + cell->setPort("\\Y", wire); + return wire; } // helper function for creating RTLIL code for multiplexers static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { - assert(cond.width == 1); + log_assert(cond.size() == 1); std::stringstream sstr; - sstr << "$ternary$" << that->filename << ":" << that->linenum << "$" << (RTLIL::autoidx++); + sstr << "$ternary$" << that->filename << ":" << that->linenum << "$" << (autoidx++); - RTLIL::Cell *cell = new RTLIL::Cell; + RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$mux"); cell->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); - cell->name = sstr.str(); - cell->type = "$mux"; - current_module->cells[cell->name] = cell; - RTLIL::Wire *wire = new RTLIL::Wire; + RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size()); wire->attributes["\\src"] = stringf("%s:%d", that->filename.c_str(), that->linenum); - wire->name = cell->name + "_Y"; - wire->width = left.width; - current_module->wires[wire->name] = wire; - - RTLIL::SigChunk chunk; - chunk.wire = wire; - chunk.width = wire->width; - chunk.offset = 0; - - RTLIL::SigSpec sig; - sig.chunks.push_back(chunk); - sig.width = chunk.width; for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) @@ -215,14 +157,14 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const cell->attributes[attr.first] = attr.second->asAttrConst(); } - cell->parameters["\\WIDTH"] = RTLIL::Const(left.width); + cell->parameters["\\WIDTH"] = RTLIL::Const(left.size()); - cell->connections["\\A"] = right; - cell->connections["\\B"] = left; - cell->connections["\\S"] = cond; - cell->connections["\\Y"] = sig; + cell->setPort("\\A", right); + cell->setPort("\\B", left); + cell->setPort("\\S", cond); + cell->setPort("\\Y", wire); - return sig; + return wire; } // helper class for converting AST always nodes to RTLIL processes @@ -232,23 +174,23 @@ struct AST_INTERNAL::ProcessGenerator AstNode *always; RTLIL::SigSpec initSyncSignals; RTLIL::Process *proc; - const RTLIL::SigSpec &outputSignals; + RTLIL::SigSpec outputSignals; // This always points to the RTLIL::CaseRule beeing filled at the moment RTLIL::CaseRule *current_case; - // This two variables contain the replacement pattern to be used in the right hand side + // This map contains the replacement pattern to be used in the right hand side // of an assignment. E.g. in the code "foo = bar; foo = func(foo);" the foo in the right // hand side of the 2nd assignment needs to be replace with the temporary signal holding // the value assigned in the first assignment. So when the first assignement is processed // the according information is appended to subst_rvalue_from and subst_rvalue_to. - RTLIL::SigSpec subst_rvalue_from, subst_rvalue_to; + stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_rvalue_map; - // This two variables contain the replacement pattern to be used in the left hand side + // This map contains the replacement pattern to be used in the left hand side // of an assignment. E.g. in the code "always @(posedge clk) foo <= bar" the signal bar // should not be connected to the signal foo. Instead it must be connected to the temporary // signal that is used as input for the register that drives the signal foo. - RTLIL::SigSpec subst_lvalue_from, subst_lvalue_to; + stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_lvalue_map; // The code here generates a number of temprorary signal for each output register. This // map helps generating nice numbered names for all this temporary signals. @@ -257,12 +199,12 @@ struct AST_INTERNAL::ProcessGenerator // Buffer for generating the init action RTLIL::SigSpec init_lvalue, init_rvalue; - ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), outputSignals(subst_lvalue_from) + ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg) { // generate process and simple root case proc = new RTLIL::Process; proc->attributes["\\src"] = stringf("%s:%d", always->filename.c_str(), always->linenum); - proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->linenum, RTLIL::autoidx++); + 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", @@ -273,8 +215,10 @@ struct AST_INTERNAL::ProcessGenerator current_case = &proc->root_case; // create initial temporary signal for all output registers + RTLIL::SigSpec subst_lvalue_from, subst_lvalue_to; collect_lvalues(subst_lvalue_from, always, true, true); subst_lvalue_to = new_temp_signal(subst_lvalue_from); + subst_lvalue_map = subst_lvalue_from.to_sigbit_map(subst_lvalue_to); bool found_anyedge_syncs = false; for (auto child : always->children) @@ -310,8 +254,7 @@ struct AST_INTERNAL::ProcessGenerator // create initial assignments for the temporary signals if ((flag_nolatches || always->get_bool_attribute("\\nolatches") || current_module->get_bool_attribute("\\nolatches")) && !found_clocked_sync) { - subst_rvalue_from = subst_lvalue_from; - subst_rvalue_to = RTLIL::SigSpec(RTLIL::State::Sx, subst_rvalue_from.width); + subst_rvalue_map = subst_lvalue_from.to_sigbit_map(RTLIL::SigSpec(RTLIL::State::Sx, SIZE(subst_lvalue_from))); } else { addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from); } @@ -321,51 +264,70 @@ struct AST_INTERNAL::ProcessGenerator if (child->type == AST_BLOCK) processAst(child); - if (initSyncSignals.width > 0) + if (initSyncSignals.size() > 0) { RTLIL::SyncRule *sync = new RTLIL::SyncRule; sync->type = RTLIL::SyncType::STi; proc->syncs.push_back(sync); - assert(init_lvalue.width == init_rvalue.width); - init_lvalue.optimize(); - init_rvalue.optimize(); + log_assert(init_lvalue.size() == init_rvalue.size()); int offset = 0; - for (size_t i = 0; i < init_lvalue.chunks.size(); i++) { - RTLIL::SigSpec lhs = init_lvalue.chunks[i]; - RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue.chunks[i].width); + for (auto &init_lvalue_c : init_lvalue.chunks()) { + RTLIL::SigSpec lhs = init_lvalue_c; + RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue_c.width); + remove_unwanted_lvalue_bits(lhs, rhs); sync->actions.push_back(RTLIL::SigSig(lhs, rhs)); - offset += lhs.width; + offset += lhs.size(); } } + + outputSignals = RTLIL::SigSpec(subst_lvalue_from); + } + + void remove_unwanted_lvalue_bits(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) + { + RTLIL::SigSpec new_lhs, new_rhs; + + log_assert(SIZE(lhs) == SIZE(rhs)); + for (int i = 0; i < SIZE(lhs); i++) { + if (lhs[i].wire == nullptr) + continue; + new_lhs.append(lhs[i]); + new_rhs.append(rhs[i]); + } + + lhs = new_lhs; + rhs = new_rhs; } // create new temporary signals RTLIL::SigSpec new_temp_signal(RTLIL::SigSpec sig) { - sig.optimize(); - for (size_t i = 0; i < sig.chunks.size(); i++) + std::vector<RTLIL::SigChunk> chunks = sig.chunks(); + + for (int i = 0; i < SIZE(chunks); i++) { - RTLIL::SigChunk &chunk = sig.chunks[i]; + RTLIL::SigChunk &chunk = chunks[i]; if (chunk.wire == NULL) continue; - RTLIL::Wire *wire = new RTLIL::Wire; - wire->attributes["\\src"] = stringf("%s:%d", always->filename.c_str(), always->linenum); + std::string wire_name; do { - wire->name = stringf("$%d%s[%d:%d]", new_temp_count[chunk.wire]++, + wire_name = stringf("$%d%s[%d:%d]", new_temp_count[chunk.wire]++, chunk.wire->name.c_str(), chunk.width+chunk.offset-1, chunk.offset);; - if (chunk.wire->name.find('$') != std::string::npos) - wire->name += stringf("$%d", RTLIL::autoidx++); - } while (current_module->wires.count(wire->name) > 0); - wire->width = chunk.width; - current_module->wires[wire->name] = wire; + if (chunk.wire->name.str().find('$') != std::string::npos) + wire_name += stringf("$%d", autoidx++); + } while (current_module->wires_.count(wire_name) > 0); + + RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width); + wire->attributes["\\src"] = stringf("%s:%d", always->filename.c_str(), always->linenum); chunk.wire = wire; chunk.offset = 0; } - return sig; + + return chunks; } // recursively traverse the AST an collect all assigned signals @@ -376,7 +338,7 @@ struct AST_INTERNAL::ProcessGenerator case AST_CASE: for (auto child : ast->children) if (child != ast->children[0]) { - assert(child->type == AST_COND); + log_assert(child->type == AST_COND); collect_lvalues(reg, child, type_eq, type_le, false); } break; @@ -401,18 +363,23 @@ struct AST_INTERNAL::ProcessGenerator break; default: - assert(0); + log_abort(); } - if (run_sort_and_unify) - reg.sort_and_unify(); + if (run_sort_and_unify) { + std::set<RTLIL::SigBit> sorted_reg; + for (auto bit : reg) + if (bit.wire) + sorted_reg.insert(bit); + reg = RTLIL::SigSpec(sorted_reg); + } } // remove all assignments to the given signal pattern in a case and all its children. // e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this // function is called to clean up the first two assignments as they are overwritten by // the third assignment. - void removeSignalFromCaseTree(RTLIL::SigSpec pattern, RTLIL::CaseRule *cs) + void removeSignalFromCaseTree(const std::set<RTLIL::SigBit> &pattern, RTLIL::CaseRule *cs) { for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) it->first.remove2(pattern, &it->second); @@ -426,23 +393,22 @@ struct AST_INTERNAL::ProcessGenerator // are avoided and the generated $mux cells have a more "natural" size. void addChunkActions(std::vector<RTLIL::SigSig> &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, bool inSyncRule = false) { - if (inSyncRule && initSyncSignals.width > 0) { + if (inSyncRule && initSyncSignals.size() > 0) { init_lvalue.append(lvalue.extract(initSyncSignals)); init_rvalue.append(lvalue.extract(initSyncSignals, &rvalue)); lvalue.remove2(initSyncSignals, &rvalue); } - assert(lvalue.width == rvalue.width); - lvalue.optimize(); - rvalue.optimize(); + log_assert(lvalue.size() == rvalue.size()); int offset = 0; - for (size_t i = 0; i < lvalue.chunks.size(); i++) { - RTLIL::SigSpec lhs = lvalue.chunks[i]; - RTLIL::SigSpec rhs = rvalue.extract(offset, lvalue.chunks[i].width); - if (inSyncRule && lvalue.chunks[i].wire && lvalue.chunks[i].wire->get_bool_attribute("\\nosync")) - rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.width); + for (auto &lvalue_c : lvalue.chunks()) { + RTLIL::SigSpec lhs = lvalue_c; + RTLIL::SigSpec rhs = rvalue.extract(offset, lvalue_c.width); + if (inSyncRule && lvalue_c.wire && lvalue_c.wire->get_bool_attribute("\\nosync")) + rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.size()); + remove_unwanted_lvalue_bits(lhs, rhs); actions.push_back(RTLIL::SigSig(lhs, rhs)); - offset += lhs.width; + offset += lhs.size(); } } @@ -460,18 +426,16 @@ struct AST_INTERNAL::ProcessGenerator case AST_ASSIGN_LE: { RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue; - RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.width, &subst_rvalue_from, &subst_rvalue_to); - lvalue.replace(subst_lvalue_from, subst_lvalue_to); + RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), &subst_rvalue_map.stdmap()); + lvalue.replace(subst_lvalue_map.stdmap()); if (ast->type == AST_ASSIGN_EQ) { - subst_rvalue_from.remove2(unmapped_lvalue, &subst_rvalue_to); - subst_rvalue_from.append(unmapped_lvalue); - subst_rvalue_from.optimize(); - subst_rvalue_to.append(rvalue); - subst_rvalue_to.optimize(); + for (int i = 0; i < SIZE(unmapped_lvalue); i++) + subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]); } - removeSignalFromCaseTree(lvalue, current_case); + removeSignalFromCaseTree(lvalue.to_sigbit_set(), current_case); + remove_unwanted_lvalue_bits(lvalue, rvalue); current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue)); } break; @@ -479,7 +443,7 @@ struct AST_INTERNAL::ProcessGenerator case AST_CASE: { RTLIL::SwitchRule *sw = new RTLIL::SwitchRule; - sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_from, &subst_rvalue_to); + sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap()); current_case->switches.push_back(sw); for (auto &attr : ast->attributes) { @@ -495,13 +459,7 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::SigSpec this_case_eq_ltemp = new_temp_signal(this_case_eq_lvalue); RTLIL::SigSpec this_case_eq_rvalue = this_case_eq_lvalue; - this_case_eq_rvalue.replace(subst_rvalue_from, subst_rvalue_to); - - RTLIL::SigSpec backup_subst_lvalue_from = subst_lvalue_from; - RTLIL::SigSpec backup_subst_lvalue_to = subst_lvalue_to; - - RTLIL::SigSpec backup_subst_rvalue_from = subst_rvalue_from; - RTLIL::SigSpec backup_subst_rvalue_to = subst_rvalue_to; + this_case_eq_rvalue.replace(subst_rvalue_map.stdmap()); RTLIL::CaseRule *default_case = NULL; RTLIL::CaseRule *last_generated_case = NULL; @@ -509,19 +467,13 @@ struct AST_INTERNAL::ProcessGenerator { if (child == ast->children[0]) continue; - assert(child->type == AST_COND); - - subst_lvalue_from = backup_subst_lvalue_from; - subst_lvalue_to = backup_subst_lvalue_to; + log_assert(child->type == AST_COND); - subst_rvalue_from = backup_subst_rvalue_from; - subst_rvalue_to = backup_subst_rvalue_to; + subst_lvalue_map.save(); + subst_rvalue_map.save(); - subst_lvalue_from.remove2(this_case_eq_lvalue, &subst_lvalue_to); - subst_lvalue_from.append(this_case_eq_lvalue); - subst_lvalue_from.optimize(); - subst_lvalue_to.append(this_case_eq_ltemp); - subst_lvalue_to.optimize(); + for (int i = 0; i < SIZE(this_case_eq_lvalue); i++) + subst_lvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); RTLIL::CaseRule *backup_case = current_case; current_case = new RTLIL::CaseRule; @@ -533,13 +485,16 @@ struct AST_INTERNAL::ProcessGenerator else if (node->type == AST_BLOCK) processAst(node); else - current_case->compare.push_back(node->genWidthRTLIL(sw->signal.width, &subst_rvalue_from, &subst_rvalue_to)); + current_case->compare.push_back(node->genWidthRTLIL(sw->signal.size(), &subst_rvalue_map.stdmap())); } if (default_case != current_case) sw->cases.push_back(current_case); else log_assert(current_case->compare.size() == 0); current_case = backup_case; + + subst_lvalue_map.restore(); + subst_rvalue_map.restore(); } if (last_generated_case != NULL && ast->get_bool_attribute("\\full_case") && default_case == NULL) { @@ -552,20 +507,11 @@ struct AST_INTERNAL::ProcessGenerator sw->cases.push_back(default_case); } - subst_lvalue_from = backup_subst_lvalue_from; - subst_lvalue_to = backup_subst_lvalue_to; - - subst_rvalue_from = backup_subst_rvalue_from; - subst_rvalue_to = backup_subst_rvalue_to; + for (int i = 0; i < SIZE(this_case_eq_lvalue); i++) + subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); - subst_rvalue_from.remove2(this_case_eq_lvalue, &subst_rvalue_to); - subst_rvalue_from.append(this_case_eq_lvalue); - subst_rvalue_from.optimize(); - subst_rvalue_to.append(this_case_eq_ltemp); - subst_rvalue_to.optimize(); - - this_case_eq_lvalue.replace(subst_lvalue_from, subst_lvalue_to); - removeSignalFromCaseTree(this_case_eq_lvalue, current_case); + this_case_eq_lvalue.replace(subst_lvalue_map.stdmap()); + removeSignalFromCaseTree(this_case_eq_lvalue.to_sigbit_set(), current_case); addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp); } break; @@ -579,13 +525,13 @@ struct AST_INTERNAL::ProcessGenerator break; default: - assert(0); + log_abort(); } } }; // detect sign and width of an expression -void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) +void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real) { std::string type_name; bool sub_sign_hint = true; @@ -594,6 +540,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) AstNode *range = NULL; AstNode *id_ast = NULL; + bool local_found_real = false; + if (found_real == NULL) + found_real = &local_found_real; + switch (type) { case AST_CONSTANT: @@ -602,6 +552,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) sign_hint = false; break; + case AST_REALVALUE: + *found_real = true; + width_hint = std::max(width_hint, 32); + break; + case AST_IDENTIFIER: id_ast = id2ast; if (id_ast == NULL && current_scope.count(str)) @@ -623,10 +578,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) if (id_ast->type == AST_AUTOWIRE) this_width = 1; else { - // current_ast_mod->dumpAst(stdout, ""); - // printf("---\n"); - // dumpAst(stdout, ""); - // fflush(stdout); + // current_ast_mod->dumpAst(NULL, "mod> "); + // log("---\n"); + // id_ast->dumpAst(NULL, "decl> "); + // dumpAst(NULL, "ref> "); log_error("Failed to detect with of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); } } else { @@ -648,8 +603,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) else if (!range->range_valid) { AstNode *left_at_zero_ast = children[0]->children[0]->clone(); AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone(); - while (left_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } - while (right_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } + 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); @@ -658,14 +613,15 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) delete right_at_zero_ast; } else this_width = range->range_left - range->range_right + 1; - } else - width_hint = std::max(width_hint, this_width); + sign_hint = false; + } + width_hint = std::max(width_hint, this_width); if (!id_ast->is_signed) sign_hint = false; break; case AST_TO_BITS: - while (children[0]->simplify(true, false, false, 1, -1, false) == true) { } + 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); children[1]->detectSignWidthWorker(sub_width_hint, sign_hint); @@ -693,7 +649,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) break; case AST_REPLICATE: - while (children[0]->simplify(true, false, false, 1, -1, false) == true) { } + 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); children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint); @@ -704,7 +660,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) case AST_NEG: case AST_BIT_NOT: case AST_POS: - children[0]->detectSignWidthWorker(width_hint, sign_hint); + children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real); break; case AST_BIT_AND: @@ -712,7 +668,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) case AST_BIT_XOR: case AST_BIT_XNOR: for (auto child : children) - child->detectSignWidthWorker(width_hint, sign_hint); + child->detectSignWidthWorker(width_hint, sign_hint, found_real); break; case AST_REDUCE_AND: @@ -729,7 +685,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) case AST_SHIFT_SLEFT: case AST_SHIFT_SRIGHT: case AST_POW: - children[0]->detectSignWidthWorker(width_hint, sign_hint); + children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real); break; case AST_LT: @@ -750,7 +706,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) case AST_DIV: case AST_MOD: for (auto child : children) - child->detectSignWidthWorker(width_hint, sign_hint); + child->detectSignWidthWorker(width_hint, sign_hint, found_real); break; case AST_LOGIC_AND: @@ -761,8 +717,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) break; case AST_TERNARY: - children.at(1)->detectSignWidthWorker(width_hint, sign_hint); - children.at(2)->detectSignWidthWorker(width_hint, sign_hint); + children.at(1)->detectSignWidthWorker(width_hint, sign_hint, found_real); + children.at(2)->detectSignWidthWorker(width_hint, sign_hint, found_real); break; case AST_MEMRD: @@ -781,13 +737,19 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint) 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); } + + if (*found_real) + sign_hint = true; } // detect sign and width of an expression -void AstNode::detectSignWidth(int &width_hint, bool &sign_hint) +void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real) { - width_hint = -1, sign_hint = true; - detectSignWidthWorker(width_hint, sign_hint); + width_hint = -1; + sign_hint = true; + if (found_real) + *found_real = false; + detectSignWidthWorker(width_hint, sign_hint, found_real); } // create RTLIL from an AST node @@ -815,6 +777,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // and are only accessed here thru this references case AST_TASK: case AST_FUNCTION: + case AST_DPI_FUNCTION: case AST_AUTOWIRE: case AST_LOCALPARAM: case AST_DEFPARAM: @@ -832,28 +795,22 @@ 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) + 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); if (!range_valid) log_error("Signal `%s' with non-constant width at %s:%d!\n", str.c_str(), filename.c_str(), linenum); - if (range_left < range_right && (range_left != -1 || range_right != 0)) { - int tmp = range_left; - range_left = range_right; - range_right = tmp; - } + log_assert(range_left >= range_right || (range_left == -1 && range_right == 0)); - RTLIL::Wire *wire = new RTLIL::Wire; + RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1); wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - wire->name = str; - wire->width = range_left - range_right + 1; wire->start_offset = range_right; wire->port_id = port_id; wire->port_input = is_input; wire->port_output = is_output; - current_module->wires[wire->name] = wire; + wire->upto = range_swapped; for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) @@ -870,9 +827,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_error("Re-definition of memory `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); - assert(children.size() >= 2); - assert(children[0]->type == AST_RANGE); - assert(children[1]->type == AST_RANGE); + 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", @@ -905,17 +862,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (width_hint < 0) detectSignWidth(width_hint, sign_hint); - RTLIL::SigChunk chunk; - chunk.wire = NULL; - chunk.data.bits = bits; - chunk.width = bits.size(); - chunk.offset = 0; - - RTLIL::SigSpec sig; - sig.chunks.push_back(chunk); - sig.width = chunk.width; - is_signed = sign_hint; + return RTLIL::SigSpec(bitsAsConst()); + } + + 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); return sig; } @@ -927,12 +882,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::Wire *wire = NULL; RTLIL::SigChunk chunk; - if (id2ast && id2ast->type == AST_AUTOWIRE && current_module->wires.count(str) == 0) { - RTLIL::Wire *wire = new RTLIL::Wire; + int add_undef_bits_msb = 0; + int add_undef_bits_lsb = 0; + + if (id2ast && id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) { + RTLIL::Wire *wire = current_module->addWire(str); wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); wire->name = str; - log("Warning: Identifier `%s' is implicitly declared at %s:%d.\n", str.c_str(), filename.c_str(), linenum); - current_module->wires[str] = wire; + if (flag_autowire) + log("Warning: Identifier `%s' is implicitly declared at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + 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); } else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) { if (id2ast->children[0]->type != AST_CONSTANT) @@ -942,7 +902,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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) + 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); @@ -950,19 +910,21 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_error("Identifier `%s' does map to an unexpanded memory at %s:%d!\n", str.c_str(), filename.c_str(), linenum); - wire = current_module->wires[str]; + wire = current_module->wires_[str]; chunk.wire = wire; chunk.width = wire->width; chunk.offset = 0; use_const_chunk: if (children.size() != 0) { - assert(children[0]->type == AST_RANGE); + log_assert(children[0]->type == AST_RANGE); + int source_width = id2ast->range_left - id2ast->range_right + 1; + int source_offset = id2ast->range_right; if (!children[0]->range_valid) { AstNode *left_at_zero_ast = children[0]->children[0]->clone(); AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone(); - while (left_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } - while (right_at_zero_ast->simplify(true, true, false, 1, -1, false)) { } + 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); @@ -970,27 +932,59 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : children[0]->children[0]->clone()); fake_ast->children[0]->delete_children(); - RTLIL::SigSpec sig = binop2rtlil(fake_ast, "$shr", width, - fake_ast->children[0]->genRTLIL(), fake_ast->children[1]->genRTLIL()); + RTLIL::SigSpec shift_val = fake_ast->children[1]->genRTLIL(); + if (id2ast->range_right != 0) { + shift_val = current_module->Sub(NEW_ID, shift_val, id2ast->range_right, fake_ast->children[1]->is_signed); + fake_ast->children[1]->is_signed = true; + } + if (id2ast->range_swapped) { + shift_val = current_module->Sub(NEW_ID, RTLIL::SigSpec(source_width - width), shift_val, fake_ast->children[1]->is_signed); + fake_ast->children[1]->is_signed = true; + } + if (SIZE(shift_val) >= 32) + fake_ast->children[1]->is_signed = true; + RTLIL::SigSpec sig = binop2rtlil(fake_ast, "$shiftx", width, fake_ast->children[0]->genRTLIL(), shift_val); delete left_at_zero_ast; delete right_at_zero_ast; delete fake_ast; return sig; } else { - chunk.offset = children[0]->range_right - id2ast->range_right; chunk.width = children[0]->range_left - children[0]->range_right + 1; - if (children[0]->range_left > id2ast->range_left || id2ast->range_right > children[0]->range_right) - log_error("Range select out of bounds on signal `%s' at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + chunk.offset = children[0]->range_right - source_offset; + if (id2ast->range_swapped) + 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); + 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); + chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width); + } else { + if (chunk.width + chunk.offset > source_width) { + add_undef_bits_msb = (chunk.width + chunk.offset) - source_width; + chunk.width -= add_undef_bits_msb; + } + if (chunk.offset < 0) { + add_undef_bits_lsb = -chunk.offset; + chunk.width -= add_undef_bits_lsb; + 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); + 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); + } } } - RTLIL::SigSpec sig; - sig.chunks.push_back(chunk); - sig.width = chunk.width; + RTLIL::SigSpec sig = { RTLIL::SigSpec(RTLIL::State::Sx, add_undef_bits_msb), chunk, RTLIL::SigSpec(RTLIL::State::Sx, add_undef_bits_lsb) }; - if (genRTLIL_subst_from && genRTLIL_subst_to) - sig.replace(*genRTLIL_subst_from, *genRTLIL_subst_to); + if (genRTLIL_subst_ptr) + sig.replace(*genRTLIL_subst_ptr); is_signed = children.size() > 0 ? false : id2ast->is_signed && sign_hint; return sig; @@ -1000,7 +994,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_TO_SIGNED: case AST_TO_UNSIGNED: { RTLIL::SigSpec sig = children[0]->genRTLIL(); - if (sig.width < width_hint) + if (sig.size() < width_hint) sig.extend_u0(width_hint, sign_hint); is_signed = sign_hint; return sig; @@ -1009,15 +1003,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // concatenation of signals can be done directly using RTLIL::SigSpec case AST_CONCAT: { RTLIL::SigSpec sig; - sig.width = 0; - for (auto it = children.begin(); it != children.end(); it++) { - RTLIL::SigSpec s = (*it)->genRTLIL(); - for (size_t i = 0; i < s.chunks.size(); i++) { - sig.chunks.push_back(s.chunks[i]); - sig.width += s.chunks[i].width; - } - } - if (sig.width < width_hint) + for (auto it = children.begin(); it != children.end(); it++) + sig.append((*it)->genRTLIL()); + if (sig.size() < width_hint) sig.extend_u0(width_hint, false); return sig; } @@ -1032,7 +1020,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::SigSpec sig; for (int i = 0; i < count; i++) sig.append(right); - if (sig.width < width_hint) + if (sig.size() < width_hint) sig.extend_u0(width_hint, false); is_signed = false; return sig; @@ -1045,7 +1033,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { RTLIL::SigSpec arg = children[0]->genRTLIL(width_hint, sign_hint); is_signed = children[0]->is_signed; - int width = arg.width; + int width = arg.size(); if (width_hint > 0) { width = width_hint; widthExtend(this, arg, width, is_signed); @@ -1063,7 +1051,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) detectSignWidth(width_hint, sign_hint); RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint); - int width = std::max(left.width, right.width); + int width = std::max(left.size(), right.size()); if (width_hint > 0) width = width_hint; is_signed = children[0]->is_signed && children[1]->is_signed; @@ -1086,7 +1074,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (0) { case AST_REDUCE_BOOL: type_name = "$reduce_bool"; } { RTLIL::SigSpec arg = children[0]->genRTLIL(); - RTLIL::SigSpec sig = arg.width > 1 ? uniop2rtlil(this, type_name, std::max(width_hint, 1), arg) : arg; + RTLIL::SigSpec sig = arg.size() > 1 ? uniop2rtlil(this, type_name, std::max(width_hint, 1), arg) : arg; return sig; } @@ -1100,7 +1088,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) detectSignWidth(width_hint, sign_hint); RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(); - int width = width_hint > 0 ? width_hint : left.width; + int width = width_hint > 0 ? width_hint : left.size(); is_signed = children[0]->is_signed; return binop2rtlil(this, type_name, width, left, right); } @@ -1115,10 +1103,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) detectSignWidth(width_hint, sign_hint); RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(right_width, right_signed); - int width = width_hint > 0 ? width_hint : left.width; + int width = width_hint > 0 ? width_hint : left.size(); is_signed = children[0]->is_signed; if (!flag_noopt && left.is_fully_const() && left.as_int() == 2 && !right_signed) - return binop2rtlil(this, "$shl", width, RTLIL::SigSpec(1, left.width), right); + return binop2rtlil(this, "$shl", width, RTLIL::SigSpec(1, left.size()), right); return binop2rtlil(this, "$pow", width, left, right); } @@ -1154,7 +1142,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint); #if 0 - int width = std::max(left.width, right.width); + int width = std::max(left.size(), right.size()); if (width > width_hint && width_hint > 0) width = width_hint; if (width < width_hint) { @@ -1163,10 +1151,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (type == AST_SUB && (!children[0]->is_signed || !children[1]->is_signed)) width = width_hint; if (type == AST_MUL) - width = std::min(left.width + right.width, width_hint); + width = std::min(left.size() + right.size(), width_hint); } #else - int width = std::max(std::max(left.width, right.width), width_hint); + int width = std::max(std::max(left.size(), right.size()), width_hint); #endif is_signed = children[0]->is_signed && children[1]->is_signed; return binop2rtlil(this, type_name, width, left, right); @@ -1198,17 +1186,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::SigSpec val1 = children[1]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec val2 = children[2]->genRTLIL(width_hint, sign_hint); - if (cond.width > 1) + if (cond.size() > 1) cond = uniop2rtlil(this, "$reduce_bool", 1, cond, false); - int width = std::max(val1.width, val2.width); + int width = std::max(val1.size(), val2.size()); is_signed = children[1]->is_signed && children[2]->is_signed; widthExtend(this, val1, width, is_signed); widthExtend(this, val2, width, is_signed); RTLIL::SigSpec sig = mux2rtlil(this, cond, val1, val2); - if (sig.width < width_hint) + if (sig.size() < width_hint) sig.extend_u0(width_hint, sign_hint); return sig; } @@ -1217,27 +1205,21 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MEMRD: { std::stringstream sstr; - sstr << "$memrd$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + sstr << "$memrd$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++); - RTLIL::Cell *cell = new RTLIL::Cell; + RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$memrd"); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - cell->name = sstr.str(); - cell->type = "$memrd"; - current_module->cells[cell->name] = cell; - RTLIL::Wire *wire = new RTLIL::Wire; + RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width); wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - wire->name = cell->name + "_DATA"; - wire->width = current_module->memories[str]->width; - current_module->wires[wire->name] = wire; int addr_bits = 1; while ((1 << addr_bits) < current_module->memories[str]->size) addr_bits++; - cell->connections["\\CLK"] = RTLIL::SigSpec(RTLIL::State::Sx, 1); - cell->connections["\\ADDR"] = children[0]->genWidthRTLIL(addr_bits); - cell->connections["\\DATA"] = RTLIL::SigSpec(wire); + cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); + cell->setPort("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); + cell->setPort("\\DATA", RTLIL::SigSpec(wire)); cell->parameters["\\MEMID"] = RTLIL::Const(str); cell->parameters["\\ABITS"] = RTLIL::Const(addr_bits); @@ -1254,25 +1236,19 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MEMWR: { std::stringstream sstr; - sstr << "$memwr$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + sstr << "$memwr$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++); - RTLIL::Cell *cell = new RTLIL::Cell; + RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$memwr"); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - cell->name = sstr.str(); - cell->type = "$memwr"; - current_module->cells[cell->name] = cell; int addr_bits = 1; while ((1 << addr_bits) < current_module->memories[str]->size) addr_bits++; - cell->connections["\\CLK"] = RTLIL::SigSpec(RTLIL::State::Sx, 1); - cell->connections["\\ADDR"] = children[0]->genWidthRTLIL(addr_bits); - cell->connections["\\DATA"] = children[1]->genWidthRTLIL(current_module->memories[str]->width); - cell->connections["\\EN"] = children[2]->genRTLIL(); - - if (cell->connections["\\EN"].width > 1) - cell->connections["\\EN"] = uniop2rtlil(this, "$reduce_bool", 1, cell->connections["\\EN"], false); + cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); + cell->setPort("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); + cell->setPort("\\DATA", children[1]->genWidthRTLIL(current_module->memories[str]->width)); + cell->setPort("\\EN", children[2]->genRTLIL()); cell->parameters["\\MEMID"] = RTLIL::Const(str); cell->parameters["\\ABITS"] = RTLIL::Const(addr_bits); @@ -1281,7 +1257,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0); - cell->parameters["\\PRIORITY"] = RTLIL::Const(RTLIL::autoidx-1); + cell->parameters["\\PRIORITY"] = RTLIL::Const(autoidx-1); } break; @@ -1291,19 +1267,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_assert(children.size() == 2); RTLIL::SigSpec check = children[0]->genRTLIL(); - log_assert(check.width == 1); + log_assert(check.size() == 1); RTLIL::SigSpec en = children[1]->genRTLIL(); - log_assert(en.width == 1); + log_assert(en.size() == 1); std::stringstream sstr; - sstr << "$assert$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++); - RTLIL::Cell *cell = new RTLIL::Cell; + RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$assert"); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - cell->name = sstr.str(); - cell->type = "$assert"; - current_module->cells[cell->name] = cell; for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) @@ -1312,8 +1285,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) cell->attributes[attr.first] = attr.second->asAttrConst(); } - cell->connections["\\A"] = check; - cell->connections["\\EN"] = en; + cell->setPort("\\A", check); + cell->setPort("\\EN", en); } break; @@ -1322,12 +1295,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_AUTOWIRE) { RTLIL::SigSpec right = children[1]->genRTLIL(); - RTLIL::SigSpec left = children[0]->genWidthRTLIL(right.width); - current_module->connections.push_back(RTLIL::SigSig(left, right)); + RTLIL::SigSpec left = children[0]->genWidthRTLIL(right.size()); + current_module->connect(RTLIL::SigSig(left, right)); } else { RTLIL::SigSpec left = children[0]->genRTLIL(); - RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.width); - current_module->connections.push_back(RTLIL::SigSig(left, right)); + RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size()); + current_module->connect(RTLIL::SigSig(left, right)); } } break; @@ -1336,9 +1309,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_CELL: { int port_counter = 0, para_counter = 0; - RTLIL::Cell *cell = new RTLIL::Cell; + + 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); + + RTLIL::Cell *cell = current_module->addCell(str, ""); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - cell->name = str; + for (auto it = children.begin(); it != children.end(); it++) { AstNode *child = *it; if (child->type == AST_CELLTYPE) { @@ -1367,13 +1345,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (child->str.size() == 0) { char buf[100]; snprintf(buf, 100, "$%d", ++port_counter); - cell->connections[buf] = sig; + cell->setPort(buf, sig); } else { - cell->connections[child->str] = sig; + cell->setPort(child->str, sig); } continue; } - assert(0); + log_abort(); } for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) @@ -1381,10 +1359,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) attr.first.c_str(), filename.c_str(), linenum); cell->attributes[attr.first] = attr.second->asAttrConst(); } - if (current_module->cells.count(cell->name) != 0) - log_error("Re-definition of cell `%s' at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); - current_module->cells[str] = cell; } break; @@ -1417,23 +1391,19 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // this is a wrapper for AstNode::genRTLIL() when a specific signal width is requested and/or // signals must be substituted before beeing used as input values (used by ProcessGenerator) // note that this is using some global variables to communicate this special settings to AstNode::genRTLIL(). -RTLIL::SigSpec AstNode::genWidthRTLIL(int width, RTLIL::SigSpec *subst_from, RTLIL::SigSpec *subst_to) +RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const std::map<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr) { - RTLIL::SigSpec *backup_subst_from = genRTLIL_subst_from; - RTLIL::SigSpec *backup_subst_to = genRTLIL_subst_to; + const std::map<RTLIL::SigBit, RTLIL::SigBit> *backup_subst_ptr = genRTLIL_subst_ptr; - if (subst_from) - genRTLIL_subst_from = subst_from; - if (subst_to) - genRTLIL_subst_to = subst_to; + if (new_subst_ptr) + genRTLIL_subst_ptr = new_subst_ptr; bool sign_hint = true; int width_hint = width; detectSignWidthWorker(width_hint, sign_hint); RTLIL::SigSpec sig = genRTLIL(width_hint, sign_hint); - genRTLIL_subst_from = backup_subst_from; - genRTLIL_subst_to = backup_subst_to; + genRTLIL_subst_ptr = backup_subst_ptr; if (width >= 0) sig.extend_u0(width, is_signed); @@ -1441,3 +1411,5 @@ RTLIL::SigSpec AstNode::genWidthRTLIL(int width, RTLIL::SigSpec *subst_from, RT return sig; } +YOSYS_NAMESPACE_END + diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index b51079ce5..969cc2302 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -32,7 +32,9 @@ #include <sstream> #include <stdarg.h> -#include <assert.h> +#include <math.h> + +YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; @@ -43,16 +45,23 @@ using namespace AST_INTERNAL; // // this function also does all name resolving and sets the id2ast member of all // nodes that link to a different node using names and lexical scoping. -bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint) +bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param) { AstNode *newNode = NULL; bool did_something = false; +#if 0 + log("-------------\n"); + log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n", + int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param)); + dumpAst(NULL, "> "); +#endif + if (stage == 0) { - assert(type == AST_MODULE); + log_assert(type == AST_MODULE); - while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint)) { } + while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { } if (!flag_nomem2reg && !get_bool_attribute("\\nomem2reg")) { @@ -66,7 +75,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { AstNode *mem = it.first; uint32_t memflags = it.second; - assert((memflags & ~0x00ffff00) == 0); + log_assert((memflags & ~0x00ffff00) == 0); if (mem->get_bool_attribute("\\nomem2reg")) continue; @@ -83,6 +92,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE)) goto verbose_activate; + if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS) + goto verbose_activate; + // log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags)); continue; @@ -114,7 +126,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, reg->is_reg = true; reg->is_signed = node->is_signed; children.push_back(reg); - while (reg->simplify(true, false, false, 1, -1, false)) { } + while (reg->simplify(true, false, false, 1, -1, false, false)) { } } } @@ -128,7 +140,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } - while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint)) { } + while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint, in_param)) { } return false; } @@ -140,7 +152,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_FUNCTION || type == AST_TASK) return false; - // deactivate all calls non-synthesis system taks + // deactivate all calls to non-synthesis system taks if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$stop" || str == "$finish")) { delete_children(); str = std::string(); @@ -152,6 +164,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) const_fold = true; + // in certain cases a function must be evaluated constant. this is what in_param controls. + if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX) + in_param = true; + std::map<std::string, AstNode*> backup_scope; // create name resolution entries for all objects with names @@ -202,12 +218,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; delete node; 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); + continue; } this_wire_scope[node->str] = node; } - wires_are_incompatible: if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || - node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_CELL) { + node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL) { backup_scope[node->str] = current_scope[node->str]; current_scope[node->str] = node; } @@ -215,7 +234,8 @@ 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) - while (node->simplify(true, false, false, 1, -1, false)) { } + while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM)) + did_something = true; } } @@ -229,6 +249,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, bool detect_width_simple = false; bool child_0_is_self_determined = false; bool child_1_is_self_determined = false; + bool child_2_is_self_determined = false; bool children_are_self_determined = false; bool reset_width_after_children = false; @@ -237,8 +258,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, case AST_ASSIGN_EQ: case AST_ASSIGN_LE: case AST_ASSIGN: - while (!children[0]->basic_prep && children[0]->simplify(false, false, true, stage, -1, false) == true) { } - while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false) == true) { } + while (!children[0]->basic_prep && children[0]->simplify(false, false, true, stage, -1, false, in_param) == true) + did_something = true; + while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, in_param) == true) + did_something = true; children[0]->detectSignWidth(backup_width_hint, backup_sign_hint); children[1]->detectSignWidth(width_hint, sign_hint); width_hint = std::max(width_hint, backup_width_hint); @@ -247,11 +270,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, case AST_PARAMETER: case AST_LOCALPARAM: - while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false) == true) { } + while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true) + did_something = true; children[0]->detectSignWidth(width_hint, sign_hint); - if (children.size() > 1) { - assert(children[1]->type == AST_RANGE); - while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false) == true) { } + if (children.size() > 1 && children[1]->type == AST_RANGE) { + 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); width_hint = std::max(width_hint, children[1]->range_left - children[1]->range_right + 1); @@ -307,7 +331,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, width_hint = -1; sign_hint = true; for (auto child : children) { - while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false) == true) { } + while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false, in_param) == true) + did_something = true; child->detectSignWidthWorker(width_hint, sign_hint); } reset_width_after_children = true; @@ -336,13 +361,27 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (detect_width_simple && width_hint < 0) { - for (auto child : children) - while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false) == true) { } if (type == AST_REPLICATE) - while (children[0]->simplify(true, false, in_lvalue, stage, -1, false) == true) { } + while (children[0]->simplify(true, false, in_lvalue, stage, -1, false, true) == true) + did_something = true; + for (auto child : children) + while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false, in_param) == true) + did_something = true; detectSignWidth(width_hint, sign_hint); } + if (type == AST_TERNARY) { + int width_hint_left, width_hint_right; + bool sign_hint_left, sign_hint_right; + bool found_real_left, found_real_right; + children[1]->detectSignWidth(width_hint_left, sign_hint_left, &found_real_left); + children[2]->detectSignWidth(width_hint_right, sign_hint_right, &found_real_right); + if (found_real_left || found_real_right) { + child_1_is_self_determined = true; + child_2_is_self_determined = true; + } + } + // simplify all children first // (iterate by index as e.g. auto wires can add new children in the process) for (size_t i = 0; i < children.size(); i++) { @@ -361,8 +400,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, bool const_fold_here = const_fold, in_lvalue_here = in_lvalue; int width_hint_here = width_hint; bool sign_hint_here = sign_hint; - if (i == 0 && type == AST_REPLICATE) - const_fold_here = true; + bool in_param_here = in_param; + if (i == 0 && (type == AST_REPLICATE || type == AST_WIRE)) + const_fold_here = true, in_param_here = true; if (type == AST_PARAMETER || type == AST_LOCALPARAM) const_fold_here = true; if (i == 0 && (type == AST_ASSIGN || type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE)) @@ -377,15 +417,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, width_hint_here = -1, sign_hint_here = false; if (i == 1 && child_1_is_self_determined) width_hint_here = -1, sign_hint_here = false; + if (i == 2 && child_2_is_self_determined) + width_hint_here = -1, sign_hint_here = false; if (children_are_self_determined) width_hint_here = -1, sign_hint_here = false; - did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage, width_hint_here, sign_hint_here); + did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage, width_hint_here, sign_hint_here, in_param_here); if (did_something_here) did_something = true; } + if (stage == 2 && children[i]->type == AST_INITIAL && current_ast_mod != this) { + current_ast_mod->children.push_back(children[i]); + children.erase(children.begin() + (i--)); + did_something = true; + } } for (auto &attr : attributes) { - while (attr.second->simplify(true, false, false, stage, -1, false)) { } + while (attr.second->simplify(true, false, false, stage, -1, false, true)) + did_something = true; } if (reset_width_after_children) { @@ -416,11 +464,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_DEFPARAM && !str.empty()) { size_t pos = str.rfind('.'); if (pos == std::string::npos) - log_error("Defparam `%s' does not contain a dot (module/parameter seperator) at %s:%d!\n", - RTLIL::id2cstr(str.c_str()), filename.c_str(), linenum); + 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::id2cstr(modname), RTLIL::id2cstr(paraname), filename.c_str(), linenum); + 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(); cell->children.insert(cell->children.begin() + 1, paraset); paraset->type = AST_PARASET; @@ -434,7 +482,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // dumpAst(NULL, "> "); log_error("Index in generate block prefix syntax at %s:%d is not constant!\n", filename.c_str(), linenum); } - assert(children[1]->type == AST_IDENTIFIER); + log_assert(children[1]->type == AST_IDENTIFIER); newNode = children[1]->clone(); const char *second_part = children[1]->str.c_str(); if (second_part[0] == '\\') @@ -458,9 +506,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_RANGE) { bool old_range_valid = range_valid; range_valid = false; + range_swapped = false; range_left = -1; range_right = 0; - assert(children.size() >= 1); + log_assert(children.size() >= 1); if (children[0]->type == AST_CONSTANT) { range_valid = true; range_left = children[0]->integer; @@ -479,6 +528,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int tmp = range_right; range_right = range_left; range_left = tmp; + range_swapped = true; } } @@ -489,6 +539,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (!range_valid) did_something = true; range_valid = true; + range_swapped = children[0]->range_swapped; range_left = children[0]->range_left; range_right = children[0]->range_right; } @@ -496,24 +547,98 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (!range_valid) did_something = true; range_valid = true; + range_swapped = false; range_left = 0; range_right = 0; } } + // resolve multiranges on memory decl + if (type == AST_MEMORY && children.size() > 1 && children[1]->type == AST_MULTIRANGE) + { + int total_size = 1; + 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); + multirange_dimensions.push_back(std::min(range->range_left, range->range_right)); + multirange_dimensions.push_back(std::max(range->range_left, range->range_right) - std::min(range->range_left, range->range_right) + 1); + total_size *= multirange_dimensions.back(); + } + delete children[1]; + children[1] = new AstNode(AST_RANGE, AstNode::mkconst_int(0, true), AstNode::mkconst_int(total_size-1, true)); + did_something = true; + } + + // resolve multiranges on memory access + if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY && children.size() > 0 && children[0]->type == AST_MULTIRANGE) + { + AstNode *index_expr = nullptr; + + for (int i = 0; 2*i < SIZE(id2ast->multirange_dimensions); i++) + { + if (SIZE(children[0]->children) < i) + log_error("Insufficient number of array indices for %s at %s:%d.\n", log_id(str), filename.c_str(), linenum); + + AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone(); + + if (id2ast->multirange_dimensions[2*i]) + new_index_expr = new AstNode(AST_SUB, new_index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i], true)); + + if (i == 0) + index_expr = new_index_expr; + else + index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i-1], true)), new_index_expr); + } + + for (int i = SIZE(id2ast->multirange_dimensions)/1; i < SIZE(children[0]->children); i++) + children.push_back(children[0]->children[i]->clone()); + + delete children[0]; + if (index_expr == nullptr) + children.erase(children.begin()); + else + children[0] = new AstNode(AST_RANGE, index_expr); + + did_something = true; + } + // trim/extend parameters - if ((type == AST_PARAMETER || type == AST_LOCALPARAM) && children[0]->type == AST_CONSTANT && children.size() > 1) { - if (!children[1]->range_valid) - log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum); - int width = children[1]->range_left - children[1]->range_right + 1; - if (width != int(children[0]->bits.size())) { - RTLIL::SigSpec sig(children[0]->bits); - sig.extend_u0(width, children[0]->is_signed); - AstNode *old_child_0 = children[0]; - children[0] = mkconst_bits(sig.as_const().bits, children[0]->is_signed); - delete old_child_0; + 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); + int width = 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); + delete children[0]; + children[0] = mkconst_bits(constvalue.bits, sign_hint); + did_something = true; + } + if (children[0]->type == AST_CONSTANT) { + if (width != int(children[0]->bits.size())) { + RTLIL::SigSpec sig(children[0]->bits); + sig.extend_u0(width, children[0]->is_signed); + AstNode *old_child_0 = children[0]; + children[0] = mkconst_bits(sig.as_const().bits, children[0]->is_signed); + delete old_child_0; + } + children[0]->is_signed = is_signed; + } + range_valid = true; + range_swapped = children[1]->range_swapped; + range_left = children[1]->range_left; + range_right = children[1]->range_right; + } else + if (children.size() > 1 && children[1]->type == AST_REALVALUE && children[0]->type == AST_CONSTANT) { + double as_realvalue = children[0]->asReal(sign_hint); + delete children[0]; + children[0] = new AstNode(AST_REALVALUE); + children[0]->realvalue = as_realvalue; + did_something = true; } - children[0]->is_signed = is_signed; } // annotate identifiers using scope resolution and create auto-wires as needed @@ -521,7 +646,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (current_scope.count(str) == 0) { for (auto node : current_ast_mod->children) { if ((node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || - node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK) && str == node->str) { + node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION) && str == node->str) { current_scope[node->str] = node; break; } @@ -535,20 +660,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, current_scope[str] = auto_wire; did_something = true; } - id2ast = current_scope[str]; + if (id2ast != current_scope[str]) { + id2ast = current_scope[str]; + did_something = true; + } } // split memory access with bit select to individual statements - if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE) + 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 || 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); int mem_width, mem_size, addr_bits; id2ast->meminfo(mem_width, mem_size, addr_bits); std::stringstream sstr; - sstr << "$mem2bits$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + sstr << "$mem2bits$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++); std::string wire_id = sstr.str(); AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); @@ -556,7 +684,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (current_block) wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false); current_ast_mod->children.push_back(wire); - while (wire->simplify(true, false, false, 1, -1, false)) { } + while (wire->simplify(true, false, false, 1, -1, false, false)) { } AstNode *data = clone(); delete data->children[1]; @@ -587,6 +715,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, goto apply_newNode; } + if (type == AST_WHILE) + log_error("While loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum); + + if (type == AST_REPEAT) + log_error("Repeat loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum); + // unroll for loops and generate-for blocks if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0) { @@ -621,7 +755,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // eval 1st expression AstNode *varbuf = init_ast->children[1]->clone(); - while (varbuf->simplify(true, false, false, stage, width_hint, sign_hint)) { } + while (varbuf->simplify(true, false, false, stage, width_hint, sign_hint, 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); @@ -643,7 +777,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { // eval 2nd expression AstNode *buf = while_ast->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } + 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); @@ -662,7 +796,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, buf = new AstNode(AST_GENBLOCK, body_ast->clone()); if (buf->str.empty()) { std::stringstream sstr; - sstr << "$genblock$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + sstr << "$genblock$" << filename << ":" << linenum << "$" << (autoidx++); buf->str = sstr.str(); } std::map<std::string, std::string> name_map; @@ -672,7 +806,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_GENFOR) { for (size_t i = 0; i < buf->children.size(); i++) { - buf->children[i]->simplify(false, false, false, stage, -1, false); + buf->children[i]->simplify(false, false, false, stage, -1, false, false); current_ast_mod->children.push_back(buf->children[i]); } } else { @@ -684,7 +818,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // eval 3rd expression buf = next_ast->children[1]->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } + while (buf->simplify(true, false, false, stage, width_hint, sign_hint, 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); @@ -708,8 +842,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, std::vector<AstNode*> new_children; for (size_t i = 0; i < children.size(); i++) if (children[i]->type == AST_WIRE) { - children[i]->simplify(false, false, false, stage, -1, false); + 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]; } else new_children.push_back(children[i]); @@ -727,7 +862,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } for (size_t i = 0; i < children.size(); i++) { - children[i]->simplify(false, false, false, stage, -1, false); + children[i]->simplify(false, false, false, stage, -1, false, false); current_ast_mod->children.push_back(children[i]); } @@ -739,7 +874,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_GENIF && children.size() != 0) { AstNode *buf = children[0]->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } + while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) // dumpAst(f, "verilog-ast> "); @@ -764,7 +899,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } for (size_t i = 0; i < buf->children.size(); i++) { - buf->children[i]->simplify(false, false, false, stage, -1, false); + buf->children[i]->simplify(false, false, false, stage, -1, false, false); current_ast_mod->children.push_back(buf->children[i]); } @@ -780,7 +915,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_GENCASE && children.size() != 0) { AstNode *buf = children[0]->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } + while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) // dumpAst(f, "verilog-ast> "); @@ -814,14 +949,17 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, continue; buf = child->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } + while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } 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); } - if (RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool()) { + bool is_selected = RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool(); + delete buf; + + if (is_selected) { selected_case = this_genblock; i = children.size(); break; @@ -840,7 +978,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } for (size_t i = 0; i < buf->children.size(); i++) { - buf->children[i]->simplify(false, false, false, stage, -1, false); + buf->children[i]->simplify(false, false, false, stage, -1, false, false); current_ast_mod->children.push_back(buf->children[i]); } @@ -852,6 +990,31 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; } + // unroll cell arrays + 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); + + newNode = new AstNode(AST_GENBLOCK); + int num = std::max(children.at(0)->range_left, children.at(0)->range_right) - std::min(children.at(0)->range_left, children.at(0)->range_right) + 1; + + for (int i = 0; i < num; i++) { + int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i; + AstNode *new_cell = children.at(1)->clone(); + 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); + } 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()); + } + } + + goto apply_newNode; + } + // replace primitives with assignmens if (type == AST_PRIMITIVE) { @@ -861,8 +1024,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, std::vector<AstNode*> children_list; for (auto child : children) { - assert(child->type == AST_ARGUMENT); - assert(child->children.size() == 1); + log_assert(child->type == AST_ARGUMENT); + log_assert(child->children.size() == 1); children_list.push_back(child->children[0]); child->children.clear(); delete child; @@ -917,7 +1080,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, op_type = AST_POS; if (str == "not") op_type = AST_POS, invert_results = true; - assert(op_type != AST_NONE); + log_assert(op_type != AST_NONE); AstNode *node = children_list[1]; if (op_type != AST_POS) @@ -955,12 +1118,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, shift_expr = range->children[1]->clone(); AstNode *left_at_zero_ast = range->children[0]->clone(); AstNode *right_at_zero_ast = range->children[1]->clone(); - while (left_at_zero_ast->simplify(true, true, false, stage, -1, false)) { } - while (right_at_zero_ast->simplify(true, true, false, stage, -1, false)) { } + 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); - result_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; + result_width = abs(left_at_zero_ast->integer - right_at_zero_ast->integer) + 1; } did_something = true; newNode = new AstNode(AST_CASE, shift_expr); @@ -981,14 +1144,14 @@ skip_dynamic_range_lvalue_expansion:; if (stage > 1 && type == AST_ASSERT && current_block != NULL) { std::stringstream sstr; - sstr << "$assert$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++); std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN"; AstNode *wire_check = new AstNode(AST_WIRE); wire_check->str = id_check; 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)) { } + while (wire_check->simplify(true, false, false, 1, -1, false, false)) { } AstNode *wire_en = new AstNode(AST_WIRE); wire_en->str = id_en; @@ -996,7 +1159,7 @@ skip_dynamic_range_lvalue_expansion:; 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_scope[wire_en->str] = wire_en; - while (wire_en->simplify(true, false, false, 1, -1, false)) { } + while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } std::vector<RTLIL::State> x_bit; x_bit.push_back(RTLIL::State::Sx); @@ -1051,16 +1214,16 @@ skip_dynamic_range_lvalue_expansion:; // assignment with memory in left-hand side expression -> replace with memory write port if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER && - children[0]->children.size() == 1 && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && - children[0]->id2ast->children.size() >= 2 && children[0]->id2ast->children[0]->range_valid && - children[0]->id2ast->children[1]->range_valid) + children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 && + children[0]->id2ast->children[0]->range_valid && children[0]->id2ast->children[1]->range_valid && + (children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE) { std::stringstream sstr; - sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN"; if (type == AST_ASSIGN_EQ) - log("Warining: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n", + log("Warning: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n", filename.c_str(), linenum); int mem_width, mem_size, addr_bits; @@ -1070,31 +1233,35 @@ skip_dynamic_range_lvalue_expansion:; wire_addr->str = id_addr; 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)) { } + 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; current_ast_mod->children.push_back(wire_data); current_scope[wire_data->str] = wire_data; - while (wire_data->simplify(true, false, false, 1, -1, false)) { } + while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } - AstNode *wire_en = new AstNode(AST_WIRE); + AstNode *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; 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)) { } + while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } - std::vector<RTLIL::State> x_bits; + std::vector<RTLIL::State> x_bits_addr, x_bits_data, set_bits_en; + for (int i = 0; i < addr_bits; i++) + x_bits_addr.push_back(RTLIL::State::Sx); for (int i = 0; i < mem_width; i++) - x_bits.push_back(RTLIL::State::Sx); + x_bits_data.push_back(RTLIL::State::Sx); + for (int i = 0; i < mem_width; i++) + set_bits_en.push_back(RTLIL::State::S1); - AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits, false)); + 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; - AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits, false)); + 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; - AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, 1)); + AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); assign_en->children[0]->str = id_en; AstNode *default_signals = new AstNode(AST_BLOCK); @@ -1106,11 +1273,62 @@ 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_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); - assign_data->children[0]->str = id_data; + if (children[0]->children.size() == 2) + { + if (children[0]->children[1]->range_valid) + { + int offset = children[0]->children[1]->range_right; + int width = children[0]->children[1]->range_left - offset + 1; - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1)); - assign_en->children[0]->str = id_en; + std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx); + + for (int i = 0; i < mem_width; i++) + set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; + + 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_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + } + else + { + AstNode *the_range = children[0]->children[1]; + AstNode *left_at_zero_ast = the_range->children[0]->clone(); + AstNode *right_at_zero_ast = the_range->children.size() >= 2 ? the_range->children[1]->clone() : left_at_zero_ast->clone(); + AstNode *offset_ast = right_at_zero_ast->clone(); + + 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); + int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; + + for (int i = 0; i < mem_width; i++) + set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; + + 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_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; + + delete left_at_zero_ast; + delete right_at_zero_ast; + delete offset_ast; + } + } + else + { + assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); + assign_data->children[0]->str = id_data; + + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + } newNode = new AstNode(AST_BLOCK); newNode->children.push_back(assign_addr); @@ -1137,21 +1355,127 @@ 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); + AstNode *buf = children[0]->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { } + 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); RTLIL::Const arg_value = buf->bitsAsConst(); + if (arg_value.as_bool()) + arg_value = const_sub(arg_value, 1, false, false, SIZE(arg_value)); + delete buf; + uint32_t result = 0; for (size_t i = 0; i < arg_value.bits.size(); i++) if (arg_value.bits.at(i) == RTLIL::State::S1) - result = i; + result = i + 1; newNode = mkconst_int(result, 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") + { + 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); + } 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); + } + + 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); + int child_width_hint = width_hint; + bool child_sign_hint = sign_hint; + children[0]->detectSignWidth(child_width_hint, child_sign_hint); + x = children[0]->asReal(child_sign_hint); + } + + 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); + 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(); + goto apply_newNode; + } + + if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION) + { + AstNode *dpi_decl = current_scope[str]; + + std::string rtype, fname; + std::vector<std::string> argtypes; + std::vector<AstNode*> args; + + rtype = RTLIL::unescape_id(dpi_decl->children.at(0)->str); + fname = RTLIL::unescape_id(dpi_decl->children.at(1)->str); + + for (int i = 2; i < SIZE(dpi_decl->children); i++) + { + if (i-2 >= SIZE(children)) + log_error("Insufficient number of arguments in DPI function call at %s:%d.\n", filename.c_str(), linenum); + + 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); + } + + newNode = dpi_call(rtype, fname, argtypes, args); + + for (auto arg : args) + delete arg; + + goto apply_newNode; + } + 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); } @@ -1161,22 +1485,47 @@ skip_dynamic_range_lvalue_expansion:; } AstNode *decl = current_scope[str]; + std::stringstream sstr; - sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++) << "$"; + sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++) << "$"; std::string prefix = sstr.str(); + bool recommend_const_eval = false; + bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); + if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count("\\via_celltype")) + { + bool all_args_const = true; + for (auto child : children) { + while (child->simplify(true, false, false, 1, -1, false, true)) { } + if (child->type != AST_CONSTANT) + all_args_const = false; + } + + if (all_args_const) { + AstNode *func_workspace = current_scope[str]->clone(); + newNode = func_workspace->eval_const_function(this); + delete func_workspace; + goto apply_newNode; + } + + if (in_param) + log_error("Non-constant function call in constant expression at %s:%d.\n", filename.c_str(), linenum); + 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); + } + size_t arg_count = 0; std::map<std::string, std::string> replace_rules; if (current_block == NULL) { - assert(type == AST_FCALL); + log_assert(type == AST_FCALL); AstNode *wire = NULL; for (auto child : decl->children) if (child->type == AST_WIRE && child->str == str) wire = child->clone(); - assert(wire != NULL); + log_assert(wire != NULL); wire->str = prefix + str; wire->port_id = 0; @@ -1184,7 +1533,7 @@ skip_dynamic_range_lvalue_expansion:; wire->is_output = false; current_ast_mod->children.push_back(wire); - while (wire->simplify(true, false, false, 1, -1, false)) { } + while (wire->simplify(true, false, false, 1, -1, false, false)) { } AstNode *lvalue = new AstNode(AST_IDENTIFIER); lvalue->str = wire->str; @@ -1196,8 +1545,69 @@ skip_dynamic_range_lvalue_expansion:; goto replace_fcall_with_id; } - for (auto child : decl->children) + if (decl->attributes.count("\\via_celltype")) { + std::string celltype = decl->attributes.at("\\via_celltype")->asAttrConst().decode_string(); + std::string outport = str; + + if (celltype.find(' ') != std::string::npos) { + int pos = celltype.find(' '); + outport = RTLIL::escape_id(celltype.substr(pos+1)); + celltype = RTLIL::escape_id(celltype.substr(0, pos)); + } else + celltype = RTLIL::escape_id(celltype); + + AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE)); + cell->str = prefix.substr(0, SIZE(prefix)-1); + cell->children[0]->str = celltype; + + for (auto attr : decl->attributes) + if (attr.first.str().rfind("\\via_celltype_defparam_", 0) == 0) + { + AstNode *cell_arg = new AstNode(AST_PARASET, attr.second->clone()); + cell_arg->str = RTLIL::escape_id(attr.first.str().substr(strlen("\\via_celltype_defparam_"))); + cell->children.push_back(cell_arg); + } + + for (auto child : decl->children) + if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str))) + { + AstNode *wire = child->clone(); + wire->str = prefix + wire->str; + wire->port_id = 0; + wire->is_input = false; + wire->is_output = false; + current_ast_mod->children.push_back(wire); + while (wire->simplify(true, false, false, 1, -1, false, false)) { } + + AstNode *wire_id = new AstNode(AST_IDENTIFIER); + wire_id->str = wire->str; + + if ((child->is_input || child->is_output) && arg_count < children.size()) + { + AstNode *arg = children[arg_count++]->clone(); + AstNode *assign = child->is_input ? + new AstNode(AST_ASSIGN_EQ, wire_id->clone(), arg) : + new AstNode(AST_ASSIGN_EQ, arg, wire_id->clone()); + + for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { + if (*it != current_block_child) + continue; + current_block->children.insert(it, assign); + break; + } + } + + AstNode *cell_arg = new AstNode(AST_ARGUMENT, wire_id); + cell_arg->str = child->str == str ? outport : child->str; + cell->children.push_back(cell_arg); + } + + current_ast_mod->children.push_back(cell); + goto replace_fcall_with_id; + } + + for (auto child : decl->children) if (child->type == AST_WIRE) { AstNode *wire = child->clone(); @@ -1206,16 +1616,18 @@ skip_dynamic_range_lvalue_expansion:; wire->is_input = false; wire->is_output = false; current_ast_mod->children.push_back(wire); - while (wire->simplify(true, false, false, 1, -1, false)) { } + while (wire->simplify(true, false, false, 1, -1, false, false)) { } replace_rules[child->str] = wire->str; - if (child->is_input && arg_count < children.size()) + if ((child->is_input || child->is_output) && arg_count < children.size()) { AstNode *arg = children[arg_count++]->clone(); AstNode *wire_id = new AstNode(AST_IDENTIFIER); wire_id->str = wire->str; - AstNode *assign = new AstNode(AST_ASSIGN_EQ, wire_id, arg); + AstNode *assign = child->is_input ? + new AstNode(AST_ASSIGN_EQ, wire_id, arg) : + new AstNode(AST_ASSIGN_EQ, arg, wire_id); for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { if (*it != current_block_child) @@ -1225,10 +1637,12 @@ skip_dynamic_range_lvalue_expansion:; } } } - else + + for (auto child : decl->children) + if (child->type != AST_WIRE) { AstNode *stmt = child->clone(); - stmt->replace_ids(replace_rules); + stmt->replace_ids(prefix, replace_rules); for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { if (*it != current_block_child) @@ -1237,7 +1651,6 @@ skip_dynamic_range_lvalue_expansion:; break; } } - } replace_fcall_with_id: if (type == AST_FCALL) { @@ -1251,7 +1664,7 @@ skip_dynamic_range_lvalue_expansion:; } // perform const folding when activated - if (const_fold && newNode == NULL) + if (const_fold) { bool string_op; std::vector<RTLIL::State> tmp_bits; @@ -1265,13 +1678,29 @@ skip_dynamic_range_lvalue_expansion:; if (current_scope[str]->children[0]->type == AST_CONSTANT) { if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) { std::vector<RTLIL::State> data; - for (int i = children[0]->range_right; i <= children[0]->range_left; i++) - data.push_back(current_scope[str]->children[0]->bits[i]); + bool param_upto = current_scope[str]->range_valid && current_scope[str]->range_swapped; + int param_offset = current_scope[str]->range_valid ? current_scope[str]->range_right : 0; + int param_width = current_scope[str]->range_valid ? current_scope[str]->range_left - current_scope[str]->range_right + 1 : + SIZE(current_scope[str]->children[0]->bits); + int tmp_range_left = children[0]->range_left, tmp_range_right = children[0]->range_right; + if (param_upto) { + tmp_range_left = (param_width + 2*param_offset) - children[0]->range_right - 1; + tmp_range_right = (param_width + 2*param_offset) - children[0]->range_left - 1; + } + for (int i = tmp_range_right; i <= tmp_range_left; i++) { + int index = i - param_offset; + if (0 <= index && index < param_width) + data.push_back(current_scope[str]->children[0]->bits[index]); + else + data.push_back(RTLIL::State::Sx); + } newNode = mkconst_bits(data, false); } else if (children.size() == 0) newNode = current_scope[str]->children[0]->clone(); - } + } else + if (current_scope[str]->children[0]->isConst()) + newNode = current_scope[str]->children[0]->clone(); } else if (at_zero && current_scope.count(str) > 0 && (current_scope[str]->type == AST_WIRE || current_scope[str]->type == AST_AUTOWIRE)) { newNode = mkconst_int(0, sign_hint, width_hint); @@ -1314,6 +1743,9 @@ skip_dynamic_range_lvalue_expansion:; if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1); newNode = mkconst_bits(y.bits, false); + } else + if (children[0]->isConst()) { + newNode = mkconst_int(children[0]->asReal(sign_hint) == 0, false, 1); } break; if (0) { case AST_LOGIC_AND: const_func = RTLIL::const_logic_and; } @@ -1322,6 +1754,12 @@ skip_dynamic_range_lvalue_expansion:; RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits), children[0]->is_signed, children[1]->is_signed, -1); newNode = mkconst_bits(y.bits, false); + } else + if (children[0]->isConst() && children[1]->isConst()) { + if (type == AST_LOGIC_AND) + newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) && (children[1]->asReal(sign_hint) != 0), false, 1); + else + newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) || (children[1]->asReal(sign_hint) != 0), false, 1); } break; if (0) { case AST_SHIFT_LEFT: const_func = RTLIL::const_shl; } @@ -1333,6 +1771,10 @@ skip_dynamic_range_lvalue_expansion:; RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), RTLIL::Const(children[1]->bits), sign_hint, type == AST_POW ? children[1]->is_signed : false, width_hint); newNode = mkconst_bits(y.bits, sign_hint); + } else + if (type == AST_POW && children[0]->isConst() && children[1]->isConst()) { + newNode = new AstNode(AST_REALVALUE); + newNode->realvalue = pow(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint)); } break; if (0) { case AST_LT: const_func = RTLIL::const_lt; } @@ -1349,6 +1791,20 @@ skip_dynamic_range_lvalue_expansion:; RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed), children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1); newNode = mkconst_bits(y.bits, false); + } else + if (children[0]->isConst() && children[1]->isConst()) { + bool cmp_signed = (children[0]->type == AST_REALVALUE || children[0]->is_signed) && (children[1]->type == AST_REALVALUE || children[1]->is_signed); + switch (type) { + case AST_LT: newNode = mkconst_int(children[0]->asReal(cmp_signed) < children[1]->asReal(cmp_signed), false, 1); break; + case AST_LE: newNode = mkconst_int(children[0]->asReal(cmp_signed) <= children[1]->asReal(cmp_signed), false, 1); break; + case AST_EQ: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; + case AST_NE: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; + case AST_EQX: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; + case AST_NEX: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; + case AST_GE: newNode = mkconst_int(children[0]->asReal(cmp_signed) >= children[1]->asReal(cmp_signed), false, 1); break; + case AST_GT: newNode = mkconst_int(children[0]->asReal(cmp_signed) > children[1]->asReal(cmp_signed), false, 1); break; + default: log_abort(); + } } break; if (0) { case AST_ADD: const_func = RTLIL::const_add; } @@ -1360,6 +1816,17 @@ skip_dynamic_range_lvalue_expansion:; RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint); newNode = mkconst_bits(y.bits, sign_hint); + } else + if (children[0]->isConst() && children[1]->isConst()) { + newNode = new AstNode(AST_REALVALUE); + switch (type) { + case AST_ADD: newNode->realvalue = children[0]->asReal(sign_hint) + children[1]->asReal(sign_hint); break; + case AST_SUB: newNode->realvalue = children[0]->asReal(sign_hint) - children[1]->asReal(sign_hint); break; + case AST_MUL: newNode->realvalue = children[0]->asReal(sign_hint) * children[1]->asReal(sign_hint); break; + case AST_DIV: newNode->realvalue = children[0]->asReal(sign_hint) / children[1]->asReal(sign_hint); break; + case AST_MOD: newNode->realvalue = fmod(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint)); break; + default: log_abort(); + } } break; if (0) { case AST_POS: const_func = RTLIL::const_pos; } @@ -1367,37 +1834,105 @@ skip_dynamic_range_lvalue_expansion:; if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint); newNode = mkconst_bits(y.bits, sign_hint); + } else + if (children[0]->isConst()) { + newNode = new AstNode(AST_REALVALUE); + if (type == AST_POS) + newNode->realvalue = +children[0]->asReal(sign_hint); + else + newNode->realvalue = -children[0]->asReal(sign_hint); + } + break; + case AST_CASE: + if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) { + std::vector<AstNode*> new_children; + new_children.push_back(children[0]); + for (int i = 1; i < SIZE(children); i++) { + AstNode *child = children[i]; + log_assert(child->type == AST_COND); + for (auto v : child->children) { + if (v->type == AST_DEFAULT) + goto keep_const_cond; + if (v->type == AST_BLOCK) + continue; + if (v->type == AST_CONSTANT && v->bits_only_01()) { + if (v->bits == children[0]->bits) { + while (i+1 < SIZE(children)) + delete children[++i]; + goto keep_const_cond; + } + continue; + } + goto keep_const_cond; + } + if (0) + keep_const_cond: + new_children.push_back(child); + else + delete child; + } + new_children.swap(children); } break; case AST_TERNARY: - if (children[0]->type == AST_CONSTANT) { + if (children[0]->isConst()) + { bool found_sure_true = false; bool found_maybe_true = false; - for (auto &bit : children[0]->bits) { - if (bit == RTLIL::State::S1) - found_sure_true = true; - if (bit > RTLIL::State::S1) - found_maybe_true = true; - } - AstNode *choice = NULL; + + if (children[0]->type == AST_CONSTANT) + for (auto &bit : children[0]->bits) { + if (bit == RTLIL::State::S1) + found_sure_true = true; + if (bit > RTLIL::State::S1) + found_maybe_true = true; + } + else + found_sure_true = children[0]->asReal(sign_hint) != 0; + + AstNode *choice = NULL, *not_choice = NULL; if (found_sure_true) - choice = children[1]; + choice = children[1], not_choice = children[2]; else if (!found_maybe_true) - choice = children[2]; - if (choice != NULL && choice->type == AST_CONSTANT) { - RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint); - if (choice->is_string && y.bits.size() % 8 == 0 && sign_hint == false) - newNode = mkconst_str(y.bits); - else - newNode = mkconst_bits(y.bits, sign_hint); + choice = children[2], not_choice = children[1]; + + if (choice != NULL) { + if (choice->type == AST_CONSTANT) { + int other_width_hint = width_hint; + bool other_sign_hint = sign_hint, other_real = false; + not_choice->detectSignWidth(other_width_hint, other_sign_hint, &other_real); + if (other_real) { + newNode = new AstNode(AST_REALVALUE); + choice->detectSignWidth(width_hint, sign_hint); + newNode->realvalue = choice->asReal(sign_hint); + } else { + RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint); + if (choice->is_string && y.bits.size() % 8 == 0 && sign_hint == false) + newNode = mkconst_str(y.bits); + else + newNode = mkconst_bits(y.bits, sign_hint); + } + } else + if (choice->isConst()) { + newNode = choice->clone(); + } } else if (children[1]->type == AST_CONSTANT && children[2]->type == AST_CONSTANT) { RTLIL::Const a = children[1]->bitsAsConst(width_hint, sign_hint); RTLIL::Const b = children[2]->bitsAsConst(width_hint, sign_hint); - assert(a.bits.size() == b.bits.size()); + log_assert(a.bits.size() == b.bits.size()); for (size_t i = 0; i < a.bits.size(); i++) if (a.bits[i] != b.bits[i]) a.bits[i] = RTLIL::State::Sx; newNode = mkconst_bits(a.bits, sign_hint); + } else if (children[1]->isConst() && children[2]->isConst()) { + newNode = new AstNode(AST_REALVALUE); + if (children[1]->asReal(sign_hint) == children[2]->asReal(sign_hint)) + newNode->realvalue = children[1]->asReal(sign_hint); + else + // IEEE Std 1800-2012 Sec. 11.4.11 states that the entry in Table 7-1 for + // the data type in question should be returned if the ?: is ambiguous. The + // value in Table 7-1 for the 'real' type is 0.0. + newNode->realvalue = 0.0; } } break; @@ -1431,7 +1966,7 @@ apply_newNode: // fprintf(stderr, "----\n"); // dumpAst(stderr, "- "); // newNode->dumpAst(stderr, "+ "); - assert(newNode != NULL); + log_assert(newNode != NULL); newNode->filename = filename; newNode->linenum = linenum; newNode->cloneInto(this); @@ -1501,12 +2036,45 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma } // rename stuff (used when tasks of functions are instanciated) -void AstNode::replace_ids(std::map<std::string, std::string> &rules) +void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules) { - if (type == AST_IDENTIFIER && rules.count(str) > 0) - str = rules[str]; - for (auto child : children) - child->replace_ids(rules); + if (type == AST_BLOCK) + { + std::map<std::string, std::string> new_rules = rules; + std::string new_prefix = prefix + str; + + for (auto child : children) + if (child->type == AST_WIRE) { + new_rules[child->str] = new_prefix + child->str; + child->str = new_prefix + child->str; + } + + for (auto child : children) + if (child->type != AST_WIRE) + child->replace_ids(new_prefix, new_rules); + } + else + { + if (type == AST_IDENTIFIER && rules.count(str) > 0) + str = rules.at(str); + for (auto child : children) + child->replace_ids(prefix, rules); + } +} + +// helper function for mem2reg_as_needed_pass1 +static void mark_memories_assign_lhs_complex(std::map<AstNode*, std::set<std::string>> &mem2reg_places, + std::map<AstNode*, uint32_t> &mem2reg_candidates, AstNode *that) +{ + for (auto &child : that->children) + mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child); + + if (that->type == AST_IDENTIFIER && that->id2ast && that->id2ast->type == AST_MEMORY) { + AstNode *mem = that->id2ast; + if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_CMPLX_LHS)) + mem2reg_places[mem].insert(stringf("%s:%d", that->filename.c_str(), that->linenum)); + mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS; + } } // find memories that should be replaced by registers @@ -1518,6 +2086,10 @@ void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) { + // mark all memories that are used in a complex expression on the left side of an assignment + for (auto &lhs_child : children[0]->children) + mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child); + if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY) { AstNode *mem = children[0]->id2ast; @@ -1588,7 +2160,7 @@ void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> uint32_t backup_flags = flags; flags |= children_flags; - assert((flags & ~0x000000ff) == 0); + log_assert((flags & ~0x000000ff) == 0); for (auto child : children) if (ignore_children_counter > 0) @@ -1617,7 +2189,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * mem2reg_set.count(children[0]->id2ast) > 0 && children[0]->children[0]->children[0]->type != AST_CONSTANT) { std::stringstream sstr; - sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; int mem_width, mem_size, addr_bits; @@ -1628,20 +2200,20 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * wire_addr->is_reg = 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)) { } + 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->is_reg = true; wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false); mod->children.push_back(wire_data); - while (wire_data->simplify(true, false, false, 1, -1, false)) { } + while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } - assert(block != NULL); + log_assert(block != NULL); size_t assign_idx = 0; while (assign_idx < block->children.size() && block->children[assign_idx] != this) assign_idx++; - assert(assign_idx < block->children.size()); + log_assert(assign_idx < block->children.size()); AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); assign_addr->children[0]->str = id_addr; @@ -1654,6 +2226,8 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * continue; AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); AstNode *assign_reg = new AstNode(type, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); + if (children[0]->children.size() == 2) + assign_reg->children[0]->children.push_back(children[0]->children[1]->clone()); assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i); assign_reg->children[1]->str = id_data; cond_node->children[1]->children.push_back(assign_reg); @@ -1670,6 +2244,10 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * if (type == AST_IDENTIFIER && id2ast && mem2reg_set.count(id2ast) > 0) { + AstNode *bit_part_sel = NULL; + if (children.size() == 2) + bit_part_sel = children[1]->clone(); + if (children[0]->children[0]->type == AST_CONSTANT) { int id = children[0]->children[0]->integer; @@ -1682,7 +2260,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * else { std::stringstream sstr; - sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++); + sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; int mem_width, mem_size, addr_bits; @@ -1694,7 +2272,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * if (block) 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)) { } + 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; @@ -1702,7 +2280,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * if (block) wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false); mod->children.push_back(wire_data); - while (wire_data->simplify(true, false, false, 1, -1, false)) { } + while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } 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; @@ -1736,7 +2314,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * size_t assign_idx = 0; while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this)) assign_idx++; - assert(assign_idx < block->children.size()); + log_assert(assign_idx < block->children.size()); block->children.insert(block->children.begin()+assign_idx, case_node); block->children.insert(block->children.begin()+assign_idx, assign_addr); } @@ -1753,9 +2331,12 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * id2ast = NULL; str = id_data; } + + if (bit_part_sel) + children.push_back(bit_part_sel); } - assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0); + log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0); auto children_list = children; for (size_t i = 0; i < children_list.size(); i++) @@ -1765,7 +2346,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * // calulate memory dimensions void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) { - assert(type == AST_MEMORY); + log_assert(type == AST_MEMORY); mem_width = children[0]->range_left - children[0]->range_right + 1; mem_size = children[1]->range_left - children[1]->range_right; @@ -1779,3 +2360,267 @@ void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) addr_bits++; } +bool AstNode::has_const_only_constructs(bool &recommend_const_eval) +{ + if (type == AST_FOR) + recommend_const_eval = true; + if (type == AST_WHILE || type == AST_REPEAT) + return true; + if (type == AST_FCALL && current_scope.count(str)) + if (current_scope[str]->has_const_only_constructs(recommend_const_eval)) + return true; + for (auto child : children) + if (child->AstNode::has_const_only_constructs(recommend_const_eval)) + return true; + return false; +} + +// helper function for AstNode::eval_const_function() +void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall) +{ + if (type == AST_IDENTIFIER && variables.count(str)) { + 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); + 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); + offset = std::min(children.at(0)->range_left, children.at(0)->range_right); + width = std::min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); + } + offset -= variables.at(str).offset; + std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits; + std::vector<RTLIL::State> new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width); + AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed); + newNode->cloneInto(this); + delete newNode; + return; + } + + for (auto &child : children) + child->replace_variables(variables, fcall); +} + +// evaluate functions with all-const arguments +AstNode *AstNode::eval_const_function(AstNode *fcall) +{ + std::map<std::string, AstNode*> backup_scope; + std::map<std::string, AstNode::varinfo_t> variables; + bool delete_temp_block = false; + AstNode *block = NULL; + + size_t argidx = 0; + for (auto child : children) + { + if (child->type == AST_BLOCK) + { + log_assert(block == NULL); + block = child; + continue; + } + + if (child->type == AST_WIRE) + { + 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); + variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1); + variables[child->str].offset = std::min(child->range_left, child->range_right); + variables[child->str].is_signed = child->is_signed; + if (child->is_input && argidx < fcall->children.size()) + variables[child->str].val = fcall->children.at(argidx++)->bitsAsConst(variables[child->str].val.bits.size()); + backup_scope[child->str] = current_scope[child->str]; + current_scope[child->str] = child; + continue; + } + + log_assert(block == NULL); + delete_temp_block = true; + block = new AstNode(AST_BLOCK); + block->children.push_back(child->clone()); + } + + log_assert(block != NULL); + log_assert(variables.count(str)); + + while (!block->children.empty()) + { + AstNode *stmt = block->children.front(); + +#if 0 + log("-----------------------------------\n"); + for (auto &it : variables) + log("%20s %40s\n", it.first.c_str(), log_signal(it.second.val)); + stmt->dumpAst(NULL, "stmt> "); +#endif + + if (stmt->type == AST_ASSIGN_EQ) + { + stmt->children.at(1)->replace_variables(variables, fcall); + while (stmt->simplify(true, false, false, 1, -1, false, true)) { } + + if (stmt->type != AST_ASSIGN_EQ) + 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); + + 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); + + 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); + + 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); + int offset = std::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]; + RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size()); + for (int i = 0; i < width; i++) + v.val.bits.at(i+offset-v.offset) = r.bits.at(i); + } + + delete block->children.front(); + block->children.erase(block->children.begin()); + continue; + } + + if (stmt->type == AST_FOR) + { + block->children.insert(block->children.begin(), stmt->children.at(0)); + stmt->children.at(3)->children.push_back(stmt->children.at(2)); + stmt->children.erase(stmt->children.begin() + 2); + stmt->children.erase(stmt->children.begin()); + stmt->type = AST_WHILE; + continue; + } + + if (stmt->type == AST_WHILE) + { + AstNode *cond = stmt->children.at(0)->clone(); + cond->replace_variables(variables, 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); + + if (cond->asBool()) { + block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); + } else { + delete block->children.front(); + block->children.erase(block->children.begin()); + } + + delete cond; + continue; + } + + if (stmt->type == AST_REPEAT) + { + AstNode *num = stmt->children.at(0)->clone(); + num->replace_variables(variables, 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); + + block->children.erase(block->children.begin()); + for (int i = 0; i < num->bitsAsConst().as_int(); i++) + block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); + + delete stmt; + delete num; + continue; + } + + if (stmt->type == AST_CASE) + { + AstNode *expr = stmt->children.at(0)->clone(); + expr->replace_variables(variables, fcall); + while (expr->simplify(true, false, false, 1, -1, false, true)) { } + + AstNode *sel_case = NULL; + for (size_t i = 1; i < stmt->children.size(); i++) + { + bool found_match = false; + log_assert(stmt->children.at(i)->type == AST_COND); + + if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) { + sel_case = stmt->children.at(i)->children.back(); + continue; + } + + for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++) + { + AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); + cond->replace_variables(variables, fcall); + + cond = new AstNode(AST_EQ, expr->clone(), cond); + 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); + + found_match = cond->asBool(); + delete cond; + } + + if (found_match) { + sel_case = stmt->children.at(i)->children.back(); + break; + } + } + + block->children.erase(block->children.begin()); + if (sel_case) + block->children.insert(block->children.begin(), sel_case->clone()); + delete stmt; + delete expr; + continue; + } + + if (stmt->type == AST_BLOCK) + { + block->children.erase(block->children.begin()); + block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); + stmt->children.clear(); + delete stmt; + 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_abort(); + } + + if (delete_temp_block) + delete block; + + for (auto &it : backup_scope) + if (it.second == NULL) + current_scope.erase(it.first); + else + current_scope[it.first] = it.second; + + return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); +} + +YOSYS_NAMESPACE_END + diff --git a/frontends/ilang/Makefile.inc b/frontends/ilang/Makefile.inc index 07ebf0856..e832cfed8 100644 --- a/frontends/ilang/Makefile.inc +++ b/frontends/ilang/Makefile.inc @@ -4,12 +4,14 @@ GENFILES += frontends/ilang/parser.tab.h GENFILES += frontends/ilang/parser.output GENFILES += frontends/ilang/lexer.cc -frontends/ilang/parser.tab.cc frontends/ilang/parser.tab.h: frontends/ilang/parser.y - bison -d -r all -b frontends/ilang/parser frontends/ilang/parser.y - mv frontends/ilang/parser.tab.c frontends/ilang/parser.tab.cc +frontends/ilang/parser.tab.cc: frontends/ilang/parser.y + $(P) bison -d -r all -b frontends/ilang/parser frontends/ilang/parser.y + $(Q) mv frontends/ilang/parser.tab.c frontends/ilang/parser.tab.cc + +frontends/ilang/parser.tab.h: frontends/ilang/parser.tab.cc frontends/ilang/lexer.cc: frontends/ilang/lexer.l - flex -o frontends/ilang/lexer.cc frontends/ilang/lexer.l + $(P) flex -o frontends/ilang/lexer.cc frontends/ilang/lexer.l OBJS += frontends/ilang/parser.tab.o frontends/ilang/lexer.o OBJS += frontends/ilang/ilang_frontend.o diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc index 572a35720..f6f926db1 100644 --- a/frontends/ilang/ilang_frontend.cc +++ b/frontends/ilang/ilang_frontend.cc @@ -26,6 +26,8 @@ #include "kernel/register.h" #include "kernel/log.h" +YOSYS_NAMESPACE_BEGIN + void rtlil_frontend_ilang_yyerror(char const *s) { log_error("Parser error in line %d: %s\n", rtlil_frontend_ilang_yyget_lineno(), s); @@ -43,17 +45,20 @@ struct IlangFrontend : public Frontend { log("representation of a design in yosys's internal format.)\n"); log("\n"); } - virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { log_header("Executing ILANG frontend.\n"); extra_args(f, filename, args, 1); log("Input filename: %s\n", filename.c_str()); + ILANG_FRONTEND::lexin = f; ILANG_FRONTEND::current_design = design; rtlil_frontend_ilang_yydebug = false; - rtlil_frontend_ilang_yyrestart(f); + rtlil_frontend_ilang_yyrestart(NULL); rtlil_frontend_ilang_yyparse(); rtlil_frontend_ilang_yylex_destroy(); } } IlangFrontend; +YOSYS_NAMESPACE_END + diff --git a/frontends/ilang/ilang_frontend.h b/frontends/ilang/ilang_frontend.h index 5e768c3b9..b04d6c512 100644 --- a/frontends/ilang/ilang_frontend.h +++ b/frontends/ilang/ilang_frontend.h @@ -25,20 +25,23 @@ #ifndef ILANG_FRONTEND_H #define ILANG_FRONTEND_H -#include "kernel/rtlil.h" -#include <stdio.h> +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN namespace ILANG_FRONTEND { - void ilang_frontend(FILE *f, RTLIL::Design *design); + extern std::istream *lexin; extern RTLIL::Design *current_design; } +YOSYS_NAMESPACE_END + extern int rtlil_frontend_ilang_yydebug; int rtlil_frontend_ilang_yylex(void); void rtlil_frontend_ilang_yyerror(char const *s); void rtlil_frontend_ilang_yyrestart(FILE *f); int rtlil_frontend_ilang_yyparse(void); -void rtlil_frontend_ilang_yylex_destroy(void); +int rtlil_frontend_ilang_yylex_destroy(void); int rtlil_frontend_ilang_yyget_lineno(void); #endif diff --git a/frontends/ilang/lexer.l b/frontends/ilang/lexer.l index 5da8ce675..4109cd4bc 100644 --- a/frontends/ilang/lexer.l +++ b/frontends/ilang/lexer.l @@ -23,9 +23,18 @@ */ %{ -#include "kernel/rtlil.h" + +#ifdef __clang__ +// bison generates code using the 'register' storage class specifier +#pragma clang diagnostic ignored "-Wdeprecated-register" +#endif + +#include "ilang_frontend.h" #include "parser.tab.h" -void update_autoidx(const char *p); + +#define YY_INPUT(buf,result,max_size) \ + result = ILANG_FRONTEND::lexin->readsome(buf, max_size); + %} %option yylineno @@ -37,6 +46,7 @@ void update_autoidx(const char *p); %% +"autoidx" { return TOK_AUTOIDX; } "module" { return TOK_MODULE; } "attribute" { return TOK_ATTRIBUTE; } "parameter" { return TOK_PARAMETER; } @@ -44,6 +54,7 @@ void update_autoidx(const char *p); "wire" { return TOK_WIRE; } "memory" { return TOK_MEMORY; } "width" { return TOK_WIDTH; } +"upto" { return TOK_UPTO; } "offset" { return TOK_OFFSET; } "size" { return TOK_SIZE; } "input" { return TOK_INPUT; } @@ -69,11 +80,11 @@ void update_autoidx(const char *p); [a-z]+ { return TOK_INVALID; } "\\"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; } -"$"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); update_autoidx(yytext); return TOK_ID; } +"$"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; } "."[0-9]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; } [0-9]+'[01xzm-]* { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; } -[0-9]+ { rtlil_frontend_ilang_yylval.integer = atoi(yytext); return TOK_INT; } +-?[0-9]+ { rtlil_frontend_ilang_yylval.integer = atoi(yytext); return TOK_INT; } \" { BEGIN(STRING); } <STRING>\\. { yymore(); } @@ -117,27 +128,6 @@ void update_autoidx(const char *p); %% -void update_autoidx(const char *p) -{ - if (*p != '$') - return; - - while (*p) { - if (*(p++) != '$') - continue; - if ('0' <= *p && *p <= '9') { - const char *q = p; - while ('0' <= *q && *q <= '9') - q++; - if ((q - p) < 10) { - int idx = atoi(p); - if (idx > RTLIL::autoidx) - RTLIL::autoidx = idx; - } - } - } -} - // this is a hack to avoid the 'yyinput defined but not used' error msgs void *rtlil_frontend_ilang_avoid_input_warnings() { return (void*)&yyinput; diff --git a/frontends/ilang/parser.y b/frontends/ilang/parser.y index c2e090220..a5cc06898 100644 --- a/frontends/ilang/parser.y +++ b/frontends/ilang/parser.y @@ -25,7 +25,9 @@ %{ #include <list> #include "ilang_frontend.h" +YOSYS_NAMESPACE_BEGIN namespace ILANG_FRONTEND { + std::istream *lexin; RTLIL::Design *current_design; RTLIL::Module *current_module; RTLIL::Wire *current_wire; @@ -37,24 +39,26 @@ namespace ILANG_FRONTEND { std::map<RTLIL::IdString, RTLIL::Const> attrbuf; } using namespace ILANG_FRONTEND; +YOSYS_NAMESPACE_END +USING_YOSYS_NAMESPACE %} -%name-prefix="rtlil_frontend_ilang_yy" +%name-prefix "rtlil_frontend_ilang_yy" %union { char *string; int integer; - RTLIL::Const *data; - RTLIL::SigSpec *sigspec; + YOSYS_NAMESPACE_PREFIX RTLIL::Const *data; + YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec *sigspec; } %token <string> TOK_ID TOK_VALUE TOK_STRING %token <integer> TOK_INT -%token TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT +%token TOK_AUTOIDX TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT %token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC %token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_INIT %token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET -%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED +%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_UPTO %type <sigspec> sigspec sigspec_list %type <integer> sync_type @@ -82,21 +86,23 @@ optional_eol: design: design module | design attr_stmt | + design autoidx_stmt | /* empty */; module: TOK_MODULE TOK_ID EOL { - if (current_design->modules.count($2) != 0) - rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of module %s.", $2).c_str()); + if (current_design->has($2)) + rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str()); current_module = new RTLIL::Module; current_module->name = $2; current_module->attributes = attrbuf; - current_design->modules[$2] = current_module; + current_design->add(current_module); attrbuf.clear(); free($2); } module_body TOK_END { if (attrbuf.size() != 0) rtlil_frontend_ilang_yyerror("dangling attribute"); + current_module->fixup_ports(); } EOL; module_body: @@ -113,16 +119,20 @@ attr_stmt: free($2); }; +autoidx_stmt: + TOK_AUTOIDX TOK_INT EOL { + autoidx = std::max(autoidx, $2); + }; + wire_stmt: TOK_WIRE { - current_wire = new RTLIL::Wire; + current_wire = current_module->addWire("$__ilang_frontend_tmp__"); current_wire->attributes = attrbuf; attrbuf.clear(); } wire_options TOK_ID EOL { - if (current_module->wires.count($4) != 0) - rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of wire %s.", $4).c_str()); - current_wire->name = $4; - current_module->wires[$4] = current_wire; + if (current_module->wires_.count($4) != 0) + rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of wire %s.", $4).c_str()); + current_module->rename(current_wire, $4); free($4); }; @@ -130,6 +140,9 @@ wire_options: wire_options TOK_WIDTH TOK_INT { current_wire->width = $3; } | + wire_options TOK_UPTO { + current_wire->upto = true; + } | wire_options TOK_OFFSET TOK_INT { current_wire->start_offset = $3; } | @@ -157,7 +170,7 @@ memory_stmt: attrbuf.clear(); } memory_options TOK_ID EOL { if (current_module->memories.count($4) != 0) - rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of memory %s.", $4).c_str()); + rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of memory %s.", $4).c_str()); current_memory->name = $4; current_module->memories[$4] = current_memory; free($4); @@ -174,13 +187,10 @@ memory_options: cell_stmt: TOK_CELL TOK_ID TOK_ID EOL { - if (current_module->cells.count($3) != 0) - rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of cell %s.", $3).c_str()); - current_cell = new RTLIL::Cell; - current_cell->type = $2; - current_cell->name = $3; + if (current_module->cells_.count($3) != 0) + rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell %s.", $3).c_str()); + current_cell = current_module->addCell($3, $2); current_cell->attributes = attrbuf; - current_module->cells[$3] = current_cell; attrbuf.clear(); free($2); free($3); @@ -199,9 +209,9 @@ cell_body: delete $5; } | cell_body TOK_CONNECT TOK_ID sigspec EOL { - if (current_cell->connections.count($3) != 0) - rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of cell port %s.", $3).c_str()); - current_cell->connections[$3] = *$4; + if (current_cell->hasPort($3)) + rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell port %s.", $3).c_str()); + current_cell->setPort($3, *$4); delete $4; free($3); } | @@ -210,7 +220,7 @@ cell_body: proc_stmt: TOK_PROCESS TOK_ID EOL { if (current_module->processes.count($2) != 0) - rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of process %s.", $2).c_str()); + rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of process %s.", $2).c_str()); current_process = new RTLIL::Process; current_process->name = $2; current_process->attributes = attrbuf; @@ -219,6 +229,7 @@ proc_stmt: switch_stack.push_back(¤t_process->root_case.switches); case_stack.clear(); case_stack.push_back(¤t_process->root_case); + attrbuf.clear(); free($2); } case_body sync_list TOK_END EOL; @@ -350,50 +361,25 @@ constant: sigspec: constant { - RTLIL::SigChunk chunk; - chunk.wire = NULL; - chunk.width = $1->bits.size(); - chunk.offset = 0; - chunk.data = *$1; - $$ = new RTLIL::SigSpec; - $$->chunks.push_back(chunk); - $$->width = chunk.width; + $$ = new RTLIL::SigSpec(*$1); delete $1; } | TOK_ID { - if (current_module->wires.count($1) == 0) - rtlil_frontend_ilang_yyerror(stringf("scope error: wire %s not found", $1).c_str()); - RTLIL::SigChunk chunk; - chunk.wire = current_module->wires[$1]; - chunk.width = current_module->wires[$1]->width; - chunk.offset = 0; - $$ = new RTLIL::SigSpec; - $$->chunks.push_back(chunk); - $$->width = chunk.width; + if (current_module->wires_.count($1) == 0) + rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str()); + $$ = new RTLIL::SigSpec(current_module->wires_[$1]); free($1); } | TOK_ID '[' TOK_INT ']' { - if (current_module->wires.count($1) == 0) - rtlil_frontend_ilang_yyerror(stringf("scope error: wire %s not found", $1).c_str()); - RTLIL::SigChunk chunk; - chunk.wire = current_module->wires[$1]; - chunk.offset = $3; - chunk.width = 1; - $$ = new RTLIL::SigSpec; - $$->chunks.push_back(chunk); - $$->width = 1; + if (current_module->wires_.count($1) == 0) + rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str()); + $$ = new RTLIL::SigSpec(current_module->wires_[$1], $3); free($1); } | TOK_ID '[' TOK_INT ':' TOK_INT ']' { - if (current_module->wires.count($1) == 0) - rtlil_frontend_ilang_yyerror(stringf("scope error: wire %s not found", $1).c_str()); - RTLIL::SigChunk chunk; - chunk.wire = current_module->wires[$1]; - chunk.width = $3 - $5 + 1; - chunk.offset = $5; - $$ = new RTLIL::SigSpec; - $$->chunks.push_back(chunk); - $$->width = chunk.width; + if (current_module->wires_.count($1) == 0) + rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str()); + $$ = new RTLIL::SigSpec(current_module->wires_[$1], $5, $3 - $5 + 1); free($1); } | '{' sigspec_list '}' { @@ -403,14 +389,8 @@ sigspec: sigspec_list: sigspec_list sigspec { $$ = new RTLIL::SigSpec; - for (auto it = $2->chunks.begin(); it != $2->chunks.end(); it++) { - $$->chunks.push_back(*it); - $$->width += it->width; - } - for (auto it = $1->chunks.begin(); it != $1->chunks.end(); it++) { - $$->chunks.push_back(*it); - $$->width += it->width; - } + $$->append(*$2); + $$->append(*$1); delete $1; delete $2; } | @@ -422,7 +402,7 @@ conn_stmt: TOK_CONNECT sigspec sigspec EOL { if (attrbuf.size() != 0) rtlil_frontend_ilang_yyerror("dangling attribute"); - current_module->connections.push_back(RTLIL::SigSig(*$2, *$3)); + current_module->connect(*$2, *$3); delete $2; delete $3; }; diff --git a/frontends/liberty/Makefile.inc b/frontends/liberty/Makefile.inc new file mode 100644 index 000000000..a02ef5e45 --- /dev/null +++ b/frontends/liberty/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += frontends/liberty/liberty.o + diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc new file mode 100644 index 000000000..a9ab022a4 --- /dev/null +++ b/frontends/liberty/liberty.cc @@ -0,0 +1,578 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "passes/techmap/libparse.h" +#include "kernel/register.h" +#include "kernel/log.h" + +YOSYS_NAMESPACE_BEGIN +using namespace PASS_DFFLIBMAP; + +struct token_t { + char type; + RTLIL::SigSpec sig; + token_t (char t) : type(t) { } + token_t (char t, RTLIL::SigSpec s) : type(t), sig(s) { } +}; + +static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&expr) +{ + log_assert(*expr != 0); + + int id_len = 0; + while (('a' <= expr[id_len] && expr[id_len] <= 'z') || ('A' <= expr[id_len] && expr[id_len] <= 'Z') || + ('0' <= expr[id_len] && expr[id_len] <= '9') || expr[id_len] == '.' || expr[id_len] == '_') id_len++; + + if (id_len == 0) + log_error("Expected identifier at `%s'.\n", expr); + + if (id_len == 1 && (*expr == '0' || *expr == '1')) + return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1; + + std::string id = RTLIL::escape_id(std::string(expr, id_len)); + if (!module->wires_.count(id)) + log_error("Can't resolve wire name %s.\n", RTLIL::unescape_id(id).c_str()); + + expr += id_len; + return module->wires_.at(id); +} + +static RTLIL::SigSpec create_inv_cell(RTLIL::Module *module, RTLIL::SigSpec A) +{ + RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_"); + cell->setPort("\\A", A); + cell->setPort("\\Y", module->addWire(NEW_ID)); + return cell->getPort("\\Y"); +} + +static RTLIL::SigSpec create_xor_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B) +{ + RTLIL::Cell *cell = module->addCell(NEW_ID, "$_XOR_"); + cell->setPort("\\A", A); + cell->setPort("\\B", B); + cell->setPort("\\Y", module->addWire(NEW_ID)); + return cell->getPort("\\Y"); +} + +static RTLIL::SigSpec create_and_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B) +{ + RTLIL::Cell *cell = module->addCell(NEW_ID, "$_AND_"); + cell->setPort("\\A", A); + cell->setPort("\\B", B); + cell->setPort("\\Y", module->addWire(NEW_ID)); + return cell->getPort("\\Y"); +} + +static RTLIL::SigSpec create_or_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B) +{ + RTLIL::Cell *cell = module->addCell(NEW_ID, "$_OR_"); + cell->setPort("\\A", A); + cell->setPort("\\B", B); + cell->setPort("\\Y", module->addWire(NEW_ID)); + return cell->getPort("\\Y"); +} + +static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack, token_t next_token) +{ + int top = int(stack.size())-1; + + if (0 <= top-1 && stack[top].type == 0 && stack[top-1].type == '!') { + token_t t = token_t(0, create_inv_cell(module, stack[top].sig)); + stack.pop_back(); + stack.pop_back(); + stack.push_back(t); + return true; + } + + if (0 <= top-1 && stack[top].type == '\'' && stack[top-1].type == 0) { + token_t t = token_t(0, create_inv_cell(module, stack[top-1].sig)); + stack.pop_back(); + stack.pop_back(); + stack.push_back(t); + return true; + } + + if (0 <= top && stack[top].type == 0) { + if (next_token.type == '\'') + return false; + stack[top].type = 1; + return true; + } + + if (0 <= top-2 && stack[top-2].type == 1 && stack[top-1].type == '^' && stack[top].type == 1) { + token_t t = token_t(1, create_xor_cell(module, stack[top-2].sig, stack[top].sig)); + stack.pop_back(); + stack.pop_back(); + stack.pop_back(); + stack.push_back(t); + return true; + } + + if (0 <= top && stack[top].type == 1) { + if (next_token.type == '^') + return false; + stack[top].type = 2; + return true; + } + + if (0 <= top-1 && stack[top-1].type == 2 && stack[top].type == 2) { + token_t t = token_t(2, create_and_cell(module, stack[top-1].sig, stack[top].sig)); + stack.pop_back(); + stack.pop_back(); + stack.push_back(t); + return true; + } + + if (0 <= top-2 && stack[top-2].type == 2 && (stack[top-1].type == '*' || stack[top-1].type == '&') && stack[top].type == 2) { + token_t t = token_t(2, create_and_cell(module, stack[top-2].sig, stack[top].sig)); + stack.pop_back(); + stack.pop_back(); + stack.pop_back(); + stack.push_back(t); + return true; + } + + if (0 <= top && stack[top].type == 2) { + if (next_token.type == '*' || next_token.type == '&' || next_token.type == 0 || next_token.type == '(') + return false; + stack[top].type = 3; + return true; + } + + if (0 <= top-2 && stack[top-2].type == 3 && (stack[top-1].type == '+' || stack[top-1].type == '|') && stack[top].type == 3) { + token_t t = token_t(3, create_or_cell(module, stack[top-2].sig, stack[top].sig)); + stack.pop_back(); + stack.pop_back(); + stack.pop_back(); + stack.push_back(t); + return true; + } + + if (0 <= top-2 && stack[top-2].type == '(' && stack[top-1].type == 3 && stack[top].type == ')') { + token_t t = token_t(0, stack[top-1].sig); + stack.pop_back(); + stack.pop_back(); + stack.pop_back(); + stack.push_back(t); + return true; + } + + return false; +} + +static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr) +{ + const char *orig_expr = expr; + std::vector<token_t> stack; + + while (*expr) + { + if (*expr == ' ' || *expr == '\t' || *expr == '\r' || *expr == '\n' || *expr == '"') { + expr++; + continue; + } + + token_t next_token(0); + if (*expr == '(' || *expr == ')' || *expr == '\'' || *expr == '!' || *expr == '^' || *expr == '*' || *expr == '+' || *expr == '|') + next_token = token_t(*(expr++)); + else + next_token = token_t(0, parse_func_identifier(module, expr)); + + while (parse_func_reduce(module, stack, next_token)) {} + stack.push_back(next_token); + } + + while (parse_func_reduce(module, stack, token_t('.'))) {} + +#if 0 + for (size_t i = 0; i < stack.size(); i++) + if (stack[i].type < 16) + log("%3d: %d %s\n", int(i), stack[i].type, log_signal(stack[i].sig)); + else + log("%3d: %c\n", int(i), stack[i].type); +#endif + + if (stack.size() != 1 || stack.back().type != 3) + log_error("Parser error in function expr `%s'.\n", orig_expr); + + return stack.back().sig; +} + +static void create_ff(RTLIL::Module *module, LibertyAst *node) +{ + 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)))); + + RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig; + bool clk_polarity = true, clear_polarity = true, preset_polarity = true; + + for (auto child : node->children) { + if (child->id == "clocked_on") + clk_sig = parse_func_expr(module, child->value.c_str()); + if (child->id == "next_state") + data_sig = parse_func_expr(module, child->value.c_str()); + if (child->id == "clear") + clear_sig = parse_func_expr(module, child->value.c_str()); + if (child->id == "preset") + preset_sig = parse_func_expr(module, child->value.c_str()); + } + + if (clk_sig.size() == 0 || data_sig.size() == 0) + log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", log_id(module->name)); + + for (bool rerun_invert_rollback = true; rerun_invert_rollback;) + { + rerun_invert_rollback = false; + + for (auto &it : module->cells_) { + if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clk_sig) { + clk_sig = it.second->getPort("\\A"); + clk_polarity = !clk_polarity; + rerun_invert_rollback = true; + } + if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clear_sig) { + clear_sig = it.second->getPort("\\A"); + clear_polarity = !clear_polarity; + rerun_invert_rollback = true; + } + if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == preset_sig) { + preset_sig = it.second->getPort("\\A"); + preset_polarity = !preset_polarity; + rerun_invert_rollback = true; + } + } + } + + RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_"); + cell->setPort("\\A", iq_sig); + cell->setPort("\\Y", iqn_sig); + + cell = module->addCell(NEW_ID, ""); + cell->setPort("\\D", data_sig); + cell->setPort("\\Q", iq_sig); + cell->setPort("\\C", clk_sig); + + if (clear_sig.size() == 0 && preset_sig.size() == 0) { + cell->type = stringf("$_DFF_%c_", clk_polarity ? 'P' : 'N'); + } + + if (clear_sig.size() == 1 && preset_sig.size() == 0) { + cell->type = stringf("$_DFF_%c%c0_", clk_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N'); + cell->setPort("\\R", clear_sig); + } + + if (clear_sig.size() == 0 && preset_sig.size() == 1) { + cell->type = stringf("$_DFF_%c%c1_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N'); + cell->setPort("\\R", preset_sig); + } + + if (clear_sig.size() == 1 && preset_sig.size() == 1) { + cell->type = stringf("$_DFFSR_%c%c%c_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N'); + cell->setPort("\\S", preset_sig); + cell->setPort("\\R", clear_sig); + } + + log_assert(!cell->type.empty()); +} + +static void create_latch(RTLIL::Module *module, LibertyAst *node) +{ + 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)))); + + RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig; + bool enable_polarity = true, clear_polarity = true, preset_polarity = true; + + for (auto child : node->children) { + if (child->id == "enable") + enable_sig = parse_func_expr(module, child->value.c_str()); + if (child->id == "data_in") + data_sig = parse_func_expr(module, child->value.c_str()); + if (child->id == "clear") + clear_sig = parse_func_expr(module, child->value.c_str()); + if (child->id == "preset") + 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)); + + for (bool rerun_invert_rollback = true; rerun_invert_rollback;) + { + rerun_invert_rollback = false; + + for (auto &it : module->cells_) { + if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == enable_sig) { + enable_sig = it.second->getPort("\\A"); + enable_polarity = !enable_polarity; + rerun_invert_rollback = true; + } + if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clear_sig) { + clear_sig = it.second->getPort("\\A"); + clear_polarity = !clear_polarity; + rerun_invert_rollback = true; + } + if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == preset_sig) { + preset_sig = it.second->getPort("\\A"); + preset_polarity = !preset_polarity; + rerun_invert_rollback = true; + } + } + } + + RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_"); + cell->setPort("\\A", iq_sig); + cell->setPort("\\Y", iqn_sig); + + if (clear_sig.size() == 1) + { + RTLIL::SigSpec clear_negative = clear_sig; + RTLIL::SigSpec clear_enable = clear_sig; + + if (clear_polarity == true || clear_polarity != enable_polarity) + { + RTLIL::Cell *inv = module->addCell(NEW_ID, "$_NOT_"); + inv->setPort("\\A", clear_sig); + inv->setPort("\\Y", module->addWire(NEW_ID)); + + if (clear_polarity == true) + clear_negative = inv->getPort("\\Y"); + if (clear_polarity != enable_polarity) + clear_enable = inv->getPort("\\Y"); + } + + RTLIL::Cell *data_gate = module->addCell(NEW_ID, "$_AND_"); + data_gate->setPort("\\A", data_sig); + data_gate->setPort("\\B", clear_negative); + data_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID)); + + RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? "$_OR_" : "$_AND_"); + enable_gate->setPort("\\A", enable_sig); + enable_gate->setPort("\\B", clear_enable); + enable_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID)); + } + + if (preset_sig.size() == 1) + { + RTLIL::SigSpec preset_positive = preset_sig; + RTLIL::SigSpec preset_enable = preset_sig; + + if (preset_polarity == false || preset_polarity != enable_polarity) + { + RTLIL::Cell *inv = module->addCell(NEW_ID, "$_NOT_"); + inv->setPort("\\A", preset_sig); + inv->setPort("\\Y", module->addWire(NEW_ID)); + + if (preset_polarity == false) + preset_positive = inv->getPort("\\Y"); + if (preset_polarity != enable_polarity) + preset_enable = inv->getPort("\\Y"); + } + + RTLIL::Cell *data_gate = module->addCell(NEW_ID, "$_OR_"); + data_gate->setPort("\\A", data_sig); + data_gate->setPort("\\B", preset_positive); + data_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID)); + + RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? "$_OR_" : "$_AND_"); + enable_gate->setPort("\\A", enable_sig); + enable_gate->setPort("\\B", preset_enable); + enable_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID)); + } + + cell = module->addCell(NEW_ID, stringf("$_DLATCH_%c_", enable_polarity ? 'P' : 'N')); + cell->setPort("\\D", data_sig); + cell->setPort("\\Q", iq_sig); + cell->setPort("\\E", enable_sig); +} + +struct LibertyFrontend : public Frontend { + LibertyFrontend() : Frontend("liberty", "read cells from liberty file") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read_liberty [filename]\n"); + log("\n"); + log("Read cells from liberty file as modules into current design.\n"); + log("\n"); + log(" -lib\n"); + log(" only create empty blackbox modules\n"); + log("\n"); + log(" -ignore_redef\n"); + log(" ignore re-definitions of modules. (the default behavior is to\n"); + log(" create an error message.)\n"); + log("\n"); + log(" -ignore_miss_func\n"); + log(" ignore cells with missing function specification of outputs\n"); + log("\n"); + log(" -ignore_miss_dir\n"); + log(" ignore cells with a missing or invalid direction\n"); + log(" specification on a pin\n"); + log("\n"); + log(" -setattr <attribute_name>\n"); + log(" set the specified attribute (to the value 1) on all loaded modules\n"); + log("\n"); + } + virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + { + bool flag_lib = false; + bool flag_ignore_redef = false; + bool flag_ignore_miss_func = false; + bool flag_ignore_miss_dir = false; + std::vector<std::string> attributes; + + log_header("Executing Liberty frontend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-lib") { + flag_lib = true; + continue; + } + if (arg == "-ignore_redef") { + flag_ignore_redef = true; + continue; + } + if (arg == "-ignore_miss_func") { + flag_ignore_miss_func = true; + continue; + } + if (arg == "-ignore_miss_dir") { + flag_ignore_miss_dir = true; + continue; + } + if (arg == "-setattr" && argidx+1 < args.size()) { + attributes.push_back(RTLIL::escape_id(args[++argidx])); + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + LibertyParser parser(*f); + int cell_count = 0; + + for (auto cell : parser.ast->children) + { + if (cell->id != "cell" || cell->args.size() != 1) + continue; + + std::string cell_name = RTLIL::escape_id(cell->args.at(0)); + + if (design->has(cell_name)) { + if (flag_ignore_redef) + continue; + log_error("Duplicate definition of cell/module %s.\n", RTLIL::unescape_id(cell_name).c_str()); + } + + // log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name).c_str()); + + RTLIL::Module *module = new RTLIL::Module; + module->name = cell_name; + + if (flag_lib) + module->set_bool_attribute("\\blackbox"); + + for (auto &attr : attributes) + module->attributes[attr] = 1; + + for (auto node : cell->children) + if (node->id == "pin" && node->args.size() == 1) { + LibertyAst *dir = node->find("direction"); + if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal")) + { + if (!flag_ignore_miss_dir) + { + log_error("Missing or invalid direction for pin %s of cell %s.\n", node->args.at(0).c_str(), log_id(module->name)); + } else { + log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0).c_str()); + delete module; + goto skip_cell; + } + } + if (!flag_lib || dir->value != "internal") + module->addWire(RTLIL::escape_id(node->args.at(0))); + } + + for (auto node : cell->children) + { + if (!flag_lib) { + 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 (node->id == "pin" && node->args.size() == 1) + { + LibertyAst *dir = node->find("direction"); + + if (flag_lib && dir->value == "internal") + continue; + + RTLIL::Wire *wire = module->wires_.at(RTLIL::escape_id(node->args.at(0))); + + if (dir && dir->value == "inout") { + wire->port_input = true; + wire->port_output = true; + } + + if (dir && dir->value == "input") { + wire->port_input = true; + continue; + } + + if (dir && dir->value == "output") + wire->port_output = true; + + if (flag_lib) + continue; + + LibertyAst *func = node->find("function"); + if (func == NULL) + { + if (!flag_ignore_miss_func) + { + log_error("Missing function on output %s of cell %s.\n", log_id(wire->name), log_id(module->name)); + } else { + log("Ignoring cell %s with missing function on output %s.\n", log_id(module->name), log_id(wire->name)); + delete module; + goto skip_cell; + } + } + + RTLIL::SigSpec out_sig = parse_func_expr(module, func->value.c_str()); + module->connect(RTLIL::SigSig(wire, out_sig)); + } + } + + module->fixup_ports(); + design->add(module); + cell_count++; +skip_cell:; + } + + log("Imported %d cell types from liberty file.\n", cell_count); + } +} LibertyFrontend; + +YOSYS_NAMESPACE_END + diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc new file mode 100644 index 000000000..13f242c4b --- /dev/null +++ b/frontends/verific/Makefile.inc @@ -0,0 +1,16 @@ + +OBJS += frontends/verific/verific.o + +ifeq ($(ENABLE_VERIFIC),1) + +EXTRA_TARGETS += share/verific + +share/verific: + $(P) rm -rf share/verific.new + $(Q) mkdir -p share/verific.new + $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs share/verific.new/vhdl_vdbs_1993 + $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008 share/verific.new/vhdl_vdbs_2008 + $(Q) mv share/verific.new share/verific + +endif + diff --git a/frontends/verific/build_amd64.txt b/frontends/verific/build_amd64.txt new file mode 100644 index 000000000..94615d38d --- /dev/null +++ b/frontends/verific/build_amd64.txt @@ -0,0 +1,31 @@ + +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_QT4 := 0 +ENABLE_ABC := 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:amd64 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/test_navre.ys b/frontends/verific/test_navre.ys new file mode 100644 index 000000000..a56b725ac --- /dev/null +++ b/frontends/verific/test_navre.ys @@ -0,0 +1,18 @@ +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 new file mode 100644 index 000000000..d0f148386 --- /dev/null +++ b/frontends/verific/verific.cc @@ -0,0 +1,949 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <dirent.h> + +USING_YOSYS_NAMESPACE + +#ifdef YOSYS_ENABLE_VERIFIC + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverloaded-virtual" + +#include "veri_file.h" +#include "vhdl_file.h" +#include "VeriModule.h" +#include "VhdlUnits.h" +#include "DataBase.h" +#include "Message.h" + +#pragma clang diagnostic pop + +#ifdef VERIFIC_NAMESPACE +using namespace Verific ; +#endif + +static 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] ", + msg_type == VERIFIC_NONE ? "NONE" : + msg_type == VERIFIC_ERROR ? "ERROR" : + msg_type == VERIFIC_WARNING ? "WARNING" : + msg_type == VERIFIC_IGNORE ? "IGNORE" : + 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"); +} + +static void import_attributes(std::map<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj) +{ + MapIter mi; + Att *attr; + + if (obj->Linefile()) + attributes["\\src"] = stringf("%s:%d", LineFile::GetFileName(obj->Linefile()), LineFile::GetLineNo(obj->Linefile())); + + // FIXME: Parse numeric attributes + FOREACH_ATTRIBUTE(obj, mi, attr) + attributes[RTLIL::escape_id(attr->Key())] = RTLIL::Const(std::string(attr->Value())); +} + +static RTLIL::SigSpec operatorInput(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map) +{ + RTLIL::SigSpec sig; + for (int i = int(inst->InputSize())-1; i >= 0; i--) + if (inst->GetInputBit(i)) + sig.append(net_map.at(inst->GetInputBit(i))); + else + sig.append(RTLIL::State::Sz); + return sig; +} + +static RTLIL::SigSpec operatorInput1(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map) +{ + RTLIL::SigSpec sig; + for (int i = int(inst->Input1Size())-1; i >= 0; i--) + if (inst->GetInput1Bit(i)) + sig.append(net_map.at(inst->GetInput1Bit(i))); + else + sig.append(RTLIL::State::Sz); + return sig; +} + +static RTLIL::SigSpec operatorInput2(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map) +{ + RTLIL::SigSpec sig; + for (int i = int(inst->Input2Size())-1; i >= 0; i--) + if (inst->GetInput2Bit(i)) + sig.append(net_map.at(inst->GetInput2Bit(i))); + else + sig.append(RTLIL::State::Sz); + return sig; +} + +static RTLIL::SigSpec operatorInport(Instance *inst, const char *portname, std::map<Net*, RTLIL::SigBit> &net_map) +{ + PortBus *portbus = inst->View()->GetPortBus(portname); + if (portbus) { + RTLIL::SigSpec sig; + for (unsigned i = 0; i < portbus->Size(); i++) { + Net *net = inst->GetNet(portbus->ElementAtIndex(i)); + if (net) { + if (net->IsGnd()) + sig.append(RTLIL::State::S0); + else if (net->IsPwr()) + sig.append(RTLIL::State::S1); + else + sig.append(net_map.at(net)); + } else + sig.append(RTLIL::State::Sz); + } + return sig; + } else { + Port *port = inst->View()->GetPort(portname); + log_assert(port != NULL); + Net *net = inst->GetNet(port); + return net_map.at(net); + } +} + +static RTLIL::SigSpec operatorOutput(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map, RTLIL::Module *module) +{ + 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))); + dummy_wire = NULL; + } else { + if (dummy_wire == NULL) + dummy_wire = module->addWire(NEW_ID); + else + dummy_wire->width++; + sig.append(RTLIL::SigSpec(dummy_wire, dummy_wire->width - 1)); + } + return sig; +} + +static bool import_netlist_instance_gates(RTLIL::Module *module, std::map<Net*, RTLIL::SigBit> &net_map, Instance *inst) +{ + 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())); + 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())); + 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())); + 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())); + 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())); + return true; + } + + if (inst->Type() == PRIM_INV) { + module->addNotGate(RTLIL::escape_id(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())); + 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())); + 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 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->addAndGate(NEW_ID, tmp1, c, tmp2); + module->addAndGate(NEW_ID, a, b, tmp3); + module->addOrGate(NEW_ID, tmp2, tmp3, x); + return true; + } + + if (inst->Type() == PRIM_DFFRS) + { + 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())); + 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); + 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); + 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())); + return true; + } + + return false; +} + +static bool import_netlist_instance_cells(RTLIL::Module *module, std::map<Net*, RTLIL::SigBit> &net_map, Instance *inst) +{ + 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())); + 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())); + 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())); + 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())); + 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())); + 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())); + return true; + } + + if (inst->Type() == PRIM_INV) { + module->addNot(RTLIL::escape_id(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())); + 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())); + 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); + 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); + return true; + } + + if (inst->Type() == PRIM_DFFRS) + { + 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())); + 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); + 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); + 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())); + 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 SIGNED inst->View()->IsSigned() + + if (inst->Type() == OPER_ADDER) { + RTLIL::SigSpec out = OUT; + if (inst->GetCout() != NULL) + out.append(net_map.at(inst->GetCout())); + if (inst->GetCin()->IsGnd()) { + module->addAdd(RTLIL::escape_id(inst->Name()), IN1, IN2, out, SIGNED); + } else { + RTLIL::SigSpec tmp = module->addWire(NEW_ID, SIZE(out)); + module->addAdd(NEW_ID, IN1, IN2, tmp, SIGNED); + module->addAdd(RTLIL::escape_id(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); + return true; + } + + if (inst->Type() == OPER_DIVIDER) { + module->addDiv(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + return true; + } + + if (inst->Type() == OPER_MODULO) { + module->addMod(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + return true; + } + + if (inst->Type() == OPER_REMAINDER) { + module->addMod(RTLIL::escape_id(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); + return true; + } + + if (inst->Type() == OPER_SHIFT_RIGHT) { + Net *net_cin = inst->GetCin(); + Net *net_a_msb = inst->GetInput1Bit(0); + if (net_cin->IsGnd()) + module->addShr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, false); + else if (net_cin == net_a_msb) + module->addSshr(RTLIL::escape_id(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); + return true; + } + + if (inst->Type() == OPER_REDUCE_OR) { + module->addReduceOr(RTLIL::escape_id(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); + return true; + } + + if (inst->Type() == OPER_REDUCE_XNOR) { + module->addReduceXnor(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED); + 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); + else if (net_cin->IsPwr()) + module->addLe(RTLIL::escape_id(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); + return true; + } + + if (inst->Type() == OPER_WIDE_OR) { + module->addOr(RTLIL::escape_id(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); + return true; + } + + if (inst->Type() == OPER_WIDE_XNOR) { + module->addXnor(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + return true; + } + + if (inst->Type() == OPER_WIDE_BUF) { + module->addPos(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED); + return true; + } + + if (inst->Type() == OPER_WIDE_INV) { + module->addNot(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED); + return true; + } + + if (inst->Type() == OPER_MINUS) { + module->addSub(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + return true; + } + + if (inst->Type() == OPER_UMINUS) { + module->addNeg(RTLIL::escape_id(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); + return true; + } + + if (inst->Type() == OPER_NEQUAL) { + module->addNe(RTLIL::escape_id(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); + 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); + 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 (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); + else + module->addDffsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), sig_set, sig_reset, IN, OUT); + return true; + } + + #undef IN + #undef IN1 + #undef IN2 + #undef OUT + #undef SIGNED + + return false; +} + +static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo, bool mode_gates) +{ + std::string module_name = nl->IsOperator() ? std::string("$verific$") + nl->Owner()->Name() : RTLIL::escape_id(nl->Owner()->Name()); + + 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->name = module_name; + design->add(module); + + log("Importing module %s.\n", RTLIL::id2cstr(module->name)); + + std::map<Net*, RTLIL::SigBit> net_map; + + SetIter si; + MapIter mi, mi2; + Port *port; + PortBus *portbus; + Net *net; + NetBus *netbus; + Instance *inst; + PortRef *pr; + + FOREACH_PORT_OF_NETLIST(nl, mi, port) + { + if (port->Bus()) + continue; + + // log(" importing port %s.\n", port->Name()); + + RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(port->Name())); + import_attributes(wire->attributes, port); + + wire->port_id = nl->IndexOf(port) + 1; + + if (port->GetDir() == DIR_INOUT || port->GetDir() == DIR_IN) + wire->port_input = true; + if (port->GetDir() == DIR_INOUT || port->GetDir() == DIR_OUT) + wire->port_output = true; + + if (port->GetNet()) { + net = port->GetNet(); + if (net_map.count(net) == 0) + net_map[net] = wire; + else if (wire->port_input) + module->connect(net_map.at(net), wire); + else + module->connect(wire, net_map.at(net)); + } + } + + FOREACH_PORTBUS_OF_NETLIST(nl, mi, portbus) + { + // log(" importing portbus %s.\n", portbus->Name()); + + RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(portbus->Name()), portbus->Size()); + wire->start_offset = std::min(portbus->LeftIndex(), portbus->RightIndex()); + import_attributes(wire->attributes, portbus); + + if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN) + wire->port_input = true; + if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_OUT) + wire->port_output = true; + + for (int i = portbus->LeftIndex();; i += portbus->IsUp() ? +1 : -1) { + if (portbus->ElementAtIndex(i) && portbus->ElementAtIndex(i)->GetNet()) { + net = portbus->ElementAtIndex(i)->GetNet(); + RTLIL::SigBit bit(wire, i - wire->start_offset); + if (net_map.count(net) == 0) + net_map[net] = bit; + else if (wire->port_input) + module->connect(net_map.at(net), bit); + else + module->connect(bit, net_map.at(net)); + } + if (i == portbus->RightIndex()) + break; + } + } + + module->fixup_ports(); + + FOREACH_NET_OF_NETLIST(nl, mi, net) + { + if (net->IsRamNet()) + { + RTLIL::Memory *memory = new RTLIL::Memory; + memory->name = RTLIL::escape_id(net->Name()); + log_assert(module->count_id(memory->name) == 0); + module->memories[memory->name] = memory; + + int number_of_bits = net->Size(); + int bits_in_word = number_of_bits; + FOREACH_PORTREF_OF_NET(net, si, pr) { + if (pr->GetInst()->Type() == OPER_READ_PORT) { + bits_in_word = std::min<int>(bits_in_word, pr->GetInst()->OutputSize()); + continue; + } + if (pr->GetInst()->Type() == OPER_WRITE_PORT || pr->GetInst()->Type() == OPER_CLOCKED_WRITE_PORT) { + bits_in_word = std::min<int>(bits_in_word, pr->GetInst()->Input2Size()); + continue; + } + log_error("Verific RamNet %s is connected to unsupported instance type %s (%s).\n", + net->Name(), pr->GetInst()->View()->Owner()->Name(), pr->GetInst()->Name()); + } + + memory->width = bits_in_word; + memory->size = number_of_bits / bits_in_word; + continue; + } + + if (net_map.count(net)) { + // 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(RTLIL::escape_id(net->Name())); + RTLIL::Wire *wire = module->addWire(wire_name); + import_attributes(wire->attributes, net); + + net_map[net] = wire; + } + + FOREACH_NETBUS_OF_NETLIST(nl, mi, netbus) + { + bool found_new_net = false; + for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) { + net = netbus->ElementAtIndex(i); + if (net_map.count(net) == 0) + found_new_net = true; + if (i == netbus->RightIndex()) + break; + } + + if (found_new_net) + { + // log(" importing netbus %s.\n", netbus->Name()); + + RTLIL::IdString wire_name = module->uniquify(RTLIL::escape_id(netbus->Name())); + RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size()); + wire->start_offset = std::min(netbus->LeftIndex(), netbus->RightIndex()); + import_attributes(wire->attributes, netbus); + + for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) { + if (netbus->ElementAtIndex(i)) { + net = netbus->ElementAtIndex(i); + RTLIL::SigBit bit(wire, i - wire->start_offset); + if (net_map.count(net) == 0) + net_map[net] = bit; + else + module->connect(bit, net_map.at(net)); + } + if (i == netbus->RightIndex()) + break; + } + } + else + { + // log(" skipping netbus %s.\n", netbus->Name()); + } + } + + FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst) + { + // log(" importing cell %s (%s).\n", inst->Name(), inst->View()->Owner()->Name()); + + if (inst->Type() == PRIM_PWR) { + 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); + continue; + } + + if (inst->Type() == PRIM_X) { + 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); + 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()); + + RTLIL::SigSpec addr = operatorInput1(inst, net_map); + RTLIL::SigSpec data = operatorOutput(inst, net_map, module); + + 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"] = SIZE(addr); + cell->parameters["\\WIDTH"] = SIZE(data); + cell->setPort("\\CLK", RTLIL::State::S0); + cell->setPort("\\ADDR", addr); + cell->setPort("\\DATA", data); + continue; + } + + if (inst->Type() == OPER_WRITE_PORT || inst->Type() == OPER_CLOCKED_WRITE_PORT) + { + RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetOutput()->Name())); + if (memory->width != int(inst->Input2Size())) + log_error("Import of asymetric memories from Verific is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name()); + + RTLIL::SigSpec addr = operatorInput1(inst, net_map); + RTLIL::SigSpec data = operatorInput2(inst, net_map); + + RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(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"] = SIZE(addr); + cell->parameters["\\WIDTH"] = SIZE(data); + cell->setPort("\\EN", RTLIL::SigSpec(net_map.at(inst->GetControl())).repeat(SIZE(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())); + } + continue; + } + + if (!mode_gates) { + if (import_netlist_instance_cells(module, net_map, inst)) + continue; + if (inst->IsOperator()) + 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)) + continue; + } + + if (inst->IsPrimitive()) + log_error("Unsupported Verific primitive: %s\n", inst->View()->Owner()->Name()); + + nl_todo.insert(inst->View()); + + RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), inst->IsOperator() ? + std::string("$verific$") + inst->View()->Owner()->Name() : RTLIL::escape_id(inst->View()->Owner()->Name())); + + FOREACH_PORTREF_OF_INST(inst, mi2, pr) { + // 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()) { + port_name = pr->GetPort()->Bus()->Name(); + port_offset = pr->GetPort()->Bus()->IndexOf(pr->GetPort()) - + std::min(pr->GetPort()->Bus()->LeftIndex(), pr->GetPort()->Bus()->RightIndex()); + } + RTLIL::SigSpec conn; + if (cell->hasPort(RTLIL::escape_id(port_name))) + conn = cell->getPort(RTLIL::escape_id(port_name)); + while (SIZE(conn) <= port_offset) { + if (pr->GetPort()->GetDir() != DIR_IN) + conn.append(module->addWire(NEW_ID, port_offset - SIZE(conn))); + conn.append(RTLIL::State::Sz); + } + conn.replace(port_offset, net_map.at(pr->GetNet())); + cell->setPort(RTLIL::escape_id(port_name), conn); + } + } +} + +#endif /* YOSYS_ENABLE_VERIFIC */ + +YOSYS_NAMESPACE_BEGIN + +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} <verilog-file>..\n"); + log("\n"); + log("Load the specified Verilog/SystemVerilog files into Verific.\n"); + log("\n"); + log("\n"); + log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008} <vhdl-file>..\n"); + log("\n"); + log("Load the specified VHDL files into Verific.\n"); + log("\n"); + log("\n"); + log(" verific -import [-gates] {-all | <top-module>..}\n"); + log("\n"); + log("Elaborate the design for the sepcified 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"); + } +#ifdef YOSYS_ENABLE_VERIFIC + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("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; + } + + 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; + } + + if (args.size() > 1 && args[1] == "-sv2005") { + for (size_t argidx = 2; argidx < args.size(); argidx++) + if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG_2005)) + log_cmd_error("Reading `%s' in SYSTEM_VERILOG_2005 mode failed.\n", args[argidx].c_str()); + return; + } + + if (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 (args.size() > 1 && args[1] == "-sv") { + for (size_t argidx = 2; argidx < args.size(); argidx++) + if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG)) + log_cmd_error("Reading `%s' in SYSTEM_VERILOG mode failed.\n", args[argidx].c_str()); + return; + } + + if (args.size() > 1 && args[1] == "-vhdl87") { + 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_87)) + log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", args[argidx].c_str()); + return; + } + + 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()); + 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)) + log_cmd_error("Reading `%s' in VHDL_2K mode failed.\n", args[argidx].c_str()); + return; + } + + if (args.size() > 1 && args[1] == "-vhdl2008") { + 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)) + log_cmd_error("Reading `%s' in VHDL_2008 mode failed.\n", args[argidx].c_str()); + return; + } + + if (args.size() > 1 && args[1] == "-import") + { + std::set<Netlist*> nl_todo, nl_done; + bool mode_all = false, mode_gates = false; + + size_t argidx = 2; + for (; argidx < args.size(); argidx++) { + if (args[argidx] == "-all") { + mode_all = true; + continue; + } + if (args[argidx] == "-gates") { + mode_gates = true; + continue; + } + break; + } + + if (argidx > args.size() && 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()); + } + } + else + if (argidx == args.size()) + log_cmd_error("No top module specified.\n"); + + for (; argidx < args.size(); argidx++) { + if (veri_file::GetModule(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 { + 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()); + } + } + + while (!nl_todo.empty()) { + Netlist *nl = *nl_todo.begin(); + if (nl_done.count(nl) == 0) + import_netlist(design, nl, nl_todo, mode_gates); + nl_todo.erase(nl); + nl_done.insert(nl); + } + + Libset::Reset(); + return; + } + + log_cmd_error("Missing or unsupported mode parameter.\n"); + } +#else /* YOSYS_ENABLE_VERIFIC */ + virtual void execute(std::vector<std::string>, RTLIL::Design *) { + log_cmd_error("This version of Yosys is built without Verific support.\n"); + } +#endif +} VerificPass; + +YOSYS_NAMESPACE_END + diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc index 5586b4cc2..49eb320ec 100644 --- a/frontends/verilog/Makefile.inc +++ b/frontends/verilog/Makefile.inc @@ -5,13 +5,13 @@ GENFILES += frontends/verilog/parser.output GENFILES += frontends/verilog/lexer.cc frontends/verilog/parser.tab.cc: frontends/verilog/parser.y - bison -d -r all -b frontends/verilog/parser frontends/verilog/parser.y - mv frontends/verilog/parser.tab.c frontends/verilog/parser.tab.cc + $(P) bison -d -r all -b frontends/verilog/parser frontends/verilog/parser.y + $(Q) mv frontends/verilog/parser.tab.c frontends/verilog/parser.tab.cc frontends/verilog/parser.tab.h: frontends/verilog/parser.tab.cc frontends/verilog/lexer.cc: frontends/verilog/lexer.l - flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l + $(P) flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l OBJS += frontends/verilog/parser.tab.o OBJS += frontends/verilog/lexer.o diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index c95ce5dc4..a81e3010f 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -36,10 +36,11 @@ #include "verilog_frontend.h" #include "kernel/log.h" -#include <assert.h> #include <string.h> #include <math.h> +YOSYS_NAMESPACE_BEGIN + using namespace AST; // divide an arbitrary length decimal number by two and return the rest @@ -47,11 +48,13 @@ static int my_decimal_div_by_two(std::vector<uint8_t> &digits) { int carry = 0; for (size_t i = 0; i < digits.size(); i++) { - assert(digits[i] < 10); + log_assert(digits[i] < 10); digits[i] += carry * 10; carry = digits[i] % 2; digits[i] /= 2; } + while (!digits.empty() && !digits.front()) + digits.erase(digits.begin()); return carry; } @@ -90,10 +93,15 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le if (base == 10) { data.clear(); - if (len_in_bits < 0) - len_in_bits = ceil(digits.size()/log10(2)); - for (int i = 0; i < len_in_bits; i++) - data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + if (len_in_bits < 0) { + while (!digits.empty()) + data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + while (data.size() < 32) + data.push_back(RTLIL::S0); + } else { + for (int i = 0; i < len_in_bits; i++) + data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + } return; } @@ -151,20 +159,24 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type) str = code.c_str(); char *endptr; - long intval = strtol(str, &endptr, 10); + long len_in_bits = strtol(str, &endptr, 10); + + // Simple base-10 integer + if (*endptr == 0) { + std::vector<RTLIL::State> data; + my_strtobin(data, str, -1, 10, case_type); + if (data.back() == RTLIL::S1) + data.push_back(RTLIL::S0); + return AstNode::mkconst_bits(data, true); + } - // Simple 32 bit integer - if (*endptr == 0) - return AstNode::mkconst_int(intval, true); - // unsized constant if (str == endptr) - intval = -1; + len_in_bits = -1; // The "<bits>'s?[bodh]<digits>" syntax if (*endptr == '\'') { - int len_in_bits = intval; std::vector<RTLIL::State> data; bool is_signed = false; if (*(endptr+1) == 's') { @@ -188,9 +200,17 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type) default: return NULL; } + if (len_in_bits < 0) { + if (is_signed && data.back() == RTLIL::S1) + data.push_back(RTLIL::S0); + while (data.size() < 32) + data.push_back(RTLIL::S0); + } return AstNode::mkconst_bits(data, is_signed); } return NULL; } +YOSYS_NAMESPACE_END + diff --git a/frontends/verilog/lexer.l b/frontends/verilog/lexer.l index 81167cf4e..c9302aba8 100644 --- a/frontends/verilog/lexer.l +++ b/frontends/verilog/lexer.l @@ -34,18 +34,37 @@ %{ +#ifdef __clang__ +// bison generates code using the 'register' storage class specifier +#pragma clang diagnostic ignored "-Wdeprecated-register" +#endif + #include "kernel/log.h" #include "verilog_frontend.h" #include "frontends/ast/ast.h" #include "parser.tab.h" +USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; +YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { std::vector<std::string> fn_stack; std::vector<int> ln_stack; } +YOSYS_NAMESPACE_END + +#define SV_KEYWORD(_tok) \ + if (sv_mode) return _tok; \ + log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\ + "recognized unless read_verilog is called with -sv!\n", yytext, \ + AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); \ + frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); \ + return TOK_ID; + +#define YY_INPUT(buf,result,max_size) \ + result = lexin->readsome(buf, max_size); %} @@ -58,29 +77,53 @@ namespace VERILOG_FRONTEND { %x STRING %x SYNOPSYS_TRANSLATE_OFF %x SYNOPSYS_FLAGS +%x IMPORT_DPI %% -"`file_push "[^\n]* { +<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_push "[^\n]* { fn_stack.push_back(current_filename); ln_stack.push_back(frontend_verilog_yyget_lineno()); current_filename = yytext+11; frontend_verilog_yyset_lineno(0); } -"`file_pop"[^\n]*\n { +<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_pop"[^\n]*\n { current_filename = fn_stack.back(); fn_stack.pop_back(); frontend_verilog_yyset_lineno(ln_stack.back()); ln_stack.pop_back(); } +<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`line"[ \t]+[^ \t\r\n]+[ \t]+\"[^ \r\n]+\"[^\r\n]*\n { + char *p = yytext + 5; + while (*p == ' ' || *p == '\t') p++; + frontend_verilog_yyset_lineno(atoi(p)); + while (*p && *p != ' ' && *p != '\t') p++; + while (*p == ' ' || *p == '\t') p++; + char *q = *p ? p + 1 : p; + while (*q && *q != '"') q++; + current_filename = std::string(p).substr(1, q-p-1); +} + "`file_notfound "[^\n]* { log_error("Can't open include file `%s'!\n", yytext + 15); } "`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */ +"`default_nettype"[ \t]+[^ \t\r\n/]+ { + char *p = yytext; + while (*p != 0 && *p != ' ' && *p != '\t') p++; + while (*p == ' ' || *p == '\t') p++; + if (!strcmp(p, "none")) + VERILOG_FRONTEND::default_nettype_wire = false; + else if (!strcmp(p, "wire")) + VERILOG_FRONTEND::default_nettype_wire = true; + else + frontend_verilog_yyerror("Unsupported default nettype: %s", p); +} + "`"[a-zA-Z_$][a-zA-Z0-9_$]* { frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext); } @@ -112,8 +155,17 @@ namespace VERILOG_FRONTEND { "default" { return TOK_DEFAULT; } "generate" { return TOK_GENERATE; } "endgenerate" { return TOK_ENDGENERATE; } +"while" { return TOK_WHILE; } +"repeat" { return TOK_REPEAT; } -"assert"([ \t\r\n]+"property")? { return TOK_ASSERT; } +"always_comb" { SV_KEYWORD(TOK_ALWAYS); } +"always_ff" { SV_KEYWORD(TOK_ALWAYS); } +"always_latch" { SV_KEYWORD(TOK_ALWAYS); } + +"assert" { SV_KEYWORD(TOK_ASSERT); } +"property" { SV_KEYWORD(TOK_PROPERTY); } +"logic" { SV_KEYWORD(TOK_REG); } +"bit" { SV_KEYWORD(TOK_REG); } "input" { return TOK_INPUT; } "output" { return TOK_OUTPUT; } @@ -123,6 +175,7 @@ namespace VERILOG_FRONTEND { "integer" { return TOK_INTEGER; } "signed" { return TOK_SIGNED; } "genvar" { return TOK_GENVAR; } +"real" { return TOK_REAL; } [0-9]+ { frontend_verilog_yylval.string = new std::string(yytext); @@ -134,6 +187,16 @@ namespace VERILOG_FRONTEND { return TOK_CONST; } +[0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)? { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_REALVAL; +} + +[0-9][0-9_]*[eE][-+]?[0-9_]+ { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_REALVAL; +} + \" { BEGIN(STRING); } <STRING>\\. { yymore(); } <STRING>\" { @@ -215,6 +278,27 @@ supply1 { return TOK_SUPPLY1; } <SYNOPSYS_FLAGS>. /* ignore everything else */ <SYNOPSYS_FLAGS>"*/" { BEGIN(0); } +import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { + BEGIN(IMPORT_DPI); + return TOK_DPI_FUNCTION; +} + +<IMPORT_DPI>[a-zA-Z_$][a-zA-Z0-9_$]* { + frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); + return TOK_ID; +} + +<IMPORT_DPI>[ \t\r\n] /* ignore whitespaces */ + +<IMPORT_DPI>";" { + BEGIN(0); + return *yytext; +} + +<IMPORT_DPI>. { + return *yytext; +} + "\\"[^ \t\r\n]+ { frontend_verilog_yylval.string = new std::string(yytext); return TOK_ID; diff --git a/frontends/verilog/parser.y b/frontends/verilog/parser.y index 5b6bf58c2..a9f69a49c 100644 --- a/frontends/verilog/parser.y +++ b/frontends/verilog/parser.y @@ -35,13 +35,15 @@ %{ #include <list> -#include <assert.h> +#include <string.h> #include "verilog_frontend.h" #include "kernel/log.h" +USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; +YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { int port_counter; std::map<std::string, int> port_stubs; @@ -53,7 +55,12 @@ namespace VERILOG_FRONTEND { struct AstNode *current_ast, *current_ast_mod; int current_function_or_task_port_id; std::vector<char> case_type_stack; + bool do_not_require_port_stubs; + bool default_nettype_wire; + bool sv_mode; + std::istream *lexin; } +YOSYS_NAMESPACE_END static void append_attr(AstNode *ast, std::map<std::string, AstNode*> *al) { @@ -83,30 +90,31 @@ static void free_attr(std::map<std::string, AstNode*> *al) %} -%name-prefix="frontend_verilog_yy" +%name-prefix "frontend_verilog_yy" %union { std::string *string; - struct AstNode *ast; - std::map<std::string, AstNode*> *al; + struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast; + std::map<std::string, YOSYS_NAMESPACE_PREFIX AST::AstNode*> *al; bool boolean; } -%token <string> TOK_STRING TOK_ID TOK_CONST TOK_PRIMITIVE +%token <string> TOK_STRING TOK_ID TOK_CONST 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_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL -%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR -%token TOK_POSEDGE TOK_NEGEDGE TOK_OR +%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_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT %token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK -%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR +%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 +%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_PROPERTY -%type <ast> wire_type range non_opt_range expr basic_expr concat_list rvalue lvalue lvalue_concat_list +%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int +%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list %type <string> opt_label tok_prim_wrapper hierarchical_id %type <boolean> opt_signed %type <al> attr @@ -130,14 +138,21 @@ static void free_attr(std::map<std::string, AstNode*> *al) %% -input: - module input | - defattr input | - /* empty */ { - for (auto &it : default_attr_list) - delete it.second; - default_attr_list.clear(); - }; +input: { + ast_stack.push_back(current_ast); +} design { + ast_stack.pop_back(); + log_assert(SIZE(ast_stack) == 0); + for (auto &it : default_attr_list) + delete it.second; + default_attr_list.clear(); +}; + +design: + module design | + defattr design | + task_func_decl design | + /* empty */; attr: { @@ -205,10 +220,11 @@ hierarchical_id: module: attr TOK_MODULE TOK_ID { + do_not_require_port_stubs = false; AstNode *mod = new AstNode(AST_MODULE); - current_ast->children.push_back(mod); - current_ast_mod = mod; + ast_stack.back()->children.push_back(mod); ast_stack.push_back(mod); + current_ast_mod = mod; port_stubs.clear(); port_counter = 0; mod->str = *$3; @@ -219,7 +235,8 @@ module: frontend_verilog_yyerror("Missing details for module port `%s'.", port_stubs.begin()->first.c_str()); ast_stack.pop_back(); - assert(ast_stack.size() == 0); + log_assert(ast_stack.size() == 1); + current_ast_mod = NULL; }; module_para_opt: @@ -288,7 +305,10 @@ module_arg: ast_stack.back()->children.push_back(node); append_attr(node, $1); delete $4; - } module_arg_opt_assignment; + } module_arg_opt_assignment | + '.' '.' '.' { + do_not_require_port_stubs = true; + }; wire_type: { @@ -320,6 +340,7 @@ wire_type_token: astbuf3->is_reg = true; astbuf3->range_left = 31; astbuf3->range_right = 0; + astbuf3->is_signed = true; } | TOK_GENVAR { astbuf3->type = AST_GENVAR; @@ -352,6 +373,15 @@ non_opt_range: $$->children.push_back($2); }; +non_opt_multirange: + non_opt_range non_opt_range { + $$ = new AstNode(AST_MULTIRANGE, $1, $2); + } | + non_opt_multirange non_opt_range { + $$ = $1; + $$->children.push_back($2); + }; + range: non_opt_range { $$ = $1; @@ -360,44 +390,120 @@ range: $$ = NULL; }; +range_or_multirange: + range { $$ = $1; } | + non_opt_multirange { $$ = $1; }; + +range_or_signed_int: + range { + $$ = $1; + } | + TOK_INTEGER { + $$ = new AstNode(AST_RANGE); + $$->children.push_back(AstNode::mkconst_int(31, true)); + $$->children.push_back(AstNode::mkconst_int(0, true)); + $$->is_signed = true; + }; + module_body: module_body module_body_stmt | + /* the following line makes the generate..endgenrate keywords optional */ + module_body gen_stmt | /* 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; + always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property; task_func_decl: - TOK_TASK TOK_ID ';' { + attr TOK_DPI_FUNCTION TOK_ID TOK_ID { + current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$3), AstNode::mkconst_str(*$4)); + current_function_or_task->str = *$4; + append_attr(current_function_or_task, $1); + ast_stack.back()->children.push_back(current_function_or_task); + delete $3; + delete $4; + } opt_dpi_function_args ';' { + current_function_or_task = NULL; + } | + attr TOK_DPI_FUNCTION TOK_ID '=' TOK_ID TOK_ID { + current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$5), AstNode::mkconst_str(*$3)); + current_function_or_task->str = *$6; + append_attr(current_function_or_task, $1); + ast_stack.back()->children.push_back(current_function_or_task); + delete $3; + delete $5; + delete $6; + } opt_dpi_function_args ';' { + current_function_or_task = NULL; + } | + attr TOK_DPI_FUNCTION TOK_ID ':' TOK_ID '=' TOK_ID TOK_ID { + current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$7), AstNode::mkconst_str(*$3 + ":" + RTLIL::unescape_id(*$5))); + current_function_or_task->str = *$8; + append_attr(current_function_or_task, $1); + ast_stack.back()->children.push_back(current_function_or_task); + delete $3; + delete $5; + delete $7; + delete $8; + } opt_dpi_function_args ';' { + current_function_or_task = NULL; + } | + attr TOK_TASK TOK_ID ';' { current_function_or_task = new AstNode(AST_TASK); - current_function_or_task->str = *$2; + current_function_or_task->str = *$3; + 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 $2; + delete $3; } task_func_body TOK_ENDTASK { current_function_or_task = NULL; ast_stack.pop_back(); } | - TOK_FUNCTION opt_signed range TOK_ID ';' { + attr TOK_FUNCTION opt_signed range_or_signed_int TOK_ID ';' { current_function_or_task = new AstNode(AST_FUNCTION); - current_function_or_task->str = *$4; + current_function_or_task->str = *$5; + 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); - if ($3 != NULL) - outreg->children.push_back($3); - outreg->str = *$4; - outreg->is_signed = $2; + 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; + } current_function_or_task->children.push_back(outreg); current_function_or_task_port_id = 1; - delete $4; + delete $5; } task_func_body TOK_ENDFUNCTION { current_function_or_task = NULL; ast_stack.pop_back(); }; +dpi_function_arg: + TOK_ID TOK_ID { + current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); + delete $1; + delete $2; + } | + TOK_ID { + current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); + delete $1; + }; + +opt_dpi_function_args: + '(' dpi_function_args ')' | + /* empty */; + +dpi_function_args: + dpi_function_args ',' dpi_function_arg | + dpi_function_args ',' | + dpi_function_arg | + /* empty */; + opt_signed: TOK_SIGNED { $$ = true; @@ -422,6 +528,14 @@ param_integer: astbuf1->children.push_back(new AstNode(AST_RANGE)); astbuf1->children.back()->children.push_back(AstNode::mkconst_int(31, true)); astbuf1->children.back()->children.push_back(AstNode::mkconst_int(0, true)); + astbuf1->is_signed = true; + } | /* empty */; + +param_real: + TOK_REAL { + if (astbuf1->children.size() != 1) + frontend_verilog_yyerror("Syntax error."); + astbuf1->children.push_back(new AstNode(AST_REALVALUE)); } | /* empty */; param_range: @@ -437,7 +551,7 @@ param_decl: TOK_PARAMETER { astbuf1 = new AstNode(AST_PARAMETER); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - } param_signed param_integer param_range param_decl_list ';' { + } param_signed param_integer param_real param_range param_decl_list ';' { delete astbuf1; }; @@ -445,7 +559,7 @@ localparam_decl: TOK_LOCALPARAM { astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - } param_signed param_integer param_range param_decl_list ';' { + } param_signed param_integer param_real param_range param_decl_list ';' { delete astbuf1; }; @@ -533,7 +647,7 @@ wire_name_and_opt_assign: }; wire_name: - TOK_ID range { + TOK_ID range_or_multirange { AstNode *node = astbuf1->clone(); node->str = *$1; append_attr_clone(node, albuf); @@ -552,6 +666,9 @@ wire_name: node->children.push_back($2); } if (current_function_or_task == NULL) { + if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { + port_stubs[*$1] = ++port_counter; + } if (port_stubs.count(*$1) != 0) { if (!node->is_input && !node->is_output) frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $1->c_str()); @@ -563,12 +680,11 @@ wire_name: if (node->is_input || node->is_output) frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str()); } - ast_stack.back()->children.push_back(node); } else { if (node->is_input || node->is_output) node->port_id = current_function_or_task_port_id++; - current_function_or_task->children.push_back(node); } + ast_stack.back()->children.push_back(node); delete $1; }; @@ -621,6 +737,13 @@ single_cell: astbuf2->str = *$1; delete $1; ast_stack.back()->children.push_back(astbuf2); + } '(' cell_port_list ')' | + TOK_ID non_opt_range { + astbuf2 = astbuf1->clone(); + if (astbuf2->type != AST_PRIMITIVE) + astbuf2->str = *$1; + delete $1; + ast_stack.back()->children.push_back(new AstNode(AST_CELLARRAY, $2, astbuf2)); } '(' cell_port_list ')'; prim_list: @@ -753,6 +876,11 @@ assert: ast_stack.back()->children.push_back(new AstNode(AST_ASSERT, $3)); }; +assert_property: + TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_ASSERT, $4)); + }; + simple_behavioral_stmt: lvalue '=' expr { AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3); @@ -808,6 +936,32 @@ behavioral_stmt: ast_stack.pop_back(); ast_stack.pop_back(); } | + attr TOK_WHILE '(' expr ')' { + AstNode *node = new AstNode(AST_WHILE); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $1); + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back($4); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } behavioral_stmt { + ast_stack.pop_back(); + ast_stack.pop_back(); + } | + attr TOK_REPEAT '(' expr ')' { + AstNode *node = new AstNode(AST_REPEAT); + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + append_attr(node, $1); + AstNode *block = new AstNode(AST_BLOCK); + ast_stack.back()->children.push_back($4); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } behavioral_stmt { + ast_stack.pop_back(); + ast_stack.pop_back(); + } | attr TOK_IF '(' expr ')' { AstNode *node = new AstNode(AST_CASE); AstNode *block = new AstNode(AST_BLOCK); @@ -934,8 +1088,8 @@ rvalue: $$->str = *$1; delete $1; } | - hierarchical_id non_opt_range non_opt_range { - $$ = new AstNode(AST_IDENTIFIER, $2, $3); + hierarchical_id non_opt_multirange { + $$ = new AstNode(AST_IDENTIFIER, $2); $$->str = *$1; delete $1; }; @@ -976,9 +1130,12 @@ single_arg: }; module_gen_body: - module_gen_body gen_stmt | + module_gen_body gen_stmt_or_module_body_stmt | /* empty */; +gen_stmt_or_module_body_stmt: + gen_stmt | module_body_stmt; + // this production creates the obligatory if-else shift/reduce conflict gen_stmt: TOK_FOR '(' { @@ -1017,15 +1174,14 @@ gen_stmt: if ($6 != NULL) delete $6; ast_stack.pop_back(); - } | - module_body_stmt; + }; gen_stmt_block: { AstNode *node = new AstNode(AST_GENBLOCK); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); - } gen_stmt { + } gen_stmt_or_module_body_stmt { ast_stack.pop_back(); }; @@ -1073,12 +1229,30 @@ basic_expr: delete $1; delete $2; } | + TOK_CONST TOK_CONST { + $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back()); + if ($$ == NULL || (*$2)[0] != '\'') + log_error("Value conversion failed: `%s%s'\n", $1->c_str(), $2->c_str()); + delete $1; + delete $2; + } | TOK_CONST { $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back()); if ($$ == NULL) log_error("Value conversion failed: `%s'\n", $1->c_str()); delete $1; } | + TOK_REALVAL { + $$ = new AstNode(AST_REALVALUE); + char *p = strdup($1->c_str()), *q; + for (int i = 0, j = 0; !p[j]; j++) + if (p[j] != '_') + p[i++] = p[j], p[i] = 0; + $$->realvalue = strtod(p, &q); + log_assert(*q == 0); + delete $1; + free(p); + } | TOK_STRING { $$ = AstNode::mkconst_str(*$1); delete $1; diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index db53e8c68..f83433219 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -37,7 +37,8 @@ #include <stdarg.h> #include <stdio.h> #include <string.h> -#include <assert.h> + +YOSYS_NAMESPACE_BEGIN static std::list<std::string> output_code; static std::list<std::string> input_buffer; @@ -65,7 +66,7 @@ static char next_char() if (input_buffer.empty()) return 0; - assert(input_buffer_charp <= input_buffer.front().size()); + log_assert(input_buffer_charp <= input_buffer.front().size()); if (input_buffer_charp == input_buffer.front().size()) { input_buffer_charp = 0; input_buffer.pop_front(); @@ -130,6 +131,12 @@ static std::string next_token(bool pass_newline = false) token += ch; } } + if (token == "\"\"" && (ch = next_char()) != 0) { + if (ch == '"') + token += ch; + else + return_char(ch); + } } else if (ch == '/') { @@ -186,7 +193,7 @@ static std::string next_token(bool pass_newline = false) return token; } -static void input_file(FILE *f, std::string filename) +static void input_file(std::istream &f, std::string filename) { char buffer[513]; int rc; @@ -195,14 +202,14 @@ static void input_file(FILE *f, std::string filename) auto it = input_buffer.begin(); input_buffer.insert(it, "`file_push " + filename + "\n"); - while ((rc = fread(buffer, 1, sizeof(buffer)-1, f)) > 0) { + while ((rc = f.readsome(buffer, sizeof(buffer)-1)) > 0) { buffer[rc] = 0; input_buffer.insert(it, buffer); } - input_buffer.insert(it, "`file_pop\n"); + input_buffer.insert(it, "\n`file_pop\n"); } -std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs) +std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs) { std::set<std::string> defines_with_args; std::map<std::string, std::string> defines_map(pre_defines_map); @@ -281,27 +288,28 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m else fn = fn.substr(0, pos) + fn.substr(pos+1); } - FILE *fp = fopen(fn.c_str(), "r"); - if (fp == NULL && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) { + std::ifstream ff; + ff.clear(); + ff.open(fn.c_str()); + if (ff.fail() && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) { // 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 - std::string fn2 = filename.substr(0, filename.rfind('/')+1) + fn; - fp = fopen(fn2.c_str(), "r"); + ff.clear(); + ff.open(filename.substr(0, filename.rfind('/')+1) + fn); } - if (fp == NULL && fn.size() > 0 && fn[0] != '/') { + if (ff.fail() && fn.size() > 0 && fn[0] != '/') { // 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) { - std::string fn2 = incdir + '/' + fn; - fp = fopen(fn2.c_str(), "r"); - if (fp != NULL) break; + ff.clear(); + ff.open(incdir + '/' + fn); + if (!ff.fail()) break; } } - if (fp != NULL) { - input_file(fp, fn); - fclose(fp); - } else - output_code.push_back("`file_notfound " + fn + "\n"); + if (ff.fail()) + output_code.push_back("`file_notfound " + fn); + else + input_file(ff, fn); continue; } @@ -310,12 +318,17 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m std::map<std::string, int> args; skip_spaces(); name = next_token(true); + bool here_doc_mode = false; int newline_count = 0; int state = 0; if (skip_spaces() != "") state = 3; while (!tok.empty()) { tok = next_token(); + if (tok == "\"\"\"") { + here_doc_mode = !here_doc_mode; + continue; + } if (state == 0 && tok == "(") { state = 1; skip_spaces(); @@ -332,9 +345,14 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m if (state != 2) state = 3; if (tok == "\n") { - return_char('\n'); - break; - } + if (here_doc_mode) { + value += " "; + newline_count++; + } else { + return_char('\n'); + break; + } + } else if (tok == "\\") { char ch = next_char(); if (ch == '\n') { @@ -373,7 +391,6 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m } if (tok == "`timescale") { - std::string name; skip_spaces(); while (!tok.empty() && tok != "\n") tok = next_token(true); @@ -429,3 +446,5 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m return output; } +YOSYS_NAMESPACE_END + diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index c70d6f305..c6d4a0b79 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -27,13 +27,11 @@ */ #include "verilog_frontend.h" -#include "kernel/register.h" -#include "kernel/log.h" +#include "kernel/yosys.h" #include "libs/sha1/sha1.h" -#include <sstream> #include <stdarg.h> -#include <assert.h> +YOSYS_NAMESPACE_BEGIN using namespace VERILOG_FRONTEND; // use the Verilog bison/flex parser to generate an AST and use AST::process() to convert it to RTLIL @@ -47,11 +45,15 @@ struct VerilogFrontend : public Frontend { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" read_verilog [filename]\n"); + log(" read_verilog [options] [filename]\n"); log("\n"); log("Load modules from a verilog file to the current design. A large subset of\n"); log("Verilog-2005 is supported.\n"); log("\n"); + log(" -sv\n"); + log(" enable support for SystemVerilog features. (only a small subset\n"); + log(" of SystemVerilog is supported)\n"); + log("\n"); log(" -dump_ast1\n"); log(" dump abstract syntax tree (before simplification)\n"); log("\n"); @@ -106,6 +108,11 @@ struct VerilogFrontend : public Frontend { log(" ignore re-definitions of modules. (the default behavior is to\n"); log(" create an error message.)\n"); log("\n"); + log(" -defer\n"); + log(" only read the abstract syntax tree and defer actual compilation\n"); + log(" to a later 'hierarchy' command. Useful in cases where the default\n"); + log(" parameters of modules yield invalid or not synthesizable code.\n"); + log("\n"); log(" -setattr <attribute_name>\n"); log(" set the specified attribute (to the value 1) on all loaded modules\n"); log("\n"); @@ -120,8 +127,13 @@ struct VerilogFrontend : public Frontend { log("The command 'verilog_defaults' can be used to register default options for\n"); log("subsequent calls to 'read_verilog'.\n"); log("\n"); + log("Note that the Verilog frontend does a pretty good job of processing valid\n"); + log("verilog input, but has not very good error reporting. It generally is\n"); + 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"); } - virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { bool flag_dump_ast1 = false; bool flag_dump_ast2 = false; @@ -135,10 +147,13 @@ struct VerilogFrontend : public Frontend { bool flag_noopt = false; bool flag_icells = false; bool flag_ignore_redef = false; + bool flag_defer = false; std::map<std::string, std::string> defines_map; std::list<std::string> include_dirs; std::list<std::string> attributes; + frontend_verilog_yydebug = false; + sv_mode = false; log_header("Executing Verilog-2005 frontend.\n"); @@ -147,6 +162,10 @@ struct VerilogFrontend : public Frontend { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; + if (arg == "-sv") { + sv_mode = true; + continue; + } if (arg == "-dump_ast1") { flag_dump_ast1 = true; continue; @@ -199,6 +218,10 @@ struct VerilogFrontend : public Frontend { flag_ignore_redef = true; continue; } + if (arg == "-defer") { + flag_defer = true; + continue; + } if (arg == "-setattr" && argidx+1 < args.size()) { attributes.push_back(RTLIL::escape_id(args[++argidx])); continue; @@ -241,33 +264,34 @@ struct VerilogFrontend : public Frontend { AST::get_line_num = &frontend_verilog_yyget_lineno; current_ast = new AST::AstNode(AST::AST_DESIGN); + default_nettype_wire = true; - FILE *fp = f; + lexin = f; 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, include_dirs); if (flag_ppdump) log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str()); - fp = fmemopen((void*)code_after_preproc.c_str(), code_after_preproc.size(), "r"); + lexin = new std::istringstream(code_after_preproc); } frontend_verilog_yyset_lineno(1); - frontend_verilog_yyrestart(fp); + frontend_verilog_yyrestart(NULL); frontend_verilog_yyparse(); frontend_verilog_yylex_destroy(); for (auto &child : current_ast->children) { - log_assert(child->type == AST::AST_MODULE); - for (auto &attr : attributes) - if (child->attributes.count(attr) == 0) - child->attributes[attr] = AST::AstNode::mkconst_int(1, false); + if (child->type == AST::AST_MODULE) + for (auto &attr : attributes) + if (child->attributes.count(attr) == 0) + child->attributes[attr] = AST::AstNode::mkconst_int(1, false); } - AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_ignore_redef); + AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_ignore_redef, flag_defer, default_nettype_wire); if (!flag_nopp) - fclose(fp); + delete lexin; delete current_ast; current_ast = NULL; @@ -350,3 +374,5 @@ struct VerilogDefaults : public Pass { } } VerilogDefaults; +YOSYS_NAMESPACE_END + diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index 8b4fae6e9..af6495f8f 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -29,12 +29,14 @@ #ifndef VERILOG_FRONTEND_H #define VERILOG_FRONTEND_H -#include "kernel/rtlil.h" +#include "kernel/yosys.h" #include "frontends/ast/ast.h" #include <stdio.h> #include <stdint.h> #include <list> +YOSYS_NAMESPACE_BEGIN + namespace VERILOG_FRONTEND { // this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser @@ -42,10 +44,21 @@ namespace VERILOG_FRONTEND // this function converts a Verilog constant to an AST_CONSTANT node AST::AstNode *const2ast(std::string code, char case_type = 0); + + // state of `default_nettype + extern bool default_nettype_wire; + + // running in SystemVerilog mode + extern bool sv_mode; + + // lexer input stream + extern std::istream *lexin; } // the pre-processor -std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs); +std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs); + +YOSYS_NAMESPACE_END // the usual bison/flex stuff extern int frontend_verilog_yydebug; diff --git a/frontends/vhdl2verilog/Makefile.inc b/frontends/vhdl2verilog/Makefile.inc new file mode 100644 index 000000000..003d89c4a --- /dev/null +++ b/frontends/vhdl2verilog/Makefile.inc @@ -0,0 +1 @@ +OBJS += frontends/vhdl2verilog/vhdl2verilog.o diff --git a/frontends/vhdl2verilog/vhdl2verilog.cc b/frontends/vhdl2verilog/vhdl2verilog.cc new file mode 100644 index 000000000..b408d621b --- /dev/null +++ b/frontends/vhdl2verilog/vhdl2verilog.cc @@ -0,0 +1,196 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> +#include <limits.h> + +YOSYS_NAMESPACE_BEGIN + +struct Vhdl2verilogPass : public Pass { + Vhdl2verilogPass() : Pass("vhdl2verilog", "importing VHDL designs using vhdl2verilog") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" vhdl2verilog [options] <vhdl-file>..\n"); + log("\n"); + log("This command reads VHDL source files using the 'vhdl2verilog' tool and the\n"); + log("Yosys Verilog frontend.\n"); + log("\n"); + log(" -out <out_file>\n"); + log(" do not import the vhdl2verilog output. instead write it to the\n"); + log(" specified file.\n"); + log("\n"); + log(" -vhdl2verilog_dir <directory>\n"); + log(" do use the specified vhdl2verilog installation. this is the directory\n"); + log(" that contains the setup_env.sh file. when this option is not present,\n"); + log(" it is assumed that vhdl2verilog is in the PATH environment variable.\n"); + log("\n"); + log(" -top <top-entity-name>\n"); + log(" The name of the top entity. This option is mandatory.\n"); + log("\n"); + log("The following options are passed as-is to vhdl2verilog:\n"); + log("\n"); + log(" -arch <architecture_name>\n"); + log(" -unroll_generate\n"); + log(" -nogenericeval\n"); + log(" -nouniquify\n"); + log(" -oldparser\n"); + log(" -suppress <list>\n"); + log(" -quiet\n"); + log(" -nobanner\n"); + log(" -mapfile <file>\n"); + log("\n"); + log("vhdl2verilog can be obtained from:\n"); + log("http://www.edautils.com/vhdl2verilog.html\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("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"); + + char tempdir_name[] = "/tmp/yosys-vhdl2verilog-XXXXXX"; + char *p = mkdtemp(tempdir_name); + log("Using temp directory %s.\n", tempdir_name); + if (p == NULL) + log_error("For some reason mkdtemp() failed!\n"); + + 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(), "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, + 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()); + + errno = ENOMEM; // popen does not set errno if memory allocation fails, therefore set it by hand + f = popen(command.c_str(), "r"); + if (f == NULL) + log_error("Opening pipe to `%s' for reading failed: %s\n", command.c_str(), strerror(errno)); + + char logbuf[1024]; + while (fgets(logbuf, 1024, f) != NULL) + log("%s", logbuf); + + int ret = pclose(f); + if (ret < 0) + log_error("Closing pipe to `%s' failed: %s\n", command.c_str(), strerror(errno)); + if (WEXITSTATUS(ret) != 0) + log_error("Execution of command \"%s\" failed: the shell returned %d\n", command.c_str(), WEXITSTATUS(ret)); + + if (out_file.empty()) { + std::ifstream ff; + ff.open(stringf("%s/vhdl2verilog_output.v", tempdir_name).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), "verilog"); + } + + log_header("Removing temp directory `%s':\n", tempdir_name); + if (system(stringf("rm -rf '%s'", tempdir_name).c_str()) != 0) + log_error("Execution of \"rm -rf '%s'\" failed!\n", tempdir_name); + + log_pop(); + } +} Vhdl2verilogPass; + +YOSYS_NAMESPACE_END + |