Merge pull request #567 from litghost/initial_fpga_interchange
Initial FPGA interchange arch
This commit is contained in:
commit
8b4163b77c
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@
|
||||
/nextpnr-ice40*
|
||||
/nextpnr-ecp5*
|
||||
/nextpnr-nexus*
|
||||
/nextpnr-fpga_interchange*
|
||||
cmake-build-*/
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
|
@ -66,9 +66,9 @@ endif()
|
||||
set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables")
|
||||
|
||||
# List of families to build
|
||||
set(FAMILIES generic ice40 ecp5 nexus gowin)
|
||||
set(FAMILIES generic ice40 ecp5 nexus gowin fpga_interchange)
|
||||
set(STABLE_FAMILIES generic ice40 ecp5)
|
||||
set(EXPERIMENTAL_FAMILIES nexus gowin)
|
||||
set(EXPERIMENTAL_FAMILIES nexus gowin fpga_interchange)
|
||||
|
||||
set(ARCH "" CACHE STRING "Architecture family for nextpnr build")
|
||||
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
|
||||
|
@ -32,6 +32,7 @@ NPNR_PACKED_STRUCT(template <typename T> struct RelSlice {
|
||||
const T *end() const { return get() + length; }
|
||||
|
||||
const size_t size() const { return length; }
|
||||
const ptrdiff_t ssize() const { return length; }
|
||||
|
||||
const T &operator*() const { return *(get()); }
|
||||
|
||||
|
162
fpga_interchange/README.md
Normal file
162
fpga_interchange/README.md
Normal file
@ -0,0 +1,162 @@
|
||||
## 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 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.
|
||||
|
||||
- [ ] Placement constraints are unimplemented, meaning invalid or unroutable
|
||||
designs can be generated from the placer.
|
||||
- [ ] 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).
|
||||
- [ ] Cell -> BEL pin mapping is not in place, meaning any primitives that
|
||||
have different BEL pins with respect to their cell pins will not be
|
||||
routable.
|
||||
- [ ] Nextpnr only allows for cell -> BEL pin maps that are 1 to 1. The
|
||||
FPGA interchange accommodates cell -> BEL pin maps that include 1 to
|
||||
many relationships for sinks. A common primitives that uses 1 to many
|
||||
maps are the RAMB18E1.
|
||||
- [ ] The router lookahead is missing, meaning that router runtime
|
||||
performance will be terrible.
|
||||
- [ ] Physical netlist backend is missing, so even if
|
||||
`nextpnr-fpga_interchange` completes successfully, there is no way to
|
||||
generate output that can be consumed by downstream tools.
|
||||
- [ ] XDC parsing and port constraints are unimplemented, so IO pins cannot
|
||||
be fixed. The chipdb BBA output is also missing package pin data, so
|
||||
only site constraints are currently possible. Eventually the chipdb BBA
|
||||
should also include package pin data to allow for ports to be bound to
|
||||
package pins.
|
||||
- [ ] The routing graph that is currently emitted does not have ground and
|
||||
VCC networks, so all signals must currently be tied to an IO signal.
|
||||
Site pins being tied to constants also needs handling so that site
|
||||
local inverters are used rather than routing signals suboptimally.
|
||||
- [ ] Pseudo pips (e.g. pips that consume BELs and or site resources) should
|
||||
block their respective resources. This effects designs that have some
|
||||
routing in place before placement.
|
||||
- [ ] Pseudo site pips (e.g. site pips that route through BELs) should block
|
||||
their respective resources. Without this, using some pseudo site pips
|
||||
could result in invalid placements.
|
||||
- [ ] 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
|
||||
schema, it needs to be added to the architecture.
|
||||
|
||||
#### FPGA interchange fabrics
|
||||
|
||||
Currently only Xilinx 7-series, UltraScale and UltraScale+ fabrics have a
|
||||
device database generator, via [RapidWright](https://github.com/Xilinx/RapidWright).
|
||||
|
||||
##### Artix 35T example
|
||||
|
||||
Download RapidWright and generate the device database.
|
||||
```
|
||||
# FIXME: Use main branch once interchange branch is merged.
|
||||
git clone -b interchange https://github.com/Xilinx/RapidWright.git
|
||||
cd RapidWright
|
||||
make update_jars
|
||||
|
||||
# FIXME: Current RapidWright jars generate database with duplicate PIPs
|
||||
# https://github.com/Xilinx/RapidWright/issues/127
|
||||
# Remove this wget once the latest RapidWright JAR is published.
|
||||
wget https://github.com/Xilinx/RapidWright/releases/download/v2020.2.1-beta/rapidwright-api-lib-2020.2.1_update1.jar
|
||||
mv rapidwright-api-lib-2020.2.1_update1.jar jars/rapidwright-api-lib-2020.2.0.jar
|
||||
|
||||
./scripts/invoke_rapidwright.sh com.xilinx.rapidwright.interchange.DeviceResourcesExample xc7a35tcpg236-1
|
||||
export RAPIDWRIGHT_PATH=$(pwd)
|
||||
export INTERCHANGE_DIR=$(pwd)/interchange
|
||||
```
|
||||
|
||||
Install python FPGA interchange library.
|
||||
```
|
||||
git clone https://github.com/SymbiFlow/python-fpga-interchange.git
|
||||
cd python-fpga-interchange
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Patch device database with cell constraints and LUT annotations:
|
||||
```
|
||||
python3 -mfpga_interchange.patch \
|
||||
--schema_dir ${INTERCHANGE_DIR} \
|
||||
--schema device \
|
||||
--patch_path constraints \
|
||||
--patch_format yaml \
|
||||
${RAPIDWRIGHT_PATH}/xc7a35tcpg236-1.device \
|
||||
test_data/series7_constraints.yaml \
|
||||
xc7a35tcpg236-1_constraints.device
|
||||
python3 -mfpga_interchange.patch \
|
||||
--schema_dir ${INTERCHANGE_DIR} \
|
||||
--schema device \
|
||||
--patch_path lutDefinitions \
|
||||
--patch_format yaml \
|
||||
xc7a35tcpg236-1_constraints.device \
|
||||
test_data/series7_luts.yaml \
|
||||
xc7a35tcpg236-1_constraints_luts.device
|
||||
```
|
||||
|
||||
Generate nextpnr BBA and constids.inc from device database:
|
||||
```
|
||||
python3 -mfpga_interchange.nextpnr_emit \
|
||||
--schema_dir ${INTERCHANGE_DIR} \
|
||||
--output_dir ${NEXTPNR_DIR}/fpga_interchange/ \
|
||||
--device xc7a35tcpg236-1_constraints_luts.device
|
||||
```
|
||||
|
||||
Build nextpnr:
|
||||
|
||||
```
|
||||
cd ${NEXTPNR_DIR}
|
||||
cmake -DARCH=fpga_interchange .
|
||||
make -j
|
||||
```
|
||||
|
||||
Compile generated BBA:
|
||||
```
|
||||
bba/bbasm -l fpga_interchange/chipdb.bba fpga_interchange/chipdb.bin
|
||||
```
|
||||
|
||||
Run nextpnr archcheck:
|
||||
```
|
||||
./nextpnr-fpga_interchange --chipdb fpga_interchange/chipdb.bin --test
|
||||
```
|
||||
|
||||
Once nextpnr can complete the place and route task and output the physical
|
||||
netlist, RapidWright can be used to generate a DCP suitable for bitstream
|
||||
output and DRC checks.
|
||||
|
||||
```
|
||||
${RAPIDWRIGHT_PATH}/scripts/invoke_rapidwright.sh \
|
||||
com.xilinx.rapidwright.interchange.PhysicalNetlistToDcp \
|
||||
<logical netlist file> <physical netlist file> <XDC file> <output DCP>
|
||||
```
|
578
fpga_interchange/arch.cc
Normal file
578
fpga_interchange/arch.cc
Normal file
@ -0,0 +1,578 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Wolf <claire@symbioticeda.com>
|
||||
* Copyright (C) 2018-19 David Shah <david@symbioticeda.com>
|
||||
* 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 <algorithm>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <queue>
|
||||
#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"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
static std::pair<std::string, std::string> 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<ChipInfoPOD> *ptr) { return ptr->get(); }
|
||||
|
||||
Arch::Arch(ArchArgs args) : args(args)
|
||||
{
|
||||
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<const char *>(blob_file.data());
|
||||
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(blob));
|
||||
} catch (...) {
|
||||
log_error("Unable to read chipdb %s\n", args.chipdb.c_str());
|
||||
}
|
||||
|
||||
// Read strings from constids into IdString database, checking that list
|
||||
// is unique and matches expected constid value.
|
||||
int id = 1;
|
||||
for (const auto &constid : *chip_info->constids) {
|
||||
IdString::initialize_add(this, constid.get(), id++);
|
||||
}
|
||||
|
||||
tileStatus.resize(chip_info->tiles.size());
|
||||
for (int i = 0; i < chip_info->tiles.ssize(); i++) {
|
||||
tileStatus[i].boundcells.resize(chip_info->tile_types[chip_info->tiles[i].type].bel_data.size());
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
std::string Arch::getChipName() const { return chip_info->name.get(); }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
IdString Arch::archArgsToId(ArchArgs args) const { return IdString(); }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void Arch::setup_byname() const
|
||||
{
|
||||
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 (int j = 0; j < tile_type.number_sites; j++) {
|
||||
auto &site = chip_info->sites[tile.sites[j]];
|
||||
site_by_name[id(site.name.get())] = std::make_pair(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 { return id(""); }
|
||||
std::vector<std::pair<IdString, std::string>> Arch::getWireAttrs(WireId wire) const { return {}; }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
PipId Arch::getPipByName(IdStringList name) const
|
||||
{
|
||||
// PIP name structure:
|
||||
// Tile PIP: <tile name>/<source wire name>.<destination wire name>
|
||||
// Site PIP: <site name>/<bel name>/<input bel pin name>
|
||||
// Site pin: <site name>/<bel name>
|
||||
// Psuedo site PIP: <site name>/<source wire name>.<destination wire name>
|
||||
|
||||
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_info = chip_info->tile_types[chip_info->tiles[tile].type];
|
||||
|
||||
std::array<IdString, 2> 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>/<src site wire>.<dst site wire>
|
||||
// site pins are <site>/<bel>
|
||||
int tile;
|
||||
int site;
|
||||
std::tie(tile, site) = iter->second;
|
||||
auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type];
|
||||
|
||||
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]);
|
||||
auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type];
|
||||
|
||||
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: <tile name>/<source wire name>.<destination wire name>
|
||||
// Psuedo site PIP: <site name>/<input site wire>.<output site wire>
|
||||
// Site PIP: <site name>/<bel name>/<input bel pin name>
|
||||
// Site pin: <site name>/<bel name>
|
||||
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 = chip_info->sites[tile.sites[pip_info.site]];
|
||||
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<IdString, 2> 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<IdString, 3> 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<IdString, 2> 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<IdString, 2> ids{id(std::string(tile.name.get())), pip};
|
||||
return IdStringList(ids);
|
||||
}
|
||||
}
|
||||
|
||||
IdString Arch::getPipType(PipId pip) const { return id("PIP"); }
|
||||
|
||||
std::vector<std::pair<IdString, std::string>> 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<std::pair<IdString, std::string>> Arch::getBelAttrs(BelId bel) const { return {}; }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
ArcBounds 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 x0, x1, y0, y1;
|
||||
x0 = src_tile % chip_info->width;
|
||||
x1 = x0;
|
||||
y0 = src_tile / chip_info->width;
|
||||
y1 = y0;
|
||||
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_tile % chip_info->width, dst_tile / chip_info->width);
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
delay_t Arch::getWireRipupDelayPenalty(WireId wire) const { return getRipupDelayPenalty(); }
|
||||
|
||||
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
bool Arch::pack()
|
||||
{
|
||||
// FIXME: Implement this
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Arch::place()
|
||||
{
|
||||
// FIXME: Implement this
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Arch::route()
|
||||
{
|
||||
// FIXME: Implement this
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
std::vector<GraphicElement> 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, bool debug) const
|
||||
{
|
||||
// FIXME: Implement something to push the A* router in the right direction.
|
||||
return 0;
|
||||
}
|
||||
|
||||
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
|
||||
{
|
||||
// FIXME: Implement when adding timing-driven place and route.
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &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;
|
||||
}
|
||||
|
||||
#ifdef WITH_HEAP
|
||||
const std::string Arch::defaultPlacer = "heap";
|
||||
#else
|
||||
const std::string Arch::defaultPlacer = "sa";
|
||||
#endif
|
||||
|
||||
const std::vector<std::string> Arch::availablePlacers = {"sa",
|
||||
#ifdef WITH_HEAP
|
||||
"heap"
|
||||
#endif
|
||||
};
|
||||
|
||||
const std::string Arch::defaultRouter = "router2";
|
||||
const std::vector<std::string> Arch::availableRouters = {"router1", "router2"};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
1277
fpga_interchange/arch.h
Normal file
1277
fpga_interchange/arch.h
Normal file
File diff suppressed because it is too large
Load Diff
77
fpga_interchange/arch_pybindings.cc
Normal file
77
fpga_interchange/arch_pybindings.cc
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2020 David Shah <dave@ds0.me>
|
||||
* 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_<ArchArgs>(m, "ArchArgs").def_readwrite("chipdb", &ArchArgs::chipdb);
|
||||
|
||||
py::class_<BelId>(m, "BelId").def_readwrite("index", &BelId::index);
|
||||
|
||||
py::class_<WireId>(m, "WireId").def_readwrite("index", &WireId::index);
|
||||
|
||||
py::class_<PipId>(m, "PipId").def_readwrite("index", &PipId::index);
|
||||
|
||||
auto arch_cls = py::class_<Arch, BaseCtx>(m, "Arch").def(py::init<ArchArgs>());
|
||||
auto ctx_cls = py::class_<Context, Arch>(m, "Context")
|
||||
.def("checksum", &Context::checksum)
|
||||
.def("pack", &Context::pack)
|
||||
.def("place", &Context::place)
|
||||
.def("route", &Context::route);
|
||||
|
||||
fn_wrapper_2a<Context, decltype(&Context::isValidBelForCell), &Context::isValidBelForCell, pass_through<bool>,
|
||||
addr_and_unwrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "isValidBelForCell");
|
||||
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
|
||||
typedef std::unordered_map<IdString, IdString> AliasMap;
|
||||
typedef std::unordered_map<IdString, HierarchicalCell> HierarchyMap;
|
||||
|
||||
auto belpin_cls = py::class_<ContextualWrapper<BelPin>>(m, "BelPin");
|
||||
readonly_wrapper<BelPin, decltype(&BelPin::bel), &BelPin::bel, conv_to_str<BelId>>::def_wrap(belpin_cls, "bel");
|
||||
readonly_wrapper<BelPin, decltype(&BelPin::pin), &BelPin::pin, conv_to_str<IdString>>::def_wrap(belpin_cls, "pin");
|
||||
|
||||
typedef FilteredBelRange BelRangeForBelBucket;
|
||||
#include "arch_pybindings_shared.h"
|
||||
|
||||
WRAP_RANGE(m, BelBucket, conv_to_str<BelBucketId>);
|
||||
WRAP_RANGE(m, Bel, conv_to_str<BelId>);
|
||||
WRAP_RANGE(m, Wire, conv_to_str<WireId>);
|
||||
WRAP_RANGE(m, AllPip, conv_to_str<PipId>);
|
||||
WRAP_RANGE(m, UphillPip, conv_to_str<PipId>);
|
||||
WRAP_RANGE(m, DownhillPip, conv_to_str<PipId>);
|
||||
WRAP_RANGE(m, BelPin, wrap_context<BelPin>);
|
||||
|
||||
WRAP_MAP_UPTR(m, CellMap, "IdCellMap");
|
||||
WRAP_MAP_UPTR(m, NetMap, "IdNetMap");
|
||||
WRAP_MAP(m, HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // NO_PYTHON
|
110
fpga_interchange/arch_pybindings.h
Normal file
110
fpga_interchange/arch_pybindings.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2020 David Shah <dave@ds0.me>
|
||||
* 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>
|
||||
{
|
||||
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>
|
||||
{
|
||||
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<const WireId>
|
||||
{
|
||||
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>
|
||||
{
|
||||
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>
|
||||
{
|
||||
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>
|
||||
{
|
||||
BelPin from_str(Context *ctx, std::string name)
|
||||
{
|
||||
NPNR_ASSERT_FALSE("string_converter<BelPin>::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
|
192
fpga_interchange/archdefs.h
Normal file
192
fpga_interchange/archdefs.h
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Wolf <claire@symbioticeda.com>
|
||||
* 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 NEXTPNR_H
|
||||
#error Include "archdefs.h" via "nextpnr.h" only.
|
||||
#endif
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
typedef int delay_t;
|
||||
|
||||
struct DelayInfo
|
||||
{
|
||||
delay_t delay = 0;
|
||||
|
||||
delay_t minRaiseDelay() const { return delay; }
|
||||
delay_t maxRaiseDelay() const { return delay; }
|
||||
|
||||
delay_t minFallDelay() const { return delay; }
|
||||
delay_t maxFallDelay() const { return delay; }
|
||||
|
||||
delay_t minDelay() const { return delay; }
|
||||
delay_t maxDelay() const { return delay; }
|
||||
|
||||
DelayInfo operator+(const DelayInfo &other) const
|
||||
{
|
||||
DelayInfo ret;
|
||||
ret.delay = this->delay + other.delay;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
struct GroupId
|
||||
{
|
||||
bool operator==(const GroupId &other) const { return true; }
|
||||
bool operator!=(const GroupId &other) const { return false; }
|
||||
};
|
||||
|
||||
struct DecalId
|
||||
{
|
||||
bool operator==(const DecalId &other) const { return true; }
|
||||
bool operator!=(const DecalId &other) const { return false; }
|
||||
};
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
struct ArchNetInfo
|
||||
{
|
||||
};
|
||||
|
||||
struct NetInfo;
|
||||
|
||||
struct ArchCellInfo
|
||||
{
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelId>
|
||||
{
|
||||
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, hash<int>()(bel.tile));
|
||||
boost::hash_combine(seed, hash<int>()(bel.index));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX WireId>
|
||||
{
|
||||
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, hash<int>()(wire.tile));
|
||||
boost::hash_combine(seed, hash<int>()(wire.index));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PipId>
|
||||
{
|
||||
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, hash<int>()(pip.tile));
|
||||
boost::hash_combine(seed, hash<int>()(pip.index));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX GroupId>
|
||||
{
|
||||
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
|
||||
{
|
||||
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelBucketId>
|
||||
{
|
||||
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelBucketId &bucket) const noexcept
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(bucket.name));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
0
fpga_interchange/family.cmake
Normal file
0
fpga_interchange/family.cmake
Normal file
84
fpga_interchange/main.cc
Normal file
84
fpga_interchange/main.cc
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Claire Wolf <claire@symbioticeda.com>
|
||||
* 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 <fstream>
|
||||
#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<Context> createContext(std::unordered_map<std::string, Property> &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<std::string>(), "name of chip database binary");
|
||||
specific.add_options()("xdc", po::value<std::vector<std::string>>(), "XDC-style constraints file");
|
||||
specific.add_options()("phys", po::value<std::string>(), "FPGA interchange Physical netlist to write");
|
||||
|
||||
return specific;
|
||||
}
|
||||
|
||||
void FpgaInterchangeCommandHandler::customBitstream(Context *ctx)
|
||||
{
|
||||
if (vm.count("phys")) {
|
||||
std::string filename = vm["phys"].as<std::string>();
|
||||
ctx->write_physical_netlist(filename);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> FpgaInterchangeCommandHandler::createContext(std::unordered_map<std::string, Property> &values)
|
||||
{
|
||||
ArchArgs chipArgs;
|
||||
if (!vm.count("chipdb")) {
|
||||
log_error("chip database binary must be provided\n");
|
||||
}
|
||||
chipArgs.chipdb = vm["chipdb"].as<std::string>();
|
||||
return std::unique_ptr<Context>(new Context(chipArgs));
|
||||
}
|
||||
|
||||
void FpgaInterchangeCommandHandler::customAfterLoad(Context *ctx) {}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
FpgaInterchangeCommandHandler handler(argc, argv);
|
||||
return handler.exec();
|
||||
}
|
||||
|
||||
#endif
|
0
gui/fpga_interchange/family.cmake
Normal file
0
gui/fpga_interchange/family.cmake
Normal file
50
gui/fpga_interchange/mainwindow.cc
Normal file
50
gui/fpga_interchange/mainwindow.cc
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
||||
* 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 "mainwindow.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <cstdlib>
|
||||
|
||||
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
|
||||
: BaseMainWindow(std::move(context), handler, parent)
|
||||
{
|
||||
initMainResource();
|
||||
QMessageBox::critical(0, "Error - FIXME", "No GUI support for nextpnr-generic");
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {}
|
||||
|
||||
void MainWindow::newContext(Context *ctx)
|
||||
{
|
||||
std::string title = "nextpnr-generic - " + ctx->getChipName();
|
||||
setWindowTitle(title.c_str());
|
||||
}
|
||||
|
||||
void MainWindow::createMenu() {}
|
||||
|
||||
void MainWindow::new_proj() {}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
46
gui/fpga_interchange/mainwindow.h
Normal file
46
gui/fpga_interchange/mainwindow.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
||||
* 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 MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include "../basewindow.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
class MainWindow : public BaseMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
|
||||
virtual ~MainWindow();
|
||||
|
||||
public:
|
||||
void createMenu();
|
||||
|
||||
protected Q_SLOTS:
|
||||
void new_proj() override;
|
||||
void newContext(Context *ctx);
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // MAINWINDOW_H
|
2
gui/fpga_interchange/nextpnr.qrc
Normal file
2
gui/fpga_interchange/nextpnr.qrc
Normal file
@ -0,0 +1,2 @@
|
||||
<RCC>
|
||||
</RCC>
|
Loading…
Reference in New Issue
Block a user