diff --git a/CMakeLists.txt b/CMakeLists.txt index 40707d0e..2ae0b681 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -334,6 +334,7 @@ foreach (family ${ARCH}) if (BUILD_TESTS) set(family_targets ${family_targets} ${PROGRAM_PREFIX}nextpnr-${family}-test) + set(family_test_targets ${PROGRAM_PREFIX}nextpnr-${family}-test) endif() # Include the family-specific CMakeFile diff --git a/README.md b/README.md index 8f3070f1..a5db81a2 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Currently nextpnr supports: * Lattice ECP5 devices supported by [Project Trellis](https://github.com/YosysHQ/prjtrellis) * Lattice Nexus devices supported by [Project Oxide](https://github.com/gatecat/prjoxide) * Gowin LittleBee devices supported by [Project Apicula](https://github.com/YosysHQ/apicula) + * NanoXplore NG-Ultra devices supported by [Project Beyond](https://github.com/yosyshq-GmbH/prjbeyond-db) * *(experimental)* Cyclone V devices supported by [Mistral](https://github.com/Ravenslofty/mistral) * *(experimental)* Lattice MachXO2 devices supported by [Project Trellis](https://github.com/YosysHQ/prjtrellis) * *(experimental)* a "generic" back-end for user-defined architectures @@ -146,6 +147,18 @@ sudo make install - Examples of the Gowin flow for a range of boards can be found in the [Project Apicula Examples](https://github.com/YosysHQ/apicula/tree/master/examples). +#### ng-ultra + +For NanoXplore NG-Ultra support, clone [Project Beyond DB](https://github.com/yosyshq-GmbH/prjbeyond-db) repo + +``` +cmake . -DARCH="himbaechel" -DHIMBAECHEL_PRJBEYOND_DB=/path/to/prjbeyond-db -DHIMBAECHEL_NGULTRA_DEVICES=ng-ultra +make -j$(nproc) +sudo make install +``` + +*Please note that binary bitstream creation requires Impulse tool from NanoXplore.* + ### GUI The nextpnr GUI is not built by default, to reduce the number of dependencies for a standard headless build. To enable it, add `-DBUILD_GUI=ON` to the CMake command line and ensure that Qt5 and OpenGL are available: diff --git a/himbaechel/family.cmake b/himbaechel/family.cmake index 88a52b33..5061124d 100644 --- a/himbaechel/family.cmake +++ b/himbaechel/family.cmake @@ -1,8 +1,14 @@ -set(HIMBAECHEL_UARCHES "example;gowin;xilinx") +set(HIMBAECHEL_UARCHES "example;gowin;xilinx;ng-ultra") foreach(uarch ${HIMBAECHEL_UARCHES}) add_subdirectory(${family}/uarch/${uarch}) aux_source_directory(${family}/uarch/${uarch} HM_UARCH_FILES) foreach(target ${family_targets}) target_sources(${target} PRIVATE ${HM_UARCH_FILES}) endforeach() + if (BUILD_TESTS) + foreach(target ${family_test_targets}) + aux_source_directory(${family}/uarch/${uarch}/tests/ HM_UARCH_TEST_FILES) + target_sources(${target} PRIVATE ${HM_UARCH_TEST_FILES}) + endforeach() + endif() endforeach(uarch) diff --git a/himbaechel/uarch/ng-ultra/CMakeLists.txt b/himbaechel/uarch/ng-ultra/CMakeLists.txt new file mode 100644 index 00000000..f3ed2589 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/CMakeLists.txt @@ -0,0 +1,38 @@ +message(STATUS "Configuring Himbaechel-NG-ULTRA uarch") +cmake_minimum_required(VERSION 3.5) +project(himbaechel-ng-ultra-chipdb NONE) + +set(ALL_HIMBAECHEL_NGULTRA_DEVICES ng-ultra) +set(HIMBAECHEL_NGULTRA_DEVICES "" CACHE STRING + "Include support for these NG-Ultra devices (available: ${ALL_HIMBAECHEL_NGULTRA_DEVICES})") +message(STATUS "Enabled Himbaechel-NG-Ultra devices: ${HIMBAECHEL_NGULTRA_DEVICES}") +set(HIMBAECHEL_PRJBEYOND_DB "" CACHE STRING + "Path to a Project Beyond database") + +set(chipdb_binaries) +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/share/himbaechel/ng-ultra) +foreach(device ${HIMBAECHEL_NGULTRA_DEVICES}) + if("${HIMBAECHEL_PRJBEYOND_DB}" STREQUAL "") + message(SEND_ERROR "HIMBAECHEL_PRJBEYOND_DB must be set to a prjbeyond database checkout") + endif() + + set(device_bba ${CMAKE_BINARY_DIR}/share/himbaechel/ng-ultra/chipdb-${device}.bba) + set(device_bin ${CMAKE_BINARY_DIR}/share/himbaechel/ng-ultra/chipdb-${device}.bin) + string(TOUPPER ${device} upcase_device) + add_custom_command( + OUTPUT ${device_bin} + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen/arch_gen.py --db ${HIMBAECHEL_PRJBEYOND_DB} --device ${upcase_device} --bba ${device_bba} + COMMAND bbasm ${BBASM_ENDIAN_FLAG} ${device_bba} ${device_bin}.new + # atomically update + COMMAND ${CMAKE_COMMAND} -E rename ${device_bin}.new ${device_bin} + DEPENDS + bbasm + ${CMAKE_CURRENT_SOURCE_DIR}/gen/arch_gen.py + ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc + VERBATIM) + list(APPEND chipdb_binaries ${device_bin}) +endforeach() + +add_custom_target(chipdb-himbaechel-ng-ultra ALL DEPENDS ${chipdb_binaries}) +install(DIRECTORY ${CMAKE_BINARY_DIR}/share/himbaechel/ng-ultra/ DESTINATION share/nextpnr/himbaechel/ng-ultra + PATTERN "*.bba" EXCLUDE) diff --git a/himbaechel/uarch/ng-ultra/bitstream.cc b/himbaechel/uarch/ng-ultra/bitstream.cc new file mode 100644 index 00000000..145218a3 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/bitstream.cc @@ -0,0 +1,705 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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 +#include + +#include +#include + +#include "extra_data.h" +#include "himbaechel_api.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +#include "ng_ultra.h" + +#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace { +struct BitstreamJsonBackend +{ + Context *ctx; + NgUltraImpl *uarch; + std::ostream &out; + bool first_instance; + + BitstreamJsonBackend(Context *ctx, NgUltraImpl *uarch, std::ostream &out) : ctx(ctx), uarch(uarch), out(out) {}; + + std::string get_string(std::string str) + { + std::string newstr = "\""; + for (char c : str) { + if (c == '\\') + newstr += c; + newstr += c; + } + return newstr + "\""; + } + + std::string update_name(std::string tile, std::string name) + { + if (boost::starts_with(tile, "FENCE[")) { + char last = tile[tile.size() - 2]; + switch (last) { + case 'T': + case 'B': + case 'U': + case 'L': + std::string loc = tile.substr(tile.find("[") + 1, tile.find("x") - tile.find("[")); + boost::replace_all(name, "1x", loc); + return name; + } + } + if (boost::starts_with(tile, "TILE[") && boost::algorithm::contains(name, ".FE")) { + std::string last = name.substr(name.rfind('.') + 1); + if (last[0] == 'D') { + boost::replace_all(name, ".D", "."); + boost::replace_all(name, ".FE", ".DFF"); + return name; + } + if (last == "L" || last == "R") { + boost::replace_all(name, ".FE", ".DFF"); + return name; + } + if (last == "CK") { + boost::replace_all(name, ".FE", ".DFF"); + return name; + } + if (last[0] == 'L') { + boost::replace_all(name, ".L", "."); + boost::replace_all(name, ".FE", ".LUT"); + return name; + } + if (last[0] == 'P') { + boost::replace_all(name, ".PI", ".I"); + boost::replace_all(name, ".FE", ".LUT"); + return name; + } + } + return name; + } + + void add_net(std::set &nets, std::string src_tile, std::string src_name, std::string dst_tile, + std::string dst_name, IdString src_type, IdString dst_type) + { + if (src_type.in(ctx->id("LUT_PERMUTATION_WIRE"), ctx->id("MUX_WIRE"), ctx->id("INTERCONNECT_INPUT"))) + return; + if (boost::starts_with(src_type.c_str(ctx), "CROSSBAR_") && boost::ends_with(src_type.c_str(ctx), "INPUT_WIRE")) + return; + if (dst_type == ctx->id("MUX_WIRE")) + dst_name = dst_name.substr(0, dst_name.rfind('.')); + src_name = update_name(src_tile, src_name); + dst_name = update_name(dst_tile, dst_name); + + nets.emplace(stringf("%s:%s->%s:%s", src_tile.c_str(), src_name.c_str(), dst_tile.c_str(), dst_name.c_str())); + } + + std::string cleanup_name(std::string name) + { + std::replace(name.begin(), name.end(), '$', '_'); + return name; + } + + void write_nets() + { + out << "\t\"nets\": {\n"; + bool first_net = true; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + if (ni->wires.empty()) + continue; + out << (first_net ? "" : ",\n"); + first_net = false; + out << stringf("\t\t%s: [\n", get_string(cleanup_name(ni->name.c_str(ctx))).c_str()); + std::set nets; + for (auto &w : ni->wires) { + if (w.second.pip != PipId()) { + PipId pip = w.second.pip; + auto &pd = chip_pip_info(ctx->chip_info, pip); + const auto &extra_data = *uarch->pip_extra_data(pip); + WireId swire = ctx->getPipSrcWire(pip); + IdString src = ctx->getWireName(swire)[1]; + IdString src_type = ctx->getWireType(swire); + + IdString src_orig = IdString(chip_tile_info(ctx->chip_info, pip.tile).wires[pd.src_wire].name); + IdString src_orig_type = + IdString(chip_tile_info(ctx->chip_info, pip.tile).wires[pd.src_wire].wire_type); + + WireId dwire = ctx->getPipDstWire(pip); + IdString dst = ctx->getWireName(dwire)[1]; + IdString dst_type = ctx->getWireType(dwire); + + std::string s_tile_name = uarch->tile_name(swire.tile); + std::string tile_name = uarch->tile_name(pip.tile); + + if (src_orig != src) + add_net(nets, s_tile_name, src.c_str(ctx), tile_name, src_orig.c_str(ctx), src_type, + src_orig_type); + if (!extra_data.name || + (extra_data.type != PipExtra::PIP_EXTRA_BYPASS && + extra_data.type != PipExtra::PIP_EXTRA_VIRTUAL && extra_data.type != PipExtra::PIP_EXTRA_MUX)) + add_net(nets, tile_name, src_orig.c_str(ctx), tile_name, dst.c_str(ctx), src_orig_type, + dst_type); + } else if (ni->wires.size() == 1) { + IdString src = ctx->getWireName(w.first)[1]; + IdString src_type = ctx->getWireType(w.first); + std::string s_tile_name = uarch->tile_name(w.first.tile); + for (auto &u : ni->users) { + std::string tile_name = uarch->tile_name(u.cell->bel.tile); + IdString bel_name = ctx->getBelName(u.cell->bel)[1]; + add_net(nets, s_tile_name, src.c_str(ctx), tile_name, + stringf("%s.%s", bel_name.c_str(ctx), u.port.c_str(ctx)), src_type, src_type); + } + } + } + bool first = true; + for (auto &str : nets) { + out << (first ? "" : ",\n"); + out << stringf("\t\t\t%s", get_string(str).c_str()); + first = false; + } + out << "\n\t\t]"; + } + out << "\n\t},\n"; + } + + template + std::string str_or_n_value(const dict &ct, const KeyType &key, std::string def = "N") + { + auto found = ct.find(key); + if (found == ct.end()) + return def; + else { + if (!found->second.is_string) + log_error("Expecting string value but got integer %d.\n", int(found->second.intval)); + if (found->second.as_string().empty()) + return def; + return found->second.as_string(); + } + }; + + template + std::string str_or_n_value_lower(const dict &ct, const KeyType &key, std::string def = "N") + { + auto found = ct.find(key); + if (found == ct.end()) + return def; + else { + if (!found->second.is_string) + log_error("Expecting string value but got integer %d.\n", int(found->second.intval)); + if (found->second.as_string().empty()) + return def; + std::string tmp = found->second.as_string(); + boost::algorithm::to_lower(tmp); + return tmp; + } + }; + + template + std::string extract_bits_or_default(const dict &ct, const KeyType &key, int bits, int def = 0) + { + Property extr = get_or_default(ct, key, Property()).extract(0, bits); + std::string str = extr.str; + std::reverse(str.begin(), str.end()); + return str; + }; + + std::vector config; + + void open_instance(CellInfo *cell, std::string rename = "") + { + out << stringf("%s", first_instance ? "" : ",\n"); + first_instance = false; + out << stringf("\t\t%s: {\n", + get_string(cleanup_name(rename.empty() ? cell->name.c_str(ctx) : rename.c_str())).c_str()); + std::string tile_name = uarch->tile_name(cell->bel.tile); + IdString idx = ctx->getBelName(cell->bel)[1]; + std::string belname = idx.c_str(ctx); + config.clear(); + out << stringf("\t\t\t\"location\": %s,\n", get_string(tile_name + ":" + belname).c_str()); + out << stringf("\t\t\t\"type\": %s", get_string(cell->type.c_str(ctx)).c_str()); + } + + void open_instance_fe(CellInfo *cell, std::string type, std::string replace, std::string postfix = "") + { + out << stringf("%s", first_instance ? "" : ",\n"); + first_instance = false; + out << stringf("\t\t%s: {\n", get_string(cleanup_name(cell->name.c_str(ctx)) + postfix).c_str()); + std::string tile_name = uarch->tile_name(cell->bel.tile); + IdString idx = ctx->getBelName(cell->bel)[1]; + std::string belname = idx.c_str(ctx); + boost::replace_all(belname, ".FE", replace); + config.clear(); + out << stringf("\t\t\t\"location\": %s,\n", get_string(tile_name + ":" + belname).c_str()); + out << stringf("\t\t\t\"type\": %s", get_string(type).c_str()); + } + + inline void add_config(std::string name, int val) + { + config.push_back(stringf("\t\t\t\t%s:%d", get_string(name).c_str(), val)); + } + + inline void add_config(std::string name, bool val) + { + config.push_back(stringf("\t\t\t\t%s:%s", get_string(name).c_str(), val ? "true" : "false")); + } + + inline void add_config(std::string name, std::string val) + { + config.push_back(stringf("\t\t\t\t%s:%s", get_string(name).c_str(), get_string(val).c_str())); + } + + void close_instance() + { + bool first = true; + if (!config.empty()) + out << ",\n\t\t\t\"config\": {\n"; + for (auto &str : config) { + out << (first ? "" : ",\n"); + out << str.c_str(); + first = false; + } + if (!config.empty()) + out << "\n\t\t\t}"; + out << "\n\t\t}"; + config.clear(); + } + + void write_iop(CellInfo *cell) + { + open_instance(cell, str_or_default(cell->params, id_iobname, "")); + add_config("location", str_or_default(cell->params, id_location, "")); + add_config("differential", str_or_n_value_lower(cell->params, id_differential, "false")); + add_config("slewRate", str_or_default(cell->params, id_slewRate, "Medium")); + add_config("turbo", str_or_n_value_lower(cell->params, id_turbo, "false")); + add_config("weakTermination", str_or_n_value(cell->params, id_weakTermination, "PullUp")); + add_config("inputDelayLine", str_or_default(cell->params, id_inputDelayLine, "0")); + add_config("outputDelayLine", str_or_default(cell->params, id_outputDelayLine, "0")); + add_config("inputSignalSlope", str_or_default(cell->params, id_inputSignalSlope, "0")); + add_config("outputCapacity", str_or_default(cell->params, id_outputCapacity, "0")); + add_config("standard", str_or_default(cell->params, id_standard, "LVCMOS")); + add_config("drive", str_or_default(cell->params, id_drive, "2mA")); + add_config("inputDelayOn", str_or_n_value_lower(cell->params, id_inputDelayOn, "false")); + add_config("outputDelayOn", str_or_n_value_lower(cell->params, id_outputDelayOn, "false")); + add_config("dynDrive", str_or_n_value_lower(cell->params, id_dynDrive, "false")); + add_config("dynInput", str_or_n_value_lower(cell->params, id_dynInput, "false")); + add_config("dynTerm", str_or_n_value_lower(cell->params, id_dynTerm, "false")); + if (cell->type.in(id_OTP, id_ITP, id_IOTP)) { + add_config("termination", str_or_n_value(cell->params, id_termination, "0")); + add_config("terminationReference", str_or_n_value(cell->params, id_terminationReference, "VT")); + } + close_instance(); + std::string tile_name = uarch->tile_name(cell->bel.tile); + std::string bank = tile_name.substr(0, tile_name.rfind(':')); + if (uarch->bank_voltage.count(bank) == 0) { + if (bank == "IOB0" || bank == "IOB1" || bank == "IOB6" || bank == "IOB7") + uarch->bank_voltage[bank] = "3.3V"; + else + uarch->bank_voltage[bank] = "1.8V"; + } + } + + void write_ddfr(CellInfo *cell) + { + open_instance(cell); + add_config("dff_load", bool_or_default(cell->params, id_dff_load, false)); + add_config("dff_sync", bool_or_default(cell->params, id_dff_sync, false)); + add_config("dff_type", bool_or_default(cell->params, id_dff_type, false)); + add_config("iobname", str_or_default(cell->params, id_iobname, "")); + add_config("path", int_or_default(cell->params, id_path, 0)); + close_instance(); + } + + void write_dfr(CellInfo *cell) + { + open_instance(cell); + add_config("data_inv", bool_or_default(cell->params, id_data_inv, false)); + add_config("dff_edge", bool_or_default(cell->params, id_dff_edge, false)); + add_config("dff_init", bool_or_default(cell->params, id_dff_init, false)); + add_config("dff_load", bool_or_default(cell->params, id_dff_load, false)); + add_config("dff_sync", bool_or_default(cell->params, id_dff_sync, false)); + add_config("dff_type", bool_or_default(cell->params, id_dff_type, false)); + add_config("mode", int_or_default(cell->params, id_mode, 3)); + add_config("iobname", str_or_default(cell->params, id_iobname, "")); + close_instance(); + } + + void write_bfr(CellInfo *cell) + { + open_instance(cell); + add_config("mode", int_or_default(cell->params, id_mode, 2)); + add_config("iobname", str_or_default(cell->params, id_iobname, "")); + if (cell->params.count(id_data_inv)) { + add_config("data_inv", bool_or_default(cell->params, id_data_inv, false)); + } + close_instance(); + } + + void write_cy(CellInfo *cell) + { + open_instance(cell); + add_config("add_carry", int_or_default(cell->params, id_add_carry, 0)); + add_config("shifter", bool_or_default(cell->params, id_shifter, false)); + close_instance(); + } + + void write_fe(CellInfo *cell) + { + if (bool_or_default(cell->params, id_lut_used)) { + open_instance_fe(cell, "LUT", ".LUT"); + add_config("lut_table", extract_bits_or_default(cell->params, id_lut_table, 16)); + close_instance(); + } + if (bool_or_default(cell->params, id_dff_used)) { + std::string subtype = str_or_default(cell->params, id_type, "DFF"); + open_instance_fe(cell, subtype, ".DFF", "_D"); + if (subtype == "DFF") { + add_config("dff_ctxt", std::to_string(int_or_default(cell->params, id_dff_ctxt, 0))); + add_config("dff_edge", bool_or_default(cell->params, id_dff_edge, false)); + add_config("dff_init", bool_or_default(cell->params, id_dff_init, false)); + add_config("dff_load", bool_or_default(cell->params, id_dff_load, false)); + add_config("dff_sync", bool_or_default(cell->params, id_dff_sync, false)); + add_config("dff_type", bool_or_default(cell->params, id_dff_type, false)); + } + close_instance(); + } + } + + void write_xlut(CellInfo *cell) + { + open_instance(cell); + add_config("lut_table", extract_bits_or_default(cell->params, id_lut_table, 16)); + close_instance(); + } + + void write_iom(CellInfo *cell) + { + open_instance(cell); + add_config("pads_path", str_or_default(cell->params, id_pads_path, ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;")); + close_instance(); + } + + void write_gck(CellInfo *cell) + { + open_instance(cell); + add_config("inv_in", bool_or_default(cell->params, id_inv_in, false)); + add_config("inv_out", bool_or_default(cell->params, id_inv_out, false)); + add_config("std_mode", str_or_default(cell->params, id_std_mode, "BYPASS")); + close_instance(); + } + + void write_wfb(CellInfo *cell) + { + open_instance(cell); + add_config("delay_on", bool_or_default(cell->params, id_delay_on, false)); + add_config("delay", int_or_default(cell->params, id_delay, 0)); + add_config("wfg_edge", bool_or_default(cell->params, id_wfg_edge, false)); + close_instance(); + } + + void write_wfg(CellInfo *cell) + { + open_instance(cell); + add_config("mode", int_or_default(cell->params, id_mode, 0)); + add_config("delay_on", bool_or_default(cell->params, id_delay_on, false)); + add_config("delay", int_or_default(cell->params, id_delay, 0)); + add_config("wfg_edge", bool_or_default(cell->params, id_wfg_edge, false)); + add_config("pattern", extract_bits_or_default(cell->params, id_pattern, 16)); + add_config("pattern_end", int_or_default(cell->params, id_pattern_end, 0)); + add_config("div_ratio", int_or_default(cell->params, id_div_ratio, 0)); + add_config("div_phase", bool_or_default(cell->params, id_div_phase, false)); + add_config("reset_on_pll_lock_n", bool_or_default(cell->params, id_reset_on_pll_lock_n, false)); + add_config("reset_on_pll_locka_n", bool_or_default(cell->params, id_reset_on_pll_locka_n, false)); + add_config("reset_on_cal_lock_n", bool_or_default(cell->params, id_reset_on_cal_lock_n, false)); + close_instance(); + } + + void write_pll(CellInfo *cell) + { + open_instance(cell); + add_config("clk_outdiv1", extract_bits_or_default(cell->params, id_clk_outdiv1, 3)); + add_config("clk_outdiv2", extract_bits_or_default(cell->params, id_clk_outdiv2, 3)); + add_config("clk_outdiv3", extract_bits_or_default(cell->params, id_clk_outdiv3, 3)); + add_config("clk_outdiv4", extract_bits_or_default(cell->params, id_clk_outdiv4, 3)); + add_config("clk_outdivd1", extract_bits_or_default(cell->params, id_clk_outdivd1, 4)); + add_config("clk_outdivd2", extract_bits_or_default(cell->params, id_clk_outdivd2, 4)); + add_config("clk_outdivd3", extract_bits_or_default(cell->params, id_clk_outdivd3, 4)); + add_config("clk_outdivd4", extract_bits_or_default(cell->params, id_clk_outdivd4, 4)); + add_config("clk_outdivd5", extract_bits_or_default(cell->params, id_clk_outdivd5, 4)); + add_config("use_cal", bool_or_default(cell->params, id_use_cal, false)); + add_config("clk_cal_sel", extract_bits_or_default(cell->params, id_clk_cal_sel, 2)); + add_config("pll_odf", extract_bits_or_default(cell->params, id_pll_odf, 2)); + add_config("pll_lpf_res", extract_bits_or_default(cell->params, id_pll_lpf_res, 4)); + add_config("pll_lpf_cap", extract_bits_or_default(cell->params, id_pll_lpf_cap, 4)); + add_config("cal_div", extract_bits_or_default(cell->params, id_cal_div, 4)); + add_config("cal_delay", extract_bits_or_default(cell->params, id_cal_delay, 6)); + add_config("use_pll", bool_or_default(cell->params, id_use_pll, true)); + add_config("ref_intdiv", extract_bits_or_default(cell->params, id_ref_intdiv, 5)); + add_config("ref_osc_on", bool_or_default(cell->params, id_ref_osc_on, false)); + add_config("pll_cpump", extract_bits_or_default(cell->params, id_pll_cpump, 4)); + add_config("pll_lock", extract_bits_or_default(cell->params, id_pll_lock, 4)); + add_config("ext_fbk_on", bool_or_default(cell->params, id_ext_fbk_on, false)); + add_config("fbk_intdiv", extract_bits_or_default(cell->params, id_fbk_intdiv, 7)); + add_config("fbk_delay_on", bool_or_default(cell->params, id_fbk_delay_on, false)); + add_config("fbk_delay", extract_bits_or_default(cell->params, id_fbk_delay, 6)); + close_instance(); + } + + void write_rfb(CellInfo *cell) + { + open_instance(cell); + std::string context = str_or_default(cell->params, id_mem_ctxt, ""); + if (!context.empty()) + add_config("mem_ctxt", context); + add_config("wck_edge", bool_or_default(cell->params, id_wck_edge, false)); + close_instance(); + } + + void write_ram(CellInfo *cell) + { + open_instance(cell); + add_config("mcka_edge", bool_or_default(cell->params, id_mcka_edge, false)); + add_config("mckb_edge", bool_or_default(cell->params, id_mckb_edge, false)); + add_config("pcka_edge", bool_or_default(cell->params, id_pcka_edge, false)); + add_config("pckb_edge", bool_or_default(cell->params, id_pckb_edge, false)); + add_config("raw_config0", extract_bits_or_default(cell->params, id_raw_config0, 4)); + add_config("raw_config1", extract_bits_or_default(cell->params, id_raw_config1, 16)); + std::string context = str_or_default(cell->params, id_mem_ctxt, ""); + if (!context.empty()) + add_config("mem_ctxt", context); + close_instance(); + } + + void write_dsp(CellInfo *cell) + { + open_instance(cell); + add_config("raw_config0", extract_bits_or_default(cell->params, id_raw_config0, 27)); + add_config("raw_config1", extract_bits_or_default(cell->params, id_raw_config1, 24)); + add_config("raw_config2", extract_bits_or_default(cell->params, id_raw_config2, 14)); + add_config("raw_config3", extract_bits_or_default(cell->params, id_raw_config3, 3)); + close_instance(); + } + + void write_cdc(CellInfo *cell) + { + open_instance(cell); + if (cell->type.in(id_DDE, id_TDE, id_CDC, id_XCDC)) { + add_config("ck0_edge", bool_or_default(cell->params, id_ck0_edge, false)); + add_config("ck1_edge", bool_or_default(cell->params, id_ck1_edge, false)); + add_config("ack_sel", bool_or_default(cell->params, id_ack_sel, false)); + add_config("bck_sel", bool_or_default(cell->params, id_bck_sel, false)); + add_config("use_adest_arst", bool_or_default(cell->params, id_use_adest_arst, false)); + add_config("use_bdest_arst", bool_or_default(cell->params, id_use_bdest_arst, false)); + if (cell->type != id_DDE) { + add_config("use_asrc_arst", bool_or_default(cell->params, id_use_asrc_arst, false)); + add_config("use_bsrc_arst", bool_or_default(cell->params, id_use_bsrc_arst, false)); + } + if (cell->type == id_XCDC) { + add_config("cck_sel", bool_or_default(cell->params, id_cck_sel, false)); + add_config("dck_sel", bool_or_default(cell->params, id_dck_sel, false)); + add_config("use_csrc_arst", bool_or_default(cell->params, id_use_csrc_arst, false)); + add_config("use_dsrc_arst", bool_or_default(cell->params, id_use_dsrc_arst, false)); + add_config("use_cdest_arst", bool_or_default(cell->params, id_use_cdest_arst, false)); + add_config("use_ddest_arst", bool_or_default(cell->params, id_use_ddest_arst, false)); + add_config("link_BA", bool_or_default(cell->params, id_link_BA, false)); + add_config("link_CB", bool_or_default(cell->params, id_link_CB, false)); + add_config("link_DC", bool_or_default(cell->params, id_link_DC, false)); + } + } + close_instance(); + } + + void write_fifo(CellInfo *cell) + { + open_instance(cell); + add_config("rck_edge", bool_or_default(cell->params, id_rck_edge, false)); + add_config("wck_edge", bool_or_default(cell->params, id_wck_edge, false)); + if (cell->type != id_FIFO) { + add_config("use_read_arst", bool_or_default(cell->params, id_use_read_arst, false)); + add_config("use_write_arst", bool_or_default(cell->params, id_use_write_arst, false)); + } + add_config("read_addr_inv", extract_bits_or_default(cell->params, id_read_addr_inv, 7)); + close_instance(); + } + + void write_interconnections() + { + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + if (ni->wires.size() == 0) + continue; + std::vector nets; + for (auto &w : ni->wires) { + if (w.second.pip != PipId()) { + PipId pip = w.second.pip; + const auto &extra_data = *uarch->pip_extra_data(w.second.pip); + if (!extra_data.name || extra_data.type != PipExtra::PIP_EXTRA_INTERCONNECT) + continue; + auto &pd = chip_pip_info(ctx->chip_info, pip); + IdString src = IdString(chip_tile_info(ctx->chip_info, pip.tile).wires[pd.src_wire].name); + std::string tile_name = uarch->tile_name(pip.tile); + std::string src_name = src.c_str(ctx); + std::string type = "OTC"; + if (src_name.find("UI1x") != std::string::npos) + type = "ITC"; + if (boost::starts_with(src_name, "SO1.")) + type = "OTS"; + if (boost::starts_with(src_name, "SI1.")) + type = "ITS"; + src_name = update_name(tile_name, src_name); + src_name = src_name.substr(0, src_name.size() - 2); + + std::string name = cleanup_name(std::string(ni->name.c_str(ctx)) + "_" + src_name.substr(4)); + out << stringf(",\n\t\t%s: {\n", get_string(name).c_str()); + out << stringf("\t\t\t\"location\": %s,\n", get_string(tile_name + ":" + src_name).c_str()); + out << stringf("\t\t\t\"type\": %s\n\t\t}", get_string(type).c_str()); + } + } + } + } + + void write_instances() + { + out << "\t\"instances\": {\n"; + first_instance = true; + for (auto &cell : ctx->cells) { + switch (cell.second->type.index) { + case id_BEYOND_FE.index: + write_fe(cell.second.get()); + break; + case id_IOP.index: + case id_IP.index: + case id_OP.index: + case id_IOTP.index: + case id_ITP.index: + case id_OTP.index: + write_iop(cell.second.get()); + break; + case id_CY.index: + write_cy(cell.second.get()); + break; + case id_WFB.index: + write_wfb(cell.second.get()); + break; + case id_WFG.index: + write_wfg(cell.second.get()); + break; + case id_GCK.index: + write_gck(cell.second.get()); + break; + case id_IOM.index: + write_iom(cell.second.get()); + break; + case id_BFR.index: + write_bfr(cell.second.get()); + break; + case id_DDFR.index: + write_ddfr(cell.second.get()); + break; + case id_DFR.index: + write_dfr(cell.second.get()); + break; + case id_RAM.index: + write_ram(cell.second.get()); + break; + case id_RF.index: + case id_RFSP.index: + case id_XHRF.index: + case id_XWRF.index: + case id_XPRF.index: + write_rfb(cell.second.get()); + break; + case id_XLUT.index: + write_xlut(cell.second.get()); + break; + case id_FIFO.index: // mode 0 + case id_XHFIFO.index: // mode 1 + case id_XWFIFO.index: + write_fifo(cell.second.get()); + break; // mode 2 + case id_DDE.index: // mode 0 + case id_TDE.index: // mode 1 + case id_CDC.index: // mode 2 + case id_BGC.index: // mode 3 + case id_GBC.index: // mode 4 + case id_XCDC.index: + write_cdc(cell.second.get()); + break; // mode 5 + case id_DSP.index: + write_dsp(cell.second.get()); + break; + case id_PLL.index: + write_pll(cell.second.get()); + break; + // case id_CRX.index: + // case id_CTX.index: + // case id_PMA.index: + // case id_Service.index: + // case id_SOCIF.index: + default: + log_error("Unhandled cell %s of type %s\n", cell.second.get()->name.c_str(ctx), + cell.second->type.c_str(ctx)); + } + } + write_interconnections(); + out << "\n\t},\n"; + } + + void write_setup() + { + out << "\t\"setup\": {\n"; + out << "\t\t\"variant\": \"NG-ULTRA\",\n"; + out << "\t\t\"iobanks\": {\n"; + bool first = true; + for (auto &bank : uarch->bank_voltage) { + out << (first ? "" : ",\n"); + out << stringf("\t\t\t%s:%s", get_string(bank.first).c_str(), get_string(bank.second).c_str()); + first = false; + } + out << "\n\t\t}\n\t}\n"; + } + + void write_json() + { + out << "{\n"; + write_nets(); + write_instances(); + write_setup(); + out << "}\n"; + } +}; + +} // namespace + +void NgUltraImpl::write_bitstream_json(const std::string &filename) +{ + std::ofstream out(filename); + if (!out) + log_error("failed to open file %s for writing (%s)\n", filename.c_str(), strerror(errno)); + + BitstreamJsonBackend be(ctx, this, out); + be.write_json(); +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/ng-ultra/cells.cc b/himbaechel/uarch/ng-ultra/cells.cc new file mode 100644 index 00000000..322dacb0 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/cells.cc @@ -0,0 +1,84 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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 "nextpnr.h" +#include "pack.h" + +#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +CellInfo *NgUltraPacker::create_cell_ptr(IdString type, IdString name) +{ + CellInfo *cell = ctx->createCell(name, type); + + auto add_port = [&](const IdString id, PortType dir) { + cell->ports[id].name = id; + cell->ports[id].type = dir; + }; + if (type == id_BEYOND_FE) { + add_port(id_I1, PORT_IN); + add_port(id_I2, PORT_IN); + add_port(id_I3, PORT_IN); + add_port(id_I4, PORT_IN); + add_port(id_LO, PORT_OUT); + add_port(id_DI, PORT_IN); + add_port(id_L, PORT_IN); + add_port(id_CK, PORT_IN); + add_port(id_R, PORT_IN); + add_port(id_DO, PORT_OUT); + } else if (type == id_BFR) { + add_port(id_I, PORT_IN); + add_port(id_O, PORT_OUT); + } else if (type == id_DFR) { + add_port(id_I, PORT_IN); + add_port(id_O, PORT_OUT); + add_port(id_L, PORT_IN); + add_port(id_CK, PORT_IN); + add_port(id_R, PORT_IN); + } else if (type == id_DDFR) { + add_port(id_I, PORT_IN); + add_port(id_O, PORT_OUT); + add_port(id_L, PORT_IN); + add_port(id_CK, PORT_IN); + add_port(id_R, PORT_IN); + add_port(id_I2, PORT_IN); + add_port(id_O2, PORT_OUT); + add_port(id_CKF, PORT_IN); + } else if (type == id_IOM) { + add_port(id_P17RI, PORT_IN); + add_port(id_CKO1, PORT_OUT); + add_port(id_P19RI, PORT_IN); + add_port(id_CKO2, PORT_OUT); + } else if (type == id_WFB) { + add_port(id_ZI, PORT_IN); + add_port(id_ZO, PORT_OUT); + } else if (type == id_GCK) { + add_port(id_SI1, PORT_IN); + add_port(id_SI2, PORT_IN); + add_port(id_CMD, PORT_IN); + add_port(id_SO, PORT_OUT); + } else { + log_error("Trying to create unknown cell type %s\n", type.c_str(ctx)); + } + return cell; +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/ng-ultra/constids.inc b/himbaechel/uarch/ng-ultra/constids.inc new file mode 100644 index 00000000..c091c710 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/constids.inc @@ -0,0 +1,6026 @@ +// autogenerated items +// BEYOND_FE +X(BEYOND_FE) +X(CK) +X(DC) +X(DI) +X(DJ) +X(DK) +X(DO) +X(DP) +X(DS) +X(I1) +X(I2) +X(I3) +X(I4) +X(L) +X(LD) +X(LJ) +X(LK) +X(LO) +X(LX) +X(R) + +// BFF +X(BFF) +X(C) +X(I) +X(J) +X(K) +X(O) +X(P) +X(S) + +// BFR +X(BFR) +//X(I) +//X(O) + +// BGC +X(BGC) +X(AI1) +X(AI2) +X(AI3) +X(AI4) +X(AI5) +X(AI6) +X(AO1) +X(AO2) +X(AO3) +X(AO4) +X(AO5) +X(AO6) +X(BI1) +X(BI2) +X(BI3) +X(BI4) +X(BI5) +X(BI6) +X(BO1) +X(BO2) +X(BO3) +X(BO4) +X(BO5) +X(BO6) + +// CDC +X(CDC) +X(ADRSTI) +X(ADRSTO) +//X(AI1) +//X(AI2) +//X(AI3) +//X(AI4) +//X(AI5) +//X(AI6) +//X(AO1) +//X(AO2) +//X(AO3) +//X(AO4) +//X(AO5) +//X(AO6) +X(ASRSTI) +X(ASRSTO) +X(BDRSTI) +X(BDRSTO) +//X(BI1) +//X(BI2) +//X(BI3) +//X(BI4) +//X(BI5) +//X(BI6) +//X(BO1) +//X(BO2) +//X(BO3) +//X(BO4) +//X(BO5) +//X(BO6) +X(BSRSTI) +X(BSRSTO) +X(CK1) +X(CK2) + +// CRX +X(CRX) +X(ALIGN_E_I) +X(ALIGN_O) +X(ALIGN_S_I) +X(BUF_R_I) +X(BUSY_O) +X(CH_A_O1) +X(CH_A_O2) +X(CH_A_O3) +X(CH_A_O4) +X(CH_A_O5) +X(CH_A_O6) +X(CH_A_O7) +X(CH_A_O8) +X(CH_COM_O1) +X(CH_COM_O2) +X(CH_COM_O3) +X(CH_COM_O4) +X(CH_COM_O5) +X(CH_COM_O6) +X(CH_COM_O7) +X(CH_COM_O8) +X(CH_F_O1) +X(CH_F_O2) +X(CH_F_O3) +X(CH_F_O4) +X(CH_F_O5) +X(CH_F_O6) +X(CH_F_O7) +X(CH_F_O8) +X(CH_K_O1) +X(CH_K_O2) +X(CH_K_O3) +X(CH_K_O4) +X(CH_K_O5) +X(CH_K_O6) +X(CH_K_O7) +X(CH_K_O8) +X(DATA_O1) +X(DATA_O10) +X(DATA_O11) +X(DATA_O12) +X(DATA_O13) +X(DATA_O14) +X(DATA_O15) +X(DATA_O16) +X(DATA_O17) +X(DATA_O18) +X(DATA_O19) +X(DATA_O2) +X(DATA_O20) +X(DATA_O21) +X(DATA_O22) +X(DATA_O23) +X(DATA_O24) +X(DATA_O25) +X(DATA_O26) +X(DATA_O27) +X(DATA_O28) +X(DATA_O29) +X(DATA_O3) +X(DATA_O30) +X(DATA_O31) +X(DATA_O32) +X(DATA_O33) +X(DATA_O34) +X(DATA_O35) +X(DATA_O36) +X(DATA_O37) +X(DATA_O38) +X(DATA_O39) +X(DATA_O4) +X(DATA_O40) +X(DATA_O41) +X(DATA_O42) +X(DATA_O43) +X(DATA_O44) +X(DATA_O45) +X(DATA_O46) +X(DATA_O47) +X(DATA_O48) +X(DATA_O49) +X(DATA_O5) +X(DATA_O50) +X(DATA_O51) +X(DATA_O52) +X(DATA_O53) +X(DATA_O54) +X(DATA_O55) +X(DATA_O56) +X(DATA_O57) +X(DATA_O58) +X(DATA_O59) +X(DATA_O6) +X(DATA_O60) +X(DATA_O61) +X(DATA_O62) +X(DATA_O63) +X(DATA_O64) +X(DATA_O7) +X(DATA_O8) +X(DATA_O9) +X(DBG_S_I1) +X(DBG_S_I2) +X(DBG_S_I3) +X(DEC_E_I) +X(DSCR_E_I) +X(D_ERR_O1) +X(D_ERR_O2) +X(D_ERR_O3) +X(D_ERR_O4) +X(D_ERR_O5) +X(D_ERR_O6) +X(D_ERR_O7) +X(D_ERR_O8) +X(LINK) +X(LL_FLOCK_O) +X(LL_SLOCK_O) +X(LOS_O) +X(MEYE_RST_I) +X(NIT_O1) +X(NIT_O2) +X(NIT_O3) +X(NIT_O4) +X(NIT_O5) +X(NIT_O6) +X(NIT_O7) +X(NIT_O8) +X(OVS_BS_I1) +X(OVS_BS_I2) +X(PLL_LOCKT_O) +X(PLL_LOCK_O) +X(PMA_RSTN_I) +X(PWDN_N_I) +X(REP_E_I) +X(RST_N_I) +X(TST_O1) +X(TST_O2) +X(TST_O3) +X(TST_O4) +X(TST_O5) +X(TST_O6) +X(TST_O7) +X(TST_O8) +X(VREALIGN_O) + +// CSC +X(CSC) +//X(C) +//X(I) +//X(J) +//X(K) +//X(O) +//X(P) +//X(S) + +// CTX +X(CTX) +//X(BUSY_O) +X(CH_K_I1) +X(CH_K_I2) +X(CH_K_I3) +X(CH_K_I4) +X(CH_K_I5) +X(CH_K_I6) +X(CH_K_I7) +X(CH_K_I8) +X(CLK_E_I) +X(CLK_O) +X(DATA_I1) +X(DATA_I10) +X(DATA_I11) +X(DATA_I12) +X(DATA_I13) +X(DATA_I14) +X(DATA_I15) +X(DATA_I16) +X(DATA_I17) +X(DATA_I18) +X(DATA_I19) +X(DATA_I2) +X(DATA_I20) +X(DATA_I21) +X(DATA_I22) +X(DATA_I23) +X(DATA_I24) +X(DATA_I25) +X(DATA_I26) +X(DATA_I27) +X(DATA_I28) +X(DATA_I29) +X(DATA_I3) +X(DATA_I30) +X(DATA_I31) +X(DATA_I32) +X(DATA_I33) +X(DATA_I34) +X(DATA_I35) +X(DATA_I36) +X(DATA_I37) +X(DATA_I38) +X(DATA_I39) +X(DATA_I4) +X(DATA_I40) +X(DATA_I41) +X(DATA_I42) +X(DATA_I43) +X(DATA_I44) +X(DATA_I45) +X(DATA_I46) +X(DATA_I47) +X(DATA_I48) +X(DATA_I49) +X(DATA_I5) +X(DATA_I50) +X(DATA_I51) +X(DATA_I52) +X(DATA_I53) +X(DATA_I54) +X(DATA_I55) +X(DATA_I56) +X(DATA_I57) +X(DATA_I58) +X(DATA_I59) +X(DATA_I6) +X(DATA_I60) +X(DATA_I61) +X(DATA_I62) +X(DATA_I63) +X(DATA_I64) +X(DATA_I7) +X(DATA_I8) +X(DATA_I9) +X(ENC_E_I1) +X(ENC_E_I2) +X(ENC_E_I3) +X(ENC_E_I4) +X(ENC_E_I5) +X(ENC_E_I6) +X(ENC_E_I7) +X(ENC_E_I8) +X(EOF_I1) +X(EOF_I2) +X(EOF_I3) +X(EOF_I4) +X(EOF_I5) +X(EOF_I6) +X(EOF_I7) +X(EOF_I8) +X(EOMF_I1) +X(EOMF_I2) +X(EOMF_I3) +X(EOMF_I4) +X(EOMF_I5) +X(EOMF_I6) +X(EOMF_I7) +X(EOMF_I8) +X(INV_K_O) +//X(LINK) +//X(PWDN_N_I) +//X(REP_E_I) +//X(RST_N_I) +X(SCR_E_I1) +X(SCR_E_I2) +X(SCR_E_I3) +X(SCR_E_I4) +X(SCR_E_I5) +X(SCR_E_I6) +X(SCR_E_I7) +X(SCR_E_I8) + +// CY +X(CY) +X(A1) +X(A2) +X(A3) +X(A4) +X(B1) +X(B2) +X(B3) +X(B4) +X(CI) +X(CO) +//X(I1) +//X(I2) +//X(I3) +//X(I4) +X(O1) +X(O2) +X(O3) +X(O4) +X(S1) +X(S2) +X(S3) +X(S4) + +// DDE +X(DDE) +//X(ADRSTI) +//X(ADRSTO) +//X(AI1) +//X(AI2) +//X(AI3) +//X(AI4) +//X(AI5) +//X(AI6) +//X(AO1) +//X(AO2) +//X(AO3) +//X(AO4) +//X(AO5) +//X(AO6) +//X(BDRSTI) +//X(BDRSTO) +//X(BI1) +//X(BI2) +//X(BI3) +//X(BI4) +//X(BI5) +//X(BI6) +//X(BO1) +//X(BO2) +//X(BO3) +//X(BO4) +//X(BO5) +//X(BO6) +//X(CK1) +//X(CK2) + +// DDFR +X(DDFR) +//X(CK) +X(CKF) +//X(I) +//X(I2) +//X(L) +//X(O) +//X(O2) +//X(R) + +// DFF +X(DFF) +//X(C) +//X(CK) +//X(I) +//X(J) +//X(K) +//X(L) +//X(O) +//X(P) +//X(R) +//X(S) + +// DFR +X(DFR) +//X(CK) +//X(I) +//X(L) +//X(O) +//X(R) + +// DSP +X(DSP) +//X(A1) +X(A10) +X(A11) +X(A12) +X(A13) +X(A14) +X(A15) +X(A16) +X(A17) +X(A18) +X(A19) +//X(A2) +X(A20) +X(A21) +X(A22) +X(A23) +X(A24) +//X(A3) +//X(A4) +X(A5) +X(A6) +X(A7) +X(A8) +X(A9) +//X(B1) +X(B10) +X(B11) +X(B12) +X(B13) +X(B14) +X(B15) +X(B16) +X(B17) +X(B18) +//X(B2) +//X(B3) +//X(B4) +X(B5) +X(B6) +X(B7) +X(B8) +X(B9) +X(C1) +X(C10) +X(C11) +X(C12) +X(C13) +X(C14) +X(C15) +X(C16) +X(C17) +X(C18) +X(C19) +X(C2) +X(C20) +X(C21) +X(C22) +X(C23) +X(C24) +X(C25) +X(C26) +X(C27) +X(C28) +X(C29) +X(C3) +X(C30) +X(C31) +X(C32) +X(C33) +X(C34) +X(C35) +X(C36) +X(C4) +X(C5) +X(C6) +X(C7) +X(C8) +X(C9) +X(CAI1) +X(CAI10) +X(CAI11) +X(CAI12) +X(CAI13) +X(CAI14) +X(CAI15) +X(CAI16) +X(CAI17) +X(CAI18) +X(CAI19) +X(CAI2) +X(CAI20) +X(CAI21) +X(CAI22) +X(CAI23) +X(CAI24) +X(CAI3) +X(CAI4) +X(CAI5) +X(CAI6) +X(CAI7) +X(CAI8) +X(CAI9) +X(CAO1) +X(CAO10) +X(CAO11) +X(CAO12) +X(CAO13) +X(CAO14) +X(CAO15) +X(CAO16) +X(CAO17) +X(CAO18) +X(CAO19) +X(CAO2) +X(CAO20) +X(CAO21) +X(CAO22) +X(CAO23) +X(CAO24) +X(CAO3) +X(CAO4) +X(CAO5) +X(CAO6) +X(CAO7) +X(CAO8) +X(CAO9) +X(CBI1) +X(CBI10) +X(CBI11) +X(CBI12) +X(CBI13) +X(CBI14) +X(CBI15) +X(CBI16) +X(CBI17) +X(CBI18) +X(CBI2) +X(CBI3) +X(CBI4) +X(CBI5) +X(CBI6) +X(CBI7) +X(CBI8) +X(CBI9) +X(CBO1) +X(CBO10) +X(CBO11) +X(CBO12) +X(CBO13) +X(CBO14) +X(CBO15) +X(CBO16) +X(CBO17) +X(CBO18) +X(CBO2) +X(CBO3) +X(CBO4) +X(CBO5) +X(CBO6) +X(CBO7) +X(CBO8) +X(CBO9) +X(CCI) +X(CCO) +//X(CI) +//X(CK) +X(CO43) +X(CO57) +X(CZI1) +X(CZI10) +X(CZI11) +X(CZI12) +X(CZI13) +X(CZI14) +X(CZI15) +X(CZI16) +X(CZI17) +X(CZI18) +X(CZI19) +X(CZI2) +X(CZI20) +X(CZI21) +X(CZI22) +X(CZI23) +X(CZI24) +X(CZI25) +X(CZI26) +X(CZI27) +X(CZI28) +X(CZI29) +X(CZI3) +X(CZI30) +X(CZI31) +X(CZI32) +X(CZI33) +X(CZI34) +X(CZI35) +X(CZI36) +X(CZI37) +X(CZI38) +X(CZI39) +X(CZI4) +X(CZI40) +X(CZI41) +X(CZI42) +X(CZI43) +X(CZI44) +X(CZI45) +X(CZI46) +X(CZI47) +X(CZI48) +X(CZI49) +X(CZI5) +X(CZI50) +X(CZI51) +X(CZI52) +X(CZI53) +X(CZI54) +X(CZI55) +X(CZI56) +X(CZI6) +X(CZI7) +X(CZI8) +X(CZI9) +X(CZO1) +X(CZO10) +X(CZO11) +X(CZO12) +X(CZO13) +X(CZO14) +X(CZO15) +X(CZO16) +X(CZO17) +X(CZO18) +X(CZO19) +X(CZO2) +X(CZO20) +X(CZO21) +X(CZO22) +X(CZO23) +X(CZO24) +X(CZO25) +X(CZO26) +X(CZO27) +X(CZO28) +X(CZO29) +X(CZO3) +X(CZO30) +X(CZO31) +X(CZO32) +X(CZO33) +X(CZO34) +X(CZO35) +X(CZO36) +X(CZO37) +X(CZO38) +X(CZO39) +X(CZO4) +X(CZO40) +X(CZO41) +X(CZO42) +X(CZO43) +X(CZO44) +X(CZO45) +X(CZO46) +X(CZO47) +X(CZO48) +X(CZO49) +X(CZO5) +X(CZO50) +X(CZO51) +X(CZO52) +X(CZO53) +X(CZO54) +X(CZO55) +X(CZO56) +X(CZO6) +X(CZO7) +X(CZO8) +X(CZO9) +X(D1) +X(D10) +X(D11) +X(D12) +X(D13) +X(D14) +X(D15) +X(D16) +X(D17) +X(D18) +X(D2) +X(D3) +X(D4) +X(D5) +X(D6) +X(D7) +X(D8) +X(D9) +X(OVF) +//X(R) +X(RESERVED) +X(RZ) +X(WE) +X(WEZ) +X(Z1) +X(Z10) +X(Z11) +X(Z12) +X(Z13) +X(Z14) +X(Z15) +X(Z16) +X(Z17) +X(Z18) +X(Z19) +X(Z2) +X(Z20) +X(Z21) +X(Z22) +X(Z23) +X(Z24) +X(Z25) +X(Z26) +X(Z27) +X(Z28) +X(Z29) +X(Z3) +X(Z30) +X(Z31) +X(Z32) +X(Z33) +X(Z34) +X(Z35) +X(Z36) +X(Z37) +X(Z38) +X(Z39) +X(Z4) +X(Z40) +X(Z41) +X(Z42) +X(Z43) +X(Z44) +X(Z45) +X(Z46) +X(Z47) +X(Z48) +X(Z49) +X(Z5) +X(Z50) +X(Z51) +X(Z52) +X(Z53) +X(Z54) +X(Z55) +X(Z56) +X(Z6) +X(Z7) +X(Z8) +X(Z9) + +// FCI +X(FCI) +//X(I) +//X(O) + +// FCO +X(FCO) +//X(I) +//X(O) + +// FIFO +X(FIFO) +//X(I1) +X(I10) +X(I11) +X(I12) +X(I13) +X(I14) +X(I15) +X(I16) +X(I17) +X(I18) +//X(I2) +//X(I3) +//X(I4) +X(I5) +X(I6) +X(I7) +X(I8) +X(I9) +//X(O1) +X(O10) +X(O11) +X(O12) +X(O13) +X(O14) +X(O15) +X(O16) +X(O17) +X(O18) +//X(O2) +//X(O3) +//X(O4) +X(O5) +X(O6) +X(O7) +X(O8) +X(O9) +X(RAI1) +X(RAI2) +X(RAI3) +X(RAI4) +X(RAI5) +X(RAI6) +X(RAO1) +X(RAO2) +X(RAO3) +X(RAO4) +X(RAO5) +X(RAO6) +X(RCK) +X(REQ) +X(RRSTI1) +X(RRSTI2) +X(RRSTO) +X(WAI1) +X(WAI2) +X(WAI3) +X(WAI4) +X(WAI5) +X(WAI6) +X(WAO1) +X(WAO2) +X(WAO3) +X(WAO4) +X(WAO5) +X(WAO6) +X(WCK) +//X(WE) +X(WEA) +X(WEQ) +X(WRSTI1) +X(WRSTI2) +X(WRSTO) +X(WRSTI) +X(RRSTI) + +// GBC +X(GBC) +//X(AI1) +//X(AI2) +//X(AI3) +//X(AI4) +//X(AI5) +//X(AI6) +//X(AO1) +//X(AO2) +//X(AO3) +//X(AO4) +//X(AO5) +//X(AO6) +//X(BI1) +//X(BI2) +//X(BI3) +//X(BI4) +//X(BI5) +//X(BI6) +//X(BO1) +//X(BO2) +//X(BO3) +//X(BO4) +//X(BO5) +//X(BO6) + +// GCI +X(GCI) +//X(I) +//X(O) + +// GCK +X(GCK) +X(CMD) +X(SI1) +X(SI2) +X(SO) + +// GEA +X(GEA) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +X(I19) +//X(I2) +X(I20) +X(I21) +X(I22) +X(I23) +X(I24) +X(I25) +X(I26) +X(I27) +X(I28) +X(I29) +//X(I3) +X(I30) +X(I31) +X(I32) +X(I33) +X(I34) +X(I35) +X(I36) +X(I37) +X(I38) +X(I39) +//X(I4) +X(I40) +X(I41) +X(I42) +X(I43) +X(I44) +X(I45) +X(I46) +X(I47) +X(I48) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O2) +//X(O3) +//X(O4) + +// GEB +X(GEB) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) + +// GSO +X(GSO) +//X(I) +//X(O) + +// HEE +X(HEE) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) + +// HSS +X(HSS) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) + +// IOM +X(IOM) +X(AL1D) +X(AL1T) +X(AL2D) +X(AL2T) +X(AL3D) +X(AL3T) +X(ALCK1) +X(ALCK2) +X(ALCK3) +X(CA1D1) +X(CA1D2) +X(CA1D3) +X(CA1D4) +X(CA1D5) +X(CA1D6) +X(CA1N1) +X(CA1N2) +X(CA1N3) +X(CA1N4) +X(CA1P1) +X(CA1P2) +X(CA1P3) +X(CA1P4) +X(CA1T1) +X(CA1T2) +X(CA1T3) +X(CA1T4) +X(CA2D1) +X(CA2D2) +X(CA2D3) +X(CA2D4) +X(CA2D5) +X(CA2D6) +X(CA2N1) +X(CA2N2) +X(CA2N3) +X(CA2N4) +X(CA2P1) +X(CA2P2) +X(CA2P3) +X(CA2P4) +X(CA2T1) +X(CA2T2) +X(CA2T3) +X(CA2T4) +X(CCK) +X(CKO1) +X(CKO2) +X(DCL) +X(DCRN) +X(DQ1CI1) +X(DQ1CI2) +X(DQ1CI3) +X(DQ1CI4) +X(DQ1CI5) +X(DQ1CI6) +X(DQ1CI7) +X(DQ1CI8) +X(DQ2CI1) +X(DQ2CI2) +X(DQ2CI3) +X(DQ2CI4) +X(DQ2CI5) +X(DQ2CI6) +X(DQ2CI7) +X(DQ2CI8) +X(DQ3CI1) +X(DQ3CI2) +X(DQ3CI3) +X(DQ3CI4) +X(DQ3CI5) +X(DQ3CI6) +X(DQ3CI7) +X(DQ3CI8) +X(DQS1CI1) +X(DQS1CI2) +X(DQS1CI3) +X(DQS1CI4) +X(DQS1CI5) +X(DQS1CI6) +X(DQS1CI7) +X(DQS1CI8) +X(DQS2CI1) +X(DQS2CI2) +X(DQS2CI3) +X(DQS2CI4) +X(DQS2CI5) +X(DQS2CI6) +X(DQS2CI7) +X(DQS2CI8) +X(DQS3CI1) +X(DQS3CI2) +X(DQS3CI3) +X(DQS3CI4) +X(DQS3CI5) +X(DQS3CI6) +X(DQS3CI7) +X(DQS3CI8) +X(DRA1) +X(DRA2) +X(DRA3) +X(DRA4) +X(DRCCSN) +X(DRDPA1CSN) +X(DRDPA2CSN) +X(DRDPA3CSN) +X(DRE) +X(DRI1) +X(DRI1CSN) +X(DRI2) +X(DRI2CSN) +X(DRI3) +X(DRI3CSN) +X(DRI4) +X(DRI5) +X(DRI6) +X(DRO1) +X(DRO1CSN) +X(DRO2) +X(DRO2CSN) +X(DRO3) +X(DRO3CSN) +X(DRO4) +X(DRO5) +X(DRO6) +X(DRWDS) +X(DRWEN) +X(FA1) +X(FA2) +X(FA3) +X(FA4) +X(FA5) +X(FA6) +X(FCK1) +X(FCK2) +X(FDCK) +X(FLD) +X(FLG) +X(FZ) +X(LD1RN) +X(LD2RN) +X(LD3RN) +X(LDSCK1) +X(LDSCK2) +X(LDSCK3) +X(LE) +X(P10CI1) +X(P10CL) +X(P10CO) +X(P10CR) +X(P10CTI) +X(P10CTO) +X(P10EI1) +X(P10EI2) +X(P10EI3) +X(P10EI4) +X(P10EI5) +X(P10EI6) +X(P10EI7) +X(P10EI8) +X(P10EL) +X(P10EO) +X(P10ER) +X(P10RI) +X(P10RL) +X(P10RO1) +X(P10RO2) +X(P10RO3) +X(P10RO4) +X(P10RO5) +X(P10RO6) +X(P10RO7) +X(P10RO8) +X(P10RR) +X(P11CI1) +X(P11CL) +X(P11CO) +X(P11CR) +X(P11CTI) +X(P11CTO) +X(P11EI1) +X(P11EI2) +X(P11EI3) +X(P11EI4) +X(P11EI5) +X(P11EI6) +X(P11EI7) +X(P11EI8) +X(P11EL) +X(P11EO) +X(P11ER) +X(P11RI) +X(P11RL) +X(P11RO1) +X(P11RO2) +X(P11RO3) +X(P11RO4) +X(P11RO5) +X(P11RO6) +X(P11RO7) +X(P11RO8) +X(P11RR) +X(P12CI1) +X(P12CL) +X(P12CO) +X(P12CR) +X(P12CTI) +X(P12CTO) +X(P12EI1) +X(P12EI2) +X(P12EI3) +X(P12EI4) +X(P12EI5) +X(P12EI6) +X(P12EI7) +X(P12EI8) +X(P12EL) +X(P12EO) +X(P12ER) +X(P12RI) +X(P12RL) +X(P12RO1) +X(P12RO2) +X(P12RO3) +X(P12RO4) +X(P12RO5) +X(P12RO6) +X(P12RO7) +X(P12RO8) +X(P12RR) +X(P13CI1) +X(P13CL) +X(P13CO) +X(P13CR) +X(P13CTI) +X(P13CTO) +X(P13EI1) +X(P13EI2) +X(P13EI3) +X(P13EI4) +X(P13EI5) +X(P13EI6) +X(P13EI7) +X(P13EI8) +X(P13EL) +X(P13EO) +X(P13ER) +X(P13RI) +X(P13RL) +X(P13RO1) +X(P13RO2) +X(P13RO3) +X(P13RO4) +X(P13RO5) +X(P13RO6) +X(P13RO7) +X(P13RO8) +X(P13RR) +X(P14CI1) +X(P14CL) +X(P14CO) +X(P14CR) +X(P14CTI) +X(P14CTO) +X(P14EI1) +X(P14EI2) +X(P14EI3) +X(P14EI4) +X(P14EI5) +X(P14EI6) +X(P14EI7) +X(P14EI8) +X(P14EL) +X(P14EO) +X(P14ER) +X(P14RI) +X(P14RL) +X(P14RO1) +X(P14RO2) +X(P14RO3) +X(P14RO4) +X(P14RO5) +X(P14RO6) +X(P14RO7) +X(P14RO8) +X(P14RR) +X(P15CI1) +X(P15CL) +X(P15CO) +X(P15CR) +X(P15CTI) +X(P15CTO) +X(P15EI1) +X(P15EI2) +X(P15EI3) +X(P15EI4) +X(P15EI5) +X(P15EI6) +X(P15EI7) +X(P15EI8) +X(P15EL) +X(P15EO) +X(P15ER) +X(P15RI) +X(P15RL) +X(P15RO1) +X(P15RO2) +X(P15RO3) +X(P15RO4) +X(P15RO5) +X(P15RO6) +X(P15RO7) +X(P15RO8) +X(P15RR) +X(P16CI1) +X(P16CL) +X(P16CO) +X(P16CR) +X(P16CTI) +X(P16CTO) +X(P16EI1) +X(P16EI2) +X(P16EI3) +X(P16EI4) +X(P16EI5) +X(P16EI6) +X(P16EI7) +X(P16EI8) +X(P16EL) +X(P16EO) +X(P16ER) +X(P16RI) +X(P16RL) +X(P16RO1) +X(P16RO2) +X(P16RO3) +X(P16RO4) +X(P16RO5) +X(P16RO6) +X(P16RO7) +X(P16RO8) +X(P16RR) +X(P17CI1) +X(P17CL) +X(P17CO) +X(P17CR) +X(P17CTI) +X(P17CTO) +X(P17EI1) +X(P17EI2) +X(P17EI3) +X(P17EI4) +X(P17EI5) +X(P17EI6) +X(P17EI7) +X(P17EI8) +X(P17EL) +X(P17EO) +X(P17ER) +X(P17RI) +X(P17RL) +X(P17RO1) +X(P17RO2) +X(P17RO3) +X(P17RO4) +X(P17RO5) +X(P17RO6) +X(P17RO7) +X(P17RO8) +X(P17RR) +X(P18CI1) +X(P18CL) +X(P18CO) +X(P18CR) +X(P18CTI) +X(P18CTO) +X(P18EI1) +X(P18EI2) +X(P18EI3) +X(P18EI4) +X(P18EI5) +X(P18EI6) +X(P18EI7) +X(P18EI8) +X(P18EL) +X(P18EO) +X(P18ER) +X(P18RI) +X(P18RL) +X(P18RO1) +X(P18RO2) +X(P18RO3) +X(P18RO4) +X(P18RO5) +X(P18RO6) +X(P18RO7) +X(P18RO8) +X(P18RR) +X(P19CI1) +X(P19CL) +X(P19CO) +X(P19CR) +X(P19CTI) +X(P19CTO) +X(P19EI1) +X(P19EI2) +X(P19EI3) +X(P19EI4) +X(P19EI5) +X(P19EI6) +X(P19EI7) +X(P19EI8) +X(P19EL) +X(P19EO) +X(P19ER) +X(P19RI) +X(P19RL) +X(P19RO1) +X(P19RO2) +X(P19RO3) +X(P19RO4) +X(P19RO5) +X(P19RO6) +X(P19RO7) +X(P19RO8) +X(P19RR) +X(P1CI1) +X(P1CL) +X(P1CO) +X(P1CR) +X(P1CTI) +X(P1CTO) +X(P1EI1) +X(P1EI2) +X(P1EI3) +X(P1EI4) +X(P1EI5) +X(P1EI6) +X(P1EI7) +X(P1EI8) +X(P1EL) +X(P1EO) +X(P1ER) +X(P1RI) +X(P1RL) +X(P1RO1) +X(P1RO2) +X(P1RO3) +X(P1RO4) +X(P1RO5) +X(P1RO6) +X(P1RO7) +X(P1RO8) +X(P1RR) +X(P20CI1) +X(P20CL) +X(P20CO) +X(P20CR) +X(P20CTI) +X(P20CTO) +X(P20EI1) +X(P20EI2) +X(P20EI3) +X(P20EI4) +X(P20EI5) +X(P20EI6) +X(P20EI7) +X(P20EI8) +X(P20EL) +X(P20EO) +X(P20ER) +X(P20RI) +X(P20RL) +X(P20RO1) +X(P20RO2) +X(P20RO3) +X(P20RO4) +X(P20RO5) +X(P20RO6) +X(P20RO7) +X(P20RO8) +X(P20RR) +X(P21CI1) +X(P21CL) +X(P21CO) +X(P21CR) +X(P21CTI) +X(P21CTO) +X(P21EI1) +X(P21EI2) +X(P21EI3) +X(P21EI4) +X(P21EI5) +X(P21EI6) +X(P21EI7) +X(P21EI8) +X(P21EL) +X(P21EO) +X(P21ER) +X(P21RI) +X(P21RL) +X(P21RO1) +X(P21RO2) +X(P21RO3) +X(P21RO4) +X(P21RO5) +X(P21RO6) +X(P21RO7) +X(P21RO8) +X(P21RR) +X(P22CI1) +X(P22CL) +X(P22CO) +X(P22CR) +X(P22CTI) +X(P22CTO) +X(P22EI1) +X(P22EI2) +X(P22EI3) +X(P22EI4) +X(P22EI5) +X(P22EI6) +X(P22EI7) +X(P22EI8) +X(P22EL) +X(P22EO) +X(P22ER) +X(P22RI) +X(P22RL) +X(P22RO1) +X(P22RO2) +X(P22RO3) +X(P22RO4) +X(P22RO5) +X(P22RO6) +X(P22RO7) +X(P22RO8) +X(P22RR) +X(P23CI1) +X(P23CL) +X(P23CO) +X(P23CR) +X(P23CTI) +X(P23CTO) +X(P23EI1) +X(P23EI2) +X(P23EI3) +X(P23EI4) +X(P23EI5) +X(P23EI6) +X(P23EI7) +X(P23EI8) +X(P23EL) +X(P23EO) +X(P23ER) +X(P23RI) +X(P23RL) +X(P23RO1) +X(P23RO2) +X(P23RO3) +X(P23RO4) +X(P23RO5) +X(P23RO6) +X(P23RO7) +X(P23RO8) +X(P23RR) +X(P24CI1) +X(P24CL) +X(P24CO) +X(P24CR) +X(P24CTI) +X(P24CTO) +X(P24EI1) +X(P24EI2) +X(P24EI3) +X(P24EI4) +X(P24EI5) +X(P24EI6) +X(P24EI7) +X(P24EI8) +X(P24EL) +X(P24EO) +X(P24ER) +X(P24RI) +X(P24RL) +X(P24RO1) +X(P24RO2) +X(P24RO3) +X(P24RO4) +X(P24RO5) +X(P24RO6) +X(P24RO7) +X(P24RO8) +X(P24RR) +X(P25CI1) +X(P25CL) +X(P25CO) +X(P25CR) +X(P25CTI) +X(P25CTO) +X(P25EI1) +X(P25EI2) +X(P25EI3) +X(P25EI4) +X(P25EI5) +X(P25EI6) +X(P25EI7) +X(P25EI8) +X(P25EL) +X(P25EO) +X(P25ER) +X(P25RI) +X(P25RL) +X(P25RO1) +X(P25RO2) +X(P25RO3) +X(P25RO4) +X(P25RO5) +X(P25RO6) +X(P25RO7) +X(P25RO8) +X(P25RR) +X(P26CI1) +X(P26CL) +X(P26CO) +X(P26CR) +X(P26CTI) +X(P26CTO) +X(P26EI1) +X(P26EI2) +X(P26EI3) +X(P26EI4) +X(P26EI5) +X(P26EI6) +X(P26EI7) +X(P26EI8) +X(P26EL) +X(P26EO) +X(P26ER) +X(P26RI) +X(P26RL) +X(P26RO1) +X(P26RO2) +X(P26RO3) +X(P26RO4) +X(P26RO5) +X(P26RO6) +X(P26RO7) +X(P26RO8) +X(P26RR) +X(P27CI1) +X(P27CL) +X(P27CO) +X(P27CR) +X(P27CTI) +X(P27CTO) +X(P27EI1) +X(P27EI2) +X(P27EI3) +X(P27EI4) +X(P27EI5) +X(P27EI6) +X(P27EI7) +X(P27EI8) +X(P27EL) +X(P27EO) +X(P27ER) +X(P27RI) +X(P27RL) +X(P27RO1) +X(P27RO2) +X(P27RO3) +X(P27RO4) +X(P27RO5) +X(P27RO6) +X(P27RO7) +X(P27RO8) +X(P27RR) +X(P28CI1) +X(P28CL) +X(P28CO) +X(P28CR) +X(P28CTI) +X(P28CTO) +X(P28EI1) +X(P28EI2) +X(P28EI3) +X(P28EI4) +X(P28EI5) +X(P28EI6) +X(P28EI7) +X(P28EI8) +X(P28EL) +X(P28EO) +X(P28ER) +X(P28RI) +X(P28RL) +X(P28RO1) +X(P28RO2) +X(P28RO3) +X(P28RO4) +X(P28RO5) +X(P28RO6) +X(P28RO7) +X(P28RO8) +X(P28RR) +X(P29CI1) +X(P29CL) +X(P29CO) +X(P29CR) +X(P29CTI) +X(P29CTO) +X(P29EI1) +X(P29EI2) +X(P29EI3) +X(P29EI4) +X(P29EI5) +X(P29EI6) +X(P29EI7) +X(P29EI8) +X(P29EL) +X(P29EO) +X(P29ER) +X(P29RI) +X(P29RL) +X(P29RO1) +X(P29RO2) +X(P29RO3) +X(P29RO4) +X(P29RO5) +X(P29RO6) +X(P29RO7) +X(P29RO8) +X(P29RR) +X(P2CI1) +X(P2CL) +X(P2CO) +X(P2CR) +X(P2CTI) +X(P2CTO) +X(P2EI1) +X(P2EI2) +X(P2EI3) +X(P2EI4) +X(P2EI5) +X(P2EI6) +X(P2EI7) +X(P2EI8) +X(P2EL) +X(P2EO) +X(P2ER) +X(P2RI) +X(P2RL) +X(P2RO1) +X(P2RO2) +X(P2RO3) +X(P2RO4) +X(P2RO5) +X(P2RO6) +X(P2RO7) +X(P2RO8) +X(P2RR) +X(P30CI1) +X(P30CL) +X(P30CO) +X(P30CR) +X(P30CTI) +X(P30CTO) +X(P30EI1) +X(P30EI2) +X(P30EI3) +X(P30EI4) +X(P30EI5) +X(P30EI6) +X(P30EI7) +X(P30EI8) +X(P30EL) +X(P30EO) +X(P30ER) +X(P30RI) +X(P30RL) +X(P30RO1) +X(P30RO2) +X(P30RO3) +X(P30RO4) +X(P30RO5) +X(P30RO6) +X(P30RO7) +X(P30RO8) +X(P30RR) +X(P31CI1) +X(P31CL) +X(P31CO) +X(P31CR) +X(P31CTI) +X(P31CTO) +X(P31EI1) +X(P31EI2) +X(P31EI3) +X(P31EI4) +X(P31EI5) +X(P31EI6) +X(P31EI7) +X(P31EI8) +X(P31EL) +X(P31EO) +X(P31ER) +X(P31RI) +X(P31RL) +X(P31RO1) +X(P31RO2) +X(P31RO3) +X(P31RO4) +X(P31RO5) +X(P31RO6) +X(P31RO7) +X(P31RO8) +X(P31RR) +X(P32CI1) +X(P32CL) +X(P32CO) +X(P32CR) +X(P32CTI) +X(P32CTO) +X(P32EI1) +X(P32EI2) +X(P32EI3) +X(P32EI4) +X(P32EI5) +X(P32EI6) +X(P32EI7) +X(P32EI8) +X(P32EL) +X(P32EO) +X(P32ER) +X(P32RI) +X(P32RL) +X(P32RO1) +X(P32RO2) +X(P32RO3) +X(P32RO4) +X(P32RO5) +X(P32RO6) +X(P32RO7) +X(P32RO8) +X(P32RR) +X(P33CI1) +X(P33CL) +X(P33CO) +X(P33CR) +X(P33CTI) +X(P33CTO) +X(P33EI1) +X(P33EI2) +X(P33EI3) +X(P33EI4) +X(P33EI5) +X(P33EI6) +X(P33EI7) +X(P33EI8) +X(P33EL) +X(P33EO) +X(P33ER) +X(P33RI) +X(P33RL) +X(P33RO1) +X(P33RO2) +X(P33RO3) +X(P33RO4) +X(P33RO5) +X(P33RO6) +X(P33RO7) +X(P33RO8) +X(P33RR) +X(P34CI1) +X(P34CL) +X(P34CO) +X(P34CR) +X(P34CTI) +X(P34CTO) +X(P34EI1) +X(P34EI2) +X(P34EI3) +X(P34EI4) +X(P34EI5) +X(P34EI6) +X(P34EI7) +X(P34EI8) +X(P34EL) +X(P34EO) +X(P34ER) +X(P34RI) +X(P34RL) +X(P34RO1) +X(P34RO2) +X(P34RO3) +X(P34RO4) +X(P34RO5) +X(P34RO6) +X(P34RO7) +X(P34RO8) +X(P34RR) +X(P3CI1) +X(P3CL) +X(P3CO) +X(P3CR) +X(P3CTI) +X(P3CTO) +X(P3EI1) +X(P3EI2) +X(P3EI3) +X(P3EI4) +X(P3EI5) +X(P3EI6) +X(P3EI7) +X(P3EI8) +X(P3EL) +X(P3EO) +X(P3ER) +X(P3RI) +X(P3RL) +X(P3RO1) +X(P3RO2) +X(P3RO3) +X(P3RO4) +X(P3RO5) +X(P3RO6) +X(P3RO7) +X(P3RO8) +X(P3RR) +X(P4CI1) +X(P4CL) +X(P4CO) +X(P4CR) +X(P4CTI) +X(P4CTO) +X(P4EI1) +X(P4EI2) +X(P4EI3) +X(P4EI4) +X(P4EI5) +X(P4EI6) +X(P4EI7) +X(P4EI8) +X(P4EL) +X(P4EO) +X(P4ER) +X(P4RI) +X(P4RL) +X(P4RO1) +X(P4RO2) +X(P4RO3) +X(P4RO4) +X(P4RO5) +X(P4RO6) +X(P4RO7) +X(P4RO8) +X(P4RR) +X(P5CI1) +X(P5CL) +X(P5CO) +X(P5CR) +X(P5CTI) +X(P5CTO) +X(P5EI1) +X(P5EI2) +X(P5EI3) +X(P5EI4) +X(P5EI5) +X(P5EI6) +X(P5EI7) +X(P5EI8) +X(P5EL) +X(P5EO) +X(P5ER) +X(P5RI) +X(P5RL) +X(P5RO1) +X(P5RO2) +X(P5RO3) +X(P5RO4) +X(P5RO5) +X(P5RO6) +X(P5RO7) +X(P5RO8) +X(P5RR) +X(P6CI1) +X(P6CL) +X(P6CO) +X(P6CR) +X(P6CTI) +X(P6CTO) +X(P6EI1) +X(P6EI2) +X(P6EI3) +X(P6EI4) +X(P6EI5) +X(P6EI6) +X(P6EI7) +X(P6EI8) +X(P6EL) +X(P6EO) +X(P6ER) +X(P6RI) +X(P6RL) +X(P6RO1) +X(P6RO2) +X(P6RO3) +X(P6RO4) +X(P6RO5) +X(P6RO6) +X(P6RO7) +X(P6RO8) +X(P6RR) +X(P7CI1) +X(P7CL) +X(P7CO) +X(P7CR) +X(P7CTI) +X(P7CTO) +X(P7EI1) +X(P7EI2) +X(P7EI3) +X(P7EI4) +X(P7EI5) +X(P7EI6) +X(P7EI7) +X(P7EI8) +X(P7EL) +X(P7EO) +X(P7ER) +X(P7RI) +X(P7RL) +X(P7RO1) +X(P7RO2) +X(P7RO3) +X(P7RO4) +X(P7RO5) +X(P7RO6) +X(P7RO7) +X(P7RO8) +X(P7RR) +X(P8CI1) +X(P8CL) +X(P8CO) +X(P8CR) +X(P8CTI) +X(P8CTO) +X(P8EI1) +X(P8EI2) +X(P8EI3) +X(P8EI4) +X(P8EI5) +X(P8EI6) +X(P8EI7) +X(P8EI8) +X(P8EL) +X(P8EO) +X(P8ER) +X(P8RI) +X(P8RL) +X(P8RO1) +X(P8RO2) +X(P8RO3) +X(P8RO4) +X(P8RO5) +X(P8RO6) +X(P8RO7) +X(P8RO8) +X(P8RR) +X(P9CI1) +X(P9CL) +X(P9CO) +X(P9CR) +X(P9CTI) +X(P9CTO) +X(P9EI1) +X(P9EI2) +X(P9EI3) +X(P9EI4) +X(P9EI5) +X(P9EI6) +X(P9EI7) +X(P9EI8) +X(P9EL) +X(P9EO) +X(P9ER) +X(P9RI) +X(P9RL) +X(P9RO1) +X(P9RO2) +X(P9RO3) +X(P9RO4) +X(P9RO5) +X(P9RO6) +X(P9RO7) +X(P9RO8) +X(P9RR) +X(SE) +X(SWRX1CK) +X(SWRX2CK) + +// IOP +X(IOP) +//X(C) +//X(I) +//X(O) + +// IOTP +X(IOTP) +//X(C) +//X(I) +X(IO) +//X(O) +X(T) + +// IP +X(IP) +//X(C) +//X(O) + +// ITC +X(ITC) +//X(I) +//X(O) + +// ITP +X(ITP) +//X(C) +//X(IO) +//X(O) +//X(T) + +// ITS +X(ITS) +//X(I) +//X(O) + +// LUT +X(LUT) +X(D) +//X(I1) +//X(I2) +//X(I3) +//X(I4) +//X(J) +//X(K) +//X(O) +X(X) + +// MSM +X(MSM) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I19) +//X(I2) +//X(I20) +//X(I21) +//X(I22) +//X(I23) +//X(I24) +//X(I25) +//X(I26) +//X(I27) +//X(I28) +//X(I29) +//X(I3) +//X(I30) +//X(I31) +//X(I32) +//X(I33) +//X(I34) +//X(I35) +//X(I36) +//X(I37) +//X(I38) +//X(I39) +//X(I4) +//X(I40) +//X(I41) +//X(I42) +//X(I43) +//X(I44) +//X(I45) +//X(I46) +//X(I47) +//X(I48) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +X(O19) +//X(O2) +X(O20) +X(O21) +X(O22) +X(O23) +X(O24) +X(O25) +X(O26) +X(O27) +X(O28) +X(O29) +//X(O3) +X(O30) +X(O31) +X(O32) +X(O33) +X(O34) +X(O35) +X(O36) +X(O37) +X(O38) +X(O39) +//X(O4) +X(O40) +X(O41) +X(O42) +X(O43) +X(O44) +X(O45) +X(O46) +X(O47) +X(O48) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) + +// OP +X(OP) +//X(C) +//X(I) + +// OTC +X(OTC) +//X(I) +//X(O) + +// OTP +X(OTP) +//X(C) +//X(I) +//X(IO) +//X(T) + +// OTS +X(OTS) +//X(I) +//X(O) + +// PLL +X(PLL) +X(ARST_CAL) +X(CAL1) +X(CAL2) +X(CAL3) +X(CAL4) +X(CAL5) +X(CAL_LOCKED) +X(CLK_CAL) +X(CLK_CAL_DIV) +X(CLK_DIV1) +X(CLK_DIV2) +X(CLK_DIV3) +X(CLK_DIV4) +X(CLK_DIVD1) +X(CLK_DIVD2) +X(CLK_DIVD3) +X(CLK_DIVD4) +X(CLK_DIVD5) +X(EXT_CAL1) +X(EXT_CAL2) +X(EXT_CAL3) +X(EXT_CAL4) +X(EXT_CAL5) +X(EXT_CAL_LOCKED) +X(FBK) +X(LDFO) +X(OSC) +X(PLL_LOCKED) +X(PLL_LOCKEDA) +//X(R) +X(REF) +X(REFO) +X(VCO) +X(scan_en_i) +X(scan_in_i1) +X(scan_in_i10) +X(scan_in_i11) +X(scan_in_i12) +X(scan_in_i13) +X(scan_in_i14) +X(scan_in_i15) +X(scan_in_i16) +X(scan_in_i17) +X(scan_in_i18) +X(scan_in_i19) +X(scan_in_i2) +X(scan_in_i20) +X(scan_in_i21) +X(scan_in_i22) +X(scan_in_i23) +X(scan_in_i24) +X(scan_in_i25) +X(scan_in_i26) +X(scan_in_i27) +X(scan_in_i28) +X(scan_in_i29) +X(scan_in_i3) +X(scan_in_i30) +X(scan_in_i31) +X(scan_in_i32) +X(scan_in_i33) +X(scan_in_i34) +X(scan_in_i35) +X(scan_in_i36) +X(scan_in_i37) +X(scan_in_i4) +X(scan_in_i5) +X(scan_in_i6) +X(scan_in_i7) +X(scan_in_i8) +X(scan_in_i9) +X(scan_out_o1) +X(scan_out_o10) +X(scan_out_o11) +X(scan_out_o12) +X(scan_out_o13) +X(scan_out_o14) +X(scan_out_o15) +X(scan_out_o16) +X(scan_out_o17) +X(scan_out_o18) +X(scan_out_o19) +X(scan_out_o2) +X(scan_out_o20) +X(scan_out_o21) +X(scan_out_o22) +X(scan_out_o23) +X(scan_out_o24) +X(scan_out_o25) +X(scan_out_o26) +X(scan_out_o27) +X(scan_out_o28) +X(scan_out_o29) +X(scan_out_o3) +X(scan_out_o30) +X(scan_out_o31) +X(scan_out_o32) +X(scan_out_o33) +X(scan_out_o34) +X(scan_out_o35) +X(scan_out_o36) +X(scan_out_o37) +X(scan_out_o4) +X(scan_out_o5) +X(scan_out_o6) +X(scan_out_o7) +X(scan_out_o8) +X(scan_out_o9) + +// PMA +X(PMA) +X(CAL_OUT_O) +//X(CLK_O) +X(CLK_RX_O) +X(DBG_A_I) +X(DBG_R_O) +//X(DBG_S_I1) +//X(DBG_S_I2) +X(DC_ADD_I1) +X(DC_ADD_I2) +X(DC_ADD_I3) +X(DC_ADD_I4) +X(DC_CCSN_I) +X(DC_E_I) +X(DC_LCSN_I1) +X(DC_LCSN_I2) +X(DC_LCSN_I3) +X(DC_LCSN_I4) +X(DC_WDATAS_I) +X(DC_WDATA_I1) +X(DC_WDATA_I10) +X(DC_WDATA_I11) +X(DC_WDATA_I12) +X(DC_WDATA_I2) +X(DC_WDATA_I3) +X(DC_WDATA_I4) +X(DC_WDATA_I5) +X(DC_WDATA_I6) +X(DC_WDATA_I7) +X(DC_WDATA_I8) +X(DC_WDATA_I9) +X(DC_WE_N_I) +X(FB_LOCK_O) +X(LINK_RX0) +X(LINK_RX1) +X(LINK_RX2) +X(LINK_RX3) +X(LINK_TX0) +X(LINK_TX1) +X(LINK_TX2) +X(LINK_TX3) +X(LL_O1) +X(LL_O10) +X(LL_O11) +X(LL_O12) +X(LL_O13) +X(LL_O14) +X(LL_O15) +X(LL_O16) +X(LL_O17) +X(LL_O18) +X(LL_O19) +X(LL_O2) +X(LL_O20) +X(LL_O3) +X(LL_O4) +X(LL_O5) +X(LL_O6) +X(LL_O7) +X(LL_O8) +X(LL_O9) +X(LOCKA_O) +X(LOCK_O) +X(PLL_RN_I) +//X(PWDN_N_I) +//X(RST_N_I) +X(SCAN_I1) +X(SCAN_I2) +X(SCAN_I3) +X(SCAN_I4) +X(SCAN_I5) +X(SCAN_I6) +X(SCAN_I7) +X(SCAN_I8) +X(SCAN_O1) +X(SCAN_O2) +X(SCAN_O3) +X(SCAN_O4) +X(SCAN_O5) +X(SCAN_O6) +X(SCAN_O7) +X(SCAN_O8) +X(SE_I) +X(calibrate) +X(ckrefn) +X(ckrefp) +X(hssl_clock_i1) +X(hssl_clock_i2) +X(hssl_clock_i3) +X(hssl_clock_i4) +X(pll_ckref_in) +X(pll_ckref_out) +X(pma_pll_fbr_clk_ref_o) +X(rx0n) +X(rx0p) +X(rx1n) +X(rx1p) +X(rx2n) +X(rx2p) +X(rx3n) +X(rx3p) +X(tx0n) +X(tx0p) +X(tx1n) +X(tx1p) +X(tx2n) +X(tx2p) +X(tx3n) +X(tx3p) + +// RAM +X(RAM) +X(AA1) +X(AA10) +X(AA11) +X(AA12) +X(AA13) +X(AA14) +X(AA15) +X(AA16) +X(AA2) +X(AA3) +X(AA4) +X(AA5) +X(AA6) +X(AA7) +X(AA8) +X(AA9) +X(ACK) +X(ACKC) +X(ACKD) +X(ACKR) +X(ACOR) +X(ACS) +X(AERR) +//X(AI1) +X(AI10) +X(AI11) +X(AI12) +X(AI13) +X(AI14) +X(AI15) +X(AI16) +X(AI17) +X(AI18) +X(AI19) +//X(AI2) +X(AI20) +X(AI21) +X(AI22) +X(AI23) +X(AI24) +//X(AI3) +//X(AI4) +//X(AI5) +//X(AI6) +X(AI7) +X(AI8) +X(AI9) +//X(AO1) +X(AO10) +X(AO11) +X(AO12) +X(AO13) +X(AO14) +X(AO15) +X(AO16) +X(AO17) +X(AO18) +X(AO19) +//X(AO2) +X(AO20) +X(AO21) +X(AO22) +X(AO23) +X(AO24) +//X(AO3) +//X(AO4) +//X(AO5) +//X(AO6) +X(AO7) +X(AO8) +X(AO9) +X(AR) +X(AWE) +X(BA1) +X(BA10) +X(BA11) +X(BA12) +X(BA13) +X(BA14) +X(BA15) +X(BA16) +X(BA2) +X(BA3) +X(BA4) +X(BA5) +X(BA6) +X(BA7) +X(BA8) +X(BA9) +X(BCK) +X(BCKC) +X(BCKD) +X(BCKR) +X(BCOR) +X(BCS) +X(BERR) +//X(BI1) +X(BI10) +X(BI11) +X(BI12) +X(BI13) +X(BI14) +X(BI15) +X(BI16) +X(BI17) +X(BI18) +X(BI19) +//X(BI2) +X(BI20) +X(BI21) +X(BI22) +X(BI23) +X(BI24) +//X(BI3) +//X(BI4) +//X(BI5) +//X(BI6) +X(BI7) +X(BI8) +X(BI9) +//X(BO1) +X(BO10) +X(BO11) +X(BO12) +X(BO13) +X(BO14) +X(BO15) +X(BO16) +X(BO17) +X(BO18) +X(BO19) +//X(BO2) +X(BO20) +X(BO21) +X(BO22) +X(BO23) +X(BO24) +//X(BO3) +//X(BO4) +//X(BO5) +//X(BO6) +X(BO7) +X(BO8) +X(BO9) +X(BR) +X(BWE) + +// RF +X(RF) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) +X(RA1) +X(RA2) +X(RA3) +X(RA4) +X(RA5) +X(WA1) +X(WA2) +X(WA3) +X(WA4) +X(WA5) +//X(WCK) +//X(WE) +//X(WEA) + +// RFSP +X(RFSP) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) +//X(RA1) +//X(RA2) +//X(RA3) +//X(RA4) +//X(RA5) +//X(WA1) +//X(WA2) +//X(WA3) +//X(WA4) +//X(WA5) +//X(WCK) +//X(WE) +//X(WEA) + +// SCC +X(SCC) +//X(C) +//X(I) +//X(J) +//X(K) +//X(O) +//X(P) +//X(S) + +// SCI +X(SCI) +//X(I) +//X(O) + +// SCO +X(SCO) +//X(I) +//X(O) + +// SEA +X(SEA) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) + +// SEB +X(SEB) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) + +// SIC +X(SIC) +//X(I1) +//X(I2) +//X(I3) +//X(I4) +//X(O1) +//X(O2) +//X(O3) +//X(O4) + +// SLT +X(SLT) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I19) +//X(I2) +//X(I20) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) + +// SSA +X(SSA) +//X(I1) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(O1) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) + +// SSB +X(SSB) +//X(I1) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(O1) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) + +// SSI +X(SSI) +//X(I) +//X(O) + +// TCI +X(TCI) +//X(I) +//X(O) + +// TCO +X(TCO) +//X(I) +//X(O) + +// TDE +X(TDE) +//X(ADRSTI) +//X(ADRSTO) +//X(AI1) +//X(AI2) +//X(AI3) +//X(AI4) +//X(AI5) +//X(AI6) +//X(AO1) +//X(AO2) +//X(AO3) +//X(AO4) +//X(AO5) +//X(AO6) +//X(ASRSTI) +//X(ASRSTO) +//X(BDRSTI) +//X(BDRSTO) +//X(BI1) +//X(BI2) +//X(BI3) +//X(BI4) +//X(BI5) +//X(BI6) +//X(BO1) +//X(BO2) +//X(BO3) +//X(BO4) +//X(BO5) +//X(BO6) +//X(BSRSTI) +//X(BSRSTO) +//X(CK1) +//X(CK2) + +// TEA +X(TEA) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) + +// TEB +X(TEB) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) + +// TIA +X(TIA) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I19) +//X(I2) +//X(I20) +//X(I21) +//X(I22) +//X(I23) +//X(I24) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O19) +//X(O2) +//X(O20) +//X(O21) +//X(O22) +//X(O23) +//X(O24) +//X(O25) +//X(O26) +//X(O27) +//X(O28) +//X(O29) +//X(O3) +//X(O30) +//X(O31) +//X(O32) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) + +// TIB +X(TIB) +//X(I1) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) + +// TLT +X(TLT) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I19) +//X(I2) +//X(I20) +//X(I21) +//X(I22) +//X(I23) +//X(I24) +//X(I25) +//X(I26) +//X(I27) +//X(I28) +//X(I29) +//X(I3) +//X(I30) +//X(I31) +//X(I32) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O19) +//X(O2) +//X(O20) +//X(O21) +//X(O22) +//X(O23) +//X(O24) +//X(O25) +//X(O26) +//X(O27) +//X(O28) +//X(O29) +//X(O3) +//X(O30) +//X(O31) +//X(O32) +//X(O33) +//X(O34) +//X(O35) +//X(O36) +//X(O37) +//X(O38) +//X(O39) +//X(O4) +//X(O40) +//X(O41) +//X(O42) +//X(O43) +//X(O44) +//X(O45) +//X(O46) +//X(O47) +//X(O48) +X(O49) +//X(O5) +X(O50) +X(O51) +X(O52) +X(O53) +X(O54) +X(O55) +X(O56) +X(O57) +X(O58) +X(O59) +//X(O6) +X(O60) +X(O61) +X(O62) +X(O63) +X(O64) +X(O65) +X(O66) +X(O67) +X(O68) +X(O69) +//X(O7) +X(O70) +X(O71) +X(O72) +X(O73) +X(O74) +X(O75) +X(O76) +X(O77) +X(O78) +X(O79) +//X(O8) +X(O80) +X(O81) +X(O82) +X(O83) +X(O84) +X(O85) +X(O86) +//X(O9) + +// TSA +X(TSA) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) + +// TSB +X(TSB) +//X(I1) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(O1) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) + +// TSI +X(TSI) +//X(I) +//X(O) + +// TSO +X(TSO) +//X(I) +//X(O) + +// TSS +X(TSS) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I19) +//X(I2) +//X(I20) +//X(I21) +//X(I22) +//X(I23) +//X(I24) +//X(I25) +//X(I26) +//X(I27) +//X(I28) +//X(I29) +//X(I3) +//X(I30) +//X(I31) +//X(I32) +//X(I33) +//X(I34) +//X(I35) +//X(I36) +//X(I37) +//X(I38) +//X(I39) +//X(I4) +//X(I40) +//X(I41) +//X(I42) +//X(I43) +//X(I44) +//X(I45) +//X(I46) +//X(I47) +//X(I48) +X(I49) +//X(I5) +X(I50) +X(I51) +X(I52) +X(I53) +X(I54) +X(I55) +X(I56) +X(I57) +X(I58) +X(I59) +//X(I6) +X(I60) +X(I61) +X(I62) +X(I63) +X(I64) +X(I65) +X(I66) +X(I67) +X(I68) +X(I69) +//X(I7) +X(I70) +X(I71) +X(I72) +X(I73) +X(I74) +X(I75) +X(I76) +//X(I8) +//X(I9) +//X(O1) +//X(O2) +//X(O3) +//X(O4) + +// WFB +X(WFB) +X(ZI) +X(ZO) + +// WFG +X(WFG) +//X(R) +X(SI) +//X(SO) +//X(ZI) +//X(ZO) + +// XCDC +X(XCDC) +//X(ADRSTI) +//X(ADRSTO) +//X(AI1) +//X(AI2) +//X(AI3) +//X(AI4) +//X(AI5) +//X(AI6) +//X(AO1) +//X(AO2) +//X(AO3) +//X(AO4) +//X(AO5) +//X(AO6) +//X(ASRSTI) +//X(ASRSTO) +//X(BDRSTI) +//X(BDRSTO) +//X(BI1) +//X(BI2) +//X(BI3) +//X(BI4) +//X(BI5) +//X(BI6) +//X(BO1) +//X(BO2) +//X(BO3) +//X(BO4) +//X(BO5) +//X(BO6) +//X(BSRSTI) +//X(BSRSTO) +X(CDRSTI) +X(CDRSTO) +X(CI1) +X(CI2) +X(CI3) +X(CI4) +X(CI5) +X(CI6) +//X(CK1) +//X(CK2) +X(CO1) +X(CO2) +X(CO3) +X(CO4) +X(CO5) +X(CO6) +X(CSRSTI) +X(CSRSTO) +X(DDRSTI) +X(DDRSTO) +X(DI1) +X(DI2) +X(DI3) +X(DI4) +X(DI5) +X(DI6) +X(DO1) +X(DO2) +X(DO3) +X(DO4) +X(DO5) +X(DO6) +X(DSRSTI) +X(DSRSTO) + +// XFIFO +X(XFIFO) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I19) +//X(I2) +//X(I20) +//X(I21) +//X(I22) +//X(I23) +//X(I24) +//X(I25) +//X(I26) +//X(I27) +//X(I28) +//X(I29) +//X(I3) +//X(I30) +//X(I31) +//X(I32) +//X(I33) +//X(I34) +//X(I35) +//X(I36) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O19) +//X(O2) +//X(O20) +//X(O21) +//X(O22) +//X(O23) +//X(O24) +//X(O25) +//X(O26) +//X(O27) +//X(O28) +//X(O29) +//X(O3) +//X(O30) +//X(O31) +//X(O32) +//X(O33) +//X(O34) +//X(O35) +//X(O36) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) +//X(RAI1) +//X(RAI2) +//X(RAI3) +//X(RAI4) +//X(RAI5) +//X(RAI6) +X(RAI7) +//X(RAO1) +//X(RAO2) +//X(RAO3) +//X(RAO4) +//X(RAO5) +//X(RAO6) +X(RAO7) +X(RCK1) +X(RCK2) +X(REQ1) +X(REQ2) +//X(RRSTI1) +//X(RRSTI2) +X(RRSTI3) +X(RRSTI4) +//X(RRSTO) +//X(WAI1) +//X(WAI2) +//X(WAI3) +//X(WAI4) +//X(WAI5) +//X(WAI6) +X(WAI7) +//X(WAO1) +//X(WAO2) +//X(WAO3) +//X(WAO4) +//X(WAO5) +//X(WAO6) +X(WAO7) +X(WCK1) +X(WCK2) +//X(WE) +//X(WEA) +X(WEQ1) +X(WEQ2) +//X(WRSTI1) +//X(WRSTI2) +X(WRSTI3) +X(WRSTI4) +//X(WRSTO) + +// XHFIFO +X(XHFIFO) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) +//X(RAI1) +//X(RAI2) +//X(RAI3) +//X(RAI4) +//X(RAI5) +//X(RAI6) +//X(RAI7) +//X(RAO1) +//X(RAO2) +//X(RAO3) +//X(RAO4) +//X(RAO5) +//X(RAO6) +//X(RAO7) +//X(RCK1) +//X(RCK2) +//X(REQ1) +//X(REQ2) +//X(RRSTI1) +//X(RRSTI2) +//X(RRSTI3) +//X(RRSTI4) +//X(WAI1) +//X(WAI2) +//X(WAI3) +//X(WAI4) +//X(WAI5) +//X(WAI6) +//X(WAI7) +//X(WAO1) +//X(WAO2) +//X(WAO3) +//X(WAO4) +//X(WAO5) +//X(WAO6) +//X(WAO7) +//X(WCK1) +//X(WCK2) +//X(WE) +//X(WEA) +//X(WEQ1) +//X(WEQ2) +//X(WRSTI1) +//X(WRSTI2) +//X(WRSTI3) +//X(WRSTI4) + +// XHRF +X(XHRF) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O2) +//X(O3) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) +//X(RA1) +//X(RA2) +//X(RA3) +//X(RA4) +//X(RA5) +X(RA6) +//X(WA1) +//X(WA2) +//X(WA3) +//X(WA4) +//X(WA5) +X(WA6) +//X(WCK1) +//X(WCK2) +//X(WE) +//X(WEA) + +// XLUT +X(XLUT) +X(G1) +X(G2) +X(G3) +X(G4) +//X(I1) +//X(I2) +//X(I3) +//X(I4) +//X(J) +//X(O) + +// XPRF +X(XPRF) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I2) +//X(I3) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O19) +//X(O2) +//X(O20) +//X(O21) +//X(O22) +//X(O23) +//X(O24) +//X(O25) +//X(O26) +//X(O27) +//X(O28) +//X(O29) +//X(O3) +//X(O30) +//X(O31) +//X(O32) +//X(O33) +//X(O34) +//X(O35) +//X(O36) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) +//X(RA1) +X(RA10) +//X(RA2) +//X(RA3) +//X(RA4) +//X(RA5) +//X(RA6) +X(RA7) +X(RA8) +X(RA9) +//X(WA1) +//X(WA2) +//X(WA3) +//X(WA4) +//X(WA5) +//X(WCK1) +//X(WCK2) +//X(WE) +//X(WEA) + +// XRF +X(XRF) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I19) +//X(I2) +//X(I20) +//X(I21) +//X(I22) +//X(I23) +//X(I24) +//X(I25) +//X(I26) +//X(I27) +//X(I28) +//X(I29) +//X(I3) +//X(I30) +//X(I31) +//X(I32) +//X(I33) +//X(I34) +//X(I35) +//X(I36) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O19) +//X(O2) +//X(O20) +//X(O21) +//X(O22) +//X(O23) +//X(O24) +//X(O25) +//X(O26) +//X(O27) +//X(O28) +//X(O29) +//X(O3) +//X(O30) +//X(O31) +//X(O32) +//X(O33) +//X(O34) +//X(O35) +//X(O36) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) +//X(RA1) +//X(RA10) +//X(RA2) +//X(RA3) +//X(RA4) +//X(RA5) +//X(RA6) +//X(RA7) +//X(RA8) +//X(RA9) +//X(WA1) +//X(WA2) +//X(WA3) +//X(WA4) +//X(WA5) +//X(WA6) +//X(WCK1) +//X(WCK2) +//X(WE) +//X(WEA) + +// XWFIFO +X(XWFIFO) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I19) +//X(I2) +//X(I20) +//X(I21) +//X(I22) +//X(I23) +//X(I24) +//X(I25) +//X(I26) +//X(I27) +//X(I28) +//X(I29) +//X(I3) +//X(I30) +//X(I31) +//X(I32) +//X(I33) +//X(I34) +//X(I35) +//X(I36) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O19) +//X(O2) +//X(O20) +//X(O21) +//X(O22) +//X(O23) +//X(O24) +//X(O25) +//X(O26) +//X(O27) +//X(O28) +//X(O29) +//X(O3) +//X(O30) +//X(O31) +//X(O32) +//X(O33) +//X(O34) +//X(O35) +//X(O36) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) +//X(RAI1) +//X(RAI2) +//X(RAI3) +//X(RAI4) +//X(RAI5) +//X(RAI6) +//X(RAO1) +//X(RAO2) +//X(RAO3) +//X(RAO4) +//X(RAO5) +//X(RAO6) +//X(RCK1) +//X(RCK2) +//X(REQ) +//X(RRSTI1) +//X(RRSTI2) +//X(WAI1) +//X(WAI2) +//X(WAI3) +//X(WAI4) +//X(WAI5) +//X(WAI6) +//X(WAO1) +//X(WAO2) +//X(WAO3) +//X(WAO4) +//X(WAO5) +//X(WAO6) +//X(WCK1) +//X(WCK2) +//X(WE) +//X(WEA) +//X(WEQ) +//X(WRSTI1) +//X(WRSTI2) + +// XWRF +X(XWRF) +//X(I1) +//X(I10) +//X(I11) +//X(I12) +//X(I13) +//X(I14) +//X(I15) +//X(I16) +//X(I17) +//X(I18) +//X(I19) +//X(I2) +//X(I20) +//X(I21) +//X(I22) +//X(I23) +//X(I24) +//X(I25) +//X(I26) +//X(I27) +//X(I28) +//X(I29) +//X(I3) +//X(I30) +//X(I31) +//X(I32) +//X(I33) +//X(I34) +//X(I35) +//X(I36) +//X(I4) +//X(I5) +//X(I6) +//X(I7) +//X(I8) +//X(I9) +//X(O1) +//X(O10) +//X(O11) +//X(O12) +//X(O13) +//X(O14) +//X(O15) +//X(O16) +//X(O17) +//X(O18) +//X(O19) +//X(O2) +//X(O20) +//X(O21) +//X(O22) +//X(O23) +//X(O24) +//X(O25) +//X(O26) +//X(O27) +//X(O28) +//X(O29) +//X(O3) +//X(O30) +//X(O31) +//X(O32) +//X(O33) +//X(O34) +//X(O35) +//X(O36) +//X(O4) +//X(O5) +//X(O6) +//X(O7) +//X(O8) +//X(O9) +//X(RA1) +//X(RA2) +//X(RA3) +//X(RA4) +//X(RA5) +//X(WA1) +//X(WA2) +//X(WA3) +//X(WA4) +//X(WA5) +//X(WCK1) +//X(WCK2) +//X(WE) +//X(WEA) + +// end of autogenerated items + +// start of generated parameter list +// NX_BD +X(NX_BD) +X(mode) + +// NX_BFF +X(NX_BFF) + +// NX_BFR +X(NX_BFR) +X(data_inv) +X(iobname) +X(location) +//X(mode) +X(path) +X(ring) + +// NX_CDC_L +X(NX_CDC_L) +X(ack_sel) +X(bck_sel) +X(cck_sel) +X(ck0_edge) +X(ck1_edge) +X(dck_sel) +X(gt0_bypass_reg1) +X(gt0_bypass_reg2) +X(gt1_bypass_reg1) +X(gt1_bypass_reg2) +X(link_BA) +X(link_CB) +X(link_DC) +//X(mode) +X(use_adest_arst) +X(use_asrc_arst) +X(use_bdest_arst) +X(use_bsrc_arst) +X(use_cdest_arst) +X(use_csrc_arst) +X(use_ddest_arst) +X(use_dsrc_arst) + +// NX_CDC_L_2DFF +X(NX_CDC_L_2DFF) +//X(ack_sel) +//X(bck_sel) +//X(ck0_edge) +//X(ck1_edge) +//X(gt0_bypass_reg1) +//X(gt0_bypass_reg2) +//X(gt1_bypass_reg1) +//X(gt1_bypass_reg2) +//X(use_adest_arst) +//X(use_bdest_arst) + +// NX_CDC_L_3DFF +X(NX_CDC_L_3DFF) +//X(ack_sel) +//X(bck_sel) +//X(ck0_edge) +//X(ck1_edge) +//X(gt0_bypass_reg1) +//X(gt0_bypass_reg2) +//X(gt1_bypass_reg1) +//X(gt1_bypass_reg2) +//X(use_adest_arst) +//X(use_asrc_arst) +//X(use_bdest_arst) +//X(use_bsrc_arst) + +// NX_CDC_L_BIN2GRAY +X(NX_CDC_L_BIN2GRAY) + +// NX_CDC_L_FULL +X(NX_CDC_L_FULL) +//X(ack_sel) +//X(bck_sel) +//X(ck0_edge) +//X(ck1_edge) +//X(gt0_bypass_reg1) +//X(gt0_bypass_reg2) +//X(gt1_bypass_reg1) +//X(gt1_bypass_reg2) +//X(use_adest_arst) +//X(use_asrc_arst) +//X(use_bdest_arst) +//X(use_bsrc_arst) + +// NX_CDC_U +X(NX_CDC_U) +//X(ack_sel) +//X(bck_sel) +//X(cck_sel) +//X(ck0_edge) +//X(ck1_edge) +//X(dck_sel) +//X(link_BA) +//X(link_CB) +//X(link_DC) +//X(mode) +//X(use_adest_arst) +//X(use_asrc_arst) +//X(use_bdest_arst) +//X(use_bsrc_arst) +//X(use_cdest_arst) +//X(use_csrc_arst) +//X(use_ddest_arst) +//X(use_dsrc_arst) + +// NX_CDC_U_2DFF +X(NX_CDC_U_2DFF) +//X(ack_sel) +//X(bck_sel) +//X(ck0_edge) +//X(ck1_edge) +//X(use_adest_arst) +//X(use_bdest_arst) + +// NX_CDC_U_3DFF +X(NX_CDC_U_3DFF) +//X(ack_sel) +//X(bck_sel) +//X(ck0_edge) +//X(ck1_edge) +//X(use_adest_arst) +//X(use_asrc_arst) +//X(use_bdest_arst) +//X(use_bsrc_arst) + +// NX_CDC_U_BIN2GRAY +X(NX_CDC_U_BIN2GRAY) + +// NX_CDC_U_FULL +X(NX_CDC_U_FULL) +//X(ack_sel) +//X(bck_sel) +//X(ck0_edge) +//X(ck1_edge) +//X(use_adest_arst) +//X(use_asrc_arst) +//X(use_bdest_arst) +//X(use_bsrc_arst) + +// NX_CDC_U_GRAY2BIN +X(NX_CDC_U_GRAY2BIN) + +// NX_CKS +X(NX_CKS) +X(ck_edge) + +// NX_CKS_U +X(NX_CKS_U) + +// NX_CMUX_U +X(NX_CMUX_U) + +// NX_CRX_L +X(NX_CRX_L) +//X(location) +X(pcs_8b_dscr_sel) +X(pcs_align_bypass) +X(pcs_buffers_bypass) +X(pcs_buffers_use_cdc) +X(pcs_bypass_pma_cdc) +X(pcs_bypass_usr_cdc) +X(pcs_comma_mask) +X(pcs_debug_en) +X(pcs_dec_bypass) +X(pcs_dscr_bypass) +X(pcs_el_buff_diff_bef_comp) +X(pcs_el_buff_max_comp) +X(pcs_el_buff_only_one_skp) +X(pcs_el_buff_skp_char_0) +X(pcs_el_buff_skp_char_1) +X(pcs_el_buff_skp_char_2) +X(pcs_el_buff_skp_char_3) +X(pcs_el_buff_skp_header_0) +X(pcs_el_buff_skp_header_1) +X(pcs_el_buff_skp_header_2) +X(pcs_el_buff_skp_header_3) +X(pcs_el_buff_skp_header_size) +X(pcs_el_buff_skp_seq_size) +X(pcs_el_buff_underflow_handle) +X(pcs_fsm_sel) +X(pcs_fsm_watchdog_en) +X(pcs_loopback) +X(pcs_m_comma_en) +X(pcs_m_comma_val) +X(pcs_nb_comma_bef_realign) +X(pcs_p_comma_en) +X(pcs_p_comma_val) +X(pcs_polarity) +X(pcs_protocol_size) +X(pcs_replace_bypass) +X(pcs_sync_supported) +X(pma_cdr_cp) +X(pma_clk_pos) +X(pma_ctrl_term) +X(pma_loopback) +X(pma_pll_cpump_n) +X(pma_pll_divf) +X(pma_pll_divf_en_n) +X(pma_pll_divm) +X(pma_pll_divm_en_n) +X(pma_pll_divn) +X(pma_pll_divn_en_n) +X(test) + +// NX_CRX_U +X(NX_CRX_U) +X(gearbox_en) +X(gearbox_mode) +//X(location) +//X(pcs_8b_dscr_sel) +//X(pcs_align_bypass) +//X(pcs_buffers_bypass) +//X(pcs_buffers_use_cdc) +//X(pcs_bypass_pma_cdc) +//X(pcs_bypass_usr_cdc) +//X(pcs_comma_mask) +//X(pcs_debug_en) +//X(pcs_dec_bypass) +//X(pcs_dscr_bypass) +//X(pcs_el_buff_diff_bef_comp) +//X(pcs_el_buff_max_comp) +//X(pcs_el_buff_only_one_skp) +//X(pcs_el_buff_skp_char_0) +//X(pcs_el_buff_skp_char_1) +//X(pcs_el_buff_skp_char_2) +//X(pcs_el_buff_skp_char_3) +//X(pcs_el_buff_skp_header_0) +//X(pcs_el_buff_skp_header_1) +//X(pcs_el_buff_skp_header_2) +//X(pcs_el_buff_skp_header_3) +//X(pcs_el_buff_skp_header_size) +//X(pcs_el_buff_skp_seq_size) +//X(pcs_fsm_sel) +//X(pcs_fsm_watchdog_en) +//X(pcs_loopback) +//X(pcs_m_comma_en) +//X(pcs_m_comma_val) +//X(pcs_nb_comma_bef_realign) +//X(pcs_p_comma_en) +//X(pcs_p_comma_val) +//X(pcs_polarity) +//X(pcs_protocol_size) +//X(pcs_replace_bypass) +//X(pcs_sync_supported) +//X(pma_cdr_cp) +//X(pma_clk_pos) +X(pma_coarse_ppm) +//X(pma_ctrl_term) +X(pma_dco_divl) +X(pma_dco_divm) +X(pma_dco_divn) +X(pma_dco_reg_res) +X(pma_dco_vref_sel) +X(pma_fine_ppm) +//X(pma_loopback) +X(pma_m_eye_ppm) +X(pma_peak_detect_cmd) +X(pma_peak_detect_on) +//X(pma_pll_cpump_n) +//X(pma_pll_divf) +//X(pma_pll_divf_en_n) +//X(pma_pll_divm) +//X(pma_pll_divm_en_n) +//X(pma_pll_divn) +//X(pma_pll_divn_en_n) + +// NX_CTX_L +X(NX_CTX_L) +//X(location) +X(pcs_8b_scr_sel) +//X(pcs_bypass_pma_cdc) +//X(pcs_bypass_usr_cdc) +X(pcs_enc_bypass) +X(pcs_esistream_fsm_en) +//X(pcs_loopback) +//X(pcs_polarity) +//X(pcs_protocol_size) +//X(pcs_replace_bypass) +X(pcs_scr_bypass) +X(pcs_scr_init) +//X(pcs_sync_supported) +//X(pma_clk_pos) +//X(pma_loopback) +//X(test) + +// NX_CTX_U +X(NX_CTX_U) +//X(gearbox_en) +//X(gearbox_mode) +//X(location) +//X(pcs_8b_scr_sel) +//X(pcs_bypass_pma_cdc) +//X(pcs_bypass_usr_cdc) +//X(pcs_enc_bypass) +//X(pcs_esistream_fsm_en) +//X(pcs_loopback) +//X(pcs_polarity) +//X(pcs_protocol_size) +//X(pcs_replace_bypass) +//X(pcs_scr_bypass) +//X(pcs_scr_init) +//X(pcs_sync_supported) +//X(pma_clk_pos) +//X(pma_loopback) + +// NX_CY +X(NX_CY) +X(add_carry) +X(shifter) + +// NX_DES +X(NX_DES) +X(data_size) +X(differential) +X(dpath_dynamic) +X(drive) +X(inputDelayLine) +X(inputSignalSlope) +//X(location) +X(locked) +X(standard) +X(termination) +X(terminationReference) +X(turbo) +X(weakTermination) + +// NX_DFF +X(NX_DFF) +X(dff_ctxt) +X(dff_edge) +X(dff_init) +X(dff_load) +X(dff_sync) +X(dff_type) + +// NX_DFR +X(NX_DFR) +//X(data_inv) +//X(dff_edge) +//X(dff_init) +//X(dff_load) +//X(dff_sync) +//X(dff_type) +//X(iobname) +//X(location) +//X(mode) +//X(path) +//X(ring) + +// NX_DDFR_U +X(NX_DDFR_U) + +// NX_DSP +X(NX_DSP) +X(raw_config0) +X(raw_config1) +X(raw_config2) +X(raw_config3) +X(std_mode) + +// NX_DSP_L +X(NX_DSP_L) +//X(raw_config0) +//X(raw_config1) +//X(raw_config2) +//X(raw_config3) +//X(std_mode) + +// NX_DSP_L_SPLIT +X(NX_DSP_L_SPLIT) +X(ALU_DYNAMIC_OP) +X(ALU_MUX) +X(ALU_OP) +X(CO_SEL) +X(ENABLE_PR_ALU_RST) +X(ENABLE_PR_A_RST) +X(ENABLE_PR_B_RST) +X(ENABLE_PR_CI_RST) +X(ENABLE_PR_CO_RST) +X(ENABLE_PR_C_RST) +X(ENABLE_PR_D_RST) +X(ENABLE_PR_MULT_RST) +X(ENABLE_PR_OV_RST) +X(ENABLE_PR_P_RST) +X(ENABLE_PR_X_RST) +X(ENABLE_PR_Y_RST) +X(ENABLE_PR_Z_RST) +X(ENABLE_SATURATION) +X(MUX_A) +X(MUX_B) +X(MUX_CI) +X(MUX_P) +X(MUX_X) +X(MUX_Y) +X(MUX_Z) +X(PRE_ADDER_OP) +X(PR_ALU_MUX) +X(PR_A_CASCADE_MUX) +X(PR_A_MUX) +X(PR_B_CASCADE_MUX) +X(PR_B_MUX) +X(PR_CI_MUX) +X(PR_CO_MUX) +X(PR_C_MUX) +X(PR_D_MUX) +X(PR_MULT_MUX) +X(PR_OV_MUX) +X(PR_P_MUX) +X(PR_X_MUX) +X(PR_Y_MUX) +X(PR_Z_MUX) +X(SATURATION_RANK) +X(SIGNED_MODE) +X(Z_FEEDBACK_SHL12) + +// NX_DSP_L_WRAP +X(NX_DSP_L_WRAP) +//X(raw_config0) +//X(raw_config1) +//X(raw_config2) +//X(raw_config3) +//X(std_mode) + +// NX_DSP_SPLIT +X(NX_DSP_SPLIT) +//X(ALU_DYNAMIC_OP) +//X(ALU_MUX) +//X(ALU_OP) +//X(CO_SEL) +//X(ENABLE_PR_ALU_RST) +//X(ENABLE_PR_A_RST) +//X(ENABLE_PR_B_RST) +//X(ENABLE_PR_CI_RST) +//X(ENABLE_PR_CO_RST) +//X(ENABLE_PR_C_RST) +//X(ENABLE_PR_D_RST) +//X(ENABLE_PR_MULT_RST) +//X(ENABLE_PR_OV_RST) +//X(ENABLE_PR_P_RST) +//X(ENABLE_PR_X_RST) +//X(ENABLE_PR_Y_RST) +//X(ENABLE_PR_Z_RST) +//X(ENABLE_SATURATION) +//X(MUX_A) +//X(MUX_B) +//X(MUX_CI) +//X(MUX_P) +//X(MUX_X) +//X(MUX_Y) +//X(MUX_Z) +//X(PRE_ADDER_OP) +//X(PR_ALU_MUX) +//X(PR_A_CASCADE_MUX) +//X(PR_A_MUX) +//X(PR_B_CASCADE_MUX) +//X(PR_B_MUX) +//X(PR_CI_MUX) +//X(PR_CO_MUX) +//X(PR_C_MUX) +//X(PR_D_MUX) +//X(PR_MULT_MUX) +//X(PR_OV_MUX) +//X(PR_P_MUX) +//X(PR_X_MUX) +//X(PR_Y_MUX) +//X(PR_Z_MUX) +//X(SATURATION_RANK) +//X(SIGNED_MODE) +//X(Z_FEEDBACK_SHL12) + +// NX_DSP_U +X(NX_DSP_U) +//X(raw_config0) +//X(raw_config1) +//X(raw_config2) +//X(raw_config3) +//X(std_mode) + +// NX_DSP_U_SPLIT +X(NX_DSP_U_SPLIT) +//X(ALU_DYNAMIC_OP) +//X(ALU_OP) +//X(ENABLE_PR_A_RST) +//X(ENABLE_PR_B_RST) +X(ENABLE_PR_CCO_RST) +//X(ENABLE_PR_CI_RST) +//X(ENABLE_PR_CO_RST) +X(ENABLE_PR_CZ_RST) +//X(ENABLE_PR_C_RST) +//X(ENABLE_PR_D_RST) +//X(ENABLE_PR_MULT_RST) +//X(ENABLE_PR_OV_RST) +//X(ENABLE_PR_P_RST) +//X(ENABLE_PR_X_RST) +//X(ENABLE_PR_Y_RST) +//X(ENABLE_PR_Z_RST) +//X(ENABLE_SATURATION) +X(INV_RST) +X(INV_RSTZ) +X(INV_WE) +X(INV_WEZ) +//X(MUX_A) +//X(MUX_B) +X(MUX_CCI) +X(MUX_CCO) +//X(MUX_CI) +X(MUX_CZ) +//X(MUX_P) +//X(MUX_X) +//X(MUX_Y) +//X(MUX_Z) +//X(PRE_ADDER_OP) +//X(PR_A_CASCADE_MUX) +//X(PR_A_MUX) +//X(PR_B_CASCADE_MUX) +//X(PR_B_MUX) +X(PR_CCO_MUX) +//X(PR_CI_MUX) +//X(PR_CO_MUX) +X(PR_CZ_MUX) +//X(PR_C_MUX) +//X(PR_D_MUX) +//X(PR_MULT_MUX) +//X(PR_OV_MUX) +//X(PR_P_MUX) +X(PR_RSTZ_MUX) +X(PR_RST_MUX) +X(PR_WEZ_MUX) +X(PR_WE_MUX) +//X(PR_X_MUX) +//X(PR_Y_MUX) +//X(PR_Z_MUX) +//X(SATURATION_RANK) +//X(SIGNED_MODE) + +// NX_DSP_U_WRAP +X(NX_DSP_U_WRAP) +//X(raw_config0) +//X(raw_config1) +//X(raw_config2) +//X(raw_config3) +//X(std_mode) + +// NX_DSP_WRAP +X(NX_DSP_WRAP) +//X(raw_config0) +//X(raw_config1) +//X(raw_config2) +//X(raw_config3) +//X(std_mode) + +// NX_ECC +X(NX_ECC) + +// NX_FIFO_DPREG +X(NX_FIFO_DPREG) +X(rck_edge) +X(read_addr_inv) +X(use_read_arst) +X(use_write_arst) +X(wck_edge) + +// NX_FIFO_U +X(NX_FIFO_U) +//X(mode) +//X(rck_edge) +//X(read_addr_inv) +//X(use_read_arst) +//X(use_write_arst) +//X(wck_edge) + +// NX_GCK_U +X(NX_GCK_U) +X(inv_in) +X(inv_out) +//X(std_mode) + +// NX_HSSL_L_FULL +X(NX_HSSL_L_FULL) +X(cfg_main_i) +X(cfg_rx0_i) +X(cfg_rx1_i) +X(cfg_rx2_i) +X(cfg_rx3_i) +X(cfg_rx4_i) +X(cfg_rx5_i) +X(cfg_tx0_i) +X(cfg_tx1_i) +X(cfg_tx2_i) +X(cfg_tx3_i) +X(cfg_tx4_i) +X(cfg_tx5_i) +//X(location) + +// NX_HSSL_U_FULL +X(NX_HSSL_U_FULL) +X(cfg_dyn_all_rx_pma_m_eye_coarse_ena_i) +X(cfg_dyn_all_rx_pma_m_eye_dn_i) +X(cfg_dyn_all_rx_pma_m_eye_fine_ena_i) +X(cfg_dyn_all_rx_pma_m_eye_i) +X(cfg_dyn_all_rx_pma_m_eye_step_i) +X(cfg_dyn_all_rx_pma_m_eye_up_i) +X(cfg_dyn_all_rx_pma_threshold_1) +X(cfg_dyn_all_rx_pma_threshold_2) +X(cfg_dyn_all_rx_pma_trim_locked_i) +X(cfg_dyn_all_rx_pma_trim_mode_i) +X(cfg_dyn_all_rx_pma_trim_unlocked_i) +X(cfg_dyn_rx0_pma_ctle_cap_p_i) +X(cfg_dyn_rx0_pma_ctle_res_p_i) +X(cfg_dyn_rx0_pma_dfe_idac_tap1_n_i) +X(cfg_dyn_rx0_pma_dfe_idac_tap2_n_i) +X(cfg_dyn_rx0_pma_dfe_idac_tap3_n_i) +X(cfg_dyn_rx0_pma_dfe_idac_tap4_n_i) +X(cfg_dyn_rx0_pma_termination_cmd_i) +X(cfg_dyn_rx1_pma_ctle_cap_p_i) +X(cfg_dyn_rx1_pma_ctle_res_p_i) +X(cfg_dyn_rx1_pma_dfe_idac_tap1_n_i) +X(cfg_dyn_rx1_pma_dfe_idac_tap2_n_i) +X(cfg_dyn_rx1_pma_dfe_idac_tap3_n_i) +X(cfg_dyn_rx1_pma_dfe_idac_tap4_n_i) +X(cfg_dyn_rx1_pma_termination_cmd_i) +X(cfg_dyn_rx2_pma_ctle_cap_p_i) +X(cfg_dyn_rx2_pma_ctle_res_p_i) +X(cfg_dyn_rx2_pma_dfe_idac_tap1_n_i) +X(cfg_dyn_rx2_pma_dfe_idac_tap2_n_i) +X(cfg_dyn_rx2_pma_dfe_idac_tap3_n_i) +X(cfg_dyn_rx2_pma_dfe_idac_tap4_n_i) +X(cfg_dyn_rx2_pma_termination_cmd_i) +X(cfg_dyn_rx3_pma_ctle_cap_p_i) +X(cfg_dyn_rx3_pma_ctle_res_p_i) +X(cfg_dyn_rx3_pma_dfe_idac_tap1_n_i) +X(cfg_dyn_rx3_pma_dfe_idac_tap2_n_i) +X(cfg_dyn_rx3_pma_dfe_idac_tap3_n_i) +X(cfg_dyn_rx3_pma_dfe_idac_tap4_n_i) +X(cfg_dyn_rx3_pma_termination_cmd_i) +X(cfg_dyn_tx0_pma_main_en_i) +X(cfg_dyn_tx0_pma_main_sign_i) +X(cfg_dyn_tx0_pma_margin_input_i) +X(cfg_dyn_tx0_pma_margin_sel_i) +X(cfg_dyn_tx0_pma_post_en_i) +X(cfg_dyn_tx0_pma_post_sel_i) +X(cfg_dyn_tx0_pma_post_sign_i) +X(cfg_dyn_tx0_pma_pre_en_i) +X(cfg_dyn_tx0_pma_pre_sel_i) +X(cfg_dyn_tx0_pma_pre_sign_i) +X(cfg_dyn_tx1_pma_main_en_i) +X(cfg_dyn_tx1_pma_main_sign_i) +X(cfg_dyn_tx1_pma_margin_input_i) +X(cfg_dyn_tx1_pma_margin_sel_i) +X(cfg_dyn_tx1_pma_post_en_i) +X(cfg_dyn_tx1_pma_post_sel_i) +X(cfg_dyn_tx1_pma_post_sign_i) +X(cfg_dyn_tx1_pma_pre_en_i) +X(cfg_dyn_tx1_pma_pre_sel_i) +X(cfg_dyn_tx1_pma_pre_sign_i) +X(cfg_dyn_tx2_pma_main_en_i) +X(cfg_dyn_tx2_pma_main_sign_i) +X(cfg_dyn_tx2_pma_margin_input_i) +X(cfg_dyn_tx2_pma_margin_sel_i) +X(cfg_dyn_tx2_pma_post_en_i) +X(cfg_dyn_tx2_pma_post_sel_i) +X(cfg_dyn_tx2_pma_post_sign_i) +X(cfg_dyn_tx2_pma_pre_en_i) +X(cfg_dyn_tx2_pma_pre_sel_i) +X(cfg_dyn_tx2_pma_pre_sign_i) +X(cfg_dyn_tx3_pma_main_en_i) +X(cfg_dyn_tx3_pma_main_sign_i) +X(cfg_dyn_tx3_pma_margin_input_i) +X(cfg_dyn_tx3_pma_margin_sel_i) +X(cfg_dyn_tx3_pma_post_en_i) +X(cfg_dyn_tx3_pma_post_sel_i) +X(cfg_dyn_tx3_pma_post_sign_i) +X(cfg_dyn_tx3_pma_pre_en_i) +X(cfg_dyn_tx3_pma_pre_sel_i) +X(cfg_dyn_tx3_pma_pre_sign_i) +X(cfg_main_clk_to_fabric_div_en_i) +X(cfg_main_clk_to_fabric_div_mode_i) +X(cfg_main_clk_to_fabric_sel_i) +X(cfg_main_rclk_to_fabric_sel_i) +X(cfg_main_use_only_usr_clock_i) +X(cfg_pcs_ovs_en_i) +X(cfg_pcs_ovs_mode_i) +X(cfg_pcs_pll_lock_ppm_i) +X(cfg_pcs_word_len_i) +X(cfg_pll_pma_ckref_ext_i) +X(cfg_pll_pma_cpump_i) +X(cfg_pll_pma_divl_i) +X(cfg_pll_pma_divm_i) +X(cfg_pll_pma_divn_i) +X(cfg_pll_pma_gbx_en_i) +X(cfg_pll_pma_int_data_len_i) +X(cfg_pll_pma_lvds_en_i) +X(cfg_pll_pma_lvds_mux_i) +X(cfg_pll_pma_mux_ckref_i) +X(cfg_rx0_gearbox_en_i) +X(cfg_rx0_gearbox_mode_i) +X(cfg_rx0_pcs_8b_dscr_sel_i) +X(cfg_rx0_pcs_align_bypass_i) +X(cfg_rx0_pcs_buffers_bypass_i) +X(cfg_rx0_pcs_buffers_use_cdc_i) +X(cfg_rx0_pcs_bypass_pma_cdc_i) +X(cfg_rx0_pcs_bypass_usr_cdc_i) +X(cfg_rx0_pcs_comma_mask_i) +X(cfg_rx0_pcs_debug_en_i) +X(cfg_rx0_pcs_dec_bypass_i) +X(cfg_rx0_pcs_dscr_bypass_i) +X(cfg_rx0_pcs_el_buff_diff_bef_comp_i) +X(cfg_rx0_pcs_el_buff_max_comp_i) +X(cfg_rx0_pcs_el_buff_only_one_skp_i) +X(cfg_rx0_pcs_el_buff_skp_char_0_i) +X(cfg_rx0_pcs_el_buff_skp_char_1_i) +X(cfg_rx0_pcs_el_buff_skp_char_2_i) +X(cfg_rx0_pcs_el_buff_skp_char_3_i) +X(cfg_rx0_pcs_el_buff_skp_header_0_i) +X(cfg_rx0_pcs_el_buff_skp_header_1_i) +X(cfg_rx0_pcs_el_buff_skp_header_2_i) +X(cfg_rx0_pcs_el_buff_skp_header_3_i) +X(cfg_rx0_pcs_el_buff_skp_header_size_i) +X(cfg_rx0_pcs_el_buff_skp_seq_size_i) +X(cfg_rx0_pcs_fsm_sel_i) +X(cfg_rx0_pcs_fsm_watchdog_en_i) +X(cfg_rx0_pcs_loopback_i) +X(cfg_rx0_pcs_m_comma_en_i) +X(cfg_rx0_pcs_m_comma_val_i) +X(cfg_rx0_pcs_nb_comma_bef_realign_i) +X(cfg_rx0_pcs_p_comma_en_i) +X(cfg_rx0_pcs_p_comma_val_i) +X(cfg_rx0_pcs_polarity_i) +X(cfg_rx0_pcs_protocol_size_i) +X(cfg_rx0_pcs_replace_bypass_i) +X(cfg_rx0_pcs_sync_supported_i) +X(cfg_rx0_pma_cdr_cp_i) +X(cfg_rx0_pma_clk_pos_i) +X(cfg_rx0_pma_coarse_ppm_i) +X(cfg_rx0_pma_ctrl_term_i) +X(cfg_rx0_pma_dco_divl_i) +X(cfg_rx0_pma_dco_divm_i) +X(cfg_rx0_pma_dco_divn_i) +X(cfg_rx0_pma_dco_reg_res_i) +X(cfg_rx0_pma_dco_vref_sel_i) +X(cfg_rx0_pma_fine_ppm_i) +X(cfg_rx0_pma_loopback_i) +X(cfg_rx0_pma_m_eye_ppm_i) +X(cfg_rx0_pma_peak_detect_cmd_i) +X(cfg_rx0_pma_peak_detect_on_i) +X(cfg_rx0_pma_pll_cpump_n_i) +X(cfg_rx0_pma_pll_divf_en_n_i) +X(cfg_rx0_pma_pll_divf_i) +X(cfg_rx0_pma_pll_divm_en_n_i) +X(cfg_rx0_pma_pll_divm_i) +X(cfg_rx0_pma_pll_divn_en_n_i) +X(cfg_rx0_pma_pll_divn_i) +X(cfg_rx1_gearbox_en_i) +X(cfg_rx1_gearbox_mode_i) +X(cfg_rx1_pcs_8b_dscr_sel_i) +X(cfg_rx1_pcs_align_bypass_i) +X(cfg_rx1_pcs_buffers_bypass_i) +X(cfg_rx1_pcs_buffers_use_cdc_i) +X(cfg_rx1_pcs_bypass_pma_cdc_i) +X(cfg_rx1_pcs_bypass_usr_cdc_i) +X(cfg_rx1_pcs_comma_mask_i) +X(cfg_rx1_pcs_debug_en_i) +X(cfg_rx1_pcs_dec_bypass_i) +X(cfg_rx1_pcs_dscr_bypass_i) +X(cfg_rx1_pcs_el_buff_diff_bef_comp_i) +X(cfg_rx1_pcs_el_buff_max_comp_i) +X(cfg_rx1_pcs_el_buff_only_one_skp_i) +X(cfg_rx1_pcs_el_buff_skp_char_0_i) +X(cfg_rx1_pcs_el_buff_skp_char_1_i) +X(cfg_rx1_pcs_el_buff_skp_char_2_i) +X(cfg_rx1_pcs_el_buff_skp_char_3_i) +X(cfg_rx1_pcs_el_buff_skp_header_0_i) +X(cfg_rx1_pcs_el_buff_skp_header_1_i) +X(cfg_rx1_pcs_el_buff_skp_header_2_i) +X(cfg_rx1_pcs_el_buff_skp_header_3_i) +X(cfg_rx1_pcs_el_buff_skp_header_size_i) +X(cfg_rx1_pcs_el_buff_skp_seq_size_i) +X(cfg_rx1_pcs_fsm_sel_i) +X(cfg_rx1_pcs_fsm_watchdog_en_i) +X(cfg_rx1_pcs_loopback_i) +X(cfg_rx1_pcs_m_comma_en_i) +X(cfg_rx1_pcs_m_comma_val_i) +X(cfg_rx1_pcs_nb_comma_bef_realign_i) +X(cfg_rx1_pcs_p_comma_en_i) +X(cfg_rx1_pcs_p_comma_val_i) +X(cfg_rx1_pcs_polarity_i) +X(cfg_rx1_pcs_protocol_size_i) +X(cfg_rx1_pcs_replace_bypass_i) +X(cfg_rx1_pcs_sync_supported_i) +X(cfg_rx1_pma_cdr_cp_i) +X(cfg_rx1_pma_clk_pos_i) +X(cfg_rx1_pma_coarse_ppm_i) +X(cfg_rx1_pma_ctrl_term_i) +X(cfg_rx1_pma_dco_divl_i) +X(cfg_rx1_pma_dco_divm_i) +X(cfg_rx1_pma_dco_divn_i) +X(cfg_rx1_pma_dco_reg_res_i) +X(cfg_rx1_pma_dco_vref_sel_i) +X(cfg_rx1_pma_fine_ppm_i) +X(cfg_rx1_pma_loopback_i) +X(cfg_rx1_pma_m_eye_ppm_i) +X(cfg_rx1_pma_peak_detect_cmd_i) +X(cfg_rx1_pma_peak_detect_on_i) +X(cfg_rx1_pma_pll_cpump_n_i) +X(cfg_rx1_pma_pll_divf_en_n_i) +X(cfg_rx1_pma_pll_divf_i) +X(cfg_rx1_pma_pll_divm_en_n_i) +X(cfg_rx1_pma_pll_divm_i) +X(cfg_rx1_pma_pll_divn_en_n_i) +X(cfg_rx1_pma_pll_divn_i) +X(cfg_rx2_gearbox_en_i) +X(cfg_rx2_gearbox_mode_i) +X(cfg_rx2_pcs_8b_dscr_sel_i) +X(cfg_rx2_pcs_align_bypass_i) +X(cfg_rx2_pcs_buffers_bypass_i) +X(cfg_rx2_pcs_buffers_use_cdc_i) +X(cfg_rx2_pcs_bypass_pma_cdc_i) +X(cfg_rx2_pcs_bypass_usr_cdc_i) +X(cfg_rx2_pcs_comma_mask_i) +X(cfg_rx2_pcs_debug_en_i) +X(cfg_rx2_pcs_dec_bypass_i) +X(cfg_rx2_pcs_dscr_bypass_i) +X(cfg_rx2_pcs_el_buff_diff_bef_comp_i) +X(cfg_rx2_pcs_el_buff_max_comp_i) +X(cfg_rx2_pcs_el_buff_only_one_skp_i) +X(cfg_rx2_pcs_el_buff_skp_char_0_i) +X(cfg_rx2_pcs_el_buff_skp_char_1_i) +X(cfg_rx2_pcs_el_buff_skp_char_2_i) +X(cfg_rx2_pcs_el_buff_skp_char_3_i) +X(cfg_rx2_pcs_el_buff_skp_header_0_i) +X(cfg_rx2_pcs_el_buff_skp_header_1_i) +X(cfg_rx2_pcs_el_buff_skp_header_2_i) +X(cfg_rx2_pcs_el_buff_skp_header_3_i) +X(cfg_rx2_pcs_el_buff_skp_header_size_i) +X(cfg_rx2_pcs_el_buff_skp_seq_size_i) +X(cfg_rx2_pcs_fsm_sel_i) +X(cfg_rx2_pcs_fsm_watchdog_en_i) +X(cfg_rx2_pcs_loopback_i) +X(cfg_rx2_pcs_m_comma_en_i) +X(cfg_rx2_pcs_m_comma_val_i) +X(cfg_rx2_pcs_nb_comma_bef_realign_i) +X(cfg_rx2_pcs_p_comma_en_i) +X(cfg_rx2_pcs_p_comma_val_i) +X(cfg_rx2_pcs_polarity_i) +X(cfg_rx2_pcs_protocol_size_i) +X(cfg_rx2_pcs_replace_bypass_i) +X(cfg_rx2_pcs_sync_supported_i) +X(cfg_rx2_pma_cdr_cp_i) +X(cfg_rx2_pma_clk_pos_i) +X(cfg_rx2_pma_coarse_ppm_i) +X(cfg_rx2_pma_ctrl_term_i) +X(cfg_rx2_pma_dco_divl_i) +X(cfg_rx2_pma_dco_divm_i) +X(cfg_rx2_pma_dco_divn_i) +X(cfg_rx2_pma_dco_reg_res_i) +X(cfg_rx2_pma_dco_vref_sel_i) +X(cfg_rx2_pma_fine_ppm_i) +X(cfg_rx2_pma_loopback_i) +X(cfg_rx2_pma_m_eye_ppm_i) +X(cfg_rx2_pma_peak_detect_cmd_i) +X(cfg_rx2_pma_peak_detect_on_i) +X(cfg_rx2_pma_pll_cpump_n_i) +X(cfg_rx2_pma_pll_divf_en_n_i) +X(cfg_rx2_pma_pll_divf_i) +X(cfg_rx2_pma_pll_divm_en_n_i) +X(cfg_rx2_pma_pll_divm_i) +X(cfg_rx2_pma_pll_divn_en_n_i) +X(cfg_rx2_pma_pll_divn_i) +X(cfg_rx3_gearbox_en_i) +X(cfg_rx3_gearbox_mode_i) +X(cfg_rx3_pcs_8b_dscr_sel_i) +X(cfg_rx3_pcs_align_bypass_i) +X(cfg_rx3_pcs_buffers_bypass_i) +X(cfg_rx3_pcs_buffers_use_cdc_i) +X(cfg_rx3_pcs_bypass_pma_cdc_i) +X(cfg_rx3_pcs_bypass_usr_cdc_i) +X(cfg_rx3_pcs_comma_mask_i) +X(cfg_rx3_pcs_debug_en_i) +X(cfg_rx3_pcs_dec_bypass_i) +X(cfg_rx3_pcs_dscr_bypass_i) +X(cfg_rx3_pcs_el_buff_diff_bef_comp_i) +X(cfg_rx3_pcs_el_buff_max_comp_i) +X(cfg_rx3_pcs_el_buff_only_one_skp_i) +X(cfg_rx3_pcs_el_buff_skp_char_0_i) +X(cfg_rx3_pcs_el_buff_skp_char_1_i) +X(cfg_rx3_pcs_el_buff_skp_char_2_i) +X(cfg_rx3_pcs_el_buff_skp_char_3_i) +X(cfg_rx3_pcs_el_buff_skp_header_0_i) +X(cfg_rx3_pcs_el_buff_skp_header_1_i) +X(cfg_rx3_pcs_el_buff_skp_header_2_i) +X(cfg_rx3_pcs_el_buff_skp_header_3_i) +X(cfg_rx3_pcs_el_buff_skp_header_size_i) +X(cfg_rx3_pcs_el_buff_skp_seq_size_i) +X(cfg_rx3_pcs_fsm_sel_i) +X(cfg_rx3_pcs_fsm_watchdog_en_i) +X(cfg_rx3_pcs_loopback_i) +X(cfg_rx3_pcs_m_comma_en_i) +X(cfg_rx3_pcs_m_comma_val_i) +X(cfg_rx3_pcs_nb_comma_bef_realign_i) +X(cfg_rx3_pcs_p_comma_en_i) +X(cfg_rx3_pcs_p_comma_val_i) +X(cfg_rx3_pcs_polarity_i) +X(cfg_rx3_pcs_protocol_size_i) +X(cfg_rx3_pcs_replace_bypass_i) +X(cfg_rx3_pcs_sync_supported_i) +X(cfg_rx3_pma_cdr_cp_i) +X(cfg_rx3_pma_clk_pos_i) +X(cfg_rx3_pma_coarse_ppm_i) +X(cfg_rx3_pma_ctrl_term_i) +X(cfg_rx3_pma_dco_divl_i) +X(cfg_rx3_pma_dco_divm_i) +X(cfg_rx3_pma_dco_divn_i) +X(cfg_rx3_pma_dco_reg_res_i) +X(cfg_rx3_pma_dco_vref_sel_i) +X(cfg_rx3_pma_fine_ppm_i) +X(cfg_rx3_pma_loopback_i) +X(cfg_rx3_pma_m_eye_ppm_i) +X(cfg_rx3_pma_peak_detect_cmd_i) +X(cfg_rx3_pma_peak_detect_on_i) +X(cfg_rx3_pma_pll_cpump_n_i) +X(cfg_rx3_pma_pll_divf_en_n_i) +X(cfg_rx3_pma_pll_divf_i) +X(cfg_rx3_pma_pll_divm_en_n_i) +X(cfg_rx3_pma_pll_divm_i) +X(cfg_rx3_pma_pll_divn_en_n_i) +X(cfg_rx3_pma_pll_divn_i) +X(cfg_test_mode_i) +X(cfg_tx0_gearbox_en_i) +X(cfg_tx0_gearbox_mode_i) +X(cfg_tx0_pcs_8b_scr_sel_i) +X(cfg_tx0_pcs_bypass_pma_cdc_i) +X(cfg_tx0_pcs_bypass_usr_cdc_i) +X(cfg_tx0_pcs_enc_bypass_i) +X(cfg_tx0_pcs_esistream_fsm_en_i) +X(cfg_tx0_pcs_loopback_i) +X(cfg_tx0_pcs_polarity_i) +X(cfg_tx0_pcs_protocol_size_i) +X(cfg_tx0_pcs_replace_bypass_i) +X(cfg_tx0_pcs_scr_bypass_i) +X(cfg_tx0_pcs_scr_init_i) +X(cfg_tx0_pcs_sync_supported_i) +X(cfg_tx0_pma_clk_pos_i) +X(cfg_tx0_pma_loopback_i) +X(cfg_tx1_gearbox_en_i) +X(cfg_tx1_gearbox_mode_i) +X(cfg_tx1_pcs_8b_scr_sel_i) +X(cfg_tx1_pcs_bypass_pma_cdc_i) +X(cfg_tx1_pcs_bypass_usr_cdc_i) +X(cfg_tx1_pcs_enc_bypass_i) +X(cfg_tx1_pcs_esistream_fsm_en_i) +X(cfg_tx1_pcs_loopback_i) +X(cfg_tx1_pcs_polarity_i) +X(cfg_tx1_pcs_protocol_size_i) +X(cfg_tx1_pcs_replace_bypass_i) +X(cfg_tx1_pcs_scr_bypass_i) +X(cfg_tx1_pcs_scr_init_i) +X(cfg_tx1_pcs_sync_supported_i) +X(cfg_tx1_pma_clk_pos_i) +X(cfg_tx1_pma_loopback_i) +X(cfg_tx2_gearbox_en_i) +X(cfg_tx2_gearbox_mode_i) +X(cfg_tx2_pcs_8b_scr_sel_i) +X(cfg_tx2_pcs_bypass_pma_cdc_i) +X(cfg_tx2_pcs_bypass_usr_cdc_i) +X(cfg_tx2_pcs_enc_bypass_i) +X(cfg_tx2_pcs_esistream_fsm_en_i) +X(cfg_tx2_pcs_loopback_i) +X(cfg_tx2_pcs_polarity_i) +X(cfg_tx2_pcs_protocol_size_i) +X(cfg_tx2_pcs_replace_bypass_i) +X(cfg_tx2_pcs_scr_bypass_i) +X(cfg_tx2_pcs_scr_init_i) +X(cfg_tx2_pcs_sync_supported_i) +X(cfg_tx2_pma_clk_pos_i) +X(cfg_tx2_pma_loopback_i) +X(cfg_tx3_gearbox_en_i) +X(cfg_tx3_gearbox_mode_i) +X(cfg_tx3_pcs_8b_scr_sel_i) +X(cfg_tx3_pcs_bypass_pma_cdc_i) +X(cfg_tx3_pcs_bypass_usr_cdc_i) +X(cfg_tx3_pcs_enc_bypass_i) +X(cfg_tx3_pcs_esistream_fsm_en_i) +X(cfg_tx3_pcs_loopback_i) +X(cfg_tx3_pcs_polarity_i) +X(cfg_tx3_pcs_protocol_size_i) +X(cfg_tx3_pcs_replace_bypass_i) +X(cfg_tx3_pcs_scr_bypass_i) +X(cfg_tx3_pcs_scr_init_i) +X(cfg_tx3_pcs_sync_supported_i) +X(cfg_tx3_pma_clk_pos_i) +X(cfg_tx3_pma_loopback_i) +//X(location) +X(rx_usrclk_use_pcs_clk_2) +X(tx_usrclk_use_pcs_clk_2) + +// NX_IOB +X(NX_IOB) +//X(differential) +//X(drive) +X(dynDrive) +X(dynInput) +X(dynTerm) +X(extra) +//X(inputDelayLine) +X(inputDelayOn) +//X(inputSignalSlope) +//X(location) +//X(locked) +X(outputCapacity) +X(outputDelayLine) +X(outputDelayOn) +X(slewRate) +//X(standard) +//X(termination) +//X(terminationReference) +//X(turbo) +//X(weakTermination) + +// NX_IOB_I +X(NX_IOB_I) +//X(differential) +//X(drive) +//X(dynDrive) +//X(dynInput) +//X(dynTerm) +//X(extra) +//X(inputDelayLine) +//X(inputDelayOn) +//X(inputSignalSlope) +//X(location) +//X(locked) +//X(outputCapacity) +//X(outputDelayLine) +//X(outputDelayOn) +//X(slewRate) +//X(standard) +//X(termination) +//X(terminationReference) +//X(turbo) +//X(weakTermination) + +// NX_IOB_O +X(NX_IOB_O) +//X(differential) +//X(drive) +//X(dynDrive) +//X(dynInput) +//X(dynTerm) +//X(extra) +//X(inputDelayLine) +//X(inputDelayOn) +//X(inputSignalSlope) +//X(location) +//X(locked) +//X(outputCapacity) +//X(outputDelayLine) +//X(outputDelayOn) +//X(slewRate) +//X(standard) +//X(termination) +//X(terminationReference) +//X(turbo) +//X(weakTermination) + +// NX_IOM +X(NX_IOM) +X(div_rx1) +X(div_rx2) +X(div_tx1) +X(div_tx2) +X(mode_io_cal) +X(mode_side1) +X(mode_side2) +X(pads_dict) +X(pads_path) +X(sel_clk_out1) +X(sel_clk_out2) +X(sel_clkr_rx1) +X(sel_clkr_rx2) +X(sel_clkw_rx1) +X(sel_clkw_rx2) + +// NX_IOM_BIN2GRP +X(NX_IOM_BIN2GRP) + +// NX_IOM_CONTROL +X(NX_IOM_CONTROL) +//X(div_rx1) +//X(div_rx2) +//X(div_tx1) +//X(div_tx2) +X(inv_di_fclk1) +X(inv_di_fclk2) +X(latency1) +X(latency2) +//X(location) +X(mode_cpath) +X(mode_epath) +//X(mode_io_cal) +X(mode_rpath) +//X(mode_side1) +//X(mode_side2) +X(mode_tpath) +//X(sel_clk_out1) +//X(sel_clk_out2) +//X(sel_clkr_rx1) +//X(sel_clkr_rx2) +//X(sel_clkw_rx1) +//X(sel_clkw_rx2) + +// NX_IOM_CONTROL_L +X(NX_IOM_CONTROL_L) +//X(div_rx1) +//X(div_rx2) +//X(div_tx1) +//X(div_tx2) +//X(inv_di_fclk1) +//X(inv_di_fclk2) +//X(latency1) +//X(latency2) +//X(location) +//X(mode_cpath) +//X(mode_epath) +//X(mode_io_cal) +//X(mode_rpath) +//X(mode_side1) +//X(mode_side2) +//X(mode_tpath) +//X(sel_clk_out1) +//X(sel_clk_out2) +//X(sel_clkr_rx1) +//X(sel_clkr_rx2) +//X(sel_clkw_rx1) +//X(sel_clkw_rx2) + +// NX_IOM_CONTROL_M +X(NX_IOM_CONTROL_M) +//X(div_rx1) +//X(div_rx2) +//X(div_tx1) +//X(div_tx2) +//X(inv_di_fclk1) +//X(inv_di_fclk2) +//X(latency1) +//X(latency2) +//X(location) +//X(mode_cpath) +//X(mode_epath) +//X(mode_io_cal) +//X(mode_rpath) +//X(mode_side1) +//X(mode_side2) +//X(mode_tpath) +//X(sel_clk_out1) +//X(sel_clk_out2) +//X(sel_clkr_rx1) +//X(sel_clkr_rx2) +//X(sel_clkw_rx1) +//X(sel_clkw_rx2) + +// NX_IOM_CONTROL_U +X(NX_IOM_CONTROL_U) +X(cal_delay1) +X(cal_delay2) +X(div1) +X(div2) +X(div3) +X(div_swrx1) +X(div_swrx2) +X(inv_ld_sck1) +X(inv_ld_sck2) +X(inv_ld_sck3) +X(link_ld_12) +X(link_ld_23) +//X(location) +//X(mode_side1) +//X(mode_side2) +X(mode_side3) +//X(sel_clk_out1) +//X(sel_clk_out2) +X(sel_dc_clk) +X(sel_ld_fck1) +X(sel_ld_fck2) +X(sel_ld_fck3) +X(sel_sw_fck1) +X(sel_sw_fck2) +X(use_dc) + +// NX_IOM_DRIVER +X(NX_IOM_DRIVER) +X(chained) +X(cpath_edge) +X(cpath_init) +X(cpath_inv) +X(cpath_load) +X(cpath_mode) +X(cpath_sync) +X(epath_dynamic) +X(epath_edge) +X(epath_init) +X(epath_load) +X(epath_mode) +X(epath_sync) +//X(location) +X(rpath_dynamic) +X(rpath_edge) +X(rpath_init) +X(rpath_load) +X(rpath_mode) +X(rpath_sync) +X(symbol) +X(tpath_mode) +X(variant) + +// NX_IOM_DRIVER_M +X(NX_IOM_DRIVER_M) +//X(chained) +//X(cpath_edge) +//X(cpath_init) +//X(cpath_inv) +//X(cpath_load) +//X(cpath_mode) +//X(cpath_sync) +//X(epath_dynamic) +//X(epath_edge) +//X(epath_init) +//X(epath_load) +//X(epath_mode) +//X(epath_sync) +//X(location) +//X(rpath_dynamic) +//X(rpath_edge) +//X(rpath_init) +//X(rpath_load) +//X(rpath_mode) +//X(rpath_sync) +//X(symbol) +//X(tpath_mode) +//X(variant) + +// NX_IOM_DRIVER_U +X(NX_IOM_DRIVER_U) +//X(chained) +//X(cpath_edge) +//X(cpath_init) +//X(cpath_inv) +//X(cpath_load) +//X(cpath_mode) +//X(cpath_sync) +X(cpath_type) +//X(epath_dynamic) +//X(epath_edge) +//X(epath_init) +//X(epath_load) +//X(epath_mode) +//X(epath_sync) +X(epath_type) +//X(location) +//X(rpath_dynamic) +//X(rpath_edge) +//X(rpath_init) +//X(rpath_load) +//X(rpath_mode) +//X(rpath_sync) +X(rpath_type) +//X(symbol) +//X(tpath_mode) + +// NX_IOM_L +X(NX_IOM_L) +//X(div_rx1) +//X(div_rx2) +//X(div_tx1) +//X(div_tx2) +//X(mode_io_cal) +//X(mode_side1) +//X(mode_side2) +//X(pads_dict) +//X(pads_path) +//X(sel_clk_out1) +//X(sel_clk_out2) +//X(sel_clkr_rx1) +//X(sel_clkr_rx2) +//X(sel_clkw_rx1) +//X(sel_clkw_rx2) + +// NX_IOM_SERDES +X(NX_IOM_SERDES) +//X(data_size) +//X(location) + +// NX_IOM_SERDES_M +X(NX_IOM_SERDES_M) +//X(data_size) +//X(location) + +// NX_IOM_SERDES_U +X(NX_IOM_SERDES_U) +//X(data_size) +//X(location) + +// NX_IOM_U +X(NX_IOM_U) +//X(cal_delay1) +//X(cal_delay2) +//X(div1) +//X(div2) +//X(div3) +//X(div_swrx1) +//X(div_swrx2) +//X(inv_ld_sck1) +//X(inv_ld_sck2) +//X(inv_ld_sck3) +//X(link_ld_12) +//X(link_ld_23) +//X(mode_side1) +//X(mode_side2) +//X(mode_side3) +//X(pads_dict) +//X(pads_path) +//X(sel_clk_out1) +//X(sel_clk_out2) +//X(sel_dc_clk) +//X(sel_ld_fck1) +//X(sel_ld_fck2) +//X(sel_ld_fck3) +//X(sel_sw_fck1) +//X(sel_sw_fck2) +//X(use_dc) + +// NX_LUT +X(NX_LUT) +X(lut_table) + +// NX_PLL +X(NX_PLL) +X(clk_outdiv1) +X(clk_outdiv2) +X(clk_outdiv3) +X(ext_fbk_on) +X(fbk_delay) +X(fbk_delay_on) +X(fbk_div_on) +X(fbk_intdiv) +//X(location) +X(ref_div_on) +X(vco_range) + +// NX_PLL_L +X(NX_PLL_L) +X(cfg_use_pll) +X(clk_outdivo1) +X(clk_outdivp1) +X(clk_outdivp2) +X(clk_outdivp3o2) +//X(ext_fbk_on) +//X(fbk_delay) +//X(fbk_delay_on) +//X(fbk_intdiv) +//X(location) +X(pll_cpump) +X(ref_intdiv) +X(ref_osc_on) +X(wfg_sync_cal_lock) +X(wfg_sync_pll_lock) + +// NX_PLL_U +X(NX_PLL_U) +X(cal_delay) +X(cal_div) +X(clk_cal_sel) +//X(clk_outdiv1) +//X(clk_outdiv2) +//X(clk_outdiv3) +X(clk_outdiv4) +X(clk_outdivd1) +X(clk_outdivd2) +X(clk_outdivd3) +X(clk_outdivd4) +X(clk_outdivd5) +//X(ext_fbk_on) +//X(fbk_delay) +//X(fbk_delay_on) +//X(fbk_intdiv) +//X(location) +//X(pll_cpump) +X(pll_lock) +X(pll_lpf_cap) +X(pll_lpf_res) +X(pll_odf) +//X(ref_intdiv) +//X(ref_osc_on) +X(use_cal) +X(use_pll) + +// NX_PLL_U_WRAP +X(NX_PLL_U_WRAP) +//X(cal_delay) +//X(cal_div) +//X(clk_cal_sel) +//X(clk_outdiv1) +//X(clk_outdiv2) +//X(clk_outdiv3) +//X(clk_outdiv4) +//X(clk_outdivd1) +//X(clk_outdivd2) +//X(clk_outdivd3) +//X(clk_outdivd4) +//X(clk_outdivd5) +//X(ext_fbk_on) +//X(fbk_delay) +//X(fbk_delay_on) +//X(fbk_intdiv) +//X(location) +//X(pll_cpump) +//X(pll_lock) +//X(pll_lpf_cap) +//X(pll_lpf_res) +//X(pll_odf) +//X(ref_intdiv) +//X(ref_osc_on) +//X(use_cal) +//X(use_pll) + +// NX_PMA_L +X(NX_PMA_L) +//X(location) +X(main_clk_to_fabric_div_en) +X(main_clk_to_fabric_div_mode) +X(main_clk_to_fabric_sel) +X(main_test) +X(main_use_only_usr_clock) +X(main_use_pcs_clk_2) +X(pcs_ovs_mode) +X(pcs_pll_lock_count) +X(pcs_word_len) +X(pll_pma_cpump_n) +X(pll_pma_divf) +X(pll_pma_divf_en_n) +X(pll_pma_divm) +X(pll_pma_divm_en_n) +X(pll_pma_divn) +X(pll_pma_divn_en_n) +X(pll_pma_int_data_len) +X(pll_pma_lvds_mux) +X(pll_pma_mux_ckref) +X(rx_pma_half_step) + +// NX_PMA_U +X(NX_PMA_U) +X(dyn_all_rx_pma_m_eye) +X(dyn_all_rx_pma_m_eye_coarse_ena) +X(dyn_all_rx_pma_m_eye_dn) +X(dyn_all_rx_pma_m_eye_fine_ena) +X(dyn_all_rx_pma_m_eye_step) +X(dyn_all_rx_pma_m_eye_up) +X(dyn_all_rx_pma_threshold_1) +X(dyn_all_rx_pma_threshold_2) +X(dyn_all_rx_pma_trim_locked) +X(dyn_all_rx_pma_trim_mode) +X(dyn_all_rx_pma_trim_unlocked) +X(dyn_rx0_pma_ctle_cap_p) +X(dyn_rx0_pma_ctle_res_p) +X(dyn_rx0_pma_dfe_idac_tap1_n) +X(dyn_rx0_pma_dfe_idac_tap2_n) +X(dyn_rx0_pma_dfe_idac_tap3_n) +X(dyn_rx0_pma_dfe_idac_tap4_n) +X(dyn_rx0_pma_termination_cmd) +X(dyn_rx1_pma_ctle_cap_p) +X(dyn_rx1_pma_ctle_res_p) +X(dyn_rx1_pma_dfe_idac_tap1_n) +X(dyn_rx1_pma_dfe_idac_tap2_n) +X(dyn_rx1_pma_dfe_idac_tap3_n) +X(dyn_rx1_pma_dfe_idac_tap4_n) +X(dyn_rx1_pma_termination_cmd) +X(dyn_rx2_pma_ctle_cap_p) +X(dyn_rx2_pma_ctle_res_p) +X(dyn_rx2_pma_dfe_idac_tap1_n) +X(dyn_rx2_pma_dfe_idac_tap2_n) +X(dyn_rx2_pma_dfe_idac_tap3_n) +X(dyn_rx2_pma_dfe_idac_tap4_n) +X(dyn_rx2_pma_termination_cmd) +X(dyn_rx3_pma_ctle_cap_p) +X(dyn_rx3_pma_ctle_res_p) +X(dyn_rx3_pma_dfe_idac_tap1_n) +X(dyn_rx3_pma_dfe_idac_tap2_n) +X(dyn_rx3_pma_dfe_idac_tap3_n) +X(dyn_rx3_pma_dfe_idac_tap4_n) +X(dyn_rx3_pma_termination_cmd) +X(dyn_tx0_pma_main_en) +X(dyn_tx0_pma_main_sign) +X(dyn_tx0_pma_margin_input) +X(dyn_tx0_pma_margin_sel) +X(dyn_tx0_pma_post_en) +X(dyn_tx0_pma_post_sel) +X(dyn_tx0_pma_post_sign) +X(dyn_tx0_pma_pre_en) +X(dyn_tx0_pma_pre_sel) +X(dyn_tx0_pma_pre_sign) +X(dyn_tx1_pma_main_en) +X(dyn_tx1_pma_main_sign) +X(dyn_tx1_pma_margin_input) +X(dyn_tx1_pma_margin_sel) +X(dyn_tx1_pma_post_en) +X(dyn_tx1_pma_post_sel) +X(dyn_tx1_pma_post_sign) +X(dyn_tx1_pma_pre_en) +X(dyn_tx1_pma_pre_sel) +X(dyn_tx1_pma_pre_sign) +X(dyn_tx2_pma_main_en) +X(dyn_tx2_pma_main_sign) +X(dyn_tx2_pma_margin_input) +X(dyn_tx2_pma_margin_sel) +X(dyn_tx2_pma_post_en) +X(dyn_tx2_pma_post_sel) +X(dyn_tx2_pma_post_sign) +X(dyn_tx2_pma_pre_en) +X(dyn_tx2_pma_pre_sel) +X(dyn_tx2_pma_pre_sign) +X(dyn_tx3_pma_main_en) +X(dyn_tx3_pma_main_sign) +X(dyn_tx3_pma_margin_input) +X(dyn_tx3_pma_margin_sel) +X(dyn_tx3_pma_post_en) +X(dyn_tx3_pma_post_sel) +X(dyn_tx3_pma_post_sign) +X(dyn_tx3_pma_pre_en) +X(dyn_tx3_pma_pre_sel) +X(dyn_tx3_pma_pre_sign) +//X(location) +//X(main_clk_to_fabric_div_en) +//X(main_clk_to_fabric_div_mode) +//X(main_clk_to_fabric_sel) +X(main_rclk_to_fabric_sel) +//X(main_use_only_usr_clock) +X(pcs_ovs_en) +//X(pcs_ovs_mode) +X(pcs_pll_lock_ppm) +//X(pcs_word_len) +X(pll_pma_ckref_ext) +X(pll_pma_cpump) +X(pll_pma_divl) +//X(pll_pma_divm) +//X(pll_pma_divn) +X(pll_pma_gbx_en) +//X(pll_pma_int_data_len) +X(pll_pma_lvds_en) +//X(pll_pma_lvds_mux) +//X(pll_pma_mux_ckref) +//X(rx_usrclk_use_pcs_clk_2) +X(test_mode) +//X(tx_usrclk_use_pcs_clk_2) + +// NX_R5_L +X(NX_R5_L) + +// NX_R5_L_WRAP +X(NX_R5_L_WRAP) + +// NX_RAM +X(NX_RAM) +X(mcka_edge) +X(mckb_edge) +X(mem_ctxt) +X(pcka_edge) +X(pckb_edge) +X(pipe_ia) +X(pipe_ib) +X(pipe_oa) +X(pipe_ob) +//X(raw_config0) +//X(raw_config1) +X(raw_l_enable) +X(raw_l_extend) +X(raw_u_enable) +X(raw_u_extend) +//X(std_mode) + +// NX_RAM_SLOWECC_1K_36_1r1w +X(NX_RAM_SLOWECC_1K_36_1r1w) +//X(mem_ctxt) + +// NX_RAM_WRAP +X(NX_RAM_WRAP) +//X(mcka_edge) +//X(mckb_edge) +//X(mem_ctxt) +//X(pcka_edge) +//X(pckb_edge) +//X(pipe_ia) +//X(pipe_ib) +//X(pipe_oa) +//X(pipe_ob) +//X(raw_config0) +//X(raw_config1) +//X(raw_l_enable) +//X(raw_l_extend) +//X(raw_u_enable) +//X(raw_u_extend) +//X(std_mode) + +// NX_RB +X(NX_RB) +X(inputBypass) +X(inputClk) +X(inputContext) +X(outputBypass) +X(outputClk) +X(outputContext) + +// NX_RB_WRAP +X(NX_RB_WRAP) +//X(inputBypass) +//X(inputClk) +//X(inputContext) +//X(outputBypass) +//X(outputClk) +//X(outputContext) + +// NX_RFB +X(NX_RFB) +X(addr_mask) +//X(mem_ctxt) +//X(rck_edge) +//X(wck_edge) +X(we_mask) +X(wea_mask) + +// NX_RFBDP_U_WRAP +X(NX_RFBDP_U_WRAP) +//X(mem_ctxt) +//X(wck_edge) + +// NX_RFBSP_U_WRAP +X(NX_RFBSP_U_WRAP) +//X(mem_ctxt) +//X(wck_edge) + +// NX_RFB_L +X(NX_RFB_L) +//X(mem_ctxt) +//X(mode) +//X(rck_edge) +//X(wck_edge) + +// NX_RFB_L_WRAP +X(NX_RFB_L_WRAP) +//X(mem_ctxt) +//X(mode) +//X(rck_edge) +//X(wck_edge) + +// NX_RFB_M +X(NX_RFB_M) +//X(mem_ctxt) +//X(rck_edge) +//X(wck_edge) + +// NX_RFB_U +X(NX_RFB_U) +//X(mem_ctxt) +//X(mode) +//X(wck_edge) + +// NX_RFB_WRAP +X(NX_RFB_WRAP) +//X(mem_ctxt) +//X(rck_edge) +//X(wck_edge) + +// NX_SER +X(NX_SER) +//X(data_size) +//X(differential) +//X(drive) +//X(location) +//X(locked) +//X(outputCapacity) +//X(outputDelayLine) +//X(slewRate) +X(spath_dynamic) +//X(standard) + +// NX_SERDES +X(NX_SERDES) +X(cpath_registered) +//X(data_size) +//X(differential) +//X(dpath_dynamic) +//X(drive) +//X(inputDelayLine) +//X(inputSignalSlope) +//X(location) +//X(locked) +//X(outputCapacity) +//X(outputDelayLine) +//X(slewRate) +//X(spath_dynamic) +//X(standard) +//X(termination) +//X(terminationReference) +//X(turbo) +//X(weakTermination) + +// NX_WFG +X(NX_WFG) +X(delay) +X(delay_on) +//X(location) +//X(mode) +X(pattern) +X(pattern_end) +X(wfg_edge) + +// NX_WFG_L +X(NX_WFG_L) +//X(delay) +//X(delay_on) +//X(location) +//X(mode) +//X(pattern) +//X(pattern_end) +//X(wfg_edge) + +// NX_WFG_U +X(NX_WFG_U) +//X(delay) +//X(delay_on) +X(div_phase) +X(div_ratio) +//X(location) +//X(mode) +//X(pattern) +//X(pattern_end) +X(reset_on_cal_lock_n) +X(reset_on_pll_lock_n) +X(reset_on_pll_locka_n) +//X(wfg_edge) + +// NX_XCDC_U +X(NX_XCDC_U) +//X(ack_sel) +//X(bck_sel) +//X(cck_sel) +//X(ck0_edge) +//X(ck1_edge) +//X(dck_sel) +//X(link_BA) +//X(link_CB) +//X(link_DC) +//X(use_adest_arst) +//X(use_asrc_arst) +//X(use_bdest_arst) +//X(use_bsrc_arst) +//X(use_cdest_arst) +//X(use_csrc_arst) +//X(use_ddest_arst) +//X(use_dsrc_arst) + +// NX_XFIFO_32x36 +X(NX_XFIFO_32x36) +//X(rck_edge) +//X(read_addr_inv) +//X(use_read_arst) +//X(use_write_arst) +//X(wck_edge) + +// NX_XFIFO_64x18 +X(NX_XFIFO_64x18) +//X(rck_edge) +//X(read_addr_inv) +//X(use_read_arst) +//X(use_write_arst) +//X(wck_edge) + +// NX_XRFB_2R_1W +X(NX_XRFB_2R_1W) +//X(mem_ctxt) +//X(wck_edge) + +// NX_XRFB_32x36 +X(NX_XRFB_32x36) +//X(mem_ctxt) +//X(wck_edge) + +// NX_XRFB_64x18 +X(NX_XRFB_64x18) +//X(mem_ctxt) +//X(wck_edge) + +// end of generated parameter list + +// IO location attribute +X(LOC) + +X(lut_used) +X(dff_used) + +X(type) + +// Timing +X(BEYOND_FE_LUT) + +// csv +X(registered) diff --git a/himbaechel/uarch/ng-ultra/csv.cc b/himbaechel/uarch/ng-ultra/csv.cc new file mode 100644 index 00000000..32e0861a --- /dev/null +++ b/himbaechel/uarch/ng-ultra/csv.cc @@ -0,0 +1,304 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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 +#include +#include +#include + +#include "extra_data.h" +#include "himbaechel_api.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +#include "ng_ultra.h" + +#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +void NgUltraImpl::parse_csv(const std::string &filename) +{ + std::ifstream in(filename); + if (!in) + log_error("failed to open CSV file '%s'\n", filename.c_str()); + log_info("Parsing CSV file..\n"); + std::string line; + std::string linebuf; + int lineno = 0; + enum uint8_t + { + IO_PADS = 0, + IO_BANKS, + IO_GCKS, + IO_ERROR + } line_type; + + auto isempty = [](const std::string &str) { + return std::all_of(str.begin(), str.end(), [](char c) { return std::isspace(c); }); + }; + auto is_number = [](const std::string &str) { + return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit); + }; + auto get_cells = [&](std::string str) { + std::vector tgt_cells; + IdString cellname = ctx->id(str); + if (ctx->cells.count(cellname)) + tgt_cells.push_back(ctx->cells.at(cellname).get()); + return tgt_cells; + }; + + line_type = IO_PADS; + pool banks_used; + bool old_format = false; + while (std::getline(in, line)) { + ++lineno; + // Trim comments, from # until end of the line + size_t cstart = line.find('#'); + if (cstart != std::string::npos) + line = line.substr(0, cstart); + if (isempty(line)) + continue; + + std::vector arguments; + boost::split(arguments, line, boost::is_any_of(",")); + if (arguments.empty()) + continue; + switch (line_type) { + case IO_PADS: { + if (arguments.size() == 1 && arguments[0][0] == '!') { + line_type = IO_BANKS; + continue; + } + if (arguments.size() != 15) + log_error("number of parameters in line %d must be 15\n", lineno); + + std::string arg_iobname = arguments.at(0); + std::string arg_location = arguments.at(1); + std::string arg_standard = arguments.at(2); + std::string arg_drive = arguments.at(3); + std::string arg_slewRate = arguments.at(4); + std::string arg_inputDelayLine = arguments.at(5); + std::string arg_outputDelayLine = arguments.at(6); + std::string arg_differential = arguments.at(7); + std::string arg_weakTermination = arguments.at(8); + std::string arg_termination = arguments.at(9); + std::string arg_terminationReference = arguments.at(10); + std::string arg_turbo = arguments.at(11); + std::string arg_inputSignalSlope = arguments.at(12); + std::string arg_outputCapacity = arguments.at(13); + std::string arg_registered = arguments.at(14); + + // TODO: Remove this block + const std::vector weak_values_check = {"None", "PullDown", "PullUp", "Keeper"}; + auto it2 = std::find(std::begin(weak_values_check), std::end(weak_values_check), arguments.at(4)); + if (it2 != std::end(weak_values_check)) { + if (!old_format) { + log_warning("Old CSV format detected. Please update file.\n"); + old_format = true; + } + arg_weakTermination = arguments.at(4); + arg_slewRate = arguments.at(5); + arg_termination = arguments.at(6); + arg_inputDelayLine = arguments.at(7); + arg_outputDelayLine = arguments.at(8); + arg_differential = arguments.at(9); + } + // End of block + + if (!(boost::starts_with(arg_location, "IOB") && boost::contains(arg_location, "_D"))) + log_error("invalid location name '%s' must start with 'IOB' in line %d\n", arg_location.c_str(), + lineno); + + const std::vector standard_values = {"LVDS", "LVCMOS", "SSTL", "HSTL"}; // , "POD" + auto it = std::find(std::begin(standard_values), std::end(standard_values), arg_standard); + if (it == std::end(standard_values)) + log_error("unknown standard value '%s' in line %d\n", arg_standard.c_str(), lineno); + + const std::vector drive_values = {"2mA", "4mA", "8mA", "16mA", + "CatI", "CatII", "Undefined"}; // "6mA", "12mA", + it = std::find(std::begin(drive_values), std::end(drive_values), arg_drive); + if (it == std::end(drive_values)) + log_error("unknown drive value '%s' in line %d\n", arg_drive.c_str(), lineno); + + const std::vector slew_values = {"Slow", "Medium", "Fast"}; + it = std::find(std::begin(slew_values), std::end(slew_values), arg_slewRate); + if (it == std::end(slew_values)) + log_error("unknown weak termination value '%s' in line %d\n", arg_slewRate.c_str(), lineno); + + if (!is_number(arg_inputDelayLine)) { + log_error("input delay must be number, value '%s' in line %d\n", arg_inputDelayLine.c_str(), lineno); + } else { + int delay = std::stoi(arg_inputDelayLine); + if (delay < 0 || delay > 63) + log_error("input delay value must be in range from 0 to 63 in line %d\n", lineno); + } + if (!is_number(arg_outputDelayLine)) { + log_error("output delay must be number, value '%s' in line %d\n", arg_outputDelayLine.c_str(), lineno); + } else { + int delay = std::stoi(arg_outputDelayLine); + if (delay < 0 || delay > 63) + log_error("output delay value must be in range from 0 to 63 in line %d\n", lineno); + } + + if (!arg_differential.empty() && arg_differential != "True" && arg_differential != "False") + log_error("differential must be boolean, value '%s' in line %d\n", arg_differential.c_str(), lineno); + + const std::vector weak_values = {"None", "PullDown", "PullUp", "Keeper"}; + it = std::find(std::begin(weak_values), std::end(weak_values), arg_weakTermination); + if (it == std::end(weak_values)) + log_error("unknown weak termination value '%s' in line %d\n", arg_weakTermination.c_str(), lineno); + + if (!arg_termination.empty()) { + if (!is_number(arg_termination)) { + log_error("termination must be string containing int, value '%s' in line %d\n", + arg_termination.c_str(), lineno); + } else { + int termination = std::stoi(arg_termination); + if (termination < 30 || termination > 80) + log_error("termination value must be in range from 30 to 80 in line %d\n", lineno); + } + } + + const std::vector termref_values = {"Floating", "VT"}; + it = std::find(std::begin(termref_values), std::end(termref_values), arg_terminationReference); + if (it == std::end(termref_values)) + log_error("unknown termination reference value '%s' in line %d\n", arg_terminationReference.c_str(), + lineno); + + if (!arg_turbo.empty() && arg_turbo != "True" && arg_turbo != "False") + log_error("turbo must be boolean, value '%s' in line %d\n", arg_turbo.c_str(), lineno); + + if (!arg_inputSignalSlope.empty() && !is_number(arg_inputSignalSlope)) + log_error("signal slope must be number, value '%s' in line %d\n", arg_inputSignalSlope.c_str(), lineno); + if (!arg_outputCapacity.empty() && !is_number(arg_outputCapacity)) + log_error("output capacity must be number, value '%s' in line %d\n", arg_outputCapacity.c_str(), + lineno); + + const std::vector registered_values = {"Auto", "I", "IC", "O", "OC", "IO", "IOC"}; + it = std::find(std::begin(registered_values), std::end(registered_values), arg_registered); + if (it == std::end(registered_values)) + log_error("unknown registered value '%s' in line %d\n", arg_registered.c_str(), lineno); + + if (arg_standard == "LVDS" && arg_drive != "Undefined") + log_error("for port in line %d when standard is 'LVDS' drive must be 'Undefined'\n", lineno); + if (arg_standard == "LVCMOS" && !boost::ends_with(arg_drive, "mA")) + log_error("for port in line %d when standard is 'LVCMOS' drive current must be in mA\n", lineno); + if ((arg_standard == "SSTL" || arg_standard == "HSTL") && !boost::starts_with(arg_drive, "Cat")) + log_error("for port in line %d when standard is 'SSTL' or 'HSTL' drive current must be in 'CatI' or " + "'CatII'\n", + lineno); + + if (arg_terminationReference == "Floating") { + if (!(arg_differential == "True" && arg_weakTermination == "None")) { + log_error("for floating termination, differential myst be 'True' and weakTermination must be " + "'None' in line %d\n", + lineno); + } + } + std::vector dest = get_cells(arg_iobname); + for (auto c : dest) { + c->params[id_iobname] = arg_iobname; + c->params[id_location] = arg_location; + c->params[id_standard] = arg_standard; + c->params[id_drive] = arg_drive; + c->params[id_slewRate] = arg_slewRate; + c->params[id_inputDelayLine] = arg_inputDelayLine; + c->params[id_outputDelayLine] = arg_outputDelayLine; + c->params[id_inputDelayOn] = std::string((std::stoi(arg_inputDelayLine) != 0) ? "True" : "False"); + c->params[id_outputDelayOn] = std::string((std::stoi(arg_outputDelayLine) != 0) ? "True" : "False"); + c->params[id_differential] = arg_differential; + c->params[id_weakTermination] = arg_weakTermination; + if (!arg_termination.empty()) { + c->params[id_termination] = arg_termination; + c->params[id_terminationReference] = arg_terminationReference; + } + c->params[id_turbo] = arg_turbo; + c->params[id_inputSignalSlope] = arg_inputSignalSlope; + c->params[id_outputCapacity] = arg_outputCapacity; + c->params[id_registered] = arg_registered; + } + if (dest.size() == 0) + log_warning("Pad with name '%s' not found in netlist.\n", arg_iobname.c_str()); + + std::string bank_name = arg_location.substr(0, arg_location.find_first_of('_')); + banks_used.emplace(bank_name); + } break; + case IO_BANKS: { + if (arguments.size() == 1 && arguments[0][0] == '!') { + line_type = IO_GCKS; + continue; + } + if (arguments.size() != 3) + log_error("number of parameters in line %d must be 3\n", lineno); + + if (!boost::starts_with(arguments.at(0), "IOB")) + log_error("wrong bank name '%s' in line %d\n", arguments.at(0).c_str(), lineno); + + const char *voltages[] = {"1.2V", "1.5V", "1.8V", "2.5V", "3.3V"}; + auto it = std::find(std::begin(voltages), std::end(voltages), arguments.at(1)); + if (it == std::end(voltages)) + log_error("unknown voltage level '%s' in line %d\n", arguments.at(1).c_str(), lineno); + + const char *direct_io_voltages[] = {"1.8V", "2.5V", "3.3V"}; + const char *complex_io_voltages[] = {"1.2V", "1.5V", "1.8V"}; + + int bank = std::stoi(arguments.at(0).substr(3)); + switch (bank) { + // direct + case 0: + case 1: + case 6: + case 7: { + auto it = std::find(std::begin(direct_io_voltages), std::end(direct_io_voltages), arguments.at(1)); + if (it == std::end(direct_io_voltages)) + log_error("unsupported voltage level '%s' for bank '%s'\n", arguments.at(1).c_str(), + arguments.at(0).c_str()); + } break; + // complex + default: + auto it = std::find(std::begin(complex_io_voltages), std::end(complex_io_voltages), arguments.at(1)); + if (it == std::end(complex_io_voltages)) + log_error("unsupported voltage level '%s' for bank '%s'\n", arguments.at(1).c_str(), + arguments.at(0).c_str()); + } + bank_voltage[arguments.at(0)] = arguments.at(1); + } break; + case IO_GCKS: { + if (arguments.size() == 1 && arguments[0][0] == '!') { + line_type = IO_ERROR; + continue; + } + if (arguments.size() != 2) + log_error("number of parameters in line %d must be 2\n", lineno); + } break; + default: + log_error("switching to unknown block of data in line %d\n", lineno); + } + } + for (auto &bank_name : banks_used) { + if (bank_voltage.count(bank_name) == 0) { + log_error("IO for bank '%s' defined, but no bank configuration.\n", bank_name.c_str()); + } + } +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/ng-ultra/extra_data.h b/himbaechel/uarch/ng-ultra/extra_data.h new file mode 100644 index 00000000..efd922fe --- /dev/null +++ b/himbaechel/uarch/ng-ultra/extra_data.h @@ -0,0 +1,265 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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. + * + */ + +#ifndef NGULTRA_EXTRA_DATA_H +#define NGULTRA_EXTRA_DATA_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +NPNR_PACKED_STRUCT(struct NGUltraTileInstExtraDataPOD { + int32_t name; + uint8_t lobe; + uint8_t tile_type; + uint16_t dummy2; +}); + +NPNR_PACKED_STRUCT(struct NGUltraPipExtraDataPOD { + int32_t name; + uint16_t type; + uint8_t input; + uint8_t output; +}); + +NPNR_PACKED_STRUCT(struct NGUltraBelExtraDataPOD { int32_t flags; }); + +struct GckConfig +{ + explicit GckConfig(BelId belid) + { + bel = belid; + si1 = IdString(); + si2 = IdString(); + used = false; + } + BelId bel; + IdString si1; + IdString si2; + bool used; +}; + +enum TILETypeZ +{ + BEL_LUT_Z = 0, + BEL_LUT_MAX_Z = 31, + BEL_CY_Z = 32, + BEL_XLUT_Z = BEL_CY_Z + 4, + BEL_RF_Z = BEL_XLUT_Z + 8, + BEL_XRF_Z = BEL_RF_Z + 2, + BEL_FIFO_Z = BEL_XRF_Z + 1, + BEL_XFIFO_Z = BEL_FIFO_Z + 2, + BEL_CDC_Z = BEL_XFIFO_Z + 1, + BEL_XCDC_Z = BEL_CDC_Z + 2 +}; + +enum ClusterPlacement +{ + PLACE_CY_CHAIN = 1024, + PLACE_CY_FE1, + PLACE_CY_FE2, + PLACE_CY_FE3, + PLACE_CY_FE4, + PLACE_XLUT_FE1, + PLACE_XLUT_FE2, + PLACE_XLUT_FE3, + PLACE_XLUT_FE4, + PLACE_XRF_I1, + PLACE_XRF_I2, + PLACE_XRF_I3, + PLACE_XRF_I4, + PLACE_XRF_I5, + PLACE_XRF_I6, + PLACE_XRF_I7, + PLACE_XRF_I8, + PLACE_XRF_I9, + PLACE_XRF_I10, + PLACE_XRF_I11, + PLACE_XRF_I12, + PLACE_XRF_I13, + PLACE_XRF_I14, + PLACE_XRF_I15, + PLACE_XRF_I16, + PLACE_XRF_I17, + PLACE_XRF_I18, + PLACE_XRF_I19, + PLACE_XRF_I20, + PLACE_XRF_I21, + PLACE_XRF_I22, + PLACE_XRF_I23, + PLACE_XRF_I24, + PLACE_XRF_I25, + PLACE_XRF_I26, + PLACE_XRF_I27, + PLACE_XRF_I28, + PLACE_XRF_I29, + PLACE_XRF_I30, + PLACE_XRF_I31, + PLACE_XRF_I32, + PLACE_XRF_I33, + PLACE_XRF_I34, + PLACE_XRF_I35, + PLACE_XRF_I36, + PLACE_XRF_RA1, + PLACE_XRF_RA2, + PLACE_XRF_RA3, + PLACE_XRF_RA4, + PLACE_XRF_RA5, + PLACE_XRF_RA6, + PLACE_XRF_RA7, + PLACE_XRF_RA8, + PLACE_XRF_RA9, + PLACE_XRF_RA10, + PLACE_XRF_WA1, + PLACE_XRF_WA2, + PLACE_XRF_WA3, + PLACE_XRF_WA4, + PLACE_XRF_WA5, + PLACE_XRF_WA6, + PLACE_XRF_WE, + PLACE_XRF_WEA, + PLACE_DSP_CHAIN, + PLACE_CDC_AI1, + PLACE_CDC_AI2, + PLACE_CDC_AI3, + PLACE_CDC_AI4, + PLACE_CDC_AI5, + PLACE_CDC_AI6, + PLACE_CDC_BI1, + PLACE_CDC_BI2, + PLACE_CDC_BI3, + PLACE_CDC_BI4, + PLACE_CDC_BI5, + PLACE_CDC_BI6, + PLACE_CDC_ASRSTI, + PLACE_CDC_ADRSTI, + PLACE_CDC_BSRSTI, + PLACE_CDC_BDRSTI, + PLACE_CDC_CI1, + PLACE_CDC_CI2, + PLACE_CDC_CI3, + PLACE_CDC_CI4, + PLACE_CDC_CI5, + PLACE_CDC_CI6, + PLACE_CDC_DI1, + PLACE_CDC_DI2, + PLACE_CDC_DI3, + PLACE_CDC_DI4, + PLACE_CDC_DI5, + PLACE_CDC_DI6, + PLACE_CDC_CSRSTI, + PLACE_CDC_CDRSTI, + PLACE_CDC_DSRSTI, + PLACE_CDC_DDRSTI, + PLACE_FIFO_I1, + PLACE_FIFO_I2, + PLACE_FIFO_I3, + PLACE_FIFO_I4, + PLACE_FIFO_I5, + PLACE_FIFO_I6, + PLACE_FIFO_I7, + PLACE_FIFO_I8, + PLACE_FIFO_I9, + PLACE_FIFO_I10, + PLACE_FIFO_I11, + PLACE_FIFO_I12, + PLACE_FIFO_I13, + PLACE_FIFO_I14, + PLACE_FIFO_I15, + PLACE_FIFO_I16, + PLACE_FIFO_I17, + PLACE_FIFO_I18, + PLACE_FIFO_I19, + PLACE_FIFO_I20, + PLACE_FIFO_I21, + PLACE_FIFO_I22, + PLACE_FIFO_I23, + PLACE_FIFO_I24, + PLACE_FIFO_I25, + PLACE_FIFO_I26, + PLACE_FIFO_I27, + PLACE_FIFO_I28, + PLACE_FIFO_I29, + PLACE_FIFO_I30, + PLACE_FIFO_I31, + PLACE_FIFO_I32, + PLACE_FIFO_I33, + PLACE_FIFO_I34, + PLACE_FIFO_I35, + PLACE_FIFO_I36, + PLACE_FIFO_RAI1, + PLACE_FIFO_RAI2, + PLACE_FIFO_RAI3, + PLACE_FIFO_RAI4, + PLACE_FIFO_RAI5, + PLACE_FIFO_RAI6, + PLACE_FIFO_RAI7, + PLACE_FIFO_WAI1, + PLACE_FIFO_WAI2, + PLACE_FIFO_WAI3, + PLACE_FIFO_WAI4, + PLACE_FIFO_WAI5, + PLACE_FIFO_WAI6, + PLACE_FIFO_WAI7, + PLACE_FIFO_WE, + PLACE_FIFO_WEA, + PLACE_FIFO_WRSTI1, + PLACE_FIFO_RRSTI1, + PLACE_FIFO_WRSTI2, + PLACE_FIFO_RRSTI2, + PLACE_FIFO_WRSTI3, + PLACE_FIFO_RRSTI3, + PLACE_FIFO_WRSTI4, + PLACE_FIFO_RRSTI4, + PLACE_FIFO_WEQ1, + PLACE_FIFO_REQ1, + PLACE_FIFO_WEQ2, + PLACE_FIFO_REQ2, + PLACE_LUT_CHAIN, + PLACE_DFF_CHAIN, +}; + +enum PipExtra +{ + PIP_EXTRA_CROSSBAR = 1, + PIP_EXTRA_MUX = 2, + PIP_EXTRA_BYPASS = 3, + PIP_EXTRA_LUT_PERMUTATION = 4, + PIP_EXTRA_INTERCONNECT = 5, + PIP_EXTRA_VIRTUAL = 6, +}; + +enum BelExtra +{ + BEL_EXTRA_FE_CSC = 1, + BEL_EXTRA_FE_SCC = 2, +}; + +enum TileTypeExtra +{ + TILE_EXTRA_FABRIC = 0, + TILE_EXTRA_TUBE = 1, + TILE_EXTRA_SOC = 2, + TILE_EXTRA_RING = 3, + TILE_EXTRA_FENCE = 4 +}; + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/himbaechel/uarch/ng-ultra/gen/arch_gen.py b/himbaechel/uarch/ng-ultra/gen/arch_gen.py new file mode 100644 index 00000000..dc5e5aab --- /dev/null +++ b/himbaechel/uarch/ng-ultra/gen/arch_gen.py @@ -0,0 +1,726 @@ +# nextpnr -- Next Generation Place and Route +# +# Copyright (C) 2024 The Project Beyond 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. +# + +import json +from os import path +import sys +import argparse +import gzip + +# Configuration flags +USE_LUT_PERMUTATION = True + +sys.path.append(path.join(path.dirname(__file__), "../../..")) +from himbaechel_dbgen.chip import * + +@dataclass +class TileExtraData(BBAStruct): + name: IdString + lobe: int = 0 + tile_type: int = 0 + + def serialise_lists(self, context: str, bba: BBAWriter): + pass + def serialise(self, context: str, bba: BBAWriter): + bba.u32(self.name.index) + bba.u8(self.lobe) + bba.u8(self.tile_type) + bba.u16(0) # dummy + +@dataclass +class PipExtraData(BBAStruct): + name: IdString = field(default_factory=IdString) + data_type: int = 0 + input: int = 0 + output: int = 0 + + def serialise_lists(self, context: str, bba: BBAWriter): + pass + def serialise(self, context: str, bba: BBAWriter): + bba.u32(self.name.index) + bba.u16(self.data_type) + bba.u8(self.input) + bba.u8(self.output) + +@dataclass +class BelExtraData(BBAStruct): + flags: int = 0 + + def serialise_lists(self, context: str, bba: BBAWriter): + pass + def serialise(self, context: str, bba: BBAWriter): + bba.u32(self.flags) + +PLL_Z = 0 +WFG_C_Z = 1 +WFG_R_Z = 11 + +IOM_Z = 136 + +RAM_Z = 0 +DSP1_Z = 1 +DSP2_Z = 2 + +SOCIF_Z = 0 +SERVICE_Z = 1 + +LUT_Z = 0 +CY_Z = 32 +XLUT_Z = CY_Z + 4 +RF_Z = XLUT_Z + 8 +XRF_Z = RF_Z + 2 +FIFO_Z = XRF_Z + 1 +XFIFO_Z = FIFO_Z + 2 +CDC_Z = XFIFO_Z + 1 +XCDC_Z = CDC_Z + 2 + +PIP_EXTRA_CROSSBAR = 1 +PIP_EXTRA_MUX = 2 +PIP_EXTRA_BYPASS = 3 +PIP_EXTRA_LUT_PERMUTATION = 4 +PIP_EXTRA_INTERCONNECT = 5 +PIP_EXTRA_VIRTUAL = 6 + +BEL_EXTRA_FE_CSC = 1 +BEL_EXTRA_FE_SCC = 2 + +TILE_EXTRA_FABRIC = 0 +TILE_EXTRA_TUBE = 1 +TILE_EXTRA_SOC = 2 +TILE_EXTRA_RING = 3 +TILE_EXTRA_FENCE = 4 + +def bel_z(tile_type, bel_name, bel_type): + if tile_type.startswith("CKG"): + if (bel_type=="PLL"): + return PLL_Z + else: + if bel_name.startswith("WFG.WFG_C"): + return int(bel_name.replace("WFG.WFG_C","")) + WFG_C_Z -1 + else: + return int(bel_name.replace("WFG.WFG_R","")) + WFG_R_Z -1 + elif tile_type.startswith("HSSL"): + if bel_type=="PMA": + return 0 + elif bel_type=="CRX": + return int(bel_name.split(".")[0][7:]) * 2 - 1 + else: + return int(bel_name.split(".")[0][7:]) * 2 + elif tile_type.startswith("IOB") and tile_type in ["IOB0","IOB1","IOB6","IOB7"]: #direct + sp = bel_name.split(".") + if bel_type=="IOP": + return int(sp[0][1:]) * 4 + else: + return (int(sp[0][3:])-1) * 4 + (1 if sp[1][0]=='I' else 2 if sp[1][0]=='O' else 3) + elif tile_type.startswith("IOB"): # complex + sp = bel_name.split(".") + if bel_type=="IOTP": + return (int(sp[0][1:3]) *2 + (1 if sp[0][3]=='N' else 0)) * 4 + elif bel_type=="IOM": + return IOM_Z + else: + return (int(sp[0][3:])-1) * 4 + (1 if sp[1][0]=='I' else 2 if sp[1][0]=='O' else 3) + elif tile_type == "CGB": + if bel_type=="RAM": + return RAM_Z + elif bel_name=="S2.DSP1": + return DSP1_Z + else: + return DSP2_Z + elif tile_type == "SOCBank": + if bel_type=="SOCIF": + return SOCIF_Z + else: + return SERVICE_Z + elif tile_type == "TILE": + sp = bel_name.split(".") + if bel_type=="BEYOND_FE": + return (int(sp[1][2:])-1) % 32 + elif bel_type=="CY": + pos = int(sp[1][2:])-1 + # in S1, S5 and S9 they are ordered other way arround + return ((pos % 4) if pos < 12 else 3 - (pos % 4)) + CY_Z + elif bel_type=="XLUT": + return (int(sp[1][4:])-1) % 8 + XLUT_Z + elif bel_type=="RF": + return (int(sp[1][2:])-1) + RF_Z + elif bel_type=="XRF": + return (int(sp[1][3:])-1) + XRF_Z + elif bel_type=="FIFO": + return (int(sp[1][4:])-1) + FIFO_Z + elif bel_type=="XFIFO": + return (int(sp[1][5:])-1) + XFIFO_Z + elif bel_type=="CDC": + return (int(sp[1][3:])-1) + CDC_Z + elif bel_type=="XCDC": + return (int(sp[1][4:])-1) + XCDC_Z + else: + raise Exception(f"Unknown bel type {bel_type}") + elif tile_type == "TUBE": + sp = bel_name.split(".") + return (int(sp[0][1:])-1)*20 + (int(sp[1][1:])-1) + else: + raise Exception(f"Unknown type {tile_type}") + +if USE_LUT_PERMUTATION: + # Note PI1-PI4 are not real inputs but will appear + # before actual BEL input to enable LUT permutation + lut_to_beyond_fe = { + "I1" : "PI1", + "I2" : "PI2", + "I3" : "PI3", + "I4" : "PI4", + "O" : "LO", + "J" : "LJ", + "K" : "LK", + "D" : "LD", + "X" : "LX", + } +else: + lut_to_beyond_fe = { + "I1" : "I1", + "I2" : "I2", + "I3" : "I3", + "I4" : "I4", + "O" : "LO", + "J" : "LJ", + "K" : "LK", + "D" : "LD", + "X" : "LX", + } + +dff_to_beyond_fe = { + "I" : "DI", + "O" : "DO", + "L" : "L", + "CK" : "CK", + "R" : "R", + "J" : "DJ", + "P" : "DP", + "C" : "DC", + "S" : "DS", + "K" : "DK", +} + +def is_complex(tile_type): + return tile_type not in ["IOB0","IOB1","IOB6","IOB7"] + +def map_to_beyond(tile_type,bel_name,bel_type=None): + if not tile_type in ["TILE"]: + return (bel_name,bel_type) + s = bel_name.split(".") + if tile_type in ["TILE"]: + # BEYOND_FE + if not (s[1].startswith("LUT") or s[1].startswith("DFF")): + return (bel_name,bel_type) + if len(s)==3: + # BEL and PORT + if s[1].startswith("LUT"): + pin = lut_to_beyond_fe[s[2]] + else: + pin = dff_to_beyond_fe[s[2]] + if pin is None: + return (None,bel_type) + return (s[0]+".FE"+s[1][3:]+"."+pin, bel_type if type(bel_type) is int else "BEYOND_FE") + else: + # just BEL + return (s[0]+".FE"+s[1][3:], "BEYOND_FE") + +split_map = ["TILE", "CGB"] + +def split_tilegrid(tilegrid): + new_tilegrid = dict() + for k,v in tilegrid.items(): + if v["type"] not in split_map: + new_tilegrid[k] = dict() + data = dict(v) + data["orig"] = data["type"] + data["x"]=data["x"]*4 + data["y"]=data["y"]*4 + new_tilegrid[k][0] = data + else: + new_tilegrid[k] = dict() + for i in range(4*4): + data = dict(v) + data["orig"] = data["type"] + data["type"] = data["type"]+"_"+str(i) + data["x"]=data["x"]*4 + i // 4 + data["y"]=data["y"]*4 + i % 4 + new_tilegrid[k][i] = data + return new_tilegrid + +def determine_subtile(tile,bel): + if tile=="TILE": + if bel.startswith("SYSTEM"): + s = 3 + elif bel.startswith("RE") or bel.startswith("RS") or bel.startswith("RI"): + s = int(bel.split(".")[0][2:])-1 + elif bel.startswith("S"): + s = int(bel.split(".")[0][1:])+3 + else: + return 0 + return s + elif tile=="CGB": + if bel.startswith("SYSTEM"): + s = 6 + elif bel.startswith("RE"): + s = 8 + elif bel.startswith("RS"): + s = int(bel.split(".")[0][2:]) + elif bel.startswith("RI"): + s = 5 + elif bel.startswith("S1"): + s = 0 + elif bel.startswith("S2.DSP1"): + s = 7 + else: + return 15 + return s + else: + return 0 + +def split_per_bels(bels): + new_bels = dict() + for k,v in bels.items(): + if k not in split_map: + new_bels[k] = dict() + data = dict(v) + new_bels[k][0] = data + else: + new_bels[k] = dict() + for i in range(4*4): + new_bels[k][i] = dict() + for item,bel in v.items(): + (item, bel) = map_to_beyond(k,item,bel) + if item is not None: + num = determine_subtile(k,item) + new_bels[k][num][item] = bel + return new_bels + +def lookup_port_type(t): + if t == "Input": return PinType.INPUT + elif t == "Output": return PinType.OUTPUT + elif t == "Bidir": return PinType.INOUT + else: assert False + +def create_pips(tt, tile_type, muxes, num, args): + file_path = path.join(args.db, args.device, tile_type + ".txt") + if not path.isfile(file_path): + return + with open(file_path) as f: + for item in f: + line = item.strip().split(" ") + name1,_ = map_to_beyond(tile_type,line[0]) + name2,_ = map_to_beyond(tile_type,line[1]) + if name1 is None or name2 is None: + continue + num1 = determine_subtile(tile_type,name1) + num2 = determine_subtile(tile_type,name2) + + if name2 in muxes: + name2 = name2 + "." + line[2] + if num1==num and not tt.has_wire(name1): + tt.create_wire(name=name1, type=tile_type + "_WIRE") + if num2==num and not tt.has_wire(name2): + tt.create_wire(name=name2, type=tile_type + "_WIRE") + timing_class = line[3] + # Only create PIP if both ends are in same subtile + if num1==num and num2==num: + tt.create_pip(name1,name2,timing_class) + +def create_tile_types(ch: Chip, bels, bel_pins, crossbars, interconnects, muxes, args): + for tile_type,items in bels.items(): + for num in items.keys(): + if len(items)==1: + sub_type = tile_type + else: + sub_type = f"{tile_type}_{num}" + tt = ch.create_tile_type(sub_type) + + def lookup_site_wire(canon_name): + if not tt.has_wire(canon_name): + tt.create_wire(name=canon_name, type="BEL_PIN_WIRE") + return canon_name + + # Create BELs inside tile + for name,bel in bels[tile_type][num].items(): + nb = tt.create_bel(name, bel, z=bel_z(tile_type,name,bel)) + # Create wires for each BEL port + for index in bel_pins[bel].keys(): + pin = bel_pins[bel][index] + tt.add_bel_pin(nb, pin["name"], lookup_site_wire(f"{name}."+pin["name"]), lookup_port_type(pin["direction"])) + if (tile_type.startswith("TILE") and bel=="BEYOND_FE"): + flag = 0 + fe_nxd = int(name[name.index(".")+3:]) + if (((fe_nxd-1) & 127) < 64): + if ((fe_nxd-1)&31) < 16: + if (fe_nxd & 1)==1: + flag |= BEL_EXTRA_FE_SCC + else: + if (fe_nxd & 1)==0: + flag |= BEL_EXTRA_FE_SCC + + if fe_nxd in (65, 80, 81, 96, 225,240,241,256, 321,336,337,352): + flag |= BEL_EXTRA_FE_CSC + nb.extra_data = BelExtraData(flag) + + # LUT drawers, LO already connected to LJ + vi = tt.create_pip(f"{name}.LJ",f"{name}.LK","Virtual") + vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0) + vi = tt.create_pip(f"{name}.LJ",f"{name}.LD","Virtual") + vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0) + vi = tt.create_pip(f"{name}.LJ",f"{name}.LX","Virtual") + vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0) + # DFF drawers, DO already connected to DJ + vi = tt.create_pip(f"{name}.DJ",f"{name}.DP","Virtual") + vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0) + vi = tt.create_pip(f"{name}.DJ",f"{name}.DC","Virtual") + vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0) + vi = tt.create_pip(f"{name}.DJ",f"{name}.DS","Virtual") + vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0) + vi = tt.create_pip(f"{name}.DJ",f"{name}.DK","Virtual") + vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0) + # DFF bypass + by = tt.create_pip(f"{name}.DI",f"{name}.DO","BYPASS") + by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,0,0) + # LUT bypass + by = tt.create_pip(f"{name}.I1",f"{name}.LO","BYPASS") + by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,1,0) + elif (tile_type.startswith("TILE") and bel=="XLUT"): + for out in ["G1","G2","G3","G4"]: + vi = tt.create_pip(f"{name}.J",f"{name}.{out}","Virtual") + vi.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_VIRTUAL,0,0) + elif (tile_type.startswith("TILE") and bel=="CY"): + # matrix of each input to each output combination + # crossbar but use mux placeholder for convenience + for inp in ["I1","I2","I3","I4"]: + for out in ["O1","O2","O3","O4"]: + pd = tt.create_pip(f"{name}."+inp,f"{name}."+out,"MATRIX_PIP") + pd.extra_data = PipExtraData(ch.strs.id(f"{name}."+inp),PIP_EXTRA_MUX,int(inp[1:])-1,int(out[1:])-1) + + elif (tile_type.startswith("CKG") and bel=="WFG"): + by = tt.create_pip(f"{name}.ZI",f"{name}.ZO","BYPASS") + by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,0,0) + elif (tile_type.startswith("TUBE") and bel=="GCK"): + # 20 clock signals comming to 20 GCK, SI1 is bypass + by = tt.create_pip(f"{name}.SI1",f"{name}.SO","BYPASS") + by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,0,0) + # there are CMD signals that can be bypassed as well + by = tt.create_pip(f"{name}.CMD",f"{name}.SO","BYPASS") + by.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_BYPASS,1,0) + + + # Add LUT permutation + if USE_LUT_PERMUTATION and tile_type=="TILE": + for name,bel in bels[tile_type][num].items(): + if bel=="BEYOND_FE": + for inp in ["PI1","PI2","PI3","PI4"]: + tt.create_wire(name=f"{name}."+inp, type="LUT_PERMUTATION_WIRE") + for out in ["I1","I2","I3","I4"]: + pd = tt.create_pip(f"{name}."+inp,f"{name}."+out,"LUT_PERMUTATION") + pd.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_LUT_PERMUTATION,int(inp[2:])-1,int(out[1:])-1) + + # Create crossbars as multiple PIPs + for name,xb in crossbars[tile_type][num].items(): + inputs = list() + outputs = list() + for index in bel_pins[xb].keys(): + pin = bel_pins[xb][index] + tt.create_wire(name=f"{name}."+pin["name"], type="CROSSBAR_"+xb+"_INPUT_WIRE" if pin["direction"] == "Input" else "CROSSBAR_"+xb+"_OUTPUT_WIRE") + for index in bel_pins[xb].keys(): + pin = bel_pins[xb][index] + if pin["direction"] == "Input": + inputs.append(pin["name"]) + else: + outputs.append(pin["name"]) + for inp in inputs: + for out in outputs: + pd = tt.create_pip(f"{name}."+inp,f"{name}."+out,"CROSSBAR_"+xb) + pd.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_CROSSBAR,int(inp[1:])-1,int(out[1:])-1) + + # Interconnects are just PIPs with one I and one O + for name,xb in interconnects[tile_type][num].items(): + for index in bel_pins[xb].keys(): + pin = bel_pins[xb][index] + tt.create_wire(name=f"{name}."+pin["name"], type="INTERCONNECT_INPUT" if pin["direction"] == "Input" else "INTERCONNECT_OUTPUT") + inp = f"{name}."+bel_pins[xb]["I"]["name"] + out = f"{name}."+bel_pins[xb]["O"]["name"] + pd = tt.create_pip(inp,out,"INTERCONNECT") + pd.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_INTERCONNECT,0,0) + + # Create MUXes as many to one connections + if tile_type in muxes: + for (name, n) in muxes[tile_type][num].items(): + for i in range(0,n+1): + new_name = name + "." + str(i) + if not tt.has_wire(new_name): + tt.create_wire(name=new_name, type="MUX_WIRE") + pd = tt.create_pip(new_name,name,"MUX_PIP") + pd.extra_data = PipExtraData(ch.strs.id(name),PIP_EXTRA_MUX,i,0) + + m = muxes[tile_type] if tile_type in muxes else dict() + mux = muxes[tile_type][num] if num in m else dict() + create_pips(tt, tile_type, mux, num, args) + +def create_null(ch: Chip): + tt = ch.create_tile_type("NULL") + +def set_timings(ch, pip_timings, bel_timings): + speed = "DEFAULT" + tmg = ch.set_speed_grades([speed]) + for k,v in pip_timings.items(): + tmg.set_pip_class(grade=speed, name=k, delay=TimingValue(v[0])) + tmg.set_node_class(grade=speed, name=k, delay=TimingValue(v[0])) + tmg.set_pip_class(grade=speed, name="INTERCONNECT", delay=TimingValue(0)) + tmg.set_pip_class(grade=speed, name="MUX_PIP", delay=TimingValue(0)) + tmg.set_pip_class(grade=speed, name="MATRIX_PIP", delay=TimingValue(0)) + tmg.set_pip_class(grade=speed, name="LUT_PERMUTATION", delay=TimingValue(0)) + tmg.set_pip_class(grade=speed, name="BYPASS", delay=TimingValue(142)) + for k,g in bel_timings.items(): + primitive = ch.timing.add_cell_variant(speed, k) + for t,v in g.items(): + if t=="IOPath": + for from_port,values in v.items(): + for to_port,data in values.items(): + primitive.add_comb_arc(from_port, to_port, TimingValue(data[0], data[1])) + elif t=="SetupHold": + for from_port,values in v.items(): + for to_port,data in values.items(): + primitive.add_setup_hold(from_port, to_port, ClockEdge.RISING, TimingValue(data[0]), TimingValue(data[1])) + elif t=="ClockOut": + for from_port,values in v.items(): + for to_port,data in values.items(): + primitive.add_clock_out(from_port, to_port, ClockEdge.RISING, TimingValue(data[0],data[1])) + +def get_pos(tilegrid,name,bel): + tile = tilegrid[name][0]["orig"] + num = determine_subtile(tile,bel) + item = tilegrid[name][num] + x = item["x"] + y = item["y"] + return (tile,num,x,y) + + +global_connections = dict() +def load_globals(args): + print("Load global connections...") + with gzip.open(path.join(args.db, args.device, "GLOBAL.txt.gz"),"rt") as f: + for item in f: + line = item.strip().split(" ") + tile_name = line[0].split(":")[0] + if tile_name not in global_connections: + global_connections[tile_name] = dict() + if line[0] not in global_connections[tile_name]: + global_connections[tile_name][line[0]] = list() + global_connections[tile_name][line[0]].append(line) + +def create_nodes(ch, tile_name, tilegrid, muxes, pip_timings): + if tile_name not in global_connections: + return + connections = global_connections[tile_name] + for key,val in connections.items(): + name1 = key.split(":") + t1,_,x1,y1 = get_pos(tilegrid,name1[0],name1[1]) + name1[1],_ = map_to_beyond(t1,name1[1]) + if name1[1] is None: + continue + node = [NodeWire(x1, y1, name1[1])] + timing = None + timing_val = -1 + for v in val: + name2 = v[1].split(":") + name2[1],_ = map_to_beyond(tilegrid[name2[0]][0]["orig"],name2[1]) + t2,num,x2,y2 = get_pos(tilegrid,name2[0],name2[1]) + if name2[1] is None: + continue + if pip_timings[v[3]][0] > timing_val: + timing_val = pip_timings[v[3]][0] + timing = v[3] + if t2 in muxes and num in muxes[t2] and name2[1] in muxes[t2][num]: + node.append(NodeWire(x2, y2, name2[1]+"."+v[2])) + else: + node.append(NodeWire(x2, y2, name2[1])) + + ch.add_node(node,timing_class=timing) + + +subtile_connections = dict() +def create_nodes_subtiles(ch, tilegrid, name, tile_type, muxes, pip_timings, args): + if tile_type not in subtile_connections: + subtile_connections[tile_type] = dict() + + file_path = path.join(args.db, args.device, tile_type + ".txt") + if not path.isfile(file_path): + return + with open(file_path) as f: + for item in f: + line = item.strip().split(" ") + name1 = line[0] + name2 = line[1] + name1,_ = map_to_beyond(tile_type,line[0]) + name2,_ = map_to_beyond(tile_type,line[1]) + if name1 is None or name2 is None: + continue + num1 = determine_subtile(tile_type,name1) + num2 = determine_subtile(tile_type,name2) + # Only create WIRE if ends are NOT in same subtile + if num1!=num2: + if name1 not in subtile_connections[tile_type] : + subtile_connections[tile_type][name1] = list() + subtile_connections[tile_type][name1].append([name1, name2, line[2], line[3]]) + + for name1,val in subtile_connections[tile_type].items(): + _,_,x1,y1 = get_pos(tilegrid,name,name1) + node = [NodeWire(x1, y1, name1)] + timing_val = -1 + timing = None + for v in val: + name2 = v[1] + t2,num,x2,y2 = get_pos(tilegrid,name,name2) + if pip_timings[v[3]][0] > timing_val: + timing_val = pip_timings[v[3]][0] + timing = v[3] + if t2 in muxes and num in muxes[t2] and name2 in muxes[t2][num]: + node.append(NodeWire(x2, y2, name2+"."+v[2])) + else: + node.append(NodeWire(x2, y2, name2)) + + ch.add_node(node,timing_class=timing) + +def import_package(ch, package, bels, tilegrid): + pkg = ch.create_package(package) + for name, data in tilegrid.items(): + for key, item in data.items(): + ty = item["orig"] + x = item["x"] + y = item["y"] + if ty.startswith("IOB"): + for bel,ty in bels[ty][key].items(): + # Support for native primitives + if ty =="IOTP": + pin = name+"_"+bel[:4] + elif ty =="IOP": + pin = name+"_"+bel[:3] + else: + continue + pkg.create_pad(pin, f"X{x}Y{y}", bel, "", int(name[3:])) + +def main(): + xlbase = path.join(path.dirname(path.realpath(__file__)), "..") + + parser = argparse.ArgumentParser() + parser.add_argument("--db", help="Project Beyond device database path (e.g. ../prjbeyond/database)", type=str, required=True) + parser.add_argument("--device", help="name of device to export", type=str, required=True) + parser.add_argument("--constids", help="name of nextpnr constids file to read", type=str, default=path.join(xlbase, "constids.inc")) + parser.add_argument("--bba", help="bba file to write", type=str, required=True) + args = parser.parse_args() + + with open(path.join(args.db, "devices.json")) as f: + devices = json.load(f) + + width = (devices["families"][args.device]["max_col"] + 1) * 4 + height = (devices["families"][args.device]["max_row"] + 1) * 4 + packages = devices["families"][args.device]["packages"] + + ch = Chip("ng-ultra",args.device, width, height) + ch.strs.read_constids(path.join(path.dirname(__file__), "..", "constids.inc")) + + # Data that is depending of location + with open(path.join(args.db, args.device, "tilegrid.json")) as f: + tilegrid = split_tilegrid(json.load(f)) + + with open(path.join(args.db, args.device, "bels.json")) as f: + bels = split_per_bels(json.load(f)) + + with open(path.join(args.db, args.device, "crossbars.json")) as f: + crossbars = split_per_bels(json.load(f)) + + with open(path.join(args.db, args.device, "interconnects.json")) as f: + interconnects = split_per_bels(json.load(f)) + + with open(path.join(args.db, args.device, "muxes.json")) as f: + muxes = split_per_bels(json.load(f)) + + # Data that is not related to position + with open(path.join(args.db, args.device, "bel_pins.json")) as f: + bel_pins = json.load(f) + bel_pins["IOP"]["IO"] = { "direction": "Bidir", "name": "IO" } + bel_pins["IOTP"]["IO"] = { "direction": "Bidir", "name": "IO" } + + with open(path.join(args.db, args.device, "pip_timings.json")) as f: + pip_timings = json.load(f) + + with open(path.join(args.db, args.device, "bel_timings.json")) as f: + bel_timings = json.load(f) + + create_tile_types(ch, bels, bel_pins, crossbars, interconnects, muxes, args) + create_null(ch) + set_timings(ch, pip_timings, bel_timings) + + for x in range(width): + for y in range(height): + ch.set_tile_type(x,y,"NULL") + + load_globals(args) + for name, data in tilegrid.items(): + for item in data.values(): + ti = ch.set_tile_type(item["x"],item["y"],item["type"]) + lobe = 0 + if item["orig"] in ["TILE","CGB"]: + tmp = name.replace("TILE[","").replace("CGB[","").replace("]","") + x,y = tmp.split("x") + lobe = ((int(y)-1) // 12)*2 + (1 if int(x)>46 else 0) + 1 + elif item["orig"].startswith("IOB") or item["orig"].startswith("HSSL"): + match item["orig"]: + case "IOB0" | "IOB1": + lobe = 5 + case "IOB6" | "IOB7": + lobe = 6 + case "IOB8" | "IOB9" | "IOB10": + lobe = 2 + case "IOB11" | "IOB12" | "IOB13": + lobe = 1 + case "IOB2" | "IOB3" | "HSSL0" | "HSSL1" | "HSSL2" | "HSSL3": + lobe = 7 + case "IOB4" | "IOB5" | "HSSL4" | "HSSL5" | "HSSL6" | "HSSL7": + lobe = 8 + tile_type = 0 + if item["orig"] in ["TILE","CGB","MESH"]: + tile_type = TILE_EXTRA_FABRIC + elif item["orig"] in ["TUBE"]: + tile_type = TILE_EXTRA_TUBE + elif item["orig"] in ["SOCBank"]: + tile_type = TILE_EXTRA_SOC + elif item["orig"].startswith("IOB") or item["orig"].startswith("HSSL") or item["orig"].startswith("CKG"): + tile_type = TILE_EXTRA_RING + elif item["orig"].startswith("FENCE"): + tile_type = TILE_EXTRA_FENCE + + ti.extra_data = TileExtraData(ch.strs.id(name),lobe, tile_type) + + for name, data in tilegrid.items(): + print(f"Generate nodes for {name}...") + create_nodes_subtiles(ch, tilegrid, name, data[0]["orig"], muxes, pip_timings, args) + create_nodes(ch, name, tilegrid, muxes, pip_timings) + + for package in packages: + import_package(ch, package, bels, tilegrid) + + ch.write_bba(args.bba) + +if __name__ == '__main__': + main() diff --git a/himbaechel/uarch/ng-ultra/location_map.cc b/himbaechel/uarch/ng-ultra/location_map.cc new file mode 100644 index 00000000..c04c8888 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/location_map.cc @@ -0,0 +1,569 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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 "location_map.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace { +/* clang-format off */ +const Loc ng_ultra_place_cy_map[24] = { + { 0, 1, 0}, // S1 0 -> S2 0 CY24->CY1 + { 0, 0, -1}, // S1 1 -> S1 0 CY23->CY24 + { 0, 0, -1}, // S1 2 -> S1 1 CY22->CY23 + { 0, 0, -1}, // S1 3 -> S1 2 CY21->CY22 + + {-1, 0, +3}, // S5 0 -> S1 1 CY20->CY21 + { 0, 0, -1}, // S5 1 -> S5 0 CY19->CY20 + { 0, 0, -1}, // S5 2 -> S5 1 CY18->CY19 + { 0, 0, -1}, // S5 3 -> S5 2 CY17->CY18 + + {-1, 0, +3}, // S9 0 -> S5 1 CY16->CY17 + { 0, 0, -1}, // S9 1 -> S9 0 CY15->CY16 + { 0, 0, -1}, // S9 2 -> S9 1 CY14->CY15 + { 0, 0, -1}, // S9 3 -> S9 2 CY13->CY14 + + { 0, 0, +1}, // S2 0 -> S2 1 CY1->CY2 + { 0, 0, +1}, // S2 1 -> S2 2 CY2->CY3 + { 0, 0, +1}, // S2 2 -> S2 3 CY3->CY4 + { 1, 0, -3}, // S2 3 -> S6 0 CY4->CY5 + + { 0, 0, +1}, // S6 0 -> S6 1 CY5->CY6 + { 0, 0, +1}, // S6 1 -> S6 2 CY6->CY7 + { 0, 0, +1}, // S6 2 -> S6 3 CY7->CY8 + { 1, 0, -3}, // S6 3 -> S10 0 CY8->CY9 + + { 0, 0, +1}, // S10 0 -> S10 1 CY9->CY10 + { 0, 0, +1}, // S10 1 -> S10 2 CY10->CY11 + { 0, 0, +1}, // S10 2 -> S10 3 CY11->CY12 + { 0, -1, 0}, // S10 3 -> S9 3 CY12->CY13 +}; + +const Loc ng_ultra_place_xrf[] = { + {-1, 0, 1}, // I/O1 + {-1, 0, 2}, // I/O2 + {-1, 0, 5}, // I/O3 + {-1, 0, 6}, // I/O4 + {-1, 0, 7}, // I/O5 + {-1, 0, 9}, // I/O6 + {-1, 0, 10}, // I/O7 + {-1, 0, 13}, // I/O8 + {-1, 0, 14}, // I/O9 + {-1, 0, 15}, // I/O10 + {-1, 0, 16}, // I/O11 + {-1, 0, 17}, // I/O12 + {-1, 0, 18}, // I/O13 + {-1, 0, 21}, // I/O14 + {-1, 0, 24}, // I/O15 + {-1, 0, 25}, // I/O16 + {-1, 0, 26}, // I/O17 + {-1, 0, 29}, // I/O18 + + {+1, 0, 1}, // I/O19 + {+1, 0, 2}, // I/O20 + {+1, 0, 5}, // I/O21 + {+1, 0, 6}, // I/O22 + {+1, 0, 7}, // I/O23 + {+1, 0, 9}, // I/O24 + {+1, 0, 10}, // I/O25 + {+1, 0, 13}, // I/O26 + {+1, 0, 14}, // I/O27 + {+1, 0, 15}, // I/O28 + {+1, 0, 16}, // I/O29 + {+1, 0, 17}, // I/O30 + {+1, 0, 18}, // I/O31 + {+1, 0, 21}, // I/O32 + {+1, 0, 24}, // I/O33 + {+1, 0, 25}, // I/O34 + {+1, 0, 26}, // I/O35 + {+1, 0, 29}, // I/O36 + + {-1, 0, 4}, // RA1 + {-1, 0, 12}, // RA2 + {-1, 0, 20}, // RA3 + {-1, 0, 27}, // RA4 + {-1, 0, 31}, // RA5 + + {+1, 0, 4}, // RA6 + {+1, 0, 12}, // RA7 + {+1, 0, 20}, // RA8 + {+1, 0, 27}, // RA9 + {+1, 0, 31}, // RA10 + + {-1, 0, 3}, // WA1 + {-1, 0, 11}, // WA2 + {-1, 0, 19}, // WA3 + {-1, 0, 23}, // WA4 + {-1, 0, 28}, // WA5 + + {+1, 0, 3}, // WA6 + + {-1, 0, 0}, // WE + {-1, 0, 8}, // WEA + +}; + +const Loc ng_ultra_place_cdc1[] = { + {+1, 0, 1}, // AI1 + {+1, 0, 2}, // AI2 + {+1, 0, 9}, // AI3 + {+1, 0, 17}, // AI4 + {+1, 0, 18}, // AI5 + {+1, 0, 25}, // AI6 + + {+1, 0, 3}, // BI1 + {+1, 0, 10}, // BI2 + {+1, 0, 11}, // BI3 + {+1, 0, 19}, // BI4 + {+1, 0, 26}, // BI5 + {+1, 0, 27}, // BI6 + + { 0, 0, 22}, // ASRSTI + { 0, 0, 30}, // ADRSTI + {+1, 0, 24}, // BSRSTI + {+1, 0, 8}, // BDRSTI +}; + +const Loc ng_ultra_place_cdc2[] = { + {-1, 0, 4}, // AI1 + {-1, 0, 5}, // AI2 + {-1, 0, 12}, // AI3 + {-1, 0, 20}, // AI4 + {-1, 0, 21}, // AI5 + {-1, 0, 28}, // AI6 + + {-1, 0, 6}, // BI1 + {-1, 0, 13}, // BI2 + {-1, 0, 14}, // BI3 + {-1, 0, 22}, // BI4 + {-1, 0, 29}, // BI5 + {-1, 0, 30}, // BI6 + + { 0, 0, 22}, // ASRSTI + { 0, 0, 30}, // ADRSTI + {-1, 0, 23}, // BSRSTI + {-1, 0, 7}, // BDRSTI +}; + +const Loc ng_ultra_place_xcdc[] = { + { 0, 0, 1}, // AI1 + { 0, 0, 2}, // AI2 + { 0, 0, 9}, // AI3 + { 0, 0, 17}, // AI4 + { 0, 0, 18}, // AI5 + { 0, 0, 25}, // AI6 + + { 0, 0, 4}, // BI1 + { 0, 0, 5}, // BI2 + { 0, 0, 12}, // BI3 + { 0, 0, 20}, // BI4 + { 0, 0, 21}, // BI5 + { 0, 0, 28}, // BI6 + + {-1, 0, 22}, // ASRSTI + {-1, 0, 30}, // ADRSTI + {+1, 0, 22}, // BSRSTI + {+1, 0, 30}, // BDRSTI + + { 0, 0, 3}, // CI1 + { 0, 0, 10}, // CI2 + { 0, 0, 11}, // CI3 + { 0, 0, 19}, // CI4 + { 0, 0, 26}, // CI5 + { 0, 0, 27}, // CI6 + + { 0, 0, 6}, // DI1 + { 0, 0, 13}, // DI2 + { 0, 0, 14}, // DI3 + { 0, 0, 22}, // DI4 + { 0, 0, 29}, // DI5 + { 0, 0, 30}, // DI6 + + { 0, 0, 24}, // CSRSTI + { 0, 0, 8}, // CDRSTI + { 0, 0, 23}, // DSRSTI + { 0, 0, 7}, // DDRSTI +}; + +const Loc ng_ultra_place_fifo1[] = { + {-1, 0, 1}, // I/O1 + {-1, 0, 2}, // I/O2 + {-1, 0, 5}, // I/O3 + {-1, 0, 6}, // I/O4 + {-1, 0, 7}, // I/O5 + {-1, 0, 9}, // I/O6 + {-1, 0, 10}, // I/O7 + {-1, 0, 13}, // I/O8 + {-1, 0, 14}, // I/O9 + {-1, 0, 15}, // I/O10 + {-1, 0, 16}, // I/O11 + {-1, 0, 17}, // I/O12 + {-1, 0, 18}, // I/O13 + {-1, 0, 21}, // I/O14 + {-1, 0, 24}, // I/O15 + {-1, 0, 25}, // I/O16 + {-1, 0, 26}, // I/O17 + {-1, 0, 29}, // I/O18 + + { 0, 0, 0}, // I/O19 + { 0, 0, 0}, // I/O20 + { 0, 0, 0}, // I/O21 + { 0, 0, 0}, // I/O22 + { 0, 0, 0}, // I/O23 + { 0, 0, 0}, // I/O24 + { 0, 0, 0}, // I/O25 + { 0, 0, 0}, // I/O26 + { 0, 0, 0}, // I/O27 + { 0, 0, 0}, // I/O28 + { 0, 0, 0}, // I/O29 + { 0, 0, 0}, // I/O30 + { 0, 0, 0}, // I/O31 + { 0, 0, 0}, // I/O32 + { 0, 0, 0}, // I/O33 + { 0, 0, 0}, // I/O34 + { 0, 0, 0}, // I/O35 + { 0, 0, 0}, // I/O36 + + { 0, 0, 3}, // RAI1/RAO1 + { 0, 0, 10}, // RAI2/RAO2 + { 0, 0, 11}, // RAI3/RAO3 + { 0, 0, 19}, // RAI4/RAO4 + { 0, 0, 26}, // RAI5/RAO5 + { 0, 0, 27}, // RAI6/RAO6 + { 0, 0, 0}, // RAI7/RAO7 + + { 0, 0, 1}, // WAI1/WAO1 + { 0, 0, 2}, // WAI2/WAO2 + { 0, 0, 9}, // WAI3/WAO3 + { 0, 0, 17}, // WAI4/WAO4 + { 0, 0, 18}, // WAI5/WAO5 + { 0, 0, 25}, // WAI6/WAO6 + { 0, 0, 0}, // WAI7/WAO7 + + {-1, 0, 0}, // WE + {-1, 0, 8}, // WEA + + {-1, 0, 22}, // WRSTI1/WRSTO + {-1, 0, 30}, // RRSTI1/RRSTO + { 0, 0, 8}, // WRSTI2 + { 0, 0, 24}, // RRSTI2 + { 0, 0, 0}, // WRSTI3/WRSTO + { 0, 0, 0}, // RRSTI3/RRSTO + { 0, 0, 0}, // WRSTI4 + { 0, 0, 0}, // RRSTI4 + + {-1, 0, 3}, // WEQ + {-1, 0, 4}, // REQ + // {-1, 0, 11}, WEQ + // {-1, 0, 12}, REQ + // {-1, 0, 19}, WEQ + // {-1, 0, 20}, REQ + // {-1, 0, 27}, WEQ + // {-1, 0, 28}, REQ + { 0, 0, 0}, // WEQ2 + { 0, 0, 0}, // REQ2 +}; + +const Loc ng_ultra_place_fifo2[] = { + {+1, 0, 1}, // I/O1 + {+1, 0, 2}, // I/O2 + {+1, 0, 5}, // I/O3 + {+1, 0, 6}, // I/O4 + {+1, 0, 7}, // I/O5 + {+1, 0, 9}, // I/O6 + {+1, 0, 10}, // I/O7 + {+1, 0, 13}, // I/O8 + {+1, 0, 14}, // I/O9 + {+1, 0, 15}, // I/O10 + {+1, 0, 16}, // I/O11 + {+1, 0, 17}, // I/O12 + {+1, 0, 18}, // I/O13 + {+1, 0, 21}, // I/O14 + {+1, 0, 24}, // I/O15 + {+1, 0, 25}, // I/O16 + {+1, 0, 26}, // I/O17 + {+1, 0, 29}, // I/O18 + + { 0, 0, 0}, // I/O19 + { 0, 0, 0}, // I/O20 + { 0, 0, 0}, // I/O21 + { 0, 0, 0}, // I/O22 + { 0, 0, 0}, // I/O23 + { 0, 0, 0}, // I/O24 + { 0, 0, 0}, // I/O25 + { 0, 0, 0}, // I/O26 + { 0, 0, 0}, // I/O27 + { 0, 0, 0}, // I/O28 + { 0, 0, 0}, // I/O29 + { 0, 0, 0}, // I/O30 + { 0, 0, 0}, // I/O31 + { 0, 0, 0}, // I/O32 + { 0, 0, 0}, // I/O33 + { 0, 0, 0}, // I/O34 + { 0, 0, 0}, // I/O35 + { 0, 0, 0}, // I/O36 + + { 0, 0, 6}, // RAI1/RAO1 + { 0, 0, 13}, // RAI2/RAO2 + { 0, 0, 14}, // RAI3/RAO3 + { 0, 0, 22}, // RAI4/RAO4 + { 0, 0, 29}, // RAI5/RAO5 + { 0, 0, 30}, // RAI6/RAO6 + { 0, 0, 0}, // RAI7/RAO7 + + { 0, 0, 4}, // WAI1/WAO1 + { 0, 0, 5}, // WAI2/WAO2 + { 0, 0, 12}, // WAI3/WAO3 + { 0, 0, 20}, // WAI4/WAO4 + { 0, 0, 21}, // WAI5/WAO5 + { 0, 0, 28}, // WAI6/WAO6 + { 0, 0, 0}, // WAI7/WAO7 + + {+1, 0, 0}, // WE + {+1, 0, 8}, // WEA + + {+1, 0, 22}, // WRSTI1/WRSTO + {+1, 0, 30}, // RRSTI1/RRSTO + { 0, 0, 7}, // WRSTI2 + { 0, 0, 23}, // RRSTI2 + { 0, 0, 0}, // WRSTI3/WRSTO + { 0, 0, 0}, // RRSTI3/RRSTO + { 0, 0, 0}, // WRSTI4 + { 0, 0, 0}, // RRSTI4 + + {+1, 0, 3}, // WEQ + {+1, 0, 4}, // REQ + // {+1, 0, 11}, WEQ + // {+1, 0, 12}, REQ + // {+1, 0, 19}, WEQ + // {+1, 0, 20}, REQ + // {+1, 0, 27}, WEQ + // {+1, 0, 28}, REQ + { 0, 0, 0}, // WEQ2 + { 0, 0, 0}, // REQ2 +}; + +const Loc ng_ultra_place_xfifo[] = { + {-1, 0, 1}, // I/O1 + {-1, 0, 2}, // I/O2 + {-1, 0, 5}, // I/O3 + {-1, 0, 6}, // I/O4 + {-1, 0, 7}, // I/O5 + {-1, 0, 9}, // I/O6 + {-1, 0, 10}, // I/O7 + {-1, 0, 13}, // I/O8 + {-1, 0, 14}, // I/O9 + {-1, 0, 15}, // I/O10 + {-1, 0, 16}, // I/O11 + {-1, 0, 17}, // I/O12 + {-1, 0, 18}, // I/O13 + {-1, 0, 21}, // I/O14 + {-1, 0, 24}, // I/O15 + {-1, 0, 25}, // I/O16 + {-1, 0, 26}, // I/O17 + {-1, 0, 29}, // I/O18 + {+1, 0, 1}, // I/O19 + {+1, 0, 2}, // I/O20 + {+1, 0, 5}, // I/O21 + {+1, 0, 6}, // I/O22 + {+1, 0, 7}, // I/O23 + {+1, 0, 9}, // I/O24 + {+1, 0, 10}, // I/O25 + {+1, 0, 13}, // I/O26 + {+1, 0, 14}, // I/O27 + {+1, 0, 15}, // I/O28 + {+1, 0, 16}, // I/O29 + {+1, 0, 17}, // I/O30 + {+1, 0, 18}, // I/O31 + {+1, 0, 21}, // I/O32 + {+1, 0, 24}, // I/O33 + {+1, 0, 25}, // I/O34 + {+1, 0, 26}, // I/O35 + {+1, 0, 29}, // I/O36 + + { 0, 0, 3}, // RAI1/RAO1 + { 0, 0, 10}, // RAI2/RAO2 + { 0, 0, 11}, // RAI3/RAO3 + { 0, 0, 19}, // RAI4/RAO4 + { 0, 0, 26}, // RAI5/RAO5 + { 0, 0, 27}, // RAI6/RAO6 + { 0, 0, 6}, // RAI7/RAO7 + + { 0, 0, 1}, // WAI1/WAO1 + { 0, 0, 2}, // WAI2/WAO2 + { 0, 0, 9}, // WAI3/WAO3 + { 0, 0, 17}, // WAI4/WAO4 + { 0, 0, 18}, // WAI5/WAO5 + { 0, 0, 25}, // WAI6/WAO6 + { 0, 0, 4}, // WAI7/WAO7 + + {-1, 0, 0}, // WE + {-1, 0, 8}, // WEA + + {-1, 0, 22}, // WRSTI1/WRSTO + {-1, 0, 30}, // RRSTI1/RRSTO + { 0, 0, 8}, // WRSTI2 + { 0, 0, 24}, // RRSTI2 + {+1, 0, 22}, // WRSTI3/WRSTO + {+1, 0, 30}, // RRSTI3/RRSTO + { 0, 0, 7}, // WRSTI4 + { 0, 0, 23}, // RRSTI4 + + {-1, 0, 3}, // WEQ1 + {-1, 0, 4}, // REQ1 + // {-1, 0, 11}, WEQ1 + // {-1, 0, 12}, REQ1 + // {-1, 0, 19}, WEQ1 + // {-1, 0, 20}, REQ1 + // {-1, 0, 27}, WEQ1 + // {-1, 0, 28}, REQ1 + {+1, 0, 3}, // WEQ2 + {+1, 0, 4}, // REQ2 + // {+1, 0, 11}, WEQ2 + // {+1, 0, 12}, REQ2 + // {+1, 0, 19}, WEQ2 + // {+1, 0, 20}, REQ2 + // {+1, 0, 27}, WEQ2 + // {+1, 0, 28}, REQ2 +}; +/* clang-format on */ + +}; // namespace + +namespace ng_ultra { + +Loc getNextLocInDSPChain(const NgUltraImpl *impl, Loc loc) +{ + BelId bel = impl->ctx->getBelByLocation(loc); + if (impl->dsp_cascade.count(bel) == 0) { + loc.z = -1; // End of chain + return loc; + } + BelId dsp = impl->dsp_cascade.at(bel); + return impl->ctx->getBelLocation(dsp); +} + +Loc getNextLocInCYChain(Loc loc) +{ + int section = (loc.x % 4 - 1 + 3 * (loc.y % 4)) * 4 + loc.z - BEL_CY_Z; + Loc result = ng_ultra_place_cy_map[section]; + result.x += loc.x; + result.y += loc.y; + result.z += loc.z; + return result; +} + +Loc getNextLocInLUTChain(Loc loc) +{ + Loc result = loc; + result.x = loc.x; + result.y = loc.y; + result.z = (loc.z + 8) % 32; // BEL_LUT_Z is 0 + return result; +} + +Loc getNextLocInDFFChain(Loc loc) +{ + Loc result = loc; + if (loc.z == 31) { + if ((loc.x & 3) == 3) { + result.z = -1; // End of chain + return result; + } + result.z = 0; + result.x++; + return result; + } + int z = loc.z + 8; + if (z > 31) + z++; + result.z = z % 32; // BEL_LUT_Z is 0 + return result; +} + +Loc getCYFE(Loc root, int pos) +{ + int p[] = {2 - 1, 25 - 1, 10 - 1, 17 - 1}; + int cy = root.z - BEL_CY_Z; + Loc result; + result.x = root.x; + result.y = root.y; + result.z = p[pos] + cy * 2; + return result; +} + +Loc getXLUTFE(Loc root, int pos) +{ + Loc result; + result.x = root.x; + result.y = root.y; + result.z = root.z - BEL_XLUT_Z + 8 * pos; + return result; +} + +Loc getXRFFE(Loc root, int pos) +{ + Loc result = ng_ultra_place_xrf[pos]; + if (root.z == BEL_XRF_Z) { + // XRF1 + result.x += root.x; + } else { + // RF1 or RF2 + result.x = root.x + ((root.z == BEL_RF_Z) ? -1 : +1); + } + result.y = root.y; + return result; +} + +Loc getCDCFE(Loc root, int pos) +{ + Loc result; + if (root.z == BEL_CDC_Z) { + result = ng_ultra_place_cdc1[pos]; + } else if (root.z == BEL_CDC_Z + 1) { + result = ng_ultra_place_cdc2[pos]; + } else if (root.z == BEL_XCDC_Z) { + result = ng_ultra_place_xcdc[pos]; + } else { + log_error("Trying to place CDC on wrong location.\n"); + } + result.x += root.x; + result.y = root.y; + return result; +} + +Loc getFIFOFE(Loc root, int pos) +{ + Loc result; + if (root.z == BEL_FIFO_Z) { + result = ng_ultra_place_fifo1[pos]; + } else if (root.z == BEL_FIFO_Z + 1) { + result = ng_ultra_place_fifo2[pos]; + } else if (root.z == BEL_XFIFO_Z) { + result = ng_ultra_place_xfifo[pos]; + } else { + log_error("Trying to place CDC on wrong location.\n"); + } + result.x += root.x; + result.y = root.y; + return result; +} + +}; // namespace ng_ultra +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/ng-ultra/location_map.h b/himbaechel/uarch/ng-ultra/location_map.h new file mode 100644 index 00000000..f856e729 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/location_map.h @@ -0,0 +1,43 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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 "nextpnr.h" +#include "ng_ultra.h" + +#ifndef NG_ULTRA_LOCATION_MAP_H +#define NG_ULTRA_LOCATION_MAP_H + +NEXTPNR_NAMESPACE_BEGIN + +namespace ng_ultra { + +Loc getNextLocInDSPChain(const NgUltraImpl *impl, Loc loc); +Loc getNextLocInCYChain(Loc loc); +Loc getNextLocInLUTChain(Loc loc); +Loc getNextLocInDFFChain(Loc loc); +Loc getCYFE(Loc root, int pos); +Loc getXLUTFE(Loc root, int pos); +Loc getXRFFE(Loc root, int pos); +Loc getCDCFE(Loc root, int pos); +Loc getFIFOFE(Loc root, int pos); + +}; // namespace ng_ultra + +NEXTPNR_NAMESPACE_END +#endif diff --git a/himbaechel/uarch/ng-ultra/ng_ultra.cc b/himbaechel/uarch/ng-ultra/ng_ultra.cc new file mode 100644 index 00000000..f5932e71 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/ng_ultra.cc @@ -0,0 +1,1069 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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 +#include +#include + +#include "design_utils.h" +#include "extra_data.h" +#include "himbaechel_api.h" +#include "log.h" +#include "nextpnr.h" +#include "placer_heap.h" +#include "util.h" + +#include "himbaechel_helpers.h" + +#include "location_map.h" +#include "ng_ultra.h" + +#define GEN_INIT_CONSTIDS +#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc" +#include "himbaechel_constids.h" +using namespace NEXTPNR_NAMESPACE_PREFIX ng_ultra; + +NEXTPNR_NAMESPACE_BEGIN + +NgUltraImpl::~NgUltraImpl() {}; + +void NgUltraImpl::init_database(Arch *arch) +{ + init_uarch_constids(arch); + arch->load_chipdb("ng-ultra/chipdb-ng-ultra.bin"); + arch->set_package("FF-1760"); + arch->set_speed_grade("DEFAULT"); +} + +void NgUltraImpl::init(Context *ctx) +{ + HimbaechelAPI::init(ctx); + for (int i = 1; i <= 8; i++) + for (int j = 0; j < 20; j++) + gck_per_lobe[i].push_back(GckConfig(BelId())); + for (auto bel : ctx->getBels()) { + if (ctx->getBelType(bel) == id_IOM) { + std::set ckg; + IdString bank = tile_name_id(bel.tile); + iom_bels.emplace(bank, bel); + WireId belpin = ctx->getBelPinWire(bel, id_CKO1); + for (auto dh : ctx->getPipsDownhill(belpin)) { + WireId pip_dst = ctx->getPipDstWire(dh); + for (const auto &item : ctx->getWireBelPins(pip_dst)) { + if (boost::contains(ctx->nameOfBel(item.bel), "WFG_C")) { + unused_wfg[item.bel] = tile_name_id(item.bel.tile); + } else if (boost::contains(ctx->nameOfBel(item.bel), "PLL")) { + ckg.emplace(tile_name_id(item.bel.tile)); + unused_pll[item.bel] = tile_name_id(item.bel.tile); + } + } + } + std::pair p; + p.first = *ckg.begin(); + if (ckg.size() == 2) + p.second = *(ckg.begin()++); + bank_to_ckg[bank] = p; + } else if (ctx->getBelType(bel) == id_IOTP) { + if (ctx->getBelName(bel)[1] == ctx->id("D08P_CLK.IOTP")) { + global_capable_bels.emplace(bel, id_P17RI); + } else if (ctx->getBelName(bel)[1] == ctx->id("D09P_CLK.IOTP")) { + global_capable_bels.emplace(bel, id_P19RI); + } + } else if (ctx->getBelType(bel) == id_GCK) { + std::string name = ctx->getBelName(bel)[1].c_str(ctx); + int lobe = std::stoi(name.substr(1, 1)); + int num = std::stoi(name.substr(4, 2).c_str()); + gck_per_lobe[lobe][num - 1] = GckConfig(bel); + } + locations.emplace(stringf("%s:%s", tile_name(bel.tile).c_str(), ctx->getBelName(bel)[1].c_str(ctx)), bel); + Loc loc = ctx->getBelLocation(bel); + tile_locations.emplace(tile_name(bel.tile).c_str(), Loc(loc.x & 0xfffe, loc.y & 0xfffe, 0)); + } + for (auto bel : ctx->getBels()) { + if (ctx->getBelType(bel) == id_DSP) { + WireId cco = ctx->getBelPinWire(bel, id_CCO); + WireId cci; + for (auto dh : ctx->getPipsDownhill(cco)) { + cci = ctx->getPipDstWire(dh); + } + if (cci != WireId()) { + std::string loc = stringf("%s:%s", tile_name(cci.tile).c_str(), ctx->getWireName(cci)[1].c_str(ctx)); + loc.erase(loc.find(".CCI")); + BelId dsp_bel = locations[loc]; + dsp_cascade.emplace(dsp_bel, bel); + } + } + } +} + +namespace { +// Note: These are per Cell type not Bel type +// Sinks +const dict> fabric_lowskew_sinks = { + // TILE - DFF + {id_BEYOND_FE, {id_CK, id_L, id_R}}, + // { id_DFF, { id_CK }}, // This is part of BEYOND_FE + // TILE - Register file + {id_RF, {id_WCK}}, + {id_RFSP, {id_WCK}}, + {id_XHRF, {id_WCK1, id_WCK2}}, + {id_XWRF, {id_WCK1, id_WCK2}}, + {id_XPRF, {id_WCK1, id_WCK2}}, + // TILE - CDC + {id_CDC, {id_CK1, id_CK2}}, + {id_DDE, {id_CK1, id_CK2}}, + {id_TDE, {id_CK1, id_CK2}}, + {id_XCDC, {id_CK1, id_CK2}}, + // TILE - FIFO + {id_FIFO, {id_RCK, id_WCK}}, + {id_XHFIFO, {id_RCK1, id_RCK2, id_WCK1, id_WCK2}}, + {id_XWFIFO, {id_RCK1, id_RCK2, id_WCK1, id_WCK2}}, + // CGB - RAM + {id_RAM, {id_ACK, id_BCK}}, + // CGB - DSP + {id_DSP, {id_CK}}, +}; + +const dict> ring_clock_sinks = { + // CKG + {id_PLL, {id_CLK_CAL, id_FBK, id_REF}}, + {id_WFB, {id_ZI}}, + {id_WFG, {id_ZI}}}; + +const dict> ring_over_tile_clock_sinks = { + // IOB + {id_DFR, {id_CK}}, + {id_DDFR, {id_CK, id_CKF}}, +}; +// IOB +// { id_IOM, { id_ALCK1, id_ALCK2, id_ALCK3, id_CCK, id_FCK1, id_FCK2, id_FDCK, +// id_LDSCK1, id_LDSCK2, id_LDSCK3, id_SWRX1CK, id_SWRX2CK }}, + +// HSSL +// { id_CRX, { id_LINK }}, +// { id_CTX, { id_LINK }}, +// { id_PMA, { id_hssl_clock_i1, id_hssl_clock_i2, id_hssl_clock_i3, id_hssl_clock_i4 }, + +const dict> tube_clock_sinks = { + // TUBE + {id_GCK, {id_SI1, id_SI2}}, +}; +// Sources +// CKG +const dict> ring_clock_source = { + {id_IOM, {id_CKO1, id_CKO2}}, + {id_WFB, {id_ZO}}, + {id_WFG, {id_ZO}}, + {id_PLL, + {id_OSC, id_VCO, id_REFO, id_LDFO, id_CLK_DIV1, id_CLK_DIV2, id_CLK_DIV3, id_CLK_DIV4, id_CLK_DIVD1, + id_CLK_DIVD2, id_CLK_DIVD3, id_CLK_DIVD4, id_CLK_DIVD5, id_CLK_CAL_DIV}}}; +// TUBE +const dict> tube_clock_source = { + {id_GCK, {id_SO}}, +}; + +}; // namespace + +const dict> &NgUltraImpl::get_fabric_lowskew_sinks() { return fabric_lowskew_sinks; } + +bool NgUltraImpl::is_fabric_lowskew_sink(const PortRef &ref) +{ + return fabric_lowskew_sinks.count(ref.cell->type) && fabric_lowskew_sinks.at(ref.cell->type).count(ref.port); +} + +bool NgUltraImpl::is_ring_clock_sink(const PortRef &ref) +{ + return ring_clock_sinks.count(ref.cell->type) && ring_clock_sinks.at(ref.cell->type).count(ref.port); +} + +bool NgUltraImpl::is_ring_over_tile_clock_sink(const PortRef &ref) +{ + return ring_over_tile_clock_sinks.count(ref.cell->type) && + ring_over_tile_clock_sinks.at(ref.cell->type).count(ref.port); +} + +bool NgUltraImpl::is_tube_clock_sink(const PortRef &ref) +{ + return tube_clock_sinks.count(ref.cell->type) && tube_clock_sinks.at(ref.cell->type).count(ref.port); +} + +bool NgUltraImpl::is_ring_clock_source(const PortRef &ref) +{ + return ring_clock_source.count(ref.cell->type) && ring_clock_source.at(ref.cell->type).count(ref.port); +} + +bool NgUltraImpl::is_tube_clock_source(const PortRef &ref) +{ + return tube_clock_source.count(ref.cell->type) && tube_clock_source.at(ref.cell->type).count(ref.port); +} + +const NGUltraTileInstExtraDataPOD *NgUltraImpl::tile_extra_data(int tile) const +{ + return reinterpret_cast(ctx->chip_info->tile_insts[tile].extra_data.get()); +} + +const NGUltraPipExtraDataPOD *NgUltraImpl::pip_extra_data(PipId pip) const +{ + return reinterpret_cast(chip_pip_info(ctx->chip_info, pip).extra_data.get()); +} + +const NGUltraBelExtraDataPOD *NgUltraImpl::bel_extra_data(BelId bel) const +{ + return reinterpret_cast(chip_bel_info(ctx->chip_info, bel).extra_data.get()); +} + +IdString NgUltraImpl::tile_name_id(int tile) const +{ + const auto &data = *tile_extra_data(tile); + return IdString(data.name); +} + +std::string NgUltraImpl::tile_name(int tile) const { return stringf("%s", tile_name_id(tile).c_str(ctx)); } + +int NgUltraImpl::tile_lobe(int tile) const +{ + const auto &data = *tile_extra_data(tile); + return data.lobe; +} + +TileTypeExtra NgUltraImpl::tile_type(int tile) const +{ + const auto &data = *tile_extra_data(tile); + return (TileTypeExtra)data.tile_type; +} + +bool NgUltraImpl::get_mux_data(BelId bel, IdString port, uint8_t *value) +{ + return get_mux_data(ctx->getBelPinWire(bel, port), value); +} + +bool NgUltraImpl::get_mux_data(WireId wire, uint8_t *value) +{ + for (PipId pip : ctx->getPipsUphill(wire)) { + if (!ctx->getBoundPipNet(pip)) + continue; + const auto &extra_data = *pip_extra_data(pip); + if (!extra_data.name) + continue; + if (extra_data.type == PipExtra::PIP_EXTRA_MUX) { + *value = extra_data.input; + return true; + } + } + return false; +} + +bool NgUltraImpl::update_bff_to_csc(CellInfo *cell, BelId bel, PipId dst_pip) +{ + const auto &extra_data = *bel_extra_data(bel); + // Check if CSC mode only if FE is capable + if (extra_data.flags & BEL_EXTRA_FE_CSC) { + WireId dwire = ctx->getPipDstWire(dst_pip); + for (PipId pip : ctx->getPipsDownhill(dwire)) { + if (!ctx->getBoundPipNet(pip)) + continue; + for (PipId pip2 : ctx->getPipsDownhill(ctx->getPipDstWire(pip))) { + if (!ctx->getBoundPipNet(pip2)) + continue; + IdString dst = ctx->getWireName(ctx->getPipDstWire(pip2))[1]; + if (boost::ends_with(dst.c_str(ctx), ".DS")) { + cell->setParam(id_type, Property("CSC")); + return true; + } + } + } + } + return false; +} + +bool NgUltraImpl::update_bff_to_scc(CellInfo *cell, BelId bel, PipId dst_pip) +{ + const auto &extra_data = *bel_extra_data(bel); + // Check if SCC mode only if FE is capable + if (extra_data.flags & BEL_EXTRA_FE_SCC) { + WireId dwire = ctx->getPipDstWire(dst_pip); + for (PipId pip : ctx->getPipsUphill(dwire)) { + if (!ctx->getBoundPipNet(pip)) + continue; + for (PipId pip2 : ctx->getPipsUphill(ctx->getPipSrcWire(pip))) { + if (!ctx->getBoundPipNet(pip2)) + continue; + IdString dst = ctx->getWireName(ctx->getPipSrcWire(pip2))[1]; + if (boost::starts_with(dst.c_str(ctx), "SYSTEM.ST1")) { + cell->setParam(id_type, Property("SCC")); + return true; + } + } + } + } + return false; +} + +void NgUltraImpl::postRoute() +{ + ctx->assignArchInfo(); + log_break(); + log_info("Resources spent on routing:\n"); + int bff_count = 0, csc_count = 0, scc_count = 0, lut_bypass = 0, fe_new = 0, wfg_bypass = 0, gck_bypass = 0; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + for (auto &w : ni->wires) { + if (w.second.pip != PipId()) { + const auto &extra_data = *pip_extra_data(w.second.pip); + if (!extra_data.name) + continue; + if (extra_data.type == PipExtra::PIP_EXTRA_BYPASS) { + IdStringList id = ctx->getPipName(w.second.pip); + BelId bel = ctx->getBelByName(IdStringList::concat(id[0], IdString(extra_data.name))); + IdString type = ctx->getBelType(bel); + if (!ctx->getBoundBelCell(bel)) { + CellInfo *cell = ctx->createCell(ctx->id(ctx->nameOfBel(bel)), type); + ctx->bindBel(bel, cell, PlaceStrength::STRENGTH_FIXED); + if (type == id_BEYOND_FE) + fe_new++; + } + CellInfo *cell = ctx->getBoundBelCell(bel); + switch (type.index) { + case id_BEYOND_FE.index: + if (extra_data.input == 0) { + // set bypass mode for DFF + cell->setParam(id_type, Property("BFF")); + cell->params[id_dff_used] = Property(1, 1); + // Note: no conflict, CSC and SCC modes are never available on same position + if (update_bff_to_csc(cell, bel, w.second.pip)) + csc_count++; + else if (update_bff_to_scc(cell, bel, w.second.pip)) + scc_count++; + else + bff_count++; + } else { + lut_bypass++; + cell->params[id_lut_used] = Property(1, 1); + cell->params[id_lut_table] = Property(0xaaaa, 16); + } + break; + case id_WFG.index: + wfg_bypass++; + cell->type = id_WFB; + break; + case id_GCK.index: + gck_bypass++; + cell->setParam(id_std_mode, extra_data.input == 0 ? Property("BYPASS") : Property("CSC")); + break; + default: + log_error("Unmaped bel type '%s' for routing\n", type.c_str(ctx)); + } + } + } + } + } + if (bff_count) + log_info(" %6d DFFs used as BFF\n", bff_count); + if (csc_count) + log_info(" %6d DFFs used as CSC\n", csc_count); + if (scc_count) + log_info(" %6d DFFs used as SCC\n", scc_count); + if (lut_bypass) + log_info(" %6d LUTs used in bypass mode\n", lut_bypass); + if (fe_new) + log_info(" %6d newly allocated FEs\n", fe_new); + if (wfg_bypass) + log_info(" %6d WFGs used as WFB\n", wfg_bypass); + if (gck_bypass) + log_info(" %6d GCK\n", gck_bypass); + + // Handle LUT permutation + for (auto &cell : ctx->cells) { + if (cell.second->type == id_BEYOND_FE) { + // if LUT part used + if (cell.second->params.count(id_lut_table) != 0) { + std::array, 4> phys_to_log; + unsigned orig_init = int_or_default(cell.second->params, id_lut_table); + const std::array ports{id_I1, id_I2, id_I3, id_I4}; + for (unsigned i = 0; i < 4; i++) { + WireId pin_wire = ctx->getBelPinWire(cell.second->bel, ports[i]); + for (PipId pip : ctx->getPipsUphill(pin_wire)) { + if (!ctx->getBoundPipNet(pip)) + continue; + const auto &extra_data = *pip_extra_data(pip); + if (!extra_data.name) + continue; + if (extra_data.type == PipExtra::PIP_EXTRA_LUT_PERMUTATION) { + NPNR_ASSERT(extra_data.output == i); + phys_to_log[extra_data.input].push_back(i); + } + } + } + unsigned permuted_init = 0; + for (unsigned i = 0; i < 16; i++) { + unsigned log_idx = 0; + for (unsigned j = 0; j < 4; j++) { + if ((i >> j) & 0x1) { + for (auto log_pin : phys_to_log[j]) + log_idx |= (1 << log_pin); + } + } + if ((orig_init >> log_idx) & 0x1) + permuted_init |= (1 << i); + } + cell.second->params[id_lut_table] = Property(permuted_init, 16); + } + } + } + + fixup_crossbars(); + + print_utilisation(ctx); + const ArchArgs &args = ctx->args; + if (args.options.count("bit")) { + write_bitstream_json(args.options.at("bit")); + } +} + +void NgUltraImpl::configurePlacerHeap(PlacerHeapCfg &cfg) +{ + cfg.beta = 0.5; + cfg.placeAllAtOnce = true; +} + +namespace { + +template bool check_assign_sig(std::array &sig_set, const NetInfo *sig) +{ + if (sig == nullptr) + return true; + for (size_t i = 0; i < N; i++) + if (sig_set[i] == sig) { + return true; + } else if (sig_set[i] == nullptr) { + sig_set[i] = sig; + return true; + } + return false; +}; + +struct SectionFEWorker +{ + std::array clk{}; // from local system matrix + std::array reset{}; // from local system matrix + std::array load{}; // from local system matrix + std::array shared{}; // 1 from local system matrix + // Additional R and L can be used from RI network + bool run(const NgUltraImpl *impl, const Context *ctx, BelId bel, CellInfo *cell) + { + Loc loc = ctx->getBelLocation(bel); + for (uint8_t id = 0; id <= BEL_LUT_MAX_Z; id++) { + const CellInfo *ff = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, id))); + if (ff == nullptr) + continue; + if (!check_assign_sig(reset, ff->getPort(id_R))) { + if (!check_assign_sig(shared, ff->getPort(id_R))) + return false; + } + if (!check_assign_sig(load, ff->getPort(id_L))) { + if (!check_assign_sig(shared, ff->getPort(id_L))) + return false; + } + if (!check_assign_sig(clk, ff->getPort(id_CK))) + return false; + } + const auto &extra_data = *impl->bel_extra_data(bel); + if (cell->params.count(id_type)) { + const std::string &type = cell->params[id_type].as_string(); + if (type == "CSC" && (extra_data.flags & BEL_EXTRA_FE_CSC) == 0) + return false; // No CSC capability on FE + if (type == "SCC" && (extra_data.flags & BEL_EXTRA_FE_SCC) == 0) + return false; // No SCC capability on FE + } + if (extra_data.flags & BEL_EXTRA_FE_CSC) + return false; + return true; + } +}; + +}; // namespace + +bool NgUltraImpl::isBelLocationValid(BelId bel, bool explain_invalid) const +{ + CellInfo *cell = ctx->getBoundBelCell(bel); + if (cell == nullptr) { + return true; + } + if (ctx->getBelType(bel) == id_BEYOND_FE) { + SectionFEWorker worker; + return worker.run(this, ctx, bel, cell); + } else if (ctx->getBelType(bel).in(id_RF, id_XRF)) { + Loc loc = ctx->getBelLocation(bel); + if (loc.z == BEL_XRF_Z) { + // If we used any of RFs we can not used XRF + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_RF_Z)))) + return false; + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_RF_Z + 1)))) + return false; + // If we used any FIFO we can not use XRF + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_FIFO_Z)))) + return false; + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_FIFO_Z + 1)))) + return false; + // If we used XFIFO we can not use XRF + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_XFIFO_Z)))) + return false; + } else { + // If we used XRF we can not use individual RF + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_XRF_Z)))) + return false; + // If we used XFIFO we can not use RF + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_XFIFO_Z)))) + return false; + int index = loc.z - BEL_RF_Z; + // If we used coresponding FIFO we can not use RF + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_FIFO_Z + index)))) + return false; + } + } else if (ctx->getBelType(bel).in(id_FIFO, id_XFIFO)) { + Loc loc = ctx->getBelLocation(bel); + if (loc.z == BEL_XFIFO_Z) { + // If we used any of RFs we can not used XFIFO + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_RF_Z)))) + return false; + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_RF_Z + 1)))) + return false; + // If we used any FIFO we can not use XFIFO + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_FIFO_Z)))) + return false; + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_FIFO_Z + 1)))) + return false; + // If we used XFIFO we can not use XFIFO + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_XFIFO_Z)))) + return false; + // If we used any CDC we can not use XFIFO + // NOTE: CDC1 is in S4 and CDC2 is S12 + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x - 1, loc.y, BEL_CDC_Z)))) + return false; + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x + 1, loc.y, BEL_CDC_Z + 1)))) + return false; + // If we used XCDC we can not use XFIFO + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_XCDC_Z)))) + return false; + } else { + // If we used XFIFO we can not use individual FIFO + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_XFIFO_Z)))) + return false; + // If we used XRF we can not use FIFO + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_XRF_Z)))) + return false; + // If we used XCDC we can not use FIFO + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_XCDC_Z)))) + return false; + int index = loc.z - BEL_FIFO_Z; + // If we used coresponding RF we can not use FIFO + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_RF_Z + index)))) + return false; + // If we used coresponding CDC we can not use FIFO + // NOTE: CDC1 is in S4 and CDC2 is S12 + int rel = (index == 0) ? -1 : +1; + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x + rel, loc.y, BEL_CDC_Z + index)))) + return false; + } + } else if (ctx->getBelType(bel).in(id_CDC, id_XCDC)) { + Loc loc = ctx->getBelLocation(bel); + if (loc.z == BEL_XCDC_Z) { + // If we used any of CDCs we can not used XCDC + // NOTE: CDC1 is in S4 and CDC2 is S12 + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x - 1, loc.y, BEL_CDC_Z)))) + return false; + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x + 1, loc.y, BEL_CDC_Z + 1)))) + return false; + // If we used any FIFO we can not use XCDC + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_FIFO_Z)))) + return false; + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_FIFO_Z + 1)))) + return false; + // If we used XFIFO we can not use XCDC + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, BEL_XFIFO_Z)))) + return false; + } else { + // NOTE: CDC1 is in S4 and CDC2 is S12 so we move calculation relative to S8 + int index = loc.z - BEL_CDC_Z; + int fix = (index == 0) ? +1 : -1; + // If we used XCDC we can not use individual CDC + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x + fix, loc.y, BEL_XCDC_Z)))) + return false; + // If we used XFIFO we can not use CDC + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x + fix, loc.y, BEL_XFIFO_Z)))) + return false; + // If we used coresponding FIFO we can not use CDC + if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x + fix, loc.y, BEL_FIFO_Z + index)))) + return false; + } + } + return true; +} + +// Bel bucket functions +IdString NgUltraImpl::getBelBucketForCellType(IdString cell_type) const +{ + if (cell_type.in(id_IOP, id_IP, id_OP, id_IOTP, id_ITP, id_OTP)) + return ctx->idf("IOP/IOTP"); + else if (cell_type.in(id_BFR, id_DFR, id_DDFR)) + return ctx->idf("DFR/DDFR"); + else if (cell_type.in(id_RF, id_RFSP)) + return id_RF; + else if (cell_type.in(id_XHRF, id_XWRF, id_XPRF)) + return id_XRF; + else if (cell_type.in(id_DDE, id_TDE, id_CDC, id_BGC, id_GBC)) + return id_CDC; + else if (cell_type.in(id_XCDC)) + return id_XCDC; + else if (cell_type.in(id_FIFO)) + return id_FIFO; + else if (cell_type.in(id_XHFIFO, id_XWFIFO)) + return id_XFIFO; + else if (cell_type.in(id_WFB, id_WFG)) + return id_WFG; + else + return cell_type; +} + +BelBucketId NgUltraImpl::getBelBucketForBel(BelId bel) const +{ + IdString bel_type = ctx->getBelType(bel); + if (bel_type.in(id_IOP, id_IOTP)) + return ctx->idf("IOP/IOTP"); + else if (bel_type.in(id_DFR, id_DDFR)) + return ctx->idf("DFR/DDFR"); + return bel_type; +} + +bool NgUltraImpl::isValidBelForCellType(IdString cell_type, BelId bel) const +{ + IdString bel_type = ctx->getBelType(bel); + if (bel_type == id_IOTP) + return cell_type.in(id_IOP, id_IP, id_OP, id_IOTP, id_ITP, id_OTP); + else if (bel_type == id_IOP) + return cell_type.in(id_IOP, id_IP, id_OP); + else if (bel_type == id_DDFR) + return cell_type.in(id_BFR, id_DFR, id_DDFR); + else if (bel_type == id_DFR) + return cell_type.in(id_BFR, id_DFR); + else if (bel_type == id_RF) + return cell_type.in(id_RF, id_RFSP); + else if (bel_type == id_XRF) + return cell_type.in(id_XHRF, id_XWRF, id_XPRF); + else if (bel_type == id_CDC) + return cell_type.in(id_DDE, id_TDE, id_CDC, id_BGC, id_GBC); + else if (bel_type == id_XCDC) + return cell_type.in(id_XCDC); + else if (bel_type == id_FIFO) + return cell_type.in(id_FIFO); + else if (bel_type == id_XFIFO) + return cell_type.in(id_XHFIFO, id_XWFIFO); + else if (bel_type == id_WFG) + return cell_type.in(id_WFB, id_WFG); + else + return (bel_type == cell_type); +} + +bool NgUltraImpl::getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc, + std::vector> &placement) const +{ + Loc prev = root_loc; + for (auto child : cluster->constr_children) { + Loc child_loc; + switch (child->constr_z) { + case PLACE_CY_CHAIN: + child_loc = getNextLocInCYChain(prev); + prev = child_loc; + break; + case PLACE_LUT_CHAIN: + child_loc = getNextLocInLUTChain(prev); + prev = child_loc; + break; + case PLACE_DFF_CHAIN: + child_loc = getNextLocInDFFChain(prev); + prev = child_loc; + break; + case PLACE_CY_FE1 ... PLACE_CY_FE4: + child_loc = getCYFE(root_loc, child->constr_z - PLACE_CY_FE1); + break; + case PLACE_XLUT_FE1 ... PLACE_XLUT_FE4: + child_loc = getXLUTFE(root_loc, child->constr_z - PLACE_XLUT_FE1); + break; + case PLACE_XRF_I1 ... PLACE_XRF_WEA: + child_loc = getXRFFE(root_loc, child->constr_z - PLACE_XRF_I1); + break; + case PLACE_CDC_AI1 ... PLACE_CDC_DDRSTI: + child_loc = getCDCFE(root_loc, child->constr_z - PLACE_CDC_AI1); + break; + case PLACE_FIFO_I1 ... PLACE_FIFO_REQ2: + child_loc = getFIFOFE(root_loc, child->constr_z - PLACE_FIFO_I1); + break; + case PLACE_DSP_CHAIN: + child_loc = getNextLocInDSPChain(this, prev); + prev = child_loc; + break; + default: + child_loc.x = root_loc.x + child->constr_x; + child_loc.y = root_loc.y + child->constr_y; + child_loc.z = child->constr_abs_z ? child->constr_z : (root_loc.z + child->constr_z); + } + BelId child_bel = ctx->getBelByLocation(child_loc); + if (child_bel == BelId() || !this->isValidBelForCellType(child->type, child_bel)) + return false; + placement.emplace_back(child, child_bel); + if (!getChildPlacement(child, child_loc, placement)) + return false; + } + return true; +} + +bool NgUltraImpl::getClusterPlacement(ClusterId cluster, BelId root_bel, + std::vector> &placement) const +{ + CellInfo *root_cell = get_cluster_root(ctx, cluster); + placement.clear(); + NPNR_ASSERT(root_bel != BelId()); + Loc root_loc = ctx->getBelLocation(root_bel); + if (root_cell->constr_abs_z) { + // Coerce root to absolute z constraint + root_loc.z = root_cell->constr_z; + root_bel = ctx->getBelByLocation(root_loc); + if (root_bel == BelId() || !this->isValidBelForCellType(root_cell->type, root_bel)) + return false; + } + placement.emplace_back(root_cell, root_bel); + return getChildPlacement(root_cell, root_loc, placement); +} + +BoundingBox NgUltraImpl::getRouteBoundingBox(WireId src, WireId dst) const +{ + if ((tile_type(src.tile) != TILE_EXTRA_FABRIC || tile_type(dst.tile) != TILE_EXTRA_FABRIC)) { + return {0, 0, ctx->getGridDimX(), ctx->getGridDimY()}; + } + int x0, y0, x1, y1; + auto expand = [&](int x, int y) { + x0 = std::min(x0, x); + x1 = std::max(x1, x); + y0 = std::min(y0, y); + y1 = std::max(y1, y); + }; + tile_xy(ctx->chip_info, src.tile, x0, y0); + x1 = x0; + y1 = y0; + int dx, dy; + tile_xy(ctx->chip_info, dst.tile, dx, dy); + expand(dx, dy); + // Reach 7 MESH above and below (3 left and 3 right of TILE/CGB) + return {(x0 & 0xfffc) - 3 * 4, // 3 MESH on left + (y0 & 0xfffc) - 4, // row above + (x1 & 0xfffc) + 4 * 4, // MESH bellow and 3 right + (y1 & 0xfffc) + 8}; // current and row bellow +} + +void NgUltraImpl::expandBoundingBox(BoundingBox &bb) const +{ + // Updated numbers for NG-ULTRA only + // x0 and y0 substract 1 TILE + // x1 and y1 adds 1 TILE (and of one added) + bb.x0 = std::max((bb.x0 & 0xfffc) - 4, 0); + bb.y0 = std::max((bb.y0 & 0xfffc) - 4, 0); + bb.x1 = std::min((bb.x1 & 0xfffc) + 8, ctx->getGridDimX()); + bb.y1 = std::min((bb.y1 & 0xfffc) + 8, ctx->getGridDimY()); +} + +delay_t NgUltraImpl::estimateDelay(WireId src, WireId dst) const +{ + int sx, sy, dx, dy; + tile_xy(ctx->chip_info, src.tile, sx, sy); + tile_xy(ctx->chip_info, dst.tile, dx, dy); + if (sx == dx && sy == dy) { + // Same sub tile + return 50; + } else if (((sx & 0xfffc) == (dx & 0xfffc)) && ((sy & 0xfffc) == (dy & 0xfffc))) { + // Same "TILE" + return 200; + } + return 500 + 100 * (std::abs(dy - sy) / 4 + std::abs(dx - sx) / 4); +} + +delay_t NgUltraImpl::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const +{ + Loc src_loc = ctx->getBelLocation(src_bel), dst_loc = ctx->getBelLocation(dst_bel); + if (src_loc.x == dst_loc.x && src_loc.y == dst_loc.y) { + // Same sub tile + return 50; + } else if (((src_loc.x & 0xfffc) == (dst_loc.x & 0xfffc)) && ((src_loc.y & 0xfffc) == (dst_loc.y & 0xfffc))) { + // Same "TILE" + return 200; + } + return 500 + 100 * (std::abs(dst_loc.y - src_loc.y) / 4 + std::abs(dst_loc.x - src_loc.x) / 4); +} + +void NgUltraImpl::fixup_crossbars() +{ + + auto is_crossbar_pip = [&](PipId pip) { + const auto &extra_data = *pip_extra_data(pip); + return (extra_data.name && extra_data.type == PipExtra::PIP_EXTRA_CROSSBAR); + }; + + auto crossbar_key = [&](PipId pip) { + const auto &extra_data = *pip_extra_data(pip); + return std::make_pair(pip.tile, IdString(extra_data.name)); + }; + + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + dict> downstream; + // build a map of wires to sinks + for (auto &w : ni->wires) { + if (w.second.pip != PipId()) { + downstream[ctx->getPipSrcWire(w.second.pip)].push_back(w.second.pip); + } + } + // the original drivers of crossbars + dict, WireId> crossbar_entries; + // traverse from the start forwards so we always reach closer wires first in the route tree + WireId src = ctx->getNetinfoSourceWire(ni); + if (src == WireId() || !downstream.count(src)) { + continue; + } + std::queue visit; + visit.push(src); + while (!visit.empty()) { + WireId cursor = visit.front(); + visit.pop(); + if (!downstream.count(cursor)) + continue; + for (auto pip : downstream.at(cursor)) { + WireId dst = ctx->getPipDstWire(pip); + if (is_crossbar_pip(pip)) { + auto key = crossbar_key(pip); + if (!crossbar_entries.count(key)) { + crossbar_entries[key] = ctx->getPipSrcWire(pip); + } else { + WireId xbar_src = crossbar_entries.at(key); + if (ctx->getPipSrcWire(pip) != xbar_src) { + // rewrite to be driven by original entry + ctx->unbindPip(pip); + PipId found_pip = PipId(); + for (auto search_pip : ctx->getPipsUphill(dst)) { + if (ctx->getPipSrcWire(search_pip) == xbar_src) { + found_pip = search_pip; + break; + } + } + NPNR_ASSERT(found_pip != PipId()); + // rebind + // log_info(" replacing crossbar pip %s with %s on %s\n", ctx->nameOfPip(pip), + // ctx->nameOfPip(found_pip), ctx->nameOf(ni)); + ctx->bindPip(found_pip, ni, STRENGTH_STRONG); + } + } + } + visit.push(dst); + } + downstream.erase(cursor); + } + // check everything was visited by our BFS tree traversal + NPNR_ASSERT(downstream.empty()); + } +} + +void NgUltraImpl::drawBel(std::vector &g, GraphicElement::style_t style, IdString bel_type, Loc loc) +{ + GraphicElement el; + el.type = GraphicElement::TYPE_BOX; + el.style = style; + switch (bel_type.index) { + case id_BEYOND_FE.index: + el.x1 = loc.x + 0.15 + (loc.z % 8) * 0.1; + el.x2 = el.x1 + 0.05; + el.y1 = loc.y + 0.9 - (loc.z / 8) * 0.15; + el.y2 = el.y1 - 0.05; + g.push_back(el); + break; + case id_XLUT.index: + el.x1 = loc.x + 0.15 + ((loc.z - BEL_XLUT_Z) % 8) * 0.1; + el.x2 = el.x1 + 0.05; + el.y1 = loc.y + 0.9 - 4 * 0.15; + el.y2 = el.y1 - 0.1; + g.push_back(el); + break; + case id_CY.index: + el.x1 = loc.x + 0.15 + ((loc.z - BEL_CY_Z) % 4) * 0.2; + el.x2 = el.x1 + 0.15; + el.y1 = loc.y + 0.9 - 4 * 0.15; + el.y2 = el.y1 - 0.1; + g.push_back(el); + break; + case id_RF.index: + el.x1 = loc.x + 0.15 + ((loc.z - BEL_RF_Z) % 2) * 0.6; + el.x2 = el.x1 + 0.15; + el.y1 = loc.y + 0.9 - 4 * 0.15; + el.y2 = el.y1 - 0.05; + g.push_back(el); + break; + case id_XRF.index: + el.x1 = loc.x + 0.15 + 0.2; + el.x2 = el.x1 + 0.35; + el.y1 = loc.y + 0.9 - 4 * 0.15; + el.y2 = el.y1 - 0.05; + g.push_back(el); + break; + case id_CDC.index: + el.x1 = loc.x + 0.15 + ((loc.z - BEL_CDC_Z) % 2) * 0.6; + el.x2 = el.x1 + 0.15; + el.y1 = loc.y + 0.9 - 4 * 0.15 - 0.1; + el.y2 = el.y1 - 0.05; + g.push_back(el); + break; + case id_XCDC.index: + el.x1 = loc.x + 0.15 + 0.2; + el.x2 = el.x1 + 0.35; + el.y1 = loc.y + 0.9 - 4 * 0.15 - 0.1; + el.y2 = el.y1 - 0.05; + g.push_back(el); + break; + case id_FIFO.index: + el.x1 = loc.x + 0.15 + ((loc.z - BEL_FIFO_Z) % 2) * 0.6; + el.x2 = el.x1 + 0.15; + el.y1 = loc.y + 0.9 - 4 * 0.15 - 0.2; + el.y2 = el.y1 - 0.05; + g.push_back(el); + break; + case id_XFIFO.index: + el.x1 = loc.x + 0.15 + 0.2; + el.x2 = el.x1 + 0.35; + el.y1 = loc.y + 0.9 - 4 * 0.15 - 0.2; + el.y2 = el.y1 - 0.05; + g.push_back(el); + break; + case id_IOTP.index: + el.x1 = loc.x + 0.15 + loc.z / 4 * 0.11; + el.x2 = el.x1 + 0.06; + if (loc.y == 3) { // bottom + el.y1 = 0.1; + el.y2 = el.y1 + 0.2; + } else { // top + el.y1 = loc.y + 0.9; + el.y2 = el.y1 - 0.2; + } + g.push_back(el); + break; + case id_IOM.index: + el.x1 = loc.x + 0.15; + el.x2 = el.x1 + 33 * 0.11 + 0.06; + if (loc.y == 3) { // bottom + el.y1 = 0.4; + el.y2 = el.y1 + 0.2; + } else { // top + el.y1 = loc.y + 0.6; + el.y2 = el.y1 - 0.2; + } + g.push_back(el); + break; + case id_DDFR.index: + el.x1 = loc.x + 0.15 + loc.z / 4 * 0.11 + (loc.z % 4 - 1) * 0.02; + el.x2 = el.x1 + 0.015; + if (loc.y == 3) { // bottom + el.y1 = 0.7; + el.y2 = el.y1 + 0.1; + } else { // top + el.y1 = loc.y + 0.3; + el.y2 = el.y1 - 0.1; + } + g.push_back(el); + break; + case id_IOP.index: + if (loc.x == ctx->getGridDimX() - 4) { // right + el.x1 = ctx->getGridDimX() - 0.1; + el.x2 = el.x1 - 0.2; + } else { // left + el.x1 = loc.x + 0.1; + el.x2 = el.x1 + 0.2; + } + el.y1 = loc.y + 0.85 - loc.z / 4 * 0.11; + el.y2 = el.y1 - 0.06; + g.push_back(el); + break; + case id_DFR.index: + if (loc.x == ctx->getGridDimX() - 4) { // right + el.x1 = ctx->getGridDimX() - 0.4; + el.x2 = el.x1 - 0.1; + } else { // left + el.x1 = loc.x + 0.4; + el.x2 = el.x1 + 0.1; + } + el.y1 = loc.y + 0.85 - loc.z / 4 * 0.11 - (loc.z % 4 - 1) * 0.02 - 0.02; + el.y2 = el.y1 + 0.015; + g.push_back(el); + break; + case id_PLL.index: + el.x1 = loc.x + 0.1; + el.x2 = el.x1 + 0.8; + el.y1 = loc.y + 0.9; + el.y2 = el.y1 - 0.8; + g.push_back(el); + break; + case id_WFG.index: + el.x1 = loc.x + 1.1; + el.x2 = el.x1 + 0.8; + el.y1 = loc.y + 0.95 - (loc.z - 1) * 0.25 + 3; + el.y2 = el.y1 - 0.2; + g.push_back(el); + break; + case id_RAM.index: + el.x1 = loc.x + 0.2; + el.x2 = el.x1 + 3.6; + el.y1 = loc.y + 0.8; + el.y2 = el.y1 - 1.6; + g.push_back(el); + break; + case id_DSP.index: + el.x1 = (loc.x - 1) + 0.2; + el.x2 = el.x1 + 1.6; + el.y1 = loc.y + 0.8; + el.y2 = el.y1 - 1.6; + g.push_back(el); + break; + case id_GCK.index: { + int lobe = loc.z / 20; + el.x1 = (47 + (lobe % 2) * 3) * 4 + 0.1; + el.x2 = el.x1 + 0.8; + el.y1 = (ctx->getGridDimY() - 1 - (7 * 4 + 12 * 4 * (lobe >> 1))) + 0.95 - (loc.z % 20) * 0.25; + el.y2 = el.y1 - 0.2; + g.push_back(el); + } break; + default: + break; + } +} + +struct NgUltraArch : HimbaechelArch +{ + NgUltraArch() : HimbaechelArch("ng-ultra") {}; + bool match_device(const std::string &device) override { return device == "NG-ULTRA"; } + std::unique_ptr create(const std::string &device, const dict &args) + { + return std::make_unique(); + } +} ngUltraArch; + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/ng-ultra/ng_ultra.h b/himbaechel/uarch/ng-ultra/ng_ultra.h new file mode 100644 index 00000000..0ced37b2 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/ng_ultra.h @@ -0,0 +1,126 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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. + * + */ + +#ifndef HIMBAECHEL_NG_ULTRA_H +#define HIMBAECHEL_NG_ULTRA_H +#include + +#include "extra_data.h" +#include "himbaechel_api.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +#include "himbaechel_helpers.h" + +#ifdef GTEST_API_ +#define TESTABLE_PRIVATE public +#else +#define TESTABLE_PRIVATE private +#endif + +NEXTPNR_NAMESPACE_BEGIN + +struct NgUltraImpl : HimbaechelAPI +{ + ~NgUltraImpl(); + void init_database(Arch *arch) override; + + void init(Context *ctx) override; + + bool isBelLocationValid(BelId bel, bool explain_invalid = false) const override; + IdString getBelBucketForCellType(IdString cell_type) const override; + bool isValidBelForCellType(IdString cell_type, BelId bel) const override; + BelBucketId getBelBucketForBel(BelId bel) const override; + + // Flow management + void pack() override; + void postPlace() override; + void postRoute() override; + + void configurePlacerHeap(PlacerHeapCfg &cfg) override; + + bool getClusterPlacement(ClusterId cluster, BelId root_bel, + std::vector> &placement) const override; + bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc, + std::vector> &placement) const; + + BoundingBox getRouteBoundingBox(WireId src, WireId dst) const override; + delay_t estimateDelay(WireId src, WireId dst) const override; + delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override; + + bool checkPipAvail(PipId pip) const override { return blocked_pips.count(pip) == 0; } + bool checkPipAvailForNet(PipId pip, const NetInfo *net) const override { return checkPipAvail(pip); }; + + void expandBoundingBox(BoundingBox &bb) const override; + + void drawBel(std::vector &g, GraphicElement::style_t style, IdString bel_type, Loc loc) override; + + public: + int tile_lobe(int tile) const; + TileTypeExtra tile_type(int tile) const; + IdString tile_name_id(int tile) const; + std::string tile_name(int tile) const; + + const dict> &get_fabric_lowskew_sinks(); + bool is_fabric_lowskew_sink(const PortRef &ref); + bool is_ring_clock_sink(const PortRef &ref); + bool is_ring_over_tile_clock_sink(const PortRef &ref); + bool is_tube_clock_sink(const PortRef &ref); + + bool is_ring_clock_source(const PortRef &ref); + bool is_tube_clock_source(const PortRef &ref); + + const NGUltraPipExtraDataPOD *pip_extra_data(PipId pip) const; + const NGUltraBelExtraDataPOD *bel_extra_data(BelId bel) const; + + dict iom_bels; + dict bank_voltage; + dict global_capable_bels; + dict locations; + dict tile_locations; + dict> gck_per_lobe; + + pool blocked_pips; + dict> bank_to_ckg; + dict unused_wfg; + dict unused_pll; + dict dsp_cascade; + + /* clang-format off */ +TESTABLE_PRIVATE: + void write_bitstream_json(const std::string &filename); + /* clang-format on */ + void parse_csv(const std::string &filename); + void remove_constants(); + bool update_bff_to_csc(CellInfo *cell, BelId bel, PipId dst_pip); + bool update_bff_to_scc(CellInfo *cell, BelId bel, PipId dst_pip); + void disable_beyond_fe_s_output(BelId bel); + + void fixup_crossbars(); + + // Misc utility functions + bool get_mux_data(BelId bel, IdString port, uint8_t *value); + bool get_mux_data(WireId wire, uint8_t *value); + + const NGUltraTileInstExtraDataPOD *tile_extra_data(int tile) const; +}; + +NEXTPNR_NAMESPACE_END +#endif diff --git a/himbaechel/uarch/ng-ultra/pack.cc b/himbaechel/uarch/ng-ultra/pack.cc new file mode 100644 index 00000000..182be6b2 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/pack.cc @@ -0,0 +1,2738 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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 "pack.h" +#include +#include +#include +#include +#include +#include +#include "chain_utils.h" +#include "design_utils.h" +#include "extra_data.h" +#include "log.h" +#include "nextpnr.h" + +#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +// Return true if a cell is a LUT +inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_NX_LUT; } + +// Return true if a cell is a flipflop +inline bool is_dff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type.in(id_NX_DFF, id_NX_BFF); } + +// Return true if a cell is a FE +inline bool is_fe(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_BEYOND_FE; } + +// Return true if a cell is a DFR +inline bool is_dfr(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_NX_DFR; } + +// Return true if a cell is a DDFR +inline bool is_ddfr(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_NX_DDFR_U; } + +// Return true if a cell is a WFG/WFB +inline bool is_wfg(const BaseCtx *ctx, const CellInfo *cell) { return cell->type.in(id_WFB, id_WFG); } + +// Return true if a cell is a GCK +inline bool is_gck(const BaseCtx *ctx, const CellInfo *cell) { return cell->type.in(id_GCK, id_NX_GCK_U); } + +// Process the contents of packed_cells +void NgUltraPacker::flush_cells() +{ + for (auto pcell : packed_cells) { + for (auto &port : ctx->cells[pcell]->ports) { + ctx->cells[pcell]->disconnectPort(port.first); + } + ctx->cells.erase(pcell); + } + packed_cells.clear(); +} + +void NgUltraPacker::pack_constants(void) +{ + log_info("Packing constants..\n"); + // Replace constants with LUTs + const dict vcc_params = { + {id_lut_table, Property(0xFFFF, 16)}, {id_lut_used, Property(1, 1)}, {id_dff_used, Property(1, 1)}}; + const dict gnd_params = { + {id_lut_table, Property(0x0000, 16)}, {id_lut_used, Property(1, 1)}, {id_dff_used, Property(1, 1)}}; + + h.replace_constants(CellTypePort(id_BEYOND_FE, id_LO), CellTypePort(id_BEYOND_FE, id_LO), vcc_params, gnd_params); +} + +void NgUltraImpl::remove_constants() +{ + log_info("Removing constants..\n"); + auto fnd_cell = ctx->cells.find(ctx->id("$PACKER_VCC_DRV")); + if (fnd_cell != ctx->cells.end()) { + auto fnd_net = ctx->nets.find(ctx->id("$PACKER_VCC")); + if (fnd_net != ctx->nets.end() && fnd_net->second->users.entries() == 0) { + BelId bel = (*fnd_cell).second.get()->bel; + if (bel != BelId()) + ctx->unbindBel(bel); + ctx->cells.erase(fnd_cell); + ctx->nets.erase(fnd_net); + log_info(" Removed unused VCC cell\n"); + } + } + fnd_cell = ctx->cells.find(ctx->id("$PACKER_GND_DRV")); + if (fnd_cell != ctx->cells.end()) { + auto fnd_net = ctx->nets.find(ctx->id("$PACKER_GND")); + if (fnd_net != ctx->nets.end() && fnd_net->second->users.entries() == 0) { + BelId bel = (*fnd_cell).second.get()->bel; + if (bel != BelId()) + ctx->unbindBel(bel); + ctx->cells.erase(fnd_cell); + ctx->nets.erase(fnd_net); + log_info(" Removed unused GND cell\n"); + } + } +} + +void NgUltraPacker::update_lut_init() +{ + log_info("Update LUT init...\n"); + + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_LUT)) + continue; + set_lut_input_if_constant(&ci, id_I1); + set_lut_input_if_constant(&ci, id_I2); + set_lut_input_if_constant(&ci, id_I3); + set_lut_input_if_constant(&ci, id_I4); + NetInfo *o = ci.getPort(id_O); + if (!o) { + // Remove those that do not output any value + log_warning("Removing LUT '%s' since output is not connected.\n", ci.name.c_str(ctx)); + packed_cells.insert(ci.name); + } + } + flush_cells(); +} + +void NgUltraPacker::dff_rewrite(CellInfo *cell) +{ + if (int_or_default(cell->params, id_dff_init, 0) == 0) { + // Reset not used + cell->disconnectPort(id_R); + } else { + // Reset used + NetInfo *net = cell->getPort(id_R); + if (net) { + if (net->name == ctx->id("$PACKER_GND")) { + log_warning("Removing reset on '%s' since it is always 0.\n", cell->name.c_str(ctx)); + cell->setParam(id_dff_init, Property(0, 1)); + cell->disconnectPort(id_R); + } else if (net->name == ctx->id("$PACKER_VCC")) { + log_error("Invalid DFF configuration, reset on '%s' is always 1.\n", cell->name.c_str(ctx)); + } + } + } + + if (int_or_default(cell->params, id_dff_load, 0) == 0) { + // Load not used + cell->disconnectPort(id_L); + } else { + // Load used + NetInfo *net = cell->getPort(id_L); + if (net) { + if (net->name == ctx->id("$PACKER_VCC")) { + log_warning("Removing load enable on '%s' since it is always 1.\n", cell->name.c_str(ctx)); + cell->setParam(id_dff_load, Property(0, 0)); + cell->disconnectPort(id_L); + } else if (net->name == ctx->id("$PACKER_GND")) { + log_warning("Converting to self loop, since load enable on '%s' is always 0.\n", cell->name.c_str(ctx)); + cell->setParam(id_dff_load, Property(0, 0)); + cell->disconnectPort(id_L); + cell->disconnectPort(id_I); + NetInfo *out = cell->getPort(id_O); + cell->connectPort(id_I, out); + } + } + } +} + +void NgUltraPacker::ddfr_rewrite(CellInfo *cell) +{ + // Reversed logic in comparison to DFF + if (int_or_default(cell->params, id_dff_load, 0) == 1) { + // Load not used + cell->disconnectPort(id_L); + } else { + // Load used + NetInfo *net = cell->getPort(id_L); + if (net) { + if (net->name == ctx->id("$PACKER_VCC")) { + log_warning("Removing load enable on '%s' since it is always 1.\n", cell->name.c_str(ctx)); + cell->setParam(id_dff_load, Property(0, 0)); + cell->disconnectPort(id_L); + } + } + } +} + +void NgUltraPacker::update_dffs() +{ + log_info("Update DFFs...\n"); + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_DFF)) + continue; + dff_rewrite(&ci); + } +} + +int NgUltraPacker::make_init_with_const_input(int init, int input, bool value) +{ + int new_init = 0; + for (int i = 0; i < 16; i++) { + if (((i >> input) & 0x1) != value) { + int other_i = (i & (~(1 << input))) | (value << input); + if ((init >> other_i) & 0x1) + new_init |= (1 << i); + } else { + if ((init >> i) & 0x1) + new_init |= (1 << i); + } + } + return new_init; +} + +void NgUltraPacker::set_lut_input_if_constant(CellInfo *cell, IdString input) +{ + NetInfo *net = cell->getPort(input); + if (!net) + return; + if (!net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) + return; + bool value = net->name == ctx->id("$PACKER_VCC"); + int index = std::string("1234").find(input.str(ctx)); + int init = int_or_default(cell->params, id_lut_table); + int new_init = make_init_with_const_input(init, index, value); + cell->params[id_lut_table] = Property(new_init, 16); + cell->disconnectPort(input); +} + +void NgUltraPacker::disconnect_if_gnd(CellInfo *cell, IdString input) +{ + NetInfo *net = cell->getPort(input); + if (!net) + return; + if (net->name.in(ctx->id("$PACKER_GND"))) { + cell->disconnectPort(input); + } +} + +void NgUltraPacker::connect_gnd_if_unconnected(CellInfo *cell, IdString input, bool warn = true) +{ + NetInfo *net = cell->getPort(input); + if (net) + return; + if (!cell->ports.count(input)) + cell->addInput(input); + auto fnd_net = ctx->nets.find(ctx->id("$PACKER_GND")); + if (fnd_net != ctx->nets.end()) { + cell->connectPort(input, fnd_net->second.get()); + if (warn) + log_warning("Connected GND to mandatory port '%s' of cell '%s'(%s).\n", input.c_str(ctx), + cell->name.c_str(ctx), cell->type.c_str(ctx)); + } +} + +void NgUltraPacker::lut_to_fe(CellInfo *lut, CellInfo *fe, bool no_dff, Property lut_table) +{ + fe->params[id_lut_table] = lut_table; + fe->params[id_lut_used] = Property(1, 1); + lut->movePortTo(id_I1, fe, id_I1); + lut->movePortTo(id_I2, fe, id_I2); + lut->movePortTo(id_I3, fe, id_I3); + lut->movePortTo(id_I4, fe, id_I4); + lut->movePortTo(id_O, fe, id_LO); + if (no_dff) { + fe->timing_index = ctx->get_cell_timing_idx(id_BEYOND_FE_LUT); + } +} + +void NgUltraPacker::dff_to_fe(CellInfo *dff, CellInfo *fe, bool pass_thru_lut) +{ + if (pass_thru_lut) { + NetInfo *net = dff->getPort(id_I); + if (net && net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) { + // special case if driver is constant + fe->params[id_lut_table] = Property((net->name == ctx->id("$PACKER_GND")) ? 0x0000 : 0xffff, 16); + dff->disconnectPort(id_I); + } else { + // otherwise just passthru + fe->params[id_lut_table] = Property(0xaaaa, 16); + dff->movePortTo(id_I, fe, id_I1); + } + fe->params[id_lut_used] = Property(1, 1); + } else + dff->movePortTo(id_I, fe, id_DI); + fe->params[id_dff_used] = Property(1, 1); + dff->movePortTo(id_O, fe, id_DO); + if (dff->type == id_NX_BFF) { + fe->setParam(id_type, Property("BFF")); + } else { + fe->setParam(id_type, Property("DFF")); + + dff->movePortTo(id_R, fe, id_R); + dff->movePortTo(id_CK, fe, id_CK); + dff->movePortTo(id_L, fe, id_L); + + if (dff->params.count(id_dff_ctxt)) + fe->setParam(id_dff_ctxt, dff->params[id_dff_ctxt]); + if (dff->params.count(id_dff_edge)) + fe->setParam(id_dff_edge, dff->params[id_dff_edge]); + if (dff->params.count(id_dff_init)) + fe->setParam(id_dff_init, dff->params[id_dff_init]); + if (dff->params.count(id_dff_load)) + fe->setParam(id_dff_load, dff->params[id_dff_load]); + if (dff->params.count(id_dff_sync)) + fe->setParam(id_dff_sync, dff->params[id_dff_sync]); + if (dff->params.count(id_dff_type)) + fe->setParam(id_dff_type, dff->params[id_dff_type]); + } + if (pass_thru_lut) { + NetInfo *new_out = ctx->createNet(ctx->idf("%s$LO", dff->name.c_str(ctx))); + fe->connectPort(id_LO, new_out); + fe->connectPort(id_DI, new_out); + } +} + +void NgUltraPacker::bind_attr_loc(CellInfo *cell, dict *attrs) +{ + if (attrs->count(id_LOC)) { + std::string name = attrs->at(id_LOC).as_string(); + if (boost::starts_with(name, "TILE[")) { + boost::replace_all(name, ".DFF", ".FE"); + boost::replace_all(name, ".LUT", ".FE"); + } + if (!uarch->locations.count(name)) { + log_error("Unable to find location %s\n", name.c_str()); + } + BelId bel = uarch->locations.at(name); + ctx->bindBel(bel, cell, PlaceStrength::STRENGTH_LOCKED); + } +} + +void NgUltraPacker::pack_xluts(void) +{ + log_info("Pack XLUTs...\n"); + int xlut_used = 0, lut_only = 0, lut_and_ff = 0; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_LUT)) + continue; + if (!ci.params.count(id_lut_table)) + log_error("Cell '%s' missing lut_table\n", ci.name.c_str(ctx)); + + if (ci.cluster != ClusterId()) + continue; + CellInfo *lut[4]; + int inputs_used = 0; + int dff_parts_used = 0; + for (int i = 0; i < 4; i++) { + NetInfo *net = ci.getPort(ctx->idf("I%d", i + 1)); + if (!net) + continue; + lut[i] = net_driven_by(ctx, net, is_lut, id_O); + if (lut[i] == cell.second.get()) + continue; + if (lut[i]) { + if (net->users.entries() > 1) + dff_parts_used++; + inputs_used++; + } + } + if (inputs_used != 4) + continue; + // we must have a route out for xlut output signal + if (dff_parts_used > 3) + continue; + ci.type = id_XLUT; + bind_attr_loc(&ci, &ci.attrs); + ci.cluster = ci.name; + xlut_used++; + + NetInfo *o = ci.getPort(id_O); + CellInfo *orig_dff = o ? net_only_drives(ctx, o, is_dff, id_I, true) : nullptr; + + for (int i = 0; i < 4; i++) { + ci.constr_children.push_back(lut[i]); + lut[i]->cluster = ci.cluster; + lut[i]->type = id_BEYOND_FE; + lut[i]->constr_z = PLACE_XLUT_FE1 + i; + lut[i]->renamePort(id_O, id_LO); + lut[i]->params[id_lut_used] = Property(1, 1); + NetInfo *net = lut[i]->getPort(id_LO); + if (net->users.entries() != 2) { + if (orig_dff && net->users.entries() == 1) { + // we place DFF on XLUT output on unused DFF + dff_to_fe(orig_dff, lut[i], false); + packed_cells.insert(orig_dff->name); + lut_and_ff++; + orig_dff = nullptr; + } else { + lut[i]->timing_index = ctx->get_cell_timing_idx(id_BEYOND_FE_LUT); + lut_only++; + } + } else { + CellInfo *dff = (*net->users.begin()).cell; + if (dff->type != id_NX_DFF) + dff = (*(++net->users.begin())).cell; + if (dff->type == id_NX_DFF) { + dff_to_fe(dff, lut[i], false); + packed_cells.insert(dff->name); + lut_and_ff++; + } else { + lut[i]->timing_index = ctx->get_cell_timing_idx(id_BEYOND_FE_LUT); + lut_only++; + } + } + } + } + if (xlut_used) + log_info(" %6d XLUTs used\n", xlut_used); + if (lut_only) + log_info(" %6d FEs used as LUT only\n", lut_only); + if (lut_and_ff) + log_info(" %6d FEs used as LUT and DFF\n", lut_and_ff); + flush_cells(); +} + +void NgUltraPacker::pack_dff_chains(void) +{ + log_info("Pack DFF chains...\n"); + std::vector>> dff_chain_start; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_DFF)) + continue; + NetInfo *inp = ci.getPort(id_I); + if (!inp || (inp->driver.cell && inp->driver.cell->type.in(id_NX_DFF))) + continue; + int cnt = 0; + CellInfo *dff = &ci; + std::vector chain; + CellInfo *start_dff = &ci; + while (1) { + NetInfo *o = dff->getPort(id_O); + if (!o) + break; + if (o->users.entries() != 1) + break; + dff = (*o->users.begin()).cell; + if (dff->type == id_NX_DFF && (*o->users.begin()).port == id_I) { + if (cnt == 95) { // note that start_dff is also part of chain + dff_chain_start.push_back(make_pair(start_dff, chain)); + cnt = 0; + start_dff = dff; + chain.clear(); + } else { + chain.push_back(dff); + cnt++; + } + } else + break; + } + if (cnt) + dff_chain_start.push_back(make_pair(start_dff, chain)); + } + + int dff_only = 0, lut_and_ff = 0; + for (auto ch : dff_chain_start) { + CellInfo *dff = ch.first; + CellInfo *root = create_cell_ptr(id_BEYOND_FE, ctx->idf("%s$fe", dff->name.c_str(ctx))); + root->cluster = root->name; + NetInfo *net = dff->getPort(id_I); + if (net && net->driver.cell->type == id_NX_LUT && net->users.entries() == 1) { + CellInfo *lut = net->driver.cell; + if (!lut->params.count(id_lut_table)) + log_error("Cell '%s' missing lut_table\n", lut->name.c_str(ctx)); + lut_to_fe(lut, root, false, lut->params[id_lut_table]); + packed_cells.insert(lut->name); + dff_to_fe(dff, root, false); + packed_cells.insert(dff->name); + ++lut_and_ff; + } else { + dff_to_fe(dff, root, true); + packed_cells.insert(dff->name); + ++dff_only; + } + for (auto dff : ch.second) { + CellInfo *new_cell = create_cell_ptr(id_BEYOND_FE, ctx->idf("%s$fe", dff->name.c_str(ctx))); + dff_to_fe(dff, new_cell, true); + ++dff_only; + root->constr_children.push_back(new_cell); + new_cell->cluster = root->cluster; + new_cell->constr_z = PLACE_DFF_CHAIN; + packed_cells.insert(dff->name); + } + } + if (lut_and_ff) + log_info(" %6d FEs used as LUT and DFF\n", lut_and_ff); + if (dff_only) + log_info(" %6d FEs used as DFF only\n", dff_only); + flush_cells(); +} + +void NgUltraPacker::pack_lut_multi_dffs(void) +{ + log_info("Pack LUT-multi DFFs...\n"); + + int dff_only = 0, lut_and_ff = 0, bff_only = 0; + ; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_LUT)) + continue; + if (!ci.params.count(id_lut_table)) + log_error("Cell '%s' missing lut_table\n", ci.name.c_str(ctx)); + + NetInfo *o = ci.getPort(id_O); + if (o) { + if (o->users.entries() < 2) + continue; + + int cnt = 0; + for (auto u : o->users) { + if (u.cell->type == id_NX_DFF && u.cell->getPort(id_I) == o) + cnt++; + } + if (cnt < 2) + continue; + + CellInfo *root = create_cell_ptr(id_BEYOND_FE, ctx->idf("%s$fe", ci.name.c_str(ctx))); + packed_cells.insert(ci.name); + bind_attr_loc(root, &ci.attrs); + lut_to_fe(&ci, root, false, ci.params[id_lut_table]); + root->cluster = root->name; + + int max_use = (cnt == 4 && o->users.entries() == 4) ? 4 : 3; + bool use_bff = max_use != 4 && cnt >= 4; + int i = 0; + std::vector users; + for (auto u : o->users) { + if (u.cell->type == id_NX_DFF && u.cell->getPort(id_I) == o) { + if (i == 0) { + packed_cells.insert(u.cell->name); + dff_to_fe(u.cell, root, false); + ++lut_and_ff; + } else if (i < max_use) { + packed_cells.insert(u.cell->name); + CellInfo *new_cell = create_cell_ptr(id_BEYOND_FE, ctx->idf("%s$fe", u.cell->name.c_str(ctx))); + dff_to_fe(u.cell, new_cell, false); + root->constr_children.push_back(new_cell); + new_cell->cluster = root->cluster; + new_cell->constr_z = PLACE_LUT_CHAIN; + ++dff_only; + } else { + use_bff = true; + users.push_back(u); + u.cell->disconnectPort(u.port); + } + i++; + } else { + use_bff = true; + users.push_back(u); + u.cell->disconnectPort(u.port); + } + } + if (use_bff) { + CellInfo *new_cell = create_cell_ptr(id_BEYOND_FE, ctx->idf("%s$bff", ci.name.c_str(ctx))); + new_cell->params[id_dff_used] = Property(1, 1); + new_cell->setParam(id_type, Property("BFF")); + new_cell->connectPort(id_DI, o); + root->constr_children.push_back(new_cell); + new_cell->cluster = root->cluster; + new_cell->constr_z = PLACE_LUT_CHAIN; + bff_only++; + NetInfo *new_out = ctx->createNet(ctx->idf("%s$new", o->name.c_str(ctx))); + new_cell->connectPort(id_DO, new_out); + for (auto &user : users) { + user.cell->connectPort(user.port, new_out); + } + } + } + } + if (dff_only) + log_info(" %6d FEs used as DFF only\n", dff_only); + if (bff_only) + log_info(" %6d FEs used as BFF only\n", bff_only); + if (lut_and_ff) + log_info(" %6d FEs used as LUT and DFF\n", lut_and_ff); + flush_cells(); +} + +void NgUltraPacker::pack_lut_dffs(void) +{ + log_info("Pack LUT-DFFs...\n"); + + int lut_only = 0, lut_and_ff = 0; + std::vector lut_list; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_LUT)) + continue; + if (!ci.params.count(id_lut_table)) + log_error("Cell '%s' missing lut_table\n", ci.name.c_str(ctx)); + packed_cells.insert(ci.name); + lut_list.push_back(&ci); + } + for (auto ci : lut_list) { + CellInfo *packed = create_cell_ptr(id_BEYOND_FE, ctx->idf("%s$fe", ci->name.c_str(ctx))); + bind_attr_loc(packed, &ci->attrs); + + bool packed_dff = false; + NetInfo *o = ci->getPort(id_O); + if (o) { + CellInfo *dff = net_only_drives(ctx, o, is_dff, id_I, true); + if (dff) { + if (ctx->verbose) + log_info("found attached dff %s\n", dff->name.c_str(ctx)); + lut_to_fe(ci, packed, false, ci->params[id_lut_table]); + dff_to_fe(dff, packed, false); + ++lut_and_ff; + packed_cells.insert(dff->name); + if (ctx->verbose) + log_info("packed cell %s into %s\n", dff->name.c_str(ctx), packed->name.c_str(ctx)); + packed_dff = true; + } + } + if (!packed_dff) { + lut_to_fe(ci, packed, true, ci->params[id_lut_table]); + ++lut_only; + } + } + if (lut_only) + log_info(" %6d FEs used as LUT only\n", lut_only); + if (lut_and_ff) + log_info(" %6d FEs used as LUT and DFF\n", lut_and_ff); + flush_cells(); +} + +void NgUltraPacker::pack_dffs(void) +{ + int dff_only = 0; + std::vector dff_list; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_DFF, id_NX_BFF)) + continue; + packed_cells.insert(ci.name); + dff_list.push_back(&ci); + } + for (auto ci : dff_list) { + CellInfo *packed = create_cell_ptr(id_BEYOND_FE, ctx->idf("%s$fe", ci->name.c_str(ctx))); + dff_to_fe(ci, packed, true); + bind_attr_loc(packed, &ci->attrs); + ++dff_only; + } + if (dff_only) + log_info(" %6d FEs used as DFF only\n", dff_only); + flush_cells(); +} + +void NgUltraPacker::pack_iobs(void) +{ + log_info("Pack IOBs...\n"); + // Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis + for (auto &port : ctx->ports) { + if (!ctx->cells.count(port.first)) + log_error("Port '%s' doesn't seem to have a corresponding top level IO\n", ctx->nameOf(port.first)); + CellInfo *ci = ctx->cells.at(port.first).get(); + + PortRef top_port; + top_port.cell = nullptr; + bool is_npnr_iob = false; + + if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + // Might have an input buffer connected to it + is_npnr_iob = true; + NetInfo *o = ci->getPort(id_O); + if (o == nullptr) + ; + else if (o->users.entries() > 1) + log_error("Top level pin '%s' has multiple input buffers\n", ctx->nameOf(port.first)); + else if (o->users.entries() == 1) + top_port = *o->users.begin(); + } + if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) { + // Might have an output buffer connected to it + is_npnr_iob = true; + NetInfo *i = ci->getPort(id_I); + if (i != nullptr && i->driver.cell != nullptr) { + if (top_port.cell != nullptr) + log_error("Top level pin '%s' has multiple input/output buffers\n", ctx->nameOf(port.first)); + top_port = i->driver; + } + // Edge case of a bidirectional buffer driving an output pin + if (i->users.entries() > 2) { + log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); + } else if (i->users.entries() == 2) { + if (top_port.cell != nullptr) + log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first)); + for (auto &usr : i->users) { + if (usr.cell->type == ctx->id("$nextpnr_obuf") || usr.cell->type == ctx->id("$nextpnr_iobuf")) + continue; + top_port = usr; + break; + } + } + } + if (!is_npnr_iob) + log_error("Port '%s' doesn't seem to have a corresponding top level IO (internal cell type mismatch)\n", + ctx->nameOf(port.first)); + + if (top_port.cell == nullptr) { + log_info("Trimming port '%s' as it is unused.\n", ctx->nameOf(port.first)); + } else { + // Copy attributes to real IO buffer + for (auto &attrs : ci->attrs) + top_port.cell->attrs[attrs.first] = attrs.second; + for (auto ¶ms : ci->params) + top_port.cell->params[params.first] = params.second; + + // Make sure that top level net is set correctly + port.second.net = top_port.cell->ports.at(top_port.port).net; + } + // Now remove the nextpnr-inserted buffer + ci->disconnectPort(id_I); + ci->disconnectPort(id_O); + ctx->cells.erase(port.first); + } + std::vector to_update; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_IOB_I, id_NX_IOB_O, id_NX_IOB)) + continue; + if (ci.params.count(id_location) == 0) { + log_error("Unconstrained IO:%s\n", ctx->nameOf(&ci)); + } + std::string loc = ci.params.at(id_location).to_string(); + BelId bel = ctx->get_package_pin_bel(ctx->id(loc)); + if (bel == BelId()) + log_error("Unable to constrain IO '%s', device does not have a pin named '%s'\n", ci.name.c_str(ctx), + loc.c_str()); + log_info(" Constraining '%s' to pad '%s'\n", ci.name.c_str(ctx), loc.c_str()); + if (!ctx->checkBelAvail(bel)) { + log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci), ctx->nameOfBel(bel), + ctx->nameOf(ctx->getBoundBelCell(bel))); + } + + IdString new_type = id_IOP; + disconnect_if_gnd(&ci, id_T); + if (ci.getPort(id_T)) { + // In case T input is used must use different types + new_type = id_IOTP; + if (ci.type == id_NX_IOB_O) + new_type = id_OTP; + if (ci.type == id_NX_IOB_I) + new_type = id_ITP; + } else { + if (ci.type == id_NX_IOB_O) + new_type = id_OP; + if (ci.type == id_NX_IOB_I) + new_type = id_IP; + } + ci.type = new_type; + ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_LOCKED); + if (!ctx->isValidBelForCellType(new_type, bel)) + log_error("Invalid type of IO for specified location %s %s.\n", new_type.c_str(ctx), + ctx->getBelType(bel).c_str(ctx)); + to_update.push_back(&ci); + } + int bfr_added = 0; + int dfr_added = 0; + int ddfr_added = 0; + for (auto cell : to_update) { + NetInfo *c_net = cell->getPort(id_C); + if (!c_net) + log_error("C input of IO primitive %s must be connected.\n", cell->name.c_str(ctx)); + if (c_net->name == ctx->id("$PACKER_GND") && !cell->getPort(id_O)) { + log_warning("O port of IO primitive %s must be connected. Removing cell.\n", cell->name.c_str(ctx)); + packed_cells.emplace(cell->name); + continue; + } + if (c_net->name == ctx->id("$PACKER_VCC") && !cell->getPort(id_I)) { + log_warning("I port of IO primitive %s must be connected. Removing cell.\n", cell->name.c_str(ctx)); + packed_cells.emplace(cell->name); + continue; + } + if (!cell->getPort(id_I) && !cell->getPort(id_O)) { + log_warning("I or O port of IO primitive %s must be connected. Removing cell.\n", cell->name.c_str(ctx)); + packed_cells.emplace(cell->name); + continue; + } + + { + CellInfo *iod = net_driven_by(ctx, c_net, is_dfr, id_O); + if (iod && c_net->users.entries() != 1) + log_error("NX_DFR '%s' can only directly drive IOB.\n", iod->name.c_str(ctx)); + if (!iod) { + iod = net_driven_by(ctx, c_net, is_ddfr, id_O); + if (iod && c_net->users.entries() != 1) + log_error("NX_DDFR '%s' can only directly drive IOB.\n", iod->name.c_str(ctx)); + if (!iod) { + bfr_added++; + iod = create_cell_ptr(id_BFR, ctx->idf("%s$iod_cd", cell->name.c_str(ctx))); + NetInfo *new_out = ctx->createNet(ctx->idf("%s$O", iod->name.c_str(ctx))); + iod->setParam(id_iobname, str_or_default(cell->params, id_iobname, "")); + cell->disconnectPort(id_C); + if (c_net->name == ctx->id("$PACKER_GND")) + iod->setParam(id_mode, Property(0, 2)); + else if (c_net->name == ctx->id("$PACKER_VCC")) + iod->setParam(id_mode, Property(1, 2)); + else { + iod->connectPort(id_I, c_net); + iod->setParam(id_mode, Property(2, 2)); + iod->setParam(id_data_inv, Property(0, 1)); + } + iod->connectPort(id_O, new_out); + cell->connectPort(id_C, new_out); + } else { + ddfr_added++; + iod->type = id_DDFR; + iod->setParam(id_iobname, str_or_default(cell->params, id_iobname, "")); + iod->setParam(id_path, Property(2, 2)); + ddfr_rewrite(iod); + disconnect_unused(iod, id_O2); + disconnect_if_gnd(iod, id_L); + disconnect_if_gnd(iod, id_R); + } + } else { + dfr_added++; + iod->type = id_DFR; + iod->setParam(id_iobname, str_or_default(cell->params, id_iobname, "")); + dff_rewrite(iod); + } + Loc cd_loc = cell->getLocation(); + cd_loc.z += 3; + BelId bel = ctx->getBelByLocation(cd_loc); + ctx->bindBel(bel, iod, PlaceStrength::STRENGTH_LOCKED); + } + NetInfo *i_net = cell->getPort(id_I); + if (i_net) { + CellInfo *iod = net_driven_by(ctx, i_net, is_dfr, id_O); + if (iod && i_net->users.entries() != 1) + log_error("NX_DFR '%s' can only directly drive IOB.\n", iod->name.c_str(ctx)); + if (!iod) { + iod = net_driven_by(ctx, i_net, is_ddfr, id_O); + if (iod && i_net->users.entries() != 1) + log_error("NX_DDFR '%s' can only directly drive IOB.\n", iod->name.c_str(ctx)); + if (!iod) { + bfr_added++; + iod = create_cell_ptr(id_BFR, ctx->idf("%s$iod_od", cell->name.c_str(ctx))); + NetInfo *new_out = ctx->createNet(ctx->idf("%s$O", iod->name.c_str(ctx))); + iod->setParam(id_iobname, str_or_default(cell->params, id_iobname, "")); + cell->disconnectPort(id_I); + if (i_net->name == ctx->id("$PACKER_GND")) + iod->setParam(id_mode, Property(0, 2)); + else if (i_net->name == ctx->id("$PACKER_VCC")) + iod->setParam(id_mode, Property(1, 2)); + else { + iod->connectPort(id_I, i_net); + iod->setParam(id_mode, Property(2, 2)); + iod->setParam(id_data_inv, Property(0, 1)); + } + iod->connectPort(id_O, new_out); + cell->connectPort(id_I, new_out); + } else { + ddfr_added++; + iod->type = id_DDFR; + iod->setParam(id_iobname, str_or_default(cell->params, id_iobname, "")); + iod->setParam(id_path, Property(0, 2)); + ddfr_rewrite(iod); + disconnect_unused(iod, id_O2); + disconnect_if_gnd(iod, id_L); + disconnect_if_gnd(iod, id_R); + } + } else { + dfr_added++; + iod->type = id_DFR; + iod->setParam(id_iobname, str_or_default(cell->params, id_iobname, "")); + dff_rewrite(iod); + } + Loc cd_loc = cell->getLocation(); + cd_loc.z += 2; + BelId bel = ctx->getBelByLocation(cd_loc); + ctx->bindBel(bel, iod, PlaceStrength::STRENGTH_LOCKED); + } + + NetInfo *o_net = cell->getPort(id_O); + if (o_net) { + CellInfo *iod = net_only_drives(ctx, o_net, is_dfr, id_I, true); + if (!(o_net->users.entries() == 1 && (*o_net->users.begin()).cell->type == id_NX_IOM_U)) { + bool bfr_mode = false; + bool ddfr_mode = false; + if (!iod) { + iod = net_only_drives(ctx, o_net, is_ddfr, id_I, true); + if (!iod) { + bfr_added++; + iod = create_cell_ptr(id_BFR, ctx->idf("%s$iod_id", cell->name.c_str(ctx))); + NetInfo *new_in = ctx->createNet(ctx->idf("%s$I", iod->name.c_str(ctx))); + iod->setParam(id_iobname, str_or_default(cell->params, id_iobname, "")); + cell->disconnectPort(id_O); + iod->connectPort(id_O, o_net); + iod->setParam(id_mode, Property(2, 2)); + iod->setParam(id_data_inv, Property(0, 1)); + iod->connectPort(id_I, new_in); + cell->connectPort(id_O, new_in); + bfr_mode = true; + } else { + ddfr_mode = true; + ddfr_added++; + iod->type = id_DDFR; + iod->setParam(id_iobname, str_or_default(cell->params, id_iobname, "")); + iod->setParam(id_path, Property(1, 2)); + ddfr_rewrite(iod); + disconnect_if_gnd(iod, id_I2); + disconnect_if_gnd(iod, id_L); + disconnect_if_gnd(iod, id_R); + } + } else { + dfr_added++; + iod->type = id_DFR; + iod->setParam(id_iobname, str_or_default(cell->params, id_iobname, "")); + dff_rewrite(iod); + } + Loc cd_loc = cell->getLocation(); + cd_loc.z += 1; + BelId bel = ctx->getBelByLocation(cd_loc); + ctx->bindBel(bel, iod, PlaceStrength::STRENGTH_LOCKED); + + // Depending of DDFR mode we must use one of dedicated routes (ITCs) + if (!ddfr_mode && ctx->getBelType(bel) == id_DDFR) { + WireId dwire = ctx->getBelPinWire(bel, id_O); + for (PipId pip : ctx->getPipsDownhill(dwire)) { + const auto &extra_data = *uarch->pip_extra_data(pip); + if (!extra_data.name) + continue; + if (extra_data.type != PipExtra::PIP_EXTRA_MUX) + continue; + if (bfr_mode && extra_data.input == 2) { + uarch->blocked_pips.emplace(pip); + } else if (!bfr_mode && extra_data.input == 1) { + uarch->blocked_pips.emplace(pip); + } + } + } + } + } + } + if (ddfr_added) + log_info(" %6d DDFRs used as DDFR\n", ddfr_added); + if (dfr_added) + log_info(" %6d DFRs/DDFRs used as DFR\n", dfr_added); + if (bfr_added) + log_info(" %6d DFRs/DDFRs used as BFR\n", bfr_added); + flush_cells(); +} + +void NgUltraPacker::pack_ioms(void) +{ + log_info("Pack IOMs...\n"); + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_IOM_U)) + continue; + ci.type = id_IOM; + IdString iob; + for (auto &port : ci.ports) { + if (port.second.type == PORT_IN && port.second.net != nullptr) { + if (port.second.net->name == ctx->id("$PACKER_GND")) + ci.disconnectPort(port.first); + else if (port.second.net->driver.cell != nullptr && + port.second.net->driver.cell->type.in(id_IP, id_OP, id_IOP, id_ITP, id_OTP, id_IOTP)) { + IdString loc = uarch->tile_name_id(port.second.net->driver.cell->bel.tile); + if (iob != IdString() && loc != iob) { + log_error("Unable to constrain IOM '%s', connection to multiple IO banks exist.\n", + ci.name.c_str(ctx)); + } + iob = loc; + } + } + } + if (iob == IdString()) + log_error("Unable to constrain IOM '%s', no connection to nearby IO banks found.\n", ci.name.c_str(ctx)); + log_info(" Constraining '%s' to bank '%s'\n", ci.name.c_str(ctx), iob.c_str(ctx)); + BelId bel = uarch->iom_bels[iob]; + if (!ctx->checkBelAvail(bel)) { + log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci), ctx->nameOfBel(bel), + ctx->nameOf(ctx->getBoundBelCell(bel))); + } + ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_LOCKED); + } +} + +void NgUltraPacker::pack_cy_input_and_output(CellInfo *cy, IdString cluster, IdString in_port, IdString out_port, + int placer, int &lut_only, int &lut_and_ff, int &dff_only) +{ + CellInfo *fe = create_cell_ptr(id_BEYOND_FE, ctx->idf("%s$%s", cy->name.c_str(ctx), in_port.c_str(ctx))); + NetInfo *net = cy->getPort(in_port); + if (net) { + if (net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) { + fe->params[id_lut_table] = Property((net->name == ctx->id("$PACKER_GND")) ? 0x0000 : 0xffff, 16); + fe->params[id_lut_used] = Property(1, 1); + cy->disconnectPort(in_port); + NetInfo *new_out = ctx->createNet(ctx->idf("%s$o", fe->name.c_str(ctx))); + fe->connectPort(id_LO, new_out); + cy->connectPort(in_port, new_out); + } else { + fe->params[id_lut_table] = Property(0xaaaa, 16); + fe->params[id_lut_used] = Property(1, 1); + cy->disconnectPort(in_port); + NetInfo *new_out = ctx->createNet(ctx->idf("%s$o", fe->name.c_str(ctx))); + fe->connectPort(id_I1, net); + fe->connectPort(id_LO, new_out); + cy->connectPort(in_port, new_out); + } + lut_only++; + } + net = cy->getPort(out_port); + CellInfo *dff = net_only_drives(ctx, net, is_dff, id_I, true); + if (dff) { + if (ctx->verbose) + log_info("found attached dff %s\n", dff->name.c_str(ctx)); + dff_to_fe(dff, fe, false); + packed_cells.insert(dff->name); + if (net) { + lut_only--; + lut_and_ff++; + } else + dff_only++; + } else { + fe->timing_index = ctx->get_cell_timing_idx(id_BEYOND_FE_LUT); + } + fe->cluster = cluster; + fe->constr_z = placer; + cy->constr_children.push_back(fe); +} + +void NgUltraPacker::exchange_if_constant(CellInfo *cell, IdString input1, IdString input2) +{ + NetInfo *net1 = cell->getPort(input1); + NetInfo *net2 = cell->getPort(input2); + // GND on A + if (net1->name.in(ctx->id("$PACKER_GND"))) { + return; + } + // VCC on A -> exchange + // if GND on B and not on A -> exchange + if (net1->name.in(ctx->id("$PACKER_VCC")) || net2->name.in(ctx->id("$PACKER_GND"))) { + cell->disconnectPort(input1); + cell->disconnectPort(input2); + cell->connectPort(input1, net2); + cell->connectPort(input2, net1); + } +} + +void NgUltraPacker::pack_cys(void) +{ + log_info("Packing carries..\n"); + std::vector root_cys; + int lut_only = 0, lut_and_ff = 0, dff_only = 0; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type != id_NX_CY) + continue; + bind_attr_loc(ci, &ci->attrs); + NetInfo *ci_net = ci->getPort(id_CI); + if (!ci_net || !ci_net->driver.cell || ci_net->driver.cell->type != id_NX_CY) { + root_cys.push_back(ci); + } + for (int i = 1; i <= 4; i++) { + connect_gnd_if_unconnected(ci, ctx->idf("A%d", i), false); + connect_gnd_if_unconnected(ci, ctx->idf("B%d", i), false); + exchange_if_constant(ci, ctx->idf("A%d", i), ctx->idf("B%d", i)); + } + NetInfo *co_net = ci->getPort(id_CO); + if (!co_net) { + disconnect_unused(ci, id_CO); + // Disconnect unused ports on last CY in chain + // at least id_A1 and id_B1 will be connected + // Reverse direction, must stop if used, then + // rest is used as well + if (!ci->getPort(id_S4)) { + disconnect_unused(ci, id_S4); + disconnect_unused(ci, id_A4); + disconnect_unused(ci, id_B4); + if (!ci->getPort(id_S3)) { + disconnect_unused(ci, id_S3); + disconnect_unused(ci, id_A3); + disconnect_unused(ci, id_B3); + if (!ci->getPort(id_S2)) { + disconnect_unused(ci, id_S2); + disconnect_unused(ci, id_A2); + disconnect_unused(ci, id_B2); + }; + } + } + } + } + + std::vector> groups; + for (auto root : root_cys) { + std::vector group; + CellInfo *cy = root; + group.push_back(cy); + while (true) { + NetInfo *co_net = cy->getPort(id_CO); + if (co_net && co_net->users.entries() > 0) { + cy = (*co_net->users.begin()).cell; + if (cy->type != id_NX_CY || co_net->users.entries() != 1) { + log_warning("Cells %s CO output connected to:\n", group.back()->name.c_str(ctx)); + for (auto user : co_net->users) + log_warning("\t%s of type %s\n", user.cell->name.c_str(ctx), user.cell->type.c_str(ctx)); + log_error("NX_CY can only be chained with one other NX_CY cell\n"); + } + group.push_back(cy); + } else + break; + } + groups.push_back(group); + } + + for (auto &grp : groups) { + CellInfo *root = grp.front(); + root->type = id_CY; + root->cluster = root->name; + if (grp.size() > 24) + log_error("NX_CY chains are limited to contain 24 elements maximum.\n"); + + for (int i = 0; i < int(grp.size()); i++) { + CellInfo *cy = grp.at(i); + cy->type = id_CY; + if (i != 0) { + cy->cluster = root->name; + root->constr_children.push_back(cy); + cy->constr_z = PLACE_CY_CHAIN; + } + pack_cy_input_and_output(cy, root->name, id_B1, id_S1, PLACE_CY_FE1, lut_only, lut_and_ff, dff_only); + // Must check for B input otherwise we have bogus FEs + if (cy->getPort(id_B2)) + pack_cy_input_and_output(cy, root->name, id_B2, id_S2, PLACE_CY_FE2, lut_only, lut_and_ff, dff_only); + if (cy->getPort(id_B3)) + pack_cy_input_and_output(cy, root->name, id_B3, id_S3, PLACE_CY_FE3, lut_only, lut_and_ff, dff_only); + if (cy->getPort(id_B4)) + pack_cy_input_and_output(cy, root->name, id_B4, id_S4, PLACE_CY_FE4, lut_only, lut_and_ff, dff_only); + NetInfo *net = cy->getPort(id_CI); + if (net) { + if (net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) { + // Constant driver for CI is configuration + cy->disconnectPort(id_CI); + } else { + if (net->driver.cell && net->driver.cell->type != id_CY) + log_error("CI must be constant or driven by CO in cell '%s'\n", cy->name.c_str(ctx)); + } + } + } + } + if (lut_only) + log_info(" %6d FEs used as LUT only\n", lut_only); + if (lut_and_ff) + log_info(" %6d FEs used as LUT and DFF\n", lut_and_ff); + if (dff_only) + log_info(" %6d FEs used as DFF only\n", dff_only); + flush_cells(); +} + +void NgUltraPacker::pack_xrf_input_and_output(CellInfo *xrf, IdString cluster, IdString in_port, IdString out_port, + ClusterPlacement placement, int &lut_only, int &lut_and_ff, int &dff_only) +{ + NetInfo *net = xrf->getPort(in_port); + NetInfo *net_out = nullptr; + if (out_port != IdString()) { + net_out = xrf->getPort(out_port); + if (net_out && net_out->users.entries() == 0) { + xrf->disconnectPort(out_port); + net_out = nullptr; + } + } + if (!net && !net_out) + return; + IdString name = in_port; + if (name == IdString()) + name = out_port; + CellInfo *fe = create_cell_ptr(id_BEYOND_FE, ctx->idf("%s$%s", xrf->name.c_str(ctx), name.c_str(ctx))); + + if (net) { + if (net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) { + fe->params[id_lut_table] = Property((net->name == ctx->id("$PACKER_GND")) ? 0x0000 : 0xffff, 16); + fe->params[id_lut_used] = Property(1, 1); + xrf->disconnectPort(in_port); + NetInfo *new_out = ctx->createNet(ctx->idf("%s$o", fe->name.c_str(ctx))); + fe->connectPort(id_LO, new_out); + xrf->connectPort(in_port, new_out); + } else { + CellInfo *lut = net_driven_by(ctx, net, is_lut, id_O); + if (lut && net->users.entries() == 1) { + if (!lut->params.count(id_lut_table)) + log_error("Cell '%s' missing lut_table\n", lut->name.c_str(ctx)); + lut_to_fe(lut, fe, false, lut->params[id_lut_table]); + packed_cells.insert(lut->name); + } else { + fe->params[id_lut_table] = Property(0xaaaa, 16); + fe->params[id_lut_used] = Property(1, 1); + xrf->disconnectPort(in_port); + NetInfo *new_out = ctx->createNet(ctx->idf("%s$o", fe->name.c_str(ctx))); + fe->connectPort(id_I1, net); + fe->connectPort(id_LO, new_out); + xrf->connectPort(in_port, new_out); + } + } + lut_only++; + } + if (net_out) { + CellInfo *dff = net_only_drives(ctx, net_out, is_dff, id_I, true); + if (dff) { + if (ctx->verbose) + log_info("found attached dff %s\n", dff->name.c_str(ctx)); + dff_to_fe(dff, fe, false); + packed_cells.insert(dff->name); + if (net) { + lut_only--; + lut_and_ff++; + } else + dff_only++; + } + } + fe->cluster = cluster; + fe->constr_z = placement; + xrf->constr_children.push_back(fe); +} + +void NgUltraPacker::disconnect_unused(CellInfo *cell, IdString port) +{ + NetInfo *net = cell->getPort(port); + if (net) { + // NanoXplore tools usually connects 0 to unused port, no need to warn + // Sometimes there is unused nets, so number of entries is zero + if (net->users.entries() != 0 && !net->name.in(ctx->id("$PACKER_GND"))) + log_warning("Disconnected unused port '%s' from cell '%s'.\n", port.c_str(ctx), cell->name.c_str(ctx)); + cell->disconnectPort(port); + } +} + +void NgUltraPacker::pack_rfs(void) +{ + log_info("Packing RFs..\n"); + int lut_only = 0, lut_and_ff = 0, dff_only = 0; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_RFB_U)) + continue; + int mode = int_or_default(ci.params, id_mode, 0); + switch (mode) { + case 0: + ci.type = id_RF; + break; + case 1: + ci.type = id_RFSP; + break; + case 2: + ci.type = id_XHRF; + break; + case 3: + ci.type = id_XWRF; + break; + case 4: + ci.type = id_XPRF; + break; + default: + log_error("Unknown mode %d for cell '%s'.\n", mode, ci.name.c_str(ctx)); + } + ci.cluster = ci.name; + bind_attr_loc(&ci, &ci.attrs); + + for (int i = 1; i <= 18; i++) { + connect_gnd_if_unconnected(&ci, ctx->idf("I%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("I%d", i), ctx->idf("O%d", i), + ClusterPlacement(PLACE_XRF_I1 + i - 1), lut_only, lut_and_ff, dff_only); + } + if (mode != 1) { + for (int i = 1; i <= 5; i++) { + connect_gnd_if_unconnected(&ci, ctx->idf("RA%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("RA%d", i), IdString(), + ClusterPlacement(PLACE_XRF_RA1 + i - 1), lut_only, lut_and_ff, dff_only); + } + } else { + // SPREG mode does not use RA inputs + for (int i = 1; i <= 5; i++) + disconnect_unused(&ci, ctx->idf("RA%d", i)); + } + + if (mode == 2 || mode == 4) { + connect_gnd_if_unconnected(&ci, id_RA6); + pack_xrf_input_and_output(&ci, ci.name, id_RA6, IdString(), PLACE_XRF_RA6, lut_only, lut_and_ff, dff_only); + } else { + disconnect_unused(&ci, id_RA6); + } + + if (mode == 4) { + for (int i = 7; i <= 10; i++) { + connect_gnd_if_unconnected(&ci, ctx->idf("RA%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("RA%d", i), IdString(), + ClusterPlacement(PLACE_XRF_RA1 + i - 1), lut_only, lut_and_ff, dff_only); + } + } else { + for (int i = 7; i <= 10; i++) + disconnect_unused(&ci, ctx->idf("RA%d", i)); + } + + for (int i = 1; i <= 5; i++) { + connect_gnd_if_unconnected(&ci, ctx->idf("WA%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("WA%d", i), IdString(), + ClusterPlacement(PLACE_XRF_WA1 + i - 1), lut_only, lut_and_ff, dff_only); + } + + if (mode == 2) { + connect_gnd_if_unconnected(&ci, id_WA6); + pack_xrf_input_and_output(&ci, ci.name, id_WA6, IdString(), PLACE_XRF_WA6, lut_only, lut_and_ff, dff_only); + } else { + disconnect_unused(&ci, id_WA6); + } + + connect_gnd_if_unconnected(&ci, id_WE); + pack_xrf_input_and_output(&ci, ci.name, id_WE, IdString(), PLACE_XRF_WE, lut_only, lut_and_ff, dff_only); + + disconnect_if_gnd(&ci, id_WEA); + pack_xrf_input_and_output(&ci, ci.name, id_WEA, IdString(), PLACE_XRF_WEA, lut_only, lut_and_ff, dff_only); + + if (mode == 3) { + for (int i = 19; i <= 36; i++) { + connect_gnd_if_unconnected(&ci, ctx->idf("I%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("I%d", i), ctx->idf("O%d", i), + ClusterPlacement(PLACE_XRF_I1 + i - 1), lut_only, lut_and_ff, dff_only); + } + } else if (mode == 4) { + for (int i = 19; i <= 36; i++) { + disconnect_unused(&ci, ctx->idf("I%d", i)); + pack_xrf_input_and_output(&ci, ci.name, IdString(), ctx->idf("O%d", i), + ClusterPlacement(PLACE_XRF_I1 + i - 1), lut_only, lut_and_ff, dff_only); + } + } else { + for (int i = 19; i <= 36; i++) { + disconnect_unused(&ci, ctx->idf("I%d", i)); + disconnect_unused(&ci, ctx->idf("O%d", i)); + } + } + + if (mode > 1) { + // XRF + ci.ports[id_WCK1].name = id_WCK1; + ci.ports[id_WCK1].type = PORT_IN; + ci.ports[id_WCK2].name = id_WCK2; + ci.ports[id_WCK2].type = PORT_IN; + NetInfo *net = ci.getPort(id_WCK); + if (net) { + ci.disconnectPort(id_WCK); + + ci.connectPort(id_WCK1, net); + ci.connectPort(id_WCK2, net); + } + } + } + if (lut_only) + log_info(" %6d FEs used as LUT only\n", lut_only); + if (lut_and_ff) + log_info(" %6d FEs used as LUT and DFF\n", lut_and_ff); + if (dff_only) + log_info(" %6d FEs used as DFF only\n", dff_only); + flush_cells(); +} + +void NgUltraPacker::pack_cdcs(void) +{ + log_info("Packing CDCs..\n"); + int lut_only = 0, lut_and_ff = 0, dff_only = 0; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_CDC_U)) + continue; + int mode = int_or_default(ci.params, id_mode, 0); + switch (mode) { + case 0: + ci.type = id_DDE; + break; + case 1: + ci.type = id_TDE; + break; + case 2: + ci.type = id_CDC; + break; + case 3: + ci.type = id_BGC; + break; + case 4: + ci.type = id_GBC; + break; + case 5: + ci.type = id_XCDC; + break; + default: + log_error("Unknown mode %d for cell '%s'.\n", mode, ci.name.c_str(ctx)); + } + ci.cluster = ci.name; + + // If unconnected, connect GND to inputs that are actually used as outputs + for (int i = 1; i <= 6; i++) { + if (ci.getPort(ctx->idf("AO%d", i))) { + connect_gnd_if_unconnected(&ci, ctx->idf("AI%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("AI%d", i), ctx->idf("AO%d", i), + ClusterPlacement(PLACE_CDC_AI1 + i - 1), lut_only, lut_and_ff, dff_only); + } else + disconnect_unused(&ci, ctx->idf("AI%d", i)); + if (ci.getPort(ctx->idf("BO%d", i))) { + connect_gnd_if_unconnected(&ci, ctx->idf("BI%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("BI%d", i), ctx->idf("BO%d", i), + ClusterPlacement(PLACE_CDC_BI1 + i - 1), lut_only, lut_and_ff, dff_only); + } else + disconnect_unused(&ci, ctx->idf("BI%d", i)); + if (ci.type.in(id_XCDC)) { + if (ci.getPort(ctx->idf("CO%d", i))) { + connect_gnd_if_unconnected(&ci, ctx->idf("CI%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("CI%d", i), ctx->idf("CO%d", i), + ClusterPlacement(PLACE_CDC_CI1 + i - 1), lut_only, lut_and_ff, dff_only); + } else + disconnect_unused(&ci, ctx->idf("CI%d", i)); + if (ci.getPort(ctx->idf("DO%d", i))) { + connect_gnd_if_unconnected(&ci, ctx->idf("DI%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("DI%d", i), ctx->idf("DO%d", i), + ClusterPlacement(PLACE_CDC_DI1 + i - 1), lut_only, lut_and_ff, dff_only); + } else + disconnect_unused(&ci, ctx->idf("DI%d", i)); + } + } + + // Remove inputs and outputs that are not used for specific types + if (ci.type.in(id_BGC, id_GBC)) { + disconnect_unused(&ci, id_CK1); + disconnect_unused(&ci, id_CK2); + disconnect_unused(&ci, id_ADRSTI); + disconnect_unused(&ci, id_ADRSTO); + disconnect_unused(&ci, id_BDRSTI); + disconnect_unused(&ci, id_BDRSTO); + } else { + connect_gnd_if_unconnected(&ci, id_ADRSTI); + pack_xrf_input_and_output(&ci, ci.name, id_ADRSTI, id_ADRSTO, PLACE_CDC_ADRSTI, lut_only, lut_and_ff, + dff_only); + connect_gnd_if_unconnected(&ci, id_BDRSTI); + pack_xrf_input_and_output(&ci, ci.name, id_BDRSTI, id_BDRSTO, PLACE_CDC_BDRSTI, lut_only, lut_and_ff, + dff_only); + } + if (ci.type.in(id_BGC, id_GBC, id_DDE)) { + disconnect_unused(&ci, id_ASRSTI); + disconnect_unused(&ci, id_ASRSTO); + disconnect_unused(&ci, id_BSRSTI); + disconnect_unused(&ci, id_BSRSTO); + } else { + connect_gnd_if_unconnected(&ci, id_ASRSTI); + pack_xrf_input_and_output(&ci, ci.name, id_ASRSTI, id_ASRSTO, PLACE_CDC_ASRSTI, lut_only, lut_and_ff, + dff_only); + connect_gnd_if_unconnected(&ci, id_BSRSTI); + pack_xrf_input_and_output(&ci, ci.name, id_BSRSTI, id_BSRSTO, PLACE_CDC_BSRSTI, lut_only, lut_and_ff, + dff_only); + } + + // Only XCDC is using these ports, remove from others if used + if (!ci.type.in(id_XCDC)) { + disconnect_unused(&ci, id_CDRSTI); + disconnect_unused(&ci, id_CDRSTO); + for (int i = 1; i <= 6; i++) { + disconnect_unused(&ci, ctx->idf("CI%d", i)); + disconnect_unused(&ci, ctx->idf("CO%d", i)); + } + disconnect_unused(&ci, id_CSRSTI); + disconnect_unused(&ci, id_CSRSTO); + + disconnect_unused(&ci, id_DDRSTI); + disconnect_unused(&ci, id_DDRSTO); + for (int i = 1; i <= 6; i++) { + disconnect_unused(&ci, ctx->idf("DI%d", i)); + disconnect_unused(&ci, ctx->idf("DO%d", i)); + } + disconnect_unused(&ci, id_DSRSTI); + disconnect_unused(&ci, id_DSRSTO); + } else { + connect_gnd_if_unconnected(&ci, id_CDRSTI); + pack_xrf_input_and_output(&ci, ci.name, id_CDRSTI, id_CDRSTO, PLACE_CDC_CDRSTI, lut_only, lut_and_ff, + dff_only); + connect_gnd_if_unconnected(&ci, id_DDRSTI); + pack_xrf_input_and_output(&ci, ci.name, id_DDRSTI, id_DDRSTO, PLACE_CDC_DDRSTI, lut_only, lut_and_ff, + dff_only); + connect_gnd_if_unconnected(&ci, id_CSRSTI); + pack_xrf_input_and_output(&ci, ci.name, id_CSRSTI, id_CSRSTO, PLACE_CDC_CSRSTI, lut_only, lut_and_ff, + dff_only); + connect_gnd_if_unconnected(&ci, id_DSRSTI); + pack_xrf_input_and_output(&ci, ci.name, id_DSRSTI, id_DSRSTO, PLACE_CDC_DSRSTI, lut_only, lut_and_ff, + dff_only); + } + } + if (lut_only) + log_info(" %6d FEs used as LUT only\n", lut_only); + if (lut_and_ff) + log_info(" %6d FEs used as LUT and DFF\n", lut_and_ff); + if (dff_only) + log_info(" %6d FEs used as DFF only\n", dff_only); + flush_cells(); +} + +void NgUltraPacker::pack_fifos(void) +{ + log_info("Packing FIFOs..\n"); + int lut_only = 0, lut_and_ff = 0, dff_only = 0; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_FIFO_U)) + continue; + int mode = int_or_default(ci.params, id_mode, 0); + switch (mode) { + case 0: + ci.type = id_FIFO; + break; + case 1: + ci.type = id_XHFIFO; + break; + case 2: + ci.type = id_XWFIFO; + break; + default: + log_error("Unknown mode %d for cell '%s'.\n", mode, ci.name.c_str(ctx)); + } + ci.cluster = ci.name; + bool use_write_arst = bool_or_default(ci.params, id_use_write_arst, false); + bool use_read_arst = bool_or_default(ci.params, id_use_read_arst, false); + + int rsti = (ci.type == id_FIFO) ? 2 : 4; + for (int i = 1; i <= rsti; i++) { + IdString port = ctx->idf("WRSTI%d", i); + ci.ports[port].name = port; + ci.ports[port].type = PORT_IN; + port = ctx->idf("RRSTI%d", i); + ci.ports[port].name = port; + ci.ports[port].type = PORT_IN; + } + + if (use_write_arst) { + IdString port = id_WRSTI; + connect_gnd_if_unconnected(&ci, port); + NetInfo *wrsti_net = ci.getPort(port); + ci.disconnectPort(port); + for (int i = 1; i <= rsti; i++) + ci.connectPort(ctx->idf("WRSTI%d", i), wrsti_net); + if (mode != 0) + disconnect_unused(&ci, id_WRSTO); + pack_xrf_input_and_output(&ci, ci.name, id_WRSTI1, id_WRSTO, PLACE_FIFO_WRSTI1, lut_only, lut_and_ff, + dff_only); + pack_xrf_input_and_output(&ci, ci.name, id_WRSTI2, IdString(), PLACE_FIFO_WRSTI2, lut_only, lut_and_ff, + dff_only); + if (mode != 0) { + pack_xrf_input_and_output(&ci, ci.name, id_WRSTI3, IdString(), PLACE_FIFO_WRSTI3, lut_only, lut_and_ff, + dff_only); + pack_xrf_input_and_output(&ci, ci.name, id_WRSTI4, IdString(), PLACE_FIFO_WRSTI4, lut_only, lut_and_ff, + dff_only); + } + } else { + disconnect_unused(&ci, id_WRSTI); + } + if (use_read_arst) { + IdString port = id_RRSTI; + connect_gnd_if_unconnected(&ci, port); + NetInfo *rrsti_net = ci.getPort(port); + ci.disconnectPort(port); + for (int i = 1; i <= rsti; i++) + ci.connectPort(ctx->idf("RRSTI%d", i), rrsti_net); + if (mode != 0) + disconnect_unused(&ci, id_RRSTO); + pack_xrf_input_and_output(&ci, ci.name, id_RRSTI1, id_RRSTO, PLACE_FIFO_RRSTI1, lut_only, lut_and_ff, + dff_only); + pack_xrf_input_and_output(&ci, ci.name, id_RRSTI2, IdString(), PLACE_FIFO_RRSTI2, lut_only, lut_and_ff, + dff_only); + if (mode != 0) { + pack_xrf_input_and_output(&ci, ci.name, id_RRSTI3, IdString(), PLACE_FIFO_RRSTI3, lut_only, lut_and_ff, + dff_only); + pack_xrf_input_and_output(&ci, ci.name, id_RRSTI4, IdString(), PLACE_FIFO_RRSTI4, lut_only, lut_and_ff, + dff_only); + } + } else { + disconnect_unused(&ci, id_RRSTI); + } + + for (int i = 1; i <= 18; i++) { + connect_gnd_if_unconnected(&ci, ctx->idf("I%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("I%d", i), ctx->idf("O%d", i), + ClusterPlacement(PLACE_FIFO_I1 + i - 1), lut_only, lut_and_ff, dff_only); + } + + if (mode == 0) { + for (int i = 19; i <= 36; i++) { + disconnect_unused(&ci, ctx->idf("I%d", i)); + disconnect_unused(&ci, ctx->idf("O%d", i)); + } + } else { + for (int i = 19; i <= 36; i++) { + connect_gnd_if_unconnected(&ci, ctx->idf("I%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("I%d", i), ctx->idf("O%d", i), + ClusterPlacement(PLACE_FIFO_I1 + i - 1), lut_only, lut_and_ff, dff_only); + } + } + for (int i = 1; i <= 6; i++) { + connect_gnd_if_unconnected(&ci, ctx->idf("RAI%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("RAI%d", i), ctx->idf("RAO%d", i), + ClusterPlacement(PLACE_FIFO_RAI1 + i - 1), lut_only, lut_and_ff, dff_only); + + connect_gnd_if_unconnected(&ci, ctx->idf("WAI%d", i)); + pack_xrf_input_and_output(&ci, ci.name, ctx->idf("WAI%d", i), ctx->idf("WAO%d", i), + ClusterPlacement(PLACE_FIFO_WAI1 + i - 1), lut_only, lut_and_ff, dff_only); + } + + if (mode == 0) { + disconnect_unused(&ci, id_RAI7); + disconnect_unused(&ci, id_WAI7); + } else { + connect_gnd_if_unconnected(&ci, id_RAI7); + pack_xrf_input_and_output(&ci, ci.name, id_RAI7, id_RAO7, PLACE_FIFO_RAI7, lut_only, lut_and_ff, dff_only); + + connect_gnd_if_unconnected(&ci, id_WAI7); + pack_xrf_input_and_output(&ci, ci.name, id_WAI7, id_WAO7, PLACE_FIFO_WAI7, lut_only, lut_and_ff, dff_only); + } + + connect_gnd_if_unconnected(&ci, id_WE); + pack_xrf_input_and_output(&ci, ci.name, id_WE, IdString(), PLACE_FIFO_WE, lut_only, lut_and_ff, dff_only); + + disconnect_if_gnd(&ci, id_WEA); + pack_xrf_input_and_output(&ci, ci.name, id_WEA, IdString(), PLACE_FIFO_WEA, lut_only, lut_and_ff, dff_only); + + if (mode == 0) { + // FIFO + ci.renamePort(id_WEQ1, id_WEQ); + pack_xrf_input_and_output(&ci, ci.name, IdString(), id_WEQ, PLACE_FIFO_WEQ1, lut_only, lut_and_ff, + dff_only); + disconnect_unused(&ci, id_WEQ2); + + ci.renamePort(id_REQ1, id_REQ); + pack_xrf_input_and_output(&ci, ci.name, IdString(), id_REQ, PLACE_FIFO_REQ1, lut_only, lut_and_ff, + dff_only); + disconnect_unused(&ci, id_REQ2); + } else { + pack_xrf_input_and_output(&ci, ci.name, IdString(), id_WEQ1, PLACE_FIFO_WEQ1, lut_only, lut_and_ff, + dff_only); + pack_xrf_input_and_output(&ci, ci.name, IdString(), id_WEQ2, PLACE_FIFO_WEQ2, lut_only, lut_and_ff, + dff_only); + pack_xrf_input_and_output(&ci, ci.name, IdString(), id_WEQ1, PLACE_FIFO_REQ1, lut_only, lut_and_ff, + dff_only); + pack_xrf_input_and_output(&ci, ci.name, IdString(), id_WEQ2, PLACE_FIFO_REQ2, lut_only, lut_and_ff, + dff_only); + + // XFIFO + ci.ports[id_WCK1].name = id_WCK1; + ci.ports[id_WCK1].type = PORT_IN; + ci.ports[id_WCK2].name = id_WCK2; + ci.ports[id_WCK2].type = PORT_IN; + ci.ports[id_RCK1].name = id_RCK1; + ci.ports[id_RCK1].type = PORT_IN; + ci.ports[id_RCK2].name = id_RCK2; + ci.ports[id_RCK2].type = PORT_IN; + NetInfo *net = ci.getPort(id_WCK); + if (net) { + ci.disconnectPort(id_WCK); + + ci.connectPort(id_WCK1, net); + ci.connectPort(id_WCK2, net); + } + net = ci.getPort(id_RCK); + if (net) { + ci.disconnectPort(id_RCK); + + ci.connectPort(id_RCK1, net); + ci.connectPort(id_RCK2, net); + } + } + } + if (lut_only) + log_info(" %6d FEs used as LUT only\n", lut_only); + if (lut_and_ff) + log_info(" %6d FEs used as LUT and DFF\n", lut_and_ff); + if (dff_only) + log_info(" %6d FEs used as DFF only\n", dff_only); + flush_cells(); +} + +void NgUltraPacker::insert_ioms() +{ + std::vector pins_needing_iom; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + // Skip undriven nets + if (ni->driver.cell == nullptr) + continue; + if (!ni->driver.cell->type.in(id_BFR)) { + continue; + } + Loc iotp_loc = ni->driver.cell->getLocation(); + iotp_loc.z -= 1; + BelId bel = ctx->getBelByLocation(iotp_loc); + if (uarch->global_capable_bels.count(bel) == 0) + continue; + for (const auto &usr : ni->users) { + if (uarch->is_fabric_lowskew_sink(usr) || uarch->is_ring_clock_sink(usr) || + uarch->is_tube_clock_sink(usr) || uarch->is_ring_over_tile_clock_sink(usr)) { + pins_needing_iom.emplace_back(ni->name); + break; + } + } + } + // Sort clocks by max fanout + log_info("Inserting IOMs...\n"); + int bfr_removed = 0; + for (size_t i = 0; i < pins_needing_iom.size(); i++) { + NetInfo *net = ctx->nets.at(pins_needing_iom.at(i)).get(); + Loc iotp_loc = net->driver.cell->getLocation(); + iotp_loc.z -= 1; + BelId iotp_bel = ctx->getBelByLocation(iotp_loc); + + IdString iob = uarch->tile_name_id(iotp_bel.tile); + BelId bel = uarch->iom_bels[iob]; + + CellInfo *iom = nullptr; + IdString port = uarch->global_capable_bels.at(iotp_bel); + CellInfo *input_pad = ctx->getBoundBelCell(iotp_bel); + std::string iobname = str_or_default(input_pad->params, id_iobname, ""); + if (!ctx->checkBelAvail(bel)) { + iom = ctx->getBoundBelCell(bel); + log_info(" Reusing IOM in bank '%s' for signal '%s'\n", iob.c_str(ctx), iobname.c_str()); + } else { + iom = create_cell_ptr(id_IOM, ctx->idf("%s$iom", iob.c_str(ctx))); + log_info(" Adding IOM in bank '%s' for signal '%s'\n", iob.c_str(ctx), iobname.c_str()); + } + if (iom->getPort(port)) { + log_error("Port '%s' of IOM cell '%s' is already used.\n", port.c_str(ctx), iom->name.c_str(ctx)); + } + NetInfo *iom_to_clk = ctx->createNet(ctx->idf("%s$iom", net->name.c_str(ctx))); + for (const auto &usr : net->users) { + IdString port = usr.port; + usr.cell->disconnectPort(port); + usr.cell->connectPort(port, iom_to_clk); + } + iom->connectPort(port, input_pad->getPort(id_O)); + iom->connectPort((port == id_P17RI) ? id_CKO1 : id_CKO2, iom_to_clk); + if (ctx->checkBelAvail(bel)) + ctx->bindBel(bel, iom, PlaceStrength::STRENGTH_LOCKED); + CellInfo *bfr = net->driver.cell; + if (bfr->type == id_BFR && bfr->getPort(id_O)->users.empty()) { + bfr->disconnectPort(id_O); + bfr->disconnectPort(id_I); + bfr_removed++; + ctx->cells.erase(bfr->name); + } + } + if (bfr_removed) + log_info(" Removed %d unused BFR\n", bfr_removed); +} + +void NgUltraPacker::insert_wfbs() +{ + log_info("Inserting WFBs...\n"); + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (ci.type.in(id_IOM)) { + insert_wfb(&ci, id_CKO1); + insert_wfb(&ci, id_CKO2); + } else if (ci.type.in(id_PLL)) { + insert_wfb(&ci, id_OSC); + insert_wfb(&ci, id_VCO); + insert_wfb(&ci, id_REFO); + insert_wfb(&ci, id_LDFO); + insert_wfb(&ci, id_CLK_DIV1); + insert_wfb(&ci, id_CLK_DIV2); + insert_wfb(&ci, id_CLK_DIV3); + insert_wfb(&ci, id_CLK_DIV4); + insert_wfb(&ci, id_CLK_DIVD1); + insert_wfb(&ci, id_CLK_DIVD2); + insert_wfb(&ci, id_CLK_DIVD3); + insert_wfb(&ci, id_CLK_DIVD4); + insert_wfb(&ci, id_CLK_DIVD5); + insert_wfb(&ci, id_CLK_CAL_DIV); + } + } +} + +void NgUltraPacker::mandatory_param(CellInfo *cell, IdString param) +{ + if (!cell->params.count(param)) + log_error("Mandatory parameter '%s' of cell '%s'(%s) is missing.\n", param.c_str(ctx), cell->name.c_str(ctx), + cell->type.c_str(ctx)); +} + +int NgUltraPacker::memory_width(int config, bool ecc) +{ + if (ecc) { + if (config == 4) + return 18; + else + log_error("ECC mode only support width of 18.\n"); + } else { + switch (config) { + case 0: + return 1; // NOECC_48kx1 + case 1: + return 2; // NOECC_24kx2 + case 2: + return 4; // NOECC_12kx4 + case 3: + return 8; // NOECC_6kx8 + case 4: + return 12; // NOECC_4kx12 + case 5: + return 24; // NOECC_2kx24 + case 6: + return 3; // NOECC_16kx3 + case 7: + return 6; // NOECC_8kx6 + } + log_error("Unknown memory configuration width config '%d'.\n", config); + } +} + +int NgUltraPacker::memory_addr_bits(int config, bool ecc) +{ + if (ecc) { + if (config == 4) + return 11; + else + log_error("ECC mode only support width of 18.\n"); + } else { + switch (config) { + case 0: + return 16; // NOECC_48kx1 + case 1: + return 15; // NOECC_24kx2 + case 2: + return 14; // NOECC_12kx4 + case 3: + return 13; // NOECC_6kx8 + case 4: + return 12; // NOECC_4kx12 + case 5: + return 11; // NOECC_2kx24 + case 6: + return 14; // NOECC_16kx3 + case 7: + return 13; // NOECC_8kx6 + } + log_error("Unknown memory configuration width config '%d'.\n", config); + } +} + +void NgUltraPacker::insert_wfb(CellInfo *cell, IdString port) +{ + NetInfo *net = cell->getPort(port); + if (!net) + return; + + CellInfo *wfg = net_only_drives(ctx, net, is_wfg, id_ZI, true); + if (wfg) + return; + bool in_fabric = false; + bool in_ring = false; + for (const auto &usr : net->users) { + if (uarch->is_fabric_lowskew_sink(usr) || uarch->is_tube_clock_sink(usr) || + uarch->is_ring_over_tile_clock_sink(usr)) + in_fabric = true; + else + in_ring = true; + } + // If all in ring and none in fabric no need for WFB + if (in_ring && !in_fabric) + return; + log_info(" Inserting WFB for cell '%s' port '%s'\n", cell->name.c_str(ctx), port.c_str(ctx)); + CellInfo *wfb = create_cell_ptr(id_WFB, ctx->idf("%s$%s", cell->name.c_str(ctx), port.c_str(ctx))); + if (in_ring && in_fabric) { + // If both in ring and in fabric create new signal + wfb->connectPort(id_ZI, net); + NetInfo *net_zo = ctx->createNet(ctx->idf("%s$ZO", net->name.c_str(ctx))); + wfb->connectPort(id_ZO, net_zo); + for (const auto &usr : net->users) { + if (uarch->is_fabric_lowskew_sink(usr) || uarch->is_ring_over_tile_clock_sink(usr)) { + usr.cell->disconnectPort(usr.port); + usr.cell->connectPort(usr.port, net_zo); + } + } + } else { + // Only in fabric, reconnect wire directly to WFB + cell->disconnectPort(port); + wfb->connectPort(id_ZO, net); + NetInfo *new_out = ctx->createNet(ctx->idf("%s$%s", net->name.c_str(ctx), port.c_str(ctx))); + cell->connectPort(port, new_out); + wfb->connectPort(id_ZI, new_out); + } +} + +void NgUltraPacker::constrain_location(CellInfo *cell) +{ + std::string location = str_or_default(cell->params, id_location, ""); + if (!location.empty()) { + if (uarch->locations.count(location)) { + BelId bel = uarch->locations[location]; + if (ctx->getBelType(bel) != cell->type) { + log_error("Location '%s' is wrong for bel type '%s'.\n", location.c_str(), cell->type.c_str(ctx)); + } + if (ctx->checkBelAvail(bel)) { + log_info(" Constraining %s '%s' to '%s'\n", cell->type.c_str(ctx), cell->name.c_str(ctx), + location.c_str()); + ctx->bindBel(bel, cell, PlaceStrength::STRENGTH_LOCKED); + } else { + log_error("Bel at location '%s' is already used by other cell.\n", location.c_str()); + } + + } else { + log_error("Unknown location '%s' for cell '%s'.\n", location.c_str(), cell->name.c_str(ctx)); + } + } +} + +void NgUltraPacker::pack_plls(void) +{ + log_info("Packing PLLs..\n"); + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_PLL_U)) + continue; + ci.type = id_PLL; + constrain_location(&ci); + + disconnect_if_gnd(&ci, id_FBK); + disconnect_if_gnd(&ci, id_CLK_CAL); + disconnect_if_gnd(&ci, id_R); + disconnect_if_gnd(&ci, id_EXT_CAL1); + disconnect_if_gnd(&ci, id_EXT_CAL2); + disconnect_if_gnd(&ci, id_EXT_CAL3); + disconnect_if_gnd(&ci, id_EXT_CAL4); + disconnect_if_gnd(&ci, id_EXT_CAL5); + disconnect_if_gnd(&ci, id_EXT_CAL_LOCKED); + disconnect_if_gnd(&ci, id_ARST_CAL); + } +} + +void NgUltraPacker::pack_wfgs(void) +{ + log_info("Packing WFGs..\n"); + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_WFG_U)) + continue; + ci.type = id_WFG; + constrain_location(&ci); + int mode = int_or_default(ci.params, id_mode, 1); + if (mode == 0) { // WFB - bypass mode + ci.type = id_WFB; + // must not be used, zero is tollerated + disconnect_unused(&ci, id_SI); + disconnect_unused(&ci, id_SO); + disconnect_unused(&ci, id_R); + } else if (mode == 1) { + // Can be unused, if zero it is unused + disconnect_if_gnd(&ci, id_SI); + disconnect_if_gnd(&ci, id_R); + } else if (mode == 2) { + // Allow mode 2 + } else { + log_error("Unknown mode %d for cell '%s'.\n", mode, ci.name.c_str(ctx)); + } + NetInfo *zi = ci.getPort(id_ZI); + if (!zi || !zi->driver.cell) + log_error("WFG port ZI of '%s' must be driven.\n", ci.name.c_str(ctx)); + NetInfo *zo = ci.getPort(id_ZO); + if (!zo || zo->users.entries() == 0) + log_error("WFG port ZO of '%s' must be connected.\n", ci.name.c_str(ctx)); + } +} + +void NgUltraPacker::pack_gcks(void) +{ + log_info("Packing GCKs..\n"); + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_GCK_U)) + continue; + ci.type = id_GCK; + std::string mode = str_or_default(ci.params, id_std_mode, "BYPASS"); + if (mode == "BYPASS") { + disconnect_unused(&ci, id_SI2); + disconnect_unused(&ci, id_CMD); + } else if (mode == "CSC") { + disconnect_unused(&ci, id_SI1); + disconnect_unused(&ci, id_SI2); + } else if (mode == "CKS") { + disconnect_unused(&ci, id_SI2); + } else if (mode == "MUX") { + // all used + } else + log_error("Unknown mode '%s' for cell '%s'.\n", mode.c_str(), ci.name.c_str(ctx)); + + if (net_driven_by(ctx, ci.getPort(id_SI1), is_gck, id_SO) || + net_driven_by(ctx, ci.getPort(id_SI2), is_gck, id_SO) || + net_driven_by(ctx, ci.getPort(id_CMD), is_gck, id_SO)) { + log_error("Cascaded GCKs are not allowed.\n"); + } + } +} + +void NgUltraPacker::pack_rams(void) +{ + log_info("Packing RAMs..\n"); + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_RAM)) + continue; + ci.type = id_RAM; + bind_attr_loc(&ci, &ci.attrs); + // These ACKx and BCKx exists for NX_RAM, but are not available on NX_ULTRA + ci.disconnectPort(id_ACKC); + ci.disconnectPort(id_ACKD); + ci.disconnectPort(id_ACKR); + ci.disconnectPort(id_BCKC); + ci.disconnectPort(id_BCKD); + ci.disconnectPort(id_BCKR); + mandatory_param(&ci, id_raw_config0); + mandatory_param(&ci, id_raw_config1); + Property extr = ci.params[id_raw_config1].extract(0, 16); + std::vector bits = extr.as_bits(); + // int ecc_mode = (bits[12] ? 1 : 0) | (bits[13] ? 2 : 0) | (bits[14] ? 4 : 0) | (bits[15] ? 8 : 0); + bool ecc = bits[12]; + // int a_in_width = memory_width((bits[0] ? 1 : 0) | (bits[1] ? 2 : 0) | (bits[2] ? 4 : 0), ecc); + // int b_in_width = memory_width((bits[3] ? 1 : 0) | (bits[4] ? 2 : 0) | (bits[5] ? 4 : 0), ecc); + int a_out_width = memory_width((bits[6] ? 1 : 0) | (bits[7] ? 2 : 0) | (bits[8] ? 4 : 0), ecc); + int b_out_width = memory_width((bits[9] ? 1 : 0) | (bits[10] ? 2 : 0) | (bits[11] ? 4 : 0), ecc); + + int a_addr = std::max(memory_addr_bits((bits[0] ? 1 : 0) | (bits[1] ? 2 : 0) | (bits[2] ? 4 : 0), ecc), + memory_addr_bits((bits[6] ? 1 : 0) | (bits[7] ? 2 : 0) | (bits[8] ? 4 : 0), ecc)); + int b_addr = std::max(memory_addr_bits((bits[3] ? 1 : 0) | (bits[4] ? 2 : 0) | (bits[5] ? 4 : 0), ecc), + memory_addr_bits((bits[9] ? 1 : 0) | (bits[10] ? 2 : 0) | (bits[11] ? 4 : 0), ecc)); + + NetInfo *a_cs = ci.getPort(id_ACS); + if (!a_cs || a_cs->name.in(ctx->id("$PACKER_GND"))) { + // If there is no chip-select disconnect all + disconnect_unused(&ci, id_ACK); + for (int i = 0; i < 24; i++) { + disconnect_unused(&ci, ctx->idf("AI%d", i + 1)); + disconnect_unused(&ci, ctx->idf("AO%d", i + 1)); + } + for (int i = 0; i < 16; i++) + disconnect_unused(&ci, ctx->idf("AA%d", i + 1)); + } else { + for (int i = a_out_width; i < 24; i++) + disconnect_unused(&ci, ctx->idf("AO%d", i + 1)); + for (int i = a_addr; i < 16; i++) + disconnect_unused(&ci, ctx->idf("AA%d", i + 1)); + } + + NetInfo *b_cs = ci.getPort(id_BCS); + if (!b_cs || b_cs->name.in(ctx->id("$PACKER_GND"))) { + // If there is no chip-select disconnect all + disconnect_unused(&ci, id_BCK); + for (int i = 0; i < 24; i++) { + disconnect_unused(&ci, ctx->idf("BI%d", i + 1)); + disconnect_unused(&ci, ctx->idf("BO%d", i + 1)); + } + for (int i = 0; i < 16; i++) + disconnect_unused(&ci, ctx->idf("BA%d", i + 1)); + } else { + for (int i = b_out_width; i < 24; i++) + disconnect_unused(&ci, ctx->idf("BO%d", i + 1)); + for (int i = b_addr; i < 16; i++) + disconnect_unused(&ci, ctx->idf("BA%d", i + 1)); + } + + for (auto &p : ci.ports) { + if (p.second.type == PortType::PORT_IN) + disconnect_if_gnd(&ci, p.first); + } + } +} + +void NgUltraPacker::dsp_same_driver(IdString port, CellInfo *cell, CellInfo **target) +{ + if (cell->getPort(port)) { + CellInfo *driver = cell->getPort(port)->driver.cell; + if (!driver->type.in(id_DSP, id_NX_DSP_U)) + log_error("Port '%s' of '%s' can only be driven by DSP.\n", port.c_str(ctx), cell->name.c_str(ctx)); + if (*target && *target != driver) + log_error("CAI1-24, CBI1-18, CZI1..56 and CCI must be from same DSP for '%s'.\n", cell->name.c_str(ctx)); + *target = driver; + } +} + +void NgUltraPacker::dsp_same_sink(IdString port, CellInfo *cell, CellInfo **target) +{ + if (cell->getPort(port)) { + if (cell->getPort(port)->users.entries() != 1) + log_error("Port '%s' of '%s' can only drive one DSP.\n", port.c_str(ctx), cell->name.c_str(ctx)); + CellInfo *driver = (*cell->getPort(port)->users.begin()).cell; + if (!driver->type.in(id_DSP, id_NX_DSP_U)) + log_error("Port '%s' of '%s' can only drive DSP.\n", port.c_str(ctx), cell->name.c_str(ctx)); + if (*target && *target != driver) + log_error("CAI1-24, CBI1-18, CZI1..56 and CCI must be from same DSP for '%s'.\n", cell->name.c_str(ctx)); + *target = driver; + } +} + +void NgUltraPacker::pack_dsps(void) +{ + log_info("Packing DSPs..\n"); + dict dsp_output; + std::vector root_dsps; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_NX_DSP_U)) + continue; + ci.type = id_DSP; + bind_attr_loc(&ci, &ci.attrs); + mandatory_param(&ci, id_raw_config0); + mandatory_param(&ci, id_raw_config1); + mandatory_param(&ci, id_raw_config2); + mandatory_param(&ci, id_raw_config3); + + for (auto &p : ci.ports) { + if (p.second.type == PortType::PORT_IN) + disconnect_if_gnd(&ci, p.first); + } + + // CAI1-24, CBI1-18, CZI1..56 and CCI must be from same DSP + CellInfo *dsp = nullptr; + for (int i = 1; i <= 24; i++) + dsp_same_driver(ctx->idf("CAI%d", i), &ci, &dsp); + for (int i = 1; i <= 18; i++) + dsp_same_driver(ctx->idf("CBI%d", i), &ci, &dsp); + for (int i = 1; i <= 56; i++) + dsp_same_driver(ctx->idf("CZI%d", i), &ci, &dsp); + dsp_same_driver(id_CCI, &ci, &dsp); + if (!dsp) + root_dsps.push_back(&ci); + + // CAO1-24, CBO1-18, CZO1..56 and CCO must go to same DSP + dsp = nullptr; + for (int i = 1; i <= 24; i++) + dsp_same_sink(ctx->idf("CAO%d", i), &ci, &dsp); + for (int i = 1; i <= 18; i++) + dsp_same_sink(ctx->idf("CBO%d", i), &ci, &dsp); + for (int i = 1; i <= 56; i++) + dsp_same_sink(ctx->idf("CZO%d", i), &ci, &dsp); + dsp_same_sink(id_CCO, &ci, &dsp); + if (dsp) + dsp_output.emplace(ci.name, dsp); + } + for (auto root : root_dsps) { + CellInfo *dsp = root; + if (dsp_output.count(dsp->name) == 0) + continue; + root->cluster = root->name; + while (true) { + if (dsp_output.count(dsp->name) == 0) + break; + dsp = dsp_output[dsp->name]; + dsp->cluster = root->name; + root->constr_children.push_back(dsp); + dsp->constr_z = PLACE_DSP_CHAIN; + } + } +} + +void NgUltraPacker::remove_not_used() +{ + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + for (auto &p : ci.ports) { + if (p.second.type == PortType::PORT_OUT) { + NetInfo *net = ci.getPort(p.first); + if (net && net->users.entries() == 0) { + ci.disconnectPort(p.first); + } + } + } + } +} + +void NgUltraImpl::pack() +{ + const ArchArgs &args = ctx->args; + if (args.options.count("csv")) { + parse_csv(args.options.at("csv")); + } + + // Setup + NgUltraPacker packer(ctx, this); + packer.pack_constants(); + packer.remove_not_used(); + packer.update_lut_init(); + packer.update_dffs(); + + // CGB + packer.pack_rams(); + packer.pack_dsps(); + + // TILE + packer.pack_rfs(); + packer.pack_cdcs(); + packer.pack_fifos(); + packer.pack_cys(); + if (!args.options.count("no-xlut")) + packer.pack_xluts(); + if (!args.options.count("no-lut-chains")) + packer.pack_lut_multi_dffs(); + if (!args.options.count("no-dff-chains")) + packer.pack_dff_chains(); + packer.pack_lut_dffs(); + packer.pack_dffs(); + + // Tube + packer.pack_gcks(); + + // Ring + packer.pack_iobs(); + packer.pack_ioms(); + packer.pack_plls(); + packer.pack_wfgs(); + packer.insert_ioms(); + packer.insert_wfbs(); + + packer.pre_place(); +} + +IdString NgUltraPacker::assign_wfg(IdString ckg, IdString ckg2, CellInfo *cell) +{ + for (auto &item : uarch->unused_wfg) { + BelId bel = item.first; + if (item.second == ckg || item.second == ckg2) { + IdString ckg = item.second; + uarch->unused_wfg.erase(bel); + log_info(" Using '%s:%s' for cell '%s'.\n", uarch->tile_name(bel.tile).c_str(), + ctx->getBelName(bel)[1].c_str(ctx), cell->name.c_str(ctx)); + ctx->bindBel(bel, cell, PlaceStrength::STRENGTH_LOCKED); + return ckg; + } + } + log_error(" No more available WFGs for cell '%s'.\n", cell->name.c_str(ctx)); +} + +void NgUltraPacker::extract_lowskew_signals(CellInfo *cell, + dict>> &lowskew_signals) +{ + IdString loc; + if (cell->bel != BelId()) + loc = uarch->tile_name_id(cell->bel.tile); + + PortRef ref; + ref.cell = cell; + auto &sinks = uarch->get_fabric_lowskew_sinks(); + if (sinks.count(cell->type)) { + for (auto &port : sinks.at(cell->type)) { + NetInfo *clock = cell->getPort(port); + if (clock && !global_lowskew.count(clock->name)) { + ref.port = port; + lowskew_signals[loc][clock->name].push_back(ref); + } + } + } +} + +void NgUltraPacker::pre_place(void) +{ + log_info("Pre-placing PLLs..\n"); + + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_PLL)) + continue; + // Remove from list those that are used + if (ci.bel != BelId()) { + uarch->unused_pll.erase(ci.bel); + } + } + // First process those on dedicated clock pins + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_PLL) || ci.bel != BelId()) + continue; + + NetInfo *ref = ci.getPort(id_REF); + if (ref && ref->driver.cell && ref->driver.cell->type == id_IOM) { + IdString bank = uarch->tile_name_id(ref->driver.cell->bel.tile); + // bool found = false; + for (auto &item : uarch->unused_pll) { + BelId bel = item.first; + std::pair &ckgs = uarch->bank_to_ckg[bank]; + if (item.second == ckgs.first || item.second == ckgs.second) { + uarch->unused_pll.erase(bel); + log_info(" Using PLL in '%s' for cell '%s'.\n", uarch->tile_name(bel.tile).c_str(), + ci.name.c_str(ctx)); + ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_LOCKED); + // found = true; + break; + } + } + } + } + // If PLL use any other pin, location is not relevant, so we pick available + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_PLL) || ci.bel != BelId()) + continue; + if (uarch->unused_pll.empty()) + log_error(" No more available PLLs for driving '%s'.\n", ci.name.c_str(ctx)); + BelId bel = uarch->unused_pll.begin()->first; + uarch->unused_pll.erase(bel); + log_info(" Using PLL in '%s' for cell '%s'.\n", uarch->tile_name(bel.tile).c_str(), ci.name.c_str(ctx)); + ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_LOCKED); + } + + log_info("Pre-placing WFB/WFGs..\n"); + + std::vector root_wfgs; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_WFG, id_WFB)) + continue; + NetInfo *zi = ci.getPort(id_ZI); + if (!zi || !zi->driver.cell || !zi->driver.cell->type.in(id_WFG, id_WFB)) + root_wfgs.push_back(&ci); + } + + std::vector> groups; + for (auto root : root_wfgs) { + std::vector group; + CellInfo *wfg = root; + group.push_back(wfg); + while (true) { + NetInfo *zo_net = wfg->getPort(id_ZO); + if (zo_net && zo_net->users.entries() > 0) { + wfg = (*zo_net->users.begin()).cell; + if (wfg->type.in(id_WFG, id_WFB)) { + if (zo_net->users.entries() != 1) + log_error("WFG can only be chained with one other WFG cell\n"); + group.push_back(wfg); + } else + break; + } else + break; + } + groups.push_back(group); + } + + // First pre-place those depending of PLL + for (auto &grp : groups) { + CellInfo *root = grp.front(); + NetInfo *zi = root->getPort(id_ZI); + if (zi->driver.cell->type.in(id_PLL)) { + IdString ckg = uarch->tile_name_id(zi->driver.cell->bel.tile); + assign_wfg(ckg, IdString(), root); + for (int i = 1; i < int(grp.size()); i++) { + assign_wfg(ckg, IdString(), grp.at(i)); + } + } + } + // Then those that depend on IOM + for (auto &grp : groups) { + CellInfo *root = grp.front(); + NetInfo *zi = root->getPort(id_ZI); + if (zi->driver.cell->type.in(id_IOM)) { + IdString bank = uarch->tile_name_id(zi->driver.cell->bel.tile); + std::pair &ckgs = uarch->bank_to_ckg[bank]; + IdString ckg = assign_wfg(ckgs.first, ckgs.second, root); + for (int i = 1; i < int(grp.size()); i++) { + assign_wfg(ckg, IdString(), grp.at(i)); + } + } + } + for (auto &grp : groups) { + CellInfo *root = grp.front(); + if (root->bel != BelId()) + continue; + // Assign first available + BelId bel = uarch->unused_pll.begin()->first; + IdString ckg = uarch->unused_pll.begin()->second; + uarch->unused_pll.erase(bel); + for (int i = 1; i < int(grp.size()); i++) { + assign_wfg(ckg, IdString(), grp.at(i)); + } + } + + dict>> lowskew_signals; + for (auto &cell : ctx->cells) + extract_lowskew_signals(cell.second.get(), lowskew_signals); + + log_info("Adding GCK for lowskew signals..\n"); + for (auto &n : lowskew_signals[IdString()]) { + NetInfo *net = ctx->nets.at(n.first).get(); + if (net->driver.cell->type.in(id_BFR, id_DFR, id_DDFR)) { + CellInfo *bfr = net->driver.cell; + CellInfo *gck_cell = create_cell_ptr(id_GCK, ctx->idf("%s$csc", bfr->name.c_str(ctx))); + gck_cell->params[id_std_mode] = Property("CSC"); + NetInfo *new_out = ctx->createNet(ctx->idf("%s$bfr", bfr->name.c_str(ctx))); + NetInfo *old = bfr->getPort(id_O); + bfr->disconnectPort(id_O); + gck_cell->connectPort(id_SO, old); + gck_cell->connectPort(id_CMD, new_out); + bfr->connectPort(id_O, new_out); + log_info(" Create GCK '%s' for signal '%s'\n", gck_cell->name.c_str(ctx), n.first.c_str(ctx)); + } + if (net->driver.cell->type.in(id_BEYOND_FE)) { + CellInfo *fe = net->driver.cell; + if (!fe->params.count(id_lut_table)) + continue; + if (fe->params.count(id_dff_used)) + continue; + if (fe->params[id_lut_table] != Property(0x5555, 16)) + continue; + if (!fe->getPort(id_I1)) + continue; + CellInfo *bfr = fe->getPort(id_I1)->driver.cell; + if (bfr->type.in(id_BFR, id_DFR, id_DDFR)) { + CellInfo *gck_cell = create_cell_ptr(id_GCK, ctx->idf("%s$csc", bfr->name.c_str(ctx))); + gck_cell->params[id_std_mode] = Property("CSC"); + gck_cell->params[id_inv_out] = Property(true); + NetInfo *old_out = fe->getPort(id_LO); + NetInfo *old_in = fe->getPort(id_I1); + fe->disconnectPort(id_LO); + fe->disconnectPort(id_I1); + gck_cell->connectPort(id_SO, old_out); + gck_cell->connectPort(id_CMD, old_in); + packed_cells.insert(fe->name); + log_info(" Create GCK '%s' for signal '%s'\n", gck_cell->name.c_str(ctx), n.first.c_str(ctx)); + } + } + } + flush_cells(); +} + +void NgUltraImpl::disable_beyond_fe_s_output(BelId bel) +{ + WireId dwire = ctx->getBelPinWire(bel, id_DO); + for (PipId pip : ctx->getPipsDownhill(dwire)) { + for (PipId pip2 : ctx->getPipsDownhill(ctx->getPipDstWire(pip))) { + IdString dst = ctx->getWireName(ctx->getPipDstWire(pip2))[1]; + if (boost::ends_with(dst.c_str(ctx), ".DS")) { + blocked_pips.emplace(pip2); + return; + } + } + } +} + +void NgUltraImpl::postPlace() +{ + log_break(); + log_info("Limiting routing...\n"); + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (ci.type == id_BEYOND_FE) { + const auto &extra_data = *bel_extra_data(ci.bel); + // Check if CSC mode only if FE is capable + if ((extra_data.flags & BEL_EXTRA_FE_CSC)) { + if (str_or_default(ci.params, id_type, "") == "CSC") + continue; + // Disable routing to S output if CSC is not used + disable_beyond_fe_s_output(ci.bel); + } + } else if (ci.type == id_CY) { + // In case A input is actually used, but it is connectd to GND + // it is considered that signal is comming from RI1 crossbar. + // We need to prevent router to use that crossbar output for + // any other signal. + for (int i = 1; i <= 4; i++) { + IdString port = ctx->idf("A%d", i); + NetInfo *net = ci.getPort(port); + if (!net) + continue; + if (net->name.in(ctx->id("$PACKER_GND"))) { + WireId dwire = ctx->getBelPinWire(ci.bel, port); + for (PipId pip : ctx->getPipsUphill(dwire)) { + WireId src = ctx->getPipSrcWire(pip); + const std::string src_name = ctx->getWireName(src)[1].str(ctx); + if (boost::starts_with(src_name, "RI1")) { + for (PipId pip2 : ctx->getPipsDownhill(src)) { + blocked_pips.emplace(pip2); + } + } + } + ci.disconnectPort(port); // Disconnect A + } + } + } + } + remove_constants(); + + const ArchArgs &args = ctx->args; + NgUltraPacker packer(ctx, this); + log_break(); + log_info("Running post-placement ...\n"); + packer.duplicate_gck(); + packer.insert_bypass_gck(); + if (!args.options.count("no-csc-insertion")) + packer.insert_csc(); + log_break(); + ctx->assignArchInfo(); +} + +BelId NgUltraPacker::getCSC(Loc l, int row) +{ + const int z_loc[] = {0, 15, 16, 31}; + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 4; i++) { + BelId bel = ctx->getBelByLocation(Loc(l.x + j + 1, l.y + j % 2 + 2, z_loc[i])); + if (!ctx->getBoundBelCell(bel) && (row == 0 || row == (i + 1))) + return bel; + } + } + return BelId(); +} + +void NgUltraPacker::insert_csc() +{ + dict>> local_system_matrix; + log_info("Inserting CSCs...\n"); + int insert_new_csc = 0, change_to_csc = 0; + for (auto &cell : ctx->cells) { + auto ci = cell.second.get(); + if (ci->bel != BelId()) { + if (uarch->tile_type(ci->bel.tile) == TILE_EXTRA_FABRIC) { + extract_lowskew_signals(ci, local_system_matrix); + } + } + } + for (auto &lsm : local_system_matrix) { + std::string name = lsm.first.c_str(ctx); + Loc loc = uarch->tile_locations[name]; + std::vector> fanout; + for (auto &n : lsm.second) { + fanout.push_back(std::make_pair(n.second.size(), n.first)); + } + std::sort(fanout.begin(), fanout.end(), std::greater>()); + int available_csc = 12; + for (std::size_t i = 0; i < std::min(fanout.size(), available_csc); i++) { + auto &n = fanout.at(i); + + NetInfo *net = ctx->nets.at(n.second).get(); + CellInfo *cell = net->driver.cell; + if (uarch->tile_name(cell->bel.tile) == lsm.first.c_str(ctx) && !cell->params.count(id_dff_used) && + cell->cluster == ClusterId()) { + BelId newbel = getCSC(loc, 0); + if (newbel == BelId()) + break; + + ctx->unbindBel(cell->bel); + cell->disconnectPort(id_LO); + NetInfo *new_out = ctx->createNet(ctx->idf("%s$o", cell->name.c_str(ctx))); + cell->params[id_CSC] = Property(Property::State::S1); + cell->params[id_type] = Property("CSC"); + cell->params[id_dff_used] = Property(1, 1); + cell->connectPort(id_LO, new_out); + cell->connectPort(id_DI, new_out); + + cell->connectPort(id_DO, net); + ctx->bindBel(newbel, cell, PlaceStrength::STRENGTH_LOCKED); + change_to_csc++; + continue; + } + Loc cell_loc = ctx->getBelLocation(cell->bel); + BelId newbel = getCSC(loc, (cell_loc.y & 3) + 1); // Take CSC from pefered row + if (newbel == BelId()) + newbel = getCSC(loc, 0); // Try getting any other CSC + if (newbel == BelId()) + break; + if (lsm.second[n.second].size() < 4) + break; + + CellInfo *fe = + create_cell_ptr(id_BEYOND_FE, ctx->idf("%s$%s$csc", net->name.c_str(ctx), lsm.first.c_str(ctx))); + NetInfo *new_out = ctx->createNet(ctx->idf("%s$o", fe->name.c_str(ctx))); + fe->params[id_lut_table] = Property(0xaaaa, 16); + fe->params[id_lut_used] = Property(1, 1); + fe->params[id_CSC] = Property(Property::State::S1); + fe->params[id_type] = Property("CSC"); + fe->params[id_dff_used] = Property(1, 1); + fe->connectPort(id_I1, net); + fe->connectPort(id_DO, new_out); + insert_new_csc++; + + for (auto &conn : lsm.second[n.second]) + conn.cell->disconnectPort(conn.port); + for (auto &conn : lsm.second[n.second]) + conn.cell->connectPort(conn.port, new_out); + + ctx->bindBel(newbel, fe, PlaceStrength::STRENGTH_LOCKED); + } + } + if (insert_new_csc) + log_info(" %6d FEs inserted as CSC\n", insert_new_csc); + if (change_to_csc) + log_info(" %6d FEs converted to CSC\n", change_to_csc); +} +BelId NgUltraPacker::get_available_gck(int lobe, NetInfo *si1, NetInfo *si2) +{ + auto &gcks = uarch->gck_per_lobe[lobe]; + for (int i = 0; i < 20; i++) { + auto &g = gcks.at(i); + if (g.used) + continue; + if (si1 && g.si1 != IdString() && g.si1 != si1->name) + continue; + if (si2 && g.si2 != IdString() && g.si2 != si2->name) + continue; + if (si1) + g.si1 = si1->name; + if (si2) + g.si2 = si2->name; + g.used = true; + if (i % 2 == 0) { + // next GCK share inputs in reverse order + if (si2) + gcks.at(i + 1).si1 = si2->name; + if (si1) + gcks.at(i + 1).si2 = si1->name; + } + return g.bel; + } + log_error("No GCK left to promote lowskew signal.\n"); + return BelId(); +} + +void NgUltraPacker::duplicate_gck() +{ + // Unbind all GCKs that are inserted + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_GCK)) + continue; + ctx->unbindBel(ci.bel); + } + + log_info("Duplicating existing GCKs...\n"); + for (auto &net : ctx->nets) { + NetInfo *glb_net = net.second.get(); + if (!glb_net->driver.cell) + continue; + + if (!uarch->is_tube_clock_source(glb_net->driver)) + continue; + + log_info(" Lowskew signal '%s'\n", glb_net->name.c_str(ctx)); + dict> connections; + for (const auto &usr : glb_net->users) { + if (uarch->is_fabric_lowskew_sink(usr) || uarch->is_ring_over_tile_clock_sink(usr)) { + if (usr.cell->bel == BelId()) { + log_error("Cell '%s' not placed\n", usr.cell->name.c_str(ctx)); + } + int lobe = uarch->tile_lobe(usr.cell->bel.tile); + if (lobe > 0) { + connections[lobe].push_back(usr); + usr.cell->disconnectPort(usr.port); + } + } + } + int cnt = 0; + CellInfo *driver = glb_net->driver.cell; + NetInfo *si1 = driver->getPort(id_SI1); + NetInfo *si2 = driver->getPort(id_SI2); + NetInfo *cmd = driver->getPort(id_CMD); + for (auto &conn : connections) { + BelId bel = get_available_gck(conn.first, si1, si2); + CellInfo *gck_cell = nullptr; + if (cnt == 0) { + gck_cell = driver; + log_info(" Assign GCK '%s' to lobe %d\n", gck_cell->name.c_str(ctx), conn.first); + } else { + gck_cell = create_cell_ptr(id_GCK, ctx->idf("%s$gck_%d", driver->name.c_str(ctx), conn.first)); + log_info(" Create GCK '%s' for lobe %d\n", gck_cell->name.c_str(ctx), conn.first); + for (auto ¶ms : driver->params) + gck_cell->params[params.first] = params.second; + if (si1) + gck_cell->connectPort(id_SI1, si1); + if (si2) + gck_cell->connectPort(id_SI2, si2); + if (cmd) + gck_cell->connectPort(id_CMD, cmd); + } + gck_cell->disconnectPort(id_SO); + NetInfo *new_clk = ctx->createNet(ctx->id(gck_cell->name.str(ctx))); + gck_cell->connectPort(id_SO, new_clk); + for (const auto &usr : conn.second) { + CellInfo *cell = usr.cell; + IdString port = usr.port; + cell->connectPort(port, new_clk); + } + global_lowskew.emplace(new_clk->name); + ctx->bindBel(bel, gck_cell, PlaceStrength::STRENGTH_LOCKED); + cnt++; + } + } +} + +void NgUltraPacker::insert_bypass_gck() +{ + log_info("Inserting bypass GCKs...\n"); + for (auto &net : ctx->nets) { + NetInfo *glb_net = net.second.get(); + if (!glb_net->driver.cell) + continue; + + if (!uarch->is_ring_clock_source(glb_net->driver)) + continue; + + log_info(" Lowskew signal '%s'\n", glb_net->name.c_str(ctx)); + dict> connections; + for (const auto &usr : glb_net->users) { + if (uarch->is_fabric_lowskew_sink(usr) || uarch->is_ring_over_tile_clock_sink(usr)) { + if (usr.cell->bel == BelId()) { + log_error("Cell '%s' not placed\n", usr.cell->name.c_str(ctx)); + } + int lobe = uarch->tile_lobe(usr.cell->bel.tile); + if (lobe > 0) { + connections[lobe].push_back(usr); + usr.cell->disconnectPort(usr.port); + } + } + } + for (auto &conn : connections) { + BelId bel = get_available_gck(conn.first, glb_net, nullptr); + + log_info(" Create GCK for lobe %d\n", conn.first); + CellInfo *gck_cell = create_cell_ptr(id_GCK, ctx->idf("%s$gck_%d", glb_net->name.c_str(ctx), conn.first)); + gck_cell->params[id_std_mode] = Property("BYPASS"); + gck_cell->connectPort(id_SI1, glb_net); + NetInfo *new_clk = ctx->createNet(ctx->id(gck_cell->name.str(ctx))); + gck_cell->connectPort(id_SO, new_clk); + for (const auto &usr : conn.second) { + CellInfo *cell = usr.cell; + IdString port = usr.port; + cell->connectPort(port, new_clk); + } + global_lowskew.emplace(new_clk->name); + ctx->bindBel(bel, gck_cell, PlaceStrength::STRENGTH_LOCKED); + } + } +} +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/ng-ultra/pack.h b/himbaechel/uarch/ng-ultra/pack.h new file mode 100644 index 00000000..09a99fa2 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/pack.h @@ -0,0 +1,131 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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 +#include +#include +#include +#include +#include "chain_utils.h" +#include "design_utils.h" +#include "log.h" +#include "nextpnr.h" +#include "ng_ultra.h" + +#ifndef NG_ULTRA_PACK_H +#define NG_ULTRA_PACK_H + +NEXTPNR_NAMESPACE_BEGIN + +struct NgUltraPacker +{ + NgUltraPacker(Context *ctx, NgUltraImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); }; + + void remove_not_used(); + // Constants + void pack_constants(); + void remove_constants(); + + // LUTs & FFs + void update_lut_init(); + void update_dffs(); + void pack_xluts(); + void pack_lut_multi_dffs(); + void pack_dff_chains(); + void pack_lut_dffs(); + void pack_dffs(); + void pack_cys(); + void pack_rfs(); + void pack_cdcs(); + void pack_fifos(); + + void pack_rams(); + void pack_dsps(); + + // IO + void pack_iobs(); + void pack_ioms(); + + void pack_gcks(); + void pack_plls(); + void pack_wfgs(); + void pre_place(); + + void insert_ioms(); + void insert_wfbs(); + + // Post placement + void duplicate_gck(); + void insert_bypass_gck(); + void insert_csc(); + + /* clang-format off */ +TESTABLE_PRIVATE: + void set_lut_input_if_constant(CellInfo *cell, IdString input); + /* clang-format on */ + void lut_to_fe(CellInfo *lut, CellInfo *fe, bool no_dff, Property lut_table); + void dff_to_fe(CellInfo *dff, CellInfo *fe, bool pass_thru_lut); + void dff_rewrite(CellInfo *cell); + void ddfr_rewrite(CellInfo *cell); + + void exchange_if_constant(CellInfo *cell, IdString input1, IdString input2); + void pack_cy_input_and_output(CellInfo *cy, IdString cluster, IdString in_port, IdString out_port, int placer, + int &lut_only, int &lut_and_ff, int &dff_only); + + void pack_xrf_input_and_output(CellInfo *cy, IdString cluster, IdString in_port, IdString out_port, + ClusterPlacement placement, int &lut_only, int &lut_and_ff, int &dff_only); + + void connect_gnd_if_unconnected(CellInfo *cell, IdString input, bool warn); + void disconnect_if_gnd(CellInfo *cell, IdString input); + + void insert_wfb(CellInfo *cell, IdString port); + + void mandatory_param(CellInfo *cell, IdString param); + void disconnect_unused(CellInfo *cell, IdString port); + void bind_attr_loc(CellInfo *cell, dict *attrs); + BelId get_available_gck(int lobe, NetInfo *si1, NetInfo *si2); + BelId getCSC(Loc l, int row); + // General helper functions + void flush_cells(); + + IdString assign_wfg(IdString ckg, IdString ckg2, CellInfo *cell); + void dsp_same_driver(IdString port, CellInfo *cell, CellInfo **target); + void dsp_same_sink(IdString port, CellInfo *cell, CellInfo **target); + + int make_init_with_const_input(int init, int input, bool value); + + int memory_width(int config, bool ecc); + int memory_addr_bits(int config, bool ecc); + + void constrain_location(CellInfo *cell); + void extract_lowskew_signals(CellInfo *cell, dict>> &lowskew_signals); + // Cell creating + CellInfo *create_cell_ptr(IdString type, IdString name); + + Context *ctx; + NgUltraImpl *uarch; + + pool packed_cells; + pool global_lowskew; + + HimbaechelHelpers h; +}; + +NEXTPNR_NAMESPACE_END +#endif diff --git a/himbaechel/uarch/ng-ultra/tests/lut_dff.cc b/himbaechel/uarch/ng-ultra/tests/lut_dff.cc new file mode 100644 index 00000000..87cce6ff --- /dev/null +++ b/himbaechel/uarch/ng-ultra/tests/lut_dff.cc @@ -0,0 +1,161 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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 +#include "command.h" +#include "gtest/gtest.h" +#include "nextpnr.h" +#include "uarch/ng-ultra/ng_ultra.h" +#include "uarch/ng-ultra/pack.h" +#define HIMBAECHEL_CONSTIDS "uarch/ng-ultra/constids.inc" +#include "himbaechel_constids.h" + +USING_NEXTPNR_NAMESPACE + +class NGUltraLutDffTest : public ::testing::Test +{ + protected: + virtual void SetUp() + { + init_share_dirname(); + chipArgs.device = "NG-ULTRA"; + ctx = new Context(chipArgs); + ctx->uarch->init(ctx); + ctx->late_init(); + impl = (NgUltraImpl *)(ctx->uarch.get()); + } + + virtual void TearDown() { delete ctx; } + + int const_autoidx = 0; + NetInfo *add_constant_driver(const char *name, char constval) + { + IdString cell_name = ctx->idf("%s%s%d", name, (constval == '1' ? "$VCC$" : "$GND$"), const_autoidx++); + CellInfo *cc = ctx->createCell(cell_name, ctx->id(constval == '1' ? "VCC" : "GND")); + cc->ports[ctx->id("Y")].name = ctx->id("Y"); + cc->ports[ctx->id("Y")].type = PORT_OUT; + NetInfo *net = ctx->createNet(cell_name); + cc->connectPort(ctx->id("Y"), net); + return net; + } + + void add_port(CellInfo *cell, const std::string &name, PortType dir) + { + IdString id = ctx->id(name); + cell->ports[id].name = id; + cell->ports[id].type = dir; + }; + + int evaluate_lut(int I1, int I2, int I3, int I4, int lut_table) + { + int S1 = I4 ? (lut_table >> 8) & 0xff : lut_table & 0xff; + int S2 = I3 ? (S1 >> 4 & 0xf) : S1 & 0xf; + int S3 = I2 ? (S2 >> 2 & 0x3) : S2 & 0x3; + int O = I1 ? (S3 >> 1 & 0x1) : S3 & 0x1; + return O; + } + + ArchArgs chipArgs; + Context *ctx; + NgUltraImpl *impl; +}; + +TEST_F(NGUltraLutDffTest, pack_constants) +{ + NgUltraPacker packer(ctx, impl); + packer.pack_constants(); + ASSERT_EQ(ctx->cells.size(), 2LU); +} + +TEST_F(NGUltraLutDffTest, remove_constants) +{ + NgUltraPacker packer(ctx, impl); + packer.pack_constants(); + impl->remove_constants(); + ASSERT_EQ(ctx->cells.size(), 0LU); +} + +TEST_F(NGUltraLutDffTest, remove_unused_gnd) +{ + NgUltraPacker packer(ctx, impl); + CellInfo *cell = ctx->createCell(ctx->id("TEST"), id_NX_LUT); + add_port(cell, "I1", PORT_IN); + add_port(cell, "I2", PORT_IN); + add_port(cell, "I3", PORT_IN); + add_port(cell, "I4", PORT_IN); + cell->connectPort(id_I1, add_constant_driver("TEST", '1')); + cell->connectPort(id_I2, add_constant_driver("TEST", '1')); + cell->connectPort(id_I3, add_constant_driver("TEST", '1')); + + ASSERT_EQ(ctx->cells.size(), 4LU); + packer.pack_constants(); + ASSERT_EQ(ctx->cells.size(), 3LU); + impl->remove_constants(); + ASSERT_EQ(ctx->cells.size(), 2LU); + ASSERT_EQ(ctx->cells.find(ctx->id("$PACKER_GND_DRV")), ctx->cells.end()); + ASSERT_NE(ctx->cells.find(ctx->id("$PACKER_VCC_DRV")), ctx->cells.end()); + ASSERT_EQ(ctx->nets.find(ctx->id("$PACKER_GND")), ctx->nets.end()); + ASSERT_NE(ctx->nets.find(ctx->id("$PACKER_VCC")), ctx->nets.end()); +} + +TEST_F(NGUltraLutDffTest, remove_unused_vcc) +{ + NgUltraPacker packer(ctx, impl); + CellInfo *cell = ctx->createCell(ctx->id("TEST"), id_NX_LUT); + add_port(cell, "I1", PORT_IN); + add_port(cell, "I2", PORT_IN); + add_port(cell, "I3", PORT_IN); + add_port(cell, "I4", PORT_IN); + cell->connectPort(id_I1, add_constant_driver("TEST", '0')); + cell->connectPort(id_I2, add_constant_driver("TEST", '0')); + cell->connectPort(id_I3, add_constant_driver("TEST", '0')); + + ASSERT_EQ(ctx->cells.size(), 4LU); + packer.pack_constants(); + ASSERT_EQ(ctx->cells.size(), 3LU); + impl->remove_constants(); + ASSERT_EQ(ctx->cells.size(), 2LU); + ASSERT_NE(ctx->cells.find(ctx->id("$PACKER_GND_DRV")), ctx->cells.end()); + ASSERT_EQ(ctx->cells.find(ctx->id("$PACKER_VCC_DRV")), ctx->cells.end()); + ASSERT_NE(ctx->nets.find(ctx->id("$PACKER_GND")), ctx->nets.end()); + ASSERT_EQ(ctx->nets.find(ctx->id("$PACKER_VCC")), ctx->nets.end()); +} + +TEST_F(NGUltraLutDffTest, make_init_with_const_input) +{ + NgUltraPacker packer(ctx, impl); + for (int lut_table = 0; lut_table < 0x10000; lut_table++) { + for (int lut = 0; lut < 16; lut++) { + int I4 = (lut & 8) ? 1 : 0; + int I3 = (lut & 4) ? 1 : 0; + int I2 = (lut & 2) ? 1 : 0; + int I1 = (lut & 1) ? 1 : 0; + + int tab1 = packer.make_init_with_const_input(lut_table, 0, I1); + int tab2 = packer.make_init_with_const_input(tab1, 1, I2); + int tab3 = packer.make_init_with_const_input(tab2, 2, I3); + int tab4 = packer.make_init_with_const_input(tab3, 3, I4); + + ASSERT_EQ(evaluate_lut(I1, I2, I3, I4, lut_table), evaluate_lut(I1, I2, I3, I4, tab1)); + ASSERT_EQ(evaluate_lut(I1, I2, I3, I4, lut_table), evaluate_lut(I1, I2, I3, I4, tab2)); + ASSERT_EQ(evaluate_lut(I1, I2, I3, I4, lut_table), evaluate_lut(I1, I2, I3, I4, tab3)); + ASSERT_EQ(evaluate_lut(I1, I2, I3, I4, lut_table), evaluate_lut(I1, I2, I3, I4, tab4)); + } + } +} diff --git a/himbaechel/uarch/ng-ultra/tests/main.cc b/himbaechel/uarch/ng-ultra/tests/main.cc new file mode 100644 index 00000000..92e027f2 --- /dev/null +++ b/himbaechel/uarch/ng-ultra/tests/main.cc @@ -0,0 +1,32 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Beyond 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 +#include "gtest/gtest.h" +#include "log.h" + +USING_NEXTPNR_NAMESPACE + +int main(int argc, char **argv) +{ + // TODO: Remove for delivery, useful while checking tests + log_streams.push_back(std::make_pair(&std::cerr, LogLevel::LOG_MSG)); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}