/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Clifford Wolf * Copyright (C) 2018 David Shah * Copyright (C) 2018 Serge Bazanski * * 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 "xdl.h" #include #include #include "cells.h" #include "log.h" #include "nextpnr.h" #include "util.h" #include #include "torc/Physical.hpp" using namespace torc::architecture::xilinx; using namespace torc::physical; NEXTPNR_NAMESPACE_BEGIN DesignSharedPtr create_torc_design(const Context *ctx) { auto designPtr = Factory::newDesignPtr("name", torc_info->ddb->getDeviceName(), ctx->args.package, "-1", ""); std::unordered_map site_to_instance; std::vector> lut_inputs; lut_inputs.reserve(6); auto bel_to_lut = [](const BelId bel) { switch (torc_info->bel_to_loc[bel.index].z) { case 0: case 4: return "A"; break; case 1: case 5: return "B"; break; case 2: case 6: return "C"; break; case 3: case 7: return "D"; break; default: throw; } }; for (const auto &cell : ctx->cells) { const char *type; if (cell.second->type == id_SLICE_LUT6) type = "SLICEL"; else if (cell.second->type == id_IOB33 || cell.second->type == id_IOB18 || cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7 || cell.second->type == id_MMCME2_ADV) type = cell.second->type.c_str(ctx); else log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx)); auto site_index = torc_info->bel_to_site_index[cell.second->bel.index]; auto ret = site_to_instance.emplace(site_index, nullptr); InstanceSharedPtr instPtr; if (ret.second) { instPtr = Factory::newInstancePtr(cell.second->name.str(ctx), type, "", ""); auto b = designPtr->addInstance(instPtr); assert(b); ret.first->second = instPtr; const auto &tile_info = torc_info->bel_to_tile_info(cell.second->bel.index); instPtr->setTile(tile_info.getName()); instPtr->setSite(torc_info->bel_to_name(cell.second->bel.index)); } else instPtr = ret.first->second; if (cell.second->type == id_SLICE_LUT6) { std::string setting, name, value; const std::string lut = bel_to_lut(cell.second->bel); setting = lut + "6LUT"; value = "#LUT:O6="; lut_inputs.clear(); if (get_net_or_empty(cell.second.get(), id_I1)) lut_inputs.emplace_back("A1", "~A1"); if (get_net_or_empty(cell.second.get(), id_I2)) lut_inputs.emplace_back("A2", "~A2"); if (get_net_or_empty(cell.second.get(), id_I3)) lut_inputs.emplace_back("A3", "~A3"); if (get_net_or_empty(cell.second.get(), id_I4)) lut_inputs.emplace_back("A4", "~A4"); if (get_net_or_empty(cell.second.get(), id_I5)) lut_inputs.emplace_back("A5", "~A5"); if (get_net_or_empty(cell.second.get(), id_I6)) lut_inputs.emplace_back("A6", "~A6"); const auto &init = cell.second->params[ctx->id("INIT")]; // Assume from Yosys that INIT masks of less than 32 bits are output as uint32_t if (lut_inputs.size() < 6) { auto init_as_uint = boost::lexical_cast(init); NPNR_ASSERT(init_as_uint <= ((1ull << (1u << lut_inputs.size())) - 1)); if (lut_inputs.empty()) value += init; else { unsigned n = 0; for (unsigned o = 0; o < (1u << lut_inputs.size()); ++o) { if (!((init_as_uint >> o) & 1)) continue; if (n++ > 0) value += "+"; value += "("; value += (o & 1) ? lut_inputs[0].first : lut_inputs[0].second; for (unsigned i = 1; i < lut_inputs.size(); ++i) { value += "*"; value += o & (1 << i) ? lut_inputs[i].first : lut_inputs[i].second; } value += ")"; } } } // Otherwise as a bit string else { NPNR_ASSERT(init.size() == (1u << lut_inputs.size())); unsigned n = 0; for (unsigned i = 0; i < init.size(); ++i) { if (init[init.size() - 1 - i] == '0') continue; if (n++ > 0) value += "+"; value += "("; value += (i & 1) ? lut_inputs[0].first : lut_inputs[0].second; for (unsigned j = 1; j < lut_inputs.size(); ++j) { value += "*"; value += i & (1 << j) ? lut_inputs[j].first : lut_inputs[j].second; } value += ")"; } } auto it = cell.second->params.find(ctx->id("LUT_NAME")); if (it != cell.second->params.end()) name = it->second; else name = cell.second->name.str(ctx); boost::replace_all(name, ":", "\\:"); instPtr->setConfig(setting, name, value); auto O = get_net_or_empty(cell.second.get(), id_O); if (O) { setting = lut; setting += "USED"; instPtr->setConfig(setting, "", "0"); } auto OQ = get_net_or_empty(cell.second.get(), id_OQ); if (OQ) { setting = lut; setting += "FF"; name = OQ->name.str(ctx); boost::replace_all(name, ":", "\\:"); instPtr->setConfig(setting, name, "#FF"); instPtr->setConfig(setting + "MUX", "", "O6"); instPtr->setConfig(setting + "INIT", "", cell.second->params.at(ctx->id("FFINIT"))); if (cell.second->lcInfo.negClk) instPtr->setConfig("CLKINV", "", "CLK_B"); else instPtr->setConfig("CLKINV", "", "CLK"); if (get_net_or_empty(cell.second.get(), id_SR)) { instPtr->setConfig(setting + "SR", "", cell.second->params.at(id_SR)); instPtr->setConfig("SRUSEDMUX", "", "IN"); } instPtr->setConfig("SYNC_ATTR", "", cell.second->params.at(ctx->id("SYNC_ATTR"))); if (get_net_or_empty(cell.second.get(), ctx->id("CE"))) instPtr->setConfig("CEUSEDMUX", "", "IN"); } } else if (cell.second->type == id_IOB33) { if (get_net_or_empty(cell.second.get(), id_I)) { instPtr->setConfig("IUSED", "", "0"); instPtr->setConfig("IBUF_LOW_PWR", "", "TRUE"); instPtr->setConfig("ISTANDARD", "", "LVCMOS33"); } else { instPtr->setConfig("OUSED", "", "0"); instPtr->setConfig("OSTANDARD", "", "LVCMOS33"); instPtr->setConfig("DRIVE", "", "12"); instPtr->setConfig("SLEW", "", "SLOW"); } } else if (cell.second->type == id_IOB18) { if (get_net_or_empty(cell.second.get(), id_I)) { instPtr->setConfig("IUSED", "", "0"); instPtr->setConfig("IBUF_LOW_PWR", "", "TRUE"); instPtr->setConfig("ISTANDARD", "", "LVCMOS18"); } else { instPtr->setConfig("OUSED", "", "0"); instPtr->setConfig("OSTANDARD", "", "LVCMOS18"); instPtr->setConfig("DRIVE", "", "12"); instPtr->setConfig("SLEW", "", "SLOW"); } } else if (cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7 || cell.second->type == id_MMCME2_ADV) { for (const auto& i : cell.second->params) instPtr->setConfig(i.first.str(ctx), "", i.second); } else log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx)); } for (const auto &net : ctx->nets) { const auto &driver = net.second->driver; auto site_index = torc_info->bel_to_site_index[driver.cell->bel.index]; auto instPtr = site_to_instance.at(site_index); auto netPtr = Factory::newNetPtr(net.second->name.str(ctx)); auto pin_name = driver.port.str(ctx); // For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT if (driver.cell->type == id_SLICE_LUT6 && (pin_name[0] == 'I' || pin_name[0] == 'O')) { const auto lut = bel_to_lut(driver.cell->bel); pin_name[0] = lut[0]; } // e.g. Convert DDRARB[0] -> DDRARB0 pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end()); auto pinPtr = Factory::newInstancePinPtr(instPtr, pin_name); netPtr->addSource(pinPtr); if (!net.second->users.empty()) { for (const auto &user : net.second->users) { site_index = torc_info->bel_to_site_index[user.cell->bel.index]; instPtr = site_to_instance.at(site_index); pin_name = user.port.str(ctx); // For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT if (user.cell->type == id_SLICE_LUT6 && (pin_name[0] == 'I' || pin_name[0] == 'O')) { const auto lut = bel_to_lut(user.cell->bel); pin_name[0] = lut[0]; } else { // e.g. Convert DDRARB[0] -> DDRARB0 pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end()); } pinPtr = Factory::newInstancePinPtr(instPtr, pin_name); netPtr->addSink(pinPtr); } auto b = designPtr->addNet(netPtr); assert(b); for (const auto &i : net.second->wires) { const auto &pip_map = i.second; if (pip_map.pip == PipId()) continue; ExtendedWireInfo ewi_src(*torc_info->ddb, torc_info->pip_to_arc[pip_map.pip.index].getSourceTilewire()); ExtendedWireInfo ewi_dst(*torc_info->ddb, torc_info->pip_to_arc[pip_map.pip.index].getSinkTilewire()); auto p = Factory::newPip(ewi_src.mTileName, ewi_src.mWireName, ewi_dst.mWireName, ePipUnidirectionalBuffered); netPtr->addPip(p); } } } return designPtr; } void write_xdl(const Context *ctx, std::ostream &out) { XdlExporter exporter(out); auto designPtr = create_torc_design(ctx); exporter(designPtr); } NEXTPNR_NAMESPACE_END