aboutsummaryrefslogtreecommitdiffstats
path: root/frontends
diff options
context:
space:
mode:
Diffstat (limited to 'frontends')
-rw-r--r--frontends/aiger/aigerparse.cc53
-rw-r--r--frontends/ast/ast.cc54
-rw-r--r--frontends/ast/ast.h1
-rw-r--r--frontends/ast/simplify.cc36
-rw-r--r--frontends/rpc/Makefile.inc2
-rw-r--r--frontends/rpc/rpc_frontend.cc589
-rw-r--r--frontends/verilog/const2ast.cc10
-rw-r--r--frontends/verilog/verilog_lexer.l2
8 files changed, 693 insertions, 54 deletions
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index 2e1fb8fad..5a1da4db1 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() == ' ') {
@@ -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..21279cbfa 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -158,6 +158,11 @@ 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)
#undef X
default:
@@ -1099,6 +1104,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 +1296,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) {
@@ -1467,24 +1481,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
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();
+ loadconfig();
std::string para_info;
AstNode *new_ast = ast->clone();
@@ -1565,6 +1562,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..93fee913e 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -299,6 +299,7 @@ namespace AST
std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail);
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/simplify.cc b/frontends/ast/simplify.cc
index 86dd80c65..b1ee22f42 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -1530,10 +1530,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 +1552,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 +1570,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,8 +2895,15 @@ 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)
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..b4b2fa3a2
--- /dev/null
+++ b/frontends/rpc/rpc_frontend.cc
@@ -0,0 +1,589 @@
+/*
+ * 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>
+#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)
+
+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;
}