/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * 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 data_array; dict data_dict; vector 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 == '\\') { ch = f.get(); switch (ch) { case EOF: log_error("Unexpected EOF in JSON string.\n"); break; case '"': case '/': case '\\': break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case 'u': int val = 0; for (int i = 0; i < 4; i++) { ch = f.get(); val <<= 4; if (ch >= '0' && '9' >= ch) { val += ch - '0'; } else if (ch >= 'A' && 'F' >= ch) { val += 10 + ch - 'A'; } else if (ch >= 'a' && 'f' >= ch) { val += 10 + ch - 'a'; } else log_error("Unexpected non-digit character in \\uXXXX sequence: %c.\n", ch); } if (val < 128) ch = val; else log_error("Unsupported \\uXXXX sequence in JSON string: %04X.\n", val); break; } } data_string += ch; } break; } if (('0' <= ch && ch <= '9') || ch == '-') { bool negative = false; type = 'N'; if (ch == '-') { data_number = 0; negative = true; } else { 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_number = negative ? -data_number : data_number; 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 &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 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("signed") != 0) { JsonNode *val = port_node->data_dict.at("signed"); if (val->type == 'N') port_wire->is_signed = 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")); } } if (node->data_dict.count("memories")) { JsonNode *memories_node = node->data_dict.at("memories"); if (memories_node->type != 'D') log_error("JSON memories node is not a dictionary.\n"); for (auto &memory_node_it : memories_node->data_dict) { IdString memory_name = RTLIL::escape_id(memory_node_it.first.c_str()); JsonNode *memory_node = memory_node_it.second; RTLIL::Memory *mem = new RTLIL::Memory; mem->name = memory_name; if (memory_node->type != 'D') log_error("JSON memory node '%s' is not a dictionary.\n", log_id(memory_name)); if (memory_node->data_dict.count("width") == 0) log_error("JSON memory node '%s' has no width attribute.\n", log_id(memory_name)); JsonNode *width_node = memory_node->data_dict.at("width"); if (width_node->type != 'N') log_error("JSON memory node '%s' has a non-number width.\n", log_id(memory_name)); mem->width = width_node->data_number; if (memory_node->data_dict.count("size") == 0) log_error("JSON memory node '%s' has no size attribute.\n", log_id(memory_name)); JsonNode *size_node = memory_node->data_dict.at("size"); if (size_node->type != 'N') log_error("JSON memory node '%s' has a non-number size.\n", log_id(memory_name)); mem->size = size_node->data_number; mem->start_offset = 0; if (memory_node->data_dict.count("start_offset") != 0) { JsonNode *val = memory_node->data_dict.at("start_offset"); if (val->type == 'N') mem->start_offset = val->data_number; } if (memory_node->data_dict.count("attributes")) json_parse_attr_param(mem->attributes, memory_node->data_dict.at("attributes")); module->memories[mem->name] = mem; } } // remove duplicates from connections array pool unique_connections(module->connections_.begin(), module->connections_.end()); module->connections_ = std::vector(unique_connections.begin(), unique_connections.end()); } struct JsonFrontend : public Frontend { JsonFrontend() : Frontend("json", "read JSON file") { } void help() 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 args, RTLIL::Design *design) 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