| /* |
| * yosys -- Yosys Open SYnthesis Suite |
| * |
| * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> |
| * Copyright (C) 2020 The Symbiflow Authors |
| * |
| * 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 "pcf_parser.hh" |
| #include "pinmap_parser.hh" |
| |
| #include "kernel/register.h" |
| #include "kernel/rtlil.h" |
| |
| USING_YOSYS_NAMESPACE |
| PRIVATE_NAMESPACE_BEGIN |
| |
| void register_in_tcl_interpreter(const std::string& command) { |
| Tcl_Interp* interp = yosys_get_tcl_interp(); |
| std::string tcl_script = stringf("proc %s args { return [yosys %s {*}$args] }", command.c_str(), command.c_str()); |
| Tcl_Eval(interp, tcl_script.c_str()); |
| } |
| |
| struct QuicklogicIob : public Pass { |
| |
| QuicklogicIob () : |
| Pass("quicklogic_iob", "Map IO buffers to cells that correspond to their assigned locations") { |
| register_in_tcl_interpreter(pass_name); |
| } |
| |
| void help() YS_OVERRIDE { |
| log("Help!\n"); |
| } |
| |
| void execute(std::vector<std::string> a_Args, RTLIL::Design* a_Design) YS_OVERRIDE { |
| if (a_Args.size() < 3) { |
| log_cmd_error("Usage: quicklogic_iob <PCF file> <pinmap file>"); |
| } |
| |
| // Get the top module of the design |
| RTLIL::Module* topModule = a_Design->top_module(); |
| if (topModule == nullptr) { |
| log_cmd_error("No top module detected!\n"); |
| } |
| |
| // Read and parse the PCF file |
| log("Loading PCF from '%s'...\n", a_Args[1].c_str()); |
| auto pcfParser = PcfParser(); |
| if (!pcfParser.parse(a_Args[1])) { |
| log_cmd_error("Failed to parse the PCF file!\n"); |
| } |
| |
| // Build a map of net names to constraints |
| std::unordered_map<std::string, const PcfParser::Constraint> constraintMap; |
| for (auto& constraint : pcfParser.getConstraints()) { |
| if (constraintMap.count(constraint.netName) != 0) { |
| log_cmd_error("The net '%s' is constrained twice!", constraint.netName.c_str()); |
| } |
| constraintMap.emplace(constraint.netName, constraint); |
| } |
| |
| // Read and parse pinmap CSV file |
| log("Loading pinmap CSV from '%s'...\n", a_Args[2].c_str()); |
| auto pinmapParser = PinmapParser(); |
| if (!pinmapParser.parse(a_Args[2])) { |
| log_cmd_error("Failed to parse the pinmap CSV file!\n"); |
| } |
| |
| // Build a map of pad names to entries |
| std::unordered_map<std::string, const PinmapParser::Entry> pinmapMap; |
| for (auto& entry : pinmapParser.getEntries()) { |
| if (entry.count("name") != 0) { |
| pinmapMap.emplace(entry.at("name"), entry); |
| } |
| } |
| |
| // A map of IO cell types and their port names that should go to a pad |
| std::unordered_map<std::string, std::string> ioCellTypes; |
| |
| // TODO |
| ioCellTypes.insert(std::make_pair("inpad", "P")); |
| ioCellTypes.insert(std::make_pair("outpad", "P")); |
| |
| // Check all IO cells |
| log("\n"); |
| log(" type | instance | pad | loc | cell \n"); |
| log(" ------------+----------------------+------------+----------+----------\n"); |
| for (auto cell : topModule->cells()) { |
| auto cellType = RTLIL::unescape_id(cell->type); |
| |
| // Not an IO cell |
| if (ioCellTypes.count(cellType) == 0) { |
| continue; |
| } |
| |
| log(" %-10s | %-20s ", cellType.c_str(), cell->name.c_str()); |
| |
| // Get connections to the specified port |
| std::string port = RTLIL::escape_id(ioCellTypes.at(cellType)); |
| if (cell->connections().count(port) == 0) { |
| log(" Port '%s' not found!\n", port.c_str()); |
| continue; |
| } |
| |
| // Get the sigspec of the connection |
| auto sigspec = cell->connections().at(port); |
| |
| // Get the connected wire |
| if (!sigspec.is_wire()) { |
| log(" Couldn't determine the connection!\n"); |
| continue; |
| } |
| auto wire = sigspec.as_wire(); |
| |
| // Has to be top level wire |
| if (!wire->port_input && !wire->port_output) { |
| log(" Not a top-level wire!\n"); |
| continue; |
| } |
| |
| // Check if the wire is constrained |
| auto wireName = RTLIL::unescape_id(wire->name); |
| if (constraintMap.count(wireName) == 0) { |
| log("\n"); |
| continue; |
| } |
| |
| // Get the constraint |
| auto constraint = constraintMap.at(wireName); |
| log("| %-10s ", constraint.padName.c_str()); |
| |
| // Check if there is an entry in the pinmap for this pad name |
| if (pinmapMap.count(constraint.padName) == 0) { |
| log("\n"); |
| continue; |
| } |
| |
| // Get the entry |
| auto entry = pinmapMap.at(constraint.padName); |
| |
| // The pinmap entry does not have cell type defined, skip it. |
| if (entry.count("cell") == 0) { |
| log("\n"); |
| continue; |
| } |
| |
| // Location string |
| std::string loc; |
| if (entry.count("x") && entry.count("y")) { |
| loc = stringf("X%sY%s", |
| entry.at("x").c_str(), |
| entry.at("y").c_str() |
| ); |
| } |
| |
| log("| %-8s | %s\n", loc.c_str(), entry.at("cell").c_str()); |
| |
| // Annotate the cell by setting its parameters |
| cell->setParam(RTLIL::escape_id("IO_PAD"), constraint.padName); |
| cell->setParam(RTLIL::escape_id("IO_LOC"), loc); |
| cell->setParam(RTLIL::escape_id("IO_TYPE"), entry.at("cell")); |
| } |
| } |
| |
| } QuicklogicIob; |
| |
| PRIVATE_NAMESPACE_END |