aboutsummaryrefslogtreecommitdiffstats
path: root/frontends/rpc/rpc_frontend.cc
blob: c12640ef055eaa4a1ac6ed006b24757cac9649ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197<
/*
Copyright 2013 Jun Wako <wakojun@gmail.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "host.h"
#include "report.h"
#include "debug.h"
#include "action_util.h"
#include "action_layer.h"
#include "timer.h"
#include "keycode_config.h"

extern keymap_config_t keymap_config;


static uint8_t real_mods = 0;
static uint8_t weak_mods = 0;
static uint8_t macro_mods = 0;

#ifdef USB_6KRO_ENABLE
#define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS)
#define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS)
#define RO_INC(a) RO_ADD(a, 1)
#define RO_DEC(a) RO_SUB(a, 1)
static int8_t cb_head = 0;
static int8_t cb_tail = 0;
static int8_t cb_count = 0;
#endif

// TODO: pointer variable is not needed
//report_keyboard_t keyboard_report = {};
report_keyboard_t *keyboard_report = &(report_keyboard_t){};

extern inline void add_key(uint8_t key);
extern inline void del_key(uint8_t key);
extern inline void clear_keys(void);

#ifndef NO_ACTION_ONESHOT
static int8_t oneshot_mods = 0;
static int8_t oneshot_locked_mods = 0;
int8_t get_oneshot_locked_mods(void) { return oneshot_locked_mods; }
void set_oneshot_locked_mods(int8_t mods) { oneshot_locked_mods = mods; }
void clear_oneshot_locked_mods(void) { oneshot_locked_mods = 0; }
#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
static int16_t oneshot_time = 0;
bool has_oneshot_mods_timed_out(void) {
  return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT;
}
#else
bool has_oneshot_mods_timed_out(void) {
    return false;
}
#endif
#endif

/* oneshot layer */
#ifndef NO_ACTION_ONESHOT
/* oneshot_layer_data bits
* LLLL LSSS
* where:
*   L => are layer bits
*   S => oneshot state bits
*/
static int8_t oneshot_layer_data = 0;

inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; }
inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; }

#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
static int16_t oneshot_layer_time = 0;
inline pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 *  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, const dict<RTLIL::IdString, RTLIL::Const> &parameters, bool /*mayfail*/) 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(ID::top);
				if (!module.second->has_attribute(ID::hdlname))
					module.second->set_string_attribute(ID::hdlname, module.first.str());
				design->modules_[mangled_name] = module.second;
				derived_design->modules_.erase(module.first);
			}

			delete derived_design;
		}

		return derived_name;
	}

	RTLIL::Module *clone() const 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) 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() 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) 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() 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() 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\": \"[rtlil|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) 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());
				argidx = args.size()-1;
				continue;
			}
			if (arg == "-path" && argidx+1 < args.size()) {
				path = args[++argidx];
				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