| /* |
| * yosys -- Yosys Open SYnthesis Suite |
| * |
| * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> |
| * |
| * 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. |
| * |
| */ |
| |
| #include "kernel/yosys.h" |
| |
| YOSYS_NAMESPACE_BEGIN |
| |
| struct JsonNode |
| { |
| char type; // S=String, N=Number, A=Array, D=Dict |
| string data_string; |
| int64_t data_number; |
| vector<JsonNode*> data_array; |
| dict<string, JsonNode*> data_dict; |
| vector<string> data_dict_keys; |
| |
| JsonNode(std::istream &f) |
| { |
| type = 0; |
| data_number = 0; |
| |
| while (1) |
| { |
| int ch = f.get(); |
| |
| if (ch == EOF) |
| log_error("Unexpected EOF in JSON file.\n"); |
| |
| if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') |
| continue; |
| |
| if (ch == '"') |
| { |
| type = 'S'; |
| |
| while (1) |
| { |
| ch = f.get(); |
| |
| if (ch == EOF) |
| log_error("Unexpected EOF in JSON string.\n"); |
| |
| if (ch == '"') |
| break; |
| |
| if (ch == '\\') { |
| int ch = f.get(); |
| |
| if (ch == EOF) |
| log_error("Unexpected EOF in JSON string.\n"); |
| } |
| |
| data_string += ch; |
| } |
| |
| break; |
| } |
| |
| if ('0' <= ch && ch <= '9') |
| { |
| type = 'N'; |
| data_number = ch - '0'; |
| data_string += ch; |
| |
| while (1) |
| { |
| ch = f.get(); |
| |
| if (ch == EOF) |
| break; |
| |
| if (ch == '.') |
| goto parse_real; |
| |
| if (ch < '0' || '9' < ch) { |
| f.unget(); |
| break; |
| } |
| |
| data_number = data_number*10 + (ch - '0'); |
| data_string += ch; |
| } |
| |
| data_string = ""; |
| break; |
| |
| parse_real: |
| type = 'S'; |
| data_number = 0; |
| data_string += ch; |
| |
| while (1) |
| { |
| ch = f.get(); |
| |
| if (ch == EOF) |
| break; |
| |
| if (ch < '0' || '9' < ch) { |
| f.unget(); |
| break; |
| } |
| |
| data_string += ch; |
| } |
| |
| break; |
| } |
| |
| if (ch == '[') |
| { |
| type = 'A'; |
| |
| while (1) |
| { |
| ch = f.get(); |
| |
| if (ch == EOF) |
| log_error("Unexpected EOF in JSON file.\n"); |
| |
| if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',') |
| continue; |
| |
| if (ch == ']') |
| break; |
| |
| f.unget(); |
| data_array.push_back(new JsonNode(f)); |
| } |
| |
| break; |
| } |
| |
| if (ch == '{') |
| { |
| type = 'D'; |
| |
| while (1) |
| { |
| ch = f.get(); |
| |
| if (ch == EOF) |
| log_error("Unexpected EOF in JSON file.\n"); |
| |
| if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',') |
| continue; |
| |
| if (ch == '}') |
| break; |
| |
| f.unget(); |
| JsonNode key(f); |
| |
| while (1) |
| { |
| ch = f.get(); |
| |
| if (ch == EOF) |
| log_error("Unexpected EOF in JSON file.\n"); |
| |
| if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ':') |
| continue; |
| |
| f.unget(); |
| break; |
| } |
| |
| JsonNode *value = new JsonNode(f); |
| |
| if (key.type != 'S') |
| log_error("Unexpected non-string key in JSON dict.\n"); |
| |
| data_dict[key.data_string] = value; |
| data_dict_keys.push_back(key.data_string); |
| } |
| |
| break; |
| } |
| |
| log_error("Unexpected character in JSON file: '%c'\n", ch); |
| } |
| } |
| |
| ~JsonNode() |
| { |
| for (auto it : data_array) |
| delete it; |
| for (auto &it : data_dict) |
| delete it.second; |
| } |
| }; |
| |
| Const json_parse_attr_param_value(JsonNode *node) |
| { |
| Const value; |
| |
| if (node->type == 'S') { |
| string &s = node->data_string; |
| size_t cursor = s.find_first_not_of("01xz"); |
| if (cursor == string::npos) { |
| value = Const::from_string(s); |
| } else if (s.find_first_not_of(' ', cursor) == string::npos) { |
| value = Const(s.substr(0, GetSize(s)-1)); |
| } else { |
| value = Const(s); |
| } |
| } else |
| if (node->type == 'N') { |
| value = Const(node->data_number, 32); |
| if (node->data_number < 0) |
| value.flags |= RTLIL::CONST_FLAG_SIGNED; |
| } else |
| if (node->type == 'A') { |
| log_error("JSON attribute or parameter value is an array.\n"); |
| } else |
| if (node->type == 'D') { |
| log_error("JSON attribute or parameter value is a dict.\n"); |
| } else { |
| log_abort(); |
| } |
| |
| return value; |
| } |
| |
| void json_parse_attr_param(dict<IdString, Const> &results, JsonNode *node) |
| { |
| if (node->type != 'D') |
| log_error("JSON attributes or parameters node is not a dictionary.\n"); |
| |
| for (auto it : node->data_dict) |
| { |
| IdString key = RTLIL::escape_id(it.first.c_str()); |
| Const value = json_parse_attr_param_value(it.second); |
| results[key] = value; |
| } |
| } |
| |
| void json_import(Design *design, string &modname, JsonNode *node) |
| { |
| log("Importing module %s from JSON tree.\n", modname.c_str()); |
| |
| Module *module = new RTLIL::Module; |
| module->name = RTLIL::escape_id(modname.c_str()); |
| |
| if (design->module(module->name)) |
| log_error("Re-definition of module %s.\n", log_id(module->name)); |
| |
| design->add(module); |
| |
| if (node->data_dict.count("attributes")) |
| json_parse_attr_param(module->attributes, node->data_dict.at("attributes")); |
| |
| dict<int, SigBit> signal_bits; |
| |
| if (node->data_dict.count("ports")) |
| { |
| JsonNode *ports_node = node->data_dict.at("ports"); |
| |
| if (ports_node->type != 'D') |
| log_error("JSON ports node is not a dictionary.\n"); |
| |
| for (int port_id = 1; port_id <= GetSize(ports_node->data_dict_keys); port_id++) |
| { |
| IdString port_name = RTLIL::escape_id(ports_node->data_dict_keys[port_id-1].c_str()); |
| JsonNode *port_node = ports_node->data_dict.at(ports_node->data_dict_keys[port_id-1]); |
| |
| if (port_node->type != 'D') |
| log_error("JSON port node '%s' is not a dictionary.\n", log_id(port_name)); |
| |
| if (port_node->data_dict.count("direction") == 0) |
| log_error("JSON port node '%s' has no direction attribute.\n", log_id(port_name)); |
| |
| if (port_node->data_dict.count("bits") == 0) |
| log_error("JSON port node '%s' has no bits attribute.\n", log_id(port_name)); |
| |
| JsonNode *port_direction_node = port_node->data_dict.at("direction"); |
| JsonNode *port_bits_node = port_node->data_dict.at("bits"); |
| |
| if (port_direction_node->type != 'S') |
| log_error("JSON port node '%s' has non-string direction attribute.\n", log_id(port_name)); |
| |
| if (port_bits_node->type != 'A') |
| log_error("JSON port node '%s' has non-array bits attribute.\n", log_id(port_name)); |
| |
| Wire *port_wire = module->wire(port_name); |
| |
| if (port_wire == nullptr) |
| port_wire = module->addWire(port_name, GetSize(port_bits_node->data_array)); |
| |
| if (port_node->data_dict.count("upto") != 0) { |
| JsonNode *val = port_node->data_dict.at("upto"); |
| if (val->type == 'N') |
| port_wire->upto = val->data_number != 0; |
| } |
| |
| if (port_node->data_dict.count("offset") != 0) { |
| JsonNode *val = port_node->data_dict.at("offset"); |
| if (val->type == 'N') |
| port_wire->start_offset = val->data_number; |
| } |
| |
| if (port_direction_node->data_string == "input") { |
| port_wire->port_input = true; |
| } else |
| if (port_direction_node->data_string == "output") { |
| port_wire->port_output = true; |
| } else |
| if (port_direction_node->data_string == "inout") { |
| port_wire->port_input = true; |
| port_wire->port_output = true; |
| } else |
| log_error("JSON port node '%s' has invalid '%s' direction attribute.\n", log_id(port_name), port_direction_node->data_string.c_str()); |
| |
| port_wire->port_id = port_id; |
| |
| for (int i = 0; i < GetSize(port_bits_node->data_array); i++) |
| { |
| JsonNode *bitval_node = port_bits_node->data_array.at(i); |
| SigBit sigbit(port_wire, i); |
| |
| if (bitval_node->type == 'S') { |
| if (bitval_node->data_string == "0") |
| module->connect(sigbit, State::S0); |
| else if (bitval_node->data_string == "1") |
| module->connect(sigbit, State::S1); |
| else if (bitval_node->data_string == "x") |
| module->connect(sigbit, State::Sx); |
| else if (bitval_node->data_string == "z") |
| module->connect(sigbit, State::Sz); |
| else |
| log_error("JSON port node '%s' has invalid '%s' bit string value on bit %d.\n", |
| log_id(port_name), bitval_node->data_string.c_str(), i); |
| } else |
| if (bitval_node->type == 'N') { |
| int bitidx = bitval_node->data_number; |
| if (signal_bits.count(bitidx)) { |
| if (port_wire->port_output) { |
| module->connect(sigbit, signal_bits.at(bitidx)); |
| } else { |
| module->connect(signal_bits.at(bitidx), sigbit); |
| signal_bits[bitidx] = sigbit; |
| } |
| } else { |
| signal_bits[bitidx] = sigbit; |
| } |
| } else |
| log_error("JSON port node '%s' has invalid bit value on bit %d.\n", log_id(port_name), i); |
| } |
| } |
| |
| module->fixup_ports(); |
| } |
| |
| if (node->data_dict.count("netnames")) |
| { |
| JsonNode *netnames_node = node->data_dict.at("netnames"); |
| |
| if (netnames_node->type != 'D') |
| log_error("JSON netnames node is not a dictionary.\n"); |
| |
| for (auto &net : netnames_node->data_dict) |
| { |
| IdString net_name = RTLIL::escape_id(net.first.c_str()); |
| JsonNode *net_node = net.second; |
| |
| if (net_node->type != 'D') |
| log_error("JSON netname node '%s' is not a dictionary.\n", log_id(net_name)); |
| |
| if (net_node->data_dict.count("bits") == 0) |
| log_error("JSON netname node '%s' has no bits attribute.\n", log_id(net_name)); |
| |
| JsonNode *bits_node = net_node->data_dict.at("bits"); |
| |
| if (bits_node->type != 'A') |
| log_error("JSON netname node '%s' has non-array bits attribute.\n", log_id(net_name)); |
| |
| Wire *wire = module->wire(net_name); |
| |
| if (wire == nullptr) |
| wire = module->addWire(net_name, GetSize(bits_node->data_array)); |
| |
| if (net_node->data_dict.count("upto") != 0) { |
| JsonNode *val = net_node->data_dict.at("upto"); |
| if (val->type == 'N') |
| wire->upto = val->data_number != 0; |
| } |
| |
| if (net_node->data_dict.count("offset") != 0) { |
| JsonNode *val = net_node->data_dict.at("offset"); |
| if (val->type == 'N') |
| wire->start_offset = val->data_number; |
| } |
| |
| for (int i = 0; i < GetSize(bits_node->data_array); i++) |
| { |
| JsonNode *bitval_node = bits_node->data_array.at(i); |
| SigBit sigbit(wire, i); |
| |
| if (bitval_node->type == 'S') { |
| if (bitval_node->data_string == "0") |
| module->connect(sigbit, State::S0); |
| else if (bitval_node->data_string == "1") |
| module->connect(sigbit, State::S1); |
| else if (bitval_node->data_string == "x") |
| module->connect(sigbit, State::Sx); |
| else if (bitval_node->data_string == "z") |
| module->connect(sigbit, State::Sz); |
| else |
| log_error("JSON netname node '%s' has invalid '%s' bit string value on bit %d.\n", |
| log_id(net_name), bitval_node->data_string.c_str(), i); |
| } else |
| if (bitval_node->type == 'N') { |
| int bitidx = bitval_node->data_number; |
| if (signal_bits.count(bitidx)) { |
| if (sigbit != signal_bits.at(bitidx)) |
| module->connect(sigbit, signal_bits.at(bitidx)); |
| } else { |
| signal_bits[bitidx] = sigbit; |
| } |
| } else |
| log_error("JSON netname node '%s' has invalid bit value on bit %d.\n", log_id(net_name), i); |
| } |
| |
| if (net_node->data_dict.count("attributes")) |
| json_parse_attr_param(wire->attributes, net_node->data_dict.at("attributes")); |
| } |
| } |
| |
| if (node->data_dict.count("cells")) |
| { |
| JsonNode *cells_node = node->data_dict.at("cells"); |
| |
| if (cells_node->type != 'D') |
| log_error("JSON cells node is not a dictionary.\n"); |
| |
| for (auto &cell_node_it : cells_node->data_dict) |
| { |
| IdString cell_name = RTLIL::escape_id(cell_node_it.first.c_str()); |
| JsonNode *cell_node = cell_node_it.second; |
| |
| if (cell_node->type != 'D') |
| log_error("JSON cells node '%s' is not a dictionary.\n", log_id(cell_name)); |
| |
| if (cell_node->data_dict.count("type") == 0) |
| log_error("JSON cells node '%s' has no type attribute.\n", log_id(cell_name)); |
| |
| JsonNode *type_node = cell_node->data_dict.at("type"); |
| |
| if (type_node->type != 'S') |
| log_error("JSON cells node '%s' has a non-string type.\n", log_id(cell_name)); |
| |
| IdString cell_type = RTLIL::escape_id(type_node->data_string.c_str()); |
| |
| Cell *cell = module->addCell(cell_name, cell_type); |
| |
| if (cell_node->data_dict.count("connections") == 0) |
| log_error("JSON cells node '%s' has no connections attribute.\n", log_id(cell_name)); |
| |
| JsonNode *connections_node = cell_node->data_dict.at("connections"); |
| |
| if (connections_node->type != 'D') |
| log_error("JSON cells node '%s' has non-dictionary connections attribute.\n", log_id(cell_name)); |
| |
| for (auto &conn_it : connections_node->data_dict) |
| { |
| IdString conn_name = RTLIL::escape_id(conn_it.first.c_str()); |
| JsonNode *conn_node = conn_it.second; |
| |
| if (conn_node->type != 'A') |
| log_error("JSON cells node '%s' connection '%s' is not an array.\n", log_id(cell_name), log_id(conn_name)); |
| |
| SigSpec sig; |
| |
| for (int i = 0; i < GetSize(conn_node->data_array); i++) |
| { |
| JsonNode *bitval_node = conn_node->data_array.at(i); |
| |
| if (bitval_node->type == 'S') { |
| if (bitval_node->data_string == "0") |
| sig.append(State::S0); |
| else if (bitval_node->data_string == "1") |
| sig.append(State::S1); |
| else if (bitval_node->data_string == "x") |
| sig.append(State::Sx); |
| else if (bitval_node->data_string == "z") |
| sig.append(State::Sz); |
| else |
| log_error("JSON cells node '%s' connection '%s' has invalid '%s' bit string value on bit %d.\n", |
| log_id(cell_name), log_id(conn_name), bitval_node->data_string.c_str(), i); |
| } else |
| if (bitval_node->type == 'N') { |
| int bitidx = bitval_node->data_number; |
| if (signal_bits.count(bitidx) == 0) |
| signal_bits[bitidx] = module->addWire(NEW_ID); |
| sig.append(signal_bits.at(bitidx)); |
| } else |
| log_error("JSON cells node '%s' connection '%s' has invalid bit value on bit %d.\n", |
| log_id(cell_name), log_id(conn_name), i); |
| |
| } |
| |
| cell->setPort(conn_name, sig); |
| } |
| |
| if (cell_node->data_dict.count("attributes")) |
| json_parse_attr_param(cell->attributes, cell_node->data_dict.at("attributes")); |
| |
| if (cell_node->data_dict.count("parameters")) |
| json_parse_attr_param(cell->parameters, cell_node->data_dict.at("parameters")); |
| } |
| } |
| } |
| |
| struct JsonFrontend : public Frontend { |
| JsonFrontend() : Frontend("json", "read JSON file") { } |
| void help() YS_OVERRIDE |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" read_json [filename]\n"); |
| log("\n"); |
| log("Load modules from a JSON file into the current design See \"help write_json\"\n"); |
| log("for a description of the file format.\n"); |
| log("\n"); |
| } |
| void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| log_header(design, "Executing JSON frontend.\n"); |
| |
| size_t argidx; |
| for (argidx = 1; argidx < args.size(); argidx++) { |
| // std::string arg = args[argidx]; |
| // if (arg == "-sop") { |
| // sop_mode = true; |
| // continue; |
| // } |
| break; |
| } |
| extra_args(f, filename, args, argidx); |
| |
| JsonNode root(*f); |
| |
| if (root.type != 'D') |
| log_error("JSON root node is not a dictionary.\n"); |
| |
| if (root.data_dict.count("modules") != 0) |
| { |
| JsonNode *modules = root.data_dict.at("modules"); |
| |
| if (modules->type != 'D') |
| log_error("JSON modules node is not a dictionary.\n"); |
| |
| for (auto &it : modules->data_dict) |
| json_import(design, it.first, it.second); |
| } |
| } |
| } JsonFrontend; |
| |
| YOSYS_NAMESPACE_END |
| |