diff options
author | Keith Rothman <537074+litghost@users.noreply.github.com> | 2021-02-12 16:12:16 -0800 |
---|---|---|
committer | Keith Rothman <537074+litghost@users.noreply.github.com> | 2021-02-15 09:54:58 -0800 |
commit | 664407089b51e1c66d91ff1ee4aa4d4148bb8f1b (patch) | |
tree | 052acc1abc92bd87748b616e29ba7c396d38cae9 | |
parent | 5e11e29ba2e5927047b2dbf7d3b613dc33a9cc98 (diff) | |
download | nextpnr-664407089b51e1c66d91ff1ee4aa4d4148bb8f1b.tar.gz nextpnr-664407089b51e1c66d91ff1ee4aa4d4148bb8f1b.tar.bz2 nextpnr-664407089b51e1c66d91ff1ee4aa4d4148bb8f1b.zip |
Add FPGA interchange frontend and backend.
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | fpga_interchange/arch.cc | 21 | ||||
-rw-r--r-- | fpga_interchange/arch.h | 10 | ||||
-rw-r--r-- | fpga_interchange/archdefs.h | 4 | ||||
-rw-r--r-- | fpga_interchange/family.cmake | 22 | ||||
-rw-r--r-- | fpga_interchange/fpga_interchange.cpp | 826 | ||||
-rw-r--r-- | fpga_interchange/fpga_interchange.h | 35 |
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 |