aboutsummaryrefslogtreecommitdiffstats
path: root/frontends
diff options
context:
space:
mode:
authorPepijn de Vos <pepijndevos@gmail.com>2019-10-21 10:51:34 +0200
committerPepijn de Vos <pepijndevos@gmail.com>2019-10-21 10:51:34 +0200
commit69fb3b8db21c8a50fa333bff3ef844af42729e0d (patch)
tree1a62aebe9ece22b19b4087f2c5cb5581b571c270 /frontends
parent72323e11a4ee222c0ce928669d33333c46fb25aa (diff)
parentfa989e59e5a37d804d8a82050e022b8f4b7070d8 (diff)
downloadyosys-69fb3b8db21c8a50fa333bff3ef844af42729e0d.tar.gz
yosys-69fb3b8db21c8a50fa333bff3ef844af42729e0d.tar.bz2
yosys-69fb3b8db21c8a50fa333bff3ef844af42729e0d.zip
Merge branch 'master' of https://github.com/YosysHQ/yosys into gowin
Diffstat (limited to 'frontends')
-rw-r--r--frontends/aiger/aigerparse.cc61
-rw-r--r--frontends/ast/ast.cc147
-rw-r--r--frontends/ast/ast.h10
-rw-r--r--frontends/ast/genrtlil.cc1
-rw-r--r--frontends/ast/simplify.cc152
-rw-r--r--frontends/blif/blifparse.cc8
-rw-r--r--frontends/rpc/Makefile.inc2
-rw-r--r--frontends/rpc/rpc_frontend.cc595
-rw-r--r--frontends/verilog/const2ast.cc10
-rw-r--r--frontends/verilog/verilog_lexer.l2
-rw-r--r--frontends/verilog/verilog_parser.y80
11 files changed, 955 insertions, 113 deletions
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index 2e1fb8fad..cf060193d 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -285,6 +285,8 @@ end_of_header:
}
else if (c == 'c') {
f.ignore(1);
+ if (f.peek() == '\r')
+ f.ignore(1);
if (f.peek() == '\n')
break;
// Else constraint (TODO)
@@ -430,6 +432,7 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
else if (c == 'r') {
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
flopNum = parse_xaiger_literal(f);
+ log_debug("flopNum: %u\n", flopNum);
log_assert(dataSize == (flopNum+1) * sizeof(uint32_t));
f.ignore(flopNum * sizeof(uint32_t));
}
@@ -496,8 +499,7 @@ void AigerReader::parse_aiger_ascii()
// Parse latches
RTLIL::Wire *clk_wire = nullptr;
- if (L > 0) {
- log_assert(clk_name != "");
+ if (L > 0 && !clk_name.empty()) {
clk_wire = module->wire(clk_name);
log_assert(!clk_wire);
log_debug2("Creating %s\n", clk_name.c_str());
@@ -513,7 +515,10 @@ void AigerReader::parse_aiger_ascii()
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
- module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire);
+ if (clk_wire)
+ module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire);
+ else
+ module->addFfGate(NEW_ID, d_wire, q_wire);
// Reset logic is optional in AIGER 1.9
if (f.peek() == ' ') {
@@ -621,8 +626,7 @@ void AigerReader::parse_aiger_binary()
// Parse latches
RTLIL::Wire *clk_wire = nullptr;
- if (L > 0) {
- log_assert(clk_name != "");
+ if (L > 0 && !clk_name.empty()) {
clk_wire = module->wire(clk_name);
log_assert(!clk_wire);
log_debug2("Creating %s\n", clk_name.c_str());
@@ -638,7 +642,10 @@ void AigerReader::parse_aiger_binary()
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
- module->addDff(NEW_ID, clk_wire, d_wire, q_wire);
+ if (clk_wire)
+ module->addDff(NEW_ID, clk_wire, d_wire, q_wire);
+ else
+ module->addFf(NEW_ID, d_wire, q_wire);
// Reset logic is optional in AIGER 1.9
if (f.peek() == ' ') {
@@ -733,22 +740,22 @@ void AigerReader::post_process()
log_assert(box_module);
if (seen_boxes.insert(cell->type).second) {
- auto it = box_module->attributes.find("\\abc_carry");
+ auto it = box_module->attributes.find("\\abc9_carry");
if (it != box_module->attributes.end()) {
RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr;
auto carry_in_out = it->second.decode_string();
auto pos = carry_in_out.find(',');
if (pos == std::string::npos)
- log_error("'abc_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type));
+ log_error("'abc9_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type));
auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos));
carry_in = box_module->wire(carry_in_name);
if (!carry_in || !carry_in->port_input)
- log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str());
+ log_error("'abc9_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str());
auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1));
carry_out = box_module->wire(carry_out_name);
if (!carry_out || !carry_out->port_output)
- log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str());
+ log_error("'abc9_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str());
auto &ports = box_module->ports;
for (auto jt = ports.begin(); jt != ports.end(); ) {
@@ -776,19 +783,19 @@ void AigerReader::post_process()
// NB: Assume box_module->ports are sorted alphabetically
// (as RTLIL::Module::fixup_ports() would do)
for (auto port_name : box_module->ports) {
- RTLIL::Wire* w = box_module->wire(port_name);
- log_assert(w);
+ RTLIL::Wire* port = box_module->wire(port_name);
+ log_assert(port);
RTLIL::SigSpec rhs;
- RTLIL::Wire* wire = nullptr;
- for (int i = 0; i < GetSize(w); i++) {
- if (w->port_input) {
+ for (int i = 0; i < GetSize(port); i++) {
+ RTLIL::Wire* wire = nullptr;
+ if (port->port_input) {
log_assert(co_count < outputs.size());
wire = outputs[co_count++];
log_assert(wire);
log_assert(wire->port_output);
wire->port_output = false;
}
- if (w->port_output) {
+ if (port->port_output) {
log_assert((piNum + ci_count) < inputs.size());
wire = inputs[piNum + ci_count++];
log_assert(wire);
@@ -797,6 +804,7 @@ void AigerReader::post_process()
}
rhs.append(wire);
}
+
cell->setPort(port_name, rhs);
}
}
@@ -814,6 +822,7 @@ void AigerReader::post_process()
RTLIL::Wire* wire = inputs[variable];
log_assert(wire);
log_assert(wire->port_input);
+ log_debug("Renaming input %s", log_id(wire));
if (index == 0) {
// Cope with the fact that a CI might be identical
@@ -840,6 +849,7 @@ void AigerReader::post_process()
wire->port_input = false;
}
}
+ log_debug(" -> %s\n", log_id(wire));
}
else if (type == "output") {
log_assert(static_cast<unsigned>(variable + co_count) < outputs.size());
@@ -850,6 +860,7 @@ void AigerReader::post_process()
wire->port_output = false;
continue;
}
+ log_debug("Renaming output %s", log_id(wire));
if (index == 0) {
// Cope with the fact that a CO might be identical
@@ -859,7 +870,7 @@ void AigerReader::post_process()
if (!existing) {
if (escaped_s.ends_with("$inout.out")) {
wire->port_output = false;
- RTLIL::Wire *in_wire = module->wire(escaped_s.substr(0, escaped_s.size()-10));
+ RTLIL::Wire *in_wire = module->wire(escaped_s.substr(1, escaped_s.size()-11));
log_assert(in_wire);
log_assert(in_wire->port_input && !in_wire->port_output);
in_wire->port_output = true;
@@ -871,6 +882,7 @@ void AigerReader::post_process()
else {
wire->port_output = false;
module->connect(wire, existing);
+ wire = existing;
}
}
else if (index > 0) {
@@ -879,7 +891,7 @@ void AigerReader::post_process()
if (!existing) {
if (escaped_s.ends_with("$inout.out")) {
wire->port_output = false;
- RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(0, escaped_s.size()-10).c_str(), index));
+ RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(1, escaped_s.size()-11).c_str(), index));
log_assert(in_wire);
log_assert(in_wire->port_input && !in_wire->port_output);
in_wire->port_output = true;
@@ -896,6 +908,7 @@ void AigerReader::post_process()
wire->port_output = false;
}
}
+ log_debug(" -> %s\n", log_id(wire));
}
else if (type == "box") {
RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable));
@@ -974,7 +987,7 @@ void AigerReader::post_process()
// operate (and run checks on) this one module
RTLIL::Design *mapped_design = new RTLIL::Design;
mapped_design->add(module);
- Pass::call(mapped_design, "clean -purge");
+ Pass::call(mapped_design, "clean");
mapped_design->modules_.erase(module->name);
delete mapped_design;
@@ -1004,8 +1017,8 @@ struct AigerFrontend : public Frontend {
log(" Name of module to be created (default: <filename>)\n");
log("\n");
log(" -clk_name <wire_name>\n");
- log(" AIGER latches to be transformed into posedge DFFs clocked by wire of");
- log(" this name (default: clk)\n");
+ log(" If specified, AIGER latches to be transformed into $_DFF_P_ cells\n");
+ log(" clocked by wire of this name. Otherwise, $_FF_ cells will be used.\n");
log("\n");
log(" -map <filename>\n");
log(" read file with port and latch symbols\n");
@@ -1045,13 +1058,15 @@ struct AigerFrontend : public Frontend {
}
break;
}
- extra_args(f, filename, args, argidx);
+ extra_args(f, filename, args, argidx, true);
if (module_name.empty()) {
#ifdef _WIN32
char fname[_MAX_FNAME];
_splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */);
- module_name = fname;
+ char* bn = strdup(fname);
+ module_name = RTLIL::escape_id(bn);
+ free(bn);
#else
char* bn = strdup(filename.c_str());
module_name = RTLIL::escape_id(bn);
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 82283fb5b..5bbea0faf 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -158,7 +158,14 @@ std::string AST::type2str(AstNodeType type)
X(AST_POSEDGE)
X(AST_NEGEDGE)
X(AST_EDGE)
+ X(AST_INTERFACE)
+ X(AST_INTERFACEPORT)
+ X(AST_INTERFACEPORTTYPE)
+ X(AST_MODPORT)
+ X(AST_MODPORTMEMBER)
X(AST_PACKAGE)
+ X(AST_WIRETYPE)
+ X(AST_TYPEDEF)
#undef X
default:
log_abort();
@@ -201,6 +208,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
was_checked = false;
range_valid = false;
range_swapped = false;
+ is_custom_type = false;
port_id = 0;
range_left = -1;
range_right = 0;
@@ -1099,6 +1107,13 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
ignoreThisSignalsInInitial = RTLIL::SigSpec();
}
+ else {
+ for (auto &attr : ast->attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ continue;
+ current_module->attributes[attr.first] = attr.second->asAttrConst();
+ }
+ }
if (ast->type == AST_INTERFACE)
current_module->set_bool_attribute("\\is_interface");
@@ -1284,6 +1299,8 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule
// from AST. The interface members are copied into the AST module with the prefix of the interface.
void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces)
{
+ loadconfig();
+
bool is_top = false;
AstNode *new_ast = ast->clone();
for (auto &intf : local_interfaces) {
@@ -1368,10 +1385,10 @@ void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RT
// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
// This method is used to explode the interface when the interface is a port of the module (not instantiated inside)
-RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail)
+RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool /*mayfail*/)
{
AstNode *new_ast = NULL;
- std::string modname = derive_common(design, parameters, &new_ast, mayfail);
+ std::string modname = derive_common(design, parameters, &new_ast);
// Since interfaces themselves may be instantiated with different parameters,
// "modname" must also take those into account, so that unique modules
@@ -1384,11 +1401,17 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
has_interfaces = true;
}
+ std::string new_modname = modname;
if (has_interfaces)
- modname += "$interfaces$" + interf_info;
+ new_modname += "$interfaces$" + interf_info;
- if (!design->has(modname)) {
+ if (!design->has(new_modname)) {
+ if (!new_ast) {
+ auto mod = dynamic_cast<AstModule*>(design->module(modname));
+ new_ast = mod->ast->clone();
+ }
+ modname = new_modname;
new_ast->str = modname;
// Iterate over all interfaces which are ports in this module:
@@ -1441,10 +1464,10 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
}
// create a new parametric module (when needed) and return the name of the generated module - without support for interfaces
-RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail)
+RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool /*mayfail*/)
{
AstNode *new_ast = NULL;
- std::string modname = derive_common(design, parameters, &new_ast, mayfail);
+ std::string modname = derive_common(design, parameters, &new_ast);
if (!design->has(modname)) {
new_ast->str = modname;
@@ -1459,64 +1482,75 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
}
// create a new parametric module (when needed) and return the name of the generated module
-std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool)
+std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out)
{
std::string stripped_name = name.str();
if (stripped_name.compare(0, 9, "$abstract") == 0)
stripped_name = stripped_name.substr(9);
- log_header(design, "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;
- flag_dump_ast2 = false;
- flag_dump_vlog1 = false;
- flag_dump_vlog2 = false;
- flag_nolatches = nolatches;
- flag_nomeminit = nomeminit;
- flag_nomem2reg = nomem2reg;
- flag_mem2reg = mem2reg;
- flag_noblackbox = noblackbox;
- flag_lib = lib;
- flag_nowb = nowb;
- flag_noopt = noopt;
- flag_icells = icells;
- flag_pwires = pwires;
- flag_autowire = autowire;
- use_internal_line_num();
-
std::string para_info;
- 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;
+ for (const auto child : ast->children) {
if (child->type != AST_PARAMETER)
continue;
para_counter++;
std::string para_id = child->str;
if (parameters.count(para_id) > 0) {
log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str])));
- rewrite_parameter:
para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
- delete child->children.at(0);
- if ((parameters[para_id].flags & RTLIL::CONST_FLAG_REAL) != 0) {
- child->children[0] = new AstNode(AST_REALVALUE);
- child->children[0]->realvalue = std::stod(parameters[para_id].decode_string());
- } else if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0)
- child->children[0] = AstNode::mkconst_str(parameters[para_id].decode_string());
- else
- child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0);
- parameters.erase(para_id);
continue;
}
para_id = stringf("$%d", para_counter);
if (parameters.count(para_id) > 0) {
log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
+ para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
+ continue;
+ }
+ }
+
+ std::string modname;
+ if (parameters.size() == 0)
+ modname = stripped_name;
+ else if (para_info.size() > 60)
+ modname = "$paramod$" + sha1(para_info) + stripped_name;
+ else
+ modname = "$paramod" + stripped_name + para_info;
+
+ if (design->has(modname))
+ return modname;
+
+ log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str());
+ loadconfig();
+
+ AstNode *new_ast = ast->clone();
+ para_counter = 0;
+ for (auto child : new_ast->children) {
+ if (child->type != AST_PARAMETER)
+ continue;
+ para_counter++;
+ std::string para_id = child->str;
+ if (parameters.count(para_id) > 0) {
+ log("Parameter %s = %s\n", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[child->str])));
goto rewrite_parameter;
}
+ para_id = stringf("$%d", para_counter);
+ if (parameters.count(para_id) > 0) {
+ log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
+ goto rewrite_parameter;
+ }
+ continue;
+ rewrite_parameter:
+ delete child->children.at(0);
+ if ((parameters[para_id].flags & RTLIL::CONST_FLAG_REAL) != 0) {
+ child->children[0] = new AstNode(AST_REALVALUE);
+ child->children[0]->realvalue = std::stod(parameters[para_id].decode_string());
+ } else if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0)
+ child->children[0] = AstNode::mkconst_str(parameters[para_id].decode_string());
+ else
+ child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0);
+ parameters.erase(para_id);
}
for (auto param : parameters) {
@@ -1529,16 +1563,6 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
new_ast->children.push_back(defparam);
}
- std::string modname;
-
- if (orig_parameters_n == 0)
- modname = stripped_name;
- else if (para_info.size() > 60)
- modname = "$paramod$" + sha1(para_info) + stripped_name;
- else
- modname = "$paramod" + stripped_name + para_info;
-
-
(*new_ast_out) = new_ast;
return modname;
}
@@ -1565,6 +1589,27 @@ RTLIL::Module *AstModule::clone() const
return new_mod;
}
+void AstModule::loadconfig() const
+{
+ current_ast = NULL;
+ flag_dump_ast1 = false;
+ flag_dump_ast2 = false;
+ flag_dump_vlog1 = false;
+ flag_dump_vlog2 = false;
+ flag_nolatches = nolatches;
+ flag_nomeminit = nomeminit;
+ flag_nomem2reg = nomem2reg;
+ flag_mem2reg = mem2reg;
+ flag_noblackbox = noblackbox;
+ flag_lib = lib;
+ flag_nowb = nowb;
+ flag_noopt = noopt;
+ flag_icells = icells;
+ flag_pwires = pwires;
+ flag_autowire = autowire;
+ use_internal_line_num();
+}
+
// internal dummy line number callbacks
namespace {
int internal_line_num;
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 54b2fb319..918d178c7 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -148,7 +148,10 @@ namespace AST
AST_INTERFACEPORTTYPE,
AST_MODPORT,
AST_MODPORTMEMBER,
- AST_PACKAGE
+ AST_PACKAGE,
+
+ AST_WIRETYPE,
+ AST_TYPEDEF
};
// convert an node type to a string (e.g. for debug output)
@@ -174,7 +177,7 @@ 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_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized;
+ bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized, is_custom_type;
int port_id, range_left, range_right;
uint32_t integer;
double realvalue;
@@ -296,9 +299,10 @@ namespace AST
~AstModule() YS_OVERRIDE;
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE;
- std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail);
+ std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out);
void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE;
RTLIL::Module *clone() const YS_OVERRIDE;
+ void loadconfig() const;
};
// this must be set by the language frontend before parsing the sources
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index 407a34472..94f5c0a04 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -863,6 +863,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_PACKAGE:
case AST_MODPORT:
case AST_MODPORTMEMBER:
+ case AST_TYPEDEF:
break;
case AST_INTERFACEPORT: {
// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 86dd80c65..44fd32cdc 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -318,7 +318,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
- if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX)
+ if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF)
const_fold = true;
if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM))
const_fold = true;
@@ -336,6 +336,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
std::map<std::string, AstNode*> this_wire_scope;
for (size_t i = 0; i < children.size(); i++) {
AstNode *node = children[i];
+
if (node->type == AST_WIRE) {
if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
for (auto c : node->children[0]->children) {
@@ -405,14 +406,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
this_wire_scope[node->str] = node;
}
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_DPI_FUNCTION || 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 ||
+ node->type == AST_TYPEDEF) {
backup_scope[node->str] = current_scope[node->str];
current_scope[node->str] = node;
}
}
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 || node->type == AST_MEMORY)
+ if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY || node->type == AST_TYPEDEF)
while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM))
did_something = true;
}
@@ -780,6 +782,99 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
delete_children();
}
+ // resolve typedefs
+ if (type == AST_TYPEDEF) {
+ log_assert(children.size() == 1);
+ log_assert(children[0]->type == AST_WIRE || children[0]->type == AST_MEMORY);
+ while(children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param))
+ did_something = true;
+ log_assert(!children[0]->is_custom_type);
+ }
+
+ // resolve types of wires
+ if (type == AST_WIRE || type == AST_MEMORY) {
+ if (is_custom_type) {
+ log_assert(children.size() >= 1);
+ log_assert(children[0]->type == AST_WIRETYPE);
+ if (!current_scope.count(children[0]->str))
+ log_file_error(filename, linenum, "Unknown identifier `%s' used as type name\n", children[0]->str.c_str());
+ AstNode *resolved_type = current_scope.at(children[0]->str);
+ if (resolved_type->type != AST_TYPEDEF)
+ log_file_error(filename, linenum, "`%s' does not name a type\n", children[0]->str.c_str());
+ log_assert(resolved_type->children.size() == 1);
+ AstNode *templ = resolved_type->children[0];
+ // Remove type reference
+ delete children[0];
+ children.erase(children.begin());
+
+ // Ensure typedef itself is fully simplified
+ while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+
+ if (type == AST_WIRE)
+ type = templ->type;
+ is_reg = templ->is_reg;
+ is_logic = templ->is_logic;
+ is_signed = templ->is_signed;
+ is_string = templ->is_string;
+ is_custom_type = templ->is_custom_type;
+
+ range_valid = templ->range_valid;
+ range_swapped = templ->range_swapped;
+ range_left = templ->range_left;
+ range_right = templ->range_right;
+
+ // Insert clones children from template at beginning
+ for (int i = 0; i < GetSize(templ->children); i++)
+ children.insert(children.begin() + i, templ->children[i]->clone());
+
+ if (type == AST_MEMORY && GetSize(children) == 1) {
+ // Single-bit memories must have [0:0] range
+ AstNode *rng = new AstNode(AST_RANGE);
+ rng->children.push_back(AstNode::mkconst_int(0, true));
+ rng->children.push_back(AstNode::mkconst_int(0, true));
+ children.insert(children.begin(), rng);
+ }
+
+ did_something = true;
+ }
+ log_assert(!is_custom_type);
+ }
+
+ // resolve types of parameters
+ if (type == AST_LOCALPARAM || type == AST_PARAMETER) {
+ if (is_custom_type) {
+ log_assert(children.size() == 2);
+ log_assert(children[1]->type == AST_WIRETYPE);
+ if (!current_scope.count(children[1]->str))
+ log_file_error(filename, linenum, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str());
+ AstNode *resolved_type = current_scope.at(children[1]->str);
+ if (resolved_type->type != AST_TYPEDEF)
+ log_file_error(filename, linenum, "`%s' does not name a type\n", children[1]->str.c_str());
+ log_assert(resolved_type->children.size() == 1);
+ AstNode *templ = resolved_type->children[0];
+ delete children[1];
+ children.pop_back();
+
+ // Ensure typedef itself is fully simplified
+ while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+
+ if (templ->type == AST_MEMORY)
+ log_file_error(filename, linenum, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str());
+ is_signed = templ->is_signed;
+ is_string = templ->is_string;
+ is_custom_type = templ->is_custom_type;
+
+ range_valid = templ->range_valid;
+ range_swapped = templ->range_swapped;
+ range_left = templ->range_left;
+ range_right = templ->range_right;
+ for (auto template_child : templ->children)
+ children.push_back(template_child->clone());
+ did_something = true;
+ }
+ log_assert(!is_custom_type);
+ }
+
// resolve constant prefixes
if (type == AST_PREFIX) {
if (children[0]->type != AST_CONSTANT) {
@@ -1194,7 +1289,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_BLOCK && str.empty())
{
for (size_t i = 0; i < children.size(); i++)
- if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM)
+ if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF)
log_file_error(children[i]->filename, children[i]->linenum, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n");
}
@@ -1206,7 +1301,7 @@ 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]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) {
+ if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) {
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];
@@ -1530,10 +1625,16 @@ skip_dynamic_range_lvalue_expansion:;
current_scope[wire_en->str] = wire_en;
while (wire_en->simplify(true, false, false, 1, -1, false, false)) { }
- std::vector<RTLIL::State> x_bit;
- x_bit.push_back(RTLIL::State::Sx);
+ AstNode *check_defval;
+ if (type == AST_LIVE || type == AST_FAIR) {
+ check_defval = new AstNode(AST_REDUCE_BOOL, children[0]->clone());
+ } else {
+ std::vector<RTLIL::State> x_bit;
+ x_bit.push_back(RTLIL::State::Sx);
+ check_defval = mkconst_bits(x_bit, false);
+ }
- AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bit, false));
+ AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), check_defval);
assign_check->children[0]->str = id_check;
assign_check->children[0]->was_checked = true;
@@ -1546,9 +1647,13 @@ skip_dynamic_range_lvalue_expansion:;
default_signals->children.push_back(assign_en);
current_top_block->children.insert(current_top_block->children.begin(), default_signals);
- assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone()));
- assign_check->children[0]->str = id_check;
- assign_check->children[0]->was_checked = true;
+ if (type == AST_LIVE || type == AST_FAIR) {
+ assign_check = nullptr;
+ } else {
+ assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone()));
+ assign_check->children[0]->str = id_check;
+ assign_check->children[0]->was_checked = true;
+ }
if (current_always == nullptr || current_always->type != AST_INITIAL) {
assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1));
@@ -1560,7 +1665,8 @@ skip_dynamic_range_lvalue_expansion:;
assign_en->children[0]->was_checked = true;
newNode = new AstNode(AST_BLOCK);
- newNode->children.push_back(assign_check);
+ if (assign_check != nullptr)
+ newNode->children.push_back(assign_check);
newNode->children.push_back(assign_en);
AstNode *assertnode = new AstNode(type);
@@ -2884,11 +2990,18 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map)
{
if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) {
- current_scope[index_var]->children[0]->cloneInto(this);
- return;
+ if (children.empty()) {
+ current_scope[index_var]->children[0]->cloneInto(this);
+ } else {
+ AstNode *p = new AstNode(AST_LOCALPARAM, current_scope[index_var]->children[0]->clone());
+ p->str = stringf("$genval$%d", autoidx++);
+ current_ast_mod->children.push_back(p);
+ str = p->str;
+ id2ast = p;
+ }
}
- if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL) && name_map.count(str) > 0)
+ if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) && name_map.count(str) > 0)
str = name_map[str];
std::map<std::string, std::string> backup_name_map;
@@ -2896,7 +3009,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
for (size_t i = 0; i < children.size(); i++) {
AstNode *child = children[i];
if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM ||
- child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL) {
+ child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF) {
if (backup_name_map.size() == 0)
backup_name_map = name_map;
std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
@@ -2927,6 +3040,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
child->expand_genblock(index_var, prefix, name_map);
}
+
if (backup_name_map.size() > 0)
name_map.swap(backup_name_map);
}
@@ -2980,6 +3094,9 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
uint32_t children_flags = 0;
int lhs_children_counter = 0;
+ if (type == AST_TYPEDEF)
+ return; // don't touch content of typedefs
+
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
@@ -3137,6 +3254,9 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
if (type == AST_FUNCTION || type == AST_TASK)
return false;
+ if (type == AST_TYPEDEF)
+ return false;
+
if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast))
{
log_assert(children[0]->type == AST_CONSTANT);
diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc
index d17cacf29..cab210605 100644
--- a/frontends/blif/blifparse.cc
+++ b/frontends/blif/blifparse.cc
@@ -174,6 +174,12 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
if (module == nullptr)
goto error;
+ if (!strcmp(cmd, ".blackbox"))
+ {
+ module->attributes["\\blackbox"] = RTLIL::Const(1);
+ continue;
+ }
+
if (!strcmp(cmd, ".end"))
{
for (auto &wp : wideports_cache)
@@ -280,7 +286,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool
goto error_with_reason;
}
- module->rename(lastcell, p);
+ module->rename(lastcell, RTLIL::escape_id(p));
continue;
}
diff --git a/frontends/rpc/Makefile.inc b/frontends/rpc/Makefile.inc
new file mode 100644
index 000000000..9af505098
--- /dev/null
+++ b/frontends/rpc/Makefile.inc
@@ -0,0 +1,2 @@
+
+OBJS += frontends/rpc/rpc_frontend.o
diff --git a/frontends/rpc/rpc_frontend.cc b/frontends/rpc/rpc_frontend.cc
new file mode 100644
index 000000000..add17c243
--- /dev/null
+++ b/frontends/rpc/rpc_frontend.cc
@@ -0,0 +1,595 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2019 whitequark <whitequark@whitequark.org>
+ *
+ * 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.
+ *
+ */
+
+// The reason the -path mode of connect_rpc uses byte-oriented and not message-oriented sockets, even though
+// it is a message-oriented interface, is that the system can place various limits on the message size, which
+// are not always transparent or easy to change. Given that generated HDL code get be extremely large, it is
+// unwise to rely on those limits being large enough, and using byte-oriented sockets is guaranteed to work.
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <spawn.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+extern char **environ;
+#endif
+
+#include "libs/json11/json11.hpp"
+#include "libs/sha1/sha1.h"
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+#if defined(_WIN32)
+static std::wstring str2wstr(const std::string &in) {
+ if(in == "") return L"";
+ std::wstring out;
+ out.resize(MultiByteToWideChar(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpMultiByteStr=*/&in[0], /*cbMultiByte=*/(int)in.length(), /*lpWideCharStr=*/NULL, /*cchWideChar=*/0));
+ int written = MultiByteToWideChar(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpMultiByteStr=*/&in[0], /*cbMultiByte=*/(int)in.length(), /*lpWideCharStr=*/&out[0], /*cchWideChar=*/(int)out.length());
+ log_assert(written == (int)out.length());
+ return out;
+}
+
+static std::string wstr2str(const std::wstring &in) {
+ if(in == L"") return "";
+ std::string out;
+ out.resize(WideCharToMultiByte(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpWideCharStr=*/&in[0], /*cchWideChar=*/(int)in.length(), /*lpMultiByteStr=*/NULL, /*cbMultiByte=*/0, /*lpDefaultChar=*/NULL, /*lpUsedDefaultChar=*/NULL));
+ int written = WideCharToMultiByte(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpWideCharStr=*/&in[0], /*cchWideChar=*/(int)in.length(), /*lpMultiByteStr=*/&out[0], /*cbMultiByte=*/(int)out.length(), /*lpDefaultChar=*/NULL, /*lpUsedDefaultChar=*/NULL);
+ log_assert(written == (int)out.length());
+ return out;
+}
+
+static std::string get_last_error_str() {
+ DWORD last_error = GetLastError();
+ LPWSTR out_w;
+ DWORD size_w = FormatMessageW(/*dwFlags=*/FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS, /*lpSource=*/NULL, /*dwMessageId=*/last_error, /*dwLanguageId=*/0, /*lpBuffer=*/(LPWSTR)&out_w, /*nSize=*/0, /*Arguments=*/NULL);
+ if (size_w == 0)
+ return std::to_string(last_error);
+ std::string out = wstr2str(std::wstring(out_w, size_w));
+ LocalFree(out_w);
+ return out;
+}
+#endif
+
+using json11::Json;
+
+struct RpcServer {
+ std::string name;
+
+ RpcServer(const std::string &name) : name(name) { }
+ virtual ~RpcServer() { }
+
+ virtual void write(const std::string &data) = 0;
+ virtual std::string read() = 0;
+
+ Json call(const Json &json_request) {
+ std::string request;
+ json_request.dump(request);
+ request += '\n';
+ log_debug("RPC frontend request: %s", request.c_str());
+ write(request);
+
+ std::string response = read();
+ log_debug("RPC frontend response: %s", response.c_str());
+ std::string error;
+ Json json_response = Json::parse(response, error);
+ if (json_response.is_null())
+ log_cmd_error("parsing JSON failed: %s\n", error.c_str());
+ if (json_response["error"].is_string())
+ log_cmd_error("RPC frontend returned an error: %s\n", json_response["error"].string_value().c_str());
+ return json_response;
+ }
+
+ std::vector<std::string> get_module_names() {
+ Json response = call(Json::object {
+ { "method", "modules" },
+ });
+ bool is_valid = true;
+ std::vector<std::string> modules;
+ if (response["modules"].is_array()) {
+ for (auto &json_module : response["modules"].array_items()) {
+ if (json_module.is_string())
+ modules.push_back(json_module.string_value());
+ else is_valid = false;
+ }
+ } else is_valid = false;
+ if (!is_valid)
+ log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str());
+ return modules;
+ }
+
+ std::pair<std::string, std::string> derive_module(const std::string &module, const dict<RTLIL::IdString, RTLIL::Const> &parameters) {
+ Json::object json_parameters;
+ for (auto &param : parameters) {
+ std::string type, value;
+ if (param.second.flags & RTLIL::CONST_FLAG_REAL) {
+ type = "real";
+ value = param.second.decode_string();
+ } else if (param.second.flags & RTLIL::CONST_FLAG_STRING) {
+ type = "string";
+ value = param.second.decode_string();
+ } else if ((param.second.flags & ~RTLIL::CONST_FLAG_SIGNED) == RTLIL::CONST_FLAG_NONE) {
+ type = (param.second.flags & RTLIL::CONST_FLAG_SIGNED) ? "signed" : "unsigned";
+ value = param.second.as_string();
+ } else
+ log_cmd_error("Unserializable constant flags 0x%x\n", param.second.flags);
+ json_parameters[param.first.str()] = Json::object {
+ { "type", type },
+ { "value", value },
+ };
+ }
+ Json response = call(Json::object {
+ { "method", "derive" },
+ { "module", module },
+ { "parameters", json_parameters },
+ });
+ bool is_valid = true;
+ std::string frontend, source;
+ if (response["frontend"].is_string())
+ frontend = response["frontend"].string_value();
+ else is_valid = false;
+ if (response["source"].is_string())
+ source = response["source"].string_value();
+ else is_valid = false;
+ if (!is_valid)
+ log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str());
+ return std::make_pair(frontend, source);
+ }
+};
+
+struct RpcModule : RTLIL::Module {
+ std::shared_ptr<RpcServer> server;
+
+ RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool /*mayfail*/) YS_OVERRIDE {
+ std::string stripped_name = name.str();
+ if (stripped_name.compare(0, 9, "$abstract") == 0)
+ stripped_name = stripped_name.substr(9);
+ log_assert(stripped_name[0] == '\\');
+
+ log_header(design, "Executing RPC frontend `%s' for module `%s'.\n", server->name.c_str(), stripped_name.c_str());
+
+ std::string parameter_info;
+ for (auto &param : parameters) {
+ log("Parameter %s = %s\n", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second)));
+ parameter_info += stringf("%s=%s", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second)));
+ }
+
+ std::string derived_name;
+ if (parameters.empty())
+ derived_name = stripped_name;
+ else if (parameter_info.size() > 60)
+ derived_name = "$paramod$" + sha1(parameter_info) + stripped_name;
+ else
+ derived_name = "$paramod" + stripped_name + parameter_info;
+
+ if (design->has(derived_name)) {
+ log("Found cached RTLIL representation for module `%s'.\n", derived_name.c_str());
+ } else {
+ std::string command, input;
+ std::tie(command, input) = server->derive_module(stripped_name.substr(1), parameters);
+
+ std::istringstream input_stream(input);
+ RTLIL::Design *derived_design = new RTLIL::Design;
+ Frontend::frontend_call(derived_design, &input_stream, "<rpc>" + derived_name.substr(8), command);
+ derived_design->check();
+
+ dict<std::string, std::string> name_mangling;
+ bool found_derived_top = false;
+ for (auto module : derived_design->modules()) {
+ std::string original_name = module->name.str();
+ if (original_name == stripped_name) {
+ found_derived_top = true;
+ name_mangling[original_name] = derived_name;
+ } else {
+ name_mangling[original_name] = derived_name + module->name.str();
+ }
+ }
+ if (!found_derived_top)
+ log_cmd_error("RPC frontend did not return requested module `%s`!\n", stripped_name.c_str());
+
+ for (auto module : derived_design->modules())
+ for (auto cell : module->cells())
+ if (name_mangling.count(cell->type.str()))
+ cell->type = name_mangling[cell->type.str()];
+
+ for (auto module : derived_design->modules_) {
+ std::string mangled_name = name_mangling[module.first.str()];
+
+ log("Importing `%s' as `%s'.\n", log_id(module.first), log_id(mangled_name));
+
+ module.second->name = mangled_name;
+ module.second->design = design;
+ module.second->attributes.erase("\\top");
+ design->modules_[mangled_name] = module.second;
+ derived_design->modules_.erase(module.first);
+ }
+
+ delete derived_design;
+ }
+
+ return derived_name;
+ }
+
+ RTLIL::Module *clone() const YS_OVERRIDE {
+ RpcModule *new_mod = new RpcModule;
+ new_mod->server = server;
+ cloneInto(new_mod);
+ return new_mod;
+ }
+};
+
+#if defined(_WIN32)
+
+#if defined(_MSC_VER)
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#endif
+
+struct HandleRpcServer : RpcServer {
+ HANDLE hsend, hrecv;
+
+ HandleRpcServer(const std::string &name, HANDLE hsend, HANDLE hrecv)
+ : RpcServer(name), hsend(hsend), hrecv(hrecv) { }
+
+ void write(const std::string &data) YS_OVERRIDE {
+ log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1);
+ ssize_t offset = 0;
+ do {
+ DWORD data_written;
+ if (!WriteFile(hsend, &data[offset], data.length() - offset, &data_written, /*lpOverlapped=*/NULL))
+ log_cmd_error("WriteFile failed: %s\n", get_last_error_str().c_str());
+ offset += data_written;
+ } while(offset < (ssize_t)data.length());
+ }
+
+ std::string read() YS_OVERRIDE {
+ std::string data;
+ ssize_t offset = 0;
+ while (data.length() == 0 || data[data.length() - 1] != '\n') {
+ data.resize(data.length() + 1024);
+ DWORD data_read;
+ if (!ReadFile(hrecv, &data[offset], data.length() - offset, &data_read, /*lpOverlapped=*/NULL))
+ log_cmd_error("ReadFile failed: %s\n", get_last_error_str().c_str());
+ offset += data_read;
+ data.resize(offset);
+ size_t term_pos = data.find('\n', offset);
+ if (term_pos != data.length() - 1 && term_pos != std::string::npos)
+ log_cmd_error("read failed: more than one response\n");
+ }
+ return data;
+ }
+
+ ~HandleRpcServer() {
+ CloseHandle(hsend);
+ if (hrecv != hsend)
+ CloseHandle(hrecv);
+ }
+};
+
+#else
+
+struct FdRpcServer : RpcServer {
+ int fdsend, fdrecv;
+ pid_t pid;
+
+ FdRpcServer(const std::string &name, int fdsend, int fdrecv, pid_t pid = -1)
+ : RpcServer(name), fdsend(fdsend), fdrecv(fdrecv), pid(pid) { }
+
+ void check_pid() {
+ if (pid == -1) return;
+ // If we're communicating with a process, check that it's still running, or we may get killed with SIGPIPE.
+ pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
+ if (wait_result == -1)
+ log_cmd_error("waitpid failed: %s\n", strerror(errno));
+ if (wait_result == pid)
+ log_cmd_error("RPC frontend terminated unexpectedly\n");
+ }
+
+ void write(const std::string &data) YS_OVERRIDE {
+ log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1);
+ ssize_t offset = 0;
+ do {
+ check_pid();
+ ssize_t result = ::write(fdsend, &data[offset], data.length() - offset);
+ if (result == -1)
+ log_cmd_error("write failed: %s\n", strerror(errno));
+ offset += result;
+ } while(offset < (ssize_t)data.length());
+ }
+
+ std::string read() YS_OVERRIDE {
+ std::string data;
+ ssize_t offset = 0;
+ while (data.length() == 0 || data[data.length() - 1] != '\n') {
+ data.resize(data.length() + 1024);
+ check_pid();
+ ssize_t result = ::read(fdrecv, &data[offset], data.length() - offset);
+ if (result == -1)
+ log_cmd_error("read failed: %s\n", strerror(errno));
+ offset += result;
+ data.resize(offset);
+ size_t term_pos = data.find('\n', offset);
+ if (term_pos != data.length() - 1 && term_pos != std::string::npos)
+ log_cmd_error("read failed: more than one response\n");
+ }
+ return data;
+ }
+
+ ~FdRpcServer() {
+ close(fdsend);
+ if (fdrecv != fdsend)
+ close(fdrecv);
+ }
+};
+
+#endif
+
+// RpcFrontend does not inherit from Frontend since it does not read files.
+struct RpcFrontend : public Pass {
+ RpcFrontend() : Pass("connect_rpc", "connect to RPC frontend") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" connect_rpc -exec <command> [args...]\n");
+ log(" connect_rpc -path <path>\n");
+ log("\n");
+ log("Load modules using an out-of-process frontend.\n");
+ log("\n");
+ log(" -exec <command> [args...]\n");
+ log(" run <command> with arguments [args...]. send requests on stdin, read\n");
+ log(" responses from stdout.\n");
+ log("\n");
+ log(" -path <path>\n");
+ log(" connect to Unix domain socket at <path>. (Unix)\n");
+ log(" connect to bidirectional byte-type named pipe at <path>. (Windows)\n");
+ log("\n");
+ log("A simple JSON-based, newline-delimited protocol is used for communicating with\n");
+ log("the frontend. Yosys requests data from the frontend by sending exactly 1 line\n");
+ log("of JSON. Frontend responds with data or error message by replying with exactly\n");
+ log("1 line of JSON as well.\n");
+ log("\n");
+ log(" -> {\"method\": \"modules\"}\n");
+ log(" <- {\"modules\": [\"<module-name>\", ...]}\n");
+ log(" <- {\"error\": \"<error-message>\"}\n");
+ log(" request for the list of modules that can be derived by this frontend.\n");
+ log(" the 'hierarchy' command will call back into this frontend if a cell\n");
+ log(" with type <module-name> is instantiated in the design.\n");
+ log("\n");
+ log(" -> {\"method\": \"derive\", \"module\": \"<module-name\">, \"parameters\": {\n");
+ log(" \"<param-name>\": {\"type\": \"[unsigned|signed|string|real]\",\n");
+ log(" \"value\": \"<param-value>\"}, ...}}\n");
+ log(" <- {\"frontend\": \"[ilang|verilog|...]\",\"source\": \"<source>\"}}\n");
+ log(" <- {\"error\": \"<error-message>\"}\n");
+ log(" request for the module <module-name> to be derived for a specific set of\n");
+ log(" parameters. <param-name> starts with \\ for named parameters, and with $\n");
+ log(" for unnamed parameters, which are numbered starting at 1.<param-value>\n");
+ log(" for integer parameters is always specified as a binary string of unlimited\n");
+ log(" precision. the <source> returned by the frontend is hygienically parsed\n");
+ log(" by a built-in Yosys <frontend>, allowing the RPC frontend to return any\n");
+ log(" convenient representation of the module. the derived module is cached,\n");
+ log(" so the response should be the same whenever the same set of parameters\n");
+ log(" is provided.\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Connecting to RPC frontend.\n");
+
+ std::vector<std::string> command;
+ std::string path;
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-exec" && argidx+1 < args.size()) {
+ command.insert(command.begin(), args.begin() + argidx + 1, args.end());
+ continue;
+ }
+ if (arg == "-path" && argidx+1 < args.size()) {
+ path = args[argidx+1];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if ((!command.empty()) + (!path.empty()) != 1)
+ log_cmd_error("Exactly one of -exec, -unix must be specified.\n");
+
+ std::shared_ptr<RpcServer> server;
+ if (!command.empty()) {
+ std::string command_line;
+ bool first = true;
+ for (auto &arg : command) {
+ if (!first) command_line += ' ';
+ command_line += arg;
+ first = false;
+ }
+
+#ifdef _WIN32
+ std::wstring command_w = str2wstr(command[0]);
+ std::wstring command_path_w;
+ std::wstring command_line_w = str2wstr(command_line);
+ DWORD command_path_len_w;
+ SECURITY_ATTRIBUTES pipe_attr = {};
+ HANDLE send_r = NULL, send_w = NULL, recv_r = NULL, recv_w = NULL;
+ STARTUPINFOW startup_info = {};
+ PROCESS_INFORMATION proc_info = {};
+
+ command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/0, /*lpBuffer=*/NULL, /*lpFilePart=*/NULL);
+ if (command_path_len_w == 0) {
+ log_error("SearchPathW failed: %s\n", get_last_error_str().c_str());
+ goto cleanup_exec;
+ }
+ command_path_w.resize(command_path_len_w - 1);
+ command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/command_path_len_w, /*lpBuffer=*/&command_path_w[0], /*lpFilePart=*/NULL);
+ log_assert(command_path_len_w == command_path_w.length());
+
+ pipe_attr.nLength = sizeof(pipe_attr);
+ pipe_attr.bInheritHandle = TRUE;
+ pipe_attr.lpSecurityDescriptor = NULL;
+ if (!CreatePipe(&send_r, &send_w, &pipe_attr, /*nSize=*/0)) {
+ log_error("CreatePipe failed: %s\n", get_last_error_str().c_str());
+ goto cleanup_exec;
+ }
+ if (!SetHandleInformation(send_w, HANDLE_FLAG_INHERIT, 0)) {
+ log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str());
+ goto cleanup_exec;
+ }
+ if (!CreatePipe(&recv_r, &recv_w, &pipe_attr, /*nSize=*/0)) {
+ log_error("CreatePipe failed: %s\n", get_last_error_str().c_str());
+ goto cleanup_exec;
+ }
+ if (!SetHandleInformation(recv_r, HANDLE_FLAG_INHERIT, 0)) {
+ log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str());
+ goto cleanup_exec;
+ }
+
+ startup_info.cb = sizeof(startup_info);
+ startup_info.hStdInput = send_r;
+ startup_info.hStdOutput = recv_w;
+ startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+ if (!CreateProcessW(/*lpApplicationName=*/command_path_w.c_str(), /*lpCommandLine=*/&command_line_w[0], /*lpProcessAttributes=*/NULL, /*lpThreadAttributes=*/NULL, /*bInheritHandles=*/TRUE, /*dwCreationFlags=*/0, /*lpEnvironment=*/NULL, /*lpCurrentDirectory=*/NULL, &startup_info, &proc_info)) {
+ log_error("CreateProcessW failed: %s\n", get_last_error_str().c_str());
+ goto cleanup_exec;
+ }
+ CloseHandle(proc_info.hProcess);
+ CloseHandle(proc_info.hThread);
+
+ server = std::make_shared<HandleRpcServer>(path, send_w, recv_r);
+ send_w = NULL;
+ recv_r = NULL;
+
+cleanup_exec:
+ if (send_r != NULL) CloseHandle(send_r);
+ if (send_w != NULL) CloseHandle(send_w);
+ if (recv_r != NULL) CloseHandle(recv_r);
+ if (recv_w != NULL) CloseHandle(recv_w);
+#else
+ std::vector<char *> argv;
+ int send[2] = {-1,-1}, recv[2] = {-1,-1};
+ posix_spawn_file_actions_t file_actions, *file_actions_p = NULL;
+ pid_t pid;
+
+ for (auto &arg : command)
+ argv.push_back(&arg[0]);
+ argv.push_back(nullptr);
+
+ if (pipe(send) != 0) {
+ log_error("pipe failed: %s\n", strerror(errno));
+ goto cleanup_exec;
+ }
+ if (pipe(recv) != 0) {
+ log_error("pipe failed: %s\n", strerror(errno));
+ goto cleanup_exec;
+ }
+
+ if (posix_spawn_file_actions_init(&file_actions) != 0) {
+ log_error("posix_spawn_file_actions_init failed: %s\n", strerror(errno));
+ goto cleanup_exec;
+ }
+ file_actions_p = &file_actions;
+ if (posix_spawn_file_actions_adddup2(file_actions_p, send[0], STDIN_FILENO) != 0) {
+ log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno));
+ goto cleanup_exec;
+ }
+ if (posix_spawn_file_actions_addclose(file_actions_p, send[1]) != 0) {
+ log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno));
+ goto cleanup_exec;
+ }
+ if (posix_spawn_file_actions_adddup2(file_actions_p, recv[1], STDOUT_FILENO) != 0) {
+ log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno));
+ goto cleanup_exec;
+ }
+ if (posix_spawn_file_actions_addclose(file_actions_p, recv[0]) != 0) {
+ log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno));
+ goto cleanup_exec;
+ }
+
+ if (posix_spawnp(&pid, argv[0], file_actions_p, /*attrp=*/NULL, argv.data(), environ) != 0) {
+ log_error("posix_spawnp failed: %s\n", strerror(errno));
+ goto cleanup_exec;
+ }
+
+ server = std::make_shared<FdRpcServer>(command_line, send[1], recv[0], pid);
+ send[1] = -1;
+ recv[0] = -1;
+
+cleanup_exec:
+ if (send[0] != -1) close(send[0]);
+ if (send[1] != -1) close(send[1]);
+ if (recv[0] != -1) close(recv[0]);
+ if (recv[1] != -1) close(recv[1]);
+ if (file_actions_p != NULL)
+ posix_spawn_file_actions_destroy(file_actions_p);
+#endif
+ } else if (!path.empty()) {
+#ifdef _WIN32
+ std::wstring path_w = str2wstr(path);
+ HANDLE h;
+
+ h = CreateFileW(path_w.c_str(), GENERIC_READ|GENERIC_WRITE, /*dwShareMode=*/0, /*lpSecurityAttributes=*/NULL, /*dwCreationDisposition=*/OPEN_EXISTING, /*dwFlagsAndAttributes=*/0, /*hTemplateFile=*/NULL);
+ if (h == INVALID_HANDLE_VALUE) {
+ log_error("CreateFileW failed: %s\n", get_last_error_str().c_str());
+ goto cleanup_path;
+ }
+
+ server = std::make_shared<HandleRpcServer>(path, h, h);
+
+cleanup_path:
+ ;
+#else
+ struct sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1);
+
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ log_error("socket failed: %s\n", strerror(errno));
+ goto cleanup_path;
+ }
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
+ log_error("connect failed: %s\n", strerror(errno));
+ goto cleanup_path;
+ }
+
+ server = std::make_shared<FdRpcServer>(path, fd, fd);
+ fd = -1;
+
+cleanup_path:
+ if (fd != -1) close(fd);
+#endif
+ }
+
+ if (!server)
+ log_cmd_error("Failed to connect to RPC frontend.\n");
+
+ for (auto &module_name : server->get_module_names()) {
+ log("Linking module `%s'.\n", module_name.c_str());
+ RpcModule *module = new RpcModule;
+ module->name = "$abstract\\" + module_name;
+ module->server = server;
+ design->add(module);
+ }
+ }
+} RpcFrontend;
+
+YOSYS_NAMESPACE_END
diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc
index 4bf5b1cf5..49281f7e7 100644
--- a/frontends/verilog/const2ast.cc
+++ b/frontends/verilog/const2ast.cc
@@ -85,10 +85,8 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
digits.push_back(10 + *str - 'A');
else if (*str == 'x' || *str == 'X')
digits.push_back(0xf0);
- else if (*str == 'z' || *str == 'Z')
+ else if (*str == 'z' || *str == 'Z' || *str == '?')
digits.push_back(0xf1);
- else if (*str == '?')
- digits.push_back(0xf2);
str++;
}
@@ -112,8 +110,6 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx);
else if (*it == 0xf1)
data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz);
- else if (*it == 0xf2)
- data.push_back(RTLIL::Sa);
else
data.push_back((*it & bitmask) ? State::S1 : State::S0);
}
@@ -199,13 +195,13 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
if (str == endptr)
len_in_bits = -1;
- // The "<bits>'s?[bodhBODH]<digits>" syntax
+ // The "<bits>'[sS]?[bodhBODH]<digits>" syntax
if (*endptr == '\'')
{
std::vector<RTLIL::State> data;
bool is_signed = false;
bool is_unsized = len_in_bits < 0;
- if (*(endptr+1) == 's') {
+ if (*(endptr+1) == 's' || *(endptr+1) == 'S') {
is_signed = true;
endptr++;
}
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index 57e55b1f4..4acfb414d 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -239,7 +239,7 @@ YOSYS_NAMESPACE_END
return TOK_CONSTVAL;
}
-[0-9]*[ \t]*\'s?[bodhBODH]*[ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
+[0-9]*[ \t]*\'[sS]?[bodhBODH]?[ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
frontend_verilog_yylval.string = new std::string(yytext);
return TOK_CONSTVAL;
}
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index 4afd72b73..77f6d2051 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -155,7 +155,7 @@ struct specify_rise_fall {
%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 opt_sva_label tok_prim_wrapper hierarchical_id
+%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id
%type <boolean> opt_signed opt_property unique_case_attr
%type <al> attr case_attr
@@ -206,6 +206,7 @@ design:
task_func_decl design |
param_decl design |
localparam_decl design |
+ typedef_decl design |
package design |
interface design |
/* empty */;
@@ -290,6 +291,9 @@ hierarchical_id:
$$ = $1;
};
+hierarchical_type_id:
+ '(' hierarchical_id ')' { $$ = $2; };
+
module:
attr TOK_MODULE TOK_ID {
do_not_require_port_stubs = false;
@@ -324,13 +328,13 @@ single_module_para:
astbuf1 = new AstNode(AST_PARAMETER);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
append_attr(astbuf1, $1);
- } param_signed param_integer param_range single_param_decl |
+ } param_type single_param_decl |
attr TOK_LOCALPARAM {
if (astbuf1) delete astbuf1;
astbuf1 = new AstNode(AST_LOCALPARAM);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
append_attr(astbuf1, $1);
- } param_signed param_integer param_range single_param_decl |
+ } param_type single_param_decl |
single_param_decl;
module_args_opt:
@@ -426,6 +430,7 @@ package_body:
package_body package_body_stmt |;
package_body_stmt:
+ typedef_decl |
localparam_decl;
interface:
@@ -452,7 +457,7 @@ interface_body:
interface_body interface_body_stmt |;
interface_body_stmt:
- param_decl | localparam_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
+ param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
modport_stmt;
non_opt_delay:
@@ -475,8 +480,14 @@ wire_type:
};
wire_type_token_list:
- wire_type_token | wire_type_token_list wire_type_token |
- wire_type_token_io ;
+ wire_type_token |
+ wire_type_token_list wire_type_token |
+ wire_type_token_io |
+ hierarchical_type_id {
+ astbuf3->is_custom_type = true;
+ astbuf3->children.push_back(new AstNode(AST_WIRETYPE));
+ astbuf3->children.back()->str = *$1;
+ };
wire_type_token_io:
TOK_INPUT {
@@ -591,7 +602,7 @@ module_body:
/* empty */;
module_body_stmt:
- task_func_decl | specify_block |param_decl | localparam_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
+ task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
checker_decl:
@@ -1149,12 +1160,20 @@ param_range:
}
};
+param_type:
+ param_signed param_integer param_real param_range |
+ hierarchical_type_id {
+ astbuf1->is_custom_type = true;
+ astbuf1->children.push_back(new AstNode(AST_WIRETYPE));
+ astbuf1->children.back()->str = *$1;
+ };
+
param_decl:
attr TOK_PARAMETER {
astbuf1 = new AstNode(AST_PARAMETER);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
append_attr(astbuf1, $1);
- } param_signed param_integer param_real param_range param_decl_list ';' {
+ } param_type param_decl_list ';' {
delete astbuf1;
};
@@ -1163,7 +1182,7 @@ localparam_decl:
astbuf1 = new AstNode(AST_LOCALPARAM);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
append_attr(astbuf1, $1);
- } param_signed param_integer param_real param_range param_decl_list ';' {
+ } param_type param_decl_list ';' {
delete astbuf1;
};
@@ -1327,7 +1346,7 @@ wire_name:
if ($2 != NULL) {
if (node->is_input || node->is_output)
frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions.");
- if (!astbuf2) {
+ if (!astbuf2 && !node->is_custom_type) {
AstNode *rng = new AstNode(AST_RANGE);
rng->children.push_back(AstNode::mkconst_int(0, true));
rng->children.push_back(AstNode::mkconst_int(0, true));
@@ -1377,6 +1396,45 @@ assign_expr:
ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3));
};
+typedef_decl:
+ TOK_TYPEDEF wire_type range TOK_ID range_or_multirange ';' {
+ astbuf1 = $2;
+ astbuf2 = $3;
+ if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
+ if (astbuf2) {
+ frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions.");
+ } else {
+ astbuf2 = new AstNode(AST_RANGE);
+ astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
+ astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true));
+ }
+ }
+ if (astbuf2 && astbuf2->children.size() != 2)
+ frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
+ if (astbuf2)
+ astbuf1->children.push_back(astbuf2);
+
+ if ($5 != NULL) {
+ if (!astbuf2) {
+ AstNode *rng = new AstNode(AST_RANGE);
+ rng->children.push_back(AstNode::mkconst_int(0, true));
+ rng->children.push_back(AstNode::mkconst_int(0, true));
+ astbuf1->children.push_back(rng);
+ }
+ astbuf1->type = AST_MEMORY;
+ auto *rangeNode = $5;
+ if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) {
+ // SV array size [n], rewrite as [n-1:0]
+ rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true));
+ rangeNode->children.push_back(AstNode::mkconst_int(0, false));
+ }
+ astbuf1->children.push_back(rangeNode);
+ }
+
+ ast_stack.back()->children.push_back(new AstNode(AST_TYPEDEF, astbuf1));
+ ast_stack.back()->children.back()->str = *$4;
+ };
+
cell_stmt:
attr TOK_ID {
astbuf1 = new AstNode(AST_CELL);
@@ -1823,7 +1881,7 @@ simple_behavioral_stmt:
// this production creates the obligatory if-else shift/reduce conflict
behavioral_stmt:
- defattr | assert | wire_decl | param_decl | localparam_decl |
+ defattr | assert | wire_decl | param_decl | localparam_decl | typedef_decl |
non_opt_delay behavioral_stmt |
simple_behavioral_stmt ';' | ';' |
hierarchical_id attr {