diff options
| author | Keith Rothman <537074+litghost@users.noreply.github.com> | 2021-02-22 09:13:44 -0800 | 
|---|---|---|
| committer | Keith Rothman <537074+litghost@users.noreply.github.com> | 2021-02-23 14:09:28 -0800 | 
| commit | 184665652eaf351bf9337b524c5d82a50ce54041 (patch) | |
| tree | 3aaac1c9d27c62c74e01f210a808dfc8209826d8 /fpga_interchange | |
| parent | 5574455d2a20d3bb950e5dd907ef193d049a2a26 (diff) | |
| download | nextpnr-184665652eaf351bf9337b524c5d82a50ce54041.tar.gz nextpnr-184665652eaf351bf9337b524c5d82a50ce54041.tar.bz2 nextpnr-184665652eaf351bf9337b524c5d82a50ce54041.zip | |
Finish dedicated interconnect implementation.
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
Diffstat (limited to 'fpga_interchange')
| -rw-r--r-- | fpga_interchange/arch.cc | 45 | ||||
| -rw-r--r-- | fpga_interchange/dedicated_interconnect.cc | 694 | ||||
| -rw-r--r-- | fpga_interchange/dedicated_interconnect.h | 11 | 
3 files changed, 611 insertions, 139 deletions
| diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 966d74f3..776fbdb0 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -767,32 +767,45 @@ void Arch::map_cell_pins(CellInfo *cell, int32_t mapping)          IdString bel_pin(pin_map.bel_pin);          if (cell_pin.str(this) == "GND") { +            IdString gnd_net_name(chip_info->constants->gnd_net_name); +              PortInfo port_info;              port_info.name = bel_pin;              port_info.type = PORT_IN;              port_info.net = nullptr;              auto result = cell->ports.emplace(bel_pin, port_info); -            NPNR_ASSERT(result.second); - -            cell->cell_bel_pins[bel_pin].push_back(bel_pin); - -            connectPort(IdString(chip_info->constants->gnd_net_name), cell->name, bel_pin); +            if(result.second) { +                cell->cell_bel_pins[bel_pin].push_back(bel_pin); +                connectPort(gnd_net_name, cell->name, bel_pin); +            } else { +                NPNR_ASSERT(result.first->second.net == getNetByAlias(gnd_net_name)); +                auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin})); +                NPNR_ASSERT(result2.first->second.at(0) == bel_pin); +                NPNR_ASSERT(result2.first->second.size() == 1); +            }              continue;          }          if (cell_pin.str(this) == "VCC") { +            IdString vcc_net_name(chip_info->constants->vcc_net_name); +              PortInfo port_info;              port_info.name = bel_pin;              port_info.type = PORT_IN;              port_info.net = nullptr;              auto result = cell->ports.emplace(bel_pin, port_info); -            NPNR_ASSERT(result.second); - -            cell->cell_bel_pins[bel_pin].push_back(bel_pin); +            if(result.second) { +                cell->cell_bel_pins[bel_pin].push_back(bel_pin); +                connectPort(vcc_net_name, cell->name, bel_pin); +            } else { +                NPNR_ASSERT(result.first->second.net == getNetByAlias(vcc_net_name)); +                auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin})); +                NPNR_ASSERT(result2.first->second.at(0) == bel_pin); +                NPNR_ASSERT(result2.first->second.size() == 1); +            } -            connectPort(IdString(chip_info->constants->vcc_net_name), cell->name, bel_pin);              continue;          } @@ -976,6 +989,8 @@ void Arch::merge_constant_nets() {                  disconnectPort(cell, port_ref.port);                  connectPort(gnd_net_name, cell, port_ref.port);              } + +            continue;          }          if(net->driver.cell->type == vcc_cell_type) { @@ -1003,11 +1018,23 @@ void Arch::merge_constant_nets() {      for(IdString other_gnd_net : other_gnd_nets) {          NetInfo * net = getNetByAlias(other_gnd_net);          NPNR_ASSERT(net->users.empty()); +        if(net->driver.cell) { +            PortRef driver = net->driver; +            IdString cell_to_remove = driver.cell->name; +            disconnectPort(driver.cell->name, driver.port); +            NPNR_ASSERT(cells.erase(cell_to_remove)); +        }      }      for(IdString other_vcc_net : other_vcc_nets) {          NetInfo * net = getNetByAlias(other_vcc_net);          NPNR_ASSERT(net->users.empty()); +        if(net->driver.cell) { +            PortRef driver = net->driver; +            IdString cell_to_remove = driver.cell->name; +            disconnectPort(driver.cell->name, driver.port); +            NPNR_ASSERT(cells.erase(cell_to_remove)); +        }      }      for(IdString other_gnd_net : other_gnd_nets) { diff --git a/fpga_interchange/dedicated_interconnect.cc b/fpga_interchange/dedicated_interconnect.cc index 82101fbd..b9ef93b5 100644 --- a/fpga_interchange/dedicated_interconnect.cc +++ b/fpga_interchange/dedicated_interconnect.cc @@ -24,6 +24,32 @@  NEXTPNR_NAMESPACE_BEGIN +// All legal routes involved at most 2 sites, the source site and the sink +// site.  The source site and sink sites may be the same, but that is not +// dedicated routing, that is intra site routing. +// +// Dedicated routing must leave the sink site, traverse some routing and +// terminate at another site.  Routing that "flys" over a site is expressed as +// a psuedo-pip connected the relevant site pin wires, rather than traversing +// the site. +enum WireNodeState { +    IN_SINK_SITE = 0, +    IN_ROUTING = 1, +    IN_SOURCE_SITE = 2 +}; + +struct WireNode { +    WireId wire; +    WireNodeState state; +    int depth; +}; + +// Maximum depth that a dedicate interconnect is considered. +// +// Routing networks with depth <= kMaxDepth is considers a dedicated +// interconnect. +constexpr int kMaxDepth = 20; +  void DedicatedInterconnect::init(const Context *ctx) {      this->ctx = ctx; @@ -40,94 +66,290 @@ void DedicatedInterconnect::init(const Context *ctx) {  bool DedicatedInterconnect::check_routing(          BelId src_bel, IdString src_bel_pin,          BelId dst_bel, IdString dst_bel_pin) const { -    // FIXME: Implement. -    return false; -} +    std::vector<WireNode> nodes_to_expand; -bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell) const { -    NPNR_ASSERT(bel != BelId()); +    WireId src_wire = ctx->getBelPinWire(src_bel, src_bel_pin); -    Loc bel_loc = ctx->getBelLocation(bel); +    const auto & src_wire_data = ctx->wire_info(src_wire); +    NPNR_ASSERT(src_wire_data.site != -1); -    const auto &bel_data = bel_info(ctx->chip_info, bel); +    WireId dst_wire = ctx->getBelPinWire(dst_bel, dst_bel_pin); -    for(const auto &port_pair : cell->ports) { -        IdString port_name = port_pair.first; -        NetInfo *net = port_pair.second.net; -        if(net == nullptr) { -            continue; -        } +    const auto & dst_wire_data = ctx->wire_info(dst_wire); +    NPNR_ASSERT(dst_wire_data.site != -1); -        // Only check sink BELs. -        if(net->driver.cell == cell && net->driver.port == port_name) { -            continue; -        } +    WireNode wire_node; +    wire_node.wire = src_wire; +    wire_node.state = IN_SOURCE_SITE; +    wire_node.depth = 0; -        // This net doesn't have a driver, probably not valid? -        NPNR_ASSERT(net->driver.cell != nullptr); +    nodes_to_expand.push_back(wire_node); -        BelId driver_bel = net->driver.cell->bel; -        if(driver_bel == BelId()) { -            return true; -        } +    while(!nodes_to_expand.empty()) { +        WireNode node_to_expand = nodes_to_expand.back(); +        nodes_to_expand.pop_back(); -        const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel); +        for(PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) { +            if(ctx->is_pip_synthetic(pip)) { +                continue; +            } -        Loc driver_loc = ctx->getBelLocation(driver_bel); +            WireId wire = ctx->getPipDstWire(pip); +            if(wire == WireId()) { +                continue; +            } -        DeltaTileTypeBelPin driver_type_bel_pin; -        driver_type_bel_pin.delta_x = driver_loc.x - bel_loc.x; -        driver_type_bel_pin.delta_y = driver_loc.y - bel_loc.y; -        driver_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[driver_bel.tile].type; -        driver_type_bel_pin.type_bel_pin.bel_index = driver_bel.index; -        driver_type_bel_pin.type_bel_pin.bel_pin = get_only_value(ctx->getBelPinsForCellPin(net->driver.cell, net->driver.port)); +#ifdef DEBUG_EXPANSION +            log_info(" - At wire %s via %s\n", +                    ctx->nameOfWire(wire), ctx->nameOfPip(pip)); +#endif -        for(IdString bel_pin : ctx->getBelPinsForCellPin(cell, port_name)) { -            TileTypeBelPin type_bel_pin; -            type_bel_pin.tile_type = ctx->chip_info->tiles[bel.tile].type; -            type_bel_pin.bel_index = bel.index; -            type_bel_pin.bel_pin = bel_pin; +            WireNode next_node; +            next_node.wire = wire; +            next_node.depth = node_to_expand.depth += 1; + +            if(next_node.depth > kMaxDepth) { +                // Dedicated routing should reach sources by kMaxDepth (with +                // tuning). +                // +                // FIXME: Consider removing kMaxDepth and use kMaxSources? +                return false; +            } + +            auto const & wire_data = ctx->wire_info(wire); -            auto iter = pins_with_dedicate_interconnect.find(type_bel_pin); -            if(iter == pins_with_dedicate_interconnect.end()) { -                // This BEL pin doesn't have a dedicate interconnect. +            bool expand_node = true; +            if(ctx->is_site_port(pip)) { +                switch(node_to_expand.state) { +                    case IN_SOURCE_SITE: +                        NPNR_ASSERT(wire_data.site == -1); +                        next_node.state = IN_ROUTING; +                        break; +                    case IN_ROUTING: +                        NPNR_ASSERT(wire_data.site != -1); +                        if(wire.tile == src_wire.tile && wire_data.site == src_wire_data.site) { +                            // Dedicated routing won't have straight loops, +                            // general routing looks like that. +#ifdef DEBUG_EXPANSION +                            log_info(" - Not dedicated site routing because loop!"); +#endif +                            return false; +                        } +                        next_node.state = IN_SINK_SITE; +                        break; +                    case IN_SINK_SITE: +                        // Once entering a site, do not leave it again. +                        // This path is not a legal route! +                        expand_node = false; +                        break; +                    default: +                        // Unreachable!!! +                        NPNR_ASSERT(false); +                } +            } else { +                next_node.state = node_to_expand.state; +            } + +            if(expand_node) { +                nodes_to_expand.push_back(next_node); +            } else {                  continue;              } -            if(bel.tile == driver_bel.tile && bel_data.site == driver_bel_data.site) { +            if(next_node.state == IN_SINK_SITE) { +                for(BelPin bel_pin : ctx->getWireBelPins(wire)) { +                    if(bel_pin.bel == dst_bel && bel_pin.pin == dst_bel_pin) { +                        if(ctx->verbose) { +                            log_info("Valid dedicated interconnect from %s/%s to %s/%s\n", +                                    ctx->nameOfBel(src_bel), +                                    src_bel_pin.c_str(ctx), +                                    ctx->nameOfBel(dst_bel), +                                    dst_bel_pin.c_str(ctx)); +                        } +                        return true; +                    } +                } +            } +        } +    } + +    return false; +} + +bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel, +        const CellInfo* cell, IdString driver_port, NetInfo *net) const { +    const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel); + +    TileTypeBelPin type_bel_pin; +    type_bel_pin.tile_type = ctx->chip_info->tiles[driver_bel.tile].type; +    type_bel_pin.bel_index = driver_bel.index; + +    Loc driver_loc = ctx->getBelLocation(driver_bel); + +    for(IdString driver_bel_pin : ctx->getBelPinsForCellPin(cell, driver_port)) { +        type_bel_pin.bel_pin = driver_bel_pin; + +        auto iter = sources.find(type_bel_pin); +        if(iter == sources.end()) { +            // This BEL pin doesn't have a dedicate interconnect. +            continue; +        } + +        for(const PortRef & port_ref : net->users) { +            NPNR_ASSERT(port_ref.cell != nullptr); + +            if(port_ref.cell->bel == BelId()) { +                return true; +            } + +            BelId sink_bel = port_ref.cell->bel; +            const auto &sink_bel_data = bel_info(ctx->chip_info, sink_bel); +            Loc sink_loc = ctx->getBelLocation(port_ref.cell->bel); + +            if(sink_bel.tile == driver_bel.tile && sink_bel_data.site == driver_bel_data.site) {                  // This is a site local routing, even though this is a sink                  // with a dedicated interconnect.                  continue;              } -            // Do fast routing check to see if the pair of driver and sink -            // every are valid. -            if(iter->second.count(driver_type_bel_pin) == 0) { -                if(ctx->verbose) { -                    log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s\n", -                            ctx->nameOfBel(bel), -                            bel_pin.c_str(ctx), -                            ctx->nameOfBel(driver_bel), -                            driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx)); +            DeltaTileTypeBelPin sink_type_bel_pin; +            sink_type_bel_pin.delta_x = sink_loc.x - driver_loc.x; +            sink_type_bel_pin.delta_y = sink_loc.y - driver_loc.y; +            sink_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type; +            sink_type_bel_pin.type_bel_pin.bel_index = sink_bel.index; + +            for(IdString sink_bel_pin : ctx->getBelPinsForCellPin(port_ref.cell, port_ref.port)) { +                sink_type_bel_pin.type_bel_pin.bel_pin = sink_bel_pin; + +                // Do fast routing check to see if the pair of driver and sink +                // every are valid. +                if(iter->second.count(sink_type_bel_pin) == 0) { +                    if(ctx->verbose) { +                        log_info("BEL %s is not valid because pin %s cannot reach %s/%s\n", +                                ctx->nameOfBel(driver_bel), +                                driver_bel_pin.c_str(ctx), +                                ctx->nameOfBel(sink_bel), +                                sink_bel_pin.c_str(ctx)); +                    } +                    return false;                  } -                return false; -            } -            // Do detailed routing check to ensure driver can reach sink. -            // -            // FIXME: This might be too slow, but it handles a case on -            // SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the -            // delta_y=2 case is rare. -            if(!check_routing( -                    driver_bel, driver_type_bel_pin.type_bel_pin.bel_pin, -                    bel, bel_pin)) { -                if(ctx->verbose) { -                    log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s (via detailed check)\n", -                            ctx->nameOfBel(bel), -                            bel_pin.c_str(ctx), -                            ctx->nameOfBel(driver_bel), -                            driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx)); +                // Do detailed routing check to ensure driver can reach sink. +                // +                // FIXME: This might be too slow, but it handles a case on +                // SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the +                // delta_y=2 case is rare. +                if(!check_routing( +                        driver_bel, driver_bel_pin, +                        sink_bel, sink_bel_pin)) { +                    if(ctx->verbose) { +                        log_info("BEL %s is not valid because pin %s cannot be reach %s/%s (via detailed check)\n", +                                ctx->nameOfBel(driver_bel), +                                driver_bel_pin.c_str(ctx), +                                ctx->nameOfBel(sink_bel), +                                sink_bel_pin.c_str(ctx)); +                    } +                    return false;                  } +            } +        } +    } + +    return true; +} + +bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo* cell, IdString port_name, NetInfo *net) const { +    BelId driver_bel = net->driver.cell->bel; +    if(driver_bel == BelId()) { +        return true; +    } + +    const auto &bel_data = bel_info(ctx->chip_info, bel); +    const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel); + +    Loc bel_loc = ctx->getBelLocation(bel); +    Loc driver_loc = ctx->getBelLocation(driver_bel); + +    DeltaTileTypeBelPin driver_type_bel_pin; +    driver_type_bel_pin.delta_x = driver_loc.x - bel_loc.x; +    driver_type_bel_pin.delta_y = driver_loc.y - bel_loc.y; +    driver_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[driver_bel.tile].type; +    driver_type_bel_pin.type_bel_pin.bel_index = driver_bel.index; +    driver_type_bel_pin.type_bel_pin.bel_pin = get_only_value(ctx->getBelPinsForCellPin(net->driver.cell, net->driver.port)); + +    for(IdString bel_pin : ctx->getBelPinsForCellPin(cell, port_name)) { +        TileTypeBelPin type_bel_pin; +        type_bel_pin.tile_type = ctx->chip_info->tiles[bel.tile].type; +        type_bel_pin.bel_index = bel.index; +        type_bel_pin.bel_pin = bel_pin; + +        auto iter = sinks.find(type_bel_pin); +        if(iter == sinks.end()) { +            // This BEL pin doesn't have a dedicate interconnect. +            continue; +        } + +        if(bel.tile == driver_bel.tile && bel_data.site == driver_bel_data.site) { +            // This is a site local routing, even though this is a sink +            // with a dedicated interconnect. +            continue; +        } + +        // Do fast routing check to see if the pair of driver and sink +        // every are valid. +        if(iter->second.count(driver_type_bel_pin) == 0) { +            if(ctx->verbose) { +                log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s\n", +                        ctx->nameOfBel(bel), +                        bel_pin.c_str(ctx), +                        ctx->nameOfBel(driver_bel), +                        driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx)); +            } +            return false; +        } + +        // Do detailed routing check to ensure driver can reach sink. +        // +        // FIXME: This might be too slow, but it handles a case on +        // SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the +        // delta_y=2 case is rare. +        if(!check_routing( +                driver_bel, driver_type_bel_pin.type_bel_pin.bel_pin, +                bel, bel_pin)) { +            if(ctx->verbose) { +                log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s (via detailed check)\n", +                        ctx->nameOfBel(bel), +                        bel_pin.c_str(ctx), +                        ctx->nameOfBel(driver_bel), +                        driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx)); +            } +            return false; +        } +    } + +    return true; +} + +bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell) const { +    NPNR_ASSERT(bel != BelId()); + +    for(const auto &port_pair : cell->ports) { +        IdString port_name = port_pair.first; +        NetInfo *net = port_pair.second.net; +        if(net == nullptr) { +            continue; +        } + +        // This net doesn't have a driver, probably not valid? +        NPNR_ASSERT(net->driver.cell != nullptr); + +        // Only check sink BELs. +        if(net->driver.cell == cell && net->driver.port == port_name) { +            if(!is_driver_on_net_valid(bel, cell, port_name, net)) { +                return false; +            } +        } else { +            if(!is_sink_on_net_valid(bel, cell, port_name, net)) {                  return false;              }          } @@ -137,37 +359,83 @@ bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell)  }  void DedicatedInterconnect::print_dedicated_interconnect() const { -    log_info("Found %zu sinks with dedicated interconnect\n", pins_with_dedicate_interconnect.size()); +    log_info("Found %zu sinks with dedicated interconnect\n", sinks.size()); +    log_info("Found %zu sources with dedicated interconnect\n", sources.size());      std::vector<TileTypeBelPin> sorted_keys; -    for(const auto & sink_to_srcs : pins_with_dedicate_interconnect) { +    for(const auto & sink_to_srcs : sinks) {          sorted_keys.push_back(sink_to_srcs.first);      } +    for(const auto & src_to_sinks : sources) { +        sorted_keys.push_back(src_to_sinks.first); +    }      std::sort(sorted_keys.begin(), sorted_keys.end()); -    for(const auto & dst : sorted_keys) { -        for(const auto & src : pins_with_dedicate_interconnect.at(dst)) { -            const TileTypeInfoPOD & src_tile_type = ctx->chip_info->tile_types[src.type_bel_pin.tile_type]; -            const BelInfoPOD & src_bel_info = src_tile_type.bel_data[src.type_bel_pin.bel_index]; -            IdString src_site_type = IdString(src_tile_type.site_types[src_bel_info.site]); -            IdString src_bel_pin = src.type_bel_pin.bel_pin; - -            const TileTypeInfoPOD & dst_tile_type = ctx->chip_info->tile_types[dst.tile_type]; -            const BelInfoPOD & dst_bel_info = dst_tile_type.bel_data[dst.bel_index]; -            IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]); -            IdString dst_bel_pin = dst.bel_pin; - -            log_info("%s.%s/%s/%s (%d, %d) -> %s.%s/%s/%s\n", -                    IdString(src_tile_type.name).c_str(ctx), -                    src_site_type.c_str(ctx), -                    IdString(src_bel_info.name).c_str(ctx), -                    src_bel_pin.c_str(ctx), -                    src.delta_x, -                    src.delta_y, -                    IdString(dst_tile_type.name).c_str(ctx), -                    dst_site_type.c_str(ctx), -                    IdString(dst_bel_info.name).c_str(ctx), -                    dst_bel_pin.c_str(ctx)); +    for(const auto & key : sorted_keys) { +        auto iter = sinks.find(key); +        if(iter != sinks.end()) { +            auto dst = key; +            for(const auto & src_delta : iter->second) { +                auto src = src_delta.type_bel_pin; +                auto delta_x = src_delta.delta_x; +                auto delta_y = src_delta.delta_y; + +                const TileTypeInfoPOD & src_tile_type = ctx->chip_info->tile_types[src.tile_type]; +                const BelInfoPOD & src_bel_info = src_tile_type.bel_data[src.bel_index]; +                IdString src_site_type = IdString(src_tile_type.site_types[src_bel_info.site]); +                IdString src_bel_pin = src.bel_pin; + +                const TileTypeInfoPOD & dst_tile_type = ctx->chip_info->tile_types[dst.tile_type]; +                const BelInfoPOD & dst_bel_info = dst_tile_type.bel_data[dst.bel_index]; +                IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]); +                IdString dst_bel_pin = dst.bel_pin; + +                log_info("%s.%s[%d]/%s/%s (%d, %d) -> %s.%s[%d]/%s/%s\n", +                        IdString(src_tile_type.name).c_str(ctx), +                        src_site_type.c_str(ctx), +                        src_bel_info.site, +                        IdString(src_bel_info.name).c_str(ctx), +                        src_bel_pin.c_str(ctx), +                        delta_x, +                        delta_y, +                        IdString(dst_tile_type.name).c_str(ctx), +                        dst_site_type.c_str(ctx), +                        dst_bel_info.site, +                        IdString(dst_bel_info.name).c_str(ctx), +                        dst_bel_pin.c_str(ctx)); +            } +        } else { +            auto src = key; +            for(const auto & dst_delta : sources.at(key)) { +                auto dst = dst_delta.type_bel_pin; +                auto delta_x = dst_delta.delta_x; +                auto delta_y = dst_delta.delta_y; + +                const TileTypeInfoPOD & src_tile_type = ctx->chip_info->tile_types[src.tile_type]; +                const BelInfoPOD & src_bel_info = src_tile_type.bel_data[src.bel_index]; +                IdString src_site_type = IdString(src_tile_type.site_types[src_bel_info.site]); +                IdString src_bel_pin = src.bel_pin; + +                const TileTypeInfoPOD & dst_tile_type = ctx->chip_info->tile_types[dst.tile_type]; +                const BelInfoPOD & dst_bel_info = dst_tile_type.bel_data[dst.bel_index]; +                IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]); +                IdString dst_bel_pin = dst.bel_pin; + +                log_info("%s.%s[%d]/%s/%s -> %s.%s[%d]/%s/%s  (%d, %d)\n", +                        IdString(src_tile_type.name).c_str(ctx), +                        src_site_type.c_str(ctx), +                        src_bel_info.site, +                        IdString(src_bel_info.name).c_str(ctx), +                        src_bel_pin.c_str(ctx), +                        IdString(dst_tile_type.name).c_str(ctx), +                        dst_site_type.c_str(ctx), +                        dst_bel_info.site, +                        IdString(dst_bel_info.name).c_str(ctx), +                        dst_bel_pin.c_str(ctx), +                        delta_x, +                        delta_y); + +            }          }      }  } @@ -191,53 +459,72 @@ void DedicatedInterconnect::find_dedicated_interconnect() {              wire.tile = bel.tile;              wire.index = bel_data.wires[i]; -            expand_bel(bel, IdString(bel_data.ports[i]), wire); +            expand_sink_bel(bel, IdString(bel_data.ports[i]), wire);          }      } -} -// All legal routes involved at most 2 sites, the source site and the sink -// site.  The source site and sink sites may be the same, but that is not -// dedicated routing, that is intra site routing. -// -// Dedicated routing must leave the sink site, traverse some routing and -// terminate at another site.  Routing that "flys" over a site is expressed as -// a psuedo-pip connected the relevant site pin wires, rather than traversing -// the site. -enum WireNodeState { -    IN_SINK_SITE = 0, -    IN_ROUTING = 1, -    IN_SOURCE_SITE = 2 -}; +    std::unordered_set<TileTypeBelPin> seen_pins; +    for(auto sink_pair : sinks) { +        for(auto src : sink_pair.second) { +            seen_pins.emplace(src.type_bel_pin); +        } +    } -struct WireNode { -    WireId wire; -    WireNodeState state; -    int depth; -}; +    for(BelId bel : ctx->getBels()) { +        const auto & bel_data = bel_info(ctx->chip_info, bel); +        if(bel_data.category != BEL_CATEGORY_LOGIC) { +            continue; +        } +        if(bel_data.synthetic) { +            continue; +        } -// Maximum depth that a dedicate interconnect is considered. -// -// Routing networks with depth <= kMaxDepth is considers a dedicated -// interconnect. -constexpr int kMaxDepth = 20; +        for(size_t i = 0; i < bel_data.num_bel_wires; ++i) { +            if(bel_data.types[i] != PORT_OUT) { +                continue; +            } -void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) { -    NPNR_ASSERT(bel != BelId()); + +            IdString bel_pin(bel_data.ports[i]); + +            TileTypeBelPin type_bel_pin; +            type_bel_pin.tile_type = ctx->chip_info->tiles[bel.tile].type; +            type_bel_pin.bel_index = bel.index; +            type_bel_pin.bel_pin = bel_pin; + +            // Don't visit src pins already handled in the sink expansion! +            if(seen_pins.count(type_bel_pin)) { +                continue; +            } + +            WireId wire; +            wire.tile = bel.tile; +            wire.index = bel_data.wires[i]; + +            expand_source_bel(bel, bel_pin, wire); +        } +    } +} + +void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, WireId sink_wire) { +    NPNR_ASSERT(sink_bel != BelId()); +#ifdef DEBUG_EXPANSION +    log_info("Expanding from %s/%s\n", ctx->nameOfBel(sink_bel), pin.c_str(ctx)); +#endif      std::vector<WireNode> nodes_to_expand; -    const auto & src_wire_data = ctx->wire_info(wire); -    NPNR_ASSERT(src_wire_data.site != -1); +    const auto & sink_wire_data = ctx->wire_info(sink_wire); +    NPNR_ASSERT(sink_wire_data.site != -1);      WireNode wire_node; -    wire_node.wire = wire; +    wire_node.wire = sink_wire;      wire_node.state = IN_SINK_SITE;      wire_node.depth = 0;      nodes_to_expand.push_back(wire_node); -    Loc sink_loc = ctx->getBelLocation(bel); +    Loc sink_loc = ctx->getBelLocation(sink_bel);      std::unordered_set<DeltaTileTypeBelPin> srcs;      while(!nodes_to_expand.empty()) { @@ -254,6 +541,11 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {                  continue;              } +#ifdef DEBUG_EXPANSION +            log_info(" - At wire %s via %s\n", +                    ctx->nameOfWire(wire), ctx->nameOfPip(pip)); +#endif +              WireNode next_node;              next_node.wire = wire;              next_node.depth = node_to_expand.depth += 1; @@ -277,9 +569,12 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {                          break;                      case IN_ROUTING:                          NPNR_ASSERT(wire_data.site != -1); -                        if(wire_data.site == src_wire_data.site) { +                        if(wire.tile == sink_wire.tile && wire_data.site == sink_wire_data.site) {                              // Dedicated routing won't have straight loops,                              // general routing looks like that. +#ifdef DEBUG_EXPANSION +                            log_info(" - Not dedicated site routing because loop!"); +#endif                              return;                          }                          next_node.state = IN_SOURCE_SITE; @@ -307,7 +602,6 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {                  for(BelPin bel_pin : ctx->getWireBelPins(wire)) {                      BelId src_bel = bel_pin.bel;                      auto const & bel_data = bel_info(ctx->chip_info, src_bel); -                    NPNR_ASSERT(bel_data.site != src_wire_data.site);                      if(bel_data.category != BEL_CATEGORY_LOGIC) {                          continue; @@ -319,11 +613,15 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {                          continue;                      } +#ifdef DEBUG_EXPANSION +                    log_info(" - Reached %s/%s\n", ctx->nameOfBel(bel_pin.bel), bel_pin.pin.c_str(ctx)); +#endif +                      Loc src_loc = ctx->getBelLocation(src_bel);                      DeltaTileTypeBelPin delta_type_bel_pin;                      delta_type_bel_pin.delta_x = src_loc.x - sink_loc.x; -                    delta_type_bel_pin.delta_x = src_loc.y - sink_loc.y; +                    delta_type_bel_pin.delta_y = src_loc.y - sink_loc.y;                      delta_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[src_bel.tile].type;                      delta_type_bel_pin.type_bel_pin.bel_index = src_bel.index;                      delta_type_bel_pin.type_bel_pin.bel_pin = bel_pin.pin; @@ -334,11 +632,11 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {      }      TileTypeBelPin type_bel_pin; -    type_bel_pin.tile_type = ctx->chip_info->tiles[bel.tile].type; -    type_bel_pin.bel_index = bel.index; -    type_bel_pin.bel_pin = pin; +    type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type; +    type_bel_pin.bel_index = sink_bel.index; +    type_bel_pin.bel_pin = sink_pin; -    auto result = pins_with_dedicate_interconnect.emplace(type_bel_pin, srcs); +    auto result = sinks.emplace(type_bel_pin, srcs);      if(!result.second) {          // type_bel_pin was already present! Add any new sources from this          // sink type (if any); @@ -348,4 +646,144 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {      }  } +void DedicatedInterconnect::expand_source_bel(BelId src_bel, IdString src_pin, WireId src_wire) { +    NPNR_ASSERT(src_bel != BelId()); +#ifdef DEBUG_EXPANSION +    log_info("Expanding from %s/%s\n", ctx->nameOfBel(src_bel), pin.c_str(ctx)); +#endif + +    std::vector<WireNode> nodes_to_expand; + +    const auto & src_wire_data = ctx->wire_info(src_wire); +    NPNR_ASSERT(src_wire_data.site != -1); + +    WireNode wire_node; +    wire_node.wire = src_wire; +    wire_node.state = IN_SOURCE_SITE; +    wire_node.depth = 0; + +    nodes_to_expand.push_back(wire_node); + +    Loc src_loc = ctx->getBelLocation(src_bel); +    std::unordered_set<DeltaTileTypeBelPin> dsts; + +    while(!nodes_to_expand.empty()) { +        WireNode node_to_expand = nodes_to_expand.back(); +        nodes_to_expand.pop_back(); + +        for(PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) { +            if(ctx->is_pip_synthetic(pip)) { +                continue; +            } + +            WireId wire = ctx->getPipDstWire(pip); +            if(wire == WireId()) { +                continue; +            } + +#ifdef DEBUG_EXPANSION +            log_info(" - At wire %s via %s\n", +                    ctx->nameOfWire(wire), ctx->nameOfPip(pip)); +#endif + +            WireNode next_node; +            next_node.wire = wire; +            next_node.depth = node_to_expand.depth += 1; + +            if(next_node.depth > kMaxDepth) { +                // Dedicated routing should reach sources by kMaxDepth (with +                // tuning). +                // +                // FIXME: Consider removing kMaxDepth and use kMaxSources? +                return; +            } + +            auto const & wire_data = ctx->wire_info(wire); + +            bool expand_node = true; +            if(ctx->is_site_port(pip)) { +                switch(node_to_expand.state) { +                    case IN_SOURCE_SITE: +                        NPNR_ASSERT(wire_data.site == -1); +                        next_node.state = IN_ROUTING; +                        break; +                    case IN_ROUTING: +                        NPNR_ASSERT(wire_data.site != -1); +                        if(wire.tile == src_wire.tile && wire_data.site == src_wire_data.site) { +                            // Dedicated routing won't have straight loops, +                            // general routing looks like that. +#ifdef DEBUG_EXPANSION +                            log_info(" - Not dedicated site routing because loop!"); +#endif +                            return; +                        } +                        next_node.state = IN_SINK_SITE; +                        break; +                    case IN_SINK_SITE: +                        // Once entering a site, do not leave it again. +                        // This path is not a legal route! +                        expand_node = false; +                        break; +                    default: +                        // Unreachable!!! +                        NPNR_ASSERT(false); +                } +            } else { +                next_node.state = node_to_expand.state; +            } + +            if(expand_node) { +                nodes_to_expand.push_back(next_node); +            } else { +                continue; +            } + +            if(next_node.state == IN_SINK_SITE) { +                for(BelPin bel_pin : ctx->getWireBelPins(wire)) { +                    BelId sink_bel = bel_pin.bel; +                    auto const & bel_data = bel_info(ctx->chip_info, sink_bel); + +                    if(bel_data.category != BEL_CATEGORY_LOGIC) { +                        continue; +                    } +                    if(bel_data.synthetic) { +                        continue; +                    } +                    if(ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_IN) { +                        continue; +                    } + +#ifdef DEBUG_EXPANSION +                    log_info(" - Reached %s/%s\n", ctx->nameOfBel(bel_pin.bel), bel_pin.pin.c_str(ctx)); +#endif + +                    Loc sink_loc = ctx->getBelLocation(sink_bel); + +                    DeltaTileTypeBelPin delta_type_bel_pin; +                    delta_type_bel_pin.delta_x = sink_loc.x - src_loc.x; +                    delta_type_bel_pin.delta_y = sink_loc.y - src_loc.y; +                    delta_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type; +                    delta_type_bel_pin.type_bel_pin.bel_index = sink_bel.index; +                    delta_type_bel_pin.type_bel_pin.bel_pin = bel_pin.pin; +                    dsts.emplace(delta_type_bel_pin); +                } +            } +        } +    } + +    TileTypeBelPin type_bel_pin; +    type_bel_pin.tile_type = ctx->chip_info->tiles[src_bel.tile].type; +    type_bel_pin.bel_index = src_bel.index; +    type_bel_pin.bel_pin = src_pin; + +    auto result = sinks.emplace(type_bel_pin, dsts); +    if(!result.second) { +        // type_bel_pin was already present! Add any new sources from this +        // sink type (if any); +        for(auto dst : dsts) { +            result.first->second.emplace(dst); +        } +    } +} +  NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/dedicated_interconnect.h b/fpga_interchange/dedicated_interconnect.h index 5fe61d30..d603039e 100644 --- a/fpga_interchange/dedicated_interconnect.h +++ b/fpga_interchange/dedicated_interconnect.h @@ -108,7 +108,8 @@ struct Context;  struct DedicatedInterconnect {      const Context *ctx; -    std::unordered_map<TileTypeBelPin, std::unordered_set<DeltaTileTypeBelPin>> pins_with_dedicate_interconnect; +    std::unordered_map<TileTypeBelPin, std::unordered_set<DeltaTileTypeBelPin>> sinks; +    std::unordered_map<TileTypeBelPin, std::unordered_set<DeltaTileTypeBelPin>> sources;      void init(const Context *ctx); @@ -123,7 +124,13 @@ struct DedicatedInterconnect {      bool check_routing(          BelId src_bel, IdString src_bel_pin,          BelId dst_bel, IdString dst_bel_pin) const; -    void expand_bel(BelId bel, IdString pin, WireId wire); +    void expand_sink_bel(BelId bel, IdString pin, WireId wire); +    void expand_source_bel(BelId bel, IdString pin, WireId wire); + +    bool is_driver_on_net_valid(BelId driver_bel, +            const CellInfo* cell, IdString driver_port, NetInfo *net) const; +    bool is_sink_on_net_valid(BelId bel, const CellInfo* cell, +            IdString port_name, NetInfo *net) const;  };  NEXTPNR_NAMESPACE_END | 
