diff options
Diffstat (limited to 'frontends/ast')
-rw-r--r-- | frontends/ast/ast.cc | 35 | ||||
-rw-r--r-- | frontends/ast/ast.h | 28 | ||||
-rw-r--r-- | frontends/ast/dpicall.cc | 12 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 134 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 378 |
5 files changed, 412 insertions, 175 deletions
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 1e43875ae..0b63248d8 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -26,13 +26,18 @@ * */ -#include "kernel/log.h" +#include "kernel/yosys.h" #include "libs/sha1/sha1.h" #include "ast.h" #include <sstream> #include <stdarg.h> -#include <math.h> + +#if defined(__APPLE__) +# include <cmath> +#else +# include <math.h> +#endif YOSYS_NAMESPACE_BEGIN @@ -48,12 +53,12 @@ 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, flag_autowire; + bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomeminit, 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; - const std::map<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; + const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; RTLIL::SigSpec ignoreThisSignalsInInitial; - AstNode *current_top_block, *current_block, *current_block_child; + AstNode *current_always, *current_top_block, *current_block, *current_block_child; AstModule *current_module; } @@ -85,6 +90,7 @@ std::string AST::type2str(AstNodeType type) X(AST_IDENTIFIER) X(AST_PREFIX) X(AST_ASSERT) + X(AST_ASSUME) X(AST_FCALL) X(AST_TO_BITS) X(AST_TO_SIGNED) @@ -127,6 +133,7 @@ std::string AST::type2str(AstNodeType type) X(AST_TERNARY) X(AST_MEMRD) X(AST_MEMWR) + X(AST_MEMINIT) X(AST_TCALL) X(AST_ASSIGN) X(AST_CELL) @@ -175,6 +182,10 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id) // (the optional child arguments make it easier to create AST trees) AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2) { + static unsigned int hashidx_count = 123456789; + hashidx_count = mkhash_xorshift(hashidx_count); + hashidx_ = hashidx_count; + this->type = type; filename = current_filename; linenum = get_line_num(); @@ -267,7 +278,7 @@ void AstNode::dumpAst(FILE *f, std::string indent) bits[i-1] == RTLIL::S1 ? '1' : bits[i-1] == RTLIL::Sx ? 'x' : bits[i-1] == RTLIL::Sz ? 'z' : '?'); - fprintf(f, "'(%zd)", bits.size()); + fprintf(f, "'(%d)", GetSize(bits)); } if (is_input) fprintf(f, " input"); @@ -471,7 +482,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) else if (bits.size() == 32) fprintf(f, "%d", RTLIL::Const(bits).as_int()); else - fprintf(f, "%zd'b %s", bits.size(), RTLIL::Const(bits).as_string().c_str()); + fprintf(f, "%d'b %s", GetSize(bits), RTLIL::Const(bits).as_string().c_str()); break; case AST_REALVALUE: @@ -701,6 +712,8 @@ AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signe AstNode *AstNode::mkconst_str(const std::vector<RTLIL::State> &v) { AstNode *node = mkconst_str(RTLIL::Const(v).decode_string()); + while (GetSize(node->bits) < GetSize(v)) + node->bits.push_back(RTLIL::State::S0); log_assert(node->bits == v); return node; } @@ -946,6 +959,7 @@ static AstModule* process_module(AstNode *ast, bool defer) current_module->ast = ast_before_simplify; current_module->nolatches = flag_nolatches; + current_module->nomeminit = flag_nomeminit; current_module->nomem2reg = flag_nomem2reg; current_module->mem2reg = flag_mem2reg; current_module->lib = flag_lib; @@ -957,13 +971,14 @@ static AstModule* process_module(AstNode *ast, bool defer) } // create AstModule instances for all modules in the AST tree and add them to 'design' -void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire) +void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomeminit, 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; flag_dump_ast2 = dump_ast2; flag_dump_vlog = dump_vlog; flag_nolatches = nolatches; + flag_nomeminit = nomeminit; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; flag_lib = lib; @@ -1011,7 +1026,7 @@ AstModule::~AstModule() } // create a new parametric module (when needed) and return the name of the generated module -RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters) +RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters) { std::string stripped_name = name.str(); @@ -1025,6 +1040,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map<RTLIL::IdStrin flag_dump_ast2 = false; flag_dump_vlog = false; flag_nolatches = nolatches; + flag_nomeminit = nomeminit; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; flag_lib = lib; @@ -1091,6 +1107,7 @@ RTLIL::Module *AstModule::clone() const new_mod->ast = ast->clone(); new_mod->nolatches = nolatches; + new_mod->nomeminit = nomeminit; new_mod->nomem2reg = nomem2reg; new_mod->mem2reg = mem2reg; new_mod->lib = lib; diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 0a4016736..d57e91e5a 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -64,6 +64,7 @@ namespace AST AST_IDENTIFIER, AST_PREFIX, AST_ASSERT, + AST_ASSUME, AST_FCALL, AST_TO_BITS, @@ -107,6 +108,7 @@ namespace AST AST_TERNARY, AST_MEMRD, AST_MEMWR, + AST_MEMINIT, AST_TCALL, AST_ASSIGN, @@ -142,6 +144,10 @@ namespace AST // The AST is built using instances of this struct struct AstNode { + // for dict<> and pool<> + unsigned int hashidx_; + unsigned int hash() const { return hashidx_; } + // this nodes type AstNodeType type; @@ -204,11 +210,13 @@ 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 in_param); + AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr); void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map); 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 mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places, + dict<AstNode*, uint32_t> &mem2reg_flags, dict<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags); + void mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block); + bool mem2reg_check(pool<AstNode*> &mem2reg_set); void meminfo(int &mem_width, int &mem_size, int &addr_bits); // additional functionality for evaluating constant functions @@ -229,7 +237,7 @@ namespace AST // 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, const std::map<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL); + RTLIL::SigSpec genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr = NULL); // compare AST nodes bool operator==(const AstNode &other) const; @@ -258,15 +266,15 @@ namespace AST }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code - void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire); + void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomeminit, 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, autowire; + bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire; virtual ~AstModule(); - virtual RTLIL::IdString derive(RTLIL::Design *design, std::map<RTLIL::IdString, RTLIL::Const> parameters); + virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters); virtual RTLIL::Module *clone() const; }; @@ -288,12 +296,12 @@ namespace AST 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, flag_autowire; + extern bool flag_dump_ast1, flag_dump_ast2, flag_nolatches, flag_nomeminit, 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 const std::map<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr; + extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr; extern RTLIL::SigSpec ignoreThisSignalsInInitial; - extern AST::AstNode *current_top_block, *current_block, *current_block_child; + extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child; extern AST::AstModule *current_module; struct ProcessGenerator; } diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index 2eb104fa0..e566d653d 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -24,6 +24,8 @@ #include <dlfcn.h> #include <ffi.h> +YOSYS_NAMESPACE_BEGIN + typedef void (*ffi_fptr) (); static ffi_fptr resolve_fn (std::string symbol_name) @@ -73,8 +75,8 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, 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++) { + log_assert(GetSize(args) == GetSize(argtypes)); + for (int i = 0; i < GetSize(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); @@ -129,12 +131,18 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, return newNode; } +YOSYS_NAMESPACE_END + #else /* YOSYS_ENABLE_PLUGINS */ +YOSYS_NAMESPACE_BEGIN + 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()); } +YOSYS_NAMESPACE_END + #endif /* YOSYS_ENABLE_PLUGINS */ diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index f87a68f67..8ed8c673a 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -73,7 +73,7 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_wi static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_signed) { if (width <= sig.size()) { - sig.extend(width, is_signed); + sig.extend_u0(width, is_signed); return; } @@ -254,7 +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_map = subst_lvalue_from.to_sigbit_map(RTLIL::SigSpec(RTLIL::State::Sx, SIZE(subst_lvalue_from))); + subst_rvalue_map = subst_lvalue_from.to_sigbit_dict(RTLIL::SigSpec(RTLIL::State::Sx, GetSize(subst_lvalue_from))); } else { addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from); } @@ -289,8 +289,8 @@ struct AST_INTERNAL::ProcessGenerator { RTLIL::SigSpec new_lhs, new_rhs; - log_assert(SIZE(lhs) == SIZE(rhs)); - for (int i = 0; i < SIZE(lhs); i++) { + log_assert(GetSize(lhs) == GetSize(rhs)); + for (int i = 0; i < GetSize(lhs); i++) { if (lhs[i].wire == nullptr) continue; new_lhs.append(lhs[i]); @@ -306,7 +306,7 @@ struct AST_INTERNAL::ProcessGenerator { std::vector<RTLIL::SigChunk> chunks = sig.chunks(); - for (int i = 0; i < SIZE(chunks); i++) + for (int i = 0; i < GetSize(chunks); i++) { RTLIL::SigChunk &chunk = chunks[i]; if (chunk.wire == NULL) @@ -430,7 +430,7 @@ struct AST_INTERNAL::ProcessGenerator lvalue.replace(subst_lvalue_map.stdmap()); if (ast->type == AST_ASSIGN_EQ) { - for (int i = 0; i < SIZE(unmapped_lvalue); i++) + for (int i = 0; i < GetSize(unmapped_lvalue); i++) subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]); } @@ -472,7 +472,7 @@ struct AST_INTERNAL::ProcessGenerator subst_lvalue_map.save(); subst_rvalue_map.save(); - for (int i = 0; i < SIZE(this_case_eq_lvalue); i++) + for (int i = 0; i < GetSize(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; @@ -507,7 +507,7 @@ struct AST_INTERNAL::ProcessGenerator sw->cases.push_back(default_case); } - for (int i = 0; i < SIZE(this_case_eq_lvalue); i++) + for (int i = 0; i < GetSize(this_case_eq_lvalue); i++) subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); this_case_eq_lvalue.replace(subst_lvalue_map.stdmap()); @@ -567,9 +567,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) { this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1; } else - if (id_ast->children[0]->type == AST_CONSTANT) { + if (id_ast->children[0]->type != AST_CONSTANT) + while (id_ast->simplify(true, false, false, 1, -1, false, true)) { } + if (id_ast->children[0]->type == AST_CONSTANT) this_width = id_ast->children[0]->bits.size(); - } else + else log_error("Failed to detect width for parameter %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum); if (children.size() != 0) range = children[0]; @@ -839,14 +841,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); memory->name = str; memory->width = children[0]->range_left - children[0]->range_right + 1; - memory->start_offset = children[0]->range_right; - memory->size = children[1]->range_left - children[1]->range_right; + if (children[1]->range_right < children[1]->range_left) { + memory->start_offset = children[1]->range_right; + memory->size = children[1]->range_left - children[1]->range_right + 1; + } else { + memory->start_offset = children[1]->range_left; + memory->size = children[1]->range_right - children[1]->range_left + 1; + } current_module->memories[memory->name] = memory; - if (memory->size < 0) - memory->size *= -1; - memory->size += std::min(children[1]->range_left, children[1]->range_right) + 1; - for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) log_error("Attribute `%s' with non-constant value at %s:%d!\n", @@ -869,7 +872,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_REALVALUE: { RTLIL::SigSpec sig = realAsConst(width_hint); - log("Warning: converting real value %e to binary %s at %s:%d.\n", + log_warning("converting real value %e to binary %s at %s:%d.\n", realvalue, log_signal(sig), filename.c_str(), linenum); return sig; } @@ -890,7 +893,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); wire->name = str; if (flag_autowire) - log("Warning: Identifier `%s' is implicitly declared at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + log_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); } @@ -941,7 +944,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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) + if (GetSize(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; @@ -955,10 +958,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk.offset = (id2ast->range_left - id2ast->range_right + 1) - (chunk.offset + chunk.width); if (chunk.offset >= source_width || chunk.offset + chunk.width < 0) { if (chunk.width == 1) - log("Warning: Range select out of bounds on signal `%s' at %s:%d: Setting result bit to undef.\n", + 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", + 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 { @@ -972,10 +975,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk.offset += add_undef_bits_lsb; } if (add_undef_bits_lsb) - log("Warning: Range select out of bounds on signal `%s' at %s:%d: Setting %d LSB bits to undef.\n", + 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", + 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); } } @@ -1213,9 +1216,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) 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); - int addr_bits = 1; - while ((1 << addr_bits) < current_module->memories[str]->size) - addr_bits++; + int mem_width, mem_size, addr_bits; + id2ast->meminfo(mem_width, mem_size, addr_bits); cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); cell->setPort("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); @@ -1234,28 +1236,30 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // generate $memwr cells for memory write ports case AST_MEMWR: + case AST_MEMINIT: { std::stringstream sstr; - sstr << "$memwr$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++); + sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << linenum << "$" << (autoidx++); - RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$memwr"); + RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? "$memwr" : "$meminit"); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - int addr_bits = 1; - while ((1 << addr_bits) < current_module->memories[str]->size) - addr_bits++; + int mem_width, mem_size, addr_bits; + id2ast->meminfo(mem_width, mem_size, addr_bits); - 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); cell->parameters["\\WIDTH"] = RTLIL::Const(current_module->memories[str]->width); - cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0); - cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0); + if (type == AST_MEMWR) { + cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); + cell->setPort("\\EN", children[2]->genRTLIL()); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0); + } cell->parameters["\\PRIORITY"] = RTLIL::Const(autoidx-1); } @@ -1263,19 +1267,22 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // generate $assert cells case AST_ASSERT: + case AST_ASSUME: { log_assert(children.size() == 2); RTLIL::SigSpec check = children[0]->genRTLIL(); - log_assert(check.size() == 1); + if (GetSize(check) != 1) + check = current_module->ReduceBool(NEW_ID, check); RTLIL::SigSpec en = children[1]->genRTLIL(); - log_assert(en.size() == 1); + if (GetSize(en) != 1) + en = current_module->ReduceBool(NEW_ID, en); std::stringstream sstr; - sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++); + sstr << (type == AST_ASSERT ? "$assert$" : "$assume$") << filename << ":" << linenum << "$" << (autoidx++); - RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$assert"); + RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_ASSERT ? "$assert" : "$assume"); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); for (auto &attr : attributes) { @@ -1293,15 +1300,23 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // add entries to current_module->connections for assignments (outside of always blocks) case AST_ASSIGN: { - 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.size()); - current_module->connect(RTLIL::SigSig(left, right)); - } else { - RTLIL::SigSpec left = children[0]->genRTLIL(); - RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size()); - current_module->connect(RTLIL::SigSig(left, right)); + RTLIL::SigSpec left = children[0]->genRTLIL(); + RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size()); + if (left.has_const()) { + RTLIL::SigSpec new_left, new_right; + for (int i = 0; i < GetSize(left); i++) + if (left[i].wire) { + new_left.append(left[i]); + new_right.append(right[i]); + } + log_warning("Ignoring assignment to constant bits at %s:%d:\n" + " old assignment: %s = %s\n new assignment: %s = %s.\n", + filename.c_str(), linenum, log_signal(left), log_signal(right), + log_signal(new_left), log_signal(new_right)); + left = new_left; + right = new_right; } + current_module->connect(RTLIL::SigSig(left, right)); } break; @@ -1326,16 +1341,19 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) continue; } if (child->type == AST_PARASET) { - if (child->children[0]->type != AST_CONSTANT) - log_error("Parameter `%s' with non-constant value at %s:%d!\n", - child->str.c_str(), filename.c_str(), linenum); - if (child->str.size() == 0) { - char buf[100]; - snprintf(buf, 100, "$%d", ++para_counter); - cell->parameters[buf] = child->children[0]->asParaConst(); - } else { - cell->parameters[child->str] = child->children[0]->asParaConst(); + IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; + if (child->children[0]->type == AST_REALVALUE) { + log_warning("Replacing floating point parameter %s.%s = %f with string at %s:%d.\n", + log_id(cell), log_id(paraname), child->children[0]->realvalue, + filename.c_str(), linenum); + auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue)); + strnode->cloneInto(child->children[0]); + delete strnode; } + if (child->children[0]->type != AST_CONSTANT) + log_error("Parameter %s.%s with non-constant value at %s:%d!\n", + log_id(cell), log_id(paraname), filename.c_str(), linenum); + cell->parameters[paraname] = child->children[0]->asParaConst(); continue; } if (child->type == AST_ARGUMENT) { @@ -1391,9 +1409,9 @@ 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, const std::map<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr) +RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr) { - const std::map<RTLIL::SigBit, RTLIL::SigBit> *backup_subst_ptr = genRTLIL_subst_ptr; + const dict<RTLIL::SigBit, RTLIL::SigBit> *backup_subst_ptr = genRTLIL_subst_ptr; if (new_subst_ptr) genRTLIL_subst_ptr = new_subst_ptr; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 969cc2302..a65d2dbb1 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -28,10 +28,12 @@ #include "kernel/log.h" #include "libs/sha1/sha1.h" +#include "frontends/verilog/verilog_frontend.h" #include "ast.h" #include <sstream> #include <stdarg.h> +#include <stdlib.h> #include <math.h> YOSYS_NAMESPACE_BEGIN @@ -47,39 +49,55 @@ using namespace AST_INTERNAL; // 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 in_param) { + static int recursion_counter = 0; + static pair<string, int> last_blocking_assignment_warn; + static bool deep_recursion_warning = false; + + if (recursion_counter++ == 1000 && deep_recursion_warning) { + log_warning("Deep recursion in AST simplifier.\nDoes this design contain insanely long expressions?\n"); + deep_recursion_warning = false; + } + AstNode *newNode = NULL; bool did_something = false; #if 0 log("-------------\n"); + log("AST simplify[%d] depth %d at %s:%d,\n", stage, recursion_counter, filename.c_str(), linenum); 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, "> "); + // dumpAst(NULL, "> "); #endif if (stage == 0) { log_assert(type == AST_MODULE); + last_blocking_assignment_warn = pair<string, int>(); + deep_recursion_warning = true; while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { } if (!flag_nomem2reg && !get_bool_attribute("\\nomem2reg")) { - std::map<AstNode*, std::set<std::string>> mem2reg_places; - std::map<AstNode*, uint32_t> mem2reg_candidates, dummy_proc_flags; + dict<AstNode*, pool<std::string>> mem2reg_places; + dict<AstNode*, uint32_t> mem2reg_candidates, dummy_proc_flags; uint32_t flags = flag_mem2reg ? AstNode::MEM2REG_FL_ALL : 0; mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, dummy_proc_flags, flags); - std::set<AstNode*> mem2reg_set; + pool<AstNode*> mem2reg_set; for (auto &it : mem2reg_candidates) { AstNode *mem = it.first; uint32_t memflags = it.second; + bool this_nomeminit = flag_nomeminit; log_assert((memflags & ~0x00ffff00) == 0); if (mem->get_bool_attribute("\\nomem2reg")) continue; + if (mem->get_bool_attribute("\\nomeminit") || get_bool_attribute("\\nomeminit")) + this_nomeminit = true; + if (memflags & AstNode::MEM2REG_FL_FORCED) goto silent_activate; @@ -89,7 +107,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (memflags & AstNode::MEM2REG_FL_SET_ASYNC) goto verbose_activate; - if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE)) + if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE) && this_nomeminit) goto verbose_activate; if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS) @@ -100,13 +118,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, verbose_activate: if (mem2reg_set.count(mem) == 0) { - log("Warning: Replacing memory %s with list of registers.", mem->str.c_str()); + std::string message = stringf("Replacing memory %s with list of registers.", mem->str.c_str()); bool first_element = true; for (auto &place : mem2reg_places[it.first]) { - log("%s%s", first_element ? " See " : ", ", place.c_str()); + message += stringf("%s%s", first_element ? " See " : ", ", place.c_str()); first_element = false; } - log("\n"); + log_warning("%s\n", message.c_str()); } silent_activate: @@ -141,6 +159,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, in_param)) { } + recursion_counter--; return false; } @@ -149,11 +168,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // we do not look inside a task or function // (but as soon as a task of function is instanciated we process the generated AST as usual) - if (type == AST_FUNCTION || type == AST_TASK) + if (type == AST_FUNCTION || type == AST_TASK) { + recursion_counter--; return false; + } // deactivate all calls to non-synthesis system taks - if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$stop" || str == "$finish")) { + if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$strobe" || str == "$monitor" || str == "$time" || str == "$stop" || str == "$finish" || + str == "$dumpfile" || str == "$dumpvars" || str == "$dumpon" || str == "$dumpoff" || str == "$dumpall")) { + log_warning("Ignoring call to system %s %s at %s:%d.\n", type == AST_FCALL ? "function" : "task", str.c_str(), filename.c_str(), linenum); delete_children(); str = std::string(); } @@ -182,6 +205,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, AstNode *first_node = this_wire_scope[node->str]; if (!node->is_input && !node->is_output && node->is_reg && node->children.size() == 0) goto wires_are_compatible; + if (first_node->children.size() == 0 && node->children.size() == 1 && node->children[0]->type == AST_RANGE) { + AstNode *r = node->children[0]; + if (r->range_valid && r->range_left == 0 && r->range_right == 0) { + delete r; + node->children.pop_back(); + } + } if (first_node->children.size() != node->children.size()) goto wires_are_incompatible; for (size_t j = 0; j < node->children.size(); j++) { @@ -242,6 +272,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, auto backup_current_block = current_block; auto backup_current_block_child = current_block_child; auto backup_current_top_block = current_top_block; + auto backup_current_always = current_always; + + if (type == AST_ALWAYS || type == AST_INITIAL) + current_always = this; int backup_width_hint = width_hint; bool backup_sign_hint = sign_hint; @@ -382,6 +416,41 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } + if (const_fold && type == AST_CASE) + { + while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } + if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) { + std::vector<AstNode*> new_children; + new_children.push_back(children[0]); + for (int i = 1; i < GetSize(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; + while (v->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } + if (v->type == AST_CONSTANT && v->bits_only_01()) { + if (v->bits == children[0]->bits) { + while (i+1 < GetSize(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); + } + } + // 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++) { @@ -446,6 +515,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, current_block = backup_current_block; current_block_child = backup_current_block_child; current_top_block = backup_current_top_block; + current_always = backup_current_always; for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) { if (it->second == NULL) @@ -575,9 +645,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { AstNode *index_expr = nullptr; - for (int i = 0; 2*i < SIZE(id2ast->multirange_dimensions); i++) + for (int i = 0; 2*i < GetSize(id2ast->multirange_dimensions); i++) { - if (SIZE(children[0]->children) < i) + if (GetSize(children[0]->children) < i) log_error("Insufficient number of array indices for %s at %s:%d.\n", log_id(str), filename.c_str(), linenum); AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone(); @@ -591,7 +661,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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++) + for (int i = GetSize(id2ast->multirange_dimensions)/1; i < GetSize(children[0]->children); i++) children.push_back(children[0]->children[i]->clone()); delete children[0]; @@ -611,7 +681,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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", + 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); @@ -653,7 +723,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } if (current_scope.count(str) == 0) { - // log("Warning: Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str()); + // log_warning("Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str()); AstNode *auto_wire = new AstNode(AST_AUTOWIRE); auto_wire->str = str; current_ast_mod->children.push_back(auto_wire); @@ -1123,7 +1193,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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 = abs(left_at_zero_ast->integer - right_at_zero_ast->integer) + 1; + result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; } did_something = true; newNode = new AstNode(AST_CASE, shift_expr); @@ -1141,7 +1211,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } skip_dynamic_range_lvalue_expansion:; - if (stage > 1 && type == AST_ASSERT && current_block != NULL) + if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && current_block != NULL) { std::stringstream sstr; sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++); @@ -1185,7 +1255,7 @@ skip_dynamic_range_lvalue_expansion:; newNode->children.push_back(assign_check); newNode->children.push_back(assign_en); - AstNode *assertnode = new AstNode(AST_ASSERT); + AstNode *assertnode = new AstNode(type); assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); assertnode->children[0]->str = id_check; @@ -1196,9 +1266,8 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } - if (stage > 1 && type == AST_ASSERT && children.size() == 1) + if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && children.size() == 1) { - children[0] = new AstNode(AST_REDUCE_BOOL, children[0]->clone()); children.push_back(mkconst_int(1, false, 1)); did_something = true; } @@ -1222,9 +1291,13 @@ skip_dynamic_range_lvalue_expansion:; 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("Warning: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n", - filename.c_str(), linenum); + if (type == AST_ASSIGN_EQ) { + pair<string, int> this_blocking_assignment_warn(filename, linenum); + if (this_blocking_assignment_warn != last_blocking_assignment_warn) + log_warning("Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n", + filename.c_str(), linenum); + last_blocking_assignment_warn = this_blocking_assignment_warn; + } int mem_width, mem_size, addr_bits; children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); @@ -1241,11 +1314,14 @@ skip_dynamic_range_lvalue_expansion:; current_scope[wire_data->str] = wire_data; while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } - 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, false)) { } + AstNode *wire_en = nullptr; + if (current_always->type != AST_INITIAL) { + wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + wire_en->str = id_en; + current_ast_mod->children.push_back(wire_en); + current_scope[wire_en->str] = wire_en; + while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } + } std::vector<RTLIL::State> x_bits_addr, x_bits_data, set_bits_en; for (int i = 0; i < addr_bits; i++) @@ -1261,13 +1337,17 @@ skip_dynamic_range_lvalue_expansion:; 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, mem_width)); - assign_en->children[0]->str = id_en; + AstNode *assign_en = nullptr; + if (current_always->type != AST_INITIAL) { + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); + assign_en->children[0]->str = id_en; + } AstNode *default_signals = new AstNode(AST_BLOCK); default_signals->children.push_back(assign_addr); default_signals->children.push_back(assign_data); - default_signals->children.push_back(assign_en); + if (current_always->type != AST_INITIAL) + default_signals->children.push_back(assign_en); current_top_block->children.insert(current_top_block->children.begin(), default_signals); assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); @@ -1282,15 +1362,16 @@ skip_dynamic_range_lvalue_expansion:; 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; + if (current_always->type != AST_INITIAL) { + for (int i = 0; i < mem_width; i++) + set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + } } else { @@ -1305,16 +1386,17 @@ skip_dynamic_range_lvalue_expansion:; 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; + if (current_always->type != AST_INITIAL) { + for (int i = 0; i < mem_width; i++) + set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; + 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; @@ -1326,23 +1408,29 @@ skip_dynamic_range_lvalue_expansion:; assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); assign_data->children[0]->str = id_data; - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); - assign_en->children[0]->str = id_en; + if (current_always->type != AST_INITIAL) { + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + } } newNode = new AstNode(AST_BLOCK); newNode->children.push_back(assign_addr); newNode->children.push_back(assign_data); - newNode->children.push_back(assign_en); + if (current_always->type != AST_INITIAL) + newNode->children.push_back(assign_en); - AstNode *wrnode = new AstNode(AST_MEMWR); - wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR); wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + if (current_always->type != AST_INITIAL) + wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); wrnode->str = children[0]->str; + wrnode->id2ast = children[0]->id2ast; wrnode->children[0]->str = id_addr; wrnode->children[1]->str = id_data; - wrnode->children[2]->str = id_en; + if (current_always->type != AST_INITIAL) + wrnode->children[2]->str = id_en; current_ast_mod->children.push_back(wrnode); goto apply_newNode; @@ -1366,7 +1454,7 @@ skip_dynamic_range_lvalue_expansion:; RTLIL::Const arg_value = buf->bitsAsConst(); if (arg_value.as_bool()) - arg_value = const_sub(arg_value, 1, false, false, SIZE(arg_value)); + arg_value = const_sub(arg_value, 1, false, false, GetSize(arg_value)); delete buf; uint32_t result = 0; @@ -1455,9 +1543,9 @@ skip_dynamic_range_lvalue_expansion:; 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++) + for (int i = 2; i < GetSize(dpi_decl->children); i++) { - if (i-2 >= SIZE(children)) + if (i-2 >= GetSize(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)); @@ -1480,6 +1568,44 @@ skip_dynamic_range_lvalue_expansion:; log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum); } if (type == AST_TCALL) { + if (str == "\\$readmemh" || str == "\\$readmemb") + { + if (GetSize(children) < 2 || GetSize(children) > 4) + log_error("System function %s got %d arguments, expected 2-4 at %s:%d.\n", + RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum); + + AstNode *node_filename = children[0]->clone(); + while (node_filename->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_filename->type != AST_CONSTANT) + log_error("Failed to evaluate system function `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + AstNode *node_memory = children[1]->clone(); + while (node_memory->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_memory->type != AST_IDENTIFIER || node_memory->id2ast == nullptr || node_memory->id2ast->type != AST_MEMORY) + log_error("Failed to evaluate system function `%s' with non-memory 2nd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + int start_addr = -1, finish_addr = -1; + + if (GetSize(children) > 2) { + AstNode *node_addr = children[2]->clone(); + while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_addr->type != AST_CONSTANT) + log_error("Failed to evaluate system function `%s' with non-constant 3rd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + start_addr = node_addr->asInt(false); + } + + if (GetSize(children) > 3) { + AstNode *node_addr = children[3]->clone(); + while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_addr->type != AST_CONSTANT) + log_error("Failed to evaluate system function `%s' with non-constant 4th argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + finish_addr = node_addr->asInt(false); + } + + newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr); + goto apply_newNode; + } + if (current_scope.count(str) == 0 || current_scope[str]->type != AST_TASK) log_error("Can't resolve task name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum); } @@ -1558,7 +1684,7 @@ skip_dynamic_range_lvalue_expansion:; celltype = RTLIL::escape_id(celltype); AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE)); - cell->str = prefix.substr(0, SIZE(prefix)-1); + cell->str = prefix.substr(0, GetSize(prefix)-1); cell->children[0]->str = celltype; for (auto attr : decl->attributes) @@ -1681,7 +1807,7 @@ skip_dynamic_range_lvalue_expansion:; 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); + GetSize(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; @@ -1843,37 +1969,6 @@ skip_dynamic_range_lvalue_expansion:; 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]->isConst()) { @@ -1977,6 +2072,7 @@ apply_newNode: if (!did_something) basic_prep = true; + recursion_counter--; return did_something; } @@ -1988,6 +2084,83 @@ static void replace_result_wire_name_in_function(AstNode *node, std::string &fro node->str = to; } +// replace a readmem[bh] TCALL ast node with a block of memory assignments +AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr) +{ + AstNode *block = new AstNode(AST_BLOCK); + + std::ifstream f; + f.open(mem_filename.c_str()); + + if (f.fail()) + log_error("Can not open file `%s` for %s at %s:%d.\n", mem_filename.c_str(), str.c_str(), filename.c_str(), linenum); + + log_assert(GetSize(memory->children) == 2 && memory->children[1]->type == AST_RANGE && memory->children[1]->range_valid); + int range_left = memory->children[1]->range_left, range_right = memory->children[1]->range_right; + int range_min = std::min(range_left, range_right), range_max = std::max(range_left, range_right); + + if (start_addr < 0) + start_addr = range_min; + + if (finish_addr < 0) + finish_addr = range_max; + + bool in_comment = false; + int increment = start_addr <= finish_addr ? +1 : -1; + int cursor = start_addr; + + while (!f.eof()) + { + std::string line, token; + std::getline(f, line); + + for (int i = 0; i < GetSize(line); i++) { + if (in_comment && line.substr(i, 2) == "*/") { + line[i] = ' '; + line[i+1] = ' '; + in_comment = false; + continue; + } + if (!in_comment && line.substr(i, 2) == "/*") + in_comment = true; + if (in_comment) + line[i] = ' '; + } + + while (1) + { + token = next_token(line, " \t\r\n"); + if (token.empty() || token.substr(0, 2) == "//") + break; + + if (token[0] == '@') { + token = token.substr(1); + const char *nptr = token.c_str(); + char *endptr; + cursor = strtol(nptr, &endptr, 16); + if (!*nptr || *endptr) + log_error("Can not parse address `%s` for %s at %s:%d.\n", nptr, str.c_str(), filename.c_str(), linenum); + continue; + } + + AstNode *value = VERILOG_FRONTEND::const2ast((is_readmemh ? "'h" : "'b") + token); + + block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value)); + block->children.back()->children[0]->str = memory->str; + block->children.back()->children[0]->id2ast = memory; + + if ((cursor == finish_addr) || (increment > 0 && cursor >= range_max) || (increment < 0 && cursor <= range_min)) + break; + cursor += increment; + } + + if ((cursor == finish_addr) || (increment > 0 && cursor >= range_max) || (increment < 0 && cursor <= range_min)) + break; + } + + return block; +} + // annotate the names of all wires and other named objects in a generate block void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map) { @@ -2063,8 +2236,8 @@ void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, } // 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) +static void mark_memories_assign_lhs_complex(dict<AstNode*, pool<std::string>> &mem2reg_places, + dict<AstNode*, uint32_t> &mem2reg_candidates, AstNode *that) { for (auto &child : that->children) mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child); @@ -2078,8 +2251,8 @@ static void mark_memories_assign_lhs_complex(std::map<AstNode*, std::set<std::st } // find memories that should be replaced by registers -void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> &mem2reg_places, - std::map<AstNode*, uint32_t> &mem2reg_candidates, std::map<AstNode*, uint32_t> &proc_flags, uint32_t &flags) +void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places, + dict<AstNode*, uint32_t> &mem2reg_candidates, dict<AstNode*, uint32_t> &proc_flags, uint32_t &flags) { uint32_t children_flags = 0; int ignore_children_counter = 0; @@ -2141,7 +2314,7 @@ void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> if (type == AST_MODULE && get_bool_attribute("\\mem2reg")) children_flags |= AstNode::MEM2REG_FL_ALL; - std::map<AstNode*, uint32_t> *proc_flags_p = NULL; + dict<AstNode*, uint32_t> *proc_flags_p = NULL; if (type == AST_ALWAYS) { int count_edge_events = 0; @@ -2150,12 +2323,12 @@ void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> count_edge_events++; if (count_edge_events != 1) children_flags |= AstNode::MEM2REG_FL_ASYNC; - proc_flags_p = new std::map<AstNode*, uint32_t>; + proc_flags_p = new dict<AstNode*, uint32_t>; } if (type == AST_INITIAL) { children_flags |= AstNode::MEM2REG_FL_INIT; - proc_flags_p = new std::map<AstNode*, uint32_t>; + proc_flags_p = new dict<AstNode*, uint32_t>; } uint32_t backup_flags = flags; @@ -2173,20 +2346,33 @@ void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>> flags &= ~children_flags | backup_flags; if (proc_flags_p) { +#ifndef NDEBUG for (auto it : *proc_flags_p) log_assert((it.second & ~0xff000000) == 0); +#endif delete proc_flags_p; } } +bool AstNode::mem2reg_check(pool<AstNode*> &mem2reg_set) +{ + if (type != AST_IDENTIFIER || !id2ast || !mem2reg_set.count(id2ast)) + return false; + + if (children.empty() || children[0]->type != AST_RANGE || GetSize(children[0]->children) != 1) + log_error("Invalid array access at %s:%d.\n", filename.c_str(), linenum); + + return true; +} + // actually replace memories with registers -void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block) +void AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block) { if (type == AST_BLOCK) block = this; - if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL && children[0]->id2ast && - mem2reg_set.count(children[0]->id2ast) > 0 && children[0]->children[0]->children[0]->type != AST_CONSTANT) + if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL && + children[0]->mem2reg_check(mem2reg_set) && children[0]->children[0]->children[0]->type != AST_CONSTANT) { std::stringstream sstr; sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++); @@ -2242,7 +2428,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode * type = AST_ASSIGN_EQ; } - if (type == AST_IDENTIFIER && id2ast && mem2reg_set.count(id2ast) > 0) + if (mem2reg_check(mem2reg_set)) { AstNode *bit_part_sel = NULL; if (children.size() == 2) @@ -2446,7 +2632,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) } log_assert(block != NULL); - log_assert(variables.count(str)); + log_assert(variables.count(str) != 0); while (!block->children.empty()) { |