aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--fpga_interchange/arch.cc21
-rw-r--r--fpga_interchange/arch.h10
-rw-r--r--fpga_interchange/archdefs.h4
-rw-r--r--fpga_interchange/family.cmake22
-rw-r--r--fpga_interchange/fpga_interchange.cpp826
-rw-r--r--fpga_interchange/fpga_interchange.h35
7 files changed, 915 insertions, 5 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1b7e147c..87f2e9b0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -99,7 +99,7 @@ foreach(item ${ARCH})
endif()
endforeach()
-set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD 14)
if (MSVC)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /W4 /wd4100 /wd4244 /wd4125 /wd4800 /wd4456 /wd4458 /wd4305 /wd4459 /wd4121 /wd4996")
diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc
index 1abf6f30..814e4f85 100644
--- a/fpga_interchange/arch.cc
+++ b/fpga_interchange/arch.cc
@@ -25,6 +25,7 @@
#include <cmath>
#include <cstring>
#include <queue>
+#include "fpga_interchange.h"
#include "log.h"
#include "nextpnr.h"
#include "placer1.h"
@@ -82,6 +83,10 @@ Arch::Arch(ArchArgs args) : args(args)
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"));
}
// -----------------------------------------------------------------------
@@ -561,8 +566,14 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
// -----------------------------------------------------------------------
-void Arch::read_logical_netlist(const std::string &filename) {}
-void Arch::write_physical_netlist(const std::string &filename) const {}
+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)
{
@@ -574,6 +585,12 @@ void Arch::parse_xdc(const std::string &filename)
}
}
+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";
+}
+
// -----------------------------------------------------------------------
#ifdef WITH_HEAP
diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h
index 886978f1..b152bd0e 100644
--- a/fpga_interchange/arch.h
+++ b/fpga_interchange/arch.h
@@ -199,6 +199,12 @@ inline const BelInfoPOD &bel_info(const ChipInfoPOD *chip_info, BelId bel)
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];
+}
+
struct BelIterator
{
const ChipInfoPOD *chip;
@@ -742,6 +748,8 @@ struct Arch : ArchAPI<ArchRanges>
}
char getNameDelimiter() const override { return '/'; }
+ std::string get_part() const;
+
// -------------------------------------------------
void setup_byname() const;
@@ -1308,6 +1316,8 @@ struct Arch : ArchAPI<ArchRanges>
void read_logical_netlist(const std::string &filename);
void write_physical_netlist(const std::string &filename) const;
void parse_xdc(const std::string &filename);
+
+ std::unordered_set<IdString> io_port_types;
};
NEXTPNR_NAMESPACE_END
diff --git a/fpga_interchange/archdefs.h b/fpga_interchange/archdefs.h
index d6d0a3c7..e280de55 100644
--- a/fpga_interchange/archdefs.h
+++ b/fpga_interchange/archdefs.h
@@ -123,6 +123,10 @@ struct NetInfo;
struct ArchCellInfo
{
+ ArchCellInfo() : cell_mapping(-1) {}
+
+ int32_t cell_mapping;
+ std::unordered_map<IdString, std::vector<IdString>> cell_bel_pins;
};
NEXTPNR_NAMESPACE_END
diff --git a/fpga_interchange/family.cmake b/fpga_interchange/family.cmake
index c3fefaba..3f3b5a93 100644
--- a/fpga_interchange/family.cmake
+++ b/fpga_interchange/family.cmake
@@ -3,7 +3,25 @@ if(NOT ${TCL_FOUND})
message(FATAL_ERROR "Tcl is required for FPGA interchange Arch.")
endif()
+find_package(ZLIB REQUIRED)
+find_package(CapnProto REQUIRED)
+
+set(PROTOS LogicalNetlist.capnp PhysicalNetlist.capnp References.capnp)
+
+set(CAPNP_SRCS)
+set(CAPNP_HDRS)
+foreach (proto ${PROTOS})
+ capnp_generate_cpp(CAPNP_SRC CAPNP_HDR 3rdparty/fpga-interchange-schema/interchange/${proto})
+ list(APPEND CAPNP_HDRS ${CAPNP_HDR})
+ list(APPEND CAPNP_SRCS ${CAPNP_SRC})
+endforeach()
+add_library(fpga_interchange_capnp STATIC ${CAPNP_SRCS})
+
foreach (target ${family_targets})
- target_link_libraries(${target} LINK_PUBLIC ${TCL_LIBRARY})
- include_directories (${TCL_INCLUDE_PATH})
+ target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/3rdparty/fpga-interchange-schema/interchange)
+ 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 CapnProto::capnp)
+ target_link_libraries(${target} PRIVATE z)
endforeach()
diff --git a/fpga_interchange/fpga_interchange.cpp b/fpga_interchange/fpga_interchange.cpp
new file mode 100644
index 00000000..8ff080dd
--- /dev/null
+++ b/fpga_interchange/fpga_interchange.cpp
@@ -0,0 +1,826 @@
+/*
+ * 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 <capnp/message.h>
+#include <sstream>
+#include <capnp/serialize.h>
+#include <kj/std/iostream.h>
+#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<capnp::word> words = messageToFlatArray(message);
+ kj::ArrayPtr<kj::byte> bytes = words.asBytes();
+
+ gzFile file = gzopen(filename.c_str(), "w");
+ NPNR_ASSERT(file != Z_NULL);
+
+ NPNR_ASSERT(gzwrite(file, &bytes[0], bytes.size()) == bytes.size());
+ NPNR_ASSERT(gzclose(file) == Z_OK);
+}
+
+struct StringEnumerator {
+ std::vector<std::string> strings;
+ std::map<std::string, size_t> 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,
+ PipId pip,
+ PhysicalNetlist::PhysNetlist::RouteBranch::Builder branch) {
+ 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 = branch.getRouteSegment().initPip();
+ pip.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.setWire0(strings->get_index(src_wire_name.str(ctx)));
+ pip.setWire1(strings->get_index(dst_wire_name.str(ctx)));
+ pip.setForward(true);
+
+ 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();
+ 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);
+
+ 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_wire_name.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_wire_name.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)));
+
+ // 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) {
+ 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);
+
+ 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)));
+}
+
+
+static void emit_net(
+ const Context * ctx,
+ StringEnumerator * strings,
+ const std::unordered_map<WireId, std::vector<PipId>> &pip_downhill,
+ const std::unordered_map<WireId, std::vector<BelPin>> &sinks,
+ std::unordered_set<PipId> *pips,
+ 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<PipId> & 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, branches[branch_index++]);
+
+ emit_net(ctx, strings, pip_downhill, sinks, pips,
+ 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);
+ }
+ }
+}
+
+void FpgaInterchange::write_physical_netlist(const Context * ctx, const std::string &filename) {
+ ::capnp::MallocMessageBuilder message;
+
+ PhysicalNetlist::PhysNetlist::Builder phys_netlist = message.initRoot<PhysicalNetlist::PhysNetlist>();
+
+ phys_netlist.setPart(ctx->get_part());
+
+ std::unordered_set<IdString> 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;
+
+ size_t number_placements = 0;
+ for(auto & cell_name : placed_cells) {
+ const CellInfo & cell = *ctx->cells.at(cell_name);
+ if(!ctx->io_port_types.count(cell.type)) {
+ number_placements += 1;
+ }
+ }
+
+ std::unordered_map<std::string, std::string> 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(ctx->io_port_types.count(cell.type)) {
+ 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)));
+ 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);
+
+ size_t pin_count = 0;
+ for(const auto & pin : cell.cell_bel_pins) {
+ 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) {
+ 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 nets = phys_netlist.initPhysNets(ctx->nets.size());
+ auto net_iter = nets.begin();
+ for(auto & net_pair : ctx->nets) {
+ auto &net = *net_pair.second;
+ auto net_out = *net_iter++;
+
+ net_out.setName(strings.get_index(net.name.str(ctx)));
+
+ // FIXME: Mark net as signal/vcc/gnd.
+ //
+ // Also vcc/gnd nets needs to get special handling through inverters.
+ std::unordered_map<WireId, BelPin> root_wires;
+ std::unordered_map<WireId, std::vector<PipId>> pip_downhill;
+ std::unordered_set<PipId> pips;
+
+ const CellInfo *driver_cell = net.driver.cell;
+ if (driver_cell != nullptr && driver_cell->bel != BelId()) {
+ 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;
+ }
+ }
+ }
+
+ std::unordered_map<WireId, std::vector<BelPin>> sinks;
+ for(const auto &port_ref : net.users) {
+ if(port_ref.cell != nullptr && port_ref.cell->bel != BelId()) {
+ for(IdString bel_pin_name : port_ref.cell->cell_bel_pins.at(port_ref.port)) {
+ 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);
+ }
+ }
+ }
+ }
+
+ for(auto &wire_pair : net.wires) {
+ WireId downhill_wire = wire_pair.first;
+ PipId pip = wire_pair.second.pip;
+ 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));
+ }
+ }
+
+ auto sources = net_out.initSources(root_wires.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, root_wire, source_branch);
+ }
+
+ // Any pips that were not part of a tree starting from the source are
+ // stubs.
+ auto stubs = net_out.initStubs(pips.size());
+ auto stub_iter = stubs.begin();
+ for(PipId pip : pips) {
+ emit_branch(ctx, &strings, 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;
+ }
+};
+
+NEXTPNR_NAMESPACE_END
+
+template <> struct std::hash<NEXTPNR_NAMESPACE_PREFIX PortKey>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PortKey &key) const noexcept
+ {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, std::hash<int32_t>()(key.inst_idx));
+ boost::hash_combine(seed, std::hash<uint32_t>()(key.port_idx));
+ return seed;
+ }
+};
+
+NEXTPNR_NAMESPACE_BEGIN
+
+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;
+
+ std::unordered_map<PortKey, std::vector<int32_t>> connections;
+
+ ModuleReader(const LogicalNetlistImpl *root,
+ LogicalNetlist::Netlist::CellInstance::Reader cell_inst, bool is_top);
+};
+
+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;
+ std::vector<int32_t> scratch;
+};
+
+struct LogicalNetlistImpl
+{
+ LogicalNetlist::Netlist::Reader root;
+ std::vector<std::string> strings;
+
+ typedef const ModuleReader ModuleDataType;
+ typedef const PortReader& ModulePortDataType;
+ typedef const CellReader& CellDataType;
+ typedef const NetReader& NetnameDataType;
+ typedef const std::vector<int32_t>& BitVectorDataType;
+
+ LogicalNetlistImpl(LogicalNetlist::Netlist::Reader root) : root(root) {
+ strings.reserve(root.getStrList().size());
+ for(auto s : root.getStrList()) {
+ strings.push_back(s);
+ }
+ }
+
+ template <typename TFunc> void foreach_module(TFunc Func) const
+ {
+ for (const auto &cell_inst : root.getInstList()) {
+ ModuleReader module(this, cell_inst, /*is_top=*/false);
+ Func(strings.at(cell_inst.getName()), module);
+ }
+
+ auto top = root.getTopInst();
+ ModuleReader top_module(this, top, /*is_top=*/true);
+ Func(strings.at(top.getName()), top_module);
+ }
+
+ template <typename TFunc> 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 <typename TFunc> 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 <typename TFunc> void foreach_netname(const ModuleReader &mod, TFunc Func) const
+ {
+ auto nets = mod.cell.getNets();
+ for(size_t net_idx = 0; net_idx < nets.size(); ++net_idx) {
+ NetReader net_reader(&mod, net_idx);
+ auto net = nets[net_idx];
+ Func(strings.at(net.getName()), 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 <typename TFunc> 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 <typename TFunc> 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 <typename TFunc> 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 <typename TFunc> 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 <typename TFunc> 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 <typename TFunc> void foreach_attr(const NetReader &net_reader, TFunc Func) const {
+ auto net = net_reader.module->cell.getNets()[net_reader.net_idx];
+ foreach_prop_map(net.getPropMap(), Func);
+ }
+
+ template <typename TFunc> 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 <typename TFunc> void foreach_setting(const ModuleReader &obj, TFunc Func) const
+ {
+ foreach_prop_map(root.getPropMap(), Func);
+ }
+
+ template <typename TFunc> 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<int32_t> &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<int32_t> &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<int32_t> &get_net_bits(const NetReader &net) const {
+ return net.scratch;
+ }
+
+ int get_vector_length(const std::vector<int32_t> &bits) const { return int(bits.size()); }
+
+ bool is_vector_bit_constant(const std::vector<int32_t> &bits, int i) const
+ {
+ // FIXME: Check if this is right. Assumption is that cells have been
+ // emitted for GND and VCC, e.g. VCC vcc(.P(vcc_net)).
+ return false;
+ }
+
+ char get_vector_bit_constval(const std::vector<int32_t>&bits, int i) const
+ {
+ // Unreachable!
+ NPNR_ASSERT(false);
+ }
+
+ int get_vector_bit_signal(const std::vector<int32_t>&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 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<int32_t>());
+ NPNR_ASSERT(result.second);
+
+ std::vector<int32_t> & port_connections = result.first->second;
+ port_connections.resize(port_width, -1);
+ }
+
+ 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<int32_t>());
+ NPNR_ASSERT(result.second);
+
+ auto inst_port = ports[inst_port_idx];
+ size_t port_width = get_port_width(inst_port);
+
+ std::vector<int32_t> & port_connections = result.first->second;
+ port_connections.resize(port_width, -1);
+ }
+ }
+
+ auto nets = cell.getNets();
+ for(size_t net_idx = 0; net_idx < nets.size(); ++net_idx) {
+ auto net = nets[net_idx];
+
+ 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<int32_t> & port_connections = connections.at(port_key);
+
+ if(port_inst.getBusIdx().isSingleBit()) {
+ port_connections[0] = net_idx;
+ } else {
+ port_connections.at(port_inst.getBusIdx().getIdx()) = net_idx;
+ }
+ }
+ }
+}
+
+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<uint8_t> 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);
+
+ LogicalNetlist::Netlist::Reader netlist = message_reader.getRoot<LogicalNetlist::Netlist>();
+ LogicalNetlistImpl netlist_reader(netlist);
+
+ GenericFrontend<LogicalNetlistImpl>(ctx, netlist_reader, /*split_io=*/false)();
+}
+
+NEXTPNR_NAMESPACE_END
+
diff --git a/fpga_interchange/fpga_interchange.h b/fpga_interchange/fpga_interchange.h
new file mode 100644
index 00000000..239b0e96
--- /dev/null
+++ b/fpga_interchange/fpga_interchange.h
@@ -0,0 +1,35 @@
+/*
+ * 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