diff options
-rw-r--r-- | .github/workflows/interchange_ci.yml | 2 | ||||
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | common/idstringlist.h | 2 | ||||
-rw-r--r-- | common/placer_heap.cc | 1 | ||||
-rw-r--r-- | common/timing.cc | 7 | ||||
-rw-r--r-- | fpga_interchange/arch.cc | 27 | ||||
-rw-r--r-- | fpga_interchange/arch.h | 5 | ||||
-rw-r--r-- | fpga_interchange/arch_pack_clusters.cc | 7 | ||||
-rw-r--r-- | fpga_interchange/arch_pack_io.cc | 9 | ||||
-rw-r--r-- | fpga_interchange/arch_place_constr.cc | 106 | ||||
-rw-r--r-- | fpga_interchange/chipdb.h | 3 | ||||
-rw-r--r-- | fpga_interchange/dedicated_interconnect.cc | 74 | ||||
-rw-r--r-- | fpga_interchange/site_router.cc | 11 | ||||
-rw-r--r-- | fpga_interchange/xdc.cc | 81 | ||||
-rw-r--r-- | gowin/CMakeLists.txt | 2 | ||||
-rw-r--r-- | gowin/arch.cc | 40 | ||||
-rw-r--r-- | gowin/main.cc | 12 | ||||
-rw-r--r-- | gui/application.cc | 68 | ||||
-rw-r--r-- | ice40/pack.cc | 9 |
20 files changed, 402 insertions, 73 deletions
diff --git a/.github/workflows/interchange_ci.yml b/.github/workflows/interchange_ci.yml index d7dca9da..4dec38b2 100644 --- a/.github/workflows/interchange_ci.yml +++ b/.github/workflows/interchange_ci.yml @@ -114,7 +114,7 @@ jobs: env: RAPIDWRIGHT_PATH: ${{ github.workspace }}/RapidWright PYTHON_INTERCHANGE_PATH: ${{ github.workspace }}/python-fpga-interchange - PYTHON_INTERCHANGE_TAG: v0.0.15 + PYTHON_INTERCHANGE_TAG: v0.0.17 PRJOXIDE_REVISION: 1bf30dee9c023c4c66cfc44fd0bc28addd229c89 DEVICE: ${{ matrix.device }} run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 58b0fead..78c1caa1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -168,6 +168,11 @@ if (BUILD_GUI AND NOT BUILD_PYTHON) message(FATAL_ERROR "GUI requires Python to build") endif() +if (BUILD_GUI) + # For higher quality backtraces + set(CMAKE_ENABLE_EXPORTS ON) +endif() + find_package(PythonInterp 3.5 REQUIRED) if (BUILD_PYTHON) # TODO: sensible minimum Python version @@ -118,10 +118,10 @@ Nexus support is currently experimental, and has only been tested with engineeri ### nextpnr-gowin -For Gowin support, install [Project Apicula](https://github.com/YosysHQ/apicula). If a virtualenv is used, the python paths need to be provided as follows: +For Gowin support, install [Project Apicula](https://github.com/YosysHQ/apicula). If a virtualenv is used, the path to `gowin_bba` needs to be provided as follows: ``` -cmake . -DARCH=gowin -DPYTHON_EXECUTABLE=path -DGOWIN_BBA_EXECUTABLE=path +cmake . -DARCH=gowin -DGOWIN_BBA_EXECUTABLE=path make -j$(nproc) sudo make install ``` diff --git a/common/idstringlist.h b/common/idstringlist.h index 4af9801a..fd57540b 100644 --- a/common/idstringlist.h +++ b/common/idstringlist.h @@ -35,7 +35,7 @@ struct IdStringList { SSOArray<IdString, 4> ids; - IdStringList(){}; + IdStringList() : ids(1, IdString()){}; explicit IdStringList(size_t n) : ids(n, IdString()){}; explicit IdStringList(IdString id) : ids(1, id){}; template <typename Tlist> explicit IdStringList(const Tlist &list) : ids(list){}; diff --git a/common/placer_heap.cc b/common/placer_heap.cc index dbb8d36c..f8385cef 100644 --- a/common/placer_heap.cc +++ b/common/placer_heap.cc @@ -401,6 +401,7 @@ class HeAPPlacer // Initial constraints placer for (auto &cell_entry : ctx->cells) { CellInfo *cell = cell_entry.second.get(); + auto loc = cell->attrs.find(ctx->id("BEL")); if (loc != cell->attrs.end()) { std::string loc_name = loc->second.as_string(); diff --git a/common/timing.cc b/common/timing.cc index 30054e83..6a91b6e5 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -1363,7 +1363,12 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p std::vector<unsigned> bins(num_bins); unsigned max_freq = 0; for (const auto &i : slack_histogram) { - auto &bin = bins[(i.first - min_slack) / bin_size]; + int bin_idx = int((i.first - min_slack) / bin_size); + if (bin_idx < 0) + bin_idx = 0; + else if (bin_idx >= int(num_bins)) + bin_idx = num_bins - 1; + auto &bin = bins.at(bin_idx); bin += i.second; max_freq = std::max(max_freq, bin); } diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index 3b0572fa..a39f49e6 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -787,6 +787,7 @@ bool Arch::place() getCtx()->check(); #endif + place_constraints(); place_globals(); std::string placer = str_or_default(settings, id("placer"), defaultPlacer); @@ -1751,8 +1752,12 @@ bool Arch::checkPipAvailForNet(PipId pip, NetInfo *net) const } } - // If this pip is a route-though, make sure all of the route-though - // wires are unbound. + // If this pip is a route-though, make sure all of the route-though are valid. + // A route-through is valid if: + // - none of the pseudo-pip wires are bound to a net + // - the pseudo pip wires are bound to the same net and there are no + // lut elements in the tile type: this to prevent the router from choosing + // a pseudo-pip on the same LUT and on a different input pin for the same net. const TileTypeInfoPOD &tile_type = loc_info(chip_info, pip); const PipInfoPOD &pip_data = tile_type.pip_data[pip.index]; WireId wire; @@ -1763,14 +1768,20 @@ bool Arch::checkPipAvailForNet(PipId pip, NetInfo *net) const NPNR_ASSERT(dst != wire); NetInfo *other_net = getConflictingWireNet(wire); - if (other_net != nullptr && other_net != net) { + bool is_null_net = other_net == nullptr; + if (!is_null_net) { + bool is_same_net = other_net == net; + bool tile_has_luts = tile_type.lut_elements.size() > 0; + if (!is_same_net || tile_has_luts) { #ifdef DEBUG_BINDING - if (getCtx()->verbose) { - log_info("Pip %s is not available because wire %s is tied to net %s\n", getCtx()->nameOfPip(pip), - getCtx()->nameOfWire(wire), other_net->name.c_str(getCtx())); - } + if (getCtx()->verbose) + log_info("Pip %s is not available because wire %s is tied to a different net " + "(other net: %s - orig net: %s) or the pseudo pip may traverses LUTs\n", + getCtx()->nameOfPip(pip), getCtx()->nameOfWire(wire), other_net->name.c_str(getCtx()), + net == nullptr ? "NULL net" : net->name.c_str(getCtx())); #endif - return false; + return false; + } } } diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 36f6a7dc..0fb4f462 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -722,6 +722,9 @@ struct Arch : ArchAPI<ArchRanges> void prepare_cluster(const ClusterPOD *cluster, uint32_t index); dict<ClusterId, Cluster> clusters; + // User constraints + void place_constraints(); + void decode_lut_cells(); const GlobalCellPOD *global_cell_info(IdString cell_type) const; @@ -857,7 +860,7 @@ struct Arch : ArchAPI<ArchRanges> const CellInfo *cell = tile_status.boundcells[bel.index]; if (cell != nullptr) { - if (cell->cluster == ClusterId() && !dedicated_interconnect.isBelLocationValid(bel, cell)) + if (!dedicated_interconnect.isBelLocationValid(bel, cell)) return false; if (io_port_types.count(cell->type)) { diff --git a/fpga_interchange/arch_pack_clusters.cc b/fpga_interchange/arch_pack_clusters.cc index 18162aa4..f4e50233 100644 --- a/fpga_interchange/arch_pack_clusters.cc +++ b/fpga_interchange/arch_pack_clusters.cc @@ -193,6 +193,8 @@ bool Arch::getClusterPlacement(ClusterId cluster, BelId root_bel, const Context *ctx = getCtx(); const Cluster &packed_cluster = clusters.at(cluster); + auto &cluster_data = cluster_info(chip_info, packed_cluster.index); + CellInfo *root_cell = getClusterRootCell(cluster); if (!ctx->isValidBelForCellType(root_cell->type, root_bel)) return false; @@ -205,8 +207,6 @@ bool Arch::getClusterPlacement(ClusterId cluster, BelId root_bel, next_bel = root_bel; } else { // Find next chained cluster node - auto &cluster_data = cluster_info(chip_info, packed_cluster.index); - IdString next_bel_pin(cluster_data.chainable_ports[0].bel_source); WireId next_bel_pin_wire = ctx->getBelPinWire(next_bel, next_bel_pin); next_bel = BelId(); @@ -256,7 +256,8 @@ bool Arch::getClusterPlacement(ClusterId cluster, BelId root_bel, WireId bel_pin_wire = ctx->getBelPinWire(next_bel, bel_pin); ExpansionDirection direction = port_type == PORT_IN ? CLUSTER_UPHILL_DIR : CLUSTER_DOWNHILL_DIR; - pool<BelId> cluster_bels = find_cluster_bels(ctx, bel_pin_wire, direction); + pool<BelId> cluster_bels = + find_cluster_bels(ctx, bel_pin_wire, direction, (bool)cluster_data.out_of_site_clusters); if (cluster_bels.size() == 0) continue; diff --git a/fpga_interchange/arch_pack_io.cc b/fpga_interchange/arch_pack_io.cc index 19d8cece..38b619a3 100644 --- a/fpga_interchange/arch_pack_io.cc +++ b/fpga_interchange/arch_pack_io.cc @@ -55,8 +55,6 @@ bool search_routing_for_placement(Arch *arch, WireId start_wire, CellInfo *cell, WireId dst = downhill ? arch->getPipDstWire(pip) : arch->getPipSrcWire(pip); if (already_visited.count(dst)) return; - if (!arch->is_site_wire(dst) && arch->get_wire_category(dst) == WIRE_CAT_GENERAL) - return; // this pass only considers dedicated routing visit_queue.push(dst); already_visited.insert(dst); }; @@ -83,6 +81,7 @@ void Arch::place_iobufs(WireId pad_wire, NetInfo *net, if (ctx->verbose) log_info("Placed IO cell %s:%s at %s.\n", ctx->nameOf(cell_port.first), ctx->nameOf(cell_port.first->type), ctx->nameOfBel(cell_port.first->bel)); + placed_cells->insert(cell_port.first); } } @@ -246,7 +245,10 @@ void Arch::pack_ports() } if (possible_site_types.empty()) { - log_error("Port '%s' has no possible site types!\n", port_name.c_str(getCtx())); + if (getCtx()->verbose) + log_info("Port '%s' has no possible site types, falling back to all types!\n", + port_name.c_str(getCtx())); + possible_site_types = package_pin_site_types; } if (getCtx()->verbose) { @@ -315,6 +317,7 @@ void Arch::pack_ports() for (CellInfo *cell : placed_cells) { NPNR_ASSERT(cell->bel != BelId()); if (!isBelLocationValid(cell->bel)) { + explain_bel_status(cell->bel); log_error("Tightly bound BEL %s was not valid!\n", nameOfBel(cell->bel)); } } diff --git a/fpga_interchange/arch_place_constr.cc b/fpga_interchange/arch_place_constr.cc new file mode 100644 index 00000000..3af48656 --- /dev/null +++ b/fpga_interchange/arch_place_constr.cc @@ -0,0 +1,106 @@ +/* + * 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 "log.h" +#include "nextpnr.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +void Arch::place_constraints() +{ + std::vector<std::pair<IdString, BelId>> constrained_cells; + for (auto &cell_pair : cells) { + CellInfo *cell = cell_pair.second.get(); + auto loc_constr = cell->attrs.find(id("LOC")); + auto bel_constr = cell->attrs.find(id("BEL")); + + if (loc_constr == cell->attrs.end() || bel_constr == cell->attrs.end()) + continue; + + IdString loc_name = id(loc_constr->second.as_string()); + IdString bel_name = id(bel_constr->second.as_string()); + + BelId bel; + for (size_t i = 0; i < chip_info->tiles.size(); ++i) { + const auto &tile = chip_info->tiles[i]; + bool site_found = false; + for (size_t j = 0; j < tile.sites.size(); ++j) { + auto &site_data = chip_info->sites[tile.sites[j]]; + if (loc_name == id(site_data.site_name.get())) { + site_found = true; + break; + } + } + + if (!site_found) + continue; + + const auto &tile_type = chip_info->tile_types[tile.type]; + bool bel_found = false; + for (size_t j = 0; j < tile_type.bel_data.size(); ++j) { + const BelInfoPOD &bel_data = tile_type.bel_data[j]; + if (bel_name == IdString(bel_data.name)) { + bel.tile = i; + bel.index = j; + bel_found = true; + break; + } + } + + if (bel_found) + break; + else + log_error("No bel found for user constraint \'%s/%s\' for cell \'%s\'\n", loc_name.c_str(getCtx()), + bel_name.c_str(getCtx()), cell->name.c_str(getCtx())); + } + + if (!isValidBelForCellType(cell->type, bel)) + log_error("Bel \'%s\' is invalid for cell \'%s\' (%s)\n", nameOfBel(bel), cell->name.c_str(getCtx()), + cell->type.c_str(getCtx())); + + auto bound_cell = getBoundBelCell(bel); + if (bound_cell) + log_error("Cell \'%s\' cannot be bound to bel \'%s\' " + "since it is already bound to cell \'%s\'\n", + cell->name.c_str(getCtx()), nameOfBel(bel), bound_cell->name.c_str(getCtx())); + + bindBel(bel, cell, STRENGTH_USER); + + cell->attrs.erase(id("BEL")); + constrained_cells.emplace_back(cell->name, bel); + } + + if (constrained_cells.empty()) + return; + + log_info("Cell placed via user constraints:\n"); + for (auto cell_bel : constrained_cells) { + IdString cell_name = cell_bel.first; + BelId bel = cell_bel.second; + + if (!isBelLocationValid(bel)) + log_error(" - Bel \'%s\' is not valid for cell \'%s\'\n", nameOfBel(bel), cell_name.c_str(getCtx())); + + log_info(" - %s placed at %s\n", cell_name.c_str(getCtx()), nameOfBel(cell_bel.second)); + } +} + +NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/chipdb.h b/fpga_interchange/chipdb.h index fde35e7f..9ebbc1f3 100644 --- a/fpga_interchange/chipdb.h +++ b/fpga_interchange/chipdb.h @@ -34,7 +34,7 @@ NEXTPNR_NAMESPACE_BEGIN * kExpectedChipInfoVersion */ -static constexpr int32_t kExpectedChipInfoVersion = 11; +static constexpr int32_t kExpectedChipInfoVersion = 13; // Flattened site indexing. // @@ -421,6 +421,7 @@ NPNR_PACKED_STRUCT(struct ClusterPOD { RelSlice<uint32_t> root_cell_types; RelSlice<ChainablePortPOD> chainable_ports; RelSlice<ClusterCellPortPOD> cluster_cells_map; + uint32_t out_of_site_clusters; }); NPNR_PACKED_STRUCT(struct ChipInfoPOD { diff --git a/fpga_interchange/dedicated_interconnect.cc b/fpga_interchange/dedicated_interconnect.cc index 1254b367..56b6b706 100644 --- a/fpga_interchange/dedicated_interconnect.cc +++ b/fpga_interchange/dedicated_interconnect.cc @@ -39,6 +39,12 @@ enum WireNodeState IN_SOURCE_SITE = 2 }; +enum ExpansionDirection +{ + EXPAND_DOWNHILL = 0, + EXPAND_UPHILL = 1 +}; + struct WireNode { WireId wire; @@ -52,6 +58,50 @@ struct WireNode // interconnect. constexpr int kMaxDepth = 6; +static uint32_t get_num_pips(const Context *ctx, WireId wire, ExpansionDirection direction) +{ + uint32_t num_pips = 0; + + if (direction == EXPAND_DOWNHILL) { + for (PipId pip : ctx->getPipsDownhill(wire)) { + auto &pip_data = pip_info(ctx->chip_info, pip); + if (pip_data.pseudo_cell_wires.size() > 0) + continue; + + if (ctx->getPipDstWire(pip) == WireId()) + continue; + + if (ctx->is_pip_synthetic(pip)) + continue; + + if (ctx->is_site_port(pip)) + continue; + + num_pips++; + } + } else { + NPNR_ASSERT(direction == EXPAND_UPHILL); + for (PipId pip : ctx->getPipsUphill(wire)) { + auto &pip_data = pip_info(ctx->chip_info, pip); + if (pip_data.pseudo_cell_wires.size() > 0) + continue; + + if (ctx->getPipSrcWire(pip) == WireId()) + continue; + + if (ctx->is_pip_synthetic(pip)) + continue; + + if (ctx->is_site_port(pip)) + continue; + + num_pips++; + } + } + + return num_pips; +} + void DedicatedInterconnect::init(const Context *ctx) { this->ctx = ctx; @@ -99,6 +149,16 @@ bool DedicatedInterconnect::check_routing(BelId src_bel, IdString src_bel_pin, B WireNode node_to_expand = nodes_to_expand.back(); nodes_to_expand.pop_back(); + auto num_pips = get_num_pips(ctx, node_to_expand.wire, EXPAND_DOWNHILL); + + // Usually, dedicated interconnects do not have more than one PIPs in the out-of-site + if (node_to_expand.depth > 1 && node_to_expand.state == IN_ROUTING && num_pips > 1) { + if (ctx->verbose) + log_info("Wire %s is on a non-dedicated path (number of pips %d)\n", + ctx->nameOfWire(node_to_expand.wire), num_pips); + continue; + } + for (PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) { if (ctx->is_pip_synthetic(pip)) { continue; @@ -147,7 +207,7 @@ bool DedicatedInterconnect::check_routing(BelId src_bel, IdString src_bel_pin, B #ifdef DEBUG_EXPANSION log_info(" - Not dedicated site routing because loop!"); #endif - return false; + continue; } next_node.state = IN_SINK_SITE; break; @@ -365,6 +425,10 @@ bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo *cell) continue; } + if (ctx->io_port_types.count(net->driver.cell->type)) { + continue; + } + // 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)) { @@ -394,15 +458,19 @@ void DedicatedInterconnect::explain_bel_status(BelId bel, const CellInfo *cell) // This net doesn't have a driver, probably not valid? NPNR_ASSERT(net->driver.cell != nullptr); + if (ctx->io_port_types.count(net->driver.cell->type)) { + continue; + } + // 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)) { - log_info("Driver %s/%s is not valid on net '%s'", cell->name.c_str(ctx), port_name.c_str(ctx), + log_info("Driver %s/%s is not valid on net '%s'\n", cell->name.c_str(ctx), port_name.c_str(ctx), net->name.c_str(ctx)); } } else { if (!is_sink_on_net_valid(bel, cell, port_name, net)) { - log_info("Sink %s/%s is not valid on net '%s'", cell->name.c_str(ctx), port_name.c_str(ctx), + log_info("Sink %s/%s is not valid on net '%s'\n", cell->name.c_str(ctx), port_name.c_str(ctx), net->name.c_str(ctx)); } } diff --git a/fpga_interchange/site_router.cc b/fpga_interchange/site_router.cc index 820b8d68..947081f4 100644 --- a/fpga_interchange/site_router.cc +++ b/fpga_interchange/site_router.cc @@ -862,6 +862,8 @@ static void apply_constant_routing(Context *ctx, const SiteArch &site_arch, NetI SiteWire wire = user; PipId inverting_pip; + PhysicalNetlist::PhysNetlist::NetType pref_const = PhysicalNetlist::PhysNetlist::NetType::SIGNAL; + while (wire != site_net->driver) { SitePip pip = site_net->wires.at(wire).pip; NPNR_ASSERT(site_arch.getPipDstWire(pip) == wire); @@ -884,9 +886,16 @@ static void apply_constant_routing(Context *ctx, const SiteArch &site_arch, NetI } wire = site_arch.getPipSrcWire(pip); + pref_const = site_arch.prefered_constant_net_type(pip); } - if (!is_path_inverting && !path_can_invert) { + bool is_pref_const = true; + if (pref_const == PhysicalNetlist::PhysNetlist::NetType::VCC && net == gnd_net) + is_pref_const = false; + else if (pref_const == PhysicalNetlist::PhysNetlist::NetType::GND && net == vcc_net) + is_pref_const = false; + + if (!is_path_inverting && (!path_can_invert || is_pref_const)) { // This routing is boring, use base logic. apply_simple_routing(ctx, site_arch, net, site_net, user); continue; diff --git a/fpga_interchange/xdc.cc b/fpga_interchange/xdc.cc index ca1340b5..5f43ed3c 100644 --- a/fpga_interchange/xdc.cc +++ b/fpga_interchange/xdc.cc @@ -31,7 +31,7 @@ NEXTPNR_NAMESPACE_BEGIN -static int port_set_from_any(Tcl_Interp *interp, Tcl_Obj *objPtr) { return TCL_ERROR; } +static int obj_set_from_any(Tcl_Interp *interp, Tcl_Obj *objPtr) { return TCL_ERROR; } static void set_tcl_obj_string(Tcl_Obj *objPtr, const std::string &s) { @@ -55,12 +55,21 @@ static void port_update_string(Tcl_Obj *objPtr) set_tcl_obj_string(objPtr, port_name); } -static void port_dup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr) +static void cell_update_string(Tcl_Obj *objPtr) +{ + const Context *ctx = static_cast<const Context *>(objPtr->internalRep.twoPtrValue.ptr1); + CellInfo *cell_info = static_cast<CellInfo *>(objPtr->internalRep.twoPtrValue.ptr2); + + std::string cell_name = cell_info->name.str(ctx); + set_tcl_obj_string(objPtr, cell_name); +} + +static void obj_dup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr) { dupPtr->internalRep.twoPtrValue = srcPtr->internalRep.twoPtrValue; } -static void port_free(Tcl_Obj *objPtr) {} +static void obj_free(Tcl_Obj *objPtr) {} static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) { @@ -71,7 +80,11 @@ static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s) } static Tcl_ObjType port_object = { - "port", port_free, port_dup, port_update_string, port_set_from_any, + "port", obj_free, obj_dup, port_update_string, obj_set_from_any, +}; + +static Tcl_ObjType cell_object = { + "cell", obj_free, obj_dup, cell_update_string, obj_set_from_any, }; static int get_ports(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) @@ -106,6 +119,38 @@ static int get_ports(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CON } } +static int get_cells(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + const Context *ctx = static_cast<const Context *>(data); + if (objc == 1) { + // Return list of all ports. + Tcl_SetStringResult(interp, "Unimplemented"); + return TCL_ERROR; + } else if (objc == 2) { + const char *arg0 = Tcl_GetString(objv[1]); + IdString cell_name = ctx->id(arg0); + + auto iter = ctx->cells.find(cell_name); + if (iter == ctx->cells.end()) { + Tcl_SetStringResult(interp, "Could not find cell " + cell_name.str(ctx)); + return TCL_ERROR; + } + + Tcl_Obj *result = Tcl_NewObj(); + result->typePtr = &cell_object; + result->internalRep.twoPtrValue.ptr1 = (void *)(ctx); + result->internalRep.twoPtrValue.ptr2 = (void *)(iter->second.get()); + + result->bytes = nullptr; + cell_update_string(result); + + Tcl_SetObjResult(interp, result); + return TCL_OK; + } else { + return TCL_ERROR; + } +} + static int set_property(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { // set_property <property> <value> <object> @@ -118,18 +163,28 @@ static int set_property(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj * const char *value = Tcl_GetString(objv[2]); const Tcl_Obj *object = objv[3]; - if (object->typePtr != &port_object) { - Tcl_SetStringResult(interp, "Only port objects are handled right now!"); + if (object->typePtr == &port_object) { + const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1); + PortInfo *port_info = static_cast<PortInfo *>(object->internalRep.twoPtrValue.ptr2); + NPNR_ASSERT(port_info->net != nullptr); + CellInfo *cell = ctx->port_cells.at(port_info->name); + + cell->attrs[ctx->id(property)] = Property(value); + } else if (object->typePtr == &cell_object) { + const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1); + CellInfo *cell = static_cast<CellInfo *>(object->internalRep.twoPtrValue.ptr2); + + cell->attrs[ctx->id(property)] = Property(value); + } else { return TCL_ERROR; } - const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1); - PortInfo *port_info = static_cast<PortInfo *>(object->internalRep.twoPtrValue.ptr2); - NPNR_ASSERT(port_info->net != nullptr); - CellInfo *cell = ctx->port_cells.at(port_info->name); - - cell->attrs[ctx->id(property)] = Property(value); + return TCL_OK; +} +static int create_clock(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) +{ + // FIXME: add implementation return TCL_OK; } @@ -150,7 +205,9 @@ TclInterp::TclInterp(Context *ctx) " }\n" "}") == TCL_OK); Tcl_CreateObjCommand(interp, "get_ports", get_ports, ctx, nullptr); + Tcl_CreateObjCommand(interp, "get_cells", get_cells, ctx, nullptr); Tcl_CreateObjCommand(interp, "set_property", set_property, ctx, nullptr); + Tcl_CreateObjCommand(interp, "create_clock", create_clock, ctx, nullptr); } TclInterp::~TclInterp() { Tcl_DeleteInterp(interp); } diff --git a/gowin/CMakeLists.txt b/gowin/CMakeLists.txt index 5d70cd32..a356a84b 100644 --- a/gowin/CMakeLists.txt +++ b/gowin/CMakeLists.txt @@ -12,8 +12,6 @@ message(STATUS "gowin_bba executable: ${GOWIN_BBA_EXECUTABLE}") if(DEFINED GOWIN_CHIPDB) add_custom_target(chipdb-gowin-bbas ALL) else() - find_package(PythonInterp 3.6 REQUIRED) - # shared among all families set(SERIALIZE_CHIPDBS TRUE CACHE BOOL "Serialize device data preprocessing to minimize memory use") diff --git a/gowin/arch.cc b/gowin/arch.cc index e8a14522..e4dce329 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -18,6 +18,7 @@ * */ +#include <boost/algorithm/string.hpp> #include <iostream> #include <math.h> #include <regex> @@ -483,37 +484,44 @@ DelayQuad Arch::getWireTypeDelay(IdString wire) void Arch::read_cst(std::istream &in) { - std::regex iobre = std::regex("IO_LOC +\"([^\"]+)\" +([^ ;]+);"); + std::regex iobre = std::regex("IO_LOC +\"([^\"]+)\" +([^ ;]+) *;.*"); + std::regex portre = std::regex("IO_PORT +\"([^\"]+)\" +([^ =;]+=[^ =;]+) *;.*"); std::smatch match; std::string line; + bool io_loc; while (!in.eof()) { std::getline(in, line); + io_loc = true; if (!std::regex_match(line, match, iobre)) { - // empty line or comment - if (line.empty() || line.rfind("//", 0) == 0) { - continue; + if (std::regex_match(line, match, portre)) { + io_loc = false; } else { - log_warning("Invalid constraint: %s\n", line.c_str()); + if ((!line.empty()) && (line.rfind("//", 0) == std::string::npos)) { + log_warning("Invalid constraint: %s\n", line.c_str()); + } continue; } } - // std::cout << match[1] << " " << match[2] << std::endl; + IdString net = id(match[1]); - IdString pinname = id(match[2]); - const PairPOD *belname = pairLookup(package->pins.get(), package->num_pins, pinname.index); - if (belname == nullptr) - log_error("Pin %s not found\n", pinname.c_str(this)); - // BelId bel = getBelByName(belname->src_id); - // for (auto cell : sorted(cells)) { - // std::cout << cell.first.str(this) << std::endl; - // } auto it = cells.find(net); if (it == cells.end()) { log_info("Cell %s not found\n", net.c_str(this)); continue; } - std::string bel = IdString(belname->src_id).str(this); - it->second->attrs[IdString(ID_BEL)] = bel; + if (io_loc) { // IO_LOC name pin + IdString pinname = id(match[2]); + const PairPOD *belname = pairLookup(package->pins.get(), package->num_pins, pinname.index); + if (belname == nullptr) + log_error("Pin %s not found\n", pinname.c_str(this)); + std::string bel = IdString(belname->src_id).str(this); + it->second->attrs[IdString(ID_BEL)] = bel; + } else { // IO_PORT attr=value + std::string attr = "&"; + attr += match[2]; + boost::algorithm::to_upper(attr); + it->second->attrs[id(attr)] = 1; + } } } diff --git a/gowin/main.cc b/gowin/main.cc index 01fcf25b..a56cc8e8 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -62,10 +62,16 @@ std::unique_ptr<Context> GowinCommandHandler::createContext(dict<std::string, Pr } ArchArgs chipArgs; char buf[36]; - snprintf(buf, 36, "GW1N%s-%s%s", match[1].str().c_str(), match[3].str().c_str(), - match[4].str().c_str()); + snprintf(buf, 36, "GW1N%s-%s%s", match[1].str().c_str(), match[3].str().c_str(), match[4].str().c_str()); chipArgs.device = buf; - snprintf(buf, 36, "GW1N%s-%s", match[1].str().c_str(), match[3].str().c_str()); + // GW1N and GW1NR variants share the same database. + // Most Gowin devices are a System in Package with some SDRAM wirebonded to a GPIO bank. + // However, it appears that the S series with embedded ARM core are unique silicon. + if (match[1].str() == "S") { + snprintf(buf, 36, "GW1NS-%s", match[3].str().c_str()); + } else { + snprintf(buf, 36, "GW1N-%s", match[3].str().c_str()); + } chipArgs.family = buf; chipArgs.package = match[5]; chipArgs.speed = match[6]; diff --git a/gui/application.cc b/gui/application.cc index 3f6d538b..d3260684 100644 --- a/gui/application.cc +++ b/gui/application.cc @@ -27,6 +27,10 @@ #include <exception> #include "log.h" +#ifdef __linux__ +#include <execinfo.h> +#endif + NEXTPNR_NAMESPACE_BEGIN #ifdef _WIN32 @@ -39,6 +43,53 @@ BOOL WINAPI WinHandler(DWORD dwCtrlType) } #endif +namespace { +#ifdef __linux__ +std::string get_backtrace_str() +{ + static const size_t MAX_BT_SIZE = 1024; + std::array<void *, MAX_BT_SIZE> bt_data; + int bt_len = backtrace(bt_data.data(), MAX_BT_SIZE); + char **bt_symbols = backtrace_symbols(bt_data.data(), bt_len); + if (bt_symbols == nullptr) + return ""; + std::ostringstream ss; + ss << "Backtrace: " << std::endl; + for (int i = 0; i < bt_len; i++) + ss << " " << bt_symbols[i] << std::endl; + free(bt_symbols); + return ss.str(); +} +#else +std::string get_backtrace_str() { return ""; } +#endif + +void do_error() +{ + std::string bt = get_backtrace_str(); + + std::exception_ptr eptr = std::current_exception(); + std::string err_msg = "Unknown Exception Type"; + + try { + if (eptr) { + std::rethrow_exception(eptr); + } + } catch (const std::exception &e) { + err_msg = e.what(); + } catch (...) { + } + + QString msg; + QTextStream out(&msg); + out << "Internal Error: " << err_msg.c_str() << "\n"; + out << bt.c_str(); + QMessageBox::critical(0, "Error", msg); + std::abort(); +} + +} // namespace + Application::Application(int &argc, char **argv, bool noantialiasing) : QApplication(argc, argv) { QSurfaceFormat fmt; @@ -64,23 +115,18 @@ Application::Application(int &argc, char **argv, bool noantialiasing) : QApplica #ifdef _WIN32 SetConsoleCtrlHandler((PHANDLER_ROUTINE)WinHandler, TRUE); #endif + + std::set_terminate(do_error); } bool Application::notify(QObject *receiver, QEvent *event) { - bool retVal = true; try { - retVal = QApplication::notify(receiver, event); - } catch (const assertion_failure &ex) { - QString msg; - QTextStream out(&msg); - out << ex.filename.c_str() << " at " << ex.line << "\n"; - out << ex.msg.c_str(); - QMessageBox::critical(0, "Error", msg); - } catch (...) { - QMessageBox::critical(0, "Error", "Fatal error !!!"); + return QApplication::notify(receiver, event); + } catch (log_execution_error_exception) { + QMessageBox::critical(0, "Error", "Pass failed, see log for details!"); + return true; } - return retVal; } NEXTPNR_NAMESPACE_END diff --git a/ice40/pack.cc b/ice40/pack.cc index b9857fbd..0db78992 100644 --- a/ice40/pack.cc +++ b/ice40/pack.cc @@ -1090,7 +1090,7 @@ void set_period(Context *ctx, CellInfo *ci, IdString port, delay_t period) if (!equals_epsilon(to->clkconstr->period.maxDelay(), period)) log_warning(" Overriding derived constraint of %.1f MHz on net %s with user-specified constraint of " "%.1f MHz.\n", - MHz(ctx, to->clkconstr->period.maxDelay()), to->name.c_str(ctx), MHz(ctx, period)); + MHz(ctx, period), to->name.c_str(ctx), MHz(ctx, to->clkconstr->period.maxDelay())); return; } to->clkconstr = std::unique_ptr<ClockConstraint>(new ClockConstraint()); @@ -1271,11 +1271,12 @@ static void pack_special(Context *ctx) {std::make_tuple(id_SB_SPI, "0b0010"), Loc(25, 0, 1)}, {std::make_tuple(id_SB_I2C, "0b0011"), Loc(25, 31, 0)}, }; - if (map_ba74.find(std::make_tuple(ci->type, ci->params[ctx->id("BUS_ADDR74")].as_string())) == - map_ba74.end()) + std::string bus_addr74 = + str_or_default(ci->params, ctx->id("BUS_ADDR74"), is_sb_i2c(ctx, ci) ? "0b0001" : "0b0000"); + if (map_ba74.find(std::make_tuple(ci->type, bus_addr74)) == map_ba74.end()) log_error("Invalid value for BUS_ADDR74 for cell '%s' of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx)); - Loc bel_loc = map_ba74.at(std::make_tuple(ci->type, ci->params[ctx->id("BUS_ADDR74")].as_string())); + Loc bel_loc = map_ba74.at(std::make_tuple(ci->type, bus_addr74)); BelId bel = ctx->getBelByLocation(bel_loc); if (bel == BelId() || ctx->getBelType(bel) != ci->type) log_error("Unable to find placement for cell '%s' of type '%s'\n", ci->name.c_str(ctx), |