diff options
-rw-r--r-- | fpga_interchange/arch.cc | 5 | ||||
-rw-r--r-- | fpga_interchange/arch.h | 14 | ||||
-rw-r--r-- | fpga_interchange/dedicated_interconnect.cc | 351 | ||||
-rw-r--r-- | fpga_interchange/dedicated_interconnect.h | 129 | ||||
-rw-r--r-- | fpga_interchange/examples/ff/Makefile | 8 | ||||
-rw-r--r-- | fpga_interchange/examples/ff/ff.v | 11 | ||||
-rw-r--r-- | fpga_interchange/examples/ff/ff.xdc | 9 | ||||
-rw-r--r-- | fpga_interchange/examples/ff/run.tcl | 14 | ||||
-rw-r--r-- | fpga_interchange/fpga_interchange.cpp | 55 | ||||
-rw-r--r-- | fpga_interchange/main.cc | 11 | ||||
-rw-r--r-- | fpga_interchange/site_router.cc | 6 |
11 files changed, 587 insertions, 26 deletions
diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 4cffd6ca..0955c376 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -195,6 +195,11 @@ Arch::Arch(ArchArgs args) : args(args) default_tags.resize(max_tag_count); } + +void Arch::init() { + dedicated_interconnect.init(getCtx()); +} + // ----------------------------------------------------------------------- std::string Arch::getChipName() const { return chip_info->name.get(); } diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index a5352b60..780382ec 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -29,6 +29,7 @@ #include <iostream> #include "constraints.h" +#include "dedicated_interconnect.h" NEXTPNR_NAMESPACE_BEGIN @@ -772,6 +773,8 @@ struct ArchRanges using BucketBelRangeT = FilteredBelRange; }; +struct DedicatedInterconnect; + struct Arch : ArchAPI<ArchRanges> { boost::iostreams::mapped_file_source blob_file; @@ -783,8 +786,6 @@ struct Arch : ArchAPI<ArchRanges> std::unordered_map<WireId, NetInfo *> wire_to_net; std::unordered_map<PipId, NetInfo *> pip_to_net; - std::unordered_map<WireId, std::pair<int, int>> driving_pip_loc; - std::unordered_map<WireId, NetInfo *> reserved_wires; static constexpr size_t kMaxState = 8; @@ -811,10 +812,12 @@ struct Arch : ArchAPI<ArchRanges> std::vector<SiteRouter> sites; }; + DedicatedInterconnect dedicated_interconnect; std::unordered_map<int32_t, TileStatus> tileStatus; ArchArgs args; Arch(ArchArgs args); + void init(); std::string getChipName() const override; @@ -1236,9 +1239,6 @@ struct Arch : ArchAPI<ArchRanges> NPNR_ASSERT(wire_to_net[dst] == nullptr || wire_to_net[dst] == net); pip_to_net[pip] = net; - std::pair<int, int> loc; - get_tile_x_y(pip.tile, &loc.first, &loc.second); - driving_pip_loc[dst] = loc; wire_to_net[dst] = net; net->wires[dst].pip = pip; @@ -1509,6 +1509,10 @@ struct Arch : ArchAPI<ArchRanges> if (cell == nullptr) { return true; } else { + if(!dedicated_interconnect.isBelLocationValid(bel, cell)) { + return false; + } + if (io_port_types.count(cell->type)) { // FIXME: Probably need to actually constraint io port cell/bel, // but the current BBA emission doesn't support that. This only diff --git a/fpga_interchange/dedicated_interconnect.cc b/fpga_interchange/dedicated_interconnect.cc new file mode 100644 index 00000000..82101fbd --- /dev/null +++ b/fpga_interchange/dedicated_interconnect.cc @@ -0,0 +1,351 @@ +/* + * 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 "nextpnr.h" +#include "log.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +void DedicatedInterconnect::init(const Context *ctx) { + this->ctx = ctx; + + if(ctx->debug) { + log_info("Finding dedicated interconnect!\n"); + } + + find_dedicated_interconnect(); + if(ctx->debug) { + print_dedicated_interconnect(); + } +} + +bool DedicatedInterconnect::check_routing( + BelId src_bel, IdString src_bel_pin, + BelId dst_bel, IdString dst_bel_pin) const { + // FIXME: Implement. + return false; +} + +bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell) const { + NPNR_ASSERT(bel != BelId()); + + Loc bel_loc = ctx->getBelLocation(bel); + + const auto &bel_data = bel_info(ctx->chip_info, bel); + + for(const auto &port_pair : cell->ports) { + IdString port_name = port_pair.first; + NetInfo *net = port_pair.second.net; + if(net == nullptr) { + continue; + } + + // Only check sink BELs. + if(net->driver.cell == cell && net->driver.port == port_name) { + continue; + } + + // This net doesn't have a driver, probably not valid? + NPNR_ASSERT(net->driver.cell != nullptr); + + BelId driver_bel = net->driver.cell->bel; + if(driver_bel == BelId()) { + return true; + } + + const auto &driver_bel_data = bel_info(ctx->chip_info, driver_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 = pins_with_dedicate_interconnect.find(type_bel_pin); + if(iter == pins_with_dedicate_interconnect.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; +} + +void DedicatedInterconnect::print_dedicated_interconnect() const { + log_info("Found %zu sinks with dedicated interconnect\n", pins_with_dedicate_interconnect.size()); + std::vector<TileTypeBelPin> sorted_keys; + for(const auto & sink_to_srcs : pins_with_dedicate_interconnect) { + sorted_keys.push_back(sink_to_srcs.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)); + + } + } +} + +void DedicatedInterconnect::find_dedicated_interconnect() { + 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; + } + + for(size_t i = 0; i < bel_data.num_bel_wires; ++i) { + if(bel_data.types[i] != PORT_IN) { + continue; + } + + WireId wire; + wire.tile = bel.tile; + wire.index = bel_data.wires[i]; + + expand_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 +}; + +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::expand_bel(BelId bel, IdString pin, WireId wire) { + NPNR_ASSERT(bel != BelId()); + + std::vector<WireNode> nodes_to_expand; + + const auto & src_wire_data = ctx->wire_info(wire); + NPNR_ASSERT(src_wire_data.site != -1); + + WireNode wire_node; + wire_node.wire = wire; + wire_node.state = IN_SINK_SITE; + wire_node.depth = 0; + + nodes_to_expand.push_back(wire_node); + + Loc sink_loc = ctx->getBelLocation(bel); + std::unordered_set<DeltaTileTypeBelPin> srcs; + + while(!nodes_to_expand.empty()) { + WireNode node_to_expand = nodes_to_expand.back(); + nodes_to_expand.pop_back(); + + for(PipId pip : ctx->getPipsUphill(node_to_expand.wire)) { + if(ctx->is_pip_synthetic(pip)) { + continue; + } + + WireId wire = ctx->getPipSrcWire(pip); + if(wire == WireId()) { + continue; + } + + 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_SINK_SITE: + NPNR_ASSERT(wire_data.site == -1); + next_node.state = IN_ROUTING; + break; + case IN_ROUTING: + NPNR_ASSERT(wire_data.site != -1); + if(wire_data.site == src_wire_data.site) { + // Dedicated routing won't have straight loops, + // general routing looks like that. + return; + } + next_node.state = IN_SOURCE_SITE; + break; + case IN_SOURCE_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_SOURCE_SITE) { + 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; + } + if(bel_data.synthetic) { + continue; + } + if(ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_OUT) { + continue; + } + + 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.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; + srcs.emplace(delta_type_bel_pin); + } + } + } + } + + 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; + + auto result = pins_with_dedicate_interconnect.emplace(type_bel_pin, srcs); + if(!result.second) { + // type_bel_pin was already present! Add any new sources from this + // sink type (if any); + for(auto src : srcs) { + result.first->second.emplace(src); + } + } +} + +NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/dedicated_interconnect.h b/fpga_interchange/dedicated_interconnect.h new file mode 100644 index 00000000..5fe61d30 --- /dev/null +++ b/fpga_interchange/dedicated_interconnect.h @@ -0,0 +1,129 @@ +/* + * 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. + * + */ + +#ifndef NEXTPNR_H +#error Include "dedicated_interconnect.h" via "nextpnr.h" only. +#endif + +NEXTPNR_NAMESPACE_BEGIN + +struct TileTypeBelPin { + int32_t tile_type; + int32_t bel_index; + IdString bel_pin; + + bool operator < (const TileTypeBelPin &other) const { + if(tile_type >= other.tile_type) { + return false; + } + + if(bel_index >= other.bel_index) { + return false; + } + + return bel_pin < other.bel_pin; + } + + bool operator ==(const TileTypeBelPin &other) const { + return tile_type == other.tile_type && bel_index == other.bel_index && bel_pin == other.bel_pin; + } + bool operator !=(const TileTypeBelPin &other) const { + return tile_type != other.tile_type || bel_index != other.bel_index || bel_pin != other.bel_pin; + } +}; + +struct DeltaTileTypeBelPin { + int32_t delta_x; + int32_t delta_y; + TileTypeBelPin type_bel_pin; + + bool operator ==(const DeltaTileTypeBelPin &other) const { + return delta_x == other.delta_x && delta_y == other.delta_y && type_bel_pin == other.type_bel_pin; + } + bool operator !=(const DeltaTileTypeBelPin &other) const { + return delta_x != other.delta_x || delta_y != other.delta_y || type_bel_pin != other.type_bel_pin; + } +}; + +NEXTPNR_NAMESPACE_END + +template <> struct std::hash<NEXTPNR_NAMESPACE_PREFIX TileTypeBelPin> +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX TileTypeBelPin &type_bel_pin) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, std::hash<int32_t>()(type_bel_pin.tile_type)); + boost::hash_combine(seed, std::hash<int32_t>()(type_bel_pin.bel_index)); + boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(type_bel_pin.bel_pin)); + return seed; + } +}; + +template <> struct std::hash<NEXTPNR_NAMESPACE_PREFIX DeltaTileTypeBelPin> +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DeltaTileTypeBelPin &delta_bel_pin) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, std::hash<int32_t>()(delta_bel_pin.delta_x)); + boost::hash_combine(seed, std::hash<int32_t>()(delta_bel_pin.delta_y)); + boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX TileTypeBelPin>()(delta_bel_pin.type_bel_pin)); + return seed; + } +}; + +NEXTPNR_NAMESPACE_BEGIN + +struct Context; + +// This class models dedicated interconnect present in the given fabric. +// +// Examples of dedicate interconnect: +// - IBUF.O -> ISERDES.I +// - IBUF.O -> IDELAY.I +// - CARRY4.CO[3] -> CARRY4.CIN +// +// Note that CARRY4.CYINIT does not **require** dedicated interconnect, so +// it doesn't qualify. +// +// This class discovers dedicated interconnect by examing the routing graph. +// This discovery make be expensive, and require caching to accelerate +// startup. +struct DedicatedInterconnect { + const Context *ctx; + + std::unordered_map<TileTypeBelPin, std::unordered_set<DeltaTileTypeBelPin>> pins_with_dedicate_interconnect; + + void init(const Context *ctx); + + // Is this BEL placed in a location that is valid based on dedicated + // interconnect? + // + // Note: Only BEL pin sinks are checked. + bool isBelLocationValid(BelId bel, const CellInfo* cell) const; + + void find_dedicated_interconnect(); + void print_dedicated_interconnect() const; + 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); +}; + +NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/examples/ff/Makefile b/fpga_interchange/examples/ff/Makefile new file mode 100644 index 00000000..c6118ff7 --- /dev/null +++ b/fpga_interchange/examples/ff/Makefile @@ -0,0 +1,8 @@ +DESIGN := ff +DESIGN_TOP := top +PACKAGE := csg324 + +include ../template.mk + +build/ff.json: ff.v | build + yosys -c run.tcl diff --git a/fpga_interchange/examples/ff/ff.v b/fpga_interchange/examples/ff/ff.v new file mode 100644 index 00000000..1c271042 --- /dev/null +++ b/fpga_interchange/examples/ff/ff.v @@ -0,0 +1,11 @@ +module top(input clk, input d, input r, output reg q); + +always @(posedge clk) +begin + if(r) + q <= 1'b0; + else + q <= d; +end + +endmodule diff --git a/fpga_interchange/examples/ff/ff.xdc b/fpga_interchange/examples/ff/ff.xdc new file mode 100644 index 00000000..3c132f1d --- /dev/null +++ b/fpga_interchange/examples/ff/ff.xdc @@ -0,0 +1,9 @@ +set_property PACKAGE_PIN P17 [get_ports clk] +set_property PACKAGE_PIN N15 [get_ports d] +set_property PACKAGE_PIN N16 [get_ports r] +set_property PACKAGE_PIN M17 [get_ports q] + +set_property IOSTANDARD LVCMOS33 [get_ports clk] +set_property IOSTANDARD LVCMOS33 [get_ports d] +set_property IOSTANDARD LVCMOS33 [get_ports r] +set_property IOSTANDARD LVCMOS33 [get_ports q] diff --git a/fpga_interchange/examples/ff/run.tcl b/fpga_interchange/examples/ff/run.tcl new file mode 100644 index 00000000..726d86eb --- /dev/null +++ b/fpga_interchange/examples/ff/run.tcl @@ -0,0 +1,14 @@ +yosys -import + +read_verilog ff.v + +synth_xilinx -nolutram -nowidelut -nosrl -nocarry -nodsp + +# opt_expr -undriven makes sure all nets are driven, if only by the $undef +# net. +opt_expr -undriven +opt_clean + +setundef -zero -params + +write_json build/ff.json diff --git a/fpga_interchange/fpga_interchange.cpp b/fpga_interchange/fpga_interchange.cpp index ad1dd76d..fd57e09c 100644 --- a/fpga_interchange/fpga_interchange.cpp +++ b/fpga_interchange/fpga_interchange.cpp @@ -311,9 +311,6 @@ void FpgaInterchange::write_physical_netlist(const Context * ctx, const std::str for(auto & cell_name : placed_cells) { const CellInfo & cell = *ctx->cells.at(cell_name); - if(ctx->io_port_types.count(cell.type)) { - continue; - } if(ctx->is_bel_synthetic(cell.bel)) { continue; } @@ -321,15 +318,14 @@ void FpgaInterchange::write_physical_netlist(const Context * ctx, const std::str number_placements += 1; } + std::vector<IdString> ports; + 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; - } if(ctx->is_bel_synthetic(cell.bel)) { continue; } @@ -351,7 +347,13 @@ void FpgaInterchange::write_physical_netlist(const Context * ctx, const std::str auto placement = *placement_iter++; placement.setCellName(strings.get_index(cell.name.str(ctx))); - placement.setType(strings.get_index(cell.type.str(ctx))); + if(ctx->io_port_types.count(cell.type)) { + // Always mark IO ports as type <PORT>. + placement.setType(strings.get_index("<PORT>")); + ports.push_back(cell.name); + } else { + 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)); @@ -359,27 +361,38 @@ void FpgaInterchange::write_physical_netlist(const Context * ctx, const std::str placement.setIsBelFixed(cell.belStrength >= STRENGTH_FIXED); placement.setIsSiteFixed(cell.belStrength >= STRENGTH_FIXED); - size_t pin_count = 0; - for(const auto & pin : cell.cell_bel_pins) { - pin_count += pin.second.size(); - } + if(!ctx->io_port_types.count(cell.type)) { + // Don't emit pin map for ports. + 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(); + 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 & 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))); + 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 phys_cells = phys_netlist.initPhysCells(ports.size()); + auto phys_cells_iter = phys_cells.begin(); + for(IdString port : ports) { + auto phys_cell = *phys_cells_iter++; + phys_cell.setCellName(strings.get_index(port.str(ctx))); + phys_cell.setPhysType(PhysicalNetlist::PhysNetlist::PhysCellType::PORT); + } + auto nets = phys_netlist.initPhysNets(ctx->nets.size()); auto net_iter = nets.begin(); for(auto & net_pair : ctx->nets) { diff --git a/fpga_interchange/main.cc b/fpga_interchange/main.cc index 5a49cbdc..958f1d95 100644 --- a/fpga_interchange/main.cc +++ b/fpga_interchange/main.cc @@ -70,6 +70,7 @@ void FpgaInterchangeCommandHandler::customBitstream(Context *ctx) std::unique_ptr<Context> FpgaInterchangeCommandHandler::createContext(std::unordered_map<std::string, Property> &values) { auto start = std::chrono::high_resolution_clock::now(); + ArchArgs chipArgs; if (!vm.count("chipdb")) { log_error("chip database binary must be provided\n"); @@ -81,6 +82,16 @@ std::unique_ptr<Context> FpgaInterchangeCommandHandler::createContext(std::unord auto ctx = std::unique_ptr<Context>(new Context(chipArgs)); + if (vm.count("verbose")) { + ctx->verbose = true; + } + if (vm.count("debug")) { + ctx->verbose = true; + ctx->debug = true; + } + + ctx->init(); + if (vm.count("netlist")) { ctx->read_logical_netlist(vm["netlist"].as<std::string>()); } diff --git a/fpga_interchange/site_router.cc b/fpga_interchange/site_router.cc index 09e01507..a22dfcd3 100644 --- a/fpga_interchange/site_router.cc +++ b/fpga_interchange/site_router.cc @@ -260,6 +260,12 @@ struct SiteInformation if (!result.second && result.first->second != net) { // Conflict, this wire is already in use and it's not // doesn't match! + if(verbose_site_router(ctx)) { + log_info("Cannot select route because net %s != net %s\n", + result.first->second->name.c_str(ctx), + net->name.c_str(ctx)); + } + return false; } |