mirror of https://github.com/YosysHQ/yosys.git
Merge remote-tracking branch 'origin/master' into xaig_dff
This commit is contained in:
commit
8684b58bed
|
@ -49,6 +49,7 @@ Yosys 0.9 .. Yosys 0.9-dev
|
|||
- "synth_xilinx" to now infer DSP blocks (-nodsp to disable)
|
||||
- "synth_ecp5" to now infer DSP blocks (-nodsp to disable, experimental)
|
||||
- "synth_ice40 -dsp" to infer DSP blocks
|
||||
- Added latch support to synth_xilinx
|
||||
|
||||
Yosys 0.8 .. Yosys 0.9
|
||||
----------------------
|
||||
|
|
6
Makefile
6
Makefile
|
@ -115,7 +115,7 @@ LDFLAGS += -rdynamic
|
|||
LDLIBS += -lrt
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.9+431
|
||||
YOSYS_VER := 0.9+899
|
||||
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
||||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
|
@ -528,6 +528,7 @@ $(eval $(call add_include_file,kernel/satgen.h))
|
|||
$(eval $(call add_include_file,libs/ezsat/ezsat.h))
|
||||
$(eval $(call add_include_file,libs/ezsat/ezminisat.h))
|
||||
$(eval $(call add_include_file,libs/sha1/sha1.h))
|
||||
$(eval $(call add_include_file,libs/json11/json11.hpp))
|
||||
$(eval $(call add_include_file,passes/fsm/fsmdata.h))
|
||||
$(eval $(call add_include_file,frontends/ast/ast.h))
|
||||
$(eval $(call add_include_file,backends/ilang/ilang_backend.h))
|
||||
|
@ -545,6 +546,8 @@ OBJS += libs/sha1/sha1.o
|
|||
|
||||
ifneq ($(SMALL),1)
|
||||
|
||||
OBJS += libs/json11/json11.o
|
||||
|
||||
OBJS += libs/subcircuit/subcircuit.o
|
||||
|
||||
OBJS += libs/ezsat/ezsat.o
|
||||
|
@ -710,6 +713,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
|
|||
+cd tests/aiger && bash run-test.sh $(ABCOPT)
|
||||
+cd tests/arch && bash run-test.sh
|
||||
+cd tests/ice40 && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/rpc && bash run-test.sh
|
||||
@echo ""
|
||||
@echo " Passed \"make test\"."
|
||||
@echo ""
|
||||
|
|
|
@ -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)
|
||||
|
@ -1094,13 +1096,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);
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
OBJS += frontends/rpc/rpc_frontend.o
|
|
@ -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
|
|
@ -439,7 +439,7 @@ void Frontend::execute(std::vector<std::string> args, RTLIL::Design *design)
|
|||
FILE *Frontend::current_script_file = NULL;
|
||||
std::string Frontend::last_here_document;
|
||||
|
||||
void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx)
|
||||
void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_input)
|
||||
{
|
||||
bool called_with_fp = f != NULL;
|
||||
|
||||
|
@ -489,7 +489,7 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s
|
|||
next_args.insert(next_args.end(), filenames.begin()+1, filenames.end());
|
||||
}
|
||||
std::ifstream *ff = new std::ifstream;
|
||||
ff->open(filename.c_str());
|
||||
ff->open(filename.c_str(), bin_input ? std::ifstream::binary : std::ifstream::in);
|
||||
yosys_input_files.insert(filename);
|
||||
if (ff->fail())
|
||||
delete ff;
|
||||
|
|
|
@ -94,7 +94,7 @@ struct Frontend : Pass
|
|||
virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0;
|
||||
|
||||
static std::vector<std::string> next_args;
|
||||
void extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx);
|
||||
void extra_args(std::istream *&f, std::string &filename, std::vector<std::string> args, size_t argidx, bool bin_input = false);
|
||||
|
||||
static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::string command);
|
||||
static void frontend_call(RTLIL::Design *design, std::istream *f, std::string filename, std::vector<std::string> args);
|
||||
|
|
|
@ -0,0 +1,788 @@
|
|||
/* Copyright (c) 2013 Dropbox, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "json11.hpp"
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
|
||||
namespace json11 {
|
||||
|
||||
static const int max_depth = 200;
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::map;
|
||||
using std::make_shared;
|
||||
using std::initializer_list;
|
||||
using std::move;
|
||||
|
||||
/* Helper for representing null - just a do-nothing struct, plus comparison
|
||||
* operators so the helpers in JsonValue work. We can't use nullptr_t because
|
||||
* it may not be orderable.
|
||||
*/
|
||||
struct NullStruct {
|
||||
bool operator==(NullStruct) const { return true; }
|
||||
bool operator<(NullStruct) const { return false; }
|
||||
};
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Serialization
|
||||
*/
|
||||
|
||||
static void dump(NullStruct, string &out) {
|
||||
out += "null";
|
||||
}
|
||||
|
||||
static void dump(double value, string &out) {
|
||||
if (std::isfinite(value)) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof buf, "%.17g", value);
|
||||
out += buf;
|
||||
} else {
|
||||
out += "null";
|
||||
}
|
||||
}
|
||||
|
||||
static void dump(int value, string &out) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof buf, "%d", value);
|
||||
out += buf;
|
||||
}
|
||||
|
||||
static void dump(bool value, string &out) {
|
||||
out += value ? "true" : "false";
|
||||
}
|
||||
|
||||
static void dump(const string &value, string &out) {
|
||||
out += '"';
|
||||
for (size_t i = 0; i < value.length(); i++) {
|
||||
const char ch = value[i];
|
||||
if (ch == '\\') {
|
||||
out += "\\\\";
|
||||
} else if (ch == '"') {
|
||||
out += "\\\"";
|
||||
} else if (ch == '\b') {
|
||||
out += "\\b";
|
||||
} else if (ch == '\f') {
|
||||
out += "\\f";
|
||||
} else if (ch == '\n') {
|
||||
out += "\\n";
|
||||
} else if (ch == '\r') {
|
||||
out += "\\r";
|
||||
} else if (ch == '\t') {
|
||||
out += "\\t";
|
||||
} else if (static_cast<uint8_t>(ch) <= 0x1f) {
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof buf, "\\u%04x", ch);
|
||||
out += buf;
|
||||
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
|
||||
&& static_cast<uint8_t>(value[i+2]) == 0xa8) {
|
||||
out += "\\u2028";
|
||||
i += 2;
|
||||
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
|
||||
&& static_cast<uint8_t>(value[i+2]) == 0xa9) {
|
||||
out += "\\u2029";
|
||||
i += 2;
|
||||
} else {
|
||||
out += ch;
|
||||
}
|
||||
}
|
||||
out += '"';
|
||||
}
|
||||
|
||||
static void dump(const Json::array &values, string &out) {
|
||||
bool first = true;
|
||||
out += "[";
|
||||
for (const auto &value : values) {
|
||||
if (!first)
|
||||
out += ", ";
|
||||
value.dump(out);
|
||||
first = false;
|
||||
}
|
||||
out += "]";
|
||||
}
|
||||
|
||||
static void dump(const Json::object &values, string &out) {
|
||||
bool first = true;
|
||||
out += "{";
|
||||
for (const auto &kv : values) {
|
||||
if (!first)
|
||||
out += ", ";
|
||||
dump(kv.first, out);
|
||||
out += ": ";
|
||||
kv.second.dump(out);
|
||||
first = false;
|
||||
}
|
||||
out += "}";
|
||||
}
|
||||
|
||||
void Json::dump(string &out) const {
|
||||
m_ptr->dump(out);
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Value wrappers
|
||||
*/
|
||||
|
||||
template <Json::Type tag, typename T>
|
||||
class Value : public JsonValue {
|
||||
protected:
|
||||
|
||||
// Constructors
|
||||
explicit Value(const T &value) : m_value(value) {}
|
||||
explicit Value(T &&value) : m_value(move(value)) {}
|
||||
|
||||
// Get type tag
|
||||
Json::Type type() const override {
|
||||
return tag;
|
||||
}
|
||||
|
||||
// Comparisons
|
||||
bool equals(const JsonValue * other) const override {
|
||||
return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
|
||||
}
|
||||
bool less(const JsonValue * other) const override {
|
||||
return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
|
||||
}
|
||||
|
||||
const T m_value;
|
||||
void dump(string &out) const override { json11::dump(m_value, out); }
|
||||
};
|
||||
|
||||
class JsonDouble final : public Value<Json::NUMBER, double> {
|
||||
double number_value() const override { return m_value; }
|
||||
int int_value() const override { return static_cast<int>(m_value); }
|
||||
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
|
||||
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
|
||||
public:
|
||||
explicit JsonDouble(double value) : Value(value) {}
|
||||
};
|
||||
|
||||
class JsonInt final : public Value<Json::NUMBER, int> {
|
||||
double number_value() const override { return m_value; }
|
||||
int int_value() const override { return m_value; }
|
||||
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
|
||||
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
|
||||
public:
|
||||
explicit JsonInt(int value) : Value(value) {}
|
||||
};
|
||||
|
||||
class JsonBoolean final : public Value<Json::BOOL, bool> {
|
||||
bool bool_value() const override { return m_value; }
|
||||
public:
|
||||
explicit JsonBoolean(bool value) : Value(value) {}
|
||||
};
|
||||
|
||||
class JsonString final : public Value<Json::STRING, string> {
|
||||
const string &string_value() const override { return m_value; }
|
||||
public:
|
||||
explicit JsonString(const string &value) : Value(value) {}
|
||||
explicit JsonString(string &&value) : Value(move(value)) {}
|
||||
};
|
||||
|
||||
class JsonArray final : public Value<Json::ARRAY, Json::array> {
|
||||
const Json::array &array_items() const override { return m_value; }
|
||||
const Json & operator[](size_t i) const override;
|
||||
public:
|
||||
explicit JsonArray(const Json::array &value) : Value(value) {}
|
||||
explicit JsonArray(Json::array &&value) : Value(move(value)) {}
|
||||
};
|
||||
|
||||
class JsonObject final : public Value<Json::OBJECT, Json::object> {
|
||||
const Json::object &object_items() const override { return m_value; }
|
||||
const Json & operator[](const string &key) const override;
|
||||
public:
|
||||
explicit JsonObject(const Json::object &value) : Value(value) {}
|
||||
explicit JsonObject(Json::object &&value) : Value(move(value)) {}
|
||||
};
|
||||
|
||||
class JsonNull final : public Value<Json::NUL, NullStruct> {
|
||||
public:
|
||||
JsonNull() : Value({}) {}
|
||||
};
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Static globals - static-init-safe
|
||||
*/
|
||||
struct Statics {
|
||||
const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
|
||||
const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
|
||||
const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
|
||||
const string empty_string;
|
||||
const vector<Json> empty_vector;
|
||||
const map<string, Json> empty_map;
|
||||
Statics() {}
|
||||
};
|
||||
|
||||
static const Statics & statics() {
|
||||
static const Statics s {};
|
||||
return s;
|
||||
}
|
||||
|
||||
static const Json & static_null() {
|
||||
// This has to be separate, not in Statics, because Json() accesses statics().null.
|
||||
static const Json json_null;
|
||||
return json_null;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Constructors
|
||||
*/
|
||||
|
||||
Json::Json() noexcept : m_ptr(statics().null) {}
|
||||
Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {}
|
||||
Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {}
|
||||
Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
|
||||
Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
|
||||
Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
|
||||
Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
|
||||
Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
|
||||
Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
|
||||
Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
|
||||
Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
|
||||
Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Accessors
|
||||
*/
|
||||
|
||||
Json::Type Json::type() const { return m_ptr->type(); }
|
||||
double Json::number_value() const { return m_ptr->number_value(); }
|
||||
int Json::int_value() const { return m_ptr->int_value(); }
|
||||
bool Json::bool_value() const { return m_ptr->bool_value(); }
|
||||
const string & Json::string_value() const { return m_ptr->string_value(); }
|
||||
const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
|
||||
const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
|
||||
const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
|
||||
const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
|
||||
|
||||
double JsonValue::number_value() const { return 0; }
|
||||
int JsonValue::int_value() const { return 0; }
|
||||
bool JsonValue::bool_value() const { return false; }
|
||||
const string & JsonValue::string_value() const { return statics().empty_string; }
|
||||
const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
|
||||
const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
|
||||
const Json & JsonValue::operator[] (size_t) const { return static_null(); }
|
||||
const Json & JsonValue::operator[] (const string &) const { return static_null(); }
|
||||
|
||||
const Json & JsonObject::operator[] (const string &key) const {
|
||||
auto iter = m_value.find(key);
|
||||
return (iter == m_value.end()) ? static_null() : iter->second;
|
||||
}
|
||||
const Json & JsonArray::operator[] (size_t i) const {
|
||||
if (i >= m_value.size()) return static_null();
|
||||
else return m_value[i];
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Comparison
|
||||
*/
|
||||
|
||||
bool Json::operator== (const Json &other) const {
|
||||
if (m_ptr == other.m_ptr)
|
||||
return true;
|
||||
if (m_ptr->type() != other.m_ptr->type())
|
||||
return false;
|
||||
|
||||
return m_ptr->equals(other.m_ptr.get());
|
||||
}
|
||||
|
||||
bool Json::operator< (const Json &other) const {
|
||||
if (m_ptr == other.m_ptr)
|
||||
return false;
|
||||
if (m_ptr->type() != other.m_ptr->type())
|
||||
return m_ptr->type() < other.m_ptr->type();
|
||||
|
||||
return m_ptr->less(other.m_ptr.get());
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Parsing
|
||||
*/
|
||||
|
||||
/* esc(c)
|
||||
*
|
||||
* Format char c suitable for printing in an error message.
|
||||
*/
|
||||
static inline string esc(char c) {
|
||||
char buf[12];
|
||||
if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
|
||||
snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
|
||||
} else {
|
||||
snprintf(buf, sizeof buf, "(%d)", c);
|
||||
}
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
static inline bool in_range(long x, long lower, long upper) {
|
||||
return (x >= lower && x <= upper);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/* JsonParser
|
||||
*
|
||||
* Object that tracks all state of an in-progress parse.
|
||||
*/
|
||||
struct JsonParser final {
|
||||
|
||||
/* State
|
||||
*/
|
||||
const string &str;
|
||||
size_t i;
|
||||
string &err;
|
||||
bool failed;
|
||||
const JsonParse strategy;
|
||||
|
||||
/* fail(msg, err_ret = Json())
|
||||
*
|
||||
* Mark this parse as failed.
|
||||
*/
|
||||
Json fail(string &&msg) {
|
||||
return fail(move(msg), Json());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T fail(string &&msg, const T err_ret) {
|
||||
if (!failed)
|
||||
err = std::move(msg);
|
||||
failed = true;
|
||||
return err_ret;
|
||||
}
|
||||
|
||||
/* consume_whitespace()
|
||||
*
|
||||
* Advance until the current character is non-whitespace.
|
||||
*/
|
||||
void consume_whitespace() {
|
||||
while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
|
||||
i++;
|
||||
}
|
||||
|
||||
/* consume_comment()
|
||||
*
|
||||
* Advance comments (c-style inline and multiline).
|
||||
*/
|
||||
bool consume_comment() {
|
||||
bool comment_found = false;
|
||||
if (str[i] == '/') {
|
||||
i++;
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input after start of comment", false);
|
||||
if (str[i] == '/') { // inline comment
|
||||
i++;
|
||||
// advance until next line, or end of input
|
||||
while (i < str.size() && str[i] != '\n') {
|
||||
i++;
|
||||
}
|
||||
comment_found = true;
|
||||
}
|
||||
else if (str[i] == '*') { // multiline comment
|
||||
i++;
|
||||
if (i > str.size()-2)
|
||||
return fail("unexpected end of input inside multi-line comment", false);
|
||||
// advance until closing tokens
|
||||
while (!(str[i] == '*' && str[i+1] == '/')) {
|
||||
i++;
|
||||
if (i > str.size()-2)
|
||||
return fail(
|
||||
"unexpected end of input inside multi-line comment", false);
|
||||
}
|
||||
i += 2;
|
||||
comment_found = true;
|
||||
}
|
||||
else
|
||||
return fail("malformed comment", false);
|
||||
}
|
||||
return comment_found;
|
||||
}
|
||||
|
||||
/* consume_garbage()
|
||||
*
|
||||
* Advance until the current character is non-whitespace and non-comment.
|
||||
*/
|
||||
void consume_garbage() {
|
||||
consume_whitespace();
|
||||
if(strategy == JsonParse::COMMENTS) {
|
||||
bool comment_found = false;
|
||||
do {
|
||||
comment_found = consume_comment();
|
||||
if (failed) return;
|
||||
consume_whitespace();
|
||||
}
|
||||
while(comment_found);
|
||||
}
|
||||
}
|
||||
|
||||
/* get_next_token()
|
||||
*
|
||||
* Return the next non-whitespace character. If the end of the input is reached,
|
||||
* flag an error and return 0.
|
||||
*/
|
||||
char get_next_token() {
|
||||
consume_garbage();
|
||||
if (failed) return static_cast<char>(0);
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input", static_cast<char>(0));
|
||||
|
||||
return str[i++];
|
||||
}
|
||||
|
||||
/* encode_utf8(pt, out)
|
||||
*
|
||||
* Encode pt as UTF-8 and add it to out.
|
||||
*/
|
||||
void encode_utf8(long pt, string & out) {
|
||||
if (pt < 0)
|
||||
return;
|
||||
|
||||
if (pt < 0x80) {
|
||||
out += static_cast<char>(pt);
|
||||
} else if (pt < 0x800) {
|
||||
out += static_cast<char>((pt >> 6) | 0xC0);
|
||||
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||
} else if (pt < 0x10000) {
|
||||
out += static_cast<char>((pt >> 12) | 0xE0);
|
||||
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
|
||||
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||
} else {
|
||||
out += static_cast<char>((pt >> 18) | 0xF0);
|
||||
out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
|
||||
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
|
||||
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
/* parse_string()
|
||||
*
|
||||
* Parse a string, starting at the current position.
|
||||
*/
|
||||
string parse_string() {
|
||||
string out;
|
||||
long last_escaped_codepoint = -1;
|
||||
while (true) {
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input in string", "");
|
||||
|
||||
char ch = str[i++];
|
||||
|
||||
if (ch == '"') {
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
if (in_range(ch, 0, 0x1f))
|
||||
return fail("unescaped " + esc(ch) + " in string", "");
|
||||
|
||||
// The usual case: non-escaped characters
|
||||
if (ch != '\\') {
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
last_escaped_codepoint = -1;
|
||||
out += ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle escapes
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input in string", "");
|
||||
|
||||
ch = str[i++];
|
||||
|
||||
if (ch == 'u') {
|
||||
// Extract 4-byte escape sequence
|
||||
string esc = str.substr(i, 4);
|
||||
// Explicitly check length of the substring. The following loop
|
||||
// relies on std::string returning the terminating NUL when
|
||||
// accessing str[length]. Checking here reduces brittleness.
|
||||
if (esc.length() < 4) {
|
||||
return fail("bad \\u escape: " + esc, "");
|
||||
}
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
|
||||
&& !in_range(esc[j], '0', '9'))
|
||||
return fail("bad \\u escape: " + esc, "");
|
||||
}
|
||||
|
||||
long codepoint = strtol(esc.data(), nullptr, 16);
|
||||
|
||||
// JSON specifies that characters outside the BMP shall be encoded as a pair
|
||||
// of 4-hex-digit \u escapes encoding their surrogate pair components. Check
|
||||
// whether we're in the middle of such a beast: the previous codepoint was an
|
||||
// escaped lead (high) surrogate, and this is a trail (low) surrogate.
|
||||
if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
|
||||
&& in_range(codepoint, 0xDC00, 0xDFFF)) {
|
||||
// Reassemble the two surrogate pairs into one astral-plane character, per
|
||||
// the UTF-16 algorithm.
|
||||
encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
|
||||
| (codepoint - 0xDC00)) + 0x10000, out);
|
||||
last_escaped_codepoint = -1;
|
||||
} else {
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
last_escaped_codepoint = codepoint;
|
||||
}
|
||||
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
last_escaped_codepoint = -1;
|
||||
|
||||
if (ch == 'b') {
|
||||
out += '\b';
|
||||
} else if (ch == 'f') {
|
||||
out += '\f';
|
||||
} else if (ch == 'n') {
|
||||
out += '\n';
|
||||
} else if (ch == 'r') {
|
||||
out += '\r';
|
||||
} else if (ch == 't') {
|
||||
out += '\t';
|
||||
} else if (ch == '"' || ch == '\\' || ch == '/') {
|
||||
out += ch;
|
||||
} else {
|
||||
return fail("invalid escape character " + esc(ch), "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* parse_number()
|
||||
*
|
||||
* Parse a double.
|
||||
*/
|
||||
Json parse_number() {
|
||||
size_t start_pos = i;
|
||||
|
||||
if (str[i] == '-')
|
||||
i++;
|
||||
|
||||
// Integer part
|
||||
if (str[i] == '0') {
|
||||
i++;
|
||||
if (in_range(str[i], '0', '9'))
|
||||
return fail("leading 0s not permitted in numbers");
|
||||
} else if (in_range(str[i], '1', '9')) {
|
||||
i++;
|
||||
while (in_range(str[i], '0', '9'))
|
||||
i++;
|
||||
} else {
|
||||
return fail("invalid " + esc(str[i]) + " in number");
|
||||
}
|
||||
|
||||
if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
|
||||
&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
|
||||
return std::atoi(str.c_str() + start_pos);
|
||||
}
|
||||
|
||||
// Decimal part
|
||||
if (str[i] == '.') {
|
||||
i++;
|
||||
if (!in_range(str[i], '0', '9'))
|
||||
return fail("at least one digit required in fractional part");
|
||||
|
||||
while (in_range(str[i], '0', '9'))
|
||||
i++;
|
||||
}
|
||||
|
||||
// Exponent part
|
||||
if (str[i] == 'e' || str[i] == 'E') {
|
||||
i++;
|
||||
|
||||
if (str[i] == '+' || str[i] == '-')
|
||||
i++;
|
||||
|
||||
if (!in_range(str[i], '0', '9'))
|
||||
return fail("at least one digit required in exponent");
|
||||
|
||||
while (in_range(str[i], '0', '9'))
|
||||
i++;
|
||||
}
|
||||
|
||||
return std::strtod(str.c_str() + start_pos, nullptr);
|
||||
}
|
||||
|
||||
/* expect(str, res)
|
||||
*
|
||||
* Expect that 'str' starts at the character that was just read. If it does, advance
|
||||
* the input and return res. If not, flag an error.
|
||||
*/
|
||||
Json expect(const string &expected, Json res) {
|
||||
assert(i != 0);
|
||||
i--;
|
||||
if (str.compare(i, expected.length(), expected) == 0) {
|
||||
i += expected.length();
|
||||
return res;
|
||||
} else {
|
||||
return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
|
||||
}
|
||||
}
|
||||
|
||||
/* parse_json()
|
||||
*
|
||||
* Parse a JSON object.
|
||||
*/
|
||||
Json parse_json(int depth) {
|
||||
if (depth > max_depth) {
|
||||
return fail("exceeded maximum nesting depth");
|
||||
}
|
||||
|
||||
char ch = get_next_token();
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
if (ch == '-' || (ch >= '0' && ch <= '9')) {
|
||||
i--;
|
||||
return parse_number();
|
||||
}
|
||||
|
||||
if (ch == 't')
|
||||
return expect("true", true);
|
||||
|
||||
if (ch == 'f')
|
||||
return expect("false", false);
|
||||
|
||||
if (ch == 'n')
|
||||
return expect("null", Json());
|
||||
|
||||
if (ch == '"')
|
||||
return parse_string();
|
||||
|
||||
if (ch == '{') {
|
||||
map<string, Json> data;
|
||||
ch = get_next_token();
|
||||
if (ch == '}')
|
||||
return data;
|
||||
|
||||
while (1) {
|
||||
if (ch != '"')
|
||||
return fail("expected '\"' in object, got " + esc(ch));
|
||||
|
||||
string key = parse_string();
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
ch = get_next_token();
|
||||
if (ch != ':')
|
||||
return fail("expected ':' in object, got " + esc(ch));
|
||||
|
||||
data[std::move(key)] = parse_json(depth + 1);
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
ch = get_next_token();
|
||||
if (ch == '}')
|
||||
break;
|
||||
if (ch != ',')
|
||||
return fail("expected ',' in object, got " + esc(ch));
|
||||
|
||||
ch = get_next_token();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
if (ch == '[') {
|
||||
vector<Json> data;
|
||||
ch = get_next_token();
|
||||
if (ch == ']')
|
||||
return data;
|
||||
|
||||
while (1) {
|
||||
i--;
|
||||
data.push_back(parse_json(depth + 1));
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
ch = get_next_token();
|
||||
if (ch == ']')
|
||||
break;
|
||||
if (ch != ',')
|
||||
return fail("expected ',' in list, got " + esc(ch));
|
||||
|
||||
ch = get_next_token();
|
||||
(void)ch;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
return fail("expected value, got " + esc(ch));
|
||||
}
|
||||
};
|
||||
}//namespace {
|
||||
|
||||
Json Json::parse(const string &in, string &err, JsonParse strategy) {
|
||||
JsonParser parser { in, 0, err, false, strategy };
|
||||
Json result = parser.parse_json(0);
|
||||
|
||||
// Check for any trailing garbage
|
||||
parser.consume_garbage();
|
||||
if (parser.failed)
|
||||
return Json();
|
||||
if (parser.i != in.size())
|
||||
return parser.fail("unexpected trailing " + esc(in[parser.i]));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Documented in json11.hpp
|
||||
vector<Json> Json::parse_multi(const string &in,
|
||||
std::string::size_type &parser_stop_pos,
|
||||
string &err,
|
||||
JsonParse strategy) {
|
||||
JsonParser parser { in, 0, err, false, strategy };
|
||||
parser_stop_pos = 0;
|
||||
vector<Json> json_vec;
|
||||
while (parser.i != in.size() && !parser.failed) {
|
||||
json_vec.push_back(parser.parse_json(0));
|
||||
if (parser.failed)
|
||||
break;
|
||||
|
||||
// Check for another object
|
||||
parser.consume_garbage();
|
||||
if (parser.failed)
|
||||
break;
|
||||
parser_stop_pos = parser.i;
|
||||
}
|
||||
return json_vec;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Shape-checking
|
||||
*/
|
||||
|
||||
bool Json::has_shape(const shape & types, string & err) const {
|
||||
if (!is_object()) {
|
||||
err = "expected JSON object, got " + dump();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto & item : types) {
|
||||
if ((*this)[item.first].type() != item.second) {
|
||||
err = "bad type for " + item.first + " in " + dump();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace json11
|
|
@ -0,0 +1,232 @@
|
|||
/* json11
|
||||
*
|
||||
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
|
||||
*
|
||||
* The core object provided by the library is json11::Json. A Json object represents any JSON
|
||||
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
|
||||
* object (std::map).
|
||||
*
|
||||
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
|
||||
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
|
||||
* Json::parse (static) to parse a std::string as a Json object.
|
||||
*
|
||||
* Internally, the various types of Json object are represented by the JsonValue class
|
||||
* hierarchy.
|
||||
*
|
||||
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
|
||||
* so some JSON implementations distinguish between integers and floating-point numbers, while
|
||||
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
|
||||
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
|
||||
* to JSON that will be *silently* changed by a round-trip through those implementations.
|
||||
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
|
||||
* provides integer helpers.
|
||||
*
|
||||
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
|
||||
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
|
||||
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
|
||||
* will be exact for +/- 275 years.)
|
||||
*/
|
||||
|
||||
/* Copyright (c) 2013 Dropbox, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <initializer_list>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER <= 1800 // VS 2013
|
||||
#ifndef noexcept
|
||||
#define noexcept throw()
|
||||
#endif
|
||||
|
||||
#ifndef snprintf
|
||||
#define snprintf _snprintf_s
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace json11 {
|
||||
|
||||
enum JsonParse {
|
||||
STANDARD, COMMENTS
|
||||
};
|
||||
|
||||
class JsonValue;
|
||||
|
||||
class Json final {
|
||||
public:
|
||||
// Types
|
||||
enum Type {
|
||||
NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
|
||||
};
|
||||
|
||||
// Array and object typedefs
|
||||
typedef std::vector<Json> array;
|
||||
typedef std::map<std::string, Json> object;
|
||||
|
||||
// Constructors for the various types of JSON value.
|
||||
Json() noexcept; // NUL
|
||||
Json(std::nullptr_t) noexcept; // NUL
|
||||
Json(double value); // NUMBER
|
||||
Json(int value); // NUMBER
|
||||
Json(bool value); // BOOL
|
||||
Json(const std::string &value); // STRING
|
||||
Json(std::string &&value); // STRING
|
||||
Json(const char * value); // STRING
|
||||
Json(const array &values); // ARRAY
|
||||
Json(array &&values); // ARRAY
|
||||
Json(const object &values); // OBJECT
|
||||
Json(object &&values); // OBJECT
|
||||
|
||||
// Implicit constructor: anything with a to_json() function.
|
||||
template <class T, class = decltype(&T::to_json)>
|
||||
Json(const T & t) : Json(t.to_json()) {}
|
||||
|
||||
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
|
||||
template <class M, typename std::enable_if<
|
||||
std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
|
||||
&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
|
||||
int>::type = 0>
|
||||
Json(const M & m) : Json(object(m.begin(), m.end())) {}
|
||||
|
||||
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
|
||||
template <class V, typename std::enable_if<
|
||||
std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
|
||||
int>::type = 0>
|
||||
Json(const V & v) : Json(array(v.begin(), v.end())) {}
|
||||
|
||||
// This prevents Json(some_pointer) from accidentally producing a bool. Use
|
||||
// Json(bool(some_pointer)) if that behavior is desired.
|
||||
Json(void *) = delete;
|
||||
|
||||
// Accessors
|
||||
Type type() const;
|
||||
|
||||
bool is_null() const { return type() == NUL; }
|
||||
bool is_number() const { return type() == NUMBER; }
|
||||
bool is_bool() const { return type() == BOOL; }
|
||||
bool is_string() const { return type() == STRING; }
|
||||
bool is_array() const { return type() == ARRAY; }
|
||||
bool is_object() const { return type() == OBJECT; }
|
||||
|
||||
// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
|
||||
// distinguish between integer and non-integer numbers - number_value() and int_value()
|
||||
// can both be applied to a NUMBER-typed object.
|
||||
double number_value() const;
|
||||
int int_value() const;
|
||||
|
||||
// Return the enclosed value if this is a boolean, false otherwise.
|
||||
bool bool_value() const;
|
||||
// Return the enclosed string if this is a string, "" otherwise.
|
||||
const std::string &string_value() const;
|
||||
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
|
||||
const array &array_items() const;
|
||||
// Return the enclosed std::map if this is an object, or an empty map otherwise.
|
||||
const object &object_items() const;
|
||||
|
||||
// Return a reference to arr[i] if this is an array, Json() otherwise.
|
||||
const Json & operator[](size_t i) const;
|
||||
// Return a reference to obj[key] if this is an object, Json() otherwise.
|
||||
const Json & operator[](const std::string &key) const;
|
||||
|
||||
// Serialize.
|
||||
void dump(std::string &out) const;
|
||||
std::string dump() const {
|
||||
std::string out;
|
||||
dump(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Parse. If parse fails, return Json() and assign an error message to err.
|
||||
static Json parse(const std::string & in,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD);
|
||||
static Json parse(const char * in,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD) {
|
||||
if (in) {
|
||||
return parse(std::string(in), err, strategy);
|
||||
} else {
|
||||
err = "null input";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
// Parse multiple objects, concatenated or separated by whitespace
|
||||
static std::vector<Json> parse_multi(
|
||||
const std::string & in,
|
||||
std::string::size_type & parser_stop_pos,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD);
|
||||
|
||||
static inline std::vector<Json> parse_multi(
|
||||
const std::string & in,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD) {
|
||||
std::string::size_type parser_stop_pos;
|
||||
return parse_multi(in, parser_stop_pos, err, strategy);
|
||||
}
|
||||
|
||||
bool operator== (const Json &rhs) const;
|
||||
bool operator< (const Json &rhs) const;
|
||||
bool operator!= (const Json &rhs) const { return !(*this == rhs); }
|
||||
bool operator<= (const Json &rhs) const { return !(rhs < *this); }
|
||||
bool operator> (const Json &rhs) const { return (rhs < *this); }
|
||||
bool operator>= (const Json &rhs) const { return !(*this < rhs); }
|
||||
|
||||
/* has_shape(types, err)
|
||||
*
|
||||
* Return true if this is a JSON object and, for each item in types, has a field of
|
||||
* the given type. If not, return false and set err to a descriptive message.
|
||||
*/
|
||||
typedef std::initializer_list<std::pair<std::string, Type>> shape;
|
||||
bool has_shape(const shape & types, std::string & err) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<JsonValue> m_ptr;
|
||||
};
|
||||
|
||||
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
|
||||
class JsonValue {
|
||||
protected:
|
||||
friend class Json;
|
||||
friend class JsonInt;
|
||||
friend class JsonDouble;
|
||||
virtual Json::Type type() const = 0;
|
||||
virtual bool equals(const JsonValue * other) const = 0;
|
||||
virtual bool less(const JsonValue * other) const = 0;
|
||||
virtual void dump(std::string &out) const = 0;
|
||||
virtual double number_value() const;
|
||||
virtual int int_value() const;
|
||||
virtual bool bool_value() const;
|
||||
virtual const std::string &string_value() const;
|
||||
virtual const Json::array &array_items() const;
|
||||
virtual const Json &operator[](size_t i) const;
|
||||
virtual const Json::object &object_items() const;
|
||||
virtual const Json &operator[](const std::string &key) const;
|
||||
virtual ~JsonValue() {}
|
||||
};
|
||||
|
||||
} // namespace json11
|
|
@ -1081,6 +1081,8 @@ class WConstructor:
|
|||
con.args = []
|
||||
con.duplicate = False
|
||||
con.protected = protected
|
||||
if str.startswith(str_def, "inline "):
|
||||
str_def = str_def[7:]
|
||||
if not str.startswith(str_def, class_.name + "("):
|
||||
return None
|
||||
str_def = str_def[len(class_.name)+1:]
|
||||
|
|
|
@ -32,7 +32,8 @@ struct EquivOptPass:public ScriptPass
|
|||
log("\n");
|
||||
log(" equiv_opt [options] [command]\n");
|
||||
log("\n");
|
||||
log("This command checks circuit equivalence before and after an optimization pass.\n");
|
||||
log("This command uses temporal induction to check circuit equivalence before and\n");
|
||||
log("after an optimization pass.\n");
|
||||
log("\n");
|
||||
log(" -run <from_label>:<to_label>\n");
|
||||
log(" only run the commands between the labels (see below). an empty\n");
|
||||
|
@ -156,6 +157,8 @@ struct EquivOptPass:public ScriptPass
|
|||
if (check_label("prove")) {
|
||||
if (multiclock || help_mode)
|
||||
run("clk2fflogic", "(only with -multiclock)");
|
||||
if (!multiclock || help_mode)
|
||||
run("async2sync", "(only without -multiclock)");
|
||||
run("equiv_make gold gate equiv");
|
||||
if (help_mode)
|
||||
run("equiv_induct [-undef] equiv");
|
||||
|
|
|
@ -198,6 +198,7 @@ struct Async2syncPass : public Pass {
|
|||
module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q);
|
||||
}
|
||||
|
||||
cell->setPort("\\D", sig_q);
|
||||
cell->setPort("\\Q", new_q);
|
||||
cell->unsetPort("\\EN");
|
||||
cell->unsetParam("\\EN_POLARITY");
|
||||
|
|
|
@ -417,7 +417,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
|
|||
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
|
||||
|
||||
buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig");
|
||||
ifs.open(buffer);
|
||||
ifs.open(buffer, std::ifstream::binary);
|
||||
if (ifs.fail())
|
||||
log_error("Can't open ABC output file `%s'.\n", buffer.c_str());
|
||||
|
||||
|
|
|
@ -584,6 +584,50 @@ module FDSE_1 (
|
|||
`endif
|
||||
endmodule
|
||||
|
||||
module LDCE (
|
||||
output reg Q,
|
||||
(* invertible_pin = "IS_CLR_INVERTED" *)
|
||||
input CLR,
|
||||
input D,
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G,
|
||||
input GE
|
||||
);
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
parameter [0:0] IS_CLR_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
initial Q = INIT;
|
||||
wire clr = CLR ^ IS_CLR_INVERTED;
|
||||
wire g = G ^ IS_G_INVERTED;
|
||||
always @*
|
||||
if (clr) Q = 1'b0;
|
||||
else if (GE && g) Q = D;
|
||||
endmodule
|
||||
|
||||
module LDPE (
|
||||
output reg Q,
|
||||
input D,
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G,
|
||||
input GE,
|
||||
(* invertible_pin = "IS_PRE_INVERTED" *)
|
||||
input PRE
|
||||
);
|
||||
parameter [0:0] INIT = 1'b1;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_PRE_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
initial Q = INIT;
|
||||
wire g = G ^ IS_G_INVERTED;
|
||||
wire pre = PRE ^ IS_PRE_INVERTED;
|
||||
always @*
|
||||
if (pre) Q = 1'b1;
|
||||
else if (GE && g) Q = D;
|
||||
endmodule
|
||||
|
||||
module RAM32X1D (
|
||||
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957
|
||||
(* abc_arrival=1153 *)
|
||||
|
|
|
@ -108,8 +108,8 @@ XC6S_CELLS = [
|
|||
# Cell('FDRE'),
|
||||
# Cell('FDSE'),
|
||||
Cell('IDDR2', port_attrs={'C0': ['clkbuf_sink'], 'C1': ['clkbuf_sink']}),
|
||||
Cell('LDCE'),
|
||||
Cell('LDPE'),
|
||||
# Cell('LDCE'),
|
||||
# Cell('LDPE'),
|
||||
Cell('ODDR2', port_attrs={'C0': ['clkbuf_sink'], 'C1': ['clkbuf_sink']}),
|
||||
|
||||
# Slice/CLB primitives.
|
||||
|
|
|
@ -1793,36 +1793,6 @@ module IDDR2 (...);
|
|||
input S;
|
||||
endmodule
|
||||
|
||||
module LDCE (...);
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
parameter [0:0] IS_CLR_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
output Q;
|
||||
(* invertible_pin = "IS_CLR_INVERTED" *)
|
||||
input CLR;
|
||||
input D;
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G;
|
||||
input GE;
|
||||
endmodule
|
||||
|
||||
module LDPE (...);
|
||||
parameter [0:0] INIT = 1'b1;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_PRE_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
output Q;
|
||||
input D;
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G;
|
||||
input GE;
|
||||
(* invertible_pin = "IS_PRE_INVERTED" *)
|
||||
input PRE;
|
||||
endmodule
|
||||
|
||||
module ODDR2 (...);
|
||||
parameter DDR_ALIGNMENT = "NONE";
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
|
|
|
@ -18,7 +18,12 @@
|
|||
*/
|
||||
|
||||
// ============================================================================
|
||||
// FF mapping
|
||||
// FF mapping for Spartan 6. The primitives used are the same as Series 7,
|
||||
// but with one major difference: the initial value is implied by the
|
||||
// primitive type used (FFs with reset pin must have INIT set to 0 or x, FFs
|
||||
// with set pin must have INIT set to 1 or x). For Yosys primitives without
|
||||
// set/reset, this means we have to pick the primitive type based on the INIT
|
||||
// value.
|
||||
|
||||
`ifndef _NO_FFS
|
||||
|
||||
|
@ -29,6 +34,7 @@ module \$_DFF_N_ (input D, C, output Q);
|
|||
else
|
||||
FDRE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_P_ (input D, C, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
|
@ -37,6 +43,7 @@ module \$_DFF_P_ (input D, C, output Q);
|
|||
else
|
||||
FDRE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFFE_NP_ (input D, C, E, output Q);
|
||||
|
@ -46,6 +53,7 @@ module \$_DFFE_NP_ (input D, C, E, output Q);
|
|||
else
|
||||
FDRE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFFE_PP_ (input D, C, E, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
|
@ -54,6 +62,7 @@ module \$_DFFE_PP_ (input D, C, E, output Q);
|
|||
else
|
||||
FDRE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFF_NN0_ (input D, C, R, output Q);
|
||||
|
@ -63,6 +72,7 @@ module \$_DFF_NN0_ (input D, C, R, output Q);
|
|||
else
|
||||
FDCE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_NP0_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
|
@ -71,6 +81,7 @@ module \$_DFF_NP0_ (input D, C, R, output Q);
|
|||
else
|
||||
FDCE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PN0_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
|
@ -79,6 +90,7 @@ module \$_DFF_PN0_ (input D, C, R, output Q);
|
|||
else
|
||||
FDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PP0_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
|
@ -87,6 +99,7 @@ module \$_DFF_PP0_ (input D, C, R, output Q);
|
|||
else
|
||||
FDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFF_NN1_ (input D, C, R, output Q);
|
||||
|
@ -96,6 +109,7 @@ module \$_DFF_NN1_ (input D, C, R, output Q);
|
|||
else
|
||||
FDPE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_NP1_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
|
@ -104,6 +118,7 @@ module \$_DFF_NP1_ (input D, C, R, output Q);
|
|||
else
|
||||
FDPE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PN1_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
|
@ -112,6 +127,7 @@ module \$_DFF_PN1_ (input D, C, R, output Q);
|
|||
else
|
||||
FDPE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PP1_ (input D, C, R, output Q);
|
||||
parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
|
@ -120,6 +136,26 @@ module \$_DFF_PP1_ (input D, C, R, output Q);
|
|||
else
|
||||
FDPE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DLATCH_N_ (input E, D, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
LDPE #(.INIT(_TECHMAP_WIREINIT_Q_), .IS_G_INVERTED(1'b1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .PRE(1'b0));
|
||||
else
|
||||
LDCE #(.INIT(_TECHMAP_WIREINIT_Q_), .IS_G_INVERTED(1'b1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .CLR(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DLATCH_P_ (input E, D, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
generate if (_TECHMAP_WIREINIT_Q_ === 1'b1)
|
||||
LDPE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .PRE(1'b0));
|
||||
else
|
||||
LDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .CLR(1'b0));
|
||||
endgenerate
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
`endif
|
||||
|
|
|
@ -2648,36 +2648,6 @@ module IDDR_2CLK (...);
|
|||
input S;
|
||||
endmodule
|
||||
|
||||
module LDCE (...);
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
parameter [0:0] IS_CLR_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
output Q;
|
||||
(* invertible_pin = "IS_CLR_INVERTED" *)
|
||||
input CLR;
|
||||
input D;
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G;
|
||||
input GE;
|
||||
endmodule
|
||||
|
||||
module LDPE (...);
|
||||
parameter [0:0] INIT = 1'b1;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_PRE_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
output Q;
|
||||
input D;
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G;
|
||||
input GE;
|
||||
(* invertible_pin = "IS_PRE_INVERTED" *)
|
||||
input PRE;
|
||||
endmodule
|
||||
|
||||
module ODDR (...);
|
||||
parameter DDR_CLK_EDGE = "OPPOSITE_EDGE";
|
||||
parameter INIT = 1'b0;
|
||||
|
|
|
@ -5149,36 +5149,6 @@ module IDDR_2CLK (...);
|
|||
input S;
|
||||
endmodule
|
||||
|
||||
module LDCE (...);
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
parameter [0:0] IS_CLR_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
output Q;
|
||||
(* invertible_pin = "IS_CLR_INVERTED" *)
|
||||
input CLR;
|
||||
input D;
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G;
|
||||
input GE;
|
||||
endmodule
|
||||
|
||||
module LDPE (...);
|
||||
parameter [0:0] INIT = 1'b1;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_PRE_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
output Q;
|
||||
input D;
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G;
|
||||
input GE;
|
||||
(* invertible_pin = "IS_PRE_INVERTED" *)
|
||||
input PRE;
|
||||
endmodule
|
||||
|
||||
module ODDR (...);
|
||||
parameter DDR_CLK_EDGE = "OPPOSITE_EDGE";
|
||||
parameter INIT = 1'b0;
|
||||
|
|
|
@ -18,60 +18,98 @@
|
|||
*/
|
||||
|
||||
// ============================================================================
|
||||
// FF mapping
|
||||
// FF mapping for Virtex 6, Series 7 and Ultrascale. These families support
|
||||
// the following features:
|
||||
//
|
||||
// - a CLB flip-flop can be used as a latch or as a flip-flop
|
||||
// - a CLB flip-flop has the following pins:
|
||||
//
|
||||
// - data input
|
||||
// - clock (or gate for latches) (with optional inversion)
|
||||
// - clock enable (or gate enable, which is just ANDed with gate — unused by
|
||||
// synthesis)
|
||||
// - either a set or a reset input, which (for FFs) can be either
|
||||
// synchronous or asynchronous (with optional inversion)
|
||||
// - data output
|
||||
//
|
||||
// - a flip-flop also has an initial value, which is set at device
|
||||
// initialization (or whenever GSR is asserted)
|
||||
|
||||
`ifndef _NO_FFS
|
||||
|
||||
module \$_DFF_N_ (input D, C, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDRE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_P_ (input D, C, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDRE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFFE_NP_ (input D, C, E, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDRE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFFE_PP_ (input D, C, E, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDRE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFF_NN0_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDCE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_NP0_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDCE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PN0_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PP0_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DFF_NN1_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDPE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_NP1_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDPE_1 #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PN1_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDPE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DFF_PP1_ (input D, C, R, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
FDPE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
module \$_DLATCH_N_ (input E, D, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
LDCE #(.INIT(_TECHMAP_WIREINIT_Q_), .IS_G_INVERTED(1'b1)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .CLR(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
module \$_DLATCH_P_ (input E, D, output Q);
|
||||
parameter _TECHMAP_WIREINIT_Q_ = 1'bx;
|
||||
LDCE #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .G(E), .GE(1'b1), .CLR(1'b0));
|
||||
wire _TECHMAP_REMOVEINIT_Q_ = 1;
|
||||
endmodule
|
||||
|
||||
`endif
|
||||
|
|
|
@ -10731,36 +10731,6 @@ module IDDRE1 (...);
|
|||
input R;
|
||||
endmodule
|
||||
|
||||
module LDCE (...);
|
||||
parameter [0:0] INIT = 1'b0;
|
||||
parameter [0:0] IS_CLR_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
output Q;
|
||||
(* invertible_pin = "IS_CLR_INVERTED" *)
|
||||
input CLR;
|
||||
input D;
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G;
|
||||
input GE;
|
||||
endmodule
|
||||
|
||||
module LDPE (...);
|
||||
parameter [0:0] INIT = 1'b1;
|
||||
parameter [0:0] IS_G_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_PRE_INVERTED = 1'b0;
|
||||
parameter MSGON = "TRUE";
|
||||
parameter XON = "TRUE";
|
||||
output Q;
|
||||
input D;
|
||||
(* invertible_pin = "IS_G_INVERTED" *)
|
||||
input G;
|
||||
input GE;
|
||||
(* invertible_pin = "IS_PRE_INVERTED" *)
|
||||
input PRE;
|
||||
endmodule
|
||||
|
||||
module ODDRE1 (...);
|
||||
parameter [0:0] IS_C_INVERTED = 1'b0;
|
||||
parameter [0:0] IS_D1_INVERTED = 1'b0;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
*.log
|
|
@ -0,0 +1,8 @@
|
|||
module top(input [3:0] i, output [3:0] o);
|
||||
python_inv #(
|
||||
.width(4)
|
||||
) inv (
|
||||
.i(i),
|
||||
.o(o),
|
||||
);
|
||||
endmodule
|
|
@ -0,0 +1,5 @@
|
|||
connect_rpc -exec python3 frontend.py stdio
|
||||
read_verilog design.v
|
||||
hierarchy -top top
|
||||
flatten
|
||||
select -assert-count 1 t:$neg
|
|
@ -0,0 +1,126 @@
|
|||
def modules():
|
||||
return ["python_inv"]
|
||||
|
||||
def derive(module, parameters):
|
||||
assert module == r"python_inv"
|
||||
if parameters.keys() != {r"\width"}:
|
||||
raise ValueError("Invalid parameters")
|
||||
return "ilang", r"""
|
||||
module \impl
|
||||
wire width {width:d} input 1 \i
|
||||
wire width {width:d} output 2 \o
|
||||
cell $neg $0
|
||||
parameter \A_SIGNED 1'0
|
||||
parameter \A_WIDTH 32'{width:b}
|
||||
parameter \Y_WIDTH 32'{width:b}
|
||||
connect \A \i
|
||||
connect \Y \o
|
||||
end
|
||||
end
|
||||
module \python_inv
|
||||
wire width {width:d} input 1 \i
|
||||
wire width {width:d} output 2 \o
|
||||
cell \impl $0
|
||||
connect \i \i
|
||||
connect \o \o
|
||||
end
|
||||
end
|
||||
""".format(width=parameters[r"\width"])
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import sys, socket, os
|
||||
try:
|
||||
import msvcrt, win32pipe, win32file
|
||||
except ImportError:
|
||||
msvcrt = win32pipe = win32file = None
|
||||
|
||||
def map_parameter(parameter):
|
||||
if parameter["type"] == "unsigned":
|
||||
return int(parameter["value"], 2)
|
||||
if parameter["type"] == "signed":
|
||||
width = len(parameter["value"])
|
||||
value = int(parameter["value"], 2)
|
||||
if value & (1 << (width - 1)):
|
||||
value = -((1 << width) - value)
|
||||
return value
|
||||
if parameter["type"] == "string":
|
||||
return parameter["value"]
|
||||
if parameter["type"] == "real":
|
||||
return float(parameter["value"])
|
||||
|
||||
def call(input_json):
|
||||
input = json.loads(input_json)
|
||||
if input["method"] == "modules":
|
||||
return json.dumps({"modules": modules()})
|
||||
if input["method"] == "derive":
|
||||
try:
|
||||
frontend, source = derive(input["module"],
|
||||
{name: map_parameter(value) for name, value in input["parameters"].items()})
|
||||
return json.dumps({"frontend": frontend, "source": source})
|
||||
except ValueError as e:
|
||||
return json.dumps({"error": str(e)})
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
modes = parser.add_subparsers(dest="mode")
|
||||
mode_stdio = modes.add_parser("stdio")
|
||||
if os.name == "posix":
|
||||
mode_path = modes.add_parser("unix-socket")
|
||||
if os.name == "nt":
|
||||
mode_path = modes.add_parser("named-pipe")
|
||||
mode_path.add_argument("path")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.mode == "stdio":
|
||||
while True:
|
||||
input = sys.stdin.readline()
|
||||
if not input: break
|
||||
sys.stdout.write(call(input) + "\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
if args.mode == "unix-socket":
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.bind(args.path)
|
||||
try:
|
||||
sock.listen(1)
|
||||
conn, addr = sock.accept()
|
||||
file = conn.makefile("rw")
|
||||
while True:
|
||||
input = file.readline()
|
||||
if not input: break
|
||||
file.write(call(input) + "\n")
|
||||
file.flush()
|
||||
finally:
|
||||
sock.close()
|
||||
os.unlink(args.path)
|
||||
|
||||
if args.mode == "named-pipe":
|
||||
pipe = win32pipe.CreateNamedPipe(args.path, win32pipe.PIPE_ACCESS_DUPLEX,
|
||||
win32pipe.PIPE_TYPE_BYTE|win32pipe.PIPE_READMODE_BYTE|win32pipe.PIPE_WAIT,
|
||||
1, 4096, 4096, 0, None)
|
||||
win32pipe.ConnectNamedPipe(pipe, None)
|
||||
try:
|
||||
while True:
|
||||
input = b""
|
||||
while not input.endswith(b"\n"):
|
||||
result, data = win32file.ReadFile(pipe, 4096)
|
||||
assert result == 0
|
||||
input += data
|
||||
assert not b"\n" in input or input.endswith(b"\n")
|
||||
output = (call(input.decode("utf-8")) + "\n").encode("utf-8")
|
||||
length = len(output)
|
||||
while length > 0:
|
||||
result, done = win32file.WriteFile(pipe, output)
|
||||
assert result == 0
|
||||
length -= done
|
||||
except win32file.error as e:
|
||||
if e.args[0] == 109: # ERROR_BROKEN_PIPE
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
for x in *.ys; do
|
||||
echo "Running $x.."
|
||||
../../yosys -ql ${x%.ys}.log $x
|
||||
done
|
|
@ -0,0 +1,6 @@
|
|||
!python3 frontend.py unix-socket frontend.sock & sleep 0.1
|
||||
connect_rpc -path frontend.sock
|
||||
read_verilog design.v
|
||||
hierarchy -top top
|
||||
flatten
|
||||
select -assert-count 1 t:$neg
|
|
@ -0,0 +1,58 @@
|
|||
module latchp
|
||||
( input d, en, output reg q );
|
||||
always @*
|
||||
if ( en )
|
||||
q <= d;
|
||||
endmodule
|
||||
|
||||
module latchn
|
||||
( input d, en, output reg q );
|
||||
always @*
|
||||
if ( !en )
|
||||
q <= d;
|
||||
endmodule
|
||||
|
||||
module latchsr
|
||||
( input d, en, clr, pre, output reg q );
|
||||
always @*
|
||||
if ( clr )
|
||||
q <= 1'b0;
|
||||
else if ( pre )
|
||||
q <= 1'b1;
|
||||
else if ( en )
|
||||
q <= d;
|
||||
endmodule
|
||||
|
||||
|
||||
module top (
|
||||
input clk,
|
||||
input clr,
|
||||
input pre,
|
||||
input a,
|
||||
output b,b1,b2
|
||||
);
|
||||
|
||||
|
||||
latchp u_latchp (
|
||||
.en (clk ),
|
||||
.d (a ),
|
||||
.q (b )
|
||||
);
|
||||
|
||||
|
||||
latchn u_latchn (
|
||||
.en (clk ),
|
||||
.d (a ),
|
||||
.q (b1 )
|
||||
);
|
||||
|
||||
|
||||
latchsr u_latchsr (
|
||||
.en (clk ),
|
||||
.clr (clr),
|
||||
.pre (pre),
|
||||
.d (a ),
|
||||
.q (b2 )
|
||||
);
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,15 @@
|
|||
read_verilog latches.v
|
||||
|
||||
proc
|
||||
flatten
|
||||
equiv_opt -assert -run :prove -map +/xilinx/cells_sim.v synth_xilinx # equivalency check
|
||||
async2sync
|
||||
equiv_opt -assert -run prove: -map +/xilinx/cells_sim.v synth_xilinx # equivalency check
|
||||
|
||||
design -load preopt
|
||||
synth_xilinx
|
||||
cd top
|
||||
select -assert-count 1 t:LUT1
|
||||
select -assert-count 2 t:LUT3
|
||||
select -assert-count 3 t:LDCE
|
||||
select -assert-none t:LUT1 t:LUT3 t:LDCE %% t:* %D
|
Loading…
Reference in New Issue