diff options
| -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 | 
