diff --git a/.github/ci/build_interchange.sh b/.github/ci/build_interchange.sh deleted file mode 100755 index 3976ce4e..00000000 --- a/.github/ci/build_interchange.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash - -# Install capnproto libraries -function build_capnp { - curl -O https://capnproto.org/capnproto-c++-0.8.0.tar.gz - tar zxf capnproto-c++-0.8.0.tar.gz - pushd capnproto-c++-0.8.0 - ./configure - make -j`nproc` check - sudo make install - popd - - git clone https://github.com/capnproto/capnproto-java.git - pushd capnproto-java - make -j`nproc` - sudo make install - popd -} - -# Install latest Yosys -function build_yosys { - DESTDIR=`pwd`/.yosys - pushd yosys - make -j`nproc` - sudo make install DESTDIR=$DESTDIR - popd -} - - -function get_dependencies { - # Install python-fpga-interchange libraries - git clone -b ${PYTHON_INTERCHANGE_TAG} https://github.com/SymbiFlow/python-fpga-interchange.git ${PYTHON_INTERCHANGE_PATH} - pushd ${PYTHON_INTERCHANGE_PATH} - git submodule update --init --recursive - python3 -m pip install -r requirements.txt - popd - - if [ ${DEVICE} == "LIFCL-17" ] || [ ${DEVICE} == "LIFCL-40" ]; then - # Install prjoxide - curl --proto '=https' -sSf https://sh.rustup.rs | sh -s -- -y - git clone --recursive https://github.com/gatecat/prjoxide.git - pushd prjoxide/libprjoxide - # TODO: use a tag instead of a commit, like python-fpga-interchange - git reset --hard ${PRJOXIDE_REVISION} - PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide --all-features - popd - else - # Install RapidWright - git clone https://github.com/Xilinx/RapidWright.git ${RAPIDWRIGHT_PATH} - pushd ${RAPIDWRIGHT_PATH} - ./gradlew updateJars --no-watch-fs - make compile - popd - fi -} - -function build_nextpnr { - build_capnp - mkdir build - pushd build - cmake .. -DARCH=fpga_interchange -DRAPIDWRIGHT_PATH=${RAPIDWRIGHT_PATH} -DPYTHON_INTERCHANGE_PATH=${PYTHON_INTERCHANGE_PATH} -DWERROR=on - make nextpnr-fpga_interchange -j`nproc` - popd -} diff --git a/.github/workflows/interchange_ci.yml.disable b/.github/workflows/interchange_ci.yml.disable deleted file mode 100644 index 27b19c5a..00000000 --- a/.github/workflows/interchange_ci.yml.disable +++ /dev/null @@ -1,132 +0,0 @@ -name: FPGA interchange CI tests - -on: [push, pull_request] - -jobs: - Build-yosys: - runs-on: ubuntu-latest - steps: - - - uses: actions/checkout@v2 - with: - submodules: recursive - - - uses: actions/setup-python@v2 - - - name: Install - run: | - sudo apt-get update - sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev clang bison flex swig locales libtinfo-dev - - - name: ccache - uses: hendrikmuhs/ccache-action@v1 - - - name: Get yosys - run: | - git clone https://github.com/YosysHQ/yosys.git - cd yosys - echo "YOSYS_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV - - - name: Cache yosys installation - uses: actions/cache@v2 - id: cache-yosys - with: - path: .yosys - key: cache-yosys-${{ env.YOSYS_SHA }} - - - name: Build yosys - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - source ./.github/ci/build_interchange.sh - build_yosys - if: steps.cache-yosys.outputs.cache-hit != 'true' - - Build-nextpnr: - runs-on: ubuntu-latest - steps: - - - uses: actions/checkout@v2 - with: - submodules: recursive - - - uses: actions/setup-python@v2 - - - name: Install - run: | - sudo apt-get update - sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev clang bison flex swig - - - name: ccache - uses: hendrikmuhs/ccache-action@v1 - - - name: Execute build nextpnr - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - source ./.github/ci/build_interchange.sh - build_nextpnr - - Run-Tests: - runs-on: ubuntu-latest - needs: [Build-yosys, Build-nextpnr] - strategy: - # Don't terminate jobs when one fails. This is important when - # debugging CI failures. - fail-fast: false - matrix: - device: [xc7a35t, xc7a100t, xc7a200t, xc7z010, LIFCL-17, LIFCL-40] - steps: - - - uses: actions/checkout@v2 - with: - submodules: recursive - - - uses: actions/setup-python@v2 - - - name: Install - run: | - sudo apt-get update - sudo apt-get install git make cmake libboost-all-dev python3-dev libeigen3-dev tcl-dev clang bison flex swig - - - name: ccache - uses: hendrikmuhs/ccache-action@v1 - - - name: Get yosys - run: | - git clone https://github.com/YosysHQ/yosys.git - cd yosys - echo "YOSYS_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV - - - name: Cache yosys installation - uses: actions/cache@v2 - id: cache-yosys - with: - path: .yosys - key: cache-yosys-${{ env.YOSYS_SHA }} - - - name: Build yosys - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - source ./.github/ci/build_interchange.sh - build_yosys - if: steps.cache-yosys.outputs.cache-hit != 'true' - - - name: Execute build interchange script - env: - RAPIDWRIGHT_PATH: ${{ github.workspace }}/RapidWright - PYTHON_INTERCHANGE_PATH: ${{ github.workspace }}/python-fpga-interchange - PYTHON_INTERCHANGE_TAG: v0.0.20 - PRJOXIDE_REVISION: 318331f8b30c2e2a31cc41d51f104b671e180a8a - DEVICE: ${{ matrix.device }} - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - source ./.github/ci/build_interchange.sh - build_nextpnr && get_dependencies - - - name: Run tests - env: - DEVICE: ${{ matrix.device }} - run: | - export PATH="$GITHUB_WORKSPACE/.yosys/usr/local/bin:$PATH" - which yosys - cd build - make all-$DEVICE-tests -j`nproc` diff --git a/.gitmodules b/.gitmodules index c03206a3..7fec9e58 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "tests"] path = tests url = https://github.com/YosysHQ/nextpnr-tests -[submodule "fpga-interchange-schema"] - path = 3rdparty/fpga-interchange-schema - url = https://github.com/SymbiFlow/fpga-interchange-schema.git [submodule "himbaechel/uarch/xilinx/meta"] path = himbaechel/uarch/xilinx/meta url = https://github.com/gatecat/nextpnr-xilinx-meta diff --git a/3rdparty/fpga-interchange-schema b/3rdparty/fpga-interchange-schema deleted file mode 160000 index 6b297378..00000000 --- a/3rdparty/fpga-interchange-schema +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6b2973788692be86c4a8b2cff1353e603e5857a3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b4604a4..9ae095e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,9 +108,9 @@ endif() set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables") # List of families to build -set(FAMILIES generic ice40 ecp5 nexus gowin fpga_interchange machxo2 mistral himbaechel) +set(FAMILIES generic ice40 ecp5 nexus gowin machxo2 mistral himbaechel) set(STABLE_FAMILIES generic ice40 ecp5) -set(EXPERIMENTAL_FAMILIES nexus gowin fpga_interchange machxo2 mistral himbaechel) +set(EXPERIMENTAL_FAMILIES nexus gowin machxo2 mistral himbaechel) set(ARCH "" CACHE STRING "Architecture family for nextpnr build") set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES}) diff --git a/fpga_interchange/README.md b/fpga_interchange/README.md deleted file mode 100644 index f5530c0e..00000000 --- a/fpga_interchange/README.md +++ /dev/null @@ -1,78 +0,0 @@ -## FPGA interchange nextpnr architecture - -This nextpnr architecture is a meta architecture that in theory will implement -any architecture that emits a complete FPGA interchange device database. - -### FPGA interchange - -The FPGA interchange is a set of file formats intended to describe any modern -island based FPGA. It consists of three primary file formats: - - - Device database - - This is a description of a particular FPGA fabric. This description - includes placement locations, placement constraints and a complete - description of the routing fabric. - - This file will also include timing information once added. - - - Logical netlist - - This is the output of a synthesis tool. This is equivalent to the - Yosys JSON format, EDIF, or eblif. - - As part of future nextpnr development, a frontend will be added that - takes this format as input. - - - Physical netlist - - This is the output of a place and route tool. It can describe a clustered - design, a partially or fully placed design, and a partially or fully - routed design. - -### Current development status - -This architecture implementation can be compiled in conjunction with a FPGA -interchange device database, and the outputs from -`fpga_interchange.nextpnr_emit`, which is part of the -[python-fpga-interchange](https://github.com/SymbiFlow/python-fpga-interchange/) -library. - -The current implementation is missing essential features for place and route. -As these features are added, this implementation will become more useful. - - - [ ] Logical netlist macro expansion is not implemented, meaning that any - macro primitives are unplaceable. Common macro primitives examples are - differential IO buffers (IBUFDS) and some LUT RAM (e.g. RAM64X1D). - - [ ] Timing information is missing from the FPGA interchange device - database, so it is also currently missing from the FPGA interchange - architecture. Once timing information is added to the device database - -#### Weaknesses of current implementation - -Initial development on the following features is started, but needs more -refinement. - - - [ ] BEL validity checking is too expensive. The majority of the runtime - is currently in the LUT rotation. Profiling, optimization and - algorithm review is likely required to bring strict legalisation - runtimes into expected levels. - - [ ] The router lookahead is disabled by default. Without the lookahead, - router runtime is terrible. However the current lookahead - implementation is slow to compute and memory intensive, hence why it is - disabled by default. - - [ ] Pseudo pips (e.g. pips that consume BELs and or site resources) and - pseudo site pips (e.g. site pips that route through BELs) consume site - wires to indicate that they block some resources. This covers many - validity check cases, but misses some. In particular, when a pseudo - pip / pseudo site pip has an implication on the constraint system (e.g. - LUT on a LUT-RAM BEL), an edge may be allowed incorrectly, resulting - in an illegal design. - -### FPGA interchange fabrics - -Xilinx 7-series, UltraScale and UltraScale+ fabrics have a -device database generator, via [RapidWright](https://github.com/Xilinx/RapidWright). - -A Lattice Nexus device database is being worked on, via -[prjoxide](https://github.com/gatecat/prjoxide). - -### FPGA interchange build system - -Construction of chipdb's is currently integrated into nextpnr's CMake build -system. See fpga\_interchange/examples/README.md for more details. diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc deleted file mode 100644 index bf7e9ff1..00000000 --- a/fpga_interchange/arch.cc +++ /dev/null @@ -1,2144 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Claire Xenia Wolf - * Copyright (C) 2018-19 gatecat - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "arch.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "constraints.impl.h" -#include "fpga_interchange.h" -#include "log.h" -#include "nextpnr.h" -#include "placer1.h" -#include "placer_heap.h" -#include "router1.h" -#include "router2.h" -#include "timing.h" -#include "util.h" -#include "xdc.h" - -// Include tcl.h late because it messed with defines and let them leave the -// scope of the header. -#include - -// #define DEBUG_BINDING -// #define USE_LOOKAHEAD -// #define DEBUG_CELL_PIN_MAPPING - -// Define to enable some idempotent sanity checks for some important -// operations prior to placement and routing. -#define IDEMPOTENT_CHECK - -NEXTPNR_NAMESPACE_BEGIN -struct SiteBelPair -{ - std::string site; - IdString bel; - - SiteBelPair() {} - SiteBelPair(std::string site, IdString bel) : site(site), bel(bel) {} - - bool operator==(const SiteBelPair &other) const { return site == other.site && bel == other.bel; } - unsigned int hash() const { return mkhash(std::hash()(site), bel.hash()); } -}; - -static std::pair split_identifier_name_dot(const std::string &name) -{ - size_t first_dot = name.find('.'); - NPNR_ASSERT(first_dot != std::string::npos); - return std::make_pair(name.substr(0, first_dot), name.substr(first_dot + 1)); -}; - -// ----------------------------------------------------------------------- - -void IdString::initialize_arch(const BaseCtx *ctx) {} - -// ----------------------------------------------------------------------- - -static const ChipInfoPOD *get_chip_info(const RelPtr *ptr) { return ptr->get(); } - -static std::string sha1_hash(const char *data, size_t size) -{ - boost::uuids::detail::sha1 hasher; - hasher.process_bytes(data, size); - - // unsigned int[5] - boost::uuids::detail::sha1::digest_type digest; - hasher.get_digest(digest); - - std::ostringstream buf; - for (int i = 0; i < 5; ++i) - buf << std::hex << std::setfill('0') << std::setw(8) << digest[i]; - - return buf.str(); -} - -Arch::Arch(ArchArgs args) : args(args), disallow_site_routing(false) -{ - try { - blob_file.open(args.chipdb); - if (args.chipdb.empty() || !blob_file.is_open()) - log_error("Unable to read chipdb %s\n", args.chipdb.c_str()); - const char *blob = reinterpret_cast(blob_file.data()); - - chipdb_hash = sha1_hash(blob, blob_file.size()); - chip_info = get_chip_info(reinterpret_cast *>(blob)); - } catch (...) { - log_error("Unable to read chipdb %s\n", args.chipdb.c_str()); - } - - if (chip_info->version != kExpectedChipInfoVersion) { - log_error("Expected chipdb with version %d found version %d\n", kExpectedChipInfoVersion, chip_info->version); - } - - // Read strings from constids into IdString database, checking that list - // is unique and matches expected constid value. - const RelSlice> &constids = *chip_info->constids; - for (size_t i = 0; i < constids.size(); ++i) { - IdString::initialize_add(this, constids[i].get(), i + 1); - } - - id_GND = id("GND"); - id_VCC = id("VCC"); - - // Sanity check cell name ids. - const CellMapPOD &cell_map = *chip_info->cell_map; - int32_t first_cell_id = cell_map.cell_names[0]; - for (int32_t i = 0; i < cell_map.cell_names.ssize(); ++i) { - log_assert(cell_map.cell_names[i] == i + first_cell_id); - } - - io_port_types.emplace(this->id("$nextpnr_ibuf")); - io_port_types.emplace(this->id("$nextpnr_obuf")); - io_port_types.emplace(this->id("$nextpnr_iobuf")); - io_port_types.emplace(this->id("$nextpnr_inv")); - - if (!this->args.package.empty()) { - IdString package = this->id(this->args.package); - package_index = -1; - for (size_t i = 0; i < chip_info->packages.size(); ++i) { - if (IdString(chip_info->packages[i].package) == package) { - NPNR_ASSERT(package_index == -1); - package_index = i; - } - } - - if (package_index == -1) { - log_error("Could not find package '%s' in chipdb.\n", this->args.package.c_str()); - } - } else { - // Default to first package. - NPNR_ASSERT(chip_info->packages.size() > 0); - if (chip_info->packages.size() == 1) { - IdString package_name(chip_info->packages[0].package); - this->args.package = package_name.str(this); - package_index = 0; - } else { - log_info( - "Package must be specified (with --package arg) when multiple packages are available, packages:\n"); - for (const auto &package : chip_info->packages) { - log_info(" - %s\n", IdString(package.package).c_str(this)); - } - log_error("--package is required!\n"); - } - } - - pool site_bel_pads; - for (const auto &package_pin : chip_info->packages[package_index].pins) { - IdString site(package_pin.site); - IdString bel(package_pin.bel); - site_bel_pads.emplace(SiteBelPair(site.str(this), bel)); - } - - for (BelId bel : getBels()) { - auto &bel_data = bel_info(chip_info, bel); - const SiteInstInfoPOD &site = get_site_inst(bel); - auto iter = site_bel_pads.find(SiteBelPair(site.site_name.get(), IdString(bel_data.name))); - if (iter != site_bel_pads.end()) { - pads.emplace(bel); - } - } - - explain_constraints = false; - - int tile_type_index = 0; - size_t max_tag_count = 0; - - for (const TileTypeInfoPOD &tile_type : chip_info->tile_types) { - max_tag_count = std::max(max_tag_count, tile_type.tags.size()); - - auto &type_definition = constraints.definitions[tile_type_index++]; - for (const ConstraintTagPOD &tag : tile_type.tags) { - type_definition.emplace_back(); - auto &definition = type_definition.back(); - definition.prefix = IdString(tag.tag_prefix); - definition.default_state = IdString(tag.default_state); - NPNR_ASSERT(tag.states.size() < kMaxState); - - definition.states.reserve(tag.states.size()); - for (auto state : tag.states) { - definition.states.push_back(IdString(state)); - } - } - - // Logic BELs (e.g. placable BELs) should always appear first in the - // bel data list. - // - // When iterating over BELs this property is depended on to skip - // non-placable BELs (e.g. routing BELs and site ports). - bool in_logic_bels = true; - for (const BelInfoPOD &bel_info : tile_type.bel_data) { - if (in_logic_bels && bel_info.category != BEL_CATEGORY_LOGIC) { - in_logic_bels = false; - } - - if (!in_logic_bels) { - NPNR_ASSERT(bel_info.category != BEL_CATEGORY_LOGIC); - } - } - } - - // Initially LutElement vectors for each tile type. - tile_type_index = 0; - max_lut_cells = 0; - max_lut_pins = 0; - lut_elements.resize(chip_info->tile_types.size()); - for (const TileTypeInfoPOD &tile_type : chip_info->tile_types) { - std::vector &elements = lut_elements[tile_type_index++]; - elements.reserve(tile_type.lut_elements.size()); - - int lut_cells_count = 0; - for (auto &lut_element : tile_type.lut_elements) { - elements.emplace_back(); - - LutElement &element = elements.back(); - element.width = lut_element.width; - for (auto &lut_bel : lut_element.lut_bels) { - IdString name(lut_bel.name); - auto result = element.lut_bels.emplace(name, LutBel()); - NPNR_ASSERT(result.second); - LutBel &lut = result.first->second; - - lut.name = name; - - lut.low_bit = lut_bel.low_bit; - lut.high_bit = lut_bel.high_bit; - - lut.pins.reserve(lut_bel.pins.size()); - for (size_t i = 0; i < lut_bel.pins.size(); ++i) { - IdString pin(lut_bel.pins[i]); - lut.pins.push_back(pin); - lut.pin_to_index[pin] = i; - } - - lut.output_pin = IdString(lut_bel.out_pin); - lut_cells_count++; - - max_lut_pins = std::max((int)lut_bel.pins.size(), max_lut_pins); - } - - element.compute_pin_order(); - } - - max_lut_cells = std::max(lut_cells_count, max_lut_cells); - } - - // Map lut cell types to their LutCellPOD - wire_lut = nullptr; - for (const LutCellPOD &lut_cell : chip_info->cell_map->lut_cells) { - IdString cell_type(lut_cell.cell); - auto result = lut_cells.emplace(cell_type, &lut_cell); - NPNR_ASSERT(result.second); - - if (lut_cell.input_pins.size() == 1) { - // Only really expecting 1 single input LUT type! - NPNR_ASSERT(wire_lut == nullptr); - wire_lut = &lut_cell; - } - } - - raw_bin_constant = std::regex("[01]+", std::regex_constants::ECMAScript | std::regex_constants::optimize); - verilog_bin_constant = - std::regex("([0-9]+)'b([01]+)", std::regex_constants::ECMAScript | std::regex_constants::optimize); - verilog_hex_constant = - std::regex("([0-9]+)'h([0-9a-fA-F]+)", std::regex_constants::ECMAScript | std::regex_constants::optimize); - - default_tags.resize(max_tag_count); -} - -void Arch::init() -{ -#ifdef USE_LOOKAHEAD - lookahead.init(getCtx(), getCtx()); -#endif - dedicated_interconnect.init(getCtx()); - cell_parameters.init(getCtx()); - - for (size_t tile_type = 0; tile_type < chip_info->tile_types.size(); ++tile_type) { - pseudo_pip_data.init_tile_type(getCtx(), tile_type); - } - - // Warn if there is no preferred constant net defined in the architecture - IdString const_net_name(getCtx()->chip_info->constants->best_constant_net); - if (const_net_name == IdString()) { - log_warning("The architecture does not specify preferred constant net. Using VCC as default.\n"); - } -} - -// ----------------------------------------------------------------------- - -std::string Arch::getChipName() const { return chip_info->name.get(); } - -// ----------------------------------------------------------------------- - -IdString Arch::archArgsToId(ArchArgs args) const { return IdString(); } - -// ----------------------------------------------------------------------- - -void Arch::setup_byname() const -{ - by_name_mutex.lock(); - - if (tile_by_name.empty()) { - for (int i = 0; i < chip_info->tiles.ssize(); i++) { - tile_by_name[id(chip_info->tiles[i].name.get())] = i; - } - } - - if (site_by_name.empty()) { - for (int i = 0; i < chip_info->tiles.ssize(); i++) { - auto &tile = chip_info->tiles[i]; - auto &tile_type = chip_info->tile_types[tile.type]; - for (size_t j = 0; j < tile_type.site_types.size(); j++) { - auto &site = chip_info->sites[tile.sites[j]]; - site_by_name[id(site.name.get())] = std::make_pair(i, j); - } - } - } - - by_name_mutex.unlock(); -} - -BelId Arch::getBelByName(IdStringList name) const -{ - BelId ret; - if (name.ids.size() != 2) { - return BelId(); - } - - setup_byname(); - - int tile, site; - std::tie(tile, site) = site_by_name.at(name.ids[0]); - auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type]; - IdString belname = name.ids[1]; - for (int i = 0; i < tile_info.bel_data.ssize(); i++) { - if (tile_info.bel_data[i].site == site && tile_info.bel_data[i].name == belname.index) { - ret.tile = tile; - ret.index = i; - break; - } - } - - return ret; -} - -BelRange Arch::getBelsByTile(int x, int y) const -{ - BelRange br; - - br.b.cursor_tile = get_tile_index(x, y); - br.e.cursor_tile = br.b.cursor_tile; - br.b.cursor_index = 0; - br.e.cursor_index = chip_info->tile_types[chip_info->tiles[br.b.cursor_tile].type].bel_data.size(); - br.b.chip = chip_info; - br.e.chip = chip_info; - - if (br.b != br.e) { - ++br.e; - } - return br; -} - -WireId Arch::getBelPinWire(BelId bel, IdString pin) const -{ - NPNR_ASSERT(bel != BelId()); - - int pin_index = get_bel_pin_index(bel, pin); - - auto &bel_data = bel_info(chip_info, bel); - NPNR_ASSERT(pin_index >= 0 && pin_index < bel_data.num_bel_wires); - - const int32_t *wires = bel_data.wires.get(); - int32_t wire_index = wires[pin_index]; - if (wire_index < 0) { - // This BEL pin is not connected. - return WireId(); - } else { - return canonical_wire(chip_info, bel.tile, wire_index); - } -} - -PortType Arch::getBelPinType(BelId bel, IdString pin) const -{ - NPNR_ASSERT(bel != BelId()); - - int pin_index = get_bel_pin_index(bel, pin); - auto &bel_data = bel_info(chip_info, bel); - NPNR_ASSERT(pin_index >= 0 && pin_index < bel_data.num_bel_wires); - const int32_t *types = bel_data.types.get(); - return PortType(types[pin_index]); -} - -// ----------------------------------------------------------------------- - -WireId Arch::getWireByName(IdStringList name) const -{ - WireId ret; - if (name.ids.size() != 2) { - return WireId(); - } - - setup_byname(); - - auto iter = site_by_name.find(name.ids[0]); - if (iter != site_by_name.end()) { - int tile; - int site; - std::tie(tile, site) = iter->second; - auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type]; - IdString wirename = name.ids[1]; - for (int i = 0; i < tile_info.wire_data.ssize(); i++) { - if (tile_info.wire_data[i].site == site && tile_info.wire_data[i].name == wirename.index) { - ret.tile = tile; - ret.index = i; - break; - } - } - } else { - int tile = tile_by_name.at(name.ids[0]); - auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type]; - IdString wirename = name.ids[1]; - for (int i = 0; i < tile_info.wire_data.ssize(); i++) { - if (tile_info.wire_data[i].site == -1 && tile_info.wire_data[i].name == wirename.index) { - int32_t node = chip_info->tiles[tile].tile_wire_to_node[i]; - if (node == -1) { - // Not a nodal wire - ret.tile = tile; - ret.index = i; - } else { - // Is a nodal wire, set tile to -1 - ret.tile = -1; - ret.index = node; - } - break; - } - } - } - - return ret; -} - -IdString Arch::getWireType(WireId wire) const -{ - int tile = wire.tile, index = wire.index; - if (tile == -1) { - // Nodal wire - const TileWireRefPOD &wr = chip_info->nodes[wire.index].tile_wires[0]; - tile = wr.tile; - index = wr.index; - } - auto &w2t = chip_info->tiles[tile].tile_wire_to_type; - if (index >= w2t.ssize()) - return IdString(); - int wire_type = w2t[index]; - if (wire_type == -1) - return IdString(); - return IdString(chip_info->wire_types[wire_type].name); -} - -bool Arch::is_site_wire(WireId wire) const -{ - if (wire.tile == -1) - return false; - const auto &tile_type = loc_info(chip_info, wire); - return tile_type.wire_data[wire.index].site != -1; -} - -WireCategory Arch::get_wire_category(WireId wire) const -{ - int tile = wire.tile, index = wire.index; - if (tile == -1) { - // Nodal wire - const TileWireRefPOD &wr = chip_info->nodes[wire.index].tile_wires[0]; - tile = wr.tile; - index = wr.index; - } - auto &w2t = chip_info->tiles[tile].tile_wire_to_type; - if (index >= w2t.ssize()) - return WIRE_CAT_GENERAL; - int wire_type = w2t[index]; - if (wire_type == -1) - return WIRE_CAT_GENERAL; - return WireCategory(chip_info->wire_types[wire_type].category); -} - -std::vector> Arch::getWireAttrs(WireId wire) const { return {}; } - -// ----------------------------------------------------------------------- - -PipId Arch::getPipByName(IdStringList name) const -{ - // PIP name structure: - // Tile PIP: /. - // Site PIP: // - // Site pin: / - // Psuedo site PIP: /. - - setup_byname(); - - if (name.ids.size() == 3) { - // This is a Site PIP. - IdString site_name = name.ids[0]; - IdString belname = name.ids[1]; - IdString pinname = name.ids[2]; - - int tile; - int site; - std::tie(tile, site) = site_by_name.at(site_name); - auto tile_type_idx = chip_info->tiles[tile].type; - auto &tile_info = chip_info->tile_types[tile_type_idx]; - - std::array ids{name.ids[0], belname}; - BelId bel = getBelByName(IdStringList(ids)); - NPNR_ASSERT(bel != BelId()); - - int pin_index = get_bel_pin_index(bel, pinname); - NPNR_ASSERT(pin_index >= 0); - - for (int i = 0; i < tile_info.pip_data.ssize(); i++) { - if (tile_info.pip_data[i].site == site && tile_info.pip_data[i].bel == bel.index && - tile_info.pip_data[i].extra_data == pin_index) { - - PipId ret; - ret.tile = tile; - ret.index = i; - return ret; - } - } - } else { - auto iter = site_by_name.find(name.ids[0]); - if (iter != site_by_name.end()) { - // This is either a site pin or a psuedo site pip. - // psuedo site pips are /. - // site pins are / - int tile; - int site; - std::tie(tile, site) = iter->second; - auto tile_type_idx = chip_info->tiles[tile].type; - auto &tile_info = chip_info->tile_types[tile_type_idx]; - - std::string pip_second = name.ids[1].str(this); - auto split = pip_second.find('.'); - if (split == std::string::npos) { - // This is a site pin! - BelId bel = getBelByName(name); - NPNR_ASSERT(bel != BelId()); - - for (int i = 0; i < tile_info.pip_data.ssize(); i++) { - if (tile_info.pip_data[i].site == site && tile_info.pip_data[i].bel == bel.index) { - - PipId ret; - ret.tile = tile; - ret.index = i; - return ret; - } - } - } else { - // This is a psuedo site pip! - IdString src_site_wire = id(pip_second.substr(0, split)); - IdString dst_site_wire = id(pip_second.substr(split + 1)); - int32_t src_index = -1; - int32_t dst_index = -1; - - for (int i = 0; i < tile_info.wire_data.ssize(); i++) { - if (tile_info.wire_data[i].site == site && tile_info.wire_data[i].name == src_site_wire.index) { - src_index = i; - if (dst_index != -1) { - break; - } - } - if (tile_info.wire_data[i].site == site && tile_info.wire_data[i].name == dst_site_wire.index) { - dst_index = i; - if (src_index != -1) { - break; - } - } - } - - NPNR_ASSERT(src_index != -1); - NPNR_ASSERT(dst_index != -1); - - for (int i = 0; i < tile_info.pip_data.ssize(); i++) { - if (tile_info.pip_data[i].site == site && tile_info.pip_data[i].src_index == src_index && - tile_info.pip_data[i].dst_index == dst_index) { - - PipId ret; - ret.tile = tile; - ret.index = i; - return ret; - } - } - } - } else { - int tile = tile_by_name.at(name.ids[0]); - size_t tile_type_idx = chip_info->tiles[tile].type; - auto &tile_info = chip_info->tile_types[tile_type_idx]; - - std::string pip_second = name.ids[1].str(this); - auto spn = split_identifier_name_dot(pip_second); - auto src_wire_name = id(spn.first); - auto dst_wire_name = id(spn.second); - - int32_t src_index = -1; - int32_t dst_index = -1; - for (int i = 0; i < tile_info.wire_data.ssize(); i++) { - if (tile_info.wire_data[i].site == -1 && tile_info.wire_data[i].name == src_wire_name.index) { - src_index = i; - if (dst_index != -1) { - break; - } - } - if (tile_info.wire_data[i].site == -1 && tile_info.wire_data[i].name == dst_wire_name.index) { - dst_index = i; - if (src_index != -1) { - break; - } - } - } - - NPNR_ASSERT(src_index != -1); - NPNR_ASSERT(dst_index != -1); - - for (int i = 0; i < tile_info.pip_data.ssize(); i++) { - if (tile_info.pip_data[i].src_index == src_index && tile_info.pip_data[i].dst_index == dst_index) { - - PipId ret; - ret.tile = tile; - ret.index = i; - return ret; - } - } - } - } - - return PipId(); -} - -IdStringList Arch::getPipName(PipId pip) const -{ - // PIP name structure: - // Tile PIP: /. - // Psuedo site PIP: /. - // Site PIP: // - // Site pin: / - NPNR_ASSERT(pip != PipId()); - auto &tile = chip_info->tiles[pip.tile]; - auto &tile_type = loc_info(chip_info, pip); - auto &pip_info = tile_type.pip_data[pip.index]; - if (pip_info.site != -1) { - // This is either a site pin or a site pip. - auto &site = get_site_inst(pip); - auto &bel = tile_type.bel_data[pip_info.bel]; - IdString bel_name(bel.name); - if (bel.category == BEL_CATEGORY_LOGIC) { - // This is a psuedo pip - IdString src_wire_name = IdString(tile_type.wire_data[pip_info.src_index].name); - IdString dst_wire_name = IdString(tile_type.wire_data[pip_info.dst_index].name); - IdString pip = id(src_wire_name.str(this) + "." + dst_wire_name.str(this)); - std::array ids{id(site.name.get()), pip}; - return IdStringList(ids); - - } else if (bel.category == BEL_CATEGORY_ROUTING) { - // This is a site pip. - IdString pin_name(bel.ports[pip_info.extra_data]); - std::array ids{id(site.name.get()), bel_name, pin_name}; - return IdStringList(ids); - } else { - NPNR_ASSERT(bel.category == BEL_CATEGORY_SITE_PORT); - // This is a site pin, just the name of the BEL is a unique identifier. - std::array ids{id(site.name.get()), bel_name}; - return IdStringList(ids); - } - } else { - // This is a tile pip. - IdString src_wire_name = IdString(tile_type.wire_data[pip_info.src_index].name); - IdString dst_wire_name = IdString(tile_type.wire_data[pip_info.dst_index].name); - IdString pip = id(src_wire_name.str(this) + "." + dst_wire_name.str(this)); - std::array ids{id(std::string(tile.name.get())), pip}; - return IdStringList(ids); - } -} - -IdString Arch::getPipType(PipId pip) const { return id("PIP"); } - -std::vector> Arch::getPipAttrs(PipId pip) const { return {}; } - -// ----------------------------------------------------------------------- - -BelId Arch::getBelByLocation(Loc loc) const -{ - BelId bi; - if (loc.x >= chip_info->width || loc.y >= chip_info->height) - return BelId(); - bi.tile = get_tile_index(loc); - auto &li = loc_info(chip_info, bi); - - if (loc.z >= li.bel_data.ssize()) { - return BelId(); - } else { - bi.index = loc.z; - return bi; - } -} - -std::vector> Arch::getBelAttrs(BelId bel) const { return {}; } - -// ----------------------------------------------------------------------- - -BoundingBox Arch::getRouteBoundingBox(WireId src, WireId dst) const -{ - int dst_tile = dst.tile == -1 ? chip_info->nodes[dst.index].tile_wires[0].tile : dst.tile; - int src_tile = src.tile == -1 ? chip_info->nodes[src.index].tile_wires[0].tile : src.tile; - - int src_x, src_y; - get_tile_x_y(src_tile, &src_x, &src_y); - - int x0 = src_x, x1 = src_x, y0 = src_y, y1 = src_y; - - int dst_x, dst_y; - get_tile_x_y(dst_tile, &dst_x, &dst_y); - - auto expand = [&](int x, int y) { - x0 = std::min(x0, x); - x1 = std::max(x1, x); - y0 = std::min(y0, y); - y1 = std::max(y1, y); - }; - - expand(dst_x, dst_y); - - if (source_locs.count(src)) - expand(source_locs.at(src).x, source_locs.at(src).y); - - if (sink_locs.count(dst)) { - expand(sink_locs.at(dst).x, sink_locs.at(dst).y); - } - - return {x0, y0, x1, y1}; -} - -// ----------------------------------------------------------------------- - -bool Arch::pack() -{ - decode_lut_cells(); - expand_macros(); - merge_constant_nets(); - pack_ports(); - pack_default_conns(); - pack_cluster(); - return true; -} - -static void prepare_for_placement(Context *ctx) -{ - ctx->remove_site_routing(); - - // Re-map BEL pins without constant pins - for (BelId bel : ctx->getBels()) { - CellInfo *cell = ctx->getBoundBelCell(bel); - if (cell != nullptr && cell->cell_mapping != -1) { - ctx->map_cell_pins(cell, cell->cell_mapping, /*bind_constants=*/false); - } - } -} - -bool Arch::place() -{ - // Before placement, ripup placement specific bindings and unmask all cell - // pins. - getCtx()->check(); - prepare_for_placement(getCtx()); - getCtx()->check(); -#ifdef IDEMPOTENT_CHECK - prepare_for_placement(getCtx()); - getCtx()->check(); -#endif - - place_constraints(); - place_globals(); - - std::string placer = str_or_default(settings, id("placer"), defaultPlacer); - if (placer == "heap") { - PlacerHeapCfg cfg(getCtx()); - cfg.criticalityExponent = 7; - cfg.alpha = 0.08; - cfg.beta = 0.4; - cfg.placeAllAtOnce = true; - cfg.hpwl_scale_x = 1; - cfg.hpwl_scale_y = 2; - cfg.spread_scale_x = 2; - cfg.spread_scale_y = 1; - cfg.solverTolerance = 0.6e-6; - if (!placer_heap(getCtx(), cfg)) - return false; - } else if (placer == "sa") { - if (!placer1(getCtx(), Placer1Cfg(getCtx()))) - return false; - } else { - log_error("FPGA interchange architecture does not support placer '%s'\n", placer.c_str()); - } - - getCtx()->attrs[getCtx()->id("step")] = std::string("place"); - archInfoToAttributes(); - - // Print site LUT mapping caching stats - if (!getCtx()->arch_args.disable_lut_mapping_cache) { - log_info("Site LUT mapping cache stats:\n"); - log_info(" miss ratio: %.1f%%\n", getCtx()->site_lut_mapping_cache.getMissRatio() * 100.0f); - log_info(" peak size : %zuMB (%zu items)\n", getCtx()->site_lut_mapping_cache.getSizeMB(), - getCtx()->site_lut_mapping_cache.getCount()); - } - - getCtx()->check(); - - return true; -} - -static void prepare_sites_for_routing(Context *ctx) -{ - // Reset site routing and remove masked cell pins from previous router run - // (if any). - ctx->remove_site_routing(); - - // Re-map BEL pins with constant pins - for (BelId bel : ctx->getBels()) { - CellInfo *cell = ctx->getBoundBelCell(bel); - if (cell != nullptr && cell->cell_mapping != -1) { - ctx->map_cell_pins(cell, cell->cell_mapping, /*bind_constants=*/true); - } - } - - // Clear the site routing cache. This is because routing at this stage is done with the extra constraint of blocked - // pins to ensure a routeable pin choice. - ctx->site_routing_cache.clear(); - - // Clear the LUT mapping cache - ctx->site_lut_mapping_cache.clear(); - - // Have site router bind site routing (via bindPip and bindWire). - // This is important so that the pseudo pips are correctly blocked prior - // to handing the design to the generalized router algorithms. - for (auto &tile_pair : ctx->tileStatus) { - for (auto &site_router : tile_pair.second.sites) { - if (site_router.cells_in_site.empty()) { - continue; - } - - site_router.bindSiteRouting(ctx); - } - - tile_pair.second.pseudo_pip_model.prepare_for_routing(ctx, tile_pair.second.sites); - } - - // Fixup LUT vcc pins. - IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); - IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); - - IdString const_net_name(ctx->chip_info->constants->best_constant_net); - NPNR_ASSERT(const_net_name == IdString() || const_net_name == vcc_net_name || const_net_name == gnd_net_name); - - // FIXME: Use VCC if the architecture does not device the best constant - if (const_net_name == IdString()) { - const_net_name = vcc_net_name; - } - - for (BelId bel : ctx->getBels()) { - CellInfo *cell = ctx->getBoundBelCell(bel); - if (cell == nullptr) { - continue; - } - - for (const auto &it : cell->lut_cell.pin_connections) { - const auto &bel_pin = it.first; - const auto &conn = it.second; - - // Connected to an active signal or unconnected - if (conn == LutCell::PinConnection::Signal || conn == LutCell::PinConnection::Unconnected) { - continue; - } - - // We can't rely on bel pins not clashing with cell names (for Xilinx they use different naming schemes, for - // Nexus they are the same) so add a prefix to the bel pin name to disambiguate it - IdString cell_pin = ctx->idf("%s_PHYS", ctx->nameOf(bel_pin)); - - PortInfo port_info; - port_info.name = cell_pin; - port_info.type = PORT_IN; - port_info.net = nullptr; - -#ifdef DEBUG_LUT_MAPPING - if (ctx->verbose) { - log_info("%s must be tied to %s, tying now\n", ctx->nameOfWire(lut_pin_wire), - LutCell::nameOfPinConnection(conn).c_str()); - } -#endif - - IdString tie_net_name; - switch (conn) { - case LutCell::PinConnection::Vcc: - tie_net_name = vcc_net_name; - break; - case LutCell::PinConnection::Gnd: - tie_net_name = gnd_net_name; - break; - case LutCell::PinConnection::Const: - tie_net_name = const_net_name; - break; - default: - // Should never happen - NPNR_ASSERT_FALSE( - stringf("Invalid LUT cell pin connection '%s'", LutCell::nameOfPinConnection(conn).c_str()) - .c_str()); - break; - } - - auto result = cell->ports.emplace(cell_pin, port_info); - if (result.second) { - cell->cell_bel_pins[cell_pin].push_back(bel_pin); - ctx->connectPort(tie_net_name, cell->name, cell_pin); - cell->const_ports.emplace(cell_pin); - } else { - NPNR_ASSERT(result.first->second.net == ctx->getNetByAlias(tie_net_name)); - auto result2 = cell->cell_bel_pins.emplace(cell_pin, std::vector({bel_pin})); - NPNR_ASSERT(result2.first->second.at(0) == bel_pin); - NPNR_ASSERT(result2.first->second.size() == 1); - } - } - } -} - -bool Arch::route() -{ - getCtx()->check(); - prepare_sites_for_routing(getCtx()); - getCtx()->check(); -#ifdef IDEMPOTENT_CHECK - prepare_sites_for_routing(getCtx()); - getCtx()->check(); -#endif - - std::string router = str_or_default(settings, id("router"), defaultRouter); - - // Disallow site routing during general routing. This is because - // "prepare_sites_for_routing" has already assigned routing for all sites - // in the design, and if the router wants to route-thru a site, it *MUST* - // use a pseudo-pip. - // - // It is not legal in the FPGA interchange to enter a site and not - // terminate at a BEL pin. - disallow_site_routing = true; - - route_globals(); - - bool result; - if (router == "router1") { - result = router1(getCtx(), Router1Cfg(getCtx())); - } else if (router == "router2") { - router2(getCtx(), Router2Cfg(getCtx())); - result = true; - } else { - log_error("FPGA interchange architecture does not support router '%s'\n", router.c_str()); - } - - disallow_site_routing = false; - - getCtx()->attrs[getCtx()->id("step")] = std::string("route"); - archInfoToAttributes(); - - getCtx()->check(); - - // Now that routing is complete, unmask BEL pins. - unmask_bel_pins(); - - getCtx()->check(); - - return result; -} - -// ----------------------------------------------------------------------- - -std::vector Arch::getDecalGraphics(DecalId decal) const { return {}; } - -DecalXY Arch::getBelDecal(BelId bel) const -{ - DecalXY decalxy; - return decalxy; -} - -DecalXY Arch::getWireDecal(WireId wire) const -{ - DecalXY decalxy; - return decalxy; -} - -DecalXY Arch::getPipDecal(PipId pip) const { return {}; }; - -DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; }; - -// ----------------------------------------------------------------------- - -delay_t Arch::estimateDelay(WireId src, WireId dst) const -{ -#ifdef USE_LOOKAHEAD - return lookahead.estimateDelay(getCtx(), src, dst); -#else - // Note: Something is better than nothing when USE_LOOKAHEAD is not - // defined. - int src_tile = src.tile == -1 ? chip_info->nodes[src.index].tile_wires[0].tile : src.tile; - int dst_tile = dst.tile == -1 ? chip_info->nodes[dst.index].tile_wires[0].tile : dst.tile; - - int src_x, src_y; - get_tile_x_y(src_tile, &src_x, &src_y); - - int dst_x, dst_y; - get_tile_x_y(dst_tile, &dst_x, &dst_y); - - delay_t base = 30 * std::min(std::abs(dst_x - src_x), 18) + 10 * std::max(std::abs(dst_x - src_x) - 18, 0) + - 60 * std::min(std::abs(dst_y - src_y), 6) + 20 * std::max(std::abs(dst_y - src_y) - 6, 0) + 300; - - base = (base * 3) / 2; - return base; -#endif -} - -delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const -{ - // FIXME: Implement when adding timing-driven place and route. - NPNR_UNUSED(src_pin); - NPNR_UNUSED(dst_pin); - int src_x, src_y; - get_tile_x_y(src_bel.tile, &src_x, &src_y); - - int dst_x, dst_y; - get_tile_x_y(dst_bel.tile, &dst_x, &dst_y); - - delay_t base = 30 * std::min(std::abs(dst_x - src_x), 18) + 10 * std::max(std::abs(dst_x - src_x) - 18, 0) + - 60 * std::min(std::abs(dst_y - src_y), 6) + 20 * std::max(std::abs(dst_y - src_y) - 6, 0) + 300; - - base = (base * 3) / 2; - return base; -} - -bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const -{ - // FIXME: Implement when adding timing-driven place and route. - return false; -} - -TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const -{ - // FIXME: Implement when adding timing-driven place and route. - return TMG_IGNORE; -} - -TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const -{ - // FIXME: Implement when adding timing-driven place and route. - TimingClockingInfo info; - return info; -} - -// ----------------------------------------------------------------------- - -void Arch::read_logical_netlist(const std::string &filename) -{ - FpgaInterchange::read_logical_netlist(getCtx(), filename); -} -void Arch::write_physical_netlist(const std::string &filename) const -{ - FpgaInterchange::write_physical_netlist(getCtx(), filename); -} - -void Arch::parse_xdc(const std::string &filename) -{ - TclInterp interp(getCtx()); - auto result = Tcl_EvalFile(interp.interp, filename.c_str()); - if (result != TCL_OK) { - log_error("Error in %s:%d => %s\n", filename.c_str(), Tcl_GetErrorLine(interp.interp), - Tcl_GetStringResult(interp.interp)); - } -} - -std::string Arch::get_part() const -{ - // FIXME: Need a map between device / package / speed grade and part. - return chip_info->name.get() + args.package + "-1"; -} - -// ----------------------------------------------------------------------- - -const std::string Arch::defaultPlacer = "heap"; - -const std::vector Arch::availablePlacers = {"sa", "heap"}; - -const std::string Arch::defaultRouter = "router2"; -const std::vector Arch::availableRouters = {"router1", "router2"}; - -void Arch::map_cell_pins(CellInfo *cell, int32_t mapping, bool bind_constants) -{ - cell->cell_mapping = mapping; - if (cell->lut_cell.pins.empty()) { - cell->cell_bel_pins.clear(); - cell->masked_cell_bel_pins.clear(); - } else { - std::vector cell_pin_to_remove; - for (auto port_pair : cell->cell_bel_pins) { - if (!cell->lut_cell.lut_pins.count(port_pair.first)) { - cell_pin_to_remove.push_back(port_pair.first); - } - } - - for (IdString cell_pin : cell_pin_to_remove) { - NPNR_ASSERT(cell->cell_bel_pins.erase(cell_pin)); - } - } - - for (IdString const_port : cell->const_ports) { - disconnectPort(cell->name, const_port); - NPNR_ASSERT(cell->ports.erase(const_port)); - } - - const CellBelMapPOD &cell_pin_map = chip_info->cell_map->cell_bel_map[mapping]; - - IdString gnd_net_name(chip_info->constants->gnd_net_name); - IdString vcc_net_name(chip_info->constants->vcc_net_name); - - for (const auto &pin_map : cell_pin_map.common_pins) { - IdString cell_pin(pin_map.cell_pin); - IdString bel_pin(pin_map.bel_pin); - - // Skip assigned LUT pins, as they are already mapped! - if (cell->lut_cell.lut_pins.count(cell_pin) && cell->cell_bel_pins.count(cell_pin)) { - continue; - } - - if (cell_pin == id_GND) { - if (bind_constants) { - PortInfo port_info; - port_info.name = bel_pin; - port_info.type = PORT_IN; - port_info.net = nullptr; - - auto result = cell->ports.emplace(bel_pin, port_info); - if (result.second) { - cell->cell_bel_pins[bel_pin].push_back(bel_pin); - connectPort(gnd_net_name, cell->name, bel_pin); - cell->const_ports.emplace(bel_pin); - } else { - NPNR_ASSERT(result.first->second.net == getNetByAlias(gnd_net_name)); - auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector({bel_pin})); - NPNR_ASSERT(result2.first->second.at(0) == bel_pin); - NPNR_ASSERT(result2.first->second.size() == 1); - } - } - continue; - } - - if (cell_pin == id_VCC) { - if (bind_constants) { - PortInfo port_info; - port_info.name = bel_pin; - port_info.type = PORT_IN; - port_info.net = nullptr; - - auto result = cell->ports.emplace(bel_pin, port_info); - if (result.second) { - cell->cell_bel_pins[bel_pin].push_back(bel_pin); - connectPort(vcc_net_name, cell->name, bel_pin); - cell->const_ports.emplace(bel_pin); - } else { - NPNR_ASSERT(result.first->second.net == getNetByAlias(vcc_net_name)); - auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector({bel_pin})); - NPNR_ASSERT(result2.first->second.at(0) == bel_pin); - NPNR_ASSERT(result2.first->second.size() == 1); - } - } - continue; - } - - cell->cell_bel_pins[cell_pin].push_back(bel_pin); - } - - for (const auto ¶meter_pin_map : cell_pin_map.parameter_pins) { - IdString param_key(parameter_pin_map.key); - IdString param_value(parameter_pin_map.value); - - auto iter = cell->params.find(param_key); - if (iter == cell->params.end()) { - continue; - } - - if (!cell_parameters.compare_property(getCtx(), cell->type, param_key, iter->second, param_value)) { - continue; - } - -#ifdef DEBUG_CELL_PIN_MAPPING - log_info("parameter match on param_key %s\n", param_key.c_str(this)); -#endif - - for (const auto &pin_map : parameter_pin_map.pins) { - IdString cell_pin(pin_map.cell_pin); - IdString bel_pin(pin_map.bel_pin); -#ifdef DEBUG_CELL_PIN_MAPPING - log_info(" %s => %s\n", cell_pin.c_str(this), bel_pin.c_str(this)); -#endif - - // Skip assigned LUT pins, as they are already mapped! - if (cell->lut_cell.lut_pins.count(cell_pin) && cell->cell_bel_pins.count(cell_pin)) { - continue; - } - - if (cell_pin == id_GND) { - if (bind_constants) { - PortInfo port_info; - port_info.name = bel_pin; - port_info.type = PORT_IN; - port_info.net = nullptr; - - auto result = cell->ports.emplace(bel_pin, port_info); - if (result.second) { - cell->cell_bel_pins[bel_pin].push_back(bel_pin); - connectPort(gnd_net_name, cell->name, bel_pin); - cell->const_ports.emplace(bel_pin); - } else { - NPNR_ASSERT(result.first->second.net == getNetByAlias(gnd_net_name)); - auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector({bel_pin})); - NPNR_ASSERT(result2.first->second.at(0) == bel_pin); - NPNR_ASSERT(result2.first->second.size() == 1); - } - } - continue; - } - - if (cell_pin == id_VCC) { - if (bind_constants) { - PortInfo port_info; - port_info.name = bel_pin; - port_info.type = PORT_IN; - port_info.net = nullptr; - - auto result = cell->ports.emplace(bel_pin, port_info); - if (result.second) { - cell->cell_bel_pins[bel_pin].push_back(bel_pin); - connectPort(vcc_net_name, cell->name, bel_pin); - cell->const_ports.emplace(bel_pin); - } else { - NPNR_ASSERT(result.first->second.net == getNetByAlias(vcc_net_name)); - auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector({bel_pin})); - NPNR_ASSERT(result2.first->second.at(0) == bel_pin); - NPNR_ASSERT(result2.first->second.size() == 1); - } - } - continue; - } - - cell->cell_bel_pins[cell_pin].push_back(bel_pin); - } - } - -#ifdef DEBUG_CELL_PIN_MAPPING - log_info("Pin mapping for cell %s (type: %s)\n", cell->name.c_str(getCtx()), cell->type.c_str(getCtx())); - for (auto &pin_pair : cell->cell_bel_pins) { - log_info(" %s =>", pin_pair.first.c_str(getCtx())); - for (IdString bel_pin : pin_pair.second) { - log(" %s", bel_pin.c_str(getCtx())); - } - log("\n"); - } -#endif -} - -void Arch::map_port_pins(BelId bel, CellInfo *cell) const -{ - IdStringRange pins = getBelPins(bel); - IdString pin = get_only_value(pins); - - NPNR_ASSERT(cell->ports.size() == 1); - cell->cell_bel_pins[cell->ports.begin()->first].clear(); - cell->cell_bel_pins[cell->ports.begin()->first].push_back(pin); -} - -bool Arch::is_net_within_site(const NetInfo &net) const -{ - if (net.driver.cell == nullptr || net.driver.cell->bel == BelId()) { - return false; - } - - BelId driver = net.driver.cell->bel; - int32_t site = bel_info(chip_info, driver).site; - NPNR_ASSERT(site >= 0); - - for (const auto &user : net.users) { - if (user.cell == nullptr || user.cell->bel == BelId()) { - return false; - } - BelId user_bel = user.cell->bel; - - if (user_bel.tile != driver.tile) { - return false; - } - - if (bel_info(chip_info, user_bel).site != site) { - return false; - } - } - - return true; -} - -size_t Arch::get_cell_type_index(IdString cell_type) const -{ - const CellMapPOD &cell_map = *chip_info->cell_map; - int cell_offset = cell_type.index - cell_map.cell_names[0]; - if ((cell_offset < 0 || cell_offset >= cell_map.cell_names.ssize())) { - log_error("Cell %s is not a placable element.\n", cell_type.c_str(this)); - } - NPNR_ASSERT(cell_map.cell_names[cell_offset] == cell_type.index); - - return cell_offset; -} - -void Arch::merge_constant_nets() -{ - NetInfo *gnd_net = nullptr; - NetInfo *vcc_net = nullptr; - - bool need_gnd_source = false; - bool need_vcc_source = false; - - IdString gnd_net_name(chip_info->constants->gnd_net_name); - IdString gnd_cell_type(chip_info->constants->gnd_cell_name); - IdString gnd_cell_port(chip_info->constants->gnd_cell_port); - - auto gnd_iter = nets.find(gnd_net_name); - if (gnd_iter != nets.end()) { - NPNR_ASSERT(gnd_iter->second->driver.cell != nullptr); - NPNR_ASSERT(gnd_iter->second->driver.cell->type == gnd_cell_type); - NPNR_ASSERT(gnd_iter->second->driver.port == gnd_cell_port); - - gnd_net = gnd_iter->second.get(); - } else { - gnd_net = createNet(gnd_net_name); - need_gnd_source = true; - } - - IdString vcc_net_name(chip_info->constants->vcc_net_name); - IdString vcc_cell_type(chip_info->constants->vcc_cell_name); - IdString vcc_cell_port(chip_info->constants->vcc_cell_port); - - auto vcc_iter = nets.find(vcc_net_name); - if (vcc_iter != nets.end()) { - NPNR_ASSERT(vcc_iter->second->driver.cell != nullptr); - NPNR_ASSERT(vcc_iter->second->driver.cell->type == vcc_cell_type); - NPNR_ASSERT(vcc_iter->second->driver.port == vcc_cell_port); - - vcc_net = vcc_iter->second.get(); - } else { - vcc_net = createNet(vcc_net_name); - need_vcc_source = true; - } - - std::vector other_gnd_nets; - std::vector other_vcc_nets; - - for (auto &net_pair : nets) { - if (net_pair.first == gnd_net_name) { - NPNR_ASSERT(net_pair.second.get() == gnd_net); - continue; - } - - if (net_pair.first == vcc_net_name) { - NPNR_ASSERT(net_pair.second.get() == vcc_net); - continue; - } - - NetInfo *net = net_pair.second.get(); - if (net->driver.cell == nullptr) { - continue; - } - - if (net->driver.cell->type == gnd_cell_type) { - NPNR_ASSERT(net->driver.port == gnd_cell_port); - - other_gnd_nets.push_back(net_pair.first); - - if (need_gnd_source) { - IdString driver_cell = net->driver.cell->name; - disconnectPort(driver_cell, gnd_cell_port); - connectPort(gnd_net_name, driver_cell, gnd_cell_port); - need_gnd_source = false; - } - - NPNR_ASSERT(net->driver.port == gnd_cell_port); - indexed_store users_copy = net->users; - for (const PortRef &port_ref : users_copy) { - IdString cell = port_ref.cell->name; - disconnectPort(cell, port_ref.port); - connectPort(gnd_net_name, cell, port_ref.port); - } - - continue; - } - - if (net->driver.cell->type == vcc_cell_type) { - NPNR_ASSERT(net->driver.port == vcc_cell_port); - - other_vcc_nets.push_back(net_pair.first); - - if (need_vcc_source) { - IdString driver_cell = net->driver.cell->name; - disconnectPort(driver_cell, vcc_cell_port); - connectPort(vcc_net_name, driver_cell, vcc_cell_port); - need_vcc_source = false; - } - - NPNR_ASSERT(net->driver.port == vcc_cell_port); - indexed_store users_copy = net->users; - for (const PortRef &port_ref : users_copy) { - IdString cell = port_ref.cell->name; - disconnectPort(cell, port_ref.port); - connectPort(vcc_net_name, cell, port_ref.port); - } - } - } - - for (IdString other_gnd_net : other_gnd_nets) { - NetInfo *net = getNetByAlias(other_gnd_net); - NPNR_ASSERT(net->users.empty()); - if (net->driver.cell) { - PortRef driver = net->driver; - IdString cell_to_remove = driver.cell->name; - disconnectPort(driver.cell->name, driver.port); - NPNR_ASSERT(cells.erase(cell_to_remove)); - } - } - - for (IdString other_vcc_net : other_vcc_nets) { - NetInfo *net = getNetByAlias(other_vcc_net); - NPNR_ASSERT(net->users.empty()); - if (net->driver.cell) { - PortRef driver = net->driver; - IdString cell_to_remove = driver.cell->name; - disconnectPort(driver.cell->name, driver.port); - NPNR_ASSERT(cells.erase(cell_to_remove)); - } - } - - for (IdString other_gnd_net : other_gnd_nets) { - NPNR_ASSERT(nets.erase(other_gnd_net)); - gnd_net->aliases.push_back(other_gnd_net); - net_aliases[other_gnd_net] = gnd_net_name; - } - - for (IdString other_vcc_net : other_vcc_nets) { - NPNR_ASSERT(nets.erase(other_vcc_net)); - vcc_net->aliases.push_back(other_vcc_net); - net_aliases[other_vcc_net] = vcc_net_name; - } - - if (need_gnd_source) { - CellInfo *gnd_cell = createCell(gnd_cell_type, gnd_cell_type); - gnd_cell->addOutput(gnd_cell_port); - connectPort(gnd_net_name, gnd_cell_type, gnd_cell_port); - } - - if (need_vcc_source) { - CellInfo *vcc_cell = createCell(vcc_cell_type, vcc_cell_type); - vcc_cell->addOutput(vcc_cell_port); - connectPort(vcc_net_name, vcc_cell_type, vcc_cell_port); - } -} - -const std::vector &Arch::getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const -{ - auto iter = cell_info->cell_bel_pins.find(pin); - if (iter == cell_info->cell_bel_pins.end()) { - return no_pins; - } else { - return iter->second; - } -} - -void Arch::report_invalid_bel(BelId bel, CellInfo *cell) const -{ - int32_t mapping = bel_info(chip_info, bel).pin_map[get_cell_type_index(cell->type)]; - NPNR_ASSERT(mapping < 0); - log_error("Cell %s (%s) cannot be placed at BEL %s (mapping %d)\n", cell->name.c_str(this), cell->type.c_str(this), - nameOfBel(bel), mapping); -} - -void Arch::decode_lut_cells() -{ - for (auto &cell_pair : cells) { - CellInfo *cell = cell_pair.second.get(); - auto iter = lut_cells.find(cell->type); - if (iter == lut_cells.end()) { - cell->lut_cell.pins.clear(); - cell->lut_cell.equation.clear(); - continue; - } - - const LutCellPOD &lut_cell = *iter->second; - - cell->lut_cell.pins.reserve(lut_cell.input_pins.size()); - for (uint32_t pin : lut_cell.input_pins) { - cell->lut_cell.pins.push_back(IdString(pin)); - cell->lut_cell.lut_pins.emplace(IdString(pin)); - } - - IdString equation_parameter(lut_cell.parameter); - const Property &equation = cell->params.at(equation_parameter); - cell->lut_cell.equation.resize(1 << cell->lut_cell.pins.size()); - - cell->lut_cell.equation = cell_parameters.parse_int_like(getCtx(), cell->type, equation_parameter, equation); - } -} - -void Arch::remove_pip_pseudo_wires(PipId pip, NetInfo *net) -{ - WireId wire; - wire.tile = pip.tile; - const PipInfoPOD &pip_data = pip_info(chip_info, pip); - for (int32_t wire_index : pip_data.pseudo_cell_wires) { - NPNR_ASSERT(wire_index != -1); - wire.index = wire_index; - - auto iter = wire_to_net.find(wire); - NPNR_ASSERT(iter != wire_to_net.end()); - // This wire better already have been assigned to this net! - if (iter->second != net) { - if (iter->second == nullptr) { - log_error("Wire %s part of pseudo pip %s but net is null\n", nameOfWire(wire), nameOfPip(pip)); - } else { - log_error("Wire %s part of pseudo pip %s but net is '%s' instead of net '%s'\n", nameOfWire(wire), - nameOfPip(pip), iter->second->name.c_str(this), net->name.c_str(this)); - } - } - - auto wire_iter = net->wires.find(wire); - if (wire_iter != net->wires.end()) { -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Removing %s from net %s, but it's in net wires\n", nameOfWire(wire), net->name.c_str(this)); - } -#endif - // This wire is part of net->wires, make sure it has no pip, - // but leave it alone. It will get cleaned up via - // unbindWire. - } else { - // This wire is not in net->wires, update wire_to_net. -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Removing %s from net %s in remove_pip_pseudo_wires\n", nameOfWire(wire), - net->name.c_str(this)); - } -#endif - iter->second = nullptr; - } - } - - if (pip_data.pseudo_cell_wires.size() > 0) { - get_tile_status(pip.tile).pseudo_pip_model.unbindPip(getCtx(), pip); - } -} - -void Arch::assign_net_to_wire(WireId wire, NetInfo *net, const char *src, bool require_empty) -{ -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Assigning wire %s to %s from %s\n", nameOfWire(wire), net->name.c_str(this), src); - } -#endif - NPNR_ASSERT(net != nullptr); - auto result = wire_to_net.emplace(wire, net); - if (!result.second) { - // This wire was already in the map, make sure this assignment was - // legal. - if (require_empty) { - NPNR_ASSERT(result.first->second == nullptr); - } else { - NPNR_ASSERT(result.first->second == nullptr || result.first->second == net); - } - result.first->second = net; - } -} - -void Arch::unassign_wire(WireId wire) -{ - NPNR_ASSERT(wire != WireId()); -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("unassign_wire %s\n", nameOfWire(wire)); - } -#endif - - auto iter = wire_to_net.find(wire); - NPNR_ASSERT(iter != wire_to_net.end()); - - NetInfo *net = iter->second; - NPNR_ASSERT(net != nullptr); - - auto &net_wires = net->wires; - auto it = net_wires.find(wire); - NPNR_ASSERT(it != net_wires.end()); - - auto pip = it->second.pip; - if (pip != PipId()) { -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Removing pip %s because it was used to reach wire %s\n", nameOfPip(pip), nameOfWire(wire)); - } -#endif - auto pip_iter = pip_to_net.find(pip); - NPNR_ASSERT(pip_iter != pip_to_net.end()); - NPNR_ASSERT(pip_iter->second == net); - pip_iter->second = nullptr; - remove_pip_pseudo_wires(pip, net); - } - - net_wires.erase(it); -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Removing %s from net %s in unassign_wire\n", nameOfWire(wire), net->name.c_str(this)); - } -#endif - iter->second = nullptr; -} - -void Arch::unbindPip(PipId pip) -{ - NPNR_ASSERT(pip != PipId()); -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("unbindPip %s\n", nameOfPip(pip)); - } -#endif - - auto pip_iter = pip_to_net.find(pip); - NPNR_ASSERT(pip_iter != pip_to_net.end()); - NetInfo *net = pip_iter->second; - NPNR_ASSERT(net != nullptr); - - WireId dst = getPipDstWire(pip); - auto wire_iter = wire_to_net.find(dst); - NPNR_ASSERT(wire_iter != wire_to_net.end()); - NPNR_ASSERT(wire_iter->second == net); - - remove_pip_pseudo_wires(pip, net); - - // Clear the net now. - pip_iter->second = nullptr; -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Removing %s from net %s in unbindPip\n", nameOfWire(dst), net->name.c_str(this)); - } -#endif - wire_iter->second = nullptr; - NPNR_ASSERT(net->wires.erase(dst) == 1); - - refreshUiPip(pip); - refreshUiWire(dst); -} - -void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength) -{ - NPNR_ASSERT(pip != PipId()); -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("bindPip %s (%d/%d) to net %s\n", nameOfPip(pip), pip.tile, pip.index, net->name.c_str(this)); - } -#endif - WireId dst = getPipDstWire(pip); - NPNR_ASSERT(dst != WireId()); - - { - // Pip should not already be assigned to anything. - auto result = pip_to_net.emplace(pip, net); - if (!result.second) { - NPNR_ASSERT(result.first->second == nullptr); - result.first->second = net; - } - } - - assign_net_to_wire(dst, net, "bindPip", /*require_empty=*/true); - assign_pip_pseudo_wires(pip, net); - - { - auto result = net->wires.emplace(dst, PipMap{pip, strength}); - NPNR_ASSERT(result.second); - } - - refreshUiPip(pip); - refreshUiWire(dst); -} - -void Arch::bindWire(WireId wire, NetInfo *net, PlaceStrength strength) -{ - NPNR_ASSERT(wire != WireId()); -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("bindWire %s to net %s\n", nameOfWire(wire), net->name.c_str(this)); - } -#endif - assign_net_to_wire(wire, net, "bindWire", /*require_empty=*/true); - auto &pip_map = net->wires[wire]; - pip_map.pip = PipId(); - pip_map.strength = strength; - refreshUiWire(wire); -} - -bool Arch::checkPipAvailForNet(PipId pip, const NetInfo *net) const -{ - NPNR_ASSERT(pip != PipId()); - auto pip_iter = pip_to_net.find(pip); - if (pip_iter != pip_to_net.end() && pip_iter->second != nullptr) { - bool pip_blocked = false; - if (net == nullptr) { - pip_blocked = true; - } else { - if (net != pip_iter->second) { - pip_blocked = true; - } - } - if (pip_blocked) { -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Pip %s (%d/%d) is not available, tied to net %s\n", getCtx()->nameOfPip(pip), pip.tile, - pip.index, pip_iter->second->name.c_str(getCtx())); - } -#endif - NPNR_ASSERT(pip_iter->first == pip); - return false; - } - } - - WireId src = getPipSrcWire(pip); - WireId dst = getPipDstWire(pip); - - auto wire_iter = wire_to_net.find(dst); - if (wire_iter != wire_to_net.end()) { - NetInfo *wire_net = wire_iter->second; - if (wire_net != nullptr) { - auto net_iter = wire_net->wires.find(dst); - if (net_iter != wire_net->wires.end()) { - if (net == nullptr) { -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Pip %s (%d/%d) is not available, dst wire %s is tied to net %s\n", - getCtx()->nameOfPip(pip), pip.tile, pip.index, getCtx()->nameOfWire(dst), - wire_net->name.c_str(getCtx())); - } -#endif - // dst is already driven in this net, do not allow! - return false; - } else { -#ifdef DEBUG_BINDING - if (getCtx()->verbose && net_iter->second.pip != pip) { - log_info("Pip %s (%d/%d) is not available, dst wire %s is tied to net %s\n", - getCtx()->nameOfPip(pip), pip.tile, pip.index, getCtx()->nameOfWire(dst), - wire_net->name.c_str(getCtx())); - } -#endif - // This pip is available if this pip is already bound to - // this. - return net_iter->second.pip == pip; - } - } - } - } - - // If this pip is a route-though, make sure all of the route-though are valid. - // A route-through is valid if: - // - none of the pseudo-pip wires are bound to a net - // - the pseudo pip wires are bound to the same net and there are no - // lut elements in the tile type: this to prevent the router from choosing - // a pseudo-pip on the same LUT and on a different input pin for the same net. - const TileTypeInfoPOD &tile_type = loc_info(chip_info, pip); - const PipInfoPOD &pip_data = tile_type.pip_data[pip.index]; - WireId wire; - wire.tile = pip.tile; - for (int32_t wire_index : pip_data.pseudo_cell_wires) { - wire.index = wire_index; - NPNR_ASSERT(src != wire); - NPNR_ASSERT(dst != wire); - - NetInfo *other_net = getConflictingWireNet(wire); - bool is_null_net = other_net == nullptr; - if (!is_null_net) { - bool is_same_net = other_net == net; - bool tile_has_luts = tile_type.lut_elements.size() > 0; - if (!is_same_net || tile_has_luts) { -#ifdef DEBUG_BINDING - if (getCtx()->verbose) - log_info("Pip %s is not available because wire %s is tied to a different net " - "(other net: %s - orig net: %s) or the pseudo pip may traverses LUTs\n", - getCtx()->nameOfPip(pip), getCtx()->nameOfWire(wire), other_net->name.c_str(getCtx()), - net == nullptr ? "NULL net" : net->name.c_str(getCtx())); -#endif - return false; - } - } - } - - auto tile_status_iter = tileStatus.find(pip.tile); - - if (pip_data.pseudo_cell_wires.size() > 0) { - // FIXME: This pseudo pip check is incomplete, because constraint - // failures will not be detected. However the current FPGA - // interchange schema does not provide a cell type to place. - if (tile_status_iter != tileStatus.end() && - !tile_status_iter->second.pseudo_pip_model.checkPipAvail(getCtx(), pip)) { - return false; - } - } - - if (pip_data.site != -1 && net != nullptr) { - NPNR_ASSERT(net->driver.cell != nullptr); - NPNR_ASSERT(net->driver.cell->bel != BelId()); - - auto &src_wire_data = tile_type.wire_data[pip_data.src_index]; - auto &dst_wire_data = tile_type.wire_data[pip_data.dst_index]; - - bool valid_pip = false; - if (pip.tile == net->driver.cell->bel.tile) { - if (tile_status_iter == tileStatus.end()) { - // there is no tile status and nothing blocks the validity of this PIP - valid_pip = true; - } else { - const BelInfoPOD &bel_data = tile_type.bel_data[net->driver.cell->bel.index]; - const SiteRouter &site_router = get_site_status(tile_status_iter->second, bel_data); - - const auto &pips = site_router.valid_pips; - auto result = std::find(pips.begin(), pips.end(), pip); - if (result != pips.end()) { - valid_pip = true; - } - } - } - - if (disallow_site_routing && !valid_pip) { - // For now, if driver is not part of this site, and - // disallow_site_routing is set, disallow the edge. - return false; - } - - // FIXME: This check isn't perfect. If a driver and sink are in the - // same site, it is possible for the router to route-thru the site - // ports without hitting a sink, which is not legal in the FPGA - // interchange. - if (!valid_pip) { - // See if one users can enter this site. - if (dst_wire_data.site == -1) { - // This is an output site port, but not for the driver net. - // Disallow. - NPNR_ASSERT(src_wire_data.site == pip_data.site); - } else { - // This might be a valid pip, scan users. - for (auto &user : net->users) { - NPNR_ASSERT(user.cell != nullptr); - if (user.cell->bel == BelId()) { - continue; - } - - auto &bel_data = bel_info(chip_info, user.cell->bel); - if (bel_data.site == pip_data.site) { - valid_pip = true; - break; - } - } - } - } - - if (!valid_pip) { -#ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Pip %s is within a site and not available not right now\n", getCtx()->nameOfPip(pip)); - } -#endif - return false; - } - } - - return true; -} - -bool Arch::checkPipAvail(PipId pip) const { return checkPipAvailForNet(pip, nullptr); } - -std::string Arch::get_chipdb_hash() const { return chipdb_hash; } - -bool Arch::is_inverting(PipId pip) const -{ - auto &tile_type = loc_info(chip_info, pip); - auto &pip_info = tile_type.pip_data[pip.index]; - if (pip_info.site == -1) { - // FIXME: Some routing pips are inverters, but this is missing from - // the chipdb. - return false; - } - - auto &bel_data = tile_type.bel_data[pip_info.bel]; - - // Is a fixed inverter if the non_inverting_pin is another pin. - return bel_data.non_inverting_pin != pip_info.extra_data && bel_data.inverting_pin == pip_info.extra_data; -} - -bool Arch::can_invert(PipId pip) const -{ - auto &tile_type = loc_info(chip_info, pip); - auto &pip_info = tile_type.pip_data[pip.index]; - if (pip_info.site == -1) { - return false; - } - - auto &bel_data = tile_type.bel_data[pip_info.bel]; - - // Can optionally invert if this pip is both the non_inverting_pin and - // inverting pin. - return bel_data.non_inverting_pin == pip_info.extra_data && bel_data.inverting_pin == pip_info.extra_data; -} - -void Arch::mask_bel_pins_on_site_wire(NetInfo *net, WireId wire) -{ - std::vector bel_pins_to_mask; - for (const PortRef &port_ref : net->users) { - if (port_ref.cell->bel == BelId()) { - continue; - } - - NPNR_ASSERT(port_ref.cell != nullptr); - auto iter = port_ref.cell->cell_bel_pins.find(port_ref.port); - if (iter == port_ref.cell->cell_bel_pins.end()) { - continue; - } - - std::vector &cell_bel_pins = iter->second; - bel_pins_to_mask.clear(); - - for (size_t bel_pin_idx = 0; bel_pin_idx < cell_bel_pins.size(); ++bel_pin_idx) { - IdString bel_pin = cell_bel_pins.at(bel_pin_idx); - WireId bel_pin_wire = getBelPinWire(port_ref.cell->bel, bel_pin); - if (bel_pin_wire == wire) { - bel_pins_to_mask.push_back(bel_pin_idx); - } - } - - if (!bel_pins_to_mask.empty()) { - std::vector &masked_cell_bel_pins = port_ref.cell->masked_cell_bel_pins[port_ref.port]; - // Remove in reverse order to preserve indicies. - for (auto riter = bel_pins_to_mask.rbegin(); riter != bel_pins_to_mask.rend(); ++riter) { - size_t bel_pin_idx = *riter; - masked_cell_bel_pins.push_back(cell_bel_pins.at(bel_pin_idx)); - cell_bel_pins.erase(cell_bel_pins.begin() + bel_pin_idx); - } - } - } -} - -void Arch::unmask_bel_pins() -{ - for (auto &cell_pair : cells) { - CellInfo *cell = cell_pair.second.get(); - if (cell->masked_cell_bel_pins.empty()) { - continue; - } - - for (auto &mask_pair : cell->masked_cell_bel_pins) { - IdString cell_port = mask_pair.first; - const std::vector &bel_pins = mask_pair.second; - std::vector &cell_bel_pins = cell->cell_bel_pins[cell_port]; - cell_bel_pins.insert(cell_bel_pins.begin(), bel_pins.begin(), bel_pins.end()); - } - - cell->masked_cell_bel_pins.clear(); - } -} - -void Arch::remove_site_routing() -{ - pool wires_to_unbind; - for (auto &net_pair : nets) { - for (auto &wire_pair : net_pair.second->wires) { - WireId wire = wire_pair.first; - if (wire_pair.second.strength != STRENGTH_PLACER) { - // Only looking for bound placer wires - continue; - } - wires_to_unbind.emplace(wire); - } - } - - for (WireId wire : wires_to_unbind) { - unbindWire(wire); - } - - unmask_bel_pins(); - - IdString id_NEXTPNR_INV = id("$nextpnr_inv"); - IdString id_I = id("I"); - std::vector cells_to_remove; - for (auto &cell_pair : cells) { - CellInfo *cell = cell_pair.second.get(); - if (cell->type != id_NEXTPNR_INV) { - continue; - } - - disconnectPort(cell_pair.first, id_I); - cells_to_remove.push_back(cell_pair.first); - tileStatus.at(cell->bel.tile).boundcells[cell->bel.index] = nullptr; - } - - for (IdString cell_name : cells_to_remove) { - NPNR_ASSERT(cells.erase(cell_name) == 1); - } -} - -void Arch::explain_bel_status(BelId bel) const -{ - if (isBelLocationValid(bel)) { - log_info("BEL %s is valid!\n", nameOfBel(bel)); - return; - } - - auto iter = tileStatus.find(bel.tile); - NPNR_ASSERT(iter != tileStatus.end()); - const TileStatus &tile_status = iter->second; - const CellInfo *cell = tile_status.boundcells[bel.index]; - if (!dedicated_interconnect.isBelLocationValid(bel, cell)) { - dedicated_interconnect.explain_bel_status(bel, cell); - return; - } - - if (io_port_types.count(cell->type)) { - return; - } - - if (!is_cell_valid_constraints(cell, tile_status, /*explain_constraints=*/true)) { - return; - } - - auto &bel_data = bel_info(chip_info, bel); - const SiteRouter &site = get_site_status(tile_status, bel_data); - NPNR_ASSERT(!site.checkSiteRouting(getCtx(), tile_status)); - site.explain(getCtx()); -} - -DelayQuad Arch::getPipDelay(PipId pip) const -{ - // FIXME: Implement when adding timing-driven place and route. - const auto &pip_data = pip_info(chip_info, pip); - - // Scale pseudo-pips by the number of wires they consume to make them - // more expensive than a single edge. This approximation exists soley to - // make the non-timing driven solution avoid thinking that pseudo-pips - // are the same cost as regular pips. - return DelayQuad(100 * (1 + pip_data.pseudo_cell_wires.size())); -} - -const DefaultCellConnsPOD *Arch::get_default_conns(IdString cell_type) const -{ - for (const auto &conn : chip_info->constants->default_conns) { - if (IdString(conn.cell_type) == cell_type) - return &conn; - } - return nullptr; -} - -void Arch::pack_default_conns() -{ - IdString vcc_net_name(chip_info->constants->vcc_net_name); - IdString gnd_net_name(chip_info->constants->gnd_net_name); - Context *ctx = getCtx(); - - std::vector dead_nets; - - for (auto &cell : ctx->cells) { - CellInfo *ci = cell.second.get(); - const DefaultCellConnsPOD *conns = get_default_conns(ci->type); - if (conns == nullptr) - continue; - for (const auto &pin : conns->pins) { - IdString pin_name(pin.pin_name); - // pin missing, create it - if (!ci->ports.count(pin_name)) - ci->addInput(pin_name); - const NetInfo *net = ci->ports.at(pin_name).net; - if (net != nullptr) { - // pin is connected, and driven, nothing to do - if (net->driver.cell != nullptr) - continue; - // pin is connected but undriven, disconnect the existing net - ctx->disconnectPort(ci->name, pin_name); - // remove net if it has no remaining users - if (net->users.empty()) - dead_nets.push_back(net->name); - } - if (pin.value == PIN_VALUE_GND) - ctx->connectPort(gnd_net_name, ci->name, pin_name); - else if (pin.value == PIN_VALUE_VCC) - ctx->connectPort(vcc_net_name, ci->name, pin_name); - else - NPNR_ASSERT(pin.value == PIN_VALUE_FLOAT); - } - } - - // Remove any left-behind nets with no users and no drivers - for (auto net : dead_nets) - ctx->nets.erase(net); -} - -// Instance constraint templates. -template void Arch::ArchConstraints::bindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange); -template void Arch::ArchConstraints::unbindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange); -template bool Arch::ArchConstraints::isValidBelForCellType(const Context *, uint32_t, - const Arch::ArchConstraints::TagState *, - const Arch::ConstraintRange, IdString, IdString, BelId, - bool) const; - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h deleted file mode 100644 index e125d86c..00000000 --- a/fpga_interchange/arch.h +++ /dev/null @@ -1,1196 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Claire Xenia Wolf - * Copyright (C) 2018-19 gatecat - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef FPGA_INTERCHANGE_ARCH_H -#define FPGA_INTERCHANGE_ARCH_H - -#include -#include -#include - -#include "PhysicalNetlist.capnp.h" -#include "arch_api.h" -#include "constraints.h" -#include "nextpnr_types.h" -#include "relptr.h" - -#include "arch_iterators.h" -#include "cell_parameters.h" -#include "chipdb.h" -#include "dedicated_interconnect.h" -#include "lookahead.h" -#include "pseudo_pip_model.h" -#include "site_lut_mapping_cache.h" -#include "site_router.h" -#include "site_routing_cache.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct ArchArgs -{ - std::string chipdb; - std::string package; - bool rebuild_lookahead; - bool dont_write_lookahead; - bool disable_lut_mapping_cache; -}; - -struct ArchRanges -{ - using ArchArgsT = ArchArgs; - // Bels - using AllBelsRangeT = BelRange; - using TileBelsRangeT = BelRange; - using BelAttrsRangeT = std::vector>; - using BelPinsRangeT = IdStringRange; - using CellBelPinRangeT = const std::vector &; - // Wires - using AllWiresRangeT = WireRange; - using DownhillPipRangeT = DownhillPipRange; - using UphillPipRangeT = UphillPipRange; - using WireBelPinRangeT = BelPinRange; - using WireAttrsRangeT = std::vector>; - // Pips - using AllPipsRangeT = AllPipRange; - using PipAttrsRangeT = std::vector>; - // Groups - using AllGroupsRangeT = std::vector; - using GroupBelsRangeT = std::vector; - using GroupWiresRangeT = std::vector; - using GroupPipsRangeT = std::vector; - using GroupGroupsRangeT = std::vector; - // Decals - using DecalGfxRangeT = std::vector; - // Placement validity - using CellTypeRangeT = const IdStringRange; - using BelBucketRangeT = const BelBucketRange; - using BucketBelRangeT = FilteredBelRange; -}; - -static constexpr size_t kMaxState = 8; - -struct TileStatus -{ - std::vector> tags; - std::vector boundcells; - std::vector sites; - PseudoPipModel pseudo_pip_model; -}; - -struct Cluster -{ - uint32_t index; - CellInfo *root; - std::vector cluster_nodes; - dict cell_cluster_node_map; - dict>> cluster_node_cells; -}; - -struct Arch : ArchAPI -{ - boost::iostreams::mapped_file_source blob_file; - const ChipInfoPOD *chip_info; - int32_t package_index; - - // Guard initialization of "by_name" maps if accessed from multiple - // threads on a "const Context *". - mutable std::mutex by_name_mutex; - mutable dict tile_by_name; - mutable dict> site_by_name; - - dict wire_to_net; - dict pip_to_net; - - DedicatedInterconnect dedicated_interconnect; - dict tileStatus; - PseudoPipData pseudo_pip_data; - - ArchArgs args; - Arch(ArchArgs args); - virtual ~Arch(); - void init(); - - std::string getChipName() const final; - - IdString archId() const final { return id(chip_info->name.get()); } - ArchArgs archArgs() const final { return args; } - IdString archArgsToId(ArchArgs args) const final; - - // ------------------------------------------------- - - uint32_t get_tile_index(int x, int y) const { return (y * chip_info->width + x); } - uint32_t get_tile_index(Loc loc) const { return get_tile_index(loc.x, loc.y); } - template - void get_tile_x_y(TileIndex tile_index, CoordIndex *x, CoordIndex *y) const - { - *x = tile_index % chip_info->width; - *y = tile_index / chip_info->width; - } - - template void get_tile_loc(TileIndex tile_index, Loc *loc) const - { - get_tile_x_y(tile_index, &loc->x, &loc->y); - } - - int getGridDimX() const final { return chip_info->width; } - int getGridDimY() const final { return chip_info->height; } - int getTileBelDimZ(int x, int y) const final - { - return chip_info->tile_types[chip_info->tiles[get_tile_index(x, y)].type].bel_data.size(); - } - int getTilePipDimZ(int x, int y) const final - { - return chip_info->tile_types[chip_info->tiles[get_tile_index(x, y)].type].site_types.size(); - } - char getNameDelimiter() const final { return '/'; } - - std::string get_part() const; - - // ------------------------------------------------- - - void setup_byname() const; - - BelId getBelByName(IdStringList name) const final; - - IdStringList getBelName(BelId bel) const final - { - NPNR_ASSERT(bel != BelId()); - const SiteInstInfoPOD &site = get_site_inst(bel); - std::array ids{id(site.name.get()), IdString(bel_info(chip_info, bel).name)}; - return IdStringList(ids); - } - - uint32_t getBelChecksum(BelId bel) const final { return bel.index; } - - void map_cell_pins(CellInfo *cell, int32_t mapping, bool bind_constants); - void map_port_pins(BelId bel, CellInfo *cell) const; - - TileStatus &get_tile_status(int32_t tile) - { - auto result = tileStatus.emplace(tile, TileStatus()); - if (result.second) { - auto &tile_type = chip_info->tile_types[chip_info->tiles[tile].type]; - result.first->second.boundcells.resize(tile_type.bel_data.size(), nullptr); - result.first->second.tags.resize(default_tags.size()); - - result.first->second.sites.reserve(tile_type.site_types.size()); - for (size_t i = 0; i < tile_type.site_types.size(); ++i) { - result.first->second.sites.push_back(SiteRouter(i)); - } - - result.first->second.pseudo_pip_model.init(getCtx(), tile); - } - - return result.first->second; - } - - const SiteRouter &get_site_status(const TileStatus &tile_status, const BelInfoPOD &bel_data) const - { - return tile_status.sites.at(bel_data.site); - } - - SiteRouter &get_site_status(TileStatus &tile_status, const BelInfoPOD &bel_data) - { - return tile_status.sites.at(bel_data.site); - } - - BelId get_vcc_bel() const - { - auto &constants = *chip_info->constants; - BelId bel; - bel.tile = constants.vcc_bel_tile; - bel.index = constants.vcc_bel_index; - return bel; - } - - BelId get_gnd_bel() const - { - auto &constants = *chip_info->constants; - BelId bel; - bel.tile = constants.gnd_bel_tile; - bel.index = constants.gnd_bel_index; - return bel; - } - - PhysicalNetlist::PhysNetlist::NetType get_net_type(NetInfo *net) const - { - NPNR_ASSERT(net != nullptr); - IdString gnd_cell_name(chip_info->constants->gnd_cell_name); - IdString gnd_cell_port(chip_info->constants->gnd_cell_port); - - IdString vcc_cell_name(chip_info->constants->vcc_cell_name); - IdString vcc_cell_port(chip_info->constants->vcc_cell_port); - if (net->driver.cell->type == gnd_cell_name && net->driver.port == gnd_cell_port) { - return PhysicalNetlist::PhysNetlist::NetType::GND; - } else if (net->driver.cell->type == vcc_cell_name && net->driver.port == vcc_cell_port) { - return PhysicalNetlist::PhysNetlist::NetType::VCC; - } else { - return PhysicalNetlist::PhysNetlist::NetType::SIGNAL; - } - } - - void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) final - { - NPNR_ASSERT(bel != BelId()); - - TileStatus &tile_status = get_tile_status(bel.tile); - NPNR_ASSERT(tile_status.boundcells[bel.index] == nullptr); - - const auto &bel_data = bel_info(chip_info, bel); - NPNR_ASSERT(bel_data.category == BEL_CATEGORY_LOGIC); - - if (io_port_types.count(cell->type) == 0) { - int32_t mapping = bel_info(chip_info, bel).pin_map[get_cell_type_index(cell->type)]; - if (mapping < 0) { - report_invalid_bel(bel, cell); - } - NPNR_ASSERT(mapping >= 0); - - if (cell->cell_mapping != mapping) { - map_cell_pins(cell, mapping, /*bind_constants=*/false); - } - constraints.bindBel(tile_status.tags.data(), get_cell_constraints(bel, cell->type)); - - // Clean previous cell placement in tile - if (cell->bel != BelId()) { - TileStatus &prev_tile_status = get_tile_status(cell->bel.tile); - NPNR_ASSERT(prev_tile_status.boundcells[cell->bel.index] != nullptr); - - const auto &prev_bel_data = bel_info(chip_info, cell->bel); - NPNR_ASSERT(prev_bel_data.category == BEL_CATEGORY_LOGIC); - - get_site_status(prev_tile_status, prev_bel_data).unbindBel(cell); - prev_tile_status.boundcells[cell->bel.index] = nullptr; - - constraints.unbindBel(prev_tile_status.tags.data(), get_cell_constraints(cell->bel, cell->type)); - } - } else { - map_port_pins(bel, cell); - // FIXME: Probably need to actually constraint io port cell/bel, - // but the current BBA emission doesn't support that. This only - // really matters if the placer can choose IO port locations. - } - - get_site_status(tile_status, bel_data).bindBel(cell); - - tile_status.boundcells[bel.index] = cell; - - cell->bel = bel; - cell->belStrength = strength; - - refreshUiBel(bel); - } - - void unbindBel(BelId bel) final - { - NPNR_ASSERT(bel != BelId()); - - TileStatus &tile_status = get_tile_status(bel.tile); - NPNR_ASSERT(tile_status.boundcells[bel.index] != nullptr); - - CellInfo *cell = tile_status.boundcells[bel.index]; - tile_status.boundcells[bel.index] = nullptr; - - cell->bel = BelId(); - cell->belStrength = STRENGTH_NONE; - - // FIXME: Probably need to actually constraint io port cell/bel, - // but the current BBA emission doesn't support that. This only - // really matters if the placer can choose IO port locations. - if (io_port_types.count(cell->type) == 0) { - constraints.unbindBel(tile_status.tags.data(), get_cell_constraints(bel, cell->type)); - } - - const auto &bel_data = bel_info(chip_info, bel); - get_site_status(tile_status, bel_data).unbindBel(cell); - - refreshUiBel(bel); - } - - bool checkBelAvail(BelId bel) const final - { - // FIXME: This could consult the constraint system to see if this BEL - // is blocked (e.g. site type is wrong). - return getBoundBelCell(bel) == nullptr; - } - - CellInfo *getBoundBelCell(BelId bel) const final - { - NPNR_ASSERT(bel != BelId()); - auto iter = tileStatus.find(bel.tile); - if (iter == tileStatus.end()) { - return nullptr; - } else { - return iter->second.boundcells[bel.index]; - } - } - - CellInfo *getConflictingBelCell(BelId bel) const final - { - NPNR_ASSERT(bel != BelId()); - // FIXME: This could consult the constraint system to see why this BEL - // is blocked. - return getBoundBelCell(bel); - } - - BelRange getBels() const final - { - BelRange range; - range.b.cursor_tile = 0; - range.b.cursor_index = -1; - range.b.chip = chip_info; - ++range.b; //-1 and then ++ deals with the case of no Bels in the first tile - range.e.cursor_tile = chip_info->width * chip_info->height; - range.e.cursor_index = 0; - range.e.chip = chip_info; - return range; - } - - Loc getBelLocation(BelId bel) const final - { - NPNR_ASSERT(bel != BelId()); - Loc loc; - get_tile_x_y(bel.tile, &loc.x, &loc.y); - loc.z = bel.index; - return loc; - } - - BelId getBelByLocation(Loc loc) const final; - BelRange getBelsByTile(int x, int y) const final; - - bool getBelGlobalBuf(BelId bel) const final - { - auto &bel_data = bel_info(chip_info, bel); - IdString bel_name(bel_data.name); - - // Note: Check profiles and see if this should be something other than - // a linear scan. Expectation is that for most arches, this will be - // fast enough. - for (int32_t global_bel : chip_info->cell_map->global_buffers) { - IdString global_bel_name(global_bel); - if (bel_name == global_bel_name) { - return true; - } - } - - return false; - } - - bool getBelHidden(BelId bel) const final { return bel_info(chip_info, bel).category != BEL_CATEGORY_LOGIC; } - - IdString getBelType(BelId bel) const final - { - NPNR_ASSERT(bel != BelId()); - return IdString(bel_info(chip_info, bel).type); - } - - std::vector> getBelAttrs(BelId bel) const final; - - int get_bel_pin_index(BelId bel, IdString pin) const - { - NPNR_ASSERT(bel != BelId()); - int num_bel_wires = bel_info(chip_info, bel).num_bel_wires; - const int32_t *ports = bel_info(chip_info, bel).ports.get(); - for (int i = 0; i < num_bel_wires; i++) { - if (ports[i] == pin.index) { - return i; - } - } - - return -1; - } - - WireId getBelPinWire(BelId bel, IdString pin) const final; - PortType getBelPinType(BelId bel, IdString pin) const final; - - IdStringRange getBelPins(BelId bel) const final - { - NPNR_ASSERT(bel != BelId()); - - int num_bel_wires = bel_info(chip_info, bel).num_bel_wires; - const int32_t *ports = bel_info(chip_info, bel).ports.get(); - - IdStringRange str_range; - str_range.b.cursor = &ports[0]; - str_range.e.cursor = &ports[num_bel_wires]; - - return str_range; - } - - const std::vector &getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const final; - - // ------------------------------------------------- - - WireId getWireByName(IdStringList name) const final; - - const TileWireInfoPOD &wire_info(WireId wire) const - { - if (wire.tile == -1) { - const TileWireRefPOD &wr = chip_info->nodes[wire.index].tile_wires[0]; - return chip_info->tile_types[chip_info->tiles[wr.tile].type].wire_data[wr.index]; - } else { - return loc_info(chip_info, wire).wire_data[wire.index]; - } - } - - IdStringList getWireName(WireId wire) const final - { - NPNR_ASSERT(wire != WireId()); - if (wire.tile != -1) { - const auto &tile_type = loc_info(chip_info, wire); - if (tile_type.wire_data[wire.index].site != -1) { - const SiteInstInfoPOD &site = get_site_inst(wire); - std::array ids{id(site.name.get()), IdString(tile_type.wire_data[wire.index].name)}; - return IdStringList(ids); - } - } - - int32_t tile = wire.tile == -1 ? chip_info->nodes[wire.index].tile_wires[0].tile : wire.tile; - IdString tile_name = id(chip_info->tiles[tile].name.get()); - std::array ids{tile_name, IdString(wire_info(wire).name)}; - return IdStringList(ids); - } - - IdString getWireType(WireId wire) const final; - std::vector> getWireAttrs(WireId wire) const final; - - uint32_t getWireChecksum(WireId wire) const final { return wire.index; } - - void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) final; - - void unbindWire(WireId wire) final - { - NPNR_ASSERT(wire != WireId()); - unassign_wire(wire); - refreshUiWire(wire); - } - - bool checkWireAvail(WireId wire) const final - { - NPNR_ASSERT(wire != WireId()); - auto w2n = wire_to_net.find(wire); - return w2n == wire_to_net.end() || w2n->second == nullptr; - } - - NetInfo *getBoundWireNet(WireId wire) const final - { - NPNR_ASSERT(wire != WireId()); - auto w2n = wire_to_net.find(wire); - return w2n == wire_to_net.end() ? nullptr : w2n->second; - } - - WireId getConflictingWireWire(WireId wire) const final { return wire; } - - IdString getWireConstantValue(WireId wire) const final { return {}; } - - NetInfo *getConflictingWireNet(WireId wire) const final - { - NPNR_ASSERT(wire != WireId()); - auto w2n = wire_to_net.find(wire); - return w2n == wire_to_net.end() ? nullptr : w2n->second; - } - - DelayQuad getWireDelay(WireId wire) const final { return DelayQuad(0); } - - TileWireRange get_tile_wire_range(WireId wire) const - { - TileWireRange range; - range.b.chip = chip_info; - range.b.baseWire = wire; - range.b.cursor = -1; - ++range.b; - - range.e.chip = chip_info; - range.e.baseWire = wire; - if (wire.tile == -1) { - range.e.cursor = chip_info->nodes[wire.index].tile_wires.size(); - } else { - range.e.cursor = 1; - } - return range; - } - - BelPinRange getWireBelPins(WireId wire) const final - { - BelPinRange range; - NPNR_ASSERT(wire != WireId()); - - TileWireRange twr = get_tile_wire_range(wire); - range.b.chip = chip_info; - range.b.twi = twr.b; - range.b.twi_end = twr.e; - range.b.cursor = -1; - ++range.b; - - range.e.chip = chip_info; - range.e.twi = twr.e; - range.e.twi_end = twr.e; - range.e.cursor = 0; - return range; - } - - WireRange getWires() const final - { - WireRange range; - range.b.chip = chip_info; - range.b.cursor_tile = -1; - range.b.cursor_index = 0; - range.e.chip = chip_info; - range.e.cursor_tile = chip_info->tiles.size(); - range.e.cursor_index = 0; - return range; - } - - bool is_site_wire(WireId wire) const; - WireCategory get_wire_category(WireId wire) const; - - // ------------------------------------------------- - - PipId getPipByName(IdStringList name) const final; - IdStringList getPipName(PipId pip) const final; - IdString getPipType(PipId pip) const final; - std::vector> getPipAttrs(PipId pip) const final; - - void assign_net_to_wire(WireId wire, NetInfo *net, const char *src, bool require_empty); - - void assign_pip_pseudo_wires(PipId pip, NetInfo *net) - { - NPNR_ASSERT(net != nullptr); - WireId wire; - wire.tile = pip.tile; - const PipInfoPOD &pip_data = pip_info(chip_info, pip); - for (int32_t wire_index : pip_data.pseudo_cell_wires) { - wire.index = wire_index; - if (getBoundWireNet(wire) != net) - assign_net_to_wire(wire, net, "pseudo", /*require_empty=*/true); - } - - if (pip_data.pseudo_cell_wires.size() > 0) { - get_tile_status(pip.tile).pseudo_pip_model.bindPip(getCtx(), pip); - } - } - - void remove_pip_pseudo_wires(PipId pip, NetInfo *net); - - void unassign_wire(WireId wire); - - void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) final; - - void unbindPip(PipId pip) final; - - bool checkPipAvail(PipId pip) const final; - bool checkPipAvailForNet(PipId pip, const NetInfo *net) const final; - - NetInfo *getBoundPipNet(PipId pip) const final - { - NPNR_ASSERT(pip != PipId()); - auto p2n = pip_to_net.find(pip); - return p2n == pip_to_net.end() ? nullptr : p2n->second; - } - - WireId getConflictingPipWire(PipId pip) const final - { - // FIXME: This doesn't account for pseudo pips. - return getPipDstWire(pip); - } - - NetInfo *getConflictingPipNet(PipId pip) const final - { - // FIXME: This doesn't account for pseudo pips. - auto p2n = pip_to_net.find(pip); - return p2n == pip_to_net.end() ? nullptr : p2n->second; - } - - AllPipRange getPips() const final - { - AllPipRange range; - range.b.cursor_tile = 0; - range.b.cursor_index = -1; - range.b.chip = chip_info; - ++range.b; //-1 and then ++ deals with the case of no wries in the first tile - range.e.cursor_tile = chip_info->width * chip_info->height; - range.e.cursor_index = 0; - range.e.chip = chip_info; - return range; - } - - Loc getPipLocation(PipId pip) const final - { - Loc loc; - get_tile_loc(pip.tile, &loc); - loc.z = 0; - return loc; - } - - uint32_t getPipChecksum(PipId pip) const final { return pip.index; } - - WireId getPipSrcWire(PipId pip) const final NPNR_ALWAYS_INLINE - { - return canonical_wire(chip_info, pip.tile, loc_info(chip_info, pip).pip_data[pip.index].src_index); - } - - WireId getPipDstWire(PipId pip) const final NPNR_ALWAYS_INLINE - { - return canonical_wire(chip_info, pip.tile, loc_info(chip_info, pip).pip_data[pip.index].dst_index); - } - - DelayQuad getPipDelay(PipId pip) const final; - - DownhillPipRange getPipsDownhill(WireId wire) const final - { - DownhillPipRange range; - NPNR_ASSERT(wire != WireId()); - TileWireRange twr = get_tile_wire_range(wire); - range.b.chip = chip_info; - range.b.twi = twr.b; - range.b.twi_end = twr.e; - range.b.cursor = -1; - ++range.b; - range.e.chip = chip_info; - range.e.twi = twr.e; - range.e.twi_end = twr.e; - range.e.cursor = 0; - return range; - } - - UphillPipRange getPipsUphill(WireId wire) const final - { - UphillPipRange range; - NPNR_ASSERT(wire != WireId()); - TileWireRange twr = get_tile_wire_range(wire); - range.b.chip = chip_info; - range.b.twi = twr.b; - range.b.twi_end = twr.e; - range.b.cursor = -1; - ++range.b; - range.e.chip = chip_info; - range.e.twi = twr.e; - range.e.twi_end = twr.e; - range.e.cursor = 0; - return range; - } - - // ------------------------------------------------- - - // FIXME: Use groups to get access to sites. - GroupId getGroupByName(IdStringList name) const final { return GroupId(); } - IdStringList getGroupName(GroupId group) const final { return IdStringList(); } - std::vector getGroups() const final { return {}; } - std::vector getGroupBels(GroupId group) const final { return {}; } - std::vector getGroupWires(GroupId group) const final { return {}; } - std::vector getGroupPips(GroupId group) const final { return {}; } - std::vector getGroupGroups(GroupId group) const final { return {}; } - - // ------------------------------------------------- - delay_t estimateDelay(WireId src, WireId dst) const final; - delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const final; - BoundingBox getRouteBoundingBox(WireId src, WireId dst) const final; - delay_t getDelayEpsilon() const final { return 20; } - delay_t getRipupDelayPenalty() const final { return 120; } - float getDelayNS(delay_t v) const final { return v * 0.001; } - delay_t getDelayFromNS(float ns) const final { return delay_t(ns * 1000); } - uint32_t getDelayChecksum(delay_t v) const final { return v; } - - bool getArcDelayOverride(const NetInfo *net_info, const PortRef &sink, DelayQuad &delay) const final - { - return false; - } - - // ------------------------------------------------- - - void place_iobufs(WireId pad_wire, NetInfo *net, - const dict &tightly_attached_bels, - pool *placed_cells); - - void pack_ports(); - - // Clusters - void pack_cluster(); - void prepare_cluster(const ClusterPOD *cluster, uint32_t index); - void prepare_macro_cluster(const ClusterPOD *cluster, uint32_t index); - dict clusters; - - // User constraints - void place_constraints(); - - void decode_lut_cells(); - - const GlobalCellPOD *global_cell_info(IdString cell_type) const; - void place_globals(); - void route_globals(); - - bool pack() final; - bool place() final; - bool route() final; - // ------------------------------------------------- - - std::vector getDecalGraphics(DecalId decal) const final; - - DecalXY getBelDecal(BelId bel) const final; - DecalXY getWireDecal(WireId wire) const final; - DecalXY getPipDecal(PipId pip) const final; - DecalXY getGroupDecal(GroupId group) const final; - - // ------------------------------------------------- - - // Get the delay through a cell from one port to another, returning false - // if no path exists. This only considers combinational delays, as required by the Arch API - bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const; - // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port - TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const; - // Get the TimingClockingInfo of a port - TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const; - - // ------------------------------------------------- - - const BelBucketRange getBelBuckets() const final - { - BelBucketRange bel_bucket_range; - bel_bucket_range.b.cursor.cursor = chip_info->bel_buckets.begin(); - bel_bucket_range.e.cursor.cursor = chip_info->bel_buckets.end(); - return bel_bucket_range; - } - - BelBucketId getBelBucketForBel(BelId bel) const final - { - BelBucketId bel_bucket; - bel_bucket.name = IdString(bel_info(chip_info, bel).bel_bucket); - return bel_bucket; - } - - const IdStringRange getCellTypes() const final - { - const CellMapPOD &cell_map = *chip_info->cell_map; - - IdStringRange id_range; - id_range.b.cursor = cell_map.cell_names.begin(); - id_range.e.cursor = cell_map.cell_names.end(); - - return id_range; - } - - IdString getBelBucketName(BelBucketId bucket) const final { return bucket.name; } - - BelBucketId getBelBucketByName(IdString name) const final - { - for (BelBucketId bel_bucket : getBelBuckets()) { - if (bel_bucket.name == name) { - return bel_bucket; - } - } - - NPNR_ASSERT_FALSE("Failed to find BEL bucket for name."); - return BelBucketId(); - } - - size_t get_cell_type_index(IdString cell_type) const; - - BelBucketId getBelBucketForCellType(IdString cell_type) const final - { - if (io_port_types.count(cell_type)) { - BelBucketId bucket; - bucket.name = id("IOPORTS"); - return bucket; - } - - BelBucketId bucket; - const CellMapPOD &cell_map = *chip_info->cell_map; - bucket.name = IdString(cell_map.cell_bel_buckets[get_cell_type_index(cell_type)]); - return bucket; - } - - FilteredBelRange getBelsInBucket(BelBucketId bucket) const final - { - BelRange range = getBels(); - FilteredBelRange filtered_range(range.begin(), range.end(), - [this, bucket](BelId bel) { return getBelBucketForBel(bel) == bucket; }); - - return filtered_range; - } - - bool isValidBelForCellType(IdString cell_type, BelId bel) const final - { - if (io_port_types.count(cell_type)) { - return pads.count(bel) > 0; - } - - const auto &bel_data = bel_info(chip_info, bel); - if (bel_data.category != BEL_CATEGORY_LOGIC) { - return false; - } - - auto cell_type_index = get_cell_type_index(cell_type); - return bel_data.pin_map[cell_type_index] != -1; - } - - bool is_cell_valid_constraints(const CellInfo *cell, const TileStatus &tile_status, bool explain) const - { - if (io_port_types.count(cell->type)) { - return true; - } - - BelId bel = cell->bel; - NPNR_ASSERT(bel != BelId()); - - return constraints.isValidBelForCellType(getCtx(), get_constraint_prototype(bel), tile_status.tags.data(), - get_cell_constraints(bel, cell->type), - id(chip_info->tiles[bel.tile].name.get()), cell->name, bel, explain); - } - - // Return true whether all Bels at a given location are valid - bool isBelLocationValid(BelId bel, bool explain_invalid = false) const final - { - auto iter = tileStatus.find(bel.tile); - if (iter == tileStatus.end()) { - return true; - } - const TileStatus &tile_status = iter->second; - CellInfo *cell = tile_status.boundcells[bel.index]; - auto &bel_data = bel_info(chip_info, bel); - auto &site_status = get_site_status(tile_status, bel_data); - - if (cell != nullptr) { - if (!dedicated_interconnect.isBelLocationValid(bel, cell)) - return false; - - if (io_port_types.count(cell->type)) { - // FIXME: Probably need to actually constraint io port cell/bel, - // but the current BBA emission doesn't support that. This only - // really matters if the placer can choose IO port locations. - return true; - } - - if (!is_cell_valid_constraints(cell, tile_status, explain_constraints)) { - return false; - } - - for (auto ci : site_status.cells_in_site) { - if (ci->cluster != ClusterId() && ci->cluster != cell->cluster && - cluster_info(chip_info, clusters.at(ci->cluster).index).disallow_other_cells) - return false; - - if (cell->cluster != ClusterId() && ci->cluster != cell->cluster && - cluster_info(chip_info, clusters.at(cell->cluster).index).disallow_other_cells) - return false; - } - } - - // Still check site status if cell is nullptr; as other bels in the site could be illegal (for example when - // dedicated paths can no longer be used after ripping up a cell) - bool routing_status = site_status.checkSiteRouting(getCtx(), tile_status); - - return routing_status; - } - - CellInfo *getClusterRootCell(ClusterId cluster) const override; - BoundingBox getClusterBounds(ClusterId cluster) const override; - Loc getClusterOffset(const CellInfo *cell) const override; - bool isClusterStrict(const CellInfo *cell) const override; - bool normal_cluster_placement(const Context *, const Cluster &, const ClusterPOD &, CellInfo *, BelId, - std::vector> &) const; - bool macro_cluster_placement(const Context *, const Cluster &, const ClusterPOD &, CellInfo *, BelId, - std::vector> &) const; - bool getClusterPlacement(ClusterId cluster, BelId root_bel, - std::vector> &placement) const override; - - IdString get_bel_tiletype(BelId bel) const { return IdString(loc_info(chip_info, bel).name); } - - dict sink_locs, source_locs; - // ------------------------------------------------- - void assignArchInfo() final {} - - // ------------------------------------------------- - - static const std::string defaultPlacer; - static const std::vector availablePlacers; - - static const std::string defaultRouter; - static const std::vector availableRouters; - - // ------------------------------------------------- - void read_logical_netlist(const std::string &filename); - void write_physical_netlist(const std::string &filename) const; - void parse_xdc(const std::string &filename); - - pool io_port_types; - pool pads; - - bool is_site_port(PipId pip) const - { - const PipInfoPOD &pip_data = pip_info(chip_info, pip); - if (pip_data.site == -1) { - return false; - } - - BelId bel; - bel.tile = pip.tile; - bel.index = pip_data.bel; - - const BelInfoPOD &bel_data = bel_info(chip_info, bel); - - return bel_data.category == BEL_CATEGORY_SITE_PORT; - } - - // Is the driver and all users of this net located within the same site? - // - // Returns false if any element of the net is not placed. - bool is_net_within_site(const NetInfo &net) const; - - using ArchConstraints = Constraints; - ArchConstraints constraints; - std::vector default_tags; - bool explain_constraints; - - struct StateRange - { - const int32_t *b; - const int32_t *e; - - const int32_t *begin() const { return b; } - const int32_t *end() const { return e; } - }; - - struct Constraint : ArchConstraints::Constraint - { - const CellConstraintPOD *constraint; - Constraint(const CellConstraintPOD *constraint) : constraint(constraint) {} - - size_t tag() const final { return constraint->tag; } - - ArchConstraints::ConstraintType constraint_type() const final - { - return Constraints::ConstraintType(constraint->constraint_type); - } - - ArchConstraints::ConstraintStateType state() const final - { - NPNR_ASSERT(constraint_type() == Constraints::CONSTRAINT_TAG_IMPLIES); - NPNR_ASSERT(constraint->states.size() == 1); - return constraint->states[0]; - } - - StateRange states() const final - { - StateRange range; - range.b = constraint->states.get(); - range.e = range.b + constraint->states.size(); - - return range; - } - }; - - struct ConstraintIterator - { - const CellConstraintPOD *constraint; - ConstraintIterator() {} - - ConstraintIterator operator++() - { - ++constraint; - return *this; - } - - bool operator!=(const ConstraintIterator &other) const { return constraint != other.constraint; } - - bool operator==(const ConstraintIterator &other) const { return constraint == other.constraint; } - - Constraint operator*() const { return Constraint(constraint); } - }; - - struct ConstraintRange - { - ConstraintIterator b, e; - - ConstraintIterator begin() const { return b; } - ConstraintIterator end() const { return e; } - }; - - uint32_t get_constraint_prototype(BelId bel) const { return chip_info->tiles[bel.tile].type; } - - ConstraintRange get_cell_constraints(BelId bel, IdString cell_type) const - { - const auto &bel_data = bel_info(chip_info, bel); - NPNR_ASSERT(bel_data.category == BEL_CATEGORY_LOGIC); - - int32_t mapping = bel_data.pin_map[get_cell_type_index(cell_type)]; - NPNR_ASSERT(mapping >= 0); - - auto &cell_bel_map = chip_info->cell_map->cell_bel_map[mapping]; - ConstraintRange range; - range.b.constraint = cell_bel_map.constraints.get(); - range.e.constraint = range.b.constraint + cell_bel_map.constraints.size(); - - return range; - } - - const char *get_site_name(int32_t tile, size_t site) const - { - return site_inst_info(chip_info, tile, site).name.get(); - } - - const char *get_site_name(BelId bel) const - { - auto &bel_data = bel_info(chip_info, bel); - return get_site_name(bel.tile, bel_data.site); - } - - const SiteInstInfoPOD &get_site_inst(BelId bel) const - { - auto &bel_data = bel_info(chip_info, bel); - return site_inst_info(chip_info, bel.tile, bel_data.site); - } - - const SiteInstInfoPOD &get_site_inst(WireId wire) const - { - auto &wire_data = wire_info(wire); - NPNR_ASSERT(wire_data.site != -1); - return site_inst_info(chip_info, wire.tile, wire_data.site); - } - - const SiteInstInfoPOD &get_site_inst(PipId pip) const - { - auto &pip_data = pip_info(chip_info, pip); - return site_inst_info(chip_info, pip.tile, pip_data.site); - } - - // Is this bel synthetic (e.g. added during import process)? - // - // This is generally used for constant networks, but can also be used for - // static partitions. - bool is_bel_synthetic(BelId bel) const - { - const BelInfoPOD &bel_data = bel_info(chip_info, bel); - - return bel_data.synthetic != 0; - } - - // Is this pip synthetic (e.g. added during import process)? - // - // This is generally used for constant networks, but can also be used for - // static partitions. - bool is_pip_synthetic(PipId pip) const - { - auto &pip_data = pip_info(chip_info, pip); - if (pip_data.site == -1) { - return pip_data.extra_data == -1; - } else { - BelId bel; - bel.tile = pip.tile; - bel.index = pip_data.bel; - return is_bel_synthetic(bel); - } - } - - bool is_same_site(WireId wire_a, WireId wire_b) const - { - if (wire_a.tile == -1) { - return false; - } - - if (wire_a.tile != wire_b.tile) { - return false; - } - - auto &wire_a_data = wire_info(wire_a); - auto &wire_b_data = wire_info(wire_b); - - return wire_a_data.site == wire_b_data.site && wire_a_data.site != -1; - } - - bool is_wire_in_site(WireId wire) const - { - if (wire.tile == -1) { - return false; - } - - auto &wire_data = wire_info(wire); - return wire_data.site != -1; - } - - // Does this pip always invert its signal? - bool is_inverting(PipId pip) const; - - // Can this pip optional invert its signal? - bool can_invert(PipId pip) const; - - void merge_constant_nets(); - void report_invalid_bel(BelId bel, CellInfo *cell) const; - - std::vector no_pins; - IdString gnd_cell_pin; - IdString vcc_cell_pin; - std::vector> lut_elements; - dict lut_cells; - - // Defines the max number of LUT cells in a site and LUT pins - // to allow a correct functioning of the site lut mapping cache - int max_lut_cells; - int max_lut_pins; - - // Of the LUT cells, which is used for wires? - // Note: May be null in arch's without wire LUT types. Assumption is - // that these arch's don't need wire LUT's because the LUT share is simple - // enough to avoid it. - const LutCellPOD *wire_lut; - - std::regex raw_bin_constant; - std::regex verilog_bin_constant; - std::regex verilog_hex_constant; - void read_lut_equation(DynamicBitarray<> *equation, const Property &equation_parameter) const; - - IdString id_GND; - IdString id_VCC; - Lookahead lookahead; - mutable RouteNodeStorage node_storage; - mutable SiteRoutingCache site_routing_cache; - mutable SiteLutMappingCache site_lut_mapping_cache; - bool disallow_site_routing; - CellParameters cell_parameters; - - std::string chipdb_hash; - std::string get_chipdb_hash() const; - - // Masking moves BEL pins from cell_bel_pins to masked_cell_bel_pins for - // the purposes routing. The idea is that masked BEL pins are already - // handled during site routing, and they shouldn't be visible to the - // router. - void mask_bel_pins_on_site_wire(NetInfo *net, WireId wire); - - // This removes pips and wires bound by the site router, and unmasks all - // BEL pins masked during site routing. - void remove_site_routing(); - - // This unmasks any BEL pins that were masked when site routing was bound. - void unmask_bel_pins(); - - void explain_bel_status(BelId bel) const; - - const DefaultCellConnsPOD *get_default_conns(IdString cell_type) const; - void pack_default_conns(); - - dict> macro_to_cells; - void expand_macros(); -}; - -NEXTPNR_NAMESPACE_END - -#endif /* FPGA_INTERCHANGE_ARCH_H */ diff --git a/fpga_interchange/arch_iterators.h b/fpga_interchange/arch_iterators.h deleted file mode 100644 index c59a7d18..00000000 --- a/fpga_interchange/arch_iterators.h +++ /dev/null @@ -1,497 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef ARCH_ITERATORS_H -#define ARCH_ITERATORS_H - -#include "chipdb.h" -#include "nextpnr_namespaces.h" -#include "nextpnr_types.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct BelIterator -{ - const ChipInfoPOD *chip; - int cursor_index; - int cursor_tile; - - BelIterator operator++() - { - cursor_index++; - while (cursor_tile < chip->tiles.ssize() && cursor_index >= tile_info(chip, cursor_tile).bel_data.ssize()) { - cursor_index = 0; - cursor_tile++; - } - return *this; - } - BelIterator operator++(int) - { - BelIterator prior(*this); - ++(*this); - return prior; - } - - bool operator!=(const BelIterator &other) const - { - return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile; - } - - bool operator==(const BelIterator &other) const - { - return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile; - } - - BelId operator*() const - { - BelId ret; - ret.tile = cursor_tile; - ret.index = cursor_index; - return ret; - } -}; - -struct BelRange -{ - BelIterator b, e; - BelIterator begin() const { return b; } - BelIterator end() const { return e; } -}; - -struct FilteredBelIterator -{ - std::function filter; - BelIterator b, e; - - FilteredBelIterator operator++() - { - ++b; - while (b != e) { - if (filter(*b)) { - break; - } - - ++b; - } - return *this; - } - - bool operator!=(const FilteredBelIterator &other) const - { - NPNR_ASSERT(e == other.e); - return b != other.b; - } - - bool operator==(const FilteredBelIterator &other) const - { - NPNR_ASSERT(e == other.e); - return b == other.b; - } - - BelId operator*() const - { - BelId bel = *b; - NPNR_ASSERT(filter(bel)); - return bel; - } -}; - -struct FilteredBelRange -{ - FilteredBelRange(BelIterator bel_b, BelIterator bel_e, std::function filter) - { - b.filter = filter; - b.b = bel_b; - b.e = bel_e; - - if (b.b != b.e && !filter(*b.b)) { - ++b; - } - - e.b = bel_e; - e.e = bel_e; - - if (b != e) { - NPNR_ASSERT(filter(*b.b)); - } - } - - FilteredBelIterator b, e; - FilteredBelIterator begin() const { return b; } - FilteredBelIterator end() const { return e; } -}; - -// ----------------------------------------------------------------------- - -// Iterate over TileWires for a wire (will be more than one if nodal) -struct TileWireIterator -{ - const ChipInfoPOD *chip; - WireId baseWire; - int cursor = -1; - - void operator++() { cursor++; } - - bool operator==(const TileWireIterator &other) const { return cursor == other.cursor; } - bool operator!=(const TileWireIterator &other) const { return cursor != other.cursor; } - - // Returns a *denormalised* identifier always pointing to a tile wire rather than a node - WireId operator*() const - { - if (baseWire.tile == -1) { - WireId tw; - const auto &node_wire = chip->nodes[baseWire.index].tile_wires[cursor]; - tw.tile = node_wire.tile; - tw.index = node_wire.index; - return tw; - } else { - return baseWire; - } - } -}; - -struct TileWireRange -{ - TileWireIterator b, e; - TileWireIterator begin() const { return b; } - TileWireIterator end() const { return e; } -}; - -NPNR_ALWAYS_INLINE inline WireId canonical_wire(const ChipInfoPOD *chip_info, int32_t tile, int32_t wire) -{ - WireId id; - - if (wire >= chip_info->tiles[tile].tile_wire_to_node.ssize()) { - // Cannot be a nodal wire - id.tile = tile; - id.index = wire; - } else { - int32_t node = chip_info->tiles[tile].tile_wire_to_node[wire]; - if (node == -1) { - // Not a nodal wire - id.tile = tile; - id.index = wire; - } else { - // Is a nodal wire, set tile to -1 - id.tile = -1; - id.index = node; - } - } - - return id; -} - -// ----------------------------------------------------------------------- - -struct WireIterator -{ - const ChipInfoPOD *chip; - int cursor_index = 0; - int cursor_tile = -1; - - WireIterator operator++() - { - // Iterate over nodes first, then tile wires that aren't nodes - do { - cursor_index++; - if (cursor_tile == -1 && cursor_index >= chip->nodes.ssize()) { - cursor_tile = 0; - cursor_index = 0; - } - while (cursor_tile != -1 && cursor_tile < chip->tiles.ssize() && - cursor_index >= chip->tile_types[chip->tiles[cursor_tile].type].wire_data.ssize()) { - cursor_index = 0; - cursor_tile++; - } - - } while ((cursor_tile != -1 && cursor_tile < chip->tiles.ssize() && - cursor_index < chip->tiles[cursor_tile].tile_wire_to_node.ssize() && - chip->tiles[cursor_tile].tile_wire_to_node[cursor_index] != -1)); - - return *this; - } - WireIterator operator++(int) - { - WireIterator prior(*this); - ++(*this); - return prior; - } - - bool operator!=(const WireIterator &other) const - { - return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile; - } - - bool operator==(const WireIterator &other) const - { - return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile; - } - - WireId operator*() const - { - WireId ret; - ret.tile = cursor_tile; - ret.index = cursor_index; - return ret; - } -}; - -struct WireRange -{ - WireIterator b, e; - WireIterator begin() const { return b; } - WireIterator end() const { return e; } -}; - -// ----------------------------------------------------------------------- -struct AllPipIterator -{ - const ChipInfoPOD *chip; - int cursor_index; - int cursor_tile; - - AllPipIterator operator++() - { - cursor_index++; - while (cursor_tile < chip->tiles.ssize() && - cursor_index >= chip->tile_types[chip->tiles[cursor_tile].type].pip_data.ssize()) { - cursor_index = 0; - cursor_tile++; - } - return *this; - } - AllPipIterator operator++(int) - { - AllPipIterator prior(*this); - ++(*this); - return prior; - } - - bool operator!=(const AllPipIterator &other) const - { - return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile; - } - - bool operator==(const AllPipIterator &other) const - { - return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile; - } - - PipId operator*() const - { - PipId ret; - ret.tile = cursor_tile; - ret.index = cursor_index; - return ret; - } -}; - -struct AllPipRange -{ - AllPipIterator b, e; - AllPipIterator begin() const { return b; } - AllPipIterator end() const { return e; } -}; - -// ----------------------------------------------------------------------- - -struct UphillPipIterator -{ - const ChipInfoPOD *chip; - TileWireIterator twi, twi_end; - int cursor = -1; - - void operator++() - { - cursor++; - while (true) { - if (!(twi != twi_end)) - break; - WireId w = *twi; - auto &tile = chip->tile_types[chip->tiles[w.tile].type]; - if (cursor < tile.wire_data[w.index].pips_uphill.ssize()) - break; - ++twi; - cursor = 0; - } - } - bool operator!=(const UphillPipIterator &other) const { return twi != other.twi || cursor != other.cursor; } - - PipId operator*() const - { - PipId ret; - WireId w = *twi; - ret.tile = w.tile; - ret.index = chip->tile_types[chip->tiles[w.tile].type].wire_data[w.index].pips_uphill[cursor]; - return ret; - } -}; - -struct UphillPipRange -{ - UphillPipIterator b, e; - UphillPipIterator begin() const { return b; } - UphillPipIterator end() const { return e; } -}; - -struct DownhillPipIterator -{ - const ChipInfoPOD *chip; - TileWireIterator twi, twi_end; - int cursor = -1; - - int32_t tile; - int32_t tile_type; - const RelSlice *pips_downhill = nullptr; - - void operator++() - { - cursor++; - while (true) { - if (!(twi != twi_end)) - break; - - if (pips_downhill == nullptr) { - WireId w = *twi; - tile_type = chip->tiles[w.tile].type; - const TileTypeInfoPOD &type = chip->tile_types[tile_type]; - - tile = w.tile; - pips_downhill = &type.wire_data[w.index].pips_downhill; - } - - if (cursor < pips_downhill->ssize()) - break; - - ++twi; - cursor = 0; - pips_downhill = nullptr; - } - } - bool operator!=(const DownhillPipIterator &other) const { return twi != other.twi || cursor != other.cursor; } - - PipId operator*() const - { - PipId ret; - ret.tile = tile; - ret.index = (*pips_downhill)[cursor]; - return ret; - } -}; - -struct DownhillPipRange -{ - DownhillPipIterator b, e; - DownhillPipIterator begin() const { return b; } - DownhillPipIterator end() const { return e; } -}; - -struct BelPinIterator -{ - const ChipInfoPOD *chip; - TileWireIterator twi, twi_end; - int cursor = -1; - - void operator++() - { - cursor++; - - while (twi != twi_end) { - WireId w = *twi; - auto &tile = tile_info(chip, w.tile); - if (cursor < tile.wire_data[w.index].bel_pins.ssize()) - break; - - ++twi; - cursor = 0; - } - } - bool operator!=(const BelPinIterator &other) const { return twi != other.twi || cursor != other.cursor; } - - BelPin operator*() const - { - BelPin ret; - WireId w = *twi; - ret.bel.tile = w.tile; - ret.bel.index = tile_info(chip, w.tile).wire_data[w.index].bel_pins[cursor].bel_index; - ret.pin.index = tile_info(chip, w.tile).wire_data[w.index].bel_pins[cursor].port; - return ret; - } -}; - -struct BelPinRange -{ - BelPinIterator b, e; - BelPinIterator begin() const { return b; } - BelPinIterator end() const { return e; } -}; - -struct IdStringIterator : std::iterator -{ - const int32_t *cursor; - - void operator++() { cursor += 1; } - - bool operator!=(const IdStringIterator &other) const { return cursor != other.cursor; } - - bool operator==(const IdStringIterator &other) const { return cursor == other.cursor; } - - IdString operator*() const { return IdString(*cursor); } -}; - -struct IdStringRange -{ - IdStringIterator b, e; - IdStringIterator begin() const { return b; } - IdStringIterator end() const { return e; } -}; - -struct BelBucketIterator -{ - IdStringIterator cursor; - - void operator++() { ++cursor; } - - bool operator!=(const BelBucketIterator &other) const { return cursor != other.cursor; } - - bool operator==(const BelBucketIterator &other) const { return cursor == other.cursor; } - - BelBucketId operator*() const - { - BelBucketId bucket; - bucket.name = IdString(*cursor); - return bucket; - } -}; - -struct BelBucketRange -{ - BelBucketIterator b, e; - BelBucketIterator begin() const { return b; } - BelBucketIterator end() const { return e; } -}; - -NEXTPNR_NAMESPACE_END - -#endif /* ARCH_ITERATORS_H */ diff --git a/fpga_interchange/arch_pack_clusters.cc b/fpga_interchange/arch_pack_clusters.cc deleted file mode 100644 index 0dc75192..00000000 --- a/fpga_interchange/arch_pack_clusters.cc +++ /dev/null @@ -1,1076 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "arch.h" -#include "design_utils.h" -#include "log.h" -#include "nextpnr.h" -#include "util.h" - -#include -#include - -NEXTPNR_NAMESPACE_BEGIN - -namespace { -enum ClusterWireNodeState -{ - IN_SINK_SITE = 0, - IN_ROUTING = 1, - IN_SOURCE_SITE = 2, - ONLY_IN_SOURCE_SITE = 3 -}; - -enum ExpansionDirection -{ - CLUSTER_UPHILL_DIR = 0, - CLUSTER_DOWNHILL_DIR = 1, - CLUSTER_BOTH_DIR = 2 -}; - -struct ClusterWireNode -{ - WireId wire; - ClusterWireNodeState state; - int depth; - bool only_down; -}; -} // namespace - -static void handle_expansion_node(const Context *ctx, WireId prev_wire, PipId pip, ClusterWireNode curr_node, - std::vector &nodes_to_expand, pool &bels, - ExpansionDirection direction) -{ - WireId wire; - - if (direction == CLUSTER_UPHILL_DIR) - wire = ctx->getPipSrcWire(pip); - else - wire = ctx->getPipDstWire(pip); - - if (wire == WireId()) - return; - - ClusterWireNode next_node; - next_node.wire = wire; - next_node.depth = curr_node.depth; - - if (next_node.depth >= 2) - return; - - auto const &wire_data = ctx->wire_info(wire); - - bool expand_node = true; - if (ctx->is_site_port(pip)) { - switch (curr_node.state) { - case ONLY_IN_SOURCE_SITE: - expand_node = false; - break; - case IN_SOURCE_SITE: - NPNR_ASSERT(wire_data.site == -1); - next_node.state = IN_ROUTING; - break; - case IN_ROUTING: - NPNR_ASSERT(wire_data.site != -1); - next_node.state = IN_SINK_SITE; - break; - case IN_SINK_SITE: - expand_node = false; - break; - default: - // Unreachable!!! - NPNR_ASSERT(false); - } - } else { - if (curr_node.state == IN_ROUTING) - next_node.depth++; - next_node.state = curr_node.state; - } - - if (expand_node) - nodes_to_expand.push_back(next_node); - else - return; - - if (next_node.state == IN_SINK_SITE || next_node.state == ONLY_IN_SOURCE_SITE) { - for (BelPin bel_pin : ctx->getWireBelPins(wire)) { - BelId bel = bel_pin.bel; - auto const &bel_data = bel_info(ctx->chip_info, bel); - - if (bels.count(bel)) - continue; - - if (bel_data.category != BEL_CATEGORY_LOGIC) - return; - - if (bel_data.synthetic) - return; - - if (direction == CLUSTER_UPHILL_DIR) { - // Check that the BEL is indeed the one reached by backward exploration, - // by checking the previous visited wire. - for (IdString check_pin : ctx->getBelPins(bel)) { - if (prev_wire == ctx->getBelPinWire(bel, check_pin)) { - bels.insert(bel); - break; - } - } - } else { - bels.insert(bel); - } - } - } - - return; -} - -static pool find_cluster_bels(const Context *ctx, WireId wire, ExpansionDirection direction, - bool out_of_site_expansion = false) -{ - std::vector nodes_to_expand; - pool bels; - - const auto &wire_data = ctx->wire_info(wire); - NPNR_ASSERT(wire_data.site != -1); - - ClusterWireNode wire_node; - wire_node.wire = wire; - wire_node.state = IN_SOURCE_SITE; - if (!out_of_site_expansion) - wire_node.state = ONLY_IN_SOURCE_SITE; - wire_node.depth = 0; - - nodes_to_expand.push_back(wire_node); - - while (!nodes_to_expand.empty()) { - ClusterWireNode node_to_expand = nodes_to_expand.back(); - WireId prev_wire = node_to_expand.wire; - nodes_to_expand.pop_back(); - - if (direction == CLUSTER_DOWNHILL_DIR) { - for (PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) { - if (ctx->is_pip_synthetic(pip)) - continue; - - handle_expansion_node(ctx, prev_wire, pip, node_to_expand, nodes_to_expand, bels, direction); - } - } else { - NPNR_ASSERT(direction == CLUSTER_UPHILL_DIR); - for (PipId pip : ctx->getPipsUphill(node_to_expand.wire)) { - if (ctx->is_pip_synthetic(pip)) - continue; - - handle_expansion_node(ctx, prev_wire, pip, node_to_expand, nodes_to_expand, bels, direction); - } - } - } - - return bels; -} - -CellInfo *Arch::getClusterRootCell(ClusterId cluster) const -{ - NPNR_ASSERT(cluster != ClusterId()); - return clusters.at(cluster).root; -} - -bool Arch::normal_cluster_placement(const Context *ctx, const Cluster &packed_cluster, const ClusterPOD &cluster_data, - CellInfo *root_cell, BelId root_bel, - std::vector> &placement) const -{ - BelId next_bel; - - // Place cluster - for (CellInfo *cluster_node : packed_cluster.cluster_nodes) { - if (cluster_node == root_cell) { - next_bel = root_bel; - } else { - // Find next chained cluster node - IdString next_bel_pin(cluster_data.chainable_ports[0].bel_source); - WireId next_bel_pin_wire = ctx->getBelPinWire(next_bel, next_bel_pin); - next_bel = BelId(); - for (BelId bel : - find_cluster_bels(ctx, next_bel_pin_wire, CLUSTER_DOWNHILL_DIR, /*out_of_site_expansion=*/true)) { - if (ctx->isValidBelForCellType(cluster_node->type, bel)) { - next_bel = bel; - break; - } - } - - if (next_bel == BelId()) - return false; - } - - // Build a cell to bell mapping required to find BELs connected to the cluster ports. - dict> cell_bel_pins; - - int32_t mapping = bel_info(chip_info, next_bel).pin_map[get_cell_type_index(cluster_node->type)]; - NPNR_ASSERT(mapping >= 0); - - const CellBelMapPOD &cell_pin_map = chip_info->cell_map->cell_bel_map[mapping]; - for (const auto &pin_map : cell_pin_map.common_pins) { - IdString cell_pin(pin_map.cell_pin); - IdString bel_pin(pin_map.bel_pin); - - cell_bel_pins[cell_pin].push_back(bel_pin); - } - - placement.emplace_back(cluster_node, next_bel); - - // Place cluster node cells at the same site - for (auto port_cell : packed_cluster.cluster_node_cells.at(cluster_node->name)) { - bool placed_cell = false; - - IdString port = port_cell.first; - CellInfo *cell = port_cell.second; - - NPNR_ASSERT(cell_bel_pins.count(port)); - - PortType port_type = cluster_node->ports.at(port).type; - - if (port_type == PORT_INOUT) - continue; - - for (auto &bel_pin : cell_bel_pins.at(port)) { - WireId bel_pin_wire = ctx->getBelPinWire(next_bel, bel_pin); - - ExpansionDirection direction = port_type == PORT_IN ? CLUSTER_UPHILL_DIR : CLUSTER_DOWNHILL_DIR; - pool cluster_bels = - find_cluster_bels(ctx, bel_pin_wire, direction, (bool)cluster_data.out_of_site_clusters); - - if (cluster_bels.size() == 0) - continue; - - for (BelId bel : cluster_bels) { - if (ctx->isValidBelForCellType(cell->type, bel)) { - placement.emplace_back(cell, bel); - placed_cell = true; - break; - } - } - - if (placed_cell) - break; - } - - if (!placed_cell) - return false; - } - } - - return true; -} - -static dict> tileAndBelNameToBelIdCache; - -BelId check_and_return(int32_t tile, IdString name) -{ - if (tileAndBelNameToBelIdCache.count(tile) && tileAndBelNameToBelIdCache[tile].count(name)) - return tileAndBelNameToBelIdCache[tile][name]; - else - return BelId(); -} - -void add_to_cache(int32_t tile, IdString name, BelId t) { tileAndBelNameToBelIdCache[tile][name] = t; } - -bool find_site_idx(const Context *ctx, const ClusterPOD &cluster, BelId root_bel, uint32_t &idx) -{ - bool found = false; - const auto &site_inst = ctx->get_site_inst(root_bel); - IdString site_type(site_inst.site_type); - - if (ctx->debug) { - log_info("%s\n", ctx->get_site_name(root_bel)); - log_info("Root_bel site_type: %s\n", site_type.c_str(ctx)); - log_info("Allowed site_types:\n"); - } - for (const auto &site : cluster.physical_placements) { - IdString name(site.site_type); - if (ctx->debug) - log_info("\t%s\n", name.c_str(ctx)); - - if (name == site_type) { - found = true; - break; - } - idx++; - } - return found; -} - -bool find_placement_idx(const Context *ctx, const ClusterPOD &cluster, BelId root_bel, uint32_t idx, - uint32_t &placement_idx) -{ - bool found = false; - const auto &bel_data = bel_info(ctx->chip_info, root_bel); - IdString root_bel_name(bel_data.name); - if (ctx->debug) { - log_info("Root_bel name: %s\n", root_bel_name.c_str(ctx)); - log_info("Allowed root_bels:\n"); - } - for (const auto &place : cluster.physical_placements[idx].places) { - for (const auto bel : place.bels) { - IdString name(bel); - if (ctx->debug) - log_info("\t%s\n", name.c_str(ctx)); - - if (name == root_bel_name) { - found = true; - break; - } - } - if (found) - break; - placement_idx++; - } - return found; -} - -dict idx_bel_mapping(const Context *ctx, BelId root_bel, const ClusterPOD &cluster, uint32_t idx, - uint32_t placement_idx) -{ - dict idx_bel_map; - auto root_bel_full_name = ctx->getBelName(root_bel); - uint32_t t_idx = 0; - if (ctx->debug) - log_info("Used bels:\n"); - for (const auto &bel : cluster.physical_placements[idx].places[placement_idx].bels) { - IdString s_bel(bel); - BelId t = check_and_return(root_bel.tile, s_bel); - IdStringList cpy(root_bel_full_name.size()); - if (t == BelId()) { - for (uint32_t j = 0; j < root_bel_full_name.size(); j++) - cpy.ids[j] = root_bel_full_name[j]; - cpy.ids[root_bel_full_name.size() - 1] = s_bel; - t = ctx->getBelByName(cpy); - add_to_cache(root_bel.tile, s_bel, t); - } - if (ctx->debug) { - for (uint32_t j = 0; j < root_bel_full_name.size(); j++) - cpy.ids[j] = root_bel_full_name[j]; - cpy.ids[1] = s_bel; - for (auto str : cpy) - log_info("\t%s\n", str.c_str(ctx)); - } - idx_bel_map[t_idx] = t; - t_idx++; - } - return idx_bel_map; -} - -bool Arch::macro_cluster_placement(const Context *ctx, const Cluster &packed_cluster, const ClusterPOD &cluster_data, - CellInfo *root_cell, BelId root_bel, - std::vector> &placement) const -{ - // Check root_bel site_type - const auto &cluster = cluster_info(chip_info, packed_cluster.index); - uint32_t idx = 0; - if (!find_site_idx(ctx, cluster, root_bel, idx)) - return false; - - // Check if root_bel name - uint32_t placement_idx = 0; - if (!find_placement_idx(ctx, cluster, root_bel, idx, placement_idx)) - return false; - - // Map cells to bels - dict idx_bel_map = idx_bel_mapping(ctx, root_bel, cluster, idx, placement_idx); - - for (auto idx_bel : idx_bel_map) { - placement.emplace_back(packed_cluster.cluster_nodes[idx_bel.first], idx_bel.second); - } - - return true; -} - -bool Arch::getClusterPlacement(ClusterId cluster, BelId root_bel, - std::vector> &placement) const -{ - const Context *ctx = getCtx(); - const Cluster &packed_cluster = clusters.at(cluster); - - auto &cluster_data = cluster_info(chip_info, packed_cluster.index); - - CellInfo *root_cell = getClusterRootCell(cluster); - if (!ctx->isValidBelForCellType(root_cell->type, root_bel)) - return false; - if (!cluster_data.from_macro) - return normal_cluster_placement(ctx, packed_cluster, cluster_data, root_cell, root_bel, placement); - else { - bool temp = macro_cluster_placement(ctx, packed_cluster, cluster_data, root_cell, root_bel, placement); - return temp; - } -} - -BoundingBox Arch::getClusterBounds(ClusterId cluster) const -{ - // TODO: Implement this - BoundingBox bounds(0, 0, 0, 0); - return bounds; -} - -Loc Arch::getClusterOffset(const CellInfo *cell) const -{ - Loc offset; - CellInfo *root = getClusterRootCell(cell->cluster); - - if (cell->bel != BelId() && root->bel != BelId()) { - Loc root_loc = getBelLocation(root->bel); - Loc cell_loc = getBelLocation(cell->bel); - offset.x = cell_loc.x - root_loc.x; - offset.y = cell_loc.y - root_loc.y; - offset.z = cell_loc.z - root_loc.z; - } else { - Cluster cluster = clusters.at(cell->cluster); - auto &cluster_data = cluster_info(chip_info, cluster.index); - - if (cluster_data.chainable_ports.size() == 0) - return offset; - - auto &chainable_port = cluster_data.chainable_ports[0]; - - IdString cluster_node = cluster.cell_cluster_node_map.at(cell->name); - CellInfo *cluster_node_cell = cells.at(cluster_node).get(); - - auto res = std::find(cluster.cluster_nodes.begin(), cluster.cluster_nodes.end(), cluster_node_cell); - NPNR_ASSERT(res != cluster.cluster_nodes.end()); - - auto distance = std::distance(cluster.cluster_nodes.begin(), res); - - offset.x = chainable_port.avg_x_offset * distance; - offset.y = chainable_port.avg_y_offset * distance; - } - - return offset; -} - -bool Arch::isClusterStrict(const CellInfo *cell) const { return true; } - -static void dump_clusters(const ChipInfoPOD *chip_info, Context *ctx) -{ - for (size_t i = 0; i < chip_info->clusters.size(); ++i) { - const auto &cluster = chip_info->clusters[i]; - IdString cluster_name(cluster.name); - log_info("Cluster '%s' loaded! Parameters:\n", cluster_name.c_str(ctx)); - - log_info(" - root cell types:\n"); - for (auto cell : cluster.root_cell_types) - log_info(" - %s\n", IdString(cell).c_str(ctx)); - - for (auto chain_ports : cluster.chainable_ports) - log_info(" - chainable pair: source %s - sink %s\n", IdString(chain_ports.cell_source).c_str(ctx), - IdString(chain_ports.cell_sink).c_str(ctx)); - - if (cluster.cluster_cells_map.size() != 0) - log_info(" - cell port maps:\n"); - for (auto cluster_cell : cluster.cluster_cells_map) { - log_info(" - cell: %s - port: %s\n", IdString(cluster_cell.cell).c_str(ctx), - IdString(cluster_cell.port).c_str(ctx)); - } - } -} - -static bool check_cluster_cells_compatibility(CellInfo *old_cell, CellInfo *new_cell, pool &exclude_nets) -{ - NPNR_ASSERT(new_cell->type == old_cell->type); - for (auto &new_port_pair : new_cell->ports) { - PortInfo new_port_info = new_port_pair.second; - PortInfo old_port_info = old_cell->ports.at(new_port_pair.first); - - if (exclude_nets.count(new_port_info.net->name)) - continue; - - if (new_port_info.type != PORT_IN) - continue; - - if (new_port_info.net != old_port_info.net) - return false; - } - - return true; -} - -bool reduce(uint32_t x, uint32_t y, const ClusterPOD *cluster, dict> &domain, - Context *ctx) -{ - // Reduce X domain by removing values, which don't satisfy binary constraint with values from Y domain. - bool change = false; - std::vector remove_cell; - uint32_t counter = 0; - for (const auto &connection : cluster->connection_graph[x].connections) { - if (connection.target_idx == y) - break; - counter++; - } - for (const auto &x_cell : domain[x]) { - bool found = false; - for (const auto &y_cell : domain[y]) { - found = true; - for (const auto edge : cluster->connection_graph[x].connections[counter].edges) { - if (!x_cell->ports.count(IdString(edge.cell_pin)) || - !y_cell->ports.count(IdString(edge.other_cell_pin))) { - found = false; - break; - } - const auto x_net = x_cell->ports[IdString(edge.cell_pin)].net; - const auto y_net = y_cell->ports[IdString(edge.other_cell_pin)].net; - - if (x_net != y_net) { - found = false; - break; - } - bool x_driver = x_net->driver.cell == x_cell; - bool y_driver = y_net->driver.cell == y_cell; - if ((edge.dir != 0 || !y_driver) && (edge.dir != 1 || !x_driver) && - (edge.dir != 2 || y_driver || x_driver)) { - found = false; - break; - } - } - if (found) - break; - } - if (!found) - remove_cell.push_back(x_cell); - } - - for (const auto &cell : remove_cell) { - domain[x].erase(cell); - change = true; - } - - return change; -} - -void binary_constraint_check(const ClusterPOD *cluster, std::queue> &workqueue, - dict> &idx_to_cells, Context *ctx) -{ - while (!workqueue.empty()) { - std::pair arc = workqueue.front(); - workqueue.pop(); - uint32_t x, y; - x = arc.first; - y = arc.second; - if (reduce(x, y, cluster, idx_to_cells, ctx)) { - for (const auto &node : cluster->connection_graph) { - if (node.idx != arc.first) - for (const auto &connection : node.connections) - if (connection.target_idx == arc.first) - workqueue.push(std::pair(node.idx, arc.first)); - } - } - } -} - -bool back_solver(const ClusterPOD *cluster, dict> &idx_to_cells, Context *ctx) -{ - dict, hash_ptr_ops> possible_idx; - for (const auto &arc : idx_to_cells) - for (const auto &cell : arc.second) - possible_idx[cell].insert(arc.first); - std::queue prep; - for (const auto &arc : idx_to_cells) { - if (arc.second.size() == 0) - return false; - if (arc.second.size() > 1) { - for (const auto &cell : arc.second) { - auto copy_idx_to_cells(idx_to_cells); - copy_idx_to_cells[arc.first].clear(); - for (uint32_t idx : possible_idx[cell]) { - copy_idx_to_cells[idx].erase(cell); - prep.push(idx); - } - copy_idx_to_cells[arc.first].insert(cell); - std::queue> workqueue; - while (!prep.empty()) { - uint32_t idx = prep.front(); - prep.pop(); - for (const auto &connection : cluster->connection_graph[idx].connections) - if (arc.first != connection.target_idx) - workqueue.push(std::pair(arc.first, connection.target_idx)); - } - binary_constraint_check(cluster, workqueue, copy_idx_to_cells, ctx); - if (back_solver(cluster, copy_idx_to_cells, ctx)) { - idx_to_cells = std::move(copy_idx_to_cells); - return true; - } - } - } - } - return true; -} - -void Arch::prepare_macro_cluster(const ClusterPOD *cluster, uint32_t index) -{ - Context *ctx = getCtx(); - IdString cluster_name(cluster->name); - - pool cluster_cell_types; - for (auto cell_type : cluster->root_cell_types) - cluster_cell_types.insert(IdString(cell_type)); - - // Find cluster roots for each macro only ones - dict roots; - for (auto &cell : cells) { - CellInfo *ci = cell.second.get(); - if (ci->macro_parent == IdString()) - continue; - if (ci->cluster != ClusterId()) - continue; - if (!cluster_cell_types.count(ci->type)) - continue; - if (roots.count(ci->macro_parent)) - continue; - // Simple check based on cell type counting - - dict cells_in_macro, counter; - // cells_in_macro stores cell_types used in tested cluster and - // cell_types that are in macro_to_cells[ci->macro_parent] - - pool cell_types; - for (auto &cell_type : cluster->required_cells) { - cells_in_macro[IdString(cell_type.name)] = cell_type.count; - cell_types.insert(IdString(cell_type.name)); - } - - for (auto &node_cell : macro_to_cells[ci->macro_parent]) { - auto cell_type = node_cell->type; - counter[cell_type]++; - cell_types.insert(cell_type); - } - bool failed = false; - for (auto cell_type : cell_types) { - if (ctx->verbose && cells_in_macro.count(cell_type)) - log_info("Required: %s %d\n", cell_type.c_str(ctx), cells_in_macro[cell_type]); - if (ctx->verbose && cells_in_macro.count(cell_type)) - log_info("Have: %s %d\n", cell_type.c_str(ctx), counter[cell_type]); - if (!cells_in_macro.count(cell_type) || !counter.count(cell_type) || - cells_in_macro[cell_type] != counter[cell_type]) - failed = true; - if (failed && ctx->verbose) - log_info("Cell count stage failed, for sure not this cluster\n"); - if (failed) - break; - } - if (failed) { - roots[ci->macro_parent] = nullptr; - continue; - } - - // Arc consistency - dict> idx_to_cells; - // First singular constraints, like used cell type and used_cell ports - for (auto &cell : macro_to_cells[ci->macro_parent]) - for (auto &node : cluster->connection_graph) - if (IdString(node.cell_type) == cell->type) - if ((node.idx != 0 && cell->name != ci->name) || (node.idx == 0 && cell->name == ci->name)) { - idx_to_cells[node.idx].insert(cell); - } - - for (auto &arc : idx_to_cells) { - std::vector remove_cell; - pool used_ports; - for (const auto &port : cluster->connection_graph[arc.first].used_ports) - used_ports.insert(IdString(port.name)); - for (const auto &cell : arc.second) { - uint32_t count = 0; - for (const auto &port : cell->ports) { - if (!used_ports.count(port.first)) { - remove_cell.push_back(cell); - break; - } - count++; - } - if (count != used_ports.size()) { - remove_cell.push_back(cell); - break; - } - } - for (const auto &cell : remove_cell) { - arc.second.erase(cell); - } - } - if (ctx->debug) { - log_info("After mono constraints are applied\n"); - dict, hash_ptr_ops> possible_idx; - for (const auto &arc : idx_to_cells) - for (const auto &cell : arc.second) - possible_idx[cell].insert(arc.first); - - for (const auto &arc : possible_idx) { - log_info("Possible idx %s:\n", arc.first->name.c_str(ctx)); - for (const auto idx : arc.second) - log_info(" - %d\n", idx); - } - } - // Solve for binary constraints - std::queue> workqueue; - for (const auto &arc : idx_to_cells) - for (const auto &connection : cluster->connection_graph[arc.first].connections) - workqueue.emplace(arc.first, connection.target_idx); - - binary_constraint_check(cluster, workqueue, idx_to_cells, ctx); - for (const auto &arc : idx_to_cells) { - if (arc.second.size() == 0) { - if (ctx->debug) - log_info("AC-3 failed\n"); - failed = true; - break; - } - } - if (failed) - continue; - - if (ctx->debug) { - log_info("After AC-3\n"); - dict, hash_ptr_ops> possible_idx; - for (const auto &arc : idx_to_cells) - for (const auto &cell : arc.second) - possible_idx[cell].insert(arc.first); - - for (const auto &arc : possible_idx) { - log_info("Possible idx %s:\n", arc.first->name.c_str(ctx)); - for (const auto idx : arc.second) - log_info(" - %d\n", idx); - } - } - - bool change = false; - std::queue> removequeue; - // Keep assigning cells to indices that only map to single cell - // Remove this cell from other mappings and recheck binary constraints - // Fail if there is no cell for idx or cell has no idx assign - do { - change = false; - dict, hash_ptr_ops> possible_idx; - pool changed_idxs; - for (const auto &arc : idx_to_cells) { - if (arc.second.size() == 0) { - failed = true; - break; - } - for (const auto &cell : arc.second) - possible_idx[cell].insert(arc.first); - } - if (failed) - break; - for (auto &cell : macro_to_cells[ci->macro_parent]) - if (possible_idx[cell].size() == 0) { - failed = true; - break; - } - if (failed) - break; - for (const auto &arc : idx_to_cells) { - if (arc.second.size() == 1) - for (const auto &idx : possible_idx[*arc.second.begin()]) - if (idx != arc.first) - removequeue.push(std::pair(idx, *arc.second.begin())); - } - while (!removequeue.empty()) { - auto t = removequeue.front(); - removequeue.pop(); - uint32_t idx = t.first; - CellInfo *cell = t.second; - idx_to_cells[idx].erase(cell); - change = true; - changed_idxs.insert(idx); - } - for (const uint32_t &idx : changed_idxs) - for (const auto &connection : cluster->connection_graph[idx].connections) - workqueue.push(std::pair(idx, connection.target_idx)); - - binary_constraint_check(cluster, workqueue, idx_to_cells, ctx); - } while (change); - if (failed) { - if (ctx->debug) - log_info("Single cell mapping failed\n"); - continue; - } - if (ctx->debug) { - log_info("After mapping indices with single cell\n"); - dict, hash_ptr_ops> possible_idx; - for (const auto &arc : idx_to_cells) - for (const auto &cell : arc.second) - possible_idx[cell].insert(arc.first); - - for (const auto &arc : possible_idx) { - log_info("Possible idx %s:\n", arc.first->name.c_str(ctx)); - for (const auto idx : arc.second) - log_info(" - %d\n", idx); - } - } - // At this point all indices that cloud only be mapped to single cell are mapped - // Next step is to run solver with backtracing to solve for other idx<->cell mappings - if (ctx->debug) - log_info("Back solver\n"); - if (!back_solver(cluster, idx_to_cells, ctx)) { - if (ctx->debug) - log_info("Back solver failed\n"); - continue; - } - if (ctx->debug) { - log_info("Final mapping after back solver\n"); - dict, hash_ptr_ops> possible_idx; - for (const auto &arc : idx_to_cells) - for (const auto &cell : arc.second) - possible_idx[cell].insert(arc.first); - - for (const auto &arc : possible_idx) { - log_info("Possible idx %s:\n", arc.first->name.c_str(ctx)); - for (const auto idx : arc.second) - log_info(" - %d\n", idx); - } - } - Cluster cluster_info; - cluster_info.root = ci; - cluster_info.index = index; - cluster_info.cluster_nodes.resize(idx_to_cells.size()); - ci->cluster = ci->name; - for (auto &arc : idx_to_cells) { - CellInfo *sub_cell = arc.second.pop(); - if (ctx->verbose) - log_info("%d %s - %s\n", arc.first, sub_cell->name.c_str(ctx), sub_cell->type.c_str(ctx)); - sub_cell->cluster = ci->cluster; - cluster_info.cluster_nodes[arc.first] = sub_cell; - } - clusters.emplace(ci->cluster, cluster_info); - } -} - -void Arch::prepare_cluster(const ClusterPOD *cluster, uint32_t index) -{ - Context *ctx = getCtx(); - IdString cluster_name(cluster->name); - - pool cluster_cell_types; - for (auto cell_type : cluster->root_cell_types) - cluster_cell_types.insert(IdString(cell_type)); - - // Find cluster roots - std::vector roots; - for (auto &cell : cells) { - CellInfo *ci = cell.second.get(); - if (ci->macro_parent != IdString()) - continue; - - if (ci->cluster != ClusterId()) - continue; - - if (!cluster_cell_types.count(ci->type)) - continue; - - if (cluster->chainable_ports.size() == 0) { - ci->cluster.set(ctx, ci->name.str(ctx)); - roots.push_back(ci); - continue; - } - - // Only one type of dedicated interconnect is allowed. - auto chain_ports = cluster->chainable_ports[0]; - IdString source_port(chain_ports.cell_source); - IdString sink_port(chain_ports.cell_sink); - - PortRef driver = ci->ports[sink_port].net->driver; - - if (driver.cell == nullptr || driver.port != source_port) { - // We hit a root cell - ci->cluster.set(ctx, ci->name.c_str(ctx)); - roots.push_back(ci); - - // Chained cells use dedicated connections, usually not exposed to the - // general interconnect resources. The port disconnection is required for - // sink ports which are connected to GND or VCC by default, which are not - // reachable due to the fixed dedicated interconnect. - // E.g.: The CI input of carry chains in 7series corresponds to the CIN bel port, - // which can only be connected to the COUT output of the tile below. - ci->disconnectPort(sink_port); - } - } - - dict> port_cell_maps; - for (auto cell_port_map : cluster->cluster_cells_map) { - IdString cell(cell_port_map.cell); - IdString port(cell_port_map.port); - - pool cells_pool({cell}); - - port_cell_maps.emplace(port, cells_pool).first->second.insert(cell); - } - - // Generate unique clusters starting from each root - for (auto root : roots) { - Cluster cluster_info; - cluster_info.root = root; - cluster_info.index = index; - - CellInfo *next_cluster_node = root; - if (ctx->verbose) - log_info(" - forming cluster starting from root cell: %s\n", next_cluster_node->name.c_str(ctx)); - - // counter to determine whether this cluster needs to exist - uint32_t count_cluster_cells = 0; - do { - std::vector> cluster_cells; - - // type -> cells map to verify compatibility of cells in the same cluster - dict cell_type_dict; - pool exclude_nets; - - count_cluster_cells++; - - for (auto port : next_cluster_node->ports) { - if (!port_cell_maps.count(port.first)) - continue; - - PortInfo port_info = port.second; - - if (port_info.type == PORT_OUT) { - exclude_nets.insert(port_info.net->name); - auto &users = port_info.net->users; - if (users.entries() != 1) - continue; - - CellInfo *user_cell = (*users.begin()).cell; - if (user_cell == nullptr) - continue; - - if (!port_cell_maps.at(port.first).count(user_cell->type)) - continue; - - auto res = cell_type_dict.emplace(user_cell->type, user_cell); - bool compatible = true; - if (!res.second) - // Check whether a cell of the same type has all the required nets compatible with - // all other nets for the same type. If not, discard the cell. - // An example is multiple FFs belonging to the same cluster, where one of them has a different - // Set/Reset or CE net w.r.t. the others, making the cluster unplaceable. - compatible = check_cluster_cells_compatibility(res.first->second, user_cell, exclude_nets); - - if (!compatible) - continue; - - user_cell->cluster = root->cluster; - cluster_cells.push_back(std::make_pair(port.first, user_cell)); - cluster_info.cell_cluster_node_map.emplace(user_cell->name, next_cluster_node->name); - count_cluster_cells++; - - if (ctx->verbose) - log_info(" - adding user cell: %s\n", user_cell->name.c_str(ctx)); - - } else if (port_info.type == PORT_IN) { - auto &driver = port_info.net->driver; - auto &users = port_info.net->users; - if (users.entries() != 1) - continue; - - CellInfo *driver_cell = driver.cell; - if (driver_cell == nullptr) - continue; - - if (!port_cell_maps.at(port.first).count(driver_cell->type)) - continue; - - driver_cell->cluster = root->cluster; - cluster_cells.push_back(std::make_pair(port.first, driver_cell)); - cluster_info.cell_cluster_node_map.emplace(driver_cell->name, next_cluster_node->name); - count_cluster_cells++; - - if (ctx->verbose) - log_info(" - adding driver cell: %s\n", driver_cell->name.c_str(ctx)); - } - } - - cluster_info.cell_cluster_node_map.emplace(next_cluster_node->name, next_cluster_node->name); - cluster_info.cluster_nodes.push_back(next_cluster_node); - cluster_info.cluster_node_cells.emplace(next_cluster_node->name, cluster_cells); - - if (cluster->chainable_ports.size() == 0) - break; - - // Only one type of dedicated interconnect is allowed. - auto chain_ports = cluster->chainable_ports[0]; - IdString source_port(chain_ports.cell_source); - IdString sink_port(chain_ports.cell_sink); - - NetInfo *next_net = next_cluster_node->ports.at(source_port).net; - - if (next_net == nullptr) - continue; - - next_cluster_node = nullptr; - for (auto &user : next_net->users) { - CellInfo *user_cell = user.cell; - - if (user_cell == nullptr) - continue; - - if (cluster_cell_types.count(user_cell->type)) { - user_cell->cluster = root->cluster; - next_cluster_node = user_cell; - break; - } - } - - if (next_cluster_node == nullptr) - break; - - } while (true); - - if (count_cluster_cells == 1 && cluster->chainable_ports.size() == 0) { - root->cluster = ClusterId(); - continue; - } - - clusters.emplace(root->cluster, cluster_info); - } -} - -void Arch::pack_cluster() -{ - Context *ctx = getCtx(); - - if (ctx->verbose) - dump_clusters(chip_info, ctx); - - for (uint32_t i = 0; i < chip_info->clusters.size(); ++i) { - if (!chip_info->clusters[i].from_macro) { - const auto &cluster = chip_info->clusters[i]; - - prepare_cluster(&cluster, i); - } else if (chip_info->clusters[i].physical_placements.size() > 0) { - const auto &cluster = chip_info->clusters[i]; - if (ctx->verbose) { - log_info("%s\n", IdString(cluster.name).c_str(ctx)); - } - - prepare_macro_cluster(&cluster, i); - } else { - // No physical placement definitions found for given macro. - // Use default place and route algorithm as routes connectiong - // cells will use global routing - const auto &cluster = chip_info->clusters[i]; - if (ctx->verbose) - log_info("Out of site cluster from macro: %s\n", IdString(cluster.name).c_str(ctx)); - } - } -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/arch_pack_io.cc b/fpga_interchange/arch_pack_io.cc deleted file mode 100644 index 3c7c566a..00000000 --- a/fpga_interchange/arch_pack_io.cc +++ /dev/null @@ -1,356 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "log.h" -#include "nextpnr.h" -#include "util.h" - -#include - -NEXTPNR_NAMESPACE_BEGIN - -namespace { -bool search_routing_for_placement(Arch *arch, WireId start_wire, CellInfo *cell, IdString cell_pin, bool downhill) -{ - std::queue visit_queue; - pool already_visited; - visit_queue.push(start_wire); - already_visited.insert(start_wire); - int iter = 0; - while (!visit_queue.empty() && iter++ < 1000) { - WireId next = visit_queue.front(); - visit_queue.pop(); - for (auto bp : arch->getWireBelPins(next)) { - if (!arch->isValidBelForCellType(cell->type, bp.bel)) - continue; - if (!arch->checkBelAvail(bp.bel)) - continue; - // We need to do a test placement to update the bel pin map - arch->bindBel(bp.bel, cell, STRENGTH_FIXED); - for (IdString bel_pin : arch->getBelPinsForCellPin(cell, cell_pin)) { - if (bel_pin == bp.pin) - return true; - } - // Bel pin doesn't match - arch->unbindBel(bp.bel); - } - auto do_visit = [&](PipId pip) { - WireId dst = downhill ? arch->getPipDstWire(pip) : arch->getPipSrcWire(pip); - if (already_visited.count(dst)) - return; - visit_queue.push(dst); - already_visited.insert(dst); - }; - if (downhill) { - for (auto pip : arch->getPipsDownhill(next)) - do_visit(pip); - } else { - for (auto pip : arch->getPipsUphill(next)) - do_visit(pip); - } - } - return false; -} -} // namespace - -void Arch::place_iobufs(WireId pad_wire, NetInfo *net, - const dict &tightly_attached_bels, - pool *placed_cells) -{ - Context *ctx = getCtx(); - for (auto cell_port : tightly_attached_bels) { - bool downhill = (cell_port.first->ports.at(cell_port.second).type != PORT_OUT); - if (cell_port.first->bel != BelId()) - continue; - if (search_routing_for_placement(this, pad_wire, cell_port.first, cell_port.second, downhill)) { - if (ctx->verbose) - log_info("Placed IO cell %s:%s at %s.\n", ctx->nameOf(cell_port.first), - ctx->nameOf(cell_port.first->type), ctx->nameOfBel(cell_port.first->bel)); - placed_cells->insert(cell_port.first); - } - } - - // Also try, on a best-effort basis, to preplace other cells in the macro based on downstream routing. This is - // needed for the split INBUF+IBUFCTRL arrangement in the UltraScale+, as just placing the INBUF will result in an - // unrouteable site and illegal placement. - std::queue place_queue; - for (auto pc : *placed_cells) - place_queue.push(pc); - while (!place_queue.empty()) { - CellInfo *cursor = place_queue.front(); - place_queue.pop(); - // Ignore cells not part of a macro - if (cursor->macro_parent == IdString()) - continue; - for (auto &port : cursor->ports) { - // Only consider routing downstream from outputs for now - if (port.second.net == nullptr) - continue; - NetInfo *ni = port.second.net; - if (port.second.type == PORT_OUT) { - WireId src_wire = ctx->getNetinfoSourceWire(ni); - for (auto &usr : ni->users) { - // Look for unplaced users in the same macro - if (usr.cell->bel != BelId() || usr.cell->macro_parent != cursor->macro_parent) - continue; - // Try and place using dedicated routing - if (search_routing_for_placement(this, src_wire, usr.cell, usr.port, true)) { - // Successful - placed_cells->insert(usr.cell); - place_queue.push(usr.cell); - if (ctx->verbose) - log_info("Placed %s at %s based on dedicated IO macro routing.\n", ctx->nameOf(usr.cell), - ctx->nameOfBel(usr.cell->bel)); - } - } - } else { - auto &drv = ni->driver; - // Look for unplaced driver in the same macro - if (drv.cell->bel != BelId() || drv.cell->macro_parent != cursor->macro_parent) - continue; - for (auto bel_pin : ctx->getBelPinsForCellPin(cursor, port.first)) { - // Try and place using dedicated routing - WireId dst_wire = ctx->getBelPinWire(cursor->bel, bel_pin); - if (search_routing_for_placement(this, dst_wire, drv.cell, drv.port, false)) { - // Successful - placed_cells->insert(drv.cell); - place_queue.push(drv.cell); - if (ctx->verbose) - log_info("Placed %s at %s based on dedicated IO macro routing.\n", ctx->nameOf(drv.cell), - ctx->nameOfBel(drv.cell->bel)); - } - } - } - } - } - // TODO: for even more complex cases, if any future devices hit them, we probably should do a full validity check of - // all placed cells here, and backtrack and try a different placement if the first one we choose isn't legal overall -} - -void Arch::pack_ports() -{ - dict tile_type_prototypes; - for (size_t i = 0; i < chip_info->tiles.size(); ++i) { - const auto &tile = chip_info->tiles[i]; - const auto &tile_type = chip_info->tile_types[tile.type]; - IdString tile_type_name(tile_type.name); - tile_type_prototypes.emplace(tile_type_name, &tile); - } - - // set(site_types) for package pins - pool package_sites; - // Package pin -> (Site type -> BelId) - dict>> package_pin_bels; - // Placed cells across all IO - pool all_placed_io; - for (const PackagePinPOD &package_pin : chip_info->packages[package_index].pins) { - IdString pin(package_pin.package_pin); - IdString bel(package_pin.bel); - - IdString site(package_pin.site); - package_sites.emplace(site); - - for (size_t i = 0; i < chip_info->tiles.size(); ++i) { - const auto &tile = chip_info->tiles[i]; - pool package_pin_sites; - for (size_t j = 0; j < tile.sites.size(); ++j) { - auto &site_data = chip_info->sites[tile.sites[j]]; - if (site == id(site_data.site_name.get())) { - package_pin_sites.emplace(j); - } - } - - const auto &tile_type = chip_info->tile_types[tile.type]; - for (size_t j = 0; j < tile_type.bel_data.size(); ++j) { - const BelInfoPOD &bel_data = tile_type.bel_data[j]; - if (bel == IdString(bel_data.name) && package_pin_sites.count(bel_data.site)) { - auto &site_data = chip_info->sites[tile.sites[bel_data.site]]; - IdString site_type(site_data.site_type); - BelId bel; - bel.tile = i; - bel.index = j; - package_pin_bels[pin].push_back(std::make_pair(site_type, bel)); - } - } - } - } - - // Determine for each package site type, which site types are possible. - pool package_pin_site_types; - dict> possible_package_site_types; - for (const TileInstInfoPOD &tile : chip_info->tiles) { - for (size_t site_index : tile.sites) { - const SiteInstInfoPOD &site = chip_info->sites[site_index]; - IdString site_name = getCtx()->id(site.site_name.get()); - if (package_sites.count(site_name) == 1) { - possible_package_site_types[site_name].emplace(IdString(site.site_type)); - package_pin_site_types.emplace(IdString(site.site_type)); - } - } - } - - // IO sites are usually pretty weird, so see if we can define some - // constraints between the port cell create by nextpnr and cells that are - // immediately attached to that port cell. - for (auto port_pair : port_cells) { - IdString port_name = port_pair.first; - CellInfo *port_cell = port_pair.second; - dict tightly_attached_bels; - - for (auto port_pair : port_cell->ports) { - const PortInfo &port_info = port_pair.second; - const NetInfo *net = port_info.net; - if (net->driver.cell) { - tightly_attached_bels.emplace(net->driver.cell, net->driver.port); - } - - for (const PortRef &port_ref : net->users) { - if (port_ref.cell) { - tightly_attached_bels.emplace(port_ref.cell, port_ref.port); - } - } - } - - if (getCtx()->verbose) { - log_info("Tightly attached BELs for port %s\n", port_name.c_str(getCtx())); - for (auto cell_port : tightly_attached_bels) { - log_info(" - %s : %s\n", cell_port.first->name.c_str(getCtx()), cell_port.first->type.c_str(getCtx())); - } - } - - NPNR_ASSERT(tightly_attached_bels.erase(port_cell) == 1); - pool cell_types_in_io_group; - for (auto cell_port : tightly_attached_bels) { - NPNR_ASSERT(port_cells.find(cell_port.first->name) == port_cells.end()); - cell_types_in_io_group.emplace(cell_port.first->type); - } - - // Get possible placement locations for tightly coupled BELs with - // port. - pool possible_site_types; - for (const TileTypeInfoPOD &tile_type : chip_info->tile_types) { - IdString tile_type_name(tile_type.name); - for (const BelInfoPOD &bel_info : tile_type.bel_data) { - if (bel_info.category != BEL_CATEGORY_LOGIC) { - break; - } - - for (IdString cell_type : cell_types_in_io_group) { - size_t cell_type_index = get_cell_type_index(cell_type); - if (bel_info.category == BEL_CATEGORY_LOGIC && bel_info.pin_map[cell_type_index] != -1) { - auto *tile = tile_type_prototypes.at(tile_type_name); - const SiteInstInfoPOD &site = chip_info->sites[tile->sites[bel_info.site]]; - - IdString site_type(site.site_type); - if (package_pin_site_types.count(site_type)) { - possible_site_types.emplace(site_type); - } - } - } - } - } - - if (possible_site_types.empty()) { - if (getCtx()->verbose) - log_info("Port '%s' has no possible site types, falling back to all types!\n", - port_name.c_str(getCtx())); - possible_site_types = package_pin_site_types; - } - - if (getCtx()->verbose) { - log_info("Possible site types for port %s\n", port_name.c_str(getCtx())); - for (IdString site_type : possible_site_types) { - log_info(" - %s\n", site_type.c_str(getCtx())); - } - } - - auto iter = port_cell->attrs.find(id("PACKAGE_PIN")); - if (iter == port_cell->attrs.end()) { - iter = port_cell->attrs.find(id("LOC")); - if (iter == port_cell->attrs.end()) { - log_error("Port '%s' is missing PACKAGE_PIN or LOC property\n", port_cell->name.c_str(getCtx())); - } - } - - // dict> package_pin_bels; - IdString package_pin_id = id(iter->second.as_string()); - auto pin_iter = package_pin_bels.find(package_pin_id); - if (pin_iter == package_pin_bels.end()) { - log_error("Package pin '%s' not found in part %s\n", package_pin_id.c_str(getCtx()), get_part().c_str()); - } - NPNR_ASSERT(pin_iter != package_pin_bels.end()); - - // Select the first BEL from package_bel_pins that is a legal site - // type. - // - // This is likely the most generic (versus specialized) site type. - BelId package_bel; - for (auto site_type_and_bel : pin_iter->second) { - IdString legal_site_type = site_type_and_bel.first; - BelId bel = site_type_and_bel.second; - - if (possible_site_types.count(legal_site_type)) { - // FIXME: Need to handle case where a port can be in multiple - // modes, but only one of the modes works. - package_bel = bel; - break; - } - } - - if (package_bel == BelId()) { - log_info("Failed to find BEL for package pin '%s' in any possible site types:\n", - package_pin_id.c_str(getCtx())); - for (IdString site_type : possible_site_types) { - log_info(" - %s\n", site_type.c_str(getCtx())); - } - log_error("Failed to find BEL for package pin '%s'\n", package_pin_id.c_str(getCtx())); - } - - if (getCtx()->verbose) { - log_info("Binding port %s to BEL %s\n", port_name.c_str(getCtx()), getCtx()->nameOfBel(package_bel)); - } - - pool placed_cells; - bindBel(package_bel, port_cell, STRENGTH_FIXED); - placed_cells.emplace(port_cell); - - IdStringRange package_bel_pins = getBelPins(package_bel); - IdString pad_pin = get_only_value(package_bel_pins); - - WireId pad_wire = getBelPinWire(package_bel, pad_pin); - place_iobufs(pad_wire, ports[port_pair.first].net, tightly_attached_bels, &placed_cells); - - for (CellInfo *cell : placed_cells) - all_placed_io.insert(cell); - } - - // Check at the end of IO placement, because differential pairs might need P and N sides to both be placed to be - // legal. - for (CellInfo *cell : all_placed_io) { - log_info("%s\n", getCtx()->nameOf(cell)); - NPNR_ASSERT(cell->bel != BelId()); - if (!isBelLocationValid(cell->bel)) { - explain_bel_status(cell->bel); - log_error("Tightly bound BEL %s was not valid!\n", nameOfBel(cell->bel)); - } - } -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/arch_place_constr.cc b/fpga_interchange/arch_place_constr.cc deleted file mode 100644 index 3af48656..00000000 --- a/fpga_interchange/arch_place_constr.cc +++ /dev/null @@ -1,106 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "log.h" -#include "nextpnr.h" -#include "util.h" - -NEXTPNR_NAMESPACE_BEGIN - -void Arch::place_constraints() -{ - std::vector> constrained_cells; - for (auto &cell_pair : cells) { - CellInfo *cell = cell_pair.second.get(); - auto loc_constr = cell->attrs.find(id("LOC")); - auto bel_constr = cell->attrs.find(id("BEL")); - - if (loc_constr == cell->attrs.end() || bel_constr == cell->attrs.end()) - continue; - - IdString loc_name = id(loc_constr->second.as_string()); - IdString bel_name = id(bel_constr->second.as_string()); - - BelId bel; - for (size_t i = 0; i < chip_info->tiles.size(); ++i) { - const auto &tile = chip_info->tiles[i]; - bool site_found = false; - for (size_t j = 0; j < tile.sites.size(); ++j) { - auto &site_data = chip_info->sites[tile.sites[j]]; - if (loc_name == id(site_data.site_name.get())) { - site_found = true; - break; - } - } - - if (!site_found) - continue; - - const auto &tile_type = chip_info->tile_types[tile.type]; - bool bel_found = false; - for (size_t j = 0; j < tile_type.bel_data.size(); ++j) { - const BelInfoPOD &bel_data = tile_type.bel_data[j]; - if (bel_name == IdString(bel_data.name)) { - bel.tile = i; - bel.index = j; - bel_found = true; - break; - } - } - - if (bel_found) - break; - else - log_error("No bel found for user constraint \'%s/%s\' for cell \'%s\'\n", loc_name.c_str(getCtx()), - bel_name.c_str(getCtx()), cell->name.c_str(getCtx())); - } - - if (!isValidBelForCellType(cell->type, bel)) - log_error("Bel \'%s\' is invalid for cell \'%s\' (%s)\n", nameOfBel(bel), cell->name.c_str(getCtx()), - cell->type.c_str(getCtx())); - - auto bound_cell = getBoundBelCell(bel); - if (bound_cell) - log_error("Cell \'%s\' cannot be bound to bel \'%s\' " - "since it is already bound to cell \'%s\'\n", - cell->name.c_str(getCtx()), nameOfBel(bel), bound_cell->name.c_str(getCtx())); - - bindBel(bel, cell, STRENGTH_USER); - - cell->attrs.erase(id("BEL")); - constrained_cells.emplace_back(cell->name, bel); - } - - if (constrained_cells.empty()) - return; - - log_info("Cell placed via user constraints:\n"); - for (auto cell_bel : constrained_cells) { - IdString cell_name = cell_bel.first; - BelId bel = cell_bel.second; - - if (!isBelLocationValid(bel)) - log_error(" - Bel \'%s\' is not valid for cell \'%s\'\n", nameOfBel(bel), cell_name.c_str(getCtx())); - - log_info(" - %s placed at %s\n", cell_name.c_str(getCtx()), nameOfBel(cell_bel.second)); - } -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/arch_pybindings.cc b/fpga_interchange/arch_pybindings.cc deleted file mode 100644 index 1ccbaff7..00000000 --- a/fpga_interchange/arch_pybindings.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2020 gatecat - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef NO_PYTHON - -#include "arch_pybindings.h" -#include "nextpnr.h" -#include "pybindings.h" - -NEXTPNR_NAMESPACE_BEGIN - -void arch_wrap_python(py::module &m) -{ - using namespace PythonConversion; - py::class_(m, "ArchArgs").def_readwrite("chipdb", &ArchArgs::chipdb); - - py::class_(m, "BelId").def_readwrite("index", &BelId::index); - - py::class_(m, "WireId").def_readwrite("index", &WireId::index); - - py::class_(m, "PipId").def_readwrite("index", &PipId::index); - - auto arch_cls = py::class_(m, "Arch").def(py::init()); - auto ctx_cls = py::class_(m, "Context") - .def("checksum", &Context::checksum) - .def("pack", &Context::pack) - .def("place", &Context::place) - .def("route", &Context::route); - - fn_wrapper_0a_v::def_wrap( - ctx_cls, "remove_site_routing"); - fn_wrapper_1a_v>::def_wrap(ctx_cls, "explain_bel_status"); - - typedef dict> CellMap; - typedef dict> NetMap; - typedef dict AliasMap; - typedef dict HierarchyMap; - - auto belpin_cls = py::class_>(m, "BelPin"); - readonly_wrapper>::def_wrap(belpin_cls, "bel"); - readonly_wrapper>::def_wrap(belpin_cls, "pin"); - - typedef FilteredBelRange BelRangeForBelBucket; -#include "arch_pybindings_shared.h" - - WRAP_RANGE(m, BelBucket, conv_to_str); - WRAP_RANGE(m, Bel, conv_to_str); - WRAP_RANGE(m, Wire, conv_to_str); - WRAP_RANGE(m, AllPip, conv_to_str); - WRAP_RANGE(m, UphillPip, conv_to_str); - WRAP_RANGE(m, DownhillPip, conv_to_str); - WRAP_RANGE(m, BelPin, wrap_context); - - WRAP_MAP_UPTR(m, CellMap, "IdCellMap"); - WRAP_MAP_UPTR(m, NetMap, "IdNetMap"); - WRAP_MAP(m, HierarchyMap, wrap_context, "HierarchyMap"); -} - -NEXTPNR_NAMESPACE_END - -#endif // NO_PYTHON diff --git a/fpga_interchange/arch_pybindings.h b/fpga_interchange/arch_pybindings.h deleted file mode 100644 index f571daa9..00000000 --- a/fpga_interchange/arch_pybindings.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2020 gatecat - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ -#ifndef ARCH_PYBINDINGS_H -#define ARCH_PYBINDINGS_H -#ifndef NO_PYTHON - -#include "nextpnr.h" -#include "pybindings.h" - -NEXTPNR_NAMESPACE_BEGIN - -namespace PythonConversion { - -template <> struct string_converter -{ - BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(IdStringList::parse(ctx, name)); } - - std::string to_str(Context *ctx, BelId id) - { - if (id == BelId()) - throw bad_wrap(); - return ctx->getBelName(id).str(ctx); - } -}; - -template <> struct string_converter -{ - WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(IdStringList::parse(ctx, name)); } - - std::string to_str(Context *ctx, WireId id) - { - if (id == WireId()) - throw bad_wrap(); - return ctx->getWireName(id).str(ctx); - } -}; - -template <> struct string_converter -{ - WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(IdStringList::parse(ctx, name)); } - - std::string to_str(Context *ctx, WireId id) - { - if (id == WireId()) - throw bad_wrap(); - return ctx->getWireName(id).str(ctx); - } -}; - -template <> struct string_converter -{ - PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(IdStringList::parse(ctx, name)); } - - std::string to_str(Context *ctx, PipId id) - { - if (id == PipId()) - throw bad_wrap(); - return ctx->getPipName(id).str(ctx); - } -}; - -template <> struct string_converter -{ - BelBucketId from_str(Context *ctx, std::string name) { return ctx->getBelBucketByName(ctx->id(name)); } - - std::string to_str(Context *ctx, BelBucketId id) - { - if (id == BelBucketId()) - throw bad_wrap(); - return ctx->getBelBucketName(id).str(ctx); - } -}; - -template <> struct string_converter -{ - BelPin from_str(Context *ctx, std::string name) - { - NPNR_ASSERT_FALSE("string_converter::from_str not implemented"); - } - - std::string to_str(Context *ctx, BelPin pin) - { - if (pin.bel == BelId()) - throw bad_wrap(); - return ctx->getBelName(pin.bel).str(ctx) + "/" + pin.pin.str(ctx); - } -}; - -} // namespace PythonConversion - -NEXTPNR_NAMESPACE_END -#endif -#endif diff --git a/fpga_interchange/archdefs.h b/fpga_interchange/archdefs.h deleted file mode 100644 index 4a196c29..00000000 --- a/fpga_interchange/archdefs.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Claire Xenia Wolf - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef FPGA_INTERCHANGE_ARCHDEFS_H -#define FPGA_INTERCHANGE_ARCHDEFS_H - -#include - -#include "hashlib.h" -#include "luts.h" -#include "nextpnr_namespaces.h" - -NEXTPNR_NAMESPACE_BEGIN - -typedef int delay_t; - -// ----------------------------------------------------------------------- - -struct BelId -{ - // Tile that contains this BEL. - int32_t tile = -1; - // Index into tile type BEL array. - // BEL indicies are the same for all tiles of the same type. - int32_t index = -1; - - bool operator==(const BelId &other) const { return tile == other.tile && index == other.index; } - bool operator!=(const BelId &other) const { return tile != other.tile || index != other.index; } - bool operator<(const BelId &other) const - { - return tile < other.tile || (tile == other.tile && index < other.index); - } - unsigned int hash() const { return mkhash(tile, index); } -}; - -struct WireId -{ - // Tile that contains this wire. - int32_t tile = -1; - int32_t index = -1; - - bool operator==(const WireId &other) const { return tile == other.tile && index == other.index; } - bool operator!=(const WireId &other) const { return tile != other.tile || index != other.index; } - bool operator<(const WireId &other) const - { - return tile < other.tile || (tile == other.tile && index < other.index); - } - unsigned int hash() const { return mkhash(tile, index); } -}; - -struct PipId -{ - int32_t tile = -1; - int32_t index = -1; - - bool operator==(const PipId &other) const { return tile == other.tile && index == other.index; } - bool operator!=(const PipId &other) const { return tile != other.tile || index != other.index; } - bool operator<(const PipId &other) const - { - return tile < other.tile || (tile == other.tile && index < other.index); - } - unsigned int hash() const { return mkhash(tile, index); } -}; - -struct GroupId -{ - bool operator==(const GroupId &other) const { return true; } - bool operator!=(const GroupId &other) const { return false; } - unsigned int hash() const { return 0; } -}; - -struct DecalId -{ - bool operator==(const DecalId &other) const { return true; } - bool operator!=(const DecalId &other) const { return false; } - unsigned int hash() const { return 0; } -}; - -struct BelBucketId -{ - IdString name; - - bool operator==(const BelBucketId &other) const { return (name == other.name); } - bool operator!=(const BelBucketId &other) const { return (name != other.name); } - bool operator<(const BelBucketId &other) const { return name < other.name; } - unsigned int hash() const { return name.hash(); } -}; - -typedef IdString ClusterId; - -struct SiteExpansionLoop; - -struct ArchNetInfo -{ - virtual ~ArchNetInfo(); - - SiteExpansionLoop *loop = nullptr; -}; - -struct ArchCellInfo -{ - int32_t cell_mapping = -1; - dict> cell_bel_pins; - dict> masked_cell_bel_pins; - pool const_ports; - IdString macro_parent = IdString(); - LutCell lut_cell; -}; - -NEXTPNR_NAMESPACE_END - -#endif /* FPGA_INTERCHANGE_ARCHDEFS_H */ diff --git a/fpga_interchange/cell_parameters.cc b/fpga_interchange/cell_parameters.cc deleted file mode 100644 index e008fea8..00000000 --- a/fpga_interchange/cell_parameters.cc +++ /dev/null @@ -1,222 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "cell_parameters.h" - -#include - -#include "DeviceResources.capnp.h" -#include "context.h" -#include "log.h" -#include "nextpnr_assertions.h" - -NEXTPNR_NAMESPACE_BEGIN - -CellParameters::CellParameters() - // 1'b0 - : verilog_binary_re("([1-9][0-9]*)'b([01]+)$"), - // 8'hF - verilog_hex_re("([1-9][0-9]*)'h([0-9a-fA-F]+)$"), - // 0b10 - c_binary_re("0b([01]+)$"), - // 0xF - c_hex_re("0x([0-9a-fA-F]+)$") -{ -} - -void CellParameters::init(const Context *ctx) -{ - for (const CellParameterPOD &cell_parameter : ctx->chip_info->cell_map->cell_parameters) { - IdString cell_type(cell_parameter.cell_type); - IdString parameter(cell_parameter.parameter); - auto result = parameters.emplace(std::make_pair(cell_type, parameter), &cell_parameter); - NPNR_ASSERT(result.second); - } -} - -static bool parse_int(const std::string &data, int64_t *result) -{ - NPNR_ASSERT(result != nullptr); - try { - *result = boost::lexical_cast(data); - return true; - } catch (boost::bad_lexical_cast &e) { - return false; - } -} - -DynamicBitarray<> CellParameters::parse_int_like(const Context *ctx, IdString cell_type, IdString parameter, - const Property &property) const -{ - const CellParameterPOD *definition = parameters.at(std::make_pair(cell_type, parameter)); - DeviceResources::Device::ParameterFormat format; - format = static_cast(definition->format); - - DynamicBitarray<> result; - switch (format) { - case DeviceResources::Device::ParameterFormat::BOOLEAN: - result.resize(1); - if (property.is_string) { - if (property.as_string() == "TRUE" || property.as_string() == "1") { - result.set(0, true); - } else if (property.as_string() == "FALSE" || property.as_string() == "0") { - result.set(0, false); - } else { - log_error("Property value %s not expected for BOOLEAN type.\n", property.c_str()); - } - } else { - if (property.intval == 1) { - result.set(0, true); - } else if (property.intval == 0) { - result.set(0, false); - } else { - log_error("Property value %lu not expected for BOOLEAN type.\n", property.intval); - } - } - - return result; - case DeviceResources::Device::ParameterFormat::INTEGER: - if (property.is_string) { - char *endptr; - std::uintmax_t value = strtoumax(property.c_str(), &endptr, /*base=*/10); - if (endptr != (property.c_str() + property.size())) { - log_error("Property value %s not expected for INTEGER type.\n", property.c_str()); - } - - return DynamicBitarray<>::to_bitarray(value); - } else { - return DynamicBitarray<>::to_bitarray(property.intval); - } - break; - case DeviceResources::Device::ParameterFormat::VERILOG_BINARY: - if (property.is_string) { - std::smatch m; - if (!std::regex_match(property.as_string(), m, verilog_binary_re)) { - log_error("Property value %s not expected for VERILOG_BINARY type.\n", property.c_str()); - } - - int64_t width; - if (!parse_int(m[1], &width)) { - log_error("Failed to parse width from property value %s of type VERILOG_BINARY.\n", property.c_str()); - } - if (width < 0) { - log_error("Expected width to be positive for property value %s\n", property.c_str()); - } - - return DynamicBitarray<>::parse_binary_bitstring(width, m[2]); - } else { - return DynamicBitarray<>::to_bitarray(property.intval); - } - break; - case DeviceResources::Device::ParameterFormat::VERILOG_HEX: - if (property.is_string) { - std::smatch m; - if (!std::regex_match(property.as_string(), m, verilog_hex_re)) { - log_error("Property value %s not expected for VERILOG_HEX type.\n", property.c_str()); - } - - int64_t width; - if (!parse_int(m[1], &width)) { - log_error("Failed to parse width from property value %s of type VERILOG_HEX.\n", property.c_str()); - } - if (width < 0) { - log_error("Expected width to be positive for property value %s\n", property.c_str()); - } - - return DynamicBitarray<>::parse_hex_bitstring(width, m[2]); - } else { - return DynamicBitarray<>::to_bitarray(property.intval); - } - break; - case DeviceResources::Device::ParameterFormat::C_BINARY: - if (property.is_string) { - std::smatch m; - if (!std::regex_match(property.as_string(), m, c_binary_re)) { - log_error("Property value %s not expected for C_BINARY type.\n", property.c_str()); - } - - return DynamicBitarray<>::parse_binary_bitstring(/*width=*/-1, m[1]); - } else { - return DynamicBitarray<>::to_bitarray(property.intval); - } - break; - case DeviceResources::Device::ParameterFormat::C_HEX: - if (property.is_string) { - std::smatch m; - if (!std::regex_match(property.as_string(), m, c_hex_re)) { - log_error("Property value %s not expected for C_HEX type.\n", property.c_str()); - } - - return DynamicBitarray<>::parse_hex_bitstring(/*width=*/-1, m[1]); - } else { - return DynamicBitarray<>::to_bitarray(property.intval); - } - break; - default: - log_error("Format %d is not int-like\n", definition->format); - } - - // Unreachable! - NPNR_ASSERT(false); -} - -bool CellParameters::compare_property(const Context *ctx, IdString cell_type, IdString parameter, - const Property &property, IdString value_to_compare) const -{ - const CellParameterPOD *definition = parameters.at(std::make_pair(cell_type, parameter)); - DeviceResources::Device::ParameterFormat format; - format = static_cast(definition->format); - - switch (format) { - case DeviceResources::Device::ParameterFormat::STRING: - return value_to_compare.c_str(ctx) == property.as_string(); - case DeviceResources::Device::ParameterFormat::FLOATING_POINT: - // Note: Comparing floating point is pretty weird - log_warning("Doing direct comparisions on floating points values is pretty weird, double check this. Cell " - "type %s parameter %s\n", - cell_type.c_str(ctx), parameter.c_str(ctx)); - return value_to_compare.c_str(ctx) == property.as_string(); - case DeviceResources::Device::ParameterFormat::BOOLEAN: - case DeviceResources::Device::ParameterFormat::INTEGER: - case DeviceResources::Device::ParameterFormat::VERILOG_BINARY: - case DeviceResources::Device::ParameterFormat::VERILOG_HEX: - case DeviceResources::Device::ParameterFormat::C_BINARY: - case DeviceResources::Device::ParameterFormat::C_HEX: { - if (property.is_string) { - // Given that string presentations should be equivalent if - // formatted consistently, this should work most and or all of - // the time. If there are important exceptions, revisit this. - return property.as_string() == value_to_compare.c_str(ctx); - } else { - int64_t int_to_compare; - if (!parse_int(value_to_compare.c_str(ctx), &int_to_compare)) { - log_error("Comparision failed, to compare value %s is not int-like\n", value_to_compare.c_str(ctx)); - } - - return property.intval == int_to_compare; - } - } - } - - // Unreachable! - NPNR_ASSERT(false); -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/cell_parameters.h b/fpga_interchange/cell_parameters.h deleted file mode 100644 index d6d4ccec..00000000 --- a/fpga_interchange/cell_parameters.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef CELL_PARAMETERS_H -#define CELL_PARAMETERS_H - -#include - -#include "chipdb.h" -#include "dynamic_bitarray.h" -#include "nextpnr_namespaces.h" -#include "property.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct Context; - -struct CellParameters -{ - CellParameters(); - void init(const Context *ctx); - DynamicBitarray<> parse_int_like(const Context *ctx, IdString cell_type, IdString parameter, - const Property &property) const; - bool compare_property(const Context *ctx, IdString cell_type, IdString parameter, const Property &property, - IdString value_to_compare) const; - - dict, const CellParameterPOD *> parameters; - - std::regex verilog_binary_re; - std::regex verilog_hex_re; - std::regex c_binary_re; - std::regex c_hex_re; -}; - -NEXTPNR_NAMESPACE_END - -#endif /* CELL_PARAMETERS_H */ diff --git a/fpga_interchange/chipdb.h b/fpga_interchange/chipdb.h deleted file mode 100644 index 7ef0914d..00000000 --- a/fpga_interchange/chipdb.h +++ /dev/null @@ -1,547 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef CHIPDB_H -#define CHIPDB_H - -#include "archdefs.h" -#include "nextpnr_namespaces.h" -#include "relptr.h" - -NEXTPNR_NAMESPACE_BEGIN - -/* !!! Everything in this section must be kept in sync !!! - * !!! with fpga_interchange/chip_info.py !!! - * - * When schema changes, bump version number in chip_info.py and - * kExpectedChipInfoVersion - */ - -static constexpr int32_t kExpectedChipInfoVersion = 15; - -NPNR_PACKED_STRUCT(struct BelConnectedPinsPOD { - int32_t pin1; - int32_t pin2; -}); - -// Flattened site indexing. -// -// To enable flat BelId.z spaces, every tile and sites within that tile are -// flattened. -// -// This has implications on BelId's, WireId's and PipId's. -// The flattened site space works as follows: -// - Objects that belong to the tile are first. BELs are always part of Sites, -// so no BEL objects are in this category. -// - All site alternative modes are exposed as a "full" site. -// - Each site appends it's BEL's, wires (site wires) and PIP's. -// - Sites add two types of pips. Sites will add pip data first for site -// pips, and then for site pin edges. -// 1. The first type is site pips, which connect site wires to other site -// wires. -// 2. The second type is site pin edges, which connect site wires to tile -// wires (or vise-versa). - -NPNR_PACKED_STRUCT(struct BelInfoPOD { - int32_t name; // bel name (in site) constid - int32_t type; // Type name constid - int32_t bel_bucket; // BEL bucket constid. - - int32_t num_bel_wires; - RelPtr ports; // port name constid - RelPtr types; // port type (IN/OUT/BIDIR) - RelPtr wires; // connected wire index in tile, or -1 if NA - - int16_t site; - int16_t site_variant; // some sites have alternative types - int16_t category; - int8_t synthetic; - int8_t lut_element; - - RelPtr pin_map; // Index into CellMapPOD::cell_bel_map - - // If this BEL is a site routing BEL with inverting pins, these values - // will be [0, num_bel_wires). If this BEL is either not a site routing - // BEL or this site routing has no inversion capabilities, then these will - // both be -1. - int8_t non_inverting_pin; - int8_t inverting_pin; - - int16_t padding; - - RelSlice connected_pins; -}); - -enum BELCategory -{ - // BEL is a logic element - BEL_CATEGORY_LOGIC = 0, - // BEL is a site routing mux - BEL_CATEGORY_ROUTING = 1, - // BEL is a site port, e.g. boundry between site and routing graph. - BEL_CATEGORY_SITE_PORT = 2 -}; - -NPNR_PACKED_STRUCT(struct BelPortPOD { - int32_t bel_index; - int32_t port; -}); - -NPNR_PACKED_STRUCT(struct TileWireInfoPOD { - int32_t name; // wire name constid - - // Pip index inside tile - RelSlice pips_uphill; - - // Pip index inside tile - RelSlice pips_downhill; - - // Bel index inside tile - RelSlice bel_pins; - - int16_t site; // site index in tile - int16_t site_variant; // site variant index in tile -}); - -NPNR_PACKED_STRUCT(struct PipInfoPOD { - int32_t src_index, dst_index; - int16_t site; // site index in tile - int16_t site_variant; // site variant index in tile - int16_t bel; // BEL this pip belongs to if site pip. - int16_t extra_data; - RelSlice pseudo_cell_wires; -}); - -NPNR_PACKED_STRUCT(struct ConstraintTagPOD { - int32_t tag_prefix; // constid - int32_t default_state; // constid - RelSlice states; // constid -}); - -NPNR_PACKED_STRUCT(struct LutBelPOD { - uint32_t name; // constid - RelSlice pins; // constid - uint32_t low_bit; - uint32_t high_bit; - int32_t out_pin; // constid -}); - -NPNR_PACKED_STRUCT(struct LutElementPOD { - int32_t width; - RelSlice lut_bels; -}); - -NPNR_PACKED_STRUCT(struct TileTypeInfoPOD { - int32_t name; // Tile type constid - - RelSlice bel_data; - - RelSlice wire_data; - - RelSlice pip_data; - - RelSlice tags; - - RelSlice lut_elements; - - RelSlice site_types; // constid -}); - -NPNR_PACKED_STRUCT(struct SiteInstInfoPOD { - RelPtr name; - RelPtr site_name; - - // Which site type is this site instance? - // constid - int32_t site_type; -}); - -NPNR_PACKED_STRUCT(struct TileInstInfoPOD { - // Name of this tile. - RelPtr name; - - // Index into root.tile_types. - int32_t type; - - // This array is root.tile_types[type].site_types.size() long. - // Index into root.sites - RelSlice sites; - - // Number of tile wires; excluding any site-internal wires - // which come after general wires and are not stored here - // as they will never be nodal - // -1 if a tile-local wire; node index if nodal wire - RelSlice tile_wire_to_node; - - // Index into wire_types - RelSlice tile_wire_to_type; -}); - -NPNR_PACKED_STRUCT(struct TileWireRefPOD { - int32_t tile; - int32_t index; -}); - -NPNR_PACKED_STRUCT(struct NodeInfoPOD { RelSlice tile_wires; }); - -NPNR_PACKED_STRUCT(struct CellBelPinPOD { - int32_t cell_pin; // constid - int32_t bel_pin; // constid -}); - -NPNR_PACKED_STRUCT(struct ParameterPinsPOD { - int32_t key; // constid - int32_t value; // constid - RelSlice pins; -}); - -NPNR_PACKED_STRUCT(struct CellConstraintPOD { - int32_t tag; // Tag index - int32_t constraint_type; // Constraint::ConstraintType - RelSlice states; // State indicies -}); - -// Cell parameters metadata -NPNR_PACKED_STRUCT(struct CellParameterPOD { - int32_t cell_type; // constid - int32_t parameter; // constid - int32_t format; // ParameterFormat enum - int32_t default_value; // constid -}); - -NPNR_PACKED_STRUCT(struct CellBelMapPOD { - RelSlice common_pins; - RelSlice parameter_pins; - RelSlice constraints; -}); - -NPNR_PACKED_STRUCT(struct LutCellPOD { - int32_t cell; // constid - RelSlice input_pins; // constids - int32_t parameter; -}); - -NPNR_PACKED_STRUCT(struct CellMapPOD { - // Cell names supported in this arch. - RelSlice cell_names; // constids - - // BEL names that are global buffers. - RelSlice global_buffers; // constids - - // Name of BelBuckets. - RelSlice cell_bel_buckets; // constids - - RelSlice cell_bel_map; - - RelSlice lut_cells; - RelSlice cell_parameters; -}); - -NPNR_PACKED_STRUCT(struct PackagePinPOD { - int32_t package_pin; // constid - int32_t site; // constid - int32_t bel; // constid -}); - -NPNR_PACKED_STRUCT(struct PackagePOD { - int32_t package; // constid - RelSlice pins; -}); - -enum CellPinValue -{ - // leave floating - PIN_VALUE_FLOAT = 0, - // connect to ground - PIN_VALUE_GND = 1, - // connect to vcc - PIN_VALUE_VCC = 2, -}; - -NPNR_PACKED_STRUCT(struct DefaultCellConnPOD { - int32_t pin_name; // constid - int32_t value; // CellPinValue -}); - -NPNR_PACKED_STRUCT(struct DefaultCellConnsPOD { - int32_t cell_type; // constid - RelSlice pins; -}); - -NPNR_PACKED_STRUCT(struct ConstantsPOD { - // Cell type and port for the GND and VCC global source. - int32_t gnd_cell_name; // constid - int32_t gnd_cell_port; // constid - - int32_t vcc_cell_name; // constid - int32_t vcc_cell_port; // constid - - int32_t gnd_bel_tile; - int32_t gnd_bel_index; - int32_t gnd_bel_pin; // constid - - int32_t vcc_bel_tile; - int32_t vcc_bel_index; - int32_t vcc_bel_pin; // constid - - // Name to use for the global GND constant net - int32_t gnd_net_name; // constid - - // Name to use for the global VCC constant net - int32_t vcc_net_name; // constid - - // If a choice is available, which constant net should be used? - // Can be ''/0 if either constant net are equivilent. - int32_t best_constant_net; // constid - - // Default cell pin connections - RelSlice default_conns; -}); - -enum WireCategory -{ - WIRE_CAT_GENERAL = 0, - WIRE_CAT_SPECIAL = 1, - WIRE_CAT_GLOBAL = 2, -}; - -NPNR_PACKED_STRUCT(struct WireTypePOD { - int32_t name; // constid - int32_t category; // WireCategory -}); - -NPNR_PACKED_STRUCT(struct GlobalCellPinPOD { - int32_t name; // constid - int16_t max_hops; // max routing hops to try - int8_t guide_placement; - int8_t force_routing; -}); - -NPNR_PACKED_STRUCT(struct GlobalCellPOD { - int32_t cell_type; - RelSlice pins; -}); - -NPNR_PACKED_STRUCT(struct MacroParameterPOD { - int32_t key; // constid - int32_t value; // constid -}); - -enum MacroParamRuleType -{ - PARAM_MAP_COPY = 0, // copy parameter value - PARAM_MAP_SLICE = 1, // take a slice of bits - PARAM_MAP_TABLE = 2, // lookup strings in table -}; - -NPNR_PACKED_STRUCT(struct MacroParamMapRulePOD { - // name of parameter on parent primitive - int32_t prim_param; // constid - // name of instance to set parameter on - int32_t inst_name; // constid - // name of parameter on macro expansion instance - int32_t inst_param; // constid - // type of mapping to use to derive new value - int32_t rule_type; // MacroParamRuleType - // for slice mappings, the bits to collect - RelSlice slice_bits; - // for table mappings, the lookup table to use - RelSlice map_table; -}); - -NPNR_PACKED_STRUCT(struct MacroCellInstPOD { - int32_t name; // instance name constid - int32_t type; // instance type constid - // parameters to set on cell - RelSlice parameters; -}); - -NPNR_PACKED_STRUCT(struct MacroPortInstPOD { - // name of the cell instance the port is on; or 0/'' for top level ports - int32_t instance; - // name of the port - int32_t port; - // direction of the port - int32_t dir; -}); - -NPNR_PACKED_STRUCT(struct MacroNetPOD { - // name of the net - int32_t name; - // ports on the net - RelSlice ports; -}); - -NPNR_PACKED_STRUCT(struct MacroPOD { - // macro name - int32_t name; - // cell instances inside macro - RelSlice cell_insts; - // nets inside macro - RelSlice nets; -}); - -NPNR_PACKED_STRUCT(struct MacroExpansionPOD { - // primitive name to match - int32_t prim_name; - // macro name to expand to - int32_t macro_name; - // list of parameters to (optionally) match - RelSlice param_matches; - // how to derive parameters for expansion instances - RelSlice param_rules; -}); - -NPNR_PACKED_STRUCT(struct ClusterCellPortPOD { - uint32_t cell; - uint32_t port; -}); - -NPNR_PACKED_STRUCT(struct ChainablePortPOD { - uint32_t cell_source; - uint32_t cell_sink; - uint32_t bel_source; - uint32_t bel_sink; - int16_t avg_x_offset; - int16_t avg_y_offset; -}); - -NPNR_PACKED_STRUCT(struct ClusterRequiredCellPOD { - uint32_t name; - uint32_t count; -}); - -NPNR_PACKED_STRUCT(struct ClusterUsedPortPOD { uint32_t name; }); - -NPNR_PACKED_STRUCT(struct ClusterEdgePOD { - uint32_t dir; - uint32_t cell_pin; - uint32_t other_cell_pin; - uint32_t other_cell_type; -}); - -NPNR_PACKED_STRUCT(struct ClusterConnectionsPOD { - uint32_t target_idx; - RelSlice edges; -}); - -NPNR_PACKED_STRUCT(struct ClusterConnectionGraphPOD { - uint32_t idx; - uint32_t cell_type; - RelSlice connections; - RelSlice used_ports; -}); - -NPNR_PACKED_STRUCT(struct ClusterPhysicalPlacementEntryPOD { RelSlice bels; }); - -NPNR_PACKED_STRUCT(struct ClusterPhysicalPlacementsPOD { - uint32_t site_type; - RelSlice places; -}); - -NPNR_PACKED_STRUCT(struct ClusterPOD { - uint32_t name; - RelSlice root_cell_types; - RelSlice chainable_ports; - RelSlice cluster_cells_map; - RelSlice required_cells; - RelSlice connection_graph; - RelSlice physical_placements; - uint32_t out_of_site_clusters; - uint32_t disallow_other_cells; - uint32_t from_macro; -}); - -NPNR_PACKED_STRUCT(struct ChipInfoPOD { - RelPtr name; - RelPtr generator; - - int32_t version; - int32_t width, height; - - RelSlice tile_types; - RelSlice sites; - RelSlice tiles; - RelSlice nodes; - RelSlice packages; - RelSlice wire_types; - RelSlice global_cells; - - // Macro related data - RelSlice macros; - RelSlice macro_rules; - - RelSlice clusters; - - // BEL bucket constids. - RelSlice bel_buckets; - - RelPtr cell_map; - RelPtr constants; - - // Constid string data. - RelPtr>> constids; -}); - -/************************ End of chipdb section. ************************/ - -inline const TileTypeInfoPOD &tile_info(const ChipInfoPOD *chip_info, int32_t tile) -{ - return chip_info->tile_types[chip_info->tiles[tile].type]; -} - -template const TileTypeInfoPOD &loc_info(const ChipInfoPOD *chip_info, Id &id) -{ - return chip_info->tile_types[chip_info->tiles[id.tile].type]; -} - -NPNR_ALWAYS_INLINE inline const BelInfoPOD &bel_info(const ChipInfoPOD *chip_info, BelId bel) -{ - NPNR_ASSERT(bel != BelId()); - return loc_info(chip_info, bel).bel_data[bel.index]; -} - -inline const PipInfoPOD &pip_info(const ChipInfoPOD *chip_info, PipId pip) -{ - NPNR_ASSERT(pip != PipId()); - return loc_info(chip_info, pip).pip_data[pip.index]; -} - -inline const SiteInstInfoPOD &site_inst_info(const ChipInfoPOD *chip_info, int32_t tile, int32_t site) -{ - return chip_info->sites[chip_info->tiles[tile].sites[site]]; -} - -inline const ClusterPOD &cluster_info(const ChipInfoPOD *chip_info, int32_t cluster) -{ - return chip_info->clusters[cluster]; -} - -enum SyntheticType -{ - NOT_SYNTH = 0, - SYNTH_SIGNAL = 1, - SYNTH_GND = 2, - SYNTH_VCC = 3, -}; - -NEXTPNR_NAMESPACE_END - -#endif /* CHIPDB_H */ diff --git a/fpga_interchange/cost_map.cc b/fpga_interchange/cost_map.cc deleted file mode 100644 index 1062bfd6..00000000 --- a/fpga_interchange/cost_map.cc +++ /dev/null @@ -1,400 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "cost_map.h" - -#include "context.h" -#include "log.h" - -NEXTPNR_NAMESPACE_BEGIN - -///@brief Factor to adjust the penalty calculation for deltas outside the segment bounding box: -// factor < 1.0: penalty has less impact on the final returned delay -// factor > 1.0: penalty has more impact on the final returned delay -static constexpr float PENALTY_FACTOR = 1.f; - -///@brief Minimum penalty cost that is added when penalizing a delta outside the segment bounding box. -static constexpr delay_t PENALTY_MIN = 1; - -// also known as the L1 norm -static int manhattan_distance(const std::pair &a, const std::pair &b) -{ - return std::abs(b.first - a.first) + std::abs(b.second - a.second); -} - -static delay_t penalize(const delay_t &entry, int distance, delay_t penalty) -{ - penalty = std::max(penalty, PENALTY_MIN); - return entry + distance * penalty * PENALTY_FACTOR; -} - -delay_t CostMap::get_delay(const Context *ctx, WireId src_wire, WireId dst_wire) const -{ - TypeWirePair type_pair; - type_pair.src = TypeWireId(ctx, src_wire); - type_pair.dst = TypeWireId(ctx, dst_wire); - - int src_tile; - if (src_wire.tile == -1) { - src_tile = ctx->chip_info->nodes[src_wire.index].tile_wires[0].tile; - } else { - src_tile = src_wire.tile; - } - - int32_t src_x, src_y; - ctx->get_tile_x_y(src_tile, &src_x, &src_y); - - int dst_tile; - if (dst_wire.tile == -1) { - dst_tile = ctx->chip_info->nodes[dst_wire.index].tile_wires[0].tile; - } else { - dst_tile = dst_wire.tile; - } - - int32_t dst_x, dst_y; - ctx->get_tile_x_y(dst_tile, &dst_x, &dst_y); - - auto iter = cost_map_.find(type_pair); - if (iter == cost_map_.end()) { - auto &src_type = ctx->chip_info->tile_types[type_pair.src.type]; - IdString src_tile_type(src_type.name); - IdString src_wire_name(src_type.wire_data[type_pair.src.index].name); - - auto &dst_type = ctx->chip_info->tile_types[type_pair.dst.type]; - IdString dst_tile_type(dst_type.name); - IdString dst_wire_name(dst_type.wire_data[type_pair.dst.index].name); - -#if 0 - log_warning("Delay matrix is missing %s/%s -> %s/%s\n", - src_tile_type.c_str(ctx), - src_wire_name.c_str(ctx), - dst_tile_type.c_str(ctx), - dst_wire_name.c_str(ctx)); -#endif - return std::numeric_limits::max(); - } - - const auto &delay_matrix = iter->second; - - int32_t off_x = delay_matrix.offset.first + (dst_x - src_x); - int32_t off_y = delay_matrix.offset.second + (dst_y - src_y); - - int32_t x_dim = delay_matrix.data.shape()[0]; - int32_t y_dim = delay_matrix.data.shape()[1]; - NPNR_ASSERT(x_dim > 0); - NPNR_ASSERT(y_dim > 0); - - // Bound closest_x/y to [0, dim) - int32_t closest_x = std::min(std::max(off_x, 0), x_dim - 1); - int32_t closest_y = std::min(std::max(off_y, 0), y_dim - 1); - - // Get the cost entry from the cost map at the deltas values - auto cost = delay_matrix.data[closest_x][closest_y]; - NPNR_ASSERT(cost >= 0); - - // Get the base penalty corresponding to the current segment. - auto penalty = delay_matrix.penalty; - - // Get the distance between the closest point in the bounding box and the original coordinates. - // Note that if the original coordinates are within the bounding box, the distance will be equal to zero. - auto distance = manhattan_distance(std::make_pair(off_x, off_y), std::make_pair(closest_x, closest_y)); - - // Return the penalized cost (no penalty is added if the coordinates are within the bounding box). - return penalize(cost, distance, penalty); -} - -void CostMap::set_cost_map(const Context *ctx, const TypeWirePair &wire_pair, - const dict, delay_t> &delays) -{ - CostMapEntry delay_matrix; - - auto &offset = delay_matrix.offset; - offset.first = 0; - offset.second = 0; - - int32_t max_x_offset = 0; - int32_t max_y_offset = 0; - - for (const auto &delay_pair : delays) { - auto &dx_dy = delay_pair.first; - offset.first = std::max(-dx_dy.first, offset.first); - offset.second = std::max(-dx_dy.second, offset.second); - max_x_offset = std::max(dx_dy.first, max_x_offset); - max_y_offset = std::max(dx_dy.second, max_y_offset); - } - - int32_t x_dim = offset.first + max_x_offset + 1; - int32_t y_dim = offset.second + max_y_offset + 1; - - delay_matrix.data.resize(boost::extents[x_dim][y_dim]); - - // Fill matrix with sentinel of -1 to know where the holes in the matrix - // are. - std::fill_n(delay_matrix.data.data(), delay_matrix.data.num_elements(), -1); - - for (const auto &delay_pair : delays) { - auto &dx_dy = delay_pair.first; - int32_t off_x = dx_dy.first + offset.first; - int32_t off_y = dx_dy.second + offset.second; - NPNR_ASSERT(off_x >= 0); - NPNR_ASSERT(off_x < x_dim); - NPNR_ASSERT(off_y >= 0); - NPNR_ASSERT(off_y < y_dim); - - delay_matrix.data[off_x][off_y] = delay_pair.second; - } - - delay_matrix.penalty = get_penalty(delay_matrix.data); - fill_holes(ctx, wire_pair, delay_matrix.data, delay_matrix.penalty); - - { - cost_map_mutex_.lock(); - auto result = cost_map_.emplace(wire_pair, delay_matrix); - NPNR_ASSERT(result.second); - cost_map_mutex_.unlock(); - } -} - -static void assign_min_entry(delay_t *dst, const delay_t &src) -{ - if (src >= 0) { - if (*dst < 0) { - *dst = src; - } else if (src < *dst) { - *dst = src; - } - } -} - -std::pair CostMap::get_nearby_cost_entry(const boost::multi_array &matrix, int cx, int cy, - const BoundingBox &bounds) -{ -#ifdef DEBUG_FILL - log_info("Filling %d, %d within (%d, %d, %d, %d)\n", cx, cy, bounds.x0, bounds.y0, bounds.x1, bounds.y1); -#endif - - // spiral around (cx, cy) looking for a nearby entry - bool in_bounds = bounds.contains(cx, cy); - if (!in_bounds) { -#ifdef DEBUG_FILL - log_info("Already out of bounds, return!\n"); -#endif - return std::make_pair(-1, 0); - } - int n = 0; - delay_t fill(matrix[cx][cy]); - - while (in_bounds && (fill < 0)) { - n++; -#ifdef DEBUG_FILL - log_info("At n = %d\n", n); -#endif - in_bounds = false; - delay_t min_entry = -1; - for (int ox = -n; ox <= n; ox++) { - int x = cx + ox; - int oy = n - abs(ox); - int yp = cy + oy; - int yn = cy - oy; -#ifdef DEBUG_FILL - log_info("Testing %d, %d\n", x, yp); -#endif - if (bounds.contains(x, yp)) { - assign_min_entry(&min_entry, matrix[x][yp]); - in_bounds = true; -#ifdef DEBUG_FILL - log_info("matrix[%d, %d] = %d, min_entry = %d\n", x, yp, matrix[x][yp], min_entry); -#endif - } -#ifdef DEBUG_FILL - log_info("Testing %d, %d\n", x, yn); -#endif - if (bounds.contains(x, yn)) { - assign_min_entry(&min_entry, matrix[x][yn]); - in_bounds = true; -#ifdef DEBUG_FILL - log_info("matrix[%d, %d] = %d, min_entry = %d\n", x, yn, matrix[x][yn], min_entry); -#endif - } - } - - if (fill < 0 && min_entry >= 0) { - fill = min_entry; - } - } - - return std::make_pair(fill, n); -} - -void CostMap::fill_holes(const Context *ctx, const TypeWirePair &type_pair, boost::multi_array &matrix, - delay_t delay_penalty) -{ - // find missing cost entries and fill them in by copying a nearby cost entry - std::vector> missing; - bool couldnt_fill = false; - auto shifted_bounds = BoundingBox(0, 0, matrix.shape()[0] - 1, matrix.shape()[1] - 1); - int max_fill = 0; - for (unsigned ix = 0; ix < matrix.shape()[0]; ix++) { - for (unsigned iy = 0; iy < matrix.shape()[1]; iy++) { - delay_t &cost_entry = matrix[ix][iy]; - if (cost_entry < 0) { - // maximum search radius - delay_t filler; - int distance; - std::tie(filler, distance) = get_nearby_cost_entry(matrix, ix, iy, shifted_bounds); - if (filler >= 0) { - missing.push_back(std::make_tuple(ix, iy, penalize(filler, distance, delay_penalty))); - max_fill = std::max(max_fill, distance); - } else { - couldnt_fill = true; - } - } - } - if (couldnt_fill) { - // give up trying to fill an empty matrix - break; - } - } - - if (!couldnt_fill && max_fill > 0) { - if (ctx->verbose) { - auto &src_type_data = ctx->chip_info->tile_types[type_pair.src.type]; - IdString src_type(src_type_data.name); - IdString src_wire(src_type_data.wire_data[type_pair.src.index].name); - - auto &dst_type_data = ctx->chip_info->tile_types[type_pair.dst.type]; - IdString dst_type(dst_type_data.name); - IdString dst_wire(dst_type_data.wire_data[type_pair.dst.index].name); - -#ifdef DEBUG_FILL - log_info("At %s/%s -> %s/%s: max_fill = %d, delay_penalty = %d\n", src_type.c_str(ctx), src_wire.c_str(ctx), - dst_type.c_str(ctx), dst_wire.c_str(ctx), max_fill, delay_penalty); -#endif - } - } - - // write back the missing entries - for (auto &xy_entry : missing) { - matrix[std::get<0>(xy_entry)][std::get<1>(xy_entry)] = std::get<2>(xy_entry); - } - - if (couldnt_fill) { - auto &src_type_data = ctx->chip_info->tile_types[type_pair.src.type]; - IdString src_type(src_type_data.name); - IdString src_wire(src_type_data.wire_data[type_pair.src.index].name); - - auto &dst_type_data = ctx->chip_info->tile_types[type_pair.dst.type]; - IdString dst_type(dst_type_data.name); - IdString dst_wire(dst_type_data.wire_data[type_pair.dst.index].name); - - log_warning("Couldn't fill holes in the cost matrix %s/%s -> %s/%s %d x %d bounding box\n", src_type.c_str(ctx), - src_wire.c_str(ctx), dst_type.c_str(ctx), dst_wire.c_str(ctx), shifted_bounds.x1, - shifted_bounds.y1); - for (unsigned y = 0; y < matrix.shape()[1]; y++) { - for (unsigned x = 0; x < matrix.shape()[0]; x++) { - NPNR_ASSERT(matrix[x][y] >= 0); - } - } - } -} - -delay_t CostMap::get_penalty(const boost::multi_array &matrix) const -{ - delay_t min_delay = std::numeric_limits::max(); - delay_t max_delay = std::numeric_limits::lowest(); - - std::pair min_location(0, 0), max_location(0, 0); - for (unsigned ix = 0; ix < matrix.shape()[0]; ix++) { - for (unsigned iy = 0; iy < matrix.shape()[1]; iy++) { - const delay_t &cost_entry = matrix[ix][iy]; - if (cost_entry >= 0) { - if (cost_entry < min_delay) { - min_delay = cost_entry; - min_location = std::make_pair(ix, iy); - } - if (cost_entry > max_delay) { - max_delay = cost_entry; - max_location = std::make_pair(ix, iy); - } - } - } - } - - delay_t delay_penalty = - (max_delay - min_delay) / static_cast(std::max(1, manhattan_distance(max_location, min_location))); - - return delay_penalty; -} - -void CostMap::from_reader(lookahead_storage::CostMap::Reader reader) -{ - for (auto cost_entry : reader.getCostMap()) { - TypeWirePair key(cost_entry.getKey()); - - auto result = cost_map_.emplace(key, CostMapEntry()); - NPNR_ASSERT(result.second); - - CostMapEntry &entry = result.first->second; - auto data = cost_entry.getData(); - auto in_iter = data.begin(); - - entry.data.resize(boost::extents[cost_entry.getXDim()][cost_entry.getYDim()]); - if (entry.data.num_elements() != data.size()) { - log_error("entry.data.num_elements() %zu != data.size() %u", entry.data.num_elements(), data.size()); - } - - delay_t *out = entry.data.origin(); - for (; in_iter != data.end(); ++in_iter, ++out) { - *out = *in_iter; - } - - entry.penalty = cost_entry.getPenalty(); - - entry.offset.first = cost_entry.getXOffset(); - entry.offset.second = cost_entry.getYOffset(); - } -} - -void CostMap::to_builder(lookahead_storage::CostMap::Builder builder) const -{ - auto cost_map = builder.initCostMap(cost_map_.size()); - auto entry_iter = cost_map.begin(); - auto in = cost_map_.begin(); - for (; entry_iter != cost_map.end(); ++entry_iter, ++in) { - NPNR_ASSERT(in != cost_map_.end()); - - in->first.to_builder(entry_iter->getKey()); - const CostMapEntry &entry = in->second; - - auto data = entry_iter->initData(entry.data.num_elements()); - const delay_t *data_in = entry.data.origin(); - for (size_t i = 0; i < entry.data.num_elements(); ++i) { - data.set(i, data_in[i]); - } - - entry_iter->setXDim(entry.data.shape()[0]); - entry_iter->setYDim(entry.data.shape()[1]); - entry_iter->setXOffset(entry.offset.first); - entry_iter->setYOffset(entry.offset.second); - entry_iter->setPenalty(entry.penalty); - } -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/cost_map.h b/fpga_interchange/cost_map.h deleted file mode 100644 index dfde29f9..00000000 --- a/fpga_interchange/cost_map.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef COST_MAP_H -#define COST_MAP_H - -#include -#include - -#include "lookahead.capnp.h" -#include "nextpnr_namespaces.h" -#include "nextpnr_types.h" -#include "type_wire.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct Context; - -class CostMap -{ - public: - delay_t get_delay(const Context *ctx, WireId src, WireId dst) const; - void set_cost_map(const Context *ctx, const TypeWirePair &wire_pair, - const dict, delay_t> &delays); - - void from_reader(lookahead_storage::CostMap::Reader reader); - void to_builder(lookahead_storage::CostMap::Builder builder) const; - - private: - struct CostMapEntry - { - boost::multi_array data; - std::pair offset; - delay_t penalty; - }; - - std::mutex cost_map_mutex_; - dict cost_map_; - - void fill_holes(const Context *ctx, const TypeWirePair &wire_pair, boost::multi_array &matrix, - delay_t delay_penality); - - std::pair get_nearby_cost_entry(const boost::multi_array &matrix, int cx, int cy, - const BoundingBox &bounds); - delay_t get_penalty(const boost::multi_array &matrix) const; -}; - -NEXTPNR_NAMESPACE_END - -#endif /* COST_MAP_H */ diff --git a/fpga_interchange/dedicated_interconnect.cc b/fpga_interchange/dedicated_interconnect.cc deleted file mode 100644 index e4ce2b95..00000000 --- a/fpga_interchange/dedicated_interconnect.cc +++ /dev/null @@ -1,902 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "log.h" -#include "nextpnr.h" -#include "util.h" - -NEXTPNR_NAMESPACE_BEGIN -namespace { -// All legal routes involved at most 2 sites, the source site and the sink -// site. The source site and sink sites may be the same, but that is not -// dedicated routing, that is intra site routing. -// -// Dedicated routing must leave the sink site, traverse some routing and -// terminate at another site. Routing that "flys" over a site is expressed as -// a psuedo-pip connected the relevant site pin wires, rather than traversing -// the site. -enum WireNodeState -{ - IN_SINK_SITE = 0, - IN_ROUTING = 1, - IN_SOURCE_SITE = 2 -}; - -enum ExpansionDirection -{ - EXPAND_DOWNHILL = 0, - EXPAND_UPHILL = 1 -}; - -struct WireNode -{ - WireId wire; - WireNodeState state; - int depth; -}; -} // namespace - -// Maximum depth that a dedicate interconnect is considered. -// -// Routing networks with depth <= kMaxDepth is considers a dedicated -// interconnect. -constexpr int kMaxDepth = 6; - -static uint32_t get_num_pips(const Context *ctx, WireId wire, ExpansionDirection direction) -{ - uint32_t num_pips = 0; - - if (direction == EXPAND_DOWNHILL) { - for (PipId pip : ctx->getPipsDownhill(wire)) { - auto &pip_data = pip_info(ctx->chip_info, pip); - if (pip_data.pseudo_cell_wires.size() > 0) - continue; - - if (ctx->getPipDstWire(pip) == WireId()) - continue; - - if (ctx->is_pip_synthetic(pip)) - continue; - - if (ctx->is_site_port(pip)) - continue; - - num_pips++; - } - } else { - NPNR_ASSERT(direction == EXPAND_UPHILL); - for (PipId pip : ctx->getPipsUphill(wire)) { - auto &pip_data = pip_info(ctx->chip_info, pip); - if (pip_data.pseudo_cell_wires.size() > 0) - continue; - - if (ctx->getPipSrcWire(pip) == WireId()) - continue; - - if (ctx->is_pip_synthetic(pip)) - continue; - - if (ctx->is_site_port(pip)) - continue; - - num_pips++; - } - } - - return num_pips; -} - -void DedicatedInterconnect::init(const Context *ctx) -{ - this->ctx = ctx; - - if (ctx->debug) { - log_info("Finding dedicated interconnect!\n"); - } - - find_dedicated_interconnect(); - if (ctx->debug) { - print_dedicated_interconnect(); - } -} - -bool DedicatedInterconnect::check_routing(BelId src_bel, IdString src_bel_pin, BelId dst_bel, IdString dst_bel_pin, - bool site_only) const -{ - std::vector nodes_to_expand; - - WireId src_wire = ctx->getBelPinWire(src_bel, src_bel_pin); - - const auto &src_wire_data = ctx->wire_info(src_wire); - NPNR_ASSERT(src_wire_data.site != -1); - - WireId dst_wire = ctx->getBelPinWire(dst_bel, dst_bel_pin); - - if (src_wire == dst_wire) { - return true; - } - - const auto &dst_wire_data = ctx->wire_info(dst_wire); - NPNR_ASSERT(dst_wire_data.site != -1); - - WireNode wire_node; - wire_node.wire = src_wire; - if (src_wire.tile == dst_wire.tile && src_wire_data.site == dst_wire_data.site) - wire_node.state = IN_SINK_SITE; - else - wire_node.state = IN_SOURCE_SITE; - wire_node.depth = 0; - - nodes_to_expand.push_back(wire_node); - - while (!nodes_to_expand.empty()) { - WireNode node_to_expand = nodes_to_expand.back(); - nodes_to_expand.pop_back(); - - auto num_pips = get_num_pips(ctx, node_to_expand.wire, EXPAND_DOWNHILL); - - // Usually, dedicated interconnects do not have more than one PIPs in the out-of-site - if (node_to_expand.depth > 1 && node_to_expand.state == IN_ROUTING && num_pips > 1) { - if (ctx->verbose) - log_info("Wire %s is on a non-dedicated path (number of pips %d)\n", - ctx->nameOfWire(node_to_expand.wire), num_pips); - continue; - } - - for (PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) { - if (ctx->is_pip_synthetic(pip)) { - continue; - } - - WireId wire = ctx->getPipDstWire(pip); - if (wire == WireId()) { - continue; - } - - if (ctx->debug) { - log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip)); - } - - WireNode next_node; - next_node.wire = wire; - next_node.depth = node_to_expand.depth; - - if (node_to_expand.depth > kMaxDepth) { - // Dedicated routing should reach sources by kMaxDepth (with - // tuning). - // - // FIXME: Consider removing kMaxDepth and use kMaxSources? - continue; - } - - auto const &wire_data = ctx->wire_info(wire); - - bool expand_node = true; - if (ctx->is_site_port(pip)) { - if (site_only) { - // When routing site only, don't allow site ports. - continue; - } - - switch (node_to_expand.state) { - case IN_SOURCE_SITE: - NPNR_ASSERT(wire_data.site == -1); - next_node.state = IN_ROUTING; - break; - case IN_ROUTING: - NPNR_ASSERT(wire_data.site != -1); - if (wire.tile == src_wire.tile && wire_data.site == src_wire_data.site) { - // Dedicated routing won't have straight loops, - // general routing looks like that. -#ifdef DEBUG_EXPANSION - log_info(" - Not dedicated site routing because loop!"); -#endif - continue; - } - next_node.state = IN_SINK_SITE; - break; - case IN_SINK_SITE: - // Once entering a site, do not leave it again. - // This path is not a legal route! - expand_node = false; - break; - default: - // Unreachable!!! - NPNR_ASSERT(false); - } - } else { - next_node.state = node_to_expand.state; - if (next_node.state == IN_ROUTING) - next_node.depth++; - } - - if (expand_node) { - nodes_to_expand.push_back(next_node); - } else { - continue; - } - - if (next_node.state == IN_SINK_SITE) { - for (BelPin bel_pin : ctx->getWireBelPins(wire)) { - if (bel_pin.bel == dst_bel && bel_pin.pin == dst_bel_pin) { - if (ctx->debug) { - log_info("Valid dedicated interconnect from %s/%s to %s/%s\n", ctx->nameOfBel(src_bel), - src_bel_pin.c_str(ctx), ctx->nameOfBel(dst_bel), dst_bel_pin.c_str(ctx)); - } - return true; - } - } - } - } - } - - return false; -} - -bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel, const CellInfo *cell, IdString driver_port, - NetInfo *net) const -{ - const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel); - - TileTypeBelPin type_bel_pin; - type_bel_pin.tile_type = ctx->chip_info->tiles[driver_bel.tile].type; - type_bel_pin.bel_index = driver_bel.index; - - Loc driver_loc = ctx->getBelLocation(driver_bel); - - for (IdString driver_bel_pin : ctx->getBelPinsForCellPin(cell, driver_port)) { - type_bel_pin.bel_pin = driver_bel_pin; - - auto iter = sources.find(type_bel_pin); - if (iter == sources.end()) { - // This BEL pin doesn't have a dedicate interconnect. - continue; - } - - for (const PortRef &port_ref : net->users) { - NPNR_ASSERT(port_ref.cell != nullptr); - - if (port_ref.cell->bel == BelId()) { - // FIXME: This should actually return "unknown!" because the - // sink is unplaced. Once the sink is placed, this constraint - // can be evaluated. - if (ctx->debug) { - log_info("BEL %s is not valid because sink cell %s/%s is not placed\n", ctx->nameOfBel(driver_bel), - port_ref.cell->name.c_str(ctx), port_ref.port.c_str(ctx)); - } - return false; - } - - BelId sink_bel = port_ref.cell->bel; - const auto &sink_bel_data = bel_info(ctx->chip_info, sink_bel); - Loc sink_loc = ctx->getBelLocation(port_ref.cell->bel); - - if (sink_bel.tile == driver_bel.tile && sink_bel_data.site == driver_bel_data.site) { - // This might site local routing. See if it can be routed - for (IdString sink_bel_pin : ctx->getBelPinsForCellPin(port_ref.cell, port_ref.port)) { - if (!check_routing(driver_bel, driver_bel_pin, sink_bel, sink_bel_pin, /*site_only=*/true)) { - return false; - } - } - continue; - } - - DeltaTileTypeBelPin sink_type_bel_pin; - sink_type_bel_pin.delta_x = sink_loc.x - driver_loc.x; - sink_type_bel_pin.delta_y = sink_loc.y - driver_loc.y; - sink_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type; - sink_type_bel_pin.type_bel_pin.bel_index = sink_bel.index; - - for (IdString sink_bel_pin : ctx->getBelPinsForCellPin(port_ref.cell, port_ref.port)) { - sink_type_bel_pin.type_bel_pin.bel_pin = sink_bel_pin; - - // Do fast routing check to see if the pair of driver and sink - // every are valid. - if (iter->second.count(sink_type_bel_pin) == 0) { - if (ctx->debug) { - log_info("BEL %s is not valid because pin %s cannot reach %s/%s\n", ctx->nameOfBel(driver_bel), - driver_bel_pin.c_str(ctx), ctx->nameOfBel(sink_bel), sink_bel_pin.c_str(ctx)); - } - return false; - } - - // Do detailed routing check to ensure driver can reach sink. - // - // FIXME: This might be too slow, but it handles a case on - // SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the - // delta_y=2 case is rare. - if (!check_routing(driver_bel, driver_bel_pin, sink_bel, sink_bel_pin, /*site_only=*/false)) { - if (ctx->debug) { - log_info("BEL %s is not valid because pin %s cannot be reach %s/%s (via detailed check)\n", - ctx->nameOfBel(driver_bel), driver_bel_pin.c_str(ctx), ctx->nameOfBel(sink_bel), - sink_bel_pin.c_str(ctx)); - } - return false; - } - } - } - } - - return true; -} - -bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo *cell, IdString port_name, - NetInfo *net) const -{ - const auto &bel_data = bel_info(ctx->chip_info, bel); - Loc bel_loc = ctx->getBelLocation(bel); - - BelId driver_bel = net->driver.cell->bel; - - for (IdString bel_pin : ctx->getBelPinsForCellPin(cell, port_name)) { - TileTypeBelPin type_bel_pin; - type_bel_pin.tile_type = ctx->chip_info->tiles[bel.tile].type; - type_bel_pin.bel_index = bel.index; - type_bel_pin.bel_pin = bel_pin; - - auto iter = sinks.find(type_bel_pin); - if (iter == sinks.end()) { - // This BEL pin doesn't have a dedicate interconnect. - continue; - } - - if (driver_bel == BelId()) { - // FIXME: This should actually return "unknown!" because the - // driver is unplaced. Once the driver is placed, this constraint - // can be evaluated. - if (ctx->debug) { - log_info("BEL %s is not valid because driver cell %s/%s is not placed\n", ctx->nameOfBel(bel), - net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx)); - } - return false; - } - - const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel); - - if (bel.tile == driver_bel.tile && bel_data.site == driver_bel_data.site) { - // This is a site local routing, even though this is a sink - // with a dedicated interconnect. - continue; - } - - Loc driver_loc = ctx->getBelLocation(driver_bel); - - DeltaTileTypeBelPin driver_type_bel_pin; - driver_type_bel_pin.delta_x = driver_loc.x - bel_loc.x; - driver_type_bel_pin.delta_y = driver_loc.y - bel_loc.y; - driver_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[driver_bel.tile].type; - driver_type_bel_pin.type_bel_pin.bel_index = driver_bel.index; - driver_type_bel_pin.type_bel_pin.bel_pin = - get_only_value(ctx->getBelPinsForCellPin(net->driver.cell, net->driver.port)); - - // Do fast routing check to see if the pair of driver and sink - // every are valid. - if (iter->second.count(driver_type_bel_pin) == 0) { - if (ctx->debug) { - log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s\n", ctx->nameOfBel(bel), - bel_pin.c_str(ctx), ctx->nameOfBel(driver_bel), - driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx)); - } - return false; - } - - // Do detailed routing check to ensure driver can reach sink. - // - // FIXME: This might be too slow, but it handles a case on - // SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the - // delta_y=2 case is rare. - if (!check_routing(driver_bel, driver_type_bel_pin.type_bel_pin.bel_pin, bel, bel_pin, /*site_only=*/false)) { - if (ctx->debug) { - log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s (via detailed check)\n", - ctx->nameOfBel(bel), bel_pin.c_str(ctx), ctx->nameOfBel(driver_bel), - driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx)); - } - return false; - } - } - - return true; -} - -bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo *cell) const -{ - NPNR_ASSERT(bel != BelId()); - - for (const auto &port_pair : cell->ports) { - IdString port_name = port_pair.first; - NetInfo *net = port_pair.second.net; - if (net == nullptr || net->driver.cell == nullptr) { - continue; - } - - if (ctx->io_port_types.count(net->driver.cell->type)) { - continue; - } - - // Only check sink BELs. - if (net->driver.cell == cell && net->driver.port == port_name) { - if (!is_driver_on_net_valid(bel, cell, port_name, net)) { - return false; - } - } else { - if (!is_sink_on_net_valid(bel, cell, port_name, net)) { - return false; - } - } - } - - return true; -} - -void DedicatedInterconnect::explain_bel_status(BelId bel, const CellInfo *cell) const -{ - NPNR_ASSERT(bel != BelId()); - - for (const auto &port_pair : cell->ports) { - IdString port_name = port_pair.first; - NetInfo *net = port_pair.second.net; - if (net == nullptr) { - continue; - } - - // This net doesn't have a driver, probably not valid? - NPNR_ASSERT(net->driver.cell != nullptr); - - if (ctx->io_port_types.count(net->driver.cell->type)) { - continue; - } - - // Only check sink BELs. - if (net->driver.cell == cell && net->driver.port == port_name) { - if (!is_driver_on_net_valid(bel, cell, port_name, net)) { - log_info("Driver %s/%s is not valid on net '%s'\n", cell->name.c_str(ctx), port_name.c_str(ctx), - net->name.c_str(ctx)); - } - } else { - if (!is_sink_on_net_valid(bel, cell, port_name, net)) { - log_info("Sink %s/%s is not valid on net '%s'\n", cell->name.c_str(ctx), port_name.c_str(ctx), - net->name.c_str(ctx)); - } - } - } -} - -void DedicatedInterconnect::print_dedicated_interconnect() const -{ - log_info("Found %zu sinks with dedicated interconnect\n", sinks.size()); - log_info("Found %zu sources with dedicated interconnect\n", sources.size()); - std::vector sorted_keys; - for (const auto &sink_to_srcs : sinks) { - sorted_keys.push_back(sink_to_srcs.first); - } - for (const auto &src_to_sinks : sources) { - sorted_keys.push_back(src_to_sinks.first); - } - std::sort(sorted_keys.begin(), sorted_keys.end()); - - for (const auto &key : sorted_keys) { - auto iter = sinks.find(key); - if (iter != sinks.end()) { - auto dst = key; - for (const auto &src_delta : iter->second) { - auto src = src_delta.type_bel_pin; - auto delta_x = src_delta.delta_x; - auto delta_y = src_delta.delta_y; - - const TileTypeInfoPOD &src_tile_type = ctx->chip_info->tile_types[src.tile_type]; - const BelInfoPOD &src_bel_info = src_tile_type.bel_data[src.bel_index]; - IdString src_site_type = IdString(src_tile_type.site_types[src_bel_info.site]); - IdString src_bel_pin = src.bel_pin; - - const TileTypeInfoPOD &dst_tile_type = ctx->chip_info->tile_types[dst.tile_type]; - const BelInfoPOD &dst_bel_info = dst_tile_type.bel_data[dst.bel_index]; - IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]); - IdString dst_bel_pin = dst.bel_pin; - - log_info("%s.%s[%d]/%s/%s (%d, %d) -> %s.%s[%d]/%s/%s\n", IdString(src_tile_type.name).c_str(ctx), - src_site_type.c_str(ctx), src_bel_info.site, IdString(src_bel_info.name).c_str(ctx), - src_bel_pin.c_str(ctx), delta_x, delta_y, IdString(dst_tile_type.name).c_str(ctx), - dst_site_type.c_str(ctx), dst_bel_info.site, IdString(dst_bel_info.name).c_str(ctx), - dst_bel_pin.c_str(ctx)); - } - } else { - auto src = key; - for (const auto &dst_delta : sources.at(key)) { - auto dst = dst_delta.type_bel_pin; - auto delta_x = dst_delta.delta_x; - auto delta_y = dst_delta.delta_y; - - const TileTypeInfoPOD &src_tile_type = ctx->chip_info->tile_types[src.tile_type]; - const BelInfoPOD &src_bel_info = src_tile_type.bel_data[src.bel_index]; - IdString src_site_type = IdString(src_tile_type.site_types[src_bel_info.site]); - IdString src_bel_pin = src.bel_pin; - - const TileTypeInfoPOD &dst_tile_type = ctx->chip_info->tile_types[dst.tile_type]; - const BelInfoPOD &dst_bel_info = dst_tile_type.bel_data[dst.bel_index]; - IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]); - IdString dst_bel_pin = dst.bel_pin; - - log_info("%s.%s[%d]/%s/%s -> %s.%s[%d]/%s/%s (%d, %d)\n", IdString(src_tile_type.name).c_str(ctx), - src_site_type.c_str(ctx), src_bel_info.site, IdString(src_bel_info.name).c_str(ctx), - src_bel_pin.c_str(ctx), IdString(dst_tile_type.name).c_str(ctx), dst_site_type.c_str(ctx), - dst_bel_info.site, IdString(dst_bel_info.name).c_str(ctx), dst_bel_pin.c_str(ctx), delta_x, - delta_y); - } - } - } -} - -void DedicatedInterconnect::find_dedicated_interconnect() -{ - for (BelId bel : ctx->getBels()) { - const auto &bel_data = bel_info(ctx->chip_info, bel); - if (bel_data.category != BEL_CATEGORY_LOGIC) { - continue; - } - if (bel_data.synthetic) { - continue; - } - - for (int i = 0; i < bel_data.num_bel_wires; ++i) { - if (bel_data.types[i] != PORT_IN) { - continue; - } - - WireId wire; - wire.tile = bel.tile; - wire.index = bel_data.wires[i]; - - expand_sink_bel(bel, IdString(bel_data.ports[i]), wire); - } - } - - pool seen_pins; - for (auto sink_pair : sinks) { - for (auto src : sink_pair.second) { - seen_pins.emplace(src.type_bel_pin); - } - } - - for (BelId bel : ctx->getBels()) { - const auto &bel_data = bel_info(ctx->chip_info, bel); - if (bel_data.category != BEL_CATEGORY_LOGIC) { - continue; - } - if (bel_data.synthetic) { - continue; - } - - for (int i = 0; i < bel_data.num_bel_wires; ++i) { - if (bel_data.types[i] != PORT_OUT || bel_data.wires[i] == -1) { - continue; - } - - IdString bel_pin(bel_data.ports[i]); - - TileTypeBelPin type_bel_pin; - type_bel_pin.tile_type = ctx->chip_info->tiles[bel.tile].type; - type_bel_pin.bel_index = bel.index; - type_bel_pin.bel_pin = bel_pin; - - // Don't visit src pins already handled in the sink expansion! - if (seen_pins.count(type_bel_pin)) { - continue; - } - - WireId wire; - wire.tile = bel.tile; - wire.index = bel_data.wires[i]; - - expand_source_bel(bel, bel_pin, wire); - } - } -} - -void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, WireId sink_wire) -{ - NPNR_ASSERT(sink_bel != BelId()); -#ifdef DEBUG_EXPANSION - log_info("Expanding from %s/%s\n", ctx->nameOfBel(sink_bel), pin.c_str(ctx)); -#endif - - std::vector nodes_to_expand; - - const auto &sink_wire_data = ctx->wire_info(sink_wire); - NPNR_ASSERT(sink_wire_data.site != -1); - - WireNode wire_node; - wire_node.wire = sink_wire; - wire_node.state = IN_SINK_SITE; - wire_node.depth = 0; - - nodes_to_expand.push_back(wire_node); - - Loc sink_loc = ctx->getBelLocation(sink_bel); - pool srcs; - - while (!nodes_to_expand.empty()) { - WireNode node_to_expand = nodes_to_expand.back(); - nodes_to_expand.pop_back(); - - for (PipId pip : ctx->getPipsUphill(node_to_expand.wire)) { - if (ctx->is_pip_synthetic(pip)) { - continue; - } - - WireId wire = ctx->getPipSrcWire(pip); - if (wire == WireId()) { - continue; - } - -#ifdef DEBUG_EXPANSION - log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip)); -#endif - - WireNode next_node; - next_node.wire = wire; - next_node.depth = node_to_expand.depth; - - if (node_to_expand.depth > kMaxDepth) { - // Dedicated routing should reach sources by kMaxDepth (with - // tuning). - // - // FIXME: Consider removing kMaxDepth and use kMaxSources? -#ifdef DEBUG_EXPANSION - log_info(" - Exceeded max depth!\n"); -#endif - return; - } - - auto const &wire_data = ctx->wire_info(wire); - - bool expand_node = true; - if (ctx->is_site_port(pip)) { - switch (node_to_expand.state) { - case IN_SINK_SITE: - NPNR_ASSERT(wire_data.site == -1); - next_node.state = IN_ROUTING; - break; - case IN_ROUTING: - NPNR_ASSERT(wire_data.site != -1); - if (wire.tile == sink_wire.tile && wire_data.site == sink_wire_data.site) { - // Dedicated routing won't have straight loops, - // general routing looks like that. -#ifdef DEBUG_EXPANSION - log_info(" - Not dedicated site routing because loop!"); -#endif - return; - } - next_node.state = IN_SOURCE_SITE; - break; - case IN_SOURCE_SITE: - // Once entering a site, do not leave it again. - // This path is not a legal route! - expand_node = false; - break; - default: - // Unreachable!!! - NPNR_ASSERT(false); - } - } else { - next_node.state = node_to_expand.state; - if (next_node.state == IN_ROUTING) - next_node.depth++; - } - - if (expand_node) { - nodes_to_expand.push_back(next_node); - } else { - continue; - } - - if (next_node.state == IN_SOURCE_SITE) { - for (BelPin bel_pin : ctx->getWireBelPins(wire)) { - BelId src_bel = bel_pin.bel; - auto const &bel_data = bel_info(ctx->chip_info, src_bel); - - if (bel_data.category != BEL_CATEGORY_LOGIC) { - continue; - } - if (bel_data.synthetic) { - continue; - } - if (ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_OUT) { - continue; - } - -#ifdef DEBUG_EXPANSION - log_info(" - Reached %s/%s\n", ctx->nameOfBel(bel_pin.bel), bel_pin.pin.c_str(ctx)); -#endif - - Loc src_loc = ctx->getBelLocation(src_bel); - - DeltaTileTypeBelPin delta_type_bel_pin; - delta_type_bel_pin.delta_x = src_loc.x - sink_loc.x; - delta_type_bel_pin.delta_y = src_loc.y - sink_loc.y; - delta_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[src_bel.tile].type; - delta_type_bel_pin.type_bel_pin.bel_index = src_bel.index; - delta_type_bel_pin.type_bel_pin.bel_pin = bel_pin.pin; - srcs.emplace(delta_type_bel_pin); - } - } - } - } - - TileTypeBelPin type_bel_pin; - type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type; - type_bel_pin.bel_index = sink_bel.index; - type_bel_pin.bel_pin = sink_pin; - - auto result = sinks.emplace(type_bel_pin, srcs); - if (!result.second) { - // type_bel_pin was already present! Add any new sources from this - // sink type (if any); - for (auto src : srcs) { - result.first->second.emplace(src); - } - } -} - -void DedicatedInterconnect::expand_source_bel(BelId src_bel, IdString src_pin, WireId src_wire) -{ - NPNR_ASSERT(src_bel != BelId()); -#ifdef DEBUG_EXPANSION - log_info("Expanding from %s/%s\n", ctx->nameOfBel(src_bel), src_pin.c_str(ctx)); -#endif - - std::vector nodes_to_expand; - - const auto &src_wire_data = ctx->wire_info(src_wire); - NPNR_ASSERT(src_wire_data.site != -1); - - WireNode wire_node; - wire_node.wire = src_wire; - wire_node.state = IN_SOURCE_SITE; - wire_node.depth = 0; - - nodes_to_expand.push_back(wire_node); - - Loc src_loc = ctx->getBelLocation(src_bel); - pool dsts; - - while (!nodes_to_expand.empty()) { - WireNode node_to_expand = nodes_to_expand.back(); - nodes_to_expand.pop_back(); - - for (PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) { - if (ctx->is_pip_synthetic(pip)) { - continue; - } - - WireId wire = ctx->getPipDstWire(pip); - if (wire == WireId()) { - continue; - } - -#ifdef DEBUG_EXPANSION - log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip)); -#endif - - WireNode next_node; - next_node.wire = wire; - next_node.depth = node_to_expand.depth; - - if (node_to_expand.depth > kMaxDepth) { - // Dedicated routing should reach sources by kMaxDepth (with - // tuning). - // - // FIXME: Consider removing kMaxDepth and use kMaxSources? -#ifdef DEBUG_EXPANSION - log_info(" - Exceeded max depth!\n"); -#endif - return; - } - - auto const &wire_data = ctx->wire_info(wire); - - bool expand_node = true; - if (ctx->is_site_port(pip)) { - switch (node_to_expand.state) { - case IN_SOURCE_SITE: - NPNR_ASSERT(wire_data.site == -1); - next_node.state = IN_ROUTING; - break; - case IN_ROUTING: - NPNR_ASSERT(wire_data.site != -1); - if (wire.tile == src_wire.tile && wire_data.site == src_wire_data.site) { - // Dedicated routing won't have straight loops, - // general routing looks like that. -#ifdef DEBUG_EXPANSION - log_info(" - Not dedicated site routing because loop!"); -#endif - return; - } - next_node.state = IN_SINK_SITE; - break; - case IN_SINK_SITE: - // Once entering a site, do not leave it again. - // This path is not a legal route! - expand_node = false; - break; - default: - // Unreachable!!! - NPNR_ASSERT(false); - } - } else { - next_node.state = node_to_expand.state; - if (next_node.state == IN_ROUTING) - next_node.depth++; - } - - if (expand_node) { - nodes_to_expand.push_back(next_node); - } else { - continue; - } - - if (next_node.state == IN_SINK_SITE) { - for (BelPin bel_pin : ctx->getWireBelPins(wire)) { - BelId sink_bel = bel_pin.bel; - auto const &bel_data = bel_info(ctx->chip_info, sink_bel); - - if (bel_data.category != BEL_CATEGORY_LOGIC) { - continue; - } - if (bel_data.synthetic) { - continue; - } - if (ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_IN) { - continue; - } - -#ifdef DEBUG_EXPANSION - log_info(" - Reached %s/%s\n", ctx->nameOfBel(bel_pin.bel), bel_pin.pin.c_str(ctx)); -#endif - - Loc sink_loc = ctx->getBelLocation(sink_bel); - - DeltaTileTypeBelPin delta_type_bel_pin; - delta_type_bel_pin.delta_x = sink_loc.x - src_loc.x; - delta_type_bel_pin.delta_y = sink_loc.y - src_loc.y; - delta_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type; - delta_type_bel_pin.type_bel_pin.bel_index = sink_bel.index; - delta_type_bel_pin.type_bel_pin.bel_pin = bel_pin.pin; - dsts.emplace(delta_type_bel_pin); - } - } - } - } - - TileTypeBelPin type_bel_pin; - type_bel_pin.tile_type = ctx->chip_info->tiles[src_bel.tile].type; - type_bel_pin.bel_index = src_bel.index; - type_bel_pin.bel_pin = src_pin; - - auto result = sources.emplace(type_bel_pin, dsts); - if (!result.second) { - // type_bel_pin was already present! Add any new sources from this - // sink type (if any); - for (auto dst : dsts) { - result.first->second.emplace(dst); - } - } -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/dedicated_interconnect.h b/fpga_interchange/dedicated_interconnect.h deleted file mode 100644 index 085ced26..00000000 --- a/fpga_interchange/dedicated_interconnect.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef DEDICATED_INTERCONNECT_H -#define DEDICATED_INTERCONNECT_H - -#include -#include - -#include "archdefs.h" -#include "hashlib.h" -#include "idstring.h" -#include "nextpnr_namespaces.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct TileTypeBelPin -{ - int32_t tile_type; - int32_t bel_index; - IdString bel_pin; - - bool operator<(const TileTypeBelPin &other) const - { - if (tile_type >= other.tile_type) { - return false; - } - - if (bel_index >= other.bel_index) { - return false; - } - - return bel_pin < other.bel_pin; - } - - bool operator==(const TileTypeBelPin &other) const - { - return tile_type == other.tile_type && bel_index == other.bel_index && bel_pin == other.bel_pin; - } - bool operator!=(const TileTypeBelPin &other) const - { - return tile_type != other.tile_type || bel_index != other.bel_index || bel_pin != other.bel_pin; - } - unsigned int hash() const { return mkhash(mkhash(tile_type, bel_index), bel_pin.hash()); } -}; - -struct DeltaTileTypeBelPin -{ - int32_t delta_x; - int32_t delta_y; - TileTypeBelPin type_bel_pin; - - bool operator==(const DeltaTileTypeBelPin &other) const - { - return delta_x == other.delta_x && delta_y == other.delta_y && type_bel_pin == other.type_bel_pin; - } - bool operator!=(const DeltaTileTypeBelPin &other) const - { - return delta_x != other.delta_x || delta_y != other.delta_y || type_bel_pin != other.type_bel_pin; - } - unsigned int hash() const { return mkhash(mkhash(delta_x, delta_y), type_bel_pin.hash()); } -}; - -struct Context; - -// This class models dedicated interconnect present in the given fabric. -// -// Examples of dedicate interconnect: -// - IBUF.O -> ISERDES.I -// - IBUF.O -> IDELAY.I -// - CARRY4.CO[3] -> CARRY4.CIN -// -// Note that CARRY4.CYINIT does not **require** dedicated interconnect, so -// it doesn't qualify. -// -// This class discovers dedicated interconnect by examing the routing graph. -// This discovery make be expensive, and require caching to accelerate -// startup. -struct DedicatedInterconnect -{ - const Context *ctx; - - dict> sinks; - dict> sources; - - void init(const Context *ctx); - - // Is this BEL placed in a location that is valid based on dedicated - // interconnect? - // - // Note: Only BEL pin sinks are checked. - bool isBelLocationValid(BelId bel, const CellInfo *cell) const; - void explain_bel_status(BelId bel, const CellInfo *cell) const; - - void find_dedicated_interconnect(); - void print_dedicated_interconnect() const; - bool check_routing(BelId src_bel, IdString src_bel_pin, BelId dst_bel, IdString dst_bel_pin, bool site_only) const; - void expand_sink_bel(BelId bel, IdString pin, WireId wire); - void expand_source_bel(BelId bel, IdString pin, WireId wire); - - bool is_driver_on_net_valid(BelId driver_bel, const CellInfo *cell, IdString driver_port, NetInfo *net) const; - bool is_sink_on_net_valid(BelId bel, const CellInfo *cell, IdString port_name, NetInfo *net) const; -}; - -NEXTPNR_NAMESPACE_END - -#endif /* DEDICATED_INTERCONNECT_H */ diff --git a/fpga_interchange/examples/README.md b/fpga_interchange/examples/README.md deleted file mode 100644 index 6c1da117..00000000 --- a/fpga_interchange/examples/README.md +++ /dev/null @@ -1,76 +0,0 @@ -## FPGA interchange instructions - -These are instructions on how to get the dependencies, generate the FPGA interchange architecture build system and -run some example designs. - - -### Installing dependencies - -Install java and javac if not already installed: -``` -# Or equivalent for your local system. -sudo apt-get install openjdk-10-jdk -``` - -Install capnproto if not already installed. Version 0.8.0 is required. -As stated in the [official instructions](https://capnproto.org/install.html), the version on the common package managers -might not be up to date with the latest version, hence it is suggested to install -from the archive or, in alternative, directly from the git repository. - -Install capnproto-java if not already installed: -``` -git clone https://github.com/capnproto/capnproto-java.git -cd capnproto-java -make -sudo make install -``` - -Install python-fpga-interchange if not already installed: -``` -git clone https://github.com/SymbiFlow/python-fpga-interchange.git -cd python-fpga-interchange.git -# -# Note: Recommend checking out a specific release, for example: -# -# git checkout v0.0.5 -# -# Release of python-fpga-interchange library does have to match nextpnr -# implementation. -python -m pip install -e . -``` - -Clone RapidWright, if not already cloned: -``` -git clone https://github.com/Xilinx/RapidWright.git -cd RapidWright -make update_jars -``` - -### Build instructions - -Once dependencies are installed/cloned, configure the build system for the FPGA interchange. - -From the nextpnr root dir run: - -``` -mkdir build -cd build -cmake .. --DARCH=fpga_interchange -DRAPIDWRIGHT_PATH= -DINTERCHANGE_SCHEMA_PATH= -DPYTHON_INTERCHANGE_PATH= -``` - -To build the xc7a35t architecture, run: -``` -make chipdb-xc7a35t-bin -``` - -To build the example designs run: -``` -make test-fpga_interchange-wire_arty-dcp -``` - -The make targets for the example designs follow the same pattern: `test-fpga_interchange--`, where `output` is the name of the intermediate step of the build which can be: - -- `json`: synthesis output -- `netlist`: logical netlist -- `phys`: physical netlist -- `dcp`: design checkpoint diff --git a/fpga_interchange/examples/boards.cmake b/fpga_interchange/examples/boards.cmake deleted file mode 100644 index 3639080b..00000000 --- a/fpga_interchange/examples/boards.cmake +++ /dev/null @@ -1,50 +0,0 @@ -function(add_board) - # ~~~ - # add_board( - # name - # device_family - # device - # package - # ) - # ~~~ - # - # Generates a board target containing information on the common device and package - # of the board. - # - # Arguments: - # - name: name of the board. E.g. arty - # - device_family: the name of the family this device belongs to. - # E.g. the xc7a35t device belongs to the xc7 family - # - device: common device name of a set of parts. E.g. xc7a35tcsg324-1 and xc7a35tcpg236-1 - # share the same xc7a35t device prefix - # - package: one of the packages available for a given device. E.g. cpg236 - # - # Targets generated: - # - board- - - set(options) - set(oneValueArgs name device_family device package) - set(multiValueArgs) - - cmake_parse_arguments( - add_board - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} - ) - - set(name ${add_board_name}) - set(device_family ${add_board_device_family}) - set(device ${add_board_device}) - set(package ${add_board_package}) - - add_custom_target(board-${name} DEPENDS device-${device}) - set_target_properties( - board-${name} - PROPERTIES - DEVICE_FAMILY ${device_family} - DEVICE ${device} - PACKAGE ${package} - ) -endfunction() diff --git a/fpga_interchange/examples/boards/CMakeLists.txt b/fpga_interchange/examples/boards/CMakeLists.txt deleted file mode 100644 index ef4a97c5..00000000 --- a/fpga_interchange/examples/boards/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -add_board( - name arty35t - device_family xc7 - device xc7a35t - package csg324 -) - -add_board( - name arty100t - device_family xc7 - device xc7a100t - package csg324 -) - -add_board( - name nexys_video - device_family xc7 - device xc7a200t - package sbg484 -) - -add_board( - name basys3 - device_family xc7 - device xc7a35t - package cpg236 -) - -add_board( - name zybo - device_family xc7 - device xc7z010 - package clg400 -) - -# This isn't a real board, all the real boards currently available use the LIFCL-40 but the LIFCL-17 speeds up runtime for testing -add_board( - name lifcl17 - device_family nexus - device LIFCL-17 - package QFN72 -) - -add_board( - name lifcl40evn - device_family nexus - device LIFCL-40 - package CABGA400 -) diff --git a/fpga_interchange/examples/chipdb.cmake b/fpga_interchange/examples/chipdb.cmake deleted file mode 100644 index e820124b..00000000 --- a/fpga_interchange/examples/chipdb.cmake +++ /dev/null @@ -1,341 +0,0 @@ -include(${family}/examples/chipdb_xilinx.cmake) -include(${family}/examples/chipdb_nexus.cmake) - -function(create_patched_device_db) - # ~~~ - # create_patched_device_db( - # device - # patch_name - # patch_path - # patch_format - # patch_data - # input_device - # output_target - # ) - # ~~~ - # - # Generates a patched device database starting from an input device - # - # If output_target is specified, the variable named as the output_target - # parameter value is set to the generated output_device_file target. - # - # Arguments: - # - device: common device name of a set of parts. E.g. xc7a35tcsg324-1 and xc7a35tcpg236-1 - # share the same xc7a35t device prefix. - # - patch_name: name of the patch which determines the target name - # - patch_path: patch_path argument for the fpga_interchange.patch call - # - patch_format: patch_format argument for the fpga_interchange.patch call - # - patch_data: path to the patch_data required for the fpga_interchange.patch call - # - input_device: target for the device that needs to be patched - # - output_target: variable name that will hold the output device target for the parent scope - # - # Targets generated: - # - --device - - set(options) - set(oneValueArgs device patch_name patch_path patch_format patch_data input_device output_target) - set(multiValueArgs) - - cmake_parse_arguments( - create_patched_device_db - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} - ) - - set(device ${create_patched_device_db_device}) - set(patch_name ${create_patched_device_db_patch_name}) - set(patch_path ${create_patched_device_db_patch_path}) - set(patch_format ${create_patched_device_db_patch_format}) - set(patch_data ${create_patched_device_db_patch_data}) - set(input_device ${create_patched_device_db_input_device}) - set(output_target ${create_patched_device_db_output_target}) - - get_target_property(input_device_loc ${input_device} LOCATION) - set(output_device_file ${CMAKE_CURRENT_BINARY_DIR}/${device}_${patch_name}.device) - add_custom_command( - OUTPUT ${output_device_file} - COMMAND - ${Python3_EXECUTABLE} -mfpga_interchange.patch - --schema_dir ${INTERCHANGE_SCHEMA_PATH} - --schema device - --patch_path ${patch_path} - --patch_format ${patch_format} - ${input_device_loc} - ${patch_data} - ${output_device_file} - DEPENDS - ${patch_data} - ${input_device} - ${input_device_loc} - ) - - add_custom_target(${patch_name}-${device}-device DEPENDS ${output_device_file}) - set_property(TARGET ${patch_name}-${device}-device PROPERTY LOCATION ${output_device_file}) - - add_custom_target(${patch_name}-${device}-device-yaml - COMMAND - ${Python3_EXECUTABLE} -mfpga_interchange.convert - --schema_dir ${INTERCHANGE_SCHEMA_PATH} - --schema device - --input_format capnp - --output_format yaml - ${output_device_file} - ${output_device_file}.yaml - DEPENDS ${output_device_file}) - - if (DEFINED output_target) - set(${output_target} ${patch_name}-${device}-device PARENT_SCOPE) - endif() -endfunction() - -function(patch_device_with_prim_lib) - # ~~~ - # patch_device_with_prim_lib( - # device - # yosys_script - # input_device - # output_target - # ) - # ~~~ - # - # Patches an input device with a primitive library from Yosys - # - # If output_target is specified, the variable named as the output_target - # parameter value is set to the generated output_device_file target. - # - # Arguments: - # - device: common device name of a set of parts. E.g. xc7a35tcsg324-1 and xc7a35tcpg236-1 - # share the same xc7a35t device prefix. - # - yosys_script: yosys script to produce cell library - # - input_device: target for the device that needs to be patched - # - output_target: variable name that will hold the output device target for the parent scope - # - # Targets generated: - # - prims--device - - set(options) - set(oneValueArgs device yosys_script input_device output_target) - set(multiValueArgs) - - cmake_parse_arguments( - patch_device_with_prim_lib - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} - ) - - set(device ${patch_device_with_prim_lib_device}) - set(yosys_script ${patch_device_with_prim_lib_yosys_script}) - set(input_device ${patch_device_with_prim_lib_input_device}) - set(output_target ${patch_device_with_prim_lib_output_target}) - - get_target_property(input_device_loc ${input_device} LOCATION) - set(output_device_file ${CMAKE_CURRENT_BINARY_DIR}/${device}_prim_lib.device) - set(output_json_file ${CMAKE_CURRENT_BINARY_DIR}/${device}_prim_lib.json) - - add_custom_command( - OUTPUT ${output_json_file} - COMMAND - yosys -p '${yosys_script}\; write_json ${output_json_file}' - ) - - add_custom_command( - OUTPUT ${output_device_file} - COMMAND - ${Python3_EXECUTABLE} -mfpga_interchange.add_prim_lib - --schema_dir ${INTERCHANGE_SCHEMA_PATH} - ${input_device_loc} - ${output_json_file} - ${output_device_file} - DEPENDS - ${input_device} - ${input_device_loc} - ${output_json_file} - ) - - add_custom_target(prims-${device}-device DEPENDS ${output_device_file}) - set_property(TARGET prims-${device}-device PROPERTY LOCATION ${output_device_file}) - - if (DEFINED output_target) - set(${output_target} prims-${device}-device PARENT_SCOPE) - endif() -endfunction() - -function(generate_chipdb) - # ~~~ - # generate_chipdb( - # family - # device - # part - # device_target - # device_config - # test_package - # ) - # ~~~ - # - # Generates a chipdb BBA file, starting from a device database. - # - # The chipdb binary file is directly generated to the - # /build/fpga_interchange/chipdb/ directory. - # - # The package argument is only used to run the architecture check target. - # - # Arguments: - # - family: nextpnr architecture family (e.g. fpga_interchange) - # - device: common device name of a set of parts. E.g. xc7a35tcsg324-1 and xc7a35tcpg236-1 - # share the same xc7a35t device prefix - # - part: one among the parts available for a given device - # - device_target: target for the device from which the chipdb is generated - # - device_config: path to the device configYAML file - # This file specifies some nextpnr specific data, such as BEL bucket - # seeds and global BEL names. - # - test_package: package among the ones available for the device. This is used for architecture - # testing only - # - # Targets generated: - # - chipdb-${device}-bba - # - chipdb-${device}-bin - # - device-${device} - # - # The device-${device} target contains properties to get the interchange device as well - # as the binary chipdb - - set(options) - set(oneValueArgs family device part device_target device_config test_package) - set(multiValueArgs) - - cmake_parse_arguments( - generate_chipdb - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} - ) - - set(family ${generate_chipdb_family}) - set(device ${generate_chipdb_device}) - set(part ${generate_chipdb_part}) - set(device_target ${generate_chipdb_device_target}) - set(device_config ${generate_chipdb_device_config}) - set(test_package ${generate_chipdb_test_package}) - - get_target_property(device_loc ${device_target} LOCATION) - set(chipdb_bba ${CMAKE_CURRENT_BINARY_DIR}/chipdb.bba) - add_custom_command( - OUTPUT ${chipdb_bba} - COMMAND - ${Python3_EXECUTABLE} -mfpga_interchange.nextpnr_emit - --schema_dir ${INTERCHANGE_SCHEMA_PATH} - --output_dir ${CMAKE_CURRENT_BINARY_DIR} - --device_config ${device_config} - --device ${device_loc} - DEPENDS - ${device_config} - ${device_target} - ${device_loc} - ) - - add_custom_target(chipdb-${device}-bba DEPENDS ${chipdb_bba}) - - set(chipdb_bin ${chipdb_dir}/chipdb-${device}.bin) - add_custom_command( - OUTPUT ${chipdb_bin} - COMMAND - bbasm -l ${chipdb_bba} ${chipdb_bin} - DEPENDS - chipdb-${device}-bba - ${chipdb_bba} - bbasm - ) - - add_custom_target(chipdb-${device}-bin DEPENDS ${chipdb_bin}) - - # Setting device target properties - add_custom_target(device-${device}) - set_target_properties( - device-${device} - PROPERTIES - DEVICE_LOC ${device_loc} - DEVICE_TARGET ${device_target} - CHIPDB_BIN_LOC ${chipdb_bin} - CHIPDB_BIN_TARGET chipdb-${device}-bin - ) - - # Generate architecture check target - add_custom_target( - chipdb-${device}-bin-check - COMMAND - nextpnr-fpga_interchange - --chipdb ${chipdb_bin} - --package ${test_package} - --test - DEPENDS - ${chipdb_bin} - chipdb-${device}-bin - ) - - add_custom_target( - chipdb-${device}-bin-check-verbose - COMMAND - nextpnr-fpga_interchange - --chipdb ${chipdb_bin} - --package ${test_package} - --test --verbose - DEPENDS - ${chipdb_bin} - chipdb-${device}-bin - ) - - add_custom_target( - chipdb-${device}-bin-check-verbose2 - COMMAND - nextpnr-fpga_interchange - --chipdb ${chipdb_bin} - --package ${test_package} - --test --debug - DEPENDS - ${chipdb_bin} - chipdb-${device}-bin - ) - - add_custom_target( - chipdb-${device}-bin-check-debug - COMMAND gdb --args - $ - --chipdb ${chipdb_bin} - --package ${test_package} - --test - DEPENDS - ${chipdb_bin} - chipdb-${device}-bin - ) - - add_custom_target( - chipdb-${device}-bin-check-test-data - COMMAND - nextpnr-fpga_interchange - --chipdb ${chipdb_bin} - --package ${test_package} - --run ${PROJECT_SOURCE_DIR}/python/check_arch_api.py - DEPENDS - ${chipdb_bin} - chipdb-${device}-bin - ${test_data_binary} - WORKING_DIRECTORY - ${CMAKE_CURRENT_SOURCE_DIR} - ) - - add_dependencies(all-${family}-archcheck-tests chipdb-${device}-bin-check-test-data chipdb-${device}-bin-check) - - # All tests targets for this device are added to this target - add_custom_target( - all-${device}-tests - DEPENDS - chipdb-${device}-bin-check-test-data - chipdb-${device}-bin-check - ) -endfunction() - diff --git a/fpga_interchange/examples/chipdb_nexus.cmake b/fpga_interchange/examples/chipdb_nexus.cmake deleted file mode 100644 index 4ea180c5..00000000 --- a/fpga_interchange/examples/chipdb_nexus.cmake +++ /dev/null @@ -1,101 +0,0 @@ -function(create_prjoxide_device_db) - # ~~~ - # create_prjoxide_device_db( - # device - # output_target - # ) - # ~~~ - # - # Generates a device database from Project Oxide - # - # If output_target is specified, the output_target_name variable - # is set to the generated output_device_file target. - # - # Arguments: - # - device: common device name of a set of parts. E.g. LIFCL-17 - # - output_target: variable name that will hold the output device target for the parent scope - # - # Targets generated: - # - prjoxide--device - set(options) - set(oneValueArgs device output_target) - set(multiValueArgs) - - cmake_parse_arguments( - create_prjoxide_device_db - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} - ) - - set(device ${create_prjoxide_device_db_device}) - set(output_target ${create_prjoxide_device_db_output_target}) - set(prjoxide_device_db ${CMAKE_CURRENT_BINARY_DIR}/${device}.device) - add_custom_command( - OUTPUT ${prjoxide_device_db} - COMMAND - ${PRJOXIDE_PREFIX}/bin/prjoxide - interchange-export - ${device} - ${prjoxide_device_db} - DEPENDS - ${PRJOXIDE_PREFIX}/bin/prjoxide - ) - - add_custom_target(prjoxide-${device}-device DEPENDS ${prjoxide_device_db}) - set_property(TARGET prjoxide-${device}-device PROPERTY LOCATION ${prjoxide_device_db}) - - if (DEFINED output_target) - set(${output_target} prjoxide-${device}-device PARENT_SCOPE) - endif() - -endfunction() - -function(generate_nexus_device_db) - # ~~~ - # generate_nexus_device_db( - # device - # device_target - # ) - # ~~~ - # - # Generates a chipdb BBA file, starting from a Project Oxide device database. - # Patches applied: - # - primitive library from Yosys - # - # Arguments: - # - device: common device name of a set of parts. E.g. LIFCL-17 - # - device_target: variable name that will hold the output device target for the parent scope - set(options) - set(oneValueArgs device device_target) - set(multiValueArgs) - - cmake_parse_arguments( - generate_nexus_device_db - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} - ) - - set(device ${generate_nexus_device_db_device}) - set(device_target ${generate_nexus_device_db_device_target}) - - create_prjoxide_device_db( - device ${device} - output_target prjoxide_device - ) - - # Add primitive library - patch_device_with_prim_lib( - device ${device} - yosys_script synth_nexus - input_device ${prjoxide_device} - output_target prjoxide_prims_device - ) - - if(DEFINED device_target) - set(${device_target} ${prjoxide_prims_device} PARENT_SCOPE) - endif() -endfunction() diff --git a/fpga_interchange/examples/chipdb_xilinx.cmake b/fpga_interchange/examples/chipdb_xilinx.cmake deleted file mode 100644 index 62e6b9ed..00000000 --- a/fpga_interchange/examples/chipdb_xilinx.cmake +++ /dev/null @@ -1,138 +0,0 @@ -function(create_rapidwright_device_db) - # ~~~ - # create_rapidwright_device_db( - # device - # part - # output_target - # ) - # ~~~ - # - # Generates a device database from RapidWright - # - # If output_target is specified, the output_target_name variable - # is set to the generated output_device_file target. - # - # Arguments: - # - device: common device name of a set of parts. E.g. xc7a35tcsg324-1 and xc7a35tcpg236-1 - # share the same xc7a35t device prefix - # - part: one among the parts available for a given device - # - output_target: variable name that will hold the output device target for the parent scope - # - # Targets generated: - # - rapidwright--device - - set(options) - set(oneValueArgs device part output_target) - set(multiValueArgs) - - cmake_parse_arguments( - create_rapidwright_device_db - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} - ) - - set(device ${create_rapidwright_device_db_device}) - set(part ${create_rapidwright_device_db_part}) - set(output_target ${create_rapidwright_device_db_output_target}) - set(rapidwright_device_db ${CMAKE_CURRENT_BINARY_DIR}/${part}.device) - add_custom_command( - OUTPUT ${rapidwright_device_db} - COMMAND - RAPIDWRIGHT_PATH=${RAPIDWRIGHT_PATH} - ${INVOKE_RAPIDWRIGHT} ${JAVA_HEAP_SPACE} - com.xilinx.rapidwright.interchange.DeviceResourcesExample - ${part} - DEPENDS - ${INVOKE_RAPIDWRIGHT} - ) - - add_custom_target(rapidwright-${device}-device DEPENDS ${rapidwright_device_db}) - set_property(TARGET rapidwright-${device}-device PROPERTY LOCATION ${rapidwright_device_db}) - - add_custom_target(rapidwright-${device}-device-yaml - COMMAND - ${Python3_EXECUTABLE} -mfpga_interchange.convert - --schema_dir ${INTERCHANGE_SCHEMA_PATH} - --schema device - --input_format capnp - --output_format yaml - ${rapidwright_device_db} - ${rapidwright_device_db}.yaml - DEPENDS ${rapidwright_device_db}) - - if (DEFINED output_target) - set(${output_target} rapidwright-${device}-device PARENT_SCOPE) - endif() -endfunction() - - -function(generate_xc7_device_db) - # ~~~ - # generate_xc7_device_db( - # device - # part - # device_target - # ) - # ~~~ - # - # Generates a chipdb BBA file, starting from a RapidWright device database which is then patched. - # Patches applied: - # - constraints patch - # - luts patch - # - # Arguments: - # - device: common device name of a set of parts. E.g. xc7a35tcsg324-1 and xc7a35tcpg236-1 - # share the same xc7a35t device prefix - # - part: one among the parts available for a given device - # - device_target: variable name that will hold the output device target for the parent scope - - set(options) - set(oneValueArgs device part device_target) - set(multiValueArgs) - - cmake_parse_arguments( - create_rapidwright_device_db - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} - ) - - set(device ${create_rapidwright_device_db_device}) - set(part ${create_rapidwright_device_db_part}) - set(device_target ${create_rapidwright_device_db_device_target}) - - create_rapidwright_device_db( - device ${device} - part ${part} - output_target rapidwright_device - ) - - # Generate constraints patch - create_patched_device_db( - device ${device} - patch_name constraints - patch_path constraints - patch_format yaml - patch_data ${PYTHON_INTERCHANGE_PATH}/test_data/series7_constraints.yaml - input_device ${rapidwright_device} - output_target constraints_device - ) - - # Generate lut constraints patch - create_patched_device_db( - device ${device} - patch_name constraints-luts - patch_path lutDefinitions - patch_format yaml - patch_data ${PYTHON_INTERCHANGE_PATH}/test_data/series7_luts.yaml - input_device ${constraints_device} - output_target constraints_luts_device - ) - - if(DEFINED device_target) - set(${device_target} ${constraints_luts_device} PARENT_SCOPE) - endif() -endfunction() diff --git a/fpga_interchange/examples/devices/CMakeLists.txt b/fpga_interchange/examples/devices/CMakeLists.txt deleted file mode 100644 index 1cf8ad43..00000000 --- a/fpga_interchange/examples/devices/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Artix-7 devices -add_subdirectory(xc7a35t) -add_subdirectory(xc7a100t) -add_subdirectory(xc7a200t) - -# Zynq-7 devices -add_subdirectory(xc7z010) - -# Nexus devices -add_subdirectory(LIFCL-17) -add_subdirectory(LIFCL-40) diff --git a/fpga_interchange/examples/devices/LIFCL-17/CMakeLists.txt b/fpga_interchange/examples/devices/LIFCL-17/CMakeLists.txt deleted file mode 100644 index 572ff200..00000000 --- a/fpga_interchange/examples/devices/LIFCL-17/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -generate_nexus_device_db( - device LIFCL-17 - device_target lifcl17_target -) - -generate_chipdb( - family ${family} - device LIFCL-17 - part LIFCL-17-7SG72C - device_target ${lifcl17_target} - device_config ${PYTHON_INTERCHANGE_PATH}/test_data/nexus_device_config.yaml - test_package QFN72 -) diff --git a/fpga_interchange/examples/devices/LIFCL-17/test_data.yaml b/fpga_interchange/examples/devices/LIFCL-17/test_data.yaml deleted file mode 100644 index c4787eba..00000000 --- a/fpga_interchange/examples/devices/LIFCL-17/test_data.yaml +++ /dev/null @@ -1,8 +0,0 @@ -pip_test: - - src_wire: R3C3_PLC.PLC/JDI0_SLICEA - dst_wire: R3C3/JF0 -bel_pin_test: - - bel: R7C3_PLC.PLC/SLICEA_LUT0 - pin: D - wire: R7C3_PLC.PLC/JD0_SLICEA - diff --git a/fpga_interchange/examples/devices/LIFCL-40/CMakeLists.txt b/fpga_interchange/examples/devices/LIFCL-40/CMakeLists.txt deleted file mode 100644 index d6310116..00000000 --- a/fpga_interchange/examples/devices/LIFCL-40/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -generate_nexus_device_db( - device LIFCL-40 - device_target lifcl40_target -) - -generate_chipdb( - family ${family} - device LIFCL-40 - part LIFCL-40-9BG400C - device_target ${lifcl40_target} - device_config ${PYTHON_INTERCHANGE_PATH}/test_data/nexus_device_config.yaml - test_package CABGA400 -) diff --git a/fpga_interchange/examples/devices/LIFCL-40/test_data.yaml b/fpga_interchange/examples/devices/LIFCL-40/test_data.yaml deleted file mode 100644 index c4787eba..00000000 --- a/fpga_interchange/examples/devices/LIFCL-40/test_data.yaml +++ /dev/null @@ -1,8 +0,0 @@ -pip_test: - - src_wire: R3C3_PLC.PLC/JDI0_SLICEA - dst_wire: R3C3/JF0 -bel_pin_test: - - bel: R7C3_PLC.PLC/SLICEA_LUT0 - pin: D - wire: R7C3_PLC.PLC/JD0_SLICEA - diff --git a/fpga_interchange/examples/devices/xc7a100t/CMakeLists.txt b/fpga_interchange/examples/devices/xc7a100t/CMakeLists.txt deleted file mode 100644 index 955ee8ca..00000000 --- a/fpga_interchange/examples/devices/xc7a100t/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -generate_xc7_device_db( - device xc7a100t - part xc7a100tcsg324-1 - device_target xc7a100t_target -) - -generate_chipdb( - family ${family} - device xc7a100t - part xc7a100tcsg324-1 - device_target ${xc7a100t_target} - device_config ${PYTHON_INTERCHANGE_PATH}/test_data/series7_device_config.yaml - test_package csg324 -) diff --git a/fpga_interchange/examples/devices/xc7a100t/test_data.yaml b/fpga_interchange/examples/devices/xc7a100t/test_data.yaml deleted file mode 100644 index 268d180a..00000000 --- a/fpga_interchange/examples/devices/xc7a100t/test_data.yaml +++ /dev/null @@ -1,36 +0,0 @@ -pip_test: - - src_wire: CLBLM_R_X11Y93/CLBLM_L_D3 - dst_wire: SLICE_X15Y93.SLICEL/D3 -pip_chain_test: - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - $CONSTANTS_X0Y0/$GND_NODE - - TIEOFF_X3Y145.TIEOFF/$GND_SITE_WIRE - - TIEOFF_X3Y145.TIEOFF/HARD0GND_HARD0 - - INT_R_X3Y145/GND_WIRE - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE - - $CONSTANTS_X0Y0/$VCC_NODE - - TIEOFF_X3Y145.TIEOFF/$VCC_SITE_WIRE - - TIEOFF_X3Y145.TIEOFF/HARD1VCC_HARD1 - - INT_R_X3Y145/VCC_WIRE - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE - - $CONSTANTS_X0Y0/$VCC_NODE - - SLICE_X3Y145.SLICEL/$VCC_SITE_WIRE - - SLICE_X3Y145.SLICEL/CEUSEDVCC_HARD1 - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - $CONSTANTS_X0Y0/$GND_NODE - - SLICE_X3Y145.SLICEL/$GND_SITE_WIRE - - SLICE_X3Y145.SLICEL/SRUSEDGND_HARD0 -bel_pin_test: - - bel: SLICE_X15Y93.SLICEL/D6LUT - pin: A3 - wire: SLICE_X15Y93.SLICEL/D3 - - bel: $CONSTANTS_X0Y0.$CONSTANTS/GND - pin: G - wire: $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - bel: $CONSTANTS_X0Y0.$CONSTANTS/VCC - pin: P - wire: $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE diff --git a/fpga_interchange/examples/devices/xc7a200t/CMakeLists.txt b/fpga_interchange/examples/devices/xc7a200t/CMakeLists.txt deleted file mode 100644 index b77d77bb..00000000 --- a/fpga_interchange/examples/devices/xc7a200t/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -generate_xc7_device_db( - device xc7a200t - part xc7a200tsbg484-1 - device_target xc7a200t_target -) - -generate_chipdb( - family ${family} - device xc7a200t - part xc7a200tsbg484-1 - device_target ${xc7a200t_target} - device_config ${PYTHON_INTERCHANGE_PATH}/test_data/series7_device_config.yaml - test_package sbg484 -) diff --git a/fpga_interchange/examples/devices/xc7a200t/test_data.yaml b/fpga_interchange/examples/devices/xc7a200t/test_data.yaml deleted file mode 100644 index 268d180a..00000000 --- a/fpga_interchange/examples/devices/xc7a200t/test_data.yaml +++ /dev/null @@ -1,36 +0,0 @@ -pip_test: - - src_wire: CLBLM_R_X11Y93/CLBLM_L_D3 - dst_wire: SLICE_X15Y93.SLICEL/D3 -pip_chain_test: - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - $CONSTANTS_X0Y0/$GND_NODE - - TIEOFF_X3Y145.TIEOFF/$GND_SITE_WIRE - - TIEOFF_X3Y145.TIEOFF/HARD0GND_HARD0 - - INT_R_X3Y145/GND_WIRE - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE - - $CONSTANTS_X0Y0/$VCC_NODE - - TIEOFF_X3Y145.TIEOFF/$VCC_SITE_WIRE - - TIEOFF_X3Y145.TIEOFF/HARD1VCC_HARD1 - - INT_R_X3Y145/VCC_WIRE - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE - - $CONSTANTS_X0Y0/$VCC_NODE - - SLICE_X3Y145.SLICEL/$VCC_SITE_WIRE - - SLICE_X3Y145.SLICEL/CEUSEDVCC_HARD1 - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - $CONSTANTS_X0Y0/$GND_NODE - - SLICE_X3Y145.SLICEL/$GND_SITE_WIRE - - SLICE_X3Y145.SLICEL/SRUSEDGND_HARD0 -bel_pin_test: - - bel: SLICE_X15Y93.SLICEL/D6LUT - pin: A3 - wire: SLICE_X15Y93.SLICEL/D3 - - bel: $CONSTANTS_X0Y0.$CONSTANTS/GND - pin: G - wire: $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - bel: $CONSTANTS_X0Y0.$CONSTANTS/VCC - pin: P - wire: $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE diff --git a/fpga_interchange/examples/devices/xc7a35t/CMakeLists.txt b/fpga_interchange/examples/devices/xc7a35t/CMakeLists.txt deleted file mode 100644 index 636440c4..00000000 --- a/fpga_interchange/examples/devices/xc7a35t/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -generate_xc7_device_db( - device xc7a35t - part xc7a35tcsg324-1 - device_target xc7a35t_target -) - -generate_chipdb( - family ${family} - device xc7a35t - part xc7a35tcsg324-1 - device_target ${xc7a35t_target} - device_config ${PYTHON_INTERCHANGE_PATH}/test_data/series7_device_config.yaml - test_package csg324 -) diff --git a/fpga_interchange/examples/devices/xc7a35t/test_data.yaml b/fpga_interchange/examples/devices/xc7a35t/test_data.yaml deleted file mode 100644 index 88c6feda..00000000 --- a/fpga_interchange/examples/devices/xc7a35t/test_data.yaml +++ /dev/null @@ -1,40 +0,0 @@ -pip_test: - - src_wire: CLBLM_R_X11Y93/CLBLM_L_D3 - dst_wire: SLICE_X15Y93.SLICEL/D3 -pip_chain_test: - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - $CONSTANTS_X0Y0/$GND_NODE - - TIEOFF_X3Y145.TIEOFF/$GND_SITE_WIRE - - TIEOFF_X3Y145.TIEOFF/HARD0GND_HARD0 - - INT_R_X3Y145/GND_WIRE - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE - - $CONSTANTS_X0Y0/$VCC_NODE - - TIEOFF_X3Y145.TIEOFF/$VCC_SITE_WIRE - - TIEOFF_X3Y145.TIEOFF/HARD1VCC_HARD1 - - INT_R_X3Y145/VCC_WIRE - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE - - $CONSTANTS_X0Y0/$VCC_NODE - - SLICE_X3Y145.SLICEL/$VCC_SITE_WIRE - - SLICE_X3Y145.SLICEL/CEUSEDVCC_HARD1 - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - $CONSTANTS_X0Y0/$GND_NODE - - SLICE_X3Y145.SLICEL/$GND_SITE_WIRE - - SLICE_X3Y145.SLICEL/SRUSEDGND_HARD0 -bel_pin_test: - - bel: SLICE_X15Y93.SLICEL/D6LUT - pin: A3 - wire: SLICE_X15Y93.SLICEL/D3 - - bel: $CONSTANTS_X0Y0.$CONSTANTS/GND - pin: G - wire: $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - bel: $CONSTANTS_X0Y0.$CONSTANTS/VCC - pin: P - wire: $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE - - bel: SLICE_X1Y19.SLICEL/SRUSEDGND - pin: "0" - wire: SLICE_X1Y19.SLICEL/SRUSEDGND_HARD0 - type: PORT_OUT diff --git a/fpga_interchange/examples/devices/xc7z010/CMakeLists.txt b/fpga_interchange/examples/devices/xc7z010/CMakeLists.txt deleted file mode 100644 index ec6a7728..00000000 --- a/fpga_interchange/examples/devices/xc7z010/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -generate_xc7_device_db( - device xc7z010 - part xc7z010clg400-1 - device_target xc7z010_target -) - -generate_chipdb( - family ${family} - device xc7z010 - part xc7z010clg400-1 - device_target ${xc7z010_target} - device_config ${PYTHON_INTERCHANGE_PATH}/test_data/series7_device_config.yaml - test_package clg400 -) diff --git a/fpga_interchange/examples/devices/xc7z010/test_data.yaml b/fpga_interchange/examples/devices/xc7z010/test_data.yaml deleted file mode 100644 index dbc95845..00000000 --- a/fpga_interchange/examples/devices/xc7z010/test_data.yaml +++ /dev/null @@ -1,36 +0,0 @@ -pip_test: - - src_wire: CLBLM_L_X8Y69/CLBLM_L_D3 - dst_wire: SLICE_X11Y69.SLICEL/D3 -pip_chain_test: - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - $CONSTANTS_X0Y0/$GND_NODE - - TIEOFF_X9Y69.TIEOFF/$GND_SITE_WIRE - - TIEOFF_X9Y69.TIEOFF/HARD0GND_HARD0 - - INT_L_X8Y69/GND_WIRE - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE - - $CONSTANTS_X0Y0/$VCC_NODE - - TIEOFF_X9Y69.TIEOFF/$VCC_SITE_WIRE - - TIEOFF_X9Y69.TIEOFF/HARD1VCC_HARD1 - - INT_L_X8Y69/VCC_WIRE - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE - - $CONSTANTS_X0Y0/$VCC_NODE - - SLICE_X11Y69.SLICEL/$VCC_SITE_WIRE - - SLICE_X11Y69.SLICEL/CEUSEDVCC_HARD1 - - wires: - - $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - $CONSTANTS_X0Y0/$GND_NODE - - SLICE_X11Y69.SLICEL/$GND_SITE_WIRE - - SLICE_X11Y69.SLICEL/SRUSEDGND_HARD0 -bel_pin_test: - - bel: SLICE_X14Y63.SLICEL/D6LUT - pin: A3 - wire: SLICE_X14Y63.SLICEL/D3 - - bel: $CONSTANTS_X0Y0.$CONSTANTS/GND - pin: G - wire: $CONSTANTS_X0Y0.$CONSTANTS/$GND_SOURCE - - bel: $CONSTANTS_X0Y0.$CONSTANTS/VCC - pin: P - wire: $CONSTANTS_X0Y0.$CONSTANTS/$VCC_SOURCE diff --git a/fpga_interchange/examples/remap_nexus.v b/fpga_interchange/examples/remap_nexus.v deleted file mode 100644 index 861ce779..00000000 --- a/fpga_interchange/examples/remap_nexus.v +++ /dev/null @@ -1,180 +0,0 @@ -// Inverter support is still a TODO -module INV(input A, output Z); - LUT4 #(.INIT("0x5555")) _TECHMAP_REPLACE_ (.A(A), .B(1'b1), .C(1'b1), .D(1'b1), .Z(Z)); -endmodule - -module PDPSC16K (...); - parameter DATA_WIDTH_W = "X36"; - parameter DATA_WIDTH_R = "X36"; - parameter OUTREG = "BYPASSED"; - parameter RESETMODE = "SYNC"; - parameter GSR = "ENABLED"; - parameter ECC = "DISABLED"; - parameter INITVAL_00 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_01 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_02 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_03 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_04 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_05 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_06 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_07 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_08 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_09 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_0A = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_0B = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_0C = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_0D = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_0E = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_0F = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_10 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_11 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_12 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_13 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_14 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_15 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_16 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_17 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_18 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_19 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_1A = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_1B = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_1C = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_1D = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_1E = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_1F = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_20 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_21 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_22 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_23 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_24 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_25 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_26 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_27 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_28 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_29 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_2A = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_2B = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_2C = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_2D = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_2E = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_2F = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_30 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_31 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_32 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_33 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_34 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_35 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_36 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_37 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_38 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_39 = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_3A = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_3B = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_3C = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_3D = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_3E = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter INITVAL_3F = "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000"; - parameter CSDECODE_W = "000"; - parameter CSDECODE_R = "000"; - parameter ASYNC_RST_RELEASE = "SYNC"; - parameter INIT_DATA = "STATIC"; - input [35:0] DI; - input [13:0] ADW; - input [13:0] ADR; - input CLK; - input CER; - input CEW; - input [2:0] CSW; - input [2:0] CSR; - input RST; - output [35:0] DO; - output ONEBITERR; - output TWOBITERR; - - // TODO: this should really be a macro or otherwise dealt with more automatically - PDPSC16K_MODE #( - .DATA_WIDTH_W(DATA_WIDTH_W), - .DATA_WIDTH_R(DATA_WIDTH_R), - .OUTREG(OUTREG), - .GSR(GSR), - .ECC(ECC), - .CSDECODE_W(CSDECODE_W), - .CSDECODE_R(CSDECODE_R), - .ASYNC_RST_RELEASE(ASYNC_RST_RELEASE), - .INITVAL_00(INITVAL_00), - .INITVAL_01(INITVAL_01), - .INITVAL_02(INITVAL_02), - .INITVAL_03(INITVAL_03), - .INITVAL_04(INITVAL_04), - .INITVAL_05(INITVAL_05), - .INITVAL_06(INITVAL_06), - .INITVAL_07(INITVAL_07), - .INITVAL_08(INITVAL_08), - .INITVAL_09(INITVAL_09), - .INITVAL_0A(INITVAL_0A), - .INITVAL_0B(INITVAL_0B), - .INITVAL_0C(INITVAL_0C), - .INITVAL_0D(INITVAL_0D), - .INITVAL_0E(INITVAL_0E), - .INITVAL_0F(INITVAL_0F), - .INITVAL_10(INITVAL_10), - .INITVAL_11(INITVAL_11), - .INITVAL_12(INITVAL_12), - .INITVAL_13(INITVAL_13), - .INITVAL_14(INITVAL_14), - .INITVAL_15(INITVAL_15), - .INITVAL_16(INITVAL_16), - .INITVAL_17(INITVAL_17), - .INITVAL_18(INITVAL_18), - .INITVAL_19(INITVAL_19), - .INITVAL_1A(INITVAL_1A), - .INITVAL_1B(INITVAL_1B), - .INITVAL_1C(INITVAL_1C), - .INITVAL_1D(INITVAL_1D), - .INITVAL_1E(INITVAL_1E), - .INITVAL_1F(INITVAL_1F), - .INITVAL_20(INITVAL_20), - .INITVAL_21(INITVAL_21), - .INITVAL_22(INITVAL_22), - .INITVAL_23(INITVAL_23), - .INITVAL_24(INITVAL_24), - .INITVAL_25(INITVAL_25), - .INITVAL_26(INITVAL_26), - .INITVAL_27(INITVAL_27), - .INITVAL_28(INITVAL_28), - .INITVAL_29(INITVAL_29), - .INITVAL_2A(INITVAL_2A), - .INITVAL_2B(INITVAL_2B), - .INITVAL_2C(INITVAL_2C), - .INITVAL_2D(INITVAL_2D), - .INITVAL_2E(INITVAL_2E), - .INITVAL_2F(INITVAL_2F), - .INITVAL_30(INITVAL_30), - .INITVAL_31(INITVAL_31), - .INITVAL_32(INITVAL_32), - .INITVAL_33(INITVAL_33), - .INITVAL_34(INITVAL_34), - .INITVAL_35(INITVAL_35), - .INITVAL_36(INITVAL_36), - .INITVAL_37(INITVAL_37), - .INITVAL_38(INITVAL_38), - .INITVAL_39(INITVAL_39), - .INITVAL_3A(INITVAL_3A), - .INITVAL_3B(INITVAL_3B), - .INITVAL_3C(INITVAL_3C), - .INITVAL_3D(INITVAL_3D), - .INITVAL_3E(INITVAL_3E), - .INITVAL_3F(INITVAL_3F), - .INIT_DATA(INIT_DATA) - ) _TECHMAP_REPLACE_ ( - .CLK(CLK), .CER(CER), .CEW(CEW), .RST(RST), - .CSW0(CSW[0]), .CSW1(CSW[1]), .CSW2(CSW[2]), - .CSR0(CSR[0]), .CSR1(CSR[1]), .CSR2(CSR[2]), - .DI0(DI[0]), .DI1(DI[1]), .DI2(DI[2]), .DI3(DI[3]), .DI4(DI[4]), .DI5(DI[5]), .DI6(DI[6]), .DI7(DI[7]), .DI8(DI[8]), .DI9(DI[9]), .DI10(DI[10]), .DI11(DI[11]), .DI12(DI[12]), .DI13(DI[13]), .DI14(DI[14]), .DI15(DI[15]), .DI16(DI[16]), .DI17(DI[17]), .DI18(DI[18]), .DI19(DI[19]), .DI20(DI[20]), .DI21(DI[21]), .DI22(DI[22]), .DI23(DI[23]), .DI24(DI[24]), .DI25(DI[25]), .DI26(DI[26]), .DI27(DI[27]), .DI28(DI[28]), .DI29(DI[29]), .DI30(DI[30]), .DI31(DI[31]), .DI32(DI[32]), .DI33(DI[33]), .DI34(DI[34]), .DI35(DI[35]), - .ADW0(ADW[0]), .ADW1(ADW[1]), .ADW2(ADW[2]), .ADW3(ADW[3]), .ADW4(ADW[4]), .ADW5(ADW[5]), .ADW6(ADW[6]), .ADW7(ADW[7]), .ADW8(ADW[8]), .ADW9(ADW[9]), .ADW10(ADW[10]), .ADW11(ADW[11]), .ADW12(ADW[12]), .ADW13(ADW[13]), - .ADR0(ADR[0]), .ADR1(ADR[1]), .ADR2(ADR[2]), .ADR3(ADR[3]), .ADR4(ADR[4]), .ADR5(ADR[5]), .ADR6(ADR[6]), .ADR7(ADR[7]), .ADR8(ADR[8]), .ADR9(ADR[9]), .ADR10(ADR[10]), .ADR11(ADR[11]), .ADR12(ADR[12]), .ADR13(ADR[13]), - .DO0(DO[0]), .DO1(DO[1]), .DO2(DO[2]), .DO3(DO[3]), .DO4(DO[4]), .DO5(DO[5]), .DO6(DO[6]), .DO7(DO[7]), .DO8(DO[8]), .DO9(DO[9]), .DO10(DO[10]), .DO11(DO[11]), .DO12(DO[12]), .DO13(DO[13]), .DO14(DO[14]), .DO15(DO[15]), .DO16(DO[16]), .DO17(DO[17]), .DO18(DO[18]), .DO19(DO[19]), .DO20(DO[20]), .DO21(DO[21]), .DO22(DO[22]), .DO23(DO[23]), .DO24(DO[24]), .DO25(DO[25]), .DO26(DO[26]), .DO27(DO[27]), .DO28(DO[28]), .DO29(DO[29]), .DO30(DO[30]), .DO31(DO[31]), .DO32(DO[32]), .DO33(DO[33]), .DO34(DO[34]), .DO35(DO[35]), - .ONEBITERR(ONEBITERR), .TWOBITERR(TWOBITERR), - ); - -endmodule diff --git a/fpga_interchange/examples/remap_xilinx.v b/fpga_interchange/examples/remap_xilinx.v deleted file mode 100644 index 6dfc0b4a..00000000 --- a/fpga_interchange/examples/remap_xilinx.v +++ /dev/null @@ -1,11 +0,0 @@ -module INV(input I, output O); - -LUT1 #(.INIT(2'b01)) _TECHMAP_REPLACE_ (.I0(I), .O(O)); - -endmodule - -module BUF(input I, output O); - -LUT1 #(.INIT(2'b10)) _TECHMAP_REPLACE_ (.I0(I), .O(O)); - -endmodule diff --git a/fpga_interchange/examples/tests.cmake b/fpga_interchange/examples/tests.cmake deleted file mode 100644 index b568c5df..00000000 --- a/fpga_interchange/examples/tests.cmake +++ /dev/null @@ -1,405 +0,0 @@ -function(add_interchange_test) - # ~~~ - # add_interchange_test( - # name - # family - # device - # package - # tcl - # xdc - # sources - # [top ] - # [techmap ] - # [output_fasm] - # [device_family ] - # ) - # - # Generates targets to run desired tests - # - # Arguments: - # - name: test name. This must be unique and no other tests with the same - # name should exist - # - family: nextpnr architecture family (e.g. fpga_interchange) - # - device_family: common device name of a set of parts. E.g. xc7a35tcsg324-1 and xc7a35tcpg236-1 - # share the same xc7a35t device prefix - # - device: common device name of a set of parts. E.g. xc7a35tcsg324-1 and xc7a35tcpg236-1 - # share the same xc7a35t device prefix - # - package: package among the ones available for the device - # - tcl: tcl script used for synthesis - # - xdc: constraints file used in the physical netlist generation step - # - sources: list of HDL sources - # - top (optional): name of the top level module. - # If not provided, "top" is assigned as top level module - # - techmap (optional): techmap file used during synthesis - # - output_fasm (optional): generates a fasm output - # - device_family (optional): this information is used during FASM generation, therfore it is - # required only if the `output_fasm` option is enabled - # - # Targets generated: - # - test-fpga_interchange--json : synthesis output - # - test-fpga_interchange--netlist : interchange logical netlist - # - test-fpga_interchange--phys : interchange physical netlist - # - test-fpga_interchange--dcp : design checkpoint with RapidWright - - set(options skip_dcp output_fasm) - set(oneValueArgs name family device package tcl xdc top techmap device_family) - set(multiValueArgs sources) - - cmake_parse_arguments( - add_interchange_test - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} - ) - - set(name ${add_interchange_test_name}) - set(family ${add_interchange_test_family}) - set(device ${add_interchange_test_device}) - set(package ${add_interchange_test_package}) - set(skip_dcp ${add_interchange_test_skip_dcp}) - set(output_fasm ${add_interchange_test_output_fasm}) - set(top ${add_interchange_test_top}) - set(tcl ${CMAKE_CURRENT_SOURCE_DIR}/${add_interchange_test_tcl}) - set(xdc ${CMAKE_CURRENT_SOURCE_DIR}/${add_interchange_test_xdc}) - set(techmap ${CMAKE_CURRENT_SOURCE_DIR}/${add_interchange_test_techmap}) - set(device_family ${add_interchange_test_device_family}) - - set(sources) - foreach(source ${add_interchange_test_sources}) - list(APPEND sources ${CMAKE_CURRENT_SOURCE_DIR}/${source}) - endforeach() - - if (NOT DEFINED top) - # Setting default top value - set(top "top") - endif() - - # Synthesis - set(synth_json ${CMAKE_CURRENT_BINARY_DIR}/${name}.json) - set(synth_log ${CMAKE_CURRENT_BINARY_DIR}/${name}.json.log) - add_custom_command( - OUTPUT ${synth_json} - COMMAND ${CMAKE_COMMAND} -E env - SOURCES="${sources}" - OUT_JSON=${synth_json} - TECHMAP=${techmap} - yosys -c ${tcl} -l ${synth_log} - DEPENDS ${sources} ${techmap} ${tcl} - ) - - add_custom_target(test-${family}-${name}-json DEPENDS ${synth_json}) - - # Logical Netlist - get_property(device_target TARGET device-${device} PROPERTY DEVICE_TARGET) - get_property(device_loc TARGET device-${device} PROPERTY DEVICE_LOC) - - set(netlist ${CMAKE_CURRENT_BINARY_DIR}/${name}.netlist) - add_custom_command( - OUTPUT ${netlist} - COMMAND - ${Python3_EXECUTABLE} -mfpga_interchange.yosys_json - --schema_dir ${INTERCHANGE_SCHEMA_PATH} - --device ${device_loc} - --top ${top} - ${synth_json} - ${netlist} - DEPENDS - ${synth_json} - ${device_target} - ${device_loc} - ) - - add_custom_target(test-${family}-${name}-netlist DEPENDS ${netlist}) - - # Logical Netlist YAML - set(netlist_yaml ${CMAKE_CURRENT_BINARY_DIR}/${name}.netlist.yaml) - add_custom_command( - OUTPUT ${netlist_yaml} - COMMAND - ${Python3_EXECUTABLE} -mfpga_interchange.convert - --schema_dir ${INTERCHANGE_SCHEMA_PATH} - --schema logical - --input_format capnp - --output_format yaml - ${netlist} - ${netlist_yaml} - DEPENDS - ${netlist} - ) - - add_custom_target(test-${family}-${name}-netlist-yaml DEPENDS ${netlist_yaml}) - - # Physical Netlist - get_property(chipdb_bin_target TARGET device-${device} PROPERTY CHIPDB_BIN_TARGET) - get_property(chipdb_bin_loc TARGET device-${device} PROPERTY CHIPDB_BIN_LOC) - - set(phys ${CMAKE_CURRENT_BINARY_DIR}/${name}.phys) - set(phys_log ${CMAKE_CURRENT_BINARY_DIR}/${name}.phys.log) - add_custom_command( - OUTPUT ${phys} - COMMAND - nextpnr-fpga_interchange - --chipdb ${chipdb_bin_loc} - --xdc ${xdc} - --netlist ${netlist} - --phys ${phys} - --package ${package} - --log ${phys_log} - DEPENDS - nextpnr-fpga_interchange - ${netlist} - ${xdc} - ${chipdb_bin_target} - ${chipdb_bin_loc} - ) - - set(phys_verbose_log ${CMAKE_CURRENT_BINARY_DIR}/${name}.phys.verbose.log) - add_custom_target( - test-${family}-${name}-phys-verbose - COMMAND - nextpnr-fpga_interchange - --chipdb ${chipdb_bin_loc} - --xdc ${xdc} - --netlist ${netlist} - --phys ${phys} - --package ${package} - --verbose - --log ${phys_verbose_log} - DEPENDS - ${netlist} - ${xdc} - ${chipdb_bin_target} - ${chipdb_bin_loc} - ) - - add_custom_target( - test-${family}-${name}-phys-verbose2 - COMMAND - nextpnr-fpga_interchange - --chipdb ${chipdb_bin_loc} - --xdc ${xdc} - --netlist ${netlist} - --phys ${phys} - --package ${package} - --debug - DEPENDS - ${netlist} - ${xdc} - ${chipdb_bin_target} - ${chipdb_bin_loc} - ) - - add_custom_target( - test-${family}-${name}-phys-debug - COMMAND gdb --args - $ - --chipdb ${chipdb_bin_loc} - --xdc ${xdc} - --netlist ${netlist} - --phys ${phys} - --package ${package} - DEPENDS - ${netlist} - ${xdc} - ${chipdb_bin_target} - ${chipdb_bin_loc} - ) - - add_custom_target( - test-${family}-${name}-phys-valgrind - COMMAND - PYTHONMALLOC=malloc valgrind - $ - --chipdb ${chipdb_bin_loc} - --xdc ${xdc} - --netlist ${netlist} - --phys ${phys} - --package ${package} - DEPENDS - ${netlist} - ${xdc} - ${chipdb_bin_target} - ${chipdb_bin_loc} - ) - - if(PROFILER) - add_custom_target( - test-${family}-${name}-phys-profile - COMMAND CPUPROFILE=${name}.prof - $ - --chipdb ${chipdb_bin_loc} - --xdc ${xdc} - --netlist ${netlist} - --phys ${phys} - --package ${package} - DEPENDS - ${netlist} - ${xdc} - ${chipdb_bin_target} - ${chipdb_bin_loc} - ) - endif() - - add_custom_target(test-${family}-${name}-phys DEPENDS ${phys}) - - # Physical Netlist YAML - set(phys_yaml ${CMAKE_CURRENT_BINARY_DIR}/${name}.phys.yaml) - add_custom_command( - OUTPUT ${phys_yaml} - COMMAND - ${Python3_EXECUTABLE} -mfpga_interchange.convert - --schema_dir ${INTERCHANGE_SCHEMA_PATH} - --schema physical - --input_format capnp - --output_format yaml - ${phys} - ${phys_yaml} - DEPENDS - ${phys} - ) - - add_custom_target(test-${family}-${name}-phys-yaml DEPENDS ${phys_yaml}) - - set(last_target "") - if(skip_dcp) - set(last_target test-${family}-${name}-phys) - else() - set(dcp ${CMAKE_CURRENT_BINARY_DIR}/${name}.dcp) - add_custom_command( - OUTPUT ${dcp} - COMMAND - RAPIDWRIGHT_PATH=${RAPIDWRIGHT_PATH} - ${INVOKE_RAPIDWRIGHT} ${JAVA_HEAP_SPACE} - com.xilinx.rapidwright.interchange.PhysicalNetlistToDcp - ${netlist} ${phys} ${xdc} ${dcp} - DEPENDS - ${INVOKE_RAPIDWRIGHT} - ${phys} - ${netlist} - ) - - add_custom_target(test-${family}-${name}-dcp DEPENDS ${dcp}) - set(last_target test-${family}-${name}-dcp) - endif() - - add_dependencies(all-${device}-tests ${last_target}) - add_dependencies(all-${family}-tests ${last_target}) - - if(output_fasm) - if(NOT DEFINED device_family) - message(FATAL_ERROR "If FASM output is enabled, the device family must be provided as well!") - endif() - - # Output FASM target - set(fasm ${CMAKE_CURRENT_BINARY_DIR}/${name}.fasm) - add_custom_command( - OUTPUT ${fasm} - COMMAND - ${Python3_EXECUTABLE} -mfpga_interchange.fasm_generator - --schema_dir ${INTERCHANGE_SCHEMA_PATH} - --family ${device_family} - ${device_loc} - ${netlist} - ${phys} - ${fasm} - DEPENDS - ${device_target} - ${netlist} - ${phys} - ) - - add_custom_target(test-${family}-${name}-fasm DEPENDS ${fasm} ${last_target}) - add_dependencies(all-${device}-tests test-${family}-${name}-fasm) - add_dependencies(all-${family}-tests test-${family}-${name}-fasm) - endif() -endfunction() - -function(add_interchange_group_test) - # ~~~ - # add_interchange_group_test( - # name - # family - # board_list - # xdc_list - # tcl - # sources - # [top ] - # [techmap ] - # [skip_dcp] - # ) - # - # Generates targets to run desired tests over multiple devices. - # - # Arguments: - # - name: base test name. The real test name will be _ - # - family: nextpnr architecture family (e.g. fpga_interchange) - # - board_list: list of boards, one for each test - # - tcl: tcl script used for synthesis - # - sources: list of HDL sources - # - top (optional): name of the top level module. - # If not provided, "top" is assigned as top level module - # - techmap (optional): techmap file used during synthesis - # - # This function internally calls add_interchange_test to generate the various tests. - # - # Note: it is assumed that there exists an XDC file for each board, with the following naming - # convention: .xdc - - set(options output_fasm skip_dcp) - set(oneValueArgs name family tcl top techmap) - set(multiValueArgs sources board_list) - - cmake_parse_arguments( - add_interchange_group_test - "${options}" - "${oneValueArgs}" - "${multiValueArgs}" - ${ARGN} - ) - - set(name ${add_interchange_group_test_name}) - set(family ${add_interchange_group_test_family}) - set(top ${add_interchange_group_test_top}) - set(tcl ${add_interchange_group_test_tcl}) - set(techmap ${add_interchange_group_test_techmap}) - set(sources ${add_interchange_group_test_sources}) - set(output_fasm ${add_interchange_group_test_output_fasm}) - set(skip_dcp ${add_interchange_group_test_skip_dcp}) - - set(output_fasm_arg "") - if(output_fasm) - set(output_fasm_arg "output_fasm") - endif() - - set(skip_dcp_arg "") - if(skip_dcp) - set(skip_dcp_arg "skip_dcp") - endif() - - if (NOT DEFINED top) - # Setting default top value - set(top "top") - endif() - - foreach(board ${add_interchange_group_test_board_list}) - get_property(device_family TARGET board-${board} PROPERTY DEVICE_FAMILY) - get_property(device TARGET board-${board} PROPERTY DEVICE) - get_property(package TARGET board-${board} PROPERTY PACKAGE) - - add_interchange_test( - name ${name}_${board} - family ${family} - device ${device} - device_family ${device_family} - package ${package} - tcl ${tcl} - xdc ${board}.xdc - sources ${sources} - top ${top} - techmap ${techmap} - ${output_fasm_arg} - ${skip_dcp_arg} - ) - endforeach() -endfunction() diff --git a/fpga_interchange/examples/tests/CMakeLists.txt b/fpga_interchange/examples/tests/CMakeLists.txt deleted file mode 100644 index f8a52a41..00000000 --- a/fpga_interchange/examples/tests/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -add_subdirectory(wire) -add_subdirectory(const_wire) -add_subdirectory(counter) -add_subdirectory(ram) -add_subdirectory(ff) -add_subdirectory(lut) -add_subdirectory(lut_nexus) -add_subdirectory(lutram) -add_subdirectory(obuftds) -add_subdirectory(ram_nexus) diff --git a/fpga_interchange/examples/tests/const_wire/CMakeLists.txt b/fpga_interchange/examples/tests/const_wire/CMakeLists.txt deleted file mode 100644 index 3fff5fbb..00000000 --- a/fpga_interchange/examples/tests/const_wire/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_interchange_group_test( - name const_wire - family ${family} - board_list basys3 arty35t arty100t - tcl run.tcl - sources wire.v -) diff --git a/fpga_interchange/examples/tests/const_wire/arty100t.xdc b/fpga_interchange/examples/tests/const_wire/arty100t.xdc deleted file mode 100644 index a6e69de5..00000000 --- a/fpga_interchange/examples/tests/const_wire/arty100t.xdc +++ /dev/null @@ -1,9 +0,0 @@ -set_property PACKAGE_PIN H5 [get_ports o] -set_property PACKAGE_PIN J5 [get_ports o2] -set_property PACKAGE_PIN T9 [get_ports o3] -set_property PACKAGE_PIN T10 [get_ports o4] - -set_property IOSTANDARD LVCMOS33 [get_ports o] -set_property IOSTANDARD LVCMOS33 [get_ports o2] -set_property IOSTANDARD LVCMOS33 [get_ports o3] -set_property IOSTANDARD LVCMOS33 [get_ports o4] diff --git a/fpga_interchange/examples/tests/const_wire/arty35t.xdc b/fpga_interchange/examples/tests/const_wire/arty35t.xdc deleted file mode 100644 index a6e69de5..00000000 --- a/fpga_interchange/examples/tests/const_wire/arty35t.xdc +++ /dev/null @@ -1,9 +0,0 @@ -set_property PACKAGE_PIN H5 [get_ports o] -set_property PACKAGE_PIN J5 [get_ports o2] -set_property PACKAGE_PIN T9 [get_ports o3] -set_property PACKAGE_PIN T10 [get_ports o4] - -set_property IOSTANDARD LVCMOS33 [get_ports o] -set_property IOSTANDARD LVCMOS33 [get_ports o2] -set_property IOSTANDARD LVCMOS33 [get_ports o3] -set_property IOSTANDARD LVCMOS33 [get_ports o4] diff --git a/fpga_interchange/examples/tests/const_wire/basys3.xdc b/fpga_interchange/examples/tests/const_wire/basys3.xdc deleted file mode 100644 index f8435580..00000000 --- a/fpga_interchange/examples/tests/const_wire/basys3.xdc +++ /dev/null @@ -1,9 +0,0 @@ -set_property PACKAGE_PIN U16 [get_ports o] -set_property PACKAGE_PIN E19 [get_ports o2] -set_property PACKAGE_PIN U19 [get_ports o3] -set_property PACKAGE_PIN V19 [get_ports o4] - -set_property IOSTANDARD LVCMOS33 [get_ports o] -set_property IOSTANDARD LVCMOS33 [get_ports o2] -set_property IOSTANDARD LVCMOS33 [get_ports o3] -set_property IOSTANDARD LVCMOS33 [get_ports o4] diff --git a/fpga_interchange/examples/tests/const_wire/run.tcl b/fpga_interchange/examples/tests/const_wire/run.tcl deleted file mode 100644 index b8d0df72..00000000 --- a/fpga_interchange/examples/tests/const_wire/run.tcl +++ /dev/null @@ -1,14 +0,0 @@ -yosys -import - -read_verilog $::env(SOURCES) - -synth_xilinx -nolutram -nowidelut -nosrl -nocarry -nodsp - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/const_wire/wire.v b/fpga_interchange/examples/tests/const_wire/wire.v deleted file mode 100644 index 5b1ab692..00000000 --- a/fpga_interchange/examples/tests/const_wire/wire.v +++ /dev/null @@ -1,8 +0,0 @@ -module top(output o, output o2, output o3, output o4); - -assign o = 1'b0; -assign o2 = 1'b1; -assign o3 = 1'b0; -assign o4 = 1'b1; - -endmodule diff --git a/fpga_interchange/examples/tests/counter/CMakeLists.txt b/fpga_interchange/examples/tests/counter/CMakeLists.txt deleted file mode 100644 index 2e0eefcc..00000000 --- a/fpga_interchange/examples/tests/counter/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -add_interchange_group_test( - name counter - family ${family} - board_list basys3 arty35t arty100t zybo - tcl run_xilinx.tcl - sources counter.v - techmap ../../remap_xilinx.v - output_fasm -) - -add_interchange_group_test( - name counter - family ${family} - board_list lifcl17 lifcl40evn - tcl run_nexus.tcl - sources counter.v - techmap ../../remap_nexus.v - skip_dcp -) diff --git a/fpga_interchange/examples/tests/counter/arty100t.xdc b/fpga_interchange/examples/tests/counter/arty100t.xdc deleted file mode 100644 index c40df560..00000000 --- a/fpga_interchange/examples/tests/counter/arty100t.xdc +++ /dev/null @@ -1,14 +0,0 @@ -## arty-100t board -set_property PACKAGE_PIN E3 [get_ports clk] -set_property PACKAGE_PIN D9 [get_ports rst] -set_property PACKAGE_PIN H5 [get_ports io_led[4]] -set_property PACKAGE_PIN J5 [get_ports io_led[5]] -set_property PACKAGE_PIN T9 [get_ports io_led[6]] -set_property PACKAGE_PIN T10 [get_ports io_led[7]] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] -set_property IOSTANDARD LVCMOS33 [get_ports rst] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[4]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[5]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[6]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[7]] diff --git a/fpga_interchange/examples/tests/counter/arty35t.xdc b/fpga_interchange/examples/tests/counter/arty35t.xdc deleted file mode 100644 index 22c325ae..00000000 --- a/fpga_interchange/examples/tests/counter/arty35t.xdc +++ /dev/null @@ -1,14 +0,0 @@ -## arty-35t board -set_property PACKAGE_PIN E3 [get_ports clk] -set_property PACKAGE_PIN D9 [get_ports rst] -set_property PACKAGE_PIN H5 [get_ports io_led[4]] -set_property PACKAGE_PIN J5 [get_ports io_led[5]] -set_property PACKAGE_PIN T9 [get_ports io_led[6]] -set_property PACKAGE_PIN T10 [get_ports io_led[7]] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] -set_property IOSTANDARD LVCMOS33 [get_ports rst] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[4]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[5]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[6]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[7]] diff --git a/fpga_interchange/examples/tests/counter/basys3.xdc b/fpga_interchange/examples/tests/counter/basys3.xdc deleted file mode 100644 index 09446b5f..00000000 --- a/fpga_interchange/examples/tests/counter/basys3.xdc +++ /dev/null @@ -1,14 +0,0 @@ -## basys3 breakout board -set_property PACKAGE_PIN W5 [get_ports clk] -set_property PACKAGE_PIN V17 [get_ports rst] -set_property PACKAGE_PIN U16 [get_ports io_led[4]] -set_property PACKAGE_PIN E19 [get_ports io_led[5]] -set_property PACKAGE_PIN U19 [get_ports io_led[6]] -set_property PACKAGE_PIN V19 [get_ports io_led[7]] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] -set_property IOSTANDARD LVCMOS33 [get_ports rst] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[4]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[5]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[6]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[7]] diff --git a/fpga_interchange/examples/tests/counter/counter.v b/fpga_interchange/examples/tests/counter/counter.v deleted file mode 100644 index 4b3f343b..00000000 --- a/fpga_interchange/examples/tests/counter/counter.v +++ /dev/null @@ -1,17 +0,0 @@ -module top(input clk, input rst, output [7:4] io_led); - -localparam SIZE = 32; - -reg [SIZE-1:0] counter = SIZE'b0; - -assign io_led = {counter[SIZE-1], counter[25:23]}; - -always @(posedge clk) -begin - if(rst) - counter <= SIZE'b0; - else - counter <= counter + 1; -end - -endmodule diff --git a/fpga_interchange/examples/tests/counter/lifcl17.xdc b/fpga_interchange/examples/tests/counter/lifcl17.xdc deleted file mode 100644 index 2031964e..00000000 --- a/fpga_interchange/examples/tests/counter/lifcl17.xdc +++ /dev/null @@ -1,14 +0,0 @@ -## lifcl17 pins for testing based on breakout board -set_property PACKAGE_PIN 55 [get_ports clk] -set_property PACKAGE_PIN 57 [get_ports rst] -set_property PACKAGE_PIN 56 [get_ports io_led[4]] -set_property PACKAGE_PIN 59 [get_ports io_led[5]] -set_property PACKAGE_PIN 60 [get_ports io_led[6]] -set_property PACKAGE_PIN 61 [get_ports io_led[7]] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] -set_property IOSTANDARD LVCMOS33 [get_ports rst] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[4]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[5]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[6]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[7]] diff --git a/fpga_interchange/examples/tests/counter/lifcl40evn.xdc b/fpga_interchange/examples/tests/counter/lifcl40evn.xdc deleted file mode 100644 index 4f378aef..00000000 --- a/fpga_interchange/examples/tests/counter/lifcl40evn.xdc +++ /dev/null @@ -1,13 +0,0 @@ -set_property PACKAGE_PIN L13 [get_ports clk] -set_property PACKAGE_PIN G19 [get_ports rst] -set_property PACKAGE_PIN E17 [get_ports io_led[4]] -set_property PACKAGE_PIN F13 [get_ports io_led[5]] -set_property PACKAGE_PIN G13 [get_ports io_led[6]] -set_property PACKAGE_PIN F14 [get_ports io_led[7]] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] -set_property IOSTANDARD LVCMOS33 [get_ports rst] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[4]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[5]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[6]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[7]] diff --git a/fpga_interchange/examples/tests/counter/run_nexus.tcl b/fpga_interchange/examples/tests/counter/run_nexus.tcl deleted file mode 100644 index 80150571..00000000 --- a/fpga_interchange/examples/tests/counter/run_nexus.tcl +++ /dev/null @@ -1,15 +0,0 @@ -yosys -import - -read_verilog $::env(SOURCES) - -synth_nexus -nolutram -nowidelut -nobram -noccu2 -nodsp -techmap -max_iter 1 -map $::env(TECHMAP) - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/counter/run_xilinx.tcl b/fpga_interchange/examples/tests/counter/run_xilinx.tcl deleted file mode 100644 index c02cf933..00000000 --- a/fpga_interchange/examples/tests/counter/run_xilinx.tcl +++ /dev/null @@ -1,15 +0,0 @@ -yosys -import - -read_verilog $::env(SOURCES) - -synth_xilinx -nolutram -nowidelut -nosrl -nodsp -techmap -map $::env(TECHMAP) - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/counter/zybo.xdc b/fpga_interchange/examples/tests/counter/zybo.xdc deleted file mode 100644 index e7764d52..00000000 --- a/fpga_interchange/examples/tests/counter/zybo.xdc +++ /dev/null @@ -1,14 +0,0 @@ -# zybo board -set_property PACKAGE_PIN K17 [get_ports clk] -set_property PACKAGE_PIN K18 [get_ports rst] -set_property PACKAGE_PIN M14 [get_ports io_led[4]] -set_property PACKAGE_PIN M15 [get_ports io_led[5]] -set_property PACKAGE_PIN G14 [get_ports io_led[6]] -set_property PACKAGE_PIN D18 [get_ports io_led[7]] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] -set_property IOSTANDARD LVCMOS33 [get_ports rst] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[4]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[5]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[6]] -set_property IOSTANDARD LVCMOS33 [get_ports io_led[7]] diff --git a/fpga_interchange/examples/tests/ff/CMakeLists.txt b/fpga_interchange/examples/tests/ff/CMakeLists.txt deleted file mode 100644 index e119b7c3..00000000 --- a/fpga_interchange/examples/tests/ff/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -add_interchange_group_test( - name ff - family ${family} - board_list basys3 arty35t arty100t - tcl run.tcl - sources ff.v - output_fasm -) diff --git a/fpga_interchange/examples/tests/ff/arty100t.xdc b/fpga_interchange/examples/tests/ff/arty100t.xdc deleted file mode 100644 index 29456f2a..00000000 --- a/fpga_interchange/examples/tests/ff/arty100t.xdc +++ /dev/null @@ -1,9 +0,0 @@ -set_property PACKAGE_PIN E3 [get_ports clk] -set_property PACKAGE_PIN A8 [get_ports d] -set_property PACKAGE_PIN D9 [get_ports r] -set_property PACKAGE_PIN H5 [get_ports q] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] -set_property IOSTANDARD LVCMOS33 [get_ports d] -set_property IOSTANDARD LVCMOS33 [get_ports r] -set_property IOSTANDARD LVCMOS33 [get_ports q] diff --git a/fpga_interchange/examples/tests/ff/arty35t.xdc b/fpga_interchange/examples/tests/ff/arty35t.xdc deleted file mode 100644 index 29456f2a..00000000 --- a/fpga_interchange/examples/tests/ff/arty35t.xdc +++ /dev/null @@ -1,9 +0,0 @@ -set_property PACKAGE_PIN E3 [get_ports clk] -set_property PACKAGE_PIN A8 [get_ports d] -set_property PACKAGE_PIN D9 [get_ports r] -set_property PACKAGE_PIN H5 [get_ports q] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] -set_property IOSTANDARD LVCMOS33 [get_ports d] -set_property IOSTANDARD LVCMOS33 [get_ports r] -set_property IOSTANDARD LVCMOS33 [get_ports q] diff --git a/fpga_interchange/examples/tests/ff/basys3.xdc b/fpga_interchange/examples/tests/ff/basys3.xdc deleted file mode 100644 index ef65112a..00000000 --- a/fpga_interchange/examples/tests/ff/basys3.xdc +++ /dev/null @@ -1,9 +0,0 @@ -set_property PACKAGE_PIN W5 [get_ports clk] -set_property PACKAGE_PIN U16 [get_ports d] -set_property PACKAGE_PIN E19 [get_ports r] -set_property PACKAGE_PIN U19 [get_ports q] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] -set_property IOSTANDARD LVCMOS33 [get_ports d] -set_property IOSTANDARD LVCMOS33 [get_ports r] -set_property IOSTANDARD LVCMOS33 [get_ports q] diff --git a/fpga_interchange/examples/tests/ff/ff.v b/fpga_interchange/examples/tests/ff/ff.v deleted file mode 100644 index 1c271042..00000000 --- a/fpga_interchange/examples/tests/ff/ff.v +++ /dev/null @@ -1,11 +0,0 @@ -module top(input clk, input d, input r, output reg q); - -always @(posedge clk) -begin - if(r) - q <= 1'b0; - else - q <= d; -end - -endmodule diff --git a/fpga_interchange/examples/tests/ff/run.tcl b/fpga_interchange/examples/tests/ff/run.tcl deleted file mode 100644 index b8d0df72..00000000 --- a/fpga_interchange/examples/tests/ff/run.tcl +++ /dev/null @@ -1,14 +0,0 @@ -yosys -import - -read_verilog $::env(SOURCES) - -synth_xilinx -nolutram -nowidelut -nosrl -nocarry -nodsp - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/lut/CMakeLists.txt b/fpga_interchange/examples/tests/lut/CMakeLists.txt deleted file mode 100644 index 77a4b4da..00000000 --- a/fpga_interchange/examples/tests/lut/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -add_interchange_group_test( - name lut - family ${family} - board_list basys3 arty35t arty100t - tcl run.tcl - sources lut.v - output_fasm -) diff --git a/fpga_interchange/examples/tests/lut/arty100t.xdc b/fpga_interchange/examples/tests/lut/arty100t.xdc deleted file mode 100644 index 1dba6574..00000000 --- a/fpga_interchange/examples/tests/lut/arty100t.xdc +++ /dev/null @@ -1,7 +0,0 @@ -set_property PACKAGE_PIN A8 [get_ports i0] -set_property PACKAGE_PIN C11 [get_ports i1] -set_property PACKAGE_PIN H5 [get_ports o] - -set_property IOSTANDARD LVCMOS33 [get_ports i0] -set_property IOSTANDARD LVCMOS33 [get_ports i1] -set_property IOSTANDARD LVCMOS33 [get_ports o] diff --git a/fpga_interchange/examples/tests/lut/arty35t.xdc b/fpga_interchange/examples/tests/lut/arty35t.xdc deleted file mode 100644 index 1dba6574..00000000 --- a/fpga_interchange/examples/tests/lut/arty35t.xdc +++ /dev/null @@ -1,7 +0,0 @@ -set_property PACKAGE_PIN A8 [get_ports i0] -set_property PACKAGE_PIN C11 [get_ports i1] -set_property PACKAGE_PIN H5 [get_ports o] - -set_property IOSTANDARD LVCMOS33 [get_ports i0] -set_property IOSTANDARD LVCMOS33 [get_ports i1] -set_property IOSTANDARD LVCMOS33 [get_ports o] diff --git a/fpga_interchange/examples/tests/lut/basys3.xdc b/fpga_interchange/examples/tests/lut/basys3.xdc deleted file mode 100644 index aef287ee..00000000 --- a/fpga_interchange/examples/tests/lut/basys3.xdc +++ /dev/null @@ -1,7 +0,0 @@ -set_property PACKAGE_PIN V17 [get_ports i0] -set_property PACKAGE_PIN V16 [get_ports i1] -set_property PACKAGE_PIN U16 [get_ports o] - -set_property IOSTANDARD LVCMOS33 [get_ports i0] -set_property IOSTANDARD LVCMOS33 [get_ports i1] -set_property IOSTANDARD LVCMOS33 [get_ports o] diff --git a/fpga_interchange/examples/tests/lut/lut.v b/fpga_interchange/examples/tests/lut/lut.v deleted file mode 100644 index ca18e665..00000000 --- a/fpga_interchange/examples/tests/lut/lut.v +++ /dev/null @@ -1,5 +0,0 @@ -module top(input i0, input i1, output o); - -assign o = i0 | i1; - -endmodule diff --git a/fpga_interchange/examples/tests/lut/run.tcl b/fpga_interchange/examples/tests/lut/run.tcl deleted file mode 100644 index b8d0df72..00000000 --- a/fpga_interchange/examples/tests/lut/run.tcl +++ /dev/null @@ -1,14 +0,0 @@ -yosys -import - -read_verilog $::env(SOURCES) - -synth_xilinx -nolutram -nowidelut -nosrl -nocarry -nodsp - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/lut_nexus/CMakeLists.txt b/fpga_interchange/examples/tests/lut_nexus/CMakeLists.txt deleted file mode 100644 index 7ca36343..00000000 --- a/fpga_interchange/examples/tests/lut_nexus/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -add_interchange_test( - name lut_nexus - family ${family} - device LIFCL-17 - package QFN72 - tcl run.tcl - xdc empty.xdc - sources lut.v - skip_dcp -) - -add_interchange_test( - name lut_nexus40 - family ${family} - device LIFCL-40 - package CABGA400 - tcl run.tcl - xdc empty.xdc - sources lut.v - skip_dcp -) diff --git a/fpga_interchange/examples/tests/lut_nexus/empty.xdc b/fpga_interchange/examples/tests/lut_nexus/empty.xdc deleted file mode 100644 index e69de29b..00000000 diff --git a/fpga_interchange/examples/tests/lut_nexus/lut.v b/fpga_interchange/examples/tests/lut_nexus/lut.v deleted file mode 100644 index 5913aff1..00000000 --- a/fpga_interchange/examples/tests/lut_nexus/lut.v +++ /dev/null @@ -1,7 +0,0 @@ -module top; - wire x, y; - (*keep*) - LUT4 lut_0(.A(x), .B(x), .C(x), .D(x), .Z(y)); - (*keep*) - LUT4 lut_1(.A(y), .B(y), .C(y), .D(y), .Z(x)); -endmodule \ No newline at end of file diff --git a/fpga_interchange/examples/tests/lut_nexus/run.tcl b/fpga_interchange/examples/tests/lut_nexus/run.tcl deleted file mode 100644 index 4aa56c13..00000000 --- a/fpga_interchange/examples/tests/lut_nexus/run.tcl +++ /dev/null @@ -1,14 +0,0 @@ -yosys -import - -read_verilog $::env(SOURCES) - -synth_nexus -noccu2 -nobram -nolutram -nowidelut - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/lutram/CMakeLists.txt b/fpga_interchange/examples/tests/lutram/CMakeLists.txt deleted file mode 100644 index 0d45e8f8..00000000 --- a/fpga_interchange/examples/tests/lutram/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -add_interchange_group_test( - name lutram - family ${family} - board_list basys3 - tcl run.tcl - sources lutram.v -) - diff --git a/fpga_interchange/examples/tests/lutram/basys3.pcf b/fpga_interchange/examples/tests/lutram/basys3.pcf deleted file mode 100644 index cb4191cc..00000000 --- a/fpga_interchange/examples/tests/lutram/basys3.pcf +++ /dev/null @@ -1,41 +0,0 @@ -# basys3 100 MHz CLK -set_io clk W5 - -set_io tx A18 -set_io rx B18 -# -# in[0:15] correspond with SW0-SW15 on the basys3 -set_io sw[0] V17 -set_io sw[1] V16 -set_io sw[2] W16 -set_io sw[3] W17 -set_io sw[4] W15 -set_io sw[5] V15 -set_io sw[6] W14 -set_io sw[7] W13 -set_io sw[8] V2 -set_io sw[9] T3 -set_io sw[10] T2 -set_io sw[11] R3 -set_io sw[12] W2 -set_io sw[13] U1 -set_io sw[14] T1 -set_io sw[15] R2 - -# out[0:15] correspond with LD0-LD15 on the basys3 -set_io led[0] U16 -set_io led[1] E19 -set_io led[2] U19 -set_io led[3] V19 -set_io led[4] W18 -set_io led[5] U15 -set_io led[6] U14 -set_io led[7] V14 -set_io led[8] V13 -set_io led[9] V3 -set_io led[10] W3 -set_io led[11] U3 -set_io led[12] P3 -set_io led[13] N3 -set_io led[14] P1 -set_io led[15] L1 diff --git a/fpga_interchange/examples/tests/lutram/basys3.xdc b/fpga_interchange/examples/tests/lutram/basys3.xdc deleted file mode 100644 index 58e1859c..00000000 --- a/fpga_interchange/examples/tests/lutram/basys3.xdc +++ /dev/null @@ -1,80 +0,0 @@ -# basys3 100 MHz CLK -set_property PACKAGE_PIN W5 [get_ports clk] - -set_property PACKAGE_PIN A18 [get_ports tx] -set_property PACKAGE_PIN B18 [get_ports rx] -# -# in[0:15] correspond with SW0-SW15 on the basys3 -set_property PACKAGE_PIN V17 [get_ports sw[0]] -set_property PACKAGE_PIN V16 [get_ports sw[1]] -set_property PACKAGE_PIN W16 [get_ports sw[2]] -set_property PACKAGE_PIN W17 [get_ports sw[3]] -set_property PACKAGE_PIN W15 [get_ports sw[4]] -set_property PACKAGE_PIN V15 [get_ports sw[5]] -set_property PACKAGE_PIN W14 [get_ports sw[6]] -set_property PACKAGE_PIN W13 [get_ports sw[7]] -set_property PACKAGE_PIN V2 [get_ports sw[8]] -set_property PACKAGE_PIN T3 [get_ports sw[9]] -set_property PACKAGE_PIN T2 [get_ports sw[10]] -set_property PACKAGE_PIN R3 [get_ports sw[11]] -set_property PACKAGE_PIN W2 [get_ports sw[12]] -set_property PACKAGE_PIN U1 [get_ports sw[13]] -set_property PACKAGE_PIN T1 [get_ports sw[14]] -set_property PACKAGE_PIN R2 [get_ports sw[15]] - -# out[0:15] correspond with LD0-LD15 on the basys3 -set_property PACKAGE_PIN U16 [get_ports led[0]] -set_property PACKAGE_PIN E19 [get_ports led[1]] -set_property PACKAGE_PIN U19 [get_ports led[2]] -set_property PACKAGE_PIN V19 [get_ports led[3]] -set_property PACKAGE_PIN W18 [get_ports led[4]] -set_property PACKAGE_PIN U15 [get_ports led[5]] -set_property PACKAGE_PIN U14 [get_ports led[6]] -set_property PACKAGE_PIN V14 [get_ports led[7]] -set_property PACKAGE_PIN V13 [get_ports led[8]] -set_property PACKAGE_PIN V3 [get_ports led[9]] -set_property PACKAGE_PIN W3 [get_ports led[10]] -set_property PACKAGE_PIN U3 [get_ports led[11]] -set_property PACKAGE_PIN P3 [get_ports led[12]] -set_property PACKAGE_PIN N3 [get_ports led[13]] -set_property PACKAGE_PIN P1 [get_ports led[14]] -set_property PACKAGE_PIN L1 [get_ports led[15]] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] - -set_property IOSTANDARD LVCMOS33 [get_ports tx] -set_property IOSTANDARD LVCMOS33 [get_ports rx] -# -set_property IOSTANDARD LVCMOS33 [get_ports sw[0]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[1]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[2]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[3]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[4]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[5]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[6]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[7]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[8]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[9]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[10]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[11]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[12]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[13]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[14]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[15]] - -set_property IOSTANDARD LVCMOS33 [get_ports led[0]] -set_property IOSTANDARD LVCMOS33 [get_ports led[1]] -set_property IOSTANDARD LVCMOS33 [get_ports led[2]] -set_property IOSTANDARD LVCMOS33 [get_ports led[3]] -set_property IOSTANDARD LVCMOS33 [get_ports led[4]] -set_property IOSTANDARD LVCMOS33 [get_ports led[5]] -set_property IOSTANDARD LVCMOS33 [get_ports led[6]] -set_property IOSTANDARD LVCMOS33 [get_ports led[7]] -set_property IOSTANDARD LVCMOS33 [get_ports led[8]] -set_property IOSTANDARD LVCMOS33 [get_ports led[9]] -set_property IOSTANDARD LVCMOS33 [get_ports led[10]] -set_property IOSTANDARD LVCMOS33 [get_ports led[11]] -set_property IOSTANDARD LVCMOS33 [get_ports led[12]] -set_property IOSTANDARD LVCMOS33 [get_ports led[13]] -set_property IOSTANDARD LVCMOS33 [get_ports led[14]] -set_property IOSTANDARD LVCMOS33 [get_ports led[15]] diff --git a/fpga_interchange/examples/tests/lutram/lutram.v b/fpga_interchange/examples/tests/lutram/lutram.v deleted file mode 100644 index f38197d4..00000000 --- a/fpga_interchange/examples/tests/lutram/lutram.v +++ /dev/null @@ -1,24 +0,0 @@ -module top ( - input wire clk, - - input wire rx, - output wire tx, - - input wire [15:0] sw, - output wire [15:0] led -); - RAM128X1D #( - .INIT(128'hFFEEDDCCBBAA99887766554433221100) - ) ram_i ( - .WCLK(clk), - .A(sw[6:0]), - .DPRA(sw[13:7]), - .WE(sw[14]), - .D(sw[15]), - .SPO(led[0]), - .DPO(led[1]), - ); - - assign led[15:2] = 14'b0; - assign tx = rx; -endmodule diff --git a/fpga_interchange/examples/tests/lutram/run.tcl b/fpga_interchange/examples/tests/lutram/run.tcl deleted file mode 100644 index 79321139..00000000 --- a/fpga_interchange/examples/tests/lutram/run.tcl +++ /dev/null @@ -1,17 +0,0 @@ -yosys -import - -foreach src $::env(SOURCES) { - read_verilog $src -} - -synth_xilinx -flatten -nolutram -nowidelut -nosrl -nocarry -nodsp -techmap -map $::env(TECHMAP) - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/obuftds/CMakeLists.txt b/fpga_interchange/examples/tests/obuftds/CMakeLists.txt deleted file mode 100644 index 0313c9bb..00000000 --- a/fpga_interchange/examples/tests/obuftds/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_interchange_group_test( - name obuftds - family ${family} - board_list basys3 - tcl run.tcl - sources obuftds.v -) diff --git a/fpga_interchange/examples/tests/obuftds/basys3.xdc b/fpga_interchange/examples/tests/obuftds/basys3.xdc deleted file mode 100644 index 4b777233..00000000 --- a/fpga_interchange/examples/tests/obuftds/basys3.xdc +++ /dev/null @@ -1,9 +0,0 @@ -set_property PACKAGE_PIN V2 [get_ports sw[8] ] -set_property PACKAGE_PIN T3 [get_ports sw[9] ] -set_property PACKAGE_PIN T2 [get_ports sw[10]] -set_property PACKAGE_PIN R3 [get_ports sw[11]] - -set_property PACKAGE_PIN U19 [get_ports diff_p[0]] -set_property PACKAGE_PIN V19 [get_ports diff_n[0]] -set_property PACKAGE_PIN V13 [get_ports diff_p[1]] -set_property PACKAGE_PIN V14 [get_ports diff_n[1]] diff --git a/fpga_interchange/examples/tests/obuftds/obuftds.v b/fpga_interchange/examples/tests/obuftds/obuftds.v deleted file mode 100644 index d4e9a603..00000000 --- a/fpga_interchange/examples/tests/obuftds/obuftds.v +++ /dev/null @@ -1,37 +0,0 @@ -module top( - input wire [11:8] sw, - - output wire [1:0] diff_p, - output wire [1:0] diff_n -); - -wire [1:0] buf_i; -wire [1:0] buf_t; - -OBUFTDS # ( - .IOSTANDARD("DIFF_SSTL135"), - .SLEW("FAST") -) obuftds_0 ( - .I(buf_i[0]), - .T(buf_t[0]), - .O(diff_p[0]), - .OB(diff_n[0]) -); - -OBUFTDS # ( - .IOSTANDARD("DIFF_SSTL135"), - .SLEW("FAST") -) obuftds_1 ( - .I(buf_i[1]), - .T(buf_t[1]), - .O(diff_p[1]), - .OB(diff_n[1]) -); - -assign buf_i[0] = sw[ 8]; -assign buf_t[0] = sw[ 9]; -assign buf_i[1] = sw[10]; -assign buf_t[1] = sw[11]; - -endmodule - diff --git a/fpga_interchange/examples/tests/obuftds/run.tcl b/fpga_interchange/examples/tests/obuftds/run.tcl deleted file mode 100644 index b8d0df72..00000000 --- a/fpga_interchange/examples/tests/obuftds/run.tcl +++ /dev/null @@ -1,14 +0,0 @@ -yosys -import - -read_verilog $::env(SOURCES) - -synth_xilinx -nolutram -nowidelut -nosrl -nocarry -nodsp - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/ram/CMakeLists.txt b/fpga_interchange/examples/tests/ram/CMakeLists.txt deleted file mode 100644 index 56db4870..00000000 --- a/fpga_interchange/examples/tests/ram/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -add_interchange_group_test( - name ram - family ${family} - board_list basys3 - tcl run.tcl - sources ram.v -) - diff --git a/fpga_interchange/examples/tests/ram/basys3.pcf b/fpga_interchange/examples/tests/ram/basys3.pcf deleted file mode 100644 index cb4191cc..00000000 --- a/fpga_interchange/examples/tests/ram/basys3.pcf +++ /dev/null @@ -1,41 +0,0 @@ -# basys3 100 MHz CLK -set_io clk W5 - -set_io tx A18 -set_io rx B18 -# -# in[0:15] correspond with SW0-SW15 on the basys3 -set_io sw[0] V17 -set_io sw[1] V16 -set_io sw[2] W16 -set_io sw[3] W17 -set_io sw[4] W15 -set_io sw[5] V15 -set_io sw[6] W14 -set_io sw[7] W13 -set_io sw[8] V2 -set_io sw[9] T3 -set_io sw[10] T2 -set_io sw[11] R3 -set_io sw[12] W2 -set_io sw[13] U1 -set_io sw[14] T1 -set_io sw[15] R2 - -# out[0:15] correspond with LD0-LD15 on the basys3 -set_io led[0] U16 -set_io led[1] E19 -set_io led[2] U19 -set_io led[3] V19 -set_io led[4] W18 -set_io led[5] U15 -set_io led[6] U14 -set_io led[7] V14 -set_io led[8] V13 -set_io led[9] V3 -set_io led[10] W3 -set_io led[11] U3 -set_io led[12] P3 -set_io led[13] N3 -set_io led[14] P1 -set_io led[15] L1 diff --git a/fpga_interchange/examples/tests/ram/basys3.xdc b/fpga_interchange/examples/tests/ram/basys3.xdc deleted file mode 100644 index 58e1859c..00000000 --- a/fpga_interchange/examples/tests/ram/basys3.xdc +++ /dev/null @@ -1,80 +0,0 @@ -# basys3 100 MHz CLK -set_property PACKAGE_PIN W5 [get_ports clk] - -set_property PACKAGE_PIN A18 [get_ports tx] -set_property PACKAGE_PIN B18 [get_ports rx] -# -# in[0:15] correspond with SW0-SW15 on the basys3 -set_property PACKAGE_PIN V17 [get_ports sw[0]] -set_property PACKAGE_PIN V16 [get_ports sw[1]] -set_property PACKAGE_PIN W16 [get_ports sw[2]] -set_property PACKAGE_PIN W17 [get_ports sw[3]] -set_property PACKAGE_PIN W15 [get_ports sw[4]] -set_property PACKAGE_PIN V15 [get_ports sw[5]] -set_property PACKAGE_PIN W14 [get_ports sw[6]] -set_property PACKAGE_PIN W13 [get_ports sw[7]] -set_property PACKAGE_PIN V2 [get_ports sw[8]] -set_property PACKAGE_PIN T3 [get_ports sw[9]] -set_property PACKAGE_PIN T2 [get_ports sw[10]] -set_property PACKAGE_PIN R3 [get_ports sw[11]] -set_property PACKAGE_PIN W2 [get_ports sw[12]] -set_property PACKAGE_PIN U1 [get_ports sw[13]] -set_property PACKAGE_PIN T1 [get_ports sw[14]] -set_property PACKAGE_PIN R2 [get_ports sw[15]] - -# out[0:15] correspond with LD0-LD15 on the basys3 -set_property PACKAGE_PIN U16 [get_ports led[0]] -set_property PACKAGE_PIN E19 [get_ports led[1]] -set_property PACKAGE_PIN U19 [get_ports led[2]] -set_property PACKAGE_PIN V19 [get_ports led[3]] -set_property PACKAGE_PIN W18 [get_ports led[4]] -set_property PACKAGE_PIN U15 [get_ports led[5]] -set_property PACKAGE_PIN U14 [get_ports led[6]] -set_property PACKAGE_PIN V14 [get_ports led[7]] -set_property PACKAGE_PIN V13 [get_ports led[8]] -set_property PACKAGE_PIN V3 [get_ports led[9]] -set_property PACKAGE_PIN W3 [get_ports led[10]] -set_property PACKAGE_PIN U3 [get_ports led[11]] -set_property PACKAGE_PIN P3 [get_ports led[12]] -set_property PACKAGE_PIN N3 [get_ports led[13]] -set_property PACKAGE_PIN P1 [get_ports led[14]] -set_property PACKAGE_PIN L1 [get_ports led[15]] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] - -set_property IOSTANDARD LVCMOS33 [get_ports tx] -set_property IOSTANDARD LVCMOS33 [get_ports rx] -# -set_property IOSTANDARD LVCMOS33 [get_ports sw[0]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[1]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[2]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[3]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[4]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[5]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[6]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[7]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[8]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[9]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[10]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[11]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[12]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[13]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[14]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[15]] - -set_property IOSTANDARD LVCMOS33 [get_ports led[0]] -set_property IOSTANDARD LVCMOS33 [get_ports led[1]] -set_property IOSTANDARD LVCMOS33 [get_ports led[2]] -set_property IOSTANDARD LVCMOS33 [get_ports led[3]] -set_property IOSTANDARD LVCMOS33 [get_ports led[4]] -set_property IOSTANDARD LVCMOS33 [get_ports led[5]] -set_property IOSTANDARD LVCMOS33 [get_ports led[6]] -set_property IOSTANDARD LVCMOS33 [get_ports led[7]] -set_property IOSTANDARD LVCMOS33 [get_ports led[8]] -set_property IOSTANDARD LVCMOS33 [get_ports led[9]] -set_property IOSTANDARD LVCMOS33 [get_ports led[10]] -set_property IOSTANDARD LVCMOS33 [get_ports led[11]] -set_property IOSTANDARD LVCMOS33 [get_ports led[12]] -set_property IOSTANDARD LVCMOS33 [get_ports led[13]] -set_property IOSTANDARD LVCMOS33 [get_ports led[14]] -set_property IOSTANDARD LVCMOS33 [get_ports led[15]] diff --git a/fpga_interchange/examples/tests/ram/ram.v b/fpga_interchange/examples/tests/ram/ram.v deleted file mode 100644 index aec5c4d1..00000000 --- a/fpga_interchange/examples/tests/ram/ram.v +++ /dev/null @@ -1,134 +0,0 @@ -module ram0( - // Write port - input wrclk, - input [15:0] di, - input wren, - input [9:0] wraddr, - // Read port - input rdclk, - input rden, - input [9:0] rdaddr, - output reg [15:0] do); - - (* ram_style = "block" *) reg [15:0] ram[0:1023]; - - initial begin - ram[0] = 16'b00000000_00000001; - ram[1] = 16'b10101010_10101010; - ram[2] = 16'b01010101_01010101; - ram[3] = 16'b11111111_11111111; - ram[4] = 16'b11110000_11110000; - ram[5] = 16'b00001111_00001111; - ram[6] = 16'b11001100_11001100; - ram[7] = 16'b00110011_00110011; - ram[8] = 16'b00000000_00000010; - ram[9] = 16'b00000000_00000100; - end - - always @ (posedge wrclk) begin - if(wren == 1) begin - ram[wraddr] <= di; - end - end - - always @ (posedge rdclk) begin - if(rden == 1) begin - do <= ram[rdaddr]; - end - end - -endmodule - -module top ( - input wire clk, - - input wire rx, - output wire tx, - - input wire [15:0] sw, - output wire [15:0] led -); - wire rden; - reg wren; - wire [9:0] rdaddr; - wire [9:0] wraddr; - wire [15:0] di; - wire [15:0] do; - ram0 ram( - .wrclk(clk), - .di(di), - .wren(wren), - .wraddr(wraddr), - .rdclk(clk), - .rden(rden), - .rdaddr(rdaddr), - .do(do) - ); - - reg [9:0] address_reg; - reg [15:0] data_reg; - reg [15:0] out_reg; - - assign rdaddr = address_reg; - assign wraddr = address_reg; - - // display_mode == 00 -> ram[address_reg] - // display_mode == 01 -> address_reg - // display_mode == 10 -> data_reg - wire [1:0] display_mode; - - // input_mode == 00 -> in[9:0] -> address_reg - // input_mode == 01 -> in[7:0] -> data_reg[7:0] - // input_mode == 10 -> in[7:0] -> data_reg[15:8] - // input_mode == 11 -> data_reg -> ram[address_reg] - wire [1:0] input_mode; - - // WE == 0 -> address_reg and data_reg unchanged. - // WE == 1 -> address_reg or data_reg is updated because on input_mode. - wire we; - - assign display_mode[0] = sw[14]; - assign display_mode[1] = sw[15]; - - assign input_mode[0] = sw[12]; - assign input_mode[1] = sw[13]; - - assign we = sw[11]; - assign led = out_reg; - assign di = data_reg; - assign rden = 1; - - initial begin - address_reg = 10'b0; - data_reg = 16'b0; - out_reg = 16'b0; - end - - always @ (posedge clk) begin - if(display_mode == 0) begin - out_reg <= do; - end else if(display_mode == 1) begin - out_reg <= address_reg; - end else if(display_mode == 2) begin - out_reg <= data_reg; - end - - if(we == 1) begin - if(input_mode == 0) begin - address_reg <= sw[9:0]; - wren <= 0; - end else if(input_mode == 1) begin - data_reg[7:0] <= sw[7:0]; - wren <= 0; - end else if(input_mode == 2) begin - data_reg[15:8] <= sw[7:0]; - wren <= 0; - end else if(input_mode == 3) begin - wren <= 1; - end - end - end - - // Uart loopback - assign tx = rx; -endmodule diff --git a/fpga_interchange/examples/tests/ram/run.tcl b/fpga_interchange/examples/tests/ram/run.tcl deleted file mode 100644 index 79321139..00000000 --- a/fpga_interchange/examples/tests/ram/run.tcl +++ /dev/null @@ -1,17 +0,0 @@ -yosys -import - -foreach src $::env(SOURCES) { - read_verilog $src -} - -synth_xilinx -flatten -nolutram -nowidelut -nosrl -nocarry -nodsp -techmap -map $::env(TECHMAP) - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/ram_nexus/CMakeLists.txt b/fpga_interchange/examples/tests/ram_nexus/CMakeLists.txt deleted file mode 100644 index 49d976c5..00000000 --- a/fpga_interchange/examples/tests/ram_nexus/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -add_interchange_group_test( - name ram_nexus - family ${family} - board_list lifcl40evn - tcl run_nexus.tcl - sources ram_nexus.v - techmap ../../remap_nexus.v - skip_dcp -) - diff --git a/fpga_interchange/examples/tests/ram_nexus/lifcl40evn.xdc b/fpga_interchange/examples/tests/ram_nexus/lifcl40evn.xdc deleted file mode 100644 index 2fb0b3a2..00000000 --- a/fpga_interchange/examples/tests/ram_nexus/lifcl40evn.xdc +++ /dev/null @@ -1,57 +0,0 @@ -set_property PACKAGE_PIN L13 [get_ports clk] - -set_property PACKAGE_PIN E17 [get_ports led[0]] -set_property PACKAGE_PIN F13 [get_ports led[1]] -set_property PACKAGE_PIN G13 [get_ports led[2]] -set_property PACKAGE_PIN F14 [get_ports led[3]] -set_property PACKAGE_PIN L16 [get_ports led[4]] -set_property PACKAGE_PIN L15 [get_ports led[5]] -set_property PACKAGE_PIN L20 [get_ports led[6]] -set_property PACKAGE_PIN L19 [get_ports led[7]] -set_property PACKAGE_PIN R17 [get_ports led[8]] -set_property PACKAGE_PIN R18 [get_ports led[9]] -set_property PACKAGE_PIN U20 [get_ports led[10]] -set_property PACKAGE_PIN T20 [get_ports led[11]] -set_property PACKAGE_PIN W20 [get_ports led[12]] -set_property PACKAGE_PIN V20 [get_ports led[13]] - -set_property PACKAGE_PIN G14 [get_ports pb0] -set_property PACKAGE_PIN G15 [get_ports pb1] - -set_property PACKAGE_PIN N14 [get_ports sw[0]] -set_property PACKAGE_PIN M14 [get_ports sw[1]] -set_property PACKAGE_PIN M16 [get_ports sw[2]] -set_property PACKAGE_PIN M15 [get_ports sw[3]] -set_property PACKAGE_PIN N15 [get_ports sw[4]] -set_property PACKAGE_PIN N16 [get_ports sw[5]] -set_property PACKAGE_PIN M17 [get_ports sw[6]] -set_property PACKAGE_PIN M18 [get_ports sw[7]] - -set_property IOSTANDARD LVCMOS33 [get_ports clk] - -set_property IOSTANDARD LVCMOS33 [get_ports led[0]] -set_property IOSTANDARD LVCMOS33 [get_ports led[1]] -set_property IOSTANDARD LVCMOS33 [get_ports led[2]] -set_property IOSTANDARD LVCMOS33 [get_ports led[3]] -set_property IOSTANDARD LVCMOS33 [get_ports led[4]] -set_property IOSTANDARD LVCMOS33 [get_ports led[5]] -set_property IOSTANDARD LVCMOS33 [get_ports led[6]] -set_property IOSTANDARD LVCMOS33 [get_ports led[7]] -set_property IOSTANDARD LVCMOS33 [get_ports led[8]] -set_property IOSTANDARD LVCMOS33 [get_ports led[9]] -set_property IOSTANDARD LVCMOS33 [get_ports led[10]] -set_property IOSTANDARD LVCMOS33 [get_ports led[11]] -set_property IOSTANDARD LVCMOS33 [get_ports led[12]] -set_property IOSTANDARD LVCMOS33 [get_ports led[13]] - -set_property IOSTANDARD LVCMOS33 [get_ports pb0] -set_property IOSTANDARD LVCMOS33 [get_ports pb1] - -set_property IOSTANDARD LVCMOS33 [get_ports sw[0]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[1]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[2]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[3]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[4]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[5]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[6]] -set_property IOSTANDARD LVCMOS33 [get_ports sw[7]] \ No newline at end of file diff --git a/fpga_interchange/examples/tests/ram_nexus/ram_nexus.v b/fpga_interchange/examples/tests/ram_nexus/ram_nexus.v deleted file mode 100644 index e91a64bc..00000000 --- a/fpga_interchange/examples/tests/ram_nexus/ram_nexus.v +++ /dev/null @@ -1,123 +0,0 @@ -module ram0( - // Write port - input wrclk, - input [7:0] di, - input wren, - input [5:0] wraddr, - // Read port - input rdclk, - input rden, - input [5:0] rdaddr, - output reg [7:0] do); - - (* syn_ramstyle = "block_ram" *) reg [7:0] ram[0:63]; - - initial begin - ram[0] = 8'b00000001; - ram[1] = 8'b10101010; - ram[2] = 8'b01010101; - ram[3] = 8'b11111111; - ram[4] = 8'b11110000; - ram[5] = 8'b00001111; - ram[6] = 8'b11001100; - ram[7] = 8'b00110011; - ram[8] = 8'b00000010; - ram[9] = 8'b00000100; - end - - always @ (posedge wrclk) begin - if(wren == 1) begin - ram[wraddr] <= di; - end - end - - always @ (posedge rdclk) begin - if(rden == 1) begin - do <= ram[rdaddr]; - end - end - -endmodule - -module top ( - input wire clk, - - input wire pb0, - input wire pb1, - - input wire [7:0] sw, - output wire [13:0] led -); - wire bufclk; - DCC gbuf_i(.CLKI(clk), .CLKO(bufclk)); - - - wire rden; - reg wren; - wire [5:0] rdaddr; - wire [5:0] wraddr; - wire [7:0] di; - wire [7:0] do; - ram0 ram( - .wrclk(bufclk), - .di(di), - .wren(wren), - .wraddr(wraddr), - .rdclk(bufclk), - .rden(rden), - .rdaddr(rdaddr), - .do(do) - ); - - reg [5:0] address_reg; - reg [7:0] data_reg; - reg [7:0] out_reg; - - assign rdaddr = address_reg; - assign wraddr = address_reg; - - // input_mode == 00 -> in[3:0] -> address_reg - // input_mode == 01 -> in[3:0] -> data_reg[3:0] - // input_mode == 10 -> in[3:0] -> data_reg[7:4] - // input_mode == 11 -> data_reg -> ram[address_reg] - wire [1:0] input_mode; - - // WE == 0 -> address_reg and data_reg unchanged. - // WE == 1 -> address_reg or data_reg is updated because on input_mode. - wire we; - - assign input_mode[0] = ~sw[6]; - assign input_mode[1] = ~sw[7]; - - assign we = ~pb0; - assign led = ~{address_reg, out_reg}; - assign di = data_reg; - assign rden = 1; - - initial begin - wren = 1'b0; - address_reg = 10'b0; - data_reg = 16'b0; - out_reg = 16'b0; - end - - always @ (posedge bufclk) begin - out_reg <= do; - - if(we == 1) begin - if(input_mode == 0) begin - address_reg <= ~sw[5:0]; - wren <= 0; - end else if(input_mode == 1) begin - data_reg[3:0] <= ~sw[3:0]; - wren <= 0; - end else if(input_mode == 2) begin - data_reg[7:4] <= ~sw[3:0]; - wren <= 0; - end else if(input_mode == 3) begin - wren <= 1; - end - end - end - -endmodule diff --git a/fpga_interchange/examples/tests/ram_nexus/run_nexus.tcl b/fpga_interchange/examples/tests/ram_nexus/run_nexus.tcl deleted file mode 100644 index 4ea5e282..00000000 --- a/fpga_interchange/examples/tests/ram_nexus/run_nexus.tcl +++ /dev/null @@ -1,15 +0,0 @@ -yosys -import - -read_verilog $::env(SOURCES) - -synth_nexus -nolutram -nowidelut -noccu2 -nodsp -techmap -max_iter 1 -map $::env(TECHMAP) - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/wire/CMakeLists.txt b/fpga_interchange/examples/tests/wire/CMakeLists.txt deleted file mode 100644 index 9caffcd1..00000000 --- a/fpga_interchange/examples/tests/wire/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -add_interchange_group_test( - name wire - family ${family} - board_list basys3 arty35t zybo arty100t nexys_video - tcl run.tcl - sources wire.v - output_fasm -) - -add_interchange_group_test( - name wire - family ${family} - board_list lifcl40evn - tcl run_nexus.tcl - sources wire.v - skip_dcp -) diff --git a/fpga_interchange/examples/tests/wire/arty100t.xdc b/fpga_interchange/examples/tests/wire/arty100t.xdc deleted file mode 100644 index 54c661c9..00000000 --- a/fpga_interchange/examples/tests/wire/arty100t.xdc +++ /dev/null @@ -1,5 +0,0 @@ -set_property PACKAGE_PIN A8 [get_ports i] -set_property PACKAGE_PIN H5 [get_ports o] - -set_property IOSTANDARD LVCMOS33 [get_ports i] -set_property IOSTANDARD LVCMOS33 [get_ports o] diff --git a/fpga_interchange/examples/tests/wire/arty35t.xdc b/fpga_interchange/examples/tests/wire/arty35t.xdc deleted file mode 100644 index 54c661c9..00000000 --- a/fpga_interchange/examples/tests/wire/arty35t.xdc +++ /dev/null @@ -1,5 +0,0 @@ -set_property PACKAGE_PIN A8 [get_ports i] -set_property PACKAGE_PIN H5 [get_ports o] - -set_property IOSTANDARD LVCMOS33 [get_ports i] -set_property IOSTANDARD LVCMOS33 [get_ports o] diff --git a/fpga_interchange/examples/tests/wire/basys3.xdc b/fpga_interchange/examples/tests/wire/basys3.xdc deleted file mode 100644 index 317d5acc..00000000 --- a/fpga_interchange/examples/tests/wire/basys3.xdc +++ /dev/null @@ -1,5 +0,0 @@ -set_property PACKAGE_PIN V17 [get_ports i] -set_property PACKAGE_PIN U16 [get_ports o] - -set_property IOSTANDARD LVCMOS33 [get_ports i] -set_property IOSTANDARD LVCMOS33 [get_ports o] diff --git a/fpga_interchange/examples/tests/wire/lifcl40evn.xdc b/fpga_interchange/examples/tests/wire/lifcl40evn.xdc deleted file mode 100644 index c1a87488..00000000 --- a/fpga_interchange/examples/tests/wire/lifcl40evn.xdc +++ /dev/null @@ -1,5 +0,0 @@ -set_property PACKAGE_PIN G19 [get_ports i] -set_property PACKAGE_PIN E17 [get_ports o] - -set_property IOSTANDARD LVCMOS33 [get_ports i] -set_property IOSTANDARD LVCMOS33 [get_ports o] diff --git a/fpga_interchange/examples/tests/wire/nexys_video.xdc b/fpga_interchange/examples/tests/wire/nexys_video.xdc deleted file mode 100644 index 326f77cb..00000000 --- a/fpga_interchange/examples/tests/wire/nexys_video.xdc +++ /dev/null @@ -1,5 +0,0 @@ -set_property PACKAGE_PIN E22 [get_ports i] -set_property PACKAGE_PIN T14 [get_ports o] - -set_property IOSTANDARD LVCMOS33 [get_ports i] -set_property IOSTANDARD LVCMOS33 [get_ports o] diff --git a/fpga_interchange/examples/tests/wire/run.tcl b/fpga_interchange/examples/tests/wire/run.tcl deleted file mode 100644 index b8d0df72..00000000 --- a/fpga_interchange/examples/tests/wire/run.tcl +++ /dev/null @@ -1,14 +0,0 @@ -yosys -import - -read_verilog $::env(SOURCES) - -synth_xilinx -nolutram -nowidelut -nosrl -nocarry -nodsp - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/wire/run_nexus.tcl b/fpga_interchange/examples/tests/wire/run_nexus.tcl deleted file mode 100644 index cddad3f8..00000000 --- a/fpga_interchange/examples/tests/wire/run_nexus.tcl +++ /dev/null @@ -1,14 +0,0 @@ -yosys -import - -read_verilog $::env(SOURCES) - -synth_nexus -nolutram -nowidelut -nobram -noccu2 -nodsp - -# opt_expr -undriven makes sure all nets are driven, if only by the $undef -# net. -opt_expr -undriven -opt_clean - -setundef -zero -params - -write_json $::env(OUT_JSON) diff --git a/fpga_interchange/examples/tests/wire/wire.v b/fpga_interchange/examples/tests/wire/wire.v deleted file mode 100644 index 429d05ff..00000000 --- a/fpga_interchange/examples/tests/wire/wire.v +++ /dev/null @@ -1,5 +0,0 @@ -module top(input i, output o); - -assign o = i; - -endmodule diff --git a/fpga_interchange/examples/tests/wire/zybo.xdc b/fpga_interchange/examples/tests/wire/zybo.xdc deleted file mode 100644 index 072c19d2..00000000 --- a/fpga_interchange/examples/tests/wire/zybo.xdc +++ /dev/null @@ -1,5 +0,0 @@ -set_property PACKAGE_PIN G15 [get_ports i] -set_property PACKAGE_PIN M14 [get_ports o] - -set_property IOSTANDARD LVCMOS33 [get_ports i] -set_property IOSTANDARD LVCMOS33 [get_ports o] diff --git a/fpga_interchange/family.cmake b/fpga_interchange/family.cmake deleted file mode 100644 index 863ab06e..00000000 --- a/fpga_interchange/family.cmake +++ /dev/null @@ -1,63 +0,0 @@ -find_package(TCL) -if(NOT ${TCL_FOUND}) - message(FATAL_ERROR "Tcl is required for FPGA interchange Arch.") -endif() - -find_package(ZLIB REQUIRED) - -set(RAPIDWRIGHT_PATH $ENV{HOME}/RapidWright CACHE PATH "Path to RapidWright") -set(INVOKE_RAPIDWRIGHT "${RAPIDWRIGHT_PATH}/scripts/invoke_rapidwright.sh" CACHE PATH "Path to RapidWright invocation script") -set(JAVA_HEAP_SPACE "-Xmx8g" CACHE STRING "Heap space reserved for Java") -set(PRJOXIDE_PREFIX $ENV{HOME}/.cargo CACHE PATH "prjoxide install prefix") - -# FIXME: Make patch data available in the python package and remove this cached var -set(PYTHON_INTERCHANGE_PATH $ENV{HOME}/python-fpga-interchange CACHE PATH "Path to the FPGA interchange python library") -set(INTERCHANGE_SCHEMA_PATH ${PROJECT_SOURCE_DIR}/3rdparty/fpga-interchange-schema/interchange CACHE PATH "Path to the FPGA interchange schema dir") - -add_subdirectory(3rdparty/fpga-interchange-schema/cmake/cxx_static) - -include(${family}/examples/chipdb.cmake) -include(${family}/examples/boards.cmake) -include(${family}/examples/tests.cmake) - -set(chipdb_dir ${CMAKE_CURRENT_BINARY_DIR}/${family}/chipdb) -file(MAKE_DIRECTORY ${chipdb_dir}) - -add_custom_target(all-${family}-tests) -add_custom_target(all-${family}-fasm) -add_custom_target(all-${family}-archcheck-tests) -add_subdirectory(${family}/examples/devices) -add_subdirectory(${family}/examples/boards) -add_subdirectory(${family}/examples/tests) - -set(PROTOS lookahead.capnp) -set(CAPNP_SRCS) -set(CAPNP_HDRS) -find_package(CapnProto REQUIRED) -foreach (proto ${PROTOS}) - capnp_generate_cpp(CAPNP_SRC CAPNP_HDR fpga_interchange/${proto}) - list(APPEND CAPNP_HDRS ${CAPNP_HDR}) - list(APPEND CAPNP_SRCS ${CAPNP_SRC}) -endforeach() - -add_library(extra_capnp STATIC ${CAPNP_SRCS}) -target_link_libraries(extra_capnp PRIVATE CapnProto::capnp) - -target_include_directories(extra_capnp INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/fpga_interchange) - -foreach (target ${family_targets}) - target_include_directories(${target} PRIVATE ${TCL_INCLUDE_PATH}) - target_link_libraries(${target} PRIVATE ${TCL_LIBRARY}) - target_link_libraries(${target} PRIVATE fpga_interchange_capnp) - target_link_libraries(${target} PRIVATE extra_capnp) - target_link_libraries(${target} PRIVATE z) -endforeach() - -if(BUILD_GUI) - target_link_libraries(gui_${family} fpga_interchange_capnp) - target_link_libraries(gui_${family} extra_capnp) - target_link_libraries(gui_${family} z) -endif() -if (BUILD_TESTS) - add_subdirectory(tests/${family}/site_router_tests) -endif() diff --git a/fpga_interchange/flat_wire_map.h b/fpga_interchange/flat_wire_map.h deleted file mode 100644 index 71ecd0b6..00000000 --- a/fpga_interchange/flat_wire_map.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef FLAT_WIRE_MAP_H_ -#define FLAT_WIRE_MAP_H_ - -#include "context.h" -#include "dynamic_bitarray.h" -#include "nextpnr_namespaces.h" -#include "nextpnr_types.h" - -NEXTPNR_NAMESPACE_BEGIN - -template class FlatTileWireMap -{ - public: - std::pair emplace(const Context *ctx, WireId wire, const Value &value) - { - if (values_.empty()) { - if (wire.tile == -1) { - resize(ctx->chip_info->nodes.size()); - } else { - resize(loc_info(ctx->chip_info, wire).wire_data.size()); - } - } - - if (set_.get(wire.index)) { - return std::make_pair(&values_[wire.index], false); - } else { - values_[wire.index] = value; - set_.set(wire.index, true); - return std::make_pair(&values_[wire.index], true); - } - } - - const Value &at(WireId wire) const - { - NPNR_ASSERT(!values_.empty()); - NPNR_ASSERT(set_.get(wire.index)); - return values_.at(wire.index); - } - - void clear() - { - if (!values_.empty()) { - set_.fill(false); - } - } - - private: - void resize(size_t count) - { - set_.resize(count); - set_.fill(false); - values_.resize(count); - } - - DynamicBitarray<> set_; - std::vector values_; -}; - -template class FlatWireMap -{ - public: - FlatWireMap(const Context *ctx) : ctx_(ctx) { tiles_.resize(ctx->chip_info->tiles.size() + 1); } - - std::pair, bool> emplace(WireId wire, const Value &value) - { - // Tile = -1 is for node wires. - size_t tile_index = wire.tile + 1; - auto &tile = tiles_.at(tile_index); - - auto result = tile.emplace(ctx_, wire, value); - if (result.second) { - size_ += 1; - } - return std::make_pair(std::make_pair(wire, result.first), result.second); - } - - const Value &at(WireId wire) const - { - const auto &tile = tiles_.at(wire.tile + 1); - return tile.at(wire); - } - - size_t size() const { return size_; } - - void clear() - { - for (auto &tile : tiles_) { - tile.clear(); - } - size_ = 0; - } - - private: - const Context *ctx_; - std::vector> tiles_; - size_t size_; -}; - -NEXTPNR_NAMESPACE_END - -#endif /* FLAT_WIRE_MAP_H_ */ diff --git a/fpga_interchange/fpga_interchange.cpp b/fpga_interchange/fpga_interchange.cpp deleted file mode 100644 index 671b29f1..00000000 --- a/fpga_interchange/fpga_interchange.cpp +++ /dev/null @@ -1,1153 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "fpga_interchange.h" -#include -#include -#include -#include -#include "PhysicalNetlist.capnp.h" -#include "LogicalNetlist.capnp.h" -#include "zlib.h" -#include "frontend_base.h" - -NEXTPNR_NAMESPACE_BEGIN - -static void write_message(::capnp::MallocMessageBuilder & message, const std::string &filename) { - kj::Array words = messageToFlatArray(message); - kj::ArrayPtr bytes = words.asBytes(); - - gzFile file = gzopen(filename.c_str(), "w"); - NPNR_ASSERT(file != Z_NULL); - - NPNR_ASSERT(gzwrite(file, &bytes[0], bytes.size()) == (int)bytes.size()); - NPNR_ASSERT(gzclose(file) == Z_OK); -} - -struct StringEnumerator { - std::vector strings; - dict string_to_index; - - size_t get_index(const std::string &s) { - auto result = string_to_index.emplace(s, strings.size()); - if(result.second) { - // This string was inserted, append. - strings.push_back(s); - } - - return result.first->second; - } -}; - -static PhysicalNetlist::PhysNetlist::RouteBranch::Builder emit_branch( - const Context * ctx, - StringEnumerator * strings, - const dict &pip_place_strength, - PipId pip, - PhysicalNetlist::PhysNetlist::RouteBranch::Builder branch) { - if(ctx->is_pip_synthetic(pip)) { - log_error("FPGA interchange should not emit synthetic pip %s\n", ctx->nameOfPip(pip)); - } - - const PipInfoPOD & pip_data = pip_info(ctx->chip_info, pip); - const TileTypeInfoPOD & tile_type = loc_info(ctx->chip_info, pip); - const TileInstInfoPOD & tile = ctx->chip_info->tiles[pip.tile]; - - if(pip_data.site == -1) { - // This is a PIP - auto pip_obj = branch.getRouteSegment().initPip(); - pip_obj.setTile(strings->get_index(tile.name.get())); - - // FIXME: This might be broken for reverse bi-pips. Re-visit this one. - // - // pip_data might need to mark that it is a reversed bi-pip, so the - // pip emission for the physical netlist would be: - // - // wire0: dst_wire - // wire1: src_wire - // forward: false - // - IdString src_wire_name = IdString(tile_type.wire_data[pip_data.src_index].name); - IdString dst_wire_name = IdString(tile_type.wire_data[pip_data.dst_index].name); - pip_obj.setWire0(strings->get_index(src_wire_name.str(ctx))); - pip_obj.setWire1(strings->get_index(dst_wire_name.str(ctx))); - pip_obj.setForward(true); - pip_obj.setIsFixed(pip_place_strength.at(pip) >= STRENGTH_FIXED); - - // If this is a pseudo PIP, get its name - if (pip_data.pseudo_cell_wires.size() != 0) { - for (int32_t wire_index : pip_data.pseudo_cell_wires) { - const TileWireInfoPOD &wire_data = tile_type.wire_data[wire_index]; - - if (wire_data.site == -1) { - continue; - } - - const SiteInstInfoPOD & site_data = site_inst_info(ctx->chip_info, pip.tile, wire_data.site); - std::string site_name = site_data.site_name.get(); - int site_idx = strings->get_index(site_name); - pip_obj.setSite(site_idx); - - // It is assumed that a pseudo PIP traverses one site only - break; - } - } - - return branch; - } else { - BelId bel; - bel.tile = pip.tile; - bel.index = pip_data.bel; - - const BelInfoPOD & bel_data = bel_info(ctx->chip_info, bel); - - IdStringList bel_name = ctx->getBelName(bel); - NPNR_ASSERT(bel_name.size() == 2); - std::string site_and_type = bel_name[0].str(ctx); - auto pos = site_and_type.find_first_of('.'); - NPNR_ASSERT(pos != std::string::npos); - - std::string site_name = site_and_type.substr(0, pos); - int site_idx = strings->get_index(site_name); - - if(bel_data.category == BEL_CATEGORY_LOGIC) { - // This is a psuedo site-pip. - auto in_bel_pin = branch.getRouteSegment().initBelPin(); - WireId src_wire = ctx->getPipSrcWire(pip); - WireId dst_wire = ctx->getPipDstWire(pip); - - NPNR_ASSERT(src_wire.index == bel_data.wires[pip_data.extra_data]); - - IdString src_pin(bel_data.ports[pip_data.extra_data]); - - IdString dst_pin; - for(IdString pin : ctx->getBelPins(bel)) { - if(ctx->getBelPinWire(bel, pin) == dst_wire) { - NPNR_ASSERT(dst_pin == IdString()); - dst_pin = pin; - } - } - - NPNR_ASSERT(src_pin != IdString()); - NPNR_ASSERT(dst_pin != IdString()); - - int bel_idx = strings->get_index(bel_name[1].str(ctx)); - in_bel_pin.setSite(site_idx); - in_bel_pin.setBel(bel_idx); - in_bel_pin.setPin(strings->get_index(src_pin.str(ctx))); - - auto subbranch = branch.initBranches(1); - auto bel_pin_branch = subbranch[0]; - auto out_bel_pin = bel_pin_branch.getRouteSegment().initBelPin(); - out_bel_pin.setSite(site_idx); - out_bel_pin.setBel(bel_idx); - out_bel_pin.setPin(strings->get_index(dst_pin.str(ctx))); - - return bel_pin_branch; - } else if(bel_data.category == BEL_CATEGORY_ROUTING) { - // This is a site-pip. - IdStringList pip_name = ctx->getPipName(pip); - - auto site_pip = branch.getRouteSegment().initSitePIP(); - site_pip.setSite(site_idx); - site_pip.setBel(strings->get_index(pip_name[1].str(ctx))); - site_pip.setPin(strings->get_index(pip_name[2].str(ctx))); - site_pip.setIsFixed(pip_place_strength.at(pip) >= STRENGTH_FIXED); - - // FIXME: Mark inverter state. - // This is required for US/US+ inverters, because those inverters - // only have 1 input. - - return branch; - } else { - NPNR_ASSERT(bel_data.category == BEL_CATEGORY_SITE_PORT); - - // This is a site port. - const TileWireInfoPOD &tile_wire = tile_type.wire_data[pip_data.src_index]; - - int site_pin_idx = strings->get_index(bel_name[1].str(ctx)); - - if(tile_wire.site == -1) { - // This site port is routing -> site. - auto site_pin = branch.getRouteSegment().initSitePin(); - site_pin.setSite(site_idx); - site_pin.setPin(site_pin_idx); - - auto subbranch = branch.initBranches(1); - auto bel_pin_branch = subbranch[0]; - auto bel_pin = bel_pin_branch.getRouteSegment().initBelPin(); - bel_pin.setSite(site_idx); - bel_pin.setBel(site_pin_idx); - bel_pin.setPin(site_pin_idx); - - return bel_pin_branch; - } else { - // This site port is site -> routing. - auto bel_pin = branch.getRouteSegment().initBelPin(); - bel_pin.setSite(site_idx); - bel_pin.setBel(site_pin_idx); - bel_pin.setPin(site_pin_idx); - - auto subbranch = branch.initBranches(1); - auto site_pin_branch = subbranch[0]; - auto site_pin = site_pin_branch.getRouteSegment().initSitePin(); - site_pin.setSite(site_idx); - site_pin.setPin(site_pin_idx); - - return site_pin_branch; - } - } - } -} - - -static void init_bel_pin( - const Context * ctx, - StringEnumerator * strings, - const BelPin &bel_pin, - PhysicalNetlist::PhysNetlist::RouteBranch::Builder branch) { - if(ctx->is_bel_synthetic(bel_pin.bel)) { - log_error("FPGA interchange should not emit synthetic BEL pin %s/%s\n", - ctx->nameOfBel(bel_pin.bel), bel_pin.pin.c_str(ctx)); - } - - BelId bel = bel_pin.bel; - IdString pin_name = bel_pin.pin; - - IdStringList bel_name = ctx->getBelName(bel); - NPNR_ASSERT(bel_name.size() == 2); - std::string site_and_type = bel_name[0].str(ctx); - auto pos = site_and_type.find_first_of('.'); - NPNR_ASSERT(pos != std::string::npos); - - std::string site_name = site_and_type.substr(0, pos); - - const BelInfoPOD & bel_data = bel_info(ctx->chip_info, bel); - if(bel_data.category == BEL_CATEGORY_LOGIC) { - // This is a boring old logic BEL. - auto out_bel_pin = branch.getRouteSegment().initBelPin(); - - out_bel_pin.setSite(strings->get_index(site_name)); - out_bel_pin.setBel(strings->get_index(bel_name[1].str(ctx))); - out_bel_pin.setPin(strings->get_index(pin_name.str(ctx))); - } else { - // This is a local site inverter. This is represented with a - // $nextpnr_inv, and this BEL pin is the input to that inverter. - NPNR_ASSERT(bel_data.category == BEL_CATEGORY_ROUTING); - auto out_pip = branch.getRouteSegment().initSitePIP(); - - out_pip.setSite(strings->get_index(site_name)); - out_pip.setBel(strings->get_index(bel_name[1].str(ctx))); - out_pip.setPin(strings->get_index(pin_name.str(ctx))); - out_pip.setIsInverting(true); - } -} - - -static void emit_net( - const Context * ctx, - StringEnumerator * strings, - const dict> &pip_downhill, - const dict> &sinks, - pool *pips, - const dict &pip_place_strength, - WireId wire, PhysicalNetlist::PhysNetlist::RouteBranch::Builder branch) { - size_t number_branches = 0; - - auto downhill_iter = pip_downhill.find(wire); - if(downhill_iter != pip_downhill.end()) { - number_branches += downhill_iter->second.size(); - } - - auto sink_iter = sinks.find(wire); - if(sink_iter != sinks.end()) { - number_branches += sink_iter->second.size(); - } - - size_t branch_index = 0; - auto branches = branch.initBranches(number_branches); - - if(downhill_iter != pip_downhill.end()) { - const std::vector & wire_pips = downhill_iter->second; - for(size_t i = 0; i < wire_pips.size(); ++i) { - PipId pip = wire_pips.at(i); - NPNR_ASSERT(pips->erase(pip) == 1); - PhysicalNetlist::PhysNetlist::RouteBranch::Builder leaf_branch = emit_branch( - ctx, strings, pip_place_strength, pip, branches[branch_index++]); - - emit_net(ctx, strings, pip_downhill, sinks, pips, - pip_place_strength, - ctx->getPipDstWire(pip), leaf_branch); - } - } - - if(sink_iter != sinks.end()) { - for(const auto bel_pin : sink_iter->second) { - auto leaf_branch = branches[branch_index++]; - init_bel_pin(ctx, strings, bel_pin, leaf_branch); - } - } -} - -// Given a site wire, find the source BEL pin. -// -// All site wires should have exactly 1 source BEL pin. -// -// FIXME: Consider making sure that wire_data.bel_pins[0] is always the -// source BEL pin in the BBA generator. -static BelPin find_source(const Context *ctx, WireId source_wire) { - if (source_wire.tile == -1) { - // Nodal wire, probably a constant, cannot have an associated bel pin - return BelPin(); - } - - const TileTypeInfoPOD & tile_type = loc_info(ctx->chip_info, source_wire); - const TileWireInfoPOD & wire_data = tile_type.wire_data[source_wire.index]; - - // Make sure this is a site wire, otherwise something odd is happening - // here. - if(wire_data.site == -1) { - return BelPin(); - } - - BelPin source_bel_pin; - for(const BelPin & bel_pin : ctx->getWireBelPins(source_wire)) { - if(ctx->getBelPinType(bel_pin.bel, bel_pin.pin) == PORT_OUT) { - // Synthetic BEL's (like connection to the VCC/GND network) are - // ignored here, because synthetic BEL's don't exists outside of - // the BBA. - if(ctx->is_bel_synthetic(bel_pin.bel)) { - continue; - } - - NPNR_ASSERT(source_bel_pin.bel == BelId()); - source_bel_pin = bel_pin; - } - } - - NPNR_ASSERT(source_bel_pin.bel != BelId()); - NPNR_ASSERT(source_bel_pin.pin != IdString()); - - return source_bel_pin; -} - -// Initial a local signal source (usually VCC/GND). -static PhysicalNetlist::PhysNetlist::RouteBranch::Builder init_local_source( - const Context *ctx, - StringEnumerator * strings, - PhysicalNetlist::PhysNetlist::RouteBranch::Builder source_branch, - PipId root, - const dict &pip_place_strength, - WireId *root_wire) { - WireId source_wire = ctx->getPipSrcWire(root); - BelPin source_bel_pin = find_source(ctx, source_wire); - if(source_bel_pin.bel != BelId()) { - // This branch should first emit the BEL pin that is the source, followed - // by the pip that brings the source to the net. - init_bel_pin(ctx, strings, source_bel_pin, source_branch); - - source_branch = source_branch.initBranches(1)[0]; - } - *root_wire = ctx->getPipDstWire(root); - return emit_branch(ctx, strings, pip_place_strength, root, source_branch); -} - -static void find_non_synthetic_edges(const Context * ctx, WireId root_wire, - const dict> &pip_downhill, - std::vector *root_pips) { - std::vector wires_to_expand; - - wires_to_expand.push_back(root_wire); - while(!wires_to_expand.empty()) { - WireId wire = wires_to_expand.back(); - wires_to_expand.pop_back(); - - auto downhill_iter = pip_downhill.find(wire); - if(downhill_iter == pip_downhill.end()) { - if(root_wire != wire) { - log_warning("Wire %s never entered the real fabric?\n", - ctx->nameOfWire(wire)); - } - continue; - } - - for(PipId pip : pip_downhill.at(wire)) { - if(!ctx->is_pip_synthetic(pip)) { - // Stop following edges that are non-synthetic, they will be - // followed during emit_net - root_pips->push_back(pip); - } else { - // Continue to follow synthetic edges. - wires_to_expand.push_back(ctx->getPipDstWire(pip)); - } - } - } -} - -void FpgaInterchange::write_physical_netlist(const Context * ctx, const std::string &filename) { - ::capnp::MallocMessageBuilder message; - - PhysicalNetlist::PhysNetlist::Builder phys_netlist = message.initRoot(); - - phys_netlist.setPart(ctx->get_part()); - - pool placed_cells; - for(const auto & cell_pair : ctx->cells) { - const CellInfo & cell = *cell_pair.second; - if(cell.bel == BelId()) { - // This cell was not placed! - continue; - } - NPNR_ASSERT(cell_pair.first == cell.name); - NPNR_ASSERT(placed_cells.emplace(cell.name).second); - } - - StringEnumerator strings; - - IdString nextpnr_inv = ctx->id("$nextpnr_inv"); - - size_t number_placements = 0; - for(auto & cell_name : placed_cells) { - const CellInfo & cell = *ctx->cells.at(cell_name); - - if(cell.type == nextpnr_inv) { - continue; - } - - if(cell.bel == BelId()) { - continue; - } - - if(!ctx->isBelLocationValid(cell.bel)) { - log_error("Cell '%s' is placed at BEL '%s', but this location is currently invalid. Not writing physical netlist.\n", - cell.name.c_str(ctx), ctx->nameOfBel(cell.bel)); - } - - if(ctx->is_bel_synthetic(cell.bel)) { - continue; - } - - number_placements += 1; - } - - std::vector ports; - - dict sites; - auto placements = phys_netlist.initPlacements(number_placements); - auto placement_iter = placements.begin(); - - for(auto & cell_name : placed_cells) { - const CellInfo & cell = *ctx->cells.at(cell_name); - - if(cell.type == nextpnr_inv) { - continue; - } - - if(cell.bel == BelId()) { - continue; - } - - NPNR_ASSERT(ctx->isBelLocationValid(cell.bel)); - - if(ctx->is_bel_synthetic(cell.bel)) { - continue; - } - - IdStringList bel_name = ctx->getBelName(cell.bel); - NPNR_ASSERT(bel_name.size() == 2); - std::string site_and_type = bel_name[0].str(ctx); - auto pos = site_and_type.find_first_of('.'); - NPNR_ASSERT(pos != std::string::npos); - - std::string site_name = site_and_type.substr(0, pos); - std::string site_type = site_and_type.substr(pos + 1); - - auto result = sites.emplace(site_name, site_type); - if(!result.second) { - NPNR_ASSERT(result.first->second == site_type); - } - - auto placement = *placement_iter++; - - placement.setCellName(strings.get_index(cell.name.str(ctx))); - if(ctx->io_port_types.count(cell.type)) { - // Always mark IO ports as type . - placement.setType(strings.get_index("")); - ports.push_back(cell.name); - } else { - placement.setType(strings.get_index(cell.type.str(ctx))); - } - placement.setSite(strings.get_index(site_name)); - - size_t bel_index = strings.get_index(bel_name[1].str(ctx)); - placement.setBel(bel_index); - placement.setIsBelFixed(cell.belStrength >= STRENGTH_FIXED); - placement.setIsSiteFixed(cell.belStrength >= STRENGTH_FIXED); - - if(!ctx->io_port_types.count(cell.type)) { - // Don't emit pin map for ports. - size_t pin_count = 0; - for(const auto & pin : cell.cell_bel_pins) { - if(cell.const_ports.count(pin.first)) { - continue; - } - pin_count += pin.second.size(); - } - - auto pins = placement.initPinMap(pin_count); - auto pin_iter = pins.begin(); - - for(const auto & cell_to_bel_pins : cell.cell_bel_pins) { - if(cell.const_ports.count(cell_to_bel_pins.first)) { - continue; - } - - std::string cell_pin = cell_to_bel_pins.first.str(ctx); - size_t cell_pin_index = strings.get_index(cell_pin); - - for(const auto & bel_pin : cell_to_bel_pins.second) { - auto pin_output = *pin_iter++; - pin_output.setCellPin(cell_pin_index); - pin_output.setBel(bel_index); - pin_output.setBelPin(strings.get_index(bel_pin.str(ctx))); - } - } - } - } - - auto phys_cells = phys_netlist.initPhysCells(ports.size()); - auto phys_cells_iter = phys_cells.begin(); - for(IdString port : ports) { - auto phys_cell = *phys_cells_iter++; - phys_cell.setCellName(strings.get_index(port.str(ctx))); - phys_cell.setPhysType(PhysicalNetlist::PhysNetlist::PhysCellType::PORT); - } - - int nets_to_remove = 0; - for(auto & net_pair : ctx->nets) { - auto &net = *net_pair.second; - - // Remove disconnected nets that do not have any users - auto net_name = std::string(net.name.c_str(ctx)); - if (net.users.empty() && net_name.rfind("$frontend$", 0) == 0) - nets_to_remove++; - } - - auto nets = phys_netlist.initPhysNets(ctx->nets.size() - nets_to_remove); - auto net_iter = nets.begin(); - for(auto & net_pair : ctx->nets) { - auto &net = *net_pair.second; - - auto net_name = std::string(net.name.c_str(ctx)); - if (net.users.empty() && net_name.rfind("$frontend$", 0) == 0) - continue; - - const CellInfo *driver_cell = net.driver.cell; - - auto net_out = *net_iter++; - - // Handle GND and VCC nets. - if(driver_cell != nullptr && driver_cell->bel == ctx->get_gnd_bel()) { - IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); - net_out.setName(strings.get_index(gnd_net_name.str(ctx))); - net_out.setType(PhysicalNetlist::PhysNetlist::NetType::GND); - } else if(driver_cell != nullptr && driver_cell->bel == ctx->get_vcc_bel()) { - IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); - net_out.setName(strings.get_index(vcc_net_name.str(ctx))); - net_out.setType(PhysicalNetlist::PhysNetlist::NetType::VCC); - } else { - net_out.setName(strings.get_index(net.name.str(ctx))); - } - - dict root_wires; - dict> pip_downhill; - pool pips; - - if (driver_cell != nullptr && driver_cell->bel != BelId() && ctx->isBelLocationValid(driver_cell->bel)) { - for(IdString bel_pin_name : driver_cell->cell_bel_pins.at(net.driver.port)) { - BelPin driver_bel_pin; - driver_bel_pin.bel = driver_cell->bel; - driver_bel_pin.pin = bel_pin_name; - - WireId driver_wire = ctx->getBelPinWire(driver_bel_pin.bel, bel_pin_name); - if(driver_wire != WireId()) { - root_wires[driver_wire] = driver_bel_pin; - } - } - } - - dict> sinks; - for(const auto &port_ref : net.users) { - if(port_ref.cell != nullptr && port_ref.cell->bel != BelId() && ctx->isBelLocationValid(port_ref.cell->bel)) { - auto pin_iter = port_ref.cell->cell_bel_pins.find(port_ref.port); - if(pin_iter == port_ref.cell->cell_bel_pins.end()) { - log_warning("Cell %s port %s on net %s is legal, but has no BEL pins?\n", - port_ref.cell->name.c_str(ctx), - port_ref.port.c_str(ctx), - net.name.c_str(ctx)); - continue; - } - - for(IdString bel_pin_name : pin_iter->second) { - BelPin sink_bel_pin; - sink_bel_pin.bel = port_ref.cell->bel; - sink_bel_pin.pin = bel_pin_name; - - WireId sink_wire = ctx->getBelPinWire(sink_bel_pin.bel, bel_pin_name); - if(sink_wire != WireId()) { - sinks[sink_wire].push_back(sink_bel_pin); - } - } - } - } - - dict pip_place_strength; - - for(auto &wire_pair : net.wires) { - WireId downhill_wire = wire_pair.first; - PipId pip = wire_pair.second.pip; - PlaceStrength strength = wire_pair.second.strength; - pip_place_strength[pip] = strength; - if(pip != PipId()) { - pips.emplace(pip); - - WireId uphill_wire = ctx->getPipSrcWire(pip); - NPNR_ASSERT(downhill_wire != uphill_wire); - pip_downhill[uphill_wire].push_back(pip); - } else { - // This is a root wire. - NPNR_ASSERT(root_wires.count(downhill_wire)); - } - } - - std::vector root_pips; - std::vector roots_to_remove; - - for(const auto & root_pair : root_wires) { - WireId root_wire = root_pair.first; - BelPin src_bel_pin = root_pair.second; - - if(!ctx->is_bel_synthetic(src_bel_pin.bel)) { - continue; - } - - roots_to_remove.push_back(root_wire); - find_non_synthetic_edges(ctx, root_wire, pip_downhill, &root_pips); - } - - // Remove wires that have a synthetic root. - for(WireId wire : roots_to_remove) { - NPNR_ASSERT(root_wires.erase(wire) == 1); - } - - auto sources = net_out.initSources(root_wires.size() + root_pips.size()); - auto source_iter = sources.begin(); - - for(const auto & root_pair : root_wires) { - WireId root_wire = root_pair.first; - BelPin src_bel_pin = root_pair.second; - - PhysicalNetlist::PhysNetlist::RouteBranch::Builder source_branch = *source_iter++; - init_bel_pin(ctx, &strings, src_bel_pin, source_branch); - - emit_net(ctx, &strings, pip_downhill, sinks, &pips, pip_place_strength, root_wire, source_branch); - } - - for(const PipId root : root_pips) { - PhysicalNetlist::PhysNetlist::RouteBranch::Builder source_branch = *source_iter++; - - NPNR_ASSERT(pips.erase(root) == 1); - WireId root_wire; - source_branch = init_local_source(ctx, &strings, source_branch, root, pip_place_strength, &root_wire); - emit_net(ctx, &strings, pip_downhill, sinks, &pips, pip_place_strength, root_wire, source_branch); - } - - // Any pips that were not part of a tree starting from the source are - // stubs. - size_t real_pips = 0; - for(PipId pip : pips) { - if(ctx->is_pip_synthetic(pip)) { - continue; - } - real_pips += 1; - } - auto stubs = net_out.initStubs(real_pips); - auto stub_iter = stubs.begin(); - for(PipId pip : pips) { - if(ctx->is_pip_synthetic(pip)) { - continue; - } - emit_branch(ctx, &strings, pip_place_strength, pip, *stub_iter++); - } - } - - auto site_instances = phys_netlist.initSiteInsts(sites.size()); - auto site_inst_iter = site_instances.begin(); - auto site_iter = sites.begin(); - while(site_iter != sites.end()) { - NPNR_ASSERT(site_inst_iter != site_instances.end()); - - auto site_instance = *site_inst_iter; - site_instance.setSite(strings.get_index(site_iter->first)); - site_instance.setType(strings.get_index(site_iter->second)); - - ++site_inst_iter; - ++site_iter; - } - - auto str_list = phys_netlist.initStrList(strings.strings.size()); - for(size_t i = 0; i < strings.strings.size(); ++i) { - str_list.set(i, strings.strings[i]); - } - - write_message(message, filename); -} - -struct LogicalNetlistImpl; - -size_t get_port_width(LogicalNetlist::Netlist::Port::Reader port) { - if(port.isBit()) { - return 1; - } else { - auto bus = port.getBus(); - if(bus.getBusStart() < bus.getBusEnd()) { - return bus.getBusEnd() - bus.getBusStart() + 1; - } else { - return bus.getBusStart() - bus.getBusEnd() + 1; - } - } -} - -struct PortKey { - PortKey(int32_t inst_idx, uint32_t port_idx) : inst_idx(inst_idx), port_idx(port_idx) {} - int32_t inst_idx; - uint32_t port_idx; - - bool operator == (const PortKey &other) const { - return inst_idx == other.inst_idx && port_idx == other.port_idx; - } - unsigned int hash() const { - return mkhash(inst_idx, port_idx); - } -}; - -struct ModuleReader { - const LogicalNetlistImpl *root; - - bool is_top; - LogicalNetlist::Netlist::CellInstance::Reader cell_inst; - LogicalNetlist::Netlist::Cell::Reader cell; - LogicalNetlist::Netlist::CellDeclaration::Reader cell_decl; - - dict net_indicies; - dict disconnected_nets; - dict> connections; - - ModuleReader(const LogicalNetlistImpl *root, - LogicalNetlist::Netlist::CellInstance::Reader cell_inst, bool is_top); - - size_t translate_port_index(LogicalNetlist::Netlist::PortInstance::Reader port_inst) const; -}; - -struct PortReader { - const ModuleReader * module; - size_t port_idx; -}; - -struct CellReader { - const ModuleReader * module; - size_t inst_idx; -}; - -struct NetReader { - NetReader(const ModuleReader * module, size_t net_idx) : module(module), net_idx(net_idx) { - scratch.resize(1); - scratch[0] = net_idx; - } - - const ModuleReader * module; - size_t net_idx; - LogicalNetlist::Netlist::PropertyMap::Reader property_map; - std::vector scratch; -}; - -struct LogicalNetlistImpl -{ - LogicalNetlist::Netlist::Reader root; - std::vector strings; - - typedef const ModuleReader ModuleDataType; - typedef const PortReader& ModulePortDataType; - typedef const CellReader& CellDataType; - typedef const NetReader& NetnameDataType; - typedef const std::vector& BitVectorDataType; - - LogicalNetlistImpl(LogicalNetlist::Netlist::Reader root) : root(root) { - strings.reserve(root.getStrList().size()); - for(auto s : root.getStrList()) { - strings.push_back(s); - } - } - - template void foreach_module(TFunc Func) const - { - auto top = root.getTopInst(); - ModuleReader top_module(this, top, /*is_top=*/true); - Func(strings.at(top.getName()), top_module); - } - - template void foreach_port(const ModuleReader &mod, TFunc Func) const - { - for(auto port_idx : mod.cell_decl.getPorts()) { - PortReader port_reader; - port_reader.module = &mod; - port_reader.port_idx = port_idx; - auto port = root.getPortList()[port_idx]; - Func(strings.at(port.getName()), port_reader); - } - } - - template void foreach_cell(const ModuleReader &mod, TFunc Func) const - { - for (auto cell_inst_idx : mod.cell.getInsts()) { - auto cell_inst = root.getInstList()[cell_inst_idx]; - CellReader cell_reader; - cell_reader.module = &mod; - cell_reader.inst_idx = cell_inst_idx; - Func(strings.at(cell_inst.getName()), cell_reader); - } - } - - template void foreach_netname(const ModuleReader &mod, TFunc Func) const - { - for(auto net_pair : mod.net_indicies) { - NetReader net_reader(&mod, net_pair.first); - auto net = net_pair.second; - net_reader.property_map = net.getPropMap(); - Func(strings.at(net.getName()), net_reader); - } - - for(auto net_pair : mod.disconnected_nets) { - NetReader net_reader(&mod, net_pair.first); - Func(net_pair.second, net_reader); - } - } - - PortType get_port_type_for_direction(LogicalNetlist::Netlist::Direction dir) const { - if(dir == LogicalNetlist::Netlist::Direction::INPUT) { - return PORT_IN; - } else if (dir == LogicalNetlist::Netlist::Direction::INOUT) { - return PORT_INOUT; - } else if (dir == LogicalNetlist::Netlist::Direction::OUTPUT) { - return PORT_OUT; - } else { - NPNR_ASSERT_FALSE("invalid logical netlist port direction"); - } - } - - PortType get_port_dir(const PortReader &port_reader) const { - auto port = root.getPortList()[port_reader.port_idx]; - auto dir = port.getDir(); - return get_port_type_for_direction(dir); - } - - const std::string &get_cell_type(const CellReader &cell) const { - auto cell_inst = root.getInstList()[cell.inst_idx]; - auto cell_def = root.getCellList()[cell_inst.getCell()]; - auto cell_decl = root.getCellDecls()[cell_def.getIndex()]; - return strings.at(cell_decl.getName()); - } - - - template void foreach_port_dir(const CellReader &cell, TFunc Func) const { - auto cell_inst = root.getInstList()[cell.inst_idx]; - auto cell_def = root.getCellList()[cell_inst.getCell()]; - auto cell_decl = root.getCellDecls()[cell_def.getIndex()]; - - for(auto port_idx : cell_decl.getPorts()) { - auto port = root.getPortList()[port_idx]; - Func(strings.at(port.getName()), get_port_type_for_direction(port.getDir())); - } - } - - template void foreach_prop_map(LogicalNetlist::Netlist::PropertyMap::Reader prop_map, TFunc Func) const { - for(auto prop : prop_map.getEntries()) { - if(prop.isTextValue()) { - Func(strings.at(prop.getKey()), Property(strings.at(prop.getTextValue()))); - } else if(prop.isIntValue()) { - Func(strings.at(prop.getKey()), Property(prop.getIntValue())); - } else { - NPNR_ASSERT(prop.isBoolValue()); - Func(strings.at(prop.getKey()), Property(prop.getBoolValue())); - } - } - } - - template void foreach_attr(const ModuleReader &mod, TFunc Func) const { - if(mod.is_top) { - // Emit attribute "top" for top instance. - Func("top", Property(1)); - } - - auto cell_def = root.getCellList()[mod.cell_inst.getCell()]; - auto cell_decl = root.getCellDecls()[cell_def.getIndex()]; - foreach_prop_map(cell_decl.getPropMap(), Func); - } - - template void foreach_attr(const CellReader &cell, TFunc Func) const { - auto cell_inst = root.getInstList()[cell.inst_idx]; - foreach_prop_map(cell_inst.getPropMap(), Func); - } - - template void foreach_attr(const PortReader &port_reader, TFunc Func) const { - auto port = root.getPortList()[port_reader.port_idx]; - foreach_prop_map(port.getPropMap(), Func); - } - - template void foreach_attr(const NetReader &net_reader, TFunc Func) const { - foreach_prop_map(net_reader.property_map, Func); - } - - template void foreach_param(const CellReader &cell_reader, TFunc Func) const - { - auto cell_inst = root.getInstList()[cell_reader.inst_idx]; - foreach_prop_map(cell_inst.getPropMap(), Func); - } - - template void foreach_setting(const ModuleReader &obj, TFunc Func) const - { - foreach_prop_map(root.getPropMap(), Func); - } - - template void foreach_port_conn(const CellReader &cell, TFunc Func) const - { - auto cell_inst = root.getInstList()[cell.inst_idx]; - auto cell_def = root.getCellList()[cell_inst.getCell()]; - auto cell_decl = root.getCellDecls()[cell_def.getIndex()]; - - for(auto port_idx : cell_decl.getPorts()) { - auto port = root.getPortList()[port_idx]; - PortKey port_key(cell.inst_idx, port_idx); - const std::vector &connections = cell.module->connections.at(port_key); - Func(strings.at(port.getName()), connections); - } - } - - int get_array_offset(const NetReader &port_reader) const - { - return 0; - } - - bool is_array_upto(const NetReader &port_reader) const { - return false; - } - - int get_array_offset(const PortReader &port_reader) const - { - auto port = root.getPortList()[port_reader.port_idx]; - if(port.isBus()) { - auto bus = port.getBus(); - return std::min(bus.getBusStart(), bus.getBusEnd()); - } else { - return 0; - } - } - - bool is_array_upto(const PortReader &port_reader) const { - auto port = root.getPortList()[port_reader.port_idx]; - if(port.isBus()) { - auto bus = port.getBus(); - return bus.getBusStart() < bus.getBusEnd(); - } else { - return false; - } - } - - const std::vector &get_port_bits(const PortReader &port_reader) const { - PortKey port_key(-1, port_reader.port_idx); - return port_reader.module->connections.at(port_key); - } - - const std::vector &get_net_bits(const NetReader &net) const { - return net.scratch; - } - - int get_vector_length(const std::vector &bits) const { return int(bits.size()); } - - bool is_vector_bit_constant(const std::vector &bits, int i) const - { - // Note: This appears weird, but is correct. This is because VCC/GND - // nets are not handled in frontend_base for FPGA interchange. - return false; - } - - char get_vector_bit_constval(const std::vector&bits, int i) const - { - // Unreachable! - NPNR_ASSERT(false); - } - - int get_vector_bit_signal(const std::vector&bits, int i) const - { - return bits.at(i); - } -}; - -ModuleReader::ModuleReader(const LogicalNetlistImpl *root, - LogicalNetlist::Netlist::CellInstance::Reader cell_inst, bool is_top) : - root(root), is_top(is_top), cell_inst(cell_inst) { - cell = root->root.getCellList()[cell_inst.getCell()]; - cell_decl = root->root.getCellDecls()[cell.getIndex()]; - - // Auto-assign all ports to a net index, and then re-assign based on the - // nets. - int net_idx = 2; - - auto ports = root->root.getPortList(); - for(auto port_idx : cell_decl.getPorts()) { - auto port = ports[port_idx]; - size_t port_width = get_port_width(port); - - PortKey port_key(-1, port_idx); - auto result = connections.emplace(port_key, std::vector()); - NPNR_ASSERT(result.second); - - std::vector & port_connections = result.first->second; - port_connections.resize(port_width); - for(size_t i = 0; i < port_width; ++i) { - port_connections[i] = net_idx++; - } - } - - for(auto inst_idx : cell.getInsts()) { - auto inst = root->root.getInstList()[inst_idx]; - auto inst_cell = root->root.getCellList()[inst.getCell()]; - auto inst_cell_decl = root->root.getCellDecls()[inst_cell.getIndex()]; - - auto inst_ports = inst_cell_decl.getPorts(); - for(auto inst_port_idx : inst_ports) { - PortKey port_key(inst_idx, inst_port_idx); - auto result = connections.emplace(port_key, std::vector()); - NPNR_ASSERT(result.second); - - auto inst_port = ports[inst_port_idx]; - size_t port_width = get_port_width(inst_port); - - std::vector & port_connections = result.first->second; - port_connections.resize(port_width); - for(size_t i = 0; i < port_width; ++i) { - port_connections[i] = net_idx++; - } - } - } - - auto nets = cell.getNets(); - for(size_t i = 0; i < nets.size(); ++i, ++net_idx) { - auto net = nets[i]; - net_indicies[net_idx] = net; - - for(auto port_inst : net.getPortInsts()) { - int32_t inst_idx = -1; - if(port_inst.isInst()) { - inst_idx = port_inst.getInst(); - } - - PortKey port_key(inst_idx, port_inst.getPort()); - std::vector & port_connections = connections.at(port_key); - - size_t port_idx = translate_port_index(port_inst); - port_connections.at(port_idx) = net_idx; - } - } - - for(const auto & port_connections : connections) { - for(size_t i = 0; i < port_connections.second.size(); ++i) { - int32_t net_idx = port_connections.second[i]; - - auto iter = net_indicies.find(net_idx); - if(iter == net_indicies.end()) { - PortKey port_key = port_connections.first; - auto port = ports[port_key.port_idx]; - - // Disconnected outputs should be marked, so to be ignored when - // writing the physical netlist. - // Skipping these nets lets the basic frontend to assign a default - // name that starts with $frontend$. - if (port.getDir() == LogicalNetlist::Netlist::Direction::OUTPUT) - continue; - - disconnected_nets[net_idx] = stringf("%s.%d", root->strings.at(port.getName()).c_str(), i); - } - } - } -} - -void FpgaInterchange::read_logical_netlist(Context * ctx, const std::string &filename) { - gzFile file = gzopen(filename.c_str(), "r"); - NPNR_ASSERT(file != Z_NULL); - - std::vector output_data; - output_data.resize(4096); - std::stringstream sstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary); - while(true) { - int ret = gzread(file, output_data.data(), output_data.size()); - NPNR_ASSERT(ret >= 0); - if(ret > 0) { - sstream.write((const char*)output_data.data(), ret); - NPNR_ASSERT(sstream); - } else { - NPNR_ASSERT(ret == 0); - int error; - gzerror(file, &error); - NPNR_ASSERT(error == Z_OK); - break; - } - } - - NPNR_ASSERT(gzclose(file) == Z_OK); - - sstream.seekg(0); - kj::std::StdInputStream istream(sstream); - capnp::ReaderOptions reader_options; - reader_options.traversalLimitInWords = 32llu*1024llu*1024llu*1024llu; - capnp::InputStreamMessageReader message_reader(istream, reader_options); - - LogicalNetlist::Netlist::Reader netlist = message_reader.getRoot(); - LogicalNetlistImpl netlist_reader(netlist); - - GenericFrontend(ctx, netlist_reader, /*split_io=*/false)(); -} - -size_t ModuleReader::translate_port_index(LogicalNetlist::Netlist::PortInstance::Reader port_inst) const { - LogicalNetlist::Netlist::Port::Reader port = root->root.getPortList()[port_inst.getPort()]; - if(port_inst.getBusIdx().isSingleBit()) { - NPNR_ASSERT(port.isBit()); - return 0; - } else { - NPNR_ASSERT(port.isBus()); - uint32_t idx = port_inst.getBusIdx().getIdx(); - size_t width = get_port_width(port); - NPNR_ASSERT(idx < width); - return width - 1 - idx; - } -} - -NEXTPNR_NAMESPACE_END - diff --git a/fpga_interchange/fpga_interchange.h b/fpga_interchange/fpga_interchange.h deleted file mode 100644 index 239b0e96..00000000 --- a/fpga_interchange/fpga_interchange.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 The SymbiFlow Authors. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef PHYSICAL_NETLIST_WRITER_H -#define PHYSICAL_NETLIST_WRITER_H - -#include "nextpnr.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct FpgaInterchange -{ - static void read_logical_netlist(Context *ctx, const std::string &filename); - static void write_physical_netlist(const Context *ctx, const std::string &filename); -}; - -NEXTPNR_NAMESPACE_END - -#endif diff --git a/fpga_interchange/globals.cc b/fpga_interchange/globals.cc deleted file mode 100644 index eaff30e0..00000000 --- a/fpga_interchange/globals.cc +++ /dev/null @@ -1,276 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 gatecat - * - * 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 "log.h" -#include "nextpnr.h" -#include "util.h" - -#include - -NEXTPNR_NAMESPACE_BEGIN - -namespace { - -struct GlobalVist -{ - PipId downhill = PipId(); - int total_hops = 0; - int global_hops = 0; - bool operator<(const GlobalVist &other) const - { - return (total_hops < other.total_hops) || - ((total_hops == other.total_hops) && (global_hops > other.global_hops)); - } -}; - -// This is our main global routing implementation. It is used both to actually route globals; and also to discover if -// global buffers have available short routes from their source for auto-placement -static int route_global_arc(Context *ctx, NetInfo *net, store_index usr_idx, size_t phys_port_idx, - int max_hops, bool dry_run) -{ - auto &usr = net->users.at(usr_idx); - WireId src = ctx->getNetinfoSourceWire(net); - WireId dest = ctx->getNetinfoSinkWire(net, usr, phys_port_idx); - if (dest == WireId()) { - if (dry_run) - return -1; - else - log_error("Arc %d.%d (%s.%s) of net %s has no sink wire!\n", usr_idx.idx(), int(phys_port_idx), - ctx->nameOf(usr.cell), ctx->nameOf(usr.port), ctx->nameOf(net)); - } - // Consider any existing routing put in place by the site router, etc - int start_hops = 0; - while (net->wires.count(dest) && dest != src) { - dest = ctx->getPipSrcWire(net->wires.at(dest).pip); - ++start_hops; - } - // The main BFS implementation - // Currently this is a backwards-BFS from sink to source (or pre-existing routing) that avoids general routing. It - // currently aims for minimum hops as a primary goal and maximum global resource usage as a secondary goal. More - // advanced heuristics will likely be needed for more complex situation - WireId startpoint; - GlobalVist best_visit; - std::queue visit_queue; - dict visits; - - visit_queue.push(dest); - visits[dest].downhill = PipId(); - visits[dest].total_hops = start_hops; - - while (!visit_queue.empty()) { - WireId cursor = visit_queue.front(); - visit_queue.pop(); - auto curr_visit = visits.at(cursor); - // We're now at least one layer deeper than a valid visit, any further exploration is futile - if (startpoint != WireId() && curr_visit.total_hops > best_visit.total_hops) - break; - // Valid end of routing - if ((cursor == src) || (ctx->getBoundWireNet(cursor) == net)) { - if (startpoint == WireId() || curr_visit < best_visit) { - startpoint = cursor; - best_visit = curr_visit; - } - } - // Explore uphill - for (auto pip : ctx->getPipsUphill(cursor)) { - if (!dry_run && !ctx->checkPipAvailForNet(pip, net)) - continue; - WireId pip_src = ctx->getPipSrcWire(pip); - if (!dry_run && !ctx->checkWireAvail(pip_src) && ctx->getBoundWireNet(pip_src) != net) - continue; - auto cat = ctx->get_wire_category(pip_src); - if (!ctx->is_site_wire(pip_src) && cat == WIRE_CAT_GENERAL) - continue; // never allow general routing - GlobalVist next_visit; - next_visit.downhill = pip; - next_visit.total_hops = curr_visit.total_hops + 1; - if (max_hops != -1 && next_visit.total_hops > max_hops) - continue; - next_visit.global_hops = curr_visit.global_hops + ((cat == WIRE_CAT_GLOBAL) ? 1 : 0); - auto fnd_src = visits.find(pip_src); - if (fnd_src == visits.end() || next_visit < fnd_src->second) { - visit_queue.push(pip_src); - visits[pip_src] = next_visit; - } - } - } - - if (startpoint == WireId()) - return -1; - if (!dry_run) { - if (ctx->getBoundWireNet(startpoint) == nullptr) - ctx->bindWire(startpoint, net, STRENGTH_LOCKED); - - WireId cursor = startpoint; - std::vector pips; - // Create a list of pips on the routed path - while (true) { - PipId pip = visits.at(cursor).downhill; - if (pip == PipId()) - break; - pips.push_back(pip); - cursor = ctx->getPipDstWire(pip); - } - // Reverse that list - std::reverse(pips.begin(), pips.end()); - // Bind pips until we hit already-bound routing - for (PipId pip : pips) { - WireId dst = ctx->getPipDstWire(pip); - if (ctx->getBoundWireNet(dst) == net) - break; - ctx->bindPip(pip, net, STRENGTH_LOCKED); - } - } - return visits.at(startpoint).total_hops; -} -}; // namespace - -const GlobalCellPOD *Arch::global_cell_info(IdString cell_type) const -{ - for (const auto &glb_cell : chip_info->global_cells) - if (IdString(glb_cell.cell_type) == cell_type) - return &glb_cell; - - return nullptr; -} - -void Arch::place_globals() -{ - log_info("Placing globals...\n"); - - Context *ctx = getCtx(); - IdString gnd_net_name(chip_info->constants->gnd_net_name); - IdString vcc_net_name(chip_info->constants->vcc_net_name); - - // TODO: for more complex PLL type setups, we might want a toposort or iterative loop as the PLL must be placed - // before the GBs it drives - for (auto &cell : ctx->cells) { - CellInfo *ci = cell.second.get(); - const GlobalCellPOD *glb_cell = global_cell_info(ci->type); - if (glb_cell == nullptr) - continue; - // Ignore if already placed - if (ci->bel != BelId()) - continue; - - for (const auto &pin : glb_cell->pins) { - if (!pin.guide_placement) - continue; - - IdString pin_name(pin.name); - if (!ci->ports.count(pin_name)) - continue; - auto &port = ci->ports.at(pin_name); - - // only input ports currently used for placement guidance - if (port.type != PORT_IN) - continue; - - NetInfo *net = port.net; - if (net == nullptr || net->name == gnd_net_name || net->name == vcc_net_name) - continue; - // Ignore if there is no driver; or the driver is not placed - if (net->driver.cell == nullptr || net->driver.cell->bel == BelId()) - continue; - - // TODO: substantial performance improvements are probably possible, although of questionable benefit given - // the low number of globals in a typical device... - BelId best_bel; - int shortest_distance = std::numeric_limits::max(); - - for (auto bel : getBels()) { - int distance; - if (!isValidBelForCellType(ci->type, bel)) - continue; - if (!checkBelAvail(bel)) - continue; - // Provisionally place - bindBel(bel, ci, STRENGTH_WEAK); - if (!isBelLocationValid(bel)) - goto fail; - // Check distance - distance = route_global_arc(ctx, net, port.user_idx, 0, pin.max_hops, true); - if (distance != -1 && distance < shortest_distance) { - best_bel = bel; - shortest_distance = distance; - } - fail: - unbindBel(bel); - } - - if (best_bel != BelId()) { - bindBel(best_bel, ci, STRENGTH_LOCKED); - log_info(" placed %s:%s at %s\n", ctx->nameOf(ci), ctx->nameOf(ci->type), ctx->nameOfBel(best_bel)); - break; - } - } - } -} - -void Arch::route_globals() -{ - log_info("Routing globals...\n"); - - Context *ctx = getCtx(); - IdString gnd_net_name(chip_info->constants->gnd_net_name); - IdString vcc_net_name(chip_info->constants->vcc_net_name); - - for (auto &cell : ctx->cells) { - CellInfo *ci = cell.second.get(); - const GlobalCellPOD *glb_cell = global_cell_info(ci->type); - if (glb_cell == nullptr) - continue; - for (const auto &pin : glb_cell->pins) { - IdString pin_name(pin.name); - if (!ci->ports.count(pin_name)) - continue; - auto &port = ci->ports.at(pin_name); - - // TOOD: routing of input ports, too - // output ports are generally the first priority though - if (port.type != PORT_OUT) - continue; - - NetInfo *net = port.net; - if (net == nullptr || net->name == gnd_net_name || net->name == vcc_net_name) - continue; - - int total_sinks = 0; - int global_sinks = 0; - - for (auto usr : net->users.enumerate()) { - for (size_t j = 0; j < ctx->getNetinfoSinkWireCount(net, usr.value); j++) { - int result = route_global_arc(ctx, net, usr.index, j, pin.max_hops, false); - ++total_sinks; - if (result != -1) - ++global_sinks; - if ((result == -1) && pin.force_routing) - log_error("Failed to route arc %d.%d (%s.%s) of net %s using dedicated global routing!\n", - usr.index.idx(), int(j), ctx->nameOf(usr.value.cell), ctx->nameOf(usr.value.port), - ctx->nameOf(net)); - } - } - - log_info(" routed %d/%d sinks of net %s using dedicated routing.\n", global_sinks, total_sinks, - ctx->nameOf(net)); - } - } -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/lookahead.capnp b/fpga_interchange/lookahead.capnp deleted file mode 100644 index e69c165d..00000000 --- a/fpga_interchange/lookahead.capnp +++ /dev/null @@ -1,61 +0,0 @@ -@0x97c69817483d9dea; - -using Cxx = import "/capnp/c++.capnp"; -$Cxx.namespace("lookahead_storage"); - -using DelayType = Int32; - -struct TypeWireId { - type @0: Int32; - index @1: Int32; -} - -struct TypeWirePair { - src @0 : TypeWireId; - dst @1 : TypeWireId; -} - -struct InputSiteWireCost { - routeTo @0 : TypeWireId; - cost @1 : DelayType; -} - -struct InputSiteWireCostMap { - key @0 : TypeWireId; - value @1 : List(InputSiteWireCost); -} - -struct OutputSiteWireCostMap { - key @0 : TypeWireId; - cheapestRouteFrom @1 : TypeWireId; - cost @2 : DelayType; -} - -struct SiteToSiteCostMap { - key @0 : TypeWirePair; - cost @1 : DelayType; -} - -struct CostMapEntry { - key @0 : TypeWirePair; - data @1 : List(DelayType); - xDim @2 : UInt32; - yDim @3 : UInt32; - xOffset @4 : UInt32; - yOffset @5 : UInt32; - penalty @6 : DelayType; -} - -struct CostMap { - costMap @0 : List(CostMapEntry); -} - -struct Lookahead { - - chipdbHash @0 : Text; - inputSiteWires @1 : List(InputSiteWireCostMap); - outputSiteWires @2 : List(OutputSiteWireCostMap); - siteToSiteCost @3 : List(SiteToSiteCostMap); - costMap @4 : CostMap; -} - diff --git a/fpga_interchange/lookahead.cc b/fpga_interchange/lookahead.cc deleted file mode 100644 index f1cb13e1..00000000 --- a/fpga_interchange/lookahead.cc +++ /dev/null @@ -1,1543 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "lookahead.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "context.h" -#include "flat_wire_map.h" -#include "log.h" -#include "sampler.h" -#include "scope_lock.h" - -#if defined(NEXTPNR_USE_TBB) -#include -#endif - -NEXTPNR_NAMESPACE_BEGIN - -static constexpr size_t kNumberSamples = 4; -static constexpr int32_t kMaxExploreDist = 20; - -// Initial only explore with a depth of this. -static constexpr int32_t kInitialExploreDepth = 30; - -struct RoutingNode -{ - WireId wire_to_expand; - delay_t cost; - int32_t depth; - - bool operator<(const RoutingNode &other) const { return cost < other.cost; } -}; - -struct PipAndCost -{ - PipId upstream_pip; - delay_t cost_from_src; - int32_t depth; -}; - -static void expand_input(const Context *ctx, WireId input_wire, dict *input_costs) -{ - pool seen; - std::priority_queue to_expand; - - RoutingNode initial; - initial.cost = 0; - initial.wire_to_expand = input_wire; - - to_expand.push(initial); - - while (!to_expand.empty()) { - RoutingNode node = to_expand.top(); - to_expand.pop(); - - auto result = seen.emplace(node.wire_to_expand); - if (!result.second) { - // We've already done an expansion at this wire. - continue; - } - - for (PipId pip : ctx->getPipsUphill(node.wire_to_expand)) { - if (ctx->is_pip_synthetic(pip)) { - continue; - } - WireId new_wire = ctx->getPipSrcWire(pip); - if (new_wire == WireId()) { - continue; - } - - RoutingNode next_node; - next_node.wire_to_expand = new_wire; - next_node.cost = node.cost + ctx->getPipDelay(pip).maxDelay() + ctx->getWireDelay(new_wire).maxDelay(); - - if (ctx->is_site_port(pip)) { - // Done with expansion, record the path if cheaper. - // Only the first path to each wire will be the cheapest. - - // Get local tile wire at pip dest. Using getPipSrcWire may - // return a node wire, which is not correct here. - TypeWireId route_to; - route_to.type = ctx->chip_info->tiles[pip.tile].type; - route_to.index = loc_info(ctx->chip_info, pip).pip_data[pip.index].src_index; - if (route_to.index >= 0) { - auto result = input_costs->emplace(route_to, next_node.cost); - if (!result.second && result.first->second > next_node.cost) { - result.first->second = next_node.cost; - } - } - } else { - to_expand.push(next_node); - } - } - } -} - -static void update_site_to_site_costs(const Context *ctx, WireId first_wire, const dict &best_path, - dict *site_to_site_cost) -{ - for (auto &best_pair : best_path) { - WireId last_wire = best_pair.first; - TypeWirePair pair; - pair.dst = TypeWireId(ctx, last_wire); - - PipAndCost pip_and_cost = best_pair.second; - delay_t cost_from_src = pip_and_cost.cost_from_src; - - WireId cursor; - do { - cursor = ctx->getPipSrcWire(pip_and_cost.upstream_pip); - - pair.src = TypeWireId(ctx, cursor); - - delay_t cost = cost_from_src; - - // Only use the delta cost from cursor to last_wire, not the full - // cost from first_wire to last_wire. - if (cursor != first_wire) { - pip_and_cost = best_path.at(cursor); - cost -= pip_and_cost.cost_from_src; - } - - NPNR_ASSERT(cost >= 0); - - auto cost_result = site_to_site_cost->emplace(pair, cost); - if (!cost_result.second) { - // Update point to point cost if cheaper. - if (cost_result.first->second > cost) { - cost_result.first->second = cost; - } - } - } while (cursor != first_wire); - } -} - -static void expand_output(const Context *ctx, WireId output_wire, Lookahead::OutputSiteWireCost *output_cost, - dict *site_to_site_cost) -{ - pool seen; - std::priority_queue to_expand; - - RoutingNode initial; - initial.cost = 0; - initial.wire_to_expand = output_wire; - - to_expand.push(initial); - - dict best_path; - - while (!to_expand.empty()) { - RoutingNode node = to_expand.top(); - to_expand.pop(); - - auto result = seen.emplace(node.wire_to_expand); - if (!result.second) { - // We've already done an expansion at this wire. - continue; - } - - for (PipId pip : ctx->getPipsDownhill(node.wire_to_expand)) { - if (ctx->is_pip_synthetic(pip)) { - continue; - } - WireId new_wire = ctx->getPipDstWire(pip); - if (new_wire == WireId()) { - continue; - } - - RoutingNode next_node; - next_node.wire_to_expand = new_wire; - next_node.cost = node.cost + ctx->getPipDelay(pip).maxDelay() + ctx->getWireDelay(new_wire).maxDelay(); - - if (ctx->is_site_port(pip)) { - // Done with expansion, record the path if cheaper. - - // Get local tile wire at pip dest. Using getPipDstWire may - // return a node wire, which is not correct here. - TypeWireId route_from; - route_from.type = ctx->chip_info->tiles[pip.tile].type; - route_from.index = loc_info(ctx->chip_info, pip).pip_data[pip.index].dst_index; - if (route_from.index != -1 && output_cost != nullptr && next_node.cost < output_cost->cost) { - output_cost->cost = next_node.cost; - output_cost->cheapest_route_from = route_from; - } - } else { - to_expand.push(next_node); - - auto result = best_path.emplace(new_wire, PipAndCost()); - PipAndCost &pip_and_cost = result.first->second; - if (result.second || pip_and_cost.cost_from_src > next_node.cost) { - pip_and_cost.upstream_pip = pip; - pip_and_cost.cost_from_src = next_node.cost; - } - } - } - } - - update_site_to_site_costs(ctx, output_wire, best_path, site_to_site_cost); -} - -static void expand_input_type(const Context *ctx, DeterministicRNG *rng, const Sampler &tiles_of_type, - TypeWireId input_wire, std::vector *input_costs) -{ - dict input_costs_map; - for (size_t region = 0; region < tiles_of_type.number_of_regions(); ++region) { - size_t tile = tiles_of_type.get_sample_from_region(region, [rng]() -> int32_t { return rng->rng(); }); - - NPNR_ASSERT(ctx->chip_info->tiles[tile].type == input_wire.type); - WireId wire = canonical_wire(ctx->chip_info, tile, input_wire.index); - - expand_input(ctx, wire, &input_costs_map); - } - - input_costs->clear(); - input_costs->reserve(input_costs_map.size()); - for (const auto &input_pair : input_costs_map) { - input_costs->emplace_back(); - auto &input_cost = input_costs->back(); - input_cost.route_to = input_pair.first; - input_cost.cost = input_pair.second; - } -} - -struct DelayStorage -{ - dict, delay_t>> storage; - int32_t max_explore_depth; -}; - -static bool has_multiple_inputs(const Context *ctx, WireId wire) -{ - size_t pip_count = 0; - for (PipId pip : ctx->getPipsUphill(wire)) { - (void)pip; - pip_count += 1; - } - - return pip_count > 1; -} - -static void update_results(const Context *ctx, const FlatWireMap &best_path, WireId src_wire, - WireId sink_wire, DelayStorage *storage) -{ - TypeWireId src_wire_type(ctx, src_wire); - - int src_tile; - if (src_wire.tile == -1) { - src_tile = ctx->chip_info->nodes[src_wire.index].tile_wires[0].tile; - } else { - src_tile = src_wire.tile; - } - - int32_t src_x, src_y; - ctx->get_tile_x_y(src_tile, &src_x, &src_y); - - TypeWirePair wire_pair; - wire_pair.src = src_wire_type; - - // The first couple wires from the site pip are usually boring, don't record - // them. - bool out_of_infeed = false; - - // Starting from end of result, walk backwards and record the path into - // the delay storage. - WireId cursor = sink_wire; - pool seen; - while (cursor != src_wire) { - // No loops allowed in routing! - auto result = seen.emplace(cursor); - NPNR_ASSERT(result.second); - - if (!out_of_infeed && has_multiple_inputs(ctx, cursor)) { - out_of_infeed = true; - } - - TypeWireId dst_wire_type(ctx, cursor); - wire_pair.dst = dst_wire_type; - - int dst_tile; - if (cursor.tile == -1) { - dst_tile = ctx->chip_info->nodes[cursor.index].tile_wires[0].tile; - } else { - dst_tile = cursor.tile; - } - int32_t dst_x; - int32_t dst_y; - ctx->get_tile_x_y(dst_tile, &dst_x, &dst_y); - - std::pair dx_dy; - dx_dy.first = dst_x - src_x; - dx_dy.second = dst_y - src_y; - - const PipAndCost &pip_and_cost = best_path.at(cursor); - if (out_of_infeed) { - auto &delta_data = storage->storage[wire_pair]; - auto result2 = delta_data.emplace(dx_dy, pip_and_cost.cost_from_src); - if (!result2.second) { - if (result2.first->second > pip_and_cost.cost_from_src) { - result2.first->second = pip_and_cost.cost_from_src; - } - } - } - - cursor = ctx->getPipSrcWire(pip_and_cost.upstream_pip); - } -} - -static void expand_routing_graph_from_wire(const Context *ctx, WireId first_wire, FlatWireMap *best_path, - DelayStorage *storage) -{ - pool seen; - std::priority_queue to_expand; - - int src_tile; - if (first_wire.tile == -1) { - src_tile = ctx->chip_info->nodes[first_wire.index].tile_wires[0].tile; - } else { - src_tile = first_wire.tile; - } - - int32_t src_x, src_y; - ctx->get_tile_x_y(src_tile, &src_x, &src_y); - - RoutingNode initial; - initial.cost = 0; - initial.wire_to_expand = first_wire; - initial.depth = 0; - - to_expand.push(initial); - - best_path->clear(); - size_t skip_count = 0; - - while (!to_expand.empty()) { - RoutingNode node = to_expand.top(); - to_expand.pop(); - - auto result = seen.emplace(node.wire_to_expand); - if (!result.second) { - // We've already done an expansion at this wire. - skip_count += 1; - continue; - } - - bool has_site_pip = false; - for (PipId pip : ctx->getPipsDownhill(node.wire_to_expand)) { - if (ctx->is_pip_synthetic(pip)) { - continue; - } - - // Don't expand edges that are site pips, but do record how we - // got to the pip before the site pip! - if (ctx->is_site_port(pip)) { - has_site_pip = true; - continue; - } - - WireId new_wire = ctx->getPipDstWire(pip); - if (new_wire == WireId()) { - continue; - } - - RoutingNode next_node; - next_node.wire_to_expand = new_wire; - next_node.cost = node.cost + ctx->getPipDelay(pip).maxDelay() + ctx->getWireDelay(new_wire).maxDelay(); - next_node.depth = node.depth + 1; - - // Record best path. - PipAndCost pip_and_cost; - pip_and_cost.upstream_pip = pip; - pip_and_cost.cost_from_src = next_node.cost; - pip_and_cost.depth = next_node.depth; - auto result = best_path->emplace(new_wire, pip_and_cost); - bool is_best_path = true; - if (!result.second) { - if (result.first.second->cost_from_src > next_node.cost) { - result.first.second->cost_from_src = next_node.cost; - result.first.second->upstream_pip = pip; - result.first.second->depth = next_node.depth; - } else { - is_best_path = false; - } - } - - Loc dst = ctx->getPipLocation(pip); - int32_t dst_x = dst.x; - int32_t dst_y = dst.y; - if (is_best_path && std::abs(dst_x - src_x) < kMaxExploreDist && - std::abs(dst_y - src_y) < kMaxExploreDist && next_node.depth < storage->max_explore_depth) { - to_expand.push(next_node); - } - } - - if (has_site_pip) { - update_results(ctx, *best_path, first_wire, node.wire_to_expand, storage); - } - } -} - -static bool has_multiple_outputs(const Context *ctx, WireId wire) -{ - size_t pip_count = 0; - for (PipId pip : ctx->getPipsDownhill(wire)) { - (void)pip; - pip_count += 1; - } - - return pip_count > 1; -} - -static void expand_routing_graph(const Context *ctx, DeterministicRNG *rng, const Sampler &tiles_of_type, - TypeWireId wire_type, pool *types_explored, DelayStorage *storage, - pool *types_deferred, FlatWireMap *best_path) -{ - pool new_types_explored; - - for (size_t region = 0; region < tiles_of_type.number_of_regions(); ++region) { - size_t tile = tiles_of_type.get_sample_from_region(region, [rng]() -> int32_t { return rng->rng(); }); - - NPNR_ASSERT(ctx->chip_info->tiles[tile].type == wire_type.type); - - WireId wire = canonical_wire(ctx->chip_info, tile, wire_type.index); - TypeWireSet wire_set(ctx, wire); - - if (!has_multiple_outputs(ctx, wire)) { - types_deferred->emplace(wire_type); - continue; - } - - new_types_explored.emplace(wire_set); - - expand_routing_graph_from_wire(ctx, wire, best_path, storage); - } - - for (const TypeWireSet &new_wire_set : new_types_explored) { - types_explored->emplace(new_wire_set); - } -} - -static WireId follow_pip_chain(const Context *ctx, WireId wire, delay_t *delay_out) -{ - delay_t delay = 0; - WireId cursor = wire; - while (true) { - WireId next; - size_t pip_count = 0; - delay_t next_delay = delay; - for (PipId pip : ctx->getPipsDownhill(cursor)) { - pip_count += 1; - next = ctx->getPipDstWire(pip); - next_delay += ctx->getPipDelay(pip).maxDelay() + ctx->getWireDelay(next).maxDelay(); - } - - if (pip_count > 1) { - *delay_out = delay; - return cursor; - } - - if (next == WireId()) { - return WireId(); - } - - delay = next_delay; - - cursor = next; - } - - // Unreachable? - NPNR_ASSERT(false); -} - -static WireId follow_pip_chain_target(const Context *ctx, WireId wire, WireId target, delay_t *delay_out) -{ - delay_t delay = 0; - WireId cursor = wire; - while (cursor != target) { - WireId next; - size_t pip_count = 0; - delay_t next_delay = delay; - for (PipId pip : ctx->getPipsDownhill(cursor)) { - pip_count += 1; - next = ctx->getPipDstWire(pip); - next_delay += ctx->getPipDelay(pip).maxDelay() + ctx->getWireDelay(next).maxDelay(); - } - - if (pip_count > 1) { - *delay_out = delay; - return cursor; - } - - if (next == WireId()) { - return WireId(); - } - - delay = next_delay; - - cursor = next; - } - - *delay_out = delay; - return cursor; -} - -static WireId follow_pip_chain_up(const Context *ctx, WireId wire, delay_t *delay_out) -{ - delay_t delay = 0; - WireId cursor = wire; - while (true) { - WireId next; - size_t pip_count = 0; - delay_t next_delay = delay; - for (PipId pip : ctx->getPipsUphill(cursor)) { - pip_count += 1; - next = ctx->getPipSrcWire(pip); - next_delay += ctx->getPipDelay(pip).maxDelay() + ctx->getWireDelay(next).maxDelay(); - } - - if (pip_count > 1) { - *delay_out = delay; - return cursor; - } - - if (next == WireId()) { - return WireId(); - } - - delay = next_delay; - - cursor = next; - } - - // Unreachable? - NPNR_ASSERT(false); -} - -static void expand_deferred_routing_graph(const Context *ctx, DeterministicRNG *rng, const Sampler &tiles_of_type, - TypeWireId wire_type, pool *types_explored, - DelayStorage *storage, FlatWireMap *best_path) -{ - pool new_types_explored; - - for (size_t region = 0; region < tiles_of_type.number_of_regions(); ++region) { - size_t tile = tiles_of_type.get_sample_from_region(region, [rng]() -> int32_t { return rng->rng(); }); - - NPNR_ASSERT(ctx->chip_info->tiles[tile].type == wire_type.type); - - WireId wire = canonical_wire(ctx->chip_info, tile, wire_type.index); - TypeWireSet wire_set(ctx, wire); - if (types_explored->count(wire_set)) { - // Check if this wire set has been expanded. - continue; - } - - delay_t delay; - WireId routing_wire = follow_pip_chain(ctx, wire, &delay); - - // This wire doesn't go anywhere! - if (routing_wire == WireId()) { - continue; - } - - TypeWireSet routing_wire_set(ctx, routing_wire); - if (types_explored->count(routing_wire_set)) { - continue; - } - - new_types_explored.emplace(wire_set); - expand_routing_graph_from_wire(ctx, wire, best_path, storage); - } - - for (const TypeWireSet &new_wire_set : new_types_explored) { - types_explored->emplace(new_wire_set); - } -} - -static void expand_output_type(const Context *ctx, DeterministicRNG *rng, const Sampler &tiles_of_type, - TypeWireId output_wire, Lookahead::OutputSiteWireCost *output_cost, - dict *site_to_site_cost) -{ - for (size_t region = 0; region < tiles_of_type.number_of_regions(); ++region) { - size_t tile = tiles_of_type.get_sample_from_region(region, [rng]() -> int32_t { return rng->rng(); }); - - NPNR_ASSERT(ctx->chip_info->tiles[tile].type == output_wire.type); - WireId wire = canonical_wire(ctx->chip_info, tile, output_wire.index); - - expand_output(ctx, wire, output_cost, site_to_site_cost); - } -} - -static constexpr bool kWriteLookaheadCsv = false; - -void write_lookahead_csv(const Context *ctx, const DelayStorage &all_tiles_storage) -{ - FILE *lookahead_data = fopen("lookahead.csv", "w"); - NPNR_ASSERT(lookahead_data != nullptr); - fprintf(lookahead_data, "src_type,src_wire,dest_type,dest_wire,delta_x,delta_y,delay\n"); - for (const auto &type_pair : all_tiles_storage.storage) { - auto &src_wire_type = type_pair.first.src; - auto &src_type_data = ctx->chip_info->tile_types[src_wire_type.type]; - IdString src_type(src_type_data.name); - IdString src_wire(src_type_data.wire_data[src_wire_type.index].name); - - auto &dst_wire_type = type_pair.first.dst; - auto &dst_type_data = ctx->chip_info->tile_types[dst_wire_type.type]; - IdString dst_type(dst_type_data.name); - IdString dst_wire(dst_type_data.wire_data[dst_wire_type.index].name); - - for (const auto &delta_pair : type_pair.second) { - fprintf(lookahead_data, "%s,%s,%s,%s,%d,%d,%d\n", src_type.c_str(ctx), src_wire.c_str(ctx), - dst_type.c_str(ctx), dst_wire.c_str(ctx), delta_pair.first.first, delta_pair.first.second, - delta_pair.second); - } - } - - fclose(lookahead_data); -} - -// Storage for tile type expansion for lookahead. -struct ExpandLocals -{ - virtual ~ExpandLocals() {} - const std::vector *tiles_of_type; - DeterministicRNG *rng; - FlatWireMap *best_path; - DelayStorage *storage; - pool *explored; - pool *deferred; - - virtual void lock() {} - virtual void unlock() {} - virtual void copy_back(int32_t tile_type) {} -}; - -// Do tile type expansion for 1 tile. -static void expand_tile_type(const Context *ctx, int32_t tile_type, ExpandLocals *locals) -{ - auto &type_data = ctx->chip_info->tile_types[tile_type]; - if (ctx->verbose) { - ScopeLock lock(locals); - log_info("Expanding all wires in type %s\n", IdString(type_data.name).c_str(ctx)); - } - - auto &tile_sampler = (*locals->tiles_of_type)[tile_type]; - for (size_t wire_index = 0; wire_index < type_data.wire_data.size(); ++wire_index) { - auto &wire_data = type_data.wire_data[wire_index]; - if (wire_data.site != -1) { - // Skip site wires - continue; - } - - if (ctx->debug) { - ScopeLock lock(locals); - log_info("Expanding wire %s in type %s (%d/%zu, seen %zu types, deferred %zu types)\n", - IdString(wire_data.name).c_str(ctx), IdString(type_data.name).c_str(ctx), tile_type, - ctx->chip_info->tile_types.size(), locals->explored->size(), locals->deferred->size()); - } - - TypeWireId wire; - wire.type = tile_type; - wire.index = wire_index; - - expand_routing_graph(ctx, locals->rng, tile_sampler, wire, locals->explored, locals->storage, locals->deferred, - locals->best_path); - } - - locals->copy_back(tile_type); -} - -// Function that does all tile expansions serially. -static void expand_tile_type_serial(const Context *ctx, const std::vector &tile_types, - const std::vector &tiles_of_type, DeterministicRNG *rng, - FlatWireMap *best_path, DelayStorage *storage, - pool *explored, pool *deferred, pool *tiles_left) -{ - - for (int32_t tile_type : tile_types) { - ExpandLocals locals; - - locals.tiles_of_type = &tiles_of_type; - locals.rng = rng; - locals.best_path = best_path; - locals.storage = storage; - locals.explored = explored; - expand_tile_type(ctx, tile_type, &locals); - - NPNR_ASSERT(tiles_left->erase(tile_type) == 1); - } - - NPNR_ASSERT(tiles_left->empty()); -} - -// Additional storage for doing tile type expansion in parallel. -struct TbbExpandLocals : public ExpandLocals -{ - const Context *ctx; - std::mutex *all_costs_mutex; - - DelayStorage *all_tiles_storage; - pool *types_explored; - pool *types_deferred; - pool *tiles_left; - - void lock() override { all_costs_mutex->lock(); } - - void unlock() override { all_costs_mutex->unlock(); } - - void copy_back(int32_t tile_type) override - { - ScopeLock locker(this); - - auto &type_data = ctx->chip_info->tile_types[tile_type]; - - // Copy per tile data by to over all data structures. - if (ctx->verbose) { - log_info("Expanded all wires in type %s, merging data back\n", IdString(type_data.name).c_str(ctx)); - log_info("Testing %zu wires, saw %zu types, deferred %zu types\n", type_data.wire_data.size(), - explored->size(), deferred->size()); - } - - // Copy cheapest explored paths back to all_tiles_storage. - for (const auto &type_pair : storage->storage) { - auto &type_pair_data = all_tiles_storage->storage[type_pair.first]; - for (const auto &delta_pair : type_pair.second) { - // See if this dx/dy already has data. - auto result = type_pair_data.emplace(delta_pair.first, delta_pair.second); - if (!result.second) { - // This was already in the map, check if this new result is - // better - if (delta_pair.second < result.first->second) { - result.first->second = delta_pair.second; - } - } - } - } - - // Update explored and deferred sets. - for (auto &key : *explored) { - types_explored->emplace(key); - } - for (auto &key : *deferred) { - types_deferred->emplace(key); - } - - NPNR_ASSERT(tiles_left->erase(tile_type)); - - if (ctx->verbose) { - log_info("Done merging data from type %s, %zu tiles left\n", IdString(type_data.name).c_str(ctx), - tiles_left->size()); - } - } -}; - -// Wrapper method used if running expansion in parallel. -// -// expand_tile_type is invoked using thread local data, and then afterwards -// the data is joined with the global data. -static void expand_tile_type_parallel(const Context *ctx, int32_t tile_type, const std::vector &tiles_of_type, - DeterministicRNG *rng, std::mutex *all_costs_mutex, - DelayStorage *all_tiles_storage, pool *types_explored, - pool *types_deferred, pool *tiles_left) -{ - TbbExpandLocals locals; - - DeterministicRNG rng_copy = *rng; - FlatWireMap best_path(ctx); - pool explored; - pool deferred; - DelayStorage storage; - storage.max_explore_depth = all_tiles_storage->max_explore_depth; - - locals.tiles_of_type = &tiles_of_type; - locals.rng = &rng_copy; - locals.best_path = &best_path; - locals.storage = &storage; - locals.explored = &explored; - locals.deferred = &deferred; - - locals.ctx = ctx; - locals.all_costs_mutex = all_costs_mutex; - locals.all_tiles_storage = all_tiles_storage; - locals.types_explored = types_explored; - locals.types_deferred = types_deferred; - locals.tiles_left = tiles_left; - - expand_tile_type(ctx, tile_type, &locals); -} - -void Lookahead::build_lookahead(const Context *ctx, DeterministicRNG *rng) -{ - auto start = std::chrono::high_resolution_clock::now(); - - if (ctx->verbose) { - log_info("Building lookahead, first gathering input and output site wires\n"); - } - - pool input_site_ports; - for (BelId bel : ctx->getBels()) { - const auto &bel_data = bel_info(ctx->chip_info, bel); - - for (IdString pin : ctx->getBelPins(bel)) { - WireId pin_wire = ctx->getBelPinWire(bel, pin); - if (pin_wire == WireId()) { - continue; - } - - PortType type = ctx->getBelPinType(bel, pin); - - if (type == PORT_IN && bel_data.category == BEL_CATEGORY_LOGIC) { - input_site_wires.emplace(TypeWireId(ctx, pin_wire), std::vector()); - } else if (type == PORT_OUT && bel_data.category == BEL_CATEGORY_LOGIC) { - output_site_wires.emplace(TypeWireId(ctx, pin_wire), OutputSiteWireCost()); - } else if (type == PORT_OUT && bel_data.category == BEL_CATEGORY_SITE_PORT) { - input_site_ports.emplace(TypeWireId(ctx, pin_wire)); - } - } - } - - if (ctx->verbose) { - log_info("Have %zu input and %zu output site wire types. Creating tile type samplers\n", - input_site_wires.size(), output_site_wires.size()); - } - - std::vector tiles_of_type; - tiles_of_type.resize(ctx->chip_info->tile_types.ssize()); - - std::vector indicies; - std::vector> xys; - indicies.reserve(ctx->chip_info->tiles.size()); - xys.reserve(ctx->chip_info->tiles.size()); - - for (int32_t tile_type = 0; tile_type < ctx->chip_info->tile_types.ssize(); ++tile_type) { - indicies.clear(); - xys.clear(); - - for (size_t tile = 0; tile < ctx->chip_info->tiles.size(); ++tile) { - if (ctx->chip_info->tiles[tile].type != tile_type) { - continue; - } - - std::pair xy; - ctx->get_tile_x_y(tile, &xy.first, &xy.second); - - indicies.push_back(tile); - xys.push_back(xy); - } - - auto &tile_sampler = tiles_of_type[tile_type]; - tile_sampler.divide_samples(kNumberSamples, xys); - - // Remap Sampler::indicies from 0 to number of tiles of type to - // absolute tile indicies. - for (size_t i = 0; i < indicies.size(); ++i) { - tile_sampler.indicies[i] = indicies[tile_sampler.indicies[i]]; - } - } - - if (ctx->verbose) { - log_info("Expanding input site wires\n"); - } - - // Expand backwards from each input site wire to find the cheapest - // non-site wire. - for (auto &input_pair : input_site_wires) { - expand_input_type(ctx, rng, tiles_of_type[input_pair.first.type], input_pair.first, &input_pair.second); - } - - if (ctx->verbose) { - log_info("Expanding output site wires\n"); - } - - // Expand forward from each output site wire to find the cheapest - // non-site wire. - for (auto &output_pair : output_site_wires) { - output_pair.second.cost = std::numeric_limits::max(); - expand_output_type(ctx, rng, tiles_of_type[output_pair.first.type], output_pair.first, &output_pair.second, - &site_to_site_cost); - } - for (TypeWireId input_site_port : input_site_ports) { - expand_output_type(ctx, rng, tiles_of_type[input_site_port.type], input_site_port, nullptr, &site_to_site_cost); - } - - if (ctx->verbose) { - log_info("Expanding all wire types\n"); - } - - DelayStorage all_tiles_storage; - all_tiles_storage.max_explore_depth = kInitialExploreDepth; - - // These are wire types that have been explored. - pool types_explored; - - // These are wire types that have been deferred because they are trival - // copies of another wire type. These can be cheaply computed after the - // graph has been explored. - pool types_deferred; - - std::vector tile_types; - pool tiles_left; - tile_types.reserve(ctx->chip_info->tile_types.size()); - for (int32_t tile_type = 0; tile_type < ctx->chip_info->tile_types.ssize(); ++tile_type) { - tile_types.push_back(tile_type); - tiles_left.emplace(tile_type); - } - - FlatWireMap best_path(ctx); - - // Walk each tile type, and expand all non-site wires in the tile. - // Wires that are nodes will expand as if the node type is the first node - // in the wire. - // - // Wires that only have 1 output pip are deferred until the next loop, - // because generally those wires will get explored via another wire. - // The deferred will be expanded if this assumption doesn't hold. - bool expand_serially = true; -#if defined(NEXTPNR_USE_TBB) // Run parallely - { - std::mutex all_costs_mutex; - - expand_serially = false; - tbb::parallel_for_each(tile_types, [&](int32_t tile_type) { - expand_tile_type_parallel(ctx, tile_type, tiles_of_type, rng, &all_costs_mutex, &all_tiles_storage, - &types_explored, &types_deferred, &tiles_left); - }); - } -#else - // Supress warning that expand_tile_type_parallel if not running in - // parallel. - (void)expand_tile_type_parallel; -#endif - if (expand_serially) { - expand_tile_type_serial(ctx, tile_types, tiles_of_type, rng, &best_path, &all_tiles_storage, &types_explored, - &types_deferred, &tiles_left); - } - - // Check to see if deferred wire types were expanded. If they were not - // expanded, expand them now. If they were expanded, copy_types is - // populated with the wire types that can just copy the relevant data from - // another wire type. - for (TypeWireId wire_type : types_deferred) { - auto &type_data = ctx->chip_info->tile_types[wire_type.type]; - auto &tile_sampler = tiles_of_type[wire_type.type]; - auto &wire_data = type_data.wire_data[wire_type.index]; - - if (ctx->verbose) { - log_info("Expanding deferred wire %s in type %s (seen %zu types)\n", IdString(wire_data.name).c_str(ctx), - IdString(type_data.name).c_str(ctx), types_explored.size()); - } - - expand_deferred_routing_graph(ctx, rng, tile_sampler, wire_type, &types_explored, &all_tiles_storage, - &best_path); - } - - auto end = std::chrono::high_resolution_clock::now(); - if (ctx->verbose) { - log_info("Done with expansion, dt %02fs\n", std::chrono::duration(end - start).count()); - } - - if (kWriteLookaheadCsv) { - write_lookahead_csv(ctx, all_tiles_storage); - end = std::chrono::high_resolution_clock::now(); - if (ctx->verbose) { - log_info("Done writing data to disk, dt %02fs\n", std::chrono::duration(end - start).count()); - } - } - -#if defined(NEXTPNR_USE_TBB) // Run parallely - tbb::parallel_for_each(all_tiles_storage.storage, - [&](const std::pair, delay_t>> &type_pair) { -#else - for (const auto &type_pair : all_tiles_storage.storage) { -#endif - cost_map.set_cost_map(ctx, type_pair.first, type_pair.second); -#if defined(NEXTPNR_USE_TBB) // Run parallely - }); -#else - } -#endif - - end = std::chrono::high_resolution_clock::now(); - if (ctx->verbose) { - log_info("build_lookahead time %.02fs\n", std::chrono::duration(end - start).count()); - } -} - -constexpr static bool kUseGzipForLookahead = false; - -static void write_message(::capnp::MallocMessageBuilder &message, const std::string &filename) -{ - kj::Array words = messageToFlatArray(message); - kj::ArrayPtr bytes = words.asBytes(); - - boost::filesystem::path temp = boost::filesystem::unique_path(); - log_info("Writing tempfile to %s\n", temp.c_str()); - - if (kUseGzipForLookahead) { - gzFile file = gzopen(temp.c_str(), "w"); - NPNR_ASSERT(file != Z_NULL); - - size_t bytes_written = 0; - int result; - while (bytes_written < bytes.size()) { - size_t bytes_remaining = bytes.size() - bytes_written; - size_t bytes_to_write = bytes_remaining; - if (bytes_to_write >= std::numeric_limits::max()) { - bytes_to_write = std::numeric_limits::max(); - } - result = gzwrite(file, &bytes[0] + bytes_written, bytes_to_write); - if (result < 0) { - break; - } - - bytes_written += result; - } - - int error; - std::string error_str; - if (result < 0) { - error_str.assign(gzerror(file, &error)); - } - NPNR_ASSERT(gzclose(file) == Z_OK); - if (bytes_written != bytes.size()) { - // Remove failed writes before reporting error. - boost::filesystem::remove(temp); - } - - if (result < 0) { - log_error("Failed to write lookahead, error from gzip %s\n", error_str.c_str()); - } else { - if (bytes_written != bytes.size()) { - log_error("Failed to write lookahead, wrote %d bytes, had %zu bytes\n", result, bytes.size()); - } else { - // Written, move file into place - boost::filesystem::rename(temp, filename); - } - } - } else { - { - kj::Own fs = kj::newDiskFilesystem(); - - auto path = kj::Path::parse(temp); - auto file = fs->getCurrent().openFile(path, kj::WriteMode::CREATE); - file->writeAll(bytes); - } - - boost::filesystem::rename(temp, filename); - } -} - -bool Lookahead::read_lookahead(const std::string &chipdb_hash, const std::string &filename) -{ - capnp::ReaderOptions reader_options; - reader_options.traversalLimitInWords = 32llu * 1024llu * 1024llu * 1024llu; - - if (kUseGzipForLookahead) { - gzFile file = gzopen(filename.c_str(), "r"); - if (file == Z_NULL) { - return false; - } - - std::vector output_data; - output_data.resize(4096); - std::stringstream sstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary); - while (true) { - int ret = gzread(file, output_data.data(), output_data.size()); - NPNR_ASSERT(ret >= 0); - if (ret > 0) { - sstream.write((const char *)output_data.data(), ret); - NPNR_ASSERT(sstream); - } else { - NPNR_ASSERT(ret == 0); - int error; - gzerror(file, &error); - NPNR_ASSERT(error == Z_OK); - break; - } - } - - NPNR_ASSERT(gzclose(file) == Z_OK); - - sstream.seekg(0); - kj::std::StdInputStream istream(sstream); - capnp::InputStreamMessageReader message_reader(istream, reader_options); - - lookahead_storage::Lookahead::Reader lookahead = message_reader.getRoot(); - return from_reader(chipdb_hash, lookahead); - } else { - boost::iostreams::mapped_file_source file; - try { - file.open(filename.c_str()); - } catch (std::ios_base::failure &fail) { - return false; - } - - if (!file.is_open()) { - return false; - } - - const char *data = reinterpret_cast(file.data()); - const kj::ArrayPtr words = - kj::arrayPtr(reinterpret_cast(data), file.size() / sizeof(::capnp::word)); - ::capnp::FlatArrayMessageReader reader(words, reader_options); - lookahead_storage::Lookahead::Reader lookahead = reader.getRoot(); - return from_reader(chipdb_hash, lookahead); - } -} - -void Lookahead::write_lookahead(const std::string &chipdb_hash, const std::string &file) const -{ - ::capnp::MallocMessageBuilder message; - - lookahead_storage::Lookahead::Builder lookahead = message.initRoot(); - to_builder(chipdb_hash, lookahead); - write_message(message, file); -} - -void Lookahead::init(const Context *ctx, DeterministicRNG *rng) -{ - std::string lookahead_filename; - if (kUseGzipForLookahead) { - lookahead_filename = ctx->args.chipdb + ".lookahead.tgz"; - } else { - lookahead_filename = ctx->args.chipdb + ".lookahead"; - } - - std::string chipdb_hash = ctx->get_chipdb_hash(); - - if (ctx->args.rebuild_lookahead || !read_lookahead(chipdb_hash, lookahead_filename)) { - build_lookahead(ctx, rng); - if (!ctx->args.dont_write_lookahead) { - write_lookahead(chipdb_hash, lookahead_filename); - } - } -} - -static bool safe_add_i32(int32_t a, int32_t b, int32_t *out) -{ -#if defined(__GNUG__) || defined(__clang__) - // GCC and clang have had __builtin_add_overflow for a while. - return !__builtin_add_overflow(a, b, out); -#else - // MSVC and other don't have an intrinsic. Emit some more code. - bool safe_to_add; - if (b < 0) { - safe_to_add = a >= std::numeric_limits::min() - b; - } else { - safe_to_add = a <= std::numeric_limits::max() - b; - } - if (!safe_to_add) { - return false; - } - *out = a + b; - return true; -#endif -} - -static void saturating_incr(int32_t *acc, int32_t value) -{ - if (!safe_add_i32(*acc, value, acc)) { - if (value > 0) { - *acc = std::numeric_limits::max(); - } else { - *acc = std::numeric_limits::min(); - } - } -} - -#define DEBUG_LOOKUP - -delay_t Lookahead::estimateDelay(const Context *ctx, WireId src, WireId dst) const -{ -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Looking up %s to %s\n", ctx->nameOfWire(src), ctx->nameOfWire(dst)); - } -#endif - delay_t delay = 0; - - // Follow chain down, chasing wires with only 1 pip. Stop if dst is - // reached. - WireId orig_src = src; - src = follow_pip_chain_target(ctx, src, dst, &delay); - NPNR_ASSERT(delay >= 0); - if (src == WireId()) { - // This src wire is a dead end, tell router to avoid it! -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Source %s is a dead end!\n", ctx->nameOfWire(orig_src)); - } -#endif - return std::numeric_limits::max(); - } - -#ifdef DEBUG_LOOKUP - if (ctx->debug && src != orig_src) { - log_info("Moving src from %s to %s, delay = %d\n", ctx->nameOfWire(orig_src), ctx->nameOfWire(src), delay); - } -#endif - - if (src == dst) { - // Reached target already, done! - return delay; - } - - if (ctx->is_same_site(src, dst)) { - // Check for site to site direct path. - TypeWirePair pair; - - TypeWireId src_type(ctx, src); - pair.src = src_type; - - TypeWireId dst_type(ctx, dst); - pair.dst = dst_type; - - auto iter = site_to_site_cost.find(pair); - if (iter != site_to_site_cost.end()) { - NPNR_ASSERT(iter->second >= 0); - saturating_incr(&delay, iter->second); -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Found site to site direct path %s -> %s = %d\n", ctx->nameOfWire(src), ctx->nameOfWire(dst), - delay); - } -#endif - return delay; - } - } - - // At this point we know that the routing interconnect is needed, or - // the pair is unreachable. - orig_src = src; - TypeWireId src_type(ctx, src); - - // Find the first routing wire from the src_type. - auto src_iter = output_site_wires.find(src_type); - if (src_iter != output_site_wires.end()) { - NPNR_ASSERT(src_iter->second.cost >= 0); - saturating_incr(&delay, src_iter->second.cost); - src_type = src_iter->second.cheapest_route_from; - - src = canonical_wire(ctx->chip_info, src.tile, src_type.index); -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Moving src from %s to %s, delay = %d\n", ctx->nameOfWire(orig_src), ctx->nameOfWire(src), delay); - } -#endif - } - - // Make sure that the new wire is in the routing graph. - if (ctx->is_wire_in_site(src)) { -#ifdef DEBUG_LOOKUP - // We've already tested for direct site to site routing, if src cannot - // reach outside of the routing network, this path is impossible. - if (ctx->debug) { - log_warning("Failed to reach routing network for src %s, got to %s\n", ctx->nameOfWire(orig_src), - ctx->nameOfWire(src)); - } -#endif - return std::numeric_limits::max(); - } - - if (src == dst) { - // Reached target already, done! - return delay; - } - - // Find the first routing wire that reaches dst_type. - WireId orig_dst = dst; - TypeWireId dst_type(ctx, dst); - - auto dst_iter = input_site_wires.find(dst_type); - if (dst_iter == input_site_wires.end()) { - // dst_type isn't an input site wire, just add point to point delay. - auto &dst_data = ctx->wire_info(dst); - if (dst_data.site != -1) { -#ifdef DEBUG_LOOKUP - // We've already tested for direct site to site routing, if dst cannot - // be reached from the routing network, this path is impossible. - if (ctx->debug) { - log_warning("Failed to reach routing network for dst %s, got to %s\n", ctx->nameOfWire(orig_dst), - ctx->nameOfWire(dst)); - } -#endif - return std::numeric_limits::max(); - } - - // Follow chain up - WireId orig_dst = dst; - (void)orig_dst; - - delay_t chain_delay; - dst = follow_pip_chain_up(ctx, dst, &chain_delay); - NPNR_ASSERT(chain_delay >= 0); - saturating_incr(&delay, chain_delay); -#ifdef DEBUG_LOOKUP - if (ctx->debug && dst != orig_dst) { - log_info("Moving dst from %s to %s, delay = %d\n", ctx->nameOfWire(orig_dst), ctx->nameOfWire(dst), delay); - } -#endif - - if (src == dst) { - // Reached target already, done! - return delay; - } - - // Both src and dst are in the routing graph, lookup approx cost to go - // from src to dst. - int32_t delay_from_map = cost_map.get_delay(ctx, src, dst); - NPNR_ASSERT(delay_from_map >= 0); - saturating_incr(&delay, delay_from_map); - -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Final delay = %d\n", delay); - } -#endif - - return delay; - } else { - // dst_type is an input site wire, try each possible routing path. - delay_t base_delay = delay; - delay_t cheapest_path = std::numeric_limits::max(); - - for (const InputSiteWireCost &input_cost : dst_iter->second) { - dst = orig_dst; - delay = base_delay; - - NPNR_ASSERT(input_cost.cost >= 0); - saturating_incr(&delay, input_cost.cost); - dst_type = input_cost.route_to; - - NPNR_ASSERT(dst_type.index != -1); - dst = canonical_wire(ctx->chip_info, dst.tile, dst_type.index); - NPNR_ASSERT(dst != WireId()); - -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Moving dst from %s to %s, delay = %d\n", ctx->nameOfWire(orig_dst), ctx->nameOfWire(dst), - delay); - } -#endif - - if (dst == src) { -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Possible delay = %d\n", delay); - } -#endif - // Reached target already, done! - cheapest_path = std::min(delay, cheapest_path); - continue; - } - - auto &dst_data = ctx->wire_info(dst); - if (dst_data.site != -1) { -#ifdef DEBUG_LOOKUP - // We've already tested for direct site to site routing, if dst cannot - // be reached from the routing network, this path is impossible. - if (ctx->debug) { - log_warning("Failed to reach routing network for dst %s, got to %s\n", ctx->nameOfWire(orig_dst), - ctx->nameOfWire(dst)); - } -#endif - continue; - } - - // Follow chain up - WireId orig_dst = dst; - (void)orig_dst; - - delay_t chain_delay; - dst = follow_pip_chain_up(ctx, dst, &chain_delay); - NPNR_ASSERT(chain_delay >= 0); - saturating_incr(&delay, chain_delay); -#ifdef DEBUG_LOOKUP - if (ctx->debug && dst != orig_dst) { - log_info("Moving dst from %s to %s, delay = %d\n", ctx->nameOfWire(orig_dst), ctx->nameOfWire(dst), - delay); - } -#endif - - if (dst == WireId()) { - // This dst wire is a dead end, don't examine it! -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Dest %s is a dead end!\n", ctx->nameOfWire(dst)); - } -#endif - continue; - } - - if (src == dst) { -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Possible delay = %d\n", delay); - } -#endif - // Reached target already, done! - cheapest_path = std::min(delay, cheapest_path); - continue; - } - - // Both src and dst are in the routing graph, lookup approx cost to go - // from src to dst. - int32_t delay_from_map = cost_map.get_delay(ctx, src, dst); - NPNR_ASSERT(delay_from_map >= 0); - saturating_incr(&delay, delay_from_map); - cheapest_path = std::min(delay, cheapest_path); -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Possible delay = %d\n", delay); - } -#endif - } - -#ifdef DEBUG_LOOKUP - if (ctx->debug) { - log_info("Final delay = %d\n", delay); - } -#endif - - return cheapest_path; - } -} - -bool Lookahead::from_reader(const std::string &chipdb_hash, lookahead_storage::Lookahead::Reader reader) -{ - std::string expected_hash = reader.getChipdbHash(); - if (chipdb_hash != expected_hash) { - return false; - } - - input_site_wires.clear(); - output_site_wires.clear(); - site_to_site_cost.clear(); - - for (auto input_reader : reader.getInputSiteWires()) { - TypeWireId key(input_reader.getKey()); - - auto result = input_site_wires.emplace(key, std::vector()); - NPNR_ASSERT(result.second); - std::vector &costs = result.first->second; - auto value = input_reader.getValue(); - costs.reserve(value.size()); - for (auto cost : value) { - costs.emplace_back(InputSiteWireCost{TypeWireId(cost.getRouteTo()), cost.getCost()}); - } - } - - for (auto output_reader : reader.getOutputSiteWires()) { - TypeWireId key(output_reader.getKey()); - - auto result = output_site_wires.emplace( - key, OutputSiteWireCost{TypeWireId(output_reader.getCheapestRouteFrom()), output_reader.getCost()}); - NPNR_ASSERT(result.second); - } - - for (auto site_to_site_reader : reader.getSiteToSiteCost()) { - TypeWirePair key(site_to_site_reader.getKey()); - auto result = site_to_site_cost.emplace(key, site_to_site_reader.getCost()); - NPNR_ASSERT(result.second); - } - - cost_map.from_reader(reader.getCostMap()); - - return true; -} - -void Lookahead::to_builder(const std::string &chipdb_hash, lookahead_storage::Lookahead::Builder builder) const -{ - builder.setChipdbHash(chipdb_hash); - - auto input_out = builder.initInputSiteWires(input_site_wires.size()); - auto in = input_site_wires.begin(); - for (auto out = input_out.begin(); out != input_out.end(); ++out, ++in) { - NPNR_ASSERT(in != input_site_wires.end()); - - const TypeWireId &key = in->first; - key.to_builder(out->getKey()); - - const std::vector &costs = in->second; - auto value = out->initValue(costs.size()); - - auto value_in = costs.begin(); - for (auto value_out = value.begin(); value_out != value.end(); ++value_out, ++value_in) { - value_in->route_to.to_builder(value_out->getRouteTo()); - value_out->setCost(value_in->cost); - } - } - - auto output_out = builder.initOutputSiteWires(output_site_wires.size()); - auto out = output_site_wires.begin(); - for (auto out2 = output_out.begin(); out2 != output_out.end(); ++out, ++out2) { - NPNR_ASSERT(out != output_site_wires.end()); - - const TypeWireId &key = out->first; - key.to_builder(out2->getKey()); - - const TypeWireId &cheapest_route_from = out->second.cheapest_route_from; - cheapest_route_from.to_builder(out2->getCheapestRouteFrom()); - - out2->setCost(out->second.cost); - } - - auto site_out = builder.initSiteToSiteCost(site_to_site_cost.size()); - auto site = site_to_site_cost.begin(); - for (auto out2 = site_out.begin(); out2 != site_out.end(); ++out2, ++site) { - NPNR_ASSERT(site != site_to_site_cost.end()); - - const TypeWirePair &key = site->first; - key.to_builder(out2->getKey()); - out2->setCost(site->second); - } - - cost_map.to_builder(builder.getCostMap()); -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/lookahead.h b/fpga_interchange/lookahead.h deleted file mode 100644 index 9655e13e..00000000 --- a/fpga_interchange/lookahead.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef LOOKAHEAD_H -#define LOOKAHEAD_H - -#include -#include - -#include "cost_map.h" -#include "deterministic_rng.h" -#include "lookahead.capnp.h" -#include "nextpnr_namespaces.h" -#include "type_wire.h" - -NEXTPNR_NAMESPACE_BEGIN - -// Lookahead is a routing graph generic lookahead builder and evaluator. -// -// The lookahead data model is structured into 3 parts: -// - Output site wires to routing network cost -// - Routing network point to point cost -// - Routing network cost to input site wires -// -// If the lookahead is invoked from a routing wire to a routing wire, only -// the point to point cost is used. -// -// If the lookahead is invoked from an output site wire to a routing wire, -// the point to point cost is computed using the cheapest output routing wire -// from the current site wire and then returned cost is the sum of the output -// cost plus the point to point routing network cost. -// -// If the lookahead is invoked from a routing wire to an input site wire, -// then the cost is the point to point routing cost to the cheapest input -// routing wire plus the input routing cost. -// -// If the lookahead is invoked from an output site wire to an input site wire, -// then cost is the sum of each of the 3 parts. -struct Lookahead -{ - void init(const Context *, DeterministicRNG *rng); - void build_lookahead(const Context *, DeterministicRNG *rng); - - bool read_lookahead(const std::string &chipdb_hash, const std::string &file); - void write_lookahead(const std::string &chipdb_hash, const std::string &file) const; - bool from_reader(const std::string &chipdb_hash, lookahead_storage::Lookahead::Reader reader); - void to_builder(const std::string &chipdb_hash, lookahead_storage::Lookahead::Builder builder) const; - - delay_t estimateDelay(const Context *, WireId src, WireId dst) const; - - struct InputSiteWireCost - { - // This wire is the cheapest non-site wire that leads to this site - // wire. - TypeWireId route_to; - - // This is the cost from the cheapest_route_to wire to the site wire in - // question. - delay_t cost; - }; - - struct OutputSiteWireCost - { - // This wire is the cheapest non-site wire that is reachable from - // this site wire. - TypeWireId cheapest_route_from; - - // This is the cost from the site wire in question to - // cheapest_route_from wire. - delay_t cost; - }; - - dict> input_site_wires; - dict output_site_wires; - dict site_to_site_cost; - CostMap cost_map; -}; - -NEXTPNR_NAMESPACE_END - -#endif /* LOOKAHEAD_H */ diff --git a/fpga_interchange/luts.cc b/fpga_interchange/luts.cc deleted file mode 100644 index 009b601e..00000000 --- a/fpga_interchange/luts.cc +++ /dev/null @@ -1,554 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "luts.h" - -#include "log.h" -#include "nextpnr.h" - -#include "site_lut_mapping_cache.h" - -// #define DEBUG_LUT_ROTATION - -NEXTPNR_NAMESPACE_BEGIN - -bool rotate_and_merge_lut_equation(std::vector *result, const LutBel &lut_bel, - const DynamicBitarray<> &old_equation, const std::vector &pin_map, - uint32_t used_pins) -{ - // pin_map maps pin indicies from the old pin to the new pin. - // So a reversal of a LUT4 would have a pin map of: - // pin_map[0] = 3; - // pin_map[1] = 2; - // pin_map[2] = 1; - // pin_map[3] = 0; - - size_t bel_width = 1 << lut_bel.pins.size(); - for (size_t bel_address = 0; bel_address < bel_width; ++bel_address) { - bool address_reachable = true; - size_t cell_address = 0; - for (size_t bel_pin_idx = 0; bel_pin_idx < lut_bel.pins.size(); ++bel_pin_idx) { - // This address line is 0, so don't translate this bit to the cell - // address. - if ((bel_address & (1 << bel_pin_idx)) == 0) { - if ((used_pins & (1 << bel_pin_idx)) == 0) { - address_reachable = false; - break; - } - - continue; - } - - auto cell_pin_idx = pin_map[bel_pin_idx]; - - // Is this BEL pin used for this cell? - if (cell_pin_idx < 0) { - // This BEL pin is not used for the LUT cell, skip - continue; - } - - cell_address |= (1 << cell_pin_idx); - } - - if (!address_reachable) { - continue; - } - - bel_address += lut_bel.low_bit; - if (old_equation.get(cell_address)) { - if ((*result)[bel_address] == LL_Zero) { - // Output equation has a conflict! - return false; - } - - (*result)[bel_address] = LL_One; - } else { - if ((*result)[bel_address] == LL_One) { - // Output equation has a conflict! - return false; - } - (*result)[bel_address] = LL_Zero; - } - } - - return true; -} - -static constexpr bool kCheckOutputEquation = true; - -struct LutPin -{ - struct LutPinUser - { - size_t cell_idx; - size_t cell_pin_idx; - }; - - const NetInfo *net = nullptr; - std::vector users; - - int32_t min_pin = -1; - int32_t max_pin = -1; - - void add_user(const LutBel &lut_bel, size_t cell_idx, size_t cell_pin_idx) - { - if (min_pin < 0) { - min_pin = lut_bel.min_pin; - max_pin = lut_bel.max_pin; - } - - min_pin = std::max(min_pin, lut_bel.min_pin); - max_pin = std::min(max_pin, lut_bel.max_pin); - - users.emplace_back(); - users.back().cell_idx = cell_idx; - users.back().cell_pin_idx = cell_pin_idx; - } - - bool operator<(const LutPin &other) const { return max_pin < other.max_pin; } -}; - -const std::string LutCell::nameOfPinConnection(LutCell::PinConnection conn) -{ - switch (conn) { - case PinConnection::Unconnected: - return std::string("unconnected"); - case PinConnection::Gnd: - return std::string("Gnd"); - case PinConnection::Vcc: - return std::string("Vcc"); - case PinConnection::Const: - return std::string("Const"); - case PinConnection::Signal: - return std::string("Signal"); - default: - // Should never happen - NPNR_ASSERT_FALSE("Invalid value of LutCell::PinConnection"); - return std::string(); - } -} - -uint32_t LutMapper::check_wires(const Context *ctx) const -{ - // Unlike the 3 argument version of check_wires, this version needs to - // calculate following data based on current cell pin mapping, etc: - // - // - Index map from bel pins to cell pins, -1 for unmapped - // - Mask of used pins - // - Vector of unused LUT BELs. - - uint32_t used_pins = 0; - - std::vector> bel_to_cell_pin_remaps; - std::vector lut_bels; - bel_to_cell_pin_remaps.resize(cells.size()); - lut_bels.resize(cells.size()); - for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { - const CellInfo *cell = cells[cell_idx]; - - auto &bel_data = bel_info(ctx->chip_info, cell->bel); - IdString bel_name(bel_data.name); - auto &lut_bel = element.lut_bels.at(bel_name); - lut_bels[cell_idx] = &lut_bel; - - bel_to_cell_pin_remaps[cell_idx].resize(lut_bel.pins.size(), -1); - - for (size_t pin_idx = 0; pin_idx < cell->lut_cell.pins.size(); ++pin_idx) { - IdString lut_cell_pin = cell->lut_cell.pins[pin_idx]; - const std::vector bel_pins = cell->cell_bel_pins.at(lut_cell_pin); - NPNR_ASSERT(bel_pins.size() == 1); - - size_t bel_pin_idx = lut_bel.pin_to_index.at(bel_pins[0]); - bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] = pin_idx; - used_pins |= (1 << bel_pin_idx); - } - } - - pool blocked_luts; - return check_wires(bel_to_cell_pin_remaps, lut_bels, used_pins, &blocked_luts); -} - -uint32_t LutMapper::check_wires(const std::vector> &bel_to_cell_pin_remaps, - const std::vector &lut_bels, uint32_t used_pins, - pool *blocked_luts) const -{ - std::vector unused_luts; - for (auto &lut_bel_pair : element.lut_bels) { - if (std::find(lut_bels.begin(), lut_bels.end(), &lut_bel_pair.second) == lut_bels.end()) { - unused_luts.push_back(&lut_bel_pair.second); - blocked_luts->emplace(&lut_bel_pair.second); - } - } - - uint32_t pin_mask = 0; - - DynamicBitarray<> wire_equation; - wire_equation.resize(2); - wire_equation.set(0, false); - wire_equation.set(1, true); - - std::vector wire_bel_to_cell_pin_map; - std::vector equation_result; - for (int32_t pin_idx = 0; pin_idx < (int32_t)element.pins.size(); ++pin_idx) { - if (used_pins & (1 << pin_idx)) { - // This pin is already used, so it cannot be used for a wire. - continue; - } - - bool valid_pin_for_wire = false; - bool invalid_pin_for_wire = false; - - for (const LutBel *lut_bel : unused_luts) { - if (pin_idx < lut_bel->min_pin) { - continue; - } - - if (pin_idx > lut_bel->max_pin) { - continue; - } - - wire_bel_to_cell_pin_map.clear(); - wire_bel_to_cell_pin_map.resize(lut_bel->pins.size(), -1); - wire_bel_to_cell_pin_map[lut_bel->pin_to_index.at(element.pins[pin_idx])] = 0; - - equation_result.clear(); - equation_result.resize(element.width, LL_DontCare); - - uint32_t used_pins_with_wire = used_pins | (1 << pin_idx); - - for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { - const CellInfo *cell = cells[cell_idx]; - auto &lut_bel_for_cell = *lut_bels[cell_idx]; - if (!rotate_and_merge_lut_equation(&equation_result, lut_bel_for_cell, cell->lut_cell.equation, - bel_to_cell_pin_remaps[cell_idx], used_pins_with_wire)) { - invalid_pin_for_wire = true; - break; - } - } - - if (invalid_pin_for_wire) { - break; - } - - if (rotate_and_merge_lut_equation(&equation_result, *lut_bel, wire_equation, wire_bel_to_cell_pin_map, - used_pins_with_wire)) { - valid_pin_for_wire = true; - blocked_luts->erase(lut_bel); - } - } - - bool good_for_wire = valid_pin_for_wire && !invalid_pin_for_wire; - if (!good_for_wire) { - pin_mask |= (1 << pin_idx); - } - } - - return pin_mask; -} - -bool LutMapper::remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping, - pool *blocked_luts) -{ - dict lut_pin_map; - std::vector lut_bels; - lut_bels.resize(cells.size()); - - for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { - const CellInfo *cell = cells[cell_idx]; -#ifdef DEBUG_LUT_ROTATION - log_info("Mapping %s %s eq = %s at %s\n", cell->type.c_str(ctx), cell->name.c_str(ctx), - cell->params.at(ctx->id("INIT")).c_str(), ctx->nameOfBel(cell->bel)); -#endif - - auto &bel_data = bel_info(ctx->chip_info, cell->bel); - IdString bel_name(bel_data.name); - auto &lut_bel = element.lut_bels.at(bel_name); - lut_bels[cell_idx] = &lut_bel; - - for (size_t pin_idx = 0; pin_idx < cell->lut_cell.pins.size(); ++pin_idx) { - IdString lut_pin_name = cell->lut_cell.pins[pin_idx]; - const PortInfo &port_info = cell->ports.at(lut_pin_name); - NPNR_ASSERT(port_info.net != nullptr); - - auto result = lut_pin_map.emplace(port_info.net, LutPin()); - LutPin &lut_pin = result.first->second; - lut_pin.net = port_info.net; - lut_pin.add_user(lut_bel, cell_idx, pin_idx); - } - } - - if (lut_pin_map.size() > element.pins.size()) { - // Trival conflict, more nets entering element than pins are - // available! -#ifdef DEBUG_LUT_ROTATION - log_info("Trival failure %zu > %zu, %zu %zu\n", lut_pin_map.size(), element.pins.size(), element.width, - element.lut_bels.size()); -#endif - return false; - } - - std::vector lut_pins; - lut_pins.reserve(lut_pin_map.size()); - for (auto lut_pin_pair : lut_pin_map) { - lut_pins.push_back(std::move(lut_pin_pair.second)); - } - lut_pin_map.clear(); - std::sort(lut_pins.begin(), lut_pins.end()); - - std::vector> cell_to_bel_pin_remaps; - std::vector> bel_to_cell_pin_remaps; - cell_to_bel_pin_remaps.resize(cells.size()); - bel_to_cell_pin_remaps.resize(cells.size()); - for (size_t i = 0; i < cells.size(); ++i) { - cell_to_bel_pin_remaps[i].resize(cells[i]->lut_cell.pins.size()); - bel_to_cell_pin_remaps[i].resize(lut_bels[i]->pins.size(), -1); - } - - uint32_t used_pins = 0; - size_t idx = 0; - std::vector net_pins(lut_pins.size()); - for (auto &lut_pin : lut_pins) { - size_t net_idx = idx++; - used_pins |= (1 << net_idx); - - for (auto cell_pin_idx : lut_pin.users) { - size_t cell_idx = cell_pin_idx.cell_idx; - size_t pin_idx = cell_pin_idx.cell_pin_idx; - IdString bel_pin = lut_bels[cell_idx]->pins[net_idx]; -#ifdef DEBUG_LUT_ROTATION - log_info("%s %s %s => %s (%s)\n", cells[cell_idx]->type.c_str(ctx), cells[cell_idx]->name.c_str(ctx), - cells[cell_idx]->lut_cell.pins[pin_idx].c_str(ctx), bel_pin.c_str(ctx), - lut_pin.net->name.c_str(ctx)); -#endif - if (net_pins[net_idx] == IdString()) { - net_pins[net_idx] = bel_pin; - } else { - NPNR_ASSERT(net_pins[net_idx] == bel_pin); - } - - cell_to_bel_pin_remaps[cell_idx][pin_idx] = net_idx; - bel_to_cell_pin_remaps[cell_idx][net_idx] = pin_idx; - } - } - - // Try to see if the equations are mergable! - std::vector equation_result; - equation_result.resize(element.width, LL_DontCare); - for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { - const CellInfo *cell = cells[cell_idx]; - auto &lut_bel = *lut_bels[cell_idx]; - if (!rotate_and_merge_lut_equation(&equation_result, lut_bel, cell->lut_cell.equation, - bel_to_cell_pin_remaps[cell_idx], used_pins)) { -#ifdef DEBUG_LUT_ROTATION - log_info("Failed to find a solution!\n"); - for (auto *cell : cells) { - log_info("%s %s : %s\b\n", cell->type.c_str(ctx), cell->name.c_str(ctx), - cell->params.at(ctx->id("INIT")).c_str()); - } -#endif - return false; - } - } - -#ifdef DEBUG_LUT_ROTATION - log_info("Found a solution!\n"); -#endif - - // Sanity check final equation to make sure no assumptions are violated. - if (kCheckOutputEquation) { - for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { - CellInfo *cell = cells[cell_idx]; - auto &lut_bel = *lut_bels[cell_idx]; - - dict cell_to_bel_map; - for (size_t pin_idx = 0; pin_idx < cell->lut_cell.pins.size(); ++pin_idx) { - size_t bel_pin_idx = cell_to_bel_pin_remaps[cell_idx][pin_idx]; - NPNR_ASSERT(bel_pin_idx < lut_bel.pins.size()); - cell_to_bel_map[cell->lut_cell.pins[pin_idx]] = lut_bel.pins[bel_pin_idx]; - } - - check_equation(cell->lut_cell, cell_to_bel_map, lut_bel, equation_result, used_pins); - } - } - - // Not all LUT inputs are used - uint32_t pin_mask = 0; - if (cells.size() != element.lut_bels.size()) { - pin_mask = check_wires(bel_to_cell_pin_remaps, lut_bels, used_pins, blocked_luts); - } - -#if defined(DEBUG_LUT_ROTATION) - log_info("Cell bindings:\n"); - for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { - CellInfo *cell = cells[cell_idx]; - log_info(" - %s => %s\n", ctx->nameOfBel(cell->bel), cell->name.c_str(ctx)); - } -#endif - - // Fill in the LUT mapping result - - // Push new cell -> BEL pin maps out to cells now that equations have been - // verified! - lut_mapping->cells.reserve(cells.size()); - for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { - CellInfo *cellInfo = cells[cell_idx]; - auto &lutBel = *lut_bels[cell_idx]; - - // Add the cell data - SiteLutMappingResult::Cell cell; - cell.belIndex = cellInfo->bel.index; - - // Cell to BEL pin map - for (size_t pin_idx = 0; pin_idx < cellInfo->lut_cell.pins.size(); ++pin_idx) { - IdString cellPin = cellInfo->lut_cell.pins[pin_idx]; - IdString belPin = lutBel.pins[cell_to_bel_pin_remaps[cell_idx][pin_idx]]; - cell.belPins[cellPin] = belPin; - } - - cell.lutCell.pin_connections.clear(); - - // All LUT inputs used - if (cells.size() == element.lut_bels.size()) { - for (size_t bel_pin_idx = 0; bel_pin_idx < lutBel.pins.size(); ++bel_pin_idx) { - if ((used_pins & (1 << bel_pin_idx)) == 0) { - NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1); - cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Const); - } else { - cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Signal); - } - } - } - // Only some LUT inputs used - else { - for (size_t bel_pin_idx = 0; bel_pin_idx < lutBel.pins.size(); ++bel_pin_idx) { - if ((pin_mask & (1 << bel_pin_idx)) != 0) { - NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1); - auto pin = lutBel.pins.at(bel_pin_idx); - cell.lutCell.pin_connections.emplace(pin, LutCell::PinConnection::Const); - } else { - cell.lutCell.pin_connections.emplace(lutBel.pins.at(bel_pin_idx), LutCell::PinConnection::Signal); - } - } - } - -#if defined(DEBUG_LUT_ROTATION) - log_info("Pin connections for LUT cell %s:\n", cellInfo->name.c_str(ctx)); - for (const auto &it : cell.lutCell.pin_connections) { - log_info(" - %s : %s\n", it.first.c_str(ctx), LutCell::nameOfPinConnection(it.second).c_str()); - } -#endif - - lut_mapping->cells.push_back(cell); - } - - return true; -} - -void check_equation(const LutCell &lut_cell, const dict &cell_to_bel_map, const LutBel &lut_bel, - const std::vector &equation, uint32_t used_pins) -{ - std::vector pin_map; - pin_map.resize(lut_bel.pins.size(), -1); - - NPNR_ASSERT(lut_cell.pins.size() < (size_t)std::numeric_limits::max()); - - for (size_t cell_pin_idx = 0; cell_pin_idx < lut_cell.pins.size(); ++cell_pin_idx) { - IdString cell_pin = lut_cell.pins[cell_pin_idx]; - IdString bel_pin = cell_to_bel_map.at(cell_pin); - size_t bel_pin_idx = lut_bel.pin_to_index.at(bel_pin); - - pin_map[bel_pin_idx] = cell_pin_idx; - } - - // Iterate over all BEL addresses in the LUT, and ensure that the original - // LUT equation is respected. - size_t bel_width = 1 << lut_bel.pins.size(); - NPNR_ASSERT(lut_bel.low_bit + bel_width == lut_bel.high_bit + 1); - for (size_t bel_address = 0; bel_address < bel_width; ++bel_address) { - LogicLevel level = equation[bel_address + lut_bel.low_bit]; - - bool address_reachable = true; - size_t cell_address = 0; - for (size_t bel_pin_idx = 0; bel_pin_idx < lut_bel.pins.size(); ++bel_pin_idx) { - // This address line is 0, so don't translate this bit to the cell - // address. - if ((bel_address & (1 << bel_pin_idx)) == 0) { - // This pin is unused, so the line will be tied high, this - // address is unreachable. - if ((used_pins & (1 << bel_pin_idx)) == 0) { - address_reachable = false; - break; - } - continue; - } - - auto cell_pin_idx = pin_map[bel_pin_idx]; - - // Is this BEL pin used for this cell? - if (cell_pin_idx < 0) { - // This BEL pin is not used for the LUT cell, skip - continue; - } - - cell_address |= (1 << cell_pin_idx); - } - - if (!address_reachable) { - continue; - } - - if (lut_cell.equation.get(cell_address)) { - NPNR_ASSERT(level == LL_One); - } else { - NPNR_ASSERT(level == LL_Zero); - } - } -} - -void LutElement::compute_pin_order() -{ - pins.clear(); - pin_to_index.clear(); - - for (auto &lut_bel_pair : lut_bels) { - auto &lut_bel = lut_bel_pair.second; - - for (size_t pin_idx = 0; pin_idx < lut_bel.pins.size(); ++pin_idx) { - IdString pin = lut_bel.pins[pin_idx]; - auto result = pin_to_index.emplace(pin, pin_idx); - if (!result.second) { - // Not sure when this isn't true, but check it for now! - NPNR_ASSERT(result.first->second == pin_idx); - } - } - } - - pins.resize(pin_to_index.size()); - for (auto &pin_pair : pin_to_index) { - pins.at(pin_pair.second) = pin_pair.first; - } - - for (auto &lut_bel_pair : lut_bels) { - auto &lut_bel = lut_bel_pair.second; - lut_bel.min_pin = pin_to_index.at(lut_bel.pins.front()); - lut_bel.max_pin = pin_to_index.at(lut_bel.pins.back()); - } -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/luts.h b/fpga_interchange/luts.h deleted file mode 100644 index 892f457c..00000000 --- a/fpga_interchange/luts.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef LUTS_H -#define LUTS_H - -#include "idstring.h" -#include "nextpnr_namespaces.h" - -#include "dynamic_bitarray.h" -#include "hashlib.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct CellInfo; -struct Context; - -struct SiteLutMappingResult; - -enum LogicLevel -{ - LL_Zero, - LL_One, - LL_DontCare -}; - -struct LutCell -{ - enum class PinConnection - { - Unconnected, - Gnd, - Vcc, - Const, - Signal - }; - - // LUT cell pins for equation, LSB first. - std::vector pins; - pool lut_pins; - dict pin_connections; - DynamicBitarray<> equation; - - static const std::string nameOfPinConnection(PinConnection conn); -}; - -struct LutBel -{ - IdString name; - - // LUT BEL pins to LUT array index. - std::vector pins; - dict pin_to_index; - - IdString output_pin; - - // What part of the LUT equation does this LUT output use? - // This assumes contiguous LUT bits. - uint32_t low_bit; - uint32_t high_bit; - - int32_t min_pin; - int32_t max_pin; -}; - -struct SiteLutMapping -{ - struct LutCellMapping - { - LutCell lut_cell; - }; -}; - -// Work forward from cell definition and cell -> bel pin map and check that -// equation is valid. -void check_equation(const LutCell &lut_cell, const dict &cell_to_bel_map, const LutBel &lut_bel, - const std::vector &equation, uint32_t used_pins); - -struct LutElement -{ - size_t width; - dict lut_bels; - - void compute_pin_order(); - - std::vector pins; - dict pin_to_index; -}; - -struct LutMapper -{ - LutMapper(const LutElement &element) : element(element) {} - const LutElement &element; - - std::vector cells; - - bool remap_luts(const Context *ctx, SiteLutMappingResult *lut_mapping, - pool *blocked_luts); - - // Determine which wires given the current mapping must be tied to the - // default constant. - // - // Returns a bit mask, 1 meaning it must be tied. Otherwise means that - // the pin is free to be a signal. - uint32_t check_wires(const std::vector> &bel_to_cell_pin_remaps, - const std::vector &lut_bels, uint32_t used_pins, - pool *blocked_luts) const; - - // Version of check_wires that uses current state of cells based on pin - // mapping in cells variable. - uint32_t check_wires(const Context *ctx) const; -}; - -// Rotate and merge a LUT equation into an array of levels. -// -// If a conflict arises, return false and result is in an indeterminate state. -bool rotate_and_merge_lut_equation(std::vector *result, const LutBel &lut_bel, - const DynamicBitarray<> &old_equation, const std::vector &pin_map, - uint32_t used_pins); - -NEXTPNR_NAMESPACE_END - -#endif /* LUTS_H */ diff --git a/fpga_interchange/macros.cc b/fpga_interchange/macros.cc deleted file mode 100644 index cc67833a..00000000 --- a/fpga_interchange/macros.cc +++ /dev/null @@ -1,180 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "design_utils.h" -#include "log.h" -#include "nextpnr.h" -#include "util.h" - -NEXTPNR_NAMESPACE_BEGIN - -static const MacroPOD *lookup_macro(const ChipInfoPOD *chip, IdString cell_type) -{ - for (const auto ¯o : chip->macros) { - if (IdString(macro.name) == cell_type) - return ¯o; - } - return nullptr; -} - -static const MacroExpansionPOD *lookup_macro_rules(const ChipInfoPOD *chip, IdString cell_type) -{ - for (const auto &rule : chip->macro_rules) { - if (IdString(rule.prim_name) == cell_type) - return &rule; - } - return nullptr; -} - -static IdString derived_name(Context *ctx, IdString base_name, IdString suffix) -{ - return ctx->idf("%s/%s", base_name.c_str(ctx), suffix.c_str(ctx)); -} - -void Arch::expand_macros() -{ - log_info("Expand macros\n"); - // Make up a list of cells, so we don't have modify-while-iterating issues - Context *ctx = getCtx(); - std::vector cells; - for (auto &cell : ctx->cells) - cells.push_back(cell.second.get()); - - std::vector next_cells; - - bool first_iter = false; - do { - // Expand cells - for (auto cell : cells) { - // TODO: consult exception map - const MacroExpansionPOD *exp = lookup_macro_rules(chip_info, cell->type); - - // Block infinite expansion loop due to a macro being expanded in the same primitive. - // E.g.: OBUFTDS expands into the following cells, with an infinite loop being generated: - // - 2 OBUFTDS - // - 1 INV - if (exp && first_iter) - continue; - - const MacroPOD *macro = lookup_macro(chip_info, exp ? IdString(exp->macro_name) : cell->type); - if (macro == nullptr) - continue; - - // Get the ultimate root of this macro expansion - IdString parent = (cell->macro_parent == IdString()) ? cell->name : cell->macro_parent; - // Create child instances - for (const auto &inst : macro->cell_insts) { - CellInfo *inst_cell = - ctx->createCell(derived_name(ctx, cell->name, IdString(inst.name)), IdString(inst.type)); - for (const auto ¶m : inst.parameters) { - inst_cell->params[IdString(param.key)] = IdString(param.value).str(ctx); - } - inst_cell->macro_parent = parent; - next_cells.push_back(inst_cell); - } - // Create and connect nets - for (const auto &net_data : macro->nets) { - NetInfo *net = nullptr; - // If there is a top level port, use that as the net - for (const auto &net_port : net_data.ports) { - if (net_port.instance != 0) - continue; - // TODO: case of multiple top level ports on the same net? - NPNR_ASSERT(net == nullptr); - // Use the corresponding pre-expansion port net - net = cell->getPort(IdString(net_port.port)); - // Disconnect the original port pre-expansion - cell->disconnectPort(IdString(net_port.port)); - } - // If not on a top level port, create a new net - if (net == nullptr) - net = ctx->createNet(derived_name(ctx, cell->name, IdString(net_data.name))); - // Create and connect instance ports - for (const auto &net_port : net_data.ports) { - if (net_port.instance == 0) - continue; - IdString port_name(net_port.port); - CellInfo *inst_cell = - ctx->cells.at(derived_name(ctx, cell->name, IdString(net_port.instance))).get(); - inst_cell->ports[port_name].name = port_name; - inst_cell->ports[port_name].type = PortType(net_port.dir); - inst_cell->connectPort(port_name, net); - } - } - - if (exp != nullptr) { - // Convert parameters, according to the exception rules - for (const auto ¶m_rule : exp->param_rules) { - IdString prim_param(param_rule.prim_param); - if (!cell->params.count(prim_param)) - continue; - const auto &prim_param_val = cell->params.at(prim_param); - IdString inst_name = derived_name(ctx, cell->name, IdString(param_rule.inst_name)); - CellInfo *inst_cell = ctx->cells.at(inst_name).get(); - IdString inst_param(param_rule.inst_param); - if (param_rule.rule_type == PARAM_MAP_COPY) { - inst_cell->params[inst_param] = prim_param_val; - } else if (param_rule.rule_type == PARAM_MAP_SLICE) { - auto prim_bits = cell_parameters.parse_int_like(ctx, cell->type, prim_param, prim_param_val); - Property value(0, param_rule.slice_bits.ssize()); - for (int i = 0; i < param_rule.slice_bits.ssize(); i++) { - size_t bit = param_rule.slice_bits[i]; - if (bit >= prim_bits.size()) - continue; - value.str.at(i) = prim_bits.get(bit) ? Property::S1 : Property::S0; - } - inst_cell->params[inst_param] = value; - } else if (param_rule.rule_type == PARAM_MAP_TABLE) { - const std::string &prim_str = prim_param_val.as_string(); - IdString prim_id = ctx->id(prim_str); - for (auto &tbl_entry : param_rule.map_table) { - if (IdString(tbl_entry.key) == prim_id) { - inst_cell->params[inst_param] = IdString(tbl_entry.value).str(ctx); - break; - } - } - if (!inst_cell->params.count(inst_param)) - log_error("Unsupported value '%s' for property '%s' of cell %s:%s\n", prim_str.c_str(), - ctx->nameOf(prim_param), ctx->nameOf(cell), ctx->nameOf(cell->type)); - } - } - } - - // Remove the now-expanded cell, but first make sure we don't leave behind any dangling references - for (const auto &port : cell->ports) - if (port.second.net != nullptr) - log_error("Macro expansion of %s:%s left dangling port %s.", ctx->nameOf(cell), - ctx->nameOf(cell->type), ctx->nameOf(port.first)); - ctx->cells.erase(cell->name); - } - - // Iterate until no more expansions are possible - // The next iteration only needs to look at cells created in this iteration - std::swap(next_cells, cells); - next_cells.clear(); - - first_iter = true; - } while (!cells.empty()); - // Do this at the end, otherwise we might add cells that are later destroyed - for (auto &cell : ctx->cells) - macro_to_cells[cell.second->macro_parent].push_back(cell.second.get()); -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/main.cc b/fpga_interchange/main.cc deleted file mode 100644 index 5423c17d..00000000 --- a/fpga_interchange/main.cc +++ /dev/null @@ -1,126 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Claire Xenia Wolf - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifdef MAIN_EXECUTABLE - -#include -#include - -#include "command.h" -#include "design_utils.h" -#include "jsonwrite.h" -#include "log.h" -#include "timing.h" - -USING_NEXTPNR_NAMESPACE - -class FpgaInterchangeCommandHandler : public CommandHandler -{ - public: - FpgaInterchangeCommandHandler(int argc, char **argv); - virtual ~FpgaInterchangeCommandHandler(){}; - std::unique_ptr createContext(dict &values) override; - void setupArchContext(Context *ctx) override{}; - void customBitstream(Context *ctx) override; - void customAfterLoad(Context *ctx) override; - - protected: - po::options_description getArchOptions() override; -}; - -FpgaInterchangeCommandHandler::FpgaInterchangeCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {} - -po::options_description FpgaInterchangeCommandHandler::getArchOptions() -{ - po::options_description specific("Architecture specific options"); - specific.add_options()("chipdb", po::value(), "name of chip database binary"); - specific.add_options()("xdc", po::value>(), "XDC-style constraints file to read"); - specific.add_options()("netlist", po::value(), "FPGA interchange logical netlist to read"); - specific.add_options()("phys", po::value(), "FPGA interchange Physical netlist to write"); - specific.add_options()("package", po::value(), "Package to use"); - specific.add_options()("rebuild-lookahead", "Ignore lookahead cache and rebuild"); - specific.add_options()("dont-write-lookahead", "Don't write the lookahead file"); - specific.add_options()("disable-lut-mapping-cache", "Disable caching of LUT mapping solutions in site router"); - - return specific; -} - -void FpgaInterchangeCommandHandler::customBitstream(Context *ctx) -{ - if (vm.count("phys")) { - std::string filename = vm["phys"].as(); - ctx->write_physical_netlist(filename); - } -} - -std::unique_ptr FpgaInterchangeCommandHandler::createContext(dict &values) -{ - auto start = std::chrono::high_resolution_clock::now(); - - ArchArgs chipArgs; - chipArgs.rebuild_lookahead = vm.count("rebuild_lookahead") != 0; - chipArgs.dont_write_lookahead = vm.count("dont_write_lookahead") != 0; - chipArgs.disable_lut_mapping_cache = vm.count("disable-lut-mapping-cache") != 0; - - if (!vm.count("chipdb")) { - log_error("chip database binary must be provided\n"); - } - chipArgs.chipdb = vm["chipdb"].as(); - if (vm.count("package")) { - chipArgs.package = vm["package"].as(); - } - - auto ctx = std::unique_ptr(new Context(chipArgs)); - - if (vm.count("verbose")) { - ctx->verbose = true; - } - if (vm.count("debug")) { - ctx->verbose = true; - ctx->debug = true; - } - - ctx->init(); - - if (vm.count("netlist")) { - ctx->read_logical_netlist(vm["netlist"].as()); - } - - if (vm.count("xdc")) { - for (auto &x : vm["xdc"].as>()) { - ctx->parse_xdc(x); - } - } - - auto end = std::chrono::high_resolution_clock::now(); - log_info("createContext time %.02fs\n", std::chrono::duration(end - start).count()); - - return ctx; -} - -void FpgaInterchangeCommandHandler::customAfterLoad(Context *ctx) {} - -int main(int argc, char *argv[]) -{ - FpgaInterchangeCommandHandler handler(argc, argv); - return handler.exec(); -} - -#endif diff --git a/fpga_interchange/pseudo_pip_model.cc b/fpga_interchange/pseudo_pip_model.cc deleted file mode 100644 index 0b767369..00000000 --- a/fpga_interchange/pseudo_pip_model.cc +++ /dev/null @@ -1,509 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "pseudo_pip_model.h" - -#include "context.h" - -// #define DEBUG_PSEUDO_PIP - -NEXTPNR_NAMESPACE_BEGIN - -void PseudoPipData::init_tile_type(const Context *ctx, int32_t tile_type) -{ - if (max_pseudo_pip_for_tile_type.count(tile_type)) { - return; - } - - const TileTypeInfoPOD &type_data = ctx->chip_info->tile_types[tile_type]; - int32_t max_pseudo_pip_index = -1; - for (int32_t pip_idx = 0; pip_idx < type_data.pip_data.ssize(); ++pip_idx) { - const PipInfoPOD &pip_data = type_data.pip_data[pip_idx]; - if (pip_data.pseudo_cell_wires.size() == 0) { - continue; - } - - if (pip_idx > max_pseudo_pip_index) { - max_pseudo_pip_index = pip_idx; - } - - pool sites; - std::vector pseudo_pip_bels; - for (int32_t wire_index : pip_data.pseudo_cell_wires) { - const TileWireInfoPOD &wire_data = type_data.wire_data[wire_index]; - if (wire_data.site == -1) { - continue; - } - - // Only use primary site types for psuedo pips - // - // Note: This assumption may be too restrictive. If so, then - // need to update database generators to provide - // pseudo_cell_wires for each site type, not just the primary. - if (wire_data.site_variant != -1) { - continue; - } - - sites.emplace(wire_data.site); - - for (const BelPortPOD &bel_pin : wire_data.bel_pins) { - const BelInfoPOD &bel_data = type_data.bel_data[bel_pin.bel_index]; - if (bel_data.synthetic != NOT_SYNTH) { - // Ignore synthetic BELs - continue; - } - - if (bel_data.category != BEL_CATEGORY_LOGIC) { - // Ignore site ports and site routing - continue; - } - - int32_t bel_pin_idx = -1; - for (int32_t i = 0; i < bel_data.num_bel_wires; ++i) { - if (bel_data.ports[i] == bel_pin.port) { - bel_pin_idx = i; - break; - } - } - - NPNR_ASSERT(bel_pin_idx != -1); - if (bel_data.types[bel_pin_idx] != PORT_OUT) { - // Only care about output ports. Input ports may not be - // part of the pseudo pip. - continue; - } - - PseudoPipBel bel; - bel.bel_index = bel_pin.bel_index; - bel.output_bel_pin = bel_pin_idx; - pseudo_pip_bels.push_back(bel); - } - } - - std::pair key{tile_type, pip_idx}; - std::vector &sites_for_pseudo_pip = possibles_sites_for_pip[key]; - sites_for_pseudo_pip.clear(); - sites_for_pseudo_pip.insert(sites_for_pseudo_pip.begin(), sites.begin(), sites.end()); - std::sort(sites_for_pseudo_pip.begin(), sites_for_pseudo_pip.end()); - - // Initialize "logic_bels_for_pip" for every site that this pseudo pip - // appears. This means that if there are no pseudo_pip_bels, those - // vectors will be empty. - for (int32_t site : sites_for_pseudo_pip) { - logic_bels_for_pip[LogicBelKey{tile_type, pip_idx, site}].clear(); - } - - if (!pseudo_pip_bels.empty()) { - pool pseudo_cell_wires; - pseudo_cell_wires.insert(pip_data.pseudo_cell_wires.begin(), pip_data.pseudo_cell_wires.end()); - - // For each BEL, find the input bel pin used, and attach it to - // the vector for that site. - // - // Note: Intentially copying the bel for mutation, and then - // pushing onto vector. - for (PseudoPipBel bel : pseudo_pip_bels) { - const BelInfoPOD &bel_data = type_data.bel_data[bel.bel_index]; - int32_t site = bel_data.site; - - int32_t input_bel_pin = -1; - int32_t output_bel_pin = -1; - for (int32_t i = 0; i < bel_data.num_bel_wires; ++i) { - if (!pseudo_cell_wires.count(bel_data.wires[i])) { - continue; - } - - if (bel_data.types[i] == PORT_OUT) { - NPNR_ASSERT(output_bel_pin == -1); - output_bel_pin = i; - } - - if (bel_data.types[i] == PORT_IN && input_bel_pin == -1) { - // Take first input BEL pin - // - // FIXME: This heuristic feels fragile. - // This data oaught come from the database. - input_bel_pin = i; - } - } - - NPNR_ASSERT(output_bel_pin == bel.output_bel_pin); - bel.input_bel_pin = input_bel_pin; - - logic_bels_for_pip[LogicBelKey{tile_type, pip_idx, site}].push_back(bel); - } - } - } - - max_pseudo_pip_for_tile_type[tile_type] = max_pseudo_pip_index; -} - -const std::vector &PseudoPipData::get_possible_sites_for_pip(const Context *ctx, PipId pip) const -{ - int32_t tile_type = ctx->chip_info->tiles[pip.tile].type; - return possibles_sites_for_pip.at(std::make_pair(tile_type, pip.index)); -} - -size_t PseudoPipData::get_max_pseudo_pip(int32_t tile_type) const { return max_pseudo_pip_for_tile_type.at(tile_type); } - -const std::vector &PseudoPipData::get_logic_bels_for_pip(const Context *ctx, int32_t site, - PipId pip) const -{ - int32_t tile_type = ctx->chip_info->tiles[pip.tile].type; - return logic_bels_for_pip.at(LogicBelKey{tile_type, pip.index, site}); -} - -void PseudoPipModel::init(Context *ctx, int32_t tile_idx) -{ - int32_t tile_type = ctx->chip_info->tiles[tile_idx].type; - - this->tile = tile_idx; - - allowed_pseudo_pips.resize(ctx->pseudo_pip_data.get_max_pseudo_pip(tile_type) + 1); - allowed_pseudo_pips.fill(true); -} - -void PseudoPipModel::prepare_for_routing(const Context *ctx, const std::vector &sites) -{ - // First determine which sites have placed cells, these sites are consider - // active. - pool active_sites; - for (size_t site = 0; site < sites.size(); ++site) { - if (!sites[site].cells_in_site.empty()) { - active_sites.emplace(site); - } - } - - // Assign each pseudo pip in this tile a site, which is either the active - // site (if the site / alt site is in use) or the first site that pseudo - // pip appears in. - int32_t tile_type = ctx->chip_info->tiles[tile].type; - const TileTypeInfoPOD &type_data = ctx->chip_info->tile_types[tile_type]; - - pseudo_pip_sites.clear(); - site_to_pseudo_pips.clear(); - - for (size_t pip_idx = 0; pip_idx < type_data.pip_data.size(); ++pip_idx) { - const PipInfoPOD &pip_data = type_data.pip_data[pip_idx]; - if (pip_data.pseudo_cell_wires.size() == 0) { - continue; - } - - PipId pip; - pip.tile = tile; - pip.index = pip_idx; - const std::vector &sites = ctx->pseudo_pip_data.get_possible_sites_for_pip(ctx, pip); - - int32_t site_for_pip = -1; - for (size_t possible_site : sites) { - if (active_sites.count(possible_site)) { - site_for_pip = possible_site; - break; - } - } - - if (site_for_pip < 0) { - site_for_pip = sites.at(0); - } - - pseudo_pip_sites[pip_idx] = site_for_pip; - site_to_pseudo_pips[site_for_pip].push_back(pip_idx); - } - - for (auto &site_pair : site_to_pseudo_pips) { - update_site(ctx, site_pair.first); - } -} - -bool PseudoPipModel::checkPipAvail(const Context *ctx, PipId pip) const -{ - bool allowed = allowed_pseudo_pips.get(pip.index); - if (!allowed) { -#ifdef DEBUG_PSEUDO_PIP - if (ctx->verbose) { - log_info("Pseudo pip %s not allowed\n", ctx->nameOfPip(pip)); - } -#endif - } - - return allowed; -} - -void PseudoPipModel::bindPip(const Context *ctx, PipId pip) -{ - // If pseudo_pip_sites is empty, then prepare_for_routing was never - // invoked. This is likely because PseudoPipModel was constructed during - // routing. - if (pseudo_pip_sites.empty()) { - prepare_for_routing(ctx, ctx->tileStatus.at(tile).sites); - } - - // Do not allow pseudo pips to be bound if they are not allowed! - NPNR_ASSERT(allowed_pseudo_pips.get(pip.index)); - - // Mark that this pseudo pip is active. - auto result = active_pseudo_pips.emplace(pip.index); - NPNR_ASSERT(result.second); - - // Update the site this pseudo pip is within. - size_t site = pseudo_pip_sites.at(pip.index); - update_site(ctx, site); -} - -void PseudoPipModel::unbindPip(const Context *ctx, PipId pip) -{ - // It should not be possible for unbindPip to be invoked with - // pseudo_pip_sites being empty. - NPNR_ASSERT(!pseudo_pip_sites.empty()); - - NPNR_ASSERT(active_pseudo_pips.erase(pip.index)); - - // Remove the site this pseudo pip is within. - size_t site = pseudo_pip_sites.at(pip.index); - update_site(ctx, site); -} - -void PseudoPipModel::update_site(const Context *ctx, size_t site) -{ - // update_site consists of several steps: - // - // - Find all BELs within the site used by pseudo pips. - // - Trivially marking other pseudo pips as unavailable if it requires - // logic BELs used by active pseudo pips (or bound by cells). - // - Determine if remaining pseudo pips can be legally placed. This - // generally consists of: - // - Checking LUT element - // - FIXME: Checking constraints (when metadata is available) - - const std::vector pseudo_pips_for_site = site_to_pseudo_pips.at(site); - - std::vector &unused_pseudo_pips = scratch; - unused_pseudo_pips.clear(); - unused_pseudo_pips.reserve(pseudo_pips_for_site.size()); - - dict used_bels; - for (int32_t pseudo_pip : pseudo_pips_for_site) { - if (!active_pseudo_pips.count(pseudo_pip)) { - unused_pseudo_pips.push_back(pseudo_pip); - continue; - } - - PipId pip; - pip.tile = tile; - pip.index = pseudo_pip; - for (const PseudoPipBel &bel : ctx->pseudo_pip_data.get_logic_bels_for_pip(ctx, site, pip)) { - used_bels.emplace(bel.bel_index, bel); - } - } - - if (unused_pseudo_pips.empty()) { - return; - } - - int32_t tile_type = ctx->chip_info->tiles[tile].type; - const TileTypeInfoPOD &type_data = ctx->chip_info->tile_types[tile_type]; - - // This section builds up LUT mapping logic to determine which LUT wires - // are availble and which are not. - const std::vector &lut_elements = ctx->lut_elements.at(tile_type); - std::vector lut_mappers; - lut_mappers.reserve(lut_elements.size()); - for (size_t i = 0; i < lut_elements.size(); ++i) { - lut_mappers.push_back(LutMapper(lut_elements[i])); - } - - const TileStatus &tile_status = ctx->tileStatus.at(tile); - for (CellInfo *cell : tile_status.sites[site].cells_in_site) { - if (cell->lut_cell.pins.empty()) { - continue; - } - - BelId bel = cell->bel; - const auto &bel_data = bel_info(ctx->chip_info, bel); - if (bel_data.lut_element != -1) { - lut_mappers[bel_data.lut_element].cells.push_back(cell); - } - } - - std::vector lut_thru_cells; - lut_thru_cells.reserve(tile_status.sites[site].lut_thrus.size()); - for (auto input_bel_pin : tile_status.sites[site].lut_thrus) { - if (ctx->wire_lut == nullptr) - break; - - BelId bel; - bel.index = input_bel_pin.second; - bel.tile = tile; - const auto &bel_data = bel_info(ctx->chip_info, bel); - - NPNR_ASSERT(bel_data.lut_element != -1); - - lut_thru_cells.emplace_back(nullptr, IdString(), IdString(ctx->wire_lut->cell)); - CellInfo &cell = lut_thru_cells.back(); - - cell.bel = bel; - - NPNR_ASSERT(ctx->wire_lut->input_pins.size() == 1); - cell.lut_cell.pins.push_back(IdString(ctx->wire_lut->input_pins[0])); - - cell.lut_cell.equation.resize(2); - cell.lut_cell.equation.set(0, false); - cell.lut_cell.equation.set(1, true); - - cell.cell_bel_pins[IdString(ctx->wire_lut->input_pins[0])].push_back(input_bel_pin.first); - - lut_mappers[bel_data.lut_element].cells.push_back(&cell); - } - - std::vector lut_cells; - lut_cells.reserve(used_bels.size()); - for (const auto &bel_pair : used_bels) { - const PseudoPipBel &bel = bel_pair.second; - const BelInfoPOD &bel_data = type_data.bel_data[bel.bel_index]; - - // This used BEL isn't a LUT, skip it! - if (bel_data.lut_element == -1) { - continue; - } - - lut_cells.emplace_back(nullptr, IdString(), ctx->wire_lut ? IdString(ctx->wire_lut->cell) : IdString()); - CellInfo &cell = lut_cells.back(); - - cell.bel.tile = tile; - cell.bel.index = bel_pair.first; - - if (ctx->wire_lut == nullptr) - continue; - - NPNR_ASSERT(ctx->wire_lut->input_pins.size() == 1); - cell.lut_cell.pins.push_back(IdString(ctx->wire_lut->input_pins[0])); - - if (bel.input_bel_pin == -1) { - // FIXME: currently assume that LUT route-throughs with no input pins are GND drivers as this is all we need - // for Nexus/Xilinx where Vcc is readily available and cheap This won't be true for other arches - cell.lut_cell.equation.resize(2); - cell.lut_cell.equation.set(0, false); - cell.lut_cell.equation.set(1, false); - } else { - cell.lut_cell.equation.resize(2); - cell.lut_cell.equation.set(0, false); - cell.lut_cell.equation.set(1, true); - - // Map LUT input to input wire used by pseudo pip. - IdString input_bel_pin(bel_data.ports[bel.input_bel_pin]); - cell.cell_bel_pins[IdString(ctx->wire_lut->input_pins[0])].push_back(input_bel_pin); - } - - lut_mappers[bel_data.lut_element].cells.push_back(&cell); - } - - std::vector lut_wires_unavailable; - lut_wires_unavailable.reserve(lut_elements.size()); - for (LutMapper &lut_mapper : lut_mappers) { - lut_wires_unavailable.push_back(lut_mapper.check_wires(ctx)); - } - - // For unused pseudo pips, see if the BEL used is idle. - for (int32_t pseudo_pip : unused_pseudo_pips) { - PipId pip; - pip.tile = tile; - pip.index = pseudo_pip; - - bool blocked_by_bel = false; - const std::vector &bels = ctx->pseudo_pip_data.get_logic_bels_for_pip(ctx, site, pip); - for (const PseudoPipBel &bel : bels) { - if (tile_status.boundcells[bel.bel_index] != nullptr) { - blocked_by_bel = true; - -#ifdef DEBUG_PSEUDO_PIP - if (ctx->verbose) { - BelId abel; - abel.tile = tile; - abel.index = bel.bel_index; - log_info("Pseudo pip %s is block by a bound BEL %s\n", ctx->nameOfPip(pip), ctx->nameOfBel(abel)); - } -#endif - break; - } - - if (used_bels.count(bel.bel_index)) { -#ifdef DEBUG_PSEUDO_PIP - if (ctx->verbose) { - log_info("Pseudo pip %s is block by another pseudo pip\n", ctx->nameOfPip(pip)); - } -#endif - blocked_by_bel = true; - break; - } - } - - bool blocked_by_lut_eq = false; - - // See if any BELs are part of a LUT element. If so, see if using - // that pseudo pip violates the LUT element equation. - for (const PseudoPipBel &bel : bels) { - const BelInfoPOD &bel_data = type_data.bel_data[bel.bel_index]; - if (bel_data.lut_element == -1) { - continue; - } - - // FIXME: Check if the pseudo cell satifies the constraint system. - // Will become important for LUT-RAM/SRL testing. - - // FIXME: This lookup is static, consider moving to PseudoPipBel? - IdString bel_name(bel_data.name); - if (bel.input_bel_pin == -1) { - // No input bel pin (e.g. LUT as constant driver) - check that *any* input is available, i.e. there is - // some room in the LUT equation still - size_t pin_count = lut_elements.at(bel_data.lut_element).lut_bels.at(bel_name).pins.size(); - uint32_t pin_mask = (1 << uint32_t(pin_count)) - 1; - uint32_t blocked_inputs = lut_wires_unavailable.at(bel_data.lut_element); - if ((blocked_inputs & pin_mask) == pin_mask) { - blocked_by_lut_eq = true; - break; - } - } else { - IdString input_bel_pin(bel_data.ports[bel.input_bel_pin]); - size_t pin_idx = - lut_elements.at(bel_data.lut_element).lut_bels.at(bel_name).pin_to_index.at(input_bel_pin); - - uint32_t blocked_inputs = lut_wires_unavailable.at(bel_data.lut_element); - if ((blocked_inputs & (1 << pin_idx)) != 0) { - blocked_by_lut_eq = true; - break; - } - } - } - -#ifdef DEBUG_PSEUDO_PIP - if (blocked_by_lut_eq && ctx->verbose) { - log_info("Pseudo pip %s is blocked by invalid LUT equation\n", ctx->nameOfPip(pip)); - } -#endif - - // Pseudo pip should be allowed, mark as such. - // - // FIXME: Handle non-LUT constraint cases, as needed. - bool allow_pip = !blocked_by_lut_eq && !blocked_by_bel; - allowed_pseudo_pips.set(pseudo_pip, allow_pip); - } -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/pseudo_pip_model.h b/fpga_interchange/pseudo_pip_model.h deleted file mode 100644 index b0e28059..00000000 --- a/fpga_interchange/pseudo_pip_model.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef PSEUDO_PIP_MODEL_H -#define PSEUDO_PIP_MODEL_H - -#include - -#include "dynamic_bitarray.h" -#include "nextpnr_namespaces.h" -#include "nextpnr_types.h" -#include "site_router.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct PseudoPipBel -{ - // Which BEL in the tile does the pseudo pip use? - int32_t bel_index; - - // What is the index of the input BEL pin that the pseudo pip used? - // - // NOTE: This is **not** the name of the pin. - int32_t input_bel_pin; - - // What is the index of the output BEL pin that the pseudo pip used? - // - // NOTE: This is **not** the name of the pin. - int32_t output_bel_pin; -}; - -struct LogicBelKey -{ - int32_t tile_type; - int32_t pip_index; - int32_t site; - - std::tuple make_tuple() const { return std::make_tuple(tile_type, pip_index, site); } - - bool operator==(const LogicBelKey &other) const { return make_tuple() == other.make_tuple(); } - - bool operator<(const LogicBelKey &other) const { return make_tuple() < other.make_tuple(); } - - unsigned int hash() const { return mkhash(mkhash(tile_type, pip_index), site); } -}; - -// Storage for tile type generic pseudo pip data and lookup. -struct PseudoPipData -{ - // Initial data for specified tile type, if not already initialized. - void init_tile_type(const Context *ctx, int32_t tile_type); - - // Get the highest PipId::index found in a specified tile type. - size_t get_max_pseudo_pip(int32_t tile_type) const; - - // Get the list of possible sites that a pseudo pip might be used in. - const std::vector &get_possible_sites_for_pip(const Context *ctx, PipId pip) const; - - // Get list of BELs the pseudo pip uses, and how it routes through them. - // - // This does **not** include site ports or site pips. - const std::vector &get_logic_bels_for_pip(const Context *ctx, int32_t site, PipId pip) const; - - dict max_pseudo_pip_for_tile_type; - dict, std::vector> possibles_sites_for_pip; - dict> logic_bels_for_pip; -}; - -// Tile instance fast pseudo pip lookup. -struct PseudoPipModel -{ - int32_t tile; - DynamicBitarray<> allowed_pseudo_pips; - dict pseudo_pip_sites; - dict> site_to_pseudo_pips; - pool active_pseudo_pips; - std::vector scratch; - - // Call when a tile is initialized. - void init(Context *ctx, int32_t tile); - - // Call after placement but before routing to update which pseudo pips are - // legal. This call is important to ensure that checkPipAvail returns the - // correct value. - // - // If the tile has no placed elements, then prepare_for_routing does not - // need to be called after init. - void prepare_for_routing(const Context *ctx, const std::vector &sites); - - // Returns true if the pseudo pip is allowed given current site placements - // and other pseudo pips. - bool checkPipAvail(const Context *ctx, PipId pip) const; - - // Enables a pseudo pip in the model. May cause other pseudo pips to - // become unavailable. - void bindPip(const Context *ctx, PipId pip); - - // Removes a pseudo pip from the model. May cause other pseudo pips to - // become available. - void unbindPip(const Context *ctx, PipId pip); - - // Internal method to update pseudo pips marked as part of a site. - void update_site(const Context *ctx, size_t site); -}; - -NEXTPNR_NAMESPACE_END - -#endif /* PSEUDO_PIP_MODEL_H */ diff --git a/fpga_interchange/sampler.cc b/fpga_interchange/sampler.cc deleted file mode 100644 index 0868867e..00000000 --- a/fpga_interchange/sampler.cc +++ /dev/null @@ -1,174 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "sampler.h" -#include -#include -#include - -NEXTPNR_NAMESPACE_BEGIN - -static size_t partition_x(std::vector::iterator begin, std::vector::iterator end, - const std::vector> &samples) -{ - if (std::distance(begin, end) == 0) { - return 0; - } - - // Find the median x value. - std::vector xs; - xs.reserve(std::distance(begin, end)); - - for (auto iter = begin; iter != end; ++iter) { - xs.push_back(samples[*iter].first); - } - - std::sort(xs.begin(), xs.end()); - xs.erase(std::unique(xs.begin(), xs.end()), xs.end()); - - // Partion on the median x value (e.g. 50% of samples on one side and - // 50% of samples on the other side). - int32_t x_div = xs[(xs.size() - 1) / 2]; - - auto split = std::partition(begin, end, - [x_div, &samples](size_t index) -> bool { return samples[index].first <= x_div; }); - - return std::distance(begin, split); -} - -/* Don't both splitting when the partition has less than kMinSplit. */ -static constexpr ptrdiff_t kMinSplit = 20; - -static size_t partition_y(std::vector::iterator begin, std::vector::iterator end, - const std::vector> &samples) -{ - if (std::distance(begin, end) == 0) { - return 0; - } - - std::vector ys; - ys.reserve(std::distance(begin, end)); - - for (auto iter = begin; iter != end; ++iter) { - ys.push_back(samples[*iter].second); - } - - std::sort(ys.begin(), ys.end()); - ys.erase(std::unique(ys.begin(), ys.end()), ys.end()); - - int32_t y_div = ys[(ys.size() - 1) / 2]; - - auto split = std::partition(begin, end, - [y_div, &samples](size_t index) -> bool { return samples[index].second <= y_div; }); - - return std::distance(begin, split); -} - -static void add_split(std::vector *splits, size_t new_split) -{ - if (splits->back() < new_split) { - splits->push_back(new_split); - } else if (splits->back() != new_split) { - throw std::runtime_error("Split is not consectutive!"); - } -} - -void Sampler::divide_samples(size_t target_sample_count, const std::vector> &samples) -{ - // Initialize indicies lookup and make 1 split with entire sample range. - indicies.resize(samples.size()); - for (size_t i = 0; i < samples.size(); ++i) { - indicies[i] = i; - } - - splits.reserve(2); - splits.push_back(0); - splits.push_back(samples.size()); - - size_t divisions = std::ceil(std::sqrt(target_sample_count) / 2.); - if (divisions == 0) { - throw std::runtime_error("Math failure, unreachable!"); - } - - if (divisions > samples.size()) { - // Handle cases where there are few samples. - return; - } - - // Recursively split samples first 50% / 50% in x direction, and then - // 50% / 50% in y direction. Repeat until the bucket is smaller than - // kMinSplit or the samples have been divided `divisions` times. - std::vector new_splits; - for (size_t division_count = 0; division_count < divisions; ++division_count) { - new_splits.clear(); - new_splits.push_back(0); - for (size_t i = 0; i < splits.size() - 1; ++i) { - size_t split_begin = splits.at(i); - size_t split_end = splits.at(i + 1); - if (split_end > indicies.size()) { - throw std::runtime_error("split_end is not valid!"); - } - if (split_begin >= split_end) { - throw std::runtime_error("Invalid split from earlier pass!"); - } - - std::vector::iterator begin = indicies.begin() + split_begin; - std::vector::iterator end = indicies.begin() + split_end; - - if (std::distance(begin, end) < kMinSplit) { - add_split(&new_splits, split_begin); - continue; - } - - // Try to split samples 50/50 in x direction. - size_t split = partition_x(begin, end, samples); - // Try to split samples 50/50 in y direction after the x split. - size_t split_y1 = partition_y(begin, begin + split, samples); - size_t split_y2 = partition_y(begin + split, end, samples); - - // Because the y2 split starts at split, add it here. - split_y2 += split; - - add_split(&new_splits, split_begin); - add_split(&new_splits, split_begin + split_y1); - add_split(&new_splits, split_begin + split); - add_split(&new_splits, split_begin + split_y2); - } - - add_split(&new_splits, samples.size()); - - if (new_splits.front() != 0) { - throw std::runtime_error("Split must start at 0"); - } - if (new_splits.back() != samples.size()) { - throw std::runtime_error("Split must end at last element"); - } - - for (size_t i = 0; i < new_splits.size() - 1; ++i) { - if (new_splits[i] >= new_splits[i + 1]) { - throw std::runtime_error("Split indicies must be increasing"); - } - } - - std::swap(splits, new_splits); - } -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/sampler.h b/fpga_interchange/sampler.h deleted file mode 100644 index e8475d45..00000000 --- a/fpga_interchange/sampler.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef SAMPLER_H_ -#define SAMPLER_H_ - -#include -#include -#include -#include - -#include "nextpnr_namespaces.h" - -NEXTPNR_NAMESPACE_BEGIN - -// Given a set of coordinates, generates random samples that are geometric -// distributed. -struct Sampler -{ - - void divide_samples(size_t target_sample_count, const std::vector> &samples); - - size_t number_of_regions() const { return splits.size() - 1; } - - size_t get_sample_from_region(size_t region, std::function rng) const - { - if (region >= (splits.size() - 1)) { - throw std::runtime_error("region out of range"); - } - size_t split_begin = splits[region]; - size_t split_end = splits[region + 1]; - if (split_begin == split_end) { - throw std::runtime_error("Splits should never be empty!"); - } - - // Pick a random element from that region. - return indicies[split_begin + (rng() % (split_end - split_begin))]; - } - - size_t get_sample(std::function rng) const - { - size_t region = rng() % number_of_regions(); - return get_sample_from_region(region, rng); - } - - std::vector indicies; - std::vector splits; -}; - -NEXTPNR_NAMESPACE_END - -#endif /* SAMPLER_H_ */ diff --git a/fpga_interchange/site_arch.cc b/fpga_interchange/site_arch.cc deleted file mode 100644 index d0a5c48c..00000000 --- a/fpga_interchange/site_arch.cc +++ /dev/null @@ -1,489 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "site_arch.h" -#include "site_arch.impl.h" - -NEXTPNR_NAMESPACE_BEGIN - -SiteInformation::SiteInformation(const Context *ctx, int32_t tile, int32_t site, - const pool &cells_in_site) - : ctx(ctx), tile(tile), tile_type(ctx->chip_info->tiles[tile].type), site(site), cells_in_site(cells_in_site) -{ -} - -bool SiteArch::bindPip(const SitePip &pip, SiteNetInfo *net) -{ - SiteWire src = getPipSrcWire(pip); - SiteWire dst = getPipDstWire(pip); - - if (!bindWire(src, net)) { - return false; - } - if (!bindWire(dst, net)) { - unbindWire(src); - return false; - } - - auto result = net->wires.emplace(dst, SitePipMap{pip, 1}); - if (!result.second) { - if (result.first->second.pip != pip) { - // Pip conflict! - if (debug()) { - log_info("Pip conflict binding pip %s to wire %s, conflicts with pip %s\n", nameOfPip(pip), - nameOfWire(dst), nameOfPip(result.first->second.pip)); - } - - unbindWire(src); - unbindWire(dst); - return false; - } - - result.first->second.count += 1; - } - - if (debug()) { - log_info("Bound pip %s to wire %s\n", nameOfPip(pip), nameOfWire(dst)); - } - - return true; -} - -void SiteArch::unbindPip(const SitePip &pip) -{ - SiteWire src = getPipSrcWire(pip); - SiteWire dst = getPipDstWire(pip); - - if (debug()) { - log_info("Unbinding pip %s from wire %s\n", nameOfPip(pip), nameOfWire(dst)); - } - - SiteNetInfo *src_net = unbindWire(src); - SiteNetInfo *dst_net = unbindWire(dst); - NPNR_ASSERT(src_net == dst_net); - auto iter = dst_net->wires.find(dst); - NPNR_ASSERT(iter != dst_net->wires.end()); - NPNR_ASSERT(iter->second.count >= 1); - iter->second.count -= 1; - - if (iter->second.count == 0) { - dst_net->wires.erase(iter); - } -} - -void SiteArch::archcheck() -{ - for (SiteWire wire : getWires()) { - for (SitePip pip : getPipsDownhill(wire)) { - SiteWire wire2 = getPipSrcWire(pip); - log_assert(wire == wire2); - } - - for (SitePip pip : getPipsUphill(wire)) { - SiteWire wire2 = getPipDstWire(pip); - log_assert(wire == wire2); - } - } -} - -SiteArch::SiteArch(const SiteInformation *site_info) - : ctx(site_info->ctx), site_info(site_info), blocking_net(site_info->ctx->id("$nextpnr_blocked_net")) -{ - // Build list of input and output site ports - // - // FIXME: This doesn't need to be computed over and over, move to - // arch/chip db. - const TileTypeInfoPOD &tile_type = loc_info(&site_info->chip_info(), *site_info); - PipId pip; - pip.tile = site_info->tile; - for (size_t pip_index = 0; pip_index < tile_type.pip_data.size(); ++pip_index) { - if (tile_type.pip_data[pip_index].site != site_info->site) { - continue; - } - - pip.index = pip_index; - - if (!site_info->is_site_port(pip)) { - continue; - } - - WireId src_wire = ctx->getPipSrcWire(pip); - if (site_info->is_wire_in_site(src_wire)) { - output_site_ports.push_back(pip); - } else { - input_site_ports.push_back(pip); - } - } - - // Create list of out of site sources and sinks. - bool have_vcc_pins = false; - bool have_gnd_pins = false; - - IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); - IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); - - IdString const_net_name(ctx->chip_info->constants->best_constant_net); - NPNR_ASSERT(const_net_name == IdString() || const_net_name == vcc_net_name || const_net_name == gnd_net_name); - - // FIXME: Use VCC if the architecture does not device the best constant - if (const_net_name == IdString()) { - const_net_name = vcc_net_name; - } - - for (CellInfo *cell : site_info->cells_in_site) { - for (const auto &pin_pair : cell->cell_bel_pins) { - if (!cell->ports.count(pin_pair.first)) - continue; - const PortInfo &port = cell->ports.at(pin_pair.first); - if (port.net != nullptr) { - nets.emplace(port.net, SiteNetInfo{port.net}); - } - } - - for (const auto &conn : cell->lut_cell.pin_connections) { - if (conn.second == LutCell::PinConnection::Vcc) { - have_vcc_pins = true; - } else if (conn.second == LutCell::PinConnection::Gnd) { - have_gnd_pins = true; - } else if (conn.second == LutCell::PinConnection::Const) { - if (const_net_name == vcc_net_name) { - have_vcc_pins = true; - } else if (const_net_name == gnd_net_name) { - have_gnd_pins = true; - } - } - } - } - - for (auto &net_pair : nets) { - NetInfo *net = net_pair.first; - SiteNetInfo &net_info = net_pair.second; - - // All nets require drivers - if (net->driver.cell == nullptr) - continue; - - bool net_driven_out_of_site = false; - if (net->driver.cell->bel == BelId()) { - // The driver of this site hasn't been placed, so treat it as - // out of site. - out_of_site_sources.push_back(SiteWire::make(site_info, PORT_OUT, net)); - net_info.driver = out_of_site_sources.back(); - net_driven_out_of_site = true; - } else { - if (!site_info->is_bel_in_site(net->driver.cell->bel)) { - - // The driver of this site has been placed, it is an out - // of site source. - out_of_site_sources.push_back(SiteWire::make(site_info, PORT_OUT, net)); - // out_of_site_sources.back().wire = ctx->getNetinfoSourceWire(net); - net_info.driver = out_of_site_sources.back(); - - net_driven_out_of_site = true; - } else { - net_info.driver = SiteWire::make(site_info, ctx->getNetinfoSourceWire(net)); - } - } - - if (net_driven_out_of_site) { - // Because this net is driven from a source out of the site, - // no out of site sink is required. - continue; - } - - // Examine net to determine if it has any users not in this site. - bool net_used_out_of_site = false; - for (const PortRef &user : net->users) { - NPNR_ASSERT(user.cell != nullptr); - - if (user.cell->bel == BelId()) { - // Because this net has a user that has not been placed, - // and this net is being driven from this site, make sure - // this net can be routed from this site. - net_used_out_of_site = true; - break; - } - - if (!site_info->is_bel_in_site(user.cell->bel)) { - net_used_out_of_site = true; - break; - } - } - - if (net_used_out_of_site) { - out_of_site_sinks.push_back(SiteWire::make(site_info, PORT_IN, net)); - net_info.users.emplace(out_of_site_sinks.back()); - } - } - - // At this point all nets have a driver SiteWire, but user SiteWire's - // within the site are not present. Add them now. - for (auto &net_pair : nets) { - NetInfo *net = net_pair.first; - SiteNetInfo &net_info = net_pair.second; - - for (const PortRef &user : net->users) { - if (!site_info->is_bel_in_site(user.cell->bel)) { - // Only care about BELs within the site at this point. - continue; - } - - for (IdString bel_pin : ctx->getBelPinsForCellPin(user.cell, user.port)) { - SiteWire wire = getBelPinWire(user.cell->bel, bel_pin); - // Don't add users that are trivially routable! - if (wire != net_info.driver) { -#ifdef DEBUG_SITE_ARCH - if (ctx->debug) { - log_info("Add user %s because it isn't driver %s\n", nameOfWire(wire), - nameOfWire(net_info.driver)); - } -#endif - net_info.users.emplace(wire); - } - } - } - } - - NetInfo *vcc_net = ctx->nets.at(vcc_net_name).get(); - auto vcc_iter = nets.find(vcc_net); - if (vcc_iter == nets.end() && have_vcc_pins) { - // VCC net isn't present, add it. - SiteNetInfo net_info; - net_info.net = vcc_net; - net_info.driver.type = SiteWire::OUT_OF_SITE_SOURCE; - net_info.driver.net = vcc_net; - auto result = nets.emplace(vcc_net, net_info); - NPNR_ASSERT(result.second); - vcc_iter = result.first; - } - - NetInfo *gnd_net = ctx->nets.at(gnd_net_name).get(); - auto gnd_iter = nets.find(gnd_net); - if (gnd_iter == nets.end() && have_gnd_pins) { - // GND net isn't present, add it. - SiteNetInfo net_info; - net_info.net = gnd_net; - net_info.driver.type = SiteWire::OUT_OF_SITE_SOURCE; - net_info.driver.net = gnd_net; - auto result = nets.emplace(gnd_net, net_info); - NPNR_ASSERT(result.second); - gnd_iter = result.first; - } - - for (CellInfo *cell : site_info->cells_in_site) { - for (const auto &it : cell->lut_cell.pin_connections) { - const auto &pin = it.first; - const auto &conn = it.second; - - if (conn == LutCell::PinConnection::Unconnected || conn == LutCell::PinConnection::Signal) { - continue; - } - - if (conn == LutCell::PinConnection::Vcc) { - SiteWire wire = getBelPinWire(cell->bel, pin); - vcc_iter->second.users.emplace(wire); - } else if (conn == LutCell::PinConnection::Gnd) { - SiteWire wire = getBelPinWire(cell->bel, pin); - gnd_iter->second.users.emplace(wire); - } else if (conn == LutCell::PinConnection::Const) { - SiteWire wire = getBelPinWire(cell->bel, pin); - if (const_net_name == vcc_net_name) { - vcc_iter->second.users.emplace(wire); - } - if (const_net_name == gnd_net_name) { - gnd_iter->second.users.emplace(wire); - } - } - -#ifdef DEBUG_LUT_MAPPING - if (ctx->verbose) { - log_info("Tying %s.%s to %s\n", cell->name.c_str(ctx), pin.c_str(ctx), - LutCell::nameOfPinConnection(conn).c_str()); - } -#endif - } - } - - for (auto &net_pair : nets) { - if (net_pair.first->driver.cell == nullptr) - continue; - SiteNetInfo *net_info = &net_pair.second; - auto result = wire_to_nets.emplace(net_info->driver, SiteNetMap{net_info, 1}); - // By this point, trivial congestion at sources should already by - // avoided, and there should be no duplicates in the - // driver/users data. - NPNR_ASSERT(result.second); - - for (const auto &user : net_info->users) { - result = wire_to_nets.emplace(user, SiteNetMap{net_info, 1}); - NPNR_ASSERT(result.second); - } - } - - blocking_site_net.net = &blocking_net; -} - -const char *SiteArch::nameOfWire(const SiteWire &wire) const -{ - switch (wire.type) { - case SiteWire::SITE_WIRE: - return ctx->nameOfWire(wire.wire); - case SiteWire::SITE_PORT_SINK: - return ctx->nameOfWire(wire.wire); - case SiteWire::SITE_PORT_SOURCE: - return ctx->nameOfWire(wire.wire); - case SiteWire::OUT_OF_SITE_SOURCE: { - std::string &str = ctx->log_strs.next(); - str = stringf("Out of site source for net %s", wire.net->name.c_str(ctx)); - return str.c_str(); - } - case SiteWire::OUT_OF_SITE_SINK: { - std::string &str = ctx->log_strs.next(); - str = stringf("Out of sink source for net %s", wire.net->name.c_str(ctx)); - return str.c_str(); - } - default: - // Unreachable! - NPNR_ASSERT(false); - } -} - -const char *SiteArch::nameOfPip(const SitePip &pip) const -{ - switch (pip.type) { - case SitePip::SITE_PIP: - return ctx->nameOfPip(pip.pip); - case SitePip::SITE_PORT: - return ctx->nameOfPip(pip.pip); - case SitePip::SOURCE_TO_SITE_PORT: { - std::string &str = ctx->log_strs.next(); - str = stringf("Out of site source for net %s => %s", pip.wire.net->name.c_str(ctx), - ctx->nameOfWire(ctx->getPipSrcWire(pip.pip))); - return str.c_str(); - } - case SitePip::SITE_PORT_TO_SINK: { - std::string &str = ctx->log_strs.next(); - str = stringf("%s => Out of site sink for net %s", ctx->nameOfWire(ctx->getPipDstWire(pip.pip)), - pip.wire.net->name.c_str(ctx)); - return str.c_str(); - } - case SitePip::SITE_PORT_TO_SITE_PORT: { - std::string &str = ctx->log_strs.next(); - str = stringf("%s => %s", ctx->nameOfWire(ctx->getPipSrcWire(pip.pip)), - ctx->nameOfWire(ctx->getPipDstWire(pip.other_pip))); - return str.c_str(); - } - default: - // Unreachable! - NPNR_ASSERT(false); - } -} - -const char *SiteArch::nameOfNet(const SiteNetInfo *net) const { return net->net->name.c_str(ctx); } - -bool SiteArch::debug() const { return ctx->debug; } - -SitePipUphillRange::SitePipUphillRange(const SiteArch *site_arch, SiteWire site_wire) - : site_arch(site_arch), site_wire(site_wire) -{ - switch (site_wire.type) { - case SiteWire::SITE_WIRE: - pip_range = site_arch->ctx->getPipsUphill(site_wire.wire); - break; - case SiteWire::OUT_OF_SITE_SOURCE: - // No normal pips! - break; - case SiteWire::OUT_OF_SITE_SINK: - // No normal pips! - break; - case SiteWire::SITE_PORT_SINK: - // No normal pips! - break; - case SiteWire::SITE_PORT_SOURCE: - // No normal pips! - break; - default: - // Unreachable! - NPNR_ASSERT(false); - } -} - -SitePip SitePipUphillIterator::operator*() const -{ - switch (state) { - case NORMAL_PIPS: - return SitePip::make(site_arch->site_info, *iter); - case PORT_SRC_TO_PORT_SINK: - return SitePip::make(site_arch->site_info, site_arch->output_site_ports.at(cursor), site_wire.pip); - case OUT_OF_SITE_SOURCES: - return SitePip::make(site_arch->site_info, site_arch->out_of_site_sources.at(cursor), site_wire.pip); - case OUT_OF_SITE_SINK_TO_PORT_SINK: - return SitePip::make(site_arch->site_info, site_arch->output_site_ports.at(cursor), site_wire); - case SITE_PORT: - return SitePip::make(site_arch->site_info, site_wire.pip); - default: - // Unreachable! - NPNR_ASSERT(false); - } -} - -SiteWire SiteWireIterator::operator*() const -{ - WireId wire; - PipId pip; - SiteWire site_wire; - switch (state) { - case NORMAL_WIRES: - wire.tile = site_arch->site_info->tile; - wire.index = cursor; - return SiteWire::make(site_arch->site_info, wire); - case INPUT_SITE_PORTS: - pip = site_arch->input_site_ports.at(cursor); - site_wire = SiteWire::make_site_port(site_arch->site_info, pip, /*dst_wire=*/false); - NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SOURCE); - return site_wire; - case OUTPUT_SITE_PORTS: - pip = site_arch->output_site_ports.at(cursor); - site_wire = SiteWire::make_site_port(site_arch->site_info, pip, /*dst_wire=*/true); - NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SINK); - return site_wire; - case OUT_OF_SITE_SOURCES: - return site_arch->out_of_site_sources.at(cursor); - case OUT_OF_SITE_SINKS: - return site_arch->out_of_site_sinks.at(cursor); - default: - // Unreachable! - NPNR_ASSERT(false); - } -} - -SiteWireIterator SiteWireRange::begin() const -{ - SiteWireIterator b; - - b.state = SiteWireIterator::BEGIN; - b.site_arch = site_arch; - b.tile_type = &loc_info(&site_arch->site_info->chip_info(), *site_arch->site_info); - - ++b; - return b; -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/site_arch.h b/fpga_interchange/site_arch.h deleted file mode 100644 index 81c0b96c..00000000 --- a/fpga_interchange/site_arch.h +++ /dev/null @@ -1,810 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef SITE_ARCH_H -#define SITE_ARCH_H - -#include -#include - -#include "PhysicalNetlist.capnp.h" -#include "arch_iterators.h" -#include "chipdb.h" -#include "hashlib.h" -#include "log.h" -#include "nextpnr_namespaces.h" -#include "nextpnr_types.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct Context; - -struct SiteInformation -{ - const Context *ctx; - - const int32_t tile; - const int32_t tile_type; - const int32_t site; - const pool &cells_in_site; - - SiteInformation(const Context *ctx, int32_t tile, int32_t site, - const pool &cells_in_site); - - inline const ChipInfoPOD &chip_info() const NPNR_ALWAYS_INLINE; - - inline bool is_wire_in_site(WireId wire) const NPNR_ALWAYS_INLINE; - - inline bool is_bel_in_site(BelId bel) const NPNR_ALWAYS_INLINE; - - inline bool is_pip_part_of_site(PipId pip) const NPNR_ALWAYS_INLINE; - - inline bool is_site_port(PipId pip) const NPNR_ALWAYS_INLINE; -}; - -// Site routing needs a modification of the routing graph. Within the site, -// the arch can be consulted for edges. However the rest of the routing graph -// needs to be reduced for analysis purposes. Wires within the site are -// SITE_WIRE's. 4 additional nodes are introduced to model out of site -// routing: -// - OUT_OF_SITE_SOURCE / OUT_OF_SITE_SINK -// - These represent net sources and sinks that are only reachable via the -// routing graph (e.g. outside of the site). -// - SITE_PORT_SOURCE / SITE_PORT_SINK -// - These represent the routing resources connected to other side of site -// ports. -// -// The non-site wire graph is connected like: -// -// ┌─────────────────┐ ┌────────────────────┐ -// │ │ │ │ -// │ OUT_OF_SITE_SRC │ │ OUT_OF_SITE_SINK │◄────┐ -// │ │ │ │ │ -// └┬────────────────┘ └────────────────────┘ │ -// │ │ -// │ ┌─────────────────────────────────────────────────────┤ -// │ │ │ -// │ │ │ -// │ │ │ -// │ │ │ -// │ ▼ │ -// │ ┌─────────────────┐ ┌─────────────┐ ┌────────────────┐ │ -// │ │ │ │ │ │ │ │ -// └─────►│ SITE_PORT_SRC ├──►│ Site ├──────►│ SITE_PORT_SINK ├──┘ -// │ │ │ │ │ │ -// └─────────────────┘ └─────────────┘ └────────────────┘ -// -struct SiteWire -{ - enum Type - { - // This wire is just a plain site wire. - SITE_WIRE = 0, - // This wire is a source that is from outside of the site. - OUT_OF_SITE_SOURCE = 1, - // This wire is a sink that is from outside of the site. - OUT_OF_SITE_SINK = 2, - // This wire is the routing graph wire on the dst side of a site port. - SITE_PORT_SINK = 3, - // This wire is the routing graph wire on the src side of a site port. - SITE_PORT_SOURCE = 4, - NUMBER_SITE_WIRE_TYPES = 5, - }; - - static inline SiteWire make(const SiteInformation *site_info, WireId site_wire) NPNR_ALWAYS_INLINE; - - static SiteWire make(const SiteInformation *site_info, PortType port_type, NetInfo *net) NPNR_ALWAYS_INLINE - { - SiteWire out; - if (port_type == PORT_OUT) { - out.type = OUT_OF_SITE_SOURCE; - out.net = net; - } else { - out.type = OUT_OF_SITE_SINK; - out.net = net; - } - return out; - } - - static inline SiteWire make_site_port(const SiteInformation *site_info, PipId pip, bool dst_wire); - - bool operator==(const SiteWire &other) const - { - return wire == other.wire && type == other.type && pip == other.pip && net == other.net; - } - bool operator!=(const SiteWire &other) const - { - return wire != other.wire || type != other.type || pip != other.pip || net != other.net; - } - bool operator<(const SiteWire &other) const - { - return std::make_tuple(type, wire, pip, net) < std::make_tuple(other.type, other.wire, other.pip, other.net); - } - - Type type = NUMBER_SITE_WIRE_TYPES; - WireId wire; - PipId pip; - NetInfo *net = nullptr; - unsigned int hash() const { return mkhash(mkhash(int(type), wire.hash()), mkhash(pip.hash(), uintptr_t(net))); } -}; - -struct SitePip -{ - enum Type - { - // This is a plain regular site pip. - SITE_PIP = 0, - // This pip is a site port, and connects a SITE_WIRE to a SITE_PORT_SINK/SITE_PORT_SRC - SITE_PORT = 1, - // This pip connects a OUT_OF_SITE_SOURCE to a SITE_PORT_SRC - SOURCE_TO_SITE_PORT = 2, - // This pip connects a SITE_PORT_SINK to a OUT_OF_SITE_SINK - SITE_PORT_TO_SINK = 3, - // This pip connects a SITE_PORT_SINK to a SITE_PORT_SRC. - SITE_PORT_TO_SITE_PORT = 4, - INVALID_TYPE = 5, - }; - - static inline SitePip make(const SiteInformation *site_info, PipId pip); - - static SitePip make(const SiteInformation *site_info, SiteWire src, PipId dst) - { - NPNR_ASSERT(src.type == SiteWire::OUT_OF_SITE_SOURCE); - - SitePip out; - out.type = SOURCE_TO_SITE_PORT; - out.pip = dst; - out.wire = src; - - return out; - } - - static SitePip make(const SiteInformation *site_info, PipId src, SiteWire dst) - { - NPNR_ASSERT(dst.type == SiteWire::OUT_OF_SITE_SINK); - - SitePip out; - out.type = SITE_PORT_TO_SINK; - out.pip = src; - out.wire = dst; - - return out; - } - - static SitePip make(const SiteInformation *site_info, PipId src_pip, PipId dst_pip) - { - SitePip out; - out.type = SITE_PORT_TO_SITE_PORT; - out.pip = src_pip; - out.other_pip = dst_pip; - - return out; - } - - Type type = INVALID_TYPE; - // For SITE_PORT_TO_SITE_PORT connections, pip is the site -> routing pip. - PipId pip; - SiteWire wire; - // For SITE_PORT_TO_SITE_PORT connections, other_pip is the routing -> - // site pip. - PipId other_pip; - - bool operator==(const SitePip &other) const - { - return type == other.type && pip == other.pip && wire == other.wire && other_pip == other.other_pip; - } - bool operator!=(const SitePip &other) const - { - return type != other.type || pip != other.pip || wire != other.wire || other_pip != other.other_pip; - } - unsigned int hash() const { return mkhash(mkhash(int(type), pip.hash()), mkhash(wire.hash(), other_pip.hash())); } -}; - -struct SitePipDownhillRange; -struct SitePipUphillRange; -struct SiteWireRange; -struct SiteNetInfo; - -struct SitePipMap -{ - SitePip pip; - size_t count; -}; - -struct SiteNetMap -{ - SiteNetInfo *net; - size_t count; -}; - -struct SiteNetInfo -{ - NetInfo *net; - SiteWire driver; - pool users; - - dict wires; -}; - -struct SiteArch -{ - const Context *const ctx; - const SiteInformation *const site_info; - - dict nets; - dict wire_to_nets; - - NetInfo blocking_net; - SiteNetInfo blocking_site_net; - - std::vector input_site_ports; - std::vector output_site_ports; - - std::vector out_of_site_sources; - std::vector out_of_site_sinks; - - // A site port that is present in this dictionary is blocked for all those nets except any in the attached pool - dict> blocked_site_ports; - - SiteArch(const SiteInformation *site_info); - - inline SiteWire getPipSrcWire(const SitePip &site_pip) const NPNR_ALWAYS_INLINE; - inline SiteWire getPipDstWire(const SitePip &site_pip) const NPNR_ALWAYS_INLINE; - - // Does this site pip always invert its signal? - inline bool isInverting(const SitePip &site_pip) const NPNR_ALWAYS_INLINE; - - // Can this site pip optional invert its signal? - inline bool canInvert(const SitePip &site_pip) const NPNR_ALWAYS_INLINE; - - // For a site port, returns the preferred constant net type. - // - // If no preference, then NetType is SIGNAL. - inline PhysicalNetlist::PhysNetlist::NetType prefered_constant_net_type(const SitePip &site_pip) const; - - inline SitePipDownhillRange getPipsDownhill(const SiteWire &site_wire) const NPNR_ALWAYS_INLINE; - inline SitePipUphillRange getPipsUphill(const SiteWire &site_wire) const NPNR_ALWAYS_INLINE; - SiteWireRange getWires() const; - - inline SiteWire getBelPinWire(BelId bel, IdString pin) const NPNR_ALWAYS_INLINE; - inline PortType getBelPinType(BelId bel, IdString pin) const NPNR_ALWAYS_INLINE; - - const char *nameOfWire(const SiteWire &wire) const; - const char *nameOfPip(const SitePip &pip) const; - const char *nameOfNet(const SiteNetInfo *net) const; - - bool debug() const; - - bool bindWire(const SiteWire &wire, SiteNetInfo *net) - { - auto result = wire_to_nets.emplace(wire, SiteNetMap{net, 1}); - if (result.first->second.net != net) { - if (debug()) { - log_info("Net conflict binding wire %s to net %s, conflicts with net %s\n", nameOfWire(wire), - nameOfNet(net), nameOfNet(result.first->second.net)); - } - return false; - } - - if (!result.second) { - result.first->second.count += 1; - } - - return true; - } - - SiteNetInfo *unbindWire(const SiteWire &wire) - { - auto iter = wire_to_nets.find(wire); - NPNR_ASSERT(iter != wire_to_nets.end()); - NPNR_ASSERT(iter->second.count >= 1); - SiteNetInfo *net = iter->second.net; - iter->second.count -= 1; - - if (iter->second.count == 0) { - wire_to_nets.erase(iter); - } - - return net; - } - - bool bindPip(const SitePip &pip, SiteNetInfo *net); - void unbindPip(const SitePip &pip); - - void archcheck(); - - bool is_pip_synthetic(const SitePip &pip) const NPNR_ALWAYS_INLINE; - SyntheticType pip_synthetic_type(const SitePip &pip) const NPNR_ALWAYS_INLINE; -}; - -struct SitePipDownhillIterator -{ - enum DownhillIteratorState - { - // Initial state - BEGIN = 0, - // Iterating over normal pips. - NORMAL_PIPS = 1, - // Iterating off all site port sources. - PORT_SINK_TO_PORT_SRC = 2, - // Iterating over out of site sinks. - OUT_OF_SITE_SINKS = 3, - // Iterating off all site port sources. - OUT_OF_SITE_SOURCE_TO_PORT_SRC = 4, - SITE_PORT = 5, - END = 6, - NUMBER_STATES = 7, - }; - - DownhillIteratorState state = BEGIN; - const SiteArch *site_arch; - SiteWire site_wire; - const RelSlice *pips_downhill; - size_t cursor; - - bool advance_in_state() NPNR_ALWAYS_INLINE - { - switch (state) { - case BEGIN: - return false; - case NORMAL_PIPS: - ++cursor; - return (cursor < pips_downhill->size()); - case PORT_SINK_TO_PORT_SRC: - ++cursor; - return (cursor < site_arch->input_site_ports.size()); - case OUT_OF_SITE_SINKS: - ++cursor; - return (cursor < site_arch->out_of_site_sinks.size()); - case OUT_OF_SITE_SOURCE_TO_PORT_SRC: - ++cursor; - return (cursor < site_arch->input_site_ports.size()); - case SITE_PORT: - ++cursor; - return false; - default: - // Unreachable! - NPNR_ASSERT(false); - } - } - - bool check_first() const NPNR_ALWAYS_INLINE - { - switch (state) { - case BEGIN: - return false; - case NORMAL_PIPS: - return (cursor < pips_downhill->size()); - case PORT_SINK_TO_PORT_SRC: - return (cursor < site_arch->input_site_ports.size()); - case OUT_OF_SITE_SINKS: - return (cursor < site_arch->out_of_site_sinks.size()); - case OUT_OF_SITE_SOURCE_TO_PORT_SRC: - return (cursor < site_arch->input_site_ports.size()); - case SITE_PORT: - return true; - case END: - return true; - default: - // Unreachable! - NPNR_ASSERT(false); - } - } - - const std::array, SiteWire::NUMBER_SITE_WIRE_TYPES> - get_state_table() const - { - std::array, SiteWire::NUMBER_SITE_WIRE_TYPES> state_table; - for (size_t j = 0; j < SiteWire::NUMBER_SITE_WIRE_TYPES; ++j) { - for (size_t i = 0; i < NUMBER_STATES; ++i) { - state_table[j][i] = NUMBER_STATES; - } - } - - state_table[SiteWire::SITE_WIRE][BEGIN] = NORMAL_PIPS; - state_table[SiteWire::SITE_WIRE][NORMAL_PIPS] = END; - - state_table[SiteWire::OUT_OF_SITE_SOURCE][BEGIN] = OUT_OF_SITE_SOURCE_TO_PORT_SRC; - state_table[SiteWire::OUT_OF_SITE_SOURCE][OUT_OF_SITE_SOURCE_TO_PORT_SRC] = END; - - state_table[SiteWire::OUT_OF_SITE_SINK][BEGIN] = END; - - state_table[SiteWire::SITE_PORT_SINK][BEGIN] = PORT_SINK_TO_PORT_SRC; - state_table[SiteWire::SITE_PORT_SINK][PORT_SINK_TO_PORT_SRC] = OUT_OF_SITE_SINKS; - state_table[SiteWire::SITE_PORT_SINK][OUT_OF_SITE_SINKS] = END; - - state_table[SiteWire::SITE_PORT_SOURCE][BEGIN] = SITE_PORT; - state_table[SiteWire::SITE_PORT_SOURCE][SITE_PORT] = END; - - return state_table; - } - - void advance_state() NPNR_ALWAYS_INLINE - { - state = get_state_table().at(site_wire.type).at(state); - cursor = 0; - NPNR_ASSERT(state >= BEGIN && state <= END); - } - - void operator++() NPNR_ALWAYS_INLINE - { - NPNR_ASSERT(state != END); - while (state != END) { - if (advance_in_state()) { - break; - } else { - advance_state(); - if (check_first()) { - break; - } - } - } - } - - bool operator!=(const SitePipDownhillIterator &other) const - { - return state != other.state || cursor != other.cursor; - } - - inline SitePip operator*() const NPNR_ALWAYS_INLINE; -}; - -struct SitePipDownhillRange -{ - const SiteArch *site_arch; - SiteWire site_wire; - - SitePipDownhillRange(const SiteArch *site_arch, const SiteWire &site_wire) - : site_arch(site_arch), site_wire(site_wire) - { - } - - inline const RelSlice *init_pip_range() const NPNR_ALWAYS_INLINE; - - inline SitePipDownhillIterator begin() const NPNR_ALWAYS_INLINE; - - SitePipDownhillIterator end() const NPNR_ALWAYS_INLINE - { - SitePipDownhillIterator e; - e.state = SitePipDownhillIterator::END; - e.cursor = 0; - - return e; - } -}; - -struct SitePipUphillIterator -{ - enum UphillIteratorState - { - // Initial state - BEGIN = 0, - // Iterating over normal pips. - NORMAL_PIPS = 1, - // Iterating off all site port sources. - PORT_SRC_TO_PORT_SINK = 2, - // Iterating over out of site sinks. - OUT_OF_SITE_SOURCES = 3, - // Iterating off all site port sources. - OUT_OF_SITE_SINK_TO_PORT_SINK = 4, - SITE_PORT = 5, - END = 6, - NUMBER_STATES = 7, - }; - - UphillIteratorState state = BEGIN; - const SiteArch *site_arch; - SiteWire site_wire; - size_t cursor; - UphillPipIterator iter; - UphillPipIterator uphill_end; - - bool advance_in_state() - { - switch (state) { - case BEGIN: - return false; - case NORMAL_PIPS: - while (iter != uphill_end) { - ++iter; - if (!(iter != uphill_end)) { - break; - } - } - - return false; - case PORT_SRC_TO_PORT_SINK: - ++cursor; - return (cursor < site_arch->output_site_ports.size()); - case OUT_OF_SITE_SOURCES: - ++cursor; - return (cursor < site_arch->out_of_site_sources.size()); - case OUT_OF_SITE_SINK_TO_PORT_SINK: - ++cursor; - return (cursor < site_arch->output_site_ports.size()); - case SITE_PORT: - ++cursor; - return false; - default: - // Unreachable! - NPNR_ASSERT(false); - } - } - - bool check_first() const - { - switch (state) { - case BEGIN: - return false; - case NORMAL_PIPS: - if (!(iter != uphill_end)) { - return false; - } else { - return true; - } - case PORT_SRC_TO_PORT_SINK: - return (cursor < site_arch->output_site_ports.size()); - case OUT_OF_SITE_SOURCES: - return (cursor < site_arch->out_of_site_sources.size()); - case OUT_OF_SITE_SINK_TO_PORT_SINK: - return (cursor < site_arch->output_site_ports.size()); - case SITE_PORT: - return true; - case END: - return true; - default: - // Unreachable! - NPNR_ASSERT(false); - } - } - - const std::array, SiteWire::NUMBER_SITE_WIRE_TYPES> - get_state_table() const - { - std::array, SiteWire::NUMBER_SITE_WIRE_TYPES> state_table; - for (size_t j = 0; j < SiteWire::NUMBER_SITE_WIRE_TYPES; ++j) { - for (size_t i = 0; i < NUMBER_STATES; ++i) { - state_table[j][i] = NUMBER_STATES; - } - } - - state_table[SiteWire::SITE_WIRE][BEGIN] = NORMAL_PIPS; - state_table[SiteWire::SITE_WIRE][NORMAL_PIPS] = END; - - state_table[SiteWire::OUT_OF_SITE_SOURCE][BEGIN] = END; - - state_table[SiteWire::OUT_OF_SITE_SINK][BEGIN] = OUT_OF_SITE_SINK_TO_PORT_SINK; - state_table[SiteWire::OUT_OF_SITE_SINK][OUT_OF_SITE_SINK_TO_PORT_SINK] = END; - - state_table[SiteWire::SITE_PORT_SINK][BEGIN] = SITE_PORT; - state_table[SiteWire::SITE_PORT_SINK][SITE_PORT] = END; - - state_table[SiteWire::SITE_PORT_SOURCE][BEGIN] = PORT_SRC_TO_PORT_SINK; - state_table[SiteWire::SITE_PORT_SOURCE][PORT_SRC_TO_PORT_SINK] = OUT_OF_SITE_SOURCES; - state_table[SiteWire::SITE_PORT_SOURCE][OUT_OF_SITE_SOURCES] = END; - - return state_table; - } - - void advance_state() - { - state = get_state_table().at(site_wire.type).at(state); - cursor = 0; - NPNR_ASSERT(state >= BEGIN && state <= END); - } - - void operator++() - { - NPNR_ASSERT(state != END); - while (state != END) { - if (advance_in_state()) { - break; - } else { - advance_state(); - if (check_first()) { - break; - } - } - } - } - - bool operator!=(const SitePipUphillIterator &other) const - { - return state != other.state || cursor != other.cursor || iter != other.iter; - } - - SitePip operator*() const; -}; - -struct SitePipUphillRange -{ - const SiteArch *site_arch; - SiteWire site_wire; - UphillPipRange pip_range; - - SitePipUphillRange(const SiteArch *site_arch, SiteWire site_wire); - - SitePipUphillIterator begin() const - { - SitePipUphillIterator b; - b.state = SitePipUphillIterator::BEGIN; - b.site_arch = site_arch; - b.site_wire = site_wire; - b.cursor = 0; - b.iter = pip_range.b; - b.uphill_end = pip_range.e; - - ++b; - - return b; - } - - SitePipUphillIterator end() const - { - SitePipUphillIterator e; - e.state = SitePipUphillIterator::END; - e.site_arch = site_arch; - e.site_wire = site_wire; - e.cursor = 0; - e.iter = pip_range.e; - e.uphill_end = pip_range.e; - - return e; - } -}; - -inline SitePipDownhillRange SiteArch::getPipsDownhill(const SiteWire &site_wire) const -{ - return SitePipDownhillRange(this, site_wire); -} - -inline SitePipUphillRange SiteArch::getPipsUphill(const SiteWire &site_wire) const -{ - return SitePipUphillRange(this, site_wire); -} - -struct SiteWireIterator -{ - enum SiteWireIteratorState - { - // Initial state - BEGIN = 0, - NORMAL_WIRES = 1, - INPUT_SITE_PORTS = 2, - OUTPUT_SITE_PORTS = 3, - OUT_OF_SITE_SOURCES = 4, - OUT_OF_SITE_SINKS = 5, - END = 6, - }; - - SiteWireIteratorState state = BEGIN; - const SiteArch *site_arch; - const TileTypeInfoPOD *tile_type; - size_t cursor = 0; - - bool advance_in_state() - { - switch (state) { - case BEGIN: - return false; - case NORMAL_WIRES: - while (true) { - ++cursor; - if (cursor >= tile_type->wire_data.size()) { - return false; - } - if (tile_type->wire_data[cursor].site == site_arch->site_info->site) { - return true; - } - } - case INPUT_SITE_PORTS: - ++cursor; - return (cursor < site_arch->input_site_ports.size()); - case OUTPUT_SITE_PORTS: - ++cursor; - return (cursor < site_arch->output_site_ports.size()); - case OUT_OF_SITE_SOURCES: - ++cursor; - return (cursor < site_arch->out_of_site_sources.size()); - case OUT_OF_SITE_SINKS: - ++cursor; - return (cursor < site_arch->out_of_site_sinks.size()); - default: - // Unreachable! - NPNR_ASSERT(false); - } - } - - // See if initial value in state is good. - bool check_first() const - { - switch (state) { - case BEGIN: - return false; - case NORMAL_WIRES: - if (cursor >= tile_type->wire_data.size()) { - return false; - } - return tile_type->wire_data[cursor].site == site_arch->site_info->site; - case INPUT_SITE_PORTS: - return (cursor < site_arch->input_site_ports.size()); - case OUTPUT_SITE_PORTS: - return (cursor < site_arch->output_site_ports.size()); - case OUT_OF_SITE_SOURCES: - return (cursor < site_arch->out_of_site_sources.size()); - case OUT_OF_SITE_SINKS: - return (cursor < site_arch->out_of_site_sinks.size()); - case END: - return true; - default: - // Unreachable! - NPNR_ASSERT(false); - } - } - - void advance_state() - { - NPNR_ASSERT(state >= BEGIN && state < END); - state = static_cast(state + 1); - cursor = 0; - NPNR_ASSERT(state >= BEGIN && state <= END); - } - - void operator++() - { - NPNR_ASSERT(state != END); - while (state != END) { - if (advance_in_state()) { - break; - } else { - advance_state(); - if (check_first()) { - break; - } - } - } - } - - bool operator!=(const SiteWireIterator &other) const { return state != other.state || cursor != other.cursor; } - - SiteWire operator*() const; -}; - -struct SiteWireRange -{ - const SiteArch *site_arch; - SiteWireRange(const SiteArch *site_arch) : site_arch(site_arch) {} - SiteWireIterator begin() const; - - SiteWireIterator end() const - { - SiteWireIterator e; - - e.state = SiteWireIterator::END; - - return e; - } -}; - -inline SiteWireRange SiteArch::getWires() const { return SiteWireRange(this); } - -NEXTPNR_NAMESPACE_END - -#endif /* SITE_ARCH_H */ diff --git a/fpga_interchange/site_arch.impl.h b/fpga_interchange/site_arch.impl.h deleted file mode 100644 index 88df6c0f..00000000 --- a/fpga_interchange/site_arch.impl.h +++ /dev/null @@ -1,327 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef SITE_ARCH_IMPL_H -#define SITE_ARCH_IMPL_H - -#include "context.h" -#include "site_arch.h" - -NEXTPNR_NAMESPACE_BEGIN - -inline const ChipInfoPOD &SiteInformation::chip_info() const { return *ctx->chip_info; } - -inline bool SiteInformation::is_wire_in_site(WireId wire) const -{ - if (wire.tile != tile) { - return false; - } - - return ctx->wire_info(wire).site == site; -} - -inline bool SiteInformation::is_bel_in_site(BelId bel) const -{ - if (bel.tile != tile) { - return false; - } - - return bel_info(ctx->chip_info, bel).site == site; -} - -inline bool SiteInformation::is_pip_part_of_site(PipId pip) const -{ - if (pip.tile != tile) { - return false; - } - - const auto &tile_type_data = ctx->chip_info->tile_types[tile_type]; - const auto &pip_data = tile_type_data.pip_data[pip.index]; - return pip_data.site == site; -} - -inline bool SiteInformation::is_site_port(PipId pip) const -{ - const auto &tile_type_data = ctx->chip_info->tile_types[tile_type]; - const auto &pip_data = tile_type_data.pip_data[pip.index]; - if (pip_data.site == -1) { - return false; - } - auto &bel_data = tile_type_data.bel_data[pip_data.bel]; - return bel_data.category == BEL_CATEGORY_SITE_PORT; -} - -inline SiteWire SiteWire::make(const SiteInformation *site_info, WireId site_wire) -{ - NPNR_ASSERT(site_info->is_wire_in_site(site_wire)); - SiteWire out; - out.type = SITE_WIRE; - out.wire = site_wire; - return out; -} - -inline SiteWire SiteWire::make_site_port(const SiteInformation *site_info, PipId pip, bool dst_wire) -{ - const auto &tile_type_data = site_info->chip_info().tile_types[site_info->tile_type]; - const auto &pip_data = tile_type_data.pip_data[pip.index]; - - // This pip should definitely be part of this site - NPNR_ASSERT(pip_data.site == site_info->site); - - SiteWire out; - - const auto &src_data = tile_type_data.wire_data[pip_data.src_index]; - const auto &dst_data = tile_type_data.wire_data[pip_data.dst_index]; - - if (dst_wire) { - if (src_data.site == site_info->site) { - NPNR_ASSERT(dst_data.site == -1); - out.type = SITE_PORT_SINK; - out.pip = pip; - out.wire = canonical_wire(&site_info->chip_info(), pip.tile, pip_data.dst_index); - } else { - NPNR_ASSERT(src_data.site == -1); - NPNR_ASSERT(dst_data.site == site_info->site); - out.type = SITE_WIRE; - out.wire.tile = pip.tile; - out.wire.index = pip_data.dst_index; - } - } else { - if (src_data.site == site_info->site) { - NPNR_ASSERT(dst_data.site == -1); - out.type = SITE_WIRE; - out.wire.tile = pip.tile; - out.wire.index = pip_data.src_index; - } else { - NPNR_ASSERT(src_data.site == -1); - NPNR_ASSERT(dst_data.site == site_info->site); - out.type = SITE_PORT_SOURCE; - out.pip = pip; - out.wire = canonical_wire(&site_info->chip_info(), pip.tile, pip_data.src_index); - } - } - - return out; -} - -inline SitePip SitePip::make(const SiteInformation *site_info, PipId pip) -{ - SitePip out; - out.pip = pip; - - if (site_info->is_site_port(pip)) { - out.type = SITE_PORT; - } else { - out.type = SITE_PIP; - } - return out; -} - -inline SiteWire SiteArch::getPipSrcWire(const SitePip &site_pip) const -{ - SiteWire site_wire; - switch (site_pip.type) { - case SitePip::Type::SITE_PIP: - return SiteWire::make(site_info, ctx->getPipSrcWire(site_pip.pip)); - case SitePip::Type::SITE_PORT: - return SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/false); - case SitePip::Type::SOURCE_TO_SITE_PORT: - NPNR_ASSERT(site_pip.wire.type == SiteWire::OUT_OF_SITE_SOURCE); - return site_pip.wire; - case SitePip::Type::SITE_PORT_TO_SINK: - site_wire = SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/true); - NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SINK); - return site_wire; - case SitePip::Type::SITE_PORT_TO_SITE_PORT: - site_wire = SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/true); - NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SINK); - return site_wire; - default: - // Unreachable! - NPNR_ASSERT(false); - } -} - -inline SiteWire SiteArch::getPipDstWire(const SitePip &site_pip) const -{ - switch (site_pip.type) { - case SitePip::Type::SITE_PIP: - return SiteWire::make(site_info, ctx->getPipDstWire(site_pip.pip)); - case SitePip::Type::SITE_PORT: - return SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/true); - case SitePip::Type::SOURCE_TO_SITE_PORT: { - SiteWire site_wire = SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/false); - NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SOURCE); - return site_wire; - } - case SitePip::Type::SITE_PORT_TO_SINK: - NPNR_ASSERT(site_pip.wire.type == SiteWire::OUT_OF_SITE_SINK); - return site_pip.wire; - case SitePip::Type::SITE_PORT_TO_SITE_PORT: { - SiteWire site_wire = SiteWire::make_site_port(site_info, site_pip.other_pip, /*dst_wire=*/false); - NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SOURCE); - return site_wire; - } - default: - // Unreachable! - NPNR_ASSERT(false); - } -} - -inline bool SiteArch::is_pip_synthetic(const SitePip &pip) const -{ - if (pip.type != SitePip::SITE_PORT) { - // This isn't a site port, so its valid! - return false; - } - - auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type]; - auto &pip_data = tile_type.pip_data[pip.pip.index]; - if (pip_data.site == -1) { - return pip_data.extra_data == -1; - } else { - auto &bel_data = tile_type.bel_data[pip_data.bel]; - return bel_data.synthetic != 0; - } -} - -inline SyntheticType SiteArch::pip_synthetic_type(const SitePip &pip) const -{ - if (pip.type != SitePip::SITE_PORT) { - // This isn't a site port, so its valid! - return NOT_SYNTH; - } - - auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type]; - auto &pip_data = tile_type.pip_data[pip.pip.index]; - NPNR_ASSERT(pip_data.site != -1); - auto &bel_data = tile_type.bel_data[pip_data.bel]; - return SyntheticType(bel_data.synthetic); -} - -inline SitePip SitePipDownhillIterator::operator*() const -{ - switch (state) { - case NORMAL_PIPS: { - PipId pip; - pip.tile = site_arch->site_info->tile; - pip.index = (*pips_downhill)[cursor]; - return SitePip::make(site_arch->site_info, pip); - } - case PORT_SINK_TO_PORT_SRC: - return SitePip::make(site_arch->site_info, site_wire.pip, site_arch->input_site_ports.at(cursor)); - case OUT_OF_SITE_SINKS: - return SitePip::make(site_arch->site_info, site_wire.pip, site_arch->out_of_site_sinks.at(cursor)); - case OUT_OF_SITE_SOURCE_TO_PORT_SRC: - return SitePip::make(site_arch->site_info, site_wire, site_arch->input_site_ports.at(cursor)); - case SITE_PORT: - return SitePip::make(site_arch->site_info, site_wire.pip); - default: - // Unreachable! - NPNR_ASSERT(false); - } -} - -inline const RelSlice *SitePipDownhillRange::init_pip_range() const -{ - NPNR_ASSERT(site_wire.type == SiteWire::SITE_WIRE); - NPNR_ASSERT(site_wire.wire.tile == site_arch->site_info->tile); - return &site_arch->ctx->chip_info->tile_types[site_arch->site_info->tile_type] - .wire_data[site_wire.wire.index] - .pips_downhill; -} - -inline SitePipDownhillIterator SitePipDownhillRange::begin() const -{ - SitePipDownhillIterator b; - b.state = SitePipDownhillIterator::BEGIN; - b.site_arch = site_arch; - b.site_wire = site_wire; - b.cursor = 0; - if (site_wire.type == SiteWire::SITE_WIRE) { - b.pips_downhill = init_pip_range(); - } - - ++b; - - return b; -} - -inline bool SiteArch::isInverting(const SitePip &site_pip) const -{ - if (site_pip.type != SitePip::SITE_PIP) { - return false; - } - - auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type]; - auto &pip_data = tile_type.pip_data[site_pip.pip.index]; - NPNR_ASSERT(pip_data.site != -1); - auto &bel_data = tile_type.bel_data[pip_data.bel]; - - // Is a fixed inverter if the non_inverting_pin is another pin. - return bel_data.non_inverting_pin != pip_data.extra_data && bel_data.inverting_pin == pip_data.extra_data; -} - -inline bool SiteArch::canInvert(const SitePip &site_pip) const -{ - if (site_pip.type != SitePip::SITE_PIP) { - return false; - } - - auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type]; - auto &pip_data = tile_type.pip_data[site_pip.pip.index]; - NPNR_ASSERT(pip_data.site != -1); - auto &bel_data = tile_type.bel_data[pip_data.bel]; - - // Can optionally invert if this pip is both the non_inverting_pin and - // inverting pin. - return bel_data.non_inverting_pin == pip_data.extra_data && bel_data.inverting_pin == pip_data.extra_data; -} - -inline PhysicalNetlist::PhysNetlist::NetType SiteArch::prefered_constant_net_type(const SitePip &site_pip) const -{ - // FIXME: Implement site port overrides from chipdb once available. - IdString prefered_constant_net(ctx->chip_info->constants->best_constant_net); - IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); - IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); - - if (prefered_constant_net == IdString()) { - return PhysicalNetlist::PhysNetlist::NetType::SIGNAL; - } else if (prefered_constant_net == gnd_net_name) { - return PhysicalNetlist::PhysNetlist::NetType::GND; - } else if (prefered_constant_net == vcc_net_name) { - return PhysicalNetlist::PhysNetlist::NetType::VCC; - } else { - log_error("prefered_constant_net %s is not the GND (%s) or VCC(%s) net?\n", prefered_constant_net.c_str(ctx), - gnd_net_name.c_str(ctx), vcc_net_name.c_str(ctx)); - } -} - -inline SiteWire SiteArch::getBelPinWire(BelId bel, IdString pin) const -{ - WireId wire = ctx->getBelPinWire(bel, pin); - return SiteWire::make(site_info, wire); -} - -inline PortType SiteArch::getBelPinType(BelId bel, IdString pin) const { return ctx->getBelPinType(bel, pin); } - -NEXTPNR_NAMESPACE_END - -#endif /* SITE_ARCH_H */ diff --git a/fpga_interchange/site_lut_mapping_cache.cc b/fpga_interchange/site_lut_mapping_cache.cc deleted file mode 100644 index 82832ed9..00000000 --- a/fpga_interchange/site_lut_mapping_cache.cc +++ /dev/null @@ -1,197 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "site_lut_mapping_cache.h" -#include "nextpnr.h" - -NEXTPNR_NAMESPACE_BEGIN - -// ============================================================================ - -SiteLutMappingKey SiteLutMappingKey::create(const SiteInformation &siteInfo) -{ - const Context *ctx = siteInfo.ctx; - - // Look for LUT cells in the site - std::vector lutCells; - lutCells.reserve(siteInfo.cells_in_site.size()); - - for (CellInfo *cellInfo : siteInfo.cells_in_site) { - - // Not a LUT cell - if (cellInfo->lut_cell.pins.empty()) { - continue; - } - - // Not bound to a LUT BEL - BelId bel = cellInfo->bel; - const auto &bel_data = bel_info(ctx->chip_info, bel); - if (bel_data.lut_element == -1) { - continue; - } - - lutCells.push_back(cellInfo); - } - - // Sort cells by BEL indices to maintain always the same order - std::sort(lutCells.begin(), lutCells.end(), - [](const CellInfo *a, const CellInfo *b) { return a->bel.index > b->bel.index; }); - - // Initialize the key - SiteLutMappingKey key; - key.tileType = siteInfo.tile_type; - key.siteType = ctx->chip_info->sites[siteInfo.site].site_type; - key.numCells = 0; - key.cells.resize(ctx->max_lut_cells); - - // Get bound nets. Store localized (to the LUT cluster) net indices only - // to get always the same key for the same LUT port configuration even - // when the actual global net names are different. - dict netMap; - for (CellInfo *cellInfo : lutCells) { - - NPNR_ASSERT(key.numCells < key.cells.size()); - auto &cell = key.cells[key.numCells++]; - - cell.type = cellInfo->type; - cell.belIndex = cellInfo->bel.index; - - cell.conns.resize(ctx->max_lut_pins, 0); - - size_t portId = 0; - for (const auto &port : cellInfo->ports) { - const auto &portInfo = port.second; - - // Consider only LUT inputs - if (portInfo.type != PORT_IN) { - continue; - } - - // Assign net id if any - int32_t netId = 0; - if (portInfo.net != nullptr) { - auto netInfo = portInfo.net; - - auto it = netMap.find(netInfo->name); - if (it != netMap.end()) { - netId = it->second; - } else { - netId = (int32_t)netMap.size() + 1; - netMap[netInfo->name] = netId; - } - } - - NPNR_ASSERT(portId < cell.conns.size()); - cell.conns[portId++] = netId; - } - } - - // Compute hash - key.computeHash(); - - return key; -} - -// ============================================================================ - -bool SiteLutMappingResult::apply(const SiteInformation &siteInfo) -{ - - Context *ctx = const_cast(siteInfo.ctx); - TileStatus &tileStatus = ctx->get_tile_status(siteInfo.tile); - - for (auto &cell : cells) { - - // Get the bound cell - CellInfo *cellInfo = tileStatus.boundcells[cell.belIndex]; - NPNR_ASSERT(cellInfo); - - // Double check BEL binding - NPNR_ASSERT(cellInfo->bel.tile == siteInfo.tile); - NPNR_ASSERT(cellInfo->bel.index == cell.belIndex); - - // Cell <-> BEL pin map - size_t numPins = cellInfo->lut_cell.pins.size(); - for (size_t pinIdx = 0; pinIdx < numPins; ++pinIdx) { - const IdString &cellPin = cellInfo->lut_cell.pins[pinIdx]; - auto &belPins = cellInfo->cell_bel_pins[cellPin]; - - // There is only one pin - belPins.resize(1); - belPins[0] = cell.belPins[cellPin]; - } - - // LUT data - // FIXME: Is there any other info that is being updated than pin_connections ? - cellInfo->lut_cell.pin_connections = std::move(cell.lutCell.pin_connections); - } - - return true; -} - -size_t SiteLutMappingResult::getSizeInBytes() const -{ - - size_t size = 0; - - size += sizeof(SiteLutMappingResult); - size += blockedWires.size() * sizeof(std::pair); - - for (const auto &cell : cells) { - size += sizeof(Cell); - size += cell.belPins.size() * sizeof(decltype(cell.belPins)::value_type); - } - - return size; -} - -// ============================================================================ - -void SiteLutMappingCache::add(const SiteLutMappingKey &key, const SiteLutMappingResult &result) -{ - cache_[key] = result; -} - -bool SiteLutMappingCache::get(const SiteLutMappingKey &key, SiteLutMappingResult *result) -{ - if (cache_.count(key) == 0) { - numMisses++; - return false; - } - - numHits++; - *result = cache_[key]; - return true; -} - -void SiteLutMappingCache::clear() -{ - cache_.clear(); - clearStats(); -} - -void SiteLutMappingCache::clearStats() -{ - numHits = 0; - numMisses = 0; -} - -// ============================================================================ - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/site_lut_mapping_cache.h b/fpga_interchange/site_lut_mapping_cache.h deleted file mode 100644 index 31e7e5e1..00000000 --- a/fpga_interchange/site_lut_mapping_cache.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef SITE_LUT_MAPPING_CACHE_H -#define SITE_LUT_MAPPING_CACHE_H - -#include "idstring.h" -#include "nextpnr_namespaces.h" -#include "site_arch.h" - -NEXTPNR_NAMESPACE_BEGIN - -// Key structure used in site LUT mapping cache -struct SiteLutMappingKey -{ - // LUT Cell data - struct Cell - { - IdString type; // Cell type - int32_t belIndex; // Bound BEL index - - // Port to net assignments. These are local net ids generated during - // key creation. This is to abstract connections from actual design - // net names. the Id 0 means unconnected. - std::vector conns; - - bool operator==(const Cell &other) const - { - return (type == other.type) && (belIndex == other.belIndex) && (conns == other.conns); - } - - bool operator!=(const Cell &other) const - { - return (type != other.type) || (belIndex != other.belIndex) || (conns != other.conns); - } - }; - - int32_t tileType; // Tile type - int32_t siteType; // Site type in that tile type - size_t numCells; // LUT cell count - std::vector cells; // LUT cell data - - unsigned int hash_; // Precomputed hash - - // Creates a key from the given site state - static SiteLutMappingKey create(const SiteInformation &siteInfo); - - // Returns size in bytes of the key - size_t getSizeInBytes() const { return sizeof(SiteLutMappingKey); } - - // Precomputes hash of the key and stores it within - void computeHash() - { - hash_ = mkhash(0, tileType); - hash_ = mkhash(hash_, siteType); - hash_ = mkhash(hash_, numCells); - for (size_t j = 0; j < numCells; ++j) { - const auto &cell = cells[j]; - hash_ = mkhash(hash_, cell.type.index); - hash_ = mkhash(hash_, cell.belIndex); - for (size_t i = 0; i < cell.conns.size(); ++i) { - hash_ = mkhash(hash_, cell.conns[i]); - } - } - } - - // Compares cell data of this and other key - bool compareCells(const SiteLutMappingKey &other) const - { - if (numCells != other.numCells) { - return false; - } - - for (size_t i = 0; i < numCells; ++i) { - if (cells[i] != other.cells[i]) { - return false; - } - } - return true; - } - - bool operator==(const SiteLutMappingKey &other) const - { - return (hash_ == other.hash_) && (tileType == other.tileType) && (siteType == other.siteType) && - compareCells(other); - } - - bool operator!=(const SiteLutMappingKey &other) const - { - return (hash_ != other.hash_) || (tileType != other.tileType) || (siteType != other.siteType) || - !compareCells(other); - } - - unsigned int hash() const { return hash_; } -}; - -// Site LUT mapping result data -struct SiteLutMappingResult -{ - - // LUT cell data - struct Cell - { - int32_t belIndex; // BEL in tile index - LutCell lutCell; // LUT mapping data - dict belPins; // Cell to BEL pin mapping - }; - - bool isValid; // Validity flag - std::vector cells; // Cell data - - pool> blockedWires; // Set of blocked wires - - // Applies the mapping result to the site - bool apply(const SiteInformation &siteInfo); - - // Returns size in bytes - size_t getSizeInBytes() const; -}; - -// Site LUT mapping cache object -class SiteLutMappingCache -{ - public: - // Adds an entry to the cache - void add(const SiteLutMappingKey &key, const SiteLutMappingResult &result); - // Retrieves an entry from the cache. Returns false if not found - bool get(const SiteLutMappingKey &key, SiteLutMappingResult *result); - - // Clears the cache - void clear(); - // Clears statistics counters of the cache - void clearStats(); - - // Return get() miss ratio - float getMissRatio() const { return (float)numMisses / (float)(numHits + numMisses); } - - // Returns count of entries in the cache - size_t getCount() const { return cache_.size(); } - - // Returns size of the cache rounded upwards to full MBs. - size_t getSizeMB() const - { - size_t size = 0; - for (const auto &it : cache_) { - size += it.first.getSizeInBytes(); - size += it.second.getSizeInBytes(); - } - - const size_t MB = 1024L * 1024L; - return (size + MB - 1) / MB; // Round up to megabytes - } - - private: - dict cache_; // The cache - - size_t numHits = 0; // Hit count - size_t numMisses = 0; // Miss count -}; - -NEXTPNR_NAMESPACE_END - -#endif /* SITE_LUT_MAPPING_CACHE_H */ diff --git a/fpga_interchange/site_router.cc b/fpga_interchange/site_router.cc deleted file mode 100644 index 4e3d460a..00000000 --- a/fpga_interchange/site_router.cc +++ /dev/null @@ -1,1463 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "nextpnr.h" - -#include "design_utils.h" -#include "dynamic_bitarray.h" -#include "log.h" -#include "site_routing_cache.h" - -#include "site_arch.h" -#include "site_arch.impl.h" - -#include - -NEXTPNR_NAMESPACE_BEGIN - -bool verbose_site_router(const Context *ctx) { return ctx->debug; } - -bool verbose_site_router(const SiteArch *ctx) { return verbose_site_router(ctx->ctx); } - -void SiteRouter::bindBel(CellInfo *cell) -{ - auto result = cells_in_site.emplace(cell); - NPNR_ASSERT(result.second); - - dirty = true; -} - -void SiteRouter::unbindBel(CellInfo *cell) -{ - NPNR_ASSERT(cells_in_site.erase(cell) == 1); - - dirty = true; -} - -bool check_initial_wires(const Context *ctx, SiteInformation *site_info) -{ - // Propagate from BEL pins to first wire, checking for trivial routing - // conflicts. - dict wires; - - for (CellInfo *cell : site_info->cells_in_site) { - BelId bel = cell->bel; - for (const auto &pin_pair : cell->cell_bel_pins) { - if (!cell->ports.count(pin_pair.first)) - continue; - const PortInfo &port = cell->ports.at(pin_pair.first); - - if (port.net == nullptr) - continue; - - for (IdString bel_pin_name : pin_pair.second) { - BelPin bel_pin; - bel_pin.bel = bel; - bel_pin.pin = bel_pin_name; - - WireId wire = ctx->getBelPinWire(bel_pin.bel, bel_pin.pin); - auto result = wires.emplace(wire, port.net); - if (!result.second) { - // This wire is already in use, make sure the net bound is - // the same net, otherwise there is a trivial net - // conflict. - const NetInfo *other_net = result.first->second; - if (other_net != port.net) { - // We have a direct net conflict at the BEL pin, - // immediately short circuit the site routing check. - if (verbose_site_router(ctx)) { - log_info("Direct net conflict detected for cell %s:%s at bel %s, net %s != %s\n", - cell->name.c_str(ctx), cell->type.c_str(ctx), ctx->nameOfBel(cell->bel), - port.net->name.c_str(ctx), other_net->name.c_str(ctx)); - } - - return false; - } - } - } - } - } - - return true; -} - -static bool is_invalid_site_port(const SiteArch *ctx, const SiteNetInfo *net, const SitePip &pip) -{ - // Blocked ports - auto fnd_rsv = ctx->blocked_site_ports.find(pip.pip); - if (fnd_rsv != ctx->blocked_site_ports.end() && !fnd_rsv->second.count(net->net)) - return true; - // Synthetic pips - SyntheticType type = ctx->pip_synthetic_type(pip); - PhysicalNetlist::PhysNetlist::NetType net_type = ctx->ctx->get_net_type(net->net); - bool is_invalid = false; - if (type == SYNTH_GND) { - is_invalid = net_type != PhysicalNetlist::PhysNetlist::NetType::GND; - } else if (type == SYNTH_VCC) { - is_invalid = net_type != PhysicalNetlist::PhysNetlist::NetType::VCC; - } - - return is_invalid; -} - -struct SiteExpansionLoop -{ - RouteNodeStorage *const node_storage; - - SiteExpansionLoop(RouteNodeStorage *node_storage) : node_storage(node_storage) - { - NPNR_ASSERT(node_storage != nullptr); - } - - void clear() - { - node_storage->free_nodes(used_nodes); - used_nodes.clear(); - solution.clear(); - net_driver = SiteWire(); - } - - virtual ~SiteExpansionLoop() { node_storage->free_nodes(used_nodes); } - - // Storage for nodes - std::vector used_nodes; - - bool expand_result; - SiteWire net_driver; - pool net_users; - - SiteRoutingSolution solution; - - Node new_node(const SiteWire &wire, const SitePip &pip, const Node *parent) - { - Node node = node_storage->alloc_node(); - used_nodes.push_back(node.get_index()); - - node->wire = wire; - node->pip = pip; - if (parent != nullptr) { - node->parent = (*parent).get_index(); - node->flags = (*parent)->flags; - node->depth = (*parent)->depth + 1; - } - - if (pip.type == SitePip::SITE_PORT) { - // Site ports should always have a parent! - NPNR_ASSERT(parent != nullptr); - if (wire.type == SiteWire::SITE_PORT_SINK) { - NPNR_ASSERT((*parent)->wire.type == SiteWire::SITE_WIRE); - NPNR_ASSERT(node->can_leave_site()); - node->mark_left_site(); - node->mark_left_site_after_entering(); - } else if (wire.type == SiteWire::SITE_PORT_SOURCE) { - // This is a backward walk, so this is considered entering - // the site. - NPNR_ASSERT((*parent)->wire.type == SiteWire::SITE_WIRE); - NPNR_ASSERT(node->can_enter_site()); - node->mark_entered_site(); - } else { - // See if this is a forward or backward walk. - NPNR_ASSERT(wire.type == SiteWire::SITE_WIRE); - if ((*parent)->wire.type == SiteWire::SITE_PORT_SINK) { - // This is a backward walk, so this is considered leaving - // the site. - NPNR_ASSERT(node->can_leave_site()); - node->mark_left_site(); - node->mark_left_site_after_entering(); - } else { - NPNR_ASSERT((*parent)->wire.type == SiteWire::SITE_PORT_SOURCE); - NPNR_ASSERT(node->can_enter_site()); - node->mark_entered_site(); - } - } - } - - return node; - } - - // Expand from wire specified, always downhill. - bool expand_net(const SiteArch *ctx, SiteRoutingCache *site_routing_cache, const SiteNetInfo *net, - bool cache_disabled = false) - { - if (net->driver == net_driver && net->users == net_users) { - return expand_result; - } - - clear(); - - net_driver = net->driver; - net_users = net->users; - - if (!cache_disabled && site_routing_cache->get_solution(ctx, *net, &solution)) { - expand_result = true; - return expand_result; - } - - if (verbose_site_router(ctx)) { - log_info("Expanding net %s from %s\n", ctx->nameOfNet(net), ctx->nameOfWire(net->driver)); - } - - auto node = new_node(net->driver, SitePip(), /*parent=*/nullptr); - - pool targets; - targets.insert(net->users.begin(), net->users.end()); - - if (verbose_site_router(ctx)) { - log_info("%zu targets:\n", targets.size()); - for (auto &target : targets) { - log_info(" - %s\n", ctx->nameOfWire(target)); - } - } - - int32_t max_depth = 0; - int32_t max_depth_seen = 0; - std::vector nodes_to_expand; - nodes_to_expand.push_back(node); - - std::vector completed_routes; - while (!nodes_to_expand.empty()) { - Node parent_node = nodes_to_expand.back(); - nodes_to_expand.pop_back(); - - max_depth_seen = std::max(max_depth_seen, parent_node->depth); - - for (SitePip pip : ctx->getPipsDownhill(parent_node->wire)) { - if (is_invalid_site_port(ctx, net, pip)) { - if (verbose_site_router(ctx)) { - log_info("Pip %s is not a valid site port for net %s, skipping\n", ctx->nameOfPip(pip), - ctx->nameOfNet(net)); - } - continue; - } - - SiteWire wire = ctx->getPipDstWire(pip); - - if (pip.type == SitePip::SITE_PORT) { - if (wire.type == SiteWire::SITE_PORT_SINK) { - if (!parent_node->can_leave_site()) { - // This path has already left the site once, don't leave it again! - if (verbose_site_router(ctx)) { - log_info("%s is not a valid PIP for this path because it has already left the site\n", - ctx->nameOfPip(pip)); - } - continue; - } - } else { - NPNR_ASSERT(parent_node->wire.type == SiteWire::SITE_PORT_SOURCE); - - if (!parent_node->can_enter_site()) { - // This path has already entered the site once, - // don't enter it again! - if (verbose_site_router(ctx)) { - log_info( - "%s is not a valid PIP for this path because it has already entered the site\n", - ctx->nameOfPip(pip)); - } - continue; - } - } - } - - auto wire_iter = ctx->wire_to_nets.find(wire); - if (wire_iter != ctx->wire_to_nets.end() && wire_iter->second.net != net) { - if (verbose_site_router(ctx)) { - log_info("Wire %s is already tied to net %s, not exploring for net %s\n", ctx->nameOfWire(wire), - ctx->nameOfNet(wire_iter->second.net), ctx->nameOfNet(net)); - } - continue; - } - - auto node = new_node(wire, pip, &parent_node); - - if (!node->is_valid_node()) { - if (verbose_site_router(ctx)) { - log_info( - "%s is not a valid PIP for this path because it has left the site after entering it.\n", - ctx->nameOfPip(pip)); - } - continue; - } - - if (targets.count(wire)) { - completed_routes.push_back(node.get_index()); - max_depth = std::max(max_depth, node->depth); - } - - nodes_to_expand.push_back(node); - } - } - - // Make sure expansion reached all targets, otherwise this site is - // already unroutable! - solution.clear(); - solution.store_solution(ctx, node_storage, net->driver, completed_routes); - bool verify = solution.verify(ctx, *net); - - if (!verify) - return false; - - for (size_t route : completed_routes) { - SiteWire wire = node_storage->get_node(route)->wire; - targets.erase(wire); - } - - if (!cache_disabled && targets.empty()) { - site_routing_cache->add_solutions(ctx, *net, solution); - } - - // Return nodes back to the storage system. - node_storage->free_nodes(used_nodes); - used_nodes.clear(); - - expand_result = targets.empty(); - return expand_result; - } - - size_t num_solutions() const { return solution.num_solutions(); } - - const SiteWire &solution_sink(size_t idx) const { return solution.solution_sink(idx); } - std::vector::const_iterator solution_begin(size_t idx) const { return solution.solution_begin(idx); } - - std::vector::const_iterator solution_end(size_t idx) const { return solution.solution_end(idx); } - bool solution_inverted(size_t idx) const { return solution.solution_inverted(idx); } - bool solution_can_invert(size_t idx) const { return solution.solution_can_invert(idx); } -}; - -void print_current_state(const SiteArch *site_arch) -{ - const Context *ctx = site_arch->ctx; - auto &cells_in_site = site_arch->site_info->cells_in_site; - const CellInfo *cell = *cells_in_site.begin(); - BelId bel = cell->bel; - const auto &bel_data = bel_info(ctx->chip_info, bel); - const auto &site_inst = site_inst_info(ctx->chip_info, bel.tile, bel_data.site); - - log_info("Site %s\n", site_inst.name.get()); - - log_info(" Cells in site:\n"); - for (CellInfo *cell : cells_in_site) { - log_info(" - %s (%s) => %s\n", cell->name.c_str(ctx), cell->type.c_str(ctx), ctx->nameOfBel(cell->bel)); - } - - log_info(" Nets in site:\n"); - for (auto &net_pair : site_arch->nets) { - auto *net = net_pair.first; - log_info(" - %s, pins in site:\n", net->name.c_str(ctx)); - if (net->driver.cell && cells_in_site.count(net->driver.cell)) { - log_info(" - %s/%s (%s)\n", net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx), - net->driver.cell->type.c_str(ctx)); - } - - for (const auto user : net->users) { - if (user.cell && cells_in_site.count(user.cell)) { - log_info(" - %s/%s (%s)\n", user.cell->name.c_str(ctx), user.port.c_str(ctx), - user.cell->type.c_str(ctx)); - } - } - } - - log_info(" Consumed wires:\n"); - for (auto &wire_pair : site_arch->wire_to_nets) { - const SiteWire &site_wire = wire_pair.first; - if (site_wire.type != SiteWire::SITE_WIRE) { - continue; - } - WireId wire = site_wire.wire; - const NetInfo *net = wire_pair.second.net->net; - log_info(" - %s is bound to %s\n", ctx->nameOfWire(wire), net->name.c_str(ctx)); - } -} - -struct PossibleSolutions -{ - bool tested = false; - SiteNetInfo *net = nullptr; - std::vector::const_iterator pips_begin; - std::vector::const_iterator pips_end; - bool inverted = false; - bool can_invert = false; - PhysicalNetlist::PhysNetlist::NetType prefered_constant_net_type = PhysicalNetlist::PhysNetlist::NetType::SIGNAL; -}; - -bool test_solution(SiteArch *ctx, SiteNetInfo *net, std::vector::const_iterator pips_begin, - std::vector::const_iterator pips_end) -{ - bool valid = true; - std::vector::const_iterator good_pip_end = pips_begin; - std::vector::const_iterator iter = pips_begin; - SitePip pip; - while (iter != pips_end) { - pip = *iter; - if (!ctx->bindPip(pip, net)) { - valid = false; - break; - } - - ++iter; - good_pip_end = iter; - } - - // Unwind a bad solution - if (!valid) { - for (auto iter = pips_begin; iter != good_pip_end; ++iter) { - ctx->unbindPip(*iter); - } - } else { - NPNR_ASSERT(net->driver == ctx->getPipSrcWire(pip)); - } - - return valid; -} - -void remove_solution(SiteArch *ctx, std::vector::const_iterator pips_begin, - std::vector::const_iterator pips_end) -{ - for (auto iter = pips_begin; iter != pips_end; ++iter) { - ctx->unbindPip(*iter); - } -} - -struct SolutionPreference -{ - const SiteArch *ctx; - const std::vector &solutions; - - SolutionPreference(const SiteArch *ctx, const std::vector &solutions) - : ctx(ctx), solutions(solutions) - { - } - - bool non_inverting_preference(const PossibleSolutions &lhs, const PossibleSolutions &rhs) const - { - // If the LHS is non-inverting and the RHS is inverting, then put the - // LHS first. - if (!lhs.inverted && rhs.inverted) { - return true; - } - - // Better to have a path that can invert over a path that has no - // option to invert. - return (!lhs.can_invert) < (!rhs.can_invert); - } - - bool inverting_preference(const PossibleSolutions &lhs, const PossibleSolutions &rhs) const - { - // If the LHS is inverting and the RHS is non-inverting, then put the - // LHS first (because this is the inverting preferred case). - if (lhs.inverted && !rhs.inverted) { - return true; - } - - // Better to have a path that can invert over a path that has no - // option to invert. - return (!lhs.can_invert) < (!rhs.can_invert); - } - - bool operator()(size_t lhs_solution_idx, size_t rhs_solution_idx) const - { - const PossibleSolutions &lhs = solutions.at(lhs_solution_idx); - const PossibleSolutions &rhs = solutions.at(rhs_solution_idx); - - NPNR_ASSERT(lhs.net == rhs.net); - - PhysicalNetlist::PhysNetlist::NetType net_type = ctx->ctx->get_net_type(lhs.net->net); - if (net_type == PhysicalNetlist::PhysNetlist::NetType::SIGNAL) { - return non_inverting_preference(lhs, rhs); - } - - // All GND/VCC nets use out of site sources. Local constant sources - // are still connected via synthetic edges to the global GND/VCC - // network. - NPNR_ASSERT(lhs.net->driver.type == SiteWire::OUT_OF_SITE_SOURCE); - - bool lhs_match_preference = net_type == lhs.prefered_constant_net_type; - bool rhs_match_preference = net_type == rhs.prefered_constant_net_type; - - if (lhs_match_preference && !rhs_match_preference) { - // Prefer solutions where the net type already matches the - // prefered constant type. - return true; - } - - if (!lhs_match_preference && rhs_match_preference) { - // Prefer solutions where the net type already matches the - // prefered constant type. In this case the RHS is better, which - // means that RHS < LHS, hence false here. - return false; - } - - NPNR_ASSERT(lhs_match_preference == rhs_match_preference); - - if (!lhs_match_preference) { - // If the net type does not match the preference, then prefer - // inverted solutions. - return inverting_preference(lhs, rhs); - } else { - // If the net type does match the preference, then prefer - // non-inverted solutions. - return non_inverting_preference(lhs, rhs); - } - } -}; - -static bool find_solution_via_backtrack(SiteArch *ctx, std::vector *solutions, - std::vector> sinks_to_solutions, - const std::vector &sinks, bool explain) -{ - std::vector routed_sinks; - std::vector solution_indicies; - routed_sinks.resize(sinks_to_solutions.size(), 0); - solution_indicies.resize(sinks_to_solutions.size(), 0); - - // Scan solutions, and remove any solutions that are invalid immediately - // - // Note: This result cannot be cached because some solutions may be - // placement dependent. - for (size_t solution_idx = 0; solution_idx < solutions->size(); ++solution_idx) { - PossibleSolutions &solution = (*solutions)[solution_idx]; - if (verbose_site_router(ctx) || explain) { - log_info("Testing solution %zu\n", solution_idx); - } - if (test_solution(ctx, solution.net, solution.pips_begin, solution.pips_end)) { - if (verbose_site_router(ctx) || explain) { - log_info("Solution %zu is good\n", solution_idx); - } - remove_solution(ctx, solution.pips_begin, solution.pips_end); - } else { - if (verbose_site_router(ctx) || explain) { - log_info("Solution %zu is not useable\n", solution_idx); - } - solution.tested = true; - } - } - - // Sort sinks_to_solutions so that preferred solutions are tested earlier - // than less preferred solutions. - // - // See SolutionPreference implementation for details. - for (size_t sink_idx = 0; sink_idx < sinks_to_solutions.size(); ++sink_idx) { - std::vector &solutions_for_sink = sinks_to_solutions.at(sink_idx); - std::stable_sort(solutions_for_sink.begin(), solutions_for_sink.end(), SolutionPreference(ctx, *solutions)); - - if (verbose_site_router(ctx) || explain) { - log_info("Solutions for sink %s (%zu)\n", ctx->nameOfWire(sinks.at(sink_idx)), sink_idx); - for (size_t solution_idx : solutions_for_sink) { - const PossibleSolutions &solution = solutions->at(solution_idx); - log_info("%zu: inverted = %d, can_invert = %d, tested = %d\n", solution_idx, solution.inverted, - solution.can_invert, solution.tested); - for (auto iter = solution.pips_begin; iter != solution.pips_end; ++iter) { - log_info(" - %s\n", ctx->nameOfPip(*iter)); - } - } - } - } - - // Sort solutions by the number of possible solutions first. This allows - // the backtrack to avoid the wide searches first. - - // First create a tuple vector of (sink_idx, number of valid solutions for sink). - std::vector> solution_order; - for (size_t sink_idx = 0; sink_idx < sinks_to_solutions.size(); ++sink_idx) { - size_t solution_count = 0; - for (size_t solution_idx : sinks_to_solutions[sink_idx]) { - if (!(*solutions)[solution_idx].tested) { - solution_count += 1; - } - } - - if (solution_count == 0) { - if (verbose_site_router(ctx) || explain) { - log_info("Sink %s has no solution in site\n", ctx->nameOfWire(sinks.at(sink_idx))); - } - return false; - } - - solution_order.emplace_back(sink_idx, solution_count); - } - - // Actually sort so that sinks with fewer valid solutions are tested - // earlier. - std::sort(solution_order.begin(), solution_order.end(), - [](const std::pair &a, const std::pair &b) -> bool { - return a.second < b.second; - }); - - std::vector solution_stack; - solution_stack.reserve(sinks_to_solutions.size()); - - if (verbose_site_router(ctx)) { - log_info("Solving via backtrack with %zu solutions and %zu sinks\n", solutions->size(), - sinks_to_solutions.size()); - } - - // Simple backtrack explorer: - // - Apply the next solution at stack index. - // - If solution is valid, push solution onto stack, and advance stack - // index at solution 0. - // - If solution is not valid, pop the stack. - // - At this level of the stack, advance to the next solution. If - // there are not more solutions at this level, pop again. - // - If stack is now empty, mark root solution as tested and invalid. - // If root of stack has no more solutions, no solution is possible. - while (true) { - // Which sink is next to be tested? - size_t sink_idx = solution_order[solution_stack.size()].first; - - size_t next_solution_to_test = solution_indicies[sink_idx]; - if (verbose_site_router(ctx) || explain) { - log_info("next %zu : %zu (of %zu)\n", sink_idx, next_solution_to_test, sinks_to_solutions[sink_idx].size()); - } - if (next_solution_to_test >= sinks_to_solutions[sink_idx].size()) { - // We have exausted all solutions at this level of the stack! - if (solution_stack.empty()) { - // Search is done, failed!!! - if (verbose_site_router(ctx) || explain) { - log_info("No solution found via backtrack with %zu solutions and %zu sinks\n", solutions->size(), - sinks_to_solutions.size()); - } - return false; - } else { - // This level of the stack is completely tapped out, pop back - // to the next level up. - size_t sink_idx = solution_order[solution_stack.size() - 1].first; - size_t solution_idx = solution_stack.back(); - if (verbose_site_router(ctx) || explain) { - log_info("pop %zu : %zu\n", sink_idx, solution_idx); - } - solution_stack.pop_back(); - - // Remove the now tested bad solution at the previous level of - // the stack. - auto &solution = solutions->at(solution_idx); - remove_solution(ctx, solution.pips_begin, solution.pips_end); - - // Because we had to pop up the stack, advance the index at - // the level below us and start again. - solution_indicies[sink_idx] += 1; - continue; - } - } - - size_t solution_idx = sinks_to_solutions[sink_idx].at(next_solution_to_test); - auto &solution = solutions->at(solution_idx); - if (solution.tested) { - // This solution was already determined to be no good, skip it. - if (verbose_site_router(ctx) || explain) { - log_info("skip %zu : %zu\n", sink_idx, solution_idx); - } - solution_indicies[sink_idx] += 1; - continue; - } - - if (verbose_site_router(ctx) || explain) { - log_info("test %zu : %zu\n", sink_idx, solution_idx); - } - - if (!test_solution(ctx, solution.net, solution.pips_begin, solution.pips_end)) { - // This solution was no good, try the next one at this level of - // the stack. - solution_indicies[sink_idx] += 1; - } else { - // This solution was good, push onto the stack. - if (verbose_site_router(ctx) || explain) { - log_info("push %zu : %zu\n", sink_idx, solution_idx); - } - solution_stack.push_back(solution_idx); - if (solution_stack.size() == sinks_to_solutions.size()) { - // Found a valid solution, done! - if (verbose_site_router(ctx)) { - log_info("Solved via backtrack with %zu solutions and %zu sinks\n", solutions->size(), - sinks_to_solutions.size()); - } - return true; - } else { - // Because we pushing to a new level of stack, restart the - // search at this level. - sink_idx = solution_order[solution_stack.size()].first; - solution_indicies[sink_idx] = 0; - } - } - } - - // Unreachable!!! - NPNR_ASSERT(false); -} - -static bool route_site(SiteArch *ctx, SiteRoutingCache *site_routing_cache, RouteNodeStorage *node_storage, - bool explain, bool cache_disabled = false) -{ - // Overview: - // - Starting from each site net source, expand the site routing graph - // and record all reachable sinks. - // - Note: This step is cachable and is being cached. This cache - // behaving well is critical to the performance of route_site. - // - Convert site expansion solutions into flat solution map. - // - Use backtrack algorithm to find conflict free solution to route site. - // - std::vector expansions; - expansions.reserve(ctx->nets.size()); - - for (auto &net_pair : ctx->nets) { - if (net_pair.first->driver.cell == nullptr) - continue; - SiteNetInfo *net = &net_pair.second; - - if (net->net->loop == nullptr) { - net->net->loop = new SiteExpansionLoop(node_storage); - } - expansions.push_back(net->net->loop); - - SiteExpansionLoop *router = expansions.back(); - if (!router->expand_net(ctx, site_routing_cache, net, cache_disabled)) { - if (verbose_site_router(ctx) || explain) { - log_info("Net %s expansion failed to reach all users, site is unroutable!\n", ctx->nameOfNet(net)); - } - - return false; - } - } - - // Convert solutions into a flat solution set. - - // Create a flat sink list and map. - std::vector sinks; - dict sink_map; - size_t number_solutions = 0; - for (const auto *expansion : expansions) { - number_solutions += expansion->num_solutions(); - for (const SiteWire &unrouted_sink : expansion->net_users) { - auto result = sink_map.emplace(unrouted_sink, sink_map.size()); - NPNR_ASSERT(result.second); - sinks.push_back(unrouted_sink); - } - } - - if (sink_map.empty()) { - // All nets are trivially routed! - return true; - } - - std::vector solutions; - solutions.reserve(number_solutions); - - // Create a map from flat sink index to flat solution index. - std::vector> sinks_to_solutions; - sinks_to_solutions.resize(sink_map.size()); - - // Actually flatten solutions. - for (const auto *expansion : expansions) { - for (size_t idx = 0; idx < expansion->num_solutions(); ++idx) { - SiteWire wire = expansion->solution_sink(idx); - auto begin = expansion->solution_begin(idx); - auto end = expansion->solution_end(idx); - NPNR_ASSERT(begin != end); - - size_t sink_idx = sink_map.at(wire); - sinks_to_solutions.at(sink_idx).push_back(solutions.size()); - - solutions.emplace_back(); - auto &solution = solutions.back(); - solution.net = ctx->wire_to_nets.at(wire).net; - solution.pips_begin = begin; - solution.pips_end = end; - solution.inverted = expansion->solution_inverted(idx); - solution.can_invert = expansion->solution_can_invert(idx); - - for (auto iter = begin; iter != end; ++iter) { - const SitePip &site_pip = *iter; - NPNR_ASSERT(ctx->getPipDstWire(site_pip) == wire); - wire = ctx->getPipSrcWire(site_pip); - - // If there is a input site port, mark on the solution what the - // prefered constant net type is for this site port. - if (site_pip.type == SitePip::SITE_PORT && wire.type == SiteWire::SITE_PORT_SOURCE) { - solution.prefered_constant_net_type = ctx->prefered_constant_net_type(site_pip); - } - } - - NPNR_ASSERT(expansion->net_driver == wire); - } - } - - return find_solution_via_backtrack(ctx, &solutions, sinks_to_solutions, sinks, explain); -} - -void check_routing(const SiteArch &site_arch) -{ - for (auto &net_pair : site_arch.nets) { - const NetInfo *net = net_pair.first; - if (net->driver.cell == nullptr) - continue; - const SiteNetInfo &net_info = net_pair.second; - - for (const auto &user : net_info.users) { - NPNR_ASSERT(site_arch.wire_to_nets.at(user).net->net == net); - SiteWire cursor = user; - while (cursor != net_info.driver) { - auto iter = net_info.wires.find(cursor); - if (iter == net_info.wires.end()) { - log_error("Wire %s has no pip, but didn't reach driver wire %s\n", site_arch.nameOfWire(cursor), - site_arch.nameOfWire(net_info.driver)); - } - const SitePip &site_pip = iter->second.pip; - cursor = site_arch.getPipSrcWire(site_pip); - } - - NPNR_ASSERT(cursor == net_info.driver); - NPNR_ASSERT(site_arch.wire_to_nets.at(cursor).net->net == net); - } - } -} - -static void apply_simple_routing(Context *ctx, const SiteArch &site_arch, NetInfo *net, const SiteNetInfo *site_net, - const SiteWire &user) -{ - SiteWire wire = user; - while (wire != site_net->driver) { - SitePip site_pip = site_net->wires.at(wire).pip; - NPNR_ASSERT(site_arch.getPipDstWire(site_pip) == wire); - - if (site_pip.type == SitePip::SITE_PIP || site_pip.type == SitePip::SITE_PORT) { - NetInfo *bound_net = ctx->getBoundPipNet(site_pip.pip); - if (bound_net == nullptr) { - ctx->bindPip(site_pip.pip, net, STRENGTH_PLACER); - } else { - NPNR_ASSERT(bound_net == net); - } - } - - wire = site_arch.getPipSrcWire(site_pip); - } -} - -static void apply_constant_routing(Context *ctx, const SiteArch &site_arch, NetInfo *net, const SiteNetInfo *site_net) -{ - IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); - NetInfo *gnd_net = ctx->nets.at(gnd_net_name).get(); - - IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); - NetInfo *vcc_net = ctx->nets.at(vcc_net_name).get(); - - // This function is designed to operate only on the gnd or vcc net, and - // assumes that the GND and VCC nets have been unified. - NPNR_ASSERT(net == vcc_net || net == gnd_net); - - for (auto &user : site_net->users) { - bool is_path_inverting = false, path_can_invert = false; - - SiteWire wire = user; - PipId inverting_pip; - PhysicalNetlist::PhysNetlist::NetType pref_const = PhysicalNetlist::PhysNetlist::NetType::SIGNAL; - - while (wire != site_net->driver) { - SitePip pip = site_net->wires.at(wire).pip; - NPNR_ASSERT(site_arch.getPipDstWire(pip) == wire); - - if (site_arch.isInverting(pip)) { - // FIXME: Should be able to handle the general case of - // multiple inverters, but that is harder (and annoying). Also - // most sites won't allow for a double inversion, so just - // disallow for now. - NPNR_ASSERT(!is_path_inverting); - is_path_inverting = true; - NPNR_ASSERT(pip.type == SitePip::SITE_PIP); - inverting_pip = pip.pip; - } - - if (site_arch.canInvert(pip)) { - path_can_invert = true; - NPNR_ASSERT(pip.type == SitePip::SITE_PIP); - inverting_pip = pip.pip; - } - - wire = site_arch.getPipSrcWire(pip); - pref_const = site_arch.prefered_constant_net_type(pip); - } - - bool is_pref_const = true; - if (pref_const == PhysicalNetlist::PhysNetlist::NetType::VCC && net == gnd_net) - is_pref_const = false; - else if (pref_const == PhysicalNetlist::PhysNetlist::NetType::GND && net == vcc_net) - is_pref_const = false; - - if (!is_path_inverting && (!path_can_invert || is_pref_const)) { - // This routing is boring, use base logic. - apply_simple_routing(ctx, site_arch, net, site_net, user); - continue; - } - - NPNR_ASSERT(inverting_pip != PipId()); - - // This net is going to become two nets. - // The portion of the net prior to the inverter is going to be bound - // to the opposite net. For example, if the original net was gnd_net, - // the portion prior to the inverter will not be the vcc_net. - // - // A new cell will be generated to sink the connection from the - // opposite net. - NetInfo *net_before_inverter; - if (net == gnd_net) { - net_before_inverter = vcc_net; - } else { - NPNR_ASSERT(net == vcc_net); - net_before_inverter = gnd_net; - } - - // First find a name for the new cell - int count = 0; - CellInfo *new_cell = nullptr; - while (true) { - std::string new_cell_name = stringf("%s_%s.%d", net->name.c_str(ctx), site_arch.nameOfWire(user), count); - IdString new_cell_id = ctx->id(new_cell_name); - if (ctx->cells.count(new_cell_id)) { - count += 1; - } else { - new_cell = ctx->createCell(new_cell_id, ctx->id("$nextpnr_inv")); - break; - } - } - - auto &tile_type = loc_info(ctx->chip_info, inverting_pip); - auto &pip_data = tile_type.pip_data[inverting_pip.index]; - NPNR_ASSERT(pip_data.site != -1); - auto &bel_data = tile_type.bel_data[pip_data.bel]; - - BelId inverting_bel; - inverting_bel.tile = inverting_pip.tile; - inverting_bel.index = pip_data.bel; - - IdString in_port(bel_data.ports[pip_data.extra_data]); - NPNR_ASSERT(bel_data.types[pip_data.extra_data] == PORT_IN); - - IdString id_I = ctx->id("I"); - new_cell->addInput(id_I); - new_cell->cell_bel_pins[id_I].push_back(in_port); - - new_cell->bel = inverting_bel; - new_cell->belStrength = STRENGTH_PLACER; - ctx->tileStatus.at(inverting_bel.tile).boundcells[inverting_bel.index] = new_cell; - - new_cell->connectPort(id_I, net_before_inverter); - - // The original BEL pin is now routed, but only through the inverter. - // Because the cell/net model doesn't allow for multiple source pins - // and the fact that the portion of the net after the inverter is - // currently routed, all BEL pins on this site wire are going to be - // masked from the router. - NPNR_ASSERT(user.type == SiteWire::SITE_WIRE); - ctx->mask_bel_pins_on_site_wire(net, user.wire); - - // Bind wires and pips to the two nets. - bool after_inverter = true; - wire = user; - while (wire != site_net->driver) { - SitePip site_pip = site_net->wires.at(wire).pip; - NPNR_ASSERT(site_arch.getPipDstWire(site_pip) == wire); - - if (site_arch.isInverting(site_pip) || site_arch.canInvert(site_pip)) { - NPNR_ASSERT(after_inverter); - after_inverter = false; - - // Because this wire is just after the inverter, bind it to - // the net without the pip, as this is a "source". - NPNR_ASSERT(wire.type == SiteWire::SITE_WIRE); - ctx->bindWire(wire.wire, net, STRENGTH_PLACER); - } else { - if (site_pip.type == SitePip::SITE_PIP || site_pip.type == SitePip::SITE_PORT) { - if (after_inverter) { - ctx->bindPip(site_pip.pip, net, STRENGTH_PLACER); - } else { - ctx->bindPip(site_pip.pip, net_before_inverter, STRENGTH_PLACER); - } - } - } - - wire = site_arch.getPipSrcWire(site_pip); - } - } -} - -static void apply_routing(Context *ctx, const SiteArch &site_arch, pool> &lut_thrus) -{ - IdString gnd_net_name(ctx->chip_info->constants->gnd_net_name); - NetInfo *gnd_net = ctx->nets.at(gnd_net_name).get(); - - IdString vcc_net_name(ctx->chip_info->constants->vcc_net_name); - NetInfo *vcc_net = ctx->nets.at(vcc_net_name).get(); - - for (auto &net_pair : site_arch.nets) { - NetInfo *net = net_pair.first; - const SiteNetInfo *site_net = &net_pair.second; - - if (net == gnd_net || net == vcc_net) { - apply_constant_routing(ctx, site_arch, net, site_net); - } else { - // If the driver wire is a site wire, bind it. - if (site_net->driver.type == SiteWire::SITE_WIRE) { - WireId driver_wire = site_net->driver.wire; - if (ctx->getBoundWireNet(driver_wire) != net) { - ctx->bindWire(driver_wire, net, STRENGTH_PLACER); - } - } - - for (auto &wire_pair : site_net->wires) { - const SitePip &site_pip = wire_pair.second.pip; - if (site_pip.type != SitePip::SITE_PIP && site_pip.type != SitePip::SITE_PORT) { - continue; - } - - auto &pip_data = pip_info(ctx->chip_info, site_pip.pip); - - BelId bel; - bel.tile = site_pip.pip.tile; - bel.index = pip_data.bel; - const auto &bel_data = bel_info(ctx->chip_info, bel); - - // Detect and store LUT thrus for allowance check during routing - if (bel_data.lut_element != -1) { - WireId src_wire = ctx->getPipSrcWire(site_pip.pip); - - for (BelPin bel_pin : ctx->getWireBelPins(src_wire)) { - if (bel_pin.bel != bel) - continue; - - lut_thrus.insert(std::make_pair(bel_pin.pin, bel_pin.bel.index)); - break; - } - } - - ctx->bindPip(site_pip.pip, net, STRENGTH_PLACER); - } - } - } -} - -static bool map_luts_in_site(const SiteInformation &site_info, pool> *blocked_wires) -{ - const Context *ctx = site_info.ctx; - bool enable_cache = !ctx->arch_args.disable_lut_mapping_cache; - - // Create a site LUT mapping key - SiteLutMappingKey key = SiteLutMappingKey::create(site_info); - - // Get the solution from cache. If not found then compute it - SiteLutMappingResult lutMapping; - if (!enable_cache || !ctx->site_lut_mapping_cache.get(key, &lutMapping)) { - - const std::vector &lut_elements = ctx->lut_elements.at(site_info.tile_type); - std::vector lut_mappers; - lut_mappers.reserve(lut_elements.size()); - for (size_t i = 0; i < lut_elements.size(); ++i) { - lut_mappers.push_back(LutMapper(lut_elements[i])); - } - - for (CellInfo *cell : site_info.cells_in_site) { - if (cell->lut_cell.pins.empty()) { - continue; - } - - BelId bel = cell->bel; - const auto &bel_data = bel_info(ctx->chip_info, bel); - if (bel_data.lut_element != -1) { - lut_mappers[bel_data.lut_element].cells.push_back(cell); - } - } - - bool res = true; - - lutMapping.blockedWires.clear(); - for (LutMapper lut_mapper : lut_mappers) { - if (lut_mapper.cells.empty()) { - continue; - } - - pool blocked_luts; - if (!lut_mapper.remap_luts(ctx, &lutMapping, &blocked_luts)) { - res = false; - break; - } - - for (const LutBel *lut_bel : blocked_luts) { - lutMapping.blockedWires.emplace(std::make_pair(lut_bel->name, lut_bel->output_pin)); - } - } - - lutMapping.isValid = res; - - // Add the solution to the cache - if (enable_cache) { - ctx->site_lut_mapping_cache.add(key, lutMapping); - } - } - - // Apply the solution if valid - if (lutMapping.isValid) { - - lutMapping.apply(site_info); - - blocked_wires->clear(); - blocked_wires->insert(lutMapping.blockedWires.begin(), lutMapping.blockedWires.end()); - } - - return lutMapping.isValid; -} - -// Block outputs of unavailable LUTs to prevent site router from using them. -static void block_lut_outputs(SiteArch *site_arch, const pool> &blocked_wires) -{ - const Context *ctx = site_arch->site_info->ctx; - auto &tile_info = ctx->chip_info->tile_types[site_arch->site_info->tile_type]; - for (const auto &bel_pin_pair : blocked_wires) { - IdString bel_name = bel_pin_pair.first; - IdString bel_pin = bel_pin_pair.second; - - int32_t bel_index = -1; - for (int32_t i = 0; i < tile_info.bel_data.ssize(); i++) { - if (tile_info.bel_data[i].site == site_arch->site_info->site && - tile_info.bel_data[i].name == bel_name.index) { - bel_index = i; - break; - } - } - - NPNR_ASSERT(bel_index != -1); - BelId bel; - bel.tile = site_arch->site_info->tile; - bel.index = bel_index; - - SiteWire lut_output_wire = site_arch->getBelPinWire(bel, bel_pin); - site_arch->bindWire(lut_output_wire, &site_arch->blocking_site_net); - } -} - -// Block wires corresponding to dedicated interconnections that are not -// exposed to the general interconnect. -// These wires cannot be chosen for the first element in a BEL chain, as they -// would result in an unroutability error. -static void block_cluster_wires(SiteArch *site_arch) -{ - const Context *ctx = site_arch->site_info->ctx; - auto &cells_in_site = site_arch->site_info->cells_in_site; - - for (auto &cell : cells_in_site) { - if (cell->cluster == ClusterId()) - continue; - - if (ctx->getClusterRootCell(cell->cluster) != cell) - continue; - - Cluster cluster = ctx->clusters.at(cell->cluster); - - uint32_t cluster_id = cluster.index; - auto &cluster_data = cluster_info(ctx->chip_info, cluster_id); - - if (cluster_data.chainable_ports.size() == 0) - continue; - - IdString cluster_chain_input(cluster_data.chainable_ports[0].cell_sink); - - if (cluster_chain_input == IdString()) - continue; - - auto &cell_bel_pins = cell->cell_bel_pins.at(cluster_chain_input); - for (auto &bel_pin : cell_bel_pins) { - SiteWire bel_pin_wire = site_arch->getBelPinWire(cell->bel, bel_pin); - site_arch->bindWire(bel_pin_wire, &site_arch->blocking_site_net); - } - } -} - -// Reserve site ports that are on dedicated rather than general interconnect -static void reserve_site_ports(SiteArch *site_arch) -{ - const Context *ctx = site_arch->site_info->ctx; - site_arch->blocked_site_ports.clear(); - for (PipId in_pip : site_arch->input_site_ports) { - pool dedicated_nets; - const int max_iters = 100; - - std::queue visit_queue; - pool already_visited; - WireId src = ctx->getPipSrcWire(in_pip); - visit_queue.push(src); - already_visited.insert(src); - - int iter = 0; - while (!visit_queue.empty() && iter++ < max_iters) { - WireId next = visit_queue.front(); - visit_queue.pop(); - for (auto bp : ctx->getWireBelPins(next)) { - // Bel pins could mean dedicated routes - CellInfo *bound = ctx->getBoundBelCell(bp.bel); - if (bound == nullptr) - continue; - // Need to find the corresponding cell pin - for (auto &port : bound->ports) { - if (port.second.net == nullptr) - continue; - for (auto bel_pin : ctx->getBelPinsForCellPin(bound, port.first)) { - if (bel_pin == bp.pin) - dedicated_nets.insert(port.second.net); - } - } - } - for (auto pip : ctx->getPipsUphill(next)) { - WireId next_src = ctx->getPipSrcWire(pip); - if (already_visited.count(next_src)) - continue; - visit_queue.push(next_src); - already_visited.insert(next_src); - } - } - if (iter < max_iters) { - if (ctx->debug) - log_info("Blocking PIP %s\n", ctx->nameOfPip(in_pip)); - // If we didn't search up to the iteration limit, assume this node is not reachable from general routing - site_arch->blocked_site_ports[in_pip] = dedicated_nets; - } - } -} - -// Recursively visit downhill PIPs until a SITE_PORT_SINK is reached. -// Marks all PIPs for all valid paths. -static bool visit_downhill_pips(const SiteArch *site_arch, const SiteWire &site_wire, std::vector &valid_pips) -{ - bool valid_path_exists = false; - for (SitePip site_pip : site_arch->getPipsDownhill(site_wire)) { - const SiteWire &dst_wire = site_arch->getPipDstWire(site_pip); - if (dst_wire.type == SiteWire::SITE_PORT_SINK) { - valid_pips.push_back(site_pip.pip); - return true; - } - - bool path_ok = visit_downhill_pips(site_arch, dst_wire, valid_pips); - valid_path_exists |= path_ok; - - if (path_ok) { - valid_pips.push_back(site_pip.pip); - } - } - - return valid_path_exists; -} - -// Checks all downhill PIPs starting from driver wires. -// All valid PIPs are stored and returned in a vector. -static void check_downhill_pips(Context *ctx, const SiteArch *site_arch, std::vector &valid_pips) -{ - auto &cells_in_site = site_arch->site_info->cells_in_site; - - for (auto &net_pair : site_arch->nets) { - NetInfo *net = net_pair.first; - const SiteNetInfo *site_net = &net_pair.second; - - if (net->driver.cell && cells_in_site.count(net->driver.cell)) { - const SiteWire &site_wire = site_net->driver; - - visit_downhill_pips(site_arch, site_wire, valid_pips); - } - } -} - -bool SiteRouter::checkSiteRouting(const Context *ctx, const TileStatus &tile_status) const -{ - // Overview: - // - Make sure all cells in site satisfy the constraints. - // - Ensure that the LUT equation elements in the site are legal - // - Check that the site is routable. - - // Because site routing checks are expensive, cache them. - // SiteRouter::bindBel/unbindBel should correctly invalid the cache by - // setting dirty=true. - - if (!dirty) { - return site_ok; - } - - dirty = false; - - // Empty sites are trivially correct. - if (cells_in_site.size() == 0) { - site_ok = true; - return site_ok; - } - - site_ok = false; - - // Make sure all cells in this site belong! - auto iter = cells_in_site.begin(); - NPNR_ASSERT((*iter)->bel != BelId()); - auto tile = (*iter)->bel.tile; - - if (verbose_site_router(ctx)) { - log_info("Checking site routing for site %s\n", ctx->get_site_name(tile, site)); - } - - for (CellInfo *cell : cells_in_site) { - // All cells in the site must be placed. - NPNR_ASSERT(cell->bel != BelId()); - - // Sanity check that all cells in this site are part of the same site. - NPNR_ASSERT(tile == cell->bel.tile); - NPNR_ASSERT(site == bel_info(ctx->chip_info, cell->bel).site); - - // As a first pass make sure each assigned cell in site is valid by - // constraints. - if (!ctx->is_cell_valid_constraints(cell, tile_status, verbose_site_router(ctx))) { - if (verbose_site_router(ctx)) { - log_info("Sanity check failed, cell_type %s at %s has an invalid constraints, so site is not good\n", - cell->type.c_str(ctx), ctx->nameOfBel(cell->bel)); - } - site_ok = false; - return site_ok; - } - } - - SiteInformation site_info(ctx, tile, site, cells_in_site); - pool> blocked_wires; - if (!map_luts_in_site(site_info, &blocked_wires)) { - site_ok = false; - return site_ok; - } - - // Push from cell pins to the first WireId from each cell pin. - // - // If a site wire conflict occurs here, the site is trivially unroutable. - if (!check_initial_wires(ctx, &site_info)) { - site_ok = false; - return site_ok; - } - - // Construct a model of the site routing that is suitable for routing - // algorithms. - SiteArch site_arch(&site_info); - - // SiteArch::archcheck is a good sanity check on SiteArch and the chipdb, - // but isn't cheap, so disabled for now. - // - // site_arch.archcheck(); - - block_lut_outputs(&site_arch, blocked_wires); - block_cluster_wires(&site_arch); - - // Do a detailed routing check to see if the site has at least 1 valid - // routing solution. - site_ok = route_site(&site_arch, &ctx->site_routing_cache, &ctx->node_storage, /*explain=*/false); - if (verbose_site_router(ctx)) { - if (site_ok) { - log_info("Site %s is routable\n", ctx->get_site_name(tile, site)); - } else { - log_info("Site %s is not routable\n", ctx->get_site_name(tile, site)); - } - } - - if (site_ok) { - check_routing(site_arch); - } - return site_ok; -} - -void SiteRouter::bindSiteRouting(Context *ctx) -{ - NPNR_ASSERT(!dirty); - NPNR_ASSERT(site_ok); - - // Make sure all cells in this site belong! - auto iter = cells_in_site.begin(); - NPNR_ASSERT((*iter)->bel != BelId()); - - auto tile = (*iter)->bel.tile; - auto &tile_type = loc_info(ctx->chip_info, (*iter)->bel); - - // Unbind all bound site wires - WireId wire; - wire.tile = tile; - for (size_t wire_index = 0; wire_index < tile_type.wire_data.size(); ++wire_index) { - const TileWireInfoPOD &wire_data = tile_type.wire_data[wire_index]; - - if (wire_data.site != this->site) { - continue; - } - - wire.index = wire_index; - - NetInfo *net = ctx->getBoundWireNet(wire); - if (net == nullptr) { - continue; - } - - auto &pip_map = net->wires.at(wire); - if (pip_map.strength <= STRENGTH_STRONG) { - ctx->unbindWire(wire); - } - } - - SiteInformation site_info(ctx, tile, site, cells_in_site); - pool> blocked_wires; - NPNR_ASSERT(map_luts_in_site(site_info, &blocked_wires)); - - SiteArch site_arch(&site_info); - block_lut_outputs(&site_arch, blocked_wires); - block_cluster_wires(&site_arch); - reserve_site_ports(&site_arch); - NPNR_ASSERT(route_site(&site_arch, &ctx->site_routing_cache, &ctx->node_storage, /*explain=*/false, - /*cache_disabled=*/true)); - - check_routing(site_arch); - apply_routing(ctx, site_arch, lut_thrus); - - check_downhill_pips(ctx, &site_arch, valid_pips); - if (verbose_site_router(ctx)) { - print_current_state(&site_arch); - } -} - -void SiteRouter::explain(const Context *ctx) const -{ - NPNR_ASSERT(!dirty); - if (site_ok) { - return; - } - - // Make sure all cells in this site belong! - auto iter = cells_in_site.begin(); - NPNR_ASSERT((*iter)->bel != BelId()); - - auto tile = (*iter)->bel.tile; - - SiteInformation site_info(ctx, tile, site, cells_in_site); - SiteArch site_arch(&site_info); - bool route_status = route_site(&site_arch, &ctx->site_routing_cache, &ctx->node_storage, /*explain=*/true); - if (!route_status) { - print_current_state(&site_arch); - } -} - -ArchNetInfo::~ArchNetInfo() { delete loop; } - -Arch::~Arch() -{ - for (auto &net_pair : nets) { - if (net_pair.second->loop) { - net_pair.second->loop->clear(); - } - } -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/site_router.h b/fpga_interchange/site_router.h deleted file mode 100644 index ad1cbf66..00000000 --- a/fpga_interchange/site_router.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef SITE_ROUTER_H -#define SITE_ROUTER_H - -#include - -#include "hashlib.h" -#include "nextpnr_namespaces.h" -#include "nextpnr_types.h" -#include "site_arch.h" -#include "site_routing_storage.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct Context; -struct TileStatus; - -struct SiteRouter -{ - SiteRouter(int16_t site) : site(site), dirty(false), site_ok(true) {} - - pool cells_in_site; - std::vector valid_pips; - pool> lut_thrus; - const int16_t site; - - mutable bool dirty; - mutable bool site_ok; - - void bindBel(CellInfo *cell); - void unbindBel(CellInfo *cell); - bool checkSiteRouting(const Context *ctx, const TileStatus &tile_status) const; - void bindSiteRouting(Context *ctx); - void explain(const Context *ctx) const; -}; - -NEXTPNR_NAMESPACE_END - -#endif /* SITE_ROUTER_H */ diff --git a/fpga_interchange/site_routing_cache.cc b/fpga_interchange/site_routing_cache.cc deleted file mode 100644 index 82bbbf1b..00000000 --- a/fpga_interchange/site_routing_cache.cc +++ /dev/null @@ -1,209 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "site_routing_cache.h" - -#include "context.h" -#include "site_arch.impl.h" - -NEXTPNR_NAMESPACE_BEGIN - -void SiteRoutingSolution::store_solution(const SiteArch *ctx, const RouteNodeStorage *node_storage, - const SiteWire &driver, std::vector solutions) -{ - clear(); - - solution_sinks.reserve(solutions.size()); - inverted.reserve(solutions.size()); - can_invert.reserve(solutions.size()); - - for (size_t route : solutions) { - bool sol_inverted = false; - bool sol_can_invert = false; - - SiteWire wire = node_storage->get_node(route)->wire; - solution_sinks.push_back(wire); - - solution_offsets.push_back(solution_storage.size()); - Node cursor = node_storage->get_node(route); - while (cursor.has_parent()) { - if (ctx->isInverting(cursor->pip) && !sol_can_invert) { - sol_inverted = !sol_inverted; - } - if (ctx->canInvert(cursor->pip)) { - sol_inverted = false; - sol_can_invert = true; - } - - solution_storage.push_back(cursor->pip); - Node parent = cursor.parent(); - NPNR_ASSERT(ctx->getPipDstWire(cursor->pip) == cursor->wire); - NPNR_ASSERT(ctx->getPipSrcWire(cursor->pip) == parent->wire); - cursor = parent; - } - - inverted.push_back(sol_inverted); - can_invert.push_back(sol_can_invert); - - NPNR_ASSERT(cursor->wire == driver); - } - - solution_offsets.push_back(solution_storage.size()); -} - -bool SiteRoutingSolution::verify(const SiteArch *ctx, const SiteNetInfo &net) -{ - pool seen_users; - for (size_t i = 0; i < num_solutions(); ++i) { - SiteWire cursor = solution_sink(i); - NPNR_ASSERT(net.users.count(cursor) == 1); - seen_users.emplace(cursor); - - auto begin = solution_begin(i); - auto end = solution_end(i); - - for (auto iter = begin; iter != end; ++iter) { - SitePip pip = *iter; - NPNR_ASSERT(ctx->getPipDstWire(pip) == cursor); - cursor = ctx->getPipSrcWire(pip); - } - - NPNR_ASSERT(net.driver == cursor); - } - - return seen_users.size() == net.users.size(); -} - -SiteRoutingKey SiteRoutingKey::make(const SiteArch *ctx, const SiteNetInfo &site_net) -{ - SiteRoutingKey out; - - out.tile_type = ctx->site_info->tile_type; - out.site = ctx->site_info->site; - - out.net_type = ctx->ctx->get_net_type(site_net.net); - out.driver_type = site_net.driver.type; - if (site_net.driver.type == SiteWire::SITE_WIRE) { - out.driver_index = site_net.driver.wire.index; - } else { - NPNR_ASSERT(site_net.driver.type == SiteWire::OUT_OF_SITE_SOURCE); - out.driver_index = -1; - } - - out.user_types.reserve(site_net.users.size()); - out.user_indicies.reserve(site_net.users.size()); - - std::vector users; - users.reserve(site_net.users.size()); - users.insert(users.begin(), site_net.users.begin(), site_net.users.end()); - - std::sort(users.begin(), users.end()); - - for (const SiteWire &user : users) { - out.user_types.push_back(user.type); - - if (user.type == SiteWire::SITE_WIRE) { - out.user_indicies.push_back(user.wire.index); - } else { - NPNR_ASSERT(user.type == SiteWire::OUT_OF_SITE_SINK); - out.user_indicies.push_back(-1); - } - } - - return out; -} - -bool SiteRoutingCache::get_solution(const SiteArch *ctx, const SiteNetInfo &net, SiteRoutingSolution *solution) const -{ - SiteRoutingKey key = SiteRoutingKey::make(ctx, net); - auto iter = cache_.find(key); - if (iter == cache_.end()) { - return false; - } - - *solution = iter->second; - const auto &tile_type_data = ctx->site_info->chip_info().tile_types[ctx->site_info->tile_type]; - - for (SiteWire &wire : solution->solution_sinks) { - switch (wire.type) { - case SiteWire::SITE_WIRE: - wire.wire.tile = ctx->site_info->tile; - break; - case SiteWire::OUT_OF_SITE_SOURCE: - wire.net = net.net; - break; - case SiteWire::OUT_OF_SITE_SINK: - wire.net = net.net; - break; - case SiteWire::SITE_PORT_SINK: { - const auto &pip_data = tile_type_data.pip_data[wire.pip.index]; - wire.pip.tile = ctx->site_info->tile; - wire.wire = canonical_wire(&ctx->site_info->chip_info(), ctx->site_info->tile, pip_data.dst_index); - break; - } - case SiteWire::SITE_PORT_SOURCE: { - const auto &pip_data = tile_type_data.pip_data[wire.pip.index]; - wire.pip.tile = ctx->site_info->tile; - wire.wire = canonical_wire(&ctx->site_info->chip_info(), ctx->site_info->tile, pip_data.src_index); - break; - } - default: - NPNR_ASSERT(false); - } - } - - for (SitePip &pip : solution->solution_storage) { - pip.pip.tile = ctx->site_info->tile; - switch (pip.type) { - case SitePip::SITE_PIP: - // Done! - break; - case SitePip::SITE_PORT: - // Done! - break; - case SitePip::SOURCE_TO_SITE_PORT: - NPNR_ASSERT(pip.wire.type == SiteWire::OUT_OF_SITE_SOURCE); - pip.wire.net = net.net; - break; - case SitePip::SITE_PORT_TO_SINK: - NPNR_ASSERT(pip.wire.type == SiteWire::OUT_OF_SITE_SINK); - pip.wire.net = net.net; - break; - case SitePip::SITE_PORT_TO_SITE_PORT: - pip.other_pip.tile = ctx->site_info->tile; - break; - default: - NPNR_ASSERT(false); - } - } - - return solution->verify(ctx, net); -} - -void SiteRoutingCache::add_solutions(const SiteArch *ctx, const SiteNetInfo &net, const SiteRoutingSolution &solution) -{ - SiteRoutingKey key = SiteRoutingKey::make(ctx, net); - - cache_[key] = solution; -} - -void SiteRoutingCache::clear() { cache_.clear(); } - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/site_routing_cache.h b/fpga_interchange/site_routing_cache.h deleted file mode 100644 index 5f5e7b75..00000000 --- a/fpga_interchange/site_routing_cache.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef SITE_ROUTING_CACHE_H -#define SITE_ROUTING_CACHE_H - -#include "PhysicalNetlist.capnp.h" -#include "nextpnr_namespaces.h" -#include "site_arch.h" -#include "site_routing_storage.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct SiteRoutingSolution -{ - void store_solution(const SiteArch *ctx, const RouteNodeStorage *node_storage, const SiteWire &driver, - std::vector solutions); - bool verify(const SiteArch *ctx, const SiteNetInfo &net); - - void clear() - { - solution_offsets.clear(); - solution_storage.clear(); - solution_sinks.clear(); - inverted.clear(); - can_invert.clear(); - } - - size_t num_solutions() const { return solution_sinks.size(); } - - const SiteWire &solution_sink(size_t solution) const { return solution_sinks.at(solution); } - - std::vector::const_iterator solution_begin(size_t solution) const - { - NPNR_ASSERT(solution + 1 < solution_offsets.size()); - return solution_storage.begin() + solution_offsets.at(solution); - } - - std::vector::const_iterator solution_end(size_t solution) const - { - NPNR_ASSERT(solution + 1 < solution_offsets.size()); - return solution_storage.begin() + solution_offsets.at(solution + 1); - } - - bool solution_inverted(size_t solution) const { return inverted.at(solution) != 0; } - - bool solution_can_invert(size_t solution) const { return can_invert.at(solution) != 0; } - - std::vector solution_offsets; - std::vector solution_storage; - std::vector solution_sinks; - std::vector inverted; - std::vector can_invert; -}; - -struct SiteRoutingKey -{ - int32_t tile_type; - int32_t site; - // The net type matters for site routing. Legal routes for VCC/GND/SIGNAL - // nets are different. - PhysicalNetlist::PhysNetlist::NetType net_type; - SiteWire::Type driver_type; - int32_t driver_index; - std::vector user_types; - std::vector user_indicies; - - bool operator==(const SiteRoutingKey &other) const - { - return tile_type == other.tile_type && site == other.site && net_type == other.net_type && - driver_type == other.driver_type && driver_index == other.driver_index && - user_types == other.user_types && user_indicies == other.user_indicies; - } - bool operator!=(const SiteRoutingKey &other) const - { - return tile_type != other.tile_type || site != other.site || net_type != other.net_type || - driver_type != other.driver_type || driver_index != other.driver_index || - user_types != other.user_types || user_indicies != other.user_indicies; - } - - static SiteRoutingKey make(const SiteArch *ctx, const SiteNetInfo &site_net); - - unsigned int hash() const - { - unsigned int seed = 0; - seed = mkhash(seed, tile_type); - seed = mkhash(seed, site); - seed = mkhash(seed, int(net_type)); - seed = mkhash(seed, int(driver_type)); - seed = mkhash(seed, driver_index); - seed = mkhash(seed, user_types.size()); - for (auto t : user_types) - seed = mkhash(seed, int(t)); - seed = mkhash(seed, user_indicies.size()); - for (auto i : user_indicies) - seed = mkhash(seed, i); - return seed; - } -}; - -// Provides an LRU cache for site routing solutions. -class SiteRoutingCache -{ - public: - bool get_solution(const SiteArch *ctx, const SiteNetInfo &net, SiteRoutingSolution *solution) const; - void add_solutions(const SiteArch *ctx, const SiteNetInfo &net, const SiteRoutingSolution &solution); - void clear(); - - private: - dict cache_; -}; - -NEXTPNR_NAMESPACE_END - -#endif /* SITE_ROUTING_CACHE_H */ diff --git a/fpga_interchange/site_routing_storage.h b/fpga_interchange/site_routing_storage.h deleted file mode 100644 index 2fbc246e..00000000 --- a/fpga_interchange/site_routing_storage.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef SITE_ROUTING_STORAGE -#define SITE_ROUTING_STORAGE - -#include - -#include "nextpnr_namespaces.h" -#include "site_arch.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct RouteNode -{ - void clear() - { - parent = std::numeric_limits::max(); - pip = SitePip(); - wire = SiteWire(); - flags = 0; - depth = 0; - } - - enum Flags - { - // Has this path left the site? - LEFT_SITE = 0, - // Has this path entered the site? - ENTERED_SITE = 1, - // Has this path left the site after entering it? - // This node should be discarded as being part of an illegal path - // which allows entering and exiting a site, situation that needs - // to be handled with a tile PIP. - LEFT_SITE_AFTER_ENTERING = 2, - }; - - bool has_left_site() const { return (flags & (1 << LEFT_SITE)) != 0; } - - bool has_left_site_after_entering() const { return (flags & (1 << LEFT_SITE_AFTER_ENTERING)) != 0; } - - bool can_leave_site() const { return !has_left_site(); } - - bool is_valid_node() const { return !has_left_site_after_entering(); } - - void mark_left_site() { flags |= (1 << LEFT_SITE); } - - void mark_left_site_after_entering() { flags |= (has_entered_site() << LEFT_SITE_AFTER_ENTERING); } - - bool has_entered_site() const { return (flags & (1 << ENTERED_SITE)) != 0; } - - bool can_enter_site() const { return !has_entered_site(); } - - void mark_entered_site() { flags |= (1 << ENTERED_SITE); } - - size_t parent; - - SitePip pip; // What pip was taken to reach this node. - SiteWire wire; // What wire is this routing node located at? - int32_t flags; - int32_t depth; -}; - -struct RouteNodeStorage; - -class Node -{ - public: - Node(RouteNodeStorage *storage, size_t idx) : storage_(storage), idx_(idx) {} - - size_t get_index() const { return idx_; } - - RouteNode &operator*(); - const RouteNode &operator*() const; - RouteNode *operator->(); - const RouteNode *operator->() const; - bool has_parent() const; - Node parent(); - - private: - RouteNodeStorage *storage_; - size_t idx_; -}; - -struct RouteNodeStorage -{ - // Free list of nodes. - std::vector nodes; - std::vector free_list; - - // Either allocate a new node if no nodes are on the free list, or return - // an element from the free list. - Node alloc_node() - { - if (free_list.empty()) { - nodes.emplace_back(); - nodes.back().clear(); - return Node(this, nodes.size() - 1); - } - - size_t idx = free_list.back(); - free_list.pop_back(); - nodes[idx].clear(); - - return Node(this, idx); - } - - Node get_node(size_t idx) - { - NPNR_ASSERT(idx < nodes.size()); - return Node(this, idx); - } - - const Node get_node(size_t idx) const - { - NPNR_ASSERT(idx < nodes.size()); - return Node(const_cast(this), idx); - } - - // Return all node from the current owner to the free list. - void free_nodes(std::vector &other_free_list) - { - free_list.insert(free_list.end(), other_free_list.begin(), other_free_list.end()); - } -}; - -inline RouteNode &Node::operator*() { return storage_->nodes[idx_]; } -inline const RouteNode &Node::operator*() const { return storage_->nodes[idx_]; } - -inline RouteNode *Node::operator->() { return &storage_->nodes[idx_]; } -inline const RouteNode *Node::operator->() const { return &storage_->nodes[idx_]; } - -inline bool Node::has_parent() const { return storage_->nodes[idx_].parent < storage_->nodes.size(); } - -inline Node Node::parent() -{ - size_t parent_idx = storage_->nodes[idx_].parent; - NPNR_ASSERT(parent_idx < storage_->nodes.size()); - return Node(storage_, parent_idx); -} - -NEXTPNR_NAMESPACE_END - -#endif /* SITE_ROUTING_STORAGE */ diff --git a/fpga_interchange/type_wire.cc b/fpga_interchange/type_wire.cc deleted file mode 100644 index d1bdaed0..00000000 --- a/fpga_interchange/type_wire.cc +++ /dev/null @@ -1,82 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "type_wire.h" -#include "nextpnr.h" - -NEXTPNR_NAMESPACE_BEGIN - -TypeWireId::TypeWireId(const Context *ctx, WireId wire_inst) -{ - NPNR_ASSERT(wire_inst != WireId()); - - if (wire_inst.tile == -1) { - auto &tile_wire = ctx->chip_info->nodes[wire_inst.index].tile_wires[0]; - type = ctx->chip_info->tiles[tile_wire.tile].type; - index = tile_wire.index; - } else { - type = ctx->chip_info->tiles[wire_inst.tile].type; - index = wire_inst.index; - } -} - -TypeWireSet::TypeWireSet(const Context *ctx, WireId wire) -{ - if (wire.tile == -1) { - const auto &node_data = ctx->chip_info->nodes[wire.index]; - wire_types_.reserve(node_data.tile_wires.size()); - for (const auto &tile_wire : node_data.tile_wires) { - wire_types_.emplace_back(); - wire_types_.back().type = ctx->chip_info->tiles[tile_wire.tile].type; - wire_types_.back().index = tile_wire.index; - } - } else { - TypeWireId wire_type(ctx, wire); - wire_types_.push_back(wire_type); - } - - std::sort(wire_types_.begin(), wire_types_.end()); - - hash_ = wire_types_.size(); - for (const auto &wire : wire_types_) { - hash_ = mkhash(hash_, wire.hash()); - } -} - -TypeWireId::TypeWireId(lookahead_storage::TypeWireId::Reader reader) : type(reader.getType()), index(reader.getIndex()) -{ -} -void TypeWireId::to_builder(lookahead_storage::TypeWireId::Builder builder) const -{ - builder.setType(type); - builder.setIndex(index); -} - -TypeWirePair::TypeWirePair(lookahead_storage::TypeWirePair::Reader reader) : src(reader.getSrc()), dst(reader.getDst()) -{ -} - -void TypeWirePair::to_builder(lookahead_storage::TypeWirePair::Builder builder) const -{ - src.to_builder(builder.getSrc()); - dst.to_builder(builder.getDst()); -} - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/type_wire.h b/fpga_interchange/type_wire.h deleted file mode 100644 index a472bccc..00000000 --- a/fpga_interchange/type_wire.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef TYPE_WIRE_H -#define TYPE_WIRE_H - -#include -#include - -#include "hashlib.h" -#include "nextpnr_namespaces.h" -#include "nextpnr_types.h" - -#include "lookahead.capnp.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct Context; - -struct TypeWireId -{ - TypeWireId() : type(-1), index(-1) {} - TypeWireId(const Context *ctx, WireId wire_inst); - - explicit TypeWireId(lookahead_storage::TypeWireId::Reader reader); - void to_builder(lookahead_storage::TypeWireId::Builder builder) const; - - bool operator==(const TypeWireId &other) const { return type == other.type && index == other.index; } - bool operator!=(const TypeWireId &other) const { return type != other.type || index != other.index; } - bool operator<(const TypeWireId &other) const - { - return type < other.type || (type == other.type && index < other.index); - } - - unsigned int hash() const { return mkhash(type, index); } - - int32_t type; - int32_t index; -}; - -struct TypeWirePair -{ - TypeWireId src; - TypeWireId dst; - - TypeWirePair() = default; - explicit TypeWirePair(lookahead_storage::TypeWirePair::Reader reader); - void to_builder(lookahead_storage::TypeWirePair::Builder builder) const; - - bool operator==(const TypeWirePair &other) const { return src == other.src && dst == other.dst; } - bool operator!=(const TypeWirePair &other) const { return src != other.src || dst != other.dst; } - - unsigned int hash() const { return mkhash(src.hash(), dst.hash()); } -}; - -struct TypeWireSet -{ - public: - TypeWireSet(const Context *ctx, WireId wire); - unsigned int hash() const { return hash_; } - - bool operator==(const TypeWireSet &other) const { return wire_types_ == other.wire_types_; } - bool operator!=(const TypeWireSet &other) const { return wire_types_ != other.wire_types_; } - - private: - unsigned int hash_; - std::vector wire_types_; -}; - -NEXTPNR_NAMESPACE_END - -#endif /* TYPE_WIRE_H */ diff --git a/fpga_interchange/xdc.cc b/fpga_interchange/xdc.cc deleted file mode 100644 index 53a80b7d..00000000 --- a/fpga_interchange/xdc.cc +++ /dev/null @@ -1,230 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2019 gatecat - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include "xdc.h" - -#include - -#include "context.h" -#include "log.h" - -// Include tcl.h late because it messed with #define's and lets them leave the -// scope of the header. -#include - -NEXTPNR_NAMESPACE_BEGIN - -static int obj_set_from_any(Tcl_Interp *interp, Tcl_Obj *objPtr) { return TCL_ERROR; } - -static void set_tcl_obj_string(Tcl_Obj *objPtr, const std::string &s) -{ - NPNR_ASSERT(objPtr->bytes == nullptr); - // Need to have space for the end null byte. - objPtr->bytes = Tcl_Alloc(s.size() + 1); - - // Length is length of string, not including the end null byte. - objPtr->length = s.size(); - - std::copy(s.begin(), s.end(), objPtr->bytes); - objPtr->bytes[objPtr->length] = '\0'; -} - -static void port_update_string(Tcl_Obj *objPtr) -{ - const Context *ctx = static_cast(objPtr->internalRep.twoPtrValue.ptr1); - PortInfo *port_info = static_cast(objPtr->internalRep.twoPtrValue.ptr2); - - std::string port_name = port_info->name.str(ctx); - set_tcl_obj_string(objPtr, port_name); -} - -static void cell_update_string(Tcl_Obj *objPtr) -{ - const Context *ctx = static_cast(objPtr->internalRep.twoPtrValue.ptr1); - CellInfo *cell_info = static_cast(objPtr->internalRep.twoPtrValue.ptr2); - - std::string cell_name = cell_info->name.str(ctx); - set_tcl_obj_string(objPtr, cell_name); -} - -static void obj_dup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr) -{ - dupPtr->internalRep.twoPtrValue = srcPtr->internalRep.twoPtrValue; -} - -static void obj_free(Tcl_Obj *objPtr) {} - -static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) -{ - char *copy = Tcl_Alloc(s.size() + 1); - std::copy(s.begin(), s.end(), copy); - copy[s.size()] = '\0'; - Tcl_SetResult(interp, copy, TCL_DYNAMIC); -} - -static Tcl_ObjType port_object = { - "port", obj_free, obj_dup, port_update_string, obj_set_from_any, -}; - -static Tcl_ObjType cell_object = { - "cell", obj_free, obj_dup, cell_update_string, obj_set_from_any, -}; - -static int get_ports(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) -{ - const Context *ctx = static_cast(data); - if (objc == 1) { - // Return list of all ports. - Tcl_SetStringResult(interp, "Unimplemented"); - return TCL_ERROR; - } else if (objc == 2) { - const char *arg0 = Tcl_GetString(objv[1]); - IdString port_name = ctx->id(arg0); - - auto iter = ctx->ports.find(port_name); - if (iter == ctx->ports.end()) { - Tcl_SetStringResult(interp, "Could not find port " + port_name.str(ctx)); - return TCL_ERROR; - } - - Tcl_Obj *result = Tcl_NewObj(); - result->typePtr = &port_object; - result->internalRep.twoPtrValue.ptr1 = (void *)(ctx); - result->internalRep.twoPtrValue.ptr2 = (void *)(&iter->second); - - result->bytes = nullptr; - port_update_string(result); - - Tcl_SetObjResult(interp, result); - return TCL_OK; - } else if (objc > 2) { - log_warning("get_ports options not implemented!\n"); - return TCL_OK; - } else { - return TCL_ERROR; - } -} - -static int get_cells(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) -{ - const Context *ctx = static_cast(data); - if (objc == 1) { - // Return list of all ports. - Tcl_SetStringResult(interp, "Unimplemented"); - return TCL_ERROR; - } else if (objc == 2) { - const char *arg0 = Tcl_GetString(objv[1]); - IdString cell_name = ctx->id(arg0); - - auto iter = ctx->cells.find(cell_name); - if (iter == ctx->cells.end()) { - Tcl_SetStringResult(interp, "Could not find cell " + cell_name.str(ctx)); - return TCL_ERROR; - } - - Tcl_Obj *result = Tcl_NewObj(); - result->typePtr = &cell_object; - result->internalRep.twoPtrValue.ptr1 = (void *)(ctx); - result->internalRep.twoPtrValue.ptr2 = (void *)(iter->second.get()); - - result->bytes = nullptr; - cell_update_string(result); - - Tcl_SetObjResult(interp, result); - return TCL_OK; - } else if (objc > 2) { - log_warning("get_cells options not implemented!\n"); - return TCL_OK; - } else { - return TCL_ERROR; - } -} - -static int set_property(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) -{ - // set_property - if (objc != 4) { - Tcl_SetStringResult(interp, "Only simple 'set_property ' is supported"); - return TCL_ERROR; - } - - const char *property = Tcl_GetString(objv[1]); - const char *value = Tcl_GetString(objv[2]); - const Tcl_Obj *object = objv[3]; - - if (object->typePtr == &port_object) { - const Context *ctx = static_cast(object->internalRep.twoPtrValue.ptr1); - PortInfo *port_info = static_cast(object->internalRep.twoPtrValue.ptr2); - NPNR_ASSERT(port_info->net != nullptr); - CellInfo *cell = ctx->port_cells.at(port_info->name); - - cell->attrs[ctx->id(property)] = Property(value); - } else if (object->typePtr == &cell_object) { - const Context *ctx = static_cast(object->internalRep.twoPtrValue.ptr1); - CellInfo *cell = static_cast(object->internalRep.twoPtrValue.ptr2); - - cell->attrs[ctx->id(property)] = Property(value); - } - - return TCL_OK; -} - -static int not_implemented(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) -{ - // TCL command that is not yet implemented - log_warning("%s command is not implemented!\n", Tcl_GetString(objv[0])); - return TCL_OK; -} - -TclInterp::TclInterp(Context *ctx) -{ - interp = Tcl_CreateInterp(); - NPNR_ASSERT(Tcl_Init(interp) == TCL_OK); - - Tcl_RegisterObjType(&port_object); - Tcl_RegisterObjType(&cell_object); - - NPNR_ASSERT(Tcl_Eval(interp, "rename unknown _original_unknown") == TCL_OK); - NPNR_ASSERT(Tcl_Eval(interp, "proc unknown args {\n" - " set result [scan [lindex $args 0] \"%d\" value]\n" - " if { $result == 1 && [llength $args] == 1 } {\n" - " return \\[$value\\]\n" - " } else {\n" - " uplevel 1 [list _original_unknown {*}$args]\n" - " }\n" - "}") == TCL_OK); - Tcl_CreateObjCommand(interp, "get_ports", get_ports, ctx, nullptr); - Tcl_CreateObjCommand(interp, "get_cells", get_cells, ctx, nullptr); - Tcl_CreateObjCommand(interp, "set_property", set_property, ctx, nullptr); - - // Not implemented TCL commands - Tcl_CreateObjCommand(interp, "create_clock", not_implemented, ctx, nullptr); - Tcl_CreateObjCommand(interp, "get_clocks", not_implemented, ctx, nullptr); - Tcl_CreateObjCommand(interp, "get_iobanks", not_implemented, ctx, nullptr); - Tcl_CreateObjCommand(interp, "get_nets", not_implemented, ctx, nullptr); - Tcl_CreateObjCommand(interp, "get_pins", not_implemented, ctx, nullptr); - Tcl_CreateObjCommand(interp, "set_clock_groups", not_implemented, ctx, nullptr); - Tcl_CreateObjCommand(interp, "set_false_path", not_implemented, ctx, nullptr); - Tcl_CreateObjCommand(interp, "set_max_delay", not_implemented, ctx, nullptr); -} - -TclInterp::~TclInterp() { Tcl_DeleteInterp(interp); } - -NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/xdc.h b/fpga_interchange/xdc.h deleted file mode 100644 index bafd1235..00000000 --- a/fpga_interchange/xdc.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2021 Symbiflow Authors - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#ifndef XDC_H -#define XDC_H - -#include "nextpnr_namespaces.h" - -struct Tcl_Interp; - -NEXTPNR_NAMESPACE_BEGIN - -struct Context; - -struct TclInterp -{ - TclInterp(Context *ctx); - ~TclInterp(); - - Tcl_Interp *interp; -}; - -NEXTPNR_NAMESPACE_END - -#endif