aboutsummaryrefslogtreecommitdiffstats
path: root/frontends/ast
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/ast')
-rw-r--r--frontends/ast/Makefile.inc1
-rw-r--r--frontends/ast/ast.cc323
-rw-r--r--frontends/ast/ast.h52
-rw-r--r--frontends/ast/dpicall.cc140
-rw-r--r--frontends/ast/genrtlil.cc618
-rw-r--r--frontends/ast/simplify.cc1123
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
+