From d6483adb4d9c5a5fac8ef99c53888d32f83cb808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miodrag=20Milanovi=C4=87?= Date: Tue, 22 Apr 2025 16:41:01 +0200 Subject: [PATCH] Gatemate FPGA initial support (#1473) * Initial code for GateMate * Initial work on forming bitstream * Add CCF parsing * Use CCF to set IO location * Propagate errors * Restructure code * Add support for reading from config * Start adding infrastructure for reading bitstream * Fix script * GPIO initial work * Add IN1->RAM_O2 propagation * Fixed typo * Cleanup * More parameter checks * Add LVDS support * Cleanup * Keep just used connections for now * Naive lut tree CPE pack * Naive pack CC_DFF * pack DFF fixes * Handle MUX flags * Fix DFF pack * Prevent pass trough issues * Cleanup * Use device wrapper class * Update due to API changes * Use pin connection aliases * Start work on BUFG support * Fix CC_L2T5 pack * Add CPE input inverters * Constrain routes to have correct inversion state * Add clock inversion pip * Added MX2 and MX4 support * Fix script * BUFG support * debug print if route found with wrong polarity * Some CC_DFF improvements * Create reproducible chip database * Simplify inversion of special signals * Few more DFF features * Add forgotten virtual port renames * Handle muxes with constant inputs * Allow inversion for muxes * cleanup * DFF input can be constant * init DFF only when needed * cleanup * Add basic PLL support * Add some timings * Add USR_RSTN support * Display few more primitives * Use pass trough signals to validate architecture data * Use extra tile information from chip database * Updates needed for a build system changes * Implement SB_DRIVE support * Properly named configuration bits * autogenerated constids.inc * small fix * Initial code for CPE halfs * Some cleanup * make sure FFs are compatible * reverted due to db change * Merge DFF where applicable * memory allocation issue * fix * better MX2 * ram_i handling * Cleanup MX4 * Support latches * compare L_D flag as well * Move virtual pips * Naive addf pack * carry chains grouping * Keep chip database reproducible * split addf vectors * Block CPEs when GPIO is used * Prepare placement code * RAM_I/RAM_O rewrite * fix ram_i/o index * Display RAM and add new primitives * PLL wip code * CC_PLL_ADV packing * PLL handling cleanup * Add PLL comments * Keep only high fan-out BUFG * Add skeleton for tests * Utilize move_ram_o * GPIO wip * GPIO wip * PLL fixes * cleanup * FF_OBF support * Handle FF_IBF * Make SLEW FAST if not defined as in latest p_r * Make sure FF_OBF only driving GPIO * Moved pll calc into separate file * IDDR handling and started ODDR * Route DDR input for CC_ODDR * Notify error in case ODDR or IDDR are used but not with I/O pin * cleanup for CC_USR_RSTN * Extract proper RAM location for bitstream * Code cleanup * Allow auto place of pads * Use clock source flag * Configure GPIO clock signals * Handle conflicting clk * Use BUGF in proper order * Connected CLK, works without but good for debugging * CC_CFG_CTRL placement * Group RAM data 40 bytes per row * Write BRAM content * RAM wip * Use relative constraints from chipdb * fix broken build * Memory wip * Handle custom clock for memories * Support FIFO * optimize move_ram_io * Fix SR signal handling acorrding to findings * set placer beta * Pre place what we can * Revert "debug print if route found with wrong polarity" This reverts commit cf9ded2f183db0f4e85da454e5d9f2a4da3f5cfe. * Revert "Constrain routes to have correct inversion state" This reverts commit 795c284d4856ff17e37d64f013775e86f1bed803. * Remove virtual pips * Implement post processing inversion * ADDF add ability to route additional CO * Merge two ADDFs in one CPE * Added TODO * clangformat * Cleanup * Add serdes handling in config file * Cleanup * Cleanup * Cleanup * Fix in PLL handling * Fixed ADDF edge case * No need for this * Fix latch * Sanity checks * Support CC_BRAM_20K merge * Start creating testing environment * LVDS fixes * Add connection helper * Cleanup * Fix tabs * Formatting fix * Remove optimization tests for now * remove read_bitstream * removed .c_str() * Removed config parsing * using snake_case * Use bool_or_default where applicable * refactored bitstream write code * Add allow-unconstrained option * Update DFF related messages * Add clock constraint propagation --------- Co-authored-by: Lofty --- himbaechel/CMakeLists.txt | 2 +- himbaechel/uarch/gatemate/CMakeLists.txt | 67 + himbaechel/uarch/gatemate/bitstream.cc | 252 +++ himbaechel/uarch/gatemate/ccf.cc | 215 ++ himbaechel/uarch/gatemate/cells.cc | 59 + himbaechel/uarch/gatemate/config.cc | 121 ++ himbaechel/uarch/gatemate/config.h | 85 + himbaechel/uarch/gatemate/constids.inc | 2042 ++++++++++++++++++ himbaechel/uarch/gatemate/extra_data.h | 97 + himbaechel/uarch/gatemate/gatemate.cc | 419 ++++ himbaechel/uarch/gatemate/gatemate.h | 97 + himbaechel/uarch/gatemate/gatemate_util.h | 84 + himbaechel/uarch/gatemate/gen/arch_gen.py | 200 ++ himbaechel/uarch/gatemate/gfx.cc | 89 + himbaechel/uarch/gatemate/gfxids.inc | 0 himbaechel/uarch/gatemate/pack.cc | 2188 ++++++++++++++++++++ himbaechel/uarch/gatemate/pack.h | 78 + himbaechel/uarch/gatemate/pll.cc | 352 ++++ himbaechel/uarch/gatemate/tests/lut.cc | 36 + himbaechel/uarch/gatemate/tests/main.cc | 32 + himbaechel/uarch/gatemate/tests/testing.cc | 210 ++ himbaechel/uarch/gatemate/tests/testing.h | 45 + 22 files changed, 6769 insertions(+), 1 deletion(-) create mode 100644 himbaechel/uarch/gatemate/CMakeLists.txt create mode 100644 himbaechel/uarch/gatemate/bitstream.cc create mode 100644 himbaechel/uarch/gatemate/ccf.cc create mode 100644 himbaechel/uarch/gatemate/cells.cc create mode 100644 himbaechel/uarch/gatemate/config.cc create mode 100644 himbaechel/uarch/gatemate/config.h create mode 100644 himbaechel/uarch/gatemate/constids.inc create mode 100644 himbaechel/uarch/gatemate/extra_data.h create mode 100644 himbaechel/uarch/gatemate/gatemate.cc create mode 100644 himbaechel/uarch/gatemate/gatemate.h create mode 100644 himbaechel/uarch/gatemate/gatemate_util.h create mode 100644 himbaechel/uarch/gatemate/gen/arch_gen.py create mode 100644 himbaechel/uarch/gatemate/gfx.cc create mode 100644 himbaechel/uarch/gatemate/gfxids.inc create mode 100644 himbaechel/uarch/gatemate/pack.cc create mode 100644 himbaechel/uarch/gatemate/pack.h create mode 100644 himbaechel/uarch/gatemate/pll.cc create mode 100644 himbaechel/uarch/gatemate/tests/lut.cc create mode 100644 himbaechel/uarch/gatemate/tests/main.cc create mode 100644 himbaechel/uarch/gatemate/tests/testing.cc create mode 100644 himbaechel/uarch/gatemate/tests/testing.h diff --git a/himbaechel/CMakeLists.txt b/himbaechel/CMakeLists.txt index 06430c37..daab3850 100644 --- a/himbaechel/CMakeLists.txt +++ b/himbaechel/CMakeLists.txt @@ -64,7 +64,7 @@ else() endif() -set(HIMBAECHEL_UARCHES example gowin xilinx ng-ultra) +set(HIMBAECHEL_UARCHES example gowin xilinx ng-ultra gatemate) set(HIMBAECHEL_UARCH "" CACHE STRING "Microarchitectures for nextpnr-himbaechel build") set_property(CACHE HIMBAECHEL_UARCH PROPERTY STRINGS ${HIMBAECHEL_UARCHES}) diff --git a/himbaechel/uarch/gatemate/CMakeLists.txt b/himbaechel/uarch/gatemate/CMakeLists.txt new file mode 100644 index 00000000..4e705671 --- /dev/null +++ b/himbaechel/uarch/gatemate/CMakeLists.txt @@ -0,0 +1,67 @@ +set(SOURCES + bitstream.cc + ccf.cc + cells.cc + config.cc + config.h + constids.inc + extra_data.h + gatemate.cc + gatemate.h + gfx.cc + gfxids.inc + pack.cc + pack.h + pll.cc +) + +set(TEST_SOURCES + tests/lut.cc + tests/testing.cc + tests/main.cc +) + +add_nextpnr_himbaechel_microarchitecture(${uarch} + CORE_SOURCES ${SOURCES} + TEST_SOURCES ${TEST_SOURCES} +) + +set(HIMBAECHEL_PEPPERCORN_PATH "" CACHE STRING + "Path to a Project Peppercorn database") +if (NOT HIMBAECHEL_PEPPERCORN_PATH) + message(FATAL_ERROR "HIMBAECHEL_PEPPERCORN_PATH must be set to a Project Peppercorn checkout") +endif() + +#set(ALL_HIMBAECHE_GATEMATE_DEVICES CCGM1A1 CCGM1A2 CCGM1A4 CCGM1A9 CCGM1A16 CCGM1A25) +set(ALL_HIMBAECHE_GATEMATE_DEVICES CCGM1A1) +set(HIMBAECHEL_GATEMATE_DEVICES ${ALL_HIMBAECHE_GATEMATE_DEVICES} CACHE STRING + "Include support for these GateMate devices (available: ${ALL_HIMBAECHE_GATEMATE_DEVICES})") +message(STATUS "Enabled Himbaechel-GateMate devices: ${HIMBAECHEL_GATEMATE_DEVICES}") + +foreach (device ${HIMBAECHEL_GATEMATE_DEVICES}) + if (NOT device IN_LIST ALL_HIMBAECHE_GATEMATE_DEVICES) + message(FATAL_ERROR "Device ${device} is not a supported GateMate device") + endif() + + add_bba_produce_command( + TARGET nextpnr-himbaechel-gatemate-bba + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/gen/arch_gen.py + --lib ${HIMBAECHEL_PEPPERCORN_PATH}/gatemate + --device ${device} + --bba ${CMAKE_CURRENT_BINARY_DIR}/chipdb-${device}.bba.new + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/chipdb-${device}.bba + INPUTS + ${CMAKE_CURRENT_SOURCE_DIR}/gen/arch_gen.py + ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc + ${CMAKE_CURRENT_SOURCE_DIR}/gfxids.inc + ) + + add_bba_compile_command( + TARGET nextpnr-himbaechel-gatemate-chipdb + OUTPUT himbaechel/gatemate/chipdb-${device}.bin + INPUT ${CMAKE_CURRENT_BINARY_DIR}/chipdb-${device}.bba + MODE binary + ) +endforeach() diff --git a/himbaechel/uarch/gatemate/bitstream.cc b/himbaechel/uarch/gatemate/bitstream.cc new file mode 100644 index 00000000..22647d3a --- /dev/null +++ b/himbaechel/uarch/gatemate/bitstream.cc @@ -0,0 +1,252 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 "config.h" +#include "gatemate.h" +#include "gatemate_util.h" + +#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace { + +struct BitstreamBackend +{ + Context *ctx; + GateMateImpl *uarch; + const std::string &device; + std::ostream &out; + + BitstreamBackend(Context *ctx, GateMateImpl *uarch, const std::string &device, std::ostream &out) + : ctx(ctx), uarch(uarch), device(device), out(out) {}; + + const GateMateTileExtraDataPOD *tile_extra_data(int tile) const + { + return reinterpret_cast(ctx->chip_info->tile_insts[tile].extra_data.get()); + } + + std::vector int_to_bitvector(int val, int size) + { + std::vector bv; + for (int i = 0; i < size; i++) { + bv.push_back((val & (1 << i)) != 0); + } + return bv; + } + + CfgLoc get_config_loc(int tile) + { + auto ti = *tile_extra_data(tile); + CfgLoc loc; + loc.die = ti.die; + loc.x = ti.bit_x; + loc.y = ti.bit_y; + return loc; + } + + CfgLoc get_ram_config_loc(int tile) + { + auto ti = *tile_extra_data(tile); + CfgLoc loc; + loc.die = ti.die; + loc.x = (ti.bit_x - 17) / 16; + loc.y = (ti.bit_y - 1) / 8; + return loc; + } + + void export_connection(ChipConfig &cc, PipId pip) + { + const auto extra_data = + *reinterpret_cast(chip_pip_info(ctx->chip_info, pip).extra_data.get()); + if (extra_data.type == PipExtra::PIP_EXTRA_MUX && (extra_data.flags & MUX_VISIBLE)) { + IdString name = IdString(extra_data.name); + CfgLoc loc = get_config_loc(pip.tile); + std::string word = name.c_str(ctx); + if (extra_data.flags & MUX_CONFIG) { + cc.configs[loc.die].add_word(word, int_to_bitvector(extra_data.value, extra_data.bits)); + } else { + int id = tile_extra_data(pip.tile)->prim_id; + if (boost::starts_with(word, "IM.")) + boost::replace_all(word, "IM.", stringf("IM%d.", id)); + else if (boost::starts_with(word, "OM.")) + boost::replace_all(word, "OM.", stringf("OM%d.", id)); + else if (boost::starts_with(word, "CPE.")) + boost::replace_all(word, "CPE.", stringf("CPE%d.", id)); + else if (boost::starts_with(word, "IOES.")) + boost::replace_all(word, "IOES.", stringf("IOES%d.", id)); + else if (boost::starts_with(word, "LES.")) + boost::replace_all(word, "LES.", stringf("LES%d.", id)); + else if (boost::starts_with(word, "BES.")) + boost::replace_all(word, "BES.", stringf("BES%d.", id)); + else if (boost::starts_with(word, "RES.")) + boost::replace_all(word, "RES.", stringf("RES%d.", id)); + else if (boost::starts_with(word, "TES.")) + boost::replace_all(word, "TES.", stringf("TES%d.", id)); + if (boost::starts_with(word, "SB_DRIVE.")) { + Loc l; + tile_xy(ctx->chip_info, pip.tile, l.x, l.y); + l.z = 0; + BelId cpe_bel = ctx->getBelByLocation(l); + // Only if switchbox is inside core (same as sharing location with CPE) + if (cpe_bel != BelId() && ctx->getBelType(cpe_bel).in(id_CPE_HALF_L, id_CPE_HALF_U)) { + // Convert coordinates into in-tile coordinates + int xt = ((l.x - 2 - 1) + 16) % 8; + int yt = ((l.y - 2 - 1) + 16) % 8; + // Bitstream data for certain SB_DRIVES is located in other tiles + switch (word[14]) { + case '3': + if (xt >= 4) { + loc.x -= 2; + word[14] = '1'; + }; + break; + case '4': + if (yt >= 4) { + loc.y -= 2; + word[14] = '2'; + }; + break; + case '1': + if (xt <= 3) { + loc.x += 2; + word[14] = '3'; + }; + break; + case '2': + if (yt <= 3) { + loc.y += 2; + word[14] = '4'; + }; + break; + default: + break; + } + } + } + + cc.tiles[loc].add_word(word, int_to_bitvector(extra_data.value, extra_data.bits)); + } + } + } + + void write_bitstream() + { + ChipConfig cc; + cc.chip_name = device; + cc.configs[0].add_word("GPIO.BANK_E1", int_to_bitvector(1, 1)); + cc.configs[0].add_word("GPIO.BANK_E2", int_to_bitvector(1, 1)); + cc.configs[0].add_word("GPIO.BANK_N1", int_to_bitvector(1, 1)); + cc.configs[0].add_word("GPIO.BANK_N2", int_to_bitvector(1, 1)); + cc.configs[0].add_word("GPIO.BANK_S1", int_to_bitvector(1, 1)); + cc.configs[0].add_word("GPIO.BANK_S2", int_to_bitvector(1, 1)); + cc.configs[0].add_word("GPIO.BANK_W1", int_to_bitvector(1, 1)); + cc.configs[0].add_word("GPIO.BANK_W2", int_to_bitvector(1, 1)); + for (auto &cell : ctx->cells) { + CfgLoc loc = get_config_loc(cell.second.get()->bel.tile); + auto ¶ms = cell.second.get()->params; + switch (cell.second->type.index) { + case id_CC_IBUF.index: + case id_CC_TOBUF.index: + case id_CC_OBUF.index: + case id_CC_IOBUF.index: + case id_CC_LVDS_IBUF.index: + case id_CC_LVDS_TOBUF.index: + case id_CC_LVDS_OBUF.index: + case id_CC_LVDS_IOBUF.index: + for (auto &p : params) { + cc.tiles[loc].add_word(stringf("GPIO.%s", p.first.c_str(ctx)), p.second.as_bits()); + } + break; + case id_CPE_HALF_U.index: + case id_CPE_HALF_L.index: { + int id = tile_extra_data(cell.second.get()->bel.tile)->prim_id; + for (auto &p : params) { + cc.tiles[loc].add_word(stringf("CPE%d.%s", id, p.first.c_str(ctx)), p.second.as_bits()); + } + } break; + case id_BUFG.index: { + Loc l = ctx->getBelLocation(cell.second->bel); + cc.configs[0].add_word(stringf("GLBOUT.GLB%d_EN", l.z), int_to_bitvector(1, 1)); + } break; + case id_PLL.index: { + Loc l = ctx->getBelLocation(cell.second->bel); + for (auto &p : params) { + cc.configs[0].add_word(stringf("PLL%d.%s", l.z - 4, p.first.c_str(ctx)), p.second.as_bits()); + } + } break; + case id_RAM.index: { + CfgLoc loc = get_ram_config_loc(cell.second.get()->bel.tile); + auto &bram = cc.brams[loc]; + for (auto &p : params) { + std::string name = p.first.c_str(ctx); + if (boost::starts_with(name, "RAM_cfg")) + bram.add_word(name, p.second.as_bits()); + } + + bool is_fifo = params.count(id_RAM_cfg_fifo_sync_enable) | params.count(id_RAM_cfg_fifo_async_enable); + if (!is_fifo) { + auto &bram_data = cc.bram_data[loc]; + bram_data = std::vector(5120); + for (int i = 0; i < 128; i++) { + for (int j = 0; j < 40; j++) { + bram_data[i * 40 + j] = extract_bits(params, ctx->idf("INIT_%02X", i), j * 8, 8); + } + } + } + } break; + case id_USR_RSTN.index: + case id_CFG_CTRL.index: + break; + default: + log_error("Unhandled cell %s of type %s\n", cell.second.get()->name.c_str(ctx), + cell.second->type.c_str(ctx)); + } + } + + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + if (ni->wires.empty()) + continue; + for (auto &w : ni->wires) { + if (w.second.pip != PipId()) + export_connection(cc, w.second.pip); + } + } + out << cc; + } +}; + +} // namespace + +void GateMateImpl::write_bitstream(const std::string &device, 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)); + + BitstreamBackend be(ctx, this, device, out); + be.write_bitstream(); +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/ccf.cc b/himbaechel/uarch/gatemate/ccf.cc new file mode 100644 index 00000000..f1f83eb6 --- /dev/null +++ b/himbaechel/uarch/gatemate/ccf.cc @@ -0,0 +1,215 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 "extra_data.h" +#include "himbaechel_api.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +#include "gatemate.h" + +#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct GateMateCCFReader +{ + Context *ctx; + GateMateImpl *uarch; + std::istream ∈ + int lineno; + dict defaults; + + GateMateCCFReader(Context *ctx, GateMateImpl *uarch, std::istream &in) : ctx(ctx), uarch(uarch), in(in){}; + + std::string strip_quotes(const std::string &str) + { + if (str.at(0) == '"') { + if (str.back() != '"') { + log_error("expected '\"' at end of string '%s' (on line %d).\n", str.c_str(), lineno); + } + return str.substr(1, str.size() - 2); + } else { + return str; + } + }; + + void parse_params(std::vector ¶ms, bool is_default, dict *props) + { + for (auto param : params) { + std::vector expr; + boost::split(expr, param, boost::is_any_of("=")); + if (expr.size() != 2) + log_error("each parameter must be in form NAME=VALUE (on line %d)\n", lineno); + + std::string name = expr.at(0); + boost::algorithm::trim(name); + boost::algorithm::to_upper(name); + std::string value = strip_quotes(expr.at(1)); + boost::algorithm::trim(value); + boost::algorithm::to_upper(value); + + if (name == "LOC") { + if (is_default) + log_error("Value '%s' can not be defined for default GPIO in line %d.\n", name.c_str(), lineno); + if (ctx->get_package_pin_bel(ctx->id(value)) == BelId()) + log_error("Unknown location '%s' used in line %d.\n", value.c_str(), lineno); + if (!uarch->available_pads.count(ctx->id(value))) + log_error("Pad '%s' used in line %d not available.\n", value.c_str(), lineno); + props->emplace(id_LOC, Property(value)); + uarch->available_pads.erase(ctx->id(value)); + } else if (name == "SCHMITT_TRIGGER" || name == "PULLUP" || name == "PULLDOWN" || name == "KEEPER" || + name == "FF_IBF" || name == "FF_OBF" || name == "LVDS_BOOST" || name == "LVDS_RTERM") { + if (value == "TRUE") { + props->emplace(ctx->id(name), Property(Property::State::S1)); + } else if (value == "FALSE") { + props->emplace(ctx->id(name), Property(Property::State::S0)); + } else + log_error("Uknown value '%s' for parameter '%s' in line %d, must be TRUE or FALSE.\n", + value.c_str(), name.c_str(), lineno); + } else if (name == "SLEW") { + if (value == "FAST" || value == "SLOW") { + props->emplace(ctx->id(name), Property(value)); + } else + log_error("Uknown value '%s' for parameter '%s' in line %d, must be SLOW or FAST.\n", value.c_str(), + name.c_str(), lineno); + } else if (name == "DRIVE") { + try { + int drive = boost::lexical_cast(value.c_str()); + if (drive == 3 || drive == 6 || drive == 9 || drive == 12) { + props->emplace(ctx->id(name), Property(drive, 2)); + } else + log_error("Parameter '%s' must have value 3,6,9 or 12 in line %d.\n", name.c_str(), lineno); + } catch (boost::bad_lexical_cast const &) { + log_error("Parameter '%s' must be number in line %d.\n", name.c_str(), lineno); + } + } else if (name == "DELAY_IBF" || name == "DELAY_OBF") { + try { + int delay = boost::lexical_cast(value.c_str()); + if (delay >= 0 && delay <= 15) { + props->emplace(ctx->id(name), Property(delay, 4)); + } else + log_error("Parameter '%s' must have value from 0 to 15 in line %d.\n", name.c_str(), lineno); + } catch (boost::bad_lexical_cast const &) { + log_error("Parameter '%s' must be number in line %d.\n", name.c_str(), lineno); + } + } else { + log_error("Uknown parameter name '%s' in line %d.\n", name.c_str(), lineno); + } + } + } + + void run() + { + log_info("Parsing CCF file..\n"); + + std::string line; + std::string linebuf; + defaults.clear(); + + auto isempty = [](const std::string &str) { + return std::all_of(str.begin(), str.end(), [](char c) { return isblank(c) || c == '\r' || c == '\n'; }); + }; + lineno = 0; + while (std::getline(in, line)) { + ++lineno; + // Both // and # are considered start of comment + size_t com_start = line.find("//"); + if (com_start != std::string::npos) + line = line.substr(0, com_start); + com_start = line.find('#'); + if (com_start != std::string::npos) + line = line.substr(0, com_start); + if (isempty(line)) + continue; + linebuf += line; + + size_t pos = linebuf.find(';'); + // Need to concatenate lines until there is closing ; sign + while (pos != std::string::npos) { + std::string content = linebuf.substr(0, pos); + + std::vector params; + boost::split(params, content, boost::is_any_of("|")); + std::string command = params.at(0); + + std::stringstream ss(command); + std::vector words; + std::string tmp; + while (ss >> tmp) + words.push_back(tmp); + std::string type = words.at(0); + + boost::algorithm::to_lower(type); + if (type == "default_gpio") { + if (words.size() != 1) + log_error("line with default_GPIO should not contain only parameters (in line %d).\n", lineno); + params.erase(params.begin()); + parse_params(params, true, &defaults); + + } else if (type == "net" || type == "pin_in" || type == "pin_out" || type == "pin_inout") { + if (words.size() < 3 || words.size() > 5) + log_error("pin definition line not properly formed (in line %d).\n", lineno); + std::string pin_name = strip_quotes(words.at(1)); + + // put back other words and use them as parameters + std::stringstream ss; + for (size_t i = 2; i < words.size(); i++) + ss << words.at(i); + params[0] = ss.str(); + + IdString cellname = ctx->id(pin_name); + if (ctx->cells.count(cellname)) { + CellInfo *cell = ctx->cells.at(cellname).get(); + for (auto p : defaults) + cell->params[p.first] = p.second; + parse_params(params, false, &cell->params); + } else + log_warning("Pad with name '%s' not found in netlist.\n", pin_name.c_str()); + } else { + log_error("unknown type '%s' in line %d.\n", type.c_str(), lineno); + } + + linebuf = linebuf.substr(pos + 1); + pos = linebuf.find(';'); + } + } + if (!isempty(linebuf)) + log_error("unexpected end of CCF file\n"); + } +}; + +void GateMateImpl::parse_ccf(const std::string &filename) +{ + std::ifstream in(filename); + if (!in) + log_error("failed to open CCF file '%s'\n", filename.c_str()); + GateMateCCFReader reader(ctx, this, in); + reader.run(); +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/cells.cc b/himbaechel/uarch/gatemate/cells.cc new file mode 100644 index 00000000..c473d040 --- /dev/null +++ b/himbaechel/uarch/gatemate/cells.cc @@ -0,0 +1,59 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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/gatemate/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +CellInfo *GateMatePacker::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.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) { + 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_RAM_I, PORT_IN); + add_port(id_OUT, PORT_OUT); + add_port(id_RAM_O, PORT_OUT); + add_port(id_EN, PORT_IN); + add_port(id_CLK, PORT_IN); + add_port(id_SR, PORT_IN); + if (type == id_CPE_HALF_L) { + add_port(id_COUTY1, PORT_OUT); + } + } else if (type.in(id_CC_BUFG)) { + add_port(id_I, PORT_IN); + add_port(id_O, 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/gatemate/config.cc b/himbaechel/uarch/gatemate/config.cc new file mode 100644 index 00000000..134a1455 --- /dev/null +++ b/himbaechel/uarch/gatemate/config.cc @@ -0,0 +1,121 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 "config.h" +#include +#include +#include +#include "log.h" + +NEXTPNR_NAMESPACE_BEGIN + +#define fmt(x) (static_cast(std::ostringstream() << x).str()) + +inline std::string to_string(const std::vector &bv) +{ + std::ostringstream os; + for (auto bit : boost::adaptors::reverse(bv)) + os << (bit ? '1' : '0'); + return os.str(); +} + +std::ostream &operator<<(std::ostream &out, const ConfigWord &cw) +{ + out << cw.name << " " << to_string(cw.value) << std::endl; + return out; +} + +std::ostream &operator<<(std::ostream &out, const TileConfig &tc) +{ + for (const auto &cword : tc.cwords) + out << cword; + return out; +} + +void TileConfig::add_word(const std::string &name, const std::vector &value) +{ + if (!added.count(name)) { + cwords.push_back({name, value}); + added.emplace(name, value); + } else { + auto val = added.at(name); + if (val != value) + log_error("Trying to add value to already assigned word %s\n", name.c_str()); + } +} + +std::string TileConfig::to_string() const +{ + std::stringstream ss; + ss << *this; + return ss.str(); +} + +bool TileConfig::empty() const { return cwords.empty(); } + +std::ostream &operator<<(std::ostream &out, const ChipConfig &cc) +{ + out << ".device " << cc.chip_name << std::endl << std::endl; + for (const auto &config : cc.configs) { + if (!config.second.empty()) { + out << ".config " << config.first << " " << std::endl; + out << config.second; + out << std::endl; + } + } + for (const auto &ser : cc.serdes) { + if (!ser.second.empty()) { + out << ".serdes " << ser.first << " " << std::endl; + out << ser.second; + out << std::endl; + } + } + for (const auto &tile : cc.tiles) { + if (!tile.second.empty()) { + out << ".tile " << tile.first.die << " " << tile.first.x << " " << tile.first.y << std::endl; + out << tile.second; + out << std::endl; + } + } + for (const auto &bram : cc.brams) { + if (!bram.second.empty()) { + out << ".bram " << bram.first.die << " " << bram.first.x << " " << bram.first.y << std::endl; + out << bram.second; + out << std::endl; + } + } + for (const auto &bram : cc.bram_data) { + if (!bram.second.empty()) { + out << ".bram_init " << bram.first.die << " " << bram.first.x << " " << bram.first.y << std::endl; + std::ios_base::fmtflags f(out.flags()); + for (size_t i = 0; i < bram.second.size(); i++) { + out << std::setw(2) << std::setfill('0') << std::hex << (int)bram.second.at(i); + if (i % 40 == 39) + out << std::endl; + else + out << " "; + } + out.flags(f); + out << std::endl; + } + } + return out; +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/config.h b/himbaechel/uarch/gatemate/config.h new file mode 100644 index 00000000..b9ca9e6e --- /dev/null +++ b/himbaechel/uarch/gatemate/config.h @@ -0,0 +1,85 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 GATEMATE_CONFIG_H +#define GATEMATE_CONFIG_H + +#include +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct ConfigWord +{ + std::string name; + std::vector value; + inline bool operator==(const ConfigWord &other) const { return other.name == name && other.value == value; } +}; + +std::ostream &operator<<(std::ostream &out, const ConfigWord &cw); + +struct TileConfig +{ + std::vector cwords; + std::map> added; + + void add_word(const std::string &name, const std::vector &value); + + std::string to_string() const; + + bool empty() const; +}; + +std::ostream &operator<<(std::ostream &out, const TileConfig &tc); + +struct CfgLoc +{ + int die; + int x; + int y; + + inline bool operator==(const CfgLoc &other) const { return other.die == die && other.x == x && other.y == y; } + inline bool operator!=(const CfgLoc &other) const { return other.die != die || x != other.x || y == other.y; } + + inline bool operator<(const CfgLoc &other) const + { + return die < other.die || + ((die == other.die && y < other.y) || (die == other.die && y == other.y && x < other.x)); + } +}; + +class ChipConfig +{ + public: + std::string chip_name; + std::string chip_package; + std::map tiles; + std::map brams; + std::map serdes; + std::map configs; + + // Block RAM initialisation + std::map> bram_data; +}; + +std::ostream &operator<<(std::ostream &out, const ChipConfig &cc); + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/himbaechel/uarch/gatemate/constids.inc b/himbaechel/uarch/gatemate/constids.inc new file mode 100644 index 00000000..417a334a --- /dev/null +++ b/himbaechel/uarch/gatemate/constids.inc @@ -0,0 +1,2042 @@ +// autogenerated items +// primitive CC_IBUF +X(CC_IBUF) +X(PIN_NAME) +X(V_IO) +X(PULLUP) +X(PULLDOWN) +X(KEEPER) +X(SCHMITT_TRIGGER) +X(DELAY_IBF) +X(FF_IBF) +X(I) +X(Y) + +// primitive CC_OBUF +X(CC_OBUF) +//X(PIN_NAME) +//X(V_IO) +X(DRIVE) +X(SLEW) +X(DELAY_OBF) +X(FF_OBF) +X(A) +X(O) + +// primitive CC_TOBUF +X(CC_TOBUF) +//X(PIN_NAME) +//X(V_IO) +//X(DRIVE) +//X(SLEW) +//X(PULLUP) +//X(PULLDOWN) +//X(KEEPER) +//X(DELAY_OBF) +//X(FF_OBF) +//X(A) +X(T) +//X(O) + +// primitive CC_IOBUF +X(CC_IOBUF) +//X(PIN_NAME) +//X(V_IO) +//X(DRIVE) +//X(SLEW) +//X(PULLUP) +//X(PULLDOWN) +//X(KEEPER) +//X(SCHMITT_TRIGGER) +//X(DELAY_IBF) +//X(DELAY_OBF) +//X(FF_IBF) +//X(FF_OBF) +//X(A) +//X(T) +//X(Y) +X(IO) + +// primitive CC_LVDS_IBUF +X(CC_LVDS_IBUF) +X(PIN_NAME_P) +X(PIN_NAME_N) +//X(V_IO) +X(LVDS_RTERM) +//X(DELAY_IBF) +//X(FF_IBF) +X(I_P) +X(I_N) +//X(Y) + +// primitive CC_LVDS_OBUF +X(CC_LVDS_OBUF) +//X(PIN_NAME_P) +//X(PIN_NAME_N) +//X(V_IO) +X(LVDS_BOOST) +//X(DELAY_OBF) +//X(FF_OBF) +//X(A) +X(O_P) +X(O_N) + +// primitive CC_LVDS_TOBUF +X(CC_LVDS_TOBUF) +//X(PIN_NAME_P) +//X(PIN_NAME_N) +//X(V_IO) +//X(LVDS_BOOST) +//X(DELAY_OBF) +//X(FF_OBF) +//X(A) +//X(T) +//X(O_P) +//X(O_N) + +// primitive CC_LVDS_IOBUF +X(CC_LVDS_IOBUF) +//X(PIN_NAME_P) +//X(PIN_NAME_N) +//X(V_IO) +//X(LVDS_RTERM) +//X(LVDS_BOOST) +//X(DELAY_IBF) +//X(DELAY_OBF) +//X(FF_IBF) +//X(FF_OBF) +//X(A) +//X(T) +X(IO_P) +X(IO_N) +//X(Y) + +// primitive CC_IDDR +X(CC_IDDR) +X(CLK_INV) +X(D) +X(CLK) +X(Q0) +X(Q1) + +// primitive CC_ODDR +X(CC_ODDR) +//X(CLK_INV) +X(D0) +X(D1) +//X(CLK) +X(DDR) +X(Q) + +// primitive CC_DFF +X(CC_DFF) +//X(CLK_INV) +X(EN_INV) +X(SR_INV) +X(SR_VAL) +X(INIT) +//X(D) +//X(CLK) +X(EN) +X(SR) +//X(Q) + +// primitive CC_DLT +X(CC_DLT) +X(G_INV) +//X(SR_INV) +//X(SR_VAL) +//X(INIT) +//X(D) +X(G) +//X(SR) +//X(Q) + +// primitive CC_LUT1 +X(CC_LUT1) +//X(O) +X(I0) +//X(INIT) + +// primitive CC_LUT2 +X(CC_LUT2) +//X(O) +//X(I0) +X(I1) +//X(INIT) + +// primitive CC_LUT3 +X(CC_LUT3) +//X(O) +//X(I0) +//X(I1) +X(I2) +//X(INIT) + +// primitive CC_LUT4 +X(CC_LUT4) +//X(O) +//X(I0) +//X(I1) +//X(I2) +X(I3) +//X(INIT) + +// primitive CC_MX2 +X(CC_MX2) +//X(D0) +//X(D1) +X(S0) +//X(Y) + +// primitive CC_MX4 +X(CC_MX4) +//X(D0) +//X(D1) +X(D2) +X(D3) +//X(S0) +X(S1) +//X(Y) + +// primitive CC_MX8 +X(CC_MX8) +//X(D0) +//X(D1) +//X(D2) +//X(D3) +X(D4) +X(D5) +X(D6) +X(D7) +//X(S0) +//X(S1) +X(S2) +//X(Y) + +// primitive CC_ADDF +X(CC_ADDF) +//X(A) +X(B) +X(CI) +X(CO) +X(S) + +// primitive CC_MULT +X(CC_MULT) +X(A_WIDTH) +X(B_WIDTH) +X(P_WIDTH) +//X(A) +//X(B) +X(P) + +// primitive CC_BUFG +X(CC_BUFG) +//X(I) +//X(O) + +// primitive CC_BRAM_20K +X(CC_BRAM_20K) +X(A_DO) +X(B_DO) +X(ECC_1B_ERR) +X(ECC_2B_ERR) +X(A_CLK) +X(B_CLK) +X(A_EN) +X(B_EN) +X(A_WE) +X(B_WE) +X(A_ADDR) +X(B_ADDR) +X(A_DI) +X(B_DI) +X(A_BM) +X(B_BM) +X(LOC) +X(A_RD_WIDTH) +X(B_RD_WIDTH) +X(A_WR_WIDTH) +X(B_WR_WIDTH) +X(RAM_MODE) +X(A_WR_MODE) +X(B_WR_MODE) +X(A_CLK_INV) +X(B_CLK_INV) +X(A_EN_INV) +X(B_EN_INV) +X(A_WE_INV) +X(B_WE_INV) +X(A_DO_REG) +X(B_DO_REG) +X(ECC_EN) +X(INIT_00) +X(INIT_01) +X(INIT_02) +X(INIT_03) +X(INIT_04) +X(INIT_05) +X(INIT_06) +X(INIT_07) +X(INIT_08) +X(INIT_09) +X(INIT_0A) +X(INIT_0B) +X(INIT_0C) +X(INIT_0D) +X(INIT_0E) +X(INIT_0F) +X(INIT_10) +X(INIT_11) +X(INIT_12) +X(INIT_13) +X(INIT_14) +X(INIT_15) +X(INIT_16) +X(INIT_17) +X(INIT_18) +X(INIT_19) +X(INIT_1A) +X(INIT_1B) +X(INIT_1C) +X(INIT_1D) +X(INIT_1E) +X(INIT_1F) +X(INIT_20) +X(INIT_21) +X(INIT_22) +X(INIT_23) +X(INIT_24) +X(INIT_25) +X(INIT_26) +X(INIT_27) +X(INIT_28) +X(INIT_29) +X(INIT_2A) +X(INIT_2B) +X(INIT_2C) +X(INIT_2D) +X(INIT_2E) +X(INIT_2F) +X(INIT_30) +X(INIT_31) +X(INIT_32) +X(INIT_33) +X(INIT_34) +X(INIT_35) +X(INIT_36) +X(INIT_37) +X(INIT_38) +X(INIT_39) +X(INIT_3A) +X(INIT_3B) +X(INIT_3C) +X(INIT_3D) +X(INIT_3E) +X(INIT_3F) + +// primitive CC_BRAM_40K +X(CC_BRAM_40K) +//X(A_DO) +//X(B_DO) +X(A_ECC_1B_ERR) +X(B_ECC_1B_ERR) +X(A_ECC_2B_ERR) +X(B_ECC_2B_ERR) +X(A_CO) +X(B_CO) +//X(A_CLK) +//X(B_CLK) +//X(A_EN) +//X(B_EN) +//X(A_WE) +//X(B_WE) +//X(A_ADDR) +//X(B_ADDR) +//X(A_DI) +//X(B_DI) +//X(A_BM) +//X(B_BM) +X(A_CI) +X(B_CI) +//X(LOC) +X(CAS) +//X(A_RD_WIDTH) +//X(B_RD_WIDTH) +//X(A_WR_WIDTH) +//X(B_WR_WIDTH) +//X(RAM_MODE) +//X(A_WR_MODE) +//X(B_WR_MODE) +//X(A_CLK_INV) +//X(B_CLK_INV) +//X(A_EN_INV) +//X(B_EN_INV) +//X(A_WE_INV) +//X(B_WE_INV) +//X(A_DO_REG) +//X(B_DO_REG) +X(A_ECC_EN) +X(B_ECC_EN) +//X(INIT_00) +//X(INIT_01) +//X(INIT_02) +//X(INIT_03) +//X(INIT_04) +//X(INIT_05) +//X(INIT_06) +//X(INIT_07) +//X(INIT_08) +//X(INIT_09) +//X(INIT_0A) +//X(INIT_0B) +//X(INIT_0C) +//X(INIT_0D) +//X(INIT_0E) +//X(INIT_0F) +//X(INIT_10) +//X(INIT_11) +//X(INIT_12) +//X(INIT_13) +//X(INIT_14) +//X(INIT_15) +//X(INIT_16) +//X(INIT_17) +//X(INIT_18) +//X(INIT_19) +//X(INIT_1A) +//X(INIT_1B) +//X(INIT_1C) +//X(INIT_1D) +//X(INIT_1E) +//X(INIT_1F) +//X(INIT_20) +//X(INIT_21) +//X(INIT_22) +//X(INIT_23) +//X(INIT_24) +//X(INIT_25) +//X(INIT_26) +//X(INIT_27) +//X(INIT_28) +//X(INIT_29) +//X(INIT_2A) +//X(INIT_2B) +//X(INIT_2C) +//X(INIT_2D) +//X(INIT_2E) +//X(INIT_2F) +//X(INIT_30) +//X(INIT_31) +//X(INIT_32) +//X(INIT_33) +//X(INIT_34) +//X(INIT_35) +//X(INIT_36) +//X(INIT_37) +//X(INIT_38) +//X(INIT_39) +//X(INIT_3A) +//X(INIT_3B) +//X(INIT_3C) +//X(INIT_3D) +//X(INIT_3E) +//X(INIT_3F) +X(INIT_40) +X(INIT_41) +X(INIT_42) +X(INIT_43) +X(INIT_44) +X(INIT_45) +X(INIT_46) +X(INIT_47) +X(INIT_48) +X(INIT_49) +X(INIT_4A) +X(INIT_4B) +X(INIT_4C) +X(INIT_4D) +X(INIT_4E) +X(INIT_4F) +X(INIT_50) +X(INIT_51) +X(INIT_52) +X(INIT_53) +X(INIT_54) +X(INIT_55) +X(INIT_56) +X(INIT_57) +X(INIT_58) +X(INIT_59) +X(INIT_5A) +X(INIT_5B) +X(INIT_5C) +X(INIT_5D) +X(INIT_5E) +X(INIT_5F) +X(INIT_60) +X(INIT_61) +X(INIT_62) +X(INIT_63) +X(INIT_64) +X(INIT_65) +X(INIT_66) +X(INIT_67) +X(INIT_68) +X(INIT_69) +X(INIT_6A) +X(INIT_6B) +X(INIT_6C) +X(INIT_6D) +X(INIT_6E) +X(INIT_6F) +X(INIT_70) +X(INIT_71) +X(INIT_72) +X(INIT_73) +X(INIT_74) +X(INIT_75) +X(INIT_76) +X(INIT_77) +X(INIT_78) +X(INIT_79) +X(INIT_7A) +X(INIT_7B) +X(INIT_7C) +X(INIT_7D) +X(INIT_7E) +X(INIT_7F) + +// primitive CC_FIFO_40K +X(CC_FIFO_40K) +//X(A_ECC_1B_ERR) +//X(B_ECC_1B_ERR) +//X(A_ECC_2B_ERR) +//X(B_ECC_2B_ERR) +//X(A_DO) +//X(B_DO) +//X(A_CLK) +//X(A_EN) +//X(A_DI) +//X(B_DI) +//X(A_BM) +//X(B_BM) +//X(B_CLK) +//X(B_EN) +//X(B_WE) +X(F_RST_N) +X(F_ALMOST_FULL_OFFSET) +X(F_ALMOST_EMPTY_OFFSET) +X(F_FULL) +X(F_EMPTY) +X(F_ALMOST_FULL) +X(F_ALMOST_EMPTY) +X(F_RD_ERROR) +X(F_WR_ERROR) +X(F_RD_PTR) +X(F_WR_PTR) +//X(LOC) +X(DYN_STAT_SELECT) +X(ALMOST_FULL_OFFSET) +X(ALMOST_EMPTY_OFFSET) +//X(A_WIDTH) +//X(B_WIDTH) +//X(RAM_MODE) +X(FIFO_MODE) +//X(A_CLK_INV) +//X(B_CLK_INV) +//X(A_EN_INV) +//X(B_EN_INV) +//X(A_WE_INV) +//X(B_WE_INV) +//X(A_DO_REG) +//X(B_DO_REG) +//X(A_ECC_EN) +//X(B_ECC_EN) + +// primitive CC_L2T4 +X(CC_L2T4) +//X(O) +//X(I0) +//X(I1) +//X(I2) +//X(I3) +X(INIT_L00) +X(INIT_L01) +X(INIT_L10) + +// primitive CC_L2T5 +X(CC_L2T5) +//X(O) +//X(I0) +//X(I1) +//X(I2) +//X(I3) +X(I4) +X(INIT_L02) +X(INIT_L03) +X(INIT_L11) +X(INIT_L20) + +// primitive CC_PLL +X(CC_PLL) +X(REF_CLK) +X(OUT_CLK) +X(PERF_MD) +X(LOCK_REQ) +X(CLK270_DOUB) +X(CLK180_DOUB) +X(LOW_JITTER) +X(CI_FILTER_CONST) +X(CP_FILTER_CONST) +X(CLK_REF) +X(CLK_FEEDBACK) +X(USR_CLK_REF) +X(USR_LOCKED_STDY_RST) +X(USR_PLL_LOCKED_STDY) +X(USR_PLL_LOCKED) +X(CLK270) +X(CLK180) +X(CLK90) +X(CLK0) +X(CLK_REF_OUT) + +// primitive CC_PLL_ADV +X(CC_PLL_ADV) +X(PLL_CFG_A) +X(PLL_CFG_B) +//X(CLK_REF) +//X(CLK_FEEDBACK) +//X(USR_CLK_REF) +//X(USR_LOCKED_STDY_RST) +X(USR_SEL_A_B) +//X(USR_PLL_LOCKED_STDY) +//X(USR_PLL_LOCKED) +//X(CLK270) +//X(CLK180) +//X(CLK90) +//X(CLK0) +//X(CLK_REF_OUT) + +// primitive CC_SERDES +X(CC_SERDES) +X(SERDES_CFG) +X(TX_DATA_I) +X(TX_RESET_I) +X(TX_PCS_RESET_I) +X(TX_PMA_RESET_I) +X(PLL_RESET_I) +X(TX_POWERDOWN_N_I) +X(TX_POLARITY_I) +X(TX_PRBS_SEL_I) +X(TX_PRBS_FORCE_ERR_I) +X(TX_8B10B_EN_I) +X(TX_8B10B_BYPASS_I) +X(TX_CHAR_IS_K_I) +X(TX_CHAR_DISPMODE_I) +X(TX_CHAR_DISPVAL_I) +X(TX_ELEC_IDLE_I) +X(TX_DETECT_RX_I) +X(LOOPBACK_I) +X(CLK_CORE_TX_I) +X(CLK_CORE_RX_I) +X(RX_RESET_I) +X(RX_PMA_RESET_I) +X(RX_EQA_RESET_I) +X(RX_CDR_RESET_I) +X(RX_PCS_RESET_I) +X(RX_BUF_RESET_I) +X(RX_POWERDOWN_N_I) +X(RX_POLARITY_I) +X(RX_PRBS_SEL_I) +X(RX_PRBS_CNT_RESET_I) +X(RX_8B10B_EN_I) +X(RX_8B10B_BYPASS_I) +X(RX_EN_EI_DETECTOR_I) +X(RX_COMMA_DETECT_EN_I) +X(RX_SLIDE_I) +X(RX_MCOMMA_ALIGN_I) +X(RX_PCOMMA_ALIGN_I) +X(CLK_REG_I) +X(REGFILE_WE_I) +X(REGFILE_EN_I) +X(REGFILE_ADDR_I) +X(REGFILE_DI_I) +X(REGFILE_MASK_I) +X(RX_DATA_O) +X(RX_NOT_IN_TABLE_O) +X(RX_CHAR_IS_COMMA_O) +X(RX_CHAR_IS_K_O) +X(RX_DISP_ERR_O) +X(RX_DETECT_DONE_O) +X(RX_PRESENT_O) +X(TX_BUF_ERR_O) +X(TX_RESETDONE_O) +X(RX_PRBS_ERR_O) +X(RX_BUF_ERR_O) +X(RX_BYTE_IS_ALIGNED_O) +X(RX_BYTE_REALIGN_O) +X(RX_RESETDONE_O) +X(RX_EI_EN_O) +X(CLK_CORE_RX_O) +X(CLK_CORE_PLL_O) +X(REGFILE_DO_O) +X(REGFILE_RDY_O) + +// primitive CC_CFG_CTRL +X(CC_CFG_CTRL) +X(DATA) +//X(CLK) +//X(EN) +X(RECFG) +X(VALID) + +// primitive CC_USR_RSTN +X(CC_USR_RSTN) +X(USR_RSTN) + +// hardware primitive CPE_HALF_U +X(CPE_HALF_U) +// CPE_HALF_U pins +X(RAM_I) +X(IN1) +X(IN2) +X(IN3) +X(IN4) +//X(CLK) +//X(EN) +//X(SR) +X(OUT) +X(RAM_O) + +// hardware primitive CPE_HALF_L +X(CPE_HALF_L) +// CPE_HALF_L pins +//X(RAM_I) +//X(IN1) +//X(IN2) +//X(IN3) +//X(IN4) +//X(CLK) +//X(EN) +//X(SR) +//X(OUT) +//X(RAM_O) +X(CINX) +X(PINX) +X(CINY1) +X(PINY1) +X(CINY2) +X(PINY2) +X(COUTX) +X(POUTX) +X(COUTY1) +X(POUTY1) +X(COUTY2) +X(POUTY2) + +// hardware primitive GPIO +X(GPIO) +// GPIO pins +//X(IN1) +//X(IN2) +X(OUT1) +X(OUT2) +X(OUT3) +X(OUT4) +//X(DDR) +X(RESET) +X(CLOCK1) +X(CLOCK2) +X(CLOCK3) +X(CLOCK4) + +// hardware primitive BUFG +X(BUFG) +// BUFG pins +//X(I) +//X(O) + +// hardware primitive PLL +X(PLL) +// PLL pins +//X(CLK_REF) +//X(USR_CLK_REF) +//X(USR_SEL_A_B) +//X(CLK_FEEDBACK) +//X(USR_LOCKED_STDY_RST) +//X(CLK0) +//X(CLK90) +//X(CLK180) +//X(CLK270) +//X(CLK_REF_OUT) +//X(USR_PLL_LOCKED_STDY) +//X(USR_PLL_LOCKED) + +// hardware primitive USR_RSTN +//X(USR_RSTN) +// USR_RSTN pins +//X(USR_RSTN) + +// hardware primitive CFG_CTRL +X(CFG_CTRL) +// CFG_CTRL pins +//X(DATA[7]) +//X(DATA[6]) +//X(DATA[5]) +//X(DATA[4]) +//X(DATA[3]) +//X(DATA[2]) +//X(DATA[1]) +//X(DATA[0]) +//X(CLK) +//X(EN) +//X(VALID) +//X(RECFG) + +// hardware primitive RAM +X(RAM) +// RAM pins +//X(C_ADDRA[0]) +//X(C_ADDRA[1]) +//X(C_ADDRA[2]) +//X(C_ADDRA[3]) +//X(C_ADDRA[4]) +//X(C_ADDRA[5]) +//X(C_ADDRA[6]) +//X(C_ADDRA[7]) +//X(C_ADDRB[0]) +//X(C_ADDRB[1]) +//X(C_ADDRB[2]) +//X(C_ADDRB[3]) +//X(C_ADDRB[4]) +//X(C_ADDRB[5]) +//X(C_ADDRB[6]) +//X(C_ADDRB[7]) +//X(CLKA[0]) +//X(CLKA[1]) +//X(ENA[0]) +//X(ENA[1]) +//X(GLWEA[0]) +//X(GLWEA[1]) +//X(ADDRA0[0]) +//X(ADDRA0[1]) +//X(ADDRA0[2]) +//X(ADDRA0[3]) +//X(ADDRA0[4]) +//X(ADDRA0[5]) +//X(ADDRA0[6]) +//X(ADDRA0[7]) +//X(ADDRA0[8]) +//X(ADDRA0[9]) +//X(ADDRA0[10]) +//X(ADDRA0[11]) +//X(ADDRA0[12]) +//X(ADDRA0[13]) +//X(ADDRA0[14]) +//X(ADDRA0[15]) +//X(ADDRA0X[0]) +//X(ADDRA0X[1]) +//X(ADDRA0X[2]) +//X(ADDRA0X[3]) +//X(ADDRA0X[4]) +//X(ADDRA0X[5]) +//X(ADDRA0X[6]) +//X(ADDRA0X[7]) +//X(ADDRA0X[8]) +//X(ADDRA0X[9]) +//X(ADDRA0X[10]) +//X(ADDRA0X[11]) +//X(ADDRA0X[12]) +//X(ADDRA0X[13]) +//X(ADDRA0X[14]) +//X(ADDRA0X[15]) +//X(DIA[0]) +//X(DIA[1]) +//X(DIA[2]) +//X(DIA[3]) +//X(DIA[4]) +//X(DIA[5]) +//X(DIA[6]) +//X(DIA[7]) +//X(DIA[8]) +//X(DIA[9]) +//X(DIA[10]) +//X(DIA[11]) +//X(DIA[12]) +//X(DIA[13]) +//X(DIA[14]) +//X(DIA[15]) +//X(DIA[16]) +//X(DIA[17]) +//X(DIA[18]) +//X(DIA[19]) +//X(WEA[0]) +//X(WEA[1]) +//X(WEA[2]) +//X(WEA[3]) +//X(WEA[4]) +//X(WEA[5]) +//X(WEA[6]) +//X(WEA[7]) +//X(WEA[8]) +//X(WEA[9]) +//X(WEA[10]) +//X(WEA[11]) +//X(WEA[12]) +//X(WEA[13]) +//X(WEA[14]) +//X(WEA[15]) +//X(WEA[16]) +//X(WEA[17]) +//X(WEA[18]) +//X(WEA[19]) +//X(CLKA[2]) +//X(CLKA[3]) +//X(ENA[2]) +//X(ENA[3]) +//X(GLWEA[2]) +//X(GLWEA[3]) +//X(ADDRA1[0]) +//X(ADDRA1[1]) +//X(ADDRA1[2]) +//X(ADDRA1[3]) +//X(ADDRA1[4]) +//X(ADDRA1[5]) +//X(ADDRA1[6]) +//X(ADDRA1[7]) +//X(ADDRA1[8]) +//X(ADDRA1[9]) +//X(ADDRA1[10]) +//X(ADDRA1[11]) +//X(ADDRA1[12]) +//X(ADDRA1[13]) +//X(ADDRA1[14]) +//X(ADDRA1[15]) +//X(ADDRA1X[0]) +//X(ADDRA1X[1]) +//X(ADDRA1X[2]) +//X(ADDRA1X[3]) +//X(ADDRA1X[4]) +//X(ADDRA1X[5]) +//X(ADDRA1X[6]) +//X(ADDRA1X[7]) +//X(ADDRA1X[8]) +//X(ADDRA1X[9]) +//X(ADDRA1X[10]) +//X(ADDRA1X[11]) +//X(ADDRA1X[12]) +//X(ADDRA1X[13]) +//X(ADDRA1X[14]) +//X(ADDRA1X[15]) +//X(DIA[20]) +//X(DIA[21]) +//X(DIA[22]) +//X(DIA[23]) +//X(DIA[24]) +//X(DIA[25]) +//X(DIA[26]) +//X(DIA[27]) +//X(DIA[28]) +//X(DIA[29]) +//X(DIA[30]) +//X(DIA[31]) +//X(DIA[32]) +//X(DIA[33]) +//X(DIA[34]) +//X(DIA[35]) +//X(DIA[36]) +//X(DIA[37]) +//X(DIA[38]) +//X(DIA[39]) +//X(WEA[20]) +//X(WEA[21]) +//X(WEA[22]) +//X(WEA[23]) +//X(WEA[24]) +//X(WEA[25]) +//X(WEA[26]) +//X(WEA[27]) +//X(WEA[28]) +//X(WEA[29]) +//X(WEA[30]) +//X(WEA[31]) +//X(WEA[32]) +//X(WEA[33]) +//X(WEA[34]) +//X(WEA[35]) +//X(WEA[36]) +//X(WEA[37]) +//X(WEA[38]) +//X(WEA[39]) +//X(CLKB[0]) +//X(CLKB[1]) +//X(ENB[0]) +//X(ENB[1]) +//X(GLWEB[0]) +//X(GLWEB[1]) +//X(ADDRB0[0]) +//X(ADDRB0[1]) +//X(ADDRB0[2]) +//X(ADDRB0[3]) +//X(ADDRB0[4]) +//X(ADDRB0[5]) +//X(ADDRB0[6]) +//X(ADDRB0[7]) +//X(ADDRB0[8]) +//X(ADDRB0[9]) +//X(ADDRB0[10]) +//X(ADDRB0[11]) +//X(ADDRB0[12]) +//X(ADDRB0[13]) +//X(ADDRB0[14]) +//X(ADDRB0[15]) +//X(ADDRB0X[0]) +//X(ADDRB0X[1]) +//X(ADDRB0X[2]) +//X(ADDRB0X[3]) +//X(ADDRB0X[4]) +//X(ADDRB0X[5]) +//X(ADDRB0X[6]) +//X(ADDRB0X[7]) +//X(ADDRB0X[8]) +//X(ADDRB0X[9]) +//X(ADDRB0X[10]) +//X(ADDRB0X[11]) +//X(ADDRB0X[12]) +//X(ADDRB0X[13]) +//X(ADDRB0X[14]) +//X(ADDRB0X[15]) +//X(DIB[0]) +//X(DIB[1]) +//X(DIB[2]) +//X(DIB[3]) +//X(DIB[4]) +//X(DIB[5]) +//X(DIB[6]) +//X(DIB[7]) +//X(DIB[8]) +//X(DIB[9]) +//X(DIB[10]) +//X(DIB[11]) +//X(DIB[12]) +//X(DIB[13]) +//X(DIB[14]) +//X(DIB[15]) +//X(DIB[16]) +//X(DIB[17]) +//X(DIB[18]) +//X(DIB[19]) +//X(WEB[0]) +//X(WEB[1]) +//X(WEB[2]) +//X(WEB[3]) +//X(WEB[4]) +//X(WEB[5]) +//X(WEB[6]) +//X(WEB[7]) +//X(WEB[8]) +//X(WEB[9]) +//X(WEB[10]) +//X(WEB[11]) +//X(WEB[12]) +//X(WEB[13]) +//X(WEB[14]) +//X(WEB[15]) +//X(WEB[16]) +//X(WEB[17]) +//X(WEB[18]) +//X(WEB[19]) +//X(CLKB[2]) +//X(CLKB[3]) +//X(ENB[2]) +//X(ENB[3]) +//X(GLWEB[2]) +//X(GLWEB[3]) +//X(ADDRB1[0]) +//X(ADDRB1[1]) +//X(ADDRB1[2]) +//X(ADDRB1[3]) +//X(ADDRB1[4]) +//X(ADDRB1[5]) +//X(ADDRB1[6]) +//X(ADDRB1[7]) +//X(ADDRB1[8]) +//X(ADDRB1[9]) +//X(ADDRB1[10]) +//X(ADDRB1[11]) +//X(ADDRB1[12]) +//X(ADDRB1[13]) +//X(ADDRB1[14]) +//X(ADDRB1[15]) +//X(ADDRB1X[0]) +//X(ADDRB1X[1]) +//X(ADDRB1X[2]) +//X(ADDRB1X[3]) +//X(ADDRB1X[4]) +//X(ADDRB1X[5]) +//X(ADDRB1X[6]) +//X(ADDRB1X[7]) +//X(ADDRB1X[8]) +//X(ADDRB1X[9]) +//X(ADDRB1X[10]) +//X(ADDRB1X[11]) +//X(ADDRB1X[12]) +//X(ADDRB1X[13]) +//X(ADDRB1X[14]) +//X(ADDRB1X[15]) +//X(DIB[20]) +//X(DIB[21]) +//X(DIB[22]) +//X(DIB[23]) +//X(DIB[24]) +//X(DIB[25]) +//X(DIB[26]) +//X(DIB[27]) +//X(DIB[28]) +//X(DIB[29]) +//X(DIB[30]) +//X(DIB[31]) +//X(DIB[32]) +//X(DIB[33]) +//X(DIB[34]) +//X(DIB[35]) +//X(DIB[36]) +//X(DIB[37]) +//X(DIB[38]) +//X(DIB[39]) +//X(WEB[20]) +//X(WEB[21]) +//X(WEB[22]) +//X(WEB[23]) +//X(WEB[24]) +//X(WEB[25]) +//X(WEB[26]) +//X(WEB[27]) +//X(WEB[28]) +//X(WEB[29]) +//X(WEB[30]) +//X(WEB[31]) +//X(WEB[32]) +//X(WEB[33]) +//X(WEB[34]) +//X(WEB[35]) +//X(WEB[36]) +//X(WEB[37]) +//X(WEB[38]) +//X(WEB[39]) +X(F_RSTN) +//X(DOA[0]) +//X(DOAX[0]) +//X(DOA[1]) +//X(DOAX[1]) +//X(DOA[2]) +//X(DOAX[2]) +//X(DOA[3]) +//X(DOAX[3]) +//X(DOA[4]) +//X(DOAX[4]) +//X(DOA[5]) +//X(DOAX[5]) +//X(DOA[6]) +//X(DOAX[6]) +//X(DOA[7]) +//X(DOAX[7]) +//X(DOA[8]) +//X(DOAX[8]) +//X(DOA[9]) +//X(DOAX[9]) +//X(DOA[10]) +//X(DOAX[10]) +//X(DOA[11]) +//X(DOAX[11]) +//X(DOA[12]) +//X(DOAX[12]) +//X(DOA[13]) +//X(DOAX[13]) +//X(DOA[14]) +//X(DOAX[14]) +//X(DOA[15]) +//X(DOAX[15]) +//X(DOA[16]) +//X(DOAX[16]) +//X(DOA[17]) +//X(DOAX[17]) +//X(DOA[18]) +//X(DOAX[18]) +//X(DOA[19]) +//X(DOAX[19]) +//X(DOA[20]) +//X(DOAX[20]) +//X(DOA[21]) +//X(DOAX[21]) +//X(DOA[22]) +//X(DOAX[22]) +//X(DOA[23]) +//X(DOAX[23]) +//X(DOA[24]) +//X(DOAX[24]) +//X(DOA[25]) +//X(DOAX[25]) +//X(DOA[26]) +//X(DOAX[26]) +//X(DOA[27]) +//X(DOAX[27]) +//X(DOA[28]) +//X(DOAX[28]) +//X(DOA[29]) +//X(DOAX[29]) +//X(DOA[30]) +//X(DOAX[30]) +//X(DOA[31]) +//X(DOAX[31]) +//X(DOA[32]) +//X(DOAX[32]) +//X(DOA[33]) +//X(DOAX[33]) +//X(DOA[34]) +//X(DOAX[34]) +//X(DOA[35]) +//X(DOAX[35]) +//X(DOA[36]) +//X(DOAX[36]) +//X(DOA[37]) +//X(DOAX[37]) +//X(DOA[38]) +//X(DOAX[38]) +//X(DOA[39]) +//X(DOAX[39]) +//X(CLOCKA[1]) +//X(CLOCKA[2]) +//X(CLOCKA[3]) +//X(CLOCKA[4]) +//X(DOB[0]) +//X(DOBX[0]) +//X(DOB[1]) +//X(DOBX[1]) +//X(DOB[2]) +//X(DOBX[2]) +//X(DOB[3]) +//X(DOBX[3]) +//X(DOB[4]) +//X(DOBX[4]) +//X(DOB[5]) +//X(DOBX[5]) +//X(DOB[6]) +//X(DOBX[6]) +//X(DOB[7]) +//X(DOBX[7]) +//X(DOB[8]) +//X(DOBX[8]) +//X(DOB[9]) +//X(DOBX[9]) +//X(DOB[10]) +//X(DOBX[10]) +//X(DOB[11]) +//X(DOBX[11]) +//X(DOB[12]) +//X(DOBX[12]) +//X(DOB[13]) +//X(DOBX[13]) +//X(DOB[14]) +//X(DOBX[14]) +//X(DOB[15]) +//X(DOBX[15]) +//X(DOB[16]) +//X(DOBX[16]) +//X(DOB[17]) +//X(DOBX[17]) +//X(DOB[18]) +//X(DOBX[18]) +//X(DOB[19]) +//X(DOBX[19]) +//X(DOB[20]) +//X(DOBX[20]) +//X(DOB[21]) +//X(DOBX[21]) +//X(DOB[22]) +//X(DOBX[22]) +//X(DOB[23]) +//X(DOBX[23]) +//X(DOB[24]) +//X(DOBX[24]) +//X(DOB[25]) +//X(DOBX[25]) +//X(DOB[26]) +//X(DOBX[26]) +//X(DOB[27]) +//X(DOBX[27]) +//X(DOB[28]) +//X(DOBX[28]) +//X(DOB[29]) +//X(DOBX[29]) +//X(DOB[30]) +//X(DOBX[30]) +//X(DOB[31]) +//X(DOBX[31]) +//X(DOB[32]) +//X(DOBX[32]) +//X(DOB[33]) +//X(DOBX[33]) +//X(DOB[34]) +//X(DOBX[34]) +//X(DOB[35]) +//X(DOBX[35]) +//X(DOB[36]) +//X(DOBX[36]) +//X(DOB[37]) +//X(DOBX[37]) +//X(DOB[38]) +//X(DOBX[38]) +//X(DOB[39]) +//X(DOBX[39]) +//X(CLOCKB[1]) +//X(CLOCKB[2]) +//X(CLOCKB[3]) +//X(CLOCKB[4]) +//X(ECC1B_ERRA[0]) +//X(ECC1B_ERRA[1]) +//X(ECC1B_ERRA[2]) +//X(ECC1B_ERRA[3]) +//X(ECC1B_ERRB[0]) +//X(ECC1B_ERRB[1]) +//X(ECC1B_ERRB[2]) +//X(ECC1B_ERRB[3]) +//X(ECC2B_ERRA[0]) +//X(ECC2B_ERRA[1]) +//X(ECC2B_ERRA[2]) +//X(ECC2B_ERRA[3]) +//X(ECC2B_ERRB[0]) +//X(ECC2B_ERRB[1]) +//X(ECC2B_ERRB[2]) +//X(ECC2B_ERRB[3]) +//X(F_FULL[0]) +//X(F_FULL[1]) +//X(F_EMPTY[0]) +//X(F_EMPTY[1]) +//X(F_AL_FULL[0]) +//X(F_AL_FULL[1]) +//X(F_AL_EMPTY[0]) +//X(F_AL_EMPTY[1]) +//X(FWR_ERR[0]) +//X(FWR_ERR[1]) +//X(FRD_ERR[0]) +//X(FRD_ERR[1]) +//X(FWR_ADDR[0]) +//X(FWR_ADDRX[0]) +//X(FWR_ADDR[1]) +//X(FWR_ADDRX[1]) +//X(FWR_ADDR[2]) +//X(FWR_ADDRX[2]) +//X(FWR_ADDR[3]) +//X(FWR_ADDRX[3]) +//X(FWR_ADDR[4]) +//X(FWR_ADDRX[4]) +//X(FWR_ADDR[5]) +//X(FWR_ADDRX[5]) +//X(FWR_ADDR[6]) +//X(FWR_ADDRX[6]) +//X(FWR_ADDR[7]) +//X(FWR_ADDRX[7]) +//X(FWR_ADDR[8]) +//X(FWR_ADDRX[8]) +//X(FWR_ADDR[9]) +//X(FWR_ADDRX[9]) +//X(FWR_ADDR[10]) +//X(FWR_ADDRX[10]) +//X(FWR_ADDR[11]) +//X(FWR_ADDRX[11]) +//X(FWR_ADDR[12]) +//X(FWR_ADDRX[12]) +//X(FWR_ADDR[13]) +//X(FWR_ADDRX[13]) +//X(FWR_ADDR[14]) +//X(FWR_ADDRX[14]) +//X(FWR_ADDR[15]) +//X(FWR_ADDRX[15]) +//X(FRD_ADDR[0]) +//X(FRD_ADDRX[0]) +//X(FRD_ADDR[1]) +//X(FRD_ADDRX[1]) +//X(FRD_ADDR[2]) +//X(FRD_ADDRX[2]) +//X(FRD_ADDR[3]) +//X(FRD_ADDRX[3]) +//X(FRD_ADDR[4]) +//X(FRD_ADDRX[4]) +//X(FRD_ADDR[5]) +//X(FRD_ADDRX[5]) +//X(FRD_ADDR[6]) +//X(FRD_ADDRX[6]) +//X(FRD_ADDR[7]) +//X(FRD_ADDRX[7]) +//X(FRD_ADDR[8]) +//X(FRD_ADDRX[8]) +//X(FRD_ADDR[9]) +//X(FRD_ADDRX[9]) +//X(FRD_ADDR[10]) +//X(FRD_ADDRX[10]) +//X(FRD_ADDR[11]) +//X(FRD_ADDRX[11]) +//X(FRD_ADDR[12]) +//X(FRD_ADDRX[12]) +//X(FRD_ADDR[13]) +//X(FRD_ADDRX[13]) +//X(FRD_ADDR[14]) +//X(FRD_ADDRX[14]) +//X(FRD_ADDR[15]) +//X(FRD_ADDRX[15]) +X(FORW_CAS_WRAO) +X(FORW_CAS_WRAI) +X(FORW_CAS_WRBO) +X(FORW_CAS_WRBI) +X(FORW_CAS_BMAO) +X(FORW_CAS_BMAI) +X(FORW_CAS_BMBO) +X(FORW_CAS_BMBI) +X(FORW_CAS_RDAO) +X(FORW_CAS_RDAI) +X(FORW_CAS_RDBO) +X(FORW_CAS_RDBI) +//X(FORW_UADDRAO[0]) +//X(FORW_UADDRAO[1]) +//X(FORW_UADDRAO[2]) +//X(FORW_UADDRAO[3]) +//X(FORW_UADDRAO[4]) +//X(FORW_UADDRAO[5]) +//X(FORW_UADDRAO[6]) +//X(FORW_UADDRAO[7]) +//X(FORW_UADDRAO[8]) +//X(FORW_UADDRAO[9]) +//X(FORW_UADDRAO[10]) +//X(FORW_UADDRAO[11]) +//X(FORW_UADDRAO[12]) +//X(FORW_UADDRAO[13]) +//X(FORW_UADDRAO[14]) +//X(FORW_UADDRAO[15]) +//X(FORW_LADDRAI[0]) +//X(FORW_LADDRAI[1]) +//X(FORW_LADDRAI[2]) +//X(FORW_LADDRAI[3]) +//X(FORW_LADDRAI[4]) +//X(FORW_LADDRAI[5]) +//X(FORW_LADDRAI[6]) +//X(FORW_LADDRAI[7]) +//X(FORW_LADDRAI[8]) +//X(FORW_LADDRAI[9]) +//X(FORW_LADDRAI[10]) +//X(FORW_LADDRAI[11]) +//X(FORW_LADDRAI[12]) +//X(FORW_LADDRAI[13]) +//X(FORW_LADDRAI[14]) +//X(FORW_LADDRAI[15]) +//X(FORW_LADDRAO[0]) +//X(FORW_LADDRAO[1]) +//X(FORW_LADDRAO[2]) +//X(FORW_LADDRAO[3]) +//X(FORW_LADDRAO[4]) +//X(FORW_LADDRAO[5]) +//X(FORW_LADDRAO[6]) +//X(FORW_LADDRAO[7]) +//X(FORW_LADDRAO[8]) +//X(FORW_LADDRAO[9]) +//X(FORW_LADDRAO[10]) +//X(FORW_LADDRAO[11]) +//X(FORW_LADDRAO[12]) +//X(FORW_LADDRAO[13]) +//X(FORW_LADDRAO[14]) +//X(FORW_LADDRAO[15]) +//X(FORW_UADDRAI[0]) +//X(FORW_UADDRAI[1]) +//X(FORW_UADDRAI[2]) +//X(FORW_UADDRAI[3]) +//X(FORW_UADDRAI[4]) +//X(FORW_UADDRAI[5]) +//X(FORW_UADDRAI[6]) +//X(FORW_UADDRAI[7]) +//X(FORW_UADDRAI[8]) +//X(FORW_UADDRAI[9]) +//X(FORW_UADDRAI[10]) +//X(FORW_UADDRAI[11]) +//X(FORW_UADDRAI[12]) +//X(FORW_UADDRAI[13]) +//X(FORW_UADDRAI[14]) +//X(FORW_UADDRAI[15]) +//X(FORW_UADDRBO[0]) +//X(FORW_UADDRBO[1]) +//X(FORW_UADDRBO[2]) +//X(FORW_UADDRBO[3]) +//X(FORW_UADDRBO[4]) +//X(FORW_UADDRBO[5]) +//X(FORW_UADDRBO[6]) +//X(FORW_UADDRBO[7]) +//X(FORW_UADDRBO[8]) +//X(FORW_UADDRBO[9]) +//X(FORW_UADDRBO[10]) +//X(FORW_UADDRBO[11]) +//X(FORW_UADDRBO[12]) +//X(FORW_UADDRBO[13]) +//X(FORW_UADDRBO[14]) +//X(FORW_UADDRBO[15]) +//X(FORW_LADDRBI[0]) +//X(FORW_LADDRBI[1]) +//X(FORW_LADDRBI[2]) +//X(FORW_LADDRBI[3]) +//X(FORW_LADDRBI[4]) +//X(FORW_LADDRBI[5]) +//X(FORW_LADDRBI[6]) +//X(FORW_LADDRBI[7]) +//X(FORW_LADDRBI[8]) +//X(FORW_LADDRBI[9]) +//X(FORW_LADDRBI[10]) +//X(FORW_LADDRBI[11]) +//X(FORW_LADDRBI[12]) +//X(FORW_LADDRBI[13]) +//X(FORW_LADDRBI[14]) +//X(FORW_LADDRBI[15]) +//X(FORW_LADDRBO[0]) +//X(FORW_LADDRBO[1]) +//X(FORW_LADDRBO[2]) +//X(FORW_LADDRBO[3]) +//X(FORW_LADDRBO[4]) +//X(FORW_LADDRBO[5]) +//X(FORW_LADDRBO[6]) +//X(FORW_LADDRBO[7]) +//X(FORW_LADDRBO[8]) +//X(FORW_LADDRBO[9]) +//X(FORW_LADDRBO[10]) +//X(FORW_LADDRBO[11]) +//X(FORW_LADDRBO[12]) +//X(FORW_LADDRBO[13]) +//X(FORW_LADDRBO[14]) +//X(FORW_LADDRBO[15]) +//X(FORW_UADDRBI[0]) +//X(FORW_UADDRBI[1]) +//X(FORW_UADDRBI[2]) +//X(FORW_UADDRBI[3]) +//X(FORW_UADDRBI[4]) +//X(FORW_UADDRBI[5]) +//X(FORW_UADDRBI[6]) +//X(FORW_UADDRBI[7]) +//X(FORW_UADDRBI[8]) +//X(FORW_UADDRBI[9]) +//X(FORW_UADDRBI[10]) +//X(FORW_UADDRBI[11]) +//X(FORW_UADDRBI[12]) +//X(FORW_UADDRBI[13]) +//X(FORW_UADDRBI[14]) +//X(FORW_UADDRBI[15]) +X(FORW_UA0CLKO) +X(FORW_LA0CLKI) +X(FORW_UA0ENO) +X(FORW_LA0ENI) +X(FORW_UA0WEO) +X(FORW_LA0WEI) +X(FORW_LA0CLKO) +X(FORW_UA0CLKI) +X(FORW_LA0ENO) +X(FORW_UA0ENI) +X(FORW_LA0WEO) +X(FORW_UA0WEI) +X(FORW_UA1CLKO) +X(FORW_LA1CLKI) +X(FORW_UA1ENO) +X(FORW_LA1ENI) +X(FORW_UA1WEO) +X(FORW_LA1WEI) +X(FORW_LA1CLKO) +X(FORW_UA1CLKI) +X(FORW_LA1ENO) +X(FORW_UA1ENI) +X(FORW_LA1WEO) +X(FORW_UA1WEI) +X(FORW_UB0CLKO) +X(FORW_LB0CLKI) +X(FORW_UB0ENO) +X(FORW_LB0ENI) +X(FORW_UB0WEO) +X(FORW_LB0WEI) +X(FORW_LB0CLKO) +X(FORW_UB0CLKI) +X(FORW_LB0ENO) +X(FORW_UB0ENI) +X(FORW_LB0WEO) +X(FORW_UB0WEI) +X(FORW_UB1CLKO) +X(FORW_LB1CLKI) +X(FORW_UB1ENO) +X(FORW_LB1ENI) +X(FORW_UB1WEO) +X(FORW_LB1WEI) +X(FORW_LB1CLKO) +X(FORW_UB1CLKI) +X(FORW_LB1ENO) +X(FORW_UB1ENI) +X(FORW_LB1WEO) +X(FORW_UB1WEI) +//X(CLOCK1) +//X(CLOCK2) +//X(CLOCK3) +//X(CLOCK4) + +// hardware primitive SERDES +X(SERDES) +// SERDES pins +//X(TX_DETECT_RX_I) +//X(PLL_RESET_I) +//X(CLK_REG_I) +//X(CLK_CORE_TX_I) +//X(CLK_CORE_RX_I) +//X(REGFILE_WE_I) +//X(REGFILE_EN_I) +//X(TX_RESET_I) +//X(TX_PCS_RESET_I) +//X(TX_PMA_RESET_I) +//X(TX_PRBS_FORCE_ERR_I) +//X(TX_POLARITY_I) +//X(TX_8B10B_EN_I) +//X(RX_RESET_I) +//X(RX_PMA_RESET_I) +//X(RX_EQA_RESET_I) +//X(RX_CDR_RESET_I) +//X(RX_PCS_RESET_I) +//X(RX_BUF_RESET_I) +//X(RX_PRBS_CNT_RESET_I) +//X(RX_EN_EI_DETECTOR_I) +//X(RX_COMMA_DETECT_EN_I) +//X(RX_SLIDE_I) +//X(RX_POLARITY_I) +//X(RX_8B10B_EN_I) +//X(RX_MCOMMA_ALIGN_I) +//X(RX_PCOMMA_ALIGN_I) +//X(RX_NOT_IN_TABLE_O[7]) +//X(RX_NOT_IN_TABLE_O[6]) +//X(RX_NOT_IN_TABLE_O[5]) +//X(RX_NOT_IN_TABLE_O[4]) +//X(RX_NOT_IN_TABLE_O[3]) +//X(RX_NOT_IN_TABLE_O[2]) +//X(RX_NOT_IN_TABLE_O[1]) +//X(RX_NOT_IN_TABLE_O[0]) +//X(RX_CHAR_IS_COMMA_O[7]) +//X(RX_CHAR_IS_COMMA_O[6]) +//X(RX_CHAR_IS_COMMA_O[5]) +//X(RX_CHAR_IS_COMMA_O[4]) +//X(RX_CHAR_IS_COMMA_O[3]) +//X(RX_CHAR_IS_COMMA_O[2]) +//X(RX_CHAR_IS_COMMA_O[1]) +//X(RX_CHAR_IS_COMMA_O[0]) +//X(REGFILE_ADDR_I[7]) +//X(REGFILE_ADDR_I[6]) +//X(REGFILE_ADDR_I[5]) +//X(REGFILE_ADDR_I[4]) +//X(REGFILE_ADDR_I[3]) +//X(REGFILE_ADDR_I[2]) +//X(REGFILE_ADDR_I[1]) +//X(REGFILE_ADDR_I[0]) +//X(TX_CHAR_IS_K_I[7]) +//X(TX_CHAR_IS_K_I[6]) +//X(TX_CHAR_IS_K_I[5]) +//X(TX_CHAR_IS_K_I[4]) +//X(TX_CHAR_IS_K_I[3]) +//X(TX_CHAR_IS_K_I[2]) +//X(TX_CHAR_IS_K_I[1]) +//X(TX_CHAR_IS_K_I[0]) +//X(TX_8B10B_BYPASS_I[7]) +//X(TX_8B10B_BYPASS_I[6]) +//X(TX_8B10B_BYPASS_I[5]) +//X(TX_8B10B_BYPASS_I[4]) +//X(TX_8B10B_BYPASS_I[3]) +//X(TX_8B10B_BYPASS_I[2]) +//X(TX_8B10B_BYPASS_I[1]) +//X(TX_8B10B_BYPASS_I[0]) +//X(RX_8B10B_BYPASS_I[7]) +//X(RX_8B10B_BYPASS_I[6]) +//X(RX_8B10B_BYPASS_I[5]) +//X(RX_8B10B_BYPASS_I[4]) +//X(RX_8B10B_BYPASS_I[3]) +//X(RX_8B10B_BYPASS_I[2]) +//X(RX_8B10B_BYPASS_I[1]) +//X(RX_8B10B_BYPASS_I[0]) +//X(TX_CHAR_DISPMODE_I[7]) +//X(TX_CHAR_DISPMODE_I[6]) +//X(TX_CHAR_DISPMODE_I[5]) +//X(TX_CHAR_DISPMODE_I[4]) +//X(TX_CHAR_DISPMODE_I[3]) +//X(TX_CHAR_DISPMODE_I[2]) +//X(TX_CHAR_DISPMODE_I[1]) +//X(TX_CHAR_DISPMODE_I[0]) +//X(TX_CHAR_DISPVAL_I[7]) +//X(TX_CHAR_DISPVAL_I[6]) +//X(TX_CHAR_DISPVAL_I[5]) +//X(TX_CHAR_DISPVAL_I[4]) +//X(TX_CHAR_DISPVAL_I[3]) +//X(TX_CHAR_DISPVAL_I[2]) +//X(TX_CHAR_DISPVAL_I[1]) +//X(TX_CHAR_DISPVAL_I[0]) +//X(TX_DATA_I[63]) +//X(TX_DATA_I[62]) +//X(TX_DATA_I[61]) +//X(TX_DATA_I[60]) +//X(TX_DATA_I[59]) +//X(TX_DATA_I[58]) +//X(TX_DATA_I[57]) +//X(TX_DATA_I[56]) +//X(TX_DATA_I[55]) +//X(TX_DATA_I[54]) +//X(TX_DATA_I[53]) +//X(TX_DATA_I[52]) +//X(TX_DATA_I[51]) +//X(TX_DATA_I[50]) +//X(TX_DATA_I[49]) +//X(TX_DATA_I[48]) +//X(TX_DATA_I[47]) +//X(TX_DATA_I[46]) +//X(TX_DATA_I[45]) +//X(TX_DATA_I[44]) +//X(TX_DATA_I[43]) +//X(TX_DATA_I[42]) +//X(TX_DATA_I[41]) +//X(TX_DATA_I[40]) +//X(TX_DATA_I[39]) +//X(TX_DATA_I[38]) +//X(TX_DATA_I[37]) +//X(TX_DATA_I[36]) +//X(TX_DATA_I[35]) +//X(TX_DATA_I[34]) +//X(TX_DATA_I[33]) +//X(TX_DATA_I[32]) +//X(TX_DATA_I[31]) +//X(TX_DATA_I[30]) +//X(TX_DATA_I[29]) +//X(TX_DATA_I[28]) +//X(TX_DATA_I[27]) +//X(TX_DATA_I[26]) +//X(TX_DATA_I[25]) +//X(TX_DATA_I[24]) +//X(TX_DATA_I[23]) +//X(TX_DATA_I[22]) +//X(TX_DATA_I[21]) +//X(TX_DATA_I[20]) +//X(TX_DATA_I[19]) +//X(TX_DATA_I[18]) +//X(TX_DATA_I[17]) +//X(TX_DATA_I[16]) +//X(TX_DATA_I[15]) +//X(TX_DATA_I[14]) +//X(TX_DATA_I[13]) +//X(TX_DATA_I[12]) +//X(TX_DATA_I[11]) +//X(TX_DATA_I[10]) +//X(TX_DATA_I[9]) +//X(TX_DATA_I[8]) +//X(TX_DATA_I[7]) +//X(TX_DATA_I[6]) +//X(TX_DATA_I[5]) +//X(TX_DATA_I[4]) +//X(TX_DATA_I[3]) +//X(TX_DATA_I[2]) +//X(TX_DATA_I[1]) +//X(TX_DATA_I[0]) +//X(REGFILE_DO_O[15]) +//X(REGFILE_DO_O[14]) +//X(REGFILE_DO_O[13]) +//X(REGFILE_DO_O[12]) +//X(REGFILE_DO_O[11]) +//X(REGFILE_DO_O[10]) +//X(REGFILE_DO_O[9]) +//X(REGFILE_DO_O[8]) +//X(REGFILE_DO_O[7]) +//X(REGFILE_DO_O[6]) +//X(REGFILE_DO_O[5]) +//X(REGFILE_DO_O[4]) +//X(REGFILE_DO_O[3]) +//X(REGFILE_DO_O[2]) +//X(REGFILE_DO_O[1]) +//X(REGFILE_DO_O[0]) +//X(REGFILE_DI_I[15]) +//X(REGFILE_DI_I[14]) +//X(REGFILE_DI_I[13]) +//X(REGFILE_DI_I[12]) +//X(REGFILE_DI_I[11]) +//X(REGFILE_DI_I[10]) +//X(REGFILE_DI_I[9]) +//X(REGFILE_DI_I[8]) +//X(REGFILE_DI_I[7]) +//X(REGFILE_DI_I[6]) +//X(REGFILE_DI_I[5]) +//X(REGFILE_DI_I[4]) +//X(REGFILE_DI_I[3]) +//X(REGFILE_DI_I[2]) +//X(REGFILE_DI_I[1]) +//X(REGFILE_DI_I[0]) +//X(REGFILE_MASK_I[15]) +//X(REGFILE_MASK_I[14]) +//X(REGFILE_MASK_I[13]) +//X(REGFILE_MASK_I[12]) +//X(REGFILE_MASK_I[11]) +//X(REGFILE_MASK_I[10]) +//X(REGFILE_MASK_I[9]) +//X(REGFILE_MASK_I[8]) +//X(REGFILE_MASK_I[7]) +//X(REGFILE_MASK_I[6]) +//X(REGFILE_MASK_I[5]) +//X(REGFILE_MASK_I[4]) +//X(REGFILE_MASK_I[3]) +//X(REGFILE_MASK_I[2]) +//X(REGFILE_MASK_I[1]) +//X(REGFILE_MASK_I[0]) +//X(RX_CHAR_IS_K_O[7]) +//X(RX_CHAR_IS_K_O[6]) +//X(RX_CHAR_IS_K_O[5]) +//X(RX_CHAR_IS_K_O[4]) +//X(RX_CHAR_IS_K_O[3]) +//X(RX_CHAR_IS_K_O[2]) +//X(RX_CHAR_IS_K_O[1]) +//X(RX_CHAR_IS_K_O[0]) +//X(RX_DISP_ERR_O[7]) +//X(RX_DISP_ERR_O[6]) +//X(RX_DISP_ERR_O[5]) +//X(RX_DISP_ERR_O[4]) +//X(RX_DISP_ERR_O[3]) +//X(RX_DISP_ERR_O[2]) +//X(RX_DISP_ERR_O[1]) +//X(RX_DISP_ERR_O[0]) +//X(RX_DATA_O[63]) +//X(RX_DATA_O[62]) +//X(RX_DATA_O[61]) +//X(RX_DATA_O[60]) +//X(RX_DATA_O[59]) +//X(RX_DATA_O[58]) +//X(RX_DATA_O[57]) +//X(RX_DATA_O[56]) +//X(RX_DATA_O[55]) +//X(RX_DATA_O[54]) +//X(RX_DATA_O[53]) +//X(RX_DATA_O[52]) +//X(RX_DATA_O[51]) +//X(RX_DATA_O[50]) +//X(RX_DATA_O[49]) +//X(RX_DATA_O[48]) +//X(RX_DATA_O[47]) +//X(RX_DATA_O[46]) +//X(RX_DATA_O[45]) +//X(RX_DATA_O[44]) +//X(RX_DATA_O[43]) +//X(RX_DATA_O[42]) +//X(RX_DATA_O[41]) +//X(RX_DATA_O[40]) +//X(RX_DATA_O[39]) +//X(RX_DATA_O[38]) +//X(RX_DATA_O[37]) +//X(RX_DATA_O[36]) +//X(RX_DATA_O[35]) +//X(RX_DATA_O[34]) +//X(RX_DATA_O[33]) +//X(RX_DATA_O[32]) +//X(RX_DATA_O[31]) +//X(RX_DATA_O[30]) +//X(RX_DATA_O[29]) +//X(RX_DATA_O[28]) +//X(RX_DATA_O[27]) +//X(RX_DATA_O[26]) +//X(RX_DATA_O[25]) +//X(RX_DATA_O[24]) +//X(RX_DATA_O[23]) +//X(RX_DATA_O[22]) +//X(RX_DATA_O[21]) +//X(RX_DATA_O[20]) +//X(RX_DATA_O[19]) +//X(RX_DATA_O[18]) +//X(RX_DATA_O[17]) +//X(RX_DATA_O[16]) +//X(RX_DATA_O[15]) +//X(RX_DATA_O[14]) +//X(RX_DATA_O[13]) +//X(RX_DATA_O[12]) +//X(RX_DATA_O[11]) +//X(RX_DATA_O[10]) +//X(RX_DATA_O[9]) +//X(RX_DATA_O[8]) +//X(RX_DATA_O[7]) +//X(RX_DATA_O[6]) +//X(RX_DATA_O[5]) +//X(RX_DATA_O[4]) +//X(RX_DATA_O[3]) +//X(RX_DATA_O[2]) +//X(RX_DATA_O[1]) +//X(RX_DATA_O[0]) +X(TX_DETECT_RX_DONE_O) +X(TX_DETECT_RX_PRESENT_O) +//X(CLK_CORE_RX_O) +//X(CLK_CORE_PLL_O) +//X(TX_BUF_ERR_O) +//X(TX_RESETDONE_O) +//X(REGFILE_RDY_O) +//X(RX_PRBS_ERR_O) +//X(RX_BUF_ERR_O) +//X(RX_BYTE_IS_ALIGNED_O) +//X(RX_BYTE_REALIGN_O) +//X(RX_RESETDONE_O) +//X(RX_EI_EN_O) +//X(LOOPBACK_I[2]) +//X(LOOPBACK_I[1]) +//X(LOOPBACK_I[0]) +//X(TX_PRBS_SEL_I[2]) +//X(TX_PRBS_SEL_I[1]) +//X(TX_PRBS_SEL_I[0]) +//X(RX_PRBS_SEL_I[2]) +//X(RX_PRBS_SEL_I[1]) +//X(RX_PRBS_SEL_I[0]) +//X(TX_POWERDOWN_N_I) +//X(RX_POWERDOWN_N_I) +//X(TX_ELEC_IDLE_I) + +// end of autogenerated items + +// CPE configuration parameters +//X(INIT_L00) +//X(INIT_L01) +//X(INIT_L02) +//X(INIT_L03) +//X(INIT_L10) +//X(INIT_L11) +//X(INIT_L20) +X(INIT_L30) +X(C_I1) +X(C_I2) +X(C_I3) +X(C_I4) +X(C_FUNCTION) +X(C_COMP) +X(C_COMP_I) +X(C_HORIZ) +X(C_SELX) +X(C_SELY1) +X(C_SELY2) +X(C_SEL_C) +X(C_SEL_P) +X(C_Y12) +X(C_CX_I) +X(C_CY1_I) +X(C_CY2_I) +X(C_PX_I) +X(C_PY1_I) +X(C_PY2_I) +X(C_C_P) +X(C_2D_IN) +X(C_SN) +X(C_O) +X(C_O1) +X(C_O2) +X(C_BR) +X(C_CPE_CLK) +X(C_CPE_EN) +X(C_CPE_RES) +X(C_CPE_SET) +X(C_RAM_I1) +X(C_RAM_I2) +X(C_RAM_O1) +X(C_RAM_O2) +X(C_L_D) +X(C_EN_SR) +X(C_CLKSEL) +X(C_ENSEL) +X(FF_INIT) +// Timing model for CPE +X(CPE_DFF) + +// GPIO configuration parameters +X(OPEN_DRAIN) +//X(SLEW) +//X(DRIVE) +X(INPUT_ENABLE) +//X(PULLDOWN) +//X(PULLUP) +//X(SCHMITT_TRIGGER) +X(OUT_SIGNAL) +X(OUT1_4) +X(OUT2_3) +X(OUT23_14_SEL) +X(USE_CFG_BIT) +X(USE_DDR) +X(SEL_IN_CLOCK) +X(SEL_OUT_CLOCK) +X(OE_ENABLE) +X(OE_SIGNAL) +X(OUT1_FF) +X(OUT2_FF) +X(IN1_FF) +X(IN2_FF) +X(OUT_CLOCK) +X(INV_OUT1_CLOCK) +X(INV_OUT2_CLOCK) +X(IN_CLOCK) +X(INV_IN1_CLOCK) +X(INV_IN2_CLOCK) +//X(DELAY_OBF) +//X(DELAY_IBF) +X(LVDS_EN) +//X(LVDS_BOOST) +X(LVDS_IE) +//X(LVDS_RTERM) + +// GPIO virtual pin +X(DI) + +X(CPE_HALF) +X(RAM_I1) +X(RAM_I2) +X(RAM_O1) +X(RAM_O2) +X(C_RAM_I) +X(C_RAM_O) + +X(RAM_cfg_forward_a_addr) +X(RAM_cfg_forward_b_addr) +X(RAM_cfg_forward_a0_clk) +X(RAM_cfg_forward_a0_en) +X(RAM_cfg_forward_a0_we) +X(RAM_cfg_forward_a1_clk) +X(RAM_cfg_forward_a1_en) +X(RAM_cfg_forward_a1_we) +X(RAM_cfg_forward_b0_clk) +X(RAM_cfg_forward_b0_en) +X(RAM_cfg_forward_b0_we) +X(RAM_cfg_forward_b1_clk) +X(RAM_cfg_forward_b1_en) +X(RAM_cfg_forward_b1_we) +X(RAM_cfg_sram_mode) +X(RAM_cfg_input_config_a0) +X(RAM_cfg_input_config_a1) +X(RAM_cfg_input_config_b0) +X(RAM_cfg_input_config_b1) +X(RAM_cfg_output_config_a0) +X(RAM_cfg_output_config_a1) +X(RAM_cfg_output_config_b0) +X(RAM_cfg_output_config_b1) +X(RAM_cfg_a0_writemode) +X(RAM_cfg_a1_writemode) +X(RAM_cfg_b0_writemode) +X(RAM_cfg_b1_writemode) +X(RAM_cfg_a0_set_outputreg) +X(RAM_cfg_a1_set_outputreg) +X(RAM_cfg_b0_set_outputreg) +X(RAM_cfg_b1_set_outputreg) +X(RAM_cfg_inversion_a0) +X(RAM_cfg_inversion_a1) +X(RAM_cfg_inversion_b0) +X(RAM_cfg_inversion_b1) +X(RAM_cfg_ecc_enable) +X(RAM_cfg_dyn_stat_select) +X(RAM_cfg_fifo_sync_enable) +X(RAM_cfg_almost_empty_offset) +X(RAM_cfg_fifo_async_enable) +X(RAM_cfg_almost_full_offset) +X(RAM_cfg_sram_delay) +X(RAM_cfg_datbm_sel) +X(RAM_cfg_cascade_enable) + +X(CC_ADDF2) +X(A2) +X(B2) diff --git a/himbaechel/uarch/gatemate/extra_data.h b/himbaechel/uarch/gatemate/extra_data.h new file mode 100644 index 00000000..8ecbd243 --- /dev/null +++ b/himbaechel/uarch/gatemate/extra_data.h @@ -0,0 +1,97 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 GATEMATE_EXTRA_DATA_H +#define GATEMATE_EXTRA_DATA_H + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +NPNR_PACKED_STRUCT(struct GateMateTileExtraDataPOD { + uint8_t die; + uint8_t bit_x; + uint8_t bit_y; + uint8_t prim_id; +}); + +NPNR_PACKED_STRUCT(struct GateMatePipExtraDataPOD { + int32_t name; + uint8_t bits; + uint8_t value; + uint8_t flags; + uint8_t type; +}); + +NPNR_PACKED_STRUCT(struct GateMateBelPinConstraintPOD { + int32_t name; + int16_t constr_x; + int16_t constr_y; + int16_t constr_z; + int16_t dummy; +}); + +NPNR_PACKED_STRUCT(struct GateMateBelExtraDataPOD { RelSlice constraints; }); + +enum MuxFlags +{ + MUX_INVERT = 1, + MUX_VISIBLE = 2, + MUX_CONFIG = 4, +}; + +enum PipExtra +{ + PIP_EXTRA_MUX = 1, +}; + +enum CPEFunction +{ + C_ADDF = 1, + C_ADDF2 = 2, + C_MULT = 3, + C_MX4 = 4, + C_EN_CIN = 5, + C_CONCAT = 6, + C_ADDCIN = 7, +}; + +enum ClusterPlacement +{ + PLACE_DB_CONSTR = 32, +}; + +struct PllCfgRecord +{ + double weight; + double f_core; + double f_dco; + double f_core_delta; + double core_weight; + int32_t K; + int32_t N1; + int32_t N2; + int32_t M1; + int32_t M2; + int32_t PDIV1; +}; + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/himbaechel/uarch/gatemate/gatemate.cc b/himbaechel/uarch/gatemate/gatemate.cc new file mode 100644 index 00000000..da90bf38 --- /dev/null +++ b/himbaechel/uarch/gatemate/gatemate.cc @@ -0,0 +1,419 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 "gatemate.h" +#include "design_utils.h" +#include "placer_heap.h" + +#define GEN_INIT_CONSTIDS +#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" + +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +GateMateImpl::~GateMateImpl() {}; + +void GateMateImpl::init_database(Arch *arch) +{ + const ArchArgs &args = arch->args; + init_uarch_constids(arch); + arch->load_chipdb(stringf("gatemate/chipdb-%s.bin", args.device.c_str())); + arch->set_package("FBGA324"); + arch->set_speed_grade("DEFAULT"); +} + +void GateMateImpl::init(Context *ctx) +{ + HimbaechelAPI::init(ctx); + for (const auto &pad : ctx->package_info->pads) { + available_pads.emplace(IdString(pad.package_pin)); + BelId bel = ctx->getBelByName(IdStringList::concat(IdString(pad.tile), IdString(pad.bel))); + bel_to_pad.emplace(bel, &pad); + } + for (auto bel : ctx->getBels()) { + auto *ptr = bel_extra_data(bel); + std::map pins; + for (const auto &p : ptr->constraints) + pins.emplace(IdString(p.name), &p); + pin_to_constr.emplace(bel, pins); + } +} + +delay_t GateMateImpl::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); + + return 100 * (std::abs(dx - sx) / 4 + std::abs(dy - sy) / 4 + 2); +} + +bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const +{ + CellInfo *cell = ctx->getBoundBelCell(bel); + if (cell == nullptr) { + return true; + } + if (ctx->getBelType(bel).in(id_CPE_HALF, id_CPE_HALF_L, id_CPE_HALF_U)) { + Loc loc = ctx->getBelLocation(bel); + const CellInfo *adj_half = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, loc.z == 1 ? 0 : 1))); + if (adj_half) { + const auto &half_data = fast_cell_info.at(cell->flat_index); + if (half_data.dff_used) { + const auto &adj_data = fast_cell_info.at(adj_half->flat_index); + if (adj_data.dff_used) { + if (adj_data.ff_config != half_data.ff_config) + return false; + if (adj_data.ff_en != half_data.ff_en) + return false; + if (adj_data.ff_clk != half_data.ff_clk) + return false; + if (adj_data.ff_sr != half_data.ff_sr) + return false; + } + } + } + return true; + } + return true; +} + +Loc GateMateImpl::getRelativeConstraint(Loc &root_loc, IdString id) const +{ + Loc child_loc; + BelId root_bel = ctx->getBelByLocation(root_loc); + if (pin_to_constr.count(root_bel)) { + auto &constr = pin_to_constr.at(root_bel); + if (constr.count(id)) { + auto &p = constr.at(id); + child_loc.x = root_loc.x + p->constr_x; + child_loc.y = root_loc.y + p->constr_y; + child_loc.z = p->constr_z; + } else { + log_error("Constrain info not available for pin.\n"); + } + } else { + log_error("Bel info not available for constraints.\n"); + } + return child_loc; +} + +bool GateMateImpl::getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc, + std::vector> &placement) const +{ + for (auto child : cluster->constr_children) { + Loc child_loc; + if (child->constr_z >= PLACE_DB_CONSTR) { + child_loc = getRelativeConstraint(root_loc, IdString(child->constr_z - PLACE_DB_CONSTR)); + } else { + 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 GateMateImpl::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); +} + +bool GateMateImpl::need_inversion(CellInfo *cell, IdString port) +{ + PortRef sink; + sink.cell = cell; + sink.port = port; + + NetInfo *net_info = cell->getPort(port); + if (!net_info) + return false; + + WireId src_wire = ctx->getNetinfoSourceWire(net_info); + WireId dst_wire = ctx->getNetinfoSinkWire(net_info, sink, 0); + + if (src_wire == WireId()) + return false; + + WireId cursor = dst_wire; + bool invert = false; + while (cursor != WireId() && cursor != src_wire) { + auto it = net_info->wires.find(cursor); + + if (it == net_info->wires.end()) + break; + + PipId pip = it->second.pip; + if (pip == PipId()) + break; + + invert ^= ctx->isPipInverting(pip); + cursor = ctx->getPipSrcWire(pip); + } + + return invert; +} + +void GateMateImpl::update_cpe_lt(CellInfo *cell, IdString port, IdString init) +{ + unsigned init_val = int_or_default(cell->params, init); + bool invert = need_inversion(cell, port); + if (invert) { + if (port.in(id_IN1, id_IN3)) + init_val = (init_val & 0b1010) >> 1 | (init_val & 0b0101) << 1; + else + init_val = (init_val & 0b0011) << 2 | (init_val & 0b1100) >> 2; + cell->params[init] = Property(init_val, 4); + } +} + +void GateMateImpl::update_cpe_inv(CellInfo *cell, IdString port, IdString param) +{ + unsigned init_val = int_or_default(cell->params, param); + bool invert = need_inversion(cell, port); + if (invert) { + cell->params[param] = Property(3 - init_val, 2); + } +} + +void GateMateImpl::update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit) +{ + // Mux inversion data is contained in other CPE half + Loc l = ctx->getBelLocation(cell->bel); + CellInfo *cell_l = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(l.x, l.y, 1))); + unsigned init_val = int_or_default(cell_l->params, param); + bool invert = need_inversion(cell, port); + if (invert) { + int old = (init_val >> bit) & 1; + int val = (init_val & (~(1 << bit) & 0xf)) | ((!old) << bit); + cell_l->params[param] = Property(val, 4); + } +} + +void GateMateImpl::rename_param(CellInfo *cell, IdString name, IdString new_name, int width) +{ + if (cell->params.count(name)) { + cell->params[new_name] = Property(int_or_default(cell->params, name, 0), width); + cell->unsetParam(name); + } +} + +void GateMateImpl::prePlace() { assign_cell_info(); } + +void GateMateImpl::postPlace() +{ + ctx->assignArchInfo(); + for (auto &cell : ctx->cells) { + if (cell.second->type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) { + Loc l = ctx->getBelLocation(cell.second->bel); + if (l.z == 0) { // CPE_HALF_U + if (cell.second->params.count(id_C_O) && int_or_default(cell.second->params, id_C_O, 0) == 0) + cell.second->params[id_C_2D_IN] = Property(1, 1); + rename_param(cell.second.get(), id_C_O, id_C_O2, 2); + rename_param(cell.second.get(), id_C_RAM_I, id_C_RAM_I2, 1); + rename_param(cell.second.get(), id_C_RAM_O, id_C_RAM_O2, 1); + cell.second->type = id_CPE_HALF_U; + } else { // CPE_HALF_L + if (!cell.second->params.count(id_INIT_L20)) + cell.second->params[id_INIT_L20] = Property(0b1100, 4); + rename_param(cell.second.get(), id_C_O, id_C_O1, 2); + rename_param(cell.second.get(), id_INIT_L00, id_INIT_L02, 4); + rename_param(cell.second.get(), id_INIT_L01, id_INIT_L03, 4); + rename_param(cell.second.get(), id_INIT_L10, id_INIT_L11, 4); + rename_param(cell.second.get(), id_C_RAM_I, id_C_RAM_I1, 1); + rename_param(cell.second.get(), id_C_RAM_O, id_C_RAM_O1, 1); + cell.second->type = id_CPE_HALF_L; + } + } + } +} + +void GateMateImpl::postRoute() +{ + ctx->assignArchInfo(); + // Update configuration bits based on signal inversion + for (auto &cell : ctx->cells) { + if (cell.second->type.in(id_CPE_HALF_U)) { + uint8_t func = int_or_default(cell.second->params, id_C_FUNCTION, 0); + if (func != C_MX4) { + update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00); + update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L00); + update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01); + update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L01); + } else { + update_cpe_mux(cell.second.get(), id_IN1, id_INIT_L11, 0); + update_cpe_mux(cell.second.get(), id_IN2, id_INIT_L11, 1); + update_cpe_mux(cell.second.get(), id_IN3, id_INIT_L11, 2); + update_cpe_mux(cell.second.get(), id_IN4, id_INIT_L11, 3); + } + } + if (cell.second->type.in(id_CPE_HALF_L)) { + update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L02); + update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L02); + update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L03); + update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L03); + } + if (cell.second->type.in(id_CPE_HALF_U, id_CPE_HALF_L)) { + update_cpe_inv(cell.second.get(), id_CLK, id_C_CPE_CLK); + update_cpe_inv(cell.second.get(), id_EN, id_C_CPE_EN); + bool set = int_or_default(cell.second->params, id_C_EN_SR, 0) == 1; + if (set) + update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_SET); + else + update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_RES); + } + } + // Sanity check + for (auto &c : ctx->cells) { + CellInfo *cell = c.second.get(); + if (!cell->type.in(id_CPE_HALF_U, id_CPE_HALF_L)) { + for (auto port : cell->ports) { + if (need_inversion(cell, port.first)) { + log_error("Unhandled cell '%s' of type '%s' port '%s'\n", cell->name.c_str(ctx), + cell->type.c_str(ctx), port.first.c_str(ctx)); + } + } + } + } + print_utilisation(ctx); + + const ArchArgs &args = ctx->args; + if (args.options.count("out")) { + write_bitstream(args.device, args.options.at("out")); + } +} + +void GateMateImpl::configurePlacerHeap(PlacerHeapCfg &cfg) +{ + cfg.beta = 0.5; + cfg.placeAllAtOnce = true; +} + +void GateMateImpl::assign_cell_info() +{ + fast_cell_info.resize(ctx->cells.size()); + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + auto &fc = fast_cell_info.at(ci->flat_index); + if (ci->type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) { + fc.signal_used = int_or_default(ci->params, id_C_O, -1); + fc.ff_en = ci->getPort(id_EN); + fc.ff_clk = ci->getPort(id_CLK); + fc.ff_sr = ci->getPort(id_SR); + fc.ff_config = 0; + if (fc.signal_used == 0) { + fc.ff_config |= int_or_default(ci->params, id_C_CPE_EN, 0); + fc.ff_config <<= 2; + fc.ff_config |= int_or_default(ci->params, id_C_CPE_CLK, 0); + fc.ff_config <<= 2; + fc.ff_config |= int_or_default(ci->params, id_C_CPE_RES, 0); + fc.ff_config <<= 2; + fc.ff_config |= int_or_default(ci->params, id_C_CPE_SET, 0); + fc.ff_config <<= 2; + fc.ff_config |= int_or_default(ci->params, id_C_EN_SR, 0); + fc.ff_config <<= 1; + fc.ff_config |= int_or_default(ci->params, id_C_L_D, 0); + fc.ff_config <<= 1; + fc.ff_config |= int_or_default(ci->params, id_FF_INIT, 0); + fc.dff_used = true; + } + } + } +} + +// Bel bucket functions +IdString GateMateImpl::getBelBucketForCellType(IdString cell_type) const +{ + if (cell_type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_TOBUF, + id_CC_LVDS_OBUF, id_CC_LVDS_IOBUF)) + return id_GPIO; + else if (cell_type.in(id_CPE_HALF_U, id_CPE_HALF_L, id_CPE_HALF)) + return id_CPE_HALF; + else + return cell_type; +} + +BelBucketId GateMateImpl::getBelBucketForBel(BelId bel) const +{ + IdString bel_type = ctx->getBelType(bel); + if (bel_type.in(id_CPE_HALF_U, id_CPE_HALF_L)) + return id_CPE_HALF; + return bel_type; +} + +bool GateMateImpl::isValidBelForCellType(IdString cell_type, BelId bel) const +{ + IdString bel_type = ctx->getBelType(bel); + if (bel_type == id_GPIO) + return cell_type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_TOBUF, + id_CC_LVDS_OBUF, id_CC_LVDS_IOBUF); + else if (bel_type == id_CPE_HALF_U) + return cell_type.in(id_CPE_HALF_U, id_CPE_HALF); + else if (bel_type == id_CPE_HALF_L) + return cell_type.in(id_CPE_HALF_L, id_CPE_HALF); + else + return (bel_type == cell_type); +} + +bool GateMateImpl::isPipInverting(PipId pip) const +{ + const auto &extra_data = + *reinterpret_cast(chip_pip_info(ctx->chip_info, pip).extra_data.get()); + return extra_data.type == PipExtra::PIP_EXTRA_MUX && (extra_data.flags & MUX_INVERT); +} + +const GateMateBelExtraDataPOD *GateMateImpl::bel_extra_data(BelId bel) const +{ + return reinterpret_cast(chip_bel_info(ctx->chip_info, bel).extra_data.get()); +} + +struct GateMateArch : HimbaechelArch +{ + GateMateArch() : HimbaechelArch("gatemate") {}; + bool match_device(const std::string &device) override + { + return device.size() > 6 && device.substr(0, 6) == "CCGM1A"; + } + std::unique_ptr create(const std::string &device, const dict &args) + { + return std::make_unique(); + } +} gateMateArch; + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h new file mode 100644 index 00000000..20a5ec16 --- /dev/null +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -0,0 +1,97 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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_GATEMATE_H +#define HIMBAECHEL_GATEMATE_H + +#include "extra_data.h" +#include "himbaechel_api.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +#include "himbaechel_helpers.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct GateMateImpl : HimbaechelAPI +{ + ~GateMateImpl(); + void init_database(Arch *arch) override; + + void init(Context *ctx) override; + + void pack() override; + + void prePlace() override; + void postPlace() override; + void postRoute() override; + + bool isBelLocationValid(BelId bel, bool explain_invalid = false) const override; + delay_t estimateDelay(WireId src, WireId dst) const override; + + void drawBel(std::vector &g, GraphicElement::style_t style, IdString bel_type, Loc loc) override; + + bool getClusterPlacement(ClusterId cluster, BelId root_bel, + std::vector> &placement) const override; + + IdString getBelBucketForCellType(IdString cell_type) const override; + bool isValidBelForCellType(IdString cell_type, BelId bel) const override; + BelBucketId getBelBucketForBel(BelId bel) const override; + + Loc getRelativeConstraint(Loc &root_loc, IdString id) const; + + void configurePlacerHeap(PlacerHeapCfg &cfg) override; + + bool isPipInverting(PipId pip) const override; + + std::set available_pads; + std::map bel_to_pad; + + private: + bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc, + std::vector> &placement) const; + + void write_bitstream(const std::string &device, const std::string &filename); + + void parse_ccf(const std::string &filename); + + void assign_cell_info(); + bool need_inversion(CellInfo *cell, IdString port); + void update_cpe_lt(CellInfo *cell, IdString port, IdString init); + void update_cpe_inv(CellInfo *cell, IdString port, IdString param); + void update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit); + void rename_param(CellInfo *cell, IdString name, IdString new_name, int width); + + const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const; + + struct GateMateCellInfo + { + // slice info + const NetInfo *ff_en = nullptr, *ff_clk = nullptr, *ff_sr = nullptr; + int ff_config = 0; + int signal_used = -1; + bool dff_used = false; + }; + std::vector fast_cell_info; + std::map> pin_to_constr; +}; + +NEXTPNR_NAMESPACE_END +#endif diff --git a/himbaechel/uarch/gatemate/gatemate_util.h b/himbaechel/uarch/gatemate/gatemate_util.h new file mode 100644 index 00000000..a872a1c6 --- /dev/null +++ b/himbaechel/uarch/gatemate/gatemate_util.h @@ -0,0 +1,84 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 GATEMATE_UTIL_H +#define GATEMATE_UTIL_H + +#include +#include +#include +#include "nextpnr.h" + +#include "log.h" + +NEXTPNR_NAMESPACE_BEGIN + +template +double double_or_default(const dict &ct, const KeyType &key, double def = 0) +{ + auto found = ct.find(key); + if (found == ct.end()) + return def; + else { + if (found->second.is_string) { + try { + return std::stod(found->second.as_string()); + } catch (std::invalid_argument &e) { + log_error("Expecting numeric value but got '%s'.\n", found->second.as_string().c_str()); + } + } else + return double(found->second.as_int64()); + } +}; + +template +int extract_bits(const dict &ct, const KeyType &key, int start, int bits, int def = 0) +{ + Property extr = get_or_default(ct, key, Property()).extract(start, bits); + + if (extr.is_string) { + try { + return std::stoi(extr.as_string()); + } catch (std::invalid_argument &e) { + log_error("Expecting numeric value but got '%s'.\n", extr.as_string().c_str()); + } + } else + return extr.as_int64(); +} + +template +std::vector> splitNestedVector(const std::vector> &input, size_t maxSize = 8) +{ + std::vector> result; + + for (const auto &inner : input) { + size_t i = 0; + while (i < inner.size()) { + size_t end = std::min(i + maxSize, inner.size()); + result.emplace_back(inner.begin() + i, inner.begin() + end); + i = end; + } + } + + return result; +} + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/himbaechel/uarch/gatemate/gen/arch_gen.py b/himbaechel/uarch/gatemate/gen/arch_gen.py new file mode 100644 index 00000000..55c98443 --- /dev/null +++ b/himbaechel/uarch/gatemate/gen/arch_gen.py @@ -0,0 +1,200 @@ +# +# nextpnr -- Next Generation Place and Route +# +# Copyright (C) 2024 The Project Peppercorn 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 os +from os import path +import sys +import argparse + +sys.path.append(path.join(path.dirname(__file__), "../../..")) +from himbaechel_dbgen.chip import * + +PIP_EXTRA_MUX = 1 + +MUX_INVERT = 1 +MUX_VISIBLE = 2 +MUX_CONFIG = 4 + +parser = argparse.ArgumentParser() +parser.add_argument("--lib", help="Project Peppercorn python database script path", type=str, required=True) +parser.add_argument("--device", help="name of device to export", type=str, required=True) +parser.add_argument("--bba", help="bba file to write", type=str, required=True) +args = parser.parse_args() + +sys.path.append(os.path.expanduser(args.lib)) +sys.path += args.lib +import chip +import die + +@dataclass +class TileExtraData(BBAStruct): + die : int = 0 + bit_x: int = 0 + bit_y: int = 0 + prim_id : int = 0 + + def serialise_lists(self, context: str, bba: BBAWriter): + pass + def serialise(self, context: str, bba: BBAWriter): + bba.u8(self.die) + bba.u8(self.bit_x) + bba.u8(self.bit_y) + bba.u8(self.prim_id) + +@dataclass +class PipExtraData(BBAStruct): + pip_type: int + name: IdString + bits: int = 0 + value: int = 0 + invert: 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.bits) + bba.u8(self.value) + bba.u8(self.invert) + bba.u8(self.pip_type) + +@dataclass +class BelPinConstraint(BBAStruct): + index: int + pin_name: IdString + constr_x: int = 0 + constr_y: int = 0 + constr_z: int = 0 + + def serialise_lists(self, context: str, bba: BBAWriter): + pass + def serialise(self, context: str, bba: BBAWriter): + bba.u32(self.pin_name.index) + bba.u16(self.constr_x) + bba.u16(self.constr_y) + bba.u16(self.constr_z) + bba.u16(0) + +@dataclass +class BelExtraData(BBAStruct): + constraints: list[BelPinConstraint] = field(default_factory = list) + + def add_constraints(self, pin: IdString, x: int, y: int, z:int): + item = BelPinConstraint(len(self.constraints),pin,x,y,z) + self.constraints.append(item) + + def serialise_lists(self, context: str, bba: BBAWriter): + self.constraints.sort(key=lambda p: p.pin_name.index) + bba.label(f"{context}_constraints") + for i, t in enumerate(self.constraints): + t.serialise(f"{context}_constraint{i}", bba) + pass + def serialise(self, context: str, bba: BBAWriter): + bba.slice(f"{context}_constraints", len(self.constraints)) + +def set_timings(ch): + speed = "DEFAULT" + tmg = ch.set_speed_grades([speed]) + + lut = ch.timing.add_cell_variant(speed, "CPE_HALF_L") + lut.add_comb_arc("IN1", "OUT", TimingValue(416, 418)) # IN5 to OUT1 + lut.add_comb_arc("IN2", "OUT", TimingValue(413, 422)) # IN6 to OUT1 + lut.add_comb_arc("IN3", "OUT", TimingValue(372, 374)) # IN7 to OUT1 + lut.add_comb_arc("IN4", "OUT", TimingValue(275, 385)) # IN8 to OUT1 + + lut = ch.timing.add_cell_variant(speed, "CPE_HALF_U") + lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2 + lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2 + lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2 + lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2 + + lut = ch.timing.add_cell_variant(speed, "CPE_HALF") + lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2 + lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2 + lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2 + lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2 + + dff = ch.timing.add_cell_variant(speed, "CPE_DFF") + dff.add_setup_hold("CLK", "IN1", ClockEdge.RISING, TimingValue(60), TimingValue(50)) + dff.add_setup_hold("CLK", "IN2", ClockEdge.RISING, TimingValue(60), TimingValue(50)) + dff.add_setup_hold("CLK", "IN3", ClockEdge.RISING, TimingValue(60), TimingValue(50)) + dff.add_setup_hold("CLK", "IN4", ClockEdge.RISING, TimingValue(60), TimingValue(50)) + dff.add_clock_out("CLK", "OUT", ClockEdge.RISING, TimingValue(60)) + +def main(): + # Range needs to be +1, but we are adding +2 more to coordinates, since + # they are starting from -2 instead of zero required for nextpnr + dev = chip.get_device(args.device) + ch = Chip("gatemate", args.device, dev.max_col() + 3, dev.max_row() + 3) + # Init constant ids + ch.strs.read_constids(path.join(path.dirname(__file__), "..", "constids.inc")) + ch.read_gfxids(path.join(path.dirname(__file__), "..", "gfxids.inc")) + + for type_name in sorted(die.get_tile_type_list()): + tt = ch.create_tile_type(type_name) + for group in sorted(die.get_groups_for_type(type_name)): + tt.create_group(group.name, group.type) + for wire in sorted(die.get_endpoints_for_type(type_name)): + tt.create_wire(wire.name, wire.type) + if "GPIO" in type_name: + tt.create_wire("GPIO.DI", "CPE_VIRTUAL_WIRE") + for prim in sorted(die.get_primitives_for_type(type_name)): + bel = tt.create_bel(prim.name, prim.type, prim.z) + extra = BelExtraData() + for constr in sorted(die.get_pins_constraint(type_name, prim.name, prim.type)): + extra.add_constraints(ch.strs.id(constr.name),constr.rel_x,constr.rel_y,0 if constr.pin_num==2 else 1) + bel.extra_data = extra + if prim.name == "GPIO": + tt.add_bel_pin(bel, "DI", "GPIO.DI", PinType.INPUT) + for pin in sorted(die.get_primitive_pins(prim.type)): + tt.add_bel_pin(bel, pin.name, die.get_pin_connection_name(prim,pin), pin.dir) + for mux in sorted(die.get_mux_connections_for_type(type_name)): + pp = tt.create_pip(mux.src, mux.dst) + if mux.name: + mux_flags = MUX_INVERT if mux.invert else 0 + mux_flags |= MUX_VISIBLE if mux.visible else 0 + mux_flags |= MUX_CONFIG if mux.config else 0 + pp.extra_data = PipExtraData(PIP_EXTRA_MUX, ch.strs.id(mux.name), mux.bits, mux.value, mux_flags) + if "GPIO" in type_name: + tt.create_pip("GPIO.DI", "GPIO.IN1") + tt.create_pip("GPIO.DI", "GPIO.IN2") + + # Setup tile grid + for x in range(dev.max_col() + 3): + for y in range(dev.max_row() + 3): + ti = ch.set_tile_type(x, y, dev.get_tile_type(x - 2,y - 2)) + tileinfo = dev.get_tile_info(x - 2,y - 2) + ti.extra_data = TileExtraData(tileinfo.die, tileinfo.bit_x, tileinfo.bit_y, tileinfo.prim_index) + + # Create nodes between tiles + for _,nodes in dev.get_connections(): + node = [] + for conn in sorted(nodes): + node.append(NodeWire(conn.x + 2, conn.y + 2, conn.name)) + ch.add_node(node) + set_timings(ch) + + for package in dev.get_packages(): + pkg = ch.create_package(package) + for pad in sorted(dev.get_package_pads(package)): + pkg.create_pad(pad.name, f"X{pad.x+2}Y{pad.y+2}", pad.bel, pad.function, pad.bank, pad.flags) + + ch.write_bba(args.bba) + +if __name__ == '__main__': + main() diff --git a/himbaechel/uarch/gatemate/gfx.cc b/himbaechel/uarch/gatemate/gfx.cc new file mode 100644 index 00000000..334b0650 --- /dev/null +++ b/himbaechel/uarch/gatemate/gfx.cc @@ -0,0 +1,89 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 "gatemate.h" + +#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" +#define HIMBAECHEL_GFXIDS "uarch/gatemate/gfxids.inc" +#define HIMBAECHEL_UARCH gatemate + +#include "himbaechel_constids.h" +#include "himbaechel_gfxids.h" + +NEXTPNR_NAMESPACE_BEGIN + +void GateMateImpl::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_CPE_HALF_L.index: + el.x1 = loc.x + 0.20; + el.x2 = el.x1 + 0.20; + el.y1 = loc.y + 0.25; + el.y2 = el.y1 + 0.20; + g.push_back(el); + break; + case id_CPE_HALF_U.index: + el.x1 = loc.x + 0.20; + el.x2 = el.x1 + 0.20; + el.y1 = loc.y + 0.55; + el.y2 = el.y1 + 0.20; + g.push_back(el); + break; + case id_RAM.index: + el.x1 = loc.x + 0.70; + el.x2 = el.x1 + 0.20; + el.y1 = loc.y + 0.20; + el.y2 = el.y1 + 15.60; + g.push_back(el); + break; + case id_GPIO.index: + el.x1 = loc.x + 0.20; + el.x2 = el.x1 + 0.60; + el.y1 = loc.y + 0.20; + el.y2 = el.y1 + 0.60; + g.push_back(el); + break; + case id_BUFG.index: + el.x1 = loc.x + 0.15 + loc.z * 0.20; + el.x2 = el.x1 + 0.15; + el.y1 = loc.y + 0.10; + el.y2 = el.y1 + 0.30; + g.push_back(el); + break; + case id_PLL.index: + el.x1 = loc.x + 0.15 + (loc.z - 4) * 0.20; + el.x2 = el.x1 + 0.15; + el.y1 = loc.y + 0.60; + el.y2 = el.y1 + 0.30; + g.push_back(el); + break; + case id_USR_RSTN.index: + el.x1 = loc.x + 0.20; + el.x2 = el.x1 + 0.20; + el.y1 = loc.y + 0.20; + el.y2 = el.y1 + 0.20; + g.push_back(el); + break; + } +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/gfxids.inc b/himbaechel/uarch/gatemate/gfxids.inc new file mode 100644 index 00000000..e69de29b diff --git a/himbaechel/uarch/gatemate/pack.cc b/himbaechel/uarch/gatemate/pack.cc new file mode 100644 index 00000000..26534121 --- /dev/null +++ b/himbaechel/uarch/gatemate/pack.cc @@ -0,0 +1,2188 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 "design_utils.h" +#include "gatemate_util.h" +#include "pack.h" + +#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +// Return true if a cell is a flipflop +inline bool is_dff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type.in(id_CC_DFF, id_CC_DLT); } + +void GateMatePacker::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 GateMatePacker::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); + } +} + +BelId GateMatePacker::get_bank_cpe(int bank) +{ + switch (bank) { + case 0: + return ctx->getBelByLocation(Loc(97 + 2, 128 + 2, 1)); // N1, RAM_O1 + case 1: + return ctx->getBelByLocation(Loc(97 + 2, 128 + 2, 0)); // N2, RAM_O2 + case 2: + return ctx->getBelByLocation(Loc(160 + 2, 65 + 2, 1)); // E1, RAM_O1 + case 3: + return ctx->getBelByLocation(Loc(160 + 2, 65 + 2, 0)); // E2, RAM_O2 + case 4: + return ctx->getBelByLocation(Loc(1 + 2, 65 + 2, 1)); // W1, RAM_O1 + case 5: + return ctx->getBelByLocation(Loc(1 + 2, 65 + 2, 0)); // W2, RAM_O2 + case 6: + return ctx->getBelByLocation(Loc(97 + 2, 1 + 2, 1)); // S1, RAM_O1 + case 7: + return ctx->getBelByLocation(Loc(97 + 2, 1 + 2, 0)); // S2, RAM_O2 + case 8: + return ctx->getBelByLocation(Loc(49 + 2, 1 + 2, 1)); // S3, RAM_O1 + default: + log_error("Unkown bank\n"); + } +} + +void GateMatePacker::pack_io() +{ + // 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) { + IdString key = params.first; + if (key == id_LOC && + top_port.cell->type.in(id_CC_LVDS_IBUF, id_CC_LVDS_OBUF, id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF)) { + if (top_port.port.in(id_I_P, id_O_P, id_IO_P)) + key = id_PIN_NAME_P; + if (top_port.port.in(id_I_N, id_O_N, id_IO_N)) + key = id_PIN_NAME_N; + } + if (top_port.cell->params.count(key)) { + if (top_port.cell->params[key] != params.second) { + std::string val = params.second.is_string ? params.second.as_string() + : std::to_string(params.second.as_int64()); + log_warning("Overriding parameter '%s' with value '%s' for cell '%s'.\n", key.c_str(ctx), + val.c_str(), ctx->nameOf(top_port.cell)); + } + } + top_port.cell->params[key] = 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); + } + + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_OBUF, + id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF)) + continue; + + bool is_lvds = ci.type.in(id_CC_LVDS_IBUF, id_CC_LVDS_OBUF, id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF); + + std::string loc = str_or_default(ci.params, is_lvds ? id_PIN_NAME_P : id_PIN_NAME, "UNPLACED"); + if (ci.params.count(id_LOC)) { + std::string new_loc = str_or_default(ci.params, id_LOC, "UNPLACED"); + if (loc != "UNPLACED" && loc != new_loc) + log_warning("Overriding location of cell '%s' from '%s' with '%s'\n", ctx->nameOf(&ci), loc.c_str(), + new_loc.c_str()); + loc = new_loc; + } + + if (loc == "UNPLACED") { + const ArchArgs &args = ctx->args; + if (args.options.count("allow-unconstrained")) + log_warning("IO '%s' is unconstrained in CCF and will be automatically placed.\n", ctx->nameOf(&ci)); + else + log_error("IO '%s' is unconstrained in CCF (override this error with " + "--vopt allow-unconstrained).\n", + ctx->nameOf(&ci)); + } + + disconnect_if_gnd(&ci, id_T); + if (ci.type == id_CC_TOBUF && !ci.getPort(id_T)) + ci.type = id_CC_OBUF; + if (ci.type == id_CC_LVDS_TOBUF && !ci.getPort(id_T)) + ci.type = id_CC_LVDS_OBUF; + + std::vector keys; + for (auto &p : ci.params) { + + if (p.first.in(id_PIN_NAME, id_PIN_NAME_P, id_PIN_NAME_N)) { + if (ctx->get_package_pin_bel(ctx->id(p.second.as_string())) == BelId()) + log_error("Unknown %s '%s' for cell '%s'.\n", p.first.c_str(ctx), p.second.as_string().c_str(), + ci.name.c_str(ctx)); + keys.push_back(p.first); + continue; + } + if (p.first.in(id_V_IO, id_LOC)) { + keys.push_back(p.first); + continue; + } + if (ci.type.in(id_CC_IBUF, id_CC_IOBUF) && + p.first.in(id_PULLUP, id_PULLDOWN, id_KEEPER, id_SCHMITT_TRIGGER, id_DELAY_IBF, id_FF_IBF)) + continue; + if (ci.type.in(id_CC_TOBUF) && p.first.in(id_PULLUP, id_PULLDOWN, id_KEEPER)) + continue; + if (ci.type.in(id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF) && + p.first.in(id_DRIVE, id_SLEW, id_DELAY_OBF, id_FF_OBF)) + continue; + if (ci.type.in(id_CC_LVDS_IBUF, id_CC_LVDS_IOBUF) && p.first.in(id_LVDS_RTERM, id_DELAY_IBF, id_FF_IBF)) + continue; + if (ci.type.in(id_CC_LVDS_OBUF, id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF) && + p.first.in(id_LVDS_BOOST, id_DELAY_OBF, id_FF_OBF)) + continue; + log_warning("Removing unsupported parameter '%s' for type '%s'.\n", p.first.c_str(ctx), ci.type.c_str(ctx)); + keys.push_back(p.first); + } + if (ci.params.count(id_SLEW)) { + std::string val = str_or_default(ci.params, id_SLEW, "UNDEFINED"); + if (val == "UNDEFINED") + keys.push_back(id_SLEW); + else if (val == "FAST") + ci.params[id_SLEW] = Property(Property::State::S1); + else if (val == "SLOW") + ci.params[id_SLEW] = Property(Property::State::S0); + else + log_error("Unknown value '%s' for SLEW parameter of '%s' cell.\n", val.c_str(), ci.name.c_str(ctx)); + } + if (is_lvds) { + std::string p_pin = str_or_default(ci.params, id_PIN_NAME_P, "UNPLACED"); + std::string n_pin = str_or_default(ci.params, id_PIN_NAME_N, "UNPLACED"); + if (p_pin == "UNPLACED" || n_pin == "UNPLACED") + log_error("Both LVDS pins must be set to a valid locations.\n"); + if (p_pin.substr(0, 6) != n_pin.substr(0, 6) || p_pin[7] != n_pin[7]) + log_error("LVDS pads '%s' and '%s' do not match.\n", p_pin.c_str(), n_pin.c_str()); + if (p_pin[6] != 'A') + log_error("LVDS positive pad must be from type A.\n"); + if (n_pin[6] != 'B') + log_error("LVDS negative pad must be from type B.\n"); + } + for (auto key : keys) + ci.params.erase(key); + + // For output pins set SLEW to FAST if not defined + if (!ci.params.count(id_SLEW) && ci.type.in(id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF)) + ci.params[id_SLEW] = Property(Property::State::S1); + + if ((ci.params.count(id_KEEPER) + ci.params.count(id_PULLUP) + ci.params.count(id_PULLDOWN)) > 1) + log_error("PULLUP, PULLDOWN and KEEPER are mutually exclusive parameters.\n"); + + if (is_lvds) + ci.params[id_LVDS_EN] = Property(Property::State::S1); + + // DELAY_IBF and DELAY_OBF must be set depending of type + // Also we need to enable input/output + if (ci.type.in(id_CC_IBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_IOBUF)) { + ci.params[id_DELAY_IBF] = Property(1 << int_or_default(ci.params, id_DELAY_IBF, 0), 16); + if (is_lvds) + ci.params[id_LVDS_IE] = Property(Property::State::S1); + else + ci.params[id_INPUT_ENABLE] = Property(Property::State::S1); + } + if (ci.type.in(id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_OBUF, id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF)) { + ci.params[id_DELAY_OBF] = Property(1 << int_or_default(ci.params, id_DELAY_OBF, 0), 16); + ci.params[id_OE_ENABLE] = Property(Property::State::S1); + } + if (ci.params.count(id_DRIVE)) { + int val = int_or_default(ci.params, id_DRIVE, 0); + if (val != 3 && val != 6 && val != 9 && val != 12) + log_error("Unsupported value '%d' for DRIVE parameter of '%s' cell.\n", val, ci.name.c_str(ctx)); + ci.params[id_DRIVE] = Property((val - 3) / 3, 2); + } + for (auto &p : ci.params) { + if (p.first.in(id_PULLUP, id_PULLDOWN, id_KEEPER, id_SCHMITT_TRIGGER, id_FF_OBF, id_FF_IBF, id_LVDS_RTERM, + id_LVDS_BOOST)) { + int val = int_or_default(ci.params, p.first, 0); + if (val != 0 && val != 1) + log_error("Unsupported value '%d' for %s parameter of '%s' cell.\n", val, p.first.c_str(ctx), + ci.name.c_str(ctx)); + ci.params[p.first] = Property(val, 1); + } + } + + // Disconnect PADs + ci.disconnectPort(id_IO); + ci.disconnectPort(id_I); + ci.disconnectPort(id_O); + ci.disconnectPort(id_IO_P); + ci.disconnectPort(id_IO_N); + ci.disconnectPort(id_I_P); + ci.disconnectPort(id_I_N); + ci.disconnectPort(id_O_P); + ci.disconnectPort(id_O_N); + + if (loc.empty() || loc == "UNPLACED") { + if (uarch->available_pads.empty()) + log_error("No more pads available.\n"); + IdString id = *(uarch->available_pads.begin()); + uarch->available_pads.erase(id); + loc = id.c_str(ctx); + } + ci.params[id_LOC] = Property(loc); + + 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))); + } + ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_FIXED); + } +} + +void GateMatePacker::pack_io_sel() +{ + std::vector cells; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_OBUF, + id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF)) + continue; + + cells.push_back(&ci); + } + + CellInfo *ddr[9] = {nullptr}; // for each bank + + auto set_out_clk = [&](CellInfo *cell, CellInfo *target) -> bool { + NetInfo *clk_net = cell->getPort(id_CLK); + if (clk_net) { + if (clk_net->name == ctx->id("$PACKER_GND")) { + cell->disconnectPort(id_CLK); + } else if (clk_net->name == ctx->id("$PACKER_VCC")) { + cell->disconnectPort(id_CLK); + } else { + if (!global_signals.count(clk_net)) { + cell->movePortTo(id_CLK, target, id_OUT4); + target->params[id_SEL_OUT_CLOCK] = Property(Property::State::S1); + return true; + } else { + int index = global_signals[clk_net]; + cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1)); + target->params[id_OUT_CLOCK] = Property(index, 2); + } + } + } + return false; + }; + auto set_in_clk = [&](CellInfo *cell, CellInfo *target) { + NetInfo *clk_net = cell->getPort(id_CLK); + if (clk_net) { + if (clk_net->name == ctx->id("$PACKER_GND")) { + cell->disconnectPort(id_CLK); + } else if (clk_net->name == ctx->id("$PACKER_VCC")) { + cell->disconnectPort(id_CLK); + } else { + if (!global_signals.count(clk_net)) { + cell->movePortTo(id_CLK, target, id_OUT4); + target->params[id_SEL_IN_CLOCK] = Property(Property::State::S1); + } else { + int index = global_signals[clk_net]; + cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1)); + target->params[id_IN_CLOCK] = Property(index, 2); + } + } + } + }; + + auto merge_ibf = [&](NetInfo *di_net, CellInfo &ci, bool use_custom_clock) -> bool { + CellInfo *dff = (*di_net->users.begin()).cell; + if (is_gpio_valid_dff(dff)) { + if (!global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) { + log_warning("Found DFF %s cell, but not enough CLK signals.\n", dff->name.c_str(ctx)); + return false; + } + // We configure both GPIO IN and let router decide + ci.params[id_IN1_FF] = Property(Property::State::S1); + ci.params[id_IN2_FF] = Property(Property::State::S1); + packed_cells.emplace(dff->name); + ci.disconnectPort(id_Y); + dff->movePortTo(id_Q, &ci, id_DI); + set_in_clk(dff, &ci); + bool invert = bool_or_default(dff->params, id_CLK_INV, 0); + if (invert) { + ci.params[id_INV_IN1_CLOCK] = Property(Property::State::S1); + ci.params[id_INV_IN2_CLOCK] = Property(Property::State::S1); + } + return true; + } else { + log_warning("DFF '%s' cell for IO '%s', but unable to merge.\n", dff->name.c_str(ctx), ci.name.c_str(ctx)); + } + return false; + }; + + auto merge_iddr = [&](NetInfo *di_net, CellInfo &ci, bool use_custom_clock) -> bool { + CellInfo *iddr = (*di_net->users.begin()).cell; + if (!global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) { + log_warning("Found IDDR %s cell, but not enough CLK signals.\n", iddr->name.c_str(ctx)); + return false; + } + + ci.params[id_IN1_FF] = Property(Property::State::S1); + ci.params[id_IN2_FF] = Property(Property::State::S1); + packed_cells.emplace(iddr->name); + ci.disconnectPort(id_Y); + + iddr->movePortTo(id_Q0, &ci, id_IN1); + iddr->movePortTo(id_Q1, &ci, id_IN2); + + set_in_clk(iddr, &ci); + bool invert = bool_or_default(iddr->params, id_CLK_INV, 0); + if (invert) { + ci.params[id_INV_IN1_CLOCK] = Property(Property::State::S1); + } else { + ci.params[id_INV_IN2_CLOCK] = Property(Property::State::S1); + } + return false; + }; + + for (auto &cell : cells) { + CellInfo &ci = *cell; + bool ff_obf = bool_or_default(ci.params, id_FF_OBF, 0); + bool ff_ibf = bool_or_default(ci.params, id_FF_IBF, 0); + ci.unsetParam(id_FF_OBF); + ci.unsetParam(id_FF_IBF); + + if (ci.getPort(id_T)) { + ci.params[id_OE_SIGNAL] = Property(0b10, 2); + ci.renamePort(id_T, id_OUT3); + } + + ci.cluster = ci.name; + std::string loc = str_or_default(ci.params, id_LOC, "UNPLACED"); + ci.unsetParam(id_LOC); + + NetInfo *do_net = ci.getPort(id_A); + bool use_custom_clock = false; + if (do_net) { + if (do_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) { + ci.params[id_OUT23_14_SEL] = + Property(do_net->name == ctx->id("$PACKER_VCC") ? Property::State::S1 : Property::State::S0); + ci.disconnectPort(id_A); + } else { + ci.params[id_OUT_SIGNAL] = Property(Property::State::S1); + bool ff_obf_merged = false; + if (ff_obf && do_net->driver.cell->type == id_CC_DFF && do_net->users.entries() == 1) { + CellInfo *dff = do_net->driver.cell; + if (is_gpio_valid_dff(dff)) { + ci.params[id_OUT1_FF] = Property(Property::State::S1); + packed_cells.emplace(dff->name); + ci.disconnectPort(id_A); + dff->movePortTo(id_D, &ci, id_OUT1); + use_custom_clock = set_out_clk(dff, &ci); + bool invert = bool_or_default(dff->params, id_CLK_INV, 0); + if (invert) { + ci.params[id_INV_OUT1_CLOCK] = Property(Property::State::S1); + ci.params[id_INV_OUT2_CLOCK] = Property(Property::State::S1); + } + ff_obf_merged = true; + } else { + log_warning("DFF '%s' cell for IO '%s', but unable to merge.\n", dff->name.c_str(ctx), + ci.name.c_str(ctx)); + } + } + bool oddr_merged = false; + if (do_net->driver.cell->type == id_CC_ODDR && do_net->users.entries() == 1) { + CellInfo *oddr = do_net->driver.cell; + ci.params[id_OUT1_FF] = Property(Property::State::S1); + ci.params[id_OUT2_FF] = Property(Property::State::S1); + ci.params[id_USE_DDR] = Property(Property::State::S1); + packed_cells.emplace(oddr->name); + ci.disconnectPort(id_A); + // TODO: check mapping + oddr->movePortTo(id_D0, &ci, id_OUT2); + oddr->movePortTo(id_D1, &ci, id_OUT1); + const auto &pad = ctx->get_package_pin(ctx->id(loc)); + CellInfo *cpe_half = ddr[pad->pad_bank]; + if (cpe_half) { + if (cpe_half->getPort(id_IN1) != oddr->getPort(id_DDR)) + log_error("DDR port use signal different than already occupied DDR source.\n"); + ci.ports[id_DDR].name = id_DDR; + ci.ports[id_DDR].type = PORT_IN; + ci.connectPort(id_DDR, cpe_half->getPort(id_RAM_O)); + } else { + oddr->movePortTo(id_DDR, &ci, id_DDR); + cpe_half = move_ram_o(&ci, id_DDR, false); + ctx->bindBel(get_bank_cpe(pad->pad_bank), cpe_half, PlaceStrength::STRENGTH_FIXED); + ddr[pad->pad_bank] = cpe_half; + } + use_custom_clock = set_out_clk(oddr, &ci); + bool invert = bool_or_default(oddr->params, id_CLK_INV, 0); + if (invert) { + ci.params[id_INV_OUT1_CLOCK] = Property(Property::State::S1); + } else { + ci.params[id_INV_OUT2_CLOCK] = Property(Property::State::S1); + } + oddr_merged = true; + } + if (!ff_obf_merged && !oddr_merged) + ci.renamePort(id_A, id_OUT1); + } + } + NetInfo *di_net = ci.getPort(id_Y); + if (di_net) { + bool ff_ibf_merged = false; + if (ff_ibf && di_net->users.entries() == 1 && (*di_net->users.begin()).cell->type == id_CC_DFF) { + ff_ibf_merged = merge_ibf(di_net, ci, use_custom_clock); + } + bool iddr_merged = false; + if (di_net->users.entries() == 1 && (*di_net->users.begin()).cell->type == id_CC_IDDR) { + iddr_merged = merge_iddr(di_net, ci, use_custom_clock); + } + + if (!ff_ibf_merged && !iddr_merged) + ci.renamePort(id_Y, id_DI); + } + + Loc root_loc = ctx->getBelLocation(ci.bel); + for (int i = 0; i < 4; i++) { + move_ram_o_fixed(&ci, ctx->idf("OUT%d", i + 1), root_loc); + } + } + flush_cells(); +} + +bool GateMatePacker::is_gpio_valid_dff(CellInfo *dff) +{ + NetInfo *en_net = dff->getPort(id_EN); + bool invert = bool_or_default(dff->params, id_EN_INV, 0); + if (en_net) { + if (en_net->name == ctx->id("$PACKER_GND")) { + if (!invert) + return false; + dff->disconnectPort(id_EN); + } else if (en_net->name == ctx->id("$PACKER_VCC")) { + if (invert) + return false; + dff->disconnectPort(id_EN); + } else { + return false; + } + } + dff->unsetParam(id_EN_INV); + + NetInfo *sr_net = dff->getPort(id_SR); + invert = bool_or_default(dff->params, id_SR_INV, 0); + if (sr_net) { + if (sr_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) { + bool sr_signal = sr_net->name == ctx->id("$PACKER_VCC"); + if (sr_signal ^ invert) + log_error("Currently unsupported DFF configuration for '%s'\n.", dff->name.c_str(ctx)); + dff->disconnectPort(id_SR); + } else { + return false; + } + } + dff->unsetParam(id_SR_VAL); + dff->unsetParam(id_SR_INV); + + // Sanity check for CLK signal, that it must exist + NetInfo *clk_net = dff->getPort(id_CLK); + if (clk_net) { + if (clk_net->name == ctx->id("$PACKER_GND")) { + return false; + } else if (clk_net->name == ctx->id("$PACKER_VCC")) { + return false; + } + } else { + return false; + } + + return true; +} + +void GateMatePacker::dff_to_cpe(CellInfo *dff, CellInfo *cpe) +{ + bool invert; + bool is_latch = dff->type == id_CC_DLT; + if (is_latch) { + NetInfo *g_net = cpe->getPort(id_G); + invert = bool_or_default(dff->params, id_G_INV, 0); + if (g_net) { + if (g_net->name == ctx->id("$PACKER_GND")) { + cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); + cpe->disconnectPort(id_G); + } else if (g_net->name == ctx->id("$PACKER_VCC")) { + cpe->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2); + cpe->disconnectPort(id_G); + } else { + cpe->params[id_C_CPE_CLK] = Property(invert ? 0b01 : 0b10, 2); + } + } else { + cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); + } + dff->unsetParam(id_G_INV); + cpe->renamePort(id_G, id_CLK); + + cpe->params[id_C_CPE_EN] = Property(0b11, 2); + cpe->params[id_C_L_D] = Property(0b1, 1); + } else { + NetInfo *en_net = cpe->getPort(id_EN); + bool invert = bool_or_default(dff->params, id_EN_INV, 0); + if (en_net) { + if (en_net->name == ctx->id("$PACKER_GND")) { + cpe->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2); + cpe->disconnectPort(id_EN); + } else if (en_net->name == ctx->id("$PACKER_VCC")) { + cpe->params[id_C_CPE_EN] = Property(invert ? 0b00 : 0b11, 2); + cpe->disconnectPort(id_EN); + } else { + cpe->params[id_C_CPE_EN] = Property(invert ? 0b01 : 0b10, 2); + } + } else { + cpe->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2); + } + dff->unsetParam(id_EN_INV); + + NetInfo *clk_net = cpe->getPort(id_CLK); + invert = bool_or_default(dff->params, id_CLK_INV, 0); + if (clk_net) { + if (clk_net->name == ctx->id("$PACKER_GND")) { + cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); + cpe->disconnectPort(id_CLK); + } else if (clk_net->name == ctx->id("$PACKER_VCC")) { + cpe->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2); + cpe->disconnectPort(id_CLK); + } else { + cpe->params[id_C_CPE_CLK] = Property(invert ? 0b01 : 0b10, 2); + } + } else { + cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2); + } + dff->unsetParam(id_CLK_INV); + } + + NetInfo *sr_net = cpe->getPort(id_SR); + invert = bool_or_default(dff->params, id_SR_INV, 0); + bool sr_val = bool_or_default(dff->params, id_SR_VAL, 0); + if (sr_net) { + if (sr_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) { + bool sr_signal = sr_net->name == ctx->id("$PACKER_VCC"); + if (sr_signal ^ invert) + log_error("Currently unsupported DFF configuration for '%s'\n.", dff->name.c_str(ctx)); + cpe->params[id_C_CPE_RES] = Property(0b11, 2); + cpe->params[id_C_CPE_SET] = Property(0b11, 2); + cpe->disconnectPort(id_SR); + } else { + if (sr_val) { + cpe->params[id_C_CPE_RES] = Property(0b11, 2); + cpe->params[id_C_CPE_SET] = Property(invert ? 0b10 : 0b01, 2); + if (is_latch) + cpe->renamePort(id_SR, id_EN); + else + cpe->params[id_C_EN_SR] = Property(0b1, 1); + } else { + cpe->params[id_C_CPE_RES] = Property(invert ? 0b10 : 0b01, 2); + cpe->params[id_C_CPE_SET] = Property(0b11, 2); + } + } + } else { + cpe->params[id_C_CPE_RES] = Property(0b11, 2); + cpe->params[id_C_CPE_SET] = Property(0b11, 2); + } + dff->unsetParam(id_SR_VAL); + dff->unsetParam(id_SR_INV); + + if (dff->params.count(id_INIT) && dff->params[id_INIT].is_fully_def()) { + bool init = bool_or_default(dff->params, id_INIT, 0); + if (init) + cpe->params[id_FF_INIT] = Property(0b11, 2); + else + cpe->params[id_FF_INIT] = Property(0b10, 2); + dff->unsetParam(id_INIT); + } else { + dff->unsetParam(id_INIT); + } + cpe->timing_index = ctx->get_cell_timing_idx(id_CPE_DFF); + cpe->params[id_C_O] = Property(0b00, 2); +} + +void GateMatePacker::pack_cpe() +{ + log_info("Packing CPEs..\n"); + std::vector l2t5_list; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_L2T4, id_CC_L2T5, id_CC_LUT2, id_CC_LUT1, id_CC_MX2)) + continue; + if (ci.type == id_CC_L2T5) { + l2t5_list.push_back(&ci); + ci.renamePort(id_I0, id_IN1); + ci.renamePort(id_I1, id_IN2); + ci.renamePort(id_I2, id_IN3); + ci.renamePort(id_I3, id_IN4); + + ci.renamePort(id_O, id_OUT); + + ci.params[id_C_O] = Property(0b11, 2); + ci.type = id_CPE_HALF_L; + } else if (ci.type == id_CC_MX2) { + ci.params[id_C_O] = Property(0b11, 2); + ci.renamePort(id_D1, id_IN1); + NetInfo *sel = ci.getPort(id_S0); + ci.renamePort(id_S0, id_IN2); + ci.ports[id_IN3].name = id_IN3; + ci.ports[id_IN3].type = PORT_IN; + ci.connectPort(id_IN3, sel); + ci.renamePort(id_D0, id_IN4); + ci.disconnectPort(id_D1); + ci.params[id_INIT_L00] = Property(0b1000, 4); // AND + ci.params[id_INIT_L01] = Property(0b0100, 4); // AND inv D0 + ci.params[id_INIT_L10] = Property(0b1110, 4); // OR + ci.renamePort(id_Y, id_OUT); + ci.type = id_CPE_HALF; + } else { + ci.renamePort(id_I0, id_IN1); + ci.renamePort(id_I1, id_IN2); + ci.renamePort(id_I2, id_IN3); + ci.renamePort(id_I3, id_IN4); + ci.renamePort(id_O, id_OUT); + ci.params[id_C_O] = Property(0b11, 2); + if (ci.type.in(id_CC_LUT1, id_CC_LUT2)) { + uint8_t val = int_or_default(ci.params, id_INIT, 0); + if (ci.type == id_CC_LUT1) + val = val << 2 | val; + ci.params[id_INIT_L00] = Property(val, 4); + ci.unsetParam(id_INIT); + ci.params[id_INIT_L10] = Property(0b1010, 4); + } + ci.type = id_CPE_HALF; + } + NetInfo *o = ci.getPort(id_OUT); + if (o) { + CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true); + if (dff) { + if (dff->type == id_CC_DLT) { + dff->movePortTo(id_G, &ci, id_G); + } else { + dff->movePortTo(id_EN, &ci, id_EN); + dff->movePortTo(id_CLK, &ci, id_CLK); + } + dff->movePortTo(id_SR, &ci, id_SR); + dff->disconnectPort(id_D); + ci.disconnectPort(id_OUT); + dff->movePortTo(id_Q, &ci, id_OUT); + dff_to_cpe(dff, &ci); + packed_cells.insert(dff->name); + } + } + } + + for (auto ci : l2t5_list) { + CellInfo *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", ci->name.c_str(ctx))); + upper->cluster = ci->name; + upper->constr_abs_z = false; + upper->constr_z = -1; + ci->cluster = ci->name; + ci->movePortTo(id_I4, upper, id_IN1); + upper->params[id_INIT_L00] = Property(0b1010, 4); + upper->params[id_INIT_L10] = Property(0b1010, 4); + ci->constr_children.push_back(upper); + } + l2t5_list.clear(); + + flush_cells(); + + std::vector mux_list; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_MX4)) + continue; + mux_list.push_back(&ci); + } + for (auto &cell : mux_list) { + CellInfo &ci = *cell; + ci.cluster = ci.name; + ci.renamePort(id_Y, id_OUT); + + ci.renamePort(id_S0, id_IN2); // IN6 + ci.renamePort(id_S1, id_IN4); // IN8 + + uint8_t select = 0; + uint8_t invert = 0; + for (int i = 0; i < 4; i++) { + NetInfo *net = ci.getPort(ctx->idf("D%d", i)); + if (net) { + if (net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) { + if (net->name == ctx->id("$PACKER_VCC")) + invert |= 1 << i; + ci.disconnectPort(ctx->idf("D%d", i)); + } else { + select |= 1 << i; + } + } + } + ci.params[id_C_FUNCTION] = Property(C_MX4, 3); + ci.params[id_INIT_L02] = Property(0b1100, 4); // IN6 + ci.params[id_INIT_L03] = Property(0b1100, 4); // IN8 + ci.params[id_INIT_L11] = Property(invert, 4); // Inversion bits + // ci.params[id_INIT_L20] = Property(0b1100, 4); // Always D1 + ci.params[id_C_O] = Property(0b11, 2); + ci.type = id_CPE_HALF_L; + + CellInfo *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", ci.name.c_str(ctx))); + upper->cluster = ci.name; + upper->constr_abs_z = false; + upper->constr_z = -1; + upper->params[id_INIT_L10] = Property(select, 4); // Selection bits + upper->params[id_C_FUNCTION] = Property(C_MX4, 3); + + ci.movePortTo(id_D0, upper, id_IN1); + ci.movePortTo(id_D1, upper, id_IN2); + ci.movePortTo(id_D2, upper, id_IN3); + ci.movePortTo(id_D3, upper, id_IN4); + ci.constr_children.push_back(upper); + } + mux_list.clear(); + + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_DFF, id_CC_DLT)) + continue; + ci.renamePort(id_Q, id_OUT); + NetInfo *d_net = ci.getPort(id_D); + if (d_net->name == ctx->id("$PACKER_GND")) { + ci.params[id_INIT_L00] = Property(0b0000, 4); + ci.disconnectPort(id_D); + } else if (d_net->name == ctx->id("$PACKER_VCC")) { + ci.params[id_INIT_L00] = Property(0b1111, 4); + ci.disconnectPort(id_D); + } else { + ci.params[id_INIT_L00] = Property(0b1010, 4); + } + ci.params[id_INIT_L10] = Property(0b1010, 4); + ci.renamePort(id_D, id_IN1); + dff_to_cpe(&ci, &ci); + ci.type = id_CPE_HALF; + } +} + +static bool is_addf_ci(NetInfo *net) +{ + return net && net->users.entries() == 1 && (*net->users.begin()).cell->type == id_CC_ADDF && + (*net->users.begin()).port == id_CI; +} + +void GateMatePacker::pack_addf() +{ + log_info("Packing ADDFs..\n"); + + std::vector root_cys; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type != id_CC_ADDF) + continue; + NetInfo *ci_net = ci->getPort(id_CI); + + if (!ci_net || !ci_net->driver.cell || + !(ci_net->driver.cell->type == id_CC_ADDF && ci_net->driver.port == id_CO)) { + root_cys.push_back(ci); + } + } + 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) { + bool found = false; + for (auto &usr : co_net->users) { + if (usr.cell->type == id_CC_ADDF && usr.port == id_CI) { + if (found) + log_error("Only one other ADDF can be connected.\n"); + cy = usr.cell; + group.push_back(cy); + found = true; + } + } + if (!found) + break; + } else + break; + } + groups.push_back(group); + } + + // Merge two ADDF cells to one CPE when possible + // use artificial CC_ADDF2 cell for that + for (size_t i = 0; i < groups.size(); i++) { + std::vector regrouped; + size_t pos = 0; + auto &grp = groups.at(i); + while (pos < grp.size()) { + bool merged = false; + CellInfo *cy = grp.at(pos); + NetInfo *co_net = cy->getPort(id_CO); + bool last = pos + 1 == grp.size(); + if (!last && is_addf_ci(co_net)) { + CellInfo *cy2 = grp.at(pos + 1); + co_net = cy2->getPort(id_CO); + last = pos + 2 == grp.size(); + if (!co_net || last || is_addf_ci(co_net)) { + cy2->type = id_CC_ADDF2; + cy2->disconnectPort(id_CI); + // Do actual merge of cells + cy->movePortTo(id_A, cy2, id_A2); + cy->movePortTo(id_B, cy2, id_B2); + cy->movePortTo(id_S, cy2, id_S2); + cy->disconnectPort(id_CO); + cy->movePortTo(id_CI, cy2, id_CI); + packed_cells.insert(cy->name); + regrouped.push_back(cy2); + merged = true; + pos++; + } + } + if (!merged) + regrouped.push_back(cy); + pos++; + } + grp = regrouped; + } + flush_cells(); + + for (auto &grp : splitNestedVector(groups)) { + CellInfo *root = grp.front(); + root->cluster = root->name; + + CellInfo *ci_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$ci_upper", root->name.c_str(ctx))); + root->constr_children.push_back(ci_upper); + ci_upper->cluster = root->name; + ci_upper->constr_abs_z = false; + ci_upper->constr_z = -1; + ci_upper->constr_y = -1; + + CellInfo *ci_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$ci_lower", root->name.c_str(ctx))); + root->constr_children.push_back(ci_lower); + ci_lower->cluster = root->name; + ci_lower->constr_abs_z = false; + ci_lower->constr_y = -1; + ci_lower->params[id_C_O] = Property(0b11, 2); + ci_lower->params[id_C_SELY1] = Property(1, 1); + ci_lower->params[id_C_CY1_I] = Property(1, 1); + ci_lower->params[id_INIT_L10] = Property(0b1010, 4); // D0 + + NetInfo *ci_net = root->getPort(id_CI); + if (ci_net->name == ctx->id("$PACKER_GND")) { + ci_lower->params[id_INIT_L00] = Property(0b0000, 4); + root->disconnectPort(id_CI); + } else if (ci_net->name == ctx->id("$PACKER_VCC")) { + ci_lower->params[id_INIT_L00] = Property(0b1111, 4); + root->disconnectPort(id_CI); + } else { + root->movePortTo(id_CI, ci_lower, id_IN1); + ci_lower->params[id_INIT_L00] = Property(0b1010, 4); // IN5 + } + + NetInfo *ci_conn = ctx->createNet(ctx->idf("%s$ci", root->name.c_str(ctx))); + ci_lower->connectPort(id_COUTY1, ci_conn); + + root->ports[id_CINY1].name = id_CINY1; + root->ports[id_CINY1].type = PORT_IN; + root->connectPort(id_CINY1, ci_conn); + + for (size_t i = 0; i < grp.size(); i++) { + CellInfo *cy = grp.at(i); + if (i != 0) { + cy->cluster = root->name; + root->constr_children.push_back(cy); + cy->constr_abs_z = false; + cy->constr_y = +i; + cy->renamePort(id_CI, id_CINY1); + } + + bool merged = cy->type != id_CC_ADDF; + if (merged) { + NetInfo *a_net = cy->getPort(id_A2); + if (a_net->name == ctx->id("$PACKER_GND")) { + cy->params[id_INIT_L02] = Property(0b0000, 4); + cy->disconnectPort(id_A2); + } else if (a_net->name == ctx->id("$PACKER_VCC")) { + cy->params[id_INIT_L02] = Property(0b1111, 4); + cy->disconnectPort(id_A2); + } else { + cy->renamePort(id_A2, id_IN1); + cy->params[id_INIT_L02] = Property(0b1010, 4); // IN1 + } + NetInfo *b_net = cy->getPort(id_B2); + if (b_net->name == ctx->id("$PACKER_GND")) { + cy->params[id_INIT_L03] = Property(0b0000, 4); + cy->disconnectPort(id_B2); + } else if (b_net->name == ctx->id("$PACKER_VCC")) { + cy->params[id_INIT_L03] = Property(0b1111, 4); + cy->disconnectPort(id_B2); + } else { + cy->renamePort(id_B2, id_IN3); + cy->params[id_INIT_L03] = Property(0b1010, 4); // IN3 + } + cy->params[id_INIT_L11] = Property(0b0110, 4); // XOR + cy->renamePort(id_S2, id_OUT); + } else { + cy->params[id_INIT_L02] = Property(0b0000, 4); // 0 + cy->params[id_INIT_L03] = Property(0b0000, 4); // 0 + cy->params[id_INIT_L11] = Property(0b0110, 4); // XOR + cy->params[id_INIT_L20] = Property(0b0110, 4); // XOR + } + cy->params[id_C_FUNCTION] = Property(merged ? C_ADDF2 : C_ADDF, 3); + cy->params[id_C_O] = Property(0b11, 2); + cy->type = id_CPE_HALF_L; + + CellInfo *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", cy->name.c_str(ctx))); + upper->cluster = root->name; + root->constr_children.push_back(upper); + upper->constr_abs_z = false; + upper->constr_y = +i; + upper->constr_z = -1; + if (merged) { + cy->movePortTo(id_S, upper, id_OUT); + upper->params[id_C_O] = Property(0b11, 2); + } else { + cy->renamePort(id_S, id_OUT); + } + + NetInfo *a_net = cy->getPort(id_A); + if (a_net->name == ctx->id("$PACKER_GND")) { + upper->params[id_INIT_L00] = Property(0b0000, 4); + cy->disconnectPort(id_A); + } else if (a_net->name == ctx->id("$PACKER_VCC")) { + upper->params[id_INIT_L00] = Property(0b1111, 4); + cy->disconnectPort(id_A); + } else { + cy->movePortTo(id_A, upper, id_IN1); + upper->params[id_INIT_L00] = Property(0b1010, 4); // IN1 + } + NetInfo *b_net = cy->getPort(id_B); + if (b_net->name == ctx->id("$PACKER_GND")) { + upper->params[id_INIT_L01] = Property(0b0000, 4); + cy->disconnectPort(id_B); + } else if (b_net->name == ctx->id("$PACKER_VCC")) { + upper->params[id_INIT_L01] = Property(0b1111, 4); + cy->disconnectPort(id_B); + } else { + cy->movePortTo(id_B, upper, id_IN3); + upper->params[id_INIT_L01] = Property(0b1010, 4); // IN3 + } + + upper->params[id_INIT_L10] = Property(0b0110, 4); // XOR + upper->params[id_C_FUNCTION] = Property(merged ? C_ADDF2 : C_ADDF, 3); + + if (i == grp.size() - 1) { + if (!cy->getPort(id_CO)) + break; + CellInfo *co_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$co_upper", cy->name.c_str(ctx))); + co_upper->cluster = root->name; + root->constr_children.push_back(co_upper); + co_upper->constr_abs_z = false; + co_upper->constr_z = -1; + co_upper->constr_y = +i + 1; + CellInfo *co_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$co_lower", cy->name.c_str(ctx))); + co_lower->cluster = root->name; + root->constr_children.push_back(co_lower); + co_lower->constr_abs_z = false; + co_lower->constr_y = +i + 1; + co_lower->params[id_C_O] = Property(0b11, 2); + co_lower->params[id_C_FUNCTION] = Property(C_EN_CIN, 3); + co_lower->params[id_INIT_L11] = Property(0b1100, 4); + co_lower->params[id_INIT_L20] = Property(0b1100, 4); + + NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co", cy->name.c_str(ctx))); + + co_lower->ports[id_CINY1].name = id_CINY1; + co_lower->ports[id_CINY1].type = PORT_IN; + co_lower->connectPort(id_CINY1, co_conn); + cy->ports[id_COUTY1].name = id_COUTY1; + cy->ports[id_COUTY1].type = PORT_OUT; + cy->connectPort(id_COUTY1, co_conn); + + cy->movePortTo(id_CO, co_lower, id_OUT); + } else { + NetInfo *co_net = cy->getPort(id_CO); + if (!co_net || co_net->users.entries() == 1) { + cy->renamePort(id_CO, id_COUTY1); + } else { + for (auto &usr : co_net->users) { + if (usr.cell->type == id_CC_ADDF || usr.port == id_CI) { + usr.cell->disconnectPort(id_CI); + NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co", cy->name.c_str(ctx))); + cy->ports[id_COUTY1].name = id_COUTY1; + cy->ports[id_COUTY1].type = PORT_OUT; + cy->connectPort(id_COUTY1, co_conn); + usr.cell->connectPort(id_CI, co_conn); + break; + } + } + upper->params[id_C_O] = Property(0b10, 2); + cy->movePortTo(id_CO, upper, id_OUT); + } + } + } + } +} + +void GateMatePacker::sort_bufg() +{ + struct ItemBufG + { + CellInfo *cell; + int32_t fan_out; + ItemBufG(CellInfo *cell, int32_t fan_out) : cell(cell), fan_out(fan_out) {} + }; + + std::vector bufg; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_BUFG)) + continue; + + NetInfo *i_net = ci.getPort(id_I); + if (!i_net) { + log_warning("Removing BUFG cell %s since there is no input used.\n", ci.name.c_str(ctx)); + packed_cells.emplace(ci.name); // Remove if no input + continue; + } + NetInfo *o_net = ci.getPort(id_O); + if (!o_net) { + log_warning("Removing BUFG cell %s since there is no output used.\n", ci.name.c_str(ctx)); + packed_cells.emplace(ci.name); // Remove if no output + continue; + } + bufg.push_back(ItemBufG(&ci, o_net->users.entries())); + } + + if (bufg.size() > 4) { + log_warning("More than 4 BUFG used. Those with highest fan-out will be used.\n"); + std::sort(bufg.begin(), bufg.end(), [](const ItemBufG &a, const ItemBufG &b) { return a.fan_out > b.fan_out; }); + for (size_t i = 4; i < bufg.size(); i++) { + log_warning("Removing BUFG cell %s.\n", bufg.at(i).cell->name.c_str(ctx)); + CellInfo *cell = bufg.at(i).cell; + NetInfo *i_net = cell->getPort(id_I); + NetInfo *o_net = cell->getPort(id_O); + for (auto s : o_net->users) { + s.cell->disconnectPort(s.port); + s.cell->connectPort(s.port, i_net); + } + packed_cells.emplace(bufg.at(i).cell->name); + } + } + flush_cells(); +} + +void GateMatePacker::pack_bufg() +{ + log_info("Packing BUFGs..\n"); + CellInfo *bufg[4] = {nullptr}; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_BUFG)) + continue; + + NetInfo *in_net = ci.getPort(id_I); + if (in_net) { + bool is_cpe_source = true; + if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_GPIO) { + auto pad_info = uarch->bel_to_pad[in_net->driver.cell->bel]; + if (pad_info->flags) + is_cpe_source = false; + } + if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_PLL) { + is_cpe_source = false; + int pll_index = in_net->driver.cell->constr_z - 4; + if (bufg[pll_index] == nullptr) { + bufg[pll_index] = &ci; + } else { + IdString origPort = in_net->driver.port; + int index = 0; + if (origPort == id_CLK90) + index = 1; + else if (origPort == id_CLK180) + index = 2; + else if (origPort == id_CLK270) + index = 3; + if (bufg[index] == nullptr) { + bufg[pll_index] = &ci; + } else { + log_error("Unable to place BUFG for PLL.\n"); + } + } + } + if (is_cpe_source) { + ci.cluster = ci.name; + move_ram_o(&ci, id_I); + } + + if (in_net->clkconstr) { + NetInfo *o_net = ci.getPort(id_O); + o_net->clkconstr = std::unique_ptr(new ClockConstraint()); + o_net->clkconstr->low = in_net->clkconstr->low; + o_net->clkconstr->high = in_net->clkconstr->high; + o_net->clkconstr->period = in_net->clkconstr->period; + } + } + ci.type = id_BUFG; + } + + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_BUFG)) + continue; + NetInfo *in_net = ci.getPort(id_I); + if (in_net && ctx->getBelBucketForCellType(in_net->driver.cell->type) != id_PLL) { + for (int i = 0; i < 4; i++) { + if (bufg[i] == nullptr) { + bufg[i] = &ci; + break; + } + } + } + } + + for (int i = 0; i < 4; i++) { + if (bufg[i]) { + CellInfo &ci = *bufg[i]; + global_signals.emplace(ci.getPort(id_O), i); + ci.cluster = ci.name; + ci.constr_abs_z = true; + ci.constr_z = i; + } + } +} + +CellInfo *GateMatePacker::move_ram_i(CellInfo *cell, IdString origPort, bool place) +{ + CellInfo *cpe_half = nullptr; + NetInfo *net = cell->getPort(origPort); + if (net) { + cpe_half = create_cell_ptr(id_CPE_HALF, ctx->idf("%s$%s_cpe_half", cell->name.c_str(ctx), origPort.c_str(ctx))); + if (place) { + cell->constr_children.push_back(cpe_half); + cpe_half->cluster = cell->name; + cpe_half->constr_abs_z = false; + cpe_half->constr_z = PLACE_DB_CONSTR + origPort.index; + } + cpe_half->params[id_C_RAM_I] = Property(1, 1); + + NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_half->name.c_str(ctx))); + cell->movePortTo(origPort, cpe_half, id_OUT); + cell->connectPort(origPort, ram_i); + cpe_half->connectPort(id_RAM_I, ram_i); + } + return cpe_half; +} + +CellInfo *GateMatePacker::move_ram_o(CellInfo *cell, IdString origPort, bool place) +{ + CellInfo *cpe_half = nullptr; + NetInfo *net = cell->getPort(origPort); + if (net) { + cpe_half = create_cell_ptr(id_CPE_HALF, ctx->idf("%s$%s_cpe_half", cell->name.c_str(ctx), origPort.c_str(ctx))); + if (place) { + cell->constr_children.push_back(cpe_half); + cpe_half->cluster = cell->name; + cpe_half->constr_abs_z = false; + cpe_half->constr_z = PLACE_DB_CONSTR + origPort.index; + } + if (net->name == ctx->id("$PACKER_GND")) { + cpe_half->params[id_INIT_L00] = Property(0b0000, 4); + cell->disconnectPort(origPort); + } else if (net->name == ctx->id("$PACKER_VCC")) { + cpe_half->params[id_INIT_L00] = Property(0b1111, 4); + cell->disconnectPort(origPort); + } else { + cpe_half->params[id_INIT_L00] = Property(0b1010, 4); + cell->movePortTo(origPort, cpe_half, id_IN1); + } + cpe_half->params[id_INIT_L10] = Property(0b1010, 4); + cpe_half->params[id_C_O] = Property(0b11, 2); + cpe_half->params[id_C_RAM_O] = Property(1, 1); + + NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx))); + cell->connectPort(origPort, ram_o); + cpe_half->connectPort(id_RAM_O, ram_o); + } + return cpe_half; +} + +CellInfo *GateMatePacker::move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed) +{ + CellInfo *cpe = move_ram_i(cell, origPort, false); + if (cpe) { + BelId b = ctx->getBelByLocation(uarch->getRelativeConstraint(fixed, origPort)); + ctx->bindBel(b, cpe, PlaceStrength::STRENGTH_FIXED); + } + return cpe; +} + +CellInfo *GateMatePacker::move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed) +{ + CellInfo *cpe = move_ram_o(cell, origPort, false); + if (cpe) { + BelId b = ctx->getBelByLocation(uarch->getRelativeConstraint(fixed, origPort)); + ctx->bindBel(b, cpe, PlaceStrength::STRENGTH_FIXED); + } + return cpe; +} + +CellInfo *GateMatePacker::move_ram_io(CellInfo *cell, IdString iPort, IdString oPort, bool place) +{ + CellInfo *cpe_half = nullptr; + NetInfo *i_net = cell->getPort(iPort); + NetInfo *o_net = cell->getPort(oPort); + if (!i_net && !o_net) + return cpe_half; + + cpe_half = create_cell_ptr(id_CPE_HALF, ctx->idf("%s$%s_cpe_half", cell->name.c_str(ctx), oPort.c_str(ctx))); + if (place) { + cell->constr_children.push_back(cpe_half); + cpe_half->cluster = cell->name; + cpe_half->constr_abs_z = false; + cpe_half->constr_z = PLACE_DB_CONSTR + oPort.index; + } + + if (o_net) { + if (o_net->name == ctx->id("$PACKER_GND")) { + cpe_half->params[id_INIT_L00] = Property(0b0000, 4); + cell->disconnectPort(oPort); + } else if (o_net->name == ctx->id("$PACKER_VCC")) { + cpe_half->params[id_INIT_L00] = Property(0b1111, 4); + cell->disconnectPort(oPort); + } else { + cpe_half->params[id_INIT_L00] = Property(0b1010, 4); + cell->movePortTo(oPort, cpe_half, id_IN1); + } + cpe_half->params[id_INIT_L10] = Property(0b1010, 4); + cpe_half->params[id_C_O] = Property(0b11, 2); + cpe_half->params[id_C_RAM_O] = Property(1, 1); + + NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx))); + cell->connectPort(oPort, ram_o); + cpe_half->connectPort(id_RAM_O, ram_o); + } + if (i_net) { + cpe_half->params[id_C_RAM_I] = Property(1, 1); + + NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_half->name.c_str(ctx))); + cell->movePortTo(iPort, cpe_half, id_OUT); + cell->connectPort(iPort, ram_i); + cpe_half->connectPort(id_RAM_I, ram_i); + } + // TODO: set proper timing model, without this it detects combinational loops + cpe_half->timing_index = ctx->get_cell_timing_idx(id_CPE_DFF); + return cpe_half; +} + +void GateMatePacker::pll_out(CellInfo *cell, IdString origPort, Loc fixed) +{ + NetInfo *net = cell->getPort(origPort); + if (!net) + return; + CellInfo *bufg = nullptr; + for (auto &usr : net->users) { + if (usr.cell->type == id_CC_BUFG) + bufg = usr.cell; + } + if (bufg) { + if (net->users.entries() != 1) { + log_error("not handled BUFG\n"); + } + } else { + move_ram_i_fixed(cell, origPort, fixed); + } +} + +void GateMatePacker::insert_bufg(CellInfo *cell, IdString port) +{ + NetInfo *clk = cell->getPort(port); + if (clk) { + if (!(clk->users.entries() == 1 && (*clk->users.begin()).cell->type == id_CC_BUFG)) { + CellInfo *bufg = + create_cell_ptr(id_CC_BUFG, ctx->idf("%s$BUFG_%s", cell->name.c_str(ctx), port.c_str(ctx))); + cell->movePortTo(port, bufg, id_O); + cell->ports[port].name = port; + cell->ports[port].type = PORT_OUT; + NetInfo *net = ctx->createNet(ctx->idf("%s", bufg->name.c_str(ctx))); + cell->connectPort(port, net); + bufg->connectPort(id_I, net); + log_info("Added BUFG for cell '%s' signal %s\n", cell->name.c_str(ctx), port.c_str(ctx)); + } + } +} + +void GateMatePacker::insert_pll_bufg() +{ + std::vector cells; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_PLL, id_CC_PLL_ADV)) + continue; + cells.push_back(&ci); + } + for (auto &cell : cells) { + insert_bufg(cell, id_CLK0); + insert_bufg(cell, id_CLK90); + insert_bufg(cell, id_CLK180); + insert_bufg(cell, id_CLK270); + } +} + +void GateMatePacker::pack_pll() +{ + int pll_index = 0; + log_info("Packing PLLss..\n"); + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_PLL, id_CC_PLL_ADV)) + continue; + + disconnect_if_gnd(&ci, id_CLK_REF); + disconnect_if_gnd(&ci, id_USR_CLK_REF); + disconnect_if_gnd(&ci, id_CLK_FEEDBACK); + disconnect_if_gnd(&ci, id_USR_LOCKED_STDY_RST); + + ci.cluster = ci.name; + ci.constr_abs_z = true; + ci.constr_z = 4 + pll_index; // Position to a proper Z location + + Loc fixed_loc(33 + 2, 131 + 2, 4 + pll_index); // PLL + BelId pll_bel = ctx->getBelByLocation(fixed_loc); + ctx->bindBel(pll_bel, &ci, PlaceStrength::STRENGTH_FIXED); + + if (pll_index > 4) + log_error("Used more than available PLLs.\n"); + + if (ci.getPort(id_CLK_REF) == nullptr && ci.getPort(id_USR_CLK_REF) == nullptr) + log_error("At least one reference clock (CLK_REF or USR_CLK_REF) must be set.\n"); + + if (ci.getPort(id_CLK_REF) != nullptr && ci.getPort(id_USR_CLK_REF) != nullptr) + log_error("CLK_REF and USR_CLK_REF are not allowed to be set in same time.\n"); + + NetInfo *clk = ci.getPort(id_CLK_REF); + delay_t period = ctx->getDelayFromNS(1.0e9 / ctx->setting("target_freq")); + if (clk) { + if (ctx->getBelBucketForCellType(clk->driver.cell->type) != id_GPIO) + log_error("CLK_REF must be driven with GPIO pin.\n"); + auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel]; + if (!(pad_info->flags & 1)) + log_error("CLK_REF must be driven with CLK dedicated pin.\n"); + if (clk->clkconstr) + period = clk->clkconstr->period.minDelay(); + } + + clk = ci.getPort(id_USR_CLK_REF); + if (clk) { + move_ram_o_fixed(&ci, id_USR_CLK_REF, fixed_loc); + if (clk->clkconstr) + period = clk->clkconstr->period.minDelay(); + } + // TODO: handle CLK_FEEDBACK + // TODO: handle CLK_REF_OUT + pll_out(&ci, id_CLK0, fixed_loc); + pll_out(&ci, id_CLK90, fixed_loc); + pll_out(&ci, id_CLK180, fixed_loc); + pll_out(&ci, id_CLK270, fixed_loc); + + move_ram_i_fixed(&ci, id_USR_PLL_LOCKED, fixed_loc); + move_ram_i_fixed(&ci, id_USR_PLL_LOCKED_STDY, fixed_loc); + + double out_clk_max = 0; + int clk270_doub = 0; + int clk180_doub = 0; + if (ci.type == id_CC_PLL) { + int low_jitter = int_or_default(ci.params, id_LOW_JITTER, 0); + int ci_const = int_or_default(ci.params, id_CI_FILTER_CONST, 0); + int cp_const = int_or_default(ci.params, id_CP_FILTER_CONST, 0); + clk270_doub = int_or_default(ci.params, id_CLK270_DOUB, 0); + clk180_doub = int_or_default(ci.params, id_CLK180_DOUB, 0); + int lock_req = int_or_default(ci.params, id_LOCK_REQ, 0); + + if (!ci.getPort(id_CLK_FEEDBACK)) + ci.params[id_LOCK_REQ] = Property(lock_req, 1); + ci.params[id_CLK180_DOUB] = Property(clk180_doub, 1); + ci.params[id_CLK270_DOUB] = Property(clk270_doub, 1); + std::string mode = str_or_default(ci.params, id_PERF_MD, "SPEED"); + boost::algorithm::to_upper(mode); + int perf_md; + double max_freq = 0.0; + if (mode == "LOWPOWER") { + perf_md = 1; + max_freq = 250.00; + } else if (mode == "ECONOMY") { + perf_md = 2; + max_freq = 312.50; + } else if (mode == "SPEED") { + perf_md = 3; + max_freq = 416.75; + } else { + log_error("Unknown PERF_MD parameter value '%s' for cell %s.\n", mode.c_str(), ci.name.c_str(ctx)); + } + + double ref_clk = double_or_default(ci.params, id_REF_CLK, 0.0); + if (ref_clk <= 0 || ref_clk > 125) + log_error("REF_CLK parameter is out of range (0,125.00].\n"); + + double out_clk = double_or_default(ci.params, id_OUT_CLK, 0.0); + if (out_clk <= 0 || out_clk > max_freq) + log_error("OUT_CLK parameter is out of range (0,%.2lf].\n", max_freq); + + if ((ci_const < 1) || (ci_const > 31)) { + log_warning("CI const out of range. Set to default CI = 2\n"); + ci_const = 2; + } + if ((cp_const < 1) || (cp_const > 31)) { + log_warning("CP const out of range. Set to default CP = 4\n"); + cp_const = 4; + } + // PLL_cfg_val_800_1400 PLL values from 11.08.2021 + bool feedback = false; + if (ci.getPort(id_CLK_FEEDBACK)) { + ci.params[ctx->id("CFG_A.FB_PATH")] = Property(0b1, 1); + feedback = true; + } + ci.params[ctx->id("CFG_A.FINE_TUNE")] = Property(0b00011001000, 11); + ci.params[ctx->id("CFG_A.COARSE_TUNE")] = Property(0b100, 3); + ci.params[ctx->id("CFG_A.AO_SW")] = Property(0b01000, 5); + ci.params[ctx->id("CFG_A.OPEN_LOOP")] = Property(0b0, 1); + ci.params[ctx->id("CFG_A.ENFORCE_LOCK")] = Property(0b0, 1); + ci.params[ctx->id("CFG_A.PFD_SEL")] = Property(0b0, 1); + ci.params[ctx->id("CFG_A.LOCK_DETECT_WIN")] = Property(0b0, 1); + ci.params[ctx->id("CFG_A.SYNC_BYPASS")] = Property(0b0, 1); + ci.params[ctx->id("CFG_A.FILTER_SHIFT")] = Property(0b10, 2); + ci.params[ctx->id("CFG_A.FAST_LOCK")] = Property(0b1, 1); + ci.params[ctx->id("CFG_A.SAR_LIMIT")] = Property(0b010, 3); + ci.params[ctx->id("CFG_A.OP_LOCK")] = Property(0b0, 1); + ci.params[ctx->id("CFG_A.PDIV0_MUX")] = Property(0b1, 1); + ci.params[ctx->id("CFG_A.EN_COARSE_TUNE")] = Property(0b1, 1); + ci.params[ctx->id("CFG_A.EN_USR_CFG")] = Property(0b0, 1); + ci.params[ctx->id("CFG_A.PLL_EN_SEL")] = Property(0b0, 1); + + ci.params[ctx->id("CFG_A.CI_FILTER_CONST")] = Property(ci_const, 5); + ci.params[ctx->id("CFG_A.CP_FILTER_CONST")] = Property(cp_const, 5); + /* + clock path selection + 0-0 PDIV0_MUX = 0, FB_PATH = 0 // DCO clock with intern feedback + 1-0 PDIV0_MUX = 1, FB_PATH = 0 // divided clock: PDIV1->M1->M2 with intern feedback DEFAULT + 0-1 not possible f_core = f_ref will set PDIV0_MUX = 1 + 1-1 PDIV0_MUX = 1, FB_PATH = 1 // divided clock: PDIV1->M1->M2 with extern feedback + PDIV1->M1->M2->PDIV0->N1->N2 } + */ + bool pdiv0_mux = true; + PllCfgRecord val = get_pll_settings(ref_clk, out_clk, perf_md, low_jitter, pdiv0_mux, feedback); + if (val.f_core > 0) { // cfg exists + ci.params[ctx->id("CFG_A.K")] = Property(val.K, 12); + ci.params[ctx->id("CFG_A.N1")] = Property(val.N1, 6); + ci.params[ctx->id("CFG_A.N2")] = Property(val.N2, 10); + ci.params[ctx->id("CFG_A.M1")] = Property(val.M1, 6); + ci.params[ctx->id("CFG_A.M2")] = Property(val.M2, 10); + ci.params[ctx->id("CFG_A.PDIV1_SEL")] = Property(val.PDIV1 == 2 ? 1 : 0, 1); + } else { + log_error("Unable to configure PLL %s\n", ci.name.c_str(ctx)); + } + // Remove all not propagated parameters + ci.unsetParam(id_PERF_MD); + ci.unsetParam(id_REF_CLK); + ci.unsetParam(id_OUT_CLK); + ci.unsetParam(id_LOW_JITTER); + ci.unsetParam(id_CI_FILTER_CONST); + ci.unsetParam(id_CP_FILTER_CONST); + out_clk_max = out_clk; + } else { + // Handling CC_PLL_ADV + for (int i = 0; i < 2; i++) { + char cfg = 'A' + i; + IdString id = i == 0 ? id_PLL_CFG_A : id_PLL_CFG_B; + ci.params[ctx->idf("CFG_%c.CI_FILTER_CONST", cfg)] = Property(extract_bits(ci.params, id, 0, 5), 5); + ci.params[ctx->idf("CFG_%c.CP_FILTER_CONST", cfg)] = Property(extract_bits(ci.params, id, 5, 5), 5); + ci.params[ctx->idf("CFG_%c.N1", cfg)] = Property(extract_bits(ci.params, id, 10, 6), 6); + ci.params[ctx->idf("CFG_%c.N2", cfg)] = Property(extract_bits(ci.params, id, 16, 10), 10); + ci.params[ctx->idf("CFG_%c.M1", cfg)] = Property(extract_bits(ci.params, id, 26, 6), 6); + ci.params[ctx->idf("CFG_%c.M2", cfg)] = Property(extract_bits(ci.params, id, 32, 10), 10); + ci.params[ctx->idf("CFG_%c.K", cfg)] = Property(extract_bits(ci.params, id, 42, 12), 12); + ci.params[ctx->idf("CFG_%c.FB_PATH", cfg)] = Property(extract_bits(ci.params, id, 54, 1), 1); + ci.params[ctx->idf("CFG_%c.FINE_TUNE", cfg)] = Property(extract_bits(ci.params, id, 55, 11), 11); + ci.params[ctx->idf("CFG_%c.COARSE_TUNE", cfg)] = Property(extract_bits(ci.params, id, 66, 3), 3); + ci.params[ctx->idf("CFG_%c.AO_SW", cfg)] = Property(extract_bits(ci.params, id, 69, 5), 5); + ci.params[ctx->idf("CFG_%c.OPEN_LOOP", cfg)] = Property(extract_bits(ci.params, id, 74, 1), 1); + ci.params[ctx->idf("CFG_%c.ENFORCE_LOCK", cfg)] = Property(extract_bits(ci.params, id, 75, 1), 1); + ci.params[ctx->idf("CFG_%c.PFD_SEL", cfg)] = Property(extract_bits(ci.params, id, 76, 1), 1); + ci.params[ctx->idf("CFG_%c.LOCK_DETECT_WIN", cfg)] = Property(extract_bits(ci.params, id, 77, 1), 1); + ci.params[ctx->idf("CFG_%c.SYNC_BYPASS", cfg)] = Property(extract_bits(ci.params, id, 78, 1), 1); + ci.params[ctx->idf("CFG_%c.FILTER_SHIFT", cfg)] = Property(extract_bits(ci.params, id, 79, 2), 2); + ci.params[ctx->idf("CFG_%c.FAST_LOCK", cfg)] = Property(extract_bits(ci.params, id, 81, 1), 1); + ci.params[ctx->idf("CFG_%c.SAR_LIMIT", cfg)] = Property(extract_bits(ci.params, id, 82, 3), 3); + ci.params[ctx->idf("CFG_%c.OP_LOCK", cfg)] = Property(extract_bits(ci.params, id, 85, 1), 1); + ci.params[ctx->idf("CFG_%c.PDIV1_SEL", cfg)] = Property(extract_bits(ci.params, id, 86, 1), 1); + ci.params[ctx->idf("CFG_%c.PDIV0_MUX", cfg)] = Property(extract_bits(ci.params, id, 87, 1), 1); + ci.params[ctx->idf("CFG_%c.EN_COARSE_TUNE", cfg)] = Property(extract_bits(ci.params, id, 88, 1), 1); + ci.params[ctx->idf("CFG_%c.EN_USR_CFG", cfg)] = Property(extract_bits(ci.params, id, 89, 1), 1); + ci.params[ctx->idf("CFG_%c.PLL_EN_SEL", cfg)] = Property(extract_bits(ci.params, id, 90, 1), 1); + int N1 = int_or_default(ci.params, ctx->idf("CFG_%c.N1", cfg)); + int N2 = int_or_default(ci.params, ctx->idf("CFG_%c.N2", cfg)); + int M1 = int_or_default(ci.params, ctx->idf("CFG_%c.M1", cfg)); + int M2 = int_or_default(ci.params, ctx->idf("CFG_%c.M2", cfg)); + int K = int_or_default(ci.params, ctx->idf("CFG_%c.K", cfg)); + int PDIV1 = bool_or_default(ci.params, ctx->idf("CFG_%c.PDIV1_SEL", cfg)) ? 2 : 0; + double out_clk; + double ref_clk = 1000.0f / ctx->getDelayNS(period); + if (!bool_or_default(ci.params, ctx->idf("CFG_%c.FB_PATH", cfg))) { + if (bool_or_default(ci.params, ctx->idf("CFG_%c.PDIV0_MUX", cfg))) { + out_clk = (ref_clk * N1 * N2) / (K * 2 * M1 * M2); + } else { + out_clk = (ref_clk / K) * N1 * N2 * PDIV1; + } + } else { + out_clk = (ref_clk / K) * N1 * N2; + } + if (out_clk > out_clk_max) + out_clk_max = out_clk; + } + NetInfo *select_net = ci.getPort(id_USR_SEL_A_B); + if (select_net == nullptr || select_net->name == ctx->id("$PACKER_GND")) { + ci.params[ctx->id("SET_SEL")] = Property(0b0, 1); + ci.params[ctx->id("USR_SET")] = Property(0b0, 1); + ci.disconnectPort(id_USR_SEL_A_B); + } else if (select_net->name == ctx->id("$PACKER_VCC")) { + ci.params[ctx->id("SET_SEL")] = Property(0b1, 1); + ci.params[ctx->id("USR_SET")] = Property(0b0, 1); + ci.disconnectPort(id_USR_SEL_A_B); + } else { + ci.params[ctx->id("USR_SET")] = Property(0b1, 1); + move_ram_o_fixed(&ci, id_USR_SEL_A_B, fixed_loc); + } + ci.params[ctx->id("LOCK_REQ")] = Property(0b1, 1); + ci.unsetParam(id_PLL_CFG_A); + ci.unsetParam(id_PLL_CFG_B); + if (!ci.getPort(id_CLK_FEEDBACK)) + ci.params[ctx->id("LOCK_REQ")] = Property(0b1, 1); + } + + // PLL control register A + ci.params[ctx->id("PLL_RST")] = Property(0b1, 1); + ci.params[ctx->id("PLL_EN")] = Property(0b1, 1); + // PLL_AUTN - for Autonomous Mode - not set + // SET_SEL - handled in CC_PLL_ADV + // USR_SET - handled in CC_PLL_ADV + // TODO: USR_CLK_REF - based on signals used + ci.params[ctx->id("CLK_OUT_EN")] = Property(0b1, 1); + // LOCK_REQ - set by CC_PLL parameter + + // PLL control register B + // AUTN_CT_I - for Autonomous Mode - not set + // CLK180_DOUB - set by CC_PLL parameter + // CLK270_DOUB - set by CC_PLL parameter + // bits 6 and 7 are unused + // TODO: USR_CLK_OUT - part of routing, mux from chipdb + + if (ci.getPort(id_CLK0)) + ctx->addClock(ci.getPort(id_CLK0)->name, out_clk_max); + if (ci.getPort(id_CLK90)) + ctx->addClock(ci.getPort(id_CLK90)->name, out_clk_max); + if (ci.getPort(id_CLK180)) + ctx->addClock(ci.getPort(id_CLK180)->name, clk180_doub ? out_clk_max * 2 : out_clk_max); + if (ci.getPort(id_CLK270)) + ctx->addClock(ci.getPort(id_CLK270)->name, clk270_doub ? out_clk_max * 2 : out_clk_max); + + ci.type = id_PLL; + + pll_index++; + } +} + +void GateMatePacker::pack_misc() +{ + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_USR_RSTN)) + continue; + ci.type = id_USR_RSTN; + ci.cluster = ci.name; + Loc fixed_loc(0, 0, 3); // USR_RSTN + ctx->bindBel(ctx->getBelByLocation(fixed_loc), &ci, PlaceStrength::STRENGTH_FIXED); + + move_ram_i_fixed(&ci, id_USR_RSTN, fixed_loc); + } + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_CFG_CTRL)) + continue; + ci.type = id_CFG_CTRL; + ci.cluster = ci.name; + Loc fixed_loc(0, 0, 2); // CFG_CTRL + ctx->bindBel(ctx->getBelByLocation(fixed_loc), &ci, PlaceStrength::STRENGTH_FIXED); + + move_ram_o_fixed(&ci, id_CLK, fixed_loc); + move_ram_o_fixed(&ci, id_EN, fixed_loc); + move_ram_o_fixed(&ci, id_VALID, fixed_loc); + move_ram_o_fixed(&ci, id_RECFG, fixed_loc); + for (int i = 0; i < 8; i++) + move_ram_o_fixed(&ci, ctx->idf("DATA[%d]", i), fixed_loc); + } + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_ODDR, id_CC_IDDR)) + continue; + log_error("Cell '%s' of type %s is not connected to GPIO pin.\n", ci.name.c_str(ctx), ci.type.c_str(ctx)); + } +} + +void GateMatePacker::pack_constants() +{ + log_info("Packing constants..\n"); + // Replace constants with LUTs + const dict vcc_params = {{id_INIT_L10, Property(0b1111, 4)}, {id_C_O, Property(0b11, 2)}}; + const dict gnd_params = {{id_INIT_L10, Property(0b0000, 4)}, {id_C_O, Property(0b11, 2)}}; + + h.replace_constants(CellTypePort(id_CPE_HALF, id_OUT), CellTypePort(id_CPE_HALF, id_OUT), vcc_params, gnd_params); +} + +void GateMatePacker::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 GateMatePacker::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); + } + } + } + } +} + +uint8_t GateMatePacker::ram_ctrl_signal(CellInfo *cell, IdString port, bool alt) +{ + NetInfo *net = cell->getPort(port); + if (net) { + if (net->name == ctx->id("$PACKER_GND")) { + cell->disconnectPort(port); + return 0b00000011; + } else if (net->name == ctx->id("$PACKER_VCC")) { + cell->disconnectPort(port); + return 0b00010011; + } else { + return alt ? 0b00000100 : 0b00000000; + } + } + return 0b00000011; +} + +uint8_t GateMatePacker::ram_clk_signal(CellInfo *cell, IdString port) +{ + NetInfo *clk_net = cell->getPort(port); + if (!global_signals.count(clk_net)) { + return 0b00000000; + } else { + int index = global_signals[clk_net]; + uint8_t val = 0; + switch (index) { + case 0: + val = 0b00100011; + break; + case 1: + val = 0b00110011; + break; + case 2: + val = 0b00000011; + break; + case 3: + val = 0b00010011; + break; + } + cell->disconnectPort(port); + return val; + } +} + +int width_to_config(int width) +{ + switch (width) { + case 0: + return 0; + case 1: + return 1; + case 2: + return 2; + case 3 ... 5: + return 3; + case 6 ... 10: + return 4; + case 11 ... 20: + return 5; + case 21 ... 40: + return 6; + case 41 ... 80: + return 7; + default: + log_error("Unsupported width '%d'.\n", width); + } +} + +static void rename_or_move(CellInfo *main, CellInfo *other, IdString port, IdString other_port) +{ + if (main == other) + main->renamePort(port, other_port); + else + main->movePortTo(port, other, other_port); +} + +void GateMatePacker::pack_ram_cell(CellInfo &ci, CellInfo *cell, int num, bool is_split) +{ + // Port Widths + int a_rd_width = int_or_default(cell->params, id_A_RD_WIDTH, 0); + int b_rd_width = int_or_default(cell->params, id_B_RD_WIDTH, 0); + int a_wr_width = int_or_default(cell->params, id_A_WR_WIDTH, 0); + int b_wr_width = int_or_default(cell->params, id_B_WR_WIDTH, 0); + + std::string a_wr_mode_str = str_or_default(cell->params, id_A_WR_MODE, "NO_CHANGE"); + if (a_wr_mode_str != "NO_CHANGE" && a_wr_mode_str != "WRITE_THROUGH") + log_error("Unknown A_WR_MODE parameter value '%s' for cell %s.\n", a_wr_mode_str.c_str(), + cell->name.c_str(ctx)); + int a_wr_mode = a_wr_mode_str == "NO_CHANGE" ? 0 : 1; + std::string b_wr_mode_str = str_or_default(cell->params, id_B_WR_MODE, "NO_CHANGE"); + if (b_wr_mode_str != "NO_CHANGE" && b_wr_mode_str != "WRITE_THROUGH") + log_error("Unknown B_WR_MODE parameter value '%s' for cell %s.\n", b_wr_mode_str.c_str(), + cell->name.c_str(ctx)); + int b_wr_mode = b_wr_mode_str == "NO_CHANGE" ? 0 : 1; + + // Inverting Control Pins + int a_clk_inv = int_or_default(cell->params, id_A_CLK_INV, 0); + int b_clk_inv = int_or_default(cell->params, id_B_CLK_INV, 0); + int a_en_inv = int_or_default(cell->params, id_A_EN_INV, 0); + int b_en_inv = int_or_default(cell->params, id_B_EN_INV, 0); + int a_we_inv = int_or_default(cell->params, id_A_WE_INV, 0); + int b_we_inv = int_or_default(cell->params, id_B_WE_INV, 0); + + // Output Register + int a_do_reg = int_or_default(cell->params, id_A_DO_REG, 0); + int b_do_reg = int_or_default(cell->params, id_B_DO_REG, 0); + + uint8_t cfg_a = ram_clk_signal(cell, id_A_CLK); + uint8_t cfg_b = ram_clk_signal(cell, id_B_CLK); + uint8_t a_inv = a_clk_inv << 2 | a_we_inv << 1 | a_en_inv; + uint8_t b_inv = b_clk_inv << 2 | b_we_inv << 1 | b_en_inv; + uint8_t a_en = ram_ctrl_signal(cell, id_A_EN, false); + uint8_t b_en = ram_ctrl_signal(cell, id_B_EN, false); + uint8_t a_we = ram_ctrl_signal(cell, id_A_WE, false); + uint8_t b_we = ram_ctrl_signal(cell, id_B_WE, false); + + if (num) { + ci.params[id_RAM_cfg_forward_a1_clk] = Property(cfg_a, 8); + ci.params[id_RAM_cfg_forward_b1_clk] = Property(cfg_b, 8); + + ci.params[id_RAM_cfg_forward_a1_en] = Property(a_en, 8); + ci.params[id_RAM_cfg_forward_b1_en] = Property(b_en, 8); + + ci.params[id_RAM_cfg_forward_a1_we] = Property(a_we, 8); + ci.params[id_RAM_cfg_forward_b1_we] = Property(b_we, 8); + + ci.params[id_RAM_cfg_input_config_a1] = Property(width_to_config(a_wr_width), 3); + ci.params[id_RAM_cfg_input_config_b1] = Property(width_to_config(b_wr_width), 3); + ci.params[id_RAM_cfg_output_config_a1] = Property(width_to_config(a_rd_width), 3); + ci.params[id_RAM_cfg_output_config_b1] = Property(width_to_config(b_rd_width), 3); + + ci.params[id_RAM_cfg_a1_writemode] = Property(a_wr_mode, 1); + ci.params[id_RAM_cfg_b1_writemode] = Property(b_wr_mode, 1); + + ci.params[id_RAM_cfg_a1_set_outputreg] = Property(a_do_reg, 1); + ci.params[id_RAM_cfg_b1_set_outputreg] = Property(b_do_reg, 1); + + ci.params[id_RAM_cfg_inversion_a1] = Property(a_inv, 3); + ci.params[id_RAM_cfg_inversion_b1] = Property(b_inv, 3); + } else { + ci.params[id_RAM_cfg_forward_a0_clk] = Property(cfg_a, 8); + if (!is_split) + ci.params[id_RAM_cfg_forward_a1_clk] = Property(cfg_a, 8); + + ci.params[id_RAM_cfg_forward_b0_clk] = Property(cfg_b, 8); + if (!is_split) + ci.params[id_RAM_cfg_forward_b1_clk] = Property(cfg_b, 8); + + ci.params[id_RAM_cfg_forward_a0_en] = Property(a_en, 8); + ci.params[id_RAM_cfg_forward_b0_en] = Property(b_en, 8); + + ci.params[id_RAM_cfg_forward_a0_we] = Property(a_we, 8); + ci.params[id_RAM_cfg_forward_b0_we] = Property(b_we, 8); + + ci.params[id_RAM_cfg_input_config_a0] = Property(width_to_config(a_wr_width), 3); + ci.params[id_RAM_cfg_input_config_b0] = Property(width_to_config(b_wr_width), 3); + ci.params[id_RAM_cfg_output_config_a0] = Property(width_to_config(a_rd_width), 3); + ci.params[id_RAM_cfg_output_config_b0] = Property(width_to_config(b_rd_width), 3); + + ci.params[id_RAM_cfg_a0_writemode] = Property(a_wr_mode, 1); + ci.params[id_RAM_cfg_b0_writemode] = Property(b_wr_mode, 1); + + ci.params[id_RAM_cfg_a0_set_outputreg] = Property(a_do_reg, 1); + ci.params[id_RAM_cfg_b0_set_outputreg] = Property(b_do_reg, 1); + + ci.params[id_RAM_cfg_inversion_a0] = Property(a_inv, 3); + ci.params[id_RAM_cfg_inversion_b0] = Property(b_inv, 3); + } + + int index = (num == 0) ? 0 : 2; + rename_or_move(cell, &ci, id_A_CLK, ctx->idf("CLKA[%d]", index)); + rename_or_move(cell, &ci, id_B_CLK, ctx->idf("CLKB[%d]", index)); + rename_or_move(cell, &ci, id_A_EN, ctx->idf("ENA[%d]", index)); + rename_or_move(cell, &ci, id_B_EN, ctx->idf("ENB[%d]", index)); + rename_or_move(cell, &ci, id_A_WE, ctx->idf("GLWEA[%d]", index)); + rename_or_move(cell, &ci, id_B_WE, ctx->idf("GLWEB[%d]", index)); + int items = is_split ? 20 : 40; + for (int i = 0; i < items; i++) { + rename_or_move(cell, &ci, ctx->idf("A_BM[%d]", i), ctx->idf("WEA[%d]", i + num * 20)); + rename_or_move(cell, &ci, ctx->idf("B_BM[%d]", i), ctx->idf("WEB[%d]", i + num * 20)); + } + + for (int i = 0; i < 16; i++) { + rename_or_move(cell, &ci, ctx->idf("A_ADDR[%d]", i), ctx->idf("ADDRA%d[%d]", num, i)); + rename_or_move(cell, &ci, ctx->idf("B_ADDR[%d]", i), ctx->idf("ADDRB%d[%d]", num, i)); + } + + for (int i = 0; i < items; i++) { + rename_or_move(cell, &ci, ctx->idf("A_DI[%d]", i), ctx->idf("DIA[%d]", i + num * 20)); + rename_or_move(cell, &ci, ctx->idf("A_DO[%d]", i), ctx->idf("DOA[%d]", i + num * 20)); + rename_or_move(cell, &ci, ctx->idf("B_DI[%d]", i), ctx->idf("DIB[%d]", i + num * 20)); + rename_or_move(cell, &ci, ctx->idf("B_DO[%d]", i), ctx->idf("DOB[%d]", i + num * 20)); + } +} + +void GateMatePacker::pack_ram() +{ + std::vector> rams; + std::vector> rams_merged[2]; + for (auto &cell : ctx->cells) { + CellInfo &ci = *cell.second; + if (!ci.type.in(id_CC_BRAM_20K, id_CC_BRAM_40K, id_CC_FIFO_40K)) + continue; + int split = ci.type.in(id_CC_BRAM_20K) ? 1 : 0; + std::string ram_mode_str = str_or_default(ci.params, id_RAM_MODE, "SDP"); + if (ram_mode_str != "SDP" && ram_mode_str != "TDP") + log_error("Unknown RAM_MODE parameter value '%s' for cell %s.\n", ram_mode_str.c_str(), ci.name.c_str(ctx)); + int ram_mode = ram_mode_str == "SDP" ? 1 : 0; + if (split) { + bool added = false; + if (!rams_merged[ram_mode].empty()) { + auto &last = rams_merged[ram_mode].back(); + if (last.second == nullptr) { + last.second = &ci; + packed_cells.insert(ci.name); + added = true; + } + } + if (!added) + rams_merged[ram_mode].push_back(std::make_pair(&ci, nullptr)); + } else { + rams.push_back(std::make_pair(&ci, nullptr)); + } + } + rams.insert(rams.end(), rams_merged[0].begin(), rams_merged[0].end()); + rams.insert(rams.end(), rams_merged[1].begin(), rams_merged[1].end()); + + for (auto item : rams) { + CellInfo &ci = *item.first; + int split = ci.type.in(id_CC_BRAM_20K) ? 1 : 0; + bool is_fifo = ci.type.in(id_CC_FIFO_40K); + + ci.type = id_RAM; + ci.cluster = ci.name; + + // Location format: D(0..N-1)X(0..3)Y(0..7) or UNPLACED + std::string loc = str_or_default(ci.params, id_LOC, "UNPLACED"); + std::string cas = str_or_default(ci.params, id_CAS, "NONE"); + + int cascade = 0; + if (cas == "NONE") { + cascade = 0; + } else if (cas == "UPPER") { + cascade = 1; + } else if (cas == "LOWER") { + cascade = 2; + } else { + log_error("Unknown CAS parameter value '%s' for cell %s.\n", cas.c_str(), ci.name.c_str(ctx)); + } + + // RAM and Write Modes + std::string ram_mode_str = str_or_default(ci.params, id_RAM_MODE, "SDP"); + if (ram_mode_str != "SDP" && ram_mode_str != "TDP") + log_error("Unknown RAM_MODE parameter value '%s' for cell %s.\n", ram_mode_str.c_str(), ci.name.c_str(ctx)); + int ram_mode = ram_mode_str == "SDP" ? 1 : 0; + + // Error Checking and Correction + int a_ecc_en = int_or_default(ci.params, id_A_ECC_EN, 0); + int b_ecc_en = int_or_default(ci.params, id_B_ECC_EN, 0); + + ci.params[id_RAM_cfg_forward_a_addr] = Property(0b00000000, 8); + ci.params[id_RAM_cfg_forward_b_addr] = Property(0b00000000, 8); + + ci.params[id_RAM_cfg_sram_mode] = Property(ram_mode << 1 | split, 2); + + pack_ram_cell(ci, item.first, 0, split); + if (item.second) { + pack_ram_cell(ci, item.second, 1, split); + } + if (split) { + for (int i = 63; i >= 0; i--) { + std::vector orig_first = + item.first->params.at(ctx->idf("INIT_%02X", i)).extract(0, 320).as_bits(); + std::vector orig_second; + if (item.second) + orig_second = item.second->params.at(ctx->idf("INIT_%02X", i)).extract(0, 320).as_bits(); + std::string init[2]; + + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 4; k++) { + for (int l = 0; l < 40; l++) { + if (item.second) + init[j].push_back(orig_second.at(319 - (l + k * 40 + j * 160)) ? '1' : '0'); + else + init[j].push_back('0'); + } + for (int l = 0; l < 40; l++) { + init[j].push_back(orig_first.at(319 - (l + k * 40 + j * 160)) ? '1' : '0'); + } + } + } + ci.params[ctx->idf("INIT_%02X", i * 2 + 1)] = Property::from_string(init[0]); + ci.params[ctx->idf("INIT_%02X", i * 2 + 0)] = Property::from_string(init[1]); + } + } + + if (is_fifo) { + int a_rd_width = int_or_default(ci.params, id_A_WIDTH, 0); + int b_wr_width = int_or_default(ci.params, id_B_WIDTH, 0); + if (a_rd_width != b_wr_width) + log_error("The FIFO configuration of A_WIDTH and B_WIDTH must be equal.\n"); + + if (a_rd_width != 80 && ram_mode == 1) + log_error("FIFO SDP is ony supported in 80 bit mode.\n"); + + ci.params[id_RAM_cfg_input_config_b0] = Property(width_to_config(b_wr_width), 3); + ci.params[id_RAM_cfg_output_config_a0] = Property(width_to_config(a_rd_width), 3); + + std::string fifo_mode_str = str_or_default(ci.params, id_FIFO_MODE, "SYNC"); + if (fifo_mode_str != "SYNC" && fifo_mode_str != "ASYNC") + log_error("Unknown FIFO_MODE parameter value '%s' for cell %s.\n", fifo_mode_str.c_str(), + ci.name.c_str(ctx)); + int fifo_mode = fifo_mode_str == "SYNC" ? 1 : 0; + + if (fifo_mode) + ci.params[id_RAM_cfg_fifo_sync_enable] = Property(0b1, 1); + else + ci.params[id_RAM_cfg_fifo_async_enable] = Property(0b1, 1); + + // TODO: Handle dynamic almost empty/full + int dyn_stat_select = int_or_default(ci.params, id_DYN_STAT_SELECT, 0); + if (dyn_stat_select != 0 && dyn_stat_select != 1) + log_error("DYN_STAT_SELECT must be 0 or 1.\n"); + if (dyn_stat_select != 0 && ram_mode == 1) + log_error("Dynamic FIFO offset configuration is not supported in SDP mode.\n"); + ci.params[id_RAM_cfg_dyn_stat_select] = Property(dyn_stat_select << 1, 2); + ci.params[id_RAM_cfg_almost_empty_offset] = + Property(int_or_default(ci.params, id_F_ALMOST_EMPTY_OFFSET, 0), 15); + ci.params[id_RAM_cfg_almost_full_offset] = + Property(int_or_default(ci.params, id_F_ALMOST_FULL_OFFSET, 0), 15); + } + + ci.params[id_RAM_cfg_ecc_enable] = Property(b_ecc_en << 1 | a_ecc_en, 2); + ci.params[id_RAM_cfg_sram_delay] = Property(0b000101, 6); // Always set to default + // id_RAM_cfg_datbm_sel + ci.params[id_RAM_cfg_cascade_enable] = Property(cascade, 2); + + for (int i = 0; i < 40; i++) { + move_ram_o(&ci, ctx->idf("WEA[%d]", i)); + move_ram_o(&ci, ctx->idf("WEB[%d]", i)); + } + + for (int i = 0; i < 16; i++) { + move_ram_o(&ci, ctx->idf("ADDRA0[%d]", i)); + move_ram_o(&ci, ctx->idf("ADDRB0[%d]", i)); + move_ram_o(&ci, ctx->idf("ADDRA1[%d]", i)); + move_ram_o(&ci, ctx->idf("ADDRB1[%d]", i)); + } + + for (int i = 0; i < 40; i++) { + move_ram_io(&ci, ctx->idf("DOA[%d]", i), ctx->idf("DIA[%d]", i)); + move_ram_io(&ci, ctx->idf("DOB[%d]", i), ctx->idf("DIB[%d]", i)); + } + for (int i = 0; i < 4; i++) { + move_ram_o(&ci, ctx->idf("CLKA[%d]", i)); + move_ram_o(&ci, ctx->idf("CLKB[%d]", i)); + move_ram_o(&ci, ctx->idf("ENA[%d]", i)); + move_ram_o(&ci, ctx->idf("ENB[%d]", i)); + move_ram_o(&ci, ctx->idf("GLWEA[%d]", i)); + move_ram_o(&ci, ctx->idf("GLWEB[%d]", i)); + } + + if (is_fifo) { + for (int i = 0; i < 15; i++) { + ci.disconnectPort(ctx->idf("F_ALMOST_EMPTY_OFFSET[%d]", i)); + ci.disconnectPort(ctx->idf("F_ALMOST_FULL_OFFSET[%d]", i)); + } + ci.renamePort(id_F_EMPTY, ctx->id("F_EMPTY[0]")); + move_ram_i(&ci, ctx->id("F_EMPTY[0]")); + ci.renamePort(id_F_FULL, ctx->id("F_FULL[0]")); + move_ram_i(&ci, ctx->id("F_FULL[0]")); + ci.renamePort(id_F_ALMOST_FULL, ctx->id("F_AL_FULL[0]")); + move_ram_i(&ci, ctx->id("F_AL_FULL[0]")); + ci.renamePort(id_F_ALMOST_EMPTY, ctx->id("F_AL_EMPTY[0]")); + move_ram_i(&ci, ctx->id("F_AL_EMPTY[0]")); + + ci.renamePort(id_F_WR_ERROR, ctx->id("FWR_ERR[0]")); + move_ram_i(&ci, ctx->id("FWR_ERR[0]")); + ci.renamePort(id_F_RD_ERROR, ctx->id("FRD_ERR[0]")); + move_ram_i(&ci, ctx->id("FRD_ERR[0]")); + + ci.renamePort(id_F_RST_N, ctx->id("F_RSTN")); + move_ram_o(&ci, ctx->id("F_RSTN")); + } + } + flush_cells(); +} + +void GateMateImpl::pack() +{ + const ArchArgs &args = ctx->args; + if (args.options.count("ccf")) { + parse_ccf(args.options.at("ccf")); + } + + GateMatePacker packer(ctx, this); + packer.pack_constants(); + packer.remove_not_used(); + packer.pack_io(); + packer.insert_pll_bufg(); + packer.sort_bufg(); + packer.pack_pll(); + packer.pack_bufg(); + packer.pack_io_sel(); // merge in FF and DDR + packer.pack_misc(); + packer.pack_ram(); + packer.pack_addf(); + packer.pack_cpe(); + packer.remove_constants(); +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/pack.h b/himbaechel/uarch/gatemate/pack.h new file mode 100644 index 00000000..0d4b2a6e --- /dev/null +++ b/himbaechel/uarch/gatemate/pack.h @@ -0,0 +1,78 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 GATEMATE_PACK_H +#define GATEMATE_PACK_H + +#include "gatemate.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct GateMatePacker +{ + GateMatePacker(Context *ctx, GateMateImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); }; + + void pack_io(); + void pack_io_sel(); + void pack_cpe(); + void pack_addf(); + void pack_bufg(); + void sort_bufg(); + void insert_pll_bufg(); + void pack_pll(); + void pack_misc(); + void pack_constants(); + void pack_ram(); + + void remove_constants(); + void remove_not_used(); + + private: + void dff_to_cpe(CellInfo *dff, CellInfo *cpe); + void insert_bufg(CellInfo *cell, IdString port); + void disconnect_if_gnd(CellInfo *cell, IdString input); + void pll_out(CellInfo *cell, IdString origPort, Loc fixed); + + PllCfgRecord get_pll_settings(double f_ref, double f_core, int mode, int low_jitter, bool pdiv0_mux, bool feedback); + + CellInfo *move_ram_i(CellInfo *cell, IdString origPort, bool place = true); + CellInfo *move_ram_o(CellInfo *cell, IdString origPort, bool place = true); + CellInfo *move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed); + CellInfo *move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed); + CellInfo *move_ram_io(CellInfo *cell, IdString iPort, IdString oPort, bool place = true); + uint8_t ram_ctrl_signal(CellInfo *cell, IdString port, bool alt); + uint8_t ram_clk_signal(CellInfo *cell, IdString port); + bool is_gpio_valid_dff(CellInfo *dff); + BelId get_bank_cpe(int bank); + // Cell creating + CellInfo *create_cell_ptr(IdString type, IdString name); + void flush_cells(); + void pack_ram_cell(CellInfo &ci, CellInfo *cell, int num, bool is_split); + + pool packed_cells; + std::map global_signals; + + Context *ctx; + GateMateImpl *uarch; + + HimbaechelHelpers h; +}; + +NEXTPNR_NAMESPACE_END +#endif diff --git a/himbaechel/uarch/gatemate/pll.cc b/himbaechel/uarch/gatemate/pll.cc new file mode 100644 index 00000000..5077b698 --- /dev/null +++ b/himbaechel/uarch/gatemate/pll.cc @@ -0,0 +1,352 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 "pack.h" + +#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" +#include "himbaechel_constids.h" + +namespace { +USING_NEXTPNR_NAMESPACE; + +double calculate_delta_stepsize(double num) +{ + std::ostringstream out; + out << std::fixed << std::setprecision(4) << num; + std::string str = out.str(); + switch (4 - (str.size() - str.find_last_not_of('0') - 1)) { + case 4: + return 0.0001; + case 3: + return 0.001; + case 2: + return 0.01; + case 1: + return 0.1; + default: + return 1.0; + } +} + +int getDCO_optimized_value(int f_dco_min, int f_dco_max, double f_dco) +{ + /* + optimization parameters + DCO_OPT: + 1: Optimized to lower DCO frequency. + 2: Optimized to the middle of the DCO range (DEFAULT) + 3: Optimized to upper DCO frequency. + */ + const int DCO_OPT = 2; // 1: to lower DCO, 2: to DCO middle range, 3: to upper DCO, default = 2 + const int DCO_FREQ_OPT_FACTOR = 24; // proportional factor btw. frequency match and dco optimization + + switch (DCO_OPT) { + case 1: + return round(abs(f_dco - f_dco_min)) * DCO_FREQ_OPT_FACTOR; + case 3: + return round(abs(f_dco - f_dco_max)) * DCO_FREQ_OPT_FACTOR; + default: { + int f_dco_middle = (f_dco_max + f_dco_min) / 2; // Middle of DCO Range in MHz + return round(abs(f_dco - f_dco_middle)) * DCO_FREQ_OPT_FACTOR; + } + } +} + +void get_M1_M2(double f_core, double f_core_delta, PllCfgRecord &setting, double max_input_freq) +{ + // precise output frequency preference, highest priority + // the larger value the higher priority + const int OUT_FREQ_OPT_FACTOR = 4200000; + + std::vector res_arr; + for (int M1 = 1; M1 <= 64; M1++) { + for (int M2 = 1; M2 <= 1024; M2++) { + if (((M1 * M2) % 2) != 0) { + // M1*M2-->even number(except one); + // this is important to ensure 90 degree phase shifting of clock output + if (M1 * M2 != 1) + continue; + } + + double f_core_local = setting.f_dco / (2 * setting.PDIV1 * M1 * M2); + if (f_core_local > max_input_freq / 4) + continue; // f_core max limit + if ((f_core_local - f_core) < -f_core_delta) + break; // lower limit jump to parent loop + if (abs(f_core_local - f_core) > f_core_delta) + continue; // reg limit continue + if (setting.f_dco / setting.PDIV1 > max_input_freq) + continue; // M1 input freq limit + if ((setting.f_dco / setting.PDIV1) / M1 > max_input_freq / 2) + continue; // M2 input freq limit + + PllCfgRecord tmp; + tmp.f_core = f_core_local; + tmp.M1 = M1; + tmp.M2 = M2; + tmp.weight = abs(f_core_local - f_core) * OUT_FREQ_OPT_FACTOR; + res_arr.push_back(tmp); + + // frequency match + if (abs(f_core_local - f_core) <= f_core_delta) { + auto it = std::min_element( + res_arr.begin(), res_arr.end(), + [](const PllCfgRecord &a, const PllCfgRecord &b) { return a.weight < b.weight; }); + size_t index = std::distance(res_arr.begin(), it); + setting.M1 = res_arr[index].M1; + setting.M2 = res_arr[index].M2; + setting.f_core = res_arr[index].f_core; + setting.f_core_delta = abs(res_arr[index].f_core - f_core); + setting.core_weight = res_arr[index].weight + setting.M1 + setting.M2 + setting.N1 + setting.N2 + + setting.K + setting.PDIV1; + return; + } + } + } +} + +void get_DCO_ext_feedback(double f_core, double f_ref, PllCfgRecord &setting, int f_dco_min, int f_dco_max, + double f_dco, double max_input_freq) +{ + std::vector res_arr; + + for (int M1 = 1; M1 <= 64; M1++) { + for (int M2 = 1; M2 <= 1024; M2++) { + if (((M1 * M2) % 2) != 0) { + // M1*M2-->even number(except one); + // this is important to ensure 90 degree phase shifting of clock output + if (M1 * M2 != 1) + continue; + } + double f_dco_local = (f_ref / setting.K) * 2 * setting.PDIV1 * setting.N1 * setting.N2 * M1 * M2; + if (f_dco_local > f_dco_max) + break; // upper limit + if ((f_dco_local < f_dco_min) || (f_dco_local > f_dco_max)) + continue; // dco out of range limit + if (f_dco_local / setting.PDIV1 > max_input_freq) + continue; // M1 input freq limit + if ((f_dco_local / setting.PDIV1) / M1 > max_input_freq / 2) + continue; // M2 input freq limit + + PllCfgRecord tmp; + tmp.f_dco = f_dco_local; + tmp.M1 = M1; + tmp.M2 = M2; + tmp.weight = f_dco_local + getDCO_optimized_value(f_dco_min, f_dco_max, f_dco_local); + res_arr.push_back(tmp); + } + } + + auto it = std::min_element(res_arr.begin(), res_arr.end(), + [](const PllCfgRecord &a, const PllCfgRecord &b) { return a.weight < b.weight; }); + size_t index = std::distance(res_arr.begin(), it); + setting.M1 = res_arr[index].M1; + setting.M2 = res_arr[index].M2; + setting.f_dco = res_arr[index].f_dco; +} +}; // namespace + +NEXTPNR_NAMESPACE_BEGIN + +PllCfgRecord GateMatePacker::get_pll_settings(double f_ref, double f_core, int mode, int low_jitter, bool pdiv0_mux, + bool feedback) +{ + const int MATCH_LIMIT = 10; // only for low jitter = false + // frequency tolerance for low jitter = false, the maximum frequency deviation in MHz from f_core + const double DELTA_LIMIT = 0.1; + + double max_input_freq; + int f_dco_min, f_dco_max, pdiv1_min; + double f_dco; + std::vector pll_cfg_arr; + PllCfgRecord pll_cfg_rec; + + switch (mode) { + case 1: { + // low power + f_dco_min = 500; + f_dco_max = 1000; + pdiv1_min = 1; + max_input_freq = 1000; + break; + } + case 2: { + // economy + f_dco_min = 1000; + f_dco_max = 2000; + pdiv1_min = 2; + max_input_freq = 1250; + break; + } + default: { + // initialize as speed + f_dco_min = 1250; + f_dco_max = 2500; + pdiv1_min = 2; + max_input_freq = 1666.67; + break; + } + } + double f_core_par = f_core; + + if (f_ref > 50) + log_warning("The PLL input frequency is outside the specified frequency (max 50 MHz ) range\n"); + + if (pdiv0_mux && feedback) { + double res; + if (modf(f_core / f_ref, &res) != 0) + log_warning("In this PLL mode f_core can only be greater and multiple of f_ref\n"); + } + + if (pdiv0_mux) { + if (f_core > max_input_freq / 4) { + f_core = max_input_freq / 4; + log_warning("Frequency out of range; PLL max output frequency for mode: %d: %.5f MHz\n", mode, + max_input_freq / 4); + } + } + + pll_cfg_rec.K = 1; + pll_cfg_rec.N1 = 1; + pll_cfg_rec.N2 = 1; + pll_cfg_rec.M1 = 1; + pll_cfg_rec.M2 = 1; + pll_cfg_rec.PDIV1 = 2; + pll_cfg_rec.f_dco = 0; + pll_cfg_rec.f_core_delta = std::numeric_limits::max(); + pll_cfg_rec.f_core = pll_cfg_rec.f_dco / (2 * pll_cfg_rec.PDIV1); + pll_cfg_rec.core_weight = std::numeric_limits::max(); + pll_cfg_arr.push_back(pll_cfg_rec); + + double f_core_delta_stepsize = calculate_delta_stepsize(f_core); + + int K = 1; + int K_max = low_jitter ? 1 : 1024; + int match_cnt = 0; + int match_delta_cnt = 0; + while (K <= K_max) { + for (int N1 = 1; N1 <= 64; N1++) { + for (int N2 = 1; N2 <= 1024; N2++) { + for (int PDIV1 = pdiv1_min; PDIV1 <= 2; PDIV1++) { + if (feedback) { + // extern feedback + int f_core_local = (f_ref / K) * N1 * N2; + if (f_core_local > max_input_freq / 4) + continue; + if (abs(f_core - f_core_local) < pll_cfg_arr[0].f_core_delta) { + // search for best frequency match + pll_cfg_arr[0].f_core = f_core_local; + pll_cfg_arr[0].f_core_delta = abs(f_core - f_core_local); + pll_cfg_arr[0].K = K; + pll_cfg_arr[0].N1 = N1; + pll_cfg_arr[0].N2 = N2; + pll_cfg_arr[0].PDIV1 = PDIV1; + } + if (pll_cfg_arr[0].f_core_delta == 0) { + get_DCO_ext_feedback(f_core, f_ref, pll_cfg_arr[0], f_dco_min, f_dco_max, f_dco, + max_input_freq); + log_info("PLL fout= %.4f MHz (fout error %.5f%% of requested %.4f MHz)\n", + pll_cfg_arr[0].f_core, + 100 - (100 * std::min(pll_cfg_arr[0].f_core, f_core_par) / + std::max(pll_cfg_arr[0].f_core, f_core_par)), + f_core); + return pll_cfg_arr[0]; + } + } else { + f_dco = (f_ref / K) * PDIV1 * N1 * N2; + + if ((f_dco <= f_dco_min) || (f_dco > f_dco_max)) + continue; // DCO out of range + if (f_dco == 0) + continue; // DCO = 0 + if (f_dco / PDIV1 > max_input_freq) + continue; // N1 input freq limit + if ((f_dco / PDIV1) / N1 > max_input_freq / 2) + continue; // N2 input freq limit + if (int(f_core) > int(f_dco / (2 * PDIV1))) + continue; // > f_core max + + pll_cfg_rec.f_core = 0; + pll_cfg_rec.f_dco = f_dco; + pll_cfg_rec.f_core_delta = std::numeric_limits::max(); + pll_cfg_rec.K = K; + pll_cfg_rec.N1 = N1; + pll_cfg_rec.N2 = N2; + pll_cfg_rec.PDIV1 = PDIV1; + pll_cfg_rec.M1 = 0; + pll_cfg_rec.M2 = 0; + pll_cfg_rec.core_weight = std::numeric_limits::max(); + + if (!pdiv0_mux) { + // f_core = f_dco/2 + pll_cfg_rec.M1 = 1; + pll_cfg_rec.M2 = 1; + pll_cfg_rec.f_core = pll_cfg_rec.f_dco / 2; + pll_cfg_rec.core_weight = abs(pll_cfg_rec.f_core - f_core); + } else { + // default clock path + // calculate M1, M2 then calculate weight for intern loop feedback + bool found = false; + double f_core_delta = 0; + while (f_core_delta < (round((max_input_freq / 4) - f_ref / K))) { + get_M1_M2(f_core, f_core_delta, pll_cfg_rec, max_input_freq); + if (pll_cfg_rec.f_core != 0 && pll_cfg_rec.f_core_delta <= f_core_delta) { + // best result + pll_cfg_rec.core_weight += + pll_cfg_rec.f_dco + getDCO_optimized_value(f_dco_min, f_dco_max, f_dco); + found = true; + } + if (found) + break; // best result found + f_core_delta += f_core_delta_stepsize; + } + } + pll_cfg_arr.push_back(pll_cfg_rec); + if (pll_cfg_rec.f_core_delta == 0) + match_cnt++; + if (pll_cfg_rec.f_core_delta < DELTA_LIMIT) + match_delta_cnt++; + } + } + } + } + K++; + // only with low_jitter false + if (match_cnt > MATCH_LIMIT) + break; + if ((match_cnt == 0) && (match_delta_cnt > MATCH_LIMIT)) + break; + } + if (feedback) { + // extern feedback if not exact match pick the best here + get_DCO_ext_feedback(f_core, f_ref, pll_cfg_arr[0], f_dco_min, f_dco_max, f_dco, max_input_freq); + } + auto it = + std::min_element(pll_cfg_arr.begin(), pll_cfg_arr.end(), [](const PllCfgRecord &a, const PllCfgRecord &b) { + return a.core_weight < b.core_weight; + }); + PllCfgRecord val = pll_cfg_arr.at(std::distance(pll_cfg_arr.begin(), it)); + log_info("PLL fout= %.4f MHz (fout error %.5f%% of requested %.4f MHz)\n", val.f_core, + 100 - (100 * std::min(val.f_core, f_core_par) / std::max(val.f_core, f_core_par)), f_core); + return val; +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/tests/lut.cc b/himbaechel/uarch/gatemate/tests/lut.cc new file mode 100644 index 00000000..3c7b5f62 --- /dev/null +++ b/himbaechel/uarch/gatemate/tests/lut.cc @@ -0,0 +1,36 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 "testing.h" +#include "uarch/gatemate/pack.h" +#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" +#include "himbaechel_constids.h" + +USING_NEXTPNR_NAMESPACE + +TEST_F(GateMateTest, pack_constants) +{ + GateMatePacker packer(ctx, impl); + packer.pack_constants(); + ASSERT_EQ(ctx->cells.size(), 2LU); + packer.remove_constants(); + ASSERT_EQ(ctx->cells.size(), 0LU); +} diff --git a/himbaechel/uarch/gatemate/tests/main.cc b/himbaechel/uarch/gatemate/tests/main.cc new file mode 100644 index 00000000..46d4bdaa --- /dev/null +++ b/himbaechel/uarch/gatemate/tests/main.cc @@ -0,0 +1,32 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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(); +} diff --git a/himbaechel/uarch/gatemate/tests/testing.cc b/himbaechel/uarch/gatemate/tests/testing.cc new file mode 100644 index 00000000..b66ffacd --- /dev/null +++ b/himbaechel/uarch/gatemate/tests/testing.cc @@ -0,0 +1,210 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 "testing.h" +#include +#include "command.h" +#include "uarch/gatemate/pack.h" +#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +void GateMateTest::SetUp() +{ + init_share_dirname(); + chipArgs.device = "CCGM1A1"; + ctx = new Context(chipArgs); + ctx->uarch->init(ctx); + ctx->late_init(); + impl = (GateMateImpl *)(ctx->uarch.get()); +} + +void GateMateTest::TearDown() { delete ctx; } + +CellInfo *GateMateTest::create_cell_ptr(IdString type, std::string name) +{ + CellInfo *cell = ctx->createCell(ctx->id(name), type); + + auto add_port = [&](const IdString id, PortType dir) { + cell->ports[id].name = id; + cell->ports[id].type = dir; + }; + switch (type.index) { + case id_CC_IBUF.index: + add_port(id_I, PORT_IN); + add_port(id_Y, PORT_OUT); + break; + case id_CC_OBUF.index: + add_port(id_A, PORT_IN); + add_port(id_O, PORT_OUT); + break; + case id_CC_TOBUF.index: + add_port(id_A, PORT_IN); + add_port(id_T, PORT_IN); + add_port(id_O, PORT_OUT); + break; + case id_CC_IOBUF.index: + add_port(id_A, PORT_IN); + add_port(id_T, PORT_IN); + add_port(id_Y, PORT_OUT); + add_port(id_IO, PORT_INOUT); + break; + case id_CC_LVDS_IBUF.index: + add_port(id_I_P, PORT_IN); + add_port(id_I_N, PORT_IN); + add_port(id_Y, PORT_OUT); + break; + case id_CC_LVDS_OBUF.index: + add_port(id_A, PORT_IN); + add_port(id_O_P, PORT_OUT); + add_port(id_O_N, PORT_OUT); + break; + case id_CC_LVDS_TOBUF.index: + add_port(id_A, PORT_IN); + add_port(id_T, PORT_IN); + add_port(id_O_P, PORT_OUT); + add_port(id_O_N, PORT_OUT); + break; + case id_CC_LVDS_IOBUF.index: + add_port(id_A, PORT_IN); + add_port(id_T, PORT_IN); + add_port(id_Y, PORT_OUT); + add_port(id_IO_P, PORT_INOUT); + add_port(id_IO_N, PORT_INOUT); + break; + case id_CC_IDDR.index: + add_port(id_D, PORT_IN); + add_port(id_CLK, PORT_IN); + add_port(id_Q0, PORT_OUT); + add_port(id_Q1, PORT_OUT); + break; + case id_CC_ODDR.index: + add_port(id_D0, PORT_IN); + add_port(id_D1, PORT_IN); + add_port(id_CLK, PORT_IN); + add_port(id_DDR, PORT_IN); + add_port(id_Q, PORT_OUT); + break; + case id_CC_DFF.index: + add_port(id_D, PORT_IN); + add_port(id_CLK, PORT_IN); + add_port(id_EN, PORT_IN); + add_port(id_SR, PORT_IN); + add_port(id_Q, PORT_OUT); + break; + case id_CC_DLT.index: + add_port(id_D, PORT_IN); + add_port(id_G, PORT_IN); + add_port(id_SR, PORT_IN); + add_port(id_Q, PORT_OUT); + break; + case id_CC_L2T4.index: + add_port(id_I0, PORT_IN); + add_port(id_I1, PORT_IN); + add_port(id_I2, PORT_IN); + add_port(id_I3, PORT_IN); + add_port(id_O, PORT_OUT); + break; + case id_CC_L2T5.index: + add_port(id_I0, PORT_IN); + 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_O, PORT_OUT); + break; + case id_CC_LUT1.index: + add_port(id_I0, PORT_IN); + add_port(id_O, PORT_OUT); + break; + case id_CC_LUT2.index: + add_port(id_I0, PORT_IN); + add_port(id_I1, PORT_IN); + add_port(id_O, PORT_OUT); + break; + case id_CC_MX2.index: + add_port(id_D0, PORT_IN); + add_port(id_D1, PORT_IN); + add_port(id_S0, PORT_IN); + add_port(id_Y, PORT_OUT); + break; + case id_CC_MX4.index: + add_port(id_D0, PORT_IN); + add_port(id_D1, PORT_IN); + add_port(id_D2, PORT_IN); + add_port(id_D3, PORT_IN); + add_port(id_S0, PORT_IN); + add_port(id_S1, PORT_IN); + add_port(id_Y, PORT_OUT); + break; + case id_CC_ADDF.index: + add_port(id_A, PORT_IN); + add_port(id_B, PORT_IN); + add_port(id_CI, PORT_IN); + add_port(id_CO, PORT_OUT); + add_port(id_S, PORT_OUT); + break; + case id_CC_BUFG.index: + add_port(id_I, PORT_IN); + add_port(id_O, PORT_OUT); + break; + case id_CC_USR_RSTN.index: + add_port(id_USR_RSTN, PORT_OUT); + break; + case id_CC_PLL_ADV.index: + add_port(id_USR_SEL_A_B, PORT_IN); + [[fallthrough]]; + case id_CC_PLL.index: + add_port(id_CLK_REF, PORT_IN); + add_port(id_USR_CLK_REF, PORT_IN); + add_port(id_CLK_FEEDBACK, PORT_IN); + add_port(id_USR_LOCKED_STDY_RST, PORT_IN); + add_port(id_USR_PLL_LOCKED_STDY, PORT_OUT); + add_port(id_USR_PLL_LOCKED, PORT_OUT); + add_port(id_CLK0, PORT_OUT); + add_port(id_CLK90, PORT_OUT); + add_port(id_CLK180, PORT_OUT); + add_port(id_CLK270, PORT_OUT); + add_port(id_CLK_REF_OUT, PORT_OUT); + break; + case id_CC_CFG_CTRL.index: + for (int i = 0; i < 8; i++) + add_port(ctx->idf("DATA[%d]", i), PORT_IN); + add_port(id_CLK, PORT_IN); + add_port(id_EN, PORT_IN); + add_port(id_RECFG, PORT_IN); + add_port(id_VALID, PORT_IN); + break; + + default: + log_error("Trying to create unknown cell type %s\n", type.c_str(ctx)); + break; + } + return cell; +} + +void GateMateTest::direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port) +{ + NetInfo *net = ctx->createNet(ctx->idf("%s_%s", o_cell->name.c_str(ctx), o_port.c_str(ctx))); + o_cell->connectPort(o_port, net); + i_cell->connectPort(i_port, net); +} + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/tests/testing.h b/himbaechel/uarch/gatemate/tests/testing.h new file mode 100644 index 00000000..576729cd --- /dev/null +++ b/himbaechel/uarch/gatemate/tests/testing.h @@ -0,0 +1,45 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 The Project Peppercorn 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 GATEMATE_TESTING_H +#define GATEMATE_TESTING_H + +#include "gtest/gtest.h" +#include "nextpnr.h" +#include "uarch/gatemate/gatemate.h" + +NEXTPNR_NAMESPACE_BEGIN + +class GateMateTest : public ::testing::Test +{ + protected: + virtual void SetUp() override; + + virtual void TearDown() override; + + CellInfo *create_cell_ptr(IdString type, std::string name); + void direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port); + + ArchArgs chipArgs; + Context *ctx; + GateMateImpl *impl; +}; + +NEXTPNR_NAMESPACE_END +#endif