diff options
Diffstat (limited to 'frontends')
-rw-r--r-- | frontends/aiger/aigerparse.cc | 53 | ||||
-rw-r--r-- | frontends/ast/ast.cc | 54 | ||||
-rw-r--r-- | frontends/ast/ast.h | 1 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 36 | ||||
-rw-r--r-- | frontends/rpc/Makefile.inc | 2 | ||||
-rw-r--r-- | frontends/rpc/rpc_frontend.cc | 589 | ||||
-rw-r--r-- | frontends/verilog/const2ast.cc | 10 | ||||
-rw-r--r-- | frontends/verilog/verilog_lexer.l | 2 |
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> ¶meters) { + Json::object json_parameters; + for (auto ¶m : 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 ¶m : 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; } |