diff options
Diffstat (limited to 'frontends/ast')
-rw-r--r-- | frontends/ast/Makefile.inc | 1 | ||||
-rw-r--r-- | frontends/ast/ast.cc | 323 | ||||
-rw-r--r-- | frontends/ast/ast.h | 52 | ||||
-rw-r--r-- | frontends/ast/dpicall.cc | 140 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 618 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 1123 |
6 files changed, 1691 insertions, 566 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 + |