blob: 1d99283cefa720ba3cc351816fbe391d6a9ee0a7 [file]
/*
* 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