summaryrefslogtreecommitdiff
path: root/frontends
diff options
context:
space:
mode:
Diffstat (limited to 'frontends')
-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
-rw-r--r--frontends/ilang/Makefile.inc10
-rw-r--r--frontends/ilang/ilang_frontend.cc9
-rw-r--r--frontends/ilang/ilang_frontend.h11
-rw-r--r--frontends/ilang/lexer.l40
-rw-r--r--frontends/ilang/parser.y116
-rw-r--r--frontends/liberty/Makefile.inc3
-rw-r--r--frontends/liberty/liberty.cc578
-rw-r--r--frontends/verific/Makefile.inc16
-rw-r--r--frontends/verific/build_amd64.txt31
-rw-r--r--frontends/verific/test_navre.ys18
-rw-r--r--frontends/verific/verific.cc949
-rw-r--r--frontends/verilog/Makefile.inc6
-rw-r--r--frontends/verilog/const2ast.cc46
-rw-r--r--frontends/verilog/lexer.l90
-rw-r--r--frontends/verilog/parser.y262
-rw-r--r--frontends/verilog/preproc.cc65
-rw-r--r--frontends/verilog/verilog_frontend.cc58
-rw-r--r--frontends/verilog/verilog_frontend.h17
-rw-r--r--frontends/vhdl2verilog/Makefile.inc1
-rw-r--r--frontends/vhdl2verilog/vhdl2verilog.cc196
26 files changed, 4006 insertions, 773 deletions
diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc
index 993ead92..91d917c9 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 96608ae3..1e43875a 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 01702c3c..0a401673 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 00000000..2eb104fa
--- /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 591d027c..f87a68f6 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 b51079ce..969cc230 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -32,7 +32,9 @@
#include <sstream>
#include <stdarg.h>
-#include <assert.h>
+#include <math.h>
+
+YOSYS_NAMESPACE_BEGIN
using namespace AST;
using namespace AST_INTERNAL;
@@ -43,16 +45,23 @@ using namespace AST_INTERNAL;
//
// this function also does all name resolving and sets the id2ast member of all
// nodes that link to a different node using names and lexical scoping.
-bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint)
+bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param)
{
AstNode *newNode = NULL;
bool did_something = false;
+#if 0
+ log("-------------\n");
+ log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n",
+ int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param));
+ dumpAst(NULL, "> ");
+#endif
+
if (stage == 0)
{
- assert(type == AST_MODULE);
+ log_assert(type == AST_MODULE);
- while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint)) { }
+ while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { }
if (!flag_nomem2reg && !get_bool_attribute("\\nomem2reg"))
{
@@ -66,7 +75,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
{
AstNode *mem = it.first;
uint32_t memflags = it.second;
- assert((memflags & ~0x00ffff00) == 0);
+ log_assert((memflags & ~0x00ffff00) == 0);
if (mem->get_bool_attribute("\\nomem2reg"))
continue;
@@ -83,6 +92,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE))
goto verbose_activate;
+ if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS)
+ goto verbose_activate;
+
// log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags));
continue;
@@ -114,7 +126,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
reg->is_reg = true;
reg->is_signed = node->is_signed;
children.push_back(reg);
- while (reg->simplify(true, false, false, 1, -1, false)) { }
+ while (reg->simplify(true, false, false, 1, -1, false, false)) { }
}
}
@@ -128,7 +140,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
- while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint)) { }
+ while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint, in_param)) { }
return false;
}
@@ -140,7 +152,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_FUNCTION || type == AST_TASK)
return false;
- // deactivate all calls non-synthesis system taks
+ // deactivate all calls to non-synthesis system taks
if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$stop" || str == "$finish")) {
delete_children();
str = std::string();
@@ -152,6 +164,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM))
const_fold = true;
+ // in certain cases a function must be evaluated constant. this is what in_param controls.
+ if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX)
+ in_param = true;
+
std::map<std::string, AstNode*> backup_scope;
// create name resolution entries for all objects with names
@@ -202,12 +218,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
did_something = true;
delete node;
continue;
+ wires_are_incompatible:
+ if (stage > 1)
+ log_error("Incompatible re-declaration of wire %s at %s:%d.\n", node->str.c_str(), filename.c_str(), linenum);
+ continue;
}
this_wire_scope[node->str] = node;
}
- wires_are_incompatible:
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
- node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_CELL) {
+ node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL) {
backup_scope[node->str] = current_scope[node->str];
current_scope[node->str] = node;
}
@@ -215,7 +234,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
for (size_t i = 0; i < children.size(); i++) {
AstNode *node = children[i];
if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE)
- while (node->simplify(true, false, false, 1, -1, false)) { }
+ while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM))
+ did_something = true;
}
}
@@ -229,6 +249,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
bool detect_width_simple = false;
bool child_0_is_self_determined = false;
bool child_1_is_self_determined = false;
+ bool child_2_is_self_determined = false;
bool children_are_self_determined = false;
bool reset_width_after_children = false;
@@ -237,8 +258,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
case AST_ASSIGN_EQ:
case AST_ASSIGN_LE:
case AST_ASSIGN:
- while (!children[0]->basic_prep && children[0]->simplify(false, false, true, stage, -1, false) == true) { }
- while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false) == true) { }
+ while (!children[0]->basic_prep && children[0]->simplify(false, false, true, stage, -1, false, in_param) == true)
+ did_something = true;
+ while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, in_param) == true)
+ did_something = true;
children[0]->detectSignWidth(backup_width_hint, backup_sign_hint);
children[1]->detectSignWidth(width_hint, sign_hint);
width_hint = std::max(width_hint, backup_width_hint);
@@ -247,11 +270,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
case AST_PARAMETER:
case AST_LOCALPARAM:
- while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false) == true) { }
+ while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true)
+ did_something = true;
children[0]->detectSignWidth(width_hint, sign_hint);
- if (children.size() > 1) {
- assert(children[1]->type == AST_RANGE);
- while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false) == true) { }
+ if (children.size() > 1 && children[1]->type == AST_RANGE) {
+ while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, true) == true)
+ did_something = true;
if (!children[1]->range_valid)
log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum);
width_hint = std::max(width_hint, children[1]->range_left - children[1]->range_right + 1);
@@ -307,7 +331,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
width_hint = -1;
sign_hint = true;
for (auto child : children) {
- while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false) == true) { }
+ while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false, in_param) == true)
+ did_something = true;
child->detectSignWidthWorker(width_hint, sign_hint);
}
reset_width_after_children = true;
@@ -336,13 +361,27 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
if (detect_width_simple && width_hint < 0) {
- for (auto child : children)
- while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false) == true) { }
if (type == AST_REPLICATE)
- while (children[0]->simplify(true, false, in_lvalue, stage, -1, false) == true) { }
+ while (children[0]->simplify(true, false, in_lvalue, stage, -1, false, true) == true)
+ did_something = true;
+ for (auto child : children)
+ while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false, in_param) == true)
+ did_something = true;
detectSignWidth(width_hint, sign_hint);
}
+ if (type == AST_TERNARY) {
+ int width_hint_left, width_hint_right;
+ bool sign_hint_left, sign_hint_right;
+ bool found_real_left, found_real_right;
+ children[1]->detectSignWidth(width_hint_left, sign_hint_left, &found_real_left);
+ children[2]->detectSignWidth(width_hint_right, sign_hint_right, &found_real_right);
+ if (found_real_left || found_real_right) {
+ child_1_is_self_determined = true;
+ child_2_is_self_determined = true;
+ }
+ }
+
// simplify all children first
// (iterate by index as e.g. auto wires can add new children in the process)
for (size_t i = 0; i < children.size(); i++) {
@@ -361,8 +400,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
bool const_fold_here = const_fold, in_lvalue_here = in_lvalue;
int width_hint_here = width_hint;
bool sign_hint_here = sign_hint;
- if (i == 0 && type == AST_REPLICATE)
- const_fold_here = true;
+ bool in_param_here = in_param;
+ if (i == 0 && (type == AST_REPLICATE || type == AST_WIRE))
+ const_fold_here = true, in_param_here = true;
if (type == AST_PARAMETER || type == AST_LOCALPARAM)
const_fold_here = true;
if (i == 0 && (type == AST_ASSIGN || type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE))
@@ -377,15 +417,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
width_hint_here = -1, sign_hint_here = false;
if (i == 1 && child_1_is_self_determined)
width_hint_here = -1, sign_hint_here = false;
+ if (i == 2 && child_2_is_self_determined)
+ width_hint_here = -1, sign_hint_here = false;
if (children_are_self_determined)
width_hint_here = -1, sign_hint_here = false;
- did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage, width_hint_here, sign_hint_here);
+ did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage, width_hint_here, sign_hint_here, in_param_here);
if (did_something_here)
did_something = true;
}
+ if (stage == 2 && children[i]->type == AST_INITIAL && current_ast_mod != this) {
+ current_ast_mod->children.push_back(children[i]);
+ children.erase(children.begin() + (i--));
+ did_something = true;
+ }
}
for (auto &attr : attributes) {
- while (attr.second->simplify(true, false, false, stage, -1, false)) { }
+ while (attr.second->simplify(true, false, false, stage, -1, false, true))
+ did_something = true;
}
if (reset_width_after_children) {
@@ -416,11 +464,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_DEFPARAM && !str.empty()) {
size_t pos = str.rfind('.');
if (pos == std::string::npos)
- log_error("Defparam `%s' does not contain a dot (module/parameter seperator) at %s:%d!\n",
- RTLIL::id2cstr(str.c_str()), filename.c_str(), linenum);
+ log_error("Defparam `%s' does not contain a dot (module/parameter separator) at %s:%d!\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
std::string modname = str.substr(0, pos), paraname = "\\" + str.substr(pos+1);
if (current_scope.count(modname) == 0 || current_scope.at(modname)->type != AST_CELL)
- log_error("Can't find cell for defparam `%s . %s` at %s:%d!\n", RTLIL::id2cstr(modname), RTLIL::id2cstr(paraname), filename.c_str(), linenum);
+ log_error("Can't find cell for defparam `%s . %s` at %s:%d!\n", RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paraname).c_str(), filename.c_str(), linenum);
AstNode *cell = current_scope.at(modname), *paraset = clone();
cell->children.insert(cell->children.begin() + 1, paraset);
paraset->type = AST_PARASET;
@@ -434,7 +482,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// dumpAst(NULL, "> ");
log_error("Index in generate block prefix syntax at %s:%d is not constant!\n", filename.c_str(), linenum);
}
- assert(children[1]->type == AST_IDENTIFIER);
+ log_assert(children[1]->type == AST_IDENTIFIER);
newNode = children[1]->clone();
const char *second_part = children[1]->str.c_str();
if (second_part[0] == '\\')
@@ -458,9 +506,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_RANGE) {
bool old_range_valid = range_valid;
range_valid = false;
+ range_swapped = false;
range_left = -1;
range_right = 0;
- assert(children.size() >= 1);
+ log_assert(children.size() >= 1);
if (children[0]->type == AST_CONSTANT) {
range_valid = true;
range_left = children[0]->integer;
@@ -479,6 +528,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
int tmp = range_right;
range_right = range_left;
range_left = tmp;
+ range_swapped = true;
}
}
@@ -489,6 +539,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (!range_valid)
did_something = true;
range_valid = true;
+ range_swapped = children[0]->range_swapped;
range_left = children[0]->range_left;
range_right = children[0]->range_right;
}
@@ -496,24 +547,98 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (!range_valid)
did_something = true;
range_valid = true;
+ range_swapped = false;
range_left = 0;
range_right = 0;
}
}
+ // resolve multiranges on memory decl
+ if (type == AST_MEMORY && children.size() > 1 && children[1]->type == AST_MULTIRANGE)
+ {
+ int total_size = 1;
+ multirange_dimensions.clear();
+ for (auto range : children[1]->children) {
+ if (!range->range_valid)
+ log_error("Non-constant range on memory decl at %s:%d.\n", filename.c_str(), linenum);
+ multirange_dimensions.push_back(std::min(range->range_left, range->range_right));
+ multirange_dimensions.push_back(std::max(range->range_left, range->range_right) - std::min(range->range_left, range->range_right) + 1);
+ total_size *= multirange_dimensions.back();
+ }
+ delete children[1];
+ children[1] = new AstNode(AST_RANGE, AstNode::mkconst_int(0, true), AstNode::mkconst_int(total_size-1, true));
+ did_something = true;
+ }
+
+ // resolve multiranges on memory access
+ if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY && children.size() > 0 && children[0]->type == AST_MULTIRANGE)
+ {
+ AstNode *index_expr = nullptr;
+
+ for (int i = 0; 2*i < SIZE(id2ast->multirange_dimensions); i++)
+ {
+ if (SIZE(children[0]->children) < i)
+ log_error("Insufficient number of array indices for %s at %s:%d.\n", log_id(str), filename.c_str(), linenum);
+
+ AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone();
+
+ if (id2ast->multirange_dimensions[2*i])
+ new_index_expr = new AstNode(AST_SUB, new_index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i], true));
+
+ if (i == 0)
+ index_expr = new_index_expr;
+ else
+ index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i-1], true)), new_index_expr);
+ }
+
+ for (int i = SIZE(id2ast->multirange_dimensions)/1; i < SIZE(children[0]->children); i++)
+ children.push_back(children[0]->children[i]->clone());
+
+ delete children[0];
+ if (index_expr == nullptr)
+ children.erase(children.begin());
+ else
+ children[0] = new AstNode(AST_RANGE, index_expr);
+
+ did_something = true;
+ }
+
// trim/extend parameters
- if ((type == AST_PARAMETER || type == AST_LOCALPARAM) && children[0]->type == AST_CONSTANT && children.size() > 1) {
- if (!children[1]->range_valid)
- log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum);
- int width = children[1]->range_left - children[1]->range_right + 1;
- if (width != int(children[0]->bits.size())) {
- RTLIL::SigSpec sig(children[0]->bits);
- sig.extend_u0(width, children[0]->is_signed);
- AstNode *old_child_0 = children[0];
- children[0] = mkconst_bits(sig.as_const().bits, children[0]->is_signed);
- delete old_child_0;
+ if (type == AST_PARAMETER || type == AST_LOCALPARAM) {
+ if (children.size() > 1 && children[1]->type == AST_RANGE) {
+ if (!children[1]->range_valid)
+ log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum);
+ int width = children[1]->range_left - children[1]->range_right + 1;
+ if (children[0]->type == AST_REALVALUE) {
+ RTLIL::Const constvalue = children[0]->realAsConst(width);
+ log("Warning: converting real value %e to binary %s at %s:%d.\n",
+ children[0]->realvalue, log_signal(constvalue), filename.c_str(), linenum);
+ delete children[0];
+ children[0] = mkconst_bits(constvalue.bits, sign_hint);
+ did_something = true;
+ }
+ if (children[0]->type == AST_CONSTANT) {
+ if (width != int(children[0]->bits.size())) {
+ RTLIL::SigSpec sig(children[0]->bits);
+ sig.extend_u0(width, children[0]->is_signed);
+ AstNode *old_child_0 = children[0];
+ children[0] = mkconst_bits(sig.as_const().bits, children[0]->is_signed);
+ delete old_child_0;
+ }
+ children[0]->is_signed = is_signed;
+ }
+ range_valid = true;
+ range_swapped = children[1]->range_swapped;
+ range_left = children[1]->range_left;
+ range_right = children[1]->range_right;
+ } else
+ if (children.size() > 1 && children[1]->type == AST_REALVALUE && children[0]->type == AST_CONSTANT) {
+ double as_realvalue = children[0]->asReal(sign_hint);
+ delete children[0];
+ children[0] = new AstNode(AST_REALVALUE);
+ children[0]->realvalue = as_realvalue;
+ did_something = true;
}
- children[0]->is_signed = is_signed;
}
// annotate identifiers using scope resolution and create auto-wires as needed
@@ -521,7 +646,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (current_scope.count(str) == 0) {
for (auto node : current_ast_mod->children) {
if ((node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
- node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK) && str == node->str) {
+ node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION) && str == node->str) {
current_scope[node->str] = node;
break;
}
@@ -535,20 +660,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
current_scope[str] = auto_wire;
did_something = true;
}
- id2ast = current_scope[str];
+ if (id2ast != current_scope[str]) {
+ id2ast = current_scope[str];
+ did_something = true;
+ }
}
// split memory access with bit select to individual statements
- if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE)
+ if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue)
{
- if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1 || in_lvalue)
+ if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1)
log_error("Invalid bit-select on memory access at %s:%d!\n", filename.c_str(), linenum);
int mem_width, mem_size, addr_bits;
id2ast->meminfo(mem_width, mem_size, addr_bits);
std::stringstream sstr;
- sstr << "$mem2bits$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+ sstr << "$mem2bits$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
std::string wire_id = sstr.str();
AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
@@ -556,7 +684,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (current_block)
wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
current_ast_mod->children.push_back(wire);
- while (wire->simplify(true, false, false, 1, -1, false)) { }
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
AstNode *data = clone();
delete data->children[1];
@@ -587,6 +715,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
goto apply_newNode;
}
+ if (type == AST_WHILE)
+ log_error("While loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum);
+
+ if (type == AST_REPEAT)
+ log_error("Repeat loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum);
+
// unroll for loops and generate-for blocks
if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0)
{
@@ -621,7 +755,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// eval 1st expression
AstNode *varbuf = init_ast->children[1]->clone();
- while (varbuf->simplify(true, false, false, stage, width_hint, sign_hint)) { }
+ while (varbuf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (varbuf->type != AST_CONSTANT)
log_error("Right hand side of 1st expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
@@ -643,7 +777,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
{
// eval 2nd expression
AstNode *buf = while_ast->clone();
- while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { }
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (buf->type != AST_CONSTANT)
log_error("2nd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
@@ -662,7 +796,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
buf = new AstNode(AST_GENBLOCK, body_ast->clone());
if (buf->str.empty()) {
std::stringstream sstr;
- sstr << "$genblock$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+ sstr << "$genblock$" << filename << ":" << linenum << "$" << (autoidx++);
buf->str = sstr.str();
}
std::map<std::string, std::string> name_map;
@@ -672,7 +806,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_GENFOR) {
for (size_t i = 0; i < buf->children.size(); i++) {
- buf->children[i]->simplify(false, false, false, stage, -1, false);
+ buf->children[i]->simplify(false, false, false, stage, -1, false, false);
current_ast_mod->children.push_back(buf->children[i]);
}
} else {
@@ -684,7 +818,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// eval 3rd expression
buf = next_ast->children[1]->clone();
- while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { }
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (buf->type != AST_CONSTANT)
log_error("Right hand side of 3rd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum);
@@ -708,8 +842,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
std::vector<AstNode*> new_children;
for (size_t i = 0; i < children.size(); i++)
if (children[i]->type == AST_WIRE) {
- children[i]->simplify(false, false, false, stage, -1, false);
+ children[i]->simplify(false, false, false, stage, -1, false, false);
current_ast_mod->children.push_back(children[i]);
+ current_scope[children[i]->str] = children[i];
} else
new_children.push_back(children[i]);
@@ -727,7 +862,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
for (size_t i = 0; i < children.size(); i++) {
- children[i]->simplify(false, false, false, stage, -1, false);
+ children[i]->simplify(false, false, false, stage, -1, false, false);
current_ast_mod->children.push_back(children[i]);
}
@@ -739,7 +874,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_GENIF && children.size() != 0)
{
AstNode *buf = children[0]->clone();
- while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { }
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (buf->type != AST_CONSTANT) {
// for (auto f : log_files)
// dumpAst(f, "verilog-ast> ");
@@ -764,7 +899,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
for (size_t i = 0; i < buf->children.size(); i++) {
- buf->children[i]->simplify(false, false, false, stage, -1, false);
+ buf->children[i]->simplify(false, false, false, stage, -1, false, false);
current_ast_mod->children.push_back(buf->children[i]);
}
@@ -780,7 +915,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_GENCASE && children.size() != 0)
{
AstNode *buf = children[0]->clone();
- while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { }
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (buf->type != AST_CONSTANT) {
// for (auto f : log_files)
// dumpAst(f, "verilog-ast> ");
@@ -814,14 +949,17 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
continue;
buf = child->clone();
- while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { }
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (buf->type != AST_CONSTANT) {
// for (auto f : log_files)
// dumpAst(f, "verilog-ast> ");
log_error("Expression in generate case at %s:%d is not constant!\n", filename.c_str(), linenum);
}
- if (RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool()) {
+ bool is_selected = RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool();
+ delete buf;
+
+ if (is_selected) {
selected_case = this_genblock;
i = children.size();
break;
@@ -840,7 +978,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
for (size_t i = 0; i < buf->children.size(); i++) {
- buf->children[i]->simplify(false, false, false, stage, -1, false);
+ buf->children[i]->simplify(false, false, false, stage, -1, false, false);
current_ast_mod->children.push_back(buf->children[i]);
}
@@ -852,6 +990,31 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
did_something = true;
}
+ // unroll cell arrays
+ if (type == AST_CELLARRAY)
+ {
+ if (!children.at(0)->range_valid)
+ log_error("Non-constant array range on cell array at %s:%d.\n", filename.c_str(), linenum);
+
+ newNode = new AstNode(AST_GENBLOCK);
+ int num = std::max(children.at(0)->range_left, children.at(0)->range_right) - std::min(children.at(0)->range_left, children.at(0)->range_right) + 1;
+
+ for (int i = 0; i < num; i++) {
+ int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i;
+ AstNode *new_cell = children.at(1)->clone();
+ newNode->children.push_back(new_cell);
+ new_cell->str += stringf("[%d]", idx);
+ if (new_cell->type == AST_PRIMITIVE) {
+ log_error("Cell arrays of primitives are currently not supported at %s:%d.\n", filename.c_str(), linenum);
+ } else {
+ log_assert(new_cell->children.at(0)->type == AST_CELLTYPE);
+ new_cell->children.at(0)->str = stringf("$array:%d:%d:%s", i, num, new_cell->children.at(0)->str.c_str());
+ }
+ }
+
+ goto apply_newNode;
+ }
+
// replace primitives with assignmens
if (type == AST_PRIMITIVE)
{
@@ -861,8 +1024,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
std::vector<AstNode*> children_list;
for (auto child : children) {
- assert(child->type == AST_ARGUMENT);
- assert(child->children.size() == 1);
+ log_assert(child->type == AST_ARGUMENT);
+ log_assert(child->children.size() == 1);
children_list.push_back(child->children[0]);
child->children.clear();
delete child;
@@ -917,7 +1080,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
op_type = AST_POS;
if (str == "not")
op_type = AST_POS, invert_results = true;
- assert(op_type != AST_NONE);
+ log_assert(op_type != AST_NONE);
AstNode *node = children_list[1];
if (op_type != AST_POS)
@@ -955,12 +1118,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
shift_expr = range->children[1]->clone();
AstNode *left_at_zero_ast = range->children[0]->clone();
AstNode *right_at_zero_ast = range->children[1]->clone();
- while (left_at_zero_ast->simplify(true, true, false, stage, -1, false)) { }
- while (right_at_zero_ast->simplify(true, true, false, stage, -1, false)) { }
+ while (left_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { }
+ while (right_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n",
str.c_str(), filename.c_str(), linenum);
- result_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
+ result_width = abs(left_at_zero_ast->integer - right_at_zero_ast->integer) + 1;
}
did_something = true;
newNode = new AstNode(AST_CASE, shift_expr);
@@ -981,14 +1144,14 @@ skip_dynamic_range_lvalue_expansion:;
if (stage > 1 && type == AST_ASSERT && current_block != NULL)
{
std::stringstream sstr;
- sstr << "$assert$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+ sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++);
std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN";
AstNode *wire_check = new AstNode(AST_WIRE);
wire_check->str = id_check;
current_ast_mod->children.push_back(wire_check);
current_scope[wire_check->str] = wire_check;
- while (wire_check->simplify(true, false, false, 1, -1, false)) { }
+ while (wire_check->simplify(true, false, false, 1, -1, false, false)) { }
AstNode *wire_en = new AstNode(AST_WIRE);
wire_en->str = id_en;
@@ -996,7 +1159,7 @@ skip_dynamic_range_lvalue_expansion:;
current_ast_mod->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1)))));
current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en;
current_scope[wire_en->str] = wire_en;
- while (wire_en->simplify(true, false, false, 1, -1, false)) { }
+ while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
std::vector<RTLIL::State> x_bit;
x_bit.push_back(RTLIL::State::Sx);
@@ -1051,16 +1214,16 @@ skip_dynamic_range_lvalue_expansion:;
// assignment with memory in left-hand side expression -> replace with memory write port
if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER &&
- children[0]->children.size() == 1 && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY &&
- children[0]->id2ast->children.size() >= 2 && children[0]->id2ast->children[0]->range_valid &&
- children[0]->id2ast->children[1]->range_valid)
+ children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 &&
+ children[0]->id2ast->children[0]->range_valid && children[0]->id2ast->children[1]->range_valid &&
+ (children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE)
{
std::stringstream sstr;
- sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+ sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN";
if (type == AST_ASSIGN_EQ)
- log("Warining: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n",
+ log("Warning: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n",
filename.c_str(), linenum);
int mem_width, mem_size, addr_bits;
@@ -1070,31 +1233,35 @@ skip_dynamic_range_lvalue_expansion:;
wire_addr->str = id_addr;
current_ast_mod->children.push_back(wire_addr);
current_scope[wire_addr->str] = wire_addr;
- while (wire_addr->simplify(true, false, false, 1, -1, false)) { }
+ while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
wire_data->str = id_data;
current_ast_mod->children.push_back(wire_data);
current_scope[wire_data->str] = wire_data;
- while (wire_data->simplify(true, false, false, 1, -1, false)) { }
+ while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
- AstNode *wire_en = new AstNode(AST_WIRE);
+ AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
wire_en->str = id_en;
current_ast_mod->children.push_back(wire_en);
current_scope[wire_en->str] = wire_en;
- while (wire_en->simplify(true, false, false, 1, -1, false)) { }
+ while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
- std::vector<RTLIL::State> x_bits;
+ std::vector<RTLIL::State> x_bits_addr, x_bits_data, set_bits_en;
+ for (int i = 0; i < addr_bits; i++)
+ x_bits_addr.push_back(RTLIL::State::Sx);
for (int i = 0; i < mem_width; i++)
- x_bits.push_back(RTLIL::State::Sx);
+ x_bits_data.push_back(RTLIL::State::Sx);
+ for (int i = 0; i < mem_width; i++)
+ set_bits_en.push_back(RTLIL::State::S1);
- AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits, false));
+ AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false));
assign_addr->children[0]->str = id_addr;
- AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits, false));
+ AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false));
assign_data->children[0]->str = id_data;
- AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, 1));
+ AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width));
assign_en->children[0]->str = id_en;
AstNode *default_signals = new AstNode(AST_BLOCK);
@@ -1106,11 +1273,62 @@ skip_dynamic_range_lvalue_expansion:;
assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
assign_addr->children[0]->str = id_addr;
- assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone());
- assign_data->children[0]->str = id_data;
+ if (children[0]->children.size() == 2)
+ {
+ if (children[0]->children[1]->range_valid)
+ {
+ int offset = children[0]->children[1]->range_right;
+ int width = children[0]->children[1]->range_left - offset + 1;
- assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1));
- assign_en->children[0]->str = id_en;
+ std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx);
+
+ for (int i = 0; i < mem_width; i++)
+ set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0;
+
+ assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+ new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone()));
+ assign_data->children[0]->str = id_data;
+
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
+ assign_en->children[0]->str = id_en;
+ }
+ else
+ {
+ AstNode *the_range = children[0]->children[1];
+ AstNode *left_at_zero_ast = the_range->children[0]->clone();
+ AstNode *right_at_zero_ast = the_range->children.size() >= 2 ? the_range->children[1]->clone() : left_at_zero_ast->clone();
+ AstNode *offset_ast = right_at_zero_ast->clone();
+
+ while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
+ while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
+ if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
+ log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
+ int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
+
+ for (int i = 0; i < mem_width; i++)
+ set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0;
+
+ assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+ new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
+ assign_data->children[0]->str = id_data;
+
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
+ new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone()));
+ assign_en->children[0]->str = id_en;
+
+ delete left_at_zero_ast;
+ delete right_at_zero_ast;
+ delete offset_ast;
+ }
+ }
+ else
+ {
+ assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone());
+ assign_data->children[0]->str = id_data;
+
+ assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false));
+ assign_en->children[0]->str = id_en;
+ }
newNode = new AstNode(AST_BLOCK);
newNode->children.push_back(assign_addr);
@@ -1137,21 +1355,127 @@ skip_dynamic_range_lvalue_expansion:;
{
if (str == "\\$clog2")
{
+ if (children.size() != 1)
+ log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+
AstNode *buf = children[0]->clone();
- while (buf->simplify(true, false, false, stage, width_hint, sign_hint)) { }
+ while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (buf->type != AST_CONSTANT)
log_error("Failed to evaluate system function `%s' with non-constant value at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
RTLIL::Const arg_value = buf->bitsAsConst();
+ if (arg_value.as_bool())
+ arg_value = const_sub(arg_value, 1, false, false, SIZE(arg_value));
+ delete buf;
+
uint32_t result = 0;
for (size_t i = 0; i < arg_value.bits.size(); i++)
if (arg_value.bits.at(i) == RTLIL::State::S1)
- result = i;
+ result = i + 1;
newNode = mkconst_int(result, false);
goto apply_newNode;
}
+ if (str == "\\$ln" || str == "\\$log10" || str == "\\$exp" || str == "\\$sqrt" || str == "\\$pow" ||
+ str == "\\$floor" || str == "\\$ceil" || str == "\\$sin" || str == "\\$cos" || str == "\\$tan" ||
+ str == "\\$asin" || str == "\\$acos" || str == "\\$atan" || str == "\\$atan2" || str == "\\$hypot" ||
+ str == "\\$sinh" || str == "\\$cosh" || str == "\\$tanh" || str == "\\$asinh" || str == "\\$acosh" || str == "\\$atanh")
+ {
+ bool func_with_two_arguments = str == "\\$pow" || str == "\\$atan2" || str == "\\$hypot";
+ double x = 0, y = 0;
+
+ if (func_with_two_arguments) {
+ if (children.size() != 2)
+ log_error("System function %s got %d arguments, expected 2 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+ } else {
+ if (children.size() != 1)
+ log_error("System function %s got %d arguments, expected 1 at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum);
+ }
+
+ if (children.size() >= 1) {
+ while (children[0]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (!children[0]->isConst())
+ log_error("Failed to evaluate system function `%s' with non-constant argument at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ int child_width_hint = width_hint;
+ bool child_sign_hint = sign_hint;
+ children[0]->detectSignWidth(child_width_hint, child_sign_hint);
+ x = children[0]->asReal(child_sign_hint);
+ }
+
+ if (children.size() >= 2) {
+ while (children[1]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ if (!children[1]->isConst())
+ log_error("Failed to evaluate system function `%s' with non-constant argument at %s:%d.\n",
+ RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
+ int child_width_hint = width_hint;
+ bool child_sign_hint = sign_hint;
+ children[1]->detectSignWidth(child_width_hint, child_sign_hint);
+ y = children[1]->asReal(child_sign_hint);
+ }
+
+ newNode = new AstNode(AST_REALVALUE);
+ if (str == "\\$ln") newNode->realvalue = ::log(x);
+ else if (str == "\\$log10") newNode->realvalue = ::log10(x);
+ else if (str == "\\$exp") newNode->realvalue = ::exp(x);
+ else if (str == "\\$sqrt") newNode->realvalue = ::sqrt(x);
+ else if (str == "\\$pow") newNode->realvalue = ::pow(x, y);
+ else if (str == "\\$floor") newNode->realvalue = ::floor(x);
+ else if (str == "\\$ceil") newNode->realvalue = ::ceil(x);
+ else if (str == "\\$sin") newNode->realvalue = ::sin(x);
+ else if (str == "\\$cos") newNode->realvalue = ::cos(x);
+ else if (str == "\\$tan") newNode->realvalue = ::tan(x);
+ else if (str == "\\$asin") newNode->realvalue = ::asin(x);
+ else if (str == "\\$acos") newNode->realvalue = ::acos(x);
+ else if (str == "\\$atan") newNode->realvalue = ::atan(x);
+ else if (str == "\\$atan2") newNode->realvalue = ::atan2(x, y);
+ else if (str == "\\$hypot") newNode->realvalue = ::hypot(x, y);
+ else if (str == "\\$sinh") newNode->realvalue = ::sinh(x);
+ else if (str == "\\$cosh") newNode->realvalue = ::cosh(x);
+ else if (str == "\\$tanh") newNode->realvalue = ::tanh(x);
+ else if (str == "\\$asinh") newNode->realvalue = ::asinh(x);
+ else if (str == "\\$acosh") newNode->realvalue = ::acosh(x);
+ else if (str == "\\$atanh") newNode->realvalue = ::atanh(x);
+ else log_abort();
+ goto apply_newNode;
+ }
+
+ if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION)
+ {
+ AstNode *dpi_decl = current_scope[str];
+
+ std::string rtype, fname;
+ std::vector<std::string> argtypes;
+ std::vector<AstNode*> args;
+
+ rtype = RTLIL::unescape_id(dpi_decl->children.at(0)->str);
+ fname = RTLIL::unescape_id(dpi_decl->children.at(1)->str);
+
+ for (int i = 2; i < SIZE(dpi_decl->children); i++)
+ {
+ if (i-2 >= SIZE(children))
+ log_error("Insufficient number of arguments in DPI function call at %s:%d.\n", filename.c_str(), linenum);
+
+ argtypes.push_back(RTLIL::unescape_id(dpi_decl->children.at(i)->str));
+ args.push_back(children.at(i-2)->clone());
+ while (args.back()->simplify(true, false, false, stage, -1, false, true)) { }
+
+ if (args.back()->type != AST_CONSTANT && args.back()->type != AST_REALVALUE)
+ log_error("Failed to evaluate DPI function with non-constant argument at %s:%d.\n", filename.c_str(), linenum);
+ }
+
+ newNode = dpi_call(rtype, fname, argtypes, args);
+
+ for (auto arg : args)
+ delete arg;
+
+ goto apply_newNode;
+ }
+
if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION)
log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
}
@@ -1161,22 +1485,47 @@ skip_dynamic_range_lvalue_expansion:;
}
AstNode *decl = current_scope[str];
+
std::stringstream sstr;
- sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++) << "$";
+ sstr << "$func$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++) << "$";
std::string prefix = sstr.str();
+ bool recommend_const_eval = false;
+ bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval);
+ if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count("\\via_celltype"))
+ {
+ bool all_args_const = true;
+ for (auto child : children) {
+ while (child->simplify(true, false, false, 1, -1, false, true)) { }
+ if (child->type != AST_CONSTANT)
+ all_args_const = false;
+ }
+
+ if (all_args_const) {
+ AstNode *func_workspace = current_scope[str]->clone();
+ newNode = func_workspace->eval_const_function(this);
+ delete func_workspace;
+ goto apply_newNode;
+ }
+
+ if (in_param)
+ log_error("Non-constant function call in constant expression at %s:%d.\n", filename.c_str(), linenum);
+ if (require_const_eval)
+ log_error("Function %s can only be called with constant arguments at %s:%d.\n", str.c_str(), filename.c_str(), linenum);
+ }
+
size_t arg_count = 0;
std::map<std::string, std::string> replace_rules;
if (current_block == NULL)
{
- assert(type == AST_FCALL);
+ log_assert(type == AST_FCALL);
AstNode *wire = NULL;
for (auto child : decl->children)
if (child->type == AST_WIRE && child->str == str)
wire = child->clone();
- assert(wire != NULL);
+ log_assert(wire != NULL);
wire->str = prefix + str;
wire->port_id = 0;
@@ -1184,7 +1533,7 @@ skip_dynamic_range_lvalue_expansion:;
wire->is_output = false;
current_ast_mod->children.push_back(wire);
- while (wire->simplify(true, false, false, 1, -1, false)) { }
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
AstNode *lvalue = new AstNode(AST_IDENTIFIER);
lvalue->str = wire->str;
@@ -1196,8 +1545,69 @@ skip_dynamic_range_lvalue_expansion:;
goto replace_fcall_with_id;
}
- for (auto child : decl->children)
+ if (decl->attributes.count("\\via_celltype"))
{
+ std::string celltype = decl->attributes.at("\\via_celltype")->asAttrConst().decode_string();
+ std::string outport = str;
+
+ if (celltype.find(' ') != std::string::npos) {
+ int pos = celltype.find(' ');
+ outport = RTLIL::escape_id(celltype.substr(pos+1));
+ celltype = RTLIL::escape_id(celltype.substr(0, pos));
+ } else
+ celltype = RTLIL::escape_id(celltype);
+
+ AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE));
+ cell->str = prefix.substr(0, SIZE(prefix)-1);
+ cell->children[0]->str = celltype;
+
+ for (auto attr : decl->attributes)
+ if (attr.first.str().rfind("\\via_celltype_defparam_", 0) == 0)
+ {
+ AstNode *cell_arg = new AstNode(AST_PARASET, attr.second->clone());
+ cell_arg->str = RTLIL::escape_id(attr.first.str().substr(strlen("\\via_celltype_defparam_")));
+ cell->children.push_back(cell_arg);
+ }
+
+ for (auto child : decl->children)
+ if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str)))
+ {
+ AstNode *wire = child->clone();
+ wire->str = prefix + wire->str;
+ wire->port_id = 0;
+ wire->is_input = false;
+ wire->is_output = false;
+ current_ast_mod->children.push_back(wire);
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
+
+ AstNode *wire_id = new AstNode(AST_IDENTIFIER);
+ wire_id->str = wire->str;
+
+ if ((child->is_input || child->is_output) && arg_count < children.size())
+ {
+ AstNode *arg = children[arg_count++]->clone();
+ AstNode *assign = child->is_input ?
+ new AstNode(AST_ASSIGN_EQ, wire_id->clone(), arg) :
+ new AstNode(AST_ASSIGN_EQ, arg, wire_id->clone());
+
+ for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
+ if (*it != current_block_child)
+ continue;
+ current_block->children.insert(it, assign);
+ break;
+ }
+ }
+
+ AstNode *cell_arg = new AstNode(AST_ARGUMENT, wire_id);
+ cell_arg->str = child->str == str ? outport : child->str;
+ cell->children.push_back(cell_arg);
+ }
+
+ current_ast_mod->children.push_back(cell);
+ goto replace_fcall_with_id;
+ }
+
+ for (auto child : decl->children)
if (child->type == AST_WIRE)
{
AstNode *wire = child->clone();
@@ -1206,16 +1616,18 @@ skip_dynamic_range_lvalue_expansion:;
wire->is_input = false;
wire->is_output = false;
current_ast_mod->children.push_back(wire);
- while (wire->simplify(true, false, false, 1, -1, false)) { }
+ while (wire->simplify(true, false, false, 1, -1, false, false)) { }
replace_rules[child->str] = wire->str;
- if (child->is_input && arg_count < children.size())
+ if ((child->is_input || child->is_output) && arg_count < children.size())
{
AstNode *arg = children[arg_count++]->clone();
AstNode *wire_id = new AstNode(AST_IDENTIFIER);
wire_id->str = wire->str;
- AstNode *assign = new AstNode(AST_ASSIGN_EQ, wire_id, arg);
+ AstNode *assign = child->is_input ?
+ new AstNode(AST_ASSIGN_EQ, wire_id, arg) :
+ new AstNode(AST_ASSIGN_EQ, arg, wire_id);
for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
if (*it != current_block_child)
@@ -1225,10 +1637,12 @@ skip_dynamic_range_lvalue_expansion:;
}
}
}
- else
+
+ for (auto child : decl->children)
+ if (child->type != AST_WIRE)
{
AstNode *stmt = child->clone();
- stmt->replace_ids(replace_rules);
+ stmt->replace_ids(prefix, replace_rules);
for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
if (*it != current_block_child)
@@ -1237,7 +1651,6 @@ skip_dynamic_range_lvalue_expansion:;
break;
}
}
- }
replace_fcall_with_id:
if (type == AST_FCALL) {
@@ -1251,7 +1664,7 @@ skip_dynamic_range_lvalue_expansion:;
}
// perform const folding when activated
- if (const_fold && newNode == NULL)
+ if (const_fold)
{
bool string_op;
std::vector<RTLIL::State> tmp_bits;
@@ -1265,13 +1678,29 @@ skip_dynamic_range_lvalue_expansion:;
if (current_scope[str]->children[0]->type == AST_CONSTANT) {
if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) {
std::vector<RTLIL::State> data;
- for (int i = children[0]->range_right; i <= children[0]->range_left; i++)
- data.push_back(current_scope[str]->children[0]->bits[i]);
+ bool param_upto = current_scope[str]->range_valid && current_scope[str]->range_swapped;
+ int param_offset = current_scope[str]->range_valid ? current_scope[str]->range_right : 0;
+ int param_width = current_scope[str]->range_valid ? current_scope[str]->range_left - current_scope[str]->range_right + 1 :
+ SIZE(current_scope[str]->children[0]->bits);
+ int tmp_range_left = children[0]->range_left, tmp_range_right = children[0]->range_right;
+ if (param_upto) {
+ tmp_range_left = (param_width + 2*param_offset) - children[0]->range_right - 1;
+ tmp_range_right = (param_width + 2*param_offset) - children[0]->range_left - 1;
+ }
+ for (int i = tmp_range_right; i <= tmp_range_left; i++) {
+ int index = i - param_offset;
+ if (0 <= index && index < param_width)
+ data.push_back(current_scope[str]->children[0]->bits[index]);
+ else
+ data.push_back(RTLIL::State::Sx);
+ }
newNode = mkconst_bits(data, false);
} else
if (children.size() == 0)
newNode = current_scope[str]->children[0]->clone();
- }
+ } else
+ if (current_scope[str]->children[0]->isConst())
+ newNode = current_scope[str]->children[0]->clone();
}
else if (at_zero && current_scope.count(str) > 0 && (current_scope[str]->type == AST_WIRE || current_scope[str]->type == AST_AUTOWIRE)) {
newNode = mkconst_int(0, sign_hint, width_hint);
@@ -1314,6 +1743,9 @@ skip_dynamic_range_lvalue_expansion:;
if (children[0]->type == AST_CONSTANT) {
RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1);
newNode = mkconst_bits(y.bits, false);
+ } else
+ if (children[0]->isConst()) {
+ newNode = mkconst_int(children[0]->asReal(sign_hint) == 0, false, 1);
}
break;
if (0) { case AST_LOGIC_AND: const_func = RTLIL::const_logic_and; }
@@ -1322,6 +1754,12 @@ skip_dynamic_range_lvalue_expansion:;
RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits),
children[0]->is_signed, children[1]->is_signed, -1);
newNode = mkconst_bits(y.bits, false);
+ } else
+ if (children[0]->isConst() && children[1]->isConst()) {
+ if (type == AST_LOGIC_AND)
+ newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) && (children[1]->asReal(sign_hint) != 0), false, 1);
+ else
+ newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) || (children[1]->asReal(sign_hint) != 0), false, 1);
}
break;
if (0) { case AST_SHIFT_LEFT: const_func = RTLIL::const_shl; }
@@ -1333,6 +1771,10 @@ skip_dynamic_range_lvalue_expansion:;
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
RTLIL::Const(children[1]->bits), sign_hint, type == AST_POW ? children[1]->is_signed : false, width_hint);
newNode = mkconst_bits(y.bits, sign_hint);
+ } else
+ if (type == AST_POW && children[0]->isConst() && children[1]->isConst()) {
+ newNode = new AstNode(AST_REALVALUE);
+ newNode->realvalue = pow(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint));
}
break;
if (0) { case AST_LT: const_func = RTLIL::const_lt; }
@@ -1349,6 +1791,20 @@ skip_dynamic_range_lvalue_expansion:;
RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed),
children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1);
newNode = mkconst_bits(y.bits, false);
+ } else
+ if (children[0]->isConst() && children[1]->isConst()) {
+ bool cmp_signed = (children[0]->type == AST_REALVALUE || children[0]->is_signed) && (children[1]->type == AST_REALVALUE || children[1]->is_signed);
+ switch (type) {
+ case AST_LT: newNode = mkconst_int(children[0]->asReal(cmp_signed) < children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_LE: newNode = mkconst_int(children[0]->asReal(cmp_signed) <= children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_EQ: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_NE: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_EQX: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_NEX: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_GE: newNode = mkconst_int(children[0]->asReal(cmp_signed) >= children[1]->asReal(cmp_signed), false, 1); break;
+ case AST_GT: newNode = mkconst_int(children[0]->asReal(cmp_signed) > children[1]->asReal(cmp_signed), false, 1); break;
+ default: log_abort();
+ }
}
break;
if (0) { case AST_ADD: const_func = RTLIL::const_add; }
@@ -1360,6 +1816,17 @@ skip_dynamic_range_lvalue_expansion:;
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint);
newNode = mkconst_bits(y.bits, sign_hint);
+ } else
+ if (children[0]->isConst() && children[1]->isConst()) {
+ newNode = new AstNode(AST_REALVALUE);
+ switch (type) {
+ case AST_ADD: newNode->realvalue = children[0]->asReal(sign_hint) + children[1]->asReal(sign_hint); break;
+ case AST_SUB: newNode->realvalue = children[0]->asReal(sign_hint) - children[1]->asReal(sign_hint); break;
+ case AST_MUL: newNode->realvalue = children[0]->asReal(sign_hint) * children[1]->asReal(sign_hint); break;
+ case AST_DIV: newNode->realvalue = children[0]->asReal(sign_hint) / children[1]->asReal(sign_hint); break;
+ case AST_MOD: newNode->realvalue = fmod(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint)); break;
+ default: log_abort();
+ }
}
break;
if (0) { case AST_POS: const_func = RTLIL::const_pos; }
@@ -1367,37 +1834,105 @@ skip_dynamic_range_lvalue_expansion:;
if (children[0]->type == AST_CONSTANT) {
RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint);
newNode = mkconst_bits(y.bits, sign_hint);
+ } else
+ if (children[0]->isConst()) {
+ newNode = new AstNode(AST_REALVALUE);
+ if (type == AST_POS)
+ newNode->realvalue = +children[0]->asReal(sign_hint);
+ else
+ newNode->realvalue = -children[0]->asReal(sign_hint);
+ }
+ break;
+ case AST_CASE:
+ if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) {
+ std::vector<AstNode*> new_children;
+ new_children.push_back(children[0]);
+ for (int i = 1; i < SIZE(children); i++) {
+ AstNode *child = children[i];
+ log_assert(child->type == AST_COND);
+ for (auto v : child->children) {
+ if (v->type == AST_DEFAULT)
+ goto keep_const_cond;
+ if (v->type == AST_BLOCK)
+ continue;
+ if (v->type == AST_CONSTANT && v->bits_only_01()) {
+ if (v->bits == children[0]->bits) {
+ while (i+1 < SIZE(children))
+ delete children[++i];
+ goto keep_const_cond;
+ }
+ continue;
+ }
+ goto keep_const_cond;
+ }
+ if (0)
+ keep_const_cond:
+ new_children.push_back(child);
+ else
+ delete child;
+ }
+ new_children.swap(children);
}
break;
case AST_TERNARY:
- if (children[0]->type == AST_CONSTANT) {
+ if (children[0]->isConst())
+ {
bool found_sure_true = false;
bool found_maybe_true = false;
- for (auto &bit : children[0]->bits) {
- if (bit == RTLIL::State::S1)
- found_sure_true = true;
- if (bit > RTLIL::State::S1)
- found_maybe_true = true;
- }
- AstNode *choice = NULL;
+
+ if (children[0]->type == AST_CONSTANT)
+ for (auto &bit : children[0]->bits) {
+ if (bit == RTLIL::State::S1)
+ found_sure_true = true;
+ if (bit > RTLIL::State::S1)
+ found_maybe_true = true;
+ }
+ else
+ found_sure_true = children[0]->asReal(sign_hint) != 0;
+
+ AstNode *choice = NULL, *not_choice = NULL;
if (found_sure_true)
- choice = children[1];
+ choice = children[1], not_choice = children[2];
else if (!found_maybe_true)
- choice = children[2];
- if (choice != NULL && choice->type == AST_CONSTANT) {
- RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint);
- if (choice->is_string && y.bits.size() % 8 == 0 && sign_hint == false)
- newNode = mkconst_str(y.bits);
- else
- newNode = mkconst_bits(y.bits, sign_hint);
+ choice = children[2], not_choice = children[1];
+
+ if (choice != NULL) {
+ if (choice->type == AST_CONSTANT) {
+ int other_width_hint = width_hint;
+ bool other_sign_hint = sign_hint, other_real = false;
+ not_choice->detectSignWidth(other_width_hint, other_sign_hint, &other_real);
+ if (other_real) {
+ newNode = new AstNode(AST_REALVALUE);
+ choice->detectSignWidth(width_hint, sign_hint);
+ newNode->realvalue = choice->asReal(sign_hint);
+ } else {
+ RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint);
+ if (choice->is_string && y.bits.size() % 8 == 0 && sign_hint == false)
+ newNode = mkconst_str(y.bits);
+ else
+ newNode = mkconst_bits(y.bits, sign_hint);
+ }
+ } else
+ if (choice->isConst()) {
+ newNode = choice->clone();
+ }
} else if (children[1]->type == AST_CONSTANT && children[2]->type == AST_CONSTANT) {
RTLIL::Const a = children[1]->bitsAsConst(width_hint, sign_hint);
RTLIL::Const b = children[2]->bitsAsConst(width_hint, sign_hint);
- assert(a.bits.size() == b.bits.size());
+ log_assert(a.bits.size() == b.bits.size());
for (size_t i = 0; i < a.bits.size(); i++)
if (a.bits[i] != b.bits[i])
a.bits[i] = RTLIL::State::Sx;
newNode = mkconst_bits(a.bits, sign_hint);
+ } else if (children[1]->isConst() && children[2]->isConst()) {
+ newNode = new AstNode(AST_REALVALUE);
+ if (children[1]->asReal(sign_hint) == children[2]->asReal(sign_hint))
+ newNode->realvalue = children[1]->asReal(sign_hint);
+ else
+ // IEEE Std 1800-2012 Sec. 11.4.11 states that the entry in Table 7-1 for
+ // the data type in question should be returned if the ?: is ambiguous. The
+ // value in Table 7-1 for the 'real' type is 0.0.
+ newNode->realvalue = 0.0;
}
}
break;
@@ -1431,7 +1966,7 @@ apply_newNode:
// fprintf(stderr, "----\n");
// dumpAst(stderr, "- ");
// newNode->dumpAst(stderr, "+ ");
- assert(newNode != NULL);
+ log_assert(newNode != NULL);
newNode->filename = filename;
newNode->linenum = linenum;
newNode->cloneInto(this);
@@ -1501,12 +2036,45 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
}
// rename stuff (used when tasks of functions are instanciated)
-void AstNode::replace_ids(std::map<std::string, std::string> &rules)
+void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules)
{
- if (type == AST_IDENTIFIER && rules.count(str) > 0)
- str = rules[str];
- for (auto child : children)
- child->replace_ids(rules);
+ if (type == AST_BLOCK)
+ {
+ std::map<std::string, std::string> new_rules = rules;
+ std::string new_prefix = prefix + str;
+
+ for (auto child : children)
+ if (child->type == AST_WIRE) {
+ new_rules[child->str] = new_prefix + child->str;
+ child->str = new_prefix + child->str;
+ }
+
+ for (auto child : children)
+ if (child->type != AST_WIRE)
+ child->replace_ids(new_prefix, new_rules);
+ }
+ else
+ {
+ if (type == AST_IDENTIFIER && rules.count(str) > 0)
+ str = rules.at(str);
+ for (auto child : children)
+ child->replace_ids(prefix, rules);
+ }
+}
+
+// helper function for mem2reg_as_needed_pass1
+static void mark_memories_assign_lhs_complex(std::map<AstNode*, std::set<std::string>> &mem2reg_places,
+ std::map<AstNode*, uint32_t> &mem2reg_candidates, AstNode *that)
+{
+ for (auto &child : that->children)
+ mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child);
+
+ if (that->type == AST_IDENTIFIER && that->id2ast && that->id2ast->type == AST_MEMORY) {
+ AstNode *mem = that->id2ast;
+ if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_CMPLX_LHS))
+ mem2reg_places[mem].insert(stringf("%s:%d", that->filename.c_str(), that->linenum));
+ mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS;
+ }
}
// find memories that should be replaced by registers
@@ -1518,6 +2086,10 @@ void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>>
if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)
{
+ // mark all memories that are used in a complex expression on the left side of an assignment
+ for (auto &lhs_child : children[0]->children)
+ mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child);
+
if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY)
{
AstNode *mem = children[0]->id2ast;
@@ -1588,7 +2160,7 @@ void AstNode::mem2reg_as_needed_pass1(std::map<AstNode*, std::set<std::string>>
uint32_t backup_flags = flags;
flags |= children_flags;
- assert((flags & ~0x000000ff) == 0);
+ log_assert((flags & ~0x000000ff) == 0);
for (auto child : children)
if (ignore_children_counter > 0)
@@ -1617,7 +2189,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
mem2reg_set.count(children[0]->id2ast) > 0 && children[0]->children[0]->children[0]->type != AST_CONSTANT)
{
std::stringstream sstr;
- sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+ sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
int mem_width, mem_size, addr_bits;
@@ -1628,20 +2200,20 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
wire_addr->is_reg = true;
wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
mod->children.push_back(wire_addr);
- while (wire_addr->simplify(true, false, false, 1, -1, false)) { }
+ while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
wire_data->str = id_data;
wire_data->is_reg = true;
wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
mod->children.push_back(wire_data);
- while (wire_data->simplify(true, false, false, 1, -1, false)) { }
+ while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
- assert(block != NULL);
+ log_assert(block != NULL);
size_t assign_idx = 0;
while (assign_idx < block->children.size() && block->children[assign_idx] != this)
assign_idx++;
- assert(assign_idx < block->children.size());
+ log_assert(assign_idx < block->children.size());
AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
assign_addr->children[0]->str = id_addr;
@@ -1654,6 +2226,8 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
continue;
AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK));
AstNode *assign_reg = new AstNode(type, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER));
+ if (children[0]->children.size() == 2)
+ assign_reg->children[0]->children.push_back(children[0]->children[1]->clone());
assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i);
assign_reg->children[1]->str = id_data;
cond_node->children[1]->children.push_back(assign_reg);
@@ -1670,6 +2244,10 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
if (type == AST_IDENTIFIER && id2ast && mem2reg_set.count(id2ast) > 0)
{
+ AstNode *bit_part_sel = NULL;
+ if (children.size() == 2)
+ bit_part_sel = children[1]->clone();
+
if (children[0]->children[0]->type == AST_CONSTANT)
{
int id = children[0]->children[0]->integer;
@@ -1682,7 +2260,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
else
{
std::stringstream sstr;
- sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (RTLIL::autoidx++);
+ sstr << "$mem2reg_rd$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA";
int mem_width, mem_size, addr_bits;
@@ -1694,7 +2272,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
if (block)
wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
mod->children.push_back(wire_addr);
- while (wire_addr->simplify(true, false, false, 1, -1, false)) { }
+ while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { }
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true)));
wire_data->str = id_data;
@@ -1702,7 +2280,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
if (block)
wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
mod->children.push_back(wire_data);
- while (wire_data->simplify(true, false, false, 1, -1, false)) { }
+ while (wire_data->simplify(true, false, false, 1, -1, false, false)) { }
AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone());
assign_addr->children[0]->str = id_addr;
@@ -1736,7 +2314,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
size_t assign_idx = 0;
while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this))
assign_idx++;
- assert(assign_idx < block->children.size());
+ log_assert(assign_idx < block->children.size());
block->children.insert(block->children.begin()+assign_idx, case_node);
block->children.insert(block->children.begin()+assign_idx, assign_addr);
}
@@ -1753,9 +2331,12 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
id2ast = NULL;
str = id_data;
}
+
+ if (bit_part_sel)
+ children.push_back(bit_part_sel);
}
- assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0);
+ log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0);
auto children_list = children;
for (size_t i = 0; i < children_list.size(); i++)
@@ -1765,7 +2346,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set<AstNode*> &mem2reg_set, AstNode *
// calulate memory dimensions
void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits)
{
- assert(type == AST_MEMORY);
+ log_assert(type == AST_MEMORY);
mem_width = children[0]->range_left - children[0]->range_right + 1;
mem_size = children[1]->range_left - children[1]->range_right;
@@ -1779,3 +2360,267 @@ void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits)
addr_bits++;
}
+bool AstNode::has_const_only_constructs(bool &recommend_const_eval)
+{
+ if (type == AST_FOR)
+ recommend_const_eval = true;
+ if (type == AST_WHILE || type == AST_REPEAT)
+ return true;
+ if (type == AST_FCALL && current_scope.count(str))
+ if (current_scope[str]->has_const_only_constructs(recommend_const_eval))
+ return true;
+ for (auto child : children)
+ if (child->AstNode::has_const_only_constructs(recommend_const_eval))
+ return true;
+ return false;
+}
+
+// helper function for AstNode::eval_const_function()
+void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall)
+{
+ if (type == AST_IDENTIFIER && variables.count(str)) {
+ int offset = variables.at(str).offset, width = variables.at(str).val.bits.size();
+ if (!children.empty()) {
+ if (children.size() != 1 || children.at(0)->type != AST_RANGE)
+ log_error("Memory access in constant function is not supported in %s:%d (called from %s:%d).\n",
+ filename.c_str(), linenum, fcall->filename.c_str(), fcall->linenum);
+ children.at(0)->replace_variables(variables, fcall);
+ while (simplify(true, false, false, 1, -1, false, true)) { }
+ if (!children.at(0)->range_valid)
+ log_error("Non-constant range in %s:%d (called from %s:%d).\n",
+ filename.c_str(), linenum, fcall->filename.c_str(), fcall->linenum);
+ offset = std::min(children.at(0)->range_left, children.at(0)->range_right);
+ width = std::min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width);
+ }
+ offset -= variables.at(str).offset;
+ std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits;
+ std::vector<RTLIL::State> new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width);
+ AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed);
+ newNode->cloneInto(this);
+ delete newNode;
+ return;
+ }
+
+ for (auto &child : children)
+ child->replace_variables(variables, fcall);
+}
+
+// evaluate functions with all-const arguments
+AstNode *AstNode::eval_const_function(AstNode *fcall)
+{
+ std::map<std::string, AstNode*> backup_scope;
+ std::map<std::string, AstNode::varinfo_t> variables;
+ bool delete_temp_block = false;
+ AstNode *block = NULL;
+
+ size_t argidx = 0;
+ for (auto child : children)
+ {
+ if (child->type == AST_BLOCK)
+ {
+ log_assert(block == NULL);
+ block = child;
+ continue;
+ }
+
+ if (child->type == AST_WIRE)
+ {
+ while (child->simplify(true, false, false, 1, -1, false, true)) { }
+ if (!child->range_valid)
+ log_error("Can't determine size of variable %s in %s:%d (called from %s:%d).\n",
+ child->str.c_str(), child->filename.c_str(), child->linenum, fcall->filename.c_str(), fcall->linenum);
+ variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1);
+ variables[child->str].offset = std::min(child->range_left, child->range_right);
+ variables[child->str].is_signed = child->is_signed;
+ if (child->is_input && argidx < fcall->children.size())
+ variables[child->str].val = fcall->children.at(argidx++)->bitsAsConst(variables[child->str].val.bits.size());
+ backup_scope[child->str] = current_scope[child->str];
+ current_scope[child->str] = child;
+ continue;
+ }
+
+ log_assert(block == NULL);
+ delete_temp_block = true;
+ block = new AstNode(AST_BLOCK);
+ block->children.push_back(child->clone());
+ }
+
+ log_assert(block != NULL);
+ log_assert(variables.count(str));
+
+ while (!block->children.empty())
+ {
+ AstNode *stmt = block->children.front();
+
+#if 0
+ log("-----------------------------------\n");
+ for (auto &it : variables)
+ log("%20s %40s\n", it.first.c_str(), log_signal(it.second.val));
+ stmt->dumpAst(NULL, "stmt> ");
+#endif
+
+ if (stmt->type == AST_ASSIGN_EQ)
+ {
+ stmt->children.at(1)->replace_variables(variables, fcall);
+ while (stmt->simplify(true, false, false, 1, -1, false, true)) { }
+
+ if (stmt->type != AST_ASSIGN_EQ)
+ continue;
+
+ if (stmt->children.at(1)->type != AST_CONSTANT)
+ log_error("Non-constant expression in constant function at %s:%d (called from %s:%d). X\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ if (stmt->children.at(0)->type != AST_IDENTIFIER)
+ log_error("Unsupported composite left hand side in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ if (!variables.count(stmt->children.at(0)->str))
+ log_error("Assignment to non-local variable in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ if (stmt->children.at(0)->children.empty()) {
+ variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size());
+ } else {
+ AstNode *range = stmt->children.at(0)->children.at(0);
+ if (!range->range_valid)
+ log_error("Non-constant range in %s:%d (called from %s:%d).\n",
+ range->filename.c_str(), range->linenum, fcall->filename.c_str(), fcall->linenum);
+ int offset = std::min(range->range_left, range->range_right);
+ int width = std::abs(range->range_left - range->range_right) + 1;
+ varinfo_t &v = variables[stmt->children.at(0)->str];
+ RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size());
+ for (int i = 0; i < width; i++)
+ v.val.bits.at(i+offset-v.offset) = r.bits.at(i);
+ }
+
+ delete block->children.front();
+ block->children.erase(block->children.begin());
+ continue;
+ }
+
+ if (stmt->type == AST_FOR)
+ {
+ block->children.insert(block->children.begin(), stmt->children.at(0));
+ stmt->children.at(3)->children.push_back(stmt->children.at(2));
+ stmt->children.erase(stmt->children.begin() + 2);
+ stmt->children.erase(stmt->children.begin());
+ stmt->type = AST_WHILE;
+ continue;
+ }
+
+ if (stmt->type == AST_WHILE)
+ {
+ AstNode *cond = stmt->children.at(0)->clone();
+ cond->replace_variables(variables, fcall);
+ while (cond->simplify(true, false, false, 1, -1, false, true)) { }
+
+ if (cond->type != AST_CONSTANT)
+ log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ if (cond->asBool()) {
+ block->children.insert(block->children.begin(), stmt->children.at(1)->clone());
+ } else {
+ delete block->children.front();
+ block->children.erase(block->children.begin());
+ }
+
+ delete cond;
+ continue;
+ }
+
+ if (stmt->type == AST_REPEAT)
+ {
+ AstNode *num = stmt->children.at(0)->clone();
+ num->replace_variables(variables, fcall);
+ while (num->simplify(true, false, false, 1, -1, false, true)) { }
+
+ if (num->type != AST_CONSTANT)
+ log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ block->children.erase(block->children.begin());
+ for (int i = 0; i < num->bitsAsConst().as_int(); i++)
+ block->children.insert(block->children.begin(), stmt->children.at(1)->clone());
+
+ delete stmt;
+ delete num;
+ continue;
+ }
+
+ if (stmt->type == AST_CASE)
+ {
+ AstNode *expr = stmt->children.at(0)->clone();
+ expr->replace_variables(variables, fcall);
+ while (expr->simplify(true, false, false, 1, -1, false, true)) { }
+
+ AstNode *sel_case = NULL;
+ for (size_t i = 1; i < stmt->children.size(); i++)
+ {
+ bool found_match = false;
+ log_assert(stmt->children.at(i)->type == AST_COND);
+
+ if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) {
+ sel_case = stmt->children.at(i)->children.back();
+ continue;
+ }
+
+ for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++)
+ {
+ AstNode *cond = stmt->children.at(i)->children.at(j)->clone();
+ cond->replace_variables(variables, fcall);
+
+ cond = new AstNode(AST_EQ, expr->clone(), cond);
+ while (cond->simplify(true, false, false, 1, -1, false, true)) { }
+
+ if (cond->type != AST_CONSTANT)
+ log_error("Non-constant expression in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+
+ found_match = cond->asBool();
+ delete cond;
+ }
+
+ if (found_match) {
+ sel_case = stmt->children.at(i)->children.back();
+ break;
+ }
+ }
+
+ block->children.erase(block->children.begin());
+ if (sel_case)
+ block->children.insert(block->children.begin(), sel_case->clone());
+ delete stmt;
+ delete expr;
+ continue;
+ }
+
+ if (stmt->type == AST_BLOCK)
+ {
+ block->children.erase(block->children.begin());
+ block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end());
+ stmt->children.clear();
+ delete stmt;
+ continue;
+ }
+
+ log_error("Unsupported language construct in constant function at %s:%d (called from %s:%d).\n",
+ stmt->filename.c_str(), stmt->linenum, fcall->filename.c_str(), fcall->linenum);
+ log_abort();
+ }
+
+ if (delete_temp_block)
+ delete block;
+
+ for (auto &it : backup_scope)
+ if (it.second == NULL)
+ current_scope.erase(it.first);
+ else
+ current_scope[it.first] = it.second;
+
+ return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed);
+}
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/ilang/Makefile.inc b/frontends/ilang/Makefile.inc
index 07ebf085..e832cfed 100644
--- a/frontends/ilang/Makefile.inc
+++ b/frontends/ilang/Makefile.inc
@@ -4,12 +4,14 @@ GENFILES += frontends/ilang/parser.tab.h
GENFILES += frontends/ilang/parser.output
GENFILES += frontends/ilang/lexer.cc
-frontends/ilang/parser.tab.cc frontends/ilang/parser.tab.h: frontends/ilang/parser.y
- bison -d -r all -b frontends/ilang/parser frontends/ilang/parser.y
- mv frontends/ilang/parser.tab.c frontends/ilang/parser.tab.cc
+frontends/ilang/parser.tab.cc: frontends/ilang/parser.y
+ $(P) bison -d -r all -b frontends/ilang/parser frontends/ilang/parser.y
+ $(Q) mv frontends/ilang/parser.tab.c frontends/ilang/parser.tab.cc
+
+frontends/ilang/parser.tab.h: frontends/ilang/parser.tab.cc
frontends/ilang/lexer.cc: frontends/ilang/lexer.l
- flex -o frontends/ilang/lexer.cc frontends/ilang/lexer.l
+ $(P) flex -o frontends/ilang/lexer.cc frontends/ilang/lexer.l
OBJS += frontends/ilang/parser.tab.o frontends/ilang/lexer.o
OBJS += frontends/ilang/ilang_frontend.o
diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc
index 572a3572..f6f926db 100644
--- a/frontends/ilang/ilang_frontend.cc
+++ b/frontends/ilang/ilang_frontend.cc
@@ -26,6 +26,8 @@
#include "kernel/register.h"
#include "kernel/log.h"
+YOSYS_NAMESPACE_BEGIN
+
void rtlil_frontend_ilang_yyerror(char const *s)
{
log_error("Parser error in line %d: %s\n", rtlil_frontend_ilang_yyget_lineno(), s);
@@ -43,17 +45,20 @@ struct IlangFrontend : public Frontend {
log("representation of a design in yosys's internal format.)\n");
log("\n");
}
- virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
{
log_header("Executing ILANG frontend.\n");
extra_args(f, filename, args, 1);
log("Input filename: %s\n", filename.c_str());
+ ILANG_FRONTEND::lexin = f;
ILANG_FRONTEND::current_design = design;
rtlil_frontend_ilang_yydebug = false;
- rtlil_frontend_ilang_yyrestart(f);
+ rtlil_frontend_ilang_yyrestart(NULL);
rtlil_frontend_ilang_yyparse();
rtlil_frontend_ilang_yylex_destroy();
}
} IlangFrontend;
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/ilang/ilang_frontend.h b/frontends/ilang/ilang_frontend.h
index 5e768c3b..b04d6c51 100644
--- a/frontends/ilang/ilang_frontend.h
+++ b/frontends/ilang/ilang_frontend.h
@@ -25,20 +25,23 @@
#ifndef ILANG_FRONTEND_H
#define ILANG_FRONTEND_H
-#include "kernel/rtlil.h"
-#include <stdio.h>
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
namespace ILANG_FRONTEND {
- void ilang_frontend(FILE *f, RTLIL::Design *design);
+ extern std::istream *lexin;
extern RTLIL::Design *current_design;
}
+YOSYS_NAMESPACE_END
+
extern int rtlil_frontend_ilang_yydebug;
int rtlil_frontend_ilang_yylex(void);
void rtlil_frontend_ilang_yyerror(char const *s);
void rtlil_frontend_ilang_yyrestart(FILE *f);
int rtlil_frontend_ilang_yyparse(void);
-void rtlil_frontend_ilang_yylex_destroy(void);
+int rtlil_frontend_ilang_yylex_destroy(void);
int rtlil_frontend_ilang_yyget_lineno(void);
#endif
diff --git a/frontends/ilang/lexer.l b/frontends/ilang/lexer.l
index 5da8ce67..4109cd4b 100644
--- a/frontends/ilang/lexer.l
+++ b/frontends/ilang/lexer.l
@@ -23,9 +23,18 @@
*/
%{
-#include "kernel/rtlil.h"
+
+#ifdef __clang__
+// bison generates code using the 'register' storage class specifier
+#pragma clang diagnostic ignored "-Wdeprecated-register"
+#endif
+
+#include "ilang_frontend.h"
#include "parser.tab.h"
-void update_autoidx(const char *p);
+
+#define YY_INPUT(buf,result,max_size) \
+ result = ILANG_FRONTEND::lexin->readsome(buf, max_size);
+
%}
%option yylineno
@@ -37,6 +46,7 @@ void update_autoidx(const char *p);
%%
+"autoidx" { return TOK_AUTOIDX; }
"module" { return TOK_MODULE; }
"attribute" { return TOK_ATTRIBUTE; }
"parameter" { return TOK_PARAMETER; }
@@ -44,6 +54,7 @@ void update_autoidx(const char *p);
"wire" { return TOK_WIRE; }
"memory" { return TOK_MEMORY; }
"width" { return TOK_WIDTH; }
+"upto" { return TOK_UPTO; }
"offset" { return TOK_OFFSET; }
"size" { return TOK_SIZE; }
"input" { return TOK_INPUT; }
@@ -69,11 +80,11 @@ void update_autoidx(const char *p);
[a-z]+ { return TOK_INVALID; }
"\\"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
-"$"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); update_autoidx(yytext); return TOK_ID; }
+"$"[^ \t\r\n]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
"."[0-9]+ { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_ID; }
[0-9]+'[01xzm-]* { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; }
-[0-9]+ { rtlil_frontend_ilang_yylval.integer = atoi(yytext); return TOK_INT; }
+-?[0-9]+ { rtlil_frontend_ilang_yylval.integer = atoi(yytext); return TOK_INT; }
\" { BEGIN(STRING); }
<STRING>\\. { yymore(); }
@@ -117,27 +128,6 @@ void update_autoidx(const char *p);
%%
-void update_autoidx(const char *p)
-{
- if (*p != '$')
- return;
-
- while (*p) {
- if (*(p++) != '$')
- continue;
- if ('0' <= *p && *p <= '9') {
- const char *q = p;
- while ('0' <= *q && *q <= '9')
- q++;
- if ((q - p) < 10) {
- int idx = atoi(p);
- if (idx > RTLIL::autoidx)
- RTLIL::autoidx = idx;
- }
- }
- }
-}
-
// this is a hack to avoid the 'yyinput defined but not used' error msgs
void *rtlil_frontend_ilang_avoid_input_warnings() {
return (void*)&yyinput;
diff --git a/frontends/ilang/parser.y b/frontends/ilang/parser.y
index c2e09022..a5cc0689 100644
--- a/frontends/ilang/parser.y
+++ b/frontends/ilang/parser.y
@@ -25,7 +25,9 @@
%{
#include <list>
#include "ilang_frontend.h"
+YOSYS_NAMESPACE_BEGIN
namespace ILANG_FRONTEND {
+ std::istream *lexin;
RTLIL::Design *current_design;
RTLIL::Module *current_module;
RTLIL::Wire *current_wire;
@@ -37,24 +39,26 @@ namespace ILANG_FRONTEND {
std::map<RTLIL::IdString, RTLIL::Const> attrbuf;
}
using namespace ILANG_FRONTEND;
+YOSYS_NAMESPACE_END
+USING_YOSYS_NAMESPACE
%}
-%name-prefix="rtlil_frontend_ilang_yy"
+%name-prefix "rtlil_frontend_ilang_yy"
%union {
char *string;
int integer;
- RTLIL::Const *data;
- RTLIL::SigSpec *sigspec;
+ YOSYS_NAMESPACE_PREFIX RTLIL::Const *data;
+ YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec *sigspec;
}
%token <string> TOK_ID TOK_VALUE TOK_STRING
%token <integer> TOK_INT
-%token TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT
+%token TOK_AUTOIDX TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT
%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC
%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_INIT
%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
-%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED
+%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_UPTO
%type <sigspec> sigspec sigspec_list
%type <integer> sync_type
@@ -82,21 +86,23 @@ optional_eol:
design:
design module |
design attr_stmt |
+ design autoidx_stmt |
/* empty */;
module:
TOK_MODULE TOK_ID EOL {
- if (current_design->modules.count($2) != 0)
- rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of module %s.", $2).c_str());
+ if (current_design->has($2))
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str());
current_module = new RTLIL::Module;
current_module->name = $2;
current_module->attributes = attrbuf;
- current_design->modules[$2] = current_module;
+ current_design->add(current_module);
attrbuf.clear();
free($2);
} module_body TOK_END {
if (attrbuf.size() != 0)
rtlil_frontend_ilang_yyerror("dangling attribute");
+ current_module->fixup_ports();
} EOL;
module_body:
@@ -113,16 +119,20 @@ attr_stmt:
free($2);
};
+autoidx_stmt:
+ TOK_AUTOIDX TOK_INT EOL {
+ autoidx = std::max(autoidx, $2);
+ };
+
wire_stmt:
TOK_WIRE {
- current_wire = new RTLIL::Wire;
+ current_wire = current_module->addWire("$__ilang_frontend_tmp__");
current_wire->attributes = attrbuf;
attrbuf.clear();
} wire_options TOK_ID EOL {
- if (current_module->wires.count($4) != 0)
- rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of wire %s.", $4).c_str());
- current_wire->name = $4;
- current_module->wires[$4] = current_wire;
+ if (current_module->wires_.count($4) != 0)
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of wire %s.", $4).c_str());
+ current_module->rename(current_wire, $4);
free($4);
};
@@ -130,6 +140,9 @@ wire_options:
wire_options TOK_WIDTH TOK_INT {
current_wire->width = $3;
} |
+ wire_options TOK_UPTO {
+ current_wire->upto = true;
+ } |
wire_options TOK_OFFSET TOK_INT {
current_wire->start_offset = $3;
} |
@@ -157,7 +170,7 @@ memory_stmt:
attrbuf.clear();
} memory_options TOK_ID EOL {
if (current_module->memories.count($4) != 0)
- rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of memory %s.", $4).c_str());
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of memory %s.", $4).c_str());
current_memory->name = $4;
current_module->memories[$4] = current_memory;
free($4);
@@ -174,13 +187,10 @@ memory_options:
cell_stmt:
TOK_CELL TOK_ID TOK_ID EOL {
- if (current_module->cells.count($3) != 0)
- rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of cell %s.", $3).c_str());
- current_cell = new RTLIL::Cell;
- current_cell->type = $2;
- current_cell->name = $3;
+ if (current_module->cells_.count($3) != 0)
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell %s.", $3).c_str());
+ current_cell = current_module->addCell($3, $2);
current_cell->attributes = attrbuf;
- current_module->cells[$3] = current_cell;
attrbuf.clear();
free($2);
free($3);
@@ -199,9 +209,9 @@ cell_body:
delete $5;
} |
cell_body TOK_CONNECT TOK_ID sigspec EOL {
- if (current_cell->connections.count($3) != 0)
- rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of cell port %s.", $3).c_str());
- current_cell->connections[$3] = *$4;
+ if (current_cell->hasPort($3))
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell port %s.", $3).c_str());
+ current_cell->setPort($3, *$4);
delete $4;
free($3);
} |
@@ -210,7 +220,7 @@ cell_body:
proc_stmt:
TOK_PROCESS TOK_ID EOL {
if (current_module->processes.count($2) != 0)
- rtlil_frontend_ilang_yyerror(stringf("scope error: redefinition of process %s.", $2).c_str());
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of process %s.", $2).c_str());
current_process = new RTLIL::Process;
current_process->name = $2;
current_process->attributes = attrbuf;
@@ -219,6 +229,7 @@ proc_stmt:
switch_stack.push_back(&current_process->root_case.switches);
case_stack.clear();
case_stack.push_back(&current_process->root_case);
+ attrbuf.clear();
free($2);
} case_body sync_list TOK_END EOL;
@@ -350,50 +361,25 @@ constant:
sigspec:
constant {
- RTLIL::SigChunk chunk;
- chunk.wire = NULL;
- chunk.width = $1->bits.size();
- chunk.offset = 0;
- chunk.data = *$1;
- $$ = new RTLIL::SigSpec;
- $$->chunks.push_back(chunk);
- $$->width = chunk.width;
+ $$ = new RTLIL::SigSpec(*$1);
delete $1;
} |
TOK_ID {
- if (current_module->wires.count($1) == 0)
- rtlil_frontend_ilang_yyerror(stringf("scope error: wire %s not found", $1).c_str());
- RTLIL::SigChunk chunk;
- chunk.wire = current_module->wires[$1];
- chunk.width = current_module->wires[$1]->width;
- chunk.offset = 0;
- $$ = new RTLIL::SigSpec;
- $$->chunks.push_back(chunk);
- $$->width = chunk.width;
+ if (current_module->wires_.count($1) == 0)
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
+ $$ = new RTLIL::SigSpec(current_module->wires_[$1]);
free($1);
} |
TOK_ID '[' TOK_INT ']' {
- if (current_module->wires.count($1) == 0)
- rtlil_frontend_ilang_yyerror(stringf("scope error: wire %s not found", $1).c_str());
- RTLIL::SigChunk chunk;
- chunk.wire = current_module->wires[$1];
- chunk.offset = $3;
- chunk.width = 1;
- $$ = new RTLIL::SigSpec;
- $$->chunks.push_back(chunk);
- $$->width = 1;
+ if (current_module->wires_.count($1) == 0)
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
+ $$ = new RTLIL::SigSpec(current_module->wires_[$1], $3);
free($1);
} |
TOK_ID '[' TOK_INT ':' TOK_INT ']' {
- if (current_module->wires.count($1) == 0)
- rtlil_frontend_ilang_yyerror(stringf("scope error: wire %s not found", $1).c_str());
- RTLIL::SigChunk chunk;
- chunk.wire = current_module->wires[$1];
- chunk.width = $3 - $5 + 1;
- chunk.offset = $5;
- $$ = new RTLIL::SigSpec;
- $$->chunks.push_back(chunk);
- $$->width = chunk.width;
+ if (current_module->wires_.count($1) == 0)
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
+ $$ = new RTLIL::SigSpec(current_module->wires_[$1], $5, $3 - $5 + 1);
free($1);
} |
'{' sigspec_list '}' {
@@ -403,14 +389,8 @@ sigspec:
sigspec_list:
sigspec_list sigspec {
$$ = new RTLIL::SigSpec;
- for (auto it = $2->chunks.begin(); it != $2->chunks.end(); it++) {
- $$->chunks.push_back(*it);
- $$->width += it->width;
- }
- for (auto it = $1->chunks.begin(); it != $1->chunks.end(); it++) {
- $$->chunks.push_back(*it);
- $$->width += it->width;
- }
+ $$->append(*$2);
+ $$->append(*$1);
delete $1;
delete $2;
} |
@@ -422,7 +402,7 @@ conn_stmt:
TOK_CONNECT sigspec sigspec EOL {
if (attrbuf.size() != 0)
rtlil_frontend_ilang_yyerror("dangling attribute");
- current_module->connections.push_back(RTLIL::SigSig(*$2, *$3));
+ current_module->connect(*$2, *$3);
delete $2;
delete $3;
};
diff --git a/frontends/liberty/Makefile.inc b/frontends/liberty/Makefile.inc
new file mode 100644
index 00000000..a02ef5e4
--- /dev/null
+++ b/frontends/liberty/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += frontends/liberty/liberty.o
+
diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc
new file mode 100644
index 00000000..a9ab022a
--- /dev/null
+++ b/frontends/liberty/liberty.cc
@@ -0,0 +1,578 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "passes/techmap/libparse.h"
+#include "kernel/register.h"
+#include "kernel/log.h"
+
+YOSYS_NAMESPACE_BEGIN
+using namespace PASS_DFFLIBMAP;
+
+struct token_t {
+ char type;
+ RTLIL::SigSpec sig;
+ token_t (char t) : type(t) { }
+ token_t (char t, RTLIL::SigSpec s) : type(t), sig(s) { }
+};
+
+static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&expr)
+{
+ log_assert(*expr != 0);
+
+ int id_len = 0;
+ while (('a' <= expr[id_len] && expr[id_len] <= 'z') || ('A' <= expr[id_len] && expr[id_len] <= 'Z') ||
+ ('0' <= expr[id_len] && expr[id_len] <= '9') || expr[id_len] == '.' || expr[id_len] == '_') id_len++;
+
+ if (id_len == 0)
+ log_error("Expected identifier at `%s'.\n", expr);
+
+ if (id_len == 1 && (*expr == '0' || *expr == '1'))
+ return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1;
+
+ std::string id = RTLIL::escape_id(std::string(expr, id_len));
+ if (!module->wires_.count(id))
+ log_error("Can't resolve wire name %s.\n", RTLIL::unescape_id(id).c_str());
+
+ expr += id_len;
+ return module->wires_.at(id);
+}
+
+static RTLIL::SigSpec create_inv_cell(RTLIL::Module *module, RTLIL::SigSpec A)
+{
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_");
+ cell->setPort("\\A", A);
+ cell->setPort("\\Y", module->addWire(NEW_ID));
+ return cell->getPort("\\Y");
+}
+
+static RTLIL::SigSpec create_xor_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
+{
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_XOR_");
+ cell->setPort("\\A", A);
+ cell->setPort("\\B", B);
+ cell->setPort("\\Y", module->addWire(NEW_ID));
+ return cell->getPort("\\Y");
+}
+
+static RTLIL::SigSpec create_and_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
+{
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_AND_");
+ cell->setPort("\\A", A);
+ cell->setPort("\\B", B);
+ cell->setPort("\\Y", module->addWire(NEW_ID));
+ return cell->getPort("\\Y");
+}
+
+static RTLIL::SigSpec create_or_cell(RTLIL::Module *module, RTLIL::SigSpec A, RTLIL::SigSpec B)
+{
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_OR_");
+ cell->setPort("\\A", A);
+ cell->setPort("\\B", B);
+ cell->setPort("\\Y", module->addWire(NEW_ID));
+ return cell->getPort("\\Y");
+}
+
+static bool parse_func_reduce(RTLIL::Module *module, std::vector<token_t> &stack, token_t next_token)
+{
+ int top = int(stack.size())-1;
+
+ if (0 <= top-1 && stack[top].type == 0 && stack[top-1].type == '!') {
+ token_t t = token_t(0, create_inv_cell(module, stack[top].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top-1 && stack[top].type == '\'' && stack[top-1].type == 0) {
+ token_t t = token_t(0, create_inv_cell(module, stack[top-1].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top && stack[top].type == 0) {
+ if (next_token.type == '\'')
+ return false;
+ stack[top].type = 1;
+ return true;
+ }
+
+ if (0 <= top-2 && stack[top-2].type == 1 && stack[top-1].type == '^' && stack[top].type == 1) {
+ token_t t = token_t(1, create_xor_cell(module, stack[top-2].sig, stack[top].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top && stack[top].type == 1) {
+ if (next_token.type == '^')
+ return false;
+ stack[top].type = 2;
+ return true;
+ }
+
+ if (0 <= top-1 && stack[top-1].type == 2 && stack[top].type == 2) {
+ token_t t = token_t(2, create_and_cell(module, stack[top-1].sig, stack[top].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top-2 && stack[top-2].type == 2 && (stack[top-1].type == '*' || stack[top-1].type == '&') && stack[top].type == 2) {
+ token_t t = token_t(2, create_and_cell(module, stack[top-2].sig, stack[top].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top && stack[top].type == 2) {
+ if (next_token.type == '*' || next_token.type == '&' || next_token.type == 0 || next_token.type == '(')
+ return false;
+ stack[top].type = 3;
+ return true;
+ }
+
+ if (0 <= top-2 && stack[top-2].type == 3 && (stack[top-1].type == '+' || stack[top-1].type == '|') && stack[top].type == 3) {
+ token_t t = token_t(3, create_or_cell(module, stack[top-2].sig, stack[top].sig));
+ stack.pop_back();
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ if (0 <= top-2 && stack[top-2].type == '(' && stack[top-1].type == 3 && stack[top].type == ')') {
+ token_t t = token_t(0, stack[top-1].sig);
+ stack.pop_back();
+ stack.pop_back();
+ stack.pop_back();
+ stack.push_back(t);
+ return true;
+ }
+
+ return false;
+}
+
+static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr)
+{
+ const char *orig_expr = expr;
+ std::vector<token_t> stack;
+
+ while (*expr)
+ {
+ if (*expr == ' ' || *expr == '\t' || *expr == '\r' || *expr == '\n' || *expr == '"') {
+ expr++;
+ continue;
+ }
+
+ token_t next_token(0);
+ if (*expr == '(' || *expr == ')' || *expr == '\'' || *expr == '!' || *expr == '^' || *expr == '*' || *expr == '+' || *expr == '|')
+ next_token = token_t(*(expr++));
+ else
+ next_token = token_t(0, parse_func_identifier(module, expr));
+
+ while (parse_func_reduce(module, stack, next_token)) {}
+ stack.push_back(next_token);
+ }
+
+ while (parse_func_reduce(module, stack, token_t('.'))) {}
+
+#if 0
+ for (size_t i = 0; i < stack.size(); i++)
+ if (stack[i].type < 16)
+ log("%3d: %d %s\n", int(i), stack[i].type, log_signal(stack[i].sig));
+ else
+ log("%3d: %c\n", int(i), stack[i].type);
+#endif
+
+ if (stack.size() != 1 || stack.back().type != 3)
+ log_error("Parser error in function expr `%s'.\n", orig_expr);
+
+ return stack.back().sig;
+}
+
+static void create_ff(RTLIL::Module *module, LibertyAst *node)
+{
+ RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
+ RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
+
+ RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig;
+ bool clk_polarity = true, clear_polarity = true, preset_polarity = true;
+
+ for (auto child : node->children) {
+ if (child->id == "clocked_on")
+ clk_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "next_state")
+ data_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "clear")
+ clear_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "preset")
+ preset_sig = parse_func_expr(module, child->value.c_str());
+ }
+
+ if (clk_sig.size() == 0 || data_sig.size() == 0)
+ log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", log_id(module->name));
+
+ for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
+ {
+ rerun_invert_rollback = false;
+
+ for (auto &it : module->cells_) {
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clk_sig) {
+ clk_sig = it.second->getPort("\\A");
+ clk_polarity = !clk_polarity;
+ rerun_invert_rollback = true;
+ }
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clear_sig) {
+ clear_sig = it.second->getPort("\\A");
+ clear_polarity = !clear_polarity;
+ rerun_invert_rollback = true;
+ }
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == preset_sig) {
+ preset_sig = it.second->getPort("\\A");
+ preset_polarity = !preset_polarity;
+ rerun_invert_rollback = true;
+ }
+ }
+ }
+
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_");
+ cell->setPort("\\A", iq_sig);
+ cell->setPort("\\Y", iqn_sig);
+
+ cell = module->addCell(NEW_ID, "");
+ cell->setPort("\\D", data_sig);
+ cell->setPort("\\Q", iq_sig);
+ cell->setPort("\\C", clk_sig);
+
+ if (clear_sig.size() == 0 && preset_sig.size() == 0) {
+ cell->type = stringf("$_DFF_%c_", clk_polarity ? 'P' : 'N');
+ }
+
+ if (clear_sig.size() == 1 && preset_sig.size() == 0) {
+ cell->type = stringf("$_DFF_%c%c0_", clk_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
+ cell->setPort("\\R", clear_sig);
+ }
+
+ if (clear_sig.size() == 0 && preset_sig.size() == 1) {
+ cell->type = stringf("$_DFF_%c%c1_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N');
+ cell->setPort("\\R", preset_sig);
+ }
+
+ if (clear_sig.size() == 1 && preset_sig.size() == 1) {
+ cell->type = stringf("$_DFFSR_%c%c%c_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N');
+ cell->setPort("\\S", preset_sig);
+ cell->setPort("\\R", clear_sig);
+ }
+
+ log_assert(!cell->type.empty());
+}
+
+static void create_latch(RTLIL::Module *module, LibertyAst *node)
+{
+ RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0))));
+ RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1))));
+
+ RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig;
+ bool enable_polarity = true, clear_polarity = true, preset_polarity = true;
+
+ for (auto child : node->children) {
+ if (child->id == "enable")
+ enable_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "data_in")
+ data_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "clear")
+ clear_sig = parse_func_expr(module, child->value.c_str());
+ if (child->id == "preset")
+ preset_sig = parse_func_expr(module, child->value.c_str());
+ }
+
+ if (enable_sig.size() == 0 || data_sig.size() == 0)
+ log_error("Latch cell %s has no data_in and/or enable attribute.\n", log_id(module->name));
+
+ for (bool rerun_invert_rollback = true; rerun_invert_rollback;)
+ {
+ rerun_invert_rollback = false;
+
+ for (auto &it : module->cells_) {
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == enable_sig) {
+ enable_sig = it.second->getPort("\\A");
+ enable_polarity = !enable_polarity;
+ rerun_invert_rollback = true;
+ }
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == clear_sig) {
+ clear_sig = it.second->getPort("\\A");
+ clear_polarity = !clear_polarity;
+ rerun_invert_rollback = true;
+ }
+ if (it.second->type == "$_NOT_" && it.second->getPort("\\Y") == preset_sig) {
+ preset_sig = it.second->getPort("\\A");
+ preset_polarity = !preset_polarity;
+ rerun_invert_rollback = true;
+ }
+ }
+ }
+
+ RTLIL::Cell *cell = module->addCell(NEW_ID, "$_NOT_");
+ cell->setPort("\\A", iq_sig);
+ cell->setPort("\\Y", iqn_sig);
+
+ if (clear_sig.size() == 1)
+ {
+ RTLIL::SigSpec clear_negative = clear_sig;
+ RTLIL::SigSpec clear_enable = clear_sig;
+
+ if (clear_polarity == true || clear_polarity != enable_polarity)
+ {
+ RTLIL::Cell *inv = module->addCell(NEW_ID, "$_NOT_");
+ inv->setPort("\\A", clear_sig);
+ inv->setPort("\\Y", module->addWire(NEW_ID));
+
+ if (clear_polarity == true)
+ clear_negative = inv->getPort("\\Y");
+ if (clear_polarity != enable_polarity)
+ clear_enable = inv->getPort("\\Y");
+ }
+
+ RTLIL::Cell *data_gate = module->addCell(NEW_ID, "$_AND_");
+ data_gate->setPort("\\A", data_sig);
+ data_gate->setPort("\\B", clear_negative);
+ data_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
+
+ RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? "$_OR_" : "$_AND_");
+ enable_gate->setPort("\\A", enable_sig);
+ enable_gate->setPort("\\B", clear_enable);
+ enable_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
+ }
+
+ if (preset_sig.size() == 1)
+ {
+ RTLIL::SigSpec preset_positive = preset_sig;
+ RTLIL::SigSpec preset_enable = preset_sig;
+
+ if (preset_polarity == false || preset_polarity != enable_polarity)
+ {
+ RTLIL::Cell *inv = module->addCell(NEW_ID, "$_NOT_");
+ inv->setPort("\\A", preset_sig);
+ inv->setPort("\\Y", module->addWire(NEW_ID));
+
+ if (preset_polarity == false)
+ preset_positive = inv->getPort("\\Y");
+ if (preset_polarity != enable_polarity)
+ preset_enable = inv->getPort("\\Y");
+ }
+
+ RTLIL::Cell *data_gate = module->addCell(NEW_ID, "$_OR_");
+ data_gate->setPort("\\A", data_sig);
+ data_gate->setPort("\\B", preset_positive);
+ data_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
+
+ RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? "$_OR_" : "$_AND_");
+ enable_gate->setPort("\\A", enable_sig);
+ enable_gate->setPort("\\B", preset_enable);
+ enable_gate->setPort("\\Y", data_sig = module->addWire(NEW_ID));
+ }
+
+ cell = module->addCell(NEW_ID, stringf("$_DLATCH_%c_", enable_polarity ? 'P' : 'N'));
+ cell->setPort("\\D", data_sig);
+ cell->setPort("\\Q", iq_sig);
+ cell->setPort("\\E", enable_sig);
+}
+
+struct LibertyFrontend : public Frontend {
+ LibertyFrontend() : Frontend("liberty", "read cells from liberty file") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" read_liberty [filename]\n");
+ log("\n");
+ log("Read cells from liberty file as modules into current design.\n");
+ log("\n");
+ log(" -lib\n");
+ log(" only create empty blackbox modules\n");
+ log("\n");
+ log(" -ignore_redef\n");
+ log(" ignore re-definitions of modules. (the default behavior is to\n");
+ log(" create an error message.)\n");
+ log("\n");
+ log(" -ignore_miss_func\n");
+ log(" ignore cells with missing function specification of outputs\n");
+ log("\n");
+ log(" -ignore_miss_dir\n");
+ log(" ignore cells with a missing or invalid direction\n");
+ log(" specification on a pin\n");
+ log("\n");
+ log(" -setattr <attribute_name>\n");
+ log(" set the specified attribute (to the value 1) on all loaded modules\n");
+ log("\n");
+ }
+ virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ {
+ bool flag_lib = false;
+ bool flag_ignore_redef = false;
+ bool flag_ignore_miss_func = false;
+ bool flag_ignore_miss_dir = false;
+ std::vector<std::string> attributes;
+
+ log_header("Executing Liberty frontend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-lib") {
+ flag_lib = true;
+ continue;
+ }
+ if (arg == "-ignore_redef") {
+ flag_ignore_redef = true;
+ continue;
+ }
+ if (arg == "-ignore_miss_func") {
+ flag_ignore_miss_func = true;
+ continue;
+ }
+ if (arg == "-ignore_miss_dir") {
+ flag_ignore_miss_dir = true;
+ continue;
+ }
+ if (arg == "-setattr" && argidx+1 < args.size()) {
+ attributes.push_back(RTLIL::escape_id(args[++argidx]));
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ LibertyParser parser(*f);
+ int cell_count = 0;
+
+ for (auto cell : parser.ast->children)
+ {
+ if (cell->id != "cell" || cell->args.size() != 1)
+ continue;
+
+ std::string cell_name = RTLIL::escape_id(cell->args.at(0));
+
+ if (design->has(cell_name)) {
+ if (flag_ignore_redef)
+ continue;
+ log_error("Duplicate definition of cell/module %s.\n", RTLIL::unescape_id(cell_name).c_str());
+ }
+
+ // log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name).c_str());
+
+ RTLIL::Module *module = new RTLIL::Module;
+ module->name = cell_name;
+
+ if (flag_lib)
+ module->set_bool_attribute("\\blackbox");
+
+ for (auto &attr : attributes)
+ module->attributes[attr] = 1;
+
+ for (auto node : cell->children)
+ if (node->id == "pin" && node->args.size() == 1) {
+ LibertyAst *dir = node->find("direction");
+ if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal"))
+ {
+ if (!flag_ignore_miss_dir)
+ {
+ log_error("Missing or invalid direction for pin %s of cell %s.\n", node->args.at(0).c_str(), log_id(module->name));
+ } else {
+ log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0).c_str());
+ delete module;
+ goto skip_cell;
+ }
+ }
+ if (!flag_lib || dir->value != "internal")
+ module->addWire(RTLIL::escape_id(node->args.at(0)));
+ }
+
+ for (auto node : cell->children)
+ {
+ if (!flag_lib) {
+ if (node->id == "ff" && node->args.size() == 2)
+ create_ff(module, node);
+ if (node->id == "latch" && node->args.size() == 2)
+ create_latch(module, node);
+ }
+
+ if (node->id == "pin" && node->args.size() == 1)
+ {
+ LibertyAst *dir = node->find("direction");
+
+ if (flag_lib && dir->value == "internal")
+ continue;
+
+ RTLIL::Wire *wire = module->wires_.at(RTLIL::escape_id(node->args.at(0)));
+
+ if (dir && dir->value == "inout") {
+ wire->port_input = true;
+ wire->port_output = true;
+ }
+
+ if (dir && dir->value == "input") {
+ wire->port_input = true;
+ continue;
+ }
+
+ if (dir && dir->value == "output")
+ wire->port_output = true;
+
+ if (flag_lib)
+ continue;
+
+ LibertyAst *func = node->find("function");
+ if (func == NULL)
+ {
+ if (!flag_ignore_miss_func)
+ {
+ log_error("Missing function on output %s of cell %s.\n", log_id(wire->name), log_id(module->name));
+ } else {
+ log("Ignoring cell %s with missing function on output %s.\n", log_id(module->name), log_id(wire->name));
+ delete module;
+ goto skip_cell;
+ }
+ }
+
+ RTLIL::SigSpec out_sig = parse_func_expr(module, func->value.c_str());
+ module->connect(RTLIL::SigSig(wire, out_sig));
+ }
+ }
+
+ module->fixup_ports();
+ design->add(module);
+ cell_count++;
+skip_cell:;
+ }
+
+ log("Imported %d cell types from liberty file.\n", cell_count);
+ }
+} LibertyFrontend;
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc
new file mode 100644
index 00000000..13f242c4
--- /dev/null
+++ b/frontends/verific/Makefile.inc
@@ -0,0 +1,16 @@
+
+OBJS += frontends/verific/verific.o
+
+ifeq ($(ENABLE_VERIFIC),1)
+
+EXTRA_TARGETS += share/verific
+
+share/verific:
+ $(P) rm -rf share/verific.new
+ $(Q) mkdir -p share/verific.new
+ $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs share/verific.new/vhdl_vdbs_1993
+ $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008 share/verific.new/vhdl_vdbs_2008
+ $(Q) mv share/verific.new share/verific
+
+endif
+
diff --git a/frontends/verific/build_amd64.txt b/frontends/verific/build_amd64.txt
new file mode 100644
index 00000000..94615d38
--- /dev/null
+++ b/frontends/verific/build_amd64.txt
@@ -0,0 +1,31 @@
+
+Notes on building yosys with verific support on amd64 when you
+only have the i386 eval version of Verific:
+
+
+1.) Use a Makefile.conf like the following one:
+
+--snip--
+CONFIG := clang
+ENABLE_TCL := 0
+ENABLE_QT4 := 0
+ENABLE_ABC := 0
+ENABLE_VERIFIC := 1
+CXXFLAGS += -m32
+LDFLAGS += -m32
+VERIFIC_DIR = /usr/local/src/verific_lib_eval
+--snap--
+
+
+2.) Install the necessary multilib packages
+
+Hint: On debian/ubuntu the multilib packages have names such as
+libreadline-dev:amd64 or lib32readline6-dev, depending on the
+exact version of debian/ubuntu you are working with.
+
+
+3.) Build and test
+
+make -j8
+./yosys frontends/verific/test_navre.ys
+
diff --git a/frontends/verific/test_navre.ys b/frontends/verific/test_navre.ys
new file mode 100644
index 00000000..a56b725a
--- /dev/null
+++ b/frontends/verific/test_navre.ys
@@ -0,0 +1,18 @@
+verific -vlog2k ../yosys-bigsim/softusb_navre/rtl/softusb_navre.v
+verific -import softusb_navre
+
+memory softusb_navre
+flatten softusb_navre
+rename softusb_navre gate
+
+read_verilog ../yosys-bigsim/softusb_navre/rtl/softusb_navre.v
+cd softusb_navre; proc; opt; memory; opt; cd ..
+rename softusb_navre gold
+
+expose -dff -shared gold gate
+miter -equiv -ignore_gold_x -make_assert -make_outputs -make_outcmp gold gate miter
+
+cd miter
+flatten; opt -undriven
+sat -verify -maxsteps 5 -set-init-undef -set-def-inputs -prove-asserts -tempinduct-def \
+ -seq 1 -set-at 1 in_rst 1 # -show-inputs -show-outputs
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
new file mode 100644
index 00000000..d0f14838
--- /dev/null
+++ b/frontends/verific/verific.cc
@@ -0,0 +1,949 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+
+USING_YOSYS_NAMESPACE
+
+#ifdef YOSYS_ENABLE_VERIFIC
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Woverloaded-virtual"
+
+#include "veri_file.h"
+#include "vhdl_file.h"
+#include "VeriModule.h"
+#include "VhdlUnits.h"
+#include "DataBase.h"
+#include "Message.h"
+
+#pragma clang diagnostic pop
+
+#ifdef VERIFIC_NAMESPACE
+using namespace Verific ;
+#endif
+
+static void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefile, const char *msg, va_list args)
+{
+ log("VERIFIC-%s [%s] ",
+ msg_type == VERIFIC_NONE ? "NONE" :
+ msg_type == VERIFIC_ERROR ? "ERROR" :
+ msg_type == VERIFIC_WARNING ? "WARNING" :
+ msg_type == VERIFIC_IGNORE ? "IGNORE" :
+ msg_type == VERIFIC_INFO ? "INFO" :
+ msg_type == VERIFIC_COMMENT ? "COMMENT" :
+ msg_type == VERIFIC_PROGRAM_ERROR ? "PROGRAM_ERROR" : "UNKNOWN", message_id);
+ if (linefile)
+ log("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile));
+ logv(msg, args);
+ log("\n");
+}
+
+static void import_attributes(std::map<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj)
+{
+ MapIter mi;
+ Att *attr;
+
+ if (obj->Linefile())
+ attributes["\\src"] = stringf("%s:%d", LineFile::GetFileName(obj->Linefile()), LineFile::GetLineNo(obj->Linefile()));
+
+ // FIXME: Parse numeric attributes
+ FOREACH_ATTRIBUTE(obj, mi, attr)
+ attributes[RTLIL::escape_id(attr->Key())] = RTLIL::Const(std::string(attr->Value()));
+}
+
+static RTLIL::SigSpec operatorInput(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map)
+{
+ RTLIL::SigSpec sig;
+ for (int i = int(inst->InputSize())-1; i >= 0; i--)
+ if (inst->GetInputBit(i))
+ sig.append(net_map.at(inst->GetInputBit(i)));
+ else
+ sig.append(RTLIL::State::Sz);
+ return sig;
+}
+
+static RTLIL::SigSpec operatorInput1(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map)
+{
+ RTLIL::SigSpec sig;
+ for (int i = int(inst->Input1Size())-1; i >= 0; i--)
+ if (inst->GetInput1Bit(i))
+ sig.append(net_map.at(inst->GetInput1Bit(i)));
+ else
+ sig.append(RTLIL::State::Sz);
+ return sig;
+}
+
+static RTLIL::SigSpec operatorInput2(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map)
+{
+ RTLIL::SigSpec sig;
+ for (int i = int(inst->Input2Size())-1; i >= 0; i--)
+ if (inst->GetInput2Bit(i))
+ sig.append(net_map.at(inst->GetInput2Bit(i)));
+ else
+ sig.append(RTLIL::State::Sz);
+ return sig;
+}
+
+static RTLIL::SigSpec operatorInport(Instance *inst, const char *portname, std::map<Net*, RTLIL::SigBit> &net_map)
+{
+ PortBus *portbus = inst->View()->GetPortBus(portname);
+ if (portbus) {
+ RTLIL::SigSpec sig;
+ for (unsigned i = 0; i < portbus->Size(); i++) {
+ Net *net = inst->GetNet(portbus->ElementAtIndex(i));
+ if (net) {
+ if (net->IsGnd())
+ sig.append(RTLIL::State::S0);
+ else if (net->IsPwr())
+ sig.append(RTLIL::State::S1);
+ else
+ sig.append(net_map.at(net));
+ } else
+ sig.append(RTLIL::State::Sz);
+ }
+ return sig;
+ } else {
+ Port *port = inst->View()->GetPort(portname);
+ log_assert(port != NULL);
+ Net *net = inst->GetNet(port);
+ return net_map.at(net);
+ }
+}
+
+static RTLIL::SigSpec operatorOutput(Instance *inst, std::map<Net*, RTLIL::SigBit> &net_map, RTLIL::Module *module)
+{
+ RTLIL::SigSpec sig;
+ RTLIL::Wire *dummy_wire = NULL;
+ for (int i = int(inst->OutputSize())-1; i >= 0; i--)
+ if (inst->GetOutputBit(i)) {
+ sig.append(net_map.at(inst->GetOutputBit(i)));
+ dummy_wire = NULL;
+ } else {
+ if (dummy_wire == NULL)
+ dummy_wire = module->addWire(NEW_ID);
+ else
+ dummy_wire->width++;
+ sig.append(RTLIL::SigSpec(dummy_wire, dummy_wire->width - 1));
+ }
+ return sig;
+}
+
+static bool import_netlist_instance_gates(RTLIL::Module *module, std::map<Net*, RTLIL::SigBit> &net_map, Instance *inst)
+{
+ if (inst->Type() == PRIM_AND) {
+ module->addAndGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_NAND) {
+ RTLIL::SigSpec tmp = module->addWire(NEW_ID);
+ module->addAndGate(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), tmp);
+ module->addNotGate(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_OR) {
+ module->addOrGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_NOR) {
+ RTLIL::SigSpec tmp = module->addWire(NEW_ID);
+ module->addOrGate(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), tmp);
+ module->addNotGate(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_XOR) {
+ module->addXorGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_INV) {
+ module->addNotGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_MUX) {
+ module->addMuxGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_TRI) {
+ module->addMuxGate(RTLIL::escape_id(inst->Name()), RTLIL::State::Sz, net_map.at(inst->GetInput()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_FADD)
+ {
+ RTLIL::SigSpec a = net_map.at(inst->GetInput1()), b = net_map.at(inst->GetInput2()), c = net_map.at(inst->GetCin());
+ RTLIL::SigSpec x = inst->GetCout() ? net_map.at(inst->GetCout()) : module->addWire(NEW_ID);
+ RTLIL::SigSpec y = inst->GetOutput() ? net_map.at(inst->GetOutput()) : module->addWire(NEW_ID);
+ RTLIL::SigSpec tmp1 = module->addWire(NEW_ID);
+ RTLIL::SigSpec tmp2 = module->addWire(NEW_ID);
+ RTLIL::SigSpec tmp3 = module->addWire(NEW_ID);
+ module->addXorGate(NEW_ID, a, b, tmp1);
+ module->addXorGate(RTLIL::escape_id(inst->Name()), tmp1, c, y);
+ module->addAndGate(NEW_ID, tmp1, c, tmp2);
+ module->addAndGate(NEW_ID, a, b, tmp3);
+ module->addOrGate(NEW_ID, tmp2, tmp3, x);
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DFFRS)
+ {
+ if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
+ module->addDffGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ else if (inst->GetSet()->IsGnd())
+ module->addAdffGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetReset()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), false);
+ else if (inst->GetReset()->IsGnd())
+ module->addAdffGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), true);
+ else
+ module->addDffsrGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()), net_map.at(inst->GetReset()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ return false;
+}
+
+static bool import_netlist_instance_cells(RTLIL::Module *module, std::map<Net*, RTLIL::SigBit> &net_map, Instance *inst)
+{
+ if (inst->Type() == PRIM_AND) {
+ module->addAnd(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_NAND) {
+ RTLIL::SigSpec tmp = module->addWire(NEW_ID);
+ module->addAnd(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), tmp);
+ module->addNot(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_OR) {
+ module->addOr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_NOR) {
+ RTLIL::SigSpec tmp = module->addWire(NEW_ID);
+ module->addOr(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), tmp);
+ module->addNot(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_XOR) {
+ module->addXor(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_XNOR) {
+ module->addXnor(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_INV) {
+ module->addNot(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_MUX) {
+ module->addMux(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_TRI) {
+ module->addMux(RTLIL::escape_id(inst->Name()), RTLIL::State::Sz, net_map.at(inst->GetInput()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ if (inst->Type() == PRIM_FADD)
+ {
+ RTLIL::SigSpec a_plus_b = module->addWire(NEW_ID, 2);
+ RTLIL::SigSpec y = inst->GetOutput() ? net_map.at(inst->GetOutput()) : module->addWire(NEW_ID);
+ if (inst->GetCout())
+ y.append(net_map.at(inst->GetCout()));
+ module->addAdd(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), a_plus_b);
+ module->addAdd(RTLIL::escape_id(inst->Name()), a_plus_b, net_map.at(inst->GetCin()), y);
+ return true;
+ }
+
+ if (inst->Type() == PRIM_DFFRS)
+ {
+ if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
+ module->addDff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ else if (inst->GetSet()->IsGnd())
+ module->addAdff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetReset()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), RTLIL::State::S0);
+ else if (inst->GetReset()->IsGnd())
+ module->addAdff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()), RTLIL::State::S1);
+ else
+ module->addDffsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), net_map.at(inst->GetSet()), net_map.at(inst->GetReset()),
+ net_map.at(inst->GetInput()), net_map.at(inst->GetOutput()));
+ return true;
+ }
+
+ #define IN operatorInput(inst, net_map)
+ #define IN1 operatorInput1(inst, net_map)
+ #define IN2 operatorInput2(inst, net_map)
+ #define OUT operatorOutput(inst, net_map, module)
+ #define SIGNED inst->View()->IsSigned()
+
+ if (inst->Type() == OPER_ADDER) {
+ RTLIL::SigSpec out = OUT;
+ if (inst->GetCout() != NULL)
+ out.append(net_map.at(inst->GetCout()));
+ if (inst->GetCin()->IsGnd()) {
+ module->addAdd(RTLIL::escape_id(inst->Name()), IN1, IN2, out, SIGNED);
+ } else {
+ RTLIL::SigSpec tmp = module->addWire(NEW_ID, SIZE(out));
+ module->addAdd(NEW_ID, IN1, IN2, tmp, SIGNED);
+ module->addAdd(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetCin()), out, false);
+ }
+ return true;
+ }
+
+ if (inst->Type() == OPER_MULTIPLIER) {
+ module->addMul(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_DIVIDER) {
+ module->addDiv(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_MODULO) {
+ module->addMod(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_REMAINDER) {
+ module->addMod(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_SHIFT_LEFT) {
+ module->addShl(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, false);
+ return true;
+ }
+
+ if (inst->Type() == OPER_SHIFT_RIGHT) {
+ Net *net_cin = inst->GetCin();
+ Net *net_a_msb = inst->GetInput1Bit(0);
+ if (net_cin->IsGnd())
+ module->addShr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, false);
+ else if (net_cin == net_a_msb)
+ module->addSshr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, true);
+ else
+ log_error("Can't import Verific OPER_SHIFT_RIGHT instance %s: carry_in is neither 0 nor msb of left input\n", inst->Name());
+ return true;
+ }
+
+ if (inst->Type() == OPER_REDUCE_AND) {
+ module->addReduceAnd(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_REDUCE_OR) {
+ module->addReduceOr(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_REDUCE_XOR) {
+ module->addReduceXor(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_REDUCE_XNOR) {
+ module->addReduceXnor(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_LESSTHAN) {
+ Net *net_cin = inst->GetCin();
+ if (net_cin->IsGnd())
+ module->addLt(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ else if (net_cin->IsPwr())
+ module->addLe(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ else
+ log_error("Can't import Verific OPER_LESSTHAN instance %s: carry_in is neither 0 nor 1\n", inst->Name());
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_AND) {
+ module->addAnd(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_OR) {
+ module->addOr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_XOR) {
+ module->addXor(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_XNOR) {
+ module->addXnor(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_BUF) {
+ module->addPos(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_INV) {
+ module->addNot(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_MINUS) {
+ module->addSub(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_UMINUS) {
+ module->addNeg(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_EQUAL) {
+ module->addEq(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_NEQUAL) {
+ module->addNe(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_MUX) {
+ module->addMux(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetControl()), OUT);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_TRI) {
+ module->addMux(RTLIL::escape_id(inst->Name()), RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map.at(inst->GetControl()), OUT);
+ return true;
+ }
+
+ if (inst->Type() == OPER_WIDE_DFFRS) {
+ RTLIL::SigSpec sig_set = operatorInport(inst, "set", net_map);
+ RTLIL::SigSpec sig_reset = operatorInport(inst, "reset", net_map);
+ if (sig_set.is_fully_const() && !sig_set.as_bool() && sig_reset.is_fully_const() && !sig_reset.as_bool())
+ module->addDff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), IN, OUT);
+ else
+ module->addDffsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), sig_set, sig_reset, IN, OUT);
+ return true;
+ }
+
+ #undef IN
+ #undef IN1
+ #undef IN2
+ #undef OUT
+ #undef SIGNED
+
+ return false;
+}
+
+static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo, bool mode_gates)
+{
+ std::string module_name = nl->IsOperator() ? std::string("$verific$") + nl->Owner()->Name() : RTLIL::escape_id(nl->Owner()->Name());
+
+ if (design->has(module_name)) {
+ if (!nl->IsOperator())
+ log_cmd_error("Re-definition of module `%s'.\n", nl->Owner()->Name());
+ return;
+ }
+
+ RTLIL::Module *module = new RTLIL::Module;
+ module->name = module_name;
+ design->add(module);
+
+ log("Importing module %s.\n", RTLIL::id2cstr(module->name));
+
+ std::map<Net*, RTLIL::SigBit> net_map;
+
+ SetIter si;
+ MapIter mi, mi2;
+ Port *port;
+ PortBus *portbus;
+ Net *net;
+ NetBus *netbus;
+ Instance *inst;
+ PortRef *pr;
+
+ FOREACH_PORT_OF_NETLIST(nl, mi, port)
+ {
+ if (port->Bus())
+ continue;
+
+ // log(" importing port %s.\n", port->Name());
+
+ RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(port->Name()));
+ import_attributes(wire->attributes, port);
+
+ wire->port_id = nl->IndexOf(port) + 1;
+
+ if (port->GetDir() == DIR_INOUT || port->GetDir() == DIR_IN)
+ wire->port_input = true;
+ if (port->GetDir() == DIR_INOUT || port->GetDir() == DIR_OUT)
+ wire->port_output = true;
+
+ if (port->GetNet()) {
+ net = port->GetNet();
+ if (net_map.count(net) == 0)
+ net_map[net] = wire;
+ else if (wire->port_input)
+ module->connect(net_map.at(net), wire);
+ else
+ module->connect(wire, net_map.at(net));
+ }
+ }
+
+ FOREACH_PORTBUS_OF_NETLIST(nl, mi, portbus)
+ {
+ // log(" importing portbus %s.\n", portbus->Name());
+
+ RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(portbus->Name()), portbus->Size());
+ wire->start_offset = std::min(portbus->LeftIndex(), portbus->RightIndex());
+ import_attributes(wire->attributes, portbus);
+
+ if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN)
+ wire->port_input = true;
+ if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_OUT)
+ wire->port_output = true;
+
+ for (int i = portbus->LeftIndex();; i += portbus->IsUp() ? +1 : -1) {
+ if (portbus->ElementAtIndex(i) && portbus->ElementAtIndex(i)->GetNet()) {
+ net = portbus->ElementAtIndex(i)->GetNet();
+ RTLIL::SigBit bit(wire, i - wire->start_offset);
+ if (net_map.count(net) == 0)
+ net_map[net] = bit;
+ else if (wire->port_input)
+ module->connect(net_map.at(net), bit);
+ else
+ module->connect(bit, net_map.at(net));
+ }
+ if (i == portbus->RightIndex())
+ break;
+ }
+ }
+
+ module->fixup_ports();
+
+ FOREACH_NET_OF_NETLIST(nl, mi, net)
+ {
+ if (net->IsRamNet())
+ {
+ RTLIL::Memory *memory = new RTLIL::Memory;
+ memory->name = RTLIL::escape_id(net->Name());
+ log_assert(module->count_id(memory->name) == 0);
+ module->memories[memory->name] = memory;
+
+ int number_of_bits = net->Size();
+ int bits_in_word = number_of_bits;
+ FOREACH_PORTREF_OF_NET(net, si, pr) {
+ if (pr->GetInst()->Type() == OPER_READ_PORT) {
+ bits_in_word = std::min<int>(bits_in_word, pr->GetInst()->OutputSize());
+ continue;
+ }
+ if (pr->GetInst()->Type() == OPER_WRITE_PORT || pr->GetInst()->Type() == OPER_CLOCKED_WRITE_PORT) {
+ bits_in_word = std::min<int>(bits_in_word, pr->GetInst()->Input2Size());
+ continue;
+ }
+ log_error("Verific RamNet %s is connected to unsupported instance type %s (%s).\n",
+ net->Name(), pr->GetInst()->View()->Owner()->Name(), pr->GetInst()->Name());
+ }
+
+ memory->width = bits_in_word;
+ memory->size = number_of_bits / bits_in_word;
+ continue;
+ }
+
+ if (net_map.count(net)) {
+ // log(" skipping net %s.\n", net->Name());
+ continue;
+ }
+
+ if (net->Bus())
+ continue;
+
+ // log(" importing net %s.\n", net->Name());
+
+ RTLIL::IdString wire_name = module->uniquify(RTLIL::escape_id(net->Name()));
+ RTLIL::Wire *wire = module->addWire(wire_name);
+ import_attributes(wire->attributes, net);
+
+ net_map[net] = wire;
+ }
+
+ FOREACH_NETBUS_OF_NETLIST(nl, mi, netbus)
+ {
+ bool found_new_net = false;
+ for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) {
+ net = netbus->ElementAtIndex(i);
+ if (net_map.count(net) == 0)
+ found_new_net = true;
+ if (i == netbus->RightIndex())
+ break;
+ }
+
+ if (found_new_net)
+ {
+ // log(" importing netbus %s.\n", netbus->Name());
+
+ RTLIL::IdString wire_name = module->uniquify(RTLIL::escape_id(netbus->Name()));
+ RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size());
+ wire->start_offset = std::min(netbus->LeftIndex(), netbus->RightIndex());
+ import_attributes(wire->attributes, netbus);
+
+ for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) {
+ if (netbus->ElementAtIndex(i)) {
+ net = netbus->ElementAtIndex(i);
+ RTLIL::SigBit bit(wire, i - wire->start_offset);
+ if (net_map.count(net) == 0)
+ net_map[net] = bit;
+ else
+ module->connect(bit, net_map.at(net));
+ }
+ if (i == netbus->RightIndex())
+ break;
+ }
+ }
+ else
+ {
+ // log(" skipping netbus %s.\n", netbus->Name());
+ }
+ }
+
+ FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst)
+ {
+ // log(" importing cell %s (%s).\n", inst->Name(), inst->View()->Owner()->Name());
+
+ if (inst->Type() == PRIM_PWR) {
+ module->connect(net_map.at(inst->GetOutput()), RTLIL::State::S1);
+ continue;
+ }
+
+ if (inst->Type() == PRIM_GND) {
+ module->connect(net_map.at(inst->GetOutput()), RTLIL::State::S0);
+ continue;
+ }
+
+ if (inst->Type() == PRIM_X) {
+ module->connect(net_map.at(inst->GetOutput()), RTLIL::State::Sx);
+ continue;
+ }
+
+ if (inst->Type() == PRIM_Z) {
+ module->connect(net_map.at(inst->GetOutput()), RTLIL::State::Sz);
+ continue;
+ }
+
+ if (inst->Type() == OPER_READ_PORT)
+ {
+ RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetInput()->Name()));
+ if (memory->width != int(inst->OutputSize()))
+ log_error("Import of asymetric memories from Verific is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name());
+
+ RTLIL::SigSpec addr = operatorInput1(inst, net_map);
+ RTLIL::SigSpec data = operatorOutput(inst, net_map, module);
+
+ RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), "$memrd");
+ cell->parameters["\\MEMID"] = memory->name.str();
+ cell->parameters["\\CLK_ENABLE"] = false;
+ cell->parameters["\\CLK_POLARITY"] = true;
+ cell->parameters["\\TRANSPARENT"] = false;
+ cell->parameters["\\ABITS"] = SIZE(addr);
+ cell->parameters["\\WIDTH"] = SIZE(data);
+ cell->setPort("\\CLK", RTLIL::State::S0);
+ cell->setPort("\\ADDR", addr);
+ cell->setPort("\\DATA", data);
+ continue;
+ }
+
+ if (inst->Type() == OPER_WRITE_PORT || inst->Type() == OPER_CLOCKED_WRITE_PORT)
+ {
+ RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetOutput()->Name()));
+ if (memory->width != int(inst->Input2Size()))
+ log_error("Import of asymetric memories from Verific is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name());
+
+ RTLIL::SigSpec addr = operatorInput1(inst, net_map);
+ RTLIL::SigSpec data = operatorInput2(inst, net_map);
+
+ RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), "$memwr");
+ cell->parameters["\\MEMID"] = memory->name.str();
+ cell->parameters["\\CLK_ENABLE"] = false;
+ cell->parameters["\\CLK_POLARITY"] = true;
+ cell->parameters["\\PRIORITY"] = 0;
+ cell->parameters["\\ABITS"] = SIZE(addr);
+ cell->parameters["\\WIDTH"] = SIZE(data);
+ cell->setPort("\\EN", RTLIL::SigSpec(net_map.at(inst->GetControl())).repeat(SIZE(data)));
+ cell->setPort("\\CLK", RTLIL::State::S0);
+ cell->setPort("\\ADDR", addr);
+ cell->setPort("\\DATA", data);
+
+ if (inst->Type() == OPER_CLOCKED_WRITE_PORT) {
+ cell->parameters["\\CLK_ENABLE"] = true;
+ cell->setPort("\\CLK", net_map.at(inst->GetClock()));
+ }
+ continue;
+ }
+
+ if (!mode_gates) {
+ if (import_netlist_instance_cells(module, net_map, inst))
+ continue;
+ if (inst->IsOperator())
+ log("Warning: Unsupported Verific operator: %s (fallback to gate level implementation provided by verific)\n", inst->View()->Owner()->Name());
+ } else {
+ if (import_netlist_instance_gates(module, net_map, inst))
+ continue;
+ }
+
+ if (inst->IsPrimitive())
+ log_error("Unsupported Verific primitive: %s\n", inst->View()->Owner()->Name());
+
+ nl_todo.insert(inst->View());
+
+ RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), inst->IsOperator() ?
+ std::string("$verific$") + inst->View()->Owner()->Name() : RTLIL::escape_id(inst->View()->Owner()->Name()));
+
+ FOREACH_PORTREF_OF_INST(inst, mi2, pr) {
+ // log(" .%s(%s)\n", pr->GetPort()->Name(), pr->GetNet()->Name());
+ const char *port_name = pr->GetPort()->Name();
+ int port_offset = 0;
+ if (pr->GetPort()->Bus()) {
+ port_name = pr->GetPort()->Bus()->Name();
+ port_offset = pr->GetPort()->Bus()->IndexOf(pr->GetPort()) -
+ std::min(pr->GetPort()->Bus()->LeftIndex(), pr->GetPort()->Bus()->RightIndex());
+ }
+ RTLIL::SigSpec conn;
+ if (cell->hasPort(RTLIL::escape_id(port_name)))
+ conn = cell->getPort(RTLIL::escape_id(port_name));
+ while (SIZE(conn) <= port_offset) {
+ if (pr->GetPort()->GetDir() != DIR_IN)
+ conn.append(module->addWire(NEW_ID, port_offset - SIZE(conn)));
+ conn.append(RTLIL::State::Sz);
+ }
+ conn.replace(port_offset, net_map.at(pr->GetNet()));
+ cell->setPort(RTLIL::escape_id(port_name), conn);
+ }
+ }
+}
+
+#endif /* YOSYS_ENABLE_VERIFIC */
+
+YOSYS_NAMESPACE_BEGIN
+
+struct VerificPass : public Pass {
+ VerificPass() : Pass("verific", "load Verilog and VHDL designs using Verific") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv} <verilog-file>..\n");
+ log("\n");
+ log("Load the specified Verilog/SystemVerilog files into Verific.\n");
+ log("\n");
+ log("\n");
+ log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008} <vhdl-file>..\n");
+ log("\n");
+ log("Load the specified VHDL files into Verific.\n");
+ log("\n");
+ log("\n");
+ log(" verific -import [-gates] {-all | <top-module>..}\n");
+ log("\n");
+ log("Elaborate the design for the sepcified top modules, import to Yosys and\n");
+ log("reset the internal state of Verific. A gate-level netlist is created\n");
+ log("when called with -gates.\n");
+ log("\n");
+ log("Visit http://verific.com/ for more information on Verific.\n");
+ log("\n");
+ }
+#ifdef YOSYS_ENABLE_VERIFIC
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing VERIFIC (loading Verilog and VHDL designs using Verific).\n");
+
+ Message::SetConsoleOutput(0);
+ Message::RegisterCallBackMsg(msg_func);
+
+ if (args.size() > 1 && args[1] == "-vlog95") {
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!veri_file::Analyze(args[argidx].c_str(), veri_file::VERILOG_95))
+ log_cmd_error("Reading `%s' in VERILOG_95 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-vlog2k") {
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!veri_file::Analyze(args[argidx].c_str(), veri_file::VERILOG_2K))
+ log_cmd_error("Reading `%s' in VERILOG_2K mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-sv2005") {
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG_2005))
+ log_cmd_error("Reading `%s' in SYSTEM_VERILOG_2005 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-sv2009") {
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG_2009))
+ log_cmd_error("Reading `%s' in SYSTEM_VERILOG_2009 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-sv") {
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!veri_file::Analyze(args[argidx].c_str(), veri_file::SYSTEM_VERILOG))
+ log_cmd_error("Reading `%s' in SYSTEM_VERILOG mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-vhdl87") {
+ vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_87))
+ log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-vhdl93") {
+ vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_93))
+ log_cmd_error("Reading `%s' in VHDL_93 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-vhdl2k") {
+ vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str());
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_2K))
+ log_cmd_error("Reading `%s' in VHDL_2K mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-vhdl2008") {
+ vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2008").c_str());
+ for (size_t argidx = 2; argidx < args.size(); argidx++)
+ if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_2008))
+ log_cmd_error("Reading `%s' in VHDL_2008 mode failed.\n", args[argidx].c_str());
+ return;
+ }
+
+ if (args.size() > 1 && args[1] == "-import")
+ {
+ std::set<Netlist*> nl_todo, nl_done;
+ bool mode_all = false, mode_gates = false;
+
+ size_t argidx = 2;
+ for (; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-all") {
+ mode_all = true;
+ continue;
+ }
+ if (args[argidx] == "-gates") {
+ mode_gates = true;
+ continue;
+ }
+ break;
+ }
+
+ if (argidx > args.size() && args[argidx].substr(0, 1) == "-")
+ cmd_error(args, argidx, "unknown option");
+
+ if (mode_all)
+ {
+ if (argidx != args.size())
+ log_cmd_error("Got -all and an explicit list of top modules.\n");
+
+ MapIter m1, m2, m3;
+ VeriModule *mod;
+ FOREACH_VERILOG_MODULE(m1, mod)
+ args.push_back(mod->Name());
+
+ VhdlLibrary *lib;
+ VhdlPrimaryUnit *primunit;
+ FOREACH_VHDL_LIBRARY(m1, lib)
+ FOREACH_VHDL_PRIMARY_UNIT(lib, m2, primunit) {
+ if (primunit->IsPackageDecl())
+ continue;
+ args.push_back(primunit->Name());
+ }
+ }
+ else
+ if (argidx == args.size())
+ log_cmd_error("No top module specified.\n");
+
+ for (; argidx < args.size(); argidx++) {
+ if (veri_file::GetModule(args[argidx].c_str())) {
+ if (!veri_file::Elaborate(args[argidx].c_str()))
+ log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str());
+ nl_todo.insert(Netlist::PresentDesign());
+ } else {
+ if (!vhdl_file::Elaborate(args[argidx].c_str()))
+ log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str());
+ nl_todo.insert(Netlist::PresentDesign());
+ }
+ }
+
+ while (!nl_todo.empty()) {
+ Netlist *nl = *nl_todo.begin();
+ if (nl_done.count(nl) == 0)
+ import_netlist(design, nl, nl_todo, mode_gates);
+ nl_todo.erase(nl);
+ nl_done.insert(nl);
+ }
+
+ Libset::Reset();
+ return;
+ }
+
+ log_cmd_error("Missing or unsupported mode parameter.\n");
+ }
+#else /* YOSYS_ENABLE_VERIFIC */
+ virtual void execute(std::vector<std::string>, RTLIL::Design *) {
+ log_cmd_error("This version of Yosys is built without Verific support.\n");
+ }
+#endif
+} VerificPass;
+
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc
index 5586b4cc..49eb320e 100644
--- a/frontends/verilog/Makefile.inc
+++ b/frontends/verilog/Makefile.inc
@@ -5,13 +5,13 @@ GENFILES += frontends/verilog/parser.output
GENFILES += frontends/verilog/lexer.cc
frontends/verilog/parser.tab.cc: frontends/verilog/parser.y
- bison -d -r all -b frontends/verilog/parser frontends/verilog/parser.y
- mv frontends/verilog/parser.tab.c frontends/verilog/parser.tab.cc
+ $(P) bison -d -r all -b frontends/verilog/parser frontends/verilog/parser.y
+ $(Q) mv frontends/verilog/parser.tab.c frontends/verilog/parser.tab.cc
frontends/verilog/parser.tab.h: frontends/verilog/parser.tab.cc
frontends/verilog/lexer.cc: frontends/verilog/lexer.l
- flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l
+ $(P) flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l
OBJS += frontends/verilog/parser.tab.o
OBJS += frontends/verilog/lexer.o
diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc
index c95ce5dc..a81e3010 100644
--- a/frontends/verilog/const2ast.cc
+++ b/frontends/verilog/const2ast.cc
@@ -36,10 +36,11 @@
#include "verilog_frontend.h"
#include "kernel/log.h"
-#include <assert.h>
#include <string.h>
#include <math.h>
+YOSYS_NAMESPACE_BEGIN
+
using namespace AST;
// divide an arbitrary length decimal number by two and return the rest
@@ -47,11 +48,13 @@ static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
{
int carry = 0;
for (size_t i = 0; i < digits.size(); i++) {
- assert(digits[i] < 10);
+ log_assert(digits[i] < 10);
digits[i] += carry * 10;
carry = digits[i] % 2;
digits[i] /= 2;
}
+ while (!digits.empty() && !digits.front())
+ digits.erase(digits.begin());
return carry;
}
@@ -90,10 +93,15 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
if (base == 10) {
data.clear();
- if (len_in_bits < 0)
- len_in_bits = ceil(digits.size()/log10(2));
- for (int i = 0; i < len_in_bits; i++)
- data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0);
+ if (len_in_bits < 0) {
+ while (!digits.empty())
+ data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0);
+ while (data.size() < 32)
+ data.push_back(RTLIL::S0);
+ } else {
+ for (int i = 0; i < len_in_bits; i++)
+ data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0);
+ }
return;
}
@@ -151,20 +159,24 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type)
str = code.c_str();
char *endptr;
- long intval = strtol(str, &endptr, 10);
+ long len_in_bits = strtol(str, &endptr, 10);
+
+ // Simple base-10 integer
+ if (*endptr == 0) {
+ std::vector<RTLIL::State> data;
+ my_strtobin(data, str, -1, 10, case_type);
+ if (data.back() == RTLIL::S1)
+ data.push_back(RTLIL::S0);
+ return AstNode::mkconst_bits(data, true);
+ }
- // Simple 32 bit integer
- if (*endptr == 0)
- return AstNode::mkconst_int(intval, true);
-
// unsized constant
if (str == endptr)
- intval = -1;
+ len_in_bits = -1;
// The "<bits>'s?[bodh]<digits>" syntax
if (*endptr == '\'')
{
- int len_in_bits = intval;
std::vector<RTLIL::State> data;
bool is_signed = false;
if (*(endptr+1) == 's') {
@@ -188,9 +200,17 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type)
default:
return NULL;
}
+ if (len_in_bits < 0) {
+ if (is_signed && data.back() == RTLIL::S1)
+ data.push_back(RTLIL::S0);
+ while (data.size() < 32)
+ data.push_back(RTLIL::S0);
+ }
return AstNode::mkconst_bits(data, is_signed);
}
return NULL;
}
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/verilog/lexer.l b/frontends/verilog/lexer.l
index 81167cf4..c9302aba 100644
--- a/frontends/verilog/lexer.l
+++ b/frontends/verilog/lexer.l
@@ -34,18 +34,37 @@
%{
+#ifdef __clang__
+// bison generates code using the 'register' storage class specifier
+#pragma clang diagnostic ignored "-Wdeprecated-register"
+#endif
+
#include "kernel/log.h"
#include "verilog_frontend.h"
#include "frontends/ast/ast.h"
#include "parser.tab.h"
+USING_YOSYS_NAMESPACE
using namespace AST;
using namespace VERILOG_FRONTEND;
+YOSYS_NAMESPACE_BEGIN
namespace VERILOG_FRONTEND {
std::vector<std::string> fn_stack;
std::vector<int> ln_stack;
}
+YOSYS_NAMESPACE_END
+
+#define SV_KEYWORD(_tok) \
+ if (sv_mode) return _tok; \
+ log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\
+ "recognized unless read_verilog is called with -sv!\n", yytext, \
+ AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); \
+ frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); \
+ return TOK_ID;
+
+#define YY_INPUT(buf,result,max_size) \
+ result = lexin->readsome(buf, max_size);
%}
@@ -58,29 +77,53 @@ namespace VERILOG_FRONTEND {
%x STRING
%x SYNOPSYS_TRANSLATE_OFF
%x SYNOPSYS_FLAGS
+%x IMPORT_DPI
%%
-"`file_push "[^\n]* {
+<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_push "[^\n]* {
fn_stack.push_back(current_filename);
ln_stack.push_back(frontend_verilog_yyget_lineno());
current_filename = yytext+11;
frontend_verilog_yyset_lineno(0);
}
-"`file_pop"[^\n]*\n {
+<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_pop"[^\n]*\n {
current_filename = fn_stack.back();
fn_stack.pop_back();
frontend_verilog_yyset_lineno(ln_stack.back());
ln_stack.pop_back();
}
+<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`line"[ \t]+[^ \t\r\n]+[ \t]+\"[^ \r\n]+\"[^\r\n]*\n {
+ char *p = yytext + 5;
+ while (*p == ' ' || *p == '\t') p++;
+ frontend_verilog_yyset_lineno(atoi(p));
+ while (*p && *p != ' ' && *p != '\t') p++;
+ while (*p == ' ' || *p == '\t') p++;
+ char *q = *p ? p + 1 : p;
+ while (*q && *q != '"') q++;
+ current_filename = std::string(p).substr(1, q-p-1);
+}
+
"`file_notfound "[^\n]* {
log_error("Can't open include file `%s'!\n", yytext + 15);
}
"`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */
+"`default_nettype"[ \t]+[^ \t\r\n/]+ {
+ char *p = yytext;
+ while (*p != 0 && *p != ' ' && *p != '\t') p++;
+ while (*p == ' ' || *p == '\t') p++;
+ if (!strcmp(p, "none"))
+ VERILOG_FRONTEND::default_nettype_wire = false;
+ else if (!strcmp(p, "wire"))
+ VERILOG_FRONTEND::default_nettype_wire = true;
+ else
+ frontend_verilog_yyerror("Unsupported default nettype: %s", p);
+}
+
"`"[a-zA-Z_$][a-zA-Z0-9_$]* {
frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext);
}
@@ -112,8 +155,17 @@ namespace VERILOG_FRONTEND {
"default" { return TOK_DEFAULT; }
"generate" { return TOK_GENERATE; }
"endgenerate" { return TOK_ENDGENERATE; }
+"while" { return TOK_WHILE; }
+"repeat" { return TOK_REPEAT; }
-"assert"([ \t\r\n]+"property")? { return TOK_ASSERT; }
+"always_comb" { SV_KEYWORD(TOK_ALWAYS); }
+"always_ff" { SV_KEYWORD(TOK_ALWAYS); }
+"always_latch" { SV_KEYWORD(TOK_ALWAYS); }
+
+"assert" { SV_KEYWORD(TOK_ASSERT); }
+"property" { SV_KEYWORD(TOK_PROPERTY); }
+"logic" { SV_KEYWORD(TOK_REG); }
+"bit" { SV_KEYWORD(TOK_REG); }
"input" { return TOK_INPUT; }
"output" { return TOK_OUTPUT; }
@@ -123,6 +175,7 @@ namespace VERILOG_FRONTEND {
"integer" { return TOK_INTEGER; }
"signed" { return TOK_SIGNED; }
"genvar" { return TOK_GENVAR; }
+"real" { return TOK_REAL; }
[0-9]+ {
frontend_verilog_yylval.string = new std::string(yytext);
@@ -134,6 +187,16 @@ namespace VERILOG_FRONTEND {
return TOK_CONST;
}
+[0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)? {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_REALVAL;
+}
+
+[0-9][0-9_]*[eE][-+]?[0-9_]+ {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_REALVAL;
+}
+
\" { BEGIN(STRING); }
<STRING>\\. { yymore(); }
<STRING>\" {
@@ -215,6 +278,27 @@ supply1 { return TOK_SUPPLY1; }
<SYNOPSYS_FLAGS>. /* ignore everything else */
<SYNOPSYS_FLAGS>"*/" { BEGIN(0); }
+import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
+ BEGIN(IMPORT_DPI);
+ return TOK_DPI_FUNCTION;
+}
+
+<IMPORT_DPI>[a-zA-Z_$][a-zA-Z0-9_$]* {
+ frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
+ return TOK_ID;
+}
+
+<IMPORT_DPI>[ \t\r\n] /* ignore whitespaces */
+
+<IMPORT_DPI>";" {
+ BEGIN(0);
+ return *yytext;
+}
+
+<IMPORT_DPI>. {
+ return *yytext;
+}
+
"\\"[^ \t\r\n]+ {
frontend_verilog_yylval.string = new std::string(yytext);
return TOK_ID;
diff --git a/frontends/verilog/parser.y b/frontends/verilog/parser.y
index 5b6bf58c..a9f69a49 100644
--- a/frontends/verilog/parser.y
+++ b/frontends/verilog/parser.y
@@ -35,13 +35,15 @@
%{
#include <list>
-#include <assert.h>
+#include <string.h>
#include "verilog_frontend.h"
#include "kernel/log.h"
+USING_YOSYS_NAMESPACE
using namespace AST;
using namespace VERILOG_FRONTEND;
+YOSYS_NAMESPACE_BEGIN
namespace VERILOG_FRONTEND {
int port_counter;
std::map<std::string, int> port_stubs;
@@ -53,7 +55,12 @@ namespace VERILOG_FRONTEND {
struct AstNode *current_ast, *current_ast_mod;
int current_function_or_task_port_id;
std::vector<char> case_type_stack;
+ bool do_not_require_port_stubs;
+ bool default_nettype_wire;
+ bool sv_mode;
+ std::istream *lexin;
}
+YOSYS_NAMESPACE_END
static void append_attr(AstNode *ast, std::map<std::string, AstNode*> *al)
{
@@ -83,30 +90,31 @@ static void free_attr(std::map<std::string, AstNode*> *al)
%}
-%name-prefix="frontend_verilog_yy"
+%name-prefix "frontend_verilog_yy"
%union {
std::string *string;
- struct AstNode *ast;
- std::map<std::string, AstNode*> *al;
+ struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast;
+ std::map<std::string, YOSYS_NAMESPACE_PREFIX AST::AstNode*> *al;
bool boolean;
}
-%token <string> TOK_STRING TOK_ID TOK_CONST TOK_PRIMITIVE
+%token <string> TOK_STRING TOK_ID TOK_CONST TOK_REALVAL TOK_PRIMITIVE
%token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END
%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM
%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG
%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
-%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR
-%token TOK_POSEDGE TOK_NEGEDGE TOK_OR
+%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
+%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR
%token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT
%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK
-%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR
+%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL
%token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE
%token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED
-%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT
+%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_PROPERTY
-%type <ast> wire_type range non_opt_range expr basic_expr concat_list rvalue lvalue lvalue_concat_list
+%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int
+%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
%type <string> opt_label tok_prim_wrapper hierarchical_id
%type <boolean> opt_signed
%type <al> attr
@@ -130,14 +138,21 @@ static void free_attr(std::map<std::string, AstNode*> *al)
%%
-input:
- module input |
- defattr input |
- /* empty */ {
- for (auto &it : default_attr_list)
- delete it.second;
- default_attr_list.clear();
- };
+input: {
+ ast_stack.push_back(current_ast);
+} design {
+ ast_stack.pop_back();
+ log_assert(SIZE(ast_stack) == 0);
+ for (auto &it : default_attr_list)
+ delete it.second;
+ default_attr_list.clear();
+};
+
+design:
+ module design |
+ defattr design |
+ task_func_decl design |
+ /* empty */;
attr:
{
@@ -205,10 +220,11 @@ hierarchical_id:
module:
attr TOK_MODULE TOK_ID {
+ do_not_require_port_stubs = false;
AstNode *mod = new AstNode(AST_MODULE);
- current_ast->children.push_back(mod);
- current_ast_mod = mod;
+ ast_stack.back()->children.push_back(mod);
ast_stack.push_back(mod);
+ current_ast_mod = mod;
port_stubs.clear();
port_counter = 0;
mod->str = *$3;
@@ -219,7 +235,8 @@ module:
frontend_verilog_yyerror("Missing details for module port `%s'.",
port_stubs.begin()->first.c_str());
ast_stack.pop_back();
- assert(ast_stack.size() == 0);
+ log_assert(ast_stack.size() == 1);
+ current_ast_mod = NULL;
};
module_para_opt:
@@ -288,7 +305,10 @@ module_arg:
ast_stack.back()->children.push_back(node);
append_attr(node, $1);
delete $4;
- } module_arg_opt_assignment;
+ } module_arg_opt_assignment |
+ '.' '.' '.' {
+ do_not_require_port_stubs = true;
+ };
wire_type:
{
@@ -320,6 +340,7 @@ wire_type_token:
astbuf3->is_reg = true;
astbuf3->range_left = 31;
astbuf3->range_right = 0;
+ astbuf3->is_signed = true;
} |
TOK_GENVAR {
astbuf3->type = AST_GENVAR;
@@ -352,6 +373,15 @@ non_opt_range:
$$->children.push_back($2);
};
+non_opt_multirange:
+ non_opt_range non_opt_range {
+ $$ = new AstNode(AST_MULTIRANGE, $1, $2);
+ } |
+ non_opt_multirange non_opt_range {
+ $$ = $1;
+ $$->children.push_back($2);
+ };
+
range:
non_opt_range {
$$ = $1;
@@ -360,44 +390,120 @@ range:
$$ = NULL;
};
+range_or_multirange:
+ range { $$ = $1; } |
+ non_opt_multirange { $$ = $1; };
+
+range_or_signed_int:
+ range {
+ $$ = $1;
+ } |
+ TOK_INTEGER {
+ $$ = new AstNode(AST_RANGE);
+ $$->children.push_back(AstNode::mkconst_int(31, true));
+ $$->children.push_back(AstNode::mkconst_int(0, true));
+ $$->is_signed = true;
+ };
+
module_body:
module_body module_body_stmt |
+ /* the following line makes the generate..endgenrate keywords optional */
+ module_body gen_stmt |
/* empty */;
module_body_stmt:
task_func_decl | param_decl | localparam_decl | defparam_decl | wire_decl | assign_stmt | cell_stmt |
- always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert;
+ always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property;
task_func_decl:
- TOK_TASK TOK_ID ';' {
+ attr TOK_DPI_FUNCTION TOK_ID TOK_ID {
+ current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$3), AstNode::mkconst_str(*$4));
+ current_function_or_task->str = *$4;
+ append_attr(current_function_or_task, $1);
+ ast_stack.back()->children.push_back(current_function_or_task);
+ delete $3;
+ delete $4;
+ } opt_dpi_function_args ';' {
+ current_function_or_task = NULL;
+ } |
+ attr TOK_DPI_FUNCTION TOK_ID '=' TOK_ID TOK_ID {
+ current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$5), AstNode::mkconst_str(*$3));
+ current_function_or_task->str = *$6;
+ append_attr(current_function_or_task, $1);
+ ast_stack.back()->children.push_back(current_function_or_task);
+ delete $3;
+ delete $5;
+ delete $6;
+ } opt_dpi_function_args ';' {
+ current_function_or_task = NULL;
+ } |
+ attr TOK_DPI_FUNCTION TOK_ID ':' TOK_ID '=' TOK_ID TOK_ID {
+ current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$7), AstNode::mkconst_str(*$3 + ":" + RTLIL::unescape_id(*$5)));
+ current_function_or_task->str = *$8;
+ append_attr(current_function_or_task, $1);
+ ast_stack.back()->children.push_back(current_function_or_task);
+ delete $3;
+ delete $5;
+ delete $7;
+ delete $8;
+ } opt_dpi_function_args ';' {
+ current_function_or_task = NULL;
+ } |
+ attr TOK_TASK TOK_ID ';' {
current_function_or_task = new AstNode(AST_TASK);
- current_function_or_task->str = *$2;
+ current_function_or_task->str = *$3;
+ append_attr(current_function_or_task, $1);
ast_stack.back()->children.push_back(current_function_or_task);
ast_stack.push_back(current_function_or_task);
current_function_or_task_port_id = 1;
- delete $2;
+ delete $3;
} task_func_body TOK_ENDTASK {
current_function_or_task = NULL;
ast_stack.pop_back();
} |
- TOK_FUNCTION opt_signed range TOK_ID ';' {
+ attr TOK_FUNCTION opt_signed range_or_signed_int TOK_ID ';' {
current_function_or_task = new AstNode(AST_FUNCTION);
- current_function_or_task->str = *$4;
+ current_function_or_task->str = *$5;
+ append_attr(current_function_or_task, $1);
ast_stack.back()->children.push_back(current_function_or_task);
ast_stack.push_back(current_function_or_task);
AstNode *outreg = new AstNode(AST_WIRE);
- if ($3 != NULL)
- outreg->children.push_back($3);
- outreg->str = *$4;
- outreg->is_signed = $2;
+ outreg->str = *$5;
+ outreg->is_signed = $3;
+ if ($4 != NULL) {
+ outreg->children.push_back($4);
+ outreg->is_signed = $3 || $4->is_signed;
+ $4->is_signed = false;
+ }
current_function_or_task->children.push_back(outreg);
current_function_or_task_port_id = 1;
- delete $4;
+ delete $5;
} task_func_body TOK_ENDFUNCTION {
current_function_or_task = NULL;
ast_stack.pop_back();
};
+dpi_function_arg:
+ TOK_ID TOK_ID {
+ current_function_or_task->children.push_back(AstNode::mkconst_str(*$1));
+ delete $1;
+ delete $2;
+ } |
+ TOK_ID {
+ current_function_or_task->children.push_back(AstNode::mkconst_str(*$1));
+ delete $1;
+ };
+
+opt_dpi_function_args:
+ '(' dpi_function_args ')' |
+ /* empty */;
+
+dpi_function_args:
+ dpi_function_args ',' dpi_function_arg |
+ dpi_function_args ',' |
+ dpi_function_arg |
+ /* empty */;
+
opt_signed:
TOK_SIGNED {
$$ = true;
@@ -422,6 +528,14 @@ param_integer:
astbuf1->children.push_back(new AstNode(AST_RANGE));
astbuf1->children.back()->children.push_back(AstNode::mkconst_int(31, true));
astbuf1->children.back()->children.push_back(AstNode::mkconst_int(0, true));
+ astbuf1->is_signed = true;
+ } | /* empty */;
+
+param_real:
+ TOK_REAL {
+ if (astbuf1->children.size() != 1)
+ frontend_verilog_yyerror("Syntax error.");
+ astbuf1->children.push_back(new AstNode(AST_REALVALUE));
} | /* empty */;
param_range:
@@ -437,7 +551,7 @@ param_decl:
TOK_PARAMETER {
astbuf1 = new AstNode(AST_PARAMETER);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
- } param_signed param_integer param_range param_decl_list ';' {
+ } param_signed param_integer param_real param_range param_decl_list ';' {
delete astbuf1;
};
@@ -445,7 +559,7 @@ localparam_decl:
TOK_LOCALPARAM {
astbuf1 = new AstNode(AST_LOCALPARAM);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
- } param_signed param_integer param_range param_decl_list ';' {
+ } param_signed param_integer param_real param_range param_decl_list ';' {
delete astbuf1;
};
@@ -533,7 +647,7 @@ wire_name_and_opt_assign:
};
wire_name:
- TOK_ID range {
+ TOK_ID range_or_multirange {
AstNode *node = astbuf1->clone();
node->str = *$1;
append_attr_clone(node, albuf);
@@ -552,6 +666,9 @@ wire_name:
node->children.push_back($2);
}
if (current_function_or_task == NULL) {
+ if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) {
+ port_stubs[*$1] = ++port_counter;
+ }
if (port_stubs.count(*$1) != 0) {
if (!node->is_input && !node->is_output)
frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $1->c_str());
@@ -563,12 +680,11 @@ wire_name:
if (node->is_input || node->is_output)
frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str());
}
- ast_stack.back()->children.push_back(node);
} else {
if (node->is_input || node->is_output)
node->port_id = current_function_or_task_port_id++;
- current_function_or_task->children.push_back(node);
}
+ ast_stack.back()->children.push_back(node);
delete $1;
};
@@ -621,6 +737,13 @@ single_cell:
astbuf2->str = *$1;
delete $1;
ast_stack.back()->children.push_back(astbuf2);
+ } '(' cell_port_list ')' |
+ TOK_ID non_opt_range {
+ astbuf2 = astbuf1->clone();
+ if (astbuf2->type != AST_PRIMITIVE)
+ astbuf2->str = *$1;
+ delete $1;
+ ast_stack.back()->children.push_back(new AstNode(AST_CELLARRAY, $2, astbuf2));
} '(' cell_port_list ')';
prim_list:
@@ -753,6 +876,11 @@ assert:
ast_stack.back()->children.push_back(new AstNode(AST_ASSERT, $3));
};
+assert_property:
+ TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSERT, $4));
+ };
+
simple_behavioral_stmt:
lvalue '=' expr {
AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3);
@@ -808,6 +936,32 @@ behavioral_stmt:
ast_stack.pop_back();
ast_stack.pop_back();
} |
+ attr TOK_WHILE '(' expr ')' {
+ AstNode *node = new AstNode(AST_WHILE);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $1);
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back($4);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ } behavioral_stmt {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ } |
+ attr TOK_REPEAT '(' expr ')' {
+ AstNode *node = new AstNode(AST_REPEAT);
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $1);
+ AstNode *block = new AstNode(AST_BLOCK);
+ ast_stack.back()->children.push_back($4);
+ ast_stack.back()->children.push_back(block);
+ ast_stack.push_back(block);
+ } behavioral_stmt {
+ ast_stack.pop_back();
+ ast_stack.pop_back();
+ } |
attr TOK_IF '(' expr ')' {
AstNode *node = new AstNode(AST_CASE);
AstNode *block = new AstNode(AST_BLOCK);
@@ -934,8 +1088,8 @@ rvalue:
$$->str = *$1;
delete $1;
} |
- hierarchical_id non_opt_range non_opt_range {
- $$ = new AstNode(AST_IDENTIFIER, $2, $3);
+ hierarchical_id non_opt_multirange {
+ $$ = new AstNode(AST_IDENTIFIER, $2);
$$->str = *$1;
delete $1;
};
@@ -976,9 +1130,12 @@ single_arg:
};
module_gen_body:
- module_gen_body gen_stmt |
+ module_gen_body gen_stmt_or_module_body_stmt |
/* empty */;
+gen_stmt_or_module_body_stmt:
+ gen_stmt | module_body_stmt;
+
// this production creates the obligatory if-else shift/reduce conflict
gen_stmt:
TOK_FOR '(' {
@@ -1017,15 +1174,14 @@ gen_stmt:
if ($6 != NULL)
delete $6;
ast_stack.pop_back();
- } |
- module_body_stmt;
+ };
gen_stmt_block:
{
AstNode *node = new AstNode(AST_GENBLOCK);
ast_stack.back()->children.push_back(node);
ast_stack.push_back(node);
- } gen_stmt {
+ } gen_stmt_or_module_body_stmt {
ast_stack.pop_back();
};
@@ -1073,12 +1229,30 @@ basic_expr:
delete $1;
delete $2;
} |
+ TOK_CONST TOK_CONST {
+ $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back());
+ if ($$ == NULL || (*$2)[0] != '\'')
+ log_error("Value conversion failed: `%s%s'\n", $1->c_str(), $2->c_str());
+ delete $1;
+ delete $2;
+ } |
TOK_CONST {
$$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back());
if ($$ == NULL)
log_error("Value conversion failed: `%s'\n", $1->c_str());
delete $1;
} |
+ TOK_REALVAL {
+ $$ = new AstNode(AST_REALVALUE);
+ char *p = strdup($1->c_str()), *q;
+ for (int i = 0, j = 0; !p[j]; j++)
+ if (p[j] != '_')
+ p[i++] = p[j], p[i] = 0;
+ $$->realvalue = strtod(p, &q);
+ log_assert(*q == 0);
+ delete $1;
+ free(p);
+ } |
TOK_STRING {
$$ = AstNode::mkconst_str(*$1);
delete $1;
diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc
index db53e8c6..f8343321 100644
--- a/frontends/verilog/preproc.cc
+++ b/frontends/verilog/preproc.cc
@@ -37,7 +37,8 @@
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
-#include <assert.h>
+
+YOSYS_NAMESPACE_BEGIN
static std::list<std::string> output_code;
static std::list<std::string> input_buffer;
@@ -65,7 +66,7 @@ static char next_char()
if (input_buffer.empty())
return 0;
- assert(input_buffer_charp <= input_buffer.front().size());
+ log_assert(input_buffer_charp <= input_buffer.front().size());
if (input_buffer_charp == input_buffer.front().size()) {
input_buffer_charp = 0;
input_buffer.pop_front();
@@ -130,6 +131,12 @@ static std::string next_token(bool pass_newline = false)
token += ch;
}
}
+ if (token == "\"\"" && (ch = next_char()) != 0) {
+ if (ch == '"')
+ token += ch;
+ else
+ return_char(ch);
+ }
}
else if (ch == '/')
{
@@ -186,7 +193,7 @@ static std::string next_token(bool pass_newline = false)
return token;
}
-static void input_file(FILE *f, std::string filename)
+static void input_file(std::istream &f, std::string filename)
{
char buffer[513];
int rc;
@@ -195,14 +202,14 @@ static void input_file(FILE *f, std::string filename)
auto it = input_buffer.begin();
input_buffer.insert(it, "`file_push " + filename + "\n");
- while ((rc = fread(buffer, 1, sizeof(buffer)-1, f)) > 0) {
+ while ((rc = f.readsome(buffer, sizeof(buffer)-1)) > 0) {
buffer[rc] = 0;
input_buffer.insert(it, buffer);
}
- input_buffer.insert(it, "`file_pop\n");
+ input_buffer.insert(it, "\n`file_pop\n");
}
-std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs)
+std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs)
{
std::set<std::string> defines_with_args;
std::map<std::string, std::string> defines_map(pre_defines_map);
@@ -281,27 +288,28 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m
else
fn = fn.substr(0, pos) + fn.substr(pos+1);
}
- FILE *fp = fopen(fn.c_str(), "r");
- if (fp == NULL && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) {
+ std::ifstream ff;
+ ff.clear();
+ ff.open(fn.c_str());
+ if (ff.fail() && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) {
// if the include file was not found, it is not given with an absolute path, and the
// currently read file is given with a path, then try again relative to its directory
- std::string fn2 = filename.substr(0, filename.rfind('/')+1) + fn;
- fp = fopen(fn2.c_str(), "r");
+ ff.clear();
+ ff.open(filename.substr(0, filename.rfind('/')+1) + fn);
}
- if (fp == NULL && fn.size() > 0 && fn[0] != '/') {
+ if (ff.fail() && fn.size() > 0 && fn[0] != '/') {
// if the include file was not found and it is not given with an absolute path, then
// search it in the include path
for (auto incdir : include_dirs) {
- std::string fn2 = incdir + '/' + fn;
- fp = fopen(fn2.c_str(), "r");
- if (fp != NULL) break;
+ ff.clear();
+ ff.open(incdir + '/' + fn);
+ if (!ff.fail()) break;
}
}
- if (fp != NULL) {
- input_file(fp, fn);
- fclose(fp);
- } else
- output_code.push_back("`file_notfound " + fn + "\n");
+ if (ff.fail())
+ output_code.push_back("`file_notfound " + fn);
+ else
+ input_file(ff, fn);
continue;
}
@@ -310,12 +318,17 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m
std::map<std::string, int> args;
skip_spaces();
name = next_token(true);
+ bool here_doc_mode = false;
int newline_count = 0;
int state = 0;
if (skip_spaces() != "")
state = 3;
while (!tok.empty()) {
tok = next_token();
+ if (tok == "\"\"\"") {
+ here_doc_mode = !here_doc_mode;
+ continue;
+ }
if (state == 0 && tok == "(") {
state = 1;
skip_spaces();
@@ -332,9 +345,14 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m
if (state != 2)
state = 3;
if (tok == "\n") {
- return_char('\n');
- break;
- }
+ if (here_doc_mode) {
+ value += " ";
+ newline_count++;
+ } else {
+ return_char('\n');
+ break;
+ }
+ } else
if (tok == "\\") {
char ch = next_char();
if (ch == '\n') {
@@ -373,7 +391,6 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m
}
if (tok == "`timescale") {
- std::string name;
skip_spaces();
while (!tok.empty() && tok != "\n")
tok = next_token(true);
@@ -429,3 +446,5 @@ std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::m
return output;
}
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc
index c70d6f30..c6d4a0b7 100644
--- a/frontends/verilog/verilog_frontend.cc
+++ b/frontends/verilog/verilog_frontend.cc
@@ -27,13 +27,11 @@
*/
#include "verilog_frontend.h"
-#include "kernel/register.h"
-#include "kernel/log.h"
+#include "kernel/yosys.h"
#include "libs/sha1/sha1.h"
-#include <sstream>
#include <stdarg.h>
-#include <assert.h>
+YOSYS_NAMESPACE_BEGIN
using namespace VERILOG_FRONTEND;
// use the Verilog bison/flex parser to generate an AST and use AST::process() to convert it to RTLIL
@@ -47,11 +45,15 @@ struct VerilogFrontend : public Frontend {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" read_verilog [filename]\n");
+ log(" read_verilog [options] [filename]\n");
log("\n");
log("Load modules from a verilog file to the current design. A large subset of\n");
log("Verilog-2005 is supported.\n");
log("\n");
+ log(" -sv\n");
+ log(" enable support for SystemVerilog features. (only a small subset\n");
+ log(" of SystemVerilog is supported)\n");
+ log("\n");
log(" -dump_ast1\n");
log(" dump abstract syntax tree (before simplification)\n");
log("\n");
@@ -106,6 +108,11 @@ struct VerilogFrontend : public Frontend {
log(" ignore re-definitions of modules. (the default behavior is to\n");
log(" create an error message.)\n");
log("\n");
+ log(" -defer\n");
+ log(" only read the abstract syntax tree and defer actual compilation\n");
+ log(" to a later 'hierarchy' command. Useful in cases where the default\n");
+ log(" parameters of modules yield invalid or not synthesizable code.\n");
+ log("\n");
log(" -setattr <attribute_name>\n");
log(" set the specified attribute (to the value 1) on all loaded modules\n");
log("\n");
@@ -120,8 +127,13 @@ struct VerilogFrontend : public Frontend {
log("The command 'verilog_defaults' can be used to register default options for\n");
log("subsequent calls to 'read_verilog'.\n");
log("\n");
+ log("Note that the Verilog frontend does a pretty good job of processing valid\n");
+ log("verilog input, but has not very good error reporting. It generally is\n");
+ log("recommended to use a simulator (for example icarus verilog) for checking\n");
+ log("the syntax of the code, rather than to rely on read_verilog for that.\n");
+ log("\n");
}
- virtual void execute(FILE *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
{
bool flag_dump_ast1 = false;
bool flag_dump_ast2 = false;
@@ -135,10 +147,13 @@ struct VerilogFrontend : public Frontend {
bool flag_noopt = false;
bool flag_icells = false;
bool flag_ignore_redef = false;
+ bool flag_defer = false;
std::map<std::string, std::string> defines_map;
std::list<std::string> include_dirs;
std::list<std::string> attributes;
+
frontend_verilog_yydebug = false;
+ sv_mode = false;
log_header("Executing Verilog-2005 frontend.\n");
@@ -147,6 +162,10 @@ struct VerilogFrontend : public Frontend {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx];
+ if (arg == "-sv") {
+ sv_mode = true;
+ continue;
+ }
if (arg == "-dump_ast1") {
flag_dump_ast1 = true;
continue;
@@ -199,6 +218,10 @@ struct VerilogFrontend : public Frontend {
flag_ignore_redef = true;
continue;
}
+ if (arg == "-defer") {
+ flag_defer = true;
+ continue;
+ }
if (arg == "-setattr" && argidx+1 < args.size()) {
attributes.push_back(RTLIL::escape_id(args[++argidx]));
continue;
@@ -241,33 +264,34 @@ struct VerilogFrontend : public Frontend {
AST::get_line_num = &frontend_verilog_yyget_lineno;
current_ast = new AST::AstNode(AST::AST_DESIGN);
+ default_nettype_wire = true;
- FILE *fp = f;
+ lexin = f;
std::string code_after_preproc;
if (!flag_nopp) {
- code_after_preproc = frontend_verilog_preproc(f, filename, defines_map, include_dirs);
+ code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, include_dirs);
if (flag_ppdump)
log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str());
- fp = fmemopen((void*)code_after_preproc.c_str(), code_after_preproc.size(), "r");
+ lexin = new std::istringstream(code_after_preproc);
}
frontend_verilog_yyset_lineno(1);
- frontend_verilog_yyrestart(fp);
+ frontend_verilog_yyrestart(NULL);
frontend_verilog_yyparse();
frontend_verilog_yylex_destroy();
for (auto &child : current_ast->children) {
- log_assert(child->type == AST::AST_MODULE);
- for (auto &attr : attributes)
- if (child->attributes.count(attr) == 0)
- child->attributes[attr] = AST::AstNode::mkconst_int(1, false);
+ if (child->type == AST::AST_MODULE)
+ for (auto &attr : attributes)
+ if (child->attributes.count(attr) == 0)
+ child->attributes[attr] = AST::AstNode::mkconst_int(1, false);
}
- AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_ignore_redef);
+ AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_ignore_redef, flag_defer, default_nettype_wire);
if (!flag_nopp)
- fclose(fp);
+ delete lexin;
delete current_ast;
current_ast = NULL;
@@ -350,3 +374,5 @@ struct VerilogDefaults : public Pass {
}
} VerilogDefaults;
+YOSYS_NAMESPACE_END
+
diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h
index 8b4fae6e..af6495f8 100644
--- a/frontends/verilog/verilog_frontend.h
+++ b/frontends/verilog/verilog_frontend.h
@@ -29,12 +29,14 @@
#ifndef VERILOG_FRONTEND_H
#define VERILOG_FRONTEND_H
-#include "kernel/rtlil.h"
+#include "kernel/yosys.h"
#include "frontends/ast/ast.h"
#include <stdio.h>
#include <stdint.h>
#include <list>
+YOSYS_NAMESPACE_BEGIN
+
namespace VERILOG_FRONTEND
{
// this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser
@@ -42,10 +44,21 @@ namespace VERILOG_FRONTEND
// this function converts a Verilog constant to an AST_CONSTANT node
AST::AstNode *const2ast(std::string code, char case_type = 0);
+
+ // state of `default_nettype
+ extern bool default_nettype_wire;
+
+ // running in SystemVerilog mode
+ extern bool sv_mode;
+
+ // lexer input stream
+ extern std::istream *lexin;
}
// the pre-processor
-std::string frontend_verilog_preproc(FILE *f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs);
+std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map<std::string, std::string> pre_defines_map, const std::list<std::string> include_dirs);
+
+YOSYS_NAMESPACE_END
// the usual bison/flex stuff
extern int frontend_verilog_yydebug;
diff --git a/frontends/vhdl2verilog/Makefile.inc b/frontends/vhdl2verilog/Makefile.inc
new file mode 100644
index 00000000..003d89c4
--- /dev/null
+++ b/frontends/vhdl2verilog/Makefile.inc
@@ -0,0 +1 @@
+OBJS += frontends/vhdl2verilog/vhdl2verilog.o
diff --git a/frontends/vhdl2verilog/vhdl2verilog.cc b/frontends/vhdl2verilog/vhdl2verilog.cc
new file mode 100644
index 00000000..b408d621
--- /dev/null
+++ b/frontends/vhdl2verilog/vhdl2verilog.cc
@@ -0,0 +1,196 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/log.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include <limits.h>
+
+YOSYS_NAMESPACE_BEGIN
+
+struct Vhdl2verilogPass : public Pass {
+ Vhdl2verilogPass() : Pass("vhdl2verilog", "importing VHDL designs using vhdl2verilog") { }
+ virtual void help()
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" vhdl2verilog [options] <vhdl-file>..\n");
+ log("\n");
+ log("This command reads VHDL source files using the 'vhdl2verilog' tool and the\n");
+ log("Yosys Verilog frontend.\n");
+ log("\n");
+ log(" -out <out_file>\n");
+ log(" do not import the vhdl2verilog output. instead write it to the\n");
+ log(" specified file.\n");
+ log("\n");
+ log(" -vhdl2verilog_dir <directory>\n");
+ log(" do use the specified vhdl2verilog installation. this is the directory\n");
+ log(" that contains the setup_env.sh file. when this option is not present,\n");
+ log(" it is assumed that vhdl2verilog is in the PATH environment variable.\n");
+ log("\n");
+ log(" -top <top-entity-name>\n");
+ log(" The name of the top entity. This option is mandatory.\n");
+ log("\n");
+ log("The following options are passed as-is to vhdl2verilog:\n");
+ log("\n");
+ log(" -arch <architecture_name>\n");
+ log(" -unroll_generate\n");
+ log(" -nogenericeval\n");
+ log(" -nouniquify\n");
+ log(" -oldparser\n");
+ log(" -suppress <list>\n");
+ log(" -quiet\n");
+ log(" -nobanner\n");
+ log(" -mapfile <file>\n");
+ log("\n");
+ log("vhdl2verilog can be obtained from:\n");
+ log("http://www.edautils.com/vhdl2verilog.html\n");
+ log("\n");
+ }
+ virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header("Executing VHDL2VERILOG (importing VHDL designs using vhdl2verilog).\n");
+ log_push();
+
+ std::string out_file, top_entity;
+ std::string vhdl2verilog_dir;
+ std::string extra_opts;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-out" && argidx+1 < args.size()) {
+ out_file = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ top_entity = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-vhdl2verilog_dir" && argidx+1 < args.size()) {
+ vhdl2verilog_dir = args[++argidx];
+ continue;
+ }
+ if ((args[argidx] == "-arch" || args[argidx] == "-suppress" || args[argidx] == "-mapfile") && argidx+1 < args.size()) {
+ if (args[argidx] == "-mapfile" && !args[argidx+1].empty() && args[argidx+1][0] != '/') {
+ char pwd[PATH_MAX];
+ if (!getcwd(pwd, sizeof(pwd))) {
+ log_cmd_error("getcwd failed: %s", strerror(errno));
+ log_abort();
+ }
+ args[argidx+1] = pwd + ("/" + args[argidx+1]);
+ }
+ extra_opts += std::string(" ") + args[argidx];
+ extra_opts += std::string(" '") + args[++argidx] + std::string("'");
+ continue;
+ }
+ if (args[argidx] == "-unroll_generate" || args[argidx] == "-nogenericeval" || args[argidx] == "-nouniquify" ||
+ args[argidx] == "-oldparser" || args[argidx] == "-quiet" || args[argidx] == "-nobanner") {
+ extra_opts += std::string(" ") + args[argidx];
+ continue;
+ }
+ break;
+ }
+
+ if (argidx == args.size())
+ cmd_error(args, argidx, "Missing filenames.");
+ if (args[argidx].substr(0, 1) == "-")
+ cmd_error(args, argidx, "Unknown option.");
+ if (top_entity.empty())
+ log_cmd_error("Missing -top option.\n");
+
+ char tempdir_name[] = "/tmp/yosys-vhdl2verilog-XXXXXX";
+ char *p = mkdtemp(tempdir_name);
+ log("Using temp directory %s.\n", tempdir_name);
+ if (p == NULL)
+ log_error("For some reason mkdtemp() failed!\n");
+
+ if (!out_file.empty() && out_file[0] != '/') {
+ char pwd[PATH_MAX];
+ if (!getcwd(pwd, sizeof(pwd))) {
+ log_cmd_error("getcwd failed: %s", strerror(errno));
+ log_abort();
+ }
+ out_file = pwd + ("/" + out_file);
+ }
+
+ FILE *f = fopen(stringf("%s/files.list", tempdir_name).c_str(), "wt");
+ while (argidx < args.size()) {
+ std::string file = args[argidx++];
+ if (file.empty())
+ continue;
+ if (file[0] != '/') {
+ char pwd[PATH_MAX];
+ if (!getcwd(pwd, sizeof(pwd))) {
+ log_cmd_error("getcwd failed: %s", strerror(errno));
+ log_abort();
+ }
+ file = pwd + ("/" + file);
+ }
+ fprintf(f, "%s\n", file.c_str());
+ log("Adding '%s' to the file list.\n", file.c_str());
+ }
+ fclose(f);
+
+ std::string command = "exec 2>&1; ";
+ if (!vhdl2verilog_dir.empty())
+ command += stringf("cd '%s'; . ./setup_env.sh; ", vhdl2verilog_dir.c_str());
+ command += stringf("cd '%s'; vhdl2verilog -out '%s' -filelist files.list -top '%s'%s", tempdir_name,
+ out_file.empty() ? "vhdl2verilog_output.v" : out_file.c_str(), top_entity.c_str(), extra_opts.c_str());
+
+ log("Running '%s'..\n", command.c_str());
+
+ errno = ENOMEM; // popen does not set errno if memory allocation fails, therefore set it by hand
+ f = popen(command.c_str(), "r");
+ if (f == NULL)
+ log_error("Opening pipe to `%s' for reading failed: %s\n", command.c_str(), strerror(errno));
+
+ char logbuf[1024];
+ while (fgets(logbuf, 1024, f) != NULL)
+ log("%s", logbuf);
+
+ int ret = pclose(f);
+ if (ret < 0)
+ log_error("Closing pipe to `%s' failed: %s\n", command.c_str(), strerror(errno));
+ if (WEXITSTATUS(ret) != 0)
+ log_error("Execution of command \"%s\" failed: the shell returned %d\n", command.c_str(), WEXITSTATUS(ret));
+
+ if (out_file.empty()) {
+ std::ifstream ff;
+ ff.open(stringf("%s/vhdl2verilog_output.v", tempdir_name).c_str());
+ if (ff.fail())
+ log_error("Can't open vhdl2verilog output file `vhdl2verilog_output.v'.\n");
+ Frontend::frontend_call(design, &ff, stringf("%s/vhdl2verilog_output.v", tempdir_name), "verilog");
+ }
+
+ log_header("Removing temp directory `%s':\n", tempdir_name);
+ if (system(stringf("rm -rf '%s'", tempdir_name).c_str()) != 0)
+ log_error("Execution of \"rm -rf '%s'\" failed!\n", tempdir_name);
+
+ log_pop();
+ }
+} Vhdl2verilogPass;
+
+YOSYS_NAMESPACE_END
+